From 06a1596bb76db85943326aac4ef33d6d6e818240 Mon Sep 17 00:00:00 2001 From: Steffen Meinecke Date: Wed, 22 Nov 2023 17:57:14 +0100 Subject: [PATCH 1/2] [ADDED] toolbox functions branch_buses_df(), branches_parallel_to_bus_bus_switches(), check_parallel_branch_to_bus_bus_switch() --- CHANGELOG.rst | 1 + doc/toolbox.rst | 6 + .../test/toolbox/test_element_selection.py | 55 ++++++ pandapower/toolbox/element_selection.py | 165 +++++++++++++++++- 4 files changed, 225 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0908384d9..77948c179 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -34,6 +34,7 @@ Change Log - [ADDED] option to use a second tap changer for the trafo element - [CHANGED] parameters of function merge_internal_net_and_equivalent_external_net() - [FIXED] :code:`convert_format.py`: update the attributes of the characteristic objects to match the new characteristic +- [ADDED] toolbox functions branch_buses_df(), branches_parallel_to_bus_bus_switches(), check_parallel_branch_to_bus_bus_switch() - [CHANGED] cim2pp: extracted getting default classes, added generic setting datatypes from CGMES XMI schema diff --git a/doc/toolbox.rst b/doc/toolbox.rst index 0f64d45df..2d830da71 100644 --- a/doc/toolbox.rst +++ b/doc/toolbox.rst @@ -83,6 +83,12 @@ Item/Element Selection .. autofunction:: pandapower.toolbox.count_elements +.. autofunction:: pandapower.toolbox.branch_buses_df + +.. autofunction:: pandapower.toolbox.branches_parallel_to_bus_bus_switches + +.. autofunction:: pandapower.toolbox.check_parallel_branch_to_bus_bus_switch + .. autofunction:: pandapower.toolbox.get_gc_objects_dict ==================================== diff --git a/pandapower/test/toolbox/test_element_selection.py b/pandapower/test/toolbox/test_element_selection.py index a355c056d..1ca17a8d3 100644 --- a/pandapower/test/toolbox/test_element_selection.py +++ b/pandapower/test/toolbox/test_element_selection.py @@ -2,6 +2,7 @@ # Copyright (c) 2016-2023 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. +import numpy as np import pandas as pd import pytest @@ -219,5 +220,59 @@ def test_count_elements(): assert set(received.index) == pandapower.toolbox.pp_elements() +def test_branch_buses_df(): + net = nw.example_multivoltage() + df = pp.branch_buses_df(net, "line") + assert np.allclose(df.iloc[:, :2], net.line[["from_bus", "to_bus"]].values) + assert set(df.element_type) == {"line"} + assert list(df.columns) == ["bus1", "bus2", "element_type", "element_index"] + + df = pp.branch_buses_df(net, "trafo3w", ["hv_bus", "mv_bus", "lv_bus"]) + assert list(df.columns) == ["bus1", "bus2", "element_type", "element_index"] + assert len(df) == 3*len(net.trafo3w) + + +def test_branches_parallel_to_bus_bus_switches(): + net = nw.example_multivoltage() + + assert pp.branches_parallel_to_bus_bus_switches(net).shape == (0, 4) + + sw_p = pp.create_switch(net, net.trafo.lv_bus.at[0], net.trafo.hv_bus.at[0], "b", closed=False) + assert pp.branches_parallel_to_bus_bus_switches(net).shape == (2, 4) + assert pp.branches_parallel_to_bus_bus_switches(net, keep="first").shape == (1, 4) + assert pp.branches_parallel_to_bus_bus_switches(net, keep="last").shape == (1, 4) + assert pp.branches_parallel_to_bus_bus_switches(net, closed_switches_only=True).shape == (0, 4) + assert pp.branches_parallel_to_bus_bus_switches( + net, switches=net.switch.index.difference([sw_p])).shape == (0, 4) + + # switch bus order of bus-bus switch + net.switch.loc[sw_p, ["bus", "element"]] = net.switch.loc[sw_p, ["element", "bus"]].values + + assert pp.branches_parallel_to_bus_bus_switches( + net, branch_types=["line", "trafo3w"]).shape == (0, 4) + assert pp.branches_parallel_to_bus_bus_switches( + net, branch_types=["trafo", "trafo3w"]).shape == (2, 4) + + +def test_check_parallel_branch_to_bus_bus_switch(): + net = nw.example_multivoltage() + + assert not pp.check_parallel_branch_to_bus_bus_switch(net) + + sw_p = pp.create_switch(net, net.trafo.lv_bus.at[0], net.trafo.hv_bus.at[0], "b", closed=False) + assert pp.check_parallel_branch_to_bus_bus_switch(net) + assert not pp.check_parallel_branch_to_bus_bus_switch(net, closed_switches_only=True) + assert not pp.check_parallel_branch_to_bus_bus_switch( + net, switches=net.switch.index.difference([sw_p])) + + # switch bus order of bus-bus switch + net.switch.loc[sw_p, ["bus", "element"]] = net.switch.loc[sw_p, ["element", "bus"]].values + + assert not pp.check_parallel_branch_to_bus_bus_switch( + net, branch_types=["line", "trafo3w"]) + assert pp.check_parallel_branch_to_bus_bus_switch( + net, branch_types=["trafo", "trafo3w"]) + + if __name__ == '__main__': pytest.main([__file__, "-x"]) \ No newline at end of file diff --git a/pandapower/toolbox/element_selection.py b/pandapower/toolbox/element_selection.py index 4a51f9f7d..1ccd4216e 100644 --- a/pandapower/toolbox/element_selection.py +++ b/pandapower/toolbox/element_selection.py @@ -203,7 +203,7 @@ def get_connected_elements(net, element_type, buses, respect_switches=True, resp connected_elements = set(element_table.index[(element_table.bus.isin(buses))]) elif element_type in ['_equiv_trafo3w']: # ignore '_equiv_trafo3w' - return {} + return set() else: raise UserWarning(f"Unknown element type {element_type}!") @@ -707,4 +707,165 @@ def count_elements(net, return_empties=False, **kwargs): dtype: int32 """ return pd.Series({et: net[et].shape[0] for et in pp_elements(**kwargs) if return_empties or \ - bool(net[et].shape[0])}, dtype=np.int64) \ No newline at end of file + bool(net[et].shape[0])}, dtype=np.int64) + + +def branch_buses_df( + net: pp.pandapowerNet, branch_type: str, bus_columns: list[str] = None) -> pd.DataFrame: + """Returns a DataFrame which summarizes the buses to which the elements of defined element_type + are connected to. + + Parameters + ---------- + net : pp.pandapowerNet + pandapower net + branch_type : str + branch type, e.g. "trafo", "trafo3w" or "line" + bus_columns : list[str] + list of bus columns of the element type table; if None, all columns from are used + + Returns + ------- + pd.DataFrame + summary of the buses to which the elements of defined element_type are connected to. + + Example + ------- + >>> import pandapower as pp + >>> net = pp.networks.example_multivoltage() + >>> pp.branch_buses_df(net, "trafo3w") + bus1 bus2 element_type element_index + 0 33 36 trafo3w 0 + 1 33 37 trafo3w 0 + 2 36 37 trafo3w 0 + """ + if bus_columns is None: + bus_columns = branch_element_bus_dict()[branch_type] + if len(bus_columns) == 2: + return net[branch_type][bus_columns].set_axis(["bus1", "bus2"], axis="columns").assign( + element_type=branch_type, element_index=net[branch_type].index) + elif len(bus_columns) == 3: + bus_combis = [[bus_columns[0], bus_columns[1]], + [bus_columns[0], bus_columns[2]], + [bus_columns[1], bus_columns[2]]] + return pd.concat([net[branch_type][bus_combi].set_axis( + ["bus1", "bus2"], axis="columns").assign(element_type=branch_type, element_index=net[ + branch_type].index) for bus_combi in bus_combis], ignore_index=True) + else: + raise NotImplementedError(f"{len(bus_columns)=} is not implemented.") + + +def branches_parallel_to_bus_bus_switches( + net: pp.pandapowerNet, branch_types=None, switches=None, + closed_switches_only=False, keep=False) -> pd.DataFrame: + """Returns a DataFrame of branches and/or bus-bus switches that are in parallel + + Parameters + ---------- + net : pp.pandapowerNet + pandapower net + branch_types : list[str], optional + list of names of branch types to be considered, by default None + switches : iterable, optional + list of switches to be considered, by default None + closed_switches_only : bool, optional + if True, the list of considered switches is reduced to only closed switches, by default False + keep : bool, optional + decides whether the returned DataFrame contains the branches ("last"), + the switches ("first") or both (False), by default False + + Returns + ------- + pd.DataFrame + branches and/or bus-bus switches that are in parallel + + Note + ---- + The returned branches do not necessarily contain all branches that are parallel to bus-bus + switches. + + Example + ------- + >>> import pandapower as pp + >>> net = pp.networks.example_multivoltage() + >>> pp.create_switch(net, net.trafo.lv_bus.at[0], net.trafo.hv_bus.at[0], "b", closed=False) + 88 + >>> pp.branches_parallel_to_bus_bus_switches(net) + bus1 bus2 element_type element_index + 26 13 17 trafo 0 + 65 13 17 switch 88 + >>> pp.branches_parallel_to_bus_bus_switches(net, closed_switches_only=True) + Empty DataFrame + Columns: [bus1, bus2, element_type, element_index] + Index: [] + """ + + considered_sw_df = net.switch if switches is None else net.switch.loc[switches] + if closed_switches_only: + considered_sw_df = considered_sw_df.loc[considered_sw_df.closed] + bb_sw = considered_sw_df.loc[considered_sw_df.et == "b", ["bus", "element"]].set_axis( + ["bus1", "bus2"], axis="columns") + bb_sw = bb_sw.assign(element_type="switch", element_index=bb_sw.index) + if not len(bb_sw): + return pd.DataFrame({ + 'bus1': int(), 'bus2': int(), 'element_type': str(), 'element_index': int()}, index=[]) + bebd = branch_element_bus_dict() + if branch_types is not None: + bebd = {key: val for key, val in bebd.items() if key in branch_types} + bra_buses = pd.concat([branch_buses_df(net, et, bus_columns) \ + for et, bus_columns in bebd.items()], ignore_index=True) + + # drop duplicates + bb_sw.drop_duplicates(subset=["bus1", "bus2"], inplace=True) + bra_buses.drop_duplicates(subset=["bus1", "bus2"], inplace=True) + + # order bbs_sw and bra_buses + to_order = bb_sw.bus1 > bb_sw.bus2 + bb_sw.loc[to_order, ["bus1", "bus2"]] = bb_sw.loc[to_order, ["bus2", "bus1"]].values + to_order = bra_buses.bus1 > bra_buses.bus2 + bra_buses.loc[to_order, ["bus1", "bus2"]] = bra_buses.loc[to_order, ["bus2", "bus1"]].values + + # merge bb_sw and bra_buses + df = pd.concat([bra_buses, bb_sw], ignore_index=True) + df = df.loc[df.duplicated(subset=["bus1", "bus2"], keep=keep)] + return df # further parallel branches in parallel to the returned branches can exist + + +def check_parallel_branch_to_bus_bus_switch( + net: pp.pandapowerNet, branch_types=None, switches=None, + closed_switches_only=False) -> bool: + """Returns a DataFrame of branches and/or bus-bus switches that are in parallel + + Parameters + ---------- + net : pp.pandapowerNet + pandapower net + branch_types : list[str], optional + list of names of branch types to be considered, by default None + switches : iterable, optional + list of switches to be considered, by default None + closed_switches_only : bool, optional + if True, the list of considered switches is reduced to only closed switches, by default False + + Returns + ------- + bool + whether aa least one branch (of given branch types) is parallel to a bus-bus + switches (of the given (closed) switches) + + Example + ------- + >>> import pandapower as pp + >>> net = pp.networks.example_multivoltage() + >>> pp.create_switch(net, net.trafo.lv_bus.at[0], net.trafo.hv_bus.at[0], "b", closed=False) + 88 + >>> pp.check_parallel_branch_to_bus_bus_switch(net) + True + >>> pp.check_parallel_branch_to_bus_bus_switch(net, closed_switches_only=True) + False + """ + return bool(len( + branches_parallel_to_bus_bus_switches( + net, branch_types=branch_types, switches=switches, + closed_switches_only=closed_switches_only) + )) From 9ccbd6c00e1ed88a0a411e8d109b62f782e5d3f9 Mon Sep 17 00:00:00 2001 From: Steffen Meinecke Date: Thu, 23 Nov 2023 12:41:02 +0100 Subject: [PATCH 2/2] remove type hinting to fix py3.8 tests --- pandapower/toolbox/element_selection.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pandapower/toolbox/element_selection.py b/pandapower/toolbox/element_selection.py index 1ccd4216e..1d454eb97 100644 --- a/pandapower/toolbox/element_selection.py +++ b/pandapower/toolbox/element_selection.py @@ -710,8 +710,7 @@ def count_elements(net, return_empties=False, **kwargs): bool(net[et].shape[0])}, dtype=np.int64) -def branch_buses_df( - net: pp.pandapowerNet, branch_type: str, bus_columns: list[str] = None) -> pd.DataFrame: +def branch_buses_df(net, branch_type, bus_columns=None): """Returns a DataFrame which summarizes the buses to which the elements of defined element_type are connected to. @@ -756,8 +755,7 @@ def branch_buses_df( def branches_parallel_to_bus_bus_switches( - net: pp.pandapowerNet, branch_types=None, switches=None, - closed_switches_only=False, keep=False) -> pd.DataFrame: + net, branch_types=None, switches=None, closed_switches_only=False, keep=False): """Returns a DataFrame of branches and/or bus-bus switches that are in parallel Parameters @@ -832,8 +830,7 @@ def branches_parallel_to_bus_bus_switches( def check_parallel_branch_to_bus_bus_switch( - net: pp.pandapowerNet, branch_types=None, switches=None, - closed_switches_only=False) -> bool: + net, branch_types=None, switches=None, closed_switches_only=False): """Returns a DataFrame of branches and/or bus-bus switches that are in parallel Parameters