From 25ac67d74cdb3b31aefa41f6e6360028d6841279 Mon Sep 17 00:00:00 2001 From: Bram Stoeller Date: Wed, 15 Feb 2023 09:27:12 +0100 Subject: [PATCH 1/2] Remove PP std types Signed-off-by: Bram Stoeller --- .../converters/pandapower_converter.py | 73 ++++-------- .../test_pandapower_converter_input.py | 107 ------------------ .../test_pandapower_converter_input.py | 2 +- .../test_pandapower_converter_output.py | 2 +- 4 files changed, 25 insertions(+), 159 deletions(-) diff --git a/src/power_grid_model_io/converters/pandapower_converter.py b/src/power_grid_model_io/converters/pandapower_converter.py index a0d3e5de..7970169f 100644 --- a/src/power_grid_model_io/converters/pandapower_converter.py +++ b/src/power_grid_model_io/converters/pandapower_converter.py @@ -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 @@ -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] @@ -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 = {} @@ -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 @@ -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 @@ -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 @@ -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: """ diff --git a/tests/unit/converters/test_pandapower_converter_input.py b/tests/unit/converters/test_pandapower_converter_input.py index e8093bd8..8f318487 100644 --- a/tests/unit/converters/test_pandapower_converter_input.py +++ b/tests/unit/converters/test_pandapower_converter_input.py @@ -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 @@ -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() @@ -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") diff --git a/tests/validation/converters/test_pandapower_converter_input.py b/tests/validation/converters/test_pandapower_converter_input.py index 71c0fc1a..1793e3cf 100644 --- a/tests/validation/converters/test_pandapower_converter_input.py +++ b/tests/validation/converters/test_pandapower_converter_input.py @@ -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 diff --git a/tests/validation/converters/test_pandapower_converter_output.py b/tests/validation/converters/test_pandapower_converter_output.py index 966de971..9c7bc1ea 100644 --- a/tests/validation/converters/test_pandapower_converter_output.py +++ b/tests/validation/converters/test_pandapower_converter_output.py @@ -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) From feeb65074288e79ce4f89a815826149d85f9674b Mon Sep 17 00:00:00 2001 From: Nitish Bharambe Date: Wed, 15 Feb 2023 10:55:22 +0100 Subject: [PATCH 2/2] update example Signed-off-by: Nitish Bharambe --- docs/examples/pandapower_example.ipynb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/examples/pandapower_example.ipynb b/docs/examples/pandapower_example.ipynb index 73c8d593..1f62e1d0 100644 --- a/docs/examples/pandapower_example.ipynb +++ b/docs/examples/pandapower_example.ipynb @@ -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`."