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:


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 with the 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 is 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'])
    if net.res_bus.vm_pu.min() < vmin:
        critical_lines.append([l, 'lv'])
    if net.res_line.loading_percent.max() > line_loading_max:
        critical_lines.append([l, 'ol'])
    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.


[[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()

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);


power system contingency analysis results

That’s it. Don’t forget to share this post with those you think will be interested in it.