Power System Contingency Analysis with Python PandaPower
This post will demonstrate how python’s pandapower package can be used to carry out a power system contingency analysis.
We will use the IEEE 14 bus test system included pandapower with panadapower, with some small modifications, for this demonstration.
Load the Relevant Packages in Python
import pandas as pd import pandapower as pp import pandapower.networks as ppnets import pandapower.plotting as plt import matplotlib.pyplot as mplt
Load the Test Network
net = ppnets.case14()
The network summary is given by:
net This pandapower network includes the following parameter tables: - bus (14 elements) - load (11 elements) - gen (4 elements) - shunt (1 element) - ext_grid (1 element) - line (15 elements) - trafo (5 elements) - poly_cost (5 elements) - bus_geodata (14 elements)
Modify the Test Network
Disable the included external grid so that network is essentially stand-alone so that it can be stressed a bit more. More stress on the network means more lines become critical, which is good for contingency analysis demonstration purposes.
net.ext_grid['in_service'] = False
Scale up the default load so as to further stress the network:
net.load.scaling = 1.5
Adjust the terminal voltages of the generators in the network so that bus voltages are within a more practical range, 0.95pu to 1.05pu.
net.gen['vm_pu'] = 1.045
Perform a simple generator dispatch by maxing out the first three generators and setting the fourth one as the slack.
net.gen.loc[0,'p_mw'] = 120 net.gen.loc[1,'p_mw'] = 100 net.gen.loc[2,'p_mw'] = 100 net.gen.loc[3,'slack'] = True
Run the Power Flow
Run the power flow by executing the pandapower ‘runnpp’ command:
Print the Total Generation and Load as a Quick Check
Let’s use the following commands to have a look at the initial load flow of the network to see that everything is as expected and we didn’t miss anything:
gen_mw_total = net.res_gen['p_mw'].sum() imports_mw_total = net.res_ext_grid['p_mw'].sum() print('total gen MW:', gen_mw_total + imports_mw_total) print('total imported gen MW:', imports_mw_total) print('total local gen MW:', gen_mw_total) print('total load MW:', net.res_load['p_mw'].sum()) total gen MW: 392.0112578522987 total imported gen MW: 0.0 total local gen MW: 392.0112578522987 total load MW: 388.50000000000006
Display the Generation Parameters and Generator Dispatch to Check no Limits are Violated
Display the parameters of the generators with the following code:
Check the Bus Voltages Also
Display the bus voltages using the following code:
Perform the Contingency Analysis
The power system contingency analysis is performed by sequentially taking each line out of service, running the power flow, checking for network violations and returning the line to service before repeating.
The lines that result in a network voltage or loading limit violation are stored in ‘critical_lines’ along with an abbreviation of the violation caused.
lines = net.line.index critical_lines =  critical_lines_indx =  vmax = 1.05 vmin = 0.95 line_loading_max = 100 for l in lines: net.line.loc[l, 'in_service'] = False pp.runpp(net, numba=False) if net.res_bus.vm_pu.max()>vmax: critical_lines.append([l, 'hv']) critical_lines_indx.append(l) if net.res_bus.vm_pu.min() < vmin: critical_lines.append([l, 'lv']) critical_lines_indx.append(l) if net.res_line.loading_percent.max() > line_loading_max: critical_lines.append([l, 'ol']) critical_lines_indx.append(l) net.line.loc[l, 'in_service'] = True
Print the Critical Lines
Print the critical lines and the effect that losing them will have on the network.
print(crtical_lines) [[9, 'lv'], [11, 'lv']]
Therefore lines 9 and 11 are critical lines and if either of them trips then it will result in low voltage in the network.
Bonus – Plot the Network and Highlight the Critical Lines
For easy visualization, we can plot the network and highlight the critical lines in red as shown below.
fig, ax = mplt.subplots() fig.set_figheight(6) fig.set_figwidth(8) critical_lc = plt.create_line_collection(net, critical_lines_indx, color="r", zorder=2) #create lines plt.draw_collections([critical_lc], ax=ax); plt.simple_plot(net, plot_loads=True, plot_gens=True, ax=ax, show_plot=False); mplt.show()
That’s it. Don’t forget to share this post with those you think will be interested in it.