Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
67 changes: 66 additions & 1 deletion src/power_grid_model_io/converters/pandapower_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/converters/test_pandapower_converter_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
174 changes: 174 additions & 0 deletions tests/validation/converters/test_pandapower_converter_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,177 @@ 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()

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) 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()

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

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_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()
)
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))


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))
Loading