This tutorial describes how to solve some optimization problems regarding reactive power redispatch using PandaModels. The reactive power provision of generators, e.g., net.sgen, can be optimized for
For more details on the corresponding models, see:
Note:
Grid operators want to maintain a constant voltage profile across all or a part of the system buses. Reactive power optimization, in this case, helps them identify the preferable provision of reactive power from generators. The objective is to minimize the deviation of the voltages of certain buses (here, buses of DER) from a setpoint, e.g.,$V_{\rm setpoint} = 1.03 p.u.$ As a result, the reactive power setpoint for each generator is calculated. The mathematical formulation of this objective function can have the following forms:
\begin{align} & \underset{\mathcal{X} = [q, ...]}{\text{minimize}} & & \sum_{i\in \mathcal{BI}} (V_i-V_{\rm setpoint})^2 \\ & \text{subject to} & & g(\mathcal{X})=0 \\ & & & h(\mathcal{X}) \leq 0 \end{align}where $V_{i}$ is the voltage variable of bus $i$ in $\mathcal{BI}$ which denotes the set of buses of DER. The $g(\mathcal{X})$ and $h(\mathcal{X})$, denote equality and inequality constraints, respectively. The $\mathcal{X}$ denotes the set of variables decisions, such as reactive power $q$.
How does it work? Let's first create a cigre grid with DERs(distribution energy resource) from the pandapower's network database. After running the power flow calculation, we can see that there are nine DERs (net.sgen) in the grid, and they don't provide reactive power for normal operation.
import pandapower as pp
import pandapower.networks as nw
from copy import deepcopy
net = nw.create_cigre_network_mv(with_der="pv_wind")
pp.runpp(net) # run power flow calculation
net_org = deepcopy(net) # copy of the grid for further comparison
display(net.res_sgen) # show power flow results for DER
p_mw | q_mvar | |
---|---|---|
0 | 0.02 | 0.0 |
1 | 0.02 | 0.0 |
2 | 0.03 | 0.0 |
3 | 0.03 | 0.0 |
4 | 0.03 | 0.0 |
5 | 0.03 | 0.0 |
6 | 0.04 | 0.0 |
7 | 0.01 | 0.0 |
8 | 1.50 | 0.0 |
(You can use the pandapower plotting module to visualize the grid model.)
import pandapower.plotting as plot
%matplotlib inline
plot.simple_plot(net)
<AxesSubplot:>
Before starting the optimization, we need to configure some parameters. In PandaModels, the uncontrollable loads and DER (net.sgen) are converted to load, and the controllable power elements are converted to generators. Accordingly, we neet to set loads as uncontrollable and set DER (net.sgen) as controllable elements.
net_opt = deepcopy(net) # copy of the grid for further comparison
net_opt.load['controllable'] = False
net_opt.sgen['controllable'] = True
Now, let's set the constraints.
# lower and upper bounds for buses
net_opt.bus["max_vm_pu"] = 1.1
net_opt.bus["min_vm_pu"] = 0.9
# lower and upper bounds for external grid
net_opt.ext_grid["max_q_mvar"] = 10000.0
net_opt.ext_grid["min_q_mvar"] = -10000.0
net_opt.ext_grid["max_p_mw"] = 10000.0
net_opt.ext_grid["min_p_mw"] = -10000.0
We keep the active power for DER constant to the calculated value from the power flow and define a constraint for the reactive power. (In this grid, there is no generator (net.gen))
# lower and upper bounds for DERs
net_opt.sgen["max_p_mw"] = net_opt.sgen.p_mw.values
net_opt.sgen["min_p_mw"] = net_opt.sgen.p_mw.values
net_opt.sgen["max_q_mvar"] = net_opt.sgen.p_mw.values * 0.328
net_opt.sgen["min_q_mvar"] = -net_opt.sgen.p_mw.values * 0.328
display(net_opt.sgen)
name | bus | p_mw | q_mvar | sn_mva | scaling | in_service | type | current_source | controllable | max_p_mw | min_p_mw | max_q_mvar | min_q_mvar | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | PV 3 | 3 | 0.02 | 0.0 | 0.02 | 1.0 | True | PV | True | True | 0.02 | 0.02 | 0.00656 | -0.00656 |
1 | PV 4 | 4 | 0.02 | 0.0 | 0.02 | 1.0 | True | PV | True | True | 0.02 | 0.02 | 0.00656 | -0.00656 |
2 | PV 5 | 5 | 0.03 | 0.0 | 0.03 | 1.0 | True | PV | True | True | 0.03 | 0.03 | 0.00984 | -0.00984 |
3 | PV 6 | 6 | 0.03 | 0.0 | 0.03 | 1.0 | True | PV | True | True | 0.03 | 0.03 | 0.00984 | -0.00984 |
4 | PV 8 | 8 | 0.03 | 0.0 | 0.03 | 1.0 | True | PV | True | True | 0.03 | 0.03 | 0.00984 | -0.00984 |
5 | PV 9 | 9 | 0.03 | 0.0 | 0.03 | 1.0 | True | PV | True | True | 0.03 | 0.03 | 0.00984 | -0.00984 |
6 | PV 10 | 10 | 0.04 | 0.0 | 0.04 | 1.0 | True | PV | True | True | 0.04 | 0.04 | 0.01312 | -0.01312 |
7 | PV 11 | 11 | 0.01 | 0.0 | 0.01 | 1.0 | True | PV | True | True | 0.01 | 0.01 | 0.00328 | -0.00328 |
8 | WKA 7 | 7 | 1.50 | 0.0 | 1.50 | 1.0 | True | WP | True | True | 1.50 | 1.50 | 0.49200 | -0.49200 |
Subsequently, let's set a high upper bound for lines and transformers to avoid congestion issues.
# lower and upper bounds for lines
net_opt.trafo["max_loading_percent"] = 100.0
net_opt.line["max_loading_percent"] = 100.0
Finally, we need to let PandaModels know which buses are of interest and what is user-defined voltage setpoint. To do this, we can add extra column called "pm_param/setpoint_v" in net.bus and set values for buses connecting DER (net.sgen).
net_opt.bus["pm_param/setpoint_v"] = None # add extra column
net_opt.bus["pm_param/setpoint_v"].loc[net_opt.sgen.bus] = 0.96 # voltage setpoints for theses buses are 0.96
Now let's call the function *pandapower.runpm_vstab()* to start the optimizaiton for maintaining voltage setpoints. (Note that the first time the function is called, Julia is started in the background, which may take some time.)
try:
pp.runpm_vstab(net_opt)
except Exception as err:
print(err)
hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 2.29 seconds:
Also, the following parameters and options can be modified as inputs while calling the optimization from PandaModles. For this case, we can keep the default values.
parameter | description | type | default |
---|---|---|---|
correct_pm_network_data | checks if network data is correct. If not tries to correct it | bool | True |
silence | Suppresses information and warning messages output by PowerModels | bool | True |
pm_model | PowerModels.jl model to use | str | "ACPPowerModel" |
pm_solver | "main" solver | str | "ipopt" |
pm_mip_solver | mixed integer solver | str | "cbc" |
pm_nl_solver | nonlinear solver | str | "ipopt" |
pm_tol | default desired convergence tolerance for solver to use | float | 1e-8 |
pm_log_level | solver log level in power models | int | 0 |
delete_buffer_file | If True, the .json file used by PandaModels will be deleted after optimization. | bool | True |
Let's check the results for voltage and reactive power at buses connecting DER before and after the optimization.
# results from power flow
display(net_org.res_bus.vm_pu[net_org.sgen.bus])
v_org = net_org.res_bus.vm_pu[net_org.sgen.bus].values
print("--- deviation from setpionts v=0.96 ---")
display(sum((v_org-0.96)**2))
3 0.951848 4 0.950094 5 0.948893 6 0.947488 8 0.949187 9 0.948264 10 0.947100 11 0.946916 7 0.952199 Name: vm_pu, dtype: float64
--- deviation from setpionts v=0.96 ---
0.0010976683852842502
# results from optimization
display(net_opt.res_bus.vm_pu[net.sgen.bus])
v_opt = net_opt.res_bus.vm_pu[net_opt.sgen.bus].values
print("--- deviation from setpionts v=0.96 ---")
display(sum((v_opt-0.96)**2)) # devition from setpoints v=0.96 p.u.
3 0.961792 4 0.960087 5 0.958920 6 0.957559 8 0.960321 9 0.959425 10 0.958299 11 0.958119 7 0.964668 Name: vm_pu, dtype: float64
--- deviation from setpionts v=0.96 ---
3.900054027594424e-05
As exected, the bus voltages after optimization are close to the defined setpoints. It is caused by the provision of reactive power of DER.
# results from power flow
display(net_org.res_sgen)
p_mw | q_mvar | |
---|---|---|
0 | 0.02 | 0.0 |
1 | 0.02 | 0.0 |
2 | 0.03 | 0.0 |
3 | 0.03 | 0.0 |
4 | 0.03 | 0.0 |
5 | 0.03 | 0.0 |
6 | 0.04 | 0.0 |
7 | 0.01 | 0.0 |
8 | 1.50 | 0.0 |
# results from optimization
display(net_opt.res_sgen)
p_mw | q_mvar | |
---|---|---|
0 | 0.02 | 0.006482 |
1 | 0.02 | 0.006497 |
2 | 0.03 | 0.009787 |
3 | 0.03 | 0.009799 |
4 | 0.03 | 0.009752 |
5 | 0.03 | 0.009765 |
6 | 0.04 | 0.013063 |
7 | 0.01 | 0.003228 |
8 | 1.50 | 0.441400 |
In some situations, users want to improve the voltage profiles with limited reactive power provisions. For this, the objective function is modified by minimizing the reactive power provision. As the formulation below shows, the two sub-objectives for maintaining voltage setpoints and saving reactive power provisions are connected through coefficients $c_{\rm v}$ and $c_{\rm q}$.
\begin{align} & \underset{\mathcal{X} = [q, ...]}{\text{minimize}} & & \sum_{i\in \mathcal{BI}} c_{\rm v}*(V_i-V_{\rm setpoint})^2 + c_{\rm q}*(q_i-0)^2 \\ \end{align}We can call the objective function with different coefficient combinations, e.g., $c_{\rm v}=0.99$ and $c_{\rm q}=0.01$. (In default, $c_{\rm v}=1.0$ and $c_{\rm q}=0.0$)
net_optlim = deepcopy(net_opt)
net_optlim["obj_factors"] = [0.99, 0.01] # set coefficients for the multi-objective function
try:
pp.runpm_vstab(net_optlim)
except:
print("[WinError 3] The system cannot find the path specified to the python39.dll")
hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.06 seconds:
As the results below show, with $c_{\rm v}=0.99$ and $c_{\rm q}=0.01$, the provided reactive powers are reduced, and the corresponding voltages are increased.
print ("--- optimized reactive power without limitation Cv = 1.0, Cq = 0. ----")
display(net_opt.res_sgen) # show optimized reactive power without limitation
print ("--- optimized reactive power with limitation Cv = 0.99, Cq = 0.01 ----")
display(net_optlim.res_sgen) # show optimized reactive power with limitation
print ("--- deviation from setpoints without limitation Cv = 1.0, Cq = 0. ----")
display(sum((v_opt - 0.96) ** 2)) # show optimized reactive power without limitation
print ("--- deviation from setpoints with limitation Cv = 0.99, Cq = 0.01 ----")
v_optlim = net_optlim.res_bus.vm_pu[net_optlim.sgen.bus].values
display(sum((v_optlim - 0.96) ** 2)) # show optimized bus voltages
--- optimized reactive power without limitation Cv = 1.0, Cq = 0. ----
p_mw | q_mvar | |
---|---|---|
0 | 0.02 | 0.006482 |
1 | 0.02 | 0.006497 |
2 | 0.03 | 0.009787 |
3 | 0.03 | 0.009799 |
4 | 0.03 | 0.009752 |
5 | 0.03 | 0.009765 |
6 | 0.04 | 0.013063 |
7 | 0.01 | 0.003228 |
8 | 1.50 | 0.441400 |
--- optimized reactive power with limitation Cv = 0.99, Cq = 0.01 ----
p_mw | q_mvar | |
---|---|---|
0 | 0.02 | 0.006559 |
1 | 0.02 | 0.006559 |
2 | 0.03 | 0.009839 |
3 | 0.03 | 0.009839 |
4 | 0.03 | 0.009839 |
5 | 0.03 | 0.009839 |
6 | 0.04 | 0.013119 |
7 | 0.01 | 0.003279 |
8 | 1.50 | 0.127064 |
--- deviation from setpoints without limitation Cv = 1.0, Cq = 0. ----
3.900054027594424e-05
--- deviation from setpoints with limitation Cv = 0.99, Cq = 0.01 ----
0.00043781176800306845
The coefficient combination is grid-dependent. For example, the picture below shows the result for one of our case studies with a realistic grid. The user can find the most suitable combination setting through several attempts.
There are two ways to perform time series optimizations. The first one is optimization in a loop, i.e., updating the grid condition for each time step and repeatedly calling optimization. The second one uses the "multi-timestep" optimization. PowerModels can solve the multi-grid optimization problem. This feature is used in PandaModels to deal with time series optimization for on specific grid by replacing the "multi-grid" with "multi-timestep" of the grid.
Next, you will see an example.
First, let's load the time series data.
import pandas as pd
# load time series data for 96 time steps
time_series = pd.read_json("cigre_timeseries_15min.json")
time_series.sort_index(inplace=True)
# show time series
display(time_series)
# set load type and sgen type in the cigre grid
net["load"].loc[:, "type"] = "residential"
net.sgen.loc[:, "type"] = "pv"
net.sgen.loc[8, "type"] = "wind"
# get rated power
load_p = net["load"].loc[:, "p_mw"].values
sgen_p = net["sgen"].loc[:7, "p_mw"].values
wind_p = net["sgen"].loc[8, "p_mw"]
# create dataframe for loads and sgens
load_ts = pd.DataFrame(index=time_series.index.tolist(), columns=net.load.index.tolist())
sgen_ts = pd.DataFrame(index=time_series.index.tolist(), columns=net.sgen.index.tolist())
for t in range(96):
load_ts.loc[t] = load_p * time_series.at[t, "residential"]
sgen_ts.loc[t][:8] = sgen_p * time_series.at[t, "pv"]
sgen_ts.loc[t][8] = wind_p * time_series.at[t, "wind"]
timestep | pv | wind | residential | |
---|---|---|---|---|
0 | 0 | 0.006143 | 0.622600 | 0.256022 |
1 | 1 | 0.005877 | 0.623585 | 0.248835 |
2 | 2 | 0.005505 | 0.625056 | 0.211973 |
3 | 3 | 0.005293 | 0.625897 | 0.201825 |
4 | 4 | 0.004921 | 0.627369 | 0.209164 |
5 | 5 | 0.004619 | 0.645138 | 0.198600 |
6 | 6 | 0.004286 | 0.685128 | 0.189050 |
7 | 7 | 0.003929 | 0.713267 | 0.186924 |
8 | 8 | 0.003674 | 0.719497 | 0.187007 |
9 | 9 | 0.003392 | 0.686113 | 0.183658 |
10 | 10 | 0.003029 | 0.650800 | 0.181048 |
11 | 11 | 0.002747 | 0.630733 | 0.185567 |
12 | 12 | 0.002571 | 0.639400 | 0.182282 |
13 | 13 | 0.002286 | 0.682600 | 0.192295 |
14 | 14 | 0.001995 | 0.728373 | 0.199360 |
15 | 15 | 0.001733 | 0.777240 | 0.182287 |
16 | 16 | 0.001491 | 0.807959 | 0.186546 |
17 | 17 | 0.001260 | 0.749338 | 0.192088 |
18 | 18 | 0.001056 | 0.699656 | 0.203122 |
19 | 19 | 0.000857 | 0.660600 | 0.228030 |
20 | 20 | 0.000698 | 0.629356 | 0.244633 |
21 | 21 | 0.000531 | 0.597195 | 0.240875 |
22 | 22 | 0.000355 | 0.564118 | 0.262490 |
23 | 23 | 0.000222 | 0.540456 | 0.331813 |
24 | 24 | 0.000132 | 0.535359 | 0.436350 |
25 | 25 | 0.000066 | 0.553113 | 0.501545 |
26 | 26 | 0.000011 | 0.567908 | 0.526120 |
27 | 27 | 0.000000 | 0.586867 | 0.537150 |
28 | 28 | 0.000061 | 0.607229 | 0.614585 |
29 | 29 | 0.000245 | 0.588314 | 0.631053 |
30 | 30 | 0.000367 | 0.575705 | 0.608927 |
31 | 31 | 0.000653 | 0.552257 | 0.607860 |
32 | 32 | 0.001034 | 0.531790 | 0.624715 |
33 | 33 | 0.001706 | 0.551367 | 0.670312 |
34 | 34 | 0.002437 | 0.572300 | 0.690940 |
35 | 35 | 0.022095 | 0.603033 | 0.640480 |
36 | 36 | 0.053810 | 0.620800 | 0.639200 |
37 | 37 | 0.123429 | 0.630467 | 0.636695 |
38 | 38 | 0.146629 | 0.637587 | 0.652735 |
39 | 39 | 0.319167 | 0.645700 | 0.656870 |
40 | 40 | 0.430714 | 0.627600 | 0.678112 |
41 | 41 | 0.531333 | 0.591889 | 0.648757 |
42 | 42 | 0.588714 | 0.565567 | 0.611575 |
43 | 43 | 0.653937 | 0.533422 | 0.629150 |
44 | 44 | 0.589571 | 0.533267 | 0.604570 |
45 | 45 | 0.569302 | 0.589044 | 0.614053 |
46 | 46 | 0.423933 | 0.784267 | 0.702965 |
47 | 47 | 0.311917 | 0.923333 | 0.726462 |
48 | 48 | 0.335000 | 1.005217 | 0.727100 |
49 | 49 | 0.442314 | 1.011813 | 0.730600 |
50 | 50 | 0.468524 | 0.979733 | 0.718670 |
51 | 51 | 0.483262 | 0.941833 | 0.694337 |
52 | 52 | 0.454295 | 0.901480 | 0.650027 |
53 | 53 | 0.438943 | 0.878333 | 0.632864 |
54 | 54 | 0.242460 | 0.838356 | 0.570937 |
55 | 55 | 0.148248 | 0.816720 | 0.564928 |
56 | 56 | 0.180262 | 0.797300 | 0.565415 |
57 | 57 | 0.139762 | 0.788044 | 0.523873 |
58 | 58 | 0.108638 | 0.788400 | 0.492570 |
59 | 59 | 0.106751 | 0.793954 | 0.493360 |
60 | 60 | 0.109315 | 0.802856 | 0.494898 |
61 | 61 | 0.092679 | 0.808650 | 0.481850 |
62 | 62 | 0.056399 | 0.812608 | 0.463050 |
63 | 63 | 0.035314 | 0.812680 | 0.449260 |
64 | 64 | 0.025476 | 0.809433 | 0.458250 |
65 | 65 | 0.023270 | 0.750000 | 0.508357 |
66 | 66 | 0.022214 | 0.707667 | 0.517530 |
67 | 67 | 0.021159 | 0.652778 | 0.574923 |
68 | 68 | 0.020365 | 0.611889 | 0.631390 |
69 | 69 | 0.019571 | 0.571000 | 0.695165 |
70 | 70 | 0.018976 | 0.540333 | 0.723720 |
71 | 71 | 0.017587 | 0.468778 | 0.776485 |
72 | 72 | 0.016833 | 0.488950 | 0.812940 |
73 | 73 | 0.016238 | 0.556644 | 0.859579 |
74 | 74 | 0.015452 | 0.661067 | 0.868860 |
75 | 75 | 0.014833 | 0.761600 | 0.902426 |
76 | 76 | 0.014184 | 0.754000 | 0.914777 |
77 | 77 | 0.013844 | 0.696133 | 0.904512 |
78 | 78 | 0.013103 | 0.582000 | 0.884404 |
79 | 79 | 0.012528 | 0.513667 | 0.829733 |
80 | 80 | 0.011836 | 0.463541 | 0.820933 |
81 | 81 | 0.011370 | 0.536370 | 0.795723 |
82 | 82 | 0.010671 | 0.647515 | 0.738732 |
83 | 83 | 0.010087 | 0.743303 | 0.700090 |
84 | 84 | 0.009736 | 0.800776 | 0.689043 |
85 | 85 | 0.009086 | 0.850387 | 0.677730 |
86 | 86 | 0.008686 | 0.873227 | 0.646210 |
87 | 87 | 0.008152 | 0.903680 | 0.613804 |
88 | 88 | 0.007345 | 0.932173 | 0.540327 |
89 | 89 | 0.006796 | 0.928251 | 0.499037 |
90 | 90 | 0.006384 | 0.925310 | 0.469209 |
91 | 91 | 0.005835 | 0.921388 | 0.435660 |
92 | 92 | 0.005423 | 0.918447 | 0.410870 |
93 | 93 | 0.004704 | 0.886837 | 0.344715 |
94 | 94 | 0.004143 | 0.858339 | 0.304026 |
95 | 95 | 0.003667 | 0.838370 | 0.286328 |
Now, we perform the time series optimizations in a loop.
########################################################################
####################### Optimization in a loop #########################
########################################################################
y_loop = []
for t in range(96):
# Update operating points for each time step
net_opt.load.p_mw = load_ts.loc[t]
net_opt.sgen.p_mw = sgen_ts.loc[t]
# If required, we also need to update the constraints, e.g., Qmax- and Qmin-value, for each time step.
net_opt.sgen.max_p_mw = sgen_ts.loc[t] # for load: max_p_mw == min_p_mw, because they are not controllable.
net_opt.sgen.min_p_mw = sgen_ts.loc[t]
net_opt.sgen.max_q_mvar = net_opt.sgen.max_q_mvar
net_opt.sgen.min_q_mvar = net_opt.sgen.min_q_mvar
# run optimization for time step t
try:
pp.runpm_vstab(net_opt)
except:
print("[WinError 3] The system cannot find the path specified to the python39.dll")
# save the mean voltage value for each time step
y_loop.append(net_opt.res_bus.vm_pu[net_opt.sgen.bus].values.mean())
hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.02 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.02 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.02 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.06 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.06 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.06 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.06 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.05 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.04 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.02 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_vstab') is finished in 0.03 seconds:
Alternatively, we can do it using multi-timestep optimization.
########################################################################
####################### multi-timestep optimization ####################
########################################################################
from pandapower.control import ConstControl
from pandapower.timeseries import DFData
# For this method, we need to add the time series data into the pandapower.controller.
net_opt.controller.drop(net.controller.index, inplace=True) # remove the existing controllers
# Add time series for load and sgen.
ConstControl(net_opt, element="load", variable="p_mw",
element_index=net.load.index.tolist(), profile_name=net.load.index.tolist(),
data_source=DFData(load_ts))
ConstControl(net_opt, element="sgen", variable="p_mw",
element_index=net.sgen.index.tolist(), profile_name=net.sgen.index.tolist(),
data_source=DFData(sgen_ts))
# If required, we also need to creat a time series for the constraints, e.g., Qmax- and Qmin-value, for each time step.
df_qmax, df_qmin = sgen_ts.copy(), sgen_ts.copy()
df_qmax[df_qmax.columns] = net_opt.sgen.max_q_mvar
df_qmin[df_qmin.columns] = net_opt.sgen.min_q_mvar
# Then create controllers for them.
ConstControl(net_opt, element="sgen", variable="max_p_mw",
element_index=net.sgen.index.tolist(), profile_name=net.sgen.index.tolist(),
data_source=DFData(sgen_ts))
ConstControl(net_opt, element="sgen", variable="min_p_mw",
element_index=net.sgen.index.tolist(), profile_name=net.sgen.index.tolist(),
data_source=DFData(sgen_ts))
ConstControl(net_opt, element="sgen", variable="max_q_mvar",
element_index=net.sgen.index.tolist(), profile_name=net.sgen.index.tolist(),
data_source=DFData(df_qmax))
ConstControl(net_opt, element="sgen", variable="min_q_mvar",
element_index=net.sgen.index.tolist(), profile_name=net.sgen.index.tolist(),
data_source=DFData(df_qmin))
# For the multi-timestep optimization, we need to call another PandaModels-function.
try:
pp.runpm_multi_vstab(net_opt, from_time_step=0, to_time_step=96)
except:
print("[WinError 3] The system cannot find the path specified to the python39.dll")
hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_multi_vstab') is finished in 5.87 seconds:
Note that, currently, the function *pp.runpm_multi_vstab()* doesn't support using of the multi-objective function for saving reactive power.
The results for each time step are saved in net.res_ts_opt. Let's get them.
y_multi = []
for t in range(96):
y_multi.append(net_opt.res_ts_opt[str(t)].res_bus.vm_pu[net_opt.sgen.bus].values.mean())
And then plot them.
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(1, 2, sharex=True, sharey=True)
ax1.plot(range(96), y_loop)
ax1.set_title("results for \n optimization in a loop")
ax2.plot(range(96), y_multi)
ax2.set_title("results for \n multi-timestep optimization")
for ax in ax1, ax2:
ax.grid(True)
ax.label_outer()
ax.set_xlabel("time step [15min]")
ax1.set_ylabel("mean voltage value for DER-buses [p.u.]")
Text(0, 0.5, 'mean voltage value for DER-buses [p.u.]')
As the plot above shows, both methods have the same results.
For a long-term time series optimization (>500 timesteps), we suggest using the first method, optimization in a loop.
The motivation for this objective is to minimize the deviations of reactive power injection at the TSO-DSO interface from a target value, e.g., $Q_{\rm setpoint}= 2 \rm MVar$. It is formulated by the following equation:
\begin{align} & \underset{\mathcal{X} = [q, ...]}{\text{minimize}} & & \sum_{i\in \mathcal{interfaces}} (Q_i-Q_{\rm setpoint})^2 \\ \end{align}We use the same grid model to introduce the example. As the picture low shows, the marked bus (or interface) is observed.
First, let's create the grid and check the reactive power injection at this interface.
import pandapower as pp
import pandapower.networks as nw
from copy import deepcopy
net = nw.create_cigre_network_mv(with_der="pv_wind")
pp.runpp(net)
net_org = deepcopy(net)
display(net.res_trafo.q_lv_mvar[0])
-5.941540318632734
Without optimization, the reactive power injection at this interface is -5.942 MVAr.
Let's configure optimization parameters and define a target value for this interface, $Q_{\rm setpoint}= -5 \rm MVAr$, i.e., we optimize the reactive power provision of DER to achieve this target.
net_opt = deepcopy(net) # copy of the grid for further comparison
net_opt.load['controllable'] = False
net_opt.sgen['controllable'] = True
# lower and upper bounds for buses
net_opt.bus["max_vm_pu"] = 1.1
net_opt.bus["min_vm_pu"] = 0.9
# lower and upper bounds for external grid
net_opt.ext_grid["max_q_mvar"] = 10000.0
net_opt.ext_grid["min_q_mvar"] = -10000.0
net_opt.ext_grid["max_p_mw"] = 10000.0
net_opt.ext_grid["min_p_mw"] = -10000.0
# lower and upper bounds for DERs
net_opt.sgen["max_p_mw"] = net_opt.sgen.p_mw.values
net_opt.sgen["min_p_mw"] = net_opt.sgen.p_mw.values
net_opt.sgen["max_q_mvar"] = net_opt.sgen.p_mw.values * 0.328
net_opt.sgen["min_q_mvar"] = -net_opt.sgen.p_mw.values * 0.328
display(net_opt.sgen)
# lower and upper bounds for lines
net_opt.trafo["max_loading_percent"] = 100.0
net_opt.line["max_loading_percent"] = 100.0
######################################################
####################### Attention ####################
######################################################
# We need to let PandaModels know which interface is of interest and what is user-defined setpoint.
# So, we need to add extra column called **"pm_param/setpoint_q"** in net.trafo and set target value.
net_opt.trafo["pm_param/setpoint_q"] = None # add extra column
net_opt.trafo["pm_param/setpoint_q"][0] = -5 # reactive power setpoint for trafo 0 is -5 MVAR
# Also, we need to tell PandaModels which side is observed.
net_opt.trafo["pm_param/side"] = None
net_opt.trafo["pm_param/side"][0] = "lv" # the observed side is the low-voltage side
name | bus | p_mw | q_mvar | sn_mva | scaling | in_service | type | current_source | controllable | max_p_mw | min_p_mw | max_q_mvar | min_q_mvar | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | PV 3 | 3 | 0.02 | 0.0 | 0.02 | 1.0 | True | PV | True | True | 0.02 | 0.02 | 0.00656 | -0.00656 |
1 | PV 4 | 4 | 0.02 | 0.0 | 0.02 | 1.0 | True | PV | True | True | 0.02 | 0.02 | 0.00656 | -0.00656 |
2 | PV 5 | 5 | 0.03 | 0.0 | 0.03 | 1.0 | True | PV | True | True | 0.03 | 0.03 | 0.00984 | -0.00984 |
3 | PV 6 | 6 | 0.03 | 0.0 | 0.03 | 1.0 | True | PV | True | True | 0.03 | 0.03 | 0.00984 | -0.00984 |
4 | PV 8 | 8 | 0.03 | 0.0 | 0.03 | 1.0 | True | PV | True | True | 0.03 | 0.03 | 0.00984 | -0.00984 |
5 | PV 9 | 9 | 0.03 | 0.0 | 0.03 | 1.0 | True | PV | True | True | 0.03 | 0.03 | 0.00984 | -0.00984 |
6 | PV 10 | 10 | 0.04 | 0.0 | 0.04 | 1.0 | True | PV | True | True | 0.04 | 0.04 | 0.01312 | -0.01312 |
7 | PV 11 | 11 | 0.01 | 0.0 | 0.01 | 1.0 | True | PV | True | True | 0.01 | 0.01 | 0.00328 | -0.00328 |
8 | WKA 7 | 7 | 1.50 | 0.0 | 1.50 | 1.0 | True | WP | True | True | 1.50 | 1.50 | 0.49200 | -0.49200 |
After parametrization, we can start the optimization using *pp.runpm_qflex()*.
try:
pp.runpm_qflex(net_opt)
except:
print("[WinError 3] The system cannot find the path specified to the python39.dll")
hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_qflex') is finished in 0.03 seconds:
Let's check the optimization results.
display(net_opt.res_trafo.q_lv_mvar[0]) # show Q-injection at the interface after optimization
display(net_opt.res_sgen) # show optimized reactiv power provision of DER
-5.356245826038442
p_mw | q_mvar | |
---|---|---|
0 | 0.02 | 0.00656 |
1 | 0.02 | 0.00656 |
2 | 0.03 | 0.00984 |
3 | 0.03 | 0.00984 |
4 | 0.03 | 0.00984 |
5 | 0.03 | 0.00984 |
6 | 0.04 | 0.01312 |
7 | 0.01 | 0.00328 |
8 | 1.50 | 0.49200 |
From the results above, we can see that the Q-injection at the interface is closer to the setpoint after the optimization but not achieved. The reason is that the Q-maximum is limited. We can modify the Q-maximum and try again.
net_opt.sgen["max_q_mvar"] = net_opt.sgen.p_mw.values * 0.628
net_opt.sgen["min_q_mvar"] = -net_opt.sgen.p_mw.values * 0.628
try:
pp.runpm_qflex(net_opt)
except:
print("[WinError 3] The system cannot find the path specified to the python39.dll")
display(net_opt.res_trafo.q_lv_mvar[0]) # show Q-injection at the interface after optimization
display(net_opt.res_sgen)
hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_qflex') is finished in 0.02 seconds:
-5.0000000238509115
p_mw | q_mvar | |
---|---|---|
0 | 0.02 | 0.001931 |
1 | 0.02 | 0.001945 |
2 | 0.03 | 0.004184 |
3 | 0.03 | 0.004198 |
4 | 0.03 | 0.004140 |
5 | 0.03 | 0.004168 |
6 | 0.04 | 0.006959 |
7 | 0.01 | 0.000483 |
8 | 1.50 | 0.881054 |
Now, the Q-injection at the interface is optimized to -5 MVAr.
Similar to the maintaining voltage setpoints, if you are interested in time series optimizations, you can do it through a loop or use *pp.runpm_multi_qflex()*.
The corresponding objective functions are formulated as follows:
We still use the cigre medium voltage grid to introduce the case studies. First, let's create the grid, add constraints, and define the observed branches.
import pandapower as pp
import pandapower.networks as nw
from copy import deepcopy
net = nw.create_cigre_network_mv(with_der="pv_wind")
net.load['controllable'] = False
net.sgen['controllable'] = True
net.sgen["max_p_mw"] = net.sgen.p_mw.values
net.sgen["min_p_mw"] = net.sgen.p_mw.values
net.sgen["max_q_mvar"] = net.sgen.p_mw.values * 0.328
net.sgen["min_q_mvar"] = -net.sgen.p_mw.values * 0.328
net.bus["max_vm_pu"] = 1.1
net.bus["min_vm_pu"] = 0.9
net.ext_grid["max_q_mvar"] = 10000.0
net.ext_grid["min_q_mvar"] = -10000.0
net.ext_grid["max_p_mw"] = 10000.0
net.ext_grid["min_p_mw"] = -10000.0
net.trafo["max_loading_percent"] = 100.0
net.line["max_loading_percent"] = 100.0
# add an additional column named "pm_param/target_branch" to let PandaModels know the observed branches.
net.line["pm_param/target_branch"] = True
net.switch.loc[:, "closed"] = True
# run power flow calculation
pp.runpp(net)
net_loss = deepcopy(net)
net_loading = deepcopy(net)
As you see, the power flow results without optimization are stored in the grid *net, and the grids net_loss* and *net_loading* are going to used for optimization. Now we call the functions *pp.runpm_ploss* and *pp.runpm_loading* to reduce the power loss and line loading percent.
pp.runpm_ploss(net_loss)
pp.runpm_loading(net_loading)
hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_ploss') is finished in 2.19 seconds: hp.pandapower.opf.make_objective - WARNING: no costs are given - overall generated power is minimized hp.pandapower.opf.run_pandamodels - INFO: Optimization ('run_pandamodels_loading') is finished in 0.07 seconds:
Let's compare the results.
print("loss without optimization:", net.res_line.pl_mw.values.sum())
print("loss with optimization:", net_loss.res_line.pl_mw.values.sum())
print("++++++++++++++++++++++++++++++++++++++++++++++")
print("loading without optimization:", net.res_line.loading_percent.values.sum())
print("loading with optimization:", net_loading.res_line.loading_percent.values.sum())
loss without optimization: 0.070711475427468 loss with optimization: 0.06241656797117738 ++++++++++++++++++++++++++++++++++++++++++++++ loading without optimization: 260.54100720602156 loading with optimization: 247.549588132589
Also, we can reduce loss or loading percent of transformers:
net_loss.line.drop(columns=["pm_param/target_branch"], inplace=True)
net_loss.trafo["pm_param/target_branch"] = True
pp.runpm_ploss(net_loss)
print("loss without optimization:", net.res_trafo.pl_mw.values.sum())
print("loss with optimization:", net_loss.res_trafo.pl_mw.values.sum())
no costs are given - overall generated power is minimized
loss without optimization: 0.06354567913302844 loss with optimization: 0.0629529981193997