From f93755b6cc13532cc332f562e5682a536e4e0e19 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Wed, 13 Nov 2019 17:51:32 +0100 Subject: [PATCH 01/38] some pep in collections and an additional function for node collections --- pandapower/plotting/collections.py | 215 +++++++++++++++++++---------- 1 file changed, 140 insertions(+), 75 deletions(-) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index ca95a7375..e58537bbd 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -9,7 +9,7 @@ import matplotlib.pyplot as plt import numpy as np -from matplotlib.collections import LineCollection, PatchCollection, Collection, CircleCollection +from matplotlib.collections import LineCollection, PatchCollection, Collection from matplotlib.font_manager import FontProperties from matplotlib.patches import Circle, Ellipse, Rectangle, RegularPolygon, Arc, PathPatch from matplotlib.textpath import TextPath @@ -38,8 +38,7 @@ class CustomTextPath(TextPath): implemented for TextPath. """ - def __init__(self, xy, s, size=None, prop=None, _interpolation_steps=1, usetex=False, *kl, - **kwargs): + def __init__(self, xy, s, size=None, prop=None, _interpolation_steps=1, usetex=False): """ Create a path from the text. No support for TeX yet. Note that it simply is a path, not an artist. You need to use the @@ -68,7 +67,6 @@ def __deepcopy__(self, memo=None): _interpolation_steps=self._interpolation_steps, usetex=self.usetex) - def create_annotation_collection(texts, coords, size, prop=None, **kwargs): """ Creates PatchCollection of Texts shown at the given coordinates @@ -94,6 +92,61 @@ def create_annotation_collection(texts, coords, size, prop=None, **kwargs): return PatchCollection(tp, **kwargs) +def add_cmap_to_collection(collection, cmap, norm, z, cbar_title): + collection.set_cmap(cmap) + collection.set_norm(norm) + collection.set_array(np.ma.masked_invalid(z)) + collection.has_colormap = True + collection.cbar_title = cbar_title + return collection + + +def create_node_collection(nodes, coords, size=5, patch_type="circle", colors=None, picker=False, + infos=None, **kwargs): + if len(nodes) == 0: + return None + + assert len(nodes) == len(coords) + + infos = list(infos) if infos is not None else [] + colors = [None] * len(nodes) if colors is None else colors + + # RegularPolygon has no param width/height, everything else might use defaults + if not patch_type.startswith("poly"): + if 'height' not in kwargs and 'width' not in kwargs: + kwargs['height'] = kwargs['width'] = 2 * size + + def figmaker(x, y, color): + if color is not None: + kwargs["color"] = color + if patch_type == 'ellipse' or patch_type == 'circle': # circles are just ellipses + angle = kwargs.get('angle', 0) + fig = Ellipse((x, y), angle=angle, **kwargs) + elif patch_type == "rect": + fig = Rectangle((x - kwargs['width'] / 2, y - kwargs['height'] / 2), **kwargs) + elif patch_type.startswith("poly"): + edges = int(patch_type[4:]) + fig = RegularPolygon([x, y], numVertices=edges, radius=size, **kwargs) + else: + logger.error("Wrong patchtype. Please choose a correct patch type.") + raise ValueError("Wrong patchtype") + return fig + + patches = [figmaker(x, y, col) for (x, y), col in zip(coords, colors) if x != np.nan] + pc = PatchCollection(patches, match_original=True, picker=picker) + pc.node_indices = np.array(nodes) + + pc.patch_type = patch_type + pc.size = size + if 'orientation' in kwargs: + pc.orientation = kwargs['orientation'] + if "zorder" in kwargs: + pc.set_zorder(kwargs["zorder"]) + pc.info = infos + + return pc + + def create_bus_collection(net, buses=None, size=5, patch_type="circle", colors=None, z=None, cmap=None, norm=None, infofunc=None, picker=False, bus_geodata=None, cbar_title="Bus Voltage [pu]", **kwargs): @@ -160,7 +213,7 @@ def figmaker(x, y, i): angle = kwargs['angle'] if 'angle' in kwargs else 0 fig = Ellipse((x, y), angle=angle, **kwargs) elif patch_type == "rect": - fig = Rectangle([x - kwargs['width'] / 2, y - kwargs['height'] / 2], **kwargs) + fig = Rectangle((x - kwargs['width'] / 2, y - kwargs['height'] / 2), **kwargs) elif patch_type.startswith("poly"): edges = int(patch_type[4:]) fig = RegularPolygon([x, y], numVertices=edges, radius=size, **kwargs) @@ -239,9 +292,11 @@ def create_line_collection(net, lines=None, line_geodata=None, bus_geodata=None, OUTPUT: **lc** - line collection """ + global linetab if use_bus_geodata is False and net.line_geodata.empty: # if bus geodata is available, but no line geodata - logger.warning("use_bus_geodata is automatically set to True, since net.line_geodata is empty.") + logger.warning("use_bus_geodata is automatically set to True, since net.line_geodata is " + "empty.") use_bus_geodata = True if use_bus_geodata: @@ -476,7 +531,7 @@ def create_trafo_collection(net, trafos=None, picker=False, size=None, infos.append(infofunc(i)) if len(circles) == 0: return None, None - lc = LineCollection((lines), color=color, picker=picker, linewidths=linewidths, **kwargs) + lc = LineCollection(lines, color=color, picker=picker, linewidths=linewidths, **kwargs) lc.info = infos pc = PatchCollection(circles, match_original=True, picker=picker, linewidth=linewidths, **kwargs) @@ -493,6 +548,7 @@ def create_trafo_collection(net, trafos=None, picker=False, size=None, return lc, pc +# noinspection PyArgumentList def create_trafo3w_collection(net, trafo3ws=None, picker=False, infofunc=None, cmap=None, norm=None, z=None, clim=None, cbar_title="3W-Transformer Loading", @@ -556,16 +612,16 @@ def create_trafo3w_collection(net, trafo3ws=None, picker=False, infofunc=None, e = (center - p) * (1 - 5 * r / 3 / d).reshape(3, 1) + p # save circle and line collection data ec = color if cmap is None else cmap(norm(z.at[idx])) - for i in range(3): - circles.append(Circle(m[i], r, fc=(1, 0, 0, 0), ec=ec)) - lines.append([p[i], e[i]]) + for j in range(3): + circles.append(Circle(m[j], r, fc=(1, 0, 0, 0), ec=ec)) + lines.append([p[j], e[j]]) if infofunc is not None: infos.append(infofunc(i)) infos.append(infofunc(i)) if len(circles) == 0: return None, None - lc = LineCollection((lines), color=color, picker=picker, linewidths=linewidth, **kwargs) + lc = LineCollection(lines, color=color, picker=picker, linewidths=linewidth, **kwargs) lc.info = infos pc = PatchCollection(circles, match_original=True, picker=picker, linewidth=linewidth, **kwargs) pc.info = infos @@ -581,8 +637,8 @@ def create_trafo3w_collection(net, trafo3ws=None, picker=False, infofunc=None, return lc, pc -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): +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): """ Creates a matplotlib patch collection of pandapower buses plotted as busbars @@ -628,9 +684,9 @@ def create_busbar_collection(net, buses=None, infofunc=None, cmap=None, norm=Non logger.warning("z is None and no net is provided") # the busbar is just a line collection with coords from net.bus_geodata - return create_line_collection(net, lines=buses, line_geodata=net.bus_geodata, bus_geodata=None, norm=norm, - cmap=cmap, infofunc=infofunc, picker=picker, z=z, cbar_title=cbar_title, clim=clim, - **kwargs) + return create_line_collection(net, lines=buses, line_geodata=net.bus_geodata, bus_geodata=None, + norm=norm, cmap=cmap, infofunc=infofunc, picker=picker, z=z, + cbar_title=cbar_title, clim=clim, **kwargs) def create_load_collection(net, loads=None, size=1., infofunc=None, orientation=np.pi, **kwargs): @@ -816,7 +872,7 @@ def create_ext_grid_collection(net, size=1., infofunc=None, orientation=0, picke for ext_grid_idx, bus_idx in zip(ext_grids, ext_grid_buses): p1 = net.bus_geodata[["x", "y"]].loc[bus_idx].values p2 = p1 + _rotate_dim2(np.array([0, size]), orientation) - polys.append(Rectangle([p2[0] - size / 2, p2[1] - size / 2], size, size)) + polys.append(Rectangle((p2[0] - size / 2, p2[1] - size / 2), size, size)) lines.append((p1, p2 - _rotate_dim2(np.array([0, size / 2]), orientation))) if infofunc is not None: infos.append(infofunc(ext_grid_idx)) @@ -843,17 +899,20 @@ def onBusbar(net, bus, line_coords): in the middle represent the bending points of the line OUTPUT: - **intersection** (tupel, shape= (2L,))- Intersection point of the line with the given bus. Can be used for switch position + **intersection** (tupel, shape= (2L,))- Intersection point of the line with the given bus. \ + Can be used for switch position """ - # If the line has no Intersection line will be returned and the bus coordinates can be used to calculate the switch position + # If the line has no Intersection line will be returned and the bus coordinates can be used to + # calculate the switch position intersection = None bus_coords = net.bus_geodata.loc[bus, "coords"] # Checking if bus has "coords" - if it is a busbar if bus_coords is not None and bus_coords is not np.NaN and line_coords is not None: for i in range(len(bus_coords) - 1): try: - # Calculating slope of busbar-line. If the busbar-line is vertical ZeroDivisionError occures + # Calculating slope of busbar-line. If the busbar-line is vertical ZeroDivisionError + # occurs m = (bus_coords[i + 1][1] - bus_coords[i][1]) / \ (bus_coords[i + 1][0] - bus_coords[i][0]) # Clculating the off-set of the busbar-line @@ -861,15 +920,15 @@ def onBusbar(net, bus, line_coords): # Checking if the first end of the line is on the busbar-line if 0 == m * line_coords[0][0] + b - line_coords[0][1]: # Checking if the end of the line is in the Range of the busbar-line - if (line_coords[0][0] <= bus_coords[i][0] and line_coords[0][0] >= bus_coords[i + 1][0]) \ - or (line_coords[0][0] >= bus_coords[i][0] and line_coords[0][0] <= bus_coords[i + 1][0]): + if bus_coords[i + 1][0] <= line_coords[0][0] <= bus_coords[i][0]\ + or bus_coords[i][0] <= line_coords[0][0] <= bus_coords[i + 1][0]: # Intersection found. Breaking for-loop intersection = line_coords[0] break # Checking if the second end of the line is on the busbar-line elif 0 == m * line_coords[-1][0] + b - line_coords[-1][1]: - if (line_coords[-1][0] <= bus_coords[i][0] and line_coords[-1][0] >= bus_coords[i + 1][0]) \ - or (line_coords[-1][0] >= bus_coords[i][0] and line_coords[-1][0] <= bus_coords[i + 1][0]): + if bus_coords[i][0] >= line_coords[-1][0] >= bus_coords[i + 1][0] \ + or bus_coords[i][0] <= line_coords[-1][0] <= bus_coords[i + 1][0]: # Intersection found. Breaking for-loop intersection = line_coords[-1] break @@ -878,27 +937,26 @@ def onBusbar(net, bus, line_coords): # Checking if the first line-end is at the same position if bus_coords[i][0] == line_coords[0][0]: # Checking if the first line-end is in the Range of the busbar-line - if (line_coords[0][1] <= bus_coords[i][1] and line_coords[0][1] >= bus_coords[i + 1][1]) \ - or (line_coords[0][1] >= bus_coords[i][1] and line_coords[0][1] <= bus_coords[i + 1][1]): + if bus_coords[i][1] >= line_coords[0][1] >= bus_coords[i + 1][1] \ + or bus_coords[i][1] <= line_coords[0][1] <= bus_coords[i + 1][1]: # Intersection found. Breaking for-loop intersection = line_coords[0] break # Checking if the second line-end is at the same position elif bus_coords[i][0] == line_coords[-1][0]: - if (line_coords[-1][1] <= bus_coords[i][1] and line_coords[-1][1] >= bus_coords[i + 1][1]) \ - or (line_coords[-1][1] >= bus_coords[i][1] and line_coords[-1][1] <= bus_coords[i + 1][1]): + if bus_coords[i][1] >= line_coords[-1][1] >= bus_coords[i + 1][1] \ + or bus_coords[i][1] <= line_coords[-1][1] <= bus_coords[i + 1][1]: # Intersection found. Breaking for-loop intersection = line_coords[-1] break # If the bus has no "coords" it mus be a normal bus elif bus_coords is np.NaN: - busGeo = (net["bus_geodata"].loc[bus, "x"], - net["bus_geodata"].loc[bus, "y"]) + bus_geo = (net["bus_geodata"].loc[bus, "x"], net["bus_geodata"].loc[bus, "y"]) # Checking if the first end of the line is on the bus - if busGeo == line_coords[0]: + if bus_geo == line_coords[0]: intersection = line_coords[0] # Checking if the second end of the line is on the bus - elif busGeo == line_coords[-1]: + elif bus_geo == line_coords[-1]: intersection = line_coords[-1] return intersection @@ -936,8 +994,8 @@ def create_line_switch_collection(net, size=1, distance_to_bus=3, use_line_geoda fb = line.from_bus tb = line.to_bus - line_buses = set([fb, tb]) - target_bus = list(line_buses - set([sb]))[0] + line_buses = {fb, tb} + target_bus = list(line_buses - {sb})[0] if sb not in net.bus_geodata.index or target_bus not in net.bus_geodata.index: logger.warning("Bus coordinates for switch %s not found, skipped switch!" % switch) @@ -983,7 +1041,8 @@ def create_line_switch_collection(net, size=1, distance_to_bus=3, use_line_geoda col = color if net.switch.closed.loc[switch] else "white" # create switch patch (switch size is respected to center the switch on the line) - patch = Rectangle((pos_sw[0] - size / 2, pos_sw[1] - size / 2), size, size, facecolor=col, edgecolor=color) + patch = Rectangle((pos_sw[0] - size / 2, pos_sw[1] - size / 2), size, size, facecolor=col, + edgecolor=color) # apply rotation patch.set_transform(rotation) @@ -993,11 +1052,12 @@ def create_line_switch_collection(net, size=1, distance_to_bus=3, use_line_geoda return switches -def create_bus_bus_switch_collection(net, size=1., helper_line_style=':', helper_line_size=1., helper_line_color="gray", - **kwargs): +def create_bus_bus_switch_collection(net, size=1., helper_line_style=':', helper_line_size=1., + helper_line_color="gray", **kwargs): """ - Creates a matplotlib patch collection of pandapower bus-bus switches. Switches are plotted in the center between two buses with a "helper" - line (dashed and thin) being drawn between the buses as well. + Creates a matplotlib patch collection of pandapower bus-bus switches. Switches are plotted in + the center between two buses with a "helper" line (dashed and thin) being drawn between the + buses as well. INPUT: **net** (pandapowerNet) - The pandapower network @@ -1006,11 +1066,14 @@ def create_bus_bus_switch_collection(net, size=1., helper_line_style=':', helper **size** (float, 1.0) - Size of the switch patches - **helper_line_style** (string, ':') - Line style of the "helper" line being plotted between two buses connected by a bus-bus switch + **helper_line_style** (string, ':') - Line style of the "helper" line being plotted between\ + two buses connected by a bus-bus switch - **helper_line_size** (float, 1.0) - Line width of the "helper" line being plotted between two buses connected by a bus-bus switch + **helper_line_size** (float, 1.0) - Line width of the "helper" line being plotted between \ + two buses connected by a bus-bus switch - **helper_line_color** (string, "gray") - Line color of the "helper" line being plotted between two buses connected by a bus-bus switch + **helper_line_color** (string, "gray") - Line color of the "helper" line being plotted \ + between two buses connected by a bus-bus switch **kwargs - Key word arguments are passed to the patch function @@ -1039,7 +1102,8 @@ def create_bus_bus_switch_collection(net, size=1., helper_line_style=':', helper # color switch by state col = color if net.switch.closed.loc[switch] else "white" # create switch patch (switch size is respected to center the switch on the line) - patch = Rectangle((pos_sw[0] - size / 2, pos_sw[1] - size / 2), size, size, facecolor=col, edgecolor=color) + patch = Rectangle((pos_sw[0] - size / 2, pos_sw[1] - size / 2), size, size, facecolor=col, + edgecolor=color) # apply rotation patch.set_transform(rotation) # add to collection lists @@ -1047,8 +1111,8 @@ def create_bus_bus_switch_collection(net, size=1., helper_line_style=':', helper line_patches.append([pos_sb.tolist(), pos_tb.tolist()]) # create collections and return switches = PatchCollection(switch_patches, match_original=True, **kwargs) - helper_lines = LineCollection(line_patches, linestyles=helper_line_style, linewidths=helper_line_size, - colors=helper_line_color) + helper_lines = LineCollection(line_patches, linestyles=helper_line_style, + linewidths=helper_line_size, colors=helper_line_color) return switches, helper_lines @@ -1127,33 +1191,34 @@ def add_collections_to_axes(ax, collections, plot_colorbars=True, copy_collectio if __name__ == "__main__": - if 0: - import pandapower as pp - - net = pp.create_empty_network() - b1 = pp.create_bus(net, 10, geodata=(5, 10)) - b2 = pp.create_bus(net, 0.4, geodata=(5, 15)) - b3 = pp.create_bus(net, 0.4, geodata=(0, 22)) - b4 = pp.create_bus(net, 0.4, geodata=(8, 20)) - pp.create_gen(net, b1, p_mw=0.1) - pp.create_load(net, b3, p_mw=0.1) - pp.create_ext_grid(net, b4) - - pp.create_line(net, b2, b3, 2.0, std_type="NAYY 4x50 SE") - pp.create_line(net, b2, b4, 2.0, std_type="NAYY 4x50 SE") - pp.create_transformer(net, b1, b2, std_type="0.63 MVA 10/0.4 kV") - pp.create_transformer(net, b3, b4, std_type="0.63 MVA 10/0.4 kV") - - bc = create_bus_collection(net, size=0.2, color="k") - lc = create_line_collection(net, use_line_geodata=False, color="k", linewidth=3.) - lt, bt = create_trafo_collection(net, size=2, linewidth=3.) - load1, load2 = create_load_collection(net, linewidth=2., - infofunc=lambda x: ("load", x)) - gen1, gen2 = create_gen_collection(net, linewidth=2., - infofunc=lambda x: ("gen", x)) - eg1, eg2 = create_ext_grid_collection(net, size=2., - infofunc=lambda x: ("ext_grid", x)) - - draw_collections([bc, lc, load1, load2, gen1, gen2, lt, bt, eg1, eg2]) - else: - pass + # if 0: + # import pandapower as pp + # + # ntw = pp.create_empty_network() + # b1 = pp.create_bus(ntw, 10, geodata=(5, 10)) + # b2 = pp.create_bus(ntw, 0.4, geodata=(5, 15)) + # b3 = pp.create_bus(ntw, 0.4, geodata=(0, 22)) + # b4 = pp.create_bus(ntw, 0.4, geodata=(8, 20)) + # pp.create_gen(ntw, b1, p_mw=0.1) + # pp.create_load(ntw, b3, p_mw=0.1) + # pp.create_ext_grid(ntw, b4) + # + # pp.create_line(ntw, b2, b3, 2.0, std_type="NAYY 4x50 SE") + # pp.create_line(ntw, b2, b4, 2.0, std_type="NAYY 4x50 SE") + # pp.create_transformer(ntw, b1, b2, std_type="0.63 MVA 10/0.4 kV") + # pp.create_transformer(ntw, b3, b4, std_type="0.63 MVA 10/0.4 kV") + # + # bus_col = create_bus_collection(ntw, size=0.2, color="k") + # line_col = create_line_collection(ntw, use_line_geodata=False, color="k", linewidth=3.) + # lt, bt = create_trafo_collection(ntw, size=2, linewidth=3.) + # load_col1, load_col2 = create_load_collection(ntw, linewidth=2., + # infofunc=lambda x: ("load", x)) + # gen1, gen2 = create_gen_collection(ntw, linewidth=2., + # infofunc=lambda x: ("gen", x)) + # eg1, eg2 = create_ext_grid_collection(ntw, size=2., + # infofunc=lambda x: ("ext_grid", x)) + # + # draw_collections([bus_col, line_col, load_col1, load_col2, gen1, gen2, lt, bt, eg1, eg2]) + # else: + # pass + pass From cb54ecb9977ba7c6ef2ebc8f5dab1fc3a5b6dee7 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Thu, 14 Nov 2019 14:40:04 +0100 Subject: [PATCH 02/38] implemented generic node element collection and patch maker functions --- pandapower/plotting/collections.py | 345 ++++++++++++---------------- pandapower/plotting/geo.py | 9 +- pandapower/plotting/patch_makers.py | 156 +++++++++++++ 3 files changed, 310 insertions(+), 200 deletions(-) create mode 100644 pandapower/plotting/patch_makers.py diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index e58537bbd..9172f80cb 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -11,10 +11,12 @@ import numpy as np from matplotlib.collections import LineCollection, PatchCollection, Collection from matplotlib.font_manager import FontProperties -from matplotlib.patches import Circle, Ellipse, Rectangle, RegularPolygon, Arc, PathPatch +from matplotlib.patches import Circle, Rectangle, PathPatch from matplotlib.textpath import TextPath from matplotlib.transforms import Affine2D from pandas import isnull +from pandapower.plotting.patch_makers import load_patches, _rotate_dim2, node_patches, gen_patches,\ + sgen_patches, ext_grid_patches try: import pplog as logging @@ -24,14 +26,6 @@ logger = logging.getLogger(__name__) -def _rotate_dim2(arr, ang): - """ - :param arr: array with 2 dimensions - :param ang: angle [rad] - """ - return np.dot(np.array([[np.cos(ang), np.sin(ang)], [-np.sin(ang), np.cos(ang)]]), arr) - - class CustomTextPath(TextPath): """ Create a path from the text. This class provides functionality for deepcopy, which is not @@ -92,47 +86,24 @@ def create_annotation_collection(texts, coords, size, prop=None, **kwargs): return PatchCollection(tp, **kwargs) -def add_cmap_to_collection(collection, cmap, norm, z, cbar_title): +def add_cmap_to_collection(collection, cmap, norm, z, cbar_title, clim=None): collection.set_cmap(cmap) collection.set_norm(norm) collection.set_array(np.ma.masked_invalid(z)) collection.has_colormap = True collection.cbar_title = cbar_title + if clim is not None: + collection.set_clim(clim) return collection -def create_node_collection(nodes, coords, size=5, patch_type="circle", colors=None, picker=False, +def create_node_collection(nodes, coords, size=5, patch_type="circle", color=None, picker=False, infos=None, **kwargs): - if len(nodes) == 0: + if len(coords) == 0: return None - assert len(nodes) == len(coords) - infos = list(infos) if infos is not None else [] - colors = [None] * len(nodes) if colors is None else colors - - # RegularPolygon has no param width/height, everything else might use defaults - if not patch_type.startswith("poly"): - if 'height' not in kwargs and 'width' not in kwargs: - kwargs['height'] = kwargs['width'] = 2 * size - - def figmaker(x, y, color): - if color is not None: - kwargs["color"] = color - if patch_type == 'ellipse' or patch_type == 'circle': # circles are just ellipses - angle = kwargs.get('angle', 0) - fig = Ellipse((x, y), angle=angle, **kwargs) - elif patch_type == "rect": - fig = Rectangle((x - kwargs['width'] / 2, y - kwargs['height'] / 2), **kwargs) - elif patch_type.startswith("poly"): - edges = int(patch_type[4:]) - fig = RegularPolygon([x, y], numVertices=edges, radius=size, **kwargs) - else: - logger.error("Wrong patchtype. Please choose a correct patch type.") - raise ValueError("Wrong patchtype") - return fig - - patches = [figmaker(x, y, col) for (x, y), col in zip(coords, colors) if x != np.nan] + patches = node_patches(coords, size, patch_type, color, **kwargs) pc = PatchCollection(patches, match_original=True, picker=picker) pc.node_indices = np.array(nodes) @@ -143,13 +114,76 @@ def figmaker(x, y, color): if "zorder" in kwargs: pc.set_zorder(kwargs["zorder"]) pc.info = infos + pc.patch_type = patch_type + pc.size = size + if "zorder" in kwargs: + pc.set_zorder(kwargs["zorder"]) + if 'orientation' in kwargs: + pc.orientation = kwargs['orientation'] return pc -def create_bus_collection(net, buses=None, size=5, patch_type="circle", colors=None, - z=None, cmap=None, norm=None, infofunc=None, picker=False, - bus_geodata=None, cbar_title="Bus Voltage [pu]", **kwargs): +def create_line2d_collection(coords, indices, infos=None, picker=None, **kwargs): + # This would be done anyways by matplotlib - doing it explicitly makes it a) clear and + # b) prevents unexpected behavior when observing colors being "none" + lc = LineCollection(coords, picker=picker, **kwargs) + lc.indices = np.array(indices) + lc.info = infos if infos is not None else list() + return lc + + +def create_node_element_collection(node_coords, patch_maker, size=1., infos=None, orientation=np.pi, + picker=False, patch_facecolor="w", patch_edgecolor="k", + line_color="k", **kwargs): + """ + Creates matplotlib collections of node elements. All node element collections usually consist of + one patch collection representing the element itself and a small line collection that connects + the element to the respective node. + + Input: + **node_coords** (iterable) - the coordinates (x, y) of the nodes with shape (N, 2) + + **patch_maker** (function) - a function to generate the patches of which the collections \ + consist (cf. the patch_maker module) + + OPTIONAL: + **size** (float, 1) - patch size + + **infos** (iterable, None) - additional infos to add for each element + + **orientation** (float, np.pi) - orientation of load collection. pi is directed downwards, + increasing values lead to clockwise direction changes. + + **patch_facecolor** (matplotlib color, "w") - color of the patch face (content) + + **patch_edgecolor** (matplotlib color, "k") - color of the patch edges + + **line_color** (matplotlib color, "k") - color of the connecting lines + + **kwargs - key word arguments are passed to the patch function + + OUTPUT: + **patch_coll** - patch collection representing the element + + **line_coll** - connecting line collection + """ + angles = orientation if hasattr(orientation, '__iter__') else [orientation] * len(node_coords) + assert len(node_coords) == len(angles), \ + "The length of coordinates does not match the length of the orientation angles!" + infos = [] if infos is None else infos + lines, polys = patch_maker(node_coords, size, orientation, **kwargs) + patch_coll = PatchCollection(polys, facecolor=patch_facecolor, edgecolor=patch_edgecolor, + picker=picker, **kwargs) + line_coll = LineCollection(lines, color=line_color, picker=picker, **kwargs) + patch_coll.info = infos + line_coll.info = infos + return patch_coll, line_coll + + +def create_bus_collection(net, buses=None, size=5, patch_type="circle", colors=None, z=None, + cmap=None, norm=None, infofunc=None, picker=False, bus_geodata=None, + cbar_title="Bus Voltage [pu]", **kwargs): """ Creates a matplotlib patch collection of pandapower buses. @@ -197,54 +231,14 @@ def create_bus_collection(net, buses=None, size=5, patch_type="circle", colors=N if bus_geodata is None: bus_geodata = net["bus_geodata"] - coords = zip(bus_geodata.loc[buses, "x"].values, bus_geodata.loc[buses, "y"].values) + coords = list(zip(bus_geodata.loc[buses, "x"].values, bus_geodata.loc[buses, "y"].values)) - infos = [] + infos = [infofunc(buses[i]) for i in range(len(buses))] - # RegularPolygon has no param width/height, everything else might use defaults - if not patch_type.startswith("poly"): - if 'height' not in kwargs and 'width' not in kwargs: - kwargs['height'] = kwargs['width'] = 2 * size - - def figmaker(x, y, i): - if colors is not None: - kwargs["color"] = colors[i] - if patch_type == 'ellipse' or patch_type == 'circle': # circles are just ellipses - angle = kwargs['angle'] if 'angle' in kwargs else 0 - fig = Ellipse((x, y), angle=angle, **kwargs) - elif patch_type == "rect": - fig = Rectangle((x - kwargs['width'] / 2, y - kwargs['height'] / 2), **kwargs) - elif patch_type.startswith("poly"): - edges = int(patch_type[4:]) - fig = RegularPolygon([x, y], numVertices=edges, radius=size, **kwargs) - else: - logger.error("Wrong patchtype. Please choose a correct patch type.") - raise ValueError("Wrong patchtype") - if infofunc: - infos.append(infofunc(buses[i])) - return fig - - patches = [figmaker(x, y, i) - for i, (x, y) in enumerate(coords) - if x != np.nan] - pc = PatchCollection(patches, match_original=True, picker=picker) - pc.bus_indices = np.array(buses) - if cmap is not None: - pc.set_cmap(cmap) - pc.set_norm(norm) - if z is None: - z = net.res_bus.vm_pu.loc[buses] - pc.set_array(np.ma.masked_invalid(z)) - pc.has_colormap = True - pc.cbar_title = cbar_title + pc = create_node_collection(buses, coords, size, patch_type, colors, picker, infos, **kwargs) - pc.patch_type = patch_type - pc.size = size - if 'orientation' in kwargs: - pc.orientation = kwargs['orientation'] - if "zorder" in kwargs: - pc.set_zorder(kwargs["zorder"]) - pc.info = infos + if cmap is not None: + add_cmap_to_collection(pc, cmap, norm, z, cbar_title) return pc @@ -689,7 +683,8 @@ def create_busbar_collection(net, buses=None, infofunc=None, cmap=None, norm=Non cbar_title=cbar_title, clim=clim, **kwargs) -def create_load_collection(net, loads=None, size=1., infofunc=None, orientation=np.pi, **kwargs): +def create_load_collection(net, loads=None, size=1., infofunc=None, orientation=np.pi, picker=False, + **kwargs): """ Creates a matplotlib patch collection of pandapower loads. @@ -697,6 +692,8 @@ def create_load_collection(net, loads=None, size=1., infofunc=None, orientation= **net** (pandapowerNet) - The pandapower network OPTIONAL: + **loads** (list of ints, None) - the loads to include in the collection + **size** (float, 1) - patch size **infofunc** (function, None) - infofunction for the patch element @@ -704,37 +701,27 @@ def create_load_collection(net, loads=None, size=1., infofunc=None, orientation= **orientation** (float, np.pi) - orientation of load collection. pi is directed downwards, increasing values lead to clockwise direction changes. + **picker** (bool, False) - picker argument passed to the patch collectionent + **kwargs - key word arguments are passed to the patch function OUTPUT: - **load1** - patch collection + **load_pc** - patch collection - **load2** - patch collection + **load_lc** - line collection """ - load_table = net.load if loads is None else net.load.loc[loads] - lines = [] - polys = [] - infos = [] - off = 2. - ang = orientation if hasattr(orientation, '__iter__') else [orientation] * net.load.shape[0] - color = kwargs.pop("color", "k") - - for i, idx in enumerate(load_table.index): - p1 = net.bus_geodata[["x", "y"]].loc[net.load.at[idx, "bus"]] - p2 = p1 + _rotate_dim2(np.array([0, size * off]), ang[i]) - p3 = p1 + _rotate_dim2(np.array([0, size * (off - 0.5)]), ang[i]) - polys.append(RegularPolygon(p2, numVertices=3, radius=size, orientation=-ang[i])) - lines.append((p1, p3)) - if infofunc is not None: - infos.append(infofunc(i)) - load1 = PatchCollection(polys, facecolor="w", edgecolor=color, **kwargs) - load2 = LineCollection(lines, color=color, **kwargs) - load1.info = infos - load2.info = infos - return load1, load2 - - -def create_gen_collection(net, size=1., infofunc=None, **kwargs): + if loads is None: + loads = net.load.index + infos = [infofunc(i) for i in range(len(loads))] + node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.load.loc[loads, "bus"].values] + load_pc, load_lc = create_node_element_collection( + node_coords, load_patches, size=size, infos=infos, orientation=orientation, + offset=2. * size, picker=picker, **kwargs) + return load_pc, load_lc + + +def create_gen_collection(net, gens=None, size=1., infofunc=None, orientation=np.pi, picker=False, + **kwargs): """ Creates a matplotlib patch collection of pandapower gens. @@ -742,40 +729,36 @@ def create_gen_collection(net, size=1., infofunc=None, **kwargs): **net** (pandapowerNet) - The pandapower network OPTIONAL: + **gens** (list of ints, None) - the generators to include in the collection + **size** (float, 1) - patch size **infofunc** (function, None) - infofunction for the patch element + **orientation** (float or list of floats, np.pi) - orientation of gen collection. pi is\ + directed downwards, increasing values lead to clockwise direction changes. + + **picker** (bool, False) - picker argument passed to the patch collectionent + **kwargs - key word arguments are passed to the patch function OUTPUT: - **gen1** - patch collection + **gen_pc** - patch collection - **gen2** - patch collection + **gen_lc** - line collection """ - lines = [] - polys = [] - infos = [] - off = 1.7 - for i, idx in enumerate(net.gen.index): - p1 = net.bus_geodata[["x", "y"]].loc[net.gen.at[idx, "bus"]] - p2 = p1 - np.array([0, size * off]) - polys.append(Circle(p2, size)) - polys.append( - Arc(p2 + np.array([-size / 6.2, -size / 2.6]), size / 2, size, theta1=45, theta2=135)) - polys.append( - Arc(p2 + np.array([size / 6.2, size / 2.6]), size / 2, size, theta1=225, theta2=315)) - lines.append((p1, p2 + np.array([0, size]))) - if infofunc is not None: - infos.append(infofunc(i)) - gen1 = PatchCollection(polys, facecolor="w", edgecolor="k", **kwargs) - gen2 = LineCollection(lines, color="k", **kwargs) - gen1.info = infos - gen2.info = infos - return gen1, gen2 - - -def create_sgen_collection(net, sgens=None, size=1., infofunc=None, orientation=np.pi, **kwargs): + if gens is None: + gens = net.gen.index + infos = [infofunc(i) for i in range(len(gens))] + node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.gen.loc[gens, "bus"].values] + gen_pc, gen_lc = create_node_element_collection( + node_coords, gen_patches, size=size, infos=infos, orientation=orientation, + offset=1.7 * size, picker=picker, **kwargs) + return gen_pc, gen_lc + + +def create_sgen_collection(net, sgens=None, size=1., infofunc=None, orientation=np.pi, picker=False, + **kwargs): """ Creates a matplotlib patch collection of pandapower sgen. @@ -783,53 +766,32 @@ def create_sgen_collection(net, sgens=None, size=1., infofunc=None, orientation= **net** (pandapowerNet) - The pandapower network OPTIONAL: + **sgens** (list of ints, None) - the static generators to include in the collection + **size** (float, 1) - patch size - **infofunc** (function, None) - infofunction for the patch element + **infofunc** (function, None) - infofunction for the patch elem - **orientation** (float, np.pi) - orientation of load collection. pi is directed downwards, - increasing values lead to clockwise direction changes. + **picker** (bool, False) - picker argument passed to the patch collectionent + + **orientation** (float, np.pi) - orientation of static generator collection. pi is directed\ + downwards, increasing values lead to clockwise direction changes. **kwargs - key word arguments are passed to the patch function OUTPUT: - **sgen1** - patch collection + **sgen_pc** - patch collection - **sgen2** - patch collection + **sgen_lc** - line collection """ - sgen_table = net.sgen if sgens is None else net.sgen.loc[sgens] - lines = [] - polys = [] - infos = [] - off = 1.7 - r_triangle = size * 0.4 - ang = orientation if hasattr(orientation, '__iter__') else [orientation] * net.sgen.shape[0] - - for i, idx in enumerate(sgen_table.index): - bus_geo = net.bus_geodata[["x", "y"]].loc[net.sgen.at[idx, "bus"]] - mp_circ = bus_geo + _rotate_dim2(np.array([0, size * off]), ang[i]) # mp means midpoint - circ_edge = bus_geo + _rotate_dim2(np.array([0, size * (off - 1)]), ang[i]) - mp_tri1 = mp_circ + _rotate_dim2(np.array([r_triangle, -r_triangle / 4]), ang[i]) - mp_tri2 = mp_circ + _rotate_dim2(np.array([-r_triangle, r_triangle / 4]), ang[i]) - perp_foot1 = mp_tri1 + _rotate_dim2(np.array([0, -r_triangle / 2]), - ang[i]) # dropped perpendicular foot of triangle1 - line_end1 = perp_foot1 + + _rotate_dim2(np.array([-2.5 * r_triangle, 0]), ang[i]) - perp_foot2 = mp_tri2 + _rotate_dim2(np.array([0, r_triangle / 2]), ang[i]) - line_end2 = perp_foot2 + + _rotate_dim2(np.array([2.5 * r_triangle, 0]), ang[i]) - polys.append(Circle(mp_circ, size)) - polys.append(RegularPolygon(mp_tri1, numVertices=3, radius=r_triangle, orientation=-ang[i])) - polys.append(RegularPolygon(mp_tri2, numVertices=3, radius=r_triangle, - orientation=np.pi - ang[i])) - lines.append((bus_geo, circ_edge)) - lines.append((perp_foot1, line_end1)) - lines.append((perp_foot2, line_end2)) - if infofunc is not None: - infos.append(infofunc(i)) - sgen1 = PatchCollection(polys, facecolor="w", edgecolor="k", **kwargs) - sgen2 = LineCollection(lines, color="k", **kwargs) - sgen1.info = infos - sgen2.info = infos - return sgen1, sgen2 + if sgens is None: + sgens = net.sgen.index + infos = [infofunc(i) for i in range(len(sgens))] + node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.sgen.loc[sgens, "bus"].values] + sgen_pc, sgen_lc = create_node_element_collection( + node_coords, sgen_patches, size=size, infos=infos, orientation=orientation, + offset=1.7 * size, r_triangle=size * 0.4, picker=picker, **kwargs) + return sgen_pc, sgen_lc def create_ext_grid_collection(net, size=1., infofunc=None, orientation=0, picker=False, @@ -861,27 +823,20 @@ def create_ext_grid_collection(net, size=1., infofunc=None, orientation=0, picke **ext_grid2** - patch collection """ - lines = [] - polys = [] - infos = [] - color = kwargs.pop("color", "k") - if ext_grid_buses is None: - ext_grid_buses = net.ext_grid.bus.values if ext_grids is None: ext_grids = net.ext_grid.index.values - for ext_grid_idx, bus_idx in zip(ext_grids, ext_grid_buses): - p1 = net.bus_geodata[["x", "y"]].loc[bus_idx].values - p2 = p1 + _rotate_dim2(np.array([0, size]), orientation) - polys.append(Rectangle((p2[0] - size / 2, p2[1] - size / 2), size, size)) - lines.append((p1, p2 - _rotate_dim2(np.array([0, size / 2]), orientation))) - if infofunc is not None: - infos.append(infofunc(ext_grid_idx)) - ext_grid1 = PatchCollection(polys, facecolor=(1, 0, 0, 0), edgecolor=(0, 0, 0, 1), - hatch="XXX", picker=picker, color=color, **kwargs) - ext_grid2 = LineCollection(lines, color=color, picker=picker, **kwargs) - ext_grid1.info = infos - ext_grid2.info = infos - return ext_grid1, ext_grid2 + if ext_grid_buses is None: + ext_grid_buses = net.ext_grid.bus.loc[ext_grids].values + else: + assert len(ext_grids) == len(ext_grid_buses), \ + "Length mismatch between chosen ext_grids and ext_grid_buses." + infos = [infofunc(ext_grid_idx) for ext_grid_idx in ext_grids] + + node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[ext_grid_buses] + ext_grid_pc, ext_grid_lc = create_node_element_collection( + node_coords, ext_grid_patches, size=size, infos=infos, orientation=orientation, + offset=1.7 * size, r_triangle=size * 0.4, picker=picker, hatch='XXX', **kwargs) + return ext_grid_pc, ext_grid_lc def onBusbar(net, bus, line_coords): diff --git a/pandapower/plotting/geo.py b/pandapower/plotting/geo.py index 20a13bfcc..5103b34f1 100644 --- a/pandapower/plotting/geo.py +++ b/pandapower/plotting/geo.py @@ -28,12 +28,11 @@ def convert_gis_to_geodata(net, bus_geodata=True, line_geodata=True): net.line_geodata["coords"] = net.line_geodata.geometry.apply(lambda x: list(x.coords)) -def get_collection_sizes(net, bus_size=1.0, ext_grid_size=1.0, - trafo_size=1.0, load_size=1.0, sgen_size=1.0, - switch_size=2.0, switch_distance=1.0): +def get_collection_sizes(net, bus_size=1.0, ext_grid_size=1.0, trafo_size=1.0, load_size=1.0, + sgen_size=1.0, switch_size=2.0, switch_distance=1.0): """ - Calculates the size for most collection types according to the distance between min and max geocoord so that - the collections fit the plot nicely + Calculates the size for most collection types according to the distance between min and max + geocoord so that the collections fit the plot nicely # Comment: This is implemented because if you would choose a fixed values # (e.g. bus_size = 0.2), the size diff --git a/pandapower/plotting/patch_makers.py b/pandapower/plotting/patch_makers.py new file mode 100644 index 000000000..34ed1dceb --- /dev/null +++ b/pandapower/plotting/patch_makers.py @@ -0,0 +1,156 @@ +from matplotlib.patches import RegularPolygon, Arc, Circle, Rectangle, Ellipse +import numpy as np +try: + import pplog as logging +except ImportError: + import logging + +logger = logging.getLogger(__name__) + + +def _rotate_dim2(arr, ang): + """ + :param arr: array with 2 dimensions + :param ang: angle [rad] + """ + return np.dot(np.array([[np.cos(ang), np.sin(ang)], [-np.sin(ang), np.cos(ang)]]), arr) + + +def load_patches(node_coords, size, angles, **kwargs): + offset = kwargs.pop("offset", 2. * size) + polys, lines = list(), list() + for i, node_geo in enumerate(node_coords): + p2 = node_geo + _rotate_dim2(np.array([0, size * offset]), angles[i]) + p3 = node_geo + _rotate_dim2(np.array([0, size * (offset - 0.5)]), angles[i]) + polys.append(RegularPolygon(p2, numVertices=3, radius=size, orientation=-angles[i])) + lines.append((node_geo, p3)) + return lines, polys + + +def gen_patches(node_coords, size, angles, **kwargs): + polys, lines = list(), list() + offset = kwargs.pop("offset", 2. * size) + for i, node_geo in enumerate(node_coords): + p2 = node_geo + _rotate_dim2(np.array([0, size * offset]), angles[i]) + polys.append(Circle(p2, size)) + polys.append( + Arc(p2 + np.array([-size / 6.2, -size / 2.6]), size / 2, size, theta1=45, theta2=135)) + polys.append( + Arc(p2 + np.array([size / 6.2, size / 2.6]), size / 2, size, theta1=225, theta2=315)) + lines.append((node_geo, p2 + np.array([0, size]))) + return lines, polys + + +def sgen_patches(node_coords, size, angles, **kwargs): + polys, lines = list(), list() + offset = kwargs.pop("offset", 2. * size) + r_triangle = kwargs.pop("r_triangles", size * 0.4) + for i, node_geo in enumerate(node_coords): + mid_circ = node_geo + _rotate_dim2(np.array([0, size * offset]), angles[i]) + circ_edge = node_geo + _rotate_dim2(np.array([0, size * (offset - 1)]), angles[i]) + mid_tri1 = mid_circ + _rotate_dim2(np.array([r_triangle, -r_triangle / 4]), angles[i]) + mid_tri2 = mid_circ + _rotate_dim2(np.array([-r_triangle, r_triangle / 4]), angles[i]) + # dropped perpendicular foot of triangle1 + perp_foot1 = mid_tri1 + _rotate_dim2(np.array([0, -r_triangle / 2]), angles[i]) + line_end1 = perp_foot1 + + _rotate_dim2(np.array([-2.5 * r_triangle, 0]), angles[i]) + perp_foot2 = mid_tri2 + _rotate_dim2(np.array([0, r_triangle / 2]), angles[i]) + line_end2 = perp_foot2 + + _rotate_dim2(np.array([2.5 * r_triangle, 0]), angles[i]) + polys.append(Circle(mid_circ, size)) + polys.append(RegularPolygon(mid_tri1, numVertices=3, radius=r_triangle, + orientation=-angles[i])) + polys.append(RegularPolygon(mid_tri2, numVertices=3, radius=r_triangle, + orientation=np.pi - angles[i])) + lines.append((node_geo, circ_edge)) + lines.append((perp_foot1, line_end1)) + lines.append((perp_foot2, line_end2)) + return lines, polys + + +def ext_grid_patches(node_coords, size, angles, **kwargs): + polys, lines = list(), list() + for i, node_geo in enumerate(node_coords): + p2 = node_geo + _rotate_dim2(np.array([0, size]), angles[i]) + polys.append(Rectangle((p2[0] - size / 2, p2[1] - size / 2), size, size)) + lines.append((node_geo, p2 - _rotate_dim2(np.array([0, size / 2]), angles[i]))) + return lines, polys + + +def get_list(individuals, number_entries, name_ind, name_ent): + if hasattr(individuals, "__iter__"): + if number_entries == len(individuals): + return individuals + elif number_entries > len(individuals): + logger.warning("The number of given %s (%d) is larger than the number of %s (%d) to" + " draw! The %s will be repeated to fit." + % (name_ind, len(individuals), name_ent, number_entries, name_ind)) + return (individuals * (int(number_entries / len(individuals)) + 1))[:number_entries] + else: + logger.warning("The number of given %s (%d) is smaller than the number of %s (%d) to" + " draw! The %s will be capped to fit." + % (name_ind, len(individuals), name_ent, number_entries, name_ind)) + return individuals[:number_entries] + return [individuals] * number_entries + + +def get_color_list(color, number_entries, name_entries="nodes"): + return get_list(color, number_entries, "colors", name_entries) + + +def get_angle_list(angle, number_entries, name_entries="nodes"): + return get_list(angle, number_entries, "angles", name_entries) + + +def ellipse_patches(node_coords, width, height, angle=0, color=None, **kwargs): + patches = list() + angles = get_angle_list(angle, len(node_coords)) + if color is not None: + colors = get_color_list(color, len(node_coords)) + for (x, y), col, ang in zip(node_coords, colors, angles): + patches.append(Ellipse((x, y), width, height, angle=ang, color=col, **kwargs)) + else: + for (x, y), ang in zip(node_coords, angles): + patches.append(Ellipse((x, y), width, height, angle=ang, **kwargs)) + return patches + + +def rectangle_patches(node_coords, width, height, color=None, **kwargs): + patches = list() + if color is not None: + colors = get_color_list(color, len(node_coords)) + for (x, y), col in zip(node_coords, colors): + patches.append(Rectangle((x - width / 2, y - height / 2), color=color, **kwargs)) + else: + for x, y in node_coords: + patches.append(Rectangle((x - width / 2, y - height / 2), **kwargs)) + return patches + + +def polygon_patches(node_coords, radius, num_edges, color=None, **kwargs): + patches = list() + if color is not None: + colors = get_color_list(color, len(node_coords)) + for (x, y), col in zip(node_coords, colors): + patches.append(RegularPolygon([x, y], numVertices=num_edges, radius=radius, color=color, + **kwargs)) + else: + for x, y in node_coords: + patches.append(RegularPolygon([x, y], numVertices=num_edges, radius=radius, **kwargs)) + return patches + + +def node_patches(node_coords, size, patch_type, colors=None, **kwargs): + if patch_type == 'ellipse' or patch_type == 'circle': # circles are just ellipses + width = kwargs.pop("width", 2 * size) + height = kwargs.pop("height", 2 * size) + angle = kwargs.pop('angle', 0) + return ellipse_patches(node_coords, width, height, angle, **kwargs) + elif patch_type == "rect": + width = kwargs.pop("width", 2 * size) + height = kwargs.pop("height", 2 * size) + return rectangle_patches(node_coords, width, height, **kwargs) + elif patch_type.startswith("poly"): + edges = int(patch_type[4:]) + return polygon_patches(node_coords, size, edges, **kwargs) + else: + logger.error("Wrong patchtype. Please choose a correct patch type.") + raise ValueError("Wrong patchtype") From 825623a65e6cf8154d253328313084b214fb8b0c Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Sun, 17 Nov 2019 14:45:27 +0100 Subject: [PATCH 03/38] added generic fancy branch collection creation with implementation for trafos --- pandapower/plotting/collections.py | 236 ++++++++++++++-------------- pandapower/plotting/patch_makers.py | 26 +++ 2 files changed, 146 insertions(+), 116 deletions(-) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index 9172f80cb..5b8f179c7 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -16,7 +16,7 @@ from matplotlib.transforms import Affine2D from pandas import isnull from pandapower.plotting.patch_makers import load_patches, _rotate_dim2, node_patches, gen_patches,\ - sgen_patches, ext_grid_patches + sgen_patches, ext_grid_patches, trafo_patches try: import pplog as logging @@ -86,11 +86,26 @@ def create_annotation_collection(texts, coords, size, prop=None, **kwargs): return PatchCollection(tp, **kwargs) -def add_cmap_to_collection(collection, cmap, norm, z, cbar_title, clim=None): +def coords_from_bus_geodata(element_indices, from_buses, to_buses, bus_geodata, table_name): + elements_with_geo = element_indices[np.isin(from_buses, bus_geodata.index.values) + & np.isin(to_buses, bus_geodata.index.values)] + fb_with_geo, tb_with_geo = from_buses[elements_with_geo], to_buses[elements_with_geo] + coords = [[(x_from, y_from), (x_to, y_to)] for x_from, y_from, x_to, y_to + in zip(bus_geodata.x.values[fb_with_geo], bus_geodata.y.values[fb_with_geo], + bus_geodata.x.values[tb_with_geo], bus_geodata.y.values[tb_with_geo]) + if 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: + logger.warning("No coords found for %s %s. Bus geodata is missing for those %s!" + % (table_name + "s", elements_without_geo, table_name + "s")) + return coords, elements_with_geo + + +def add_cmap_to_collection(collection, cmap, norm, z, cbar_title, plot_colormap=True, clim=None): collection.set_cmap(cmap) collection.set_norm(norm) collection.set_array(np.ma.masked_invalid(z)) - collection.has_colormap = True + collection.has_colormap = plot_colormap collection.cbar_title = cbar_title if clim is not None: collection.set_clim(clim) @@ -181,6 +196,47 @@ def create_node_element_collection(node_coords, patch_maker, size=1., infos=None return patch_coll, line_coll +def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, colors=None, + picker=False, patch_facecolor="w", patch_edgecolor="k", + line_color="k", linewidths=2., **kwargs): + """ + + :param coords: + :type coords: + :param patch_maker: + :type patch_maker: + :param size: + :type size: + :param infos: + :type infos: + :param colors: + :type colors: + :param picker: + :type picker: + :param patch_facecolor: + :type patch_facecolor: + :param patch_edgecolor: + :type patch_edgecolor: + :param line_color: + :type line_color: + :param linewidths: + :type linewidths: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + infos = [] if infos is None else infos + patches, lines = patch_maker(coords, size, colors) + patch_coll = PatchCollection(patches, facecolor=patch_facecolor, edgecolor=patch_edgecolor, + picker=picker, **kwargs) + line_coll = LineCollection(lines, color=line_color, picker=picker, linewidths=linewidths, + **kwargs) + patch_coll.info = infos + line_coll.info = infos + return patch_coll, line_coll + + def create_bus_collection(net, buses=None, size=5, patch_type="circle", colors=None, z=None, cmap=None, norm=None, infofunc=None, picker=False, bus_geodata=None, cbar_title="Bus Voltage [pu]", **kwargs): @@ -233,7 +289,7 @@ def create_bus_collection(net, buses=None, size=5, patch_type="circle", colors=N coords = list(zip(bus_geodata.loc[buses, "x"].values, bus_geodata.loc[buses, "y"].values)) - infos = [infofunc(buses[i]) for i in range(len(buses))] + infos = [infofunc(buses[i]) for i in range(len(buses))] if infofunc is not None else [] pc = create_node_collection(buses, coords, size, patch_type, colors, picker, infos, **kwargs) @@ -272,7 +328,7 @@ def create_line_collection(net, lines=None, line_geodata=None, bus_geodata=None, **norm** (matplotlib norm object, None) - matplotlib norm object - **picker** (bool, False) - picker argument passed to the patch collection + **picker** (bool, False) - picker argument passed to the line collection **z** (array, None) - array of line loading magnitudes for colormap. Used in case of given cmap. If None net.res_line.loading_percent is used. @@ -286,82 +342,47 @@ def create_line_collection(net, lines=None, line_geodata=None, bus_geodata=None, OUTPUT: **lc** - line collection """ - global linetab if use_bus_geodata is False and net.line_geodata.empty: # if bus geodata is available, but no line geodata logger.warning("use_bus_geodata is automatically set to True, since net.line_geodata is " "empty.") use_bus_geodata = True - if use_bus_geodata: - linetab = net.line if lines is None else net.line.loc[lines] - lines = net.line.index.tolist() if lines is None else list(lines) - if len(lines) == 0: - return None - if line_geodata is None: - line_geodata = net["line_geodata"] - + lines = net.line.index.values if lines is None else np.array(lines) \ + if not isinstance(lines, set) else np.array(list(lines)) if len(lines) == 0: return None - lines_with_geo = [] if use_bus_geodata: - if bus_geodata is None: - bus_geodata = net["bus_geodata"] - data = [] - buses_with_geodata = bus_geodata.index.values - bg_dict = bus_geodata.to_dict() # transforming to dict to make access faster - for line, fb, tb in zip(linetab.index, linetab.from_bus.values, linetab.to_bus.values): - if fb in buses_with_geodata and tb in buses_with_geodata: - lines_with_geo.append(line) - data.append(([(bg_dict["x"][fb], bg_dict["y"][fb]), - (bg_dict["x"][tb], bg_dict["y"][tb])], - infofunc(line) if infofunc else [])) - lines_without_geo = set(lines) - set(lines_with_geo) - if lines_without_geo: - logger.warning("Could not plot lines %s. Bus geodata is missing for those lines!" - % lines_without_geo) + coords, lines_with_geo = coords_from_bus_geodata( + lines, net.line.from_bus.loc[lines].values, net.line.to_bus.loc[lines].values, + bus_geodata if bus_geodata is not None else net["bus_geodata"], "line") else: - data = [] - coords_dict = line_geodata.coords.to_dict() # transforming to dict to make access faster - for line in lines: - if line in line_geodata.index: - lines_with_geo.append(line) - data.append((coords_dict[line], infofunc(line) if infofunc else [])) - + lines_with_geo = lines[np.isin(lines, line_geodata.index.values)] + coords = list(line_geodata[lines_with_geo]) lines_without_geo = set(lines) - set(lines_with_geo) - if len(lines_without_geo) > 0: - logger.warning("Could not plot lines %s. Line geodata is missing for those lines!" - % lines_without_geo) + if lines_without_geo: + logger.warning("Could not plot lines %s. %s geodata is missing for those lines!" + % (lines_without_geo, "Bus" if use_bus_geodata else "Line")) - if len(data) == 0: + if len(lines_with_geo) == 0: return None - data, info = list(zip(*data)) + infos = [infofunc(line) for line in lines_with_geo] if infofunc else [] + + lc = create_line2d_collection(coords, lines_with_geo, infos=infos, picker=picker, **kwargs) - # This would be done anyways by matplotlib - doing it explicitly makes it a) clear and - # b) prevents unexpected behavior when observing colors being "none" - lc = LineCollection(data, picker=picker, **kwargs) - lc.line_indices = np.array(lines_with_geo) if cmap is not None: if z is None: z = net.res_line.loading_percent.loc[lines_with_geo] - lc.set_cmap(cmap) - lc.set_norm(norm) - if clim is not None: - lc.set_clim(clim) - - lc.set_array(np.ma.masked_invalid(z)) - lc.has_colormap = plot_colormap - lc.cbar_title = cbar_title - lc.info = info + add_cmap_to_collection(lc, cmap, norm, z, cbar_title, plot_colormap, clim) return lc def create_trafo_connection_collection(net, trafos=None, bus_geodata=None, infofunc=None, cmap=None, clim=None, norm=None, z=None, - cbar_title="Transformer Loading", **kwargs): + cbar_title="Transformer Loading", picker=False, **kwargs): """ Creates a matplotlib line collection of pandapower transformers. @@ -375,7 +396,20 @@ def create_trafo_connection_collection(net, trafos=None, bus_geodata=None, infof **bus_geodata** (DataFrame, None) - coordinates to use for plotting If None, net["bus_geodata"] is used - **infofunc** (function, None) - infofunction for the patch element + **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 line loading magnitudes for colormap. Used in case of given + cmap. If None net.res_line.loading_percent is used. + + **cbar_title** (str, "Line Loading [%]") - colormap bar title in case of given cmap + + **picker** (bool, False) - picker argument passed to the line collection **kwargs - key word arguments are passed to the patch function @@ -391,24 +425,17 @@ def create_trafo_connection_collection(net, trafos=None, bus_geodata=None, infof bus_geodata.loc[trafos["hv_bus"], "y"].values)) lv_geo = list(zip(bus_geodata.loc[trafos["lv_bus"], "x"].values, bus_geodata.loc[trafos["lv_bus"], "y"].values)) - tg = list(zip(hv_geo, lv_geo)) - info = [infofunc(tr) if infofunc is not None else [] for tr in trafos.index.values] + info = [infofunc(tr) for tr in trafos.index.values] if infofunc is not None else [] + + lc = create_line2d_collection(tg, trafos.index.values, info, picker=picker, **kwargs) - lc = LineCollection([(tgd[0], tgd[1]) for tgd in tg], **kwargs) - lc.info = info if cmap is not None: if z is None: z = net.res_trafo.loading_percent.loc[trafos.index] - lc.set_cmap(cmap) - lc.set_norm(norm) - if clim is not None: - lc.set_clim(clim) + add_cmap_to_collection(lc, cmap, norm, z, cbar_title, True, clim) - lc.set_array(np.ma.masked_invalid(z)) - lc.has_colormap = True - lc.cbar_title = cbar_title return lc @@ -460,10 +487,9 @@ def create_trafo3w_connection_collection(net, trafos=None, bus_geodata=None, inf return lc -def create_trafo_collection(net, trafos=None, picker=False, size=None, - infofunc=None, cmap=None, norm=None, z=None, clim=None, - cbar_title="Transformer Loading", - plot_colormap=True, **kwargs): +def create_trafo_collection(net, trafos=None, picker=False, size=None, infofunc=None, cmap=None, + norm=None, z=None, clim=None, cbar_title="Transformer Loading", + plot_colormap=True, bus_geodata=None, **kwargs): """ Creates a matplotlib line collection of pandapower transformers. @@ -488,57 +514,35 @@ def create_trafo_collection(net, trafos=None, picker=False, size=None, **pc** - patch collection """ - trafo_table = net.trafo if trafos is None else net.trafo.loc[trafos] - lines = [] - circles = [] - infos = [] - color = kwargs.pop("color", "k") + trafos = net.trafo.index.values if trafos is None else np.array(trafos) \ + if not isinstance(trafos, set) else np.array(list(trafos)) + trafo_table = net.trafo.loc[trafos] + + coords, trafos_with_geo = coords_from_bus_geodata( + trafos, trafo_table.hv_bus.values, trafo_table.lv_bus.values, + bus_geodata if bus_geodata is not None else net["bus_geodata"], "trafo") + + if len(trafos_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 and z is None: - z = net.res_trafo.loading_percent + if cmap is not None: + if z is None: + z = net.res_trafo.loading_percent + colors = [cmap(norm(z.at[idx])) for idx in trafos_with_geo] - for i, idx in enumerate(trafo_table.index): - p1 = net.bus_geodata[["x", "y"]].loc[net.trafo.at[idx, "hv_bus"]].values - p2 = net.bus_geodata[["x", "y"]].loc[net.trafo.at[idx, "lv_bus"]].values - if np.all(p1 == p2): - continue - d = np.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) - if size is None: - size_this = np.sqrt(d) / 5 - else: - size_this = size - off = size_this * 0.35 - circ1 = (0.5 - off / d) * (p1 - p2) + p2 - circ2 = (0.5 + off / d) * (p1 - p2) + p2 - ec = color if cmap is None else cmap(norm(z.at[idx])) - circles.append(Circle(circ1, size_this, fc=(1, 0, 0, 0), ec=ec)) - circles.append(Circle(circ2, size_this, fc=(1, 0, 0, 0), ec=ec)) + infos = list(np.repeat([infofunc(i) for i in range(len(trafos_with_geo))], 2))\ + if infofunc is not None else [] + + lc, pc = create_complex_branch_collection(coords, trafo_patches, size, infos, colors=colors, + picker=picker, linewidths=linewidths, **kwargs) - lp1 = (0.5 - off / d - size_this / d) * (p2 - p1) + p1 - lp2 = (0.5 - off / d - size_this / d) * (p1 - p2) + p2 - lines.append([p1, lp1]) - lines.append([p2, lp2]) - if infofunc is not None: - infos.append(infofunc(i)) - infos.append(infofunc(i)) - if len(circles) == 0: - return None, None - lc = LineCollection(lines, color=color, picker=picker, linewidths=linewidths, **kwargs) - lc.info = infos - pc = PatchCollection(circles, match_original=True, picker=picker, linewidth=linewidths, - **kwargs) - pc.info = infos if cmap is not None: z_duplicated = np.repeat(z.values, 2) - lc.set_cmap(cmap) - lc.set_norm(norm) - if clim is not None: - lc.set_clim(clim) - lc.set_array(np.ma.masked_invalid(z_duplicated)) - lc.has_colormap = plot_colormap - lc.cbar_title = cbar_title + add_cmap_to_collection(lc, cmap, norm, z_duplicated, cbar_title, plot_colormap, clim) return lc, pc diff --git a/pandapower/plotting/patch_makers.py b/pandapower/plotting/patch_makers.py index 34ed1dceb..56ecbe3f0 100644 --- a/pandapower/plotting/patch_makers.py +++ b/pandapower/plotting/patch_makers.py @@ -154,3 +154,29 @@ def node_patches(node_coords, size, patch_type, colors=None, **kwargs): else: logger.error("Wrong patchtype. Please choose a correct patch type.") raise ValueError("Wrong patchtype") + + +def trafo_patches(hv_node_coords, lv_node_coords, size, color): + assert len(hv_node_coords) == len(lv_node_coords),\ + "Cannot create trafo patches as length of coord lists is not equal." + colors = get_color_list(color, len(hv_node_coords)) + circles, lines = list(), list() + for p1, p2, col in zip(hv_node_coords, lv_node_coords, colors): + if np.all(p1 == p2): + continue + d = np.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) + if size is None: + size_this = np.sqrt(d) / 5 + else: + size_this = size + off = size_this * 0.35 + circ1 = (0.5 - off / d) * (p1 - p2) + p2 + circ2 = (0.5 + off / d) * (p1 - p2) + p2 + circles.append(Circle(circ1, size_this, fc=(1, 0, 0, 0), ec=col)) + circles.append(Circle(circ2, size_this, fc=(1, 0, 0, 0), ec=col)) + + lp1 = (0.5 - off / d - size_this / d) * (p2 - p1) + p1 + lp2 = (0.5 - off / d - size_this / d) * (p1 - p2) + p2 + lines.append([p1, lp1]) + lines.append([p2, lp2]) + return circles, lines From 7767720f4fdcf05bd785022a17db7b0ea366bac7 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Sun, 17 Nov 2019 15:14:35 +0100 Subject: [PATCH 04/38] some small corrections --- pandapower/plotting/collections.py | 11 ++++++----- pandapower/plotting/patch_makers.py | 14 +++++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index 5b8f179c7..a2f66205a 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -87,12 +87,13 @@ def create_annotation_collection(texts, coords, size, prop=None, **kwargs): def coords_from_bus_geodata(element_indices, from_buses, to_buses, bus_geodata, table_name): - elements_with_geo = element_indices[np.isin(from_buses, bus_geodata.index.values) - & np.isin(to_buses, bus_geodata.index.values)] - fb_with_geo, tb_with_geo = from_buses[elements_with_geo], to_buses[elements_with_geo] + have_geo = np.isin(from_buses, bus_geodata.index.values) \ + & np.isin(to_buses, bus_geodata.index.values) + elements_with_geo = element_indices[have_geo] + fb_with_geo, tb_with_geo = from_buses[have_geo], to_buses[have_geo] coords = [[(x_from, y_from), (x_to, y_to)] for x_from, y_from, x_to, y_to - in zip(bus_geodata.x.values[fb_with_geo], bus_geodata.y.values[fb_with_geo], - bus_geodata.x.values[tb_with_geo], bus_geodata.y.values[tb_with_geo]) + in np.concatenate([bus_geodata.loc[fb_with_geo, ["x", "y"]].values, + bus_geodata.loc[tb_with_geo, ["x", "y"]].values], axis=1) if 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: diff --git a/pandapower/plotting/patch_makers.py b/pandapower/plotting/patch_makers.py index 56ecbe3f0..67e9d0043 100644 --- a/pandapower/plotting/patch_makers.py +++ b/pandapower/plotting/patch_makers.py @@ -80,12 +80,12 @@ def get_list(individuals, number_entries, name_ind, name_ent): if number_entries == len(individuals): return individuals elif number_entries > len(individuals): - logger.warning("The number of given %s (%d) is larger than the number of %s (%d) to" + logger.warning("The number of given %s (%d) is smaller than the number of %s (%d) to" " draw! The %s will be repeated to fit." % (name_ind, len(individuals), name_ent, number_entries, name_ind)) return (individuals * (int(number_entries / len(individuals)) + 1))[:number_entries] else: - logger.warning("The number of given %s (%d) is smaller than the number of %s (%d) to" + logger.warning("The number of given %s (%d) is larger than the number of %s (%d) to" " draw! The %s will be capped to fit." % (name_ind, len(individuals), name_ent, number_entries, name_ind)) return individuals[:number_entries] @@ -156,12 +156,12 @@ def node_patches(node_coords, size, patch_type, colors=None, **kwargs): raise ValueError("Wrong patchtype") -def trafo_patches(hv_node_coords, lv_node_coords, size, color): - assert len(hv_node_coords) == len(lv_node_coords),\ - "Cannot create trafo patches as length of coord lists is not equal." - colors = get_color_list(color, len(hv_node_coords)) +def trafo_patches(coords, size, color): + colors = get_color_list(color, len(coords)) circles, lines = list(), list() - for p1, p2, col in zip(hv_node_coords, lv_node_coords, colors): + for (p1, p2), col in zip(coords, colors): + p1 = np.array(p1) + p2 = np.array(p2) if np.all(p1 == p2): continue d = np.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) From cc5df47c33c1c9ed7603384048072ec1e1b0081d Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Sun, 17 Nov 2019 15:50:37 +0100 Subject: [PATCH 05/38] few more corrections --- pandapower/plotting/collections.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index a2f66205a..721452695 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -86,19 +86,20 @@ def create_annotation_collection(texts, coords, size, prop=None, **kwargs): return PatchCollection(tp, **kwargs) -def coords_from_bus_geodata(element_indices, from_buses, to_buses, bus_geodata, table_name): - have_geo = np.isin(from_buses, bus_geodata.index.values) \ - & np.isin(to_buses, bus_geodata.index.values) +def coords_from_node_geodata(element_indices, from_nodes, to_nodes, node_geodata, table_name, + node_name="Bus"): + have_geo = np.isin(from_nodes, node_geodata.index.values) \ + & np.isin(to_nodes, node_geodata.index.values) elements_with_geo = element_indices[have_geo] - fb_with_geo, tb_with_geo = from_buses[have_geo], to_buses[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([bus_geodata.loc[fb_with_geo, ["x", "y"]].values, - bus_geodata.loc[tb_with_geo, ["x", "y"]].values], axis=1) + in np.concatenate([node_geodata.loc[fb_with_geo, ["x", "y"]].values, + node_geodata.loc[tb_with_geo, ["x", "y"]].values], axis=1) if 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: - logger.warning("No coords found for %s %s. Bus geodata is missing for those %s!" - % (table_name + "s", elements_without_geo, table_name + "s")) + logger.warning("No coords found for %s %s. %s geodata is missing for those %s!" + % (table_name + "s", elements_without_geo, node_name, table_name + "s")) return coords, elements_with_geo @@ -290,7 +291,7 @@ def create_bus_collection(net, buses=None, size=5, patch_type="circle", colors=N coords = list(zip(bus_geodata.loc[buses, "x"].values, bus_geodata.loc[buses, "y"].values)) - infos = [infofunc(buses[i]) for i in range(len(buses))] if infofunc is not None else [] + infos = [infofunc(bus) for bus in buses] if infofunc is not None else [] pc = create_node_collection(buses, coords, size, patch_type, colors, picker, infos, **kwargs) @@ -355,7 +356,7 @@ def create_line_collection(net, lines=None, line_geodata=None, bus_geodata=None, return None if use_bus_geodata: - coords, lines_with_geo = coords_from_bus_geodata( + coords, lines_with_geo = coords_from_node_geodata( lines, net.line.from_bus.loc[lines].values, net.line.to_bus.loc[lines].values, bus_geodata if bus_geodata is not None else net["bus_geodata"], "line") else: @@ -519,7 +520,7 @@ def create_trafo_collection(net, trafos=None, picker=False, size=None, infofunc= if not isinstance(trafos, set) else np.array(list(trafos)) trafo_table = net.trafo.loc[trafos] - coords, trafos_with_geo = coords_from_bus_geodata( + coords, trafos_with_geo = coords_from_node_geodata( trafos, trafo_table.hv_bus.values, trafo_table.lv_bus.values, bus_geodata if bus_geodata is not None else net["bus_geodata"], "trafo") @@ -837,10 +838,10 @@ def create_ext_grid_collection(net, size=1., infofunc=None, orientation=0, picke "Length mismatch between chosen ext_grids and ext_grid_buses." infos = [infofunc(ext_grid_idx) for ext_grid_idx in ext_grids] - node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[ext_grid_buses] + node_coords = net.bus_geodata.loc[ext_grid_buses, ["x", "y"]].values ext_grid_pc, ext_grid_lc = create_node_element_collection( node_coords, ext_grid_patches, size=size, infos=infos, orientation=orientation, - offset=1.7 * size, r_triangle=size * 0.4, picker=picker, hatch='XXX', **kwargs) + offset=1.7 * size, picker=picker, hatch='XXX', **kwargs) return ext_grid_pc, ext_grid_lc From ec14aeabf89eb1ae983be6f02da0f9bf848d8b5c Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Sun, 17 Nov 2019 23:05:07 +0100 Subject: [PATCH 06/38] added colors to node patch maker --- pandapower/plotting/patch_makers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandapower/plotting/patch_makers.py b/pandapower/plotting/patch_makers.py index 67e9d0043..7652b6af1 100644 --- a/pandapower/plotting/patch_makers.py +++ b/pandapower/plotting/patch_makers.py @@ -143,14 +143,14 @@ def node_patches(node_coords, size, patch_type, colors=None, **kwargs): width = kwargs.pop("width", 2 * size) height = kwargs.pop("height", 2 * size) angle = kwargs.pop('angle', 0) - return ellipse_patches(node_coords, width, height, angle, **kwargs) + return ellipse_patches(node_coords, width, height, angle, color=colors, **kwargs) elif patch_type == "rect": width = kwargs.pop("width", 2 * size) height = kwargs.pop("height", 2 * size) - return rectangle_patches(node_coords, width, height, **kwargs) + return rectangle_patches(node_coords, width, height, color=colors, **kwargs) elif patch_type.startswith("poly"): edges = int(patch_type[4:]) - return polygon_patches(node_coords, size, edges, **kwargs) + return polygon_patches(node_coords, size, edges, color=colors, **kwargs) else: logger.error("Wrong patchtype. Please choose a correct patch type.") raise ValueError("Wrong patchtype") From 31fcee7d6816b0550f9206b2f55e973a49883afb Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Tue, 19 Nov 2019 10:47:30 +0100 Subject: [PATCH 07/38] added missing arguments in node collection; removing of used kwargs --- pandapower/plotting/__init__.py | 5 ++++- pandapower/plotting/collections.py | 16 +++++++++----- pandapower/plotting/patch_makers.py | 34 +++++++++++++++-------------- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/pandapower/plotting/__init__.py b/pandapower/plotting/__init__.py index e8df2893c..9079830f9 100644 --- a/pandapower/plotting/__init__.py +++ b/pandapower/plotting/__init__.py @@ -10,12 +10,15 @@ import types from matplotlib.backend_bases import GraphicsContextBase, RendererBase + class GC(GraphicsContextBase): def __init__(self): super().__init__() self._capstyle = 'round' + def custom_new_gc(self): return GC() -RendererBase.new_gc = types.MethodType(custom_new_gc, RendererBase) \ No newline at end of file + +RendererBase.new_gc = types.MethodType(custom_new_gc, RendererBase) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index 721452695..948919065 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -189,7 +189,9 @@ def create_node_element_collection(node_coords, patch_maker, size=1., infos=None assert len(node_coords) == len(angles), \ "The length of coordinates does not match the length of the orientation angles!" infos = [] if infos is None else infos - lines, polys = patch_maker(node_coords, size, orientation, **kwargs) + lines, polys, popped_keywords = patch_maker(node_coords, size, angles, **kwargs) + for kw in popped_keywords: + kwargs.pop(kw) patch_coll = PatchCollection(polys, facecolor=patch_facecolor, edgecolor=patch_edgecolor, picker=picker, **kwargs) line_coll = LineCollection(lines, color=line_color, picker=picker, **kwargs) @@ -229,7 +231,9 @@ def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, co :rtype: """ infos = [] if infos is None else infos - patches, lines = patch_maker(coords, size, colors) + lines, patches, keywords = patch_maker(coords, size, colors, **kwargs) + for kw in keywords: + kwargs.pop(kw) patch_coll = PatchCollection(patches, facecolor=patch_facecolor, edgecolor=patch_edgecolor, picker=picker, **kwargs) line_coll = LineCollection(lines, color=line_color, picker=picker, linewidths=linewidths, @@ -722,7 +726,7 @@ def create_load_collection(net, loads=None, size=1., infofunc=None, orientation= node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.load.loc[loads, "bus"].values] load_pc, load_lc = create_node_element_collection( node_coords, load_patches, size=size, infos=infos, orientation=orientation, - offset=2. * size, picker=picker, **kwargs) + offset=size, picker=picker, **kwargs) return load_pc, load_lc @@ -759,7 +763,7 @@ def create_gen_collection(net, gens=None, size=1., infofunc=None, orientation=np node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.gen.loc[gens, "bus"].values] gen_pc, gen_lc = create_node_element_collection( node_coords, gen_patches, size=size, infos=infos, orientation=orientation, - offset=1.7 * size, picker=picker, **kwargs) + offset=size, picker=picker, **kwargs) return gen_pc, gen_lc @@ -796,7 +800,7 @@ def create_sgen_collection(net, sgens=None, size=1., infofunc=None, orientation= node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.sgen.loc[sgens, "bus"].values] sgen_pc, sgen_lc = create_node_element_collection( node_coords, sgen_patches, size=size, infos=infos, orientation=orientation, - offset=1.7 * size, r_triangle=size * 0.4, picker=picker, **kwargs) + offset=size, r_triangle=size * 0.4, picker=picker, **kwargs) return sgen_pc, sgen_lc @@ -841,7 +845,7 @@ def create_ext_grid_collection(net, size=1., infofunc=None, orientation=0, picke node_coords = net.bus_geodata.loc[ext_grid_buses, ["x", "y"]].values ext_grid_pc, ext_grid_lc = create_node_element_collection( node_coords, ext_grid_patches, size=size, infos=infos, orientation=orientation, - offset=1.7 * size, picker=picker, hatch='XXX', **kwargs) + offset=size / 2, picker=picker, hatch='XXX', **kwargs) return ext_grid_pc, ext_grid_lc diff --git a/pandapower/plotting/patch_makers.py b/pandapower/plotting/patch_makers.py index 7652b6af1..dfac8f29c 100644 --- a/pandapower/plotting/patch_makers.py +++ b/pandapower/plotting/patch_makers.py @@ -17,14 +17,14 @@ def _rotate_dim2(arr, ang): def load_patches(node_coords, size, angles, **kwargs): - offset = kwargs.pop("offset", 2. * size) + offset = kwargs.get("offset", size) polys, lines = list(), list() for i, node_geo in enumerate(node_coords): - p2 = node_geo + _rotate_dim2(np.array([0, size * offset]), angles[i]) - p3 = node_geo + _rotate_dim2(np.array([0, size * (offset - 0.5)]), angles[i]) + p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), angles[i]) + p3 = node_geo + _rotate_dim2(np.array([0, offset + size / 3 * 2]), angles[i]) polys.append(RegularPolygon(p2, numVertices=3, radius=size, orientation=-angles[i])) lines.append((node_geo, p3)) - return lines, polys + return lines, polys, {"offset"} def gen_patches(node_coords, size, angles, **kwargs): @@ -38,16 +38,16 @@ def gen_patches(node_coords, size, angles, **kwargs): polys.append( Arc(p2 + np.array([size / 6.2, size / 2.6]), size / 2, size, theta1=225, theta2=315)) lines.append((node_geo, p2 + np.array([0, size]))) - return lines, polys + return lines, polys, {"offset"} def sgen_patches(node_coords, size, angles, **kwargs): polys, lines = list(), list() - offset = kwargs.pop("offset", 2. * size) + offset = kwargs.pop("offset", size) r_triangle = kwargs.pop("r_triangles", size * 0.4) for i, node_geo in enumerate(node_coords): - mid_circ = node_geo + _rotate_dim2(np.array([0, size * offset]), angles[i]) - circ_edge = node_geo + _rotate_dim2(np.array([0, size * (offset - 1)]), angles[i]) + mid_circ = node_geo + _rotate_dim2(np.array([0, offset + size]), angles[i]) + circ_edge = node_geo + _rotate_dim2(np.array([0, offset]), angles[i]) mid_tri1 = mid_circ + _rotate_dim2(np.array([r_triangle, -r_triangle / 4]), angles[i]) mid_tri2 = mid_circ + _rotate_dim2(np.array([-r_triangle, r_triangle / 4]), angles[i]) # dropped perpendicular foot of triangle1 @@ -63,20 +63,21 @@ def sgen_patches(node_coords, size, angles, **kwargs): lines.append((node_geo, circ_edge)) lines.append((perp_foot1, line_end1)) lines.append((perp_foot2, line_end2)) - return lines, polys + return lines, polys, {"offset", "r_triangle"} def ext_grid_patches(node_coords, size, angles, **kwargs): + offset = kwargs.pop("offset", size / 2) polys, lines = list(), list() for i, node_geo in enumerate(node_coords): - p2 = node_geo + _rotate_dim2(np.array([0, size]), angles[i]) + p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), angles[i]) polys.append(Rectangle((p2[0] - size / 2, p2[1] - size / 2), size, size)) - lines.append((node_geo, p2 - _rotate_dim2(np.array([0, size / 2]), angles[i]))) - return lines, polys + lines.append((node_geo, p2 - _rotate_dim2(np.array([0, offset]), angles[i]))) + return lines, polys, set() def get_list(individuals, number_entries, name_ind, name_ent): - if hasattr(individuals, "__iter__"): + if hasattr(individuals, "__iter__") and not isinstance(individuals, str): if number_entries == len(individuals): return individuals elif number_entries > len(individuals): @@ -118,10 +119,11 @@ def rectangle_patches(node_coords, width, height, color=None, **kwargs): if color is not None: colors = get_color_list(color, len(node_coords)) for (x, y), col in zip(node_coords, colors): - patches.append(Rectangle((x - width / 2, y - height / 2), color=color, **kwargs)) + patches.append(Rectangle((x - width / 2, y - height / 2), width, height, color=color, + **kwargs)) else: for x, y in node_coords: - patches.append(Rectangle((x - width / 2, y - height / 2), **kwargs)) + patches.append(Rectangle((x - width / 2, y - height / 2), width, height, **kwargs)) return patches @@ -179,4 +181,4 @@ def trafo_patches(coords, size, color): lp2 = (0.5 - off / d - size_this / d) * (p1 - p2) + p2 lines.append([p1, lp1]) lines.append([p2, lp2]) - return circles, lines + return lines, circles From 4ec07e65e0138cbd59f0521fad36612c7daef838 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Tue, 19 Nov 2019 16:12:33 +0100 Subject: [PATCH 08/38] in plotting module: - added docstrings - moved some functions to new module plotting_toolbox - some renaming --- pandapower/plotting/collections.py | 280 +++++++++++------------- pandapower/plotting/geo.py | 70 ++---- pandapower/plotting/patch_makers.py | 206 +++++++++++++---- pandapower/plotting/plotting_toolbox.py | 212 ++++++++++++++++++ pandapower/plotting/simple_plot.py | 2 +- 5 files changed, 523 insertions(+), 247 deletions(-) create mode 100644 pandapower/plotting/plotting_toolbox.py diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index 948919065..b02d3bc67 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -15,8 +15,10 @@ from matplotlib.textpath import TextPath from matplotlib.transforms import Affine2D from pandas import isnull -from pandapower.plotting.patch_makers import load_patches, _rotate_dim2, node_patches, gen_patches,\ +from pandapower.plotting.patch_makers import load_patches, node_patches, gen_patches,\ sgen_patches, ext_grid_patches, trafo_patches +from pandapower.plotting.plotting_toolbox import _rotate_dim2, coords_from_node_geodata, \ + position_on_busbar try: import pplog as logging @@ -86,24 +88,27 @@ def create_annotation_collection(texts, coords, size, prop=None, **kwargs): return PatchCollection(tp, **kwargs) -def coords_from_node_geodata(element_indices, from_nodes, to_nodes, node_geodata, table_name, - node_name="Bus"): - have_geo = np.isin(from_nodes, node_geodata.index.values) \ - & np.isin(to_nodes, node_geodata.index.values) - elements_with_geo = 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) - if 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: - logger.warning("No coords found for %s %s. %s geodata is missing for those %s!" - % (table_name + "s", elements_without_geo, node_name, table_name + "s")) - return coords, elements_with_geo - - def add_cmap_to_collection(collection, cmap, norm, z, cbar_title, plot_colormap=True, clim=None): + """ + Adds a colormap to the given collection. + + :param collection: collection for which to add colormap + :type collection: matplotlib.collections.collection + :param cmap: colormap which to use + :type cmap: any colormap from matplotlib.colors + :param norm: any norm which to use to translate values into colors + :type norm: any norm from matplotlib.colors + :param z: the array which to use in order to create the colors for the given collection + :type z: iterable + :param cbar_title: title of the colorbar + :type cbar_title: str + :param plot_colormap: flag whether the colormap is actually drawn (if False, is excluded in\ + :func:`add_single_collection`) + :type plot_colormap: bool, default True + :param clim: color limit of the collection + :type clim: list(float), default None + :return: collection - the given collection with added colormap (no copy!) + """ collection.set_cmap(cmap) collection.set_norm(norm) collection.set_array(np.ma.masked_invalid(z)) @@ -116,6 +121,32 @@ def add_cmap_to_collection(collection, cmap, norm, z, cbar_title, plot_colormap= def create_node_collection(nodes, coords, size=5, patch_type="circle", color=None, picker=False, infos=None, **kwargs): + """ + Creates a collection with patches for the given nodes. Can be used generically for different \ + types of nodes (bus in pandapower network, but also other nodes, e.g. in a networkx graph). + + :param nodes: indices of the nodes to plot + :type nodes: iterable + :param coords: list of node coordinates (shape (2, N)) + :type coords: iterable + :param size: size of the patches (handed over to patch creation function) + :type size: float + :param patch_type: type of patches that chall be created for the nodes - can be one of\ + - "circle" for a circle\ + - "rect" for a rectangle\ + - "poly" for a polygon with n edges + :type patch_type: str, default "circle" + :param color: colors or color of the node patches + :type color: iterable, float + :param picker: picker argument passed to the patch collection + :type picker: bool, default False + :param infos: list of infos belonging to each of the patches (can be displayed when hovering \ + over the elements) + :type infos: list, default None + :param kwargs: keyword arguments are passed to the patch maker and patch collection + :type kwargs: + :return: pc - patch collection for the nodes + """ if len(coords) == 0: return None @@ -141,7 +172,24 @@ def create_node_collection(nodes, coords, size=5, patch_type="circle", color=Non return pc -def create_line2d_collection(coords, indices, infos=None, picker=None, **kwargs): +def create_line2d_collection(coords, indices, infos=None, picker=False, **kwargs): + """ + Generic function to create a LineCollection from coordinates. + + :param coords: list of line coordinates (list should look like this: \ + `[[(x11, y11), (x12, y12), (x13, y13), ...], [(x21, y21), (x22, x23), ...], ...]`) + :type coords: list or np.array + :param indices: list of node indices + :type indices: list or np.array + :param infos: list of infos belonging to each of the lines (can be displayed when hovering \ + over them) + :type infos: list, default None + :param picker: picker argument passed to the line collection + :type picker: bool, default False + :param kwargs: keyword arguments are passed to the line collection + :type kwargs: + :return: lc - line collection for the given coordinates + """ # This would be done anyways by matplotlib - doing it explicitly makes it a) clear and # b) prevents unexpected behavior when observing colors being "none" lc = LineCollection(coords, picker=picker, **kwargs) @@ -158,32 +206,33 @@ def create_node_element_collection(node_coords, patch_maker, size=1., infos=None one patch collection representing the element itself and a small line collection that connects the element to the respective node. - Input: - **node_coords** (iterable) - the coordinates (x, y) of the nodes with shape (N, 2) - - **patch_maker** (function) - a function to generate the patches of which the collections \ - consist (cf. the patch_maker module) - - OPTIONAL: - **size** (float, 1) - patch size - - **infos** (iterable, None) - additional infos to add for each element - - **orientation** (float, np.pi) - orientation of load collection. pi is directed downwards, - increasing values lead to clockwise direction changes. - - **patch_facecolor** (matplotlib color, "w") - color of the patch face (content) - - **patch_edgecolor** (matplotlib color, "k") - color of the patch edges - - **line_color** (matplotlib color, "k") - color of the connecting lines - - **kwargs - key word arguments are passed to the patch function - - OUTPUT: - **patch_coll** - patch collection representing the element + :param node_coords: the coordinates (x, y) of the nodes with shape (N, 2) + :type node_coords: iterable + :param patch_maker: a function to generate the patches of which the collections consist (cf. \ + the patch_maker module) + :type patch_maker: function + :param size: patch size + :type size: float, default 1 + :param infos: list of infos belonging to each of the elements (can be displayed when hovering \ + over them) + :type infos: iterable, default None + :param orientation: orientation of load collection. pi is directed downwards, increasing values\ + lead to clockwise direction changes. + :type orientation: float, default np.pi + :param picker: picker argument passed to the line collection + :type picker: bool, default False + :param patch_facecolor: color of the patch face (content) + :type patch_facecolor: matplotlib color, "w" + :param patch_edgecolor: color of the patch edges + :type patch_edgecolor: matplotlib color, "k" + :param line_color: color of the connecting lines + :type line_color: matplotlib color, "k" + :param kwargs: key word arguments are passed to the patch function + :type kwargs: + :return: Return values:\ + - patch_coll - patch collection representing the element\ + - line_coll - connecting line collection - **line_coll** - connecting line collection """ angles = orientation if hasattr(orientation, '__iter__') else [orientation] * len(node_coords) assert len(node_coords) == len(angles), \ @@ -204,31 +253,38 @@ def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, co picker=False, patch_facecolor="w", patch_edgecolor="k", line_color="k", linewidths=2., **kwargs): """ - - :param coords: - :type coords: - :param patch_maker: - :type patch_maker: - :param size: - :type size: - :param infos: - :type infos: - :param colors: - :type colors: - :param picker: - :type picker: - :param patch_facecolor: - :type patch_facecolor: - :param patch_edgecolor: - :type patch_edgecolor: - :param line_color: - :type line_color: - :param linewidths: - :type linewidths: - :param kwargs: + Creates a matplotlib line collection and a matplotlib patch collection representing a branch\ + element that cannot be represented by just a line. + + :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 patch_maker: a function to generate the patches of which the collections consist (cf. \ + the patch_maker module) + :type patch_maker: function + :param size: patch size + :type size: float, default 1 + :param infos: list of infos belonging to each of the branches (can be displayed when hovering \ + over them) + :type infos: iterable, default None + :param colors: colors or color of the branch patches + :type colors: iterable, float + :param picker: picker argument passed to the line collection + :type picker: bool, default False + :param patch_facecolor: color of the patch face (content) + :type patch_facecolor: matplotlib color, "w" + :param patch_edgecolor: color of the patch edges + :type patch_edgecolor: matplotlib color, "k" + :param line_color: color of the connecting lines + :type line_color: matplotlib color, "k" + :param linewidths: linewidths of the connecting lines and the patch edges + :type linewidths: float, default 2. + :param kwargs: key word arguments are passed to the patch maker and the patch and line \ + collections :type kwargs: - :return: - :rtype: + :return: Return values:\ + - patch_coll - patch collection representing the branch element\ + - line_coll - line collection connecting the patches with the nodes """ infos = [] if infos is None else infos lines, patches, keywords = patch_maker(coords, size, colors, **kwargs) @@ -553,11 +609,9 @@ def create_trafo_collection(net, trafos=None, picker=False, size=None, infofunc= # noinspection PyArgumentList -def create_trafo3w_collection(net, trafo3ws=None, picker=False, infofunc=None, - cmap=None, norm=None, z=None, clim=None, - cbar_title="3W-Transformer Loading", - plot_colormap=True, - **kwargs): +def create_trafo3w_collection(net, trafo3ws=None, picker=False, infofunc=None, cmap=None, norm=None, + z=None, clim=None, cbar_title="3W-Transformer Loading", + plot_colormap=True, **kwargs): """ Creates a matplotlib line collection of pandapower transformers. @@ -849,84 +903,6 @@ def create_ext_grid_collection(net, size=1., infofunc=None, orientation=0, picke return ext_grid_pc, ext_grid_lc -def onBusbar(net, bus, line_coords): - """ - Checks if the first or the last coordinates of a line are on a bus - - Input: - **net** (pandapowerNet) - The pandapower network - - **bus** (int) - ID of the target bus on one end of the line - - **line_coords (array, shape= (,2L)) - - The linegeodata of the line. The first row should be the coordinates - of bus a and the last should be the coordinates of bus b. The points - in the middle represent the bending points of the line - - OUTPUT: - **intersection** (tupel, shape= (2L,))- Intersection point of the line with the given bus. \ - Can be used for switch position - - """ - # If the line has no Intersection line will be returned and the bus coordinates can be used to - # calculate the switch position - intersection = None - bus_coords = net.bus_geodata.loc[bus, "coords"] - # Checking if bus has "coords" - if it is a busbar - if bus_coords is not None and bus_coords is not np.NaN and line_coords is not None: - for i in range(len(bus_coords) - 1): - try: - # Calculating slope of busbar-line. If the busbar-line is vertical ZeroDivisionError - # occurs - m = (bus_coords[i + 1][1] - bus_coords[i][1]) / \ - (bus_coords[i + 1][0] - bus_coords[i][0]) - # Clculating the off-set of the busbar-line - b = bus_coords[i][1] - bus_coords[i][0] * m - # Checking if the first end of the line is on the busbar-line - if 0 == m * line_coords[0][0] + b - line_coords[0][1]: - # Checking if the end of the line is in the Range of the busbar-line - if bus_coords[i + 1][0] <= line_coords[0][0] <= bus_coords[i][0]\ - or bus_coords[i][0] <= line_coords[0][0] <= bus_coords[i + 1][0]: - # Intersection found. Breaking for-loop - intersection = line_coords[0] - break - # Checking if the second end of the line is on the busbar-line - elif 0 == m * line_coords[-1][0] + b - line_coords[-1][1]: - if bus_coords[i][0] >= line_coords[-1][0] >= bus_coords[i + 1][0] \ - or bus_coords[i][0] <= line_coords[-1][0] <= bus_coords[i + 1][0]: - # Intersection found. Breaking for-loop - intersection = line_coords[-1] - break - # If the busbar-line is a vertical line and the slope is infinitely - except ZeroDivisionError: - # Checking if the first line-end is at the same position - if bus_coords[i][0] == line_coords[0][0]: - # Checking if the first line-end is in the Range of the busbar-line - if bus_coords[i][1] >= line_coords[0][1] >= bus_coords[i + 1][1] \ - or bus_coords[i][1] <= line_coords[0][1] <= bus_coords[i + 1][1]: - # Intersection found. Breaking for-loop - intersection = line_coords[0] - break - # Checking if the second line-end is at the same position - elif bus_coords[i][0] == line_coords[-1][0]: - if bus_coords[i][1] >= line_coords[-1][1] >= bus_coords[i + 1][1] \ - or bus_coords[i][1] <= line_coords[-1][1] <= bus_coords[i + 1][1]: - # Intersection found. Breaking for-loop - intersection = line_coords[-1] - break - # If the bus has no "coords" it mus be a normal bus - elif bus_coords is np.NaN: - bus_geo = (net["bus_geodata"].loc[bus, "x"], net["bus_geodata"].loc[bus, "y"]) - # Checking if the first end of the line is on the bus - if bus_geo == line_coords[0]: - intersection = line_coords[0] - # Checking if the second end of the line is on the bus - elif bus_geo == line_coords[-1]: - intersection = line_coords[-1] - - return intersection - - def create_line_switch_collection(net, size=1, distance_to_bus=3, use_line_geodata=False, **kwargs): """ Creates a matplotlib patch collection of pandapower line-bus switches. @@ -976,7 +952,7 @@ def create_line_switch_collection(net, size=1, distance_to_bus=3, use_line_geoda if line.name in net.line_geodata.index: line_coords = net.line_geodata.coords.loc[line.name] # check, which end of the line is nearer to the switch bus - intersection = onBusbar(net, target_bus, line_coords=line_coords) + intersection = position_on_busbar(net, target_bus, busbar_coords=line_coords) if intersection is not None: pos_sb = intersection if len(line_coords) >= 2: diff --git a/pandapower/plotting/geo.py b/pandapower/plotting/geo.py index 5103b34f1..91ef674dd 100644 --- a/pandapower/plotting/geo.py +++ b/pandapower/plotting/geo.py @@ -9,14 +9,16 @@ def convert_geodata_to_gis(net, epsg=31467, bus_geodata=True, line_geodata=True): if bus_geodata: - g = net.bus_geodata - geo = [Point(x, y) for x, y in g[["x", "y"]].values] - net.bus_geodata = GeoDataFrame(g, crs=from_epsg(epsg), geometry=geo, index=g.index) + bus_geo = net.bus_geodata + geo = [Point(x, y) for x, y in bus_geo[["x", "y"]].values] + net.bus_geodata = GeoDataFrame(bus_geo, crs=from_epsg(epsg), geometry=geo, + index=bus_geo.index) if line_geodata: - l = net.line_geodata + line_geo = net.line_geodata geo = GeoSeries([LineString(x) for x in net.line_geodata.coords.values], index=net.line_geodata.index, crs=from_epsg(epsg)) - net.line_geodata = GeoDataFrame(l, crs=from_epsg(epsg), geometry=geo, index=l.index) + net.line_geodata = GeoDataFrame(line_geo, crs=from_epsg(epsg), geometry=geo, + index=line_geo.index) net["gis_epsg_code"] = epsg @@ -28,58 +30,20 @@ def convert_gis_to_geodata(net, bus_geodata=True, line_geodata=True): net.line_geodata["coords"] = net.line_geodata.geometry.apply(lambda x: list(x.coords)) -def get_collection_sizes(net, bus_size=1.0, ext_grid_size=1.0, trafo_size=1.0, load_size=1.0, - sgen_size=1.0, switch_size=2.0, switch_distance=1.0): - """ - Calculates the size for most collection types according to the distance between min and max - geocoord so that the collections fit the plot nicely - - # Comment: This is implemented because if you would choose a fixed values - # (e.g. bus_size = 0.2), the size - # could be to small for large networks and vice versa - INPUT - - net - pp net - bus_size (float) - ext_grid_size (float) - trafo_size (float) - load_size (float) - sgen_size (float) - switch_size (float) - switch_distance (float) - - Returns - - sizes (dict) - containing all scaled sizes in a dict - """ - - mean_distance_between_buses = sum((net['bus_geodata'].max() - net[ - 'bus_geodata'].min()).dropna() / 200) - - sizes = { - "bus": bus_size * mean_distance_between_buses, - "ext_grid": ext_grid_size * mean_distance_between_buses * 1.5, - "switch": switch_size * mean_distance_between_buses * 1, - "switch_distance": switch_distance * mean_distance_between_buses * 2, - "load": load_size * mean_distance_between_buses, - "sgen": sgen_size * mean_distance_between_buses, - "trafo": trafo_size * mean_distance_between_buses - } - return sizes - - def convert_epgs_bus_geodata(net, epsg_in=4326, epsg_out=31467): """ Converts bus geodata in net from epsg_in to epsg_out - Parameters - ---------- - net - epsg_in - 4326 = WGS 84 - epsg_out - 31467 = Gauss-Krüger Zone 3 + :param net: The pandapower network + :type net: pandapowerNet + :param epsg_in: current epsg projection + :type epsg_in: int, default 4326 (= WGS84) + :param epsg_out: epsg projection to be transformed to + :type epsg_out: int, default 31467 (= Gauss-Krüger Zone 3) + :return: net - the given pandapower network (no copy!) """ - inProj = Proj(init='epsg:%i' % epsg_in) - outProj = Proj(init='epsg:%i' % epsg_out) + in_proj = Proj(init='epsg:%i' % epsg_in) + out_proj = Proj(init='epsg:%i' % epsg_out) x1, y1 = net.bus_geodata.loc[:, "x"].values, net.bus_geodata.loc[:, "y"].values - net.bus_geodata.loc[:, "x"], net.bus_geodata.loc[:, "y"] = transform(inProj, outProj, x1, y1) + net.bus_geodata.loc[:, "x"], net.bus_geodata.loc[:, "y"] = transform(in_proj, out_proj, x1, y1) return net diff --git a/pandapower/plotting/patch_makers.py b/pandapower/plotting/patch_makers.py index dfac8f29c..2bfc832bf 100644 --- a/pandapower/plotting/patch_makers.py +++ b/pandapower/plotting/patch_makers.py @@ -1,5 +1,7 @@ from matplotlib.patches import RegularPolygon, Arc, Circle, Rectangle, Ellipse import numpy as np +from pandapower.plotting.plotting_toolbox import _rotate_dim2, get_color_list, get_angle_list + try: import pplog as logging except ImportError: @@ -8,26 +10,51 @@ logger = logging.getLogger(__name__) -def _rotate_dim2(arr, ang): - """ - :param arr: array with 2 dimensions - :param ang: angle [rad] +def load_patches(node_coords, size, angles, **kwargs): """ - return np.dot(np.array([[np.cos(ang), np.sin(ang)], [-np.sin(ang), np.cos(ang)]]), arr) + Creation function of patches for loads. - -def load_patches(node_coords, size, angles, **kwargs): + :param node_coords: coordinates of the nodes that the loads belong to. + :type node_coords: iterable + :param size: size of the patch + :type size: float + :param angles: angles by which to rotate the patches (in radians) + :type angles: iterable(float), float + :param kwargs: additional keyword arguments (might contain parameter "offset") + :type kwargs: + :return: Return values are: \ + - lines (list) - list of coordinates for lines leading to load patches\ + - polys (list of RegularPolygon) - list containing the load patches\ + - keywords (set) - set of keywords removed from kwargs + """ offset = kwargs.get("offset", size) + all_angles = get_angle_list(angles, len(node_coords)) polys, lines = list(), list() for i, node_geo in enumerate(node_coords): - p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), angles[i]) - p3 = node_geo + _rotate_dim2(np.array([0, offset + size / 3 * 2]), angles[i]) - polys.append(RegularPolygon(p2, numVertices=3, radius=size, orientation=-angles[i])) + p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), all_angles[i]) + p3 = node_geo + _rotate_dim2(np.array([0, offset + size / 3 * 2]), all_angles[i]) + polys.append(RegularPolygon(p2, numVertices=3, radius=size, orientation=-all_angles[i])) lines.append((node_geo, p3)) return lines, polys, {"offset"} def gen_patches(node_coords, size, angles, **kwargs): + """ + Creation function of patches for generators. + + :param node_coords: coordinates of the nodes that the generators belong to. + :type node_coords: iterable + :param size: size of the patch + :type size: float + :param angles: angles by which to rotate the patches (in radians) + :type angles: iterable(float), float + :param kwargs: additional keyword arguments (might contain parameter "offset") + :type kwargs: + :return: Return values are: \ + - lines (list) - list of coordinates for lines leading to generator patches\ + - polys (list of RegularPolygon) - list containing the generator patches\ + - keywords (set) - set of keywords removed from kwargs + """ polys, lines = list(), list() offset = kwargs.pop("offset", 2. * size) for i, node_geo in enumerate(node_coords): @@ -42,6 +69,22 @@ def gen_patches(node_coords, size, angles, **kwargs): def sgen_patches(node_coords, size, angles, **kwargs): + """ + Creation function of patches for static generators. + + :param node_coords: coordinates of the nodes that the static generators belong to. + :type node_coords: iterable + :param size: size of the patch + :type size: float + :param angles: angles by which to rotate the patches (in radians) + :type angles: iterable(float), float + :param kwargs: additional keyword arguments (might contain parameters "offset" and "r_triangle") + :type kwargs: + :return: Return values are: \ + - lines (list) - list of coordinates for lines leading to static generator patches\ + - polys (list of RegularPolygon) - list containing the static generator patches\ + - keywords (set) - set of keywords removed from kwargs + """ polys, lines = list(), list() offset = kwargs.pop("offset", size) r_triangle = kwargs.pop("r_triangles", size * 0.4) @@ -67,41 +110,49 @@ def sgen_patches(node_coords, size, angles, **kwargs): def ext_grid_patches(node_coords, size, angles, **kwargs): + """ + Creation function of patches for external grids. + + :param node_coords: coordinates of the nodes that the external grids belong to. + :type node_coords: iterable + :param size: size of the patch + :type size: float + :param angles: angles by which to rotate the patches (in radians) + :type angles: iterable(float), float + :param kwargs: additional keyword arguments (might contain parameter "offset") + :type kwargs: + :return: Return values are: \ + - lines (list) - list of coordinates for lines leading to external grid patches\ + - polys (list of RegularPolygon) - list containing the external grid patches\ + - keywords (set) - set of keywords removed from kwargs (empty + """ offset = kwargs.pop("offset", size / 2) polys, lines = list(), list() for i, node_geo in enumerate(node_coords): p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), angles[i]) polys.append(Rectangle((p2[0] - size / 2, p2[1] - size / 2), size, size)) lines.append((node_geo, p2 - _rotate_dim2(np.array([0, offset]), angles[i]))) - return lines, polys, set() - - -def get_list(individuals, number_entries, name_ind, name_ent): - if hasattr(individuals, "__iter__") and not isinstance(individuals, str): - if number_entries == len(individuals): - return individuals - elif number_entries > len(individuals): - logger.warning("The number of given %s (%d) is smaller than the number of %s (%d) to" - " draw! The %s will be repeated to fit." - % (name_ind, len(individuals), name_ent, number_entries, name_ind)) - return (individuals * (int(number_entries / len(individuals)) + 1))[:number_entries] - else: - logger.warning("The number of given %s (%d) is larger than the number of %s (%d) to" - " draw! The %s will be capped to fit." - % (name_ind, len(individuals), name_ent, number_entries, name_ind)) - return individuals[:number_entries] - return [individuals] * number_entries - - -def get_color_list(color, number_entries, name_entries="nodes"): - return get_list(color, number_entries, "colors", name_entries) - - -def get_angle_list(angle, number_entries, name_entries="nodes"): - return get_list(angle, number_entries, "angles", name_entries) + return lines, polys, set("offset") def ellipse_patches(node_coords, width, height, angle=0, color=None, **kwargs): + """ + Function to create a list of ellipse patches from node coordinates. + + :param node_coords: coordinates of the nodes to draw + :type node_coords: iterable + :param width: width of the ellipse (described by an exterior rectangle) + :type width: float + :param height: height of the ellipse (described by an exterior rectangle) + :type height: float + :param angle: angle by which to rotate the ellipse + :type angle: float + :param color: color or colors of the patches + :type color: iterable, float + :param kwargs: additional keyword arguments to pass to the Ellipse initialization + :type kwargs: dict + :return: patches - list of ellipse patches for the nodes + """ patches = list() angles = get_angle_list(angle, len(node_coords)) if color is not None: @@ -115,6 +166,21 @@ def ellipse_patches(node_coords, width, height, angle=0, color=None, **kwargs): def rectangle_patches(node_coords, width, height, color=None, **kwargs): + """ + Function to create a list of rectangle patches from node coordinates. + + :param node_coords: coordinates of the nodes to draw + :type node_coords: iterable + :param width: width of the rectangle + :type width: float + :param height: height of the rectangle + :type height: float + :param color: color or colors of the patches + :type color: iterable, float + :param kwargs: additional keyword arguments to pass to the Rectangle initialization + :type kwargs: dict + :return: patches - list of rectangle patches for the nodes + """ patches = list() if color is not None: colors = get_color_list(color, len(node_coords)) @@ -128,6 +194,22 @@ def rectangle_patches(node_coords, width, height, color=None, **kwargs): def polygon_patches(node_coords, radius, num_edges, color=None, **kwargs): + """ + Function to create a list of polygon patches from node coordinates. The number of edges for the + polygon can be defined. + + :param node_coords: coordinates of the nodes to draw + :type node_coords: iterable + :param radius: radius for the polygon (from centroid to edges) + :type radius: float + :param num_edges: number of edges of the polygon + :type num_edges: int + :param color: color or colors of the patches + :type color: iterable, float + :param kwargs: additional keyword arguments to pass to the Polygon initialization + :type kwargs: dict + :return: patches - list of rectangle patches for the nodes + """ patches = list() if color is not None: colors = get_color_list(color, len(node_coords)) @@ -141,16 +223,42 @@ def polygon_patches(node_coords, radius, num_edges, color=None, **kwargs): def node_patches(node_coords, size, patch_type, colors=None, **kwargs): - if patch_type == 'ellipse' or patch_type == 'circle': # circles are just ellipses - width = kwargs.pop("width", 2 * size) - height = kwargs.pop("height", 2 * size) + """ + Creates node patches from coordinates translating the patch type into patches. + + :param node_coords: coordinates of the nodes to draw + :type node_coords: iterable + :param size: size of the patch (can be interpreted differently, depending on the patch type) + :type size: float + :param patch_type: type of patches to create - can be one of + - "circle" or "ellipse" for an ellipse (cirlces are just ellipses with the same width \ + + height)\ + - "rect" or "rectangle" for a rectangle\ + - "poly" for a polygon with n edges + :type patch_type: str + :param colors: colors or color of the patches + :type colors: iterable, float + :param kwargs: additional keyword arguments to pass to the patch initialization \ + (might contain "width", "height", "angle" depending on the patch type) + :type kwargs: dict + :return: patches - list of rectangle patches for the nodes + """ + if patch_type.lower() == 'ellipse' or patch_type.lower() == 'circle': + # circles are just ellipses + if patch_type.lower() == "circle" and len(set(kwargs.keys()) & {"width", "height"}) == 1: + wh = kwargs["width"] if "width" in kwargs else kwargs["height"] + width = wh + height = wh + else: + width = kwargs.pop("width", 2 * size) + height = kwargs.pop("height", 2 * size) angle = kwargs.pop('angle', 0) return ellipse_patches(node_coords, width, height, angle, color=colors, **kwargs) - elif patch_type == "rect": + elif patch_type.lower() == "rect" or patch_type.lower() == "rectangle": width = kwargs.pop("width", 2 * size) height = kwargs.pop("height", 2 * size) return rectangle_patches(node_coords, width, height, color=colors, **kwargs) - elif patch_type.startswith("poly"): + elif patch_type.lower().startswith("poly"): edges = int(patch_type[4:]) return polygon_patches(node_coords, size, edges, color=colors, **kwargs) else: @@ -159,6 +267,22 @@ def node_patches(node_coords, size, patch_type, colors=None, **kwargs): def trafo_patches(coords, size, color): + """ + Creates a list of patches and line coordinates representing transformers each connecting two + nodes. + + :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 trafo patches + :type size: float + :param color: color or colors of the trafo patches (only edges and connecting lines, interior\ + is white) + :type color: iterable, float + :return: Return values are: \ + - lines (list) - list of coordinates for lines connecting nodes and transformer patches\ + - circles (list of Circle) - list containing the transformer patches (rings) + """ colors = get_color_list(color, len(coords)) circles, lines = list(), list() for (p1, p2), col in zip(coords, colors): diff --git a/pandapower/plotting/plotting_toolbox.py b/pandapower/plotting/plotting_toolbox.py new file mode 100644 index 000000000..86d2f44a4 --- /dev/null +++ b/pandapower/plotting/plotting_toolbox.py @@ -0,0 +1,212 @@ +import numpy as np +from pandapower.plotting import logger +from pandapower.plotting.patch_makers import logger + + +def _rotate_dim2(arr, ang): + """ + Rotate the input vector with the given angle. + + :param arr: array with 2 dimensions + :type arr: np.array + :param ang: angle [rad] + :type ang: float + """ + return np.dot(np.array([[np.cos(ang), np.sin(ang)], [-np.sin(ang), np.cos(ang)]]), arr) + + +def get_collection_sizes(net, bus_size=1.0, ext_grid_size=1.0, trafo_size=1.0, load_size=1.0, + sgen_size=1.0, switch_size=2.0, switch_distance=1.0): + """ + Calculates the size for most collection types according to the distance between min and max + geocoord so that the collections fit the plot nicely + + .. note: This is implemented because if you would choose a fixed values (e.g. bus_size = 0.2),\ + the size could be to small for large networks and vice versa + + :param net: pandapower network for which to create plot + :type net: pandapowerNet + :param bus_size: relative bus size + :type bus_size: float, default 1. + :param ext_grid_size: relative external grid size + :type ext_grid_size: float, default 1. + :param trafo_size: relative trafo size + :type trafo_size: float, default 1. + :param load_size: relative load size + :type load_size: float, default 1. + :param sgen_size: relative static generator size + :type sgen_size: float, default 1. + :param switch_size: relative switch size + :type switch_size: float, default 2. + :param switch_distance: relative distance between switches + :type switch_distance: float, default 1. + :return: sizes (dict) - dictionary containing all scaled sizes + """ + + mean_distance_between_buses = sum((net['bus_geodata'].max() - net[ + 'bus_geodata'].min()).dropna() / 200) + + sizes = { + "bus": bus_size * mean_distance_between_buses, + "ext_grid": ext_grid_size * mean_distance_between_buses * 1.5, + "switch": switch_size * mean_distance_between_buses * 1, + "switch_distance": switch_distance * mean_distance_between_buses * 2, + "load": load_size * mean_distance_between_buses, + "sgen": sgen_size * mean_distance_between_buses, + "trafo": trafo_size * mean_distance_between_buses + } + return sizes + + +def get_list(individuals, number_entries, name_ind, name_ent): + """ + Auxiliary function to create a list of specified length from an input value that could be either + an iterable or a single value. Strings are treated as non-iterables. In case of iterables and + the length not matching the specified length, the input values are repeated or capped to match + the specified length. + + :param individuals: list or other iterable to adapt to the given length + :type individuals: iterable + :param number_entries: length to which individuals shall be entended / capped + :type number_entries: int + :param name_ind: Name of the individuals (only for logging pupose). + :type name_ind: str + :param name_ent: Name of the entries to which the length belongs (only for logging pupose). + :type name_ent: str + :return: new_individuals (list) - a list of length __number_entries__ containing the \ + (extended / capped) individuals + """ + if hasattr(individuals, "__iter__") and not isinstance(individuals, str): + if number_entries == len(individuals): + return list(individuals) + elif number_entries > len(individuals): + logger.warning("The number of given %s (%d) is smaller than the number of %s (%d) to" + " draw! The %s will be repeated to fit." + % (name_ind, len(individuals), name_ent, number_entries, name_ind)) + return (list(individuals) * (int(number_entries / len(individuals)) + 1))[ + :number_entries] + else: + logger.warning("The number of given %s (%d) is larger than the number of %s (%d) to" + " draw! The %s will be capped to fit." + % (name_ind, len(individuals), name_ent, number_entries, name_ind)) + return list(individuals)[:number_entries] + return [individuals] * number_entries + + +def get_color_list(color, number_entries, name_entries="nodes"): + return get_list(color, number_entries, "colors", name_entries) + + +def get_angle_list(angle, number_entries, name_entries="nodes"): + return get_list(angle, number_entries, "angles", name_entries) + + +def coords_from_node_geodata(element_indices, from_nodes, to_nodes, node_geodata, table_name, + node_name="Bus"): + """ + 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 + the final list of coordinates. + + :param element_indices: Indices of the branch elements for which to find node geodata + :type element_indices: iterable + :param from_nodes: Indices of the starting nodes + :type from_nodes: iterable + :param to_nodes: Indices of the ending nodes + :type to_nodes: iterable + :param node_geodata: Dataframe containing x and y coordinates of the nodes + :type node_geodata: pd.DataFrame + :param table_name: Name of the table that the branches belong to (only for logging) + :type table_name: str + :param node_name: Name of the node type (only for logging) + :type node_name: str, default "Bus" + :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 + """ + have_geo = np.isin(from_nodes, node_geodata.index.values) \ + & np.isin(to_nodes, node_geodata.index.values) + elements_with_geo = 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) + if 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: + logger.warning("No coords found for %s %s. %s geodata is missing for those %s!" + % (table_name + "s", elements_without_geo, node_name, table_name + "s")) + return coords, elements_with_geo + + +def position_on_busbar(net, bus, busbar_coords): + """ + Checks if the first or the last coordinates of a line are on a bus + + :param net: The pandapower network + :type net: pandapowerNet + :param bus: ID of the target bus on one end of the line + :type bus: int + :param busbar_coords: The coordinates of the busbar (beginning and end point). + :type busbar_coords: array, shape= (,2L) + :return: intersection (tuple, shape= (2L,))- Intersection point of the line with the given bus.\ + Can be used for switch position + """ + # If the line has no Intersection line will be returned and the bus coordinates can be used to + # calculate the switch position + intersection = None + bus_coords = net.bus_geodata.loc[bus, "coords"] + # Checking if bus has "coords" - if it is a busbar + if bus_coords is not None and bus_coords is not np.NaN and busbar_coords is not None: + for i in range(len(bus_coords) - 1): + try: + # Calculating slope of busbar-line. If the busbar-line is vertical ZeroDivisionError + # occurs + m = (bus_coords[i + 1][1] - bus_coords[i][1]) / \ + (bus_coords[i + 1][0] - bus_coords[i][0]) + # Clculating the off-set of the busbar-line + b = bus_coords[i][1] - bus_coords[i][0] * m + # Checking if the first end of the line is on the busbar-line + if 0 == m * busbar_coords[0][0] + b - busbar_coords[0][1]: + # Checking if the end of the line is in the Range of the busbar-line + if bus_coords[i + 1][0] <= busbar_coords[0][0] <= bus_coords[i][0] \ + or bus_coords[i][0] <= busbar_coords[0][0] <= bus_coords[i + 1][0]: + # Intersection found. Breaking for-loop + intersection = busbar_coords[0] + break + # Checking if the second end of the line is on the busbar-line + elif 0 == m * busbar_coords[-1][0] + b - busbar_coords[-1][1]: + if bus_coords[i][0] >= busbar_coords[-1][0] >= bus_coords[i + 1][0] \ + or bus_coords[i][0] <= busbar_coords[-1][0] <= bus_coords[i + 1][0]: + # Intersection found. Breaking for-loop + intersection = busbar_coords[-1] + break + # If the busbar-line is a vertical line and the slope is infinitely + except ZeroDivisionError: + # Checking if the first line-end is at the same position + if bus_coords[i][0] == busbar_coords[0][0]: + # Checking if the first line-end is in the Range of the busbar-line + if bus_coords[i][1] >= busbar_coords[0][1] >= bus_coords[i + 1][1] \ + or bus_coords[i][1] <= busbar_coords[0][1] <= bus_coords[i + 1][1]: + # Intersection found. Breaking for-loop + intersection = busbar_coords[0] + break + # Checking if the second line-end is at the same position + elif bus_coords[i][0] == busbar_coords[-1][0]: + if bus_coords[i][1] >= busbar_coords[-1][1] >= bus_coords[i + 1][1] \ + or bus_coords[i][1] <= busbar_coords[-1][1] <= bus_coords[i + 1][1]: + # Intersection found. Breaking for-loop + intersection = busbar_coords[-1] + break + # If the bus has no "coords" it mus be a normal bus + elif bus_coords is np.NaN: + bus_geo = (net["bus_geodata"].loc[bus, "x"], net["bus_geodata"].loc[bus, "y"]) + # Checking if the first end of the line is on the bus + if bus_geo == busbar_coords[0]: + intersection = busbar_coords[0] + # Checking if the second end of the line is on the bus + elif bus_geo == busbar_coords[-1]: + intersection = busbar_coords[-1] + + return intersection diff --git a/pandapower/plotting/simple_plot.py b/pandapower/plotting/simple_plot.py index 7ada01fd3..08117bbbc 100644 --- a/pandapower/plotting/simple_plot.py +++ b/pandapower/plotting/simple_plot.py @@ -6,7 +6,7 @@ import matplotlib.pyplot as plt -from pandapower.plotting.geo import get_collection_sizes +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_sgen_collection, \ From 2d77879d203c1ee1764e94f9e48e494d9550cd60 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Wed, 20 Nov 2019 08:44:46 +0100 Subject: [PATCH 09/38] added repeat_info to some generic collection functions for easier handling --- pandapower/plotting/collections.py | 46 +++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index b02d3bc67..8a8289dae 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -198,9 +198,10 @@ def create_line2d_collection(coords, indices, infos=None, picker=False, **kwargs return lc -def create_node_element_collection(node_coords, patch_maker, size=1., infos=None, orientation=np.pi, - picker=False, patch_facecolor="w", patch_edgecolor="k", - line_color="k", **kwargs): +def create_node_element_collection(node_coords, patch_maker, size=1., infos=None, + repeat_infos=(1, 1), orientation=np.pi, picker=False, + patch_facecolor="w", patch_edgecolor="k", line_color="k", + **kwargs): """ Creates matplotlib collections of node elements. All node element collections usually consist of one patch collection representing the element itself and a small line collection that connects @@ -216,6 +217,9 @@ def create_node_element_collection(node_coords, patch_maker, size=1., infos=None :param infos: list of infos belonging to each of the elements (can be displayed when hovering \ over them) :type infos: iterable, default None + :param repeat_infos: determines how many times the info shall be repeated to match the number \ + of patches (first element) and lines (second element) returned by the patch maker + :type repeat_infos: tuple (length 2), default (1, 1) :param orientation: orientation of load collection. pi is directed downwards, increasing values\ lead to clockwise direction changes. :type orientation: float, default np.pi @@ -237,21 +241,27 @@ def create_node_element_collection(node_coords, patch_maker, size=1., infos=None angles = orientation if hasattr(orientation, '__iter__') else [orientation] * len(node_coords) assert len(node_coords) == len(angles), \ "The length of coordinates does not match the length of the orientation angles!" - infos = [] if infos is None else infos + if infos is None: + infos_pc = [] + infos_lc = [] + else: + infos_pc = list(np.repeat(infos, repeat_infos[0])) + infos_lc = list(np.repeat(infos, repeat_infos[1])) + lines, polys, popped_keywords = patch_maker(node_coords, size, angles, **kwargs) for kw in popped_keywords: kwargs.pop(kw) patch_coll = PatchCollection(polys, facecolor=patch_facecolor, edgecolor=patch_edgecolor, picker=picker, **kwargs) line_coll = LineCollection(lines, color=line_color, picker=picker, **kwargs) - patch_coll.info = infos - line_coll.info = infos + patch_coll.info = infos_pc + line_coll.info = infos_lc return patch_coll, line_coll -def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, colors=None, - picker=False, patch_facecolor="w", patch_edgecolor="k", - line_color="k", linewidths=2., **kwargs): +def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, repeat_infos=(2, 2), + colors=None, picker=False, patch_facecolor="w", + patch_edgecolor="k", line_color="k", linewidths=2., **kwargs): """ Creates a matplotlib line collection and a matplotlib patch collection representing a branch\ element that cannot be represented by just a line. @@ -267,6 +277,9 @@ def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, co :param infos: list of infos belonging to each of the branches (can be displayed when hovering \ over them) :type infos: iterable, default None + :param repeat_infos: determines how many times the info shall be repeated to match the number \ + of patches (first element) and lines (second element) returned by the patch maker + :type repeat_infos: tuple (length 2), default (1, 1) :param colors: colors or color of the branch patches :type colors: iterable, float :param picker: picker argument passed to the line collection @@ -286,7 +299,13 @@ def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, co - patch_coll - patch collection representing the branch element\ - line_coll - line collection connecting the patches with the nodes """ - infos = [] if infos is None else infos + if infos is None: + infos_pc = [] + infos_lc = [] + else: + infos_pc = list(np.repeat(infos, repeat_infos[0])) + infos_lc = list(np.repeat(infos, repeat_infos[1])) + lines, patches, keywords = patch_maker(coords, size, colors, **kwargs) for kw in keywords: kwargs.pop(kw) @@ -294,8 +313,8 @@ def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, co picker=picker, **kwargs) line_coll = LineCollection(lines, color=line_color, picker=picker, linewidths=linewidths, **kwargs) - patch_coll.info = infos - line_coll.info = infos + patch_coll.info = infos_pc + line_coll.info = infos_lc return patch_coll, line_coll @@ -596,8 +615,7 @@ def create_trafo_collection(net, trafos=None, picker=False, size=None, infofunc= z = net.res_trafo.loading_percent colors = [cmap(norm(z.at[idx])) for idx in trafos_with_geo] - infos = list(np.repeat([infofunc(i) for i in range(len(trafos_with_geo))], 2))\ - if infofunc is not None else [] + infos = [infofunc(i) for i in range(len(trafos_with_geo))] if infofunc is not None else [] lc, pc = create_complex_branch_collection(coords, trafo_patches, size, infos, colors=colors, picker=picker, linewidths=linewidths, **kwargs) From 510b6609aea8d0fc3a9c7d9c96294966b6eed887 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Wed, 20 Nov 2019 16:36:37 +0100 Subject: [PATCH 10/38] small correction in load patch maker --- pandapower/plotting/patch_makers.py | 4 ++-- pandapower/plotting/plotting_toolbox.py | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/pandapower/plotting/patch_makers.py b/pandapower/plotting/patch_makers.py index 2bfc832bf..64861e7af 100644 --- a/pandapower/plotting/patch_makers.py +++ b/pandapower/plotting/patch_makers.py @@ -32,7 +32,7 @@ def load_patches(node_coords, size, angles, **kwargs): polys, lines = list(), list() for i, node_geo in enumerate(node_coords): p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), all_angles[i]) - p3 = node_geo + _rotate_dim2(np.array([0, offset + size / 3 * 2]), all_angles[i]) + p3 = node_geo + _rotate_dim2(np.array([0, offset + size / 2]), all_angles[i]) polys.append(RegularPolygon(p2, numVertices=3, radius=size, orientation=-all_angles[i])) lines.append((node_geo, p3)) return lines, polys, {"offset"} @@ -290,7 +290,7 @@ def trafo_patches(coords, size, color): p2 = np.array(p2) if np.all(p1 == p2): continue - d = np.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) + d = np.sqrt(np.sum((p1 - p2) ** 2)) if size is None: size_this = np.sqrt(d) / 5 else: diff --git a/pandapower/plotting/plotting_toolbox.py b/pandapower/plotting/plotting_toolbox.py index 86d2f44a4..0b3a79340 100644 --- a/pandapower/plotting/plotting_toolbox.py +++ b/pandapower/plotting/plotting_toolbox.py @@ -1,6 +1,11 @@ import numpy as np -from pandapower.plotting import logger -from pandapower.plotting.patch_makers import logger + +try: + import pplog as logging +except ImportError: + import logging + +logger = logging.getLogger(__name__) def _rotate_dim2(arr, ang): From 576e05aab60dd50ddfe8f34290342cd4eefa603c Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Tue, 19 Nov 2019 16:12:33 +0100 Subject: [PATCH 11/38] in plotting module: - added docstrings - moved some functions to new module plotting_toolbox - some renaming --- pandapower/plotting/collections.py | 280 +++++++++++------------- pandapower/plotting/geo.py | 70 ++---- pandapower/plotting/patch_makers.py | 206 +++++++++++++---- pandapower/plotting/plotting_toolbox.py | 212 ++++++++++++++++++ pandapower/plotting/simple_plot.py | 2 +- 5 files changed, 523 insertions(+), 247 deletions(-) create mode 100644 pandapower/plotting/plotting_toolbox.py diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index 948919065..b02d3bc67 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -15,8 +15,10 @@ from matplotlib.textpath import TextPath from matplotlib.transforms import Affine2D from pandas import isnull -from pandapower.plotting.patch_makers import load_patches, _rotate_dim2, node_patches, gen_patches,\ +from pandapower.plotting.patch_makers import load_patches, node_patches, gen_patches,\ sgen_patches, ext_grid_patches, trafo_patches +from pandapower.plotting.plotting_toolbox import _rotate_dim2, coords_from_node_geodata, \ + position_on_busbar try: import pplog as logging @@ -86,24 +88,27 @@ def create_annotation_collection(texts, coords, size, prop=None, **kwargs): return PatchCollection(tp, **kwargs) -def coords_from_node_geodata(element_indices, from_nodes, to_nodes, node_geodata, table_name, - node_name="Bus"): - have_geo = np.isin(from_nodes, node_geodata.index.values) \ - & np.isin(to_nodes, node_geodata.index.values) - elements_with_geo = 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) - if 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: - logger.warning("No coords found for %s %s. %s geodata is missing for those %s!" - % (table_name + "s", elements_without_geo, node_name, table_name + "s")) - return coords, elements_with_geo - - def add_cmap_to_collection(collection, cmap, norm, z, cbar_title, plot_colormap=True, clim=None): + """ + Adds a colormap to the given collection. + + :param collection: collection for which to add colormap + :type collection: matplotlib.collections.collection + :param cmap: colormap which to use + :type cmap: any colormap from matplotlib.colors + :param norm: any norm which to use to translate values into colors + :type norm: any norm from matplotlib.colors + :param z: the array which to use in order to create the colors for the given collection + :type z: iterable + :param cbar_title: title of the colorbar + :type cbar_title: str + :param plot_colormap: flag whether the colormap is actually drawn (if False, is excluded in\ + :func:`add_single_collection`) + :type plot_colormap: bool, default True + :param clim: color limit of the collection + :type clim: list(float), default None + :return: collection - the given collection with added colormap (no copy!) + """ collection.set_cmap(cmap) collection.set_norm(norm) collection.set_array(np.ma.masked_invalid(z)) @@ -116,6 +121,32 @@ def add_cmap_to_collection(collection, cmap, norm, z, cbar_title, plot_colormap= def create_node_collection(nodes, coords, size=5, patch_type="circle", color=None, picker=False, infos=None, **kwargs): + """ + Creates a collection with patches for the given nodes. Can be used generically for different \ + types of nodes (bus in pandapower network, but also other nodes, e.g. in a networkx graph). + + :param nodes: indices of the nodes to plot + :type nodes: iterable + :param coords: list of node coordinates (shape (2, N)) + :type coords: iterable + :param size: size of the patches (handed over to patch creation function) + :type size: float + :param patch_type: type of patches that chall be created for the nodes - can be one of\ + - "circle" for a circle\ + - "rect" for a rectangle\ + - "poly" for a polygon with n edges + :type patch_type: str, default "circle" + :param color: colors or color of the node patches + :type color: iterable, float + :param picker: picker argument passed to the patch collection + :type picker: bool, default False + :param infos: list of infos belonging to each of the patches (can be displayed when hovering \ + over the elements) + :type infos: list, default None + :param kwargs: keyword arguments are passed to the patch maker and patch collection + :type kwargs: + :return: pc - patch collection for the nodes + """ if len(coords) == 0: return None @@ -141,7 +172,24 @@ def create_node_collection(nodes, coords, size=5, patch_type="circle", color=Non return pc -def create_line2d_collection(coords, indices, infos=None, picker=None, **kwargs): +def create_line2d_collection(coords, indices, infos=None, picker=False, **kwargs): + """ + Generic function to create a LineCollection from coordinates. + + :param coords: list of line coordinates (list should look like this: \ + `[[(x11, y11), (x12, y12), (x13, y13), ...], [(x21, y21), (x22, x23), ...], ...]`) + :type coords: list or np.array + :param indices: list of node indices + :type indices: list or np.array + :param infos: list of infos belonging to each of the lines (can be displayed when hovering \ + over them) + :type infos: list, default None + :param picker: picker argument passed to the line collection + :type picker: bool, default False + :param kwargs: keyword arguments are passed to the line collection + :type kwargs: + :return: lc - line collection for the given coordinates + """ # This would be done anyways by matplotlib - doing it explicitly makes it a) clear and # b) prevents unexpected behavior when observing colors being "none" lc = LineCollection(coords, picker=picker, **kwargs) @@ -158,32 +206,33 @@ def create_node_element_collection(node_coords, patch_maker, size=1., infos=None one patch collection representing the element itself and a small line collection that connects the element to the respective node. - Input: - **node_coords** (iterable) - the coordinates (x, y) of the nodes with shape (N, 2) - - **patch_maker** (function) - a function to generate the patches of which the collections \ - consist (cf. the patch_maker module) - - OPTIONAL: - **size** (float, 1) - patch size - - **infos** (iterable, None) - additional infos to add for each element - - **orientation** (float, np.pi) - orientation of load collection. pi is directed downwards, - increasing values lead to clockwise direction changes. - - **patch_facecolor** (matplotlib color, "w") - color of the patch face (content) - - **patch_edgecolor** (matplotlib color, "k") - color of the patch edges - - **line_color** (matplotlib color, "k") - color of the connecting lines - - **kwargs - key word arguments are passed to the patch function - - OUTPUT: - **patch_coll** - patch collection representing the element + :param node_coords: the coordinates (x, y) of the nodes with shape (N, 2) + :type node_coords: iterable + :param patch_maker: a function to generate the patches of which the collections consist (cf. \ + the patch_maker module) + :type patch_maker: function + :param size: patch size + :type size: float, default 1 + :param infos: list of infos belonging to each of the elements (can be displayed when hovering \ + over them) + :type infos: iterable, default None + :param orientation: orientation of load collection. pi is directed downwards, increasing values\ + lead to clockwise direction changes. + :type orientation: float, default np.pi + :param picker: picker argument passed to the line collection + :type picker: bool, default False + :param patch_facecolor: color of the patch face (content) + :type patch_facecolor: matplotlib color, "w" + :param patch_edgecolor: color of the patch edges + :type patch_edgecolor: matplotlib color, "k" + :param line_color: color of the connecting lines + :type line_color: matplotlib color, "k" + :param kwargs: key word arguments are passed to the patch function + :type kwargs: + :return: Return values:\ + - patch_coll - patch collection representing the element\ + - line_coll - connecting line collection - **line_coll** - connecting line collection """ angles = orientation if hasattr(orientation, '__iter__') else [orientation] * len(node_coords) assert len(node_coords) == len(angles), \ @@ -204,31 +253,38 @@ def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, co picker=False, patch_facecolor="w", patch_edgecolor="k", line_color="k", linewidths=2., **kwargs): """ - - :param coords: - :type coords: - :param patch_maker: - :type patch_maker: - :param size: - :type size: - :param infos: - :type infos: - :param colors: - :type colors: - :param picker: - :type picker: - :param patch_facecolor: - :type patch_facecolor: - :param patch_edgecolor: - :type patch_edgecolor: - :param line_color: - :type line_color: - :param linewidths: - :type linewidths: - :param kwargs: + Creates a matplotlib line collection and a matplotlib patch collection representing a branch\ + element that cannot be represented by just a line. + + :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 patch_maker: a function to generate the patches of which the collections consist (cf. \ + the patch_maker module) + :type patch_maker: function + :param size: patch size + :type size: float, default 1 + :param infos: list of infos belonging to each of the branches (can be displayed when hovering \ + over them) + :type infos: iterable, default None + :param colors: colors or color of the branch patches + :type colors: iterable, float + :param picker: picker argument passed to the line collection + :type picker: bool, default False + :param patch_facecolor: color of the patch face (content) + :type patch_facecolor: matplotlib color, "w" + :param patch_edgecolor: color of the patch edges + :type patch_edgecolor: matplotlib color, "k" + :param line_color: color of the connecting lines + :type line_color: matplotlib color, "k" + :param linewidths: linewidths of the connecting lines and the patch edges + :type linewidths: float, default 2. + :param kwargs: key word arguments are passed to the patch maker and the patch and line \ + collections :type kwargs: - :return: - :rtype: + :return: Return values:\ + - patch_coll - patch collection representing the branch element\ + - line_coll - line collection connecting the patches with the nodes """ infos = [] if infos is None else infos lines, patches, keywords = patch_maker(coords, size, colors, **kwargs) @@ -553,11 +609,9 @@ def create_trafo_collection(net, trafos=None, picker=False, size=None, infofunc= # noinspection PyArgumentList -def create_trafo3w_collection(net, trafo3ws=None, picker=False, infofunc=None, - cmap=None, norm=None, z=None, clim=None, - cbar_title="3W-Transformer Loading", - plot_colormap=True, - **kwargs): +def create_trafo3w_collection(net, trafo3ws=None, picker=False, infofunc=None, cmap=None, norm=None, + z=None, clim=None, cbar_title="3W-Transformer Loading", + plot_colormap=True, **kwargs): """ Creates a matplotlib line collection of pandapower transformers. @@ -849,84 +903,6 @@ def create_ext_grid_collection(net, size=1., infofunc=None, orientation=0, picke return ext_grid_pc, ext_grid_lc -def onBusbar(net, bus, line_coords): - """ - Checks if the first or the last coordinates of a line are on a bus - - Input: - **net** (pandapowerNet) - The pandapower network - - **bus** (int) - ID of the target bus on one end of the line - - **line_coords (array, shape= (,2L)) - - The linegeodata of the line. The first row should be the coordinates - of bus a and the last should be the coordinates of bus b. The points - in the middle represent the bending points of the line - - OUTPUT: - **intersection** (tupel, shape= (2L,))- Intersection point of the line with the given bus. \ - Can be used for switch position - - """ - # If the line has no Intersection line will be returned and the bus coordinates can be used to - # calculate the switch position - intersection = None - bus_coords = net.bus_geodata.loc[bus, "coords"] - # Checking if bus has "coords" - if it is a busbar - if bus_coords is not None and bus_coords is not np.NaN and line_coords is not None: - for i in range(len(bus_coords) - 1): - try: - # Calculating slope of busbar-line. If the busbar-line is vertical ZeroDivisionError - # occurs - m = (bus_coords[i + 1][1] - bus_coords[i][1]) / \ - (bus_coords[i + 1][0] - bus_coords[i][0]) - # Clculating the off-set of the busbar-line - b = bus_coords[i][1] - bus_coords[i][0] * m - # Checking if the first end of the line is on the busbar-line - if 0 == m * line_coords[0][0] + b - line_coords[0][1]: - # Checking if the end of the line is in the Range of the busbar-line - if bus_coords[i + 1][0] <= line_coords[0][0] <= bus_coords[i][0]\ - or bus_coords[i][0] <= line_coords[0][0] <= bus_coords[i + 1][0]: - # Intersection found. Breaking for-loop - intersection = line_coords[0] - break - # Checking if the second end of the line is on the busbar-line - elif 0 == m * line_coords[-1][0] + b - line_coords[-1][1]: - if bus_coords[i][0] >= line_coords[-1][0] >= bus_coords[i + 1][0] \ - or bus_coords[i][0] <= line_coords[-1][0] <= bus_coords[i + 1][0]: - # Intersection found. Breaking for-loop - intersection = line_coords[-1] - break - # If the busbar-line is a vertical line and the slope is infinitely - except ZeroDivisionError: - # Checking if the first line-end is at the same position - if bus_coords[i][0] == line_coords[0][0]: - # Checking if the first line-end is in the Range of the busbar-line - if bus_coords[i][1] >= line_coords[0][1] >= bus_coords[i + 1][1] \ - or bus_coords[i][1] <= line_coords[0][1] <= bus_coords[i + 1][1]: - # Intersection found. Breaking for-loop - intersection = line_coords[0] - break - # Checking if the second line-end is at the same position - elif bus_coords[i][0] == line_coords[-1][0]: - if bus_coords[i][1] >= line_coords[-1][1] >= bus_coords[i + 1][1] \ - or bus_coords[i][1] <= line_coords[-1][1] <= bus_coords[i + 1][1]: - # Intersection found. Breaking for-loop - intersection = line_coords[-1] - break - # If the bus has no "coords" it mus be a normal bus - elif bus_coords is np.NaN: - bus_geo = (net["bus_geodata"].loc[bus, "x"], net["bus_geodata"].loc[bus, "y"]) - # Checking if the first end of the line is on the bus - if bus_geo == line_coords[0]: - intersection = line_coords[0] - # Checking if the second end of the line is on the bus - elif bus_geo == line_coords[-1]: - intersection = line_coords[-1] - - return intersection - - def create_line_switch_collection(net, size=1, distance_to_bus=3, use_line_geodata=False, **kwargs): """ Creates a matplotlib patch collection of pandapower line-bus switches. @@ -976,7 +952,7 @@ def create_line_switch_collection(net, size=1, distance_to_bus=3, use_line_geoda if line.name in net.line_geodata.index: line_coords = net.line_geodata.coords.loc[line.name] # check, which end of the line is nearer to the switch bus - intersection = onBusbar(net, target_bus, line_coords=line_coords) + intersection = position_on_busbar(net, target_bus, busbar_coords=line_coords) if intersection is not None: pos_sb = intersection if len(line_coords) >= 2: diff --git a/pandapower/plotting/geo.py b/pandapower/plotting/geo.py index 5103b34f1..91ef674dd 100644 --- a/pandapower/plotting/geo.py +++ b/pandapower/plotting/geo.py @@ -9,14 +9,16 @@ def convert_geodata_to_gis(net, epsg=31467, bus_geodata=True, line_geodata=True): if bus_geodata: - g = net.bus_geodata - geo = [Point(x, y) for x, y in g[["x", "y"]].values] - net.bus_geodata = GeoDataFrame(g, crs=from_epsg(epsg), geometry=geo, index=g.index) + bus_geo = net.bus_geodata + geo = [Point(x, y) for x, y in bus_geo[["x", "y"]].values] + net.bus_geodata = GeoDataFrame(bus_geo, crs=from_epsg(epsg), geometry=geo, + index=bus_geo.index) if line_geodata: - l = net.line_geodata + line_geo = net.line_geodata geo = GeoSeries([LineString(x) for x in net.line_geodata.coords.values], index=net.line_geodata.index, crs=from_epsg(epsg)) - net.line_geodata = GeoDataFrame(l, crs=from_epsg(epsg), geometry=geo, index=l.index) + net.line_geodata = GeoDataFrame(line_geo, crs=from_epsg(epsg), geometry=geo, + index=line_geo.index) net["gis_epsg_code"] = epsg @@ -28,58 +30,20 @@ def convert_gis_to_geodata(net, bus_geodata=True, line_geodata=True): net.line_geodata["coords"] = net.line_geodata.geometry.apply(lambda x: list(x.coords)) -def get_collection_sizes(net, bus_size=1.0, ext_grid_size=1.0, trafo_size=1.0, load_size=1.0, - sgen_size=1.0, switch_size=2.0, switch_distance=1.0): - """ - Calculates the size for most collection types according to the distance between min and max - geocoord so that the collections fit the plot nicely - - # Comment: This is implemented because if you would choose a fixed values - # (e.g. bus_size = 0.2), the size - # could be to small for large networks and vice versa - INPUT - - net - pp net - bus_size (float) - ext_grid_size (float) - trafo_size (float) - load_size (float) - sgen_size (float) - switch_size (float) - switch_distance (float) - - Returns - - sizes (dict) - containing all scaled sizes in a dict - """ - - mean_distance_between_buses = sum((net['bus_geodata'].max() - net[ - 'bus_geodata'].min()).dropna() / 200) - - sizes = { - "bus": bus_size * mean_distance_between_buses, - "ext_grid": ext_grid_size * mean_distance_between_buses * 1.5, - "switch": switch_size * mean_distance_between_buses * 1, - "switch_distance": switch_distance * mean_distance_between_buses * 2, - "load": load_size * mean_distance_between_buses, - "sgen": sgen_size * mean_distance_between_buses, - "trafo": trafo_size * mean_distance_between_buses - } - return sizes - - def convert_epgs_bus_geodata(net, epsg_in=4326, epsg_out=31467): """ Converts bus geodata in net from epsg_in to epsg_out - Parameters - ---------- - net - epsg_in - 4326 = WGS 84 - epsg_out - 31467 = Gauss-Krüger Zone 3 + :param net: The pandapower network + :type net: pandapowerNet + :param epsg_in: current epsg projection + :type epsg_in: int, default 4326 (= WGS84) + :param epsg_out: epsg projection to be transformed to + :type epsg_out: int, default 31467 (= Gauss-Krüger Zone 3) + :return: net - the given pandapower network (no copy!) """ - inProj = Proj(init='epsg:%i' % epsg_in) - outProj = Proj(init='epsg:%i' % epsg_out) + in_proj = Proj(init='epsg:%i' % epsg_in) + out_proj = Proj(init='epsg:%i' % epsg_out) x1, y1 = net.bus_geodata.loc[:, "x"].values, net.bus_geodata.loc[:, "y"].values - net.bus_geodata.loc[:, "x"], net.bus_geodata.loc[:, "y"] = transform(inProj, outProj, x1, y1) + net.bus_geodata.loc[:, "x"], net.bus_geodata.loc[:, "y"] = transform(in_proj, out_proj, x1, y1) return net diff --git a/pandapower/plotting/patch_makers.py b/pandapower/plotting/patch_makers.py index dfac8f29c..2bfc832bf 100644 --- a/pandapower/plotting/patch_makers.py +++ b/pandapower/plotting/patch_makers.py @@ -1,5 +1,7 @@ from matplotlib.patches import RegularPolygon, Arc, Circle, Rectangle, Ellipse import numpy as np +from pandapower.plotting.plotting_toolbox import _rotate_dim2, get_color_list, get_angle_list + try: import pplog as logging except ImportError: @@ -8,26 +10,51 @@ logger = logging.getLogger(__name__) -def _rotate_dim2(arr, ang): - """ - :param arr: array with 2 dimensions - :param ang: angle [rad] +def load_patches(node_coords, size, angles, **kwargs): """ - return np.dot(np.array([[np.cos(ang), np.sin(ang)], [-np.sin(ang), np.cos(ang)]]), arr) + Creation function of patches for loads. - -def load_patches(node_coords, size, angles, **kwargs): + :param node_coords: coordinates of the nodes that the loads belong to. + :type node_coords: iterable + :param size: size of the patch + :type size: float + :param angles: angles by which to rotate the patches (in radians) + :type angles: iterable(float), float + :param kwargs: additional keyword arguments (might contain parameter "offset") + :type kwargs: + :return: Return values are: \ + - lines (list) - list of coordinates for lines leading to load patches\ + - polys (list of RegularPolygon) - list containing the load patches\ + - keywords (set) - set of keywords removed from kwargs + """ offset = kwargs.get("offset", size) + all_angles = get_angle_list(angles, len(node_coords)) polys, lines = list(), list() for i, node_geo in enumerate(node_coords): - p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), angles[i]) - p3 = node_geo + _rotate_dim2(np.array([0, offset + size / 3 * 2]), angles[i]) - polys.append(RegularPolygon(p2, numVertices=3, radius=size, orientation=-angles[i])) + p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), all_angles[i]) + p3 = node_geo + _rotate_dim2(np.array([0, offset + size / 3 * 2]), all_angles[i]) + polys.append(RegularPolygon(p2, numVertices=3, radius=size, orientation=-all_angles[i])) lines.append((node_geo, p3)) return lines, polys, {"offset"} def gen_patches(node_coords, size, angles, **kwargs): + """ + Creation function of patches for generators. + + :param node_coords: coordinates of the nodes that the generators belong to. + :type node_coords: iterable + :param size: size of the patch + :type size: float + :param angles: angles by which to rotate the patches (in radians) + :type angles: iterable(float), float + :param kwargs: additional keyword arguments (might contain parameter "offset") + :type kwargs: + :return: Return values are: \ + - lines (list) - list of coordinates for lines leading to generator patches\ + - polys (list of RegularPolygon) - list containing the generator patches\ + - keywords (set) - set of keywords removed from kwargs + """ polys, lines = list(), list() offset = kwargs.pop("offset", 2. * size) for i, node_geo in enumerate(node_coords): @@ -42,6 +69,22 @@ def gen_patches(node_coords, size, angles, **kwargs): def sgen_patches(node_coords, size, angles, **kwargs): + """ + Creation function of patches for static generators. + + :param node_coords: coordinates of the nodes that the static generators belong to. + :type node_coords: iterable + :param size: size of the patch + :type size: float + :param angles: angles by which to rotate the patches (in radians) + :type angles: iterable(float), float + :param kwargs: additional keyword arguments (might contain parameters "offset" and "r_triangle") + :type kwargs: + :return: Return values are: \ + - lines (list) - list of coordinates for lines leading to static generator patches\ + - polys (list of RegularPolygon) - list containing the static generator patches\ + - keywords (set) - set of keywords removed from kwargs + """ polys, lines = list(), list() offset = kwargs.pop("offset", size) r_triangle = kwargs.pop("r_triangles", size * 0.4) @@ -67,41 +110,49 @@ def sgen_patches(node_coords, size, angles, **kwargs): def ext_grid_patches(node_coords, size, angles, **kwargs): + """ + Creation function of patches for external grids. + + :param node_coords: coordinates of the nodes that the external grids belong to. + :type node_coords: iterable + :param size: size of the patch + :type size: float + :param angles: angles by which to rotate the patches (in radians) + :type angles: iterable(float), float + :param kwargs: additional keyword arguments (might contain parameter "offset") + :type kwargs: + :return: Return values are: \ + - lines (list) - list of coordinates for lines leading to external grid patches\ + - polys (list of RegularPolygon) - list containing the external grid patches\ + - keywords (set) - set of keywords removed from kwargs (empty + """ offset = kwargs.pop("offset", size / 2) polys, lines = list(), list() for i, node_geo in enumerate(node_coords): p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), angles[i]) polys.append(Rectangle((p2[0] - size / 2, p2[1] - size / 2), size, size)) lines.append((node_geo, p2 - _rotate_dim2(np.array([0, offset]), angles[i]))) - return lines, polys, set() - - -def get_list(individuals, number_entries, name_ind, name_ent): - if hasattr(individuals, "__iter__") and not isinstance(individuals, str): - if number_entries == len(individuals): - return individuals - elif number_entries > len(individuals): - logger.warning("The number of given %s (%d) is smaller than the number of %s (%d) to" - " draw! The %s will be repeated to fit." - % (name_ind, len(individuals), name_ent, number_entries, name_ind)) - return (individuals * (int(number_entries / len(individuals)) + 1))[:number_entries] - else: - logger.warning("The number of given %s (%d) is larger than the number of %s (%d) to" - " draw! The %s will be capped to fit." - % (name_ind, len(individuals), name_ent, number_entries, name_ind)) - return individuals[:number_entries] - return [individuals] * number_entries - - -def get_color_list(color, number_entries, name_entries="nodes"): - return get_list(color, number_entries, "colors", name_entries) - - -def get_angle_list(angle, number_entries, name_entries="nodes"): - return get_list(angle, number_entries, "angles", name_entries) + return lines, polys, set("offset") def ellipse_patches(node_coords, width, height, angle=0, color=None, **kwargs): + """ + Function to create a list of ellipse patches from node coordinates. + + :param node_coords: coordinates of the nodes to draw + :type node_coords: iterable + :param width: width of the ellipse (described by an exterior rectangle) + :type width: float + :param height: height of the ellipse (described by an exterior rectangle) + :type height: float + :param angle: angle by which to rotate the ellipse + :type angle: float + :param color: color or colors of the patches + :type color: iterable, float + :param kwargs: additional keyword arguments to pass to the Ellipse initialization + :type kwargs: dict + :return: patches - list of ellipse patches for the nodes + """ patches = list() angles = get_angle_list(angle, len(node_coords)) if color is not None: @@ -115,6 +166,21 @@ def ellipse_patches(node_coords, width, height, angle=0, color=None, **kwargs): def rectangle_patches(node_coords, width, height, color=None, **kwargs): + """ + Function to create a list of rectangle patches from node coordinates. + + :param node_coords: coordinates of the nodes to draw + :type node_coords: iterable + :param width: width of the rectangle + :type width: float + :param height: height of the rectangle + :type height: float + :param color: color or colors of the patches + :type color: iterable, float + :param kwargs: additional keyword arguments to pass to the Rectangle initialization + :type kwargs: dict + :return: patches - list of rectangle patches for the nodes + """ patches = list() if color is not None: colors = get_color_list(color, len(node_coords)) @@ -128,6 +194,22 @@ def rectangle_patches(node_coords, width, height, color=None, **kwargs): def polygon_patches(node_coords, radius, num_edges, color=None, **kwargs): + """ + Function to create a list of polygon patches from node coordinates. The number of edges for the + polygon can be defined. + + :param node_coords: coordinates of the nodes to draw + :type node_coords: iterable + :param radius: radius for the polygon (from centroid to edges) + :type radius: float + :param num_edges: number of edges of the polygon + :type num_edges: int + :param color: color or colors of the patches + :type color: iterable, float + :param kwargs: additional keyword arguments to pass to the Polygon initialization + :type kwargs: dict + :return: patches - list of rectangle patches for the nodes + """ patches = list() if color is not None: colors = get_color_list(color, len(node_coords)) @@ -141,16 +223,42 @@ def polygon_patches(node_coords, radius, num_edges, color=None, **kwargs): def node_patches(node_coords, size, patch_type, colors=None, **kwargs): - if patch_type == 'ellipse' or patch_type == 'circle': # circles are just ellipses - width = kwargs.pop("width", 2 * size) - height = kwargs.pop("height", 2 * size) + """ + Creates node patches from coordinates translating the patch type into patches. + + :param node_coords: coordinates of the nodes to draw + :type node_coords: iterable + :param size: size of the patch (can be interpreted differently, depending on the patch type) + :type size: float + :param patch_type: type of patches to create - can be one of + - "circle" or "ellipse" for an ellipse (cirlces are just ellipses with the same width \ + + height)\ + - "rect" or "rectangle" for a rectangle\ + - "poly" for a polygon with n edges + :type patch_type: str + :param colors: colors or color of the patches + :type colors: iterable, float + :param kwargs: additional keyword arguments to pass to the patch initialization \ + (might contain "width", "height", "angle" depending on the patch type) + :type kwargs: dict + :return: patches - list of rectangle patches for the nodes + """ + if patch_type.lower() == 'ellipse' or patch_type.lower() == 'circle': + # circles are just ellipses + if patch_type.lower() == "circle" and len(set(kwargs.keys()) & {"width", "height"}) == 1: + wh = kwargs["width"] if "width" in kwargs else kwargs["height"] + width = wh + height = wh + else: + width = kwargs.pop("width", 2 * size) + height = kwargs.pop("height", 2 * size) angle = kwargs.pop('angle', 0) return ellipse_patches(node_coords, width, height, angle, color=colors, **kwargs) - elif patch_type == "rect": + elif patch_type.lower() == "rect" or patch_type.lower() == "rectangle": width = kwargs.pop("width", 2 * size) height = kwargs.pop("height", 2 * size) return rectangle_patches(node_coords, width, height, color=colors, **kwargs) - elif patch_type.startswith("poly"): + elif patch_type.lower().startswith("poly"): edges = int(patch_type[4:]) return polygon_patches(node_coords, size, edges, color=colors, **kwargs) else: @@ -159,6 +267,22 @@ def node_patches(node_coords, size, patch_type, colors=None, **kwargs): def trafo_patches(coords, size, color): + """ + Creates a list of patches and line coordinates representing transformers each connecting two + nodes. + + :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 trafo patches + :type size: float + :param color: color or colors of the trafo patches (only edges and connecting lines, interior\ + is white) + :type color: iterable, float + :return: Return values are: \ + - lines (list) - list of coordinates for lines connecting nodes and transformer patches\ + - circles (list of Circle) - list containing the transformer patches (rings) + """ colors = get_color_list(color, len(coords)) circles, lines = list(), list() for (p1, p2), col in zip(coords, colors): diff --git a/pandapower/plotting/plotting_toolbox.py b/pandapower/plotting/plotting_toolbox.py new file mode 100644 index 000000000..86d2f44a4 --- /dev/null +++ b/pandapower/plotting/plotting_toolbox.py @@ -0,0 +1,212 @@ +import numpy as np +from pandapower.plotting import logger +from pandapower.plotting.patch_makers import logger + + +def _rotate_dim2(arr, ang): + """ + Rotate the input vector with the given angle. + + :param arr: array with 2 dimensions + :type arr: np.array + :param ang: angle [rad] + :type ang: float + """ + return np.dot(np.array([[np.cos(ang), np.sin(ang)], [-np.sin(ang), np.cos(ang)]]), arr) + + +def get_collection_sizes(net, bus_size=1.0, ext_grid_size=1.0, trafo_size=1.0, load_size=1.0, + sgen_size=1.0, switch_size=2.0, switch_distance=1.0): + """ + Calculates the size for most collection types according to the distance between min and max + geocoord so that the collections fit the plot nicely + + .. note: This is implemented because if you would choose a fixed values (e.g. bus_size = 0.2),\ + the size could be to small for large networks and vice versa + + :param net: pandapower network for which to create plot + :type net: pandapowerNet + :param bus_size: relative bus size + :type bus_size: float, default 1. + :param ext_grid_size: relative external grid size + :type ext_grid_size: float, default 1. + :param trafo_size: relative trafo size + :type trafo_size: float, default 1. + :param load_size: relative load size + :type load_size: float, default 1. + :param sgen_size: relative static generator size + :type sgen_size: float, default 1. + :param switch_size: relative switch size + :type switch_size: float, default 2. + :param switch_distance: relative distance between switches + :type switch_distance: float, default 1. + :return: sizes (dict) - dictionary containing all scaled sizes + """ + + mean_distance_between_buses = sum((net['bus_geodata'].max() - net[ + 'bus_geodata'].min()).dropna() / 200) + + sizes = { + "bus": bus_size * mean_distance_between_buses, + "ext_grid": ext_grid_size * mean_distance_between_buses * 1.5, + "switch": switch_size * mean_distance_between_buses * 1, + "switch_distance": switch_distance * mean_distance_between_buses * 2, + "load": load_size * mean_distance_between_buses, + "sgen": sgen_size * mean_distance_between_buses, + "trafo": trafo_size * mean_distance_between_buses + } + return sizes + + +def get_list(individuals, number_entries, name_ind, name_ent): + """ + Auxiliary function to create a list of specified length from an input value that could be either + an iterable or a single value. Strings are treated as non-iterables. In case of iterables and + the length not matching the specified length, the input values are repeated or capped to match + the specified length. + + :param individuals: list or other iterable to adapt to the given length + :type individuals: iterable + :param number_entries: length to which individuals shall be entended / capped + :type number_entries: int + :param name_ind: Name of the individuals (only for logging pupose). + :type name_ind: str + :param name_ent: Name of the entries to which the length belongs (only for logging pupose). + :type name_ent: str + :return: new_individuals (list) - a list of length __number_entries__ containing the \ + (extended / capped) individuals + """ + if hasattr(individuals, "__iter__") and not isinstance(individuals, str): + if number_entries == len(individuals): + return list(individuals) + elif number_entries > len(individuals): + logger.warning("The number of given %s (%d) is smaller than the number of %s (%d) to" + " draw! The %s will be repeated to fit." + % (name_ind, len(individuals), name_ent, number_entries, name_ind)) + return (list(individuals) * (int(number_entries / len(individuals)) + 1))[ + :number_entries] + else: + logger.warning("The number of given %s (%d) is larger than the number of %s (%d) to" + " draw! The %s will be capped to fit." + % (name_ind, len(individuals), name_ent, number_entries, name_ind)) + return list(individuals)[:number_entries] + return [individuals] * number_entries + + +def get_color_list(color, number_entries, name_entries="nodes"): + return get_list(color, number_entries, "colors", name_entries) + + +def get_angle_list(angle, number_entries, name_entries="nodes"): + return get_list(angle, number_entries, "angles", name_entries) + + +def coords_from_node_geodata(element_indices, from_nodes, to_nodes, node_geodata, table_name, + node_name="Bus"): + """ + 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 + the final list of coordinates. + + :param element_indices: Indices of the branch elements for which to find node geodata + :type element_indices: iterable + :param from_nodes: Indices of the starting nodes + :type from_nodes: iterable + :param to_nodes: Indices of the ending nodes + :type to_nodes: iterable + :param node_geodata: Dataframe containing x and y coordinates of the nodes + :type node_geodata: pd.DataFrame + :param table_name: Name of the table that the branches belong to (only for logging) + :type table_name: str + :param node_name: Name of the node type (only for logging) + :type node_name: str, default "Bus" + :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 + """ + have_geo = np.isin(from_nodes, node_geodata.index.values) \ + & np.isin(to_nodes, node_geodata.index.values) + elements_with_geo = 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) + if 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: + logger.warning("No coords found for %s %s. %s geodata is missing for those %s!" + % (table_name + "s", elements_without_geo, node_name, table_name + "s")) + return coords, elements_with_geo + + +def position_on_busbar(net, bus, busbar_coords): + """ + Checks if the first or the last coordinates of a line are on a bus + + :param net: The pandapower network + :type net: pandapowerNet + :param bus: ID of the target bus on one end of the line + :type bus: int + :param busbar_coords: The coordinates of the busbar (beginning and end point). + :type busbar_coords: array, shape= (,2L) + :return: intersection (tuple, shape= (2L,))- Intersection point of the line with the given bus.\ + Can be used for switch position + """ + # If the line has no Intersection line will be returned and the bus coordinates can be used to + # calculate the switch position + intersection = None + bus_coords = net.bus_geodata.loc[bus, "coords"] + # Checking if bus has "coords" - if it is a busbar + if bus_coords is not None and bus_coords is not np.NaN and busbar_coords is not None: + for i in range(len(bus_coords) - 1): + try: + # Calculating slope of busbar-line. If the busbar-line is vertical ZeroDivisionError + # occurs + m = (bus_coords[i + 1][1] - bus_coords[i][1]) / \ + (bus_coords[i + 1][0] - bus_coords[i][0]) + # Clculating the off-set of the busbar-line + b = bus_coords[i][1] - bus_coords[i][0] * m + # Checking if the first end of the line is on the busbar-line + if 0 == m * busbar_coords[0][0] + b - busbar_coords[0][1]: + # Checking if the end of the line is in the Range of the busbar-line + if bus_coords[i + 1][0] <= busbar_coords[0][0] <= bus_coords[i][0] \ + or bus_coords[i][0] <= busbar_coords[0][0] <= bus_coords[i + 1][0]: + # Intersection found. Breaking for-loop + intersection = busbar_coords[0] + break + # Checking if the second end of the line is on the busbar-line + elif 0 == m * busbar_coords[-1][0] + b - busbar_coords[-1][1]: + if bus_coords[i][0] >= busbar_coords[-1][0] >= bus_coords[i + 1][0] \ + or bus_coords[i][0] <= busbar_coords[-1][0] <= bus_coords[i + 1][0]: + # Intersection found. Breaking for-loop + intersection = busbar_coords[-1] + break + # If the busbar-line is a vertical line and the slope is infinitely + except ZeroDivisionError: + # Checking if the first line-end is at the same position + if bus_coords[i][0] == busbar_coords[0][0]: + # Checking if the first line-end is in the Range of the busbar-line + if bus_coords[i][1] >= busbar_coords[0][1] >= bus_coords[i + 1][1] \ + or bus_coords[i][1] <= busbar_coords[0][1] <= bus_coords[i + 1][1]: + # Intersection found. Breaking for-loop + intersection = busbar_coords[0] + break + # Checking if the second line-end is at the same position + elif bus_coords[i][0] == busbar_coords[-1][0]: + if bus_coords[i][1] >= busbar_coords[-1][1] >= bus_coords[i + 1][1] \ + or bus_coords[i][1] <= busbar_coords[-1][1] <= bus_coords[i + 1][1]: + # Intersection found. Breaking for-loop + intersection = busbar_coords[-1] + break + # If the bus has no "coords" it mus be a normal bus + elif bus_coords is np.NaN: + bus_geo = (net["bus_geodata"].loc[bus, "x"], net["bus_geodata"].loc[bus, "y"]) + # Checking if the first end of the line is on the bus + if bus_geo == busbar_coords[0]: + intersection = busbar_coords[0] + # Checking if the second end of the line is on the bus + elif bus_geo == busbar_coords[-1]: + intersection = busbar_coords[-1] + + return intersection diff --git a/pandapower/plotting/simple_plot.py b/pandapower/plotting/simple_plot.py index 7ada01fd3..08117bbbc 100644 --- a/pandapower/plotting/simple_plot.py +++ b/pandapower/plotting/simple_plot.py @@ -6,7 +6,7 @@ import matplotlib.pyplot as plt -from pandapower.plotting.geo import get_collection_sizes +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_sgen_collection, \ From a5de9ad41e67b9c192a151e7c0e383a3f8d0092f Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Wed, 20 Nov 2019 08:44:46 +0100 Subject: [PATCH 12/38] added repeat_info to some generic collection functions for easier handling --- pandapower/plotting/collections.py | 46 +++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index b02d3bc67..8a8289dae 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -198,9 +198,10 @@ def create_line2d_collection(coords, indices, infos=None, picker=False, **kwargs return lc -def create_node_element_collection(node_coords, patch_maker, size=1., infos=None, orientation=np.pi, - picker=False, patch_facecolor="w", patch_edgecolor="k", - line_color="k", **kwargs): +def create_node_element_collection(node_coords, patch_maker, size=1., infos=None, + repeat_infos=(1, 1), orientation=np.pi, picker=False, + patch_facecolor="w", patch_edgecolor="k", line_color="k", + **kwargs): """ Creates matplotlib collections of node elements. All node element collections usually consist of one patch collection representing the element itself and a small line collection that connects @@ -216,6 +217,9 @@ def create_node_element_collection(node_coords, patch_maker, size=1., infos=None :param infos: list of infos belonging to each of the elements (can be displayed when hovering \ over them) :type infos: iterable, default None + :param repeat_infos: determines how many times the info shall be repeated to match the number \ + of patches (first element) and lines (second element) returned by the patch maker + :type repeat_infos: tuple (length 2), default (1, 1) :param orientation: orientation of load collection. pi is directed downwards, increasing values\ lead to clockwise direction changes. :type orientation: float, default np.pi @@ -237,21 +241,27 @@ def create_node_element_collection(node_coords, patch_maker, size=1., infos=None angles = orientation if hasattr(orientation, '__iter__') else [orientation] * len(node_coords) assert len(node_coords) == len(angles), \ "The length of coordinates does not match the length of the orientation angles!" - infos = [] if infos is None else infos + if infos is None: + infos_pc = [] + infos_lc = [] + else: + infos_pc = list(np.repeat(infos, repeat_infos[0])) + infos_lc = list(np.repeat(infos, repeat_infos[1])) + lines, polys, popped_keywords = patch_maker(node_coords, size, angles, **kwargs) for kw in popped_keywords: kwargs.pop(kw) patch_coll = PatchCollection(polys, facecolor=patch_facecolor, edgecolor=patch_edgecolor, picker=picker, **kwargs) line_coll = LineCollection(lines, color=line_color, picker=picker, **kwargs) - patch_coll.info = infos - line_coll.info = infos + patch_coll.info = infos_pc + line_coll.info = infos_lc return patch_coll, line_coll -def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, colors=None, - picker=False, patch_facecolor="w", patch_edgecolor="k", - line_color="k", linewidths=2., **kwargs): +def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, repeat_infos=(2, 2), + colors=None, picker=False, patch_facecolor="w", + patch_edgecolor="k", line_color="k", linewidths=2., **kwargs): """ Creates a matplotlib line collection and a matplotlib patch collection representing a branch\ element that cannot be represented by just a line. @@ -267,6 +277,9 @@ def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, co :param infos: list of infos belonging to each of the branches (can be displayed when hovering \ over them) :type infos: iterable, default None + :param repeat_infos: determines how many times the info shall be repeated to match the number \ + of patches (first element) and lines (second element) returned by the patch maker + :type repeat_infos: tuple (length 2), default (1, 1) :param colors: colors or color of the branch patches :type colors: iterable, float :param picker: picker argument passed to the line collection @@ -286,7 +299,13 @@ def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, co - patch_coll - patch collection representing the branch element\ - line_coll - line collection connecting the patches with the nodes """ - infos = [] if infos is None else infos + if infos is None: + infos_pc = [] + infos_lc = [] + else: + infos_pc = list(np.repeat(infos, repeat_infos[0])) + infos_lc = list(np.repeat(infos, repeat_infos[1])) + lines, patches, keywords = patch_maker(coords, size, colors, **kwargs) for kw in keywords: kwargs.pop(kw) @@ -294,8 +313,8 @@ def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, co picker=picker, **kwargs) line_coll = LineCollection(lines, color=line_color, picker=picker, linewidths=linewidths, **kwargs) - patch_coll.info = infos - line_coll.info = infos + patch_coll.info = infos_pc + line_coll.info = infos_lc return patch_coll, line_coll @@ -596,8 +615,7 @@ def create_trafo_collection(net, trafos=None, picker=False, size=None, infofunc= z = net.res_trafo.loading_percent colors = [cmap(norm(z.at[idx])) for idx in trafos_with_geo] - infos = list(np.repeat([infofunc(i) for i in range(len(trafos_with_geo))], 2))\ - if infofunc is not None else [] + infos = [infofunc(i) for i in range(len(trafos_with_geo))] if infofunc is not None else [] lc, pc = create_complex_branch_collection(coords, trafo_patches, size, infos, colors=colors, picker=picker, linewidths=linewidths, **kwargs) From ee4e9e5d4ae29884634233eb4d466a9282722eb8 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Wed, 20 Nov 2019 16:36:37 +0100 Subject: [PATCH 13/38] small correction in load patch maker --- pandapower/plotting/patch_makers.py | 4 ++-- pandapower/plotting/plotting_toolbox.py | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/pandapower/plotting/patch_makers.py b/pandapower/plotting/patch_makers.py index 2bfc832bf..64861e7af 100644 --- a/pandapower/plotting/patch_makers.py +++ b/pandapower/plotting/patch_makers.py @@ -32,7 +32,7 @@ def load_patches(node_coords, size, angles, **kwargs): polys, lines = list(), list() for i, node_geo in enumerate(node_coords): p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), all_angles[i]) - p3 = node_geo + _rotate_dim2(np.array([0, offset + size / 3 * 2]), all_angles[i]) + p3 = node_geo + _rotate_dim2(np.array([0, offset + size / 2]), all_angles[i]) polys.append(RegularPolygon(p2, numVertices=3, radius=size, orientation=-all_angles[i])) lines.append((node_geo, p3)) return lines, polys, {"offset"} @@ -290,7 +290,7 @@ def trafo_patches(coords, size, color): p2 = np.array(p2) if np.all(p1 == p2): continue - d = np.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) + d = np.sqrt(np.sum((p1 - p2) ** 2)) if size is None: size_this = np.sqrt(d) / 5 else: diff --git a/pandapower/plotting/plotting_toolbox.py b/pandapower/plotting/plotting_toolbox.py index 86d2f44a4..0b3a79340 100644 --- a/pandapower/plotting/plotting_toolbox.py +++ b/pandapower/plotting/plotting_toolbox.py @@ -1,6 +1,11 @@ import numpy as np -from pandapower.plotting import logger -from pandapower.plotting.patch_makers import logger + +try: + import pplog as logging +except ImportError: + import logging + +logger = logging.getLogger(__name__) def _rotate_dim2(arr, ang): From 43ef8dc233e0ce2616c8422dca6a8ddfe9bdc695 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Fri, 22 Nov 2019 11:06:16 +0100 Subject: [PATCH 14/38] added more keyword options in patch makers --- pandapower/plotting/collections.py | 58 ++--- pandapower/plotting/patch_makers.py | 359 +++++++++++++++------------- 2 files changed, 225 insertions(+), 192 deletions(-) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index 8a8289dae..26ccecedf 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -248,11 +248,12 @@ def create_node_element_collection(node_coords, patch_maker, size=1., infos=None infos_pc = list(np.repeat(infos, repeat_infos[0])) infos_lc = list(np.repeat(infos, repeat_infos[1])) - lines, polys, popped_keywords = patch_maker(node_coords, size, angles, **kwargs) - for kw in popped_keywords: + lines, polys, popped_keywords = patch_maker( + node_coords, size, angles, patch_facecolor=patch_facecolor, patch_edgecolor=patch_edgecolor, + **kwargs) + for kw in set(popped_keywords) & set(kwargs.keys()): kwargs.pop(kw) - patch_coll = PatchCollection(polys, facecolor=patch_facecolor, edgecolor=patch_edgecolor, - picker=picker, **kwargs) + patch_coll = PatchCollection(polys, match_original=True, picker=picker, **kwargs) line_coll = LineCollection(lines, color=line_color, picker=picker, **kwargs) patch_coll.info = infos_pc line_coll.info = infos_lc @@ -260,8 +261,8 @@ def create_node_element_collection(node_coords, patch_maker, size=1., infos=None def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, repeat_infos=(2, 2), - colors=None, picker=False, patch_facecolor="w", - patch_edgecolor="k", line_color="k", linewidths=2., **kwargs): + picker=False, patch_facecolor="w", patch_edgecolor="k", + line_color="k", linewidths=2., **kwargs): """ Creates a matplotlib line collection and a matplotlib patch collection representing a branch\ element that cannot be represented by just a line. @@ -280,16 +281,14 @@ def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, re :param repeat_infos: determines how many times the info shall be repeated to match the number \ of patches (first element) and lines (second element) returned by the patch maker :type repeat_infos: tuple (length 2), default (1, 1) - :param colors: colors or color of the branch patches - :type colors: iterable, float :param picker: picker argument passed to the line collection :type picker: bool, default False - :param patch_facecolor: color of the patch face (content) - :type patch_facecolor: matplotlib color, "w" - :param patch_edgecolor: color of the patch edges - :type patch_edgecolor: matplotlib color, "k" - :param line_color: color of the connecting lines - :type line_color: matplotlib color, "k" + :param patch_facecolor: color or colors of the patch face (content) + :type patch_facecolor: matplotlib color or iterable, "w" + :param patch_edgecolor: color or colors of the patch edges + :type patch_edgecolor: matplotlib color or iterable, "k" + :param line_color: color or colors of the connecting lines + :type line_color: matplotlib color or iterable, "k" :param linewidths: linewidths of the connecting lines and the patch edges :type linewidths: float, default 2. :param kwargs: key word arguments are passed to the patch maker and the patch and line \ @@ -306,11 +305,11 @@ def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, re infos_pc = list(np.repeat(infos, repeat_infos[0])) infos_lc = list(np.repeat(infos, repeat_infos[1])) - lines, patches, keywords = patch_maker(coords, size, colors, **kwargs) - for kw in keywords: + lines, patches, popped_keywords = patch_maker(coords, size, patch_facecolor=patch_facecolor, + patch_edgecolor=patch_edgecolor, **kwargs) + for kw in set(popped_keywords) & set(kwargs.keys()): kwargs.pop(kw) - patch_coll = PatchCollection(patches, facecolor=patch_facecolor, edgecolor=patch_edgecolor, - picker=picker, **kwargs) + patch_coll = PatchCollection(patches, match_original=True, picker=picker, **kwargs) line_coll = LineCollection(lines, color=line_color, picker=picker, linewidths=linewidths, **kwargs) patch_coll.info = infos_pc @@ -318,7 +317,7 @@ def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, re return patch_coll, line_coll -def create_bus_collection(net, buses=None, size=5, patch_type="circle", colors=None, z=None, +def create_bus_collection(net, buses=None, size=5, patch_type="circle", color=None, z=None, cmap=None, norm=None, infofunc=None, picker=False, bus_geodata=None, cbar_title="Bus Voltage [pu]", **kwargs): """ @@ -341,7 +340,7 @@ def create_bus_collection(net, buses=None, size=5, patch_type="circle", colors=N **infofunc** (function, None) - infofunction for the patch element - **colors** (list, None) - list of colors for every element + **color** (list or color, None) - color or list of colors for every element **z** (array, None) - array of bus voltage magnitudes for colormap. Used in case of given cmap. If None net.res_bus.vm_pu is used. @@ -372,7 +371,7 @@ def create_bus_collection(net, buses=None, size=5, patch_type="circle", colors=N infos = [infofunc(bus) for bus in buses] if infofunc is not None else [] - pc = create_node_collection(buses, coords, size, patch_type, colors, picker, infos, **kwargs) + pc = create_node_collection(buses, coords, size, patch_type, color, picker, infos, **kwargs) if cmap is not None: add_cmap_to_collection(pc, cmap, norm, z, cbar_title) @@ -617,7 +616,8 @@ def create_trafo_collection(net, trafos=None, picker=False, size=None, infofunc= infos = [infofunc(i) for i in range(len(trafos_with_geo))] if infofunc is not None else [] - lc, pc = create_complex_branch_collection(coords, trafo_patches, size, infos, colors=colors, + lc, pc = create_complex_branch_collection(coords, trafo_patches, size, infos, + patch_edgecolor=colors, line_color=colors, picker=picker, linewidths=linewidths, **kwargs) if cmap is not None: @@ -794,11 +794,11 @@ def create_load_collection(net, loads=None, size=1., infofunc=None, orientation= """ if loads is None: loads = net.load.index - infos = [infofunc(i) for i in range(len(loads))] + infos = [infofunc(i) for i in range(len(loads))] if infofunc is not None else [] node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.load.loc[loads, "bus"].values] load_pc, load_lc = create_node_element_collection( node_coords, load_patches, size=size, infos=infos, orientation=orientation, - offset=size, picker=picker, **kwargs) + picker=picker, **kwargs) return load_pc, load_lc @@ -835,7 +835,7 @@ def create_gen_collection(net, gens=None, size=1., infofunc=None, orientation=np node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.gen.loc[gens, "bus"].values] gen_pc, gen_lc = create_node_element_collection( node_coords, gen_patches, size=size, infos=infos, orientation=orientation, - offset=size, picker=picker, **kwargs) + picker=picker, **kwargs) return gen_pc, gen_lc @@ -872,7 +872,7 @@ def create_sgen_collection(net, sgens=None, size=1., infofunc=None, orientation= node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.sgen.loc[sgens, "bus"].values] sgen_pc, sgen_lc = create_node_element_collection( node_coords, sgen_patches, size=size, infos=infos, orientation=orientation, - offset=size, r_triangle=size * 0.4, picker=picker, **kwargs) + picker=picker, **kwargs) return sgen_pc, sgen_lc @@ -912,12 +912,14 @@ def create_ext_grid_collection(net, size=1., infofunc=None, orientation=0, picke else: assert len(ext_grids) == len(ext_grid_buses), \ "Length mismatch between chosen ext_grids and ext_grid_buses." - infos = [infofunc(ext_grid_idx) for ext_grid_idx in ext_grids] + infos = [infofunc(ext_grid_idx) for ext_grid_idx in ext_grids] if infofunc is not None else [] node_coords = net.bus_geodata.loc[ext_grid_buses, ["x", "y"]].values + ext_grid_pc, ext_grid_lc = create_node_element_collection( node_coords, ext_grid_patches, size=size, infos=infos, orientation=orientation, - offset=size / 2, picker=picker, hatch='XXX', **kwargs) + picker=picker, hatch='XXX', **kwargs) + return ext_grid_pc, ext_grid_lc diff --git a/pandapower/plotting/patch_makers.py b/pandapower/plotting/patch_makers.py index 64861e7af..7d7168249 100644 --- a/pandapower/plotting/patch_makers.py +++ b/pandapower/plotting/patch_makers.py @@ -10,129 +10,48 @@ logger = logging.getLogger(__name__) -def load_patches(node_coords, size, angles, **kwargs): - """ - Creation function of patches for loads. - - :param node_coords: coordinates of the nodes that the loads belong to. - :type node_coords: iterable - :param size: size of the patch - :type size: float - :param angles: angles by which to rotate the patches (in radians) - :type angles: iterable(float), float - :param kwargs: additional keyword arguments (might contain parameter "offset") - :type kwargs: - :return: Return values are: \ - - lines (list) - list of coordinates for lines leading to load patches\ - - polys (list of RegularPolygon) - list containing the load patches\ - - keywords (set) - set of keywords removed from kwargs - """ - offset = kwargs.get("offset", size) - all_angles = get_angle_list(angles, len(node_coords)) - polys, lines = list(), list() - for i, node_geo in enumerate(node_coords): - p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), all_angles[i]) - p3 = node_geo + _rotate_dim2(np.array([0, offset + size / 2]), all_angles[i]) - polys.append(RegularPolygon(p2, numVertices=3, radius=size, orientation=-all_angles[i])) - lines.append((node_geo, p3)) - return lines, polys, {"offset"} - - -def gen_patches(node_coords, size, angles, **kwargs): - """ - Creation function of patches for generators. - - :param node_coords: coordinates of the nodes that the generators belong to. - :type node_coords: iterable - :param size: size of the patch - :type size: float - :param angles: angles by which to rotate the patches (in radians) - :type angles: iterable(float), float - :param kwargs: additional keyword arguments (might contain parameter "offset") - :type kwargs: - :return: Return values are: \ - - lines (list) - list of coordinates for lines leading to generator patches\ - - polys (list of RegularPolygon) - list containing the generator patches\ - - keywords (set) - set of keywords removed from kwargs - """ - polys, lines = list(), list() - offset = kwargs.pop("offset", 2. * size) - for i, node_geo in enumerate(node_coords): - p2 = node_geo + _rotate_dim2(np.array([0, size * offset]), angles[i]) - polys.append(Circle(p2, size)) - polys.append( - Arc(p2 + np.array([-size / 6.2, -size / 2.6]), size / 2, size, theta1=45, theta2=135)) - polys.append( - Arc(p2 + np.array([size / 6.2, size / 2.6]), size / 2, size, theta1=225, theta2=315)) - lines.append((node_geo, p2 + np.array([0, size]))) - return lines, polys, {"offset"} - - -def sgen_patches(node_coords, size, angles, **kwargs): - """ - Creation function of patches for static generators. - - :param node_coords: coordinates of the nodes that the static generators belong to. - :type node_coords: iterable - :param size: size of the patch - :type size: float - :param angles: angles by which to rotate the patches (in radians) - :type angles: iterable(float), float - :param kwargs: additional keyword arguments (might contain parameters "offset" and "r_triangle") - :type kwargs: - :return: Return values are: \ - - lines (list) - list of coordinates for lines leading to static generator patches\ - - polys (list of RegularPolygon) - list containing the static generator patches\ - - keywords (set) - set of keywords removed from kwargs - """ - polys, lines = list(), list() - offset = kwargs.pop("offset", size) - r_triangle = kwargs.pop("r_triangles", size * 0.4) - for i, node_geo in enumerate(node_coords): - mid_circ = node_geo + _rotate_dim2(np.array([0, offset + size]), angles[i]) - circ_edge = node_geo + _rotate_dim2(np.array([0, offset]), angles[i]) - mid_tri1 = mid_circ + _rotate_dim2(np.array([r_triangle, -r_triangle / 4]), angles[i]) - mid_tri2 = mid_circ + _rotate_dim2(np.array([-r_triangle, r_triangle / 4]), angles[i]) - # dropped perpendicular foot of triangle1 - perp_foot1 = mid_tri1 + _rotate_dim2(np.array([0, -r_triangle / 2]), angles[i]) - line_end1 = perp_foot1 + + _rotate_dim2(np.array([-2.5 * r_triangle, 0]), angles[i]) - perp_foot2 = mid_tri2 + _rotate_dim2(np.array([0, r_triangle / 2]), angles[i]) - line_end2 = perp_foot2 + + _rotate_dim2(np.array([2.5 * r_triangle, 0]), angles[i]) - polys.append(Circle(mid_circ, size)) - polys.append(RegularPolygon(mid_tri1, numVertices=3, radius=r_triangle, - orientation=-angles[i])) - polys.append(RegularPolygon(mid_tri2, numVertices=3, radius=r_triangle, - orientation=np.pi - angles[i])) - lines.append((node_geo, circ_edge)) - lines.append((perp_foot1, line_end1)) - lines.append((perp_foot2, line_end2)) - return lines, polys, {"offset", "r_triangle"} - - -def ext_grid_patches(node_coords, size, angles, **kwargs): +def node_patches(node_coords, size, patch_type, colors=None, **kwargs): """ - Creation function of patches for external grids. + Creates node patches from coordinates translating the patch type into patches. - :param node_coords: coordinates of the nodes that the external grids belong to. + :param node_coords: coordinates of the nodes to draw :type node_coords: iterable - :param size: size of the patch + :param size: size of the patch (can be interpreted differently, depending on the patch type) :type size: float - :param angles: angles by which to rotate the patches (in radians) - :type angles: iterable(float), float - :param kwargs: additional keyword arguments (might contain parameter "offset") - :type kwargs: - :return: Return values are: \ - - lines (list) - list of coordinates for lines leading to external grid patches\ - - polys (list of RegularPolygon) - list containing the external grid patches\ - - keywords (set) - set of keywords removed from kwargs (empty + :param patch_type: type of patches to create - can be one of + - "circle" or "ellipse" for an ellipse (cirlces are just ellipses with the same width \ + + height)\ + - "rect" or "rectangle" for a rectangle\ + - "poly" for a polygon with n edges + :type patch_type: str + :param colors: colors or color of the patches + :type colors: iterable, float + :param kwargs: additional keyword arguments to pass to the patch initialization \ + (might contain "width", "height", "angle" depending on the patch type) + :type kwargs: dict + :return: patches - list of rectangle patches for the nodes """ - offset = kwargs.pop("offset", size / 2) - polys, lines = list(), list() - for i, node_geo in enumerate(node_coords): - p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), angles[i]) - polys.append(Rectangle((p2[0] - size / 2, p2[1] - size / 2), size, size)) - lines.append((node_geo, p2 - _rotate_dim2(np.array([0, offset]), angles[i]))) - return lines, polys, set("offset") + if patch_type.lower() == 'ellipse' or patch_type.lower() == 'circle': + # circles are just ellipses + if patch_type.lower() == "circle" and len(set(kwargs.keys()) & {"width", "height"}) == 1: + wh = kwargs["width"] if "width" in kwargs else kwargs["height"] + width = wh + height = wh + else: + width = kwargs.pop("width", 2 * size) + height = kwargs.pop("height", 2 * size) + angle = kwargs.pop('angle', 0) + return ellipse_patches(node_coords, width, height, angle, color=colors, **kwargs) + elif patch_type.lower() == "rect" or patch_type.lower() == "rectangle": + width = kwargs.pop("width", 2 * size) + height = kwargs.pop("height", 2 * size) + return rectangle_patches(node_coords, width, height, color=colors, **kwargs) + elif patch_type.lower().startswith("poly"): + edges = int(patch_type[4:]) + return polygon_patches(node_coords, size, edges, color=colors, **kwargs) + else: + logger.error("Wrong patchtype. Please choose a correct patch type.") + raise ValueError("Wrong patchtype") def ellipse_patches(node_coords, width, height, angle=0, color=None, **kwargs): @@ -222,51 +141,160 @@ def polygon_patches(node_coords, radius, num_edges, color=None, **kwargs): return patches -def node_patches(node_coords, size, patch_type, colors=None, **kwargs): +def load_patches(node_coords, size, angles, **kwargs): """ - Creates node patches from coordinates translating the patch type into patches. + Creation function of patches for loads. - :param node_coords: coordinates of the nodes to draw + :param node_coords: coordinates of the nodes that the loads belong to. :type node_coords: iterable - :param size: size of the patch (can be interpreted differently, depending on the patch type) + :param size: size of the patch :type size: float - :param patch_type: type of patches to create - can be one of - - "circle" or "ellipse" for an ellipse (cirlces are just ellipses with the same width \ - + height)\ - - "rect" or "rectangle" for a rectangle\ - - "poly" for a polygon with n edges - :type patch_type: str - :param colors: colors or color of the patches - :type colors: iterable, float - :param kwargs: additional keyword arguments to pass to the patch initialization \ - (might contain "width", "height", "angle" depending on the patch type) - :type kwargs: dict - :return: patches - list of rectangle patches for the nodes + :param angles: angles by which to rotate the patches (in radians) + :type angles: iterable(float), float + :param kwargs: additional keyword arguments (might contain parameters "offset",\ + "patch_edgecolor" and "patch_facecolor") + :type kwargs: + :return: Return values are: \ + - lines (list) - list of coordinates for lines leading to load patches\ + - polys (list of RegularPolygon) - list containing the load patches\ + - keywords (set) - set of keywords removed from kwargs """ - if patch_type.lower() == 'ellipse' or patch_type.lower() == 'circle': - # circles are just ellipses - if patch_type.lower() == "circle" and len(set(kwargs.keys()) & {"width", "height"}) == 1: - wh = kwargs["width"] if "width" in kwargs else kwargs["height"] - width = wh - height = wh - else: - width = kwargs.pop("width", 2 * size) - height = kwargs.pop("height", 2 * size) - angle = kwargs.pop('angle', 0) - return ellipse_patches(node_coords, width, height, angle, color=colors, **kwargs) - elif patch_type.lower() == "rect" or patch_type.lower() == "rectangle": - width = kwargs.pop("width", 2 * size) - height = kwargs.pop("height", 2 * size) - return rectangle_patches(node_coords, width, height, color=colors, **kwargs) - elif patch_type.lower().startswith("poly"): - edges = int(patch_type[4:]) - return polygon_patches(node_coords, size, edges, color=colors, **kwargs) - else: - logger.error("Wrong patchtype. Please choose a correct patch type.") - raise ValueError("Wrong patchtype") + offset = kwargs.get("offset", 1.2 * size) + all_angles = get_angle_list(angles, len(node_coords)) + edgecolor = kwargs.get("patch_edgecolor", "w") + facecolor = kwargs.get("patch_facecolor", "w") + edgecolors = get_color_list(edgecolor, len(node_coords)) + facecolors = get_color_list(facecolor, len(node_coords)) + polys, lines = list(), list() + for i, node_geo in enumerate(node_coords): + p2 = node_geo + _rotate_dim2(np.array([0, offset + size]), all_angles[i]) + p3 = node_geo + _rotate_dim2(np.array([0, offset + size / 2]), all_angles[i]) + polys.append(RegularPolygon(p2, numVertices=3, radius=size, orientation=-all_angles[i], + fc=facecolors[i], ec=edgecolors[i])) + lines.append((node_geo, p3)) + return lines, polys, {"offset", "patch_edgecolor", "patch_facecolor"} + +def gen_patches(node_coords, size, angles, **kwargs): + """ + Creation function of patches for generators. -def trafo_patches(coords, size, color): + :param node_coords: coordinates of the nodes that the generators belong to. + :type node_coords: iterable + :param size: size of the patch + :type size: float + :param angles: angles by which to rotate the patches (in radians) + :type angles: iterable(float), float + :param kwargs: additional keyword arguments (might contain parameters "offset",\ + "patch_edgecolor" and "patch_facecolor") + :type kwargs: + :return: Return values are: \ + - lines (list) - list of coordinates for lines leading to generator patches\ + - polys (list of RegularPolygon) - list containing the generator patches\ + - keywords (set) - set of keywords removed from kwargs + """ + polys, lines = list(), list() + offset = kwargs.get("offset", 2. * size) + all_angles = get_angle_list(angles, len(node_coords)) + edgecolor = kwargs.get("patch_edgecolor", "w") + facecolor = kwargs.get("patch_facecolor", "w") + edgecolors = get_color_list(edgecolor, len(node_coords)) + facecolors = get_color_list(facecolor, len(node_coords)) + for i, node_geo in enumerate(node_coords): + p2 = node_geo + _rotate_dim2(np.array([0, size + offset]), all_angles[i]) + polys.append(Circle(p2, size, fc=facecolors[i], ec=edgecolors[i])) + polys.append( + Arc(p2 + np.array([-size / 6.2, -size / 2.6]), size / 2, size, theta1=45, theta2=135, + fc=facecolors[i], ec=edgecolors[i])) + polys.append( + Arc(p2 + np.array([size / 6.2, size / 2.6]), size / 2, size, theta1=225, theta2=315, + fc=facecolors[i], ec=edgecolors[i])) + lines.append((node_geo, p2 + np.array([0, size]))) + return lines, polys, {"offset", "patch_edgecolor", "patch_facecolor"} + + +def sgen_patches(node_coords, size, angles, **kwargs): + """ + Creation function of patches for static generators. + + :param node_coords: coordinates of the nodes that the static generators belong to. + :type node_coords: iterable + :param size: size of the patch + :type size: float + :param angles: angles by which to rotate the patches (in radians) + :type angles: iterable(float), float + :param kwargs: additional keyword arguments (might contain parameters "offset", "r_triangle",\ + "patch_edgecolor" and "patch_facecolor") + :type kwargs: + :return: Return values are: \ + - lines (list) - list of coordinates for lines leading to static generator patches\ + - polys (list of RegularPolygon) - list containing the static generator patches\ + - keywords (set) - set of keywords removed from kwargs + """ + polys, lines = list(), list() + offset = kwargs.get("offset", 2 * size) + r_triangle = kwargs.get("r_triangles", size * 0.4) + edgecolor = kwargs.get("patch_edgecolor", "w") + facecolor = kwargs.get("patch_facecolor", "w") + edgecolors = get_color_list(edgecolor, len(node_coords)) + facecolors = get_color_list(facecolor, len(node_coords)) + for i, node_geo in enumerate(node_coords): + mid_circ = node_geo + _rotate_dim2(np.array([0, offset + size]), angles[i]) + circ_edge = node_geo + _rotate_dim2(np.array([0, offset]), angles[i]) + mid_tri1 = mid_circ + _rotate_dim2(np.array([r_triangle, -r_triangle / 4]), angles[i]) + mid_tri2 = mid_circ + _rotate_dim2(np.array([-r_triangle, r_triangle / 4]), angles[i]) + # dropped perpendicular foot of triangle1 + perp_foot1 = mid_tri1 + _rotate_dim2(np.array([0, -r_triangle / 2]), angles[i]) + line_end1 = perp_foot1 + + _rotate_dim2(np.array([-2.5 * r_triangle, 0]), angles[i]) + perp_foot2 = mid_tri2 + _rotate_dim2(np.array([0, r_triangle / 2]), angles[i]) + line_end2 = perp_foot2 + + _rotate_dim2(np.array([2.5 * r_triangle, 0]), angles[i]) + polys.append(Circle(mid_circ, size, fc=facecolors[i], ec=edgecolors[i])) + polys.append(RegularPolygon(mid_tri1, numVertices=3, radius=r_triangle, + orientation=-angles[i], fc=facecolors[i], ec=edgecolors[i])) + polys.append(RegularPolygon(mid_tri2, numVertices=3, radius=r_triangle, + orientation=np.pi - angles[i], fc=facecolors[i], + ec=edgecolors[i])) + lines.append((node_geo, circ_edge)) + lines.append((perp_foot1, line_end1)) + lines.append((perp_foot2, line_end2)) + return lines, polys, {"offset", "r_triangle", "patch_edgecolor", "patch_facecolor"} + + +def ext_grid_patches(node_coords, size, angles, **kwargs): + """ + Creation function of patches for external grids. + + :param node_coords: coordinates of the nodes that the external grids belong to. + :type node_coords: iterable + :param size: size of the patch + :type size: float + :param angles: angles by which to rotate the patches (in radians) + :type angles: iterable(float), float + :param kwargs: additional keyword arguments (might contain parameters "offset",\ + "patch_edgecolor" and "patch_facecolor") + :type kwargs: + :return: Return values are: \ + - lines (list) - list of coordinates for lines leading to external grid patches\ + - polys (list of RegularPolygon) - list containing the external grid patches\ + - keywords (set) - set of keywords removed from kwargs (empty + """ + offset = kwargs.get("offset", 2 * size) + all_angles = get_angle_list(angles, len(node_coords)) + edgecolor = kwargs.get("patch_edgecolor", "w") + facecolor = kwargs.get("patch_facecolor", "w") + edgecolors = get_color_list(edgecolor, len(node_coords)) + facecolors = get_color_list(facecolor, len(node_coords)) + polys, lines = list(), list() + for i, node_geo in enumerate(node_coords): + p2 = node_geo + _rotate_dim2(np.array([0, offset]), all_angles[i]) + p_ll = p2 + _rotate_dim2(np.array([-size, 0]), all_angles[i]) + polys.append(Rectangle(p_ll, 2 * size, 2 * size, angle=(-all_angles[i] / np.pi * 180), + fc=facecolors[i], ec=edgecolors[i], hatch="XXX")) + lines.append((node_geo, p2)) + return lines, polys, {"offset", "patch_edgecolor", "patch_facecolor"} + + +def trafo_patches(coords, size, **kwargs): """ Creates a list of patches and line coordinates representing transformers each connecting two nodes. @@ -276,16 +304,19 @@ def trafo_patches(coords, size, color): :type coords: (N, (2, 2)) shaped iterable :param size: size of the trafo patches :type size: float - :param color: color or colors of the trafo patches (only edges and connecting lines, interior\ - is white) - :type color: iterable, 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 transformer patches\ - circles (list of Circle) - list containing the transformer patches (rings) """ - colors = get_color_list(color, len(coords)) + 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)) circles, lines = list(), list() - for (p1, p2), col in zip(coords, colors): + for i, (p1, p2) in enumerate(coords): p1 = np.array(p1) p2 = np.array(p2) if np.all(p1 == p2): @@ -298,11 +329,11 @@ def trafo_patches(coords, size, color): off = size_this * 0.35 circ1 = (0.5 - off / d) * (p1 - p2) + p2 circ2 = (0.5 + off / d) * (p1 - p2) + p2 - circles.append(Circle(circ1, size_this, fc=(1, 0, 0, 0), ec=col)) - circles.append(Circle(circ2, size_this, fc=(1, 0, 0, 0), ec=col)) + circles.append(Circle(circ1, size_this, fc=facecolors[i], ec=edgecolors[i])) + circles.append(Circle(circ2, size_this, fc=facecolors[i], ec=edgecolors[i])) lp1 = (0.5 - off / d - size_this / d) * (p2 - p1) + p1 lp2 = (0.5 - off / d - size_this / d) * (p1 - p2) + p2 lines.append([p1, lp1]) lines.append([p2, lp2]) - return lines, circles + return lines, circles, {"patch_edgecolor", "patch_facecolor"} From ee825057b63685f9e08000366d3421b95da873fd Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Fri, 22 Nov 2019 14:25:34 +0100 Subject: [PATCH 15/38] correction of trafo patch collections --- pandapower/plotting/collections.py | 10 +++++----- pandapower/plotting/patch_makers.py | 26 +++++++++++++++---------- pandapower/plotting/plotting_toolbox.py | 7 +++++++ 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index 26ccecedf..59cf7210a 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -616,9 +616,9 @@ def create_trafo_collection(net, trafos=None, picker=False, size=None, infofunc= infos = [infofunc(i) for i in range(len(trafos_with_geo))] if infofunc is not None else [] - lc, pc = create_complex_branch_collection(coords, trafo_patches, size, infos, - patch_edgecolor=colors, line_color=colors, - picker=picker, linewidths=linewidths, **kwargs) + lc, pc = create_complex_branch_collection( + coords, trafo_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) @@ -831,7 +831,7 @@ def create_gen_collection(net, gens=None, size=1., infofunc=None, orientation=np """ if gens is None: gens = net.gen.index - infos = [infofunc(i) for i in range(len(gens))] + infos = [infofunc(i) for i in range(len(gens))] if infofunc is not None else [] node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.gen.loc[gens, "bus"].values] gen_pc, gen_lc = create_node_element_collection( node_coords, gen_patches, size=size, infos=infos, orientation=orientation, @@ -868,7 +868,7 @@ def create_sgen_collection(net, sgens=None, size=1., infofunc=None, orientation= """ if sgens is None: sgens = net.sgen.index - infos = [infofunc(i) for i in range(len(sgens))] + infos = [infofunc(i) for i in range(len(sgens))] if infofunc is not None else [] node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.sgen.loc[sgens, "bus"].values] sgen_pc, sgen_lc = create_node_element_collection( node_coords, sgen_patches, size=size, infos=infos, orientation=orientation, diff --git a/pandapower/plotting/patch_makers.py b/pandapower/plotting/patch_makers.py index 7d7168249..d1d8d253a 100644 --- a/pandapower/plotting/patch_makers.py +++ b/pandapower/plotting/patch_makers.py @@ -1,6 +1,7 @@ from matplotlib.patches import RegularPolygon, Arc, Circle, Rectangle, Ellipse import numpy as np -from pandapower.plotting.plotting_toolbox import _rotate_dim2, get_color_list, get_angle_list +from pandapower.plotting.plotting_toolbox import _rotate_dim2, get_color_list, get_angle_list, \ + get_linewidth_list try: import pplog as logging @@ -196,19 +197,20 @@ def gen_patches(node_coords, size, angles, **kwargs): polys, lines = list(), list() offset = kwargs.get("offset", 2. * size) all_angles = get_angle_list(angles, len(node_coords)) - edgecolor = kwargs.get("patch_edgecolor", "w") - facecolor = kwargs.get("patch_facecolor", "w") + edgecolor = kwargs.get("patch_edgecolor", "k") + facecolor = kwargs.get("patch_facecolor", (1, 0, 0, 0)) edgecolors = get_color_list(edgecolor, len(node_coords)) facecolors = get_color_list(facecolor, len(node_coords)) for i, node_geo in enumerate(node_coords): p2 = node_geo + _rotate_dim2(np.array([0, size + offset]), all_angles[i]) polys.append(Circle(p2, size, fc=facecolors[i], ec=edgecolors[i])) polys.append( - Arc(p2 + np.array([-size / 6.2, -size / 2.6]), size / 2, size, theta1=45, theta2=135, - fc=facecolors[i], ec=edgecolors[i])) + Arc(p2 + np.array([-size / 6.2, -size / 2.6]), size / 2, size, theta1=65, theta2=120, + ec=edgecolors[i])) polys.append( - Arc(p2 + np.array([size / 6.2, size / 2.6]), size / 2, size, theta1=225, theta2=315, - fc=facecolors[i], ec=edgecolors[i])) + Arc(p2 + np.array([size / 6.2, size / 2.6]), size / 2, size, theta1=245, theta2=300, + ec=edgecolors[i])) + print("Arc:", polys[-1]) lines.append((node_geo, p2 + np.array([0, size]))) return lines, polys, {"offset", "patch_edgecolor", "patch_facecolor"} @@ -312,9 +314,11 @@ def trafo_patches(coords, size, **kwargs): - circles (list of Circle) - list containing the transformer patches (rings) """ edgecolor = kwargs.get("patch_edgecolor", "w") - facecolor = kwargs.get("patch_facecolor", "w") + facecolor = kwargs.get("patch_facecolor", (1, 0, 0, 0)) 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="trafos") circles, lines = list(), list() for i, (p1, p2) in enumerate(coords): p1 = np.array(p1) @@ -329,8 +333,10 @@ def trafo_patches(coords, size, **kwargs): off = size_this * 0.35 circ1 = (0.5 - off / d) * (p1 - p2) + p2 circ2 = (0.5 + off / d) * (p1 - p2) + p2 - circles.append(Circle(circ1, size_this, fc=facecolors[i], ec=edgecolors[i])) - circles.append(Circle(circ2, size_this, fc=facecolors[i], ec=edgecolors[i])) + circles.append(Circle(circ1, size_this, fc=facecolors[i], ec=edgecolors[i], + lw=linewidths[i])) + circles.append(Circle(circ2, size_this, fc=facecolors[i], ec=edgecolors[i], + lw=linewidths[i])) lp1 = (0.5 - off / d - size_this / d) * (p2 - p1) + p1 lp2 = (0.5 - off / d - size_this / d) * (p1 - p2) + p2 diff --git a/pandapower/plotting/plotting_toolbox.py b/pandapower/plotting/plotting_toolbox.py index 0b3a79340..12801f077 100644 --- a/pandapower/plotting/plotting_toolbox.py +++ b/pandapower/plotting/plotting_toolbox.py @@ -99,6 +99,9 @@ def get_list(individuals, number_entries, name_ind, name_ent): def get_color_list(color, number_entries, name_entries="nodes"): + if (len(color) == 3 or len(color) == 4) and all(isinstance(c, float) for c in color): + logger.info("Interpreting color %s as rgb or rgba!" % rgb) + return get_list([color], number_entries, "colors", name_entries) return get_list(color, number_entries, "colors", name_entries) @@ -106,6 +109,10 @@ def get_angle_list(angle, number_entries, name_entries="nodes"): return get_list(angle, number_entries, "angles", name_entries) +def get_linewidth_list(linewidth, number_entries, name_entries="lines"): + return get_list(linewidth, number_entries, "linewidths", name_entries) + + def coords_from_node_geodata(element_indices, from_nodes, to_nodes, node_geodata, table_name, node_name="Bus"): """ From 63481b7d60f6465e01430711af18721ad7c54ddb Mon Sep 17 00:00:00 2001 From: vkiesewetter Date: Mon, 2 Dec 2019 13:30:04 +0100 Subject: [PATCH 16/38] - changing variable name from respect_switches to respect_seperation_points - in generate_geodata.py and geo.py: making it more general, usable in pandapower and pandapipes --- pandapower/plotting/generic_geodata.py | 33 ++++++------- pandapower/plotting/geo.py | 52 ++++++++++++--------- pandapower/plotting/plotly/pf_res_plotly.py | 2 +- pandapower/plotting/plotly/simple_plotly.py | 2 +- pandapower/plotting/plotly/vlevel_plotly.py | 2 +- pandapower/plotting/simple_plot.py | 2 +- 6 files changed, 52 insertions(+), 41 deletions(-) diff --git a/pandapower/plotting/generic_geodata.py b/pandapower/plotting/generic_geodata.py index a5d4941c6..4c754a182 100644 --- a/pandapower/plotting/generic_geodata.py +++ b/pandapower/plotting/generic_geodata.py @@ -68,7 +68,8 @@ def build_igraph_from_pp(net, respect_switches=False): return g, meshed, roots # g, (not g.is_dag()) -def create_generic_coordinates(net, mg=None, library="igraph", respect_switches=False): +def create_generic_coordinates(net, mg=None, library="igraph", respect_separation_points=False, + name_node='bus'): """ 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 created either by igraph or by @@ -89,22 +90,21 @@ def create_generic_coordinates(net, mg=None, library="igraph", respect_switches= net = create_generic_coordinates(net) """ - - if "bus_geodata" in net and net.bus_geodata.shape[0]: + if "name_node +'_geodata'" in net and net[name_node +'_geodata'].shape[0]: print("Please delete all geodata. This function cannot be used with pre-existing geodata.") return - if not "bus_geodata" in net or net.bus_geodata is None: - net.bus_geodata = pd.DataFrame(columns=["x", "y"]) + if not "name_node +'_geodata'" in net or net[name_node +'_geodata'] is None: + net[name_node +'_geodata'] = pd.DataFrame(columns=["x", "y"]) gnet = copy.deepcopy(net) - gnet.bus = gnet.bus[gnet.bus.in_service == True] + gnet[name_node] = gnet[name_node][gnet[name_node].in_service == True] if library == "igraph": try: import igraph except ImportError: raise UserWarning("The library igraph is selected for plotting, " "but not installed correctly.") - graph, meshed, roots = build_igraph_from_pp(gnet, respect_switches) + graph, meshed, roots = build_igraph_from_pp(gnet, respect_separation_points) if meshed: layout = graph.layout("kk") else: @@ -113,7 +113,7 @@ def create_generic_coordinates(net, mg=None, library="igraph", respect_switches= coords = list(zip(*layout.coords)) elif library == "networkx": if mg is None: - nxg = top.create_nxgraph(gnet, respect_switches) + nxg = top.create_nxgraph(gnet, respect_separation_points) else: nxg = copy.deepcopy(mg) # workaround for bug in agraph @@ -126,19 +126,20 @@ def create_generic_coordinates(net, mg=None, library="igraph", respect_switches= coords = list(zip(*(list(nx.drawing.nx_agraph.graphviz_layout(nxg, prog='neato').values())))) else: raise ValueError("Unknown library %s - chose 'igraph' or 'networkx'"%library) - net.bus_geodata.x = coords[1] - net.bus_geodata.y = coords[0] - net.bus_geodata.index = gnet.bus.index + net[name_node +'_geodata'].x = coords[1] + net[name_node +'_geodata'].y = coords[0] + net[name_node +'_geodata'].index = gnet[name_node].index return net -def fuse_geodata(net): +def fuse_geodata(net, name_node_geodata='bus'): + name_node = name_node_geodata mg = top.create_nxgraph(net, include_lines=False, include_impedances=False, respect_switches=False) - geocoords = set(net.bus_geodata.index) + geocoords = set(net[name_node +'_geodata'].index) for area in top.connected_components(mg): if len(area & geocoords) > 1: - geo = net.bus_geodata.loc[area & geocoords].values[0] - for bus in area: - net.bus_geodata.loc[bus] = geo + geo = net[name_node +'_geodata'].loc[area & geocoords].values[0] + for name_node in area: + net[name_node +'_geodata'].loc[name_node] = geo diff --git a/pandapower/plotting/geo.py b/pandapower/plotting/geo.py index 91ef674dd..397e24428 100644 --- a/pandapower/plotting/geo.py +++ b/pandapower/plotting/geo.py @@ -7,30 +7,37 @@ pass -def convert_geodata_to_gis(net, epsg=31467, bus_geodata=True, line_geodata=True): - if bus_geodata: - bus_geo = net.bus_geodata - geo = [Point(x, y) for x, y in bus_geo[["x", "y"]].values] - net.bus_geodata = GeoDataFrame(bus_geo, crs=from_epsg(epsg), geometry=geo, - index=bus_geo.index) - if line_geodata: - line_geo = net.line_geodata - geo = GeoSeries([LineString(x) for x in net.line_geodata.coords.values], - index=net.line_geodata.index, crs=from_epsg(epsg)) - net.line_geodata = GeoDataFrame(line_geo, crs=from_epsg(epsg), geometry=geo, - index=line_geo.index) +def convert_geodata_to_gis(net, epsg=31467, node_geodata=True, branch_geodata=True, + name_node_geodata='bus', name_branch_geodata='line'): + name_node = name_node_geodata + name_branch = name_branch_geodata + if node_geodata: + node_geo = net[name_node + '_geodata'] + geo = [Point(x, y) for x, y in node_geo[["x", "y"]].values] + net[name_node + '_geodata'] = GeoDataFrame(node_geo, crs=from_epsg(epsg), geometry=geo, + index=node_geo.index) + if branch_geodata: + branch_geo = net[name_branch + '_geodata'] + geo = GeoSeries([LineString(x) for x in net[name_branch + '_geodata'].coords.values], + index=net[name_branch + '_geodata'].index, crs=from_epsg(epsg)) + net[name_branch + '_geodata'] = GeoDataFrame(branch_geo, crs=from_epsg(epsg), geometry=geo, + index=branch_geo.index) net["gis_epsg_code"] = epsg -def convert_gis_to_geodata(net, bus_geodata=True, line_geodata=True): - if bus_geodata: - net.bus_geodata["x"] = [x.x for x in net.bus_geodata.geometry] - net.bus_geodata["y"] = [x.y for x in net.bus_geodata.geometry] - if line_geodata: - net.line_geodata["coords"] = net.line_geodata.geometry.apply(lambda x: list(x.coords)) +def convert_gis_to_geodata(net, node_geodata=True, branch_geodata=True, name_node_geodata='bus', + name_branch_geodata='line'): + name_node = name_node_geodata + name_branch = name_branch_geodata + if node_geodata: + net[name_node + '_geodata']["x"] = [x.x for x in net[name_node + '_geodata'].geometry] + net[name_node + '_geodata']["y"] = [x.y for x in net[name_node + '_geodata'].geometry] + if branch_geodata: + net[name_branch +'_geodata']["coords"] = \ + net[name_branch +'_geodata'].geometry.apply(lambda x: list(x.coords)) -def convert_epgs_bus_geodata(net, epsg_in=4326, epsg_out=31467): +def convert_epgs_bus_geodata(net, epsg_in=4326, epsg_out=31467, name_node_geodata='bus'): """ Converts bus geodata in net from epsg_in to epsg_out @@ -42,8 +49,11 @@ def convert_epgs_bus_geodata(net, epsg_in=4326, epsg_out=31467): :type epsg_out: int, default 31467 (= Gauss-Krüger Zone 3) :return: net - the given pandapower network (no copy!) """ + name_node = name_node_geodata in_proj = Proj(init='epsg:%i' % epsg_in) out_proj = Proj(init='epsg:%i' % epsg_out) - x1, y1 = net.bus_geodata.loc[:, "x"].values, net.bus_geodata.loc[:, "y"].values - net.bus_geodata.loc[:, "x"], net.bus_geodata.loc[:, "y"] = transform(in_proj, out_proj, x1, y1) + x1, y1 = net[name_node + '_geodata'].loc[:, "x"].values, \ + net[name_node + '_geodata'].loc[:, "y"].values + net[name_node + '_geodata'].loc[:, "x"], net[name_node + '_geodata'].loc[:, "y"] = \ + transform(in_proj, out_proj, x1, y1) return net diff --git a/pandapower/plotting/plotly/pf_res_plotly.py b/pandapower/plotting/plotly/pf_res_plotly.py index f9034d244..8ee01dde3 100644 --- a/pandapower/plotting/plotly/pf_res_plotly.py +++ b/pandapower/plotting/plotly/pf_res_plotly.py @@ -87,7 +87,7 @@ def pf_res_plotly(net, cmap="Jet", use_line_geodata=None, on_map=False, projecti if len(net.line_geodata) == 0 and len(net.bus_geodata) == 0: logger.warning("No or insufficient geodata available --> Creating artificial coordinates." + " This may take some time") - create_generic_coordinates(net, respect_switches=True) + create_generic_coordinates(net, respect_separation_points=True) if on_map: logger.warning("Map plots not available with artificial coordinates and will be disabled!") on_map = False diff --git a/pandapower/plotting/plotly/simple_plotly.py b/pandapower/plotting/plotly/simple_plotly.py index 16ab3d61c..6c69b2c94 100644 --- a/pandapower/plotting/plotly/simple_plotly.py +++ b/pandapower/plotting/plotly/simple_plotly.py @@ -125,7 +125,7 @@ def simple_plotly(net, respect_switches=True, use_line_geodata=None, on_map=Fals if len(net.bus_geodata) == 0: logger.warning("No or insufficient geodata available --> Creating artificial coordinates." + " This may take some time...") - create_generic_coordinates(net, respect_switches=respect_switches) + create_generic_coordinates(net, respect_separation_points=respect_switches) if on_map: logger.warning("Map plots not available with artificial coordinates and will be disabled!") on_map = False diff --git a/pandapower/plotting/plotly/vlevel_plotly.py b/pandapower/plotting/plotly/vlevel_plotly.py index 8eb1040dc..ab78a155d 100644 --- a/pandapower/plotting/plotly/vlevel_plotly.py +++ b/pandapower/plotting/plotly/vlevel_plotly.py @@ -74,7 +74,7 @@ def vlevel_plotly(net, respect_switches=True, use_line_geodata=None, colors_dict if len(net.line_geodata) == 0 and len(net.bus_geodata) == 0: logger.warning("No or insufficient geodata available --> Creating artificial coordinates." + " This may take some time") - create_generic_coordinates(net, respect_switches=respect_switches) + create_generic_coordinates(net, respect_separation_points=respect_switches) if on_map: logger.warning("Map plots not available with artificial coordinates and will be disabled!") on_map = False diff --git a/pandapower/plotting/simple_plot.py b/pandapower/plotting/simple_plot.py index 08117bbbc..806ecc6f0 100644 --- a/pandapower/plotting/simple_plot.py +++ b/pandapower/plotting/simple_plot.py @@ -102,7 +102,7 @@ def simple_plot(net, respect_switches=False, line_width=1.0, bus_size=1.0, ext_g if len(net.line_geodata) == 0 and len(net.bus_geodata) == 0: logger.warning("No or insufficient geodata available --> Creating artificial coordinates." + " This may take some time") - create_generic_coordinates(net, respect_switches=respect_switches, library=library) + create_generic_coordinates(net, respect_separation_points=respect_switches, library=library) if scale_size: # if scale_size -> calc size from distance between min and max geocoord From 8482a3a54eac1dbf659ad9c93411791952ad8b9d Mon Sep 17 00:00:00 2001 From: fmeier Date: Wed, 4 Dec 2019 13:57:44 +0100 Subject: [PATCH 17/38] Fix for plotly color bars for nets with uncontinuous bus index --- pandapower/plotting/plotly/traces.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pandapower/plotting/plotly/traces.py b/pandapower/plotting/plotly/traces.py index 74d24adc8..5b40af9c8 100644 --- a/pandapower/plotting/plotly/traces.py +++ b/pandapower/plotting/plotly/traces.py @@ -387,9 +387,13 @@ def create_line_trace(net, lines=None, use_line_geodata=True, respect_switches=F # TODO for custom colormaps cbar_cmap_name = 'Jet' if cmap is 'jet' else cmap # workaround to get colorbar for lines (an unvisible node is added) - lines_cbar = dict(type='scatter', x=[net.bus_geodata.x[0]], y=[net.bus_geodata.y[0]], mode='markers', + # get x and y of first line.from_bus: + x = [net.bus_geodata.x[net.line.from_bus[net.line.index[0]]]] + y = [net.bus_geodata.y[net.line.from_bus[net.line.index[0]]]] + lines_cbar = dict(type='scatter', x=x, y=y, mode='markers', marker=Marker(size=0, cmin=cmin, cmax=cmax, color='rgb(255,255,255)', + opacity=0, colorscale=cbar_cmap_name, colorbar=ColorBar(thickness=10, x=cpos), From 10038d41990cf18ba665306abae443d26fbb1e6e Mon Sep 17 00:00:00 2001 From: Zhenqi Wang Date: Thu, 5 Dec 2019 19:27:14 +0100 Subject: [PATCH 18/38] Update current measurement unit in docs --- doc/estimation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/estimation.rst b/doc/estimation.rst index 6bf0b7d71..857e1ed71 100644 --- a/doc/estimation.rst +++ b/doc/estimation.rst @@ -44,7 +44,7 @@ Measurements are defined via the pandapower *"create_measurement"* function. The - *"v"* for voltage measurements (in per-unit) - *"p"* for active power measurements (in MW) - *"q"* for reactive power measurements (in MVar) - - *"i"* for electrical current measurements at a line (in A) + - *"i"* for electrical current measurements at a line (in kA) **Element Types** From 3cbc9256a31c1f8a9dfca0618445ec93547769ca Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Fri, 6 Dec 2019 16:29:00 +0100 Subject: [PATCH 19/38] made generic functions in collection creation private --- pandapower/plotting/collections.py | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index 59cf7210a..e72169266 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -119,8 +119,8 @@ def add_cmap_to_collection(collection, cmap, norm, z, cbar_title, plot_colormap= return collection -def create_node_collection(nodes, coords, size=5, patch_type="circle", color=None, picker=False, - infos=None, **kwargs): +def _create_node_collection(nodes, coords, size=5, patch_type="circle", color=None, picker=False, + infos=None, **kwargs): """ Creates a collection with patches for the given nodes. Can be used generically for different \ types of nodes (bus in pandapower network, but also other nodes, e.g. in a networkx graph). @@ -172,7 +172,7 @@ def create_node_collection(nodes, coords, size=5, patch_type="circle", color=Non return pc -def create_line2d_collection(coords, indices, infos=None, picker=False, **kwargs): +def _create_line2d_collection(coords, indices, infos=None, picker=False, **kwargs): """ Generic function to create a LineCollection from coordinates. @@ -198,10 +198,10 @@ def create_line2d_collection(coords, indices, infos=None, picker=False, **kwargs return lc -def create_node_element_collection(node_coords, patch_maker, size=1., infos=None, - repeat_infos=(1, 1), orientation=np.pi, picker=False, - patch_facecolor="w", patch_edgecolor="k", line_color="k", - **kwargs): +def _create_node_element_collection(node_coords, patch_maker, size=1., infos=None, + repeat_infos=(1, 1), orientation=np.pi, picker=False, + patch_facecolor="w", patch_edgecolor="k", line_color="k", + **kwargs): """ Creates matplotlib collections of node elements. All node element collections usually consist of one patch collection representing the element itself and a small line collection that connects @@ -260,9 +260,9 @@ def create_node_element_collection(node_coords, patch_maker, size=1., infos=None return patch_coll, line_coll -def create_complex_branch_collection(coords, patch_maker, size=1, infos=None, repeat_infos=(2, 2), - picker=False, patch_facecolor="w", patch_edgecolor="k", - line_color="k", linewidths=2., **kwargs): +def _create_complex_branch_collection(coords, patch_maker, size=1, infos=None, repeat_infos=(2, 2), + picker=False, patch_facecolor="w", patch_edgecolor="k", + line_color="k", linewidths=2., **kwargs): """ Creates a matplotlib line collection and a matplotlib patch collection representing a branch\ element that cannot be represented by just a line. @@ -371,7 +371,7 @@ def create_bus_collection(net, buses=None, size=5, patch_type="circle", color=No infos = [infofunc(bus) for bus in buses] if infofunc is not None else [] - pc = create_node_collection(buses, coords, size, patch_type, color, picker, infos, **kwargs) + pc = _create_node_collection(buses, coords, size, patch_type, color, picker, infos, **kwargs) if cmap is not None: add_cmap_to_collection(pc, cmap, norm, z, cbar_title) @@ -450,7 +450,7 @@ def create_line_collection(net, lines=None, line_geodata=None, bus_geodata=None, infos = [infofunc(line) for line in lines_with_geo] if infofunc else [] - lc = create_line2d_collection(coords, lines_with_geo, infos=infos, picker=picker, **kwargs) + lc = _create_line2d_collection(coords, lines_with_geo, infos=infos, picker=picker, **kwargs) if cmap is not None: if z is None: @@ -509,7 +509,7 @@ def create_trafo_connection_collection(net, trafos=None, bus_geodata=None, infof info = [infofunc(tr) for tr in trafos.index.values] if infofunc is not None else [] - lc = create_line2d_collection(tg, trafos.index.values, info, picker=picker, **kwargs) + lc = _create_line2d_collection(tg, trafos.index.values, info, picker=picker, **kwargs) if cmap is not None: if z is None: @@ -616,7 +616,7 @@ def create_trafo_collection(net, trafos=None, picker=False, size=None, infofunc= infos = [infofunc(i) for i in range(len(trafos_with_geo))] if infofunc is not None else [] - lc, pc = create_complex_branch_collection( + lc, pc = _create_complex_branch_collection( coords, trafo_patches, size, infos, patch_facecolor="none", patch_edgecolor=colors, line_color=colors, picker=picker, linewidths=linewidths, **kwargs) @@ -796,7 +796,7 @@ def create_load_collection(net, loads=None, size=1., infofunc=None, orientation= loads = net.load.index infos = [infofunc(i) for i in range(len(loads))] if infofunc is not None else [] node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.load.loc[loads, "bus"].values] - load_pc, load_lc = create_node_element_collection( + load_pc, load_lc = _create_node_element_collection( node_coords, load_patches, size=size, infos=infos, orientation=orientation, picker=picker, **kwargs) return load_pc, load_lc @@ -833,7 +833,7 @@ def create_gen_collection(net, gens=None, size=1., infofunc=None, orientation=np gens = net.gen.index infos = [infofunc(i) for i in range(len(gens))] if infofunc is not None else [] node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.gen.loc[gens, "bus"].values] - gen_pc, gen_lc = create_node_element_collection( + gen_pc, gen_lc = _create_node_element_collection( node_coords, gen_patches, size=size, infos=infos, orientation=orientation, picker=picker, **kwargs) return gen_pc, gen_lc @@ -870,7 +870,7 @@ def create_sgen_collection(net, sgens=None, size=1., infofunc=None, orientation= sgens = net.sgen.index infos = [infofunc(i) for i in range(len(sgens))] if infofunc is not None else [] node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.sgen.loc[sgens, "bus"].values] - sgen_pc, sgen_lc = create_node_element_collection( + sgen_pc, sgen_lc = _create_node_element_collection( node_coords, sgen_patches, size=size, infos=infos, orientation=orientation, picker=picker, **kwargs) return sgen_pc, sgen_lc @@ -916,7 +916,7 @@ def create_ext_grid_collection(net, size=1., infofunc=None, orientation=0, picke node_coords = net.bus_geodata.loc[ext_grid_buses, ["x", "y"]].values - ext_grid_pc, ext_grid_lc = create_node_element_collection( + ext_grid_pc, ext_grid_lc = _create_node_element_collection( node_coords, ext_grid_patches, size=size, infos=infos, orientation=orientation, picker=picker, hatch='XXX', **kwargs) From 39124bff2de4eb59adbcf2e63c324f838fbd8345 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Mon, 9 Dec 2019 16:00:34 +0100 Subject: [PATCH 20/38] adapted some tests to be np.isclose instead of equality --- pandapower/io_utils.py | 2 +- pandapower/test/opf/test_costs_mixed.py | 3 ++- pandapower/test/opf/test_costs_pol.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pandapower/io_utils.py b/pandapower/io_utils.py index bc9f8296a..a7b677820 100644 --- a/pandapower/io_utils.py +++ b/pandapower/io_utils.py @@ -482,7 +482,7 @@ def to_serializable(obj): @to_serializable.register(pandapowerNet) -def json_net(obj): +def json_pandapowernet(obj): net_dict = {k: item for k, item in obj.items() if not k.startswith("_")} d = with_signature(obj, net_dict) return d diff --git a/pandapower/test/opf/test_costs_mixed.py b/pandapower/test/opf/test_costs_mixed.py index 660936833..3ce36c06d 100644 --- a/pandapower/test/opf/test_costs_mixed.py +++ b/pandapower/test/opf/test_costs_mixed.py @@ -41,7 +41,7 @@ def test_cost_mixed(): pp.create_poly_cost(net, 0, "gen", cp1_eur_per_mw=1) pp.runopp(net) assert net["OPF_converged"] - assert net.res_cost == net.res_gen.p_mw.values[0] + assert np.isclose(net.res_cost, net.res_gen.p_mw.values[0]) net.poly_cost.cp1_eur_per_mw.at[0] = 0 net.poly_cost.cp2_eur_per_mw2.at[0] = 1 @@ -117,6 +117,7 @@ def test_mixed_p_q_pwl(): assert net["OPF_converged"] assert np.allclose(net.res_cost, net.res_gen.p_mw.values + net.res_gen.q_mvar.values) + if __name__ == "__main__": # vm_max = 1.05 # vm_min = 0.95 diff --git a/pandapower/test/opf/test_costs_pol.py b/pandapower/test/opf/test_costs_pol.py index e50af93a3..bde1f58a8 100644 --- a/pandapower/test/opf/test_costs_pol.py +++ b/pandapower/test/opf/test_costs_pol.py @@ -42,7 +42,7 @@ def test_cost_pol_gen(): pp.runopp(net) assert net["OPF_converged"] - assert net.res_cost == net.res_gen.p_mw.values + assert np.isclose(net.res_cost, net.res_gen.p_mw.values) net.poly_cost.cp1_eur_per_mw.at[0] = 0 net.poly_cost.cp2_eur_per_mw2.at[0] = 1 From 33c145c390b195b703919492753c96c5bc8a6631 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Tue, 10 Dec 2019 10:39:12 +0100 Subject: [PATCH 21/38] fix in dcline test --- pandapower/test/opf/test_dcline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandapower/test/opf/test_dcline.py b/pandapower/test/opf/test_dcline.py index a8f6b1c6f..c78278fd1 100644 --- a/pandapower/test/opf/test_dcline.py +++ b/pandapower/test/opf/test_dcline.py @@ -57,7 +57,7 @@ def test_dispatch1(dcline_net): net.bus["max_vm_pu"] = 2 net.bus["min_vm_pu"] = 0 # needs to be constrained more than default net.line["max_loading_percent"] = 1000 # does not converge if unconstrained - pp.runopp(net, delta=1e-8) + pp.runopp(net, delta=1e-7) consistency_checks(net) rel_loss_expect = (net.res_dcline.pl_mw - net.dcline.loss_mw) / \ (net.res_dcline.p_from_mw - net.res_dcline.pl_mw) * 100 @@ -69,7 +69,7 @@ def test_dispatch1(dcline_net): assert allclose(net.res_dcline.p_from_mw.values, [0.500754071], atol=1e-3) assert allclose(net.res_dcline.q_from_mvar.values, [7.78745600524]) - assert allclose(net.res_dcline.p_to_mw.values, array([-5.48553789e-05])) + assert allclose(net.res_dcline.p_to_mw.values, array([-5.48553789e-05]), atol=1e-3) assert allclose(net.res_dcline.q_to_mvar.values, array([-.62712636707])) @pytest.mark.xfail(reason="numerical issue with OPF convergence. If vm_pu delta is != 0. at ext_grid -> fail. See " From 1a168e21739b64347f70b46c0d7818b37d237a3e Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Tue, 10 Dec 2019 11:23:00 +0100 Subject: [PATCH 22/38] set dcline test to xfail --- pandapower/test/opf/test_dcline.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pandapower/test/opf/test_dcline.py b/pandapower/test/opf/test_dcline.py index c78278fd1..22fe58e62 100644 --- a/pandapower/test/opf/test_dcline.py +++ b/pandapower/test/opf/test_dcline.py @@ -41,6 +41,7 @@ def dcline_net(): return net + def get_delta_try_except(net): for delta in [1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12]: try: @@ -50,6 +51,9 @@ def get_delta_try_except(net): continue return 1e-10 + +@pytest.mark.xfail(reason="numerical issue with OPF convergence. The failure seems to depend on the" + " python version. Should be reworked.") def test_dispatch1(dcline_net): net = dcline_net pp.create_pwl_cost(net, 0, "ext_grid", [[-1e12, 1e9, 100]]) @@ -57,7 +61,7 @@ def test_dispatch1(dcline_net): net.bus["max_vm_pu"] = 2 net.bus["min_vm_pu"] = 0 # needs to be constrained more than default net.line["max_loading_percent"] = 1000 # does not converge if unconstrained - pp.runopp(net, delta=1e-7) + pp.runopp(net, delta=1e-8) consistency_checks(net) rel_loss_expect = (net.res_dcline.pl_mw - net.dcline.loss_mw) / \ (net.res_dcline.p_from_mw - net.res_dcline.pl_mw) * 100 @@ -69,11 +73,12 @@ def test_dispatch1(dcline_net): assert allclose(net.res_dcline.p_from_mw.values, [0.500754071], atol=1e-3) assert allclose(net.res_dcline.q_from_mvar.values, [7.78745600524]) - assert allclose(net.res_dcline.p_to_mw.values, array([-5.48553789e-05]), atol=1e-3) + assert allclose(net.res_dcline.p_to_mw.values, array([-5.48553789e-05])) assert allclose(net.res_dcline.q_to_mvar.values, array([-.62712636707])) -@pytest.mark.xfail(reason="numerical issue with OPF convergence. If vm_pu delta is != 0. at ext_grid -> fail. See " - "build_gen() in line 111 + 112") + +@pytest.mark.xfail(reason="numerical issue with OPF convergence. If vm_pu delta is != 0. at " + "ext_grid -> fail. See build_gen() in line 111 + 112") def test_dcline_dispatch2(dcline_net): net = dcline_net pp.create_poly_cost(net, 0, "ext_grid", cp1_eur_per_mw=80) @@ -109,8 +114,9 @@ def test_dcline_dispatch2(dcline_net): assert allclose(net.res_dcline.p_to_mw.values, p_to_expect) assert allclose(net.res_dcline.q_to_mvar.values, q_to_expect) -@pytest.mark.xfail(reason="numerical issue with OPF convergence. If vm_pu delta is != 0. at ext_grid -> fail. See " - "build_gen() in line 111 + 112") + +@pytest.mark.xfail(reason="numerical issue with OPF convergence. If vm_pu delta is != 0. at " + "ext_grid -> fail. See build_gen() in line 111 + 112") def test_dcline_dispatch3(dcline_net): net = dcline_net pp.create_poly_cost(net, 4, "dcline", cp1_eur_per_mw=1.5) From 48bd1ed52885f8f33e6d0ef77ec99ec6c1043315 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Wed, 11 Dec 2019 10:44:29 +0100 Subject: [PATCH 23/38] more generic formulations in geodata creation and manipulation for plotting --- pandapower/plotting/generic_geodata.py | 169 ++++++++++++++++--------- pandapower/plotting/geo.py | 130 ++++++++++++++----- 2 files changed, 206 insertions(+), 93 deletions(-) diff --git a/pandapower/plotting/generic_geodata.py b/pandapower/plotting/generic_geodata.py index 4c754a182..5450e97f0 100644 --- a/pandapower/plotting/generic_geodata.py +++ b/pandapower/plotting/generic_geodata.py @@ -8,9 +8,23 @@ import networkx as nx import pandas as pd +import numpy as np import pandapower.topology as top +try: + import igraph + IGRAPH_INSTALLED = True +except ImportError: + IGRAPH_INSTALLED = False + +try: + import pplog as logging +except ImportError: + import logging + +logger = logging.getLogger(__name__) + def build_igraph_from_pp(net, respect_switches=False): """ @@ -18,14 +32,14 @@ def build_igraph_from_pp(net, respect_switches=False): Lines, transformers and switches are respected. Performance vs. networkx: https://graph-tool.skewed.de/performance - Input: - - **net** - pandapower network - - Example: - - graph = build_igraph_from_pp(net + :param net: pandapower network + :type net: pandapowerNet + :param respect_switches: if True, exclude edges for open switches (also lines that are \ + connected via line switches) + :type respect_switches: bool, default False + :Example: + graph, meshed, roots = build_igraph_from_pp(net) """ try: import igraph as ig @@ -33,16 +47,16 @@ def build_igraph_from_pp(net, respect_switches=False): raise ImportError("Please install python-igraph") g = ig.Graph(directed=True) g.add_vertices(net.bus.shape[0]) - g.vs["label"] = net.bus.index.tolist() # [s.encode('unicode-escape') for s in net.bus.name.tolist()] + # g.vs["label"] = [s.encode('unicode-escape') for s in net.bus.name.tolist()] + g.vs["label"] = net.bus.index.tolist() pp_bus_mapping = dict(list(zip(net.bus.index, list(range(net.bus.index.shape[0]))))) # add lines nogolines = set(net.switch.element[(net.switch.et == "l") & (net.switch.closed == 0)]) \ - if respect_switches else set() + if respect_switches else set() for lix in (ix for ix in net.line.index if ix not in nogolines): fb, tb = net.line.at[lix, "from_bus"], net.line.at[lix, "to_bus"] - g.add_edge(pp_bus_mapping[fb], pp_bus_mapping[tb]) - g.es["weight"] = net.line.length_km.values + g.add_edge(pp_bus_mapping[fb], pp_bus_mapping[tb], weight=net.line.at[lix, "length_km"]) # add trafos for _, trafo in net.trafo.iterrows(): @@ -54,7 +68,7 @@ def build_igraph_from_pp(net, respect_switches=False): # add switches bs = net.switch[(net.switch.et == "b") & (net.switch.closed == 1)] if respect_switches else \ - net.switch[(net.switch.et == "b")] + net.switch[(net.switch.et == "b")] for fb, tb in zip(bs.bus, bs.element): g.add_edge(pp_bus_mapping[fb], pp_bus_mapping[tb], weight=0.001) @@ -68,78 +82,107 @@ def build_igraph_from_pp(net, respect_switches=False): return g, meshed, roots # g, (not g.is_dag()) -def create_generic_coordinates(net, mg=None, library="igraph", respect_separation_points=False, - name_node='bus'): +def coords_from_igraph(graph, roots, meshed=False, calculate_meshed=False): + """ + Create a list of generic coordinates from an igraph graph layout. + + :param graph: The igraph graph on which the coordinates shall be based + :type graph: igraph.Graph + :param roots: The root buses of the graph + :type roots: iterable + :param meshed: determines if the graph has any meshes + :type meshed: bool, default False + :param calculate_meshed: determines whether to calculate the meshed status + :type calculate_meshed: bool, default False + :return: coords - list of coordinates from the graph layout """ - 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 created either by igraph or by - using networkx library. + if calculate_meshed: + meshed = False + for i in range(1, net.bus.shape[0]): + if len(g.get_all_shortest_paths(0, i, mode="ALL")) > 1: + meshed = True + break + if meshed is True: + layout = graph.layout("kk") + else: + graph.to_undirected(mode="each", combine_edges="first") + layout = graph.layout("rt", root=roots) + return list(zip(*layout.coords)) - INPUT: - **net** - pandapower network - OPTIONAL: - **mg** - Existing networkx multigraph, if available. Convenience to save computation time. +def coords_from_nxgraph(mg=None): + """ + Create a list of generic coordinates from a networkx graph layout. - **library** - "igraph" to use igraph package or "networkx" to use networkx package + :param mg: The networkx graph on which the coordinates shall be based + :type mg: networkx.Graph + :return: coords - list of coordinates from the graph layout + """ + # workaround for bug in agraph + for u, v in mg.edges(data=False, keys=False): + if 'key' in mg[int(u)][int(v)]: + del mg[int(u)][int(v)]['key'] + if 'key' in mg[int(u)][int(v)][0]: + del mg[int(u)][int(v)][0]['key'] + # ToDo: Insert fallback layout for nxgraph + return list(zip(*(list(nx.drawing.nx_agraph.graphviz_layout(mg, prog='neato').values())))) - OUTPUT: - **net** - pandapower network with added geo coordinates for the buses - EXAMPLE: +def create_generic_coordinates(net, mg=None, library="igraph", respect_switches=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 + created either by igraph or by using networkx library. + + :param net: pandapower network + :type net: pandapowerNet + :param mg: Existing networkx multigraph, if available. Convenience to save computation time. + :type mg: networkx.Graph + :param library: "igraph" to use igraph package or "networkx" to use networkx package + :type library: str + :return: net - pandapower network with added geo coordinates for the buses + + :Example: net = create_generic_coordinates(net) - """ - if "name_node +'_geodata'" in net and net[name_node +'_geodata'].shape[0]: - print("Please delete all geodata. This function cannot be used with pre-existing geodata.") + + if "bus_geodata" in net and net.bus_geodata.shape[0]: + logger.warning("Please delete all geodata. This function cannot be used with pre-existing" + " geodata.") return - if not "name_node +'_geodata'" in net or net[name_node +'_geodata'] is None: - net[name_node +'_geodata'] = pd.DataFrame(columns=["x", "y"]) + if "bus_geodata" not in net or net.bus_geodata is None: + net.bus_geodata = pd.DataFrame(columns=["x", "y"]) gnet = copy.deepcopy(net) - gnet[name_node] = gnet[name_node][gnet[name_node].in_service == True] + gnet.bus = gnet.bus[gnet.bus.in_service == True] + if library == "igraph": - try: - import igraph - except ImportError: - raise UserWarning("The library igraph is selected for plotting, " - "but not installed correctly.") - graph, meshed, roots = build_igraph_from_pp(gnet, respect_separation_points) - if meshed: - layout = graph.layout("kk") - else: - graph.to_undirected(mode="each", combine_edges="first") - layout = graph.layout("rt", root=roots) - coords = list(zip(*layout.coords)) + if not IGRAPH_INSTALLED: + raise UserWarning("The library igraph is selected for plotting, but not installed " + "correctly.") + graph, meshed, roots = create_igraph(net, respect_switches) + coords = coords_from_igraph(graph, meshed, roots) elif library == "networkx": if mg is None: - nxg = top.create_nxgraph(gnet, respect_separation_points) + nxg = create_mg(gnet, respect_switches) else: nxg = copy.deepcopy(mg) - # workaround for bug in agraph - for u, v in nxg.edges(data=False, keys=False): - if 'key' in nxg[int(u)][int(v)]: - del nxg[int(u)][int(v)]['key'] - if 'key' in nxg[int(u)][int(v)][0]: - del nxg[int(u)][int(v)][0]['key'] - # ToDo: Insert fallback layout for nxgraph - coords = list(zip(*(list(nx.drawing.nx_agraph.graphviz_layout(nxg, prog='neato').values())))) + coords = coords_from_nxgraph(nxg) else: - raise ValueError("Unknown library %s - chose 'igraph' or 'networkx'"%library) - net[name_node +'_geodata'].x = coords[1] - net[name_node +'_geodata'].y = coords[0] - net[name_node +'_geodata'].index = gnet[name_node].index + raise ValueError("Unknown library %s - chose 'igraph' or 'networkx'" % library) + + net.bus_geodata.x = coords[1] + net.bus_geodata.y = coords[0] + net.bus_geodata.index = gnet.bus.index return net -def fuse_geodata(net, name_node_geodata='bus'): - name_node = name_node_geodata +def fuse_geodata(net): mg = top.create_nxgraph(net, include_lines=False, include_impedances=False, respect_switches=False) - geocoords = set(net[name_node +'_geodata'].index) + geocoords = set(net.bus_geodata.index) for area in top.connected_components(mg): if len(area & geocoords) > 1: - geo = net[name_node +'_geodata'].loc[area & geocoords].values[0] - for name_node in area: - net[name_node +'_geodata'].loc[name_node] = geo - + geo = net.bus_geodata.loc[area & geocoords].values[0] + for bus in area: + net.bus_geodata.loc[bus] = geo diff --git a/pandapower/plotting/geo.py b/pandapower/plotting/geo.py index 397e24428..0cd328fb2 100644 --- a/pandapower/plotting/geo.py +++ b/pandapower/plotting/geo.py @@ -7,37 +7,112 @@ pass -def convert_geodata_to_gis(net, epsg=31467, node_geodata=True, branch_geodata=True, - name_node_geodata='bus', name_branch_geodata='line'): - name_node = name_node_geodata - name_branch = name_branch_geodata +def _node_geometries_from_geodata(node_geo, epsg=31467): + """ + Creates a geopandas geodataframe from a given dataframe of with node coordinates as x and y + values. + + :param node_geo: The dataframe containing the node coordinates (x and y values) + :type node_geo: pandas.dataframe + :param epsg: The epsg projection of the node coordinates + :type epsg: int, default 31467 (= Gauss-Krüger Zone 3) + :return: node_geodata - a geodataframe containing the node_geo and Points in the geometry column + """ + geoms = [Point(x, y) for x, y in node_geo[["x", "y"]].values] + return GeoDataFrame(node_geo, crs=from_epsg(epsg), geometry=geoms, index=node_geo.index) + + +def _branch_geometries_from_geodata(branch_geo, epsg=31467): + geoms = GeoSeries([LineString(x) for x in branch_geo.coords.values], index=branch_geo.index, + crs=from_epsg(epsg)) + return GeoDataFrame(branch_geo, crs=from_epsg(epsg), geometry=geoms, index=branch_geo.index) + + +def _transform_node_geometry_to_geodata(node_geo): + """ + Create x and y values from geodataframe + + :param node_geo: The dataframe containing the node geometries (as shapely points) + :type node_geo: geopandas.GeoDataFrame + :return: bus_geo - The given geodataframe with x and y values + """ + node_geo["x"] = [p.x for p in node_geo.geometry] + node_geo["y"] = [p.y for p in node_geo.geometry] + return node_geo + + +def _transform_branch_geometry_to_coords(branch_geo): + """ + Create coords entries from geodataframe geometries + + :param branch_geo: The dataframe containing the branch geometries (as shapely LineStrings) + :type branch_geo: geopandas.GeoDataFrame + :return: branch_geo - The given geodataframe with coords + """ + branch_geo["coords"] = branch_geo["coords"].geometry.apply(lambda x: list(x.coords)) + return branch_geo + + +def _convert_xy_epsg(x, y, epsg_in=4326, epsg_out=31467): + """ + Converts the given x and y coordinates according to the defined epsg projections. + + :param x: x-values of coordinates + :type x: iterable + :param y: y-values of coordinates + :type y: iterable + :param epsg_in: current epsg projection + :type epsg_in: int, default 4326 (= WGS84) + :param epsg_out: epsg projection to be transformed to + :type epsg_out: int, default 31467 (= Gauss-Krüger Zone 3) + :return: transformed_coords - x and y values in new coordinate system + """ + in_proj = Proj(init='epsg:%i' % epsg_in) + out_proj = Proj(init='epsg:%i' % epsg_out) + return transform(in_proj, out_proj, x, y) + + +def convert_gis_to_geodata(net, node_geodata=True, branch_geodata=True): + """ + Extracts information on bus and line geodata from the geometries of a geopandas geodataframe. + + :param net: The net for which to convert the geodata + :type net: pandapowerNet + :param node_geodata: flag if to extract x and y values for bus geodata + :type node_geodata: bool, default True + :param branch_geodata: flag if to extract coordinates values for line geodata + :type branch_geodata: bool, default True + :return: No output. + """ if node_geodata: - node_geo = net[name_node + '_geodata'] - geo = [Point(x, y) for x, y in node_geo[["x", "y"]].values] - net[name_node + '_geodata'] = GeoDataFrame(node_geo, crs=from_epsg(epsg), geometry=geo, - index=node_geo.index) + _transform_node_geometry_to_geodata(net.bus_geodata) if branch_geodata: - branch_geo = net[name_branch + '_geodata'] - geo = GeoSeries([LineString(x) for x in net[name_branch + '_geodata'].coords.values], - index=net[name_branch + '_geodata'].index, crs=from_epsg(epsg)) - net[name_branch + '_geodata'] = GeoDataFrame(branch_geo, crs=from_epsg(epsg), geometry=geo, - index=branch_geo.index) - net["gis_epsg_code"] = epsg + _transform_branch_geometry_to_coords(net.line_geodata) -def convert_gis_to_geodata(net, node_geodata=True, branch_geodata=True, name_node_geodata='bus', - name_branch_geodata='line'): - name_node = name_node_geodata - name_branch = name_branch_geodata +def convert_geodata_to_gis(net, epsg=31467, node_geodata=True, branch_geodata=True): + """ + Transforms the bus and line geodata of a net into a geopandaas geodataframe with the respective + geometries. + + :param net: The net for which to convert the geodata + :type net: pandapowerNet + :param epsg: current epsg projection + :type epsg: int, default 4326 (= WGS84) + :param node_geodata: flag if to transform the bus geodata table + :type node_geodata: bool, default True + :param branch_geodata: flag if to transform the line geodata table + :type branch_geodata: bool, default True + :return: No output. + """ if node_geodata: - net[name_node + '_geodata']["x"] = [x.x for x in net[name_node + '_geodata'].geometry] - net[name_node + '_geodata']["y"] = [x.y for x in net[name_node + '_geodata'].geometry] + net["bus_geodata"] = _node_geometries_from_geodata(net["bus_geodata"], epsg) if branch_geodata: - net[name_branch +'_geodata']["coords"] = \ - net[name_branch +'_geodata'].geometry.apply(lambda x: list(x.coords)) + net["line_geodata"] = _branch_geometries_from_geodata(net["line_geodata"], epsg) + net["gis_epsg_code"] = epsg -def convert_epgs_bus_geodata(net, epsg_in=4326, epsg_out=31467, name_node_geodata='bus'): +def convert_epsg_bus_geodata(net, epsg_in=4326, epsg_out=31467): """ Converts bus geodata in net from epsg_in to epsg_out @@ -49,11 +124,6 @@ def convert_epgs_bus_geodata(net, epsg_in=4326, epsg_out=31467, name_node_geodat :type epsg_out: int, default 31467 (= Gauss-Krüger Zone 3) :return: net - the given pandapower network (no copy!) """ - name_node = name_node_geodata - in_proj = Proj(init='epsg:%i' % epsg_in) - out_proj = Proj(init='epsg:%i' % epsg_out) - x1, y1 = net[name_node + '_geodata'].loc[:, "x"].values, \ - net[name_node + '_geodata'].loc[:, "y"].values - net[name_node + '_geodata'].loc[:, "x"], net[name_node + '_geodata'].loc[:, "y"] = \ - transform(in_proj, out_proj, x1, y1) + net['bus_geodata'].loc[:, "x"], net['bus_geodata'].loc[:, "y"] = _convert_xy_epsg( + net['bus_geodata'].loc[:, "x"], net['bus_geodata'].loc[:, "y"], epsg_in, epsg_out) return net From b89808840ddf477f7538af25352d0e507f3016d3 Mon Sep 17 00:00:00 2001 From: rbolgaryn Date: Wed, 11 Dec 2019 14:44:26 +0100 Subject: [PATCH 24/38] ConstControl: write_to_net in time_step --- pandapower/control/controller/const_control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandapower/control/controller/const_control.py b/pandapower/control/controller/const_control.py index bb1676dd3..69e55cb44 100644 --- a/pandapower/control/controller/const_control.py +++ b/pandapower/control/controller/const_control.py @@ -131,7 +131,7 @@ def time_step(self, time): self.values = self.data_source.get_time_step_value(time_step=time, profile_name=self.profile_name, scale_factor=self.scale_factor) - # self.write_to_net() + self.write_to_net() def initialize_control(self): """ From 5df631530aa65a72b00cc3e504b74807f7618f48 Mon Sep 17 00:00:00 2001 From: rbolgaryn Date: Fri, 13 Dec 2019 15:14:25 +0100 Subject: [PATCH 25/38] small changes to address warnings --- pandapower/auxiliary.py | 2 +- pandapower/create.py | 8 ++++---- pandapower/std_types.py | 2 +- pandapower/test/api/test_convert_format.py | 3 +-- pandapower/test/api/test_file_io.py | 2 +- pandapower/test/api/test_std_types.py | 3 +-- pandapower/toolbox.py | 3 ++- 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/pandapower/auxiliary.py b/pandapower/auxiliary.py index b1e843eec..77b96dbfe 100644 --- a/pandapower/auxiliary.py +++ b/pandapower/auxiliary.py @@ -26,7 +26,7 @@ # THE SOFTWARE. # (https://github.com/bcj/AttrDict/blob/master/LICENSE.txt) -from collections import MutableMapping +from collections.abc import MutableMapping import copy import numpy as np diff --git a/pandapower/create.py b/pandapower/create.py index 9f25e7006..fd49bfe31 100644 --- a/pandapower/create.py +++ b/pandapower/create.py @@ -1271,7 +1271,7 @@ def create_ext_grid(net, bus, vm_pu=1.0, va_degree=0., name=None, in_service=Tru **min_q_mvar** (float, NaN) - Minimum reactive power injection. Only respected for OPF - \* only considered in loadflow if calculate_voltage_angles = True + ** only considered in loadflow if calculate_voltage_angles = True EXAMPLE: create_ext_grid(net, 1, voltage = 1.03) @@ -1864,7 +1864,7 @@ def create_transformer_from_parameters(net, hv_bus, lv_bus, sn_mva, vn_hv_kv, vn **df** (float) - derating factor: maximal current of transformer in relation to nominal \ current of transformer (from 0 to 1) - \* only considered in loadflow if calculate_voltage_angles = True + ** only considered in loadflow if calculate_voltage_angles = True OUTPUT: **index** (int) - The unique ID of the created transformer @@ -2112,8 +2112,8 @@ def create_transformer3w_from_parameters(net, hv_bus, mv_bus, lv_bus, vn_hv_kv, **in_service** (boolean, True) - True for in_service or False for out of service - \* only considered in loadflow if calculate_voltage_angles = True - \**The model currently only supports one tap-changer per 3W Transformer. + ** only considered in loadflow if calculate_voltage_angles = True + **The model currently only supports one tap-changer per 3W Transformer. **max_loading_percent (float)** - maximum current loading (only needed for OPF) diff --git a/pandapower/std_types.py b/pandapower/std_types.py index 1d7954eff..8ede09b45 100644 --- a/pandapower/std_types.py +++ b/pandapower/std_types.py @@ -25,7 +25,7 @@ def create_std_type(net, data, name, element="line", overwrite=True, check_requi additional parameters can be added and later loaded into pandapower with the function "parameter_from_std_type". - \* only considered in loadflow if calculate_voltage_angles = True + ** only considered in loadflow if calculate_voltage_angles = True The standard type is saved into the pandapower library of the given network by default. diff --git a/pandapower/test/api/test_convert_format.py b/pandapower/test/api/test_convert_format.py index 3647ca95f..2b92d59ef 100644 --- a/pandapower/test/api/test_convert_format.py +++ b/pandapower/test/api/test_convert_format.py @@ -38,5 +38,4 @@ def test_convert_format(version): if __name__ == '__main__': - # load_and_test_network("1.4.3") - pytest.main(__file__) + pytest.main([__file__]) diff --git a/pandapower/test/api/test_file_io.py b/pandapower/test/api/test_file_io.py index 69c811b72..391b8508e 100644 --- a/pandapower/test/api/test_file_io.py +++ b/pandapower/test/api/test_file_io.py @@ -244,4 +244,4 @@ def test_json_io_same_net(net_in, tempdir): if __name__ == "__main__": - pytest.main(["test_file_io.py", "-x"]) + pytest.main([__file__, "-x"]) diff --git a/pandapower/test/api/test_std_types.py b/pandapower/test/api/test_std_types.py index a6d74b1ac..e0fa9c005 100644 --- a/pandapower/test/api/test_std_types.py +++ b/pandapower/test/api/test_std_types.py @@ -322,5 +322,4 @@ def test_add_temperature_coefficient(): if __name__ == "__main__": -# net = pp.create_empty_network() - pytest.main(["test_std_types.py"]) + pytest.main([__file__]) diff --git a/pandapower/toolbox.py b/pandapower/toolbox.py index efb3aefae..6b641971c 100644 --- a/pandapower/toolbox.py +++ b/pandapower/toolbox.py @@ -4,7 +4,8 @@ # and Energy System Technology (IEE), Kassel. All rights reserved. import copy -from collections import Iterable, defaultdict +from collections import defaultdict +from collections.abc import Iterable from itertools import chain import numpy as np From 1ab8c667bf7a1b61166f5e397b153c7dda399d0f Mon Sep 17 00:00:00 2001 From: rbolgaryn Date: Fri, 13 Dec 2019 15:35:00 +0100 Subject: [PATCH 26/38] revert const_control.py --- pandapower/control/controller/const_control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandapower/control/controller/const_control.py b/pandapower/control/controller/const_control.py index 69e55cb44..bb1676dd3 100644 --- a/pandapower/control/controller/const_control.py +++ b/pandapower/control/controller/const_control.py @@ -131,7 +131,7 @@ def time_step(self, time): self.values = self.data_source.get_time_step_value(time_step=time, profile_name=self.profile_name, scale_factor=self.scale_factor) - self.write_to_net() + # self.write_to_net() def initialize_control(self): """ From 3a9affbcaa68512dd3c06fa6e0772cb08118e7df Mon Sep 17 00:00:00 2001 From: Derrick Oswald Date: Tue, 17 Dec 2019 21:20:21 +0100 Subject: [PATCH 27/38] remove deprecated output_writer keyword argument --- tutorials/time_series.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/time_series.ipynb b/tutorials/time_series.ipynb index 0b2df4d1f..ca6403093 100644 --- a/tutorials/time_series.ipynb +++ b/tutorials/time_series.ipynb @@ -123,7 +123,7 @@ " ow = create_output_writer(net, time_steps, output_dir=output_dir)\n", "\n", " # 5. the main time series function\n", - " run_timeseries(net, time_steps, output_writer=ow)" + " run_timeseries(net, time_steps)" ], "execution_count": 0, "outputs": [] From 09d08ea55bd942c8de92f89e33d440be121158db Mon Sep 17 00:00:00 2001 From: Derrick Oswald Date: Tue, 17 Dec 2019 21:36:49 +0100 Subject: [PATCH 28/38] remove deprecated output_writer keyword argument #2 --- tutorials/time_series_advanced_output.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/time_series_advanced_output.ipynb b/tutorials/time_series_advanced_output.ipynb index adf50f7ea..48fa1647b 100644 --- a/tutorials/time_series_advanced_output.ipynb +++ b/tutorials/time_series_advanced_output.ipynb @@ -88,7 +88,7 @@ " ow = create_output_writer(net, time_steps, output_dir)\n", "\n", " # 5. the main time series function\n", - " run_timeseries(net, time_steps, output_writer=ow)" + " run_timeseries(net, time_steps)" ] }, { From f2033fdf351c75ffdaca7dd746347f9394f59b44 Mon Sep 17 00:00:00 2001 From: Derrick Oswald Date: Tue, 17 Dec 2019 21:55:22 +0100 Subject: [PATCH 29/38] typo correction --- tutorials/time_series.ipynb | 2 +- tutorials/time_series_advanced_output.ipynb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/time_series.ipynb b/tutorials/time_series.ipynb index ca6403093..78d538a58 100644 --- a/tutorials/time_series.ipynb +++ b/tutorials/time_series.ipynb @@ -135,7 +135,7 @@ "colab_type": "text" }, "source": [ - "We start by creating a simple example pandapower net consinsting of five buses, a transfomer, three lines, a load and a sgen. " + "We start by creating a simple example pandapower net consisting of five buses, a transfomer, three lines, a load and a sgen. " ] }, { diff --git a/tutorials/time_series_advanced_output.ipynb b/tutorials/time_series_advanced_output.ipynb index 48fa1647b..b2b8548d7 100644 --- a/tutorials/time_series_advanced_output.ipynb +++ b/tutorials/time_series_advanced_output.ipynb @@ -95,7 +95,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We start by creating a simple example pandapower net consinsting of five buses, a transfomer, three lines, a load and a sgen. " + "We start by creating a simple example pandapower net consisting of five buses, a transfomer, three lines, a load and a sgen. " ] }, { From c076185dfbd8b05b232e1df6ce08397e302985a8 Mon Sep 17 00:00:00 2001 From: rbolgaryn Date: Wed, 18 Dec 2019 11:44:32 +0100 Subject: [PATCH 30/38] bugfix coords in line collections --- pandapower/plotting/collections.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index e72169266..37a8c17f1 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -439,7 +439,7 @@ def create_line_collection(net, lines=None, line_geodata=None, bus_geodata=None, bus_geodata if bus_geodata is not None else net["bus_geodata"], "line") else: lines_with_geo = lines[np.isin(lines, line_geodata.index.values)] - coords = list(line_geodata[lines_with_geo]) + coords = list(line_geodata.loc[lines_with_geo, 'coords']) lines_without_geo = set(lines) - set(lines_with_geo) if lines_without_geo: logger.warning("Could not plot lines %s. %s geodata is missing for those lines!" From f724e3d553f042c478cc9c4e5f436118393ef4b4 Mon Sep 17 00:00:00 2001 From: Florian Schaefer Date: Wed, 18 Dec 2019 16:21:48 +0100 Subject: [PATCH 31/38] minor changes in time series and refactor --- .../timeseries/test_timeseries_recycle.py | 3 ++- pandapower/timeseries/output_writer.py | 4 ++++ pandapower/timeseries/run_time_series.py | 22 +++++++++++++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/pandapower/test/timeseries/test_timeseries_recycle.py b/pandapower/test/timeseries/test_timeseries_recycle.py index f6d87d4af..cd1a755b1 100644 --- a/pandapower/test/timeseries/test_timeseries_recycle.py +++ b/pandapower/test/timeseries/test_timeseries_recycle.py @@ -109,8 +109,9 @@ def _run_recycle(net): return vm_pu, ll -def _run_normal(net): +def _run_normal(net, time_steps=time_steps): ow = OutputWriter(net, output_path=tempfile.gettempdir(), output_file_type=".json") + ow.log_variable("res_bus", "va_degree") run_timeseries(net, time_steps, recycle=False) return ow diff --git a/pandapower/timeseries/output_writer.py b/pandapower/timeseries/output_writer.py index a5a2b4528..0a54dd16d 100644 --- a/pandapower/timeseries/output_writer.py +++ b/pandapower/timeseries/output_writer.py @@ -120,6 +120,10 @@ def __repr__(self): s += "\n'" + str(table) + "." + str(variable) + "'" return s + def _monkey_patch(self, method, new): + from types import MethodType + setattr(self, method, MethodType(new, self)) + def _add_log_defaults(self): if self.log_variables is None: self.log_variables = list() diff --git a/pandapower/timeseries/run_time_series.py b/pandapower/timeseries/run_time_series.py index 384d97616..d231ddb6c 100644 --- a/pandapower/timeseries/run_time_series.py +++ b/pandapower/timeseries/run_time_series.py @@ -256,6 +256,8 @@ def init_time_series(net, time_steps, continue_on_divergence=False, verbose=True ts_variables["time_steps"] = time_steps # If True, a diverged power flow is ignored and the next step is calculated ts_variables["continue_on_divergence"] = continue_on_divergence + # print settings + ts_variables["verbose"] = verbose if logger.level is not 10 and verbose: # simple progress bar @@ -266,7 +268,7 @@ def init_time_series(net, time_steps, continue_on_divergence=False, verbose=True def cleanup(ts_variables): if isinstance(ts_variables["recycle_options"], dict): - # Todo: delete internal variaables and dumped results which are not needed + # Todo: delete internal variables and dumped results which are not needed pass @@ -285,6 +287,20 @@ def print_progress(i, time_step, time_steps, verbose, **kwargs): func = kwargs["progress_function"] func(i, time_step, time_steps, **kwargs) +def run_loop(net, ts_variables, **kwargs): + """ + runs the time series loop which calls pp.runpp (or another run function) in each iteration + + Parameters + ---------- + net - pandapower net + ts_variables - settings for time series + + """ + for i, time_step in enumerate(ts_variables["time_steps"]): + print_progress(i, time_step, ts_variables["time_steps"], ts_variables["verbose"], **kwargs) + run_time_step(net, time_step, ts_variables, **kwargs) + def run_timeseries(net, time_steps=None, continue_on_divergence=False, verbose=True, **kwargs): """ @@ -311,9 +327,7 @@ def run_timeseries(net, time_steps=None, continue_on_divergence=False, verbose=T ts_variables = init_time_series(net, time_steps, continue_on_divergence, verbose, **kwargs) control_diagnostic(net) - for i, time_step in enumerate(ts_variables["time_steps"]): - print_progress(i, time_step, ts_variables["time_steps"], verbose, **kwargs) - run_time_step(net, time_step, ts_variables, **kwargs) + run_loop(net, ts_variables, **kwargs) # cleanup functions after the last time step was calculated cleanup(ts_variables) From 43d417195c5c1b9356bf358b19465d4d85fc1f39 Mon Sep 17 00:00:00 2001 From: Florian Schaefer Date: Wed, 18 Dec 2019 16:45:46 +0100 Subject: [PATCH 32/38] hint in opf summary legacy report --- pandapower/pypower/printpf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandapower/pypower/printpf.py b/pandapower/pypower/printpf.py index 81fb924aa..e960dc095 100644 --- a/pandapower/pypower/printpf.py +++ b/pandapower/pypower/printpf.py @@ -186,7 +186,7 @@ def printpf(baseMVA, bus=None, gen=None, branch=None, f=None, success=None, if OUT_SYS_SUM: fd.write('\n================================================================================') - fd.write('\n| System Summary |') + fd.write('\n| PyPower (ppci) System Summary - these are not valid for pandapower DataFrames|') fd.write('\n================================================================================') fd.write('\n\nHow many? How much? P (MW) Q (MVAr)') fd.write('\n--------------------- ------------------- ------------- -----------------') From ecb53d31f51477d05c41bbb0b3a4eb881c32283c Mon Sep 17 00:00:00 2001 From: Florian Schaefer Date: Tue, 24 Dec 2019 13:14:13 +0100 Subject: [PATCH 33/38] fix for sgen and load plot when indices are not consecutive :xmas: --- pandapower/plotting/collections.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index 37a8c17f1..5c4a432bf 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -795,7 +795,7 @@ def create_load_collection(net, loads=None, size=1., infofunc=None, orientation= if loads is None: loads = net.load.index infos = [infofunc(i) for i in range(len(loads))] if infofunc is not None else [] - node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.load.loc[loads, "bus"].values] + node_coords = net.bus_geodata.loc[net.load.loc[loads, "bus"].values, ["x", "y"]].values load_pc, load_lc = _create_node_element_collection( node_coords, load_patches, size=size, infos=infos, orientation=orientation, picker=picker, **kwargs) @@ -869,7 +869,7 @@ def create_sgen_collection(net, sgens=None, size=1., infofunc=None, orientation= if sgens is None: sgens = net.sgen.index infos = [infofunc(i) for i in range(len(sgens))] if infofunc is not None else [] - node_coords = net.bus_geodata.loc[:, ["x", "y"]].values[net.sgen.loc[sgens, "bus"].values] + node_coords = net.bus_geodata.loc[net.sgen.loc[sgens, "bus"].values, ["x", "y"]].values sgen_pc, sgen_lc = _create_node_element_collection( node_coords, sgen_patches, size=size, infos=infos, orientation=orientation, picker=picker, **kwargs) From eae3124619d920bbb674bdd1a8b8167ffcc6de04 Mon Sep 17 00:00:00 2001 From: Derrick Oswald Date: Tue, 24 Dec 2019 21:40:33 +0100 Subject: [PATCH 34/38] Add log_variables parameter to OutputWriter constructor call to avoid default output variables. Also fixed some typos. --- pandapower/test/shortcircuit/test_1ph.py | 2 +- tutorials/time_series.ipynb | 8 ++++---- tutorials/time_series_advanced_output.ipynb | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pandapower/test/shortcircuit/test_1ph.py b/pandapower/test/shortcircuit/test_1ph.py index 98ffc589a..9afb743cb 100644 --- a/pandapower/test/shortcircuit/test_1ph.py +++ b/pandapower/test/shortcircuit/test_1ph.py @@ -41,7 +41,7 @@ def add_network(net, vector_group): "shift_degree": 150, "vector_group": vector_group, "tap_side": "hv", "tap_neutral": 0, "tap_min": -9, "tap_max": 9, "tap_step_degree": 0, "tap_step_percent": 1.5, "tap_phase_shifter": False, "vk0_percent": 5, - "vkr0_percent": 0.4, "mag0_percent": 10, "mag0_rx": 0.4, "mag0_rx": 0.4, + "vkr0_percent": 0.4, "mag0_percent": 10, "mag0_rx": 0.4, "si0_hv_partial": 0.9} pp.create_std_type(net, transformer_type, vector_group, "trafo") t1 = pp.create_transformer(net, b1, b2, std_type=vector_group, parallel=2, diff --git a/tutorials/time_series.ipynb b/tutorials/time_series.ipynb index 78d538a58..f650c1e03 100644 --- a/tutorials/time_series.ipynb +++ b/tutorials/time_series.ipynb @@ -135,7 +135,7 @@ "colab_type": "text" }, "source": [ - "We start by creating a simple example pandapower net consisting of five buses, a transfomer, three lines, a load and a sgen. " + "We start by creating a simple example pandapower net consisting of five buses, a transformer, three lines, a load and a sgen. " ] }, { @@ -262,7 +262,7 @@ }, "source": [ "def create_output_writer(net, time_steps, output_dir):\n", - " ow = OutputWriter(net, time_steps, output_path=output_dir, output_file_type=\".xls\")\n", + " ow = OutputWriter(net, time_steps, output_path=output_dir, output_file_type=\".xls\", log_variables=list())\n", " # these variables are saved to the harddisk after / during the time series loop\n", " ow.log_variable('res_load', 'p_mw')\n", " ow.log_variable('res_bus', 'vm_pu')\n", @@ -280,7 +280,7 @@ "colab_type": "text" }, "source": [ - "Now lets execude the code." + "Now lets execute the code." ] }, { @@ -443,4 +443,4 @@ "outputs": [] } ] -} \ No newline at end of file +} diff --git a/tutorials/time_series_advanced_output.ipynb b/tutorials/time_series_advanced_output.ipynb index b2b8548d7..8e02bcb3c 100644 --- a/tutorials/time_series_advanced_output.ipynb +++ b/tutorials/time_series_advanced_output.ipynb @@ -95,7 +95,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We start by creating a simple example pandapower net consisting of five buses, a transfomer, three lines, a load and a sgen. " + "We start by creating a simple example pandapower net consisting of five buses, a transformer, three lines, a load and a sgen. " ] }, { @@ -201,7 +201,7 @@ "outputs": [], "source": [ "def create_output_writer(net, time_steps, output_dir):\n", - " ow = OutputWriter(net, time_steps, output_path=output_dir, output_file_type=\".xls\")\n", + " ow = OutputWriter(net, time_steps, output_path=output_dir, output_file_type=\".xls\", log_variables=list())\n", " \n", " # create a mask to get the indices of all the hv buses in the grid \n", " mask_hv_buses = (net.bus.vn_kv > 70.0) & (net.bus.vn_kv < 380.0)\n", @@ -210,8 +210,8 @@ " mask_mv_buses = (net.bus.vn_kv > 1.0) & (net.bus.vn_kv < 70.0)\n", " mv_busses_index = net.bus.loc[mask_mv_buses].index\n", " # now define the output writer, so that it gets the indices and specify the evaluation functions\n", - " # since we want the maximum voltage of all mv buses, we provide the indices of the mv buses and the minimum \n", - " # function np.min. The variable \"eval_name\" is free to chose and contains the name of the column in\n", + " # since we want the maximum voltage of all mv buses, we provide the indices of the mv buses and the maximum \n", + " # function np.max. The variable \"eval_name\" is free to chose and contains the name of the column in\n", " # which the results are saved. \n", " ow.log_variable('res_bus', 'p_mw', index=hv_busses_index, eval_function=np.sum, eval_name=\"hv_bus_sum_p\")\n", " ow.log_variable('res_bus', 'vm_pu', index=mv_busses_index, eval_function=np.max, eval_name=\"mv_bus_max\")\n", @@ -222,7 +222,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now lets execude the code." + "Now lets execute the code." ] }, { @@ -253,7 +253,7 @@ "metadata": {}, "source": [ "If everything works you should have the desired results the temporary folder of your os (see print statement above).\n", - "In this folder two excel files should have appared containing the desired output for each of the ten time steps" + "In this folder two excel files should have appeared containing the desired output for each of the ten time steps" ] }, { From 39be8cb76920b4d868ecb8ea1db6a7df8aa23e67 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Thu, 2 Jan 2020 10:29:07 +0100 Subject: [PATCH 35/38] added OSError to exception in IOUtils upon checking for shapely (fix for Issue #584) --- pandapower/io_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandapower/io_utils.py b/pandapower/io_utils.py index a7b677820..1fd17ec1b 100644 --- a/pandapower/io_utils.py +++ b/pandapower/io_utils.py @@ -39,7 +39,7 @@ import shapely.geometry SHAPELY_INSTALLED = True -except ImportError: +except (ImportError, OSError): SHAPELY_INSTALLED = False try: From 99dd049fc5915848f8e26c95ffad35344bd5125d Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Mon, 6 Jan 2020 11:18:23 +0100 Subject: [PATCH 36/38] some pep and corrections in test_toolbox --- pandapower/test/api/test_toolbox.py | 120 ++++++++++++++-------------- 1 file changed, 62 insertions(+), 58 deletions(-) diff --git a/pandapower/test/api/test_toolbox.py b/pandapower/test/api/test_toolbox.py index 9b6e3f896..79b37a92d 100644 --- a/pandapower/test/api/test_toolbox.py +++ b/pandapower/test/api/test_toolbox.py @@ -139,17 +139,17 @@ def test_add_column_from_node_to_elements(): branch_bus = ["from_bus", "lv_bus"] pp.add_column_from_node_to_elements(net, "subnet", False, branch_bus=branch_bus) - def check_subnet_correctness(net, elements, branch_bus): + def check_subnet_correctness(ntw, elements, branch_bus_el): for elm in elements: - if "bus" in net[elm].columns: - assert all(pp.compare_arrays(net[elm]["subnet"].values, - np.array(["subnet_%i" % bus for bus in net[elm].bus]))) - elif branch_bus[0] in net[elm].columns: - assert all(pp.compare_arrays(net[elm]["subnet"].values, np.array([ - "subnet_%i" % bus for bus in net[elm][branch_bus[0]]]))) - elif branch_bus[1] in net[elm].columns: - assert all(pp.compare_arrays(net[elm]["subnet"].values, np.array([ - "subnet_%i" % bus for bus in net[elm][branch_bus[1]]]))) + if "bus" in ntw[elm].columns: + assert all(pp.compare_arrays(ntw[elm]["subnet"].values, + np.array(["subnet_%i" % bus for bus in ntw[elm].bus]))) + elif branch_bus_el[0] in ntw[elm].columns: + assert all(pp.compare_arrays(ntw[elm]["subnet"].values, np.array([ + "subnet_%i" % bus for bus in ntw[elm][branch_bus_el[0]]]))) + elif branch_bus_el[1] in ntw[elm].columns: + assert all(pp.compare_arrays(ntw[elm]["subnet"].values, np.array([ + "subnet_%i" % bus for bus in ntw[elm][branch_bus_el[1]]]))) check_subnet_correctness(net, pp.pp_elements(bus=False) - {"sgen"}, branch_bus) @@ -201,6 +201,7 @@ def test_reindex_buses(): if elm == "bus": assert all(np.array(list(net[elm].index)) == np.array(list( net_orig[elm].index)) + to_add) + np.random.seed(None) def test_continuos_bus_numbering(): @@ -229,24 +230,24 @@ def test_continuos_bus_numbering(): tb.create_continuous_bus_index(net) - l = net.bus.index - assert all(l[i] <= l[i + 1] for i in range(len(l) - 1)) # is ordered - assert all(l[i] + 1 == l[i + 1] for i in range(len(l) - 1)) # is consecutive - assert l[0] == 0 # starts at zero + buses = net.bus.index + assert all(buses[i] <= buses[i + 1] for i in range(len(buses) - 1)) # is ordered + assert all(buses[i] + 1 == buses[i + 1] for i in range(len(buses) - 1)) # is consecutive + assert buses[0] == 0 # starts at zero used_buses = [] for element in net.keys(): try: - used_buses + net[element].bus.values - except: + used_buses.extend(net[element].bus.values) + except AttributeError: try: - used_buses + net[element].from_bus.values - used_buses + net[element].to_bus.values - except: + used_buses.extend(net[element].from_bus.values) + used_buses.extend(net[element].to_bus.values) + except AttributeError: try: - used_buses + net[element].hv_bus.values - used_buses + net[element].lv_bus.values - except: + used_buses.extend(net[element].hv_bus.values) + used_buses.extend(net[element].lv_bus.values) + except AttributeError: continue # assert that no buses were used except the ones in net.bus @@ -396,19 +397,19 @@ def test_get_connected_lines_at_bus(): lines = tb.get_connected_elements(net, "line", bus0, respect_switches=False, respect_in_service=False) - assert set(lines) == set([line0, line1, line2, line3]) + assert set(lines) == {line0, line1, line2, line3} lines = tb.get_connected_elements(net, "line", bus0, respect_switches=True, respect_in_service=False) - assert set(lines) == set([line0, line2, line3]) + assert set(lines) == {line0, line2, line3} lines = tb.get_connected_elements(net, "line", bus0, respect_switches=True, respect_in_service=True) - assert set(lines) == set([line0, line3]) + assert set(lines) == {line0, line3} lines = tb.get_connected_elements(net, "line", bus0, respect_switches=False, respect_in_service=True) - assert set(lines) == set([line0, line1, line3]) + assert set(lines) == {line0, line1, line3} def test_merge_and_split_nets(): @@ -442,7 +443,7 @@ def test_overloaded_lines(): line0 = pp.create_line(net, bus0, bus1, length_km=1, std_type="NAYY 4x50 SE") line1 = pp.create_line(net, bus0, bus1, length_km=1, std_type="NA2XS2Y 1x95 RM/25 12/20 kV") line2 = pp.create_line(net, bus0, bus1, length_km=1, std_type="15-AL1/3-ST1A 0.4") - line3 = pp.create_line(net, bus0, bus1, length_km=10, std_type="149-AL1/24-ST1A 10.0") + pp.create_line(net, bus0, bus1, length_km=10, std_type="149-AL1/24-ST1A 10.0") pp.create_load(net, bus1, p_mw=0.2, q_mvar=0.05) @@ -450,12 +451,12 @@ def test_overloaded_lines(): # test the overloaded lines by default value of max_load=100 overloaded_lines = tb.overloaded_lines(net, max_load=100) - assert set(overloaded_lines) == set([line0, line1]) + assert set(overloaded_lines) == {line0, line1} # test the overloaded lines by a self defined value of max_load=50 overloaded_lines = tb.overloaded_lines(net, max_load=50) - assert set(overloaded_lines) == set([line0, line1, line2]) + assert set(overloaded_lines) == {line0, line1, line2} def test_violated_buses(): @@ -479,12 +480,12 @@ def test_add_zones_to_elements(): # add zones to lines and switchs tb.add_zones_to_elements(net, elements=["line", "switch"]) - # create 2 arrays which include "zone" in lines and switchs + # create 2 arrays which include "zone" in lines and switches zone_line = net["line"]["zone"].values zone_switch = net["switch"]["zone"].values - assert "CIGRE_MV" in net["line"]["zone"].values - assert "CIGRE_MV" in net["switch"]["zone"].values + assert "CIGRE_MV" in zone_line + assert "CIGRE_MV" in zone_switch def test_fuse_buses(): @@ -494,18 +495,18 @@ def test_fuse_buses(): line1 = pp.create_line(net, b2, b1, length_km=1, std_type="NAYY 4x50 SE") - sw1 = pp.create_switch(net, b2, line1, et="l") - sw2 = pp.create_switch(net, b1, b2, et="b") + pp.create_switch(net, b2, line1, et="l") + pp.create_switch(net, b1, b2, et="b") - load1 = pp.create_load(net, b1, p_mw=0.006) - load2 = pp.create_load(net, b2, p_mw=0.005) + pp.create_load(net, b1, p_mw=0.006) + pp.create_load(net, b2, p_mw=0.005) tb.fuse_buses(net, b1, b2, drop=True) # assertion: elements connected to b2 are given to b1 instead - assert net["line"]["from_bus"].loc[0] == b1 - assert net["switch"]["bus"].loc[0] == b1 - assert net["load"]["bus"].loc[1] == b1 + assert net["line"]["from_bus"].at[0] == b1 + assert net["switch"]["bus"].at[0] == b1 + assert net["load"]["bus"].at[1] == b1 # assertion: b2 not in net.bus table if drop=True assert b2 not in net.bus.index @@ -519,18 +520,18 @@ def test_close_switch_at_line_with_two_open_switches(): line1 = pp.create_line(net, bus2, bus3, length_km=1., std_type="NAYY 4x50 SE") line2 = pp.create_line(net, bus2, bus3, length_km=1., std_type="NAYY 4x50 SE") - line3 = pp.create_line(net, bus2, bus3, length_km=1., std_type="NAYY 4x50 SE") + pp.create_line(net, bus2, bus3, length_km=1., std_type="NAYY 4x50 SE") # line3 - sw1 = pp.create_switch(net, bus1, bus2, et="b", closed=True) + pp.create_switch(net, bus1, bus2, et="b", closed=True) # sw0 - sw2 = pp.create_switch(net, bus2, line1, et="l", closed=False) - sw3 = pp.create_switch(net, bus3, line1, et="l", closed=False) + pp.create_switch(net, bus2, line1, et="l", closed=False) # sw1 + pp.create_switch(net, bus3, line1, et="l", closed=False) # sw2 - sw4 = pp.create_switch(net, bus2, line2, et="l", closed=True) - sw5 = pp.create_switch(net, bus3, line2, et="l", closed=False) + pp.create_switch(net, bus2, line2, et="l", closed=True) # sw3 + pp.create_switch(net, bus3, line2, et="l", closed=False) # sw4 - sw6 = pp.create_switch(net, bus3, line2, et="l", closed=True) - sw7 = pp.create_switch(net, bus3, line2, et="l", closed=True) + pp.create_switch(net, bus3, line2, et="l", closed=True) # sw5 + pp.create_switch(net, bus3, line2, et="l", closed=True) # sw6 tb.close_switch_at_line_with_two_open_switches(net) @@ -651,15 +652,15 @@ def net(): pp.create_ext_grid(net, bus0, vm_pu=0.4) - line0 = pp.create_line(net, bus0, bus1, length_km=0, std_type="NAYY 4x50 SE") - line1 = pp.create_line_from_parameters(net, bus2, bus3, length_km=1, r_ohm_per_km=0, - x_ohm_per_km=0.1, c_nf_per_km=0, max_i_ka=1) - line2 = pp.create_line_from_parameters(net, bus3, bus4, length_km=1, r_ohm_per_km=0, - x_ohm_per_km=0, c_nf_per_km=0, max_i_ka=1) + pp.create_line(net, bus0, bus1, length_km=0, std_type="NAYY 4x50 SE") # line0 + pp.create_line_from_parameters(net, bus2, bus3, length_km=1, r_ohm_per_km=0, x_ohm_per_km=0.1, + c_nf_per_km=0, max_i_ka=1) # line1 + pp.create_line_from_parameters(net, bus3, bus4, length_km=1, r_ohm_per_km=0, x_ohm_per_km=0, + c_nf_per_km=0, max_i_ka=1) # line2 - impedance0 = pp.create_impedance(net, bus1, bus2, 0.01, 0.01, sn_mva=100) - impedance1 = pp.create_impedance(net, bus4, bus5, 0, 0, sn_mva=100) - impedance2 = pp.create_impedance(net, bus5, bus6, 0, 0, rtf_pu=0.1, sn_mva=100) + pp.create_impedance(net, bus1, bus2, 0.01, 0.01, sn_mva=100) # impedance0 + pp.create_impedance(net, bus4, bus5, 0, 0, sn_mva=100) # impedance1 + pp.create_impedance(net, bus5, bus6, 0, 0, rtf_pu=0.1, sn_mva=100) # impedance2 return net @@ -741,7 +742,8 @@ def test_next_bus(): std_type='63/25/38 MVA 110/20/10 kV') trafo1 = pp.create_transformer(net, hv_bus=bus2, lv_bus=bus3, std_type='0.4 MVA 10/0.4 kV') - line1 = pp.create_line(net, from_bus=bus3, to_bus=bus4, length_km=20.1, std_type='24-AL1/4-ST1A 0.4', name='line1') + line1 = pp.create_line(net, from_bus=bus3, to_bus=bus4, length_km=20.1, + std_type='24-AL1/4-ST1A 0.4', name='line1') # switch0=pp.create_switch(net, bus = bus0, element = trafo0, et = 't3') #~~~~~ not implementable now switch1 = pp.create_switch(net, bus=bus1, element=bus5, et='b') @@ -771,7 +773,8 @@ def test_get_connected_buses(): std_type='63/25/38 MVA 110/20/10 kV') trafo1 = pp.create_transformer(net, hv_bus=bus2, lv_bus=bus3, std_type='0.4 MVA 10/0.4 kV') - line1 = pp.create_line(net, from_bus=bus3, to_bus=bus4, length_km=20.1, std_type='24-AL1/4-ST1A 0.4', name='line1') + line1 = pp.create_line(net, from_bus=bus3, to_bus=bus4, length_km=20.1, + std_type='24-AL1/4-ST1A 0.4', name='line1') # switch0=pp.create_switch(net, bus = bus0, element = trafo0, et = 't3') #~~~~~ not implementable switch1 = pp.create_switch(net, bus=bus1, element=bus5, et='b') @@ -805,7 +808,8 @@ def test_drop_elements_at_buses(): std_type='63/25/38 MVA 110/20/10 kV') trafo1 = pp.create_transformer(net, hv_bus=bus2, lv_bus=bus3, std_type='0.4 MVA 10/0.4 kV') - line1 = pp.create_line(net, from_bus=bus3, to_bus=bus4, length_km=20.1, std_type='24-AL1/4-ST1A 0.4', name='line1') + line1 = pp.create_line(net, from_bus=bus3, to_bus=bus4, length_km=20.1, + std_type='24-AL1/4-ST1A 0.4', name='line1') # switch0=pp.create_switch(net, bus = bus0, element = trafo0, et = 't3') #~~~~~ not implementable now switch1 = pp.create_switch(net, bus=bus1, element=bus5, et='b') From 1ae2c4e0f24717decc156569644b01c9d8b3fb17 Mon Sep 17 00:00:00 2001 From: dlohmeier Date: Wed, 8 Jan 2020 14:36:50 +0100 Subject: [PATCH 37/38] bugfix for line collections concerning different types of geodata input --- pandapower/plotting/collections.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index 5c4a432bf..e7e6df159 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -380,10 +380,9 @@ def create_bus_collection(net, buses=None, size=5, patch_type="circle", color=No def create_line_collection(net, lines=None, line_geodata=None, bus_geodata=None, - use_bus_geodata=False, infofunc=None, - cmap=None, norm=None, picker=False, z=None, - cbar_title="Line Loading [%]", clim=None, - plot_colormap=True, **kwargs): + use_bus_geodata=False, infofunc=None, cmap=None, norm=None, picker=False, + z=None, cbar_title="Line Loading [%]", clim=None, plot_colormap=True, + **kwargs): """ Creates a matplotlib line collection of pandapower lines. @@ -422,7 +421,7 @@ def create_line_collection(net, lines=None, line_geodata=None, bus_geodata=None, OUTPUT: **lc** - line collection """ - if use_bus_geodata is False and net.line_geodata.empty: + if use_bus_geodata is False and line_geodata is None and net.line_geodata.empty: # if bus geodata is available, but no line geodata logger.warning("use_bus_geodata is automatically set to True, since net.line_geodata is " "empty.") @@ -438,6 +437,7 @@ def create_line_collection(net, lines=None, line_geodata=None, bus_geodata=None, lines, net.line.from_bus.loc[lines].values, net.line.to_bus.loc[lines].values, bus_geodata if bus_geodata is not None else net["bus_geodata"], "line") else: + line_geodata = line_geodata if line_geodata is not None else net.line_geodata lines_with_geo = lines[np.isin(lines, line_geodata.index.values)] coords = list(line_geodata.loc[lines_with_geo, 'coords']) lines_without_geo = set(lines) - set(lines_with_geo) From cf41faa0f029e31b3d89ddfd14159468fead50db Mon Sep 17 00:00:00 2001 From: fmeier Date: Tue, 14 Jan 2020 12:57:24 +0100 Subject: [PATCH 38/38] Happy new year! Changed year in license texts. --- doc/about/license.rst | 2 +- doc/conf.py | 2 +- pandapower/auxiliary.py | 2 +- pandapower/build_branch.py | 2 +- pandapower/build_bus.py | 2 +- pandapower/build_gen.py | 2 +- pandapower/control/basic_controller.py | 2 +- pandapower/control/controller/const_control.py | 2 +- pandapower/control/run_control.py | 2 +- pandapower/control/util/auxiliary.py | 2 +- pandapower/control/util/diagnostic.py | 2 +- pandapower/convert_format.py | 2 +- pandapower/converter/matpower/from_mpc.py | 2 +- pandapower/converter/matpower/to_mpc.py | 2 +- pandapower/converter/pypower/from_ppc.py | 2 +- pandapower/converter/pypower/to_ppc.py | 2 +- pandapower/create.py | 2 +- pandapower/diagnostic.py | 2 +- pandapower/diagnostic_reports.py | 2 +- pandapower/estimation/algorithm/base.py | 2 +- pandapower/estimation/algorithm/estimator.py | 2 +- pandapower/estimation/algorithm/lp.py | 2 +- pandapower/estimation/algorithm/matrix_base.py | 2 +- pandapower/estimation/idx_brch.py | 2 +- pandapower/estimation/idx_bus.py | 2 +- pandapower/estimation/ppc_conversion.py | 2 +- pandapower/estimation/results.py | 2 +- pandapower/estimation/state_estimation.py | 2 +- pandapower/estimation/util.py | 2 +- pandapower/file_io.py | 2 +- pandapower/io_utils.py | 2 +- pandapower/networks/cigre_networks.py | 2 +- pandapower/networks/create_examples.py | 2 +- pandapower/networks/kerber_extreme_networks.py | 2 +- pandapower/networks/kerber_networks.py | 2 +- pandapower/networks/mv_oberrhein.py | 2 +- pandapower/networks/power_system_test_cases.py | 2 +- pandapower/networks/simple_pandapower_test_networks.py | 2 +- pandapower/networks/synthetic_voltage_control_lv_networks.py | 2 +- pandapower/opf/make_objective.py | 2 +- pandapower/optimal_powerflow.py | 2 +- pandapower/pd2ppc.py | 2 +- pandapower/pd2ppc_zero.py | 2 +- pandapower/pf/create_jacobian_numba.py | 2 +- pandapower/pf/dSbus_dV_numba.py | 2 +- pandapower/pf/makeYbus_numba.py | 2 +- pandapower/pf/pfsoln_numba.py | 2 +- pandapower/pf/ppci_variables.py | 2 +- pandapower/pf/run_bfswpf.py | 2 +- pandapower/pf/run_dc_pf.py | 2 +- pandapower/pf/run_newton_raphson_pf.py | 2 +- pandapower/pf/runpf_pypower.py | 2 +- pandapower/plotting/collections.py | 2 +- pandapower/plotting/colormaps.py | 2 +- pandapower/plotting/generic_geodata.py | 2 +- pandapower/plotting/plotly/get_colors.py | 2 +- pandapower/plotting/plotly/mapbox_plot.py | 2 +- pandapower/plotting/plotly/pf_res_plotly.py | 2 +- pandapower/plotting/plotly/simple_plotly.py | 2 +- pandapower/plotting/plotly/traces.py | 2 +- pandapower/plotting/plotly/vlevel_plotly.py | 2 +- pandapower/plotting/powerflow_results.py | 2 +- pandapower/plotting/simple_plot.py | 2 +- pandapower/plotting/to_html.py | 2 +- pandapower/powerflow.py | 2 +- pandapower/pypower/bustypes.py | 2 +- pandapower/pypower/dSbus_dV.py | 2 +- pandapower/pypower/dcpf.py | 2 +- pandapower/pypower/idx_brch.py | 2 +- pandapower/pypower/idx_bus.py | 2 +- pandapower/pypower/makeBdc.py | 2 +- pandapower/pypower/makeLODF.py | 2 +- pandapower/pypower/makePTDF.py | 2 +- pandapower/pypower/makeYbus.py | 2 +- pandapower/pypower/newtonpf.py | 2 +- pandapower/pypower/opf.py | 2 +- pandapower/pypower/opf_execute.py | 2 +- pandapower/pypower/opf_hessfcn.py | 2 +- pandapower/pypower/opf_setup.py | 2 +- pandapower/pypower/pfsoln.py | 2 +- pandapower/pypower/pips.py | 2 +- pandapower/pypower/pipsopf_solver.py | 2 +- pandapower/results.py | 2 +- pandapower/results_branch.py | 2 +- pandapower/results_bus.py | 2 +- pandapower/results_gen.py | 2 +- pandapower/run.py | 2 +- pandapower/runpm.py | 2 +- pandapower/shortcircuit/calc_sc.py | 2 +- pandapower/shortcircuit/currents.py | 2 +- pandapower/shortcircuit/idx_brch.py | 2 +- pandapower/shortcircuit/idx_bus.py | 2 +- pandapower/shortcircuit/impedance.py | 2 +- pandapower/shortcircuit/kappa.py | 2 +- pandapower/shortcircuit/results.py | 2 +- pandapower/std_types.py | 2 +- pandapower/test/api/test_auxiliary.py | 2 +- pandapower/test/api/test_convert_format.py | 2 +- pandapower/test/api/test_create.py | 2 +- pandapower/test/api/test_diagnostic.py | 2 +- pandapower/test/api/test_file_io.py | 2 +- pandapower/test/api/test_std_types.py | 2 +- pandapower/test/api/test_toolbox.py | 2 +- pandapower/test/conftest.py | 2 +- pandapower/test/consistency_checks.py | 2 +- pandapower/test/control/test_continous_tap_control.py | 2 +- pandapower/test/control/test_control.py | 2 +- pandapower/test/control/test_discrete_tap_control.py | 2 +- pandapower/test/converter/test_from_mpc.py | 2 +- pandapower/test/converter/test_from_ppc.py | 2 +- pandapower/test/converter/test_to_ppc_and_mpc.py | 2 +- pandapower/test/estimation/test_irwls_estimation.py | 2 +- pandapower/test/estimation/test_pmu.py | 2 +- pandapower/test/estimation/test_recycle.py | 2 +- pandapower/test/estimation/test_wls_estimation.py | 2 +- pandapower/test/loadflow/result_test_network_generator.py | 2 +- pandapower/test/loadflow/test_PTDF_LODF.py | 2 +- pandapower/test/loadflow/test_results.py | 2 +- pandapower/test/loadflow/test_rundcpp.py | 2 +- pandapower/test/loadflow/test_runpp.py | 2 +- pandapower/test/loadflow/test_scenarios.py | 2 +- pandapower/test/networks/test_cigre_networks.py | 2 +- pandapower/test/networks/test_create_example.py | 2 +- pandapower/test/networks/test_dickert_lv_networks.py | 2 +- pandapower/test/networks/test_kerber_extreme_networks.py | 2 +- pandapower/test/networks/test_kerber_networks.py | 2 +- pandapower/test/networks/test_mv_oberrhein.py | 2 +- pandapower/test/networks/test_power_system_test_cases.py | 2 +- .../test/networks/test_simple_pandapower_test_networks.py | 2 +- .../test/networks/test_synthetic_voltage_control_lv_networks.py | 2 +- pandapower/test/opf/test_basic.py | 2 +- pandapower/test/opf/test_costs_mixed.py | 2 +- pandapower/test/opf/test_costs_pol.py | 2 +- pandapower/test/opf/test_costs_pwl.py | 2 +- pandapower/test/opf/test_curtailment.py | 2 +- pandapower/test/opf/test_dcline.py | 2 +- pandapower/test/opf/test_oberrhein.py | 2 +- pandapower/test/opf/test_opf_cigre.py | 2 +- pandapower/test/opf/test_powermodels.py | 2 +- pandapower/test/plotting/test_create_colormaps.py | 2 +- pandapower/test/plotting/test_to_html.py | 2 +- pandapower/test/run_tests.py | 2 +- pandapower/test/shortcircuit/test_1ph.py | 2 +- pandapower/test/shortcircuit/test_gen.py | 2 +- pandapower/test/shortcircuit/test_impedance.py | 2 +- pandapower/test/shortcircuit/test_meshing_detection.py | 2 +- pandapower/test/shortcircuit/test_min_branch_results.py | 2 +- pandapower/test/shortcircuit/test_motor.py | 2 +- pandapower/test/shortcircuit/test_ring.py | 2 +- pandapower/test/shortcircuit/test_sc_single_bus.py | 2 +- pandapower/test/shortcircuit/test_sgen.py | 2 +- pandapower/test/shortcircuit/test_trafo3w.py | 2 +- pandapower/test/shortcircuit/test_transformer.py | 2 +- pandapower/test/timeseries/test_data_source.py | 2 +- pandapower/test/timeseries/test_output_writer.py | 2 +- pandapower/test/timeseries/test_timeseries.py | 2 +- pandapower/test/toolbox.py | 2 +- pandapower/test/topology/test_create_graph.py | 2 +- pandapower/test/topology/test_graph_searches.py | 2 +- pandapower/timeseries/data_source.py | 2 +- pandapower/timeseries/data_sources/frame_data.py | 2 +- pandapower/timeseries/output_writer.py | 2 +- pandapower/timeseries/run_time_series.py | 2 +- pandapower/toolbox.py | 2 +- pandapower/topology/create_graph.py | 2 +- pandapower/topology/graph_searches.py | 2 +- setup.py | 2 +- 167 files changed, 167 insertions(+), 167 deletions(-) diff --git a/doc/about/license.rst b/doc/about/license.rst index 1fd080119..0bfe1b39d 100644 --- a/doc/about/license.rst +++ b/doc/about/license.rst @@ -10,7 +10,7 @@ License pandapower is published under the following 3-clause BSD license: :: - Copyright (c) 2018 by University of Kassel and Fraunhofer Institute for Fraunhofer Institute for + Copyright (c) 2020 by University of Kassel and Fraunhofer Institute for Fraunhofer Institute for Energy Economics and Energy System Technology (IEE) Kassel and individual contributors (see AUTHORS file for details). All rights reserved. diff --git a/doc/conf.py b/doc/conf.py index 32f9fa53b..8025b93a7 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -47,7 +47,7 @@ # General information about the project. project = u'pandapower' -copyright = u'2016-2019 by Fraunhofer IEE and University of Kassel' +copyright = u'2016-2020 by Fraunhofer IEE and University of Kassel' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/pandapower/auxiliary.py b/pandapower/auxiliary.py index 77b96dbfe..34025c365 100644 --- a/pandapower/auxiliary.py +++ b/pandapower/auxiliary.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/build_branch.py b/pandapower/build_branch.py index a1bb0202c..f7ad2f3ea 100644 --- a/pandapower/build_branch.py +++ b/pandapower/build_branch.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. # Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. diff --git a/pandapower/build_bus.py b/pandapower/build_bus.py index 5490e4481..5c4a624e5 100644 --- a/pandapower/build_bus.py +++ b/pandapower/build_bus.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/build_gen.py b/pandapower/build_gen.py index 00d46ac4e..13aa5b43d 100644 --- a/pandapower/build_gen.py +++ b/pandapower/build_gen.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/control/basic_controller.py b/pandapower/control/basic_controller.py index 4307cfb38..28aef8fd2 100644 --- a/pandapower/control/basic_controller.py +++ b/pandapower/control/basic_controller.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import copy from pandapower.auxiliary import get_free_id, _preserve_dtypes diff --git a/pandapower/control/controller/const_control.py b/pandapower/control/controller/const_control.py index bb1676dd3..e74e0fd82 100644 --- a/pandapower/control/controller/const_control.py +++ b/pandapower/control/controller/const_control.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import numpy as np diff --git a/pandapower/control/run_control.py b/pandapower/control/run_control.py index 29a237cc1..68d358b4f 100644 --- a/pandapower/control/run_control.py +++ b/pandapower/control/run_control.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import pandapower as pp diff --git a/pandapower/control/util/auxiliary.py b/pandapower/control/util/auxiliary.py index dab9ef06c..ca935b98e 100644 --- a/pandapower/control/util/auxiliary.py +++ b/pandapower/control/util/auxiliary.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import csv import random diff --git a/pandapower/control/util/diagnostic.py b/pandapower/control/util/diagnostic.py index 5942f14ed..92a9492ac 100644 --- a/pandapower/control/util/diagnostic.py +++ b/pandapower/control/util/diagnostic.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. from copy import deepcopy diff --git a/pandapower/convert_format.py b/pandapower/convert_format.py index 2b40b37fa..b682221b7 100644 --- a/pandapower/convert_format.py +++ b/pandapower/convert_format.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import pandas as pd diff --git a/pandapower/converter/matpower/from_mpc.py b/pandapower/converter/matpower/from_mpc.py index 8b169caf2..30ae5ba97 100644 --- a/pandapower/converter/matpower/from_mpc.py +++ b/pandapower/converter/matpower/from_mpc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/converter/matpower/to_mpc.py b/pandapower/converter/matpower/to_mpc.py index 8afac2ee9..7b010d926 100644 --- a/pandapower/converter/matpower/to_mpc.py +++ b/pandapower/converter/matpower/to_mpc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/converter/pypower/from_ppc.py b/pandapower/converter/pypower/from_ppc.py index 236fcfbb4..47effe025 100644 --- a/pandapower/converter/pypower/from_ppc.py +++ b/pandapower/converter/pypower/from_ppc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/converter/pypower/to_ppc.py b/pandapower/converter/pypower/to_ppc.py index 4bbf6cbb3..cc48c0c19 100644 --- a/pandapower/converter/pypower/to_ppc.py +++ b/pandapower/converter/pypower/to_ppc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/create.py b/pandapower/create.py index fd49bfe31..572146c6e 100644 --- a/pandapower/create.py +++ b/pandapower/create.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/diagnostic.py b/pandapower/diagnostic.py index 5abb5e765..9b11e3c4b 100644 --- a/pandapower/diagnostic.py +++ b/pandapower/diagnostic.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/diagnostic_reports.py b/pandapower/diagnostic_reports.py index 075372ac0..26c95a60d 100644 --- a/pandapower/diagnostic_reports.py +++ b/pandapower/diagnostic_reports.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/estimation/algorithm/base.py b/pandapower/estimation/algorithm/base.py index 6d5fb986d..30a9fe6aa 100644 --- a/pandapower/estimation/algorithm/base.py +++ b/pandapower/estimation/algorithm/base.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import numpy as np diff --git a/pandapower/estimation/algorithm/estimator.py b/pandapower/estimation/algorithm/estimator.py index eea221265..bd67220a0 100644 --- a/pandapower/estimation/algorithm/estimator.py +++ b/pandapower/estimation/algorithm/estimator.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import numpy as np diff --git a/pandapower/estimation/algorithm/lp.py b/pandapower/estimation/algorithm/lp.py index fcc3a02e2..163751011 100644 --- a/pandapower/estimation/algorithm/lp.py +++ b/pandapower/estimation/algorithm/lp.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import numpy as np diff --git a/pandapower/estimation/algorithm/matrix_base.py b/pandapower/estimation/algorithm/matrix_base.py index 4e1ccdc28..f101f226c 100644 --- a/pandapower/estimation/algorithm/matrix_base.py +++ b/pandapower/estimation/algorithm/matrix_base.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import warnings diff --git a/pandapower/estimation/idx_brch.py b/pandapower/estimation/idx_brch.py index 462f3d06b..f40a7bbd5 100644 --- a/pandapower/estimation/idx_brch.py +++ b/pandapower/estimation/idx_brch.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/estimation/idx_bus.py b/pandapower/estimation/idx_bus.py index 53150fe02..8417a29ee 100644 --- a/pandapower/estimation/idx_bus.py +++ b/pandapower/estimation/idx_bus.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/estimation/ppc_conversion.py b/pandapower/estimation/ppc_conversion.py index 6948a26a0..29e86e447 100644 --- a/pandapower/estimation/ppc_conversion.py +++ b/pandapower/estimation/ppc_conversion.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/estimation/results.py b/pandapower/estimation/results.py index 961be49b8..439dde289 100644 --- a/pandapower/estimation/results.py +++ b/pandapower/estimation/results.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import numpy as np diff --git a/pandapower/estimation/state_estimation.py b/pandapower/estimation/state_estimation.py index 1e4a82d49..1445b39a7 100644 --- a/pandapower/estimation/state_estimation.py +++ b/pandapower/estimation/state_estimation.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import numpy as np diff --git a/pandapower/estimation/util.py b/pandapower/estimation/util.py index 3e0435a4d..d7c48e789 100644 --- a/pandapower/estimation/util.py +++ b/pandapower/estimation/util.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import numpy as np diff --git a/pandapower/file_io.py b/pandapower/file_io.py index 059390ca0..325e16655 100644 --- a/pandapower/file_io.py +++ b/pandapower/file_io.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/io_utils.py b/pandapower/io_utils.py index 1fd17ec1b..850989367 100644 --- a/pandapower/io_utils.py +++ b/pandapower/io_utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/networks/cigre_networks.py b/pandapower/networks/cigre_networks.py index 86b73544f..1fa9b725d 100644 --- a/pandapower/networks/cigre_networks.py +++ b/pandapower/networks/cigre_networks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/networks/create_examples.py b/pandapower/networks/create_examples.py index fe730b27c..65334ce8d 100644 --- a/pandapower/networks/create_examples.py +++ b/pandapower/networks/create_examples.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/networks/kerber_extreme_networks.py b/pandapower/networks/kerber_extreme_networks.py index 295591c1d..60121ca79 100644 --- a/pandapower/networks/kerber_extreme_networks.py +++ b/pandapower/networks/kerber_extreme_networks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/networks/kerber_networks.py b/pandapower/networks/kerber_networks.py index cd16a514d..ba3edafdb 100644 --- a/pandapower/networks/kerber_networks.py +++ b/pandapower/networks/kerber_networks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/networks/mv_oberrhein.py b/pandapower/networks/mv_oberrhein.py index f44b99155..b8a572364 100644 --- a/pandapower/networks/mv_oberrhein.py +++ b/pandapower/networks/mv_oberrhein.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/networks/power_system_test_cases.py b/pandapower/networks/power_system_test_cases.py index a8c206b15..72f4e3434 100644 --- a/pandapower/networks/power_system_test_cases.py +++ b/pandapower/networks/power_system_test_cases.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/networks/simple_pandapower_test_networks.py b/pandapower/networks/simple_pandapower_test_networks.py index b8f2b0858..b0b133326 100644 --- a/pandapower/networks/simple_pandapower_test_networks.py +++ b/pandapower/networks/simple_pandapower_test_networks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/networks/synthetic_voltage_control_lv_networks.py b/pandapower/networks/synthetic_voltage_control_lv_networks.py index 23d19b0ff..8f418115c 100644 --- a/pandapower/networks/synthetic_voltage_control_lv_networks.py +++ b/pandapower/networks/synthetic_voltage_control_lv_networks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/opf/make_objective.py b/pandapower/opf/make_objective.py index 28e4e4be9..043a2e4bd 100644 --- a/pandapower/opf/make_objective.py +++ b/pandapower/opf/make_objective.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. from numpy import zeros, array diff --git a/pandapower/optimal_powerflow.py b/pandapower/optimal_powerflow.py index 8c9e6b30a..45c0c33fa 100644 --- a/pandapower/optimal_powerflow.py +++ b/pandapower/optimal_powerflow.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pd2ppc.py b/pandapower/pd2ppc.py index 150fcc1f7..2da0f081e 100644 --- a/pandapower/pd2ppc.py +++ b/pandapower/pd2ppc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pd2ppc_zero.py b/pandapower/pd2ppc_zero.py index af8081f06..882fd68f6 100644 --- a/pandapower/pd2ppc_zero.py +++ b/pandapower/pd2ppc_zero.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import math diff --git a/pandapower/pf/create_jacobian_numba.py b/pandapower/pf/create_jacobian_numba.py index d6a59e008..cd65c98e5 100644 --- a/pandapower/pf/create_jacobian_numba.py +++ b/pandapower/pf/create_jacobian_numba.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pf/dSbus_dV_numba.py b/pandapower/pf/dSbus_dV_numba.py index 6f58d4bb3..b6564f3f1 100644 --- a/pandapower/pf/dSbus_dV_numba.py +++ b/pandapower/pf/dSbus_dV_numba.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pf/makeYbus_numba.py b/pandapower/pf/makeYbus_numba.py index 56681044e..800c4e491 100644 --- a/pandapower/pf/makeYbus_numba.py +++ b/pandapower/pf/makeYbus_numba.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pf/pfsoln_numba.py b/pandapower/pf/pfsoln_numba.py index 73e17ba96..30aacc690 100644 --- a/pandapower/pf/pfsoln_numba.py +++ b/pandapower/pf/pfsoln_numba.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pf/ppci_variables.py b/pandapower/pf/ppci_variables.py index b36e9103c..d78f6f495 100644 --- a/pandapower/pf/ppci_variables.py +++ b/pandapower/pf/ppci_variables.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. from pandapower.pypower.idx_bus import VM, VA diff --git a/pandapower/pf/run_bfswpf.py b/pandapower/pf/run_bfswpf.py index 1eeafd831..5580ec0e2 100644 --- a/pandapower/pf/run_bfswpf.py +++ b/pandapower/pf/run_bfswpf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pf/run_dc_pf.py b/pandapower/pf/run_dc_pf.py index d08ad595d..3c22ac644 100644 --- a/pandapower/pf/run_dc_pf.py +++ b/pandapower/pf/run_dc_pf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pf/run_newton_raphson_pf.py b/pandapower/pf/run_newton_raphson_pf.py index df3b5c6e1..4388a0d63 100644 --- a/pandapower/pf/run_newton_raphson_pf.py +++ b/pandapower/pf/run_newton_raphson_pf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pf/runpf_pypower.py b/pandapower/pf/runpf_pypower.py index 2a07d1cf5..8c99fd961 100644 --- a/pandapower/pf/runpf_pypower.py +++ b/pandapower/pf/runpf_pypower.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/plotting/collections.py b/pandapower/plotting/collections.py index e7e6df159..0dbb73e87 100644 --- a/pandapower/plotting/collections.py +++ b/pandapower/plotting/collections.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import copy diff --git a/pandapower/plotting/colormaps.py b/pandapower/plotting/colormaps.py index 10d4952ea..d49ab7a0b 100644 --- a/pandapower/plotting/colormaps.py +++ b/pandapower/plotting/colormaps.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/plotting/generic_geodata.py b/pandapower/plotting/generic_geodata.py index 5450e97f0..d97f8135c 100644 --- a/pandapower/plotting/generic_geodata.py +++ b/pandapower/plotting/generic_geodata.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/plotting/plotly/get_colors.py b/pandapower/plotting/plotly/get_colors.py index cc6ce7d55..dcc99ab93 100644 --- a/pandapower/plotting/plotly/get_colors.py +++ b/pandapower/plotting/plotly/get_colors.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/plotting/plotly/mapbox_plot.py b/pandapower/plotting/plotly/mapbox_plot.py index e497bf0d1..8560d142e 100644 --- a/pandapower/plotting/plotly/mapbox_plot.py +++ b/pandapower/plotting/plotly/mapbox_plot.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/plotting/plotly/pf_res_plotly.py b/pandapower/plotting/plotly/pf_res_plotly.py index 8ee01dde3..003fd569b 100644 --- a/pandapower/plotting/plotly/pf_res_plotly.py +++ b/pandapower/plotting/plotly/pf_res_plotly.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/plotting/plotly/simple_plotly.py b/pandapower/plotting/plotly/simple_plotly.py index 6c69b2c94..afda0a552 100644 --- a/pandapower/plotting/plotly/simple_plotly.py +++ b/pandapower/plotting/plotly/simple_plotly.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/plotting/plotly/traces.py b/pandapower/plotting/plotly/traces.py index 5b40af9c8..551d0f3c6 100644 --- a/pandapower/plotting/plotly/traces.py +++ b/pandapower/plotting/plotly/traces.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/plotting/plotly/vlevel_plotly.py b/pandapower/plotting/plotly/vlevel_plotly.py index ab78a155d..8e565bf9c 100644 --- a/pandapower/plotting/plotly/vlevel_plotly.py +++ b/pandapower/plotting/plotly/vlevel_plotly.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/plotting/powerflow_results.py b/pandapower/plotting/powerflow_results.py index 114b1c898..1b7a784d6 100644 --- a/pandapower/plotting/powerflow_results.py +++ b/pandapower/plotting/powerflow_results.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/plotting/simple_plot.py b/pandapower/plotting/simple_plot.py index 806ecc6f0..9174dfaf5 100644 --- a/pandapower/plotting/simple_plot.py +++ b/pandapower/plotting/simple_plot.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/plotting/to_html.py b/pandapower/plotting/to_html.py index dd1c79bfb..05bc6f1b4 100644 --- a/pandapower/plotting/to_html.py +++ b/pandapower/plotting/to_html.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. # File created my Massimo Di Pierro diff --git a/pandapower/powerflow.py b/pandapower/powerflow.py index 9c9f91efe..50d7e0f15 100644 --- a/pandapower/powerflow.py +++ b/pandapower/powerflow.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. from numpy import nan_to_num diff --git a/pandapower/pypower/bustypes.py b/pandapower/pypower/bustypes.py index 228a32459..2da2a44ed 100644 --- a/pandapower/pypower/bustypes.py +++ b/pandapower/pypower/bustypes.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/dSbus_dV.py b/pandapower/pypower/dSbus_dV.py index a2f51e768..ed12654ee 100644 --- a/pandapower/pypower/dSbus_dV.py +++ b/pandapower/pypower/dSbus_dV.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/dcpf.py b/pandapower/pypower/dcpf.py index d8a22ac92..4984c37f1 100644 --- a/pandapower/pypower/dcpf.py +++ b/pandapower/pypower/dcpf.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/idx_brch.py b/pandapower/pypower/idx_brch.py index c6c47018c..865643a57 100644 --- a/pandapower/pypower/idx_brch.py +++ b/pandapower/pypower/idx_brch.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/idx_bus.py b/pandapower/pypower/idx_bus.py index 63c4ac3e2..367fa8962 100644 --- a/pandapower/pypower/idx_bus.py +++ b/pandapower/pypower/idx_bus.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/makeBdc.py b/pandapower/pypower/makeBdc.py index 3d391fa78..d48111685 100644 --- a/pandapower/pypower/makeBdc.py +++ b/pandapower/pypower/makeBdc.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/makeLODF.py b/pandapower/pypower/makeLODF.py index a24313110..20031aee0 100644 --- a/pandapower/pypower/makeLODF.py +++ b/pandapower/pypower/makeLODF.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. """Builds the line outage distribution factor matrix. diff --git a/pandapower/pypower/makePTDF.py b/pandapower/pypower/makePTDF.py index 6f87abf27..bc246e76c 100644 --- a/pandapower/pypower/makePTDF.py +++ b/pandapower/pypower/makePTDF.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. """Builds the DC PTDF matrix for a given choice of slack. diff --git a/pandapower/pypower/makeYbus.py b/pandapower/pypower/makeYbus.py index 48e5fee01..21048401b 100644 --- a/pandapower/pypower/makeYbus.py +++ b/pandapower/pypower/makeYbus.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/newtonpf.py b/pandapower/pypower/newtonpf.py index ace1c9cbd..e6ca709ed 100644 --- a/pandapower/pypower/newtonpf.py +++ b/pandapower/pypower/newtonpf.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/opf.py b/pandapower/pypower/opf.py index 4a1cc543e..a62399abd 100644 --- a/pandapower/pypower/opf.py +++ b/pandapower/pypower/opf.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. """Solves an optimal power flow. diff --git a/pandapower/pypower/opf_execute.py b/pandapower/pypower/opf_execute.py index c7852b645..eb1324700 100644 --- a/pandapower/pypower/opf_execute.py +++ b/pandapower/pypower/opf_execute.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/opf_hessfcn.py b/pandapower/pypower/opf_hessfcn.py index 0ebd4d25a..3421a8d02 100644 --- a/pandapower/pypower/opf_hessfcn.py +++ b/pandapower/pypower/opf_hessfcn.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/opf_setup.py b/pandapower/pypower/opf_setup.py index 3d9ba0614..d72b5f05c 100644 --- a/pandapower/pypower/opf_setup.py +++ b/pandapower/pypower/opf_setup.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/pfsoln.py b/pandapower/pypower/pfsoln.py index c56527ab1..03bdaa386 100644 --- a/pandapower/pypower/pfsoln.py +++ b/pandapower/pypower/pfsoln.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/pips.py b/pandapower/pypower/pips.py index 275a8755b..a9e6a1b7a 100644 --- a/pandapower/pypower/pips.py +++ b/pandapower/pypower/pips.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/pypower/pipsopf_solver.py b/pandapower/pypower/pipsopf_solver.py index 3d1fdc64d..d8bc9a872 100644 --- a/pandapower/pypower/pipsopf_solver.py +++ b/pandapower/pypower/pipsopf_solver.py @@ -4,7 +4,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/results.py b/pandapower/results.py index 0f6fba0e8..a8bfe701a 100644 --- a/pandapower/results.py +++ b/pandapower/results.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/results_branch.py b/pandapower/results_branch.py index 26f7f6dac..780e58f62 100644 --- a/pandapower/results_branch.py +++ b/pandapower/results_branch.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/results_bus.py b/pandapower/results_bus.py index 69c0f2f58..19645a6d7 100644 --- a/pandapower/results_bus.py +++ b/pandapower/results_bus.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/results_gen.py b/pandapower/results_gen.py index a5039c2af..34daa0913 100644 --- a/pandapower/results_gen.py +++ b/pandapower/results_gen.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/run.py b/pandapower/run.py index fb22e22ef..a57ecfbe2 100644 --- a/pandapower/run.py +++ b/pandapower/run.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/runpm.py b/pandapower/runpm.py index 114938e13..4b5488d5f 100644 --- a/pandapower/runpm.py +++ b/pandapower/runpm.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. # Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. diff --git a/pandapower/shortcircuit/calc_sc.py b/pandapower/shortcircuit/calc_sc.py index 330f14635..c464202f4 100644 --- a/pandapower/shortcircuit/calc_sc.py +++ b/pandapower/shortcircuit/calc_sc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/shortcircuit/currents.py b/pandapower/shortcircuit/currents.py index 01ec59c39..3dac48b42 100644 --- a/pandapower/shortcircuit/currents.py +++ b/pandapower/shortcircuit/currents.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/shortcircuit/idx_brch.py b/pandapower/shortcircuit/idx_brch.py index 63936dd23..fecfb9bed 100644 --- a/pandapower/shortcircuit/idx_brch.py +++ b/pandapower/shortcircuit/idx_brch.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/shortcircuit/idx_bus.py b/pandapower/shortcircuit/idx_bus.py index d73ce8cc1..8a2aaafe3 100644 --- a/pandapower/shortcircuit/idx_bus.py +++ b/pandapower/shortcircuit/idx_bus.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/shortcircuit/impedance.py b/pandapower/shortcircuit/impedance.py index 07730e1f8..2343b6964 100644 --- a/pandapower/shortcircuit/impedance.py +++ b/pandapower/shortcircuit/impedance.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/shortcircuit/kappa.py b/pandapower/shortcircuit/kappa.py index 4f092df0d..fec0e768e 100644 --- a/pandapower/shortcircuit/kappa.py +++ b/pandapower/shortcircuit/kappa.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/shortcircuit/results.py b/pandapower/shortcircuit/results.py index dca635400..7175d8135 100644 --- a/pandapower/shortcircuit/results.py +++ b/pandapower/shortcircuit/results.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/std_types.py b/pandapower/std_types.py index 8ede09b45..8db650510 100644 --- a/pandapower/std_types.py +++ b/pandapower/std_types.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/api/test_auxiliary.py b/pandapower/test/api/test_auxiliary.py index 92c5e6aaa..a23ef96d4 100644 --- a/pandapower/test/api/test_auxiliary.py +++ b/pandapower/test/api/test_auxiliary.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/api/test_convert_format.py b/pandapower/test/api/test_convert_format.py index 2b92d59ef..8525f26cd 100644 --- a/pandapower/test/api/test_convert_format.py +++ b/pandapower/test/api/test_convert_format.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/api/test_create.py b/pandapower/test/api/test_create.py index 856b76e0a..e9b3307cf 100644 --- a/pandapower/test/api/test_create.py +++ b/pandapower/test/api/test_create.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/api/test_diagnostic.py b/pandapower/test/api/test_diagnostic.py index 58a49b28f..7503ce80e 100644 --- a/pandapower/test/api/test_diagnostic.py +++ b/pandapower/test/api/test_diagnostic.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/api/test_file_io.py b/pandapower/test/api/test_file_io.py index 391b8508e..32f8d4f7e 100644 --- a/pandapower/test/api/test_file_io.py +++ b/pandapower/test/api/test_file_io.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/api/test_std_types.py b/pandapower/test/api/test_std_types.py index e0fa9c005..ee9ea9abb 100644 --- a/pandapower/test/api/test_std_types.py +++ b/pandapower/test/api/test_std_types.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/api/test_toolbox.py b/pandapower/test/api/test_toolbox.py index 79b37a92d..1ed76e15a 100644 --- a/pandapower/test/api/test_toolbox.py +++ b/pandapower/test/api/test_toolbox.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/conftest.py b/pandapower/test/conftest.py index c46ec2f1a..af3880745 100644 --- a/pandapower/test/conftest.py +++ b/pandapower/test/conftest.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/consistency_checks.py b/pandapower/test/consistency_checks.py index f10d4ae6c..325a5f320 100644 --- a/pandapower/test/consistency_checks.py +++ b/pandapower/test/consistency_checks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/control/test_continous_tap_control.py b/pandapower/test/control/test_continous_tap_control.py index 191c80aaf..aef32050c 100644 --- a/pandapower/test/control/test_continous_tap_control.py +++ b/pandapower/test/control/test_continous_tap_control.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import pandapower as pp diff --git a/pandapower/test/control/test_control.py b/pandapower/test/control/test_control.py index aa32e7fdf..2155d757a 100644 --- a/pandapower/test/control/test_control.py +++ b/pandapower/test/control/test_control.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import copy diff --git a/pandapower/test/control/test_discrete_tap_control.py b/pandapower/test/control/test_discrete_tap_control.py index 5da345121..042e56970 100644 --- a/pandapower/test/control/test_discrete_tap_control.py +++ b/pandapower/test/control/test_discrete_tap_control.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import pandapower as pp diff --git a/pandapower/test/converter/test_from_mpc.py b/pandapower/test/converter/test_from_mpc.py index c79ff3cba..4be561228 100644 --- a/pandapower/test/converter/test_from_mpc.py +++ b/pandapower/test/converter/test_from_mpc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/converter/test_from_ppc.py b/pandapower/test/converter/test_from_ppc.py index 16f979d3a..d3b483665 100644 --- a/pandapower/test/converter/test_from_ppc.py +++ b/pandapower/test/converter/test_from_ppc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/converter/test_to_ppc_and_mpc.py b/pandapower/test/converter/test_to_ppc_and_mpc.py index fcde3d058..6ce97d207 100644 --- a/pandapower/test/converter/test_to_ppc_and_mpc.py +++ b/pandapower/test/converter/test_to_ppc_and_mpc.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/estimation/test_irwls_estimation.py b/pandapower/test/estimation/test_irwls_estimation.py index cd19108f6..11a0e4773 100644 --- a/pandapower/test/estimation/test_irwls_estimation.py +++ b/pandapower/test/estimation/test_irwls_estimation.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/estimation/test_pmu.py b/pandapower/test/estimation/test_pmu.py index 6be379e9c..f1e2f74e0 100644 --- a/pandapower/test/estimation/test_pmu.py +++ b/pandapower/test/estimation/test_pmu.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/estimation/test_recycle.py b/pandapower/test/estimation/test_recycle.py index b21124ef7..b6245867d 100644 --- a/pandapower/test/estimation/test_recycle.py +++ b/pandapower/test/estimation/test_recycle.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/estimation/test_wls_estimation.py b/pandapower/test/estimation/test_wls_estimation.py index 5d5bc85f7..495f59e12 100644 --- a/pandapower/test/estimation/test_wls_estimation.py +++ b/pandapower/test/estimation/test_wls_estimation.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/loadflow/result_test_network_generator.py b/pandapower/test/loadflow/result_test_network_generator.py index 539f0aa29..55672c6dd 100644 --- a/pandapower/test/loadflow/result_test_network_generator.py +++ b/pandapower/test/loadflow/result_test_network_generator.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/loadflow/test_PTDF_LODF.py b/pandapower/test/loadflow/test_PTDF_LODF.py index c2c6d0646..63b206940 100644 --- a/pandapower/test/loadflow/test_PTDF_LODF.py +++ b/pandapower/test/loadflow/test_PTDF_LODF.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/loadflow/test_results.py b/pandapower/test/loadflow/test_results.py index e20b0263f..da52df6e6 100644 --- a/pandapower/test/loadflow/test_results.py +++ b/pandapower/test/loadflow/test_results.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/loadflow/test_rundcpp.py b/pandapower/test/loadflow/test_rundcpp.py index 7063f33c4..9625993b8 100644 --- a/pandapower/test/loadflow/test_rundcpp.py +++ b/pandapower/test/loadflow/test_rundcpp.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/loadflow/test_runpp.py b/pandapower/test/loadflow/test_runpp.py index 85cd182e3..9e9aed0c2 100644 --- a/pandapower/test/loadflow/test_runpp.py +++ b/pandapower/test/loadflow/test_runpp.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/loadflow/test_scenarios.py b/pandapower/test/loadflow/test_scenarios.py index 14e2a5db9..331b27f79 100644 --- a/pandapower/test/loadflow/test_scenarios.py +++ b/pandapower/test/loadflow/test_scenarios.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/networks/test_cigre_networks.py b/pandapower/test/networks/test_cigre_networks.py index 13446e21d..1b24d4dc4 100644 --- a/pandapower/test/networks/test_cigre_networks.py +++ b/pandapower/test/networks/test_cigre_networks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/networks/test_create_example.py b/pandapower/test/networks/test_create_example.py index c16eab8be..52224fc54 100644 --- a/pandapower/test/networks/test_create_example.py +++ b/pandapower/test/networks/test_create_example.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/networks/test_dickert_lv_networks.py b/pandapower/test/networks/test_dickert_lv_networks.py index 078ecce99..f5d2514c0 100644 --- a/pandapower/test/networks/test_dickert_lv_networks.py +++ b/pandapower/test/networks/test_dickert_lv_networks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/networks/test_kerber_extreme_networks.py b/pandapower/test/networks/test_kerber_extreme_networks.py index 2b52b7b46..d269c2902 100644 --- a/pandapower/test/networks/test_kerber_extreme_networks.py +++ b/pandapower/test/networks/test_kerber_extreme_networks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/networks/test_kerber_networks.py b/pandapower/test/networks/test_kerber_networks.py index 6012cc2f8..a1de07294 100644 --- a/pandapower/test/networks/test_kerber_networks.py +++ b/pandapower/test/networks/test_kerber_networks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/networks/test_mv_oberrhein.py b/pandapower/test/networks/test_mv_oberrhein.py index f00fc47fe..103ccfc00 100644 --- a/pandapower/test/networks/test_mv_oberrhein.py +++ b/pandapower/test/networks/test_mv_oberrhein.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/networks/test_power_system_test_cases.py b/pandapower/test/networks/test_power_system_test_cases.py index 9df10c67a..4bd01ff9f 100644 --- a/pandapower/test/networks/test_power_system_test_cases.py +++ b/pandapower/test/networks/test_power_system_test_cases.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/networks/test_simple_pandapower_test_networks.py b/pandapower/test/networks/test_simple_pandapower_test_networks.py index 486f36660..b0c3da463 100644 --- a/pandapower/test/networks/test_simple_pandapower_test_networks.py +++ b/pandapower/test/networks/test_simple_pandapower_test_networks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/networks/test_synthetic_voltage_control_lv_networks.py b/pandapower/test/networks/test_synthetic_voltage_control_lv_networks.py index 66ff19cac..0c3570c17 100644 --- a/pandapower/test/networks/test_synthetic_voltage_control_lv_networks.py +++ b/pandapower/test/networks/test_synthetic_voltage_control_lv_networks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/opf/test_basic.py b/pandapower/test/opf/test_basic.py index d36c8ff0c..4ab449bdd 100644 --- a/pandapower/test/opf/test_basic.py +++ b/pandapower/test/opf/test_basic.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/opf/test_costs_mixed.py b/pandapower/test/opf/test_costs_mixed.py index 3ce36c06d..a02ab1313 100644 --- a/pandapower/test/opf/test_costs_mixed.py +++ b/pandapower/test/opf/test_costs_mixed.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/opf/test_costs_pol.py b/pandapower/test/opf/test_costs_pol.py index bde1f58a8..1a7485376 100644 --- a/pandapower/test/opf/test_costs_pol.py +++ b/pandapower/test/opf/test_costs_pol.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/opf/test_costs_pwl.py b/pandapower/test/opf/test_costs_pwl.py index f092fb711..225d8d100 100644 --- a/pandapower/test/opf/test_costs_pwl.py +++ b/pandapower/test/opf/test_costs_pwl.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/opf/test_curtailment.py b/pandapower/test/opf/test_curtailment.py index 09e405126..f34dc5033 100644 --- a/pandapower/test/opf/test_curtailment.py +++ b/pandapower/test/opf/test_curtailment.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/opf/test_dcline.py b/pandapower/test/opf/test_dcline.py index 22fe58e62..f621fccf5 100644 --- a/pandapower/test/opf/test_dcline.py +++ b/pandapower/test/opf/test_dcline.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/opf/test_oberrhein.py b/pandapower/test/opf/test_oberrhein.py index 35b73672d..3f9d353a4 100644 --- a/pandapower/test/opf/test_oberrhein.py +++ b/pandapower/test/opf/test_oberrhein.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/opf/test_opf_cigre.py b/pandapower/test/opf/test_opf_cigre.py index 421ed5c21..48154b218 100644 --- a/pandapower/test/opf/test_opf_cigre.py +++ b/pandapower/test/opf/test_opf_cigre.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/opf/test_powermodels.py b/pandapower/test/opf/test_powermodels.py index 786869ed0..07481fa17 100644 --- a/pandapower/test/opf/test_powermodels.py +++ b/pandapower/test/opf/test_powermodels.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import copy diff --git a/pandapower/test/plotting/test_create_colormaps.py b/pandapower/test/plotting/test_create_colormaps.py index 1d1008d3f..eae9ab6b8 100644 --- a/pandapower/test/plotting/test_create_colormaps.py +++ b/pandapower/test/plotting/test_create_colormaps.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/plotting/test_to_html.py b/pandapower/test/plotting/test_to_html.py index 920de1640..eb05e0e07 100644 --- a/pandapower/test/plotting/test_to_html.py +++ b/pandapower/test/plotting/test_to_html.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/run_tests.py b/pandapower/test/run_tests.py index 08a006ad6..40bfd6afc 100644 --- a/pandapower/test/run_tests.py +++ b/pandapower/test/run_tests.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/shortcircuit/test_1ph.py b/pandapower/test/shortcircuit/test_1ph.py index 98ffc589a..ba4dbff31 100644 --- a/pandapower/test/shortcircuit/test_1ph.py +++ b/pandapower/test/shortcircuit/test_1ph.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import pandapower as pp diff --git a/pandapower/test/shortcircuit/test_gen.py b/pandapower/test/shortcircuit/test_gen.py index e5d47f5e3..dafaa46a9 100644 --- a/pandapower/test/shortcircuit/test_gen.py +++ b/pandapower/test/shortcircuit/test_gen.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/shortcircuit/test_impedance.py b/pandapower/test/shortcircuit/test_impedance.py index 27f7ac633..448a739b0 100644 --- a/pandapower/test/shortcircuit/test_impedance.py +++ b/pandapower/test/shortcircuit/test_impedance.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/shortcircuit/test_meshing_detection.py b/pandapower/test/shortcircuit/test_meshing_detection.py index b02d5f4d8..43278d077 100644 --- a/pandapower/test/shortcircuit/test_meshing_detection.py +++ b/pandapower/test/shortcircuit/test_meshing_detection.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/shortcircuit/test_min_branch_results.py b/pandapower/test/shortcircuit/test_min_branch_results.py index f1f8688af..460d1383d 100644 --- a/pandapower/test/shortcircuit/test_min_branch_results.py +++ b/pandapower/test/shortcircuit/test_min_branch_results.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/shortcircuit/test_motor.py b/pandapower/test/shortcircuit/test_motor.py index 9876a3cec..b5ec190a5 100644 --- a/pandapower/test/shortcircuit/test_motor.py +++ b/pandapower/test/shortcircuit/test_motor.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/shortcircuit/test_ring.py b/pandapower/test/shortcircuit/test_ring.py index bf9cffd11..68473b4d1 100644 --- a/pandapower/test/shortcircuit/test_ring.py +++ b/pandapower/test/shortcircuit/test_ring.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/shortcircuit/test_sc_single_bus.py b/pandapower/test/shortcircuit/test_sc_single_bus.py index 69838c977..7a81aac8d 100644 --- a/pandapower/test/shortcircuit/test_sc_single_bus.py +++ b/pandapower/test/shortcircuit/test_sc_single_bus.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/shortcircuit/test_sgen.py b/pandapower/test/shortcircuit/test_sgen.py index 8f819e422..8a7ccbfd0 100644 --- a/pandapower/test/shortcircuit/test_sgen.py +++ b/pandapower/test/shortcircuit/test_sgen.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/shortcircuit/test_trafo3w.py b/pandapower/test/shortcircuit/test_trafo3w.py index 9ee29ecb7..43934a540 100644 --- a/pandapower/test/shortcircuit/test_trafo3w.py +++ b/pandapower/test/shortcircuit/test_trafo3w.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/shortcircuit/test_transformer.py b/pandapower/test/shortcircuit/test_transformer.py index 7856be4d0..e87c806de 100644 --- a/pandapower/test/shortcircuit/test_transformer.py +++ b/pandapower/test/shortcircuit/test_transformer.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/timeseries/test_data_source.py b/pandapower/test/timeseries/test_data_source.py index 357b0ba53..b97f1a3be 100644 --- a/pandapower/test/timeseries/test_data_source.py +++ b/pandapower/test/timeseries/test_data_source.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import os diff --git a/pandapower/test/timeseries/test_output_writer.py b/pandapower/test/timeseries/test_output_writer.py index adfc832f5..96ceef9e6 100644 --- a/pandapower/test/timeseries/test_output_writer.py +++ b/pandapower/test/timeseries/test_output_writer.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/timeseries/test_timeseries.py b/pandapower/test/timeseries/test_timeseries.py index 0989e2304..2ec2d50a4 100644 --- a/pandapower/test/timeseries/test_timeseries.py +++ b/pandapower/test/timeseries/test_timeseries.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import tempfile diff --git a/pandapower/test/toolbox.py b/pandapower/test/toolbox.py index 8c42432ef..52d8bb5a2 100644 --- a/pandapower/test/toolbox.py +++ b/pandapower/test/toolbox.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/test/topology/test_create_graph.py b/pandapower/test/topology/test_create_graph.py index 6addc8005..1d9bc21e3 100644 --- a/pandapower/test/topology/test_create_graph.py +++ b/pandapower/test/topology/test_create_graph.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. from itertools import combinations diff --git a/pandapower/test/topology/test_graph_searches.py b/pandapower/test/topology/test_graph_searches.py index 7865db6c8..e9b86e02c 100644 --- a/pandapower/test/topology/test_graph_searches.py +++ b/pandapower/test/topology/test_graph_searches.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/pandapower/timeseries/data_source.py b/pandapower/timeseries/data_source.py index a67f1e33c..39f6c0d5c 100644 --- a/pandapower/timeseries/data_source.py +++ b/pandapower/timeseries/data_source.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. try: diff --git a/pandapower/timeseries/data_sources/frame_data.py b/pandapower/timeseries/data_sources/frame_data.py index cad741f0f..62e299e95 100644 --- a/pandapower/timeseries/data_sources/frame_data.py +++ b/pandapower/timeseries/data_sources/frame_data.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. from pandapower.timeseries.data_source import DataSource diff --git a/pandapower/timeseries/output_writer.py b/pandapower/timeseries/output_writer.py index 0a54dd16d..b2354a8b3 100644 --- a/pandapower/timeseries/output_writer.py +++ b/pandapower/timeseries/output_writer.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import copy import functools diff --git a/pandapower/timeseries/run_time_series.py b/pandapower/timeseries/run_time_series.py index d231ddb6c..29b5da850 100644 --- a/pandapower/timeseries/run_time_series.py +++ b/pandapower/timeseries/run_time_series.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import tempfile diff --git a/pandapower/toolbox.py b/pandapower/toolbox.py index 6b641971c..e14ef3c38 100644 --- a/pandapower/toolbox.py +++ b/pandapower/toolbox.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. import copy diff --git a/pandapower/topology/create_graph.py b/pandapower/topology/create_graph.py index 6fa9a4ae9..c8c1d06ef 100644 --- a/pandapower/topology/create_graph.py +++ b/pandapower/topology/create_graph.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. from itertools import combinations diff --git a/pandapower/topology/graph_searches.py b/pandapower/topology/graph_searches.py index c148bb0e6..5efcadc06 100644 --- a/pandapower/topology/graph_searches.py +++ b/pandapower/topology/graph_searches.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. diff --git a/setup.py b/setup.py index f6621319d..244ab8a3c 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2016-2019 by University of Kassel and Fraunhofer Institute for Energy Economics +# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics # and Energy System Technology (IEE), Kassel. All rights reserved. from setuptools import setup, find_packages