Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/branch parallel to substation #2178

Draft
wants to merge 8 commits into
base: develop
Choose a base branch
from
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ 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(), branches_parallel_to_bus_bus_switch_clusters()
- [FIXED] additional arguments from mpc saved to net._options: create "_options" if it does not exist
- [CHANGED] cim2pp: extracted getting default classes, added generic setting datatypes from CGMES XMI schema
- [ADDED] toolbox function :code:`get_substations` that finds all substations based on connectivity analysis of a networkx graph


[2.13.1] - 2023-05-12
Expand Down
10 changes: 10 additions & 0 deletions doc/toolbox.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ Item/Element Selection

.. autofunction:: pandapower.toolbox.get_connected_switches

.. autofunction:: pandapower.toolbox.get_substations

.. autofunction:: pandapower.toolbox.get_connecting_branches

.. autofunction:: pandapower.toolbox.false_elm_links
Expand All @@ -83,6 +85,14 @@ 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.branches_parallel_to_bus_bus_switch_clusters

.. autofunction:: pandapower.toolbox.get_gc_objects_dict

====================================
Expand Down
2 changes: 1 addition & 1 deletion pandapower/grid_equivalents/get_equivalent.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ def _determine_bus_groups(net, boundary_buses, internal_buses,
# --- determine buses connected to boundary buses via bus-bus-switch
boundary_buses_inclusive_bswitch = set()
mg_sw = top.create_nxgraph(net, respect_switches=True, include_lines=False, include_impedances=False,
include_tcsc=False, include_trafos=False, include_trafo3ws=False)
include_trafos=False, include_trafo3ws=False, include_tcsc=False)
for bbus in boundary_buses:
boundary_buses_inclusive_bswitch |= set(top.connected_component(mg_sw, bbus))
if len(boundary_buses_inclusive_bswitch) > len(boundary_buses):
Expand Down
7 changes: 2 additions & 5 deletions pandapower/grid_equivalents/toolbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,8 @@ def append_boundary_buses_externals_per_zone(boundary_buses, boundaries, zone, o
def get_connected_switch_buses_groups(net, buses):
all_buses = set()
bus_dict = []
mg_sw = top.create_nxgraph(net, include_trafos=False,
include_trafo3ws=False,
respect_switches=True,
include_lines=False,
include_impedances=False)
mg_sw = top.create_nxgraph(net, respect_switches=True, include_lines=False, include_impedances=False,
include_trafos=False, include_trafo3ws=False)
for bbus in buses:
if bbus in all_buses:
continue
Expand Down
15 changes: 11 additions & 4 deletions pandapower/plotting/generic_geodata.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ def create_generic_coordinates(net, mg=None, library="igraph",
respect_switches=False,
geodata_table="bus_geodata",
buses=None,
overwrite=False):
overwrite=False,
include_out_of_service_buses=False,
include_out_of_service_branches=False):
"""
This function will add arbitrary geo-coordinates for all buses based on an analysis of branches
and rings. It will remove out of service buses/lines from the net. The coordinates will be
Expand All @@ -182,6 +184,10 @@ def create_generic_coordinates(net, mg=None, library="igraph",
:type buses: list
:param overwrite: overwrite existing geodata
:type overwrite: bool
:param include_out_of_service_buses: also include the buses that are out-of-service in the graph
:type include_out_of_service_buses: bool
:param include_out_of_service_branches: also include the branches that are out-of-service in the graph
:type include_out_of_service_branches: bool
:return: net - pandapower network with added geo coordinates for the buses

:Example:
Expand All @@ -197,7 +203,8 @@ def create_generic_coordinates(net, mg=None, library="igraph",
elif library == "networkx":
if mg is None:
nxg = top.create_nxgraph(net, respect_switches=respect_switches,
include_out_of_service=True)
include_out_of_service=include_out_of_service_buses,
include_out_of_service_branches=include_out_of_service_branches)
else:
nxg = copy.deepcopy(mg)
coords = coords_from_nxgraph(nxg)
Expand All @@ -220,8 +227,8 @@ def _prepare_geodata_table(net, geodata_table, overwrite):
net[geodata_table] = pd.DataFrame(columns=["x", "y"])

def fuse_geodata(net):
mg = top.create_nxgraph(net, include_lines=False, include_impedances=False,
respect_switches=False)
mg = top.create_nxgraph(net, respect_switches=False, include_lines=False, include_impedances=False,
include_out_of_service=True)
geocoords = set(net.bus_geodata.index)
for area in top.connected_components(mg):
if len(area & geocoords) > 1:
Expand Down
3 changes: 1 addition & 2 deletions pandapower/shortcircuit/toolbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ def detect_power_station_unit(net, mode="auto",
trafo_lv_bus = net.trafo.loc[required_trafo.index, "lv_bus"].values
trafo_hv_bus = net.trafo.loc[required_trafo.index, "hv_bus"].values

g = create_nxgraph(net, respect_switches=True,
nogobuses=None, notravbuses=trafo_hv_bus)
g = create_nxgraph(net, respect_switches=True, nogobuses=None, notravbuses=trafo_hv_bus)

for t_ix in required_trafo.index:
t_lv_bus = required_trafo.at[t_ix, "lv_bus"]
Expand Down
149 changes: 148 additions & 1 deletion pandapower/test/toolbox/test_element_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

# 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 numpy as np
import pytest

import pandapower as pp
Expand Down Expand Up @@ -219,5 +221,150 @@ def test_count_elements():
assert set(received.index) == pandapower.toolbox.pp_elements()


def substation_net():
net = pp.create_empty_network()
pp.create_buses(net, 5, 110)
pp.create_buses(net, 5, 20)
pp.create_buses(net, 2, 10)

pp.create_transformer(net, 3, 5, "63 MVA 110/20 kV")
pp.create_transformer3w(net, 4, 8, 10, "63/25/38 MVA 110/20/10 kV")

pp.create_switches(net, buses=[0, 0, 2, 5, 6, 1, 8, 10], elements=[1, 2, 3, 6, 7, 4, 9, 11], et="b")
pp.create_switches(net, buses=[3, 5], elements=[0, 0], et=["t", "t"])
pp.create_switches(net, buses=[4, 8, 10], elements=[0, 0, 0], et=["t3", "t3", "t3"])
return net


def test_get_substations():
net = substation_net()
s = pp.toolbox.get_substations(net, write_to_net=False)
assert len(s) == 1
assert "substation" not in net.bus.columns
pp.toolbox.get_substations(net)
assert np.alltrue(net.bus.substation == 0)
assert np.array_equal(net.bus.index.values, s[0])

s1 = pp.toolbox.get_substations(net, include_trafos=False)
# 110 kV buses HV side, 20 kV buses for trafo and trafo3w, 10 kV buses for trafo3w
assert len(s1) == 4
assert len(net.bus.substation.unique()) == 4
for c in s1.values():
assert len(net.bus.loc[c, "vn_kv"].unique()) == 1

net.trafo.in_service = False
net.trafo3w.in_service = False
s11 = pp.toolbox.get_substations(net, include_out_of_service_branches=False)
assert len(s11) == 4
assert len(net.bus.substation.unique()) == 4
for k, v in s11.items():
assert np.array_equal(v, s1[k])

net.switch.closed = False
s2 = pp.toolbox.get_substations(net)
assert len(s2) == 1
assert len(net.bus.substation.unique()) == 1
assert np.array_equal(s[0], s2[0])

s3 = pp.toolbox.get_substations(net, respect_switches=True)
assert len(s3) == 0
assert np.alltrue(pd.isna(net.bus.substation))

s4 = pp.toolbox.get_substations(net, respect_switches=True, return_all_buses=True)
assert len(s4) == 12
assert len(net.bus.substation.unique()) == 12

# even when al switches open and trafos out of service, find 1 substation:
s5 = pp.toolbox.get_substations(net)
assert np.alltrue(net.bus.substation == 0)
assert np.array_equal(net.bus.index.values, s5[0])

# even when all buses out of service:
net.bus.in_service = False
s6 = pp.toolbox.get_substations(net)
assert np.alltrue(net.bus.substation == 0)
assert np.array_equal(net.bus.index.values, s6[0])


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"])


def test_branches_parallel_to_bus_bus_switch_clusters():
net = substation_net()
pp.create_bus(net, 10)
line = pp.create_line(net, net.bus.index[-2], net.bus.index[-1], 1.3, "NA2XS2Y 1x95 RM/25 6/10 kV")
pp.create_switch(net, net.bus.index[-2], line, "l")

df = pp.toolbox.branches_parallel_to_bus_bus_switch_clusters(net)
assert not len(df)

par1 = pp.create_line(net, net.bus.index[-3], net.bus.index[-2], 1.3, "NA2XS2Y 1x95 RM/25 6/10 kV")
par2 = pp.create_line(net, net.bus.index[1], net.bus.index[2], 13, "48-AL1/8-ST1A 110.0")

df = pp.toolbox.branches_parallel_to_bus_bus_switch_clusters(net)
assert len(df) == 2
assert list(df.element_type) == ["line"]*2
assert list(df.element_index) == [par1, par2]

net = pp.networks.example_multivoltage()
pp.create_switch(net, net.trafo.lv_bus.at[0], net.trafo.hv_bus.at[0], "b", closed=False)
df = pp.toolbox.branches_parallel_to_bus_bus_switch_clusters(net)
assert len(df) == 1
assert dict(df.iloc[0]) == {'bus1': 13, 'bus2': 17, 'element_type': 'trafo',
'element_index': 0, 'substation1': 0, 'substation2': 0}


if __name__ == '__main__':
pytest.main([__file__, "-x"])
# pytest.main([__file__, "-x"])
test_branches_parallel_to_bus_bus_switch_clusters()
Loading
Loading