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 pandas as pd
import pandapower as pp
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=100)
#create 110 kV lines
pp.create_line(net, bus2, bus3, length_km=70., std_type='149-AL1/24-ST1A 110.0', max_loading_percent=100)
pp.create_line(net, bus3, bus4, length_km=50., std_type='149-AL1/24-ST1A 110.0', max_loading_percent=100)
pp.create_line(net, bus4, bus2, length_km=40., std_type='149-AL1/24-ST1A 110.0', max_loading_percent=100)
#create loads
pp.create_load(net, bus2, p_mw=60, controllable=False)
pp.create_load(net, bus3, p_mw=70, controllable=False)
pp.create_load(net, bus4, p_mw=10, controllable=False)
#create generators
eg = pp.create_ext_grid(net, bus1)
g0 = pp.create_gen(net, bus3, p_mw=80, min_p_mw=0, max_p_mw=80, vm_pu=1.01, controllable=True)
g1 = pp.create_gen(net, bus4, p_mw=100, min_p_mw=0, max_p_mw=100, vm_pu=1.01, controllable=True)
pp.create_poly_cost(net, 0, 'gen', cp1_eur_per_mw=-1)
pp.create_poly_cost(net, 1, 'gen', cp1_eur_per_mw=-1)
pp.create_poly_cost(net, 0, 'ext_grid', cp1_eur_per_mw=0)
pp.runopp(net, verbose=True)
hp.pandapower.run - INFO: These elements have missing power constraint values, which are considered in OPF as +- 1000 TW: ['gen']
PYPOWER Version 5.1.4, 27-June-2018 -- AC Optimal Power Flow Python Interior Point Solver - PIPS, Version 1.0, 07-Feb-2011 Converged! Converged in 1.05 seconds Objective Function Value = -131.29 $/hr ================================================================================ | PyPower (ppci) System Summary - these are not valid for pandapower DataFrames| ================================================================================ How many? How much? P (MW) Q (MVAr) --------------------- ------------------- ------------- ----------------- Buses 4 Total Gen Capacity 1000000180.0 -3000000000.0 to 3000000000.0 Generators 3 On-line Capacity 1000000180.0 -3000000000.0 to 3000000000.0 Committed Gens 3 Generation (actual) 143.2 1.3 Loads 3 Load 140.0 0.0 Fixed 3 Fixed 140.0 0.0 Dispatchable 0 Dispatchable 0.0 of 0.0 0.0 Shunts 0 Shunt (inj) 0.0 0.0 Branches 4 Losses (I^2 * Z) 3.11 6.73 Transformers 4 Branch Charging (inj) - 5.4 Inter-ties 0 Total Inter-tie Flow 0.0 0.0 Areas 1 Minimum Maximum ------------------------- -------------------------------- Voltage Magnitude 1.000 p.u. @ bus 0 1.020 p.u. @ bus 3 Voltage Angle -1.33 deg @ bus 2 3.20 deg @ bus 3 P Losses (I^2*R) - 1.54 MW @ line 3-1 Q Losses (I^2*X) - 3.26 MVAr @ line 3-1 Lambda P -1.00 $/MWh @ bus 2 0.08 $/MWh @ bus 1 Lambda Q -0.00 $/MWh @ bus 3 2.11 $/MWh @ bus 1 ================================================================================ | Area Summary | ================================================================================ Area # of # of Gens # of Loads # of # of # of # of Num Buses Total Online Total Fixed Disp Shunt Brchs Xfmrs Ties ---- ----- ----- ------ ----- ----- ----- ----- ----- ----- ----- 1 4 3 3 3 3 0 0 4 4 0 ---- ----- ----- ------ ----- ----- ----- ----- ----- ----- ----- Tot: 4 3 3 3 3 0 0 4 4 0 Area Total Gen Capacity On-line Gen Capacity Generation Num MW MVAr MW MVAr MW MVAr ---- ------ ------------------ ------ ------------------ ------ ------ 1 1000000180.0 -3000000000.0 to 3000000000.0 1000000180.0 -3000000000.0 to 3000000000.0 143.2 1.3 ---- ------ ------------------ ------ ------------------ ------ ------ Area Disp Load Cap Disp Load Fixed Load Total Load Num MW MVAr MW MVAr MW MVAr MW MVAr ---- ------ ------ ------ ------ ------ ------ ------ ------ 1 0.0 0.0 0.0 0.0 140.0 0.0 140.0 0.0 ---- ------ ------ ------ ------ ------ ------ ------ ------ Tot: 0.0 0.0 0.0 0.0 140.0 0.0 140.0 0.0 Area Shunt Inj Branch Series Losses Net Export Num MW MVAr Charging MW MVAr MW MVAr ---- ------ ------ -------- ------ ------ ------ ------ 1 0.0 0.0 5.4 3.11 6.73 0.0 0.0 ---- ------ ------ -------- ------ ------ ------ ------ Tot: 0.0 0.0 5.4 3.11 6.73 - - ================================================================================ | Generator Data | ================================================================================ Gen Bus Status Pg Qg Lambda ($/MVA-hr) # # (MW) (MVAr) P Q ---- ----- ------ -------- -------- -------- -------- 0 0 1 11.88 -0.16 0.00 -0.00 1 2 1 31.29 26.78 -1.00 0.00 2 3 1 100.00 -25.32 -0.99 -0.00 -------- -------- Total: 143.17 1.30 ================================================================================ | Bus Data | ================================================================================ Bus Voltage Generation Load Lambda($/MVA-hr) # Mag(pu) Ang(deg) P (MW) Q (MVAr) P (MW) Q (MVAr) P Q ----- ------- -------- -------- -------- -------- -------- ------- ------- 0 1.000 0.000* 11.88 -0.16 - - 0.000 - 1 1.000 -0.815 - - 60.00 0.00 0.076 2.112 2 1.020 -1.328 31.29 26.78 70.00 0.00 -1.000 - 3 1.020 3.203 100.00 -25.32 10.00 0.00 -0.990 - -------- -------- -------- -------- Total: 143.17 1.30 140.00 0.00 ================================================================================ | Branch Data | ================================================================================ Brnch From To From Bus Injection To Bus Injection Loss (I^2 * Z) # Bus Bus P (MW) Q (MVAr) P (MW) Q (MVAr) P (MW) Q (MVAr) ----- ----- ----- -------- -------- -------- -------- -------- -------- 0 1 2 -0.11 -9.53 0.19 7.32 0.079 0.17 1 2 3 -38.89 19.46 40.38 -18.05 1.484 3.14 2 3 1 49.62 -7.27 -48.07 9.18 1.544 3.26 3 0 1 11.88 -0.16 -11.82 0.35 0.004 0.17 -------- -------- Total: 3.110 6.73 ================================================================================ | Voltage Constraints | ================================================================================ Bus # Vmin mu Vmin |V| Vmax Vmax mu ----- -------- ----- ----- ----- -------- 0416735.146 1.000 1.000 1.000418496.675 1 4202.091 1.000 1.000 1.020 - 2 - 1.000 1.020 1.020 912.807 3 - 1.000 1.020 1.020 1587.534 ================================================================================ | Generation Constraints | ================================================================================ Gen Bus Active Power Limits # # Pmin mu Pmin Pg Pmax Pmax mu ---- ----- ------- -------- -------- -------- ------- 0 0 - -1000000000.00 11.881000000000.00 - 1 2 - -0.00 31.29 80.00 - 2 3 - -0.00 100.00 100.00 - Gen Bus Reactive Power Limits # # Qmin mu Qmin Qg Qmax Qmax mu --- --- ------- -------- -------- -------- ------- 0 0 - -1000000000.00 -0.161000000000.00 - 1 2 - -1000000000.00 26.781000000000.00 - 2 3 - -1000000000.00 -25.321000000000.00 - ================================================================================ | Dispatchable Load Constraints | ================================================================================ Gen Bus Active Power Limits # # Pmin mu Pmin Pg Pmax Pmax mu --- --- ------- -------- -------- -------- ------- Gen Bus Reactive Power Limits # # Qmin mu Qmin Qg Qmax Qmax mu --- --- ------- -------- -------- -------- ------- ================================================================================ | Branch Flow Constraints | ================================================================================ Brnch From "From" End Limit "To" End To # Bus |If| mu |If| |Imax| |It| |It| mu Bus ----- ----- ------- -------- -------- -------- ------- ----- 0 1 - 9.53 89.55 7.18 - 2 1 2 - 42.64 89.55 43.36 - 3 2 3 - 49.16 89.55 48.94 - 1 3 0 - 11.88 100.00 11.82 - 1
Because of the negative costs, the OPF now maximizes power generation at the generators, which is constrained by their maximum power:
pd.concat([net.res_gen.p_mw, net.gen.min_p_mw, net.gen.max_p_mw], axis=1)
p_mw | min_p_mw | max_p_mw | |
---|---|---|---|
0 | 31.291535 | 0.0 | 80.0 |
1 | 99.996080 | 0.0 | 100.0 |
While gen 1 is operating at the limit, gen 0 is below the maximum output. Apparently the generator can not reach its maximum output without violating at least one power flow constraint. Let's check on the constraints.
The line and transformer constraints are not reached:
pd.concat([net.line.max_loading_percent, net.res_line.loading_percent], axis=1)
max_loading_percent | loading_percent | |
---|---|---|
0 | 100.0 | 10.641062 |
1 | 100.0 | 48.424267 |
2 | 100.0 | 54.903233 |
pd.concat([net.trafo.max_loading_percent, net.res_trafo.loading_percent], axis=1)
max_loading_percent | loading_percent | |
---|---|---|
0 | 100.0 | 11.878748 |
But the voltage constraints are:
pd.concat([net.res_bus.vm_pu, net.bus.min_vm_pu, net.bus.max_vm_pu], axis=1)
vm_pu | min_vm_pu | max_vm_pu | |
---|---|---|---|
0 | 1.00 | 1.0 | 1.02 |
1 | 1.00 | 1.0 | 1.02 |
2 | 1.02 | 1.0 | 1.02 |
3 | 1.02 | 1.0 | 1.02 |
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)
hp.pandapower.run - INFO: These elements have missing power constraint values, which are considered in OPF as +- 1000 TW: ['gen']
We see an increased feed-in of the generators:
pd.concat([net.res_gen.p_mw, net.gen.min_p_mw, net.gen.max_p_mw], axis=1)
p_mw | min_p_mw | max_p_mw | |
---|---|---|---|
0 | 79.999431 | 0.0 | 80.0 |
1 | 99.999433 | 0.0 | 100.0 |
net.res_bus
vm_pu | va_degree | p_mw | q_mvar | lam_p | lam_q | |
---|---|---|---|---|---|---|
0 | 1.000000 | 0.000000 | 35.786224 | -0.067783 | -4.109210e-20 | -5.867149e-23 |
1 | 1.001785 | 2.458261 | 60.000000 | 0.000000 | -1.244368e-05 | 1.415507e-04 |
2 | 1.040927 | 5.819145 | -9.999431 | -8.451257 | -1.093902e-04 | 1.127308e-20 |
3 | 1.045688 | 7.674112 | -89.999433 | 3.875067 | -1.234271e-04 | -5.853501e-21 |