def test_connected_components(feeder_network): net = feeder_network mg = top.create_nxgraph(net) cc = top.connected_components(mg) assert list(cc) == [{0, 1, 2, 3}] cc_notrav = top.connected_components(mg, notravbuses={0, 2}) assert list(cc_notrav) == [{0, 1, 2}, {0, 2, 3}]
def fuse_geodata(net): mg = top.create_nxgraph(net, include_lines=False, respect_switches=False) geocoords = set(net.bus_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
def plot_feeder(): net = nw.case118() fig, ax = plt.subplots(1, 1) mg = top.create_nxgraph(net, nogobuses=set(net.trafo.lv_bus.values)) colors = sns.color_palette() collections = list() sizes = pplt.get_collection_sizes(net) voltage_levels = net.bus.vn_kv.unique() voltage_level_colors = dict(zip(voltage_levels, colors)) legend_entries = dict() gens = set(net.gen.loc[:, "bus"].values) for area, color in zip(top.connected_components(mg), colors): vn_area = net.bus.loc[list(area)[0], "vn_kv"] color = voltage_level_colors[vn_area] legend_entries[vn_area] = color area_gens = gens - area other = area - gens collections.append( pplt.create_bus_collection(net, area_gens, color=color, size=sizes["bus"], zorder=11, patch_type="rect")) collections.append( pplt.create_bus_collection(net, other, color=color, size=sizes["bus"], zorder=11)) line_ind = net.line.loc[:, "from_bus"].isin( area) | net.line.loc[:, "to_bus"].isin(area) lines = net.line.loc[line_ind].index collections.append(pplt.create_line_collection(net, lines, color=color)) eg_vn = net.bus.at[net.ext_grid.bus.values[0], "vn_kv"] collections.append( pplt.create_ext_grid_collection(net, size=sizes["ext_grid"], color=voltage_level_colors[eg_vn])) collections.append( pplt.create_trafo_collection(net, size=sizes["trafo"], zorder=1)) pplt.draw_collections(collections, ax=ax) custom_legend(fig, entries=legend_entries) legend_entries = {"gen": "grey"} custom_legend(fig, entries=legend_entries, loc='center right', marker="s") print_info(net, fig) plt.show()
index=net.bus.index) return bgd if __name__ == "__main__": import pandapower as pp import pandapower.networks as nw import pandas as pd import networkx as nx import plotting net = nw.mv_oberrhein() pp.runpp(net) mg = top.create_nxgraph(net, respect_switches=True) feeders = list( top.connected_components(mg, notravbuses=set(net.trafo.lv_bus.values))) lines_with_open_switches = set( net.switch.query("not closed and et == 'l'").element.values) fig, axs = plt.subplots(2) for bgd, ax in zip( [net.bus_geodata, voltage_profile_to_bus_geodata(net)], axs): for color, f in zip(["C0", "C1", "C2", "C3"], feeders): l = set(net.line.index[net.line.from_bus.isin( f)]) - lines_with_open_switches c = plotting.create_line_collection(net, lines=l, use_bus_geodata=True, color=color, bus_geodata=bgd) ax.add_collection(c)
def disconnected_elements(net): """ Checks, if there are network sections without a connection to an ext_grid. Returns all network elements in these sections, that are in service. Elements belonging to the same disconnected networks section are grouped in lists (e.g. disconnected lines: [[1, 2, 3], [4, 5]] means, that lines 1, 2 and 3 are in one disconncted section but are connected to each other. The same stands for lines 4, 5.) INPUT: **net** (pandapowerNet) - pandapower network OUTPUT: **disc_elements** (dict) - list that contains all network elements, without a connection to an ext_grid. format: {'disconnected buses' : bus_indices, 'disconnected switches' : switch_indices, 'disconnected lines' : line_indices, 'disconnected trafos' : trafo_indices 'disconnected loads' : load_indices, 'disconnected gens' : gen_indices, 'disconnected sgens' : sgen_indices} """ import pandapower.topology as top mg = top.create_nxgraph(net) sections = top.connected_components(mg) disc_elements = [] for section in sections: section_dict = {} if not section & set(net.ext_grid.bus[net.ext_grid.in_service]).union( net.gen.bus[net.gen.slack & net.gen.in_service]) and any( net.bus.in_service.loc[section]): section_buses = list(net.bus[net.bus.index.isin(section) & (net.bus.in_service == True)].index) section_switches = list(net.switch[net.switch.bus.isin(section_buses)].index) section_lines = list(get_connected_elements(net, 'line', section_buses, respect_switches=True, respect_in_service=True)) section_trafos = list(get_connected_elements(net, 'trafo', section_buses, respect_switches=True, respect_in_service=True)) section_trafos3w = list(get_connected_elements(net, 'trafo3w', section_buses, respect_switches=True, respect_in_service=True)) section_gens = list(net.gen[net.gen.bus.isin(section) & (net.gen.in_service == True)].index) section_sgens = list(net.sgen[net.sgen.bus.isin(section) & (net.sgen.in_service == True)].index) section_loads = list(net.load[net.load.bus.isin(section) & (net.load.in_service == True)].index) if section_buses: section_dict['buses'] = section_buses if section_switches: section_dict['switches'] = section_switches if section_lines: section_dict['lines'] = section_lines if section_trafos: section_dict['trafos'] = section_trafos if section_trafos3w: section_dict['trafos3w'] = section_trafos3w if section_loads: section_dict['loads'] = section_loads if section_gens: section_dict['gens'] = section_gens if section_sgens: section_dict['sgens'] = section_sgens if any(section_dict.values()): disc_elements.append(section_dict) open_trafo_switches = net.switch[(net.switch.et == 't') & (net.switch.closed == 0)] isolated_trafos = set( (open_trafo_switches.groupby("element").count().query("bus > 1").index)) isolated_trafos_is = isolated_trafos.intersection((set(net.trafo[net.trafo.in_service == True] .index))) if isolated_trafos_is: disc_elements.append({'isolated_trafos': list(isolated_trafos_is)}) isolated_trafos3w = set( (open_trafo_switches.groupby("element").count().query("bus > 2").index)) isolated_trafos3w_is = isolated_trafos3w.intersection(( set(net.trafo[net.trafo.in_service == True].index))) if isolated_trafos3w_is: disc_elements.append({'isolated_trafos3w': list(isolated_trafos3w_is)}) if disc_elements: return disc_elements
def mv_oberrhein(scenario="load", cosphi_load=0.98, cosphi_pv=1.0, include_substations=False, separation_by_sub=False): """ Loads the Oberrhein network, a generic 20 kV network serviced by two 25 MVA HV/MV transformer stations. The network supplies 141 MV/LV substations and 6 MV loads through four MV feeders. The network layout is meshed, but the network is operated as a radial network with 6 open sectioning points. The network can be loaded with two different worst case scenarios for load and generation, which are defined by scaling factors for loads / generators as well as tap positions of the HV/MV transformers. These worst case scenarios are a good starting point for working with this network, but you are of course free to parametrize the network for your use case. The network also includes geographical information of lines and buses for plotting. OPTIONAL: **scenario** - (str, "load"): defines the scaling for load and generation - "load": high load scenario, load = 0.6 / sgen = 0, trafo taps [-2, -3] - "generation": high feed-in scenario: load = 0.1, generation = 0.8, trafo taps [0, 0] **cosphi_load** - (str, 0.98): cosine(phi) of the loads **cosphi_sgen** - (str, 1.0): cosine(phi) of the static generators **include_substations** - (bool, False): if True, the transformers of the MV/LV level are modelled, otherwise the loads representing the LV networks are connected directly to the MV node **separation_by_sub** - (bool, False): if True, the network gets separated into two sections, referring to both substations OUTPUT: **net** - pandapower network **net0, net1** - both sections of the pandapower network EXAMPLE: ``import pandapower.networks`` ``net = pandapower.networks.mv_oberrhein("generation")`` or with separation ``net0, net1 = pandapower.networks.mv_oberrhein(separation_by_sub=True)`` """ if include_substations: net = pp.from_json( os.path.join(pp_dir, "networks", "mv_oberrhein_substations.json")) else: net = pp.from_json( os.path.join(pp_dir, "networks", "mv_oberrhein.json")) net.load.q_mvar = np.tan(np.arccos(cosphi_load)) * net.load.p_mw net.sgen.q_mvar = np.tan(np.arccos(cosphi_pv)) * net.sgen.p_mw hv_trafos = net.trafo[net.trafo.sn_mva > 1].index if scenario == "load": net.load.scaling = 0.6 net.sgen.scaling = 0.0 net.trafo.tap_pos.loc[hv_trafos] = [-2, -3] elif scenario == "generation": net.load.scaling = 0.1 net.sgen.scaling = 0.8 net.trafo.tap_pos.loc[hv_trafos] = [0, 0] else: raise ValueError("Unknown scenario %s - chose 'load' or 'generation'" % scenario) if separation_by_sub == True: # creating multigraph mg = top.create_nxgraph(net) # clustering connected buses zones = [list(area) for area in top.connected_components(mg)] net1 = pp.select_subnet(net, buses=zones[0], include_switch_buses=False, include_results=True, keep_everything_else=True) net0 = pp.select_subnet(net, buses=zones[1], include_switch_buses=False, include_results=True, keep_everything_else=True) pp.runpp(net0) pp.runpp(net1) return net0, net1 pp.runpp(net) return net
def vlevel_plotly(net, respect_switches=True, use_line_geodata=None, colors_dict=None, on_map=False, projection=None, map_style='basic', figsize=1, aspectratio='auto', line_width=2, bus_size=10): """ Plots a pandapower network in plotly using lines/buses colors according to the voltage level they belong to. If no geodata is available, artificial geodata is generated. For advanced plotting see the tutorial INPUT: **net** - The pandapower format network. If none is provided, mv_oberrhein() will be plotted as an example OPTIONAL: **respect_switches** (bool, True) - Respect switches when artificial geodata is created **use_line_geodata** (bool, True) - defines if lines patches are based on net.line_geodata of the lines (True) or on net.bus_geodata of the connected buses (False) *colors_dict** (dict, None) - dictionary for customization of colors for each voltage level in the form: voltage_kv : color **on_map** (bool, False) - enables using mapbox plot in plotly If provided geodata are not real geo-coordinates in lon/lat form, on_map will be set to False. **projection** (String, None) - defines a projection from which network geo-data will be transformed to lat-long. For each projection a string can be found at http://spatialreference.org/ref/epsg/ **map_style** (str, 'basic') - enables using mapbox plot in plotly - 'streets' - 'bright' - 'light' - 'dark' - 'satellite' **figsize** (float, 1) - aspectratio is multiplied by it in order to get final image size **aspectratio** (tuple, 'auto') - when 'auto' it preserves original aspect ratio of the network geodata any custom aspectration can be given as a tuple, e.g. (1.2, 1) **line_width** (float, 1.0) - width of lines **bus_size** (float, 10.0) - size of buses to plot. """ version_check() # create geocoord if none are available if 'line_geodata' not in net: net.line_geodata = pd.DataFrame(columns=['coords']) if 'bus_geodata' not in net: net.bus_geodata = pd.DataFrame(columns=["x", "y"]) 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) if on_map: logger.warning("Map plots not available with artificial coordinates and will be disabled!") on_map = False # check if geodata are real geographycal lat/lon coordinates using geopy if on_map and projection is not None: geo_data_to_latlong(net, projection=projection) # if bus geodata is available, but no line geodata if use_line_geodata is None: use_line_geodata = False if len(net.line_geodata) == 0 else True elif use_line_geodata and len(net.line_geodata) == 0: logger.warning("No or insufficient line geodata available --> only bus geodata will be used.") use_line_geodata = False # getting connected componenets without consideration of trafos graph = create_nxgraph(net, include_trafos=False) vlev_buses = connected_components(graph) # getting unique sets of buses for each voltage level vlev_bus_dict = {} for vl_buses in vlev_buses: if net.bus.loc[vl_buses, 'vn_kv'].unique().shape[0] > 1: logger.warning('buses from the same voltage level does not have the same vn_kv !?') vn_kv = net.bus.loc[vl_buses, 'vn_kv'].unique()[0] if vlev_bus_dict.get(vn_kv): vlev_bus_dict[vn_kv].update(vl_buses) else: vlev_bus_dict[vn_kv] = vl_buses # create a default colormap for voltage levels nvlevs = len(vlev_bus_dict) colors = get_plotly_color_palette(nvlevs) colors_dict = dict(zip(vlev_bus_dict.keys(), colors)) # creating traces for buses and lines for each voltage level bus_traces = [] line_traces = [] for vn_kv, buses_vl in vlev_bus_dict.items(): vlev_color = colors_dict[vn_kv] bus_trace_vlev = create_bus_trace(net, buses=buses_vl, size=bus_size, legendgroup=str(vn_kv), color=vlev_color, trace_name='buses {0} kV'.format(vn_kv)) if bus_trace_vlev is not None: bus_traces += bus_trace_vlev vlev_lines = net.line[net.line.from_bus.isin(buses_vl) & net.line.to_bus.isin(buses_vl)].index.tolist() line_trace_vlev = create_line_trace(net, lines=vlev_lines, use_line_geodata=use_line_geodata, respect_switches=respect_switches, legendgroup=str(vn_kv), color=vlev_color, width=line_width, trace_name='lines {0} kV'.format(vn_kv)) if line_trace_vlev is not None: line_traces += line_trace_vlev trafo_traces = create_trafo_trace(net, color='gray', width=line_width * 2) draw_traces(line_traces + trafo_traces + bus_traces, showlegend=True, aspectratio=aspectratio, on_map=on_map, map_style=map_style, figsize=figsize)
draw_traces(line_traces + trafo_traces + bus_traces, showlegend=True, aspectratio=aspectratio, on_map=on_map, map_style=map_style, figsize=figsize) if __name__ == '__main__': from pandapower.plotting.plotly import simple_plotly from pandapower.networks import mv_oberrhein from pandapower import runpp net = mv_oberrhein() vlevel_plotly(net) runpp(net) line_width=2 bus_size=10 use_line_geodata = None graph = create_nxgraph(net, include_trafos=False) vlev_buses = connected_components(graph) respect_switches = True # getting unique sets of buses for each voltage level vlev_bus_dict = {} for vl_buses in vlev_buses: if net.bus.loc[vl_buses, 'vn_kv'].unique().shape[0] > 1: logger.warning('buses from the same voltage level does not have the same vn_kv !?') vn_kv = net.bus.loc[vl_buses, 'vn_kv'].unique()[0] if vlev_bus_dict.get(vn_kv): vlev_bus_dict[vn_kv].update(vl_buses) else: vlev_bus_dict[vn_kv] = vl_buses # create a default colormap for voltage levels nvlevs = len(vlev_bus_dict)
def vlevel_plotly(net, respect_switches=True, use_line_geodata=None, colors_dict=None, on_map=False, projection=None, map_style='basic', figsize=1, aspectratio='auto', line_width=2, bus_size=10, filename="temp-plot.html", auto_open=True): """ Plots a pandapower network in plotly using lines/buses colors according to the voltage level they belong to. If no geodata is available, artificial geodata is generated. For advanced plotting see the tutorial INPUT: **net** - The pandapower format network. If none is provided, mv_oberrhein() will be plotted as an example OPTIONAL: **respect_switches** (bool, True) - Respect switches when artificial geodata is created **use_line_geodata** (bool, True) - defines if lines patches are based on net.line_geodata of the lines (True) or on net.bus_geodata of the connected buses (False) *colors_dict** (dict, None) - dictionary for customization of colors for each voltage level in the form: voltage : color **on_map** (bool, False) - enables using mapbox plot in plotly If provided geodata are not real geo-coordinates in lon/lat form, on_map will be set to False. **projection** (String, None) - defines a projection from which network geo-data will be transformed to lat-long. For each projection a string can be found at http://spatialreference.org/ref/epsg/ **map_style** (str, 'basic') - enables using mapbox plot in plotly - 'streets' - 'bright' - 'light' - 'dark' - 'satellite' **figsize** (float, 1) - aspectratio is multiplied by it in order to get final image size **aspectratio** (tuple, 'auto') - when 'auto' it preserves original aspect ratio of the network geodata any custom aspectration can be given as a tuple, e.g. (1.2, 1) **line_width** (float, 1.0) - width of lines **bus_size** (float, 10.0) - size of buses to plot. **filename** (str, "temp-plot.html") - filename / path to plot to. Should end on `*.html` **auto_open** (bool, True) - automatically open plot in browser OUTPUT: **figure** (graph_objs._figure.Figure) figure object """ # getting connected componenets without consideration of trafos graph = create_nxgraph(net, include_trafos=False) vlev_buses = connected_components(graph) # getting unique sets of buses for each voltage level vlev_bus_dict = {} for vl_buses in vlev_buses: if net.bus.loc[vl_buses, 'vn_kv'].unique().shape[0] > 1: logger.warning('buses from the same voltage level does not have the same vn_kv !?') vn_kv = net.bus.loc[vl_buses, 'vn_kv'].unique()[0] if vlev_bus_dict.get(vn_kv): vlev_bus_dict[vn_kv].update(vl_buses) else: vlev_bus_dict[vn_kv] = vl_buses # create a default colormap for voltage levels nvlevs = len(vlev_bus_dict) colors = get_plotly_color_palette(nvlevs) colors_dict = colors_dict or dict(zip(vlev_bus_dict.keys(), colors)) bus_groups = [(buses, colors_dict[vlev], f"{vlev} kV") for vlev, buses in vlev_bus_dict.items()] return _draw_colored_bus_groups_plotly( net, bus_groups, respect_switches=respect_switches, use_line_geodata=use_line_geodata, on_map=on_map, projection=projection, map_style=map_style, figsize=figsize, aspectratio=aspectratio, line_width=line_width, bus_size=bus_size, filename=filename, auto_open=auto_open)
# Youtube Tutorial: https://www.youtube.com/watch?v=QYDp_-TX7C4 import pandapower as pp import pandapower.plotting as pplt import pandapower.topology as top import pandapower.networks as nw import matplotlib.pyplot as plt import seaborn as sns net = nw.mv_oberrhein() pplt.simple_plot(net) mg = top.create_nxgraph(net, nogobuses=set(net.trafo.lv_bus.values) | set(net.trafo.hv_bus.values)) colors = sns.color_palette() collections = list() sizes = pplt.get_collection_sizes(net) for area, color in zip(top.connected_components(mg), colors): collections.append(pplt.create_bus_collection(net, area, color=color, size=sizes["bus"])) line_ind = net.line.loc[:, "from_bus"].isin(area) | net.line.loc[:, "to_bus"].isin(area) lines = net.line.loc[line_ind].index collections.append(pplt.create_line_collection(net, lines, color=color)) collections.append(pplt.create_ext_grid_collection(net, size=sizes["ext_grid"])) pplt.draw_collections(collections) plt.show()