Skip to content

Commit

Permalink
create VSC collections and VSC connections collections
Browse files Browse the repository at this point in the history
  • Loading branch information
rbolgaryn committed Dec 8, 2023
1 parent 03b1d60 commit 6e61359
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 9 deletions.
144 changes: 143 additions & 1 deletion pandapower/plotting/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class TextPath: # so that the test does not fail

from pandapower.auxiliary import soft_dependency_error
from pandapower.plotting.patch_makers import load_patches, node_patches, gen_patches, \
sgen_patches, ext_grid_patches, trafo_patches, storage_patches
sgen_patches, ext_grid_patches, trafo_patches, storage_patches, vsc_patches
from pandapower.plotting.plotting_toolbox import _rotate_dim2, coords_from_node_geodata, \
position_on_busbar, get_index_array

Expand Down Expand Up @@ -938,6 +938,148 @@ def create_trafo3w_collection(net, trafo3ws=None, picker=False, infofunc=None, c
return lc, pc


def create_vsc_collection(net, vscs=None, picker=False, size=None, infofunc=None, cmap=None,
norm=None, z=None, clim=None, cbar_title="VSC power",
plot_colormap=True, bus_geodata=None, bus_dc_geodata=None, **kwargs):
"""
Creates a matplotlib line collection of pandapower transformers.
Input:
**net** (pandapowerNet) - The pandapower network
OPTIONAL:
**vscs** (list, None) - The VSC indices for which the collections are created.
If None, all VSCs in the grid are considered.
**picker** (bool, False) - picker argument passed to the patch collection
**size** (int, None) - size of VSC symbol squares. Should be >0 and
< 0.35*bus_distance
**infofunc** (function, None) - infofunction for the patch element
**kwargs** - keyword arguments are passed to the patch function
OUTPUT:
**lc** - line collection
**pc** - patch collection
"""
if not MATPLOTLIB_INSTALLED:
soft_dependency_error(str(sys._getframe().f_code.co_name)+"()", "matplotlib")

vscs = get_index_array(vscs, net.vsc.index)

if bus_geodata is None:
bus_geodata = net["bus_geodata"]
if bus_dc_geodata is None:
bus_dc_geodata = net["bus_dc_geodata"]

in_geodata = (net.vsc.bus.loc[vscs].isin(bus_geodata.index) &
net.vsc.bus_dc.loc[vscs].isin(bus_dc_geodata.index))
vscs = vscs[in_geodata]
vsc_table = net.vsc.loc[vscs]

coords, vscs_with_geo = coords_from_node_geodata(
vscs, vsc_table.bus.values, vsc_table.bus_dc.values, bus_geodata, "vsc", node_geodata_to=bus_dc_geodata)

if len(vscs_with_geo) == 0:
return None

colors = kwargs.pop("color", "k")
linewidths = kwargs.pop("linewidths", 2.)
linewidths = kwargs.pop("linewidth", linewidths)
linewidths = kwargs.pop("lw", linewidths)
if cmap is not None:
if z is None:
z = net.res_vsc.p_mw
colors = [cmap(norm(z.at[idx])) for idx in vscs_with_geo]

infos = [infofunc(i) for i in range(len(vscs_with_geo))] if infofunc is not None else []

lc, pc = _create_complex_branch_collection(
coords, vsc_patches, size, infos, patch_facecolor="none", patch_edgecolor=colors,
line_color=colors, picker=picker, linewidths=linewidths, **kwargs)

if cmap is not None:
z_duplicated = np.repeat(z.values, 2)
add_cmap_to_collection(lc, cmap, norm, z_duplicated, cbar_title, plot_colormap, clim)
return lc, pc


def create_vsc_connection_collection(net, vscs=None, bus_geodata=None, bus_dc_geodata=None, infofunc=None,
cmap=None, clim=None, norm=None, z=None,
cbar_title="Transformer Loading", picker=False, **kwargs):
"""
Creates a matplotlib line collection of pandapower VSCs.
Input:
**net** (pandapowerNet) - The pandapower network
OPTIONAL:
**vscs** (list, None) - The VSC indices for which the collections are created.
If None, all VSCs in the network are considered.
**bus_geodata** (DataFrame, None) - coordinates of AC buses to use for plotting
If None, net["bus_geodata"] is used
**bus_dc_geodata** (DataFrame, None) - coordinates of DC buses to use for plotting
If None, net["bus_dc_geodata"] is used
**infofunc** (function, None) - infofunction for the patch element
**cmap** - colormap for the patch colors
**clim** (tuple of floats, None) - setting the norm limits for image scaling
**norm** (matplotlib norm object, None) - matplotlib norm object
**z** (array, None) - array of values for colormap. Used in case of given
cmap. If None net.res_vsc.p_mw is used.
**cbar_title** (str, "VSC active power [MW]") - colormap bar title in case of given cmap
**picker** (bool, False) - picker argument passed to the line collection
**kwargs - keyword arguments are passed to the patch function
OUTPUT:
**lc** - line collection
"""
if not MATPLOTLIB_INSTALLED:
soft_dependency_error(str(sys._getframe().f_code.co_name)+"()", "matplotlib")

vscs = get_index_array(vscs, net.vsc.index)

if bus_geodata is None:
bus_geodata = net["bus_geodata"]

if bus_dc_geodata is None:
bus_dc_geodata = net["bus_dc_geodata"]

in_geodata = (net.vsc.bus.loc[vscs].isin(bus_geodata.index) &
net.vsc.bus_dc.loc[vscs].isin(bus_dc_geodata.index))
vscs = vscs[in_geodata]
vsc_table = net.vsc.loc[vscs]

ac_geo = list(zip(bus_geodata.loc[vsc_table["bus"], "x"].values,
bus_geodata.loc[vsc_table["bus_dc"], "y"].values))
dc_geo = list(zip(bus_dc_geodata.loc[vsc_table["bus_dc"], "x"].values,
bus_dc_geodata.loc[vsc_table["bus_dc"], "y"].values))
vg = list(zip(ac_geo, dc_geo))

info = [infofunc(v) for v in vscs] if infofunc is not None else []

lc = _create_line2d_collection(vg, vscs, info, picker=picker, **kwargs)

if cmap is not None:
if z is None:
z = net.res_vsc.p_mw.loc[vscs]
add_cmap_to_collection(lc, cmap, norm, z, cbar_title, True, clim)

return lc


def create_busbar_collection(net, buses=None, infofunc=None, cmap=None, norm=None, picker=False,
z=None, cbar_title="Bus Voltage [p.u.]", clim=None, **kwargs):
"""
Expand Down
46 changes: 46 additions & 0 deletions pandapower/plotting/patch_makers.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,3 +407,49 @@ def trafo_patches(coords, size, **kwargs):
lines.append([p1, lp1])
lines.append([p2, lp2])
return lines, circles, {"patch_edgecolor", "patch_facecolor"}


def vsc_patches(coords, size, **kwargs):
"""
Creates a list of patches and line coordinates representing VSCs each connecting an AC and a DC
node.
:param coords: list of connecting node coordinates (usually should be \
`[((x11, y11), (x12, y12)), ((x21, y21), (x22, y22)), ...]`)
:type coords: (N, (2, 2)) shaped iterable
:param size: size of the VSC patches
:type size: float
:param kwargs: additional keyword arguments (might contain parameters "patch_edgecolor" and\
"patch_facecolor")
:type kwargs:
:return: Return values are: \
- lines (list) - list of coordinates for lines connecting nodes and VSC patches\
- squares (list of Rectangle) - list containing the VSC patches (squares)
"""
if not MATPLOTLIB_INSTALLED:
soft_dependency_error(str(sys._getframe().f_code.co_name)+"()", "matplotlib")
edgecolor = kwargs.get("patch_edgecolor", "w")
facecolor = kwargs.get("patch_facecolor", "w")
edgecolors = get_color_list(edgecolor, len(coords))
facecolors = get_color_list(facecolor, len(coords))
linewidths = kwargs.get("linewidths", 2.)
linewidths = get_linewidth_list(linewidths, len(coords), name_entries="vscs")
squares, lines = list(), list()
for i, (p1, p2) in enumerate(coords):
p1 = np.array(p1)
p2 = np.array(p2)
if np.all(p1 == p2):
continue
d = np.sqrt(np.sum((p1 - p2) ** 2)) # distance
if size is None:
size_this = np.sqrt(d) / 3
else:
size_this = size
xy1 = p1 - np.array([size_this, size_this])
xy2 = p2 - np.array([size_this, size_this])
squares.append(Rectangle(xy1, size_this * 2, size_this * 2, fc=facecolors[i], ec=edgecolors[i],
lw=linewidths[i], hatch="+++"))
squares.append(Rectangle(xy2, size_this * 2, size_this * 2, fc=facecolors[i], ec=edgecolors[i],
lw=linewidths[i], hatch="---"))
lines.append([p1, p2])
return lines, squares, {"patch_edgecolor", "patch_facecolor"}
10 changes: 7 additions & 3 deletions pandapower/plotting/plotting_toolbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def get_index_array(indices, net_table_indices):


def coords_from_node_geodata(element_indices, from_nodes, to_nodes, node_geodata, table_name,
node_name="Bus", ignore_zero_length=True):
node_name="Bus", ignore_zero_length=True, node_geodata_to=None):
"""
Auxiliary function to get the node coordinates for a number of branches with respective from
and to nodes. The branch elements for which there is no geodata available are not included in
Expand All @@ -145,18 +145,22 @@ def coords_from_node_geodata(element_indices, from_nodes, to_nodes, node_geodata
:param ignore_zero_length: States if branches should be left out, if their length is zero, i.e.\
from_node_coords = to_node_coords
:type ignore_zero_length: bool, default True
:param node_geodata_to: Dataframe containing x and y coordinates of the "to" nodes (optional, default node_geodata)
:type node_geodata_to: pd.DataFrame
:return: Return values are:\
- coords (list) - list of branch coordinates of shape (N, (2, 2))\
- elements_with_geo (set) - the indices of branch elements for which coordinates wer found\
in the node geodata table
"""
if node_geodata_to is None:
node_geodata_to = node_geodata
have_geo = np.isin(from_nodes, node_geodata.index.values) \
& np.isin(to_nodes, node_geodata.index.values)
& np.isin(to_nodes, node_geodata_to.index.values)
elements_with_geo = np.array(element_indices)[have_geo]
fb_with_geo, tb_with_geo = from_nodes[have_geo], to_nodes[have_geo]
coords = [[(x_from, y_from), (x_to, y_to)] for x_from, y_from, x_to, y_to
in np.concatenate([node_geodata.loc[fb_with_geo, ["x", "y"]].values,
node_geodata.loc[tb_with_geo, ["x", "y"]].values], axis=1)
node_geodata_to.loc[tb_with_geo, ["x", "y"]].values], axis=1)
if not ignore_zero_length or not (x_from == x_to and y_from == y_to)]
elements_without_geo = set(element_indices) - set(elements_with_geo)
if len(elements_without_geo) > 0:
Expand Down
10 changes: 5 additions & 5 deletions pandapower/plotting/simple_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
from pandapower.plotting.plotting_toolbox import get_collection_sizes
from pandapower.plotting.collections import create_bus_collection, create_line_collection, \
create_trafo_collection, create_trafo3w_collection, \
create_line_switch_collection, draw_collections, create_bus_bus_switch_collection, create_ext_grid_collection, create_sgen_collection, \
create_gen_collection, create_load_collection, create_dcline_collection
create_line_switch_collection, draw_collections, create_bus_bus_switch_collection, create_ext_grid_collection, \
create_sgen_collection, \
create_gen_collection, create_load_collection, create_dcline_collection, create_vsc_collection
from pandapower.plotting.generic_geodata import create_generic_coordinates

try:
Expand All @@ -30,7 +31,7 @@ def simple_plot(net, respect_switches=False, line_width=1.0, bus_size=1.0, ext_g
switch_size=2.0, switch_distance=1.0, plot_line_switches=False, scale_size=True,
bus_color='b', line_color='grey', dcline_color='c', trafo_color='k',
ext_grid_color='y', switch_color='k', library='igraph', show_plot=True, ax=None,
bus_dc_size=1.0, bus_dc_color="m", line_dc_color="c", vsc_size=2.0, vsc_color="c"):
bus_dc_size=1.0, bus_dc_color="m", line_dc_color="c", vsc_size=4.0, vsc_color="orange"):
"""
Plots a pandapower network as simple as possible. If no geodata is available, artificial
geodata is generated. For advanced plotting see the tutorial
Expand Down Expand Up @@ -149,8 +150,7 @@ def simple_plot(net, respect_switches=False, line_width=1.0, bus_size=1.0, ext_g
collections.append(bc_dc)
# create VSC collection
if len(net.vsc) > 0:
vsc_ac = create_bus_collection(net, net.vsc.bus.values, size=vsc_size, color=vsc_color,
patch_type="rect", zorder=10)
vsc_ac = create_vsc_collection(net, net.vsc.index, size=vsc_size, color=vsc_color, zorder=12)
collections.append(vsc_ac)
# create line_dc collections
if len(net.line_dc) > 0:
Expand Down

0 comments on commit 6e61359

Please sign in to comment.