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, **kwargs): """ Creates a matplotlib line collection of pandapower lines. Input: **net** (pandapowerNet) - The pandapower network OPTIONAL: **lines** (list, None) - The lines for which the collections are created. If None, all lines in the network are considered. **line_geodata** (DataFrame, None) - coordinates to use for plotting. If None, net["line_geodata"] is used **bus_geodata** (DataFrame, None) - coordinates to use for plotting If None, net["bus_geodata"] is used **use_bus_geodata** (bool, False) - Defines whether bus or line geodata are used. **infofunc** (function, None) - infofunction for the patch element **cmap** - colormap for the patch colors **norm** (matplotlib norm object, None) - matplotlib norm object **picker** (bool, False) - picker argument passed to the patch collection **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. **cbar_title** (str, "Bus Voltage [pu]") - colormap bar title in case of given cmap **clim** (tuple of floats, None) - setting the norm limits for image scaling **kwargs - key word arguments are passed to the patch function OUTPUT: **lc** - line collection """ 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"] if bus_geodata is None: bus_geodata = net["bus_geodata"] if len(lines) == 0: return None if use_bus_geodata: data = [ ([(bus_geodata.at[a, "x"], bus_geodata.at[a, "y"]), (bus_geodata.at[b, "x"], bus_geodata.at[b, "y"])], infofunc(line) if infofunc else []) for line, (a, b) in net.line.loc[lines, ["from_bus", "to_bus"]].iterrows() if a in bus_geodata.index.values and b in bus_geodata.index.values ] else: data = [(line_geodata.loc[line, "coords"], infofunc(line) if infofunc else []) for line in lines if line in line_geodata.index.values] if len(data) == 0: return None data, info = list(zip(*data)) # 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) if cmap: if z is None: z = net.res_line.loading_percent.loc[lines] lc.set_cmap(cmap) lc.set_norm(norm) if clim is not None: lc.set_clim(clim) lc.set_array(np.array(z)) lc.has_colormap = True lc.cbar_title = cbar_title lc.info = info return lc
def create_line_collection(net, lines=None, line_geodata=None, use_bus_geodata=False, infofunc=None, cmap=None, norm=None, picker=False, z=None, cbar_title="Line Loading [%]", clim=None, **kwargs): """ Creates a matplotlib line collection of pandapower lines. Input: **net** (pandapowerNet) - The pandapower network OPTIONAL: **lines** (list, None) - The lines for which the collections are created. If None, all lines in the network are considered. **line_geodata** (DataFrame, None) - coordinates to use for plotting If None, net["line_geodata"] is used **infofunc** (function, None) - infofunction for the patch element **kwargs - key word arguments are passed to the patch function """ 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"] if len(lines) == 0: return None if use_bus_geodata: data = [ ([(net.bus_geodata.at[a, "x"], net.bus_geodata.at[a, "y"]), (net.bus_geodata.at[b, "x"], net.bus_geodata.at[b, "y"])], infofunc(line) if infofunc else []) for line, (a, b) in net.line.loc[lines, ["from_bus", "to_bus"]].iterrows() if a in net.bus_geodata.index.values and b in net.bus_geodata.index.values ] else: data = [(line_geodata.loc[line, "coords"], infofunc(line) if infofunc else []) for line in lines if line in line_geodata.index.values] if len(data) == 0: return None data, info = list(zip(*data)) # 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) if cmap: if z is None: z = net.res_line.loading_percent.loc[lines] lc.set_cmap(cmap) lc.set_norm(norm) if clim is not None: lc.set_clim(clim) lc.set_array(np.array(z)) lc.has_colormap = True lc.cbar_title = cbar_title lc.info = info return lc
def create_sgen_collection(net, sgens=None, size=1., infofunc=None, orientation=np.pi, **kwargs): sgen_table = net.sgen if sgens is None else net.sgen.loc[sgens] """ Creates a matplotlib patch collection of pandapower sgen. Input: **net** (pandapowerNet) - The pandapower network OPTIONAL: **size** (float, 1) - patch size **infofunc** (function, None) - infofunction for the patch element **orientation** (float, np.pi) - orientation of load 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 **sgen2** - patch collection """ lines = [] polys = [] infos = [] off = 1.7 r_triangle = size * 0.4 ang = orientation if hasattr( orientation, '__iter__') else [orientation] * net.sgen.shape[0] color = kwargs.pop("color", "k") for i, sgen in sgen_table.iterrows(): bus_geo = net.bus_geodata[["x", "y"]].loc[sgen.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
def create_trafo3w_collection(net, trafo3ws=None, picker=False, infofunc=None, **kwargs): """ Creates a matplotlib line collection of pandapower transformers. Input: **net** (pandapowerNet) - The pandapower network OPTIONAL: **trafo3ws** (list, None) - The three winding transformers for which the collections are created. If None, all three winding transformers in the network are considered. **picker** (bool, False) - picker argument passed to the patch collection **infofunc** (function, None) - infofunction for the patch element **kwargs - key word arguments are passed to the patch function OUTPUT: **lc** - line collection **pc** - patch collection """ trafo3w_table = net.trafo3w if trafo3ws is None else net.trafo3w.loc[ trafo3ws] lines = [] circles = [] infos = [] color = kwargs.pop("color", "k") linewidth = kwargs.pop("linewidths", 2.) for i, trafo3w in trafo3w_table.iterrows(): # get bus geodata p1 = net.bus_geodata[["x", "y"]].loc[trafo3w.hv_bus].values p2 = net.bus_geodata[["x", "y"]].loc[trafo3w.mv_bus].values p3 = net.bus_geodata[["x", "y"]].loc[trafo3w.lv_bus].values if np.all(p1 == p2) and np.all(p1 == p3): continue p = np.array([p1, p2, p3]) # determine center of buses and minimum distance center-buses center = sum(p) / 3 d = np.linalg.norm(p - center, axis=1) r = d.min() / 3 # determine closest bus to center and vector from center to circle midpoint in closest # direction closest = d.argmin() to_closest = (p[closest] - center) / d[closest] * 2 * r / 3 # determine vectors from center to circle midpoint order = list(range(closest, 3)) + list(range(closest)) cm = np.empty((3, 2)) cm[order.pop(0)] = to_closest ang = 2 * np.pi / 3 # 120 degree cm[order.pop(0)] = _rotate_dim2(to_closest, ang) cm[order.pop(0)] = _rotate_dim2(to_closest, -ang) # determine midpoints of circles m = center + cm # determine endpoints of circles e = (center - p) * (1 - 5 * r / 3 / d).reshape(3, 1) + p # save circle and line collection data for i in range(3): circles.append(Circle(m[i], r, fc=(1, 0, 0, 0), ec=color)) lines.append([p[i], e[i]]) 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.info = infos pc = PatchCollection(circles, match_original=True, picker=picker, linewidth=linewidth, **kwargs) pc.info = infos return lc, pc
def create_trafo_collection(net, trafos=None, picker=False, size=None, infofunc=None, **kwargs): """ Creates a matplotlib line collection of pandapower transformers. Input: **net** (pandapowerNet) - The pandapower network OPTIONAL: **trafos** (list, None) - The transformers for which the collections are created. If None, all transformers in the network are considered. **picker** (bool, False) - picker argument passed to the patch collection **size** (int, None) - size of transformer symbol circles. Should be >0 and < 0.35*bus_distance **infofunc** (function, None) - infofunction for the patch element **kwargs - key word arguments are passed to the patch function OUTPUT: **lc** - line collection **pc** - patch collection """ trafo_table = net.trafo if trafos is None else net.trafo.loc[trafos] lines = [] circles = [] infos = [] color = kwargs.pop("color", "k") linewidths = kwargs.pop("linewidths", 2.) for i, trafo in trafo_table.iterrows(): p1 = net.bus_geodata[["x", "y"]].loc[trafo.hv_bus].values p2 = net.bus_geodata[["x", "y"]].loc[trafo.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 circles.append(Circle(circ1, size_this, fc=(1, 0, 0, 0), ec=color)) circles.append(Circle(circ2, size_this, fc=(1, 0, 0, 0), ec=color)) 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 return lc, pc
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, **kwargs): """ Creates a matplotlib line collection of pandapower lines. Input: **net** (pandapowerNet) - The pandapower network OPTIONAL: **lines** (list, None) - The lines for which the collections are created. If None, all lines in the network are considered. **line_geodata** (DataFrame, None) - coordinates to use for plotting. If None, net["line_geodata"] is used **bus_geodata** (DataFrame, None) - coordinates to use for plotting If None, net["bus_geodata"] is used **use_bus_geodata** (bool, False) - Defines whether bus or line geodata are used. **infofunc** (function, None) - infofunction for the patch element **cmap** - colormap for the patch colors **norm** (matplotlib norm object, None) - matplotlib norm object **picker** (bool, False) - picker argument passed to the patch 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. **cbar_title** (str, "Line Loading [%]") - colormap bar title in case of given cmap **clim** (tuple of floats, None) - setting the norm limits for image scaling **kwargs - key word arguments are passed to the patch function OUTPUT: **lc** - line collection """ 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"] 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) 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_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 len(data) == 0: return None data, info = list(zip(*data)) # 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 = True lc.cbar_title = cbar_title lc.info = info return lc