This is an introduction on how to use the pandapower optimal power flow for calculation optimal distributed generation curtailment.
We use the four bus example network from the basic OPF tutorial:
We first create this network in pandapower:
import pandapower as pp
from numpy import array
net = pp.create_empty_network()
#create buses
bus1 = pp.create_bus(net, vn_kv=220., min_vm_pu=1.0, max_vm_pu=1.02)
bus2 = pp.create_bus(net, vn_kv=110., min_vm_pu=1.0, max_vm_pu=1.02)
bus3 = pp.create_bus(net, vn_kv=110., min_vm_pu=1.0, max_vm_pu=1.02)
bus4 = pp.create_bus(net, vn_kv=110., min_vm_pu=1.0, max_vm_pu=1.02)
#create 220/110 kV transformer
pp.create_transformer(net, bus1, bus2, std_type="100 MVA 220/110 kV", max_loading_percent=50)
#create 110 kV lines
pp.create_line(net, bus2, bus3, length_km=70., std_type='149-AL1/24-ST1A 110.0', max_loading_percent=50)
pp.create_line(net, bus3, bus4, length_km=50., std_type='149-AL1/24-ST1A 110.0', max_loading_percent=50)
pp.create_line(net, bus4, bus2, length_km=40., std_type='149-AL1/24-ST1A 110.0', max_loading_percent=50)
#create loads
pp.create_load(net, bus2, p_kw=60e3, controllable = False)
pp.create_load(net, bus3, p_kw=70e3, controllable = False)
pp.create_load(net, bus4, p_kw=10e3, controllable = False)
#create generators
eg = pp.create_ext_grid(net, bus1)
g0 = pp.create_gen(net, bus3, p_kw=-80e3, min_p_kw=-80e3, max_p_kw=0., vm_pu=1.01, controllable=True)
g1 = pp.create_gen(net, bus4, p_kw=-100e3, min_p_kw=-100e3, max_p_kw=0., vm_pu=1.01, controllable=True)
pp.create_polynomial_cost(net, 0, 'gen', array([-1e5, 0]))
pp.create_polynomial_cost(net, 1, 'gen', array([-1e5, 0]))
pp.runopp(net, verbose=True)
PYPOWER Version 5.0.0, 29-May-2015 -- AC Optimal Power Flow Python Interior Point Solver - PIPS, Version 1.0, 07-Feb-2011 Converged!
Because of the negative costs, the OPF now maximizes power generation at the generators:
net.res_gen
p_kw | q_kvar | va_degree | vm_pu | |
---|---|---|---|---|
0 | -46052.570188 | -18913.037576 | -0.485066 | 1.02 |
1 | -85097.859292 | 19478.259200 | 2.869968 | 1.02 |
net.res_cost
-13115042947.98368
net.res_ext_grid
p_kw | q_kvar | |
---|---|---|
0 | -11071.749595 | 153.95827 |
net.res_bus
vm_pu | va_degree | p_kw | q_kvar | |
---|---|---|---|---|
0 | 1.00 | 0.000000 | -11071.749595 | 153.958270 |
1 | 1.00 | -0.759444 | 60000.000000 | 0.000000 |
2 | 1.02 | -0.485066 | 23947.429812 | -18913.037576 |
3 | 1.02 | 2.869968 | -75097.859292 | 19478.259200 |
net.res_trafo
p_hv_kw | q_hv_kvar | p_lv_kw | q_lv_kvar | pl_kw | ql_kvar | i_hv_ka | i_lv_ka | loading_percent | |
---|---|---|---|---|---|---|---|---|---|
0 | 11071.749595 | -153.95827 | -11013.580692 | 324.30853 | 58.168903 | 170.35026 | 0.029059 | 0.057831 | 11.07282 |
net.res_line
p_from_kw | q_from_kvar | p_to_kw | q_to_kvar | pl_kw | ql_kvar | i_from_ka | i_to_ka | i_ka | loading_percent | |
---|---|---|---|---|---|---|---|---|---|---|
0 | -4940.648422 | -7253.511043 | 5009.659789 | 5024.015034 | 69.011368 | -2229.496009 | 0.046064 | 0.036508 | 0.046064 | 9.800771 |
1 | -28957.089601 | 13889.022542 | 29770.915374 | -13899.350544 | 813.825772 | -10.328003 | 0.165259 | 0.169067 | 0.169067 | 35.971608 |
2 | 45326.943919 | -5578.908656 | -44045.770886 | 6929.202512 | 1281.173032 | 1350.293856 | 0.235000 | 0.234024 | 0.235000 | 50.000000 |
Obviously the voltage profile was the limiting factor for the generator feed-in. If we relax this constraint a little bit:
net.bus["max_vm_pu"] = 1.05
pp.runopp(net)
We see an increased feed-in of the generators:
net.res_gen
p_kw | q_kvar | va_degree | vm_pu | |
---|---|---|---|---|
0 | -80000.00000 | -17252.534836 | 2.328268 | 1.050000 |
1 | -66517.87775 | 10226.373497 | 3.666003 | 1.040019 |
net.res_bus
vm_pu | va_degree | p_kw | q_kvar | |
---|---|---|---|---|
0 | 1.000000 | 0.000000 | 4542.418041 | 8523.878212 |
1 | 1.010373 | 0.298334 | 60000.000000 | 0.000000 |
2 | 1.050000 | 2.328268 | -10000.000000 | -17252.534836 |
3 | 1.040019 | 3.666003 | -56517.877750 | 10226.373497 |
net.res_cost
-14651787774.974997