Example #1
0
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
Example #2
0
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
Example #6
0
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