From 1fdb9eaef8fd7378a038c4ed535a617ea4185739 Mon Sep 17 00:00:00 2001 From: furqan463 Date: Sat, 13 Sep 2025 22:05:16 +0500 Subject: [PATCH 1/7] Add 3ph power validation test Signed-off-by: furqan463 --- .../test_pandapower_converter_output.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/tests/validation/converters/test_pandapower_converter_output.py b/tests/validation/converters/test_pandapower_converter_output.py index 6916b133..ab3e7d0c 100644 --- a/tests/validation/converters/test_pandapower_converter_output.py +++ b/tests/validation/converters/test_pandapower_converter_output.py @@ -278,3 +278,81 @@ def test_attributes_3ph(output_data_3ph: Tuple[PandaPowerData, PandaPowerData], # Assert pd.testing.assert_series_equal(actual_values, expected_values, atol=5e-4, rtol=1e-4) + + +def _get_total_powers_3ph(net): + """ + Calculates total complex power for sources, loads and losses + Input: Pandapower Network + Output: [s_ext_grid, s_load, s_loss] + """ + from numpy import complex128 + + s_ext_grid = ( + net.res_ext_grid_3ph.loc[:, ["p_a_mw", "p_b_mw", "p_c_mw"]].sum().sum() + + 1j * net.res_ext_grid_3ph.loc[:, ["q_a_mvar", "q_b_mvar", "q_c_mvar"]].sum().sum() + ) + + if "res_asymmetric_load_3ph" in net: + s_load_asym = ( + net.res_asymmetric_load_3ph.loc[:, ["p_a_mw", "p_b_mw", "p_c_mw"]].sum().sum() + + 1j * net.res_asymmetric_load_3ph.loc[:, ["q_a_mvar", "q_b_mvar", "q_c_mvar"]].sum().sum() + ) + else: + s_load_asym = complex128() + + if "res_load_3ph" in net: + s_load_sym = net.res_load_3ph.loc[:, "p_mw"].sum() + 1j * net.res_load_3ph.loc[:, "q_mvar"].sum() + else: + s_load_sym = complex128() + + s_load = s_load_sym + s_load_asym + + if "res_line_3ph" in net: + s_loss_line = ( + net.res_line_3ph.loc[:, ["p_a_l_mw", "p_b_l_mw", "p_c_l_mw"]].sum().sum() + + 1j * net.res_line_3ph.loc[:, ["q_a_l_mvar", "q_b_l_mvar", "q_c_l_mvar"]].sum().sum() + ) + else: + s_loss_line = complex128() + + if "res_line_3ph" in net: + s_loss_trafo = ( + net.res_trafo_3ph.loc[:, ["p_a_l_mw", "p_b_l_mw", "p_c_l_mw"]].sum().sum() + + 1j * net.res_trafo_3ph.loc[:, ["q_a_l_mvar", "q_b_l_mvar", "q_c_l_mvar"]].sum().sum() + ) + else: + s_loss_trafo = complex128() + + s_loss = s_loss_line + s_loss_trafo + return [s_ext_grid, s_load, s_loss] + + +def test_output_data_3ph__powers(): + def run_pf_asym_with_pgm(net): + from pandapower.results import reset_results + from power_grid_model import PowerGridModel + + reset_results(net, "pf_3ph") + pgm_converter = PandaPowerConverter() + input_data, _ = pgm_converter.load_input_data(net, make_extra_info=False) + pgm = PowerGridModel(input_data) + output_data = pgm.calculate_power_flow(symmetric=False) + output_tables = pgm_converter.convert(output_data) + for table in output_tables.keys(): + net[table] = output_tables[table] + + from numpy import isclose + from pandapower.networks import ieee_european_lv_asymmetric + + net = ieee_european_lv_asymmetric() + run_pf_asym_with_pgm(net) + s_ext_grid, s_load, s_loss = _get_total_powers_3ph(net) + assert isclose(s_ext_grid, (s_load + s_loss)) + + pp.create_motor(net, 100, 0.1, 0.9) + pp.create_ward(net, 200, 0.1, 0.05, 0.1, 0.05) + pp.create_shunt_as_capacitor(net, 150, 0.09, 0) + run_pf_asym_with_pgm(net) + s_ext_grid, s_load, s_loss = _get_total_powers_3ph(net) + assert isclose(s_ext_grid, (s_load + s_loss)) From 3c4525a150c6f9cf93db5076f3462a557c139790 Mon Sep 17 00:00:00 2001 From: furqan463 Date: Sat, 13 Sep 2025 22:47:35 +0500 Subject: [PATCH 2/7] Add sym power validation test Signed-off-by: furqan463 --- .../test_pandapower_converter_output.py | 105 +++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/tests/validation/converters/test_pandapower_converter_output.py b/tests/validation/converters/test_pandapower_converter_output.py index ab3e7d0c..aba997b6 100644 --- a/tests/validation/converters/test_pandapower_converter_output.py +++ b/tests/validation/converters/test_pandapower_converter_output.py @@ -306,7 +306,27 @@ def _get_total_powers_3ph(net): else: s_load_sym = complex128() - s_load = s_load_sym + s_load_asym + if "res_motor_3ph" in net: + s_motor = net.res_motor_3ph.loc[:, "p_mw"].sum() + 1j * net.res_motor_3ph.loc[:, "q_mvar"].sum() + else: + s_motor = complex128() + + if "res_ward_3ph" in net: + s_ward = ( + net.res_ward_3ph.loc[:, ["p_a_mw", "p_b_mw", "p_c_mw"]].sum().sum() + + 1j * net.res_ward_3ph.loc[:, ["q_a_mvar", "q_b_mvar", "q_c_mvar"]].sum().sum() + ) + else: + s_ward = complex128() + + # TODO: enable res_shunt_3ph when implemented, right now the table exists but columns not defined. + # if "res_shunt_3ph" in net: + # print(net.res_shunt_3ph) + # s_shunt = net.res_shunt_3ph.loc[:, "p_mw"].sum() + 1j * net.res_shunt_3ph.loc[:, "q_mvar"].sum() + # else: + s_shunt = complex128() + + s_load = s_load_sym + s_load_asym + s_motor + s_ward + s_shunt if "res_line_3ph" in net: s_loss_line = ( @@ -316,7 +336,7 @@ def _get_total_powers_3ph(net): else: s_loss_line = complex128() - if "res_line_3ph" in net: + if "res_trafo_3ph" in net: s_loss_trafo = ( net.res_trafo_3ph.loc[:, ["p_a_l_mw", "p_b_l_mw", "p_c_l_mw"]].sum().sum() + 1j * net.res_trafo_3ph.loc[:, ["q_a_l_mvar", "q_b_l_mvar", "q_c_l_mvar"]].sum().sum() @@ -356,3 +376,84 @@ def run_pf_asym_with_pgm(net): run_pf_asym_with_pgm(net) s_ext_grid, s_load, s_loss = _get_total_powers_3ph(net) assert isclose(s_ext_grid, (s_load + s_loss)) + + +def _get_total_powers(net): + """ + Calculates total complex power for sources, loads and losses + Input: Pandapower Network + Output: [s_ext_grid, s_load, s_loss] + """ + from numpy import complex128 + + s_ext_grid = net.res_ext_grid.loc[:, "p_mw"].sum() + 1j * net.res_ext_grid.loc[:, "q_mvar"].sum() + + if "res_asymmetric_load" in net: + s_load_asym = net.res_asymmetric_load.loc[:, "p_mw"].sum() + 1j * net.res_asymmetric_load.loc[:, "q_mvar"].sum() + else: + s_load_asym = complex128() + + if "res_load" in net: + s_load_sym = net.res_load.loc[:, "p_mw"].sum() + 1j * net.res_load.loc[:, "q_mvar"].sum() + else: + s_load_sym = complex128() + + if "res_motor" in net: + s_motor = net.res_motor.loc[:, "p_mw"].sum() + 1j * net.res_motor.loc[:, "q_mvar"].sum() + else: + s_motor = complex128() + + if "res_ward" in net: + s_ward = net.res_ward.loc[:, "p_mw"].sum() + 1j * net.res_ward.loc[:, "q_mvar"].sum() + else: + s_ward = complex128() + + if "res_shunt" in net: + s_shunt = net.res_shunt.loc[:, "p_mw"].sum() + 1j * net.res_shunt.loc[:, "q_mvar"].sum() + else: + s_shunt = complex128() + + s_load = s_load_sym + s_load_asym + s_motor + s_ward + s_shunt + + if "res_line" in net: + s_loss_line = net.res_line.loc[:, "pl_mw"].sum() + 1j * net.res_line.loc[:, "ql_mvar"].sum() + else: + s_loss_line = complex128() + + if "res_trafo" in net: + s_loss_trafo = net.res_trafo.loc[:, "pl_mw"].sum() + 1j * net.res_trafo.loc[:, "ql_mvar"].sum() + else: + s_loss_trafo = complex128() + + s_loss = s_loss_line + s_loss_trafo + return [s_ext_grid, s_load, s_loss] + + +def test_output_data__powers(): + def run_pf_sym_with_pgm(net): + from pandapower.results import reset_results + from power_grid_model import PowerGridModel + + reset_results(net, "pf") + pgm_converter = PandaPowerConverter() + input_data, _ = pgm_converter.load_input_data(net, make_extra_info=False) + pgm = PowerGridModel(input_data) + output_data = pgm.calculate_power_flow(symmetric=True) + output_tables = pgm_converter.convert(output_data) + for table in output_tables.keys(): + net[table] = output_tables[table] + + from numpy import isclose + from pandapower.networks import ieee_european_lv_asymmetric + + net = ieee_european_lv_asymmetric() + run_pf_sym_with_pgm(net) + s_ext_grid, s_load, s_loss = _get_total_powers(net) + assert isclose(s_ext_grid, (s_load + s_loss)) + + pp.create_motor(net, 100, 0.1, 0.9) + pp.create_ward(net, 200, 0.1, 0.05, 0.1, 0.05) + pp.create_shunt_as_capacitor(net, 150, 0.09, 0) + run_pf_sym_with_pgm(net) + s_ext_grid, s_load, s_loss = _get_total_powers(net) + assert isclose(s_ext_grid, (s_load + s_loss)) From 02f7fa0142d44054a9c8b2c1e6fd1d39c532585a Mon Sep 17 00:00:00 2001 From: furqan463 Date: Sat, 13 Sep 2025 23:58:18 +0500 Subject: [PATCH 3/7] Add res_output_3ph for shunt, motor, ward Signed-off-by: furqan463 --- .../converters/pandapower_converter.py | 67 ++++++++++++++++++- .../test_pandapower_converter_output.py | 2 +- .../test_pandapower_converter_output.py | 17 ++--- 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/power_grid_model_io/converters/pandapower_converter.py b/src/power_grid_model_io/converters/pandapower_converter.py index a4c1e7d0..1cf18994 100644 --- a/src/power_grid_model_io/converters/pandapower_converter.py +++ b/src/power_grid_model_io/converters/pandapower_converter.py @@ -360,15 +360,18 @@ def _create_output_data_3ph(self): Furthermore, creates a global node lookup table, which stores nodes' voltage magnitude per unit and the voltage angle in degrees """ - # TODO create output_data_3ph for remaining components + # TODO create output_data_3ph for trafos3w # Although Pandapower itself did not implmenet res_shunt_3ph # Since results are avaiable in PGM output, these should be converted. self._pp_buses_output_3ph() self._pp_lines_output_3ph() self._pp_ext_grids_output_3ph() self._pp_loads_output_3ph() + self._pp_shunts_output_3ph() self._pp_trafos_output_3ph() self._pp_sgens_output_3ph() + self._pp_ward_output_3ph() + self._pp_motor_output_3ph() self._pp_asym_gens_output_3ph() self._pp_asym_loads_output_3ph() @@ -2154,6 +2157,68 @@ def _pp_loads_output_3ph(self): pp_component_name="load", load_id_names=load_id_names ) + def _pp_ward_output_3ph(self): + # TODO: Create Unit tests + load_id_names = ["ward_const_power_load", "ward_const_impedance_load"] + if ( + ComponentType.sym_load not in self.pgm_output_data + or self.pgm_output_data[ComponentType.sym_load].size == 0 + or ("ward", load_id_names[0]) not in self.idx + ): + return + + # TODO Find a better way for mapping vm_pu from bus + # accumulated_loads["vm_pu"] = np.nan + # Store the results, while assuring that we are not overwriting any data + assert "res_ward_3ph" not in self.pp_output_data + self.pp_output_data["res_ward_3ph"] = self._pp_load_result_accumulate( + pp_component_name="ward", load_id_names=load_id_names + ) + + def _pp_motor_output_3ph(self): + # TODO: Create unit tests + load_id_names = ["motor_load"] + + assert "res_motor_3ph" not in self.pp_output_data + + if ( + ComponentType.sym_load not in self.pgm_output_data + or self.pgm_output_data[ComponentType.sym_load].size == 0 + or ("motor", load_id_names[0]) not in self.idx + ): + return + + # Store the results, while assuring that we are not overwriting any data + assert "res_motor" not in self.pp_output_data + self.pp_output_data["res_motor_3ph"] = self._pp_load_result_accumulate( + pp_component_name="motor", load_id_names=load_id_names + ) + + def _pp_shunts_output_3ph(self): + """ + This function converts a power-grid-model Shunt output array to a Shunt Dataframe of PandaPower. + + Returns: + a PandaPower Dataframe for the Shunt component + """ + # TODO: create unit tests for the function + assert "res_shunt_3ph" not in self.pp_output_data + + if ComponentType.shunt not in self.pgm_output_data or self.pgm_output_data[ComponentType.shunt].size == 0: + return + + pgm_output_shunts = self.pgm_output_data[ComponentType.shunt] + + pp_output_shunts = pd.DataFrame( + columns=["p_mw", "q_mvar", "vm_pu"], + index=self._get_pp_ids("shunt", pgm_output_shunts["id"]), + ) + pp_output_shunts["p_mw"] = pgm_output_shunts["p"].sum() * 1e-6 + pp_output_shunts["q_mvar"] = pgm_output_shunts["q"].sum() * 1e-6 + # TODO Find a better way for mapping vm_pu from bus + # pp_output_shunts["vm_pu"] = np.nan + self.pp_output_data["res_shunt_3ph"] = pp_output_shunts + def _pp_asym_loads_output_3ph(self): """ This function converts a power-grid-model Asymmetrical Load output array to an Asymmetrical Load Dataframe of diff --git a/tests/unit/converters/test_pandapower_converter_output.py b/tests/unit/converters/test_pandapower_converter_output.py index 85d616f8..2950c8c6 100644 --- a/tests/unit/converters/test_pandapower_converter_output.py +++ b/tests/unit/converters/test_pandapower_converter_output.py @@ -54,7 +54,7 @@ def test_create_output_data_3ph(): PandaPowerConverter._create_output_data_3ph(self=converter) # type: ignore # Assert - assert len(converter.method_calls) == 8 + assert len(converter.method_calls) == 11 converter._pp_buses_output_3ph.assert_called_once_with() converter._pp_lines_output_3ph.assert_called_once_with() converter._pp_ext_grids_output_3ph.assert_called_once_with() diff --git a/tests/validation/converters/test_pandapower_converter_output.py b/tests/validation/converters/test_pandapower_converter_output.py index aba997b6..e59439a5 100644 --- a/tests/validation/converters/test_pandapower_converter_output.py +++ b/tests/validation/converters/test_pandapower_converter_output.py @@ -311,20 +311,15 @@ def _get_total_powers_3ph(net): else: s_motor = complex128() - if "res_ward_3ph" in net: - s_ward = ( - net.res_ward_3ph.loc[:, ["p_a_mw", "p_b_mw", "p_c_mw"]].sum().sum() - + 1j * net.res_ward_3ph.loc[:, ["q_a_mvar", "q_b_mvar", "q_c_mvar"]].sum().sum() - ) + if ("res_ward_3ph" in net) and (not net.res_ward_3ph.empty): + s_ward = net.res_ward_3ph.loc[:, "p_mw"].sum() + 1j * net.res_ward_3ph.loc[:, "q_mvar"].sum() else: s_ward = complex128() - # TODO: enable res_shunt_3ph when implemented, right now the table exists but columns not defined. - # if "res_shunt_3ph" in net: - # print(net.res_shunt_3ph) - # s_shunt = net.res_shunt_3ph.loc[:, "p_mw"].sum() + 1j * net.res_shunt_3ph.loc[:, "q_mvar"].sum() - # else: - s_shunt = complex128() + if ("res_shunt_3ph" in net) and (not net.res_shunt_3ph.empty): + s_shunt = net.res_shunt_3ph.loc[:, "p_mw"].sum() + 1j * net.res_shunt_3ph.loc[:, "q_mvar"].sum() + else: + s_shunt = complex128() s_load = s_load_sym + s_load_asym + s_motor + s_ward + s_shunt From 05f6a324f4ac1a4b1f2389d6c4d16690e6b9894d Mon Sep 17 00:00:00 2001 From: furqan463 Date: Mon, 15 Sep 2025 20:33:02 +0500 Subject: [PATCH 4/7] fix imports Signed-off-by: furqan463 --- .../test_pandapower_converter_output.py | 60 ++++++++----------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/tests/validation/converters/test_pandapower_converter_output.py b/tests/validation/converters/test_pandapower_converter_output.py index e59439a5..11c21713 100644 --- a/tests/validation/converters/test_pandapower_converter_output.py +++ b/tests/validation/converters/test_pandapower_converter_output.py @@ -8,8 +8,12 @@ from pathlib import Path from typing import Tuple +import numpy as np +import pandapower.networks as pp_networks import pandas as pd import pytest +from pandapower.results import reset_results +from power_grid_model import PowerGridModel from power_grid_model.validation import assert_valid_input_data from power_grid_model_io.converters import PandaPowerConverter @@ -286,8 +290,6 @@ def _get_total_powers_3ph(net): Input: Pandapower Network Output: [s_ext_grid, s_load, s_loss] """ - from numpy import complex128 - s_ext_grid = ( net.res_ext_grid_3ph.loc[:, ["p_a_mw", "p_b_mw", "p_c_mw"]].sum().sum() + 1j * net.res_ext_grid_3ph.loc[:, ["q_a_mvar", "q_b_mvar", "q_c_mvar"]].sum().sum() @@ -299,27 +301,27 @@ def _get_total_powers_3ph(net): + 1j * net.res_asymmetric_load_3ph.loc[:, ["q_a_mvar", "q_b_mvar", "q_c_mvar"]].sum().sum() ) else: - s_load_asym = complex128() + s_load_asym = np.complex128() if "res_load_3ph" in net: s_load_sym = net.res_load_3ph.loc[:, "p_mw"].sum() + 1j * net.res_load_3ph.loc[:, "q_mvar"].sum() else: - s_load_sym = complex128() + s_load_sym = np.complex128() if "res_motor_3ph" in net: s_motor = net.res_motor_3ph.loc[:, "p_mw"].sum() + 1j * net.res_motor_3ph.loc[:, "q_mvar"].sum() else: - s_motor = complex128() + s_motor = np.complex128() if ("res_ward_3ph" in net) and (not net.res_ward_3ph.empty): s_ward = net.res_ward_3ph.loc[:, "p_mw"].sum() + 1j * net.res_ward_3ph.loc[:, "q_mvar"].sum() else: - s_ward = complex128() + s_ward = np.complex128() if ("res_shunt_3ph" in net) and (not net.res_shunt_3ph.empty): s_shunt = net.res_shunt_3ph.loc[:, "p_mw"].sum() + 1j * net.res_shunt_3ph.loc[:, "q_mvar"].sum() else: - s_shunt = complex128() + s_shunt = np.complex128() s_load = s_load_sym + s_load_asym + s_motor + s_ward + s_shunt @@ -329,7 +331,7 @@ def _get_total_powers_3ph(net): + 1j * net.res_line_3ph.loc[:, ["q_a_l_mvar", "q_b_l_mvar", "q_c_l_mvar"]].sum().sum() ) else: - s_loss_line = complex128() + s_loss_line = np.complex128() if "res_trafo_3ph" in net: s_loss_trafo = ( @@ -337,7 +339,7 @@ def _get_total_powers_3ph(net): + 1j * net.res_trafo_3ph.loc[:, ["q_a_l_mvar", "q_b_l_mvar", "q_c_l_mvar"]].sum().sum() ) else: - s_loss_trafo = complex128() + s_loss_trafo = np.complex128() s_loss = s_loss_line + s_loss_trafo return [s_ext_grid, s_load, s_loss] @@ -345,9 +347,6 @@ def _get_total_powers_3ph(net): def test_output_data_3ph__powers(): def run_pf_asym_with_pgm(net): - from pandapower.results import reset_results - from power_grid_model import PowerGridModel - reset_results(net, "pf_3ph") pgm_converter = PandaPowerConverter() input_data, _ = pgm_converter.load_input_data(net, make_extra_info=False) @@ -357,20 +356,17 @@ def run_pf_asym_with_pgm(net): for table in output_tables.keys(): net[table] = output_tables[table] - from numpy import isclose - from pandapower.networks import ieee_european_lv_asymmetric - - net = ieee_european_lv_asymmetric() + net = pp_networks.ieee_european_lv_asymmetric() run_pf_asym_with_pgm(net) s_ext_grid, s_load, s_loss = _get_total_powers_3ph(net) - assert isclose(s_ext_grid, (s_load + s_loss)) + assert np.isclose(s_ext_grid, (s_load + s_loss)) pp.create_motor(net, 100, 0.1, 0.9) pp.create_ward(net, 200, 0.1, 0.05, 0.1, 0.05) pp.create_shunt_as_capacitor(net, 150, 0.09, 0) run_pf_asym_with_pgm(net) s_ext_grid, s_load, s_loss = _get_total_powers_3ph(net) - assert isclose(s_ext_grid, (s_load + s_loss)) + assert np.isclose(s_ext_grid, (s_load + s_loss)) def _get_total_powers(net): @@ -379,46 +375,44 @@ def _get_total_powers(net): Input: Pandapower Network Output: [s_ext_grid, s_load, s_loss] """ - from numpy import complex128 - s_ext_grid = net.res_ext_grid.loc[:, "p_mw"].sum() + 1j * net.res_ext_grid.loc[:, "q_mvar"].sum() if "res_asymmetric_load" in net: s_load_asym = net.res_asymmetric_load.loc[:, "p_mw"].sum() + 1j * net.res_asymmetric_load.loc[:, "q_mvar"].sum() else: - s_load_asym = complex128() + s_load_asym = np.complex128() if "res_load" in net: s_load_sym = net.res_load.loc[:, "p_mw"].sum() + 1j * net.res_load.loc[:, "q_mvar"].sum() else: - s_load_sym = complex128() + s_load_sym = np.complex128() if "res_motor" in net: s_motor = net.res_motor.loc[:, "p_mw"].sum() + 1j * net.res_motor.loc[:, "q_mvar"].sum() else: - s_motor = complex128() + s_motor = np.complex128() if "res_ward" in net: s_ward = net.res_ward.loc[:, "p_mw"].sum() + 1j * net.res_ward.loc[:, "q_mvar"].sum() else: - s_ward = complex128() + s_ward = np.complex128() if "res_shunt" in net: s_shunt = net.res_shunt.loc[:, "p_mw"].sum() + 1j * net.res_shunt.loc[:, "q_mvar"].sum() else: - s_shunt = complex128() + s_shunt = np.complex128() s_load = s_load_sym + s_load_asym + s_motor + s_ward + s_shunt if "res_line" in net: s_loss_line = net.res_line.loc[:, "pl_mw"].sum() + 1j * net.res_line.loc[:, "ql_mvar"].sum() else: - s_loss_line = complex128() + s_loss_line = np.complex128() if "res_trafo" in net: s_loss_trafo = net.res_trafo.loc[:, "pl_mw"].sum() + 1j * net.res_trafo.loc[:, "ql_mvar"].sum() else: - s_loss_trafo = complex128() + s_loss_trafo = np.complex128() s_loss = s_loss_line + s_loss_trafo return [s_ext_grid, s_load, s_loss] @@ -426,9 +420,6 @@ def _get_total_powers(net): def test_output_data__powers(): def run_pf_sym_with_pgm(net): - from pandapower.results import reset_results - from power_grid_model import PowerGridModel - reset_results(net, "pf") pgm_converter = PandaPowerConverter() input_data, _ = pgm_converter.load_input_data(net, make_extra_info=False) @@ -438,17 +429,14 @@ def run_pf_sym_with_pgm(net): for table in output_tables.keys(): net[table] = output_tables[table] - from numpy import isclose - from pandapower.networks import ieee_european_lv_asymmetric - - net = ieee_european_lv_asymmetric() + net = pp_networks.ieee_european_lv_asymmetric() run_pf_sym_with_pgm(net) s_ext_grid, s_load, s_loss = _get_total_powers(net) - assert isclose(s_ext_grid, (s_load + s_loss)) + assert np.isclose(s_ext_grid, (s_load + s_loss)) pp.create_motor(net, 100, 0.1, 0.9) pp.create_ward(net, 200, 0.1, 0.05, 0.1, 0.05) pp.create_shunt_as_capacitor(net, 150, 0.09, 0) run_pf_sym_with_pgm(net) s_ext_grid, s_load, s_loss = _get_total_powers(net) - assert isclose(s_ext_grid, (s_load + s_loss)) + assert np.isclose(s_ext_grid, (s_load + s_loss)) From 1629de8f1e5a4dada0ba0f915ea7cae778cbeb9e Mon Sep 17 00:00:00 2001 From: furqan463 Date: Mon, 15 Sep 2025 21:50:03 +0500 Subject: [PATCH 5/7] Get rid of repetitive code, make one util function for all elements being converted as sym_load Signed-off-by: furqan463 --- .../converters/pandapower_converter.py | 139 ++++-------------- .../test_pandapower_converter_output.py | 84 ++++++++--- .../test_pandapower_converter_output.py | 2 + 3 files changed, 95 insertions(+), 130 deletions(-) diff --git a/src/power_grid_model_io/converters/pandapower_converter.py b/src/power_grid_model_io/converters/pandapower_converter.py index dc36e6d8..a2ec74ac 100644 --- a/src/power_grid_model_io/converters/pandapower_converter.py +++ b/src/power_grid_model_io/converters/pandapower_converter.py @@ -342,13 +342,13 @@ def _create_output_data(self): self._pp_buses_output() self._pp_lines_output() self._pp_ext_grids_output() - self._pp_loads_output() + self._pp_load_elements_output(element="load", symmetric=True) + self._pp_load_elements_output(element="ward", symmetric=True) + self._pp_load_elements_output(element="motor", symmetric=True) self._pp_shunts_output() self._pp_trafos_output() self._pp_sgens_output() self._pp_trafos3w_output() - self._pp_ward_output() - self._pp_motor_output() self._pp_asym_gens_output() self._pp_asym_loads_output() # Switches derive results from branches pp_output_data and pgm_output_data of links. Hence, placed in the end. @@ -361,17 +361,15 @@ def _create_output_data_3ph(self): angle in degrees """ # TODO create output_data_3ph for trafos3w - # Although Pandapower itself did not implmenet res_shunt_3ph - # Since results are avaiable in PGM output, these should be converted. self._pp_buses_output_3ph() self._pp_lines_output_3ph() self._pp_ext_grids_output_3ph() - self._pp_loads_output_3ph() + self._pp_load_elements_output(element="load", symmetric=False) + self._pp_load_elements_output(element="ward", symmetric=False) + self._pp_load_elements_output(element="motor", symmetric=False) self._pp_shunts_output_3ph() self._pp_trafos_output_3ph() self._pp_sgens_output_3ph() - self._pp_ward_output_3ph() - self._pp_motor_output_3ph() self._pp_asym_gens_output_3ph() self._pp_asym_loads_output_3ph() @@ -1563,64 +1561,37 @@ def _pp_asym_gens_output(self): self.pp_output_data["res_asymmetric_sgen"] = pp_output_asym_gens - def _pp_loads_output(self): + def _pp_load_elements_output(self, element, symmetric): """ - This function converts a power-grid-model Symmetrical Load output array to a Load Dataframe of PandaPower. - - Returns: - a PandaPower Dataframe for the Load component + Utility function to convert output of elements represented as load + in power grid model. + element: "load", "motor" or "ward" + symmetric: True or False """ - load_id_names = ["const_power", "const_impedance", "const_current"] - assert "res_load" not in self.pp_output_data - - if ( - ComponentType.sym_load not in self.pgm_output_data - or self.pgm_output_data[ComponentType.sym_load].size == 0 - or ("load", load_id_names[0]) not in self.idx - ): - return - - # Store the results, while assuring that we are not overwriting any data - assert "res_load" not in self.pp_output_data - self.pp_output_data["res_load"] = self._pp_load_result_accumulate( - pp_component_name="load", load_id_names=load_id_names - ) - - def _pp_ward_output(self): - load_id_names = ["ward_const_power_load", "ward_const_impedance_load"] - assert "res_ward" not in self.pp_output_data - - if ( - ComponentType.sym_load not in self.pgm_output_data - or self.pgm_output_data[ComponentType.sym_load].size == 0 - or ("ward", load_id_names[0]) not in self.idx - ): - return - - accumulated_loads = self._pp_load_result_accumulate(pp_component_name="ward", load_id_names=load_id_names) - # TODO Find a better way for mapping vm_pu from bus - # accumulated_loads["vm_pu"] = np.nan - - # Store the results, while assuring that we are not overwriting any data - assert "res_ward" not in self.pp_output_data - self.pp_output_data["res_ward"] = accumulated_loads + if symmetric: + res_table = "res_" + element + else: + res_table = "res_" + element + "_3ph" - def _pp_motor_output(self): - load_id_names = ["motor_load"] + if element == "load": + load_id_names = ["const_power", "const_impedance", "const_current"] + elif element == "ward": + load_id_names = ["ward_const_power_load", "ward_const_impedance_load"] + elif element == "motor": + load_id_names = ["motor_load"] - assert "res_motor" not in self.pp_output_data + assert res_table not in self.pp_output_data if ( ComponentType.sym_load not in self.pgm_output_data or self.pgm_output_data[ComponentType.sym_load].size == 0 - or ("motor", load_id_names[0]) not in self.idx + or (element, load_id_names[0]) not in self.idx ): return - # Store the results, while assuring that we are not overwriting any data - assert "res_motor" not in self.pp_output_data - self.pp_output_data["res_motor"] = self._pp_load_result_accumulate( - pp_component_name="motor", load_id_names=load_id_names + assert res_table not in self.pp_output_data + self.pp_output_data[res_table] = self._pp_load_result_accumulate( + pp_component_name=element, load_id_names=load_id_names ) def _pp_load_result_accumulate(self, pp_component_name: str, load_id_names: List[str]) -> pd.DataFrame: @@ -2134,64 +2105,6 @@ def _pp_trafos_output_3ph(self): # pylint: disable=too-many-statements assert "res_trafo_3ph" not in self.pp_output_data self.pp_output_data["res_trafo_3ph"] = pp_output_trafos_3ph - def _pp_loads_output_3ph(self): - """ - This function converts a power-grid-model Symmetrical Load output array to a Load Dataframe of PandaPower. - - Returns: - a PandaPower Dataframe for the Load component - """ - load_id_names = ["const_power", "const_impedance", "const_current"] - if ( - ComponentType.sym_load not in self.pgm_output_data - or self.pgm_output_data[ComponentType.sym_load].size == 0 - or ("load", load_id_names[0]) not in self.idx - ): - return - - # Store the results, while assuring that we are not overwriting any data - assert "res_load_3ph" not in self.pp_output_data - self.pp_output_data["res_load_3ph"] = self._pp_load_result_accumulate( - pp_component_name="load", load_id_names=load_id_names - ) - - def _pp_ward_output_3ph(self): - # TODO: Create Unit tests - load_id_names = ["ward_const_power_load", "ward_const_impedance_load"] - if ( - ComponentType.sym_load not in self.pgm_output_data - or self.pgm_output_data[ComponentType.sym_load].size == 0 - or ("ward", load_id_names[0]) not in self.idx - ): - return - - # TODO Find a better way for mapping vm_pu from bus - # accumulated_loads["vm_pu"] = np.nan - # Store the results, while assuring that we are not overwriting any data - assert "res_ward_3ph" not in self.pp_output_data - self.pp_output_data["res_ward_3ph"] = self._pp_load_result_accumulate( - pp_component_name="ward", load_id_names=load_id_names - ) - - def _pp_motor_output_3ph(self): - # TODO: Create unit tests - load_id_names = ["motor_load"] - - assert "res_motor_3ph" not in self.pp_output_data - - if ( - ComponentType.sym_load not in self.pgm_output_data - or self.pgm_output_data[ComponentType.sym_load].size == 0 - or ("motor", load_id_names[0]) not in self.idx - ): - return - - # Store the results, while assuring that we are not overwriting any data - assert "res_motor" not in self.pp_output_data - self.pp_output_data["res_motor_3ph"] = self._pp_load_result_accumulate( - pp_component_name="motor", load_id_names=load_id_names - ) - def _pp_shunts_output_3ph(self): """ This function converts a power-grid-model Shunt output array to a Shunt Dataframe of PandaPower. diff --git a/tests/unit/converters/test_pandapower_converter_output.py b/tests/unit/converters/test_pandapower_converter_output.py index 2950c8c6..521d507b 100644 --- a/tests/unit/converters/test_pandapower_converter_output.py +++ b/tests/unit/converters/test_pandapower_converter_output.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MPL-2.0 from typing import Callable, List -from unittest.mock import ANY, MagicMock, patch +from unittest.mock import ANY, MagicMock, call, patch import numpy as np import pandas as pd @@ -38,11 +38,14 @@ def test_create_output_data(): converter._pp_sgens_output.assert_called_once_with() converter._pp_trafos_output.assert_called_once_with() converter._pp_trafos3w_output.assert_called_once_with() - converter._pp_loads_output.assert_called_once_with() + expected_calls = [ + call(element="load", symmetric=True), + call(element="ward", symmetric=True), + call(element="motor", symmetric=True), + ] + converter._pp_load_elements_output.assert_has_calls(expected_calls) converter._pp_asym_loads_output.assert_called_once_with() converter._pp_asym_gens_output.assert_called_once_with() - converter._pp_motor_output.assert_called_once_with() - converter._pp_ward_output.assert_called_once_with() converter._pp_switches_output.assert_called_once_with() @@ -60,7 +63,12 @@ def test_create_output_data_3ph(): converter._pp_ext_grids_output_3ph.assert_called_once_with() converter._pp_sgens_output_3ph.assert_called_once_with() converter._pp_trafos_output_3ph.assert_called_once_with() - converter._pp_loads_output_3ph.assert_called_once_with() + expected_calls = [ + call(element="load", symmetric=False), + call(element="ward", symmetric=False), + call(element="motor", symmetric=False), + ] + converter._pp_load_elements_output.assert_has_calls(expected_calls) converter._pp_asym_loads_output_3ph.assert_called_once_with() converter._pp_asym_gens_output_3ph.assert_called_once_with() @@ -75,18 +83,13 @@ def test_create_output_data_3ph(): (PandaPowerConverter._pp_sgens_output, "sym_gen"), (PandaPowerConverter._pp_trafos_output, "transformer"), (PandaPowerConverter._pp_trafos3w_output, "three_winding_transformer"), - (PandaPowerConverter._pp_loads_output, "sym_load"), - (PandaPowerConverter._pp_asym_loads_output, "asym_load"), (PandaPowerConverter._pp_asym_gens_output, "asym_gen"), - (PandaPowerConverter._pp_ward_output, "ward"), - (PandaPowerConverter._pp_motor_output, "motor"), (PandaPowerConverter._pp_switches_output, "link"), (PandaPowerConverter._pp_buses_output_3ph, "node"), (PandaPowerConverter._pp_lines_output_3ph, "line"), (PandaPowerConverter._pp_ext_grids_output_3ph, "source"), (PandaPowerConverter._pp_sgens_output_3ph, "sym_gen"), (PandaPowerConverter._pp_trafos_output_3ph, "transformer"), - (PandaPowerConverter._pp_loads_output_3ph, "sym_load"), (PandaPowerConverter._pp_asym_loads_output_3ph, "asym_load"), (PandaPowerConverter._pp_asym_gens_output_3ph, "asym_gen"), ], @@ -109,6 +112,34 @@ def test_create_pp_output_object__empty(create_fn: Callable[[PandaPowerConverter mock_df.assert_not_called() +@pytest.mark.parametrize( + ("create_fn", "element", "symmetric", "table"), + [ + (PandaPowerConverter._pp_load_elements_output, "load", True, "sym_load"), + (PandaPowerConverter._pp_load_elements_output, "ward", True, "ward"), + (PandaPowerConverter._pp_load_elements_output, "motor", True, "motor"), + ], +) +def test_create_pp_output_object_with_params__empty( + create_fn: Callable[[PandaPowerConverter, str, bool], None], element: str, symmetric: bool, table: str +): + # Arrange: No table + converter = PandaPowerConverter() + + # Act / Assert + with patch("power_grid_model_io.converters.pandapower_converter.pd.DataFrame") as mock_df: + create_fn(converter, element, symmetric) + mock_df.assert_not_called() + + # Arrange: Empty table + converter.pgm_output_data[table] = np.array([]) # type: ignore + + # Act / Assert + with patch("power_grid_model_io.converters.pandapower_converter.pd.DataFrame") as mock_df: + create_fn(converter, element, symmetric) + mock_df.assert_not_called() + + def test_output_bus(converter): # Arrange mock_pgm_array = MagicMock() @@ -532,15 +563,34 @@ def test_pp_load_result_accumulate__asym(): @pytest.mark.parametrize( - ("output_fn", "table", "load_id_names", "result_suffix"), + ("output_fn", "element", "symmetric", "table", "load_id_names", "result_suffix"), [ - (PandaPowerConverter._pp_loads_output, "load", ["const_power", "const_impedance", "const_current"], ""), - (PandaPowerConverter._pp_motor_output, "motor", ["motor_load"], ""), - (PandaPowerConverter._pp_loads_output_3ph, "load", ["const_power", "const_impedance", "const_current"], "_3ph"), + ( + PandaPowerConverter._pp_load_elements_output, + "load", + True, + "load", + ["const_power", "const_impedance", "const_current"], + "", + ), + (PandaPowerConverter._pp_load_elements_output, "motor", True, "motor", ["motor_load"], ""), + ( + PandaPowerConverter._pp_load_elements_output, + "load", + False, + "load", + ["const_power", "const_impedance", "const_current"], + "_3ph", + ), ], ) def test_output_load_types( - output_fn: Callable[[PandaPowerConverter], None], table: str, load_id_names: List[str], result_suffix: str + output_fn: Callable[[PandaPowerConverter, str, bool], None], + element: str, + symmetric: bool, + table: str, + load_id_names: List[str], + result_suffix: str, ): # Arrange converter = PandaPowerConverter() @@ -551,7 +601,7 @@ def test_output_load_types( converter._pp_load_result_accumulate = MagicMock() # type: ignore # Act - output_fn(converter) + output_fn(converter, element, symmetric) # Assert converter._pp_load_result_accumulate.assert_called_once_with(pp_component_name=table, load_id_names=load_id_names) @@ -570,7 +620,7 @@ def test_output_load_ward(): converter._pp_load_result_accumulate = MagicMock() # Act - converter._pp_ward_output() + converter._pp_load_elements_output(element="ward", symmetric=True) # Assert converter._pp_load_result_accumulate.assert_called_once_with(pp_component_name="ward", load_id_names=load_id_names) diff --git a/tests/validation/converters/test_pandapower_converter_output.py b/tests/validation/converters/test_pandapower_converter_output.py index 11c21713..d9d8bc77 100644 --- a/tests/validation/converters/test_pandapower_converter_output.py +++ b/tests/validation/converters/test_pandapower_converter_output.py @@ -357,6 +357,7 @@ def run_pf_asym_with_pgm(net): net[table] = output_tables[table] net = pp_networks.ieee_european_lv_asymmetric() + pp.create_load(net, 50, 0.1, 0.05) run_pf_asym_with_pgm(net) s_ext_grid, s_load, s_loss = _get_total_powers_3ph(net) assert np.isclose(s_ext_grid, (s_load + s_loss)) @@ -430,6 +431,7 @@ def run_pf_sym_with_pgm(net): net[table] = output_tables[table] net = pp_networks.ieee_european_lv_asymmetric() + pp.create_load(net, 50, 0.1, 0.05) run_pf_sym_with_pgm(net) s_ext_grid, s_load, s_loss = _get_total_powers(net) assert np.isclose(s_ext_grid, (s_load + s_loss)) From c16addb18823d731b6539d7531316113617cca68 Mon Sep 17 00:00:00 2001 From: furqan463 Date: Mon, 15 Sep 2025 22:14:53 +0500 Subject: [PATCH 6/7] restore test for asym_load Signed-off-by: furqan463 --- src/power_grid_model_io/converters/pandapower_converter.py | 2 -- tests/unit/converters/test_pandapower_converter_output.py | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/power_grid_model_io/converters/pandapower_converter.py b/src/power_grid_model_io/converters/pandapower_converter.py index a2ec74ac..45ddc7e7 100644 --- a/src/power_grid_model_io/converters/pandapower_converter.py +++ b/src/power_grid_model_io/converters/pandapower_converter.py @@ -1580,8 +1580,6 @@ def _pp_load_elements_output(self, element, symmetric): elif element == "motor": load_id_names = ["motor_load"] - assert res_table not in self.pp_output_data - if ( ComponentType.sym_load not in self.pgm_output_data or self.pgm_output_data[ComponentType.sym_load].size == 0 diff --git a/tests/unit/converters/test_pandapower_converter_output.py b/tests/unit/converters/test_pandapower_converter_output.py index 521d507b..fc207d76 100644 --- a/tests/unit/converters/test_pandapower_converter_output.py +++ b/tests/unit/converters/test_pandapower_converter_output.py @@ -83,6 +83,7 @@ def test_create_output_data_3ph(): (PandaPowerConverter._pp_sgens_output, "sym_gen"), (PandaPowerConverter._pp_trafos_output, "transformer"), (PandaPowerConverter._pp_trafos3w_output, "three_winding_transformer"), + (PandaPowerConverter._pp_asym_loads_output, "asym_load"), (PandaPowerConverter._pp_asym_gens_output, "asym_gen"), (PandaPowerConverter._pp_switches_output, "link"), (PandaPowerConverter._pp_buses_output_3ph, "node"), From ab4b470df764f6f223dd5d2e5f7ef123e0b76f30 Mon Sep 17 00:00:00 2001 From: furqan463 Date: Thu, 18 Sep 2025 23:00:09 +0500 Subject: [PATCH 7/7] Fix tests Signed-off-by: furqan463 --- .../converters/pandapower_converter.py | 2 +- .../test_pandapower_converter_output.py | 78 +++++++------------ 2 files changed, 29 insertions(+), 51 deletions(-) diff --git a/src/power_grid_model_io/converters/pandapower_converter.py b/src/power_grid_model_io/converters/pandapower_converter.py index 45ddc7e7..1c22dac4 100644 --- a/src/power_grid_model_io/converters/pandapower_converter.py +++ b/src/power_grid_model_io/converters/pandapower_converter.py @@ -360,7 +360,7 @@ def _create_output_data_3ph(self): Furthermore, creates a global node lookup table, which stores nodes' voltage magnitude per unit and the voltage angle in degrees """ - # TODO create output_data_3ph for trafos3w + # TODO create output_data_3ph for trafos3w, switches self._pp_buses_output_3ph() self._pp_lines_output_3ph() self._pp_ext_grids_output_3ph() diff --git a/tests/unit/converters/test_pandapower_converter_output.py b/tests/unit/converters/test_pandapower_converter_output.py index fc207d76..c73a7ae9 100644 --- a/tests/unit/converters/test_pandapower_converter_output.py +++ b/tests/unit/converters/test_pandapower_converter_output.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: MPL-2.0 -from typing import Callable, List +from typing import Any, Callable, Dict, List from unittest.mock import ANY, MagicMock, call, patch import numpy as np @@ -74,34 +74,40 @@ def test_create_output_data_3ph(): @pytest.mark.parametrize( - ("create_fn", "table"), + ("create_fn", "table", "create_fn_kwargs"), [ - (PandaPowerConverter._pp_buses_output, "node"), - (PandaPowerConverter._pp_lines_output, "line"), - (PandaPowerConverter._pp_ext_grids_output, "source"), - (PandaPowerConverter._pp_shunts_output, "shunt"), - (PandaPowerConverter._pp_sgens_output, "sym_gen"), - (PandaPowerConverter._pp_trafos_output, "transformer"), - (PandaPowerConverter._pp_trafos3w_output, "three_winding_transformer"), - (PandaPowerConverter._pp_asym_loads_output, "asym_load"), - (PandaPowerConverter._pp_asym_gens_output, "asym_gen"), - (PandaPowerConverter._pp_switches_output, "link"), - (PandaPowerConverter._pp_buses_output_3ph, "node"), - (PandaPowerConverter._pp_lines_output_3ph, "line"), - (PandaPowerConverter._pp_ext_grids_output_3ph, "source"), - (PandaPowerConverter._pp_sgens_output_3ph, "sym_gen"), - (PandaPowerConverter._pp_trafos_output_3ph, "transformer"), - (PandaPowerConverter._pp_asym_loads_output_3ph, "asym_load"), - (PandaPowerConverter._pp_asym_gens_output_3ph, "asym_gen"), + (PandaPowerConverter._pp_buses_output, "node", {}), + (PandaPowerConverter._pp_lines_output, "line", {}), + (PandaPowerConverter._pp_ext_grids_output, "source", {}), + (PandaPowerConverter._pp_shunts_output, "shunt", {}), + (PandaPowerConverter._pp_sgens_output, "sym_gen", {}), + (PandaPowerConverter._pp_trafos_output, "transformer", {}), + (PandaPowerConverter._pp_trafos3w_output, "three_winding_transformer", {}), + (PandaPowerConverter._pp_load_elements_output, "load", {"symmetric": True, "element": "sym_load"}), + (PandaPowerConverter._pp_load_elements_output, "ward", {"symmetric": True, "element": "ward"}), + (PandaPowerConverter._pp_load_elements_output, "motor", {"symmetric": True, "element": "motor"}), + (PandaPowerConverter._pp_asym_loads_output, "asym_load", {}), + (PandaPowerConverter._pp_asym_gens_output, "asym_gen", {}), + (PandaPowerConverter._pp_switches_output, "link", {}), + (PandaPowerConverter._pp_buses_output_3ph, "node", {}), + (PandaPowerConverter._pp_lines_output_3ph, "line", {}), + (PandaPowerConverter._pp_ext_grids_output_3ph, "source", {}), + (PandaPowerConverter._pp_sgens_output_3ph, "sym_gen", {}), + (PandaPowerConverter._pp_trafos_output_3ph, "transformer", {}), + (PandaPowerConverter._pp_load_elements_output, "load", {"symmetric": False, "element": "sym_load"}), + (PandaPowerConverter._pp_load_elements_output, "ward", {"symmetric": False, "element": "ward"}), + (PandaPowerConverter._pp_load_elements_output, "motor", {"symmetric": False, "element": "motor"}), + (PandaPowerConverter._pp_asym_loads_output_3ph, "asym_load", {}), + (PandaPowerConverter._pp_asym_gens_output_3ph, "asym_gen", {}), ], ) -def test_create_pp_output_object__empty(create_fn: Callable[[PandaPowerConverter], None], table: str): +def test_create_pp_output_object__empty(create_fn: Callable[..., None], table: str, create_fn_kwargs: Dict[str, Any]): # Arrange: No table converter = PandaPowerConverter() # Act / Assert with patch("power_grid_model_io.converters.pandapower_converter.pd.DataFrame") as mock_df: - create_fn(converter) + create_fn(converter, **create_fn_kwargs) mock_df.assert_not_called() # Arrange: Empty table @@ -109,35 +115,7 @@ def test_create_pp_output_object__empty(create_fn: Callable[[PandaPowerConverter # Act / Assert with patch("power_grid_model_io.converters.pandapower_converter.pd.DataFrame") as mock_df: - create_fn(converter) - mock_df.assert_not_called() - - -@pytest.mark.parametrize( - ("create_fn", "element", "symmetric", "table"), - [ - (PandaPowerConverter._pp_load_elements_output, "load", True, "sym_load"), - (PandaPowerConverter._pp_load_elements_output, "ward", True, "ward"), - (PandaPowerConverter._pp_load_elements_output, "motor", True, "motor"), - ], -) -def test_create_pp_output_object_with_params__empty( - create_fn: Callable[[PandaPowerConverter, str, bool], None], element: str, symmetric: bool, table: str -): - # Arrange: No table - converter = PandaPowerConverter() - - # Act / Assert - with patch("power_grid_model_io.converters.pandapower_converter.pd.DataFrame") as mock_df: - create_fn(converter, element, symmetric) - mock_df.assert_not_called() - - # Arrange: Empty table - converter.pgm_output_data[table] = np.array([]) # type: ignore - - # Act / Assert - with patch("power_grid_model_io.converters.pandapower_converter.pd.DataFrame") as mock_df: - create_fn(converter, element, symmetric) + create_fn(converter, **create_fn_kwargs) mock_df.assert_not_called()