Skip to content

Commit 46a3e4a

Browse files
committed
fixed typing and added support to convert switches to pandamodels.
1 parent ced307e commit 46a3e4a

File tree

2 files changed

+107
-17
lines changed

2 files changed

+107
-17
lines changed

pandapower/converter/pandamodels/to_pm.py

Lines changed: 96 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import math
99
import os
1010
import tempfile
11+
from copy import deepcopy
1112

1213
from os import remove
1314
from os.path import isfile
@@ -25,6 +26,9 @@
2526
from pandapower.pypower.idx_cost import MODEL, NCOST, COST
2627
from pandapower.pypower.idx_gen import PG, QG, GEN_BUS, VG, GEN_STATUS, QMAX, QMIN, PMIN, PMAX
2728
from pandapower.results import init_results, verify_results
29+
from pandapower.auxiliary import pandapowerNet
30+
from collections.abc import Callable
31+
from typing import Literal
2832

2933

3034
# const value in branch for tnep
@@ -51,13 +55,31 @@ def default(self, obj):
5155
return json.JSONEncoder.default(self, obj)
5256

5357

54-
def convert_pp_to_pm(net, pm_file_path=None, correct_pm_network_data=True,
55-
calculate_voltage_angles=True,
56-
ac=True, silence=True, trafo_model="t", delta=1e-8, trafo3w_losses="hv",
57-
check_connectivity=True, pp_to_pm_callback=None, pm_model="ACPPowerModel",
58-
pm_solver="ipopt",
59-
pm_mip_solver="cbc", pm_nl_solver="ipopt", opf_flow_lim="S", pm_tol=1e-8,
60-
voltage_depend_loads=False, from_time_step=None, to_time_step=None, init_vm_pu="flat", init_va_degree="flat", **kwargs):
58+
def convert_pp_to_pm(
59+
net: pandapowerNet,
60+
pm_file_path: str | None = None,
61+
correct_pm_network_data: bool | None = True,
62+
calculate_voltage_angles: bool | None = True,
63+
ac: bool | None = True,
64+
silence: bool | None = True,
65+
trafo_model: str | None = "t",
66+
delta: float | None = 1e-8,
67+
trafo3w_losses: str | None = "hv",
68+
check_connectivity: bool | None = True,
69+
pp_to_pm_callback: Callable | None = None,
70+
pm_model: str | None = "ACPPowerModel",
71+
pm_solver: str | None = "ipopt",
72+
pm_mip_solver: str | None = "cbc",
73+
pm_nl_solver: str | None = "ipopt",
74+
opf_flow_lim: str | None = "S",
75+
pm_tol: float | None = 1e-8,
76+
voltage_depend_loads: bool | None = False,
77+
from_time_step: int | None = None,
78+
to_time_step: int | None = None,
79+
init_vm_pu: Literal["flat", "results"] | float = "flat",
80+
init_va_degree: Literal["dc", "flat", "results"] | float = "flat",
81+
**kwargs
82+
):
6183
"""
6284
Converts a pandapower net to a PowerModels.jl datastructure and saves it to a json file
6385
INPUT:
@@ -106,6 +128,19 @@ def convert_pp_to_pm(net, pm_file_path=None, correct_pm_network_data=True,
106128
net.load.const_z_q_percent and net.load.const_i_q_percent are not considered,
107129
i.e. net.load.p_mw and net.load.q_mvar are considered as constant-power loads.
108130
131+
**from_time_step** (int, None) - for timeseries calculation / optimization,
132+
starting timestep (to_time_step must be also set!).
133+
134+
**to_time_step** (int, None) - for timeseries calculation / optimization,
135+
starting timestep (from_time_step must be also set!).
136+
137+
**init_vm_pu** (["flat", "results"], float) - Allows to define initialization
138+
specifically for voltage magnitudes.
139+
140+
**init_va_degree** (["dc", "flat", "results"], float) - Allows to define
141+
initialization specifically for voltage angles.
142+
143+
109144
Returns
110145
-------
111146
"""
@@ -135,8 +170,13 @@ def convert_pp_to_pm(net, pm_file_path=None, correct_pm_network_data=True,
135170
logger = logging.getLogger(__name__)
136171

137172

138-
def convert_to_pm_structure(net, opf_flow_lim="S", from_time_step=None, to_time_step=None,
139-
**kwargs):
173+
def convert_to_pm_structure(
174+
net: pandapowerNet,
175+
# opf_flow_lim: str = "S", # unused, is saved in net["_opf_options"].
176+
from_time_step: int | None = None,
177+
to_time_step: int | None = None,
178+
**kwargs
179+
):
140180
if net["_options"]["voltage_depend_loads"] and not (
141181
np.allclose(net.load.const_z_p_percent.values, 0) and
142182
np.allclose(net.load.const_i_p_percent.values, 0) and
@@ -164,7 +204,10 @@ def convert_to_pm_structure(net, opf_flow_lim="S", from_time_step=None, to_time_
164204
return net, pm, ppc, ppci
165205

166206

167-
def dump_pm_json(pm, buffer_file=None):
207+
def dump_pm_json(
208+
pm: dict,
209+
buffer_file: str | None = None
210+
):
168211
# dump pm dict to buffer_file (*.json)
169212
if buffer_file is None:
170213
# if no buffer file is provided a random file name is generated
@@ -256,11 +299,22 @@ def ppc_to_pm(net, ppci):
256299
# create power models dict. Similar to matpower case file. ne_branch is for a tnep case
257300
# "per_unit == True" means that the grid data in PowerModels are per-unit values. In this
258301
# ppc-to-pm process, the grid data schould be transformed according to baseMVA = 1.
259-
pm = {"gen": {}, "branch": {}, "bus": {}, "dcline": {}, "load": {},
260-
"storage": {},
261-
"ne_branch": {}, "switch": {},
262-
"baseMVA": ppci["baseMVA"], "source_version": "2.0.0", "shunt": {},
263-
"sourcetype": "matpower", "per_unit": True, "name": net.name}
302+
pm = {
303+
"gen": {},
304+
"branch": {},
305+
"bus": {},
306+
"dcline": {},
307+
"load": {},
308+
"storage": {},
309+
"ne_branch": {},
310+
"switch": {},
311+
"baseMVA": ppci["baseMVA"],
312+
"source_version": "2.0.0",
313+
"shunt": {},
314+
"sourcetype": "matpower",
315+
"per_unit": True,
316+
"name": net.name
317+
}
264318
baseMVA = ppci["baseMVA"]
265319
load_idx = 1
266320
shunt_idx = 1
@@ -315,8 +369,27 @@ def ppc_to_pm(net, ppci):
315369
shunt_idx += 1
316370
pm["bus"][str(idx)] = bus
317371

372+
# number of bus-bus switches in the network
373+
n_bb_switches = net._impedance_bb_switches.sum()
374+
318375
n_lines = net.line.in_service.sum()
319376
for idx, row in enumerate(ppci["branch"], start=1):
377+
378+
# the bus-bus switches are added to the end of the ppci, +1 since we use 1-indexing.
379+
if idx > (n_lines - n_bb_switches + 1):
380+
switch = {
381+
"index": idx,
382+
"f_bus": int(row[F_BUS].real) + 1,
383+
"t_bus": int(row[T_BUS].real) + 1,
384+
"status": 1,
385+
"state": 1,
386+
"thermal_rating": np.inf,
387+
"psw": 0.,
388+
"qsw": 0.,
389+
}
390+
pm["switch"][str(idx - n_lines)] = switch
391+
continue
392+
320393
branch = {}
321394
branch["index"] = idx
322395
branch["transformer"] = bool(idx > n_lines)
@@ -346,6 +419,8 @@ def ppc_to_pm(net, ppci):
346419
branch["shift"] = math.radians(row[SHIFT].real)
347420
pm["branch"][str(idx)] = branch
348421

422+
423+
349424
#### create pm["gen"]
350425
gen_idxs_pm = [str(i+1) for i in range(len(ppci["gen"]))]
351426
gen_df = pd.DataFrame(index=gen_idxs_pm)
@@ -562,7 +637,12 @@ def add_params_to_pm(net, pm):
562637
return pm
563638

564639

565-
def add_time_series_to_pm(net, pm, from_time_step, to_time_step):
640+
def add_time_series_to_pm(
641+
net: pandapowerNet,
642+
pm: dict,
643+
from_time_step: int,
644+
to_time_step: int
645+
):
566646
from pandapower.control import ConstControl
567647
if from_time_step is None or to_time_step is None:
568648
raise ValueError("please define 'from_time_step' " +

pandapower/test/opf/test_pandamodels_runpm.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from pandapower.converter.pandamodels import convert_pp_to_pm
1717
from pandapower.converter.pandamodels.to_pm import init_ne_line
1818
from pandapower.create import create_storage, create_shunt, create_pwl_cost, create_poly_cost, create_empty_network, \
19-
create_bus, create_line, create_gen, create_load, create_transformer3w_from_parameters, create_sgen, \
19+
create_bus, create_line, create_gen, create_load, create_transformer3w_from_parameters, create_sgen, create_switch,\
2020
create_transformer3w
2121
from pandapower.networks.cigre_networks import create_cigre_network_mv
2222
from pandapower.networks.power_system_test_cases import case5, case9, case14, case30, case39, case57, case118, \
@@ -768,5 +768,15 @@ def test_ac_opf_differnt_snmva():
768768
assert res[i].values.min() - res[i].values.max() < 1e-10
769769

770770

771+
@pytest.mark.skipif(not julia_installed, reason="requires julia installation")
772+
def test_switches():
773+
net = case5()
774+
775+
create_switch(net, bus=0, element=1, et='b')
776+
create_switch(net, bus=2, element=1, et='b', z_ohm=1e-3, closed=True, in_ka=1000.)
777+
create_switch(net, bus=2, element=3, et='b')
778+
779+
runpm_ac_opf(net)
780+
771781
if __name__ == '__main__':
772782
pytest.main([__file__, "-xs"])

0 commit comments

Comments
 (0)