Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 1 addition & 3 deletions docs/examples/pandapower_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Instantiate the converter. Optionally provide the `std_types` dictionary to the converter as an additional argument. \n",
"\n",
"The pandapower net can either have all parameters present in the respective component dataframes or it can have a `type` attribute which refers to `std_types`. The converter looks up the parameters from the respective component dataframes as the first priority. If they are not available here, it gets them from the `std_types`.\n",
"Instantiate the converter. The converter assumes that all the parameters (eg. `r_ohm_per_km`) are already present in the respective component dataframes. If they are not present but a `std_type` is mentioned, then it is recommended that the user refers `pandapower.add_zero_impedance_parameters()` or `pandapower.load_std_type()` to include those parameters to the pandapower net.\n",
"\n",
"Then use `load_input_data()` to load the data and convert it to power-grid-model data.\n",
"The additional information that is not used in the powerflow calculation but may be useful to link the results to the source data is stored in `extra_info`."
Expand Down
73 changes: 23 additions & 50 deletions src/power_grid_model_io/converters/pandapower_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Panda Power Converter
"""
from functools import lru_cache
from typing import Dict, List, Mapping, MutableMapping, Optional, Tuple, Union
from typing import Dict, List, MutableMapping, Optional, Tuple, Union

import numpy as np
import pandas as pd
Expand All @@ -18,7 +18,6 @@
from power_grid_model_io.functions import get_winding
from power_grid_model_io.utils.regex import NODE_REF_RE, TRAFO3_CONNECTION_RE, TRAFO_CONNECTION_RE

StdTypes = Mapping[str, Mapping[str, Mapping[str, Union[float, int, str]]]]
PandaPowerData = MutableMapping[str, pd.DataFrame]


Expand All @@ -28,18 +27,16 @@ class PandaPowerConverter(BaseConverter[PandaPowerData]):
Panda Power Converter
"""

__slots__ = ("_std_types", "pp_input_data", "pgm_input_data", "idx", "idx_lookup", "next_idx", "system_frequency")
__slots__ = ("pp_input_data", "pgm_input_data", "idx", "idx_lookup", "next_idx", "system_frequency")

def __init__(self, std_types: Optional[StdTypes] = None, system_frequency: float = 50.0):
def __init__(self, system_frequency: float = 50.0):
"""
Prepare some member variables and optionally load "std_types"
Prepare some member variables

Args:
std_types: standard type database of possible Line, Transformer and Three Winding Transformer types
system_frequency: fundamental frequency of the alternating current and voltage in the Network measured in Hz
"""
super().__init__(source=None, destination=None)
self._std_types: StdTypes = std_types if std_types is not None else {}
self.system_frequency: float = system_frequency
self.pp_input_data: PandaPowerData = {}
self.pgm_input_data: SingleDataset = {}
Expand Down Expand Up @@ -1520,8 +1517,7 @@ def get_trafo3w_switch_states(self, trafo3w: pd.DataFrame) -> pd.DataFrame:

def get_trafo_winding_types(self) -> pd.DataFrame:
"""
This function extracts Transformers' "winding_type" attribute through "vector_group" attribute or
through "std_type" attribute.
This function extracts Transformers' "winding_type" attribute through "vector_group" attribut.

Returns:
the "from" and "to" winding types of a transformer
Expand All @@ -1536,22 +1532,14 @@ def vector_group_to_winding_types(vector_group: str) -> pd.Series:
winding_to = get_winding(match.group(2)).value
return pd.Series([winding_from, winding_to])

@lru_cache
def std_type_to_winding_types(std_type: str) -> pd.Series:
return vector_group_to_winding_types(self._std_types["trafo"][std_type]["vector_group"])

trafo = self.pp_input_data["trafo"]
if "vector_group" in trafo:
trafo = trafo["vector_group"].apply(vector_group_to_winding_types)
else:
trafo = trafo["std_type"].apply(std_type_to_winding_types)
trafo = trafo["vector_group"].apply(vector_group_to_winding_types)
trafo.columns = ["winding_from", "winding_to"]
return trafo

def get_trafo3w_winding_types(self) -> pd.DataFrame:
"""
This function extracts Three Winding Transformers' "winding_type" attribute through "vector_group" attribute or
through "std_type" attribute.
This function extracts Three Winding Transformers' "winding_type" attribute through "vector_group" attribute.

Returns:
the three winding types of Three Winding Transformers
Expand All @@ -1567,15 +1555,8 @@ def vector_group_to_winding_types(vector_group: str) -> pd.Series:
winding_3 = get_winding(match.group(4)).value
return pd.Series([winding_1, winding_2, winding_3])

@lru_cache
def std_type_to_winding_types(std_type: str) -> pd.Series:
return vector_group_to_winding_types(self._std_types["trafo3w"][std_type]["vector_group"])

trafo3w = self.pp_input_data["trafo3w"]
if "vector_group" in trafo3w:
trafo3w = trafo3w["vector_group"].apply(vector_group_to_winding_types)
else:
trafo3w = trafo3w["std_type"].apply(std_type_to_winding_types)
trafo3w = trafo3w["vector_group"].apply(vector_group_to_winding_types)
trafo3w.columns = ["winding_1", "winding_2", "winding_3"]
return trafo3w

Expand All @@ -1592,29 +1573,21 @@ def _get_pp_attr(self, table: str, attribute: str, default: Optional[Union[float
"""
pp_component_data = self.pp_input_data[table]

# If the attribute exists, return the values
if attribute in pp_component_data:
return pp_component_data[attribute].values

# Try to find the std_type value for this attribute
if self._std_types is not None and table in self._std_types and "std_type" in pp_component_data:
std_types = self._std_types[table]

@lru_cache
def get_std_value(std_type_name: str):
std_type = std_types[std_type_name]
if attribute in std_type:
return std_type[attribute]
if default is not None:
return default
raise KeyError(f"No '{attribute}' value for '{table}' with std_type '{std_type_name}'.")

return pp_component_data["std_type"].apply(get_std_value).values

# Return the default value (assume that broadcasting is handled by the caller / numpy)
if default is None:
raise KeyError(f"No '{attribute}' value for '{table}'.")
return np.array([default])
# If the attribute does not exists, return the default value
# (assume that broadcasting is handled by the caller / numpy)
if attribute not in pp_component_data:
if default is None:
raise KeyError(f"No '{attribute}' value for '{table}'.")
return np.array([default])

attr_data = pp_component_data[attribute]

# If any of the attribute values are missing, and a default is supplied, fill the nans with the default value
nan_values = np.equal(attr_data, None) if attr_data.dtype is np.dtype("O") else np.isnan(attr_data)
if any(nan_values):
attr_data = attr_data.fillna(value=default, inplace=False)

return attr_data.to_numpy()

def get_id(self, pp_table: str, pp_idx: int, name: Optional[str] = None) -> int:
"""
Expand Down
107 changes: 0 additions & 107 deletions tests/unit/converters/test_pandapower_converter_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -1414,29 +1414,6 @@ def test_get_trafo_winding_types__vector_group(mock_get_winding: MagicMock):
assert mock_get_winding.call_args_list[3] == call("d")


@patch("power_grid_model_io.converters.pandapower_converter.get_winding")
def test_get_trafo_winding_types__std_types(mock_get_winding: MagicMock):
# Arrange
std_types = {"trafo": {"std_trafo_1": {"vector_group": "YNd"}, "std_trafo_2": {"vector_group": "Dyn"}}}
converter = PandaPowerConverter(std_types=std_types)
converter.pp_input_data = {
"trafo": pd.DataFrame([(1, "std_trafo_2"), (2, "std_trafo_1"), (3, "std_trafo_2")], columns=["id", "std_type"])
}
mock_get_winding.side_effect = [WindingType.delta, WindingType.wye_n, WindingType.wye_n, WindingType.delta]
expected = pd.DataFrame([(2, 1), (1, 2), (2, 1)], columns=["winding_from", "winding_to"])

# Act
actual = converter.get_trafo_winding_types()

# Assert
pd.testing.assert_frame_equal(actual, expected)
assert len(mock_get_winding.call_args_list) == 4
assert mock_get_winding.call_args_list[0] == call("D")
assert mock_get_winding.call_args_list[1] == call("yn")
assert mock_get_winding.call_args_list[2] == call("YN")
assert mock_get_winding.call_args_list[3] == call("d")


@patch("power_grid_model_io.converters.pandapower_converter.get_winding")
def test_get_trafo3w_winding_types__vector_group(mock_get_winding: MagicMock):
# Arrange
Expand Down Expand Up @@ -1475,43 +1452,6 @@ def test_get_trafo3w_winding_types__vector_group(mock_get_winding: MagicMock):
assert mock_get_winding.call_args_list[8] == call("y")


@patch("power_grid_model_io.converters.pandapower_converter.get_winding")
def test_get_trafo3w_winding_types__std_types(mock_get_winding: MagicMock):
# Arrange
std_types = {"trafo3w": {"std_trafo3w_1": {"vector_group": "Dynz"}, "std_trafo3w_2": {"vector_group": "YNdy"}}}
converter = PandaPowerConverter(std_types=std_types)
converter.pp_input_data = {
"trafo3w": pd.DataFrame(
[(1, "std_trafo3w_2"), (2, "std_trafo3w_1"), (3, "std_trafo3w_2")], columns=["id", "std_type"]
)
}
mock_get_winding.side_effect = [
WindingType.wye_n,
WindingType.delta,
WindingType.wye,
WindingType.delta,
WindingType.wye_n,
WindingType.zigzag,
WindingType.wye_n,
WindingType.delta,
WindingType.wye,
]
expected = pd.DataFrame([[1, 2, 0], [2, 1, 3], [1, 2, 0]], columns=["winding_1", "winding_2", "winding_3"])

# Act
actual = converter.get_trafo3w_winding_types()

# Assert
pd.testing.assert_frame_equal(actual, expected)
assert len(mock_get_winding.call_args_list) == 6
assert mock_get_winding.call_args_list[0] == call("YN")
assert mock_get_winding.call_args_list[1] == call("d")
assert mock_get_winding.call_args_list[2] == call("y")
assert mock_get_winding.call_args_list[3] == call("D")
assert mock_get_winding.call_args_list[4] == call("yn")
assert mock_get_winding.call_args_list[5] == call("z")


def test_get_winding_types__value_error():
# Arrange
converter = PandaPowerConverter()
Expand Down Expand Up @@ -1735,50 +1675,3 @@ def test_get_pp_attr_use_default():

# Assert
np.testing.assert_array_equal(actual, expected)


def test_get_pp_attr_from_std():
# Arrange
converter = PandaPowerConverter()
converter._std_types = {"trafo3w": {"std_trafo3w_1": {"hv_bus": 964}}}
converter.pp_input_data = {
"trafo3w": pd.DataFrame([[2, 31, 315, "std_trafo3w_1"]], columns=["index", "mv_bus", "lv_bus", "std_type"])
}

expected = np.array(964)

# Act
actual = converter._get_pp_attr("trafo3w", "hv_bus")

# Assert
np.testing.assert_array_equal(actual, expected)


def test_get_pp_attr_default_after_checking_std():
# Arrange
converter = PandaPowerConverter()
converter._std_types = {"trafo3w": {"std_trafo3w_1": {"lv_bus": 23}}}
converter.pp_input_data = {
"trafo3w": pd.DataFrame([[2, 31, 315, "std_trafo3w_1"]], columns=["index", "mv_bus", "lv_bus", "std_type"])
}

expected = np.array(964)

# Act
actual = converter._get_pp_attr("trafo3w", "hv_bus", 964)

# Assert
np.testing.assert_array_equal(actual, expected)


def test_get_pp_attr_error_after_checking_std():
# Arrange
converter = PandaPowerConverter()
converter._std_types = {"trafo3w": {"std_trafo3w_1": {"lv_bus": 23}}}
converter.pp_input_data = {
"trafo3w": pd.DataFrame([[2, 31, 315, "std_trafo3w_1"]], columns=["index", "mv_bus", "lv_bus", "std_type"])
}

# Act/Assert
with pytest.raises(KeyError):
converter._get_pp_attr("trafo3w", "hv_bus")
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def load_and_convert_pp_data() -> Tuple[SingleDataset, ExtraInfoLookup]:
Load and convert the pandapower validation network
"""
net = pp_net()
pp_converter = PandaPowerConverter(std_types=net.std_types)
pp_converter = PandaPowerConverter()
data, extra_info = pp_converter.load_input_data(net)
return data, extra_info

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def test_generate_output(): # TODO: REMOVE THIS FUNCTION
from power_grid_model_io.converters import PgmJsonConverter

net = pp_net()
converter = PandaPowerConverter(std_types=net.std_types)
converter = PandaPowerConverter()
input_data, extra_info = converter.load_input_data(net)
assert_valid_input_data(input_data=input_data)
pgm = PowerGridModel(input_data=input_data)
Expand Down