def test_nogo_graph_tool(): net = pp.create_empty_network() add_test_line(net) mg = create_nxgraph(net, library="graph_tool") assert set(mg.nodes()) == set(net.bus.index) mg = create_nxgraph(net, nogobuses=[0], library="graph_tool") assert set(mg.nodes()) == set(net.bus.index) - {0}
def test_impedance(): net = pp.create_empty_network() add_test_impedance(net) impedance, oos_impedance = net.impedance.index f, t = net.impedance.from_bus.at[impedance], net.impedance.to_bus.at[ impedance] #check that oos impedances are neglected and switches are respected mg = create_nxgraph(net) assert set(mg.get_edge_data(f, t).keys()) == {("impedance", impedance)} assert set(mg.nodes()) == set(net.bus.index) #check not including impedances mg = create_nxgraph(net, include_impedances=False) assert mg.get_edge_data(f, t) is None assert set(mg.nodes()) == set(net.bus.index) #check edge attributes mg = create_nxgraph(net, calc_branch_impedances=True, branch_impedance_unit="pu") pp.runpp(net) par = mg.get_edge_data(f, t, key=("impedance", impedance)) assert np.isclose(par["weight"], 0) assert par["path"] == 1 f, t = net._pd2ppc_lookups["branch"]["impedance"] assert np.isclose(par["r_pu"], net._ppc["branch"][f, BR_R]) assert np.isclose(par["x_pu"], net._ppc["branch"][f, BR_X])
def test_trafo3w(): net = pp.create_empty_network() add_test_trafo3w(net) t1, t2 = net.trafo3w.index trafo3 = net.trafo3w.iloc[t1] hv, mv, lv = trafo3.hv_bus, trafo3.mv_bus, trafo3.lv_bus mg = create_nxgraph(net) for f, t in combinations([hv, mv, lv], 2): assert set(mg.get_edge_data(f, t).keys()) == {("trafo3w", t1)} assert set(mg.nodes()) == set(net.bus.index) net.trafo3w.in_service.at[t2] = True mg = create_nxgraph(net) for f, t in combinations([hv, mv, lv], 2): assert set(mg.get_edge_data(f, t).keys()) == {("trafo3w", t1), ("trafo3w", t2)} assert set(mg.nodes()) == set(net.bus.index) for sb in [hv, mv, lv]: sw = pp.create_switch(net, bus=sb, element=t1, et="t3", closed=False) mg = create_nxgraph(net) for f, t in combinations([hv, mv, lv], 2): if sb == f or t == sb: assert set(mg.get_edge_data(f, t).keys()) == {("trafo3w", t2)} else: assert set(mg.get_edge_data(f, t).keys()) == {("trafo3w", t1), ("trafo3w", t2)} assert set(mg.nodes()) == set(net.bus.index) net.switch.closed.at[sw] = True
def test_nogo(): net = pp.create_empty_network() add_test_line(net) mg = create_nxgraph(net) assert set(mg.nodes()) == set(net.bus.index) mg = create_nxgraph(net, nogobuses=[0]) assert set(mg.nodes()) == set(net.bus.index) - {0}
def test_bus_bus_switches(): net = pp.create_empty_network() add_test_bus_bus_switch(net) s = net.switch.index[0] f, t = net.switch.bus.iloc[0], net.switch.element.iloc[0] net.switch.closed.at[s] = True mg = create_nxgraph(net) assert set(mg.get_edge_data(f, t)) == {("switch", s)} assert set(mg.nodes()) == set(net.bus.index) net.switch.closed.at[s] = False mg = create_nxgraph(net) assert mg.get_edge_data(f, t) is None assert set(mg.nodes()) == set(net.bus.index) mg = create_nxgraph(net, respect_switches=False) assert set(mg.get_edge_data(f, t)) == {("switch", s)} assert set(mg.nodes()) == set(net.bus.index) mg = create_nxgraph(net, respect_switches=False, calc_branch_impedances=True) #TODO check R/X/Z values par = mg.get_edge_data(f, t, key=("switch", s)) assert np.isclose(par["r_ohm"], 0) assert np.isclose(par["z_ohm"], 0) assert np.isclose(par["weight"], 0) assert par["path"] == 1
def test_trafo(): net = pp.create_empty_network() add_test_trafo(net) for library in libraries: trafo, open_loop_trafo, oos_trafo = net.trafo.index f, t = net.trafo.hv_bus.at[trafo], net.trafo.lv_bus.at[trafo] # check that oos trafos are neglected and switches are respected mg = create_nxgraph(net, library=library) assert set(mg.get_edge_data(f, t).keys()) == {("trafo", trafo)} assert set(mg.nodes()) == set(net.bus.index) # check respect_switches mg = create_nxgraph(net, respect_switches=False, library=library) assert set(mg.get_edge_data(f, t).keys()) == {("trafo", trafo), ("trafo", open_loop_trafo)} assert set(mg.nodes()) == set(net.bus.index) # check not including trafos mg = create_nxgraph(net, include_trafos=False, library=library) assert mg.get_edge_data(f, t) is None assert set(mg.nodes()) == set(net.bus.index) # check edge attributes net.trafo.vn_hv_kv = 20 net.trafo.vn_lv_kv = 0.4 net.trafo.pfe_kw = 0 net.trafo.i0_percent = 0 mg = create_nxgraph(net, calc_branch_impedances=True, library=library) trafo_tab = net.trafo.loc[trafo] par = mg.get_edge_data(f, t, key=("trafo", trafo)) base_Z = (trafo_tab.sn_mva) / (trafo_tab.vn_hv_kv**2) r = (trafo_tab.vkr_percent / 100) / base_Z / trafo_tab.parallel z = (trafo_tab.vk_percent / 100) / base_Z / trafo_tab.parallel assert np.isclose(par["r_ohm"], r) assert np.isclose(par["z_ohm"], z) assert par["weight"] == 0 assert par["path"] == 1 mg = create_nxgraph(net, calc_branch_impedances=True, branch_impedance_unit="pu", library=library) par = mg.get_edge_data(f, t, key=("trafo", trafo)) pp.runpp(net) f, t = net._pd2ppc_lookups["branch"]["trafo"] assert np.isclose(par["r_pu"], net._ppc["branch"][f, BR_R]) assert np.isclose(par["x_pu"], net._ppc["branch"][f, BR_X])
def test_trafo3w_impedances(network_with_trafo3ws): net, t3, hv, mv, lv = network_with_trafo3ws net.trafo3w.vn_hv_kv = 20 net.trafo3w.vn_mv_kv = 0.6 net.trafo3w.vn_lv_kv = 0.4 t3 = net.trafo3w.index[0] mg = create_nxgraph(net, calc_branch_impedances=True) trafo3 = net.trafo3w.loc[t3] hv, mv, lv = trafo3.hv_bus, trafo3.mv_bus, trafo3.lv_bus base_Z_hv = min(trafo3.sn_hv_mva, trafo3.sn_mv_mva) / (trafo3.vn_hv_kv**2) base_Z_mv = min(trafo3.sn_mv_mva, trafo3.sn_lv_mva) / (trafo3.vn_hv_kv**2) base_Z_lv = min(trafo3.sn_hv_mva, trafo3.sn_lv_mva) / (trafo3.vn_hv_kv**2) par = mg.get_edge_data(hv, mv, key=("trafo3w", t3)) assert np.isclose(par["r_ohm"], (trafo3.vkr_hv_percent / 100) / base_Z_hv) assert np.isclose(par["z_ohm"], (trafo3.vk_hv_percent / 100) / base_Z_hv) assert par["weight"] == 0 assert par["path"] == 1 par = mg.get_edge_data(mv, lv, key=("trafo3w", t3)) assert np.isclose(par["r_ohm"], (trafo3.vkr_mv_percent / 100) / base_Z_mv) assert np.isclose(par["z_ohm"], (trafo3.vk_mv_percent / 100) / base_Z_mv) assert par["weight"] == 0 assert par["path"] == 1 par = mg.get_edge_data(hv, lv, key=("trafo3w", t3)) assert np.isclose(par["r_ohm"], (trafo3.vkr_lv_percent / 100) / base_Z_lv) assert np.isclose(par["z_ohm"], (trafo3.vk_lv_percent / 100) / base_Z_lv) assert par["weight"] == 0 assert par["path"] == 1
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 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. INPUT: **net** - pandapower network OPTIONAL: **mg** - Existing networkx multigraph, if available. Convenience to save computation time. **library** - "igraph" to use igraph package or "networkx" to use networkx package OUTPUT: **net** - pandapower network with added geo coordinates for the buses EXAMPLE: net = create_generic_coordinates(net) """ if "bus_geodata" in net and net.bus_geodata.shape[0]: print("Please delete all geodata. This function cannot be used with pre-existing geodata.") return if not "bus_geodata" in net: net.bus_geodata = pd.DataFrame(columns=["x", "y"]) gnet = copy.deepcopy(net) 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_switches) 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)) elif library == "networkx": if mg is None: nxg = top.create_nxgraph(gnet, respect_switches) else: nxg = copy.deepcopy(mg) # workaround for bug in agraph for u, v in nxg.edges_iter(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'] 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[0] net.bus_geodata.y = coords[1] net.bus_geodata.index = net.bus.index return net
def create_generic_coordinates(net, mg=None, library="igraph", respect_switches=False, geodata_table="bus_geodata", buses=None, overwrite=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 :param geodata_table: table to write the generic geodatas to :type geodata_table: str :param buses: buses for which generic geodata are created, all buses will be used by default :type buses: list :param overwrite: overwrite existing geodata :type overwrite: bool :return: net - pandapower network with added geo coordinates for the buses :Example: net = create_generic_coordinates(net) """ _prepare_geodata_table(net, geodata_table, overwrite) if library == "igraph": if not IGRAPH_INSTALLED: raise UserWarning( "The library igraph is selected for plotting, but not installed " "correctly. Please install python-igraph with " "`pip install python-igraph` or " "`conda install python-igraph` " "or from https://www.lfd.uci.edu/~gohlke/pythonlibs") graph, meshed, roots = build_igraph_from_pp(net, respect_switches, buses=buses) coords = coords_from_igraph(graph, roots, meshed) elif library == "networkx": if mg is None: nxg = top.create_nxgraph(net, respect_switches=respect_switches, include_out_of_service=True) else: nxg = copy.deepcopy(mg) coords = coords_from_nxgraph(nxg) else: raise ValueError("Unknown library %s - chose 'igraph' or 'networkx'" % library) if len(coords): net[geodata_table].x = coords[1] net[geodata_table].y = coords[0] net[geodata_table].index = net.bus.index if buses is None else buses return net
def test_line(): net = pp.create_empty_network() add_test_line(net) line, open_loop_line, oos_line = net.line.index f, t = net.line.from_bus.at[line], net.line.to_bus.at[line] # check that oos lines are neglected and switches are respected for library in libraries: mg = create_nxgraph(net, library=library) assert set(mg.get_edge_data(f, t).keys()) == {("line", line)} assert set(mg.nodes()) == set(net.bus.index) # check respect_switches mg = create_nxgraph(net, respect_switches=False, library=library) assert set(mg.get_edge_data(f, t).keys()) == {("line", line), ("line", open_loop_line)} assert set(mg.nodes()) == set(net.bus.index) # check not including lines mg = create_nxgraph(net, include_lines=False, library=library) assert mg.get_edge_data(f, t) is None assert set(mg.nodes()) == set(net.bus.index) # check edge attributes mg = create_nxgraph(net, calc_branch_impedances=True, library=library) line_tab = net.line.loc[line] par = mg.get_edge_data(f, t, key=("line", line)) r = line_tab.length_km / line_tab.parallel * line_tab.r_ohm_per_km x = line_tab.length_km / line_tab.parallel * line_tab.x_ohm_per_km z = np.sqrt(r**2 + x**2) assert np.isclose(par["r_ohm"], r) assert np.isclose(par["x_ohm"], x) assert np.isclose(par["z_ohm"], z) assert np.isclose(par["weight"], line_tab.length_km) assert par["path"] == 1 mg = create_nxgraph(net, calc_branch_impedances=True, branch_impedance_unit="pu", library=library) line_tab = net.line.loc[line] par = mg.get_edge_data(f, t, key=("line", line)) pp.runpp(net) f, t = net._pd2ppc_lookups["branch"]["line"] assert np.isclose(par["r_pu"], net._ppc["branch"][f, BR_R]) assert np.isclose(par["x_pu"], net._ppc["branch"][f, BR_X])
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 test_elements_on_path(): net = nw.example_simple() for multi in [True, False]: mg = top.create_nxgraph(net, multi=multi) path = nx.shortest_path(mg, 0, 6) assert top.elements_on_path(mg, path, "line") == [0, 3] assert top.lines_on_path(mg, path) == [0, 3] assert top.elements_on_path(mg, path, "trafo") == [0] assert top.elements_on_path(mg, path, "trafo3w") == [] assert top.elements_on_path(mg, path, "switch") == [0, 1]
def detect_power_station_unit(net, mode="auto", max_gen_voltage_kv=80, max_distance_km=0.01): """ Identifies the power station units configuration (gen and trafo) according to IEC 60909. Updates the power_station_trafo in the gen table INPUT: **net** - panpdapower net **mode** (str, ("auto", trafo"")) **max_gen_voltage_level** (float) **max_distance_km** (float) """ logger.info("This function will overwrites the value 'power_station_trafo' in gen table") net.gen["power_station_trafo"] = np.nan required_gen = net.gen.loc[net.bus.loc[net.gen.bus.values, "vn_kv"].values < max_gen_voltage_kv,:] required_gen_bus = required_gen.loc[:, "bus"].values if mode.lower() == "auto": required_trafo = net.trafo.loc[net.bus.loc[net.trafo.lv_bus.values, "vn_kv"].values < max_gen_voltage_kv, :] elif mode.lower() == "trafo": if "power_station_unit" in net.trafo.columns: required_trafo = net.trafo.loc[net.trafo.power_station_unit, :] else: logger.warning("Using mode 'trafo' requires 'power_station_unit' defined for trafo! Using 'auto' mode instead!") required_trafo = net.trafo.loc[net.bus.loc[net.trafo.lv_bus.values, "vn_kv"].values < max_gen_voltage_kv, :] else: raise UserWarning(f"Unsupported modes: {mode}") trafo_lv_bus = net.trafo.loc[required_trafo.index, "lv_bus"].values trafo_hv_bus = net.trafo.loc[required_trafo.index, "hv_bus"].values g = create_nxgraph(net, respect_switches=True, nogobuses=None, notravbuses=trafo_hv_bus) for t_ix in required_trafo.index: t_lv_bus = required_trafo.at[t_ix, "lv_bus"] bus_dist = pd.Series(nx.single_source_dijkstra_path_length(g, t_lv_bus, weight='weight')) connected_bus_at_lv_side = bus_dist[bus_dist < max_distance_km].index.values gen_bus_at_lv_side = np.intersect1d(connected_bus_at_lv_side, required_gen_bus) if len(gen_bus_at_lv_side) == 1: # Check parallel trafo if not len(np.intersect1d(connected_bus_at_lv_side, trafo_lv_bus)) == 1: raise UserWarning("Failure in power station units detection! Parallel trafos on generator detected!") if np.in1d(required_gen_bus, gen_bus_at_lv_side).sum() > 1: logger.info("More than 1 gen detected at the lv side of a power station trafo! Will not be considered as power station unit") continue net.gen.loc[np.in1d(net.gen.bus.values, gen_bus_at_lv_side), "power_station_trafo"] = t_ix
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 "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 "bus_geodata" not in net or net.bus_geodata is None: net.bus_geodata = pd.DataFrame(columns=["x", "y"]) gnet = copy.deepcopy(net) gnet.bus = gnet.bus[gnet.bus.in_service == True] if library == "igraph": if not IGRAPH_INSTALLED: raise UserWarning( "The library igraph is selected for plotting, but not installed " "correctly.") graph, meshed, roots = build_igraph_from_pp(gnet, respect_switches) coords = coords_from_igraph(graph, roots, meshed) elif library == "networkx": if mg is None: nxg = top.create_nxgraph(gnet, respect_switches=respect_switches) else: nxg = copy.deepcopy(mg) coords = coords_from_nxgraph(nxg) 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 return net
def test_json_different_nets(): net = networks.mv_oberrhein() net2 = networks.simple_four_bus_system() control.ContinuousTapControl(net, 114, 1.02) net.tuple = (1, "4") net.mg = topology.create_nxgraph(net) json_string = json.dumps([net, net2], cls=PPJSONEncoder) [net_out, net2_out] = json.loads(json_string, cls=PPJSONDecoder) assert_net_equal(net_out, net) assert_net_equal(net2_out, net2) pp.runpp(net_out, run_control=True) pp.runpp(net, run_control=True) assert_net_equal(net, net_out)
def test_elements_on_path(): net = nw.example_simple() for multi in [True, False]: mg = top.create_nxgraph(net, multi=multi) path = nx.shortest_path(mg, 0, 6) assert top.elements_on_path(mg, path, "line") == [0, 3] assert top.lines_on_path(mg, path) == [0, 3] assert top.elements_on_path(mg, path, "trafo") == [0] assert top.elements_on_path(mg, path, "trafo3w") == [] assert top.elements_on_path(mg, path, "switch") == [0, 1] with pytest.raises(ValueError) as exception_info: top.elements_on_path(mg, path, element="sgen") assert str(exception_info.value) == "Invalid element type sgen"
def test_graph_characteristics(feeder_network): # adapt network net = feeder_network bus0 = pp.create_bus(net, vn_kv=20.0) bus1 = pp.create_bus(net, vn_kv=20.0) bus2 = pp.create_bus(net, vn_kv=20.0) bus3 = pp.create_bus(net, vn_kv=20.0) bus4 = pp.create_bus(net, vn_kv=20.0) bus5 = pp.create_bus(net, vn_kv=20.0) bus6 = pp.create_bus(net, vn_kv=20.0) bus7 = pp.create_bus(net, vn_kv=20.0) bus8 = pp.create_bus(net, vn_kv=20.0) bus9 = pp.create_bus(net, vn_kv=20.0) new_connections = [(3, bus0), (bus0, bus1), (bus0, bus2), (1, bus3), (2, bus4), (bus3, bus4), (bus4, bus5), (bus4, bus6), (bus5, bus6), (2, bus7), (bus7, bus8), (bus8, bus9), (bus9, bus7)] for fb, tb in new_connections: pp.create_line(net, fb, tb, length_km=1.0, std_type="NA2XS2Y 1x185 RM/25 12/20 kV") # get characteristics mg = top.create_nxgraph(net, respect_switches=False) characteristics = [ "bridges", "articulation_points", "connected", "stub_buses", "required_bridges", "notn1_areas" ] char_dict = top.find_graph_characteristics(mg, net.ext_grid.bus, characteristics) bridges = char_dict["bridges"] articulation_points = char_dict["articulation_points"] connected = char_dict["connected"] stub_buses = char_dict["stub_buses"] required_bridges = char_dict["required_bridges"] notn1_areas = char_dict["notn1_areas"] assert bridges == {(3, 4), (4, 5), (4, 6), (2, 11)} assert articulation_points == {8, 3, 4, 2, 11} assert connected == {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} assert stub_buses == {4, 5, 6, 11, 12, 13} assert required_bridges == { 4: [(3, 4)], 5: [(3, 4), (4, 5)], 6: [(3, 4), (4, 6)], 11: [(2, 11)], 12: [(2, 11)], 13: [(2, 11)] } assert notn1_areas == {8: {9, 10}, 3: {4, 5, 6}, 2: {11, 12, 13}}
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()
def voltage_profile_to_bus_geodata(net, voltages=None): if voltages is None: if not net.converged: raise ValueError("no results in this pandapower network") voltages = net.res_bus.vm_pu mg = top.create_nxgraph(net, respect_switches=True) first_eg = net.ext_grid.bus.values[0] mg.add_edges_from([(first_eg, y, {"weight": 0}) for y in net.ext_grid.bus.values[1:]]) dist = pd.Series(nx.single_source_dijkstra_path_length(mg, first_eg)) bgd = pd.DataFrame({"x": dist.loc[net.bus.index.values].values, "y": voltages.loc[net.bus.index.values].values}, index=net.bus.index) return bgd
def estimate_voltage_vector(net): """ Function initializes the voltage vector of net with a rough estimation. All buses are set to the slack bus voltage. Transformer differences in magnitude and phase shifting are accounted for. :param net: pandapower network :return: pandas dataframe with estimated vm_pu and va_degree """ res_bus = DataFrame(index=net.bus.index, columns=["vm_pu", "va_degree"]) net_graph = create_nxgraph(net, include_trafos=False) for _, ext_grid in net.ext_grid.iterrows(): area = list(connected_component(net_graph, ext_grid.bus)) res_bus.vm_pu.loc[area] = ext_grid.vm_pu res_bus.va_degree.loc[area] = ext_grid.va_degree trafos = net.trafo[net.trafo.in_service == 1] trafo_index = trafos.index.tolist() while len(trafo_index): for tix in trafo_index: trafo = trafos.loc[tix] if notnull(res_bus.vm_pu.at[trafo.hv_bus]) and isnull( res_bus.vm_pu.at[trafo.lv_bus]): try: area = list(connected_component(net_graph, trafo.lv_bus)) shift = trafo.shift_degree if "shift_degree" in trafo else 0 ratio = (trafo.vn_hv_kv / trafo.vn_lv_kv) / ( net.bus.vn_kv.at[trafo.hv_bus] / net.bus.vn_kv.at[trafo.lv_bus]) res_bus.vm_pu.loc[area] = res_bus.vm_pu.at[ trafo.hv_bus] * ratio res_bus.va_degree.loc[area] = res_bus.va_degree.at[ trafo.hv_bus] - shift except KeyError: raise UserWarning( "An out-of-service bus is connected to an in-service " "transformer. Please set the transformer out of service or" "put the bus into service. Treat results with caution!" ) trafo_index.remove(tix) elif notnull(res_bus.vm_pu.at[trafo.hv_bus]): # parallel transformer, lv buses are already set from previous transformer trafo_index.remove(tix) if len(trafo_index) == len(trafos): # after the initial run we could not identify any areas correctly, it's probably a transmission grid # with slack on the LV bus and multiple transformers/gens. do flat init and return res_bus.vm_pu.loc[res_bus.vm_pu.isnull()] = 1. res_bus.va_degree.loc[res_bus.va_degree.isnull()] = 0. return res_bus return res_bus
def test_json_encoding_decoding(): net = nw.mv_oberrhein() net.tuple = (1, "4") net.mg = top.create_nxgraph(net) s = set(['1', 4]) t = tuple(['2', 3]) f = frozenset(['12', 3]) a = np.array([1., 2.]) d = {"a": net, "b": f} json_string = json.dumps([s, t, f, net, a, d], cls=PPJSONEncoder) s1, t1, f1, net1, a1, d1 = json.loads(json_string, cls=PPJSONDecoder) assert s == s1 assert t == t1 assert f == f1 assert net.tuple == net1.tuple assert np.allclose(a, a1) # TODO line_geodata isn't the same since tuples inside DataFrames are converted to lists (see test_json_tuple_in_dataframe) assert pp.nets_equal(net, net1, exclude_elms=["line_geodata"]) assert pp.nets_equal(d["a"], d1["a"], exclude_elms=["line_geodata"]) assert d["b"] == d1["b"] assert_graphs_equal(net.mg, net1.mg)
# 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()
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)
def test_branch_impedance_unit(): net = pp.create_empty_network() with pytest.raises(ValueError) as exception_info: mg = create_nxgraph(net, branch_impedance_unit="p.u.") assert str(exception_info.value ) == "branch impedance unit can be either 'ohm' or 'pu'"
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
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) 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
"y": voltages.loc[net.bus.index.values].values }, 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,
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)