Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a8e9e78
Fix Lines & Trafo loss result names, sym_load input
furqan463 Sep 25, 2025
fd06285
Fix loss_parameters according to pp_version
furqan463 Sep 26, 2025
9952520
Fix Validation Errors
furqan463 Sep 26, 2025
9b90b66
Fix Unit tests
furqan463 Sep 26, 2025
99a684c
Fix pandapower_converter for older pp version
furqan463 Sep 26, 2025
5694559
Merge branch 'PowerGridModel:main' into new_pp
furqan463 Sep 29, 2025
9d5ae50
fix data files
furqan463 Sep 29, 2025
c7e35bc
rearrange data files
furqan463 Oct 3, 2025
82c71f2
Rename Folders
furqan463 Oct 3, 2025
717ea49
Merge branch 'PowerGridModel:main' into new_pp
furqan463 Oct 3, 2025
001a603
Create README for data files
furqan463 Oct 3, 2025
18b8368
Merge branch 'new_pp' of https://github.com/furqan463/power-grid-mode…
furqan463 Oct 3, 2025
bfcb72d
Fix placing 'index' column in line and trafo
furqan463 Oct 4, 2025
46221e6
Fix mag0_percent check
furqan463 Oct 4, 2025
5e64578
add lxml as a dev dependency because it is required by pandapower
mgovers Oct 10, 2025
ede485a
Merge branch 'feature/fix-nightly' into new_pp
mgovers Oct 10, 2025
f3baccb
officially support pandapower 3.2.0 + resolve comments
mgovers Oct 10, 2025
254e5ed
Fix mag0percent check
furqan463 Oct 10, 2025
3c1100f
Apply suggestion from @mgovers
mgovers Oct 13, 2025
c6c8932
Apply suggestion from @mgovers
mgovers Oct 13, 2025
96b4313
pandapower 3.2.0 instead of 3.1.3
mgovers Oct 13, 2025
be7a362
add v3.2.0 folder
furqan463 Oct 13, 2025
f57f1db
Merge pull request #333 from furqan463/new_pp
nitbharambe Oct 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ dev = [
"ruff",
"pydantic>2", # Used in unit tests
"pandapower>2.11.1",
"lxml", # missing pandapower dependency
]
examples = [
"power-grid-model>1.9.80",
Expand Down
111 changes: 74 additions & 37 deletions src/power_grid_model_io/converters/pandapower_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@

import logging
from functools import lru_cache
from importlib.metadata import PackageNotFoundError, version
from typing import Dict, List, MutableMapping, Optional, Tuple, Type

import numpy as np
import pandas as pd
import structlog
from packaging.version import Version
from power_grid_model import (
Branch3Side,
BranchSide,
Expand All @@ -34,6 +36,20 @@

logger = structlog.get_logger(__file__)

PP_COMPATIBILITY_VERSION_3_2_0 = Version("3.2.0")
try:
PP_CONVERSION_VERSION = Version(version("pandapower"))
except PackageNotFoundError:
PP_CONVERSION_VERSION = PP_COMPATIBILITY_VERSION_3_2_0 # assume latest compatible version by default


def get_loss_params_3ph():
if PP_CONVERSION_VERSION < PP_COMPATIBILITY_VERSION_3_2_0:
loss_params = ["p_a_l_mw", "q_a_l_mvar", "p_b_l_mw", "q_b_l_mvar", "p_c_l_mw", "q_c_l_mvar"]
else:
loss_params = ["pl_a_mw", "ql_a_mvar", "pl_b_mw", "ql_b_mvar", "pl_c_mw", "ql_c_mvar"]
return loss_params


# pylint: disable=too-many-instance-attributes
class PandaPowerConverter(BaseConverter[PandaPowerData]):
Expand Down Expand Up @@ -631,34 +647,53 @@ def _create_pgm_input_sym_loads(self):
data_type=DatasetType.input, component_type=ComponentType.sym_load, shape=3 * n_loads
)

const_i_multiplier = (
self._get_pp_attr("load", "const_i_percent", expected_type="f8", default=0) * scaling * (1e-2 * 1e6)
)
const_z_multiplier = (
self._get_pp_attr("load", "const_z_percent", expected_type="f8", default=0) * scaling * (1e-2 * 1e6)
)
const_p_multiplier = (1e6 - const_i_multiplier - const_z_multiplier) * scaling
if PP_CONVERSION_VERSION < PP_COMPATIBILITY_VERSION_3_2_0:
const_i_p_multiplier = (
self._get_pp_attr("load", "const_i_percent", expected_type="f8", default=0) * scaling * (1e-2 * 1e6)
)
const_z_p_multiplier = (
self._get_pp_attr("load", "const_z_percent", expected_type="f8", default=0) * scaling * (1e-2 * 1e6)
)
const_p_multiplier = (1e6 - const_i_p_multiplier - const_z_p_multiplier) * scaling
const_q_multiplier = const_p_multiplier
const_i_q_multiplier = const_i_p_multiplier
const_z_q_multiplier = const_z_p_multiplier
else:
const_i_p_multiplier = (
self._get_pp_attr("load", "const_i_p_percent", expected_type="f8", default=0) * scaling * (1e-2 * 1e6)
)
const_z_p_multiplier = (
self._get_pp_attr("load", "const_z_p_percent", expected_type="f8", default=0) * scaling * (1e-2 * 1e6)
)
const_p_multiplier = (1e6 - const_i_p_multiplier - const_z_p_multiplier) * scaling
const_i_q_multiplier = (
self._get_pp_attr("load", "const_i_q_percent", expected_type="f8", default=0) * scaling * (1e-2 * 1e6)
)
const_z_q_multiplier = (
self._get_pp_attr("load", "const_z_q_percent", expected_type="f8", default=0) * scaling * (1e-2 * 1e6)
)
const_q_multiplier = (1e6 - const_i_q_multiplier - const_z_q_multiplier) * scaling

pgm_sym_loads["id"][:n_loads] = self._generate_ids("load", pp_loads.index, name="const_power")
pgm_sym_loads["node"][:n_loads] = self._get_pgm_ids("bus", bus)
pgm_sym_loads["status"][:n_loads] = in_service
pgm_sym_loads["type"][:n_loads] = LoadGenType.const_power
pgm_sym_loads["p_specified"][:n_loads] = const_p_multiplier * p_mw
pgm_sym_loads["q_specified"][:n_loads] = const_p_multiplier * q_mvar
pgm_sym_loads["q_specified"][:n_loads] = const_q_multiplier * q_mvar

pgm_sym_loads["id"][n_loads : 2 * n_loads] = self._generate_ids("load", pp_loads.index, name="const_impedance")
pgm_sym_loads["node"][n_loads : 2 * n_loads] = self._get_pgm_ids("bus", bus)
pgm_sym_loads["status"][n_loads : 2 * n_loads] = in_service
pgm_sym_loads["type"][n_loads : 2 * n_loads] = LoadGenType.const_impedance
pgm_sym_loads["p_specified"][n_loads : 2 * n_loads] = const_z_multiplier * p_mw
pgm_sym_loads["q_specified"][n_loads : 2 * n_loads] = const_z_multiplier * q_mvar
pgm_sym_loads["p_specified"][n_loads : 2 * n_loads] = const_z_p_multiplier * p_mw
pgm_sym_loads["q_specified"][n_loads : 2 * n_loads] = const_z_q_multiplier * q_mvar

pgm_sym_loads["id"][-n_loads:] = self._generate_ids("load", pp_loads.index, name="const_current")
pgm_sym_loads["node"][-n_loads:] = self._get_pgm_ids("bus", bus)
pgm_sym_loads["status"][-n_loads:] = in_service
pgm_sym_loads["type"][-n_loads:] = LoadGenType.const_current
pgm_sym_loads["p_specified"][-n_loads:] = const_i_multiplier * p_mw
pgm_sym_loads["q_specified"][-n_loads:] = const_i_multiplier * q_mvar
pgm_sym_loads["p_specified"][-n_loads:] = const_i_p_multiplier * p_mw
pgm_sym_loads["q_specified"][-n_loads:] = const_i_q_multiplier * q_mvar

assert ComponentType.sym_load not in self.pgm_input_data
self.pgm_input_data[ComponentType.sym_load] = pgm_sym_loads
Expand Down Expand Up @@ -1846,6 +1881,7 @@ def _pp_lines_output_3ph(self):
i_from = (pgm_output_lines["p_from"] + 1j * pgm_output_lines["q_from"]) / u_complex.iloc[from_nodes, :]
i_to = (pgm_output_lines["p_to"] + 1j * pgm_output_lines["q_to"]) / u_complex.iloc[to_nodes, :]

loss_params = get_loss_params_3ph()
pp_output_lines_3ph = pd.DataFrame(
columns=[
"p_a_from_mw",
Expand All @@ -1860,12 +1896,12 @@ def _pp_lines_output_3ph(self):
"q_b_to_mvar",
"p_c_to_mw",
"q_c_to_mvar",
"p_a_l_mw",
"q_a_l_mvar",
"p_b_l_mw",
"q_b_l_mvar",
"p_c_l_mw",
"q_c_l_mvar",
loss_params[0],
loss_params[1],
loss_params[2],
loss_params[3],
loss_params[4],
loss_params[5],
"i_a_from_ka",
"i_b_from_ka",
"i_c_from_ka",
Expand Down Expand Up @@ -1898,12 +1934,12 @@ def _pp_lines_output_3ph(self):
pp_output_lines_3ph["q_b_to_mvar"] = pgm_output_lines["q_to"][:, 1] * 1e-6
pp_output_lines_3ph["p_c_to_mw"] = pgm_output_lines["p_to"][:, 2] * 1e-6
pp_output_lines_3ph["q_c_to_mvar"] = pgm_output_lines["q_to"][:, 2] * 1e-6
pp_output_lines_3ph["p_a_l_mw"] = (pgm_output_lines["p_from"][:, 0] + pgm_output_lines["p_to"][:, 0]) * 1e-6
pp_output_lines_3ph["q_a_l_mvar"] = (pgm_output_lines["q_from"][:, 0] + pgm_output_lines["q_to"][:, 0]) * 1e-6
pp_output_lines_3ph["p_b_l_mw"] = (pgm_output_lines["p_from"][:, 1] + pgm_output_lines["p_to"][:, 1]) * 1e-6
pp_output_lines_3ph["q_b_l_mvar"] = (pgm_output_lines["q_from"][:, 1] + pgm_output_lines["q_to"][:, 1]) * 1e-6
pp_output_lines_3ph["p_c_l_mw"] = (pgm_output_lines["p_from"][:, 2] + pgm_output_lines["p_to"][:, 2]) * 1e-6
pp_output_lines_3ph["q_c_l_mvar"] = (pgm_output_lines["q_from"][:, 2] + pgm_output_lines["q_to"][:, 2]) * 1e-6
pp_output_lines_3ph[loss_params[0]] = (pgm_output_lines["p_from"][:, 0] + pgm_output_lines["p_to"][:, 0]) * 1e-6
pp_output_lines_3ph[loss_params[1]] = (pgm_output_lines["q_from"][:, 0] + pgm_output_lines["q_to"][:, 0]) * 1e-6
pp_output_lines_3ph[loss_params[2]] = (pgm_output_lines["p_from"][:, 1] + pgm_output_lines["p_to"][:, 1]) * 1e-6
pp_output_lines_3ph[loss_params[3]] = (pgm_output_lines["q_from"][:, 1] + pgm_output_lines["q_to"][:, 1]) * 1e-6
pp_output_lines_3ph[loss_params[4]] = (pgm_output_lines["p_from"][:, 2] + pgm_output_lines["p_to"][:, 2]) * 1e-6
pp_output_lines_3ph[loss_params[5]] = (pgm_output_lines["q_from"][:, 2] + pgm_output_lines["q_to"][:, 2]) * 1e-6
pp_output_lines_3ph["i_a_from_ka"] = pgm_output_lines["i_from"][:, 0] * 1e-3
pp_output_lines_3ph["i_b_from_ka"] = pgm_output_lines["i_from"][:, 1] * 1e-3
pp_output_lines_3ph["i_c_from_ka"] = pgm_output_lines["i_from"][:, 2] * 1e-3
Expand Down Expand Up @@ -2026,6 +2062,7 @@ def _pp_trafos_output_3ph(self): # pylint: disable=too-many-statements

loading = np.maximum(np.maximum(loading_a_percent, loading_b_percent), loading_c_percent)

loss_params = get_loss_params_3ph()
pp_output_trafos_3ph = pd.DataFrame(
columns=[
"p_a_hv_mw",
Expand All @@ -2040,12 +2077,12 @@ def _pp_trafos_output_3ph(self): # pylint: disable=too-many-statements
"q_b_lv_mvar",
"p_c_lv_mw",
"q_c_lv_mvar",
"p_a_l_mw",
"q_a_l_mvar",
"p_b_l_mw",
"q_b_l_mvar",
"p_c_l_mw",
"q_c_l_mvar",
loss_params[0],
loss_params[1],
loss_params[2],
loss_params[3],
loss_params[4],
loss_params[5],
"i_a_hv_ka",
"i_a_lv_ka",
"i_b_hv_ka",
Expand All @@ -2071,22 +2108,22 @@ def _pp_trafos_output_3ph(self): # pylint: disable=too-many-statements
pp_output_trafos_3ph["q_b_lv_mvar"] = pgm_output_transformers["q_to"][:, 1] * 1e-6
pp_output_trafos_3ph["p_c_lv_mw"] = pgm_output_transformers["p_to"][:, 2] * 1e-6
pp_output_trafos_3ph["q_c_lv_mvar"] = pgm_output_transformers["q_to"][:, 2] * 1e-6
pp_output_trafos_3ph["p_a_l_mw"] = (
pp_output_trafos_3ph[loss_params[0]] = (
pgm_output_transformers["p_from"][:, 0] + pgm_output_transformers["p_to"][:, 0]
) * 1e-6
pp_output_trafos_3ph["q_a_l_mvar"] = (
pp_output_trafos_3ph[loss_params[1]] = (
pgm_output_transformers["q_from"][:, 0] + pgm_output_transformers["q_to"][:, 0]
) * 1e-6
pp_output_trafos_3ph["p_b_l_mw"] = (
pp_output_trafos_3ph[loss_params[2]] = (
pgm_output_transformers["p_from"][:, 1] + pgm_output_transformers["p_to"][:, 1]
) * 1e-6
pp_output_trafos_3ph["q_b_l_mvar"] = (
pp_output_trafos_3ph[loss_params[3]] = (
pgm_output_transformers["q_from"][:, 1] + pgm_output_transformers["q_to"][:, 1]
) * 1e-6
pp_output_trafos_3ph["p_c_l_mw"] = (
pp_output_trafos_3ph[loss_params[4]] = (
pgm_output_transformers["p_from"][:, 2] + pgm_output_transformers["p_to"][:, 2]
) * 1e-6
pp_output_trafos_3ph["q_c_l_mvar"] = (
pp_output_trafos_3ph[loss_params[5]] = (
pgm_output_transformers["q_from"][:, 2] + pgm_output_transformers["q_to"][:, 2]
) * 1e-6
pp_output_trafos_3ph["i_a_hv_ka"] = pgm_output_transformers["i_from"][:, 0] * 1e-3
Expand Down Expand Up @@ -2396,7 +2433,7 @@ def get_switch_states(self, pp_table: str) -> pd.DataFrame:
else:
raise KeyError(f"Can't get switch states for {pp_table}")

component = self.pp_input_data[pp_table]
component = self.pp_input_data[pp_table].copy()
component["index"] = component.index

# Select the appropriate switches and columns
Expand Down
18 changes: 18 additions & 0 deletions tests/data/pandapower/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!--
SPDX-FileCopyrightText: Contributors to the Power Grid Model project <[email protected]>

SPDX-License-Identifier: MPL-2.0
-->
# Pandapower release 3.2.0

`pandapower` made following breaking changes to its release 3.2.0:

1. `Load` attributes have been changed:
* `const_i_percent` replaced with `const_i_p_percent` and `const_i_q_percent`
* `const_z_percent` replaced with `const_z_p_percent` and `const_z_q_percent`
2. loss attributes in 3ph load flow for `res_line_3ph` and `res_trafo_3ph` have been changed:
* `p_a_l_mw` changed to `pl_a_mw` and same for other phases
* `q_a_l_mvar` changed to `ql_a_mvar` and same for other phases

In order to maintain backward compatibility, data files compatible with both `pandapower` versions have been moved
to separate folders **v3.1.2** and **v3.2.0**.
Loading