#!/usr/bin/env python # coding: utf-8 # # Diagnostic Function # A power flow calculation on a pandapower network can fail to converge (or fail to run at all) for a vast variety of reasons, which often makes debugging difficult, annoying and time consuming. To help with that, the diagnostic function automatically checks pandapower networks for the most common issues leading to errors. It provides logging output and diagnoses with a controllable level of detail. # ## Example: faulty network # # To demonstrate the usage of the diagnostic function we will use a very basic exemplary pandapower network with several flaws. # # There will be no further explenation on how to create pandapower networks since there's a separate tutorial available in this regard. # In[1]: import warnings warnings.filterwarnings("ignore") # imports the pandapower module import pandapower as pp # defines a function that creates an example network, which will be used a lot in this tutorial # run this code first in order for the examples to work def faulty_example_network(): net = pp.create_empty_network() pp.create_bus(net, name = "110 kV bar", vn_kv = 110, type = 'b', in_service = 'True') pp.create_bus(net, name = "20 kV bar", vn_kv = 20, type = 'b') pp.create_bus(net, name = "bus 2", vn_kv = 30, type = 'b') pp.create_bus(net, name = "bus 3", vn_kv = 20, type = 'b') pp.create_bus(net, name = "bus 4", vn_kv = 20, type = 'b') pp.create_bus(net, name = "bus 5", vn_kv = -20, type = 'b') pp.create_bus(net, name = "bus 6", vn_kv = 20, type = 'b') pp.create_ext_grid(net, 0, vm_pu = 1) pp.create_line(net, name = "line 0", from_bus = 1, to_bus = 2, length_km = 0, std_type = "NAYY 4x150 SE") pp.create_line(net, name = "line 1", from_bus = 2, to_bus = 3, length_km = 1, std_type = "NAYY 4x150 SE") pp.create_line(net, name = "line 2", from_bus = 3, to_bus = 4, length_km = 1, std_type = "NAYY 4x150 SE") pp.create_line(net, name = "line 3", from_bus = 4, to_bus = 5, length_km = 1, std_type = "NAYY 4x150 SE") pp.create_line(net, name = "line 4", from_bus = 5, to_bus = 6, length_km = 1, std_type = "NAYY 4x150 SE") pp.create_line(net, name = "line 5", from_bus = 6, to_bus = 1, length_km = 1, std_type = "NAYY 4x150 SE") pp.create_transformer_from_parameters(net, hv_bus = 1, lv_bus = 0, i0_percent= 0.038, pfe_kw = 11.6, vkr_percent = 0.322, sn_mva = 40.0, vn_lv_kv = 22.0, vn_hv_kv = 110.0, vk_percent = 17.8) pp.create_load(net, 2, p_mw = -1, q_mvar = 0.200, name = "load 0") pp.create_load(net, 3, p_mw = 1, q_mvar = 0.200, name = "load 1") pp.create_load(net, 4, p_mw = 1, q_mvar = 0.200, name = "load 2") pp.create_load(net, 5, p_mw = 1, q_mvar = 0.200, name = "load 3") pp.create_load(net, 6, p_mw = 1, q_mvar = 0.200, name = "load 4") pp.create_switch(net, bus = 1, element = 0, et = 'l') pp.create_switch(net, bus = 2, element = 0, et = 'l') pp.create_switch(net, bus = 2, element = 1, et = 'l') pp.create_switch(net, bus = 3, element = 1, et = 'l') pp.create_switch(net, bus = 3, element = 2, et = 'l') pp.create_switch(net, bus = 4, element = 2, et = 'l') pp.create_switch(net, bus = 4, element = 3, et = 'l', closed = False) pp.create_switch(net, bus = 5, element = 3, et = 'l') pp.create_switch(net, bus = 5, element = 4, et = 'l', closed = False) pp.create_switch(net, bus = 6, element = 4, et = 'l', closed = False) pp.create_switch(net, bus = 6, element = 5, et = 'l') pp.create_switch(net, bus = 1, element = 5, et = 'l') return net # #### Now let's create the network and try to run a load flow calculation. # In[2]: # creates the example network faulty_net = faulty_example_network() pp.runpp(faulty_net) # As you can see there is at least one error in our network that prevents the load flow calculation from running properly. # #### Instead of analysing the code ourselves, we can use the diagnostic function to find the errors. # In[3]: # diagnoses the faulty network pp.diagnostic(faulty_net) You can scroll through the report to see all checks that have been performed and their results. In default mode, the report always looks like this: _____________ PANDAPOWER DIAGNOSTIC TOOL _____________ Checking for [description of check 1]... [detailed error description] SUMMARY: [summary of errors found] -------- Checking for [description of the check 2]... [detailed error description] SUMMARY: [summary of errors found] -------- _____________ END OF PANDAPOWER DIAGNOSTIC _____________ The function also return a dict that contains the indices of all elements where errors were found for further usage. # #### Customizing the report # # For a more compact report, there a two options that can be passed as arguments to the diagnostic function: # # - warnings_only = True : only positive check results (errors found) will be in the report # # - detailed_report = False: only error summaries, no detailed error descriptions # # # The default setting is: # # *diagnostic(net, warnings_only = False, detailed_report = True)* # In[4]: # report with warnings_only pp.diagnostic(faulty_net, warnings_only=True) # In[5]: # report with summaries only pp.diagnostic(faulty_net, report_style="compact") # #### Let's have a look a the default report again # In[6]: # diagnoses the faulty network pp.diagnostic(faulty_net) # #### Disconnected elements hp.pandapower.diagnostic_reports - WARNING: Checking for elements without a connection to an external grid... hp.pandapower.diagnostic_reports - WARNING: hp.pandapower.diagnostic_reports - WARNING: Disconnected section found, consisting of the following elements: hp.pandapower.diagnostic_reports - WARNING: buses: [5] hp.pandapower.diagnostic_reports - WARNING: switches: [7, 8] hp.pandapower.diagnostic_reports - WARNING: lines: [3] hp.pandapower.diagnostic_reports - WARNING: loads: [3] hp.pandapower.diagnostic_reports - WARNING: hp.pandapower.diagnostic_reports - WARNING: SUMMARY: 5 disconnected element(s) found. # Bus 5, line 3, load 3 as well as switches 7 and 8 are not connected to an external grid. To fix that, we close switches 8 and 9. # In[7]: faulty_net.switch.closed.loc[8, 9] = True # #### Inconsistent voltages hp.pandapower.diagnostic_reports - WARNING: Checking for connections of different voltage levels... hp.pandapower.diagnostic_reports - WARNING: hp.pandapower.diagnostic_reports - WARNING: line 0 connects bus 1: 20 kV bar (vn_kv = 20.0) and bus 2: bus 2 (vn_kv = 30.0) hp.pandapower.diagnostic_reports - WARNING: line 1 connects bus 2: bus 2 (vn_kv = 30.0) and bus 3: bus 3 (vn_kv = 20.0) hp.pandapower.diagnostic_reports - WARNING: line 3 connects bus 4: bus 4 (vn_kv = 20.0) and bus 5: bus 5 (vn_kv = -20.0) hp.pandapower.diagnostic_reports - WARNING: line 4 connects bus 5: bus 5 (vn_kv = -20.0) and bus 6: bus 6 (vn_kv = 20.0) hp.pandapower.diagnostic_reports - WARNING: hp.pandapower.diagnostic_reports - WARNING: SUMMARY: 4 element(s) that connect different voltage levels found. hp.pandapower.diagnostic_reports - WARNING: # Lines 0, 1, 3 and 4 connect different voltage levels. Apparently bus 2 (30 kV) and bus 5 (-20 kV) have wrong voltage levels values. We change that to 20 kV. # In[8]: faulty_net.bus.vn_kv.loc[2, 5] = 20 # #### Lines with impedance zero hp.pandapower.diagnostic_reports - WARNING: Checking for impedance values close to zero... hp.pandapower.diagnostic_reports - WARNING: hp.pandapower.diagnostic_reports - WARNING: line 0: r_ohm <= 0.001 or x_ohm <= 0.001 hp.pandapower.diagnostic_reports - WARNING: Power flow still does not converge after replacing implausible elements with switches. hp.pandapower.diagnostic_reports - WARNING: hp.pandapower.diagnostic_reports - WARNING: SUMMARY: 1 element(s) with impedance values close to zero found. hp.pandapower.diagnostic_reports - WARNING: # Line 0 is 0 km long (and therefore its impedance is 0 ohm). We change the length to 1 km. # In[9]: faulty_net.line.length_km.at[0] = 1 # #### Deviating nominal voltages hp.pandapower.diagnostic_reports - WARNING: Checking for components with deviating nominal voltages... hp.pandapower.diagnostic_reports - WARNING: hp.pandapower.diagnostic_reports - WARNING: Trafo(s) [0]: hv and lv connectors seem to be swapped hp.pandapower.diagnostic_reports - WARNING: hp.pandapower.diagnostic_reports - WARNING: SUMMARY: 1 component(s) with deviating nominal voltages found hp.pandapower.diagnostic_reports - WARNING: # We fix that by swapping the hv and lv connectors of trafo 0 back. # In[10]: faulty_net.trafo.hv_bus.at[0] = 0 faulty_net.trafo.lv_bus.at[0] = 1 # #### Wrong reference system hp.pandapower.diagnostic_reports - WARNING: Checking for usage of wrong reference system... hp.pandapower.diagnostic_reports - WARNING: hp.pandapower.diagnostic_reports - WARNING: Found load 0: 'load 0' with p_mw = -1.0. In load reference system p_mw should be positive. hp.pandapower.diagnostic_reports - WARNING: hp.pandapower.diagnostic_reports - WARNING: SUMMARY: Found 1 load(s) with negative p_mw. In load reference system, p_mw should be positive. If the intention was to model a constant generation, please use an sgen instead. # Load 0 has a p_mw value of -1. Since pandapower uses the load reference system for loads, the value should either be positive or an sgen should be used. We assume the former and change the value to 1. # In[11]: faulty_net.load.p_mw.at[0] = 1 # In[12]: # new diagnosis to check, whether we fixed all errors pp.diagnostic(faulty_net) # Apparently, all errors have been fixed. So we can try again to run a loadflow calculation. # In[13]: pp.runpp(faulty_net) faulty_net # Now the loadflow calculation runs without problems. # ### Detecting overloads and wrong switch configurations # Other errors the diagnostic function can help to identify, are networks with too much load or wrong switch configurations. The function automatically checks, if the loadflow converges with all loads and generation scaled down to 0.1% or with all switches closed. # #### Example # In[14]: # imports the pandapower module import pandapower as pp # defines a function that creates an example network # run this code first in order for the examples to work def overload_example_network(): net = pp.create_empty_network() pp.create_bus(net, name = "110 kV bar", vn_kv = 110, type = 'b') pp.create_bus(net, name = "20 kV bar", vn_kv = 20, type = 'b') pp.create_bus(net, name = "bus 2", vn_kv = 20, type = 'b') pp.create_bus(net, name = "bus 3", vn_kv = 20, type = 'b') pp.create_bus(net, name = "bus 4", vn_kv = 20, type = 'b') pp.create_bus(net, name = "bus 5", vn_kv = 20, type = 'b') pp.create_bus(net, name = "bus 6", vn_kv = 20, type = 'b') pp.create_ext_grid(net, 0, vm_pu = 1) pp.create_line(net, name = "line 0", from_bus = 1, to_bus = 2, length_km = 1, std_type = "NAYY 4x150 SE") pp.create_line(net, name = "line 1", from_bus = 2, to_bus = 3, length_km = 1, std_type = "NAYY 4x150 SE") pp.create_line(net, name = "line 2", from_bus = 3, to_bus = 4, length_km = 1, std_type = "NAYY 4x150 SE") pp.create_line(net, name = "line 3", from_bus = 4, to_bus = 5, length_km = 1, std_type = "NAYY 4x150 SE") pp.create_line(net, name = "line 4", from_bus = 5, to_bus = 6, length_km = 1, std_type = "NAYY 4x150 SE") pp.create_line(net, name = "line 5", from_bus = 6, to_bus = 1, length_km = 1, std_type = "NAYY 4x150 SE") pp.create_transformer_from_parameters(net, hv_bus = 0, lv_bus = 1, i0_percent= 0.038, pfe_kw = 11.6, vkr_percent = 0.322, sn_mva = 40.0, vn_lv_kv = 22.0, vn_hv_kv = 110.0, vk_percent = 17.8) pp.create_load(net, 2, p_mw = 100, q_mvar = 0.2, name = "load 0") pp.create_load(net, 3, p_mw = 100, q_mvar = 0.2, name = "load 1") pp.create_load(net, 4, p_mw = 100, q_mvar = 0.2, name = "load 2") pp.create_load(net, 5, p_mw = 100, q_mvar = 0.2, name = "load 3") pp.create_load(net, 6, p_mw = 100, q_mvar = 0.2, name = "load 4") pp.create_switch(net, bus = 1, element = 0, et = 'l') pp.create_switch(net, bus = 2, element = 0, et = 'l') pp.create_switch(net, bus = 2, element = 1, et = 'l') pp.create_switch(net, bus = 3, element = 1, et = 'l') pp.create_switch(net, bus = 3, element = 2, et = 'l') pp.create_switch(net, bus = 4, element = 2, et = 'l') pp.create_switch(net, bus = 4, element = 3, et = 'l', closed = 0) pp.create_switch(net, bus = 5, element = 3, et = 'l') pp.create_switch(net, bus = 5, element = 4, et = 'l') pp.create_switch(net, bus = 6, element = 4, et = 'l') pp.create_switch(net, bus = 6, element = 5, et = 'l') pp.create_switch(net, bus = 1, element = 5, et = 'l') return net # In[15]: overload_net = overload_example_network() pp.runpp(overload_net) # In[16]: pp.diagnostic(overload_net, warnings_only = True) hp.pandapower.diagnostic_reports - WARNING: Checking for overload... hp.pandapower.diagnostic_reports - WARNING: hp.pandapower.diagnostic_reports - WARNING: overload found: Power flow converges with load scaled down to 0.1 percent. hp.pandapower.diagnostic_reports - WARNING: # As you can see, with loads scaled down, the loadflow converges, which helps to isolate the problem. # In[ ]: