def load_topology(nuts_codes, config, voltages: List[float] = None, plot: bool = False): net = Network() net.import_from_csv_folder( f"{data_path}topologies/pypsa_entsoe_gridkit/generated/base_network/") if 1: import matplotlib.pyplot as plt net.plot(bus_sizes=0.001) plt.show() exit() # Remove all buses outside desired regions region_shapes_ds = get_shapes(nuts_codes, save=True)["geometry"] buses_in_nuts_regions = \ net.buses[['x', 'y']].apply(lambda p: any([shape.contains(Point(p)) for shape in region_shapes_ds]), axis=1) net.buses = net.buses[buses_in_nuts_regions] if 0: plt.figure() net.plot(bus_sizes=0.001) # Remove all buses which are not at the desired voltage buses_with_v_nom_to_keep_b = net.buses.v_nom.isnull() if voltages is not None: buses_with_v_nom_to_keep_b |= net.buses.v_nom.isin(voltages) logger.info( "Removing buses with voltages {}" "".format( pd.Index( net.buses.v_nom.unique()).dropna().difference(voltages))) net.buses = net.buses[buses_with_v_nom_to_keep_b] if 1: plt.figure() net.plot(bus_sizes=0.001) plt.show() # Remove dangling branches net.lines = remove_dangling_branches(net.lines, net.buses.index) net.links = remove_dangling_branches(net.links, net.buses.index) net.transformers = remove_dangling_branches(net.transformers, net.buses.index) # Set electrical parameters set_electrical_parameters_lines(net, config['lines'], net.buses.v_nom.dropna().unique().tolist()) set_electrical_parameters_links(net, config['links']) set_electrical_parameters_transformers(net, config['transformers']) # Allows to set under construction links and lines to 0 or remove them completely, # and remove some unconnected components that might appear as a result net = adjust_capacities_of_under_construction_branches( net, config['lines'], config['links']) # Remove unconnected components # TODO: allow to simplify the network (i.e. convert everything to 380) or not ? net = cluster_network(net, nuts_codes) if plot: from epippy.topologies.core.plot import plot_topology all_lines = pd.concat( (net.links[['bus0', 'bus1']], net.lines[['bus0', 'bus1']])) plot_topology(net.buses, all_lines) plt.show() return net
def cluster_network(net: pypsa.Network, nuts_codes: List[str]): # Get shapes of regions (onshore and offshore) shapes = get_shapes(nuts_codes, which='onshore') # --- Buses --- # # TODO: this is shit --> should return a list keeping the index of the original points def add_region(lon, lat): try: region_code = matched_locs[lon, lat] # Need the if because some points are exactly at the same position return region_code if (isinstance(region_code, str) or isinstance(region_code, float) or isinstance(region_code, int)) else region_code.iloc[0] except (AttributeError, KeyError): return None # plants_region_ds.loc[pp_df_in_country.index] = \ # pp_df_in_country[["lon", "lat"]].apply(lambda x: add_region(x[0], x[1]), axis=1) buses_positions = net.buses[['x', 'y']].apply(lambda p: (p.x, p.y), axis=1).tolist() matched_locs = match_points_to_regions(buses_positions, shapes["geometry"], keep_outside=False).dropna() old_to_new_buses_map = net.buses[["x", "y"]].apply(lambda p: add_region(p.x, p.y), axis=1).dropna() nuts_codes_with_buses = list(set(old_to_new_buses_map)) # Merge buses to centroid of countries (even offshore ones) buses_df = pd.DataFrame(columns=["bus_id", "x", "y"]) buses_df["bus_id"] = nuts_codes_with_buses buses_df = buses_df.set_index('bus_id') regions = shapes.loc[nuts_codes_with_buses, 'geometry'] buses_df['onshore_region'] = regions buses_df["x"] = regions.centroid.apply(lambda p: p.x) buses_df["y"] = regions.centroid.apply(lambda p: p.y) print(buses_df) def compute_distance(bus0, bus1): bus0_x, bus0_y = buses_df.loc[bus0, ['x', 'y']] bus1_x, bus1_y = buses_df.loc[bus1, ['x', 'y']] return geopy.distance.geodesic((bus0_y, bus0_x), (bus1_y, bus1_x)).km # --- Lines --- # # Remove lines associated to buses that have been removed net.lines = net.lines.loc[net.lines.bus0.isin(old_to_new_buses_map.index) & net.lines.bus1.isin(old_to_new_buses_map.index)] # Assign new bus to lines net.lines.bus0 = net.lines.bus0.map(old_to_new_buses_map) net.lines.bus1 = net.lines.bus1.map(old_to_new_buses_map) # Remove lines that have the same starting and end bus net.lines = net.lines[net.lines.bus0 != net.lines.bus1] # Merge lines with same starting and end bus # Get all lines connected to the same bus in the same direction net.lines[['bus0', 'bus1']] = [sorted((bus0, bus1)) for (bus0, bus1) in net.lines[['bus0', 'bus1']].values] # Get pairs of connected bus connected_buses = set([(bus0, bus1) for (bus0, bus1) in net.lines[['bus0', 'bus1']].values.tolist()]) all_lines_df = pd.DataFrame() # Compute reactance of lines net.lines["x"] = net.lines["length"]*net.lines['type'].map(net.line_types.x_per_length) for bus0, bus1 in connected_buses: # line_index = f"{bus0}-{bus1}" # lines_df.loc[line_index, ['bus0', 'bus1']] = (bus0, bus1) # Get all lines in original network that were connected to bus0 and bus1 old_lines_df = net.lines[(net.lines.bus0 == bus0) & (net.lines.bus1 == bus1)] # Merge those lines by voltage level # TODO: Parameters to consider # - name: ok # - bus0: ok # - bus1: ok # - underground: ? --> affect cost # - under_construction: already dealt with before # - tags: nope # - geometry: nope (replaced by straight line) # - length: how? -> just take fly-distance (times 1.25 like in pypsa-eur?) # - x: how? # - v_nom: how? # - num_parallel: how? # Use sth similar to this: net.lines.loc[non380_lines_b, 'num_parallel'] *= (net.lines.loc[non380_lines_b, 'v_nom'] / 380.)**2 ? # - s_nom: how? lines_df = old_lines_df.groupby("type")['v_nom'].unique().apply(lambda x: x[0]).to_frame() lines_df['s_nom'] = old_lines_df.groupby("type")['s_nom'].sum() lines_df['num_parallel'] = old_lines_df.groupby("type")['num_parallel'].sum() lines_df['x'] = old_lines_df.groupby("type")['x'].apply(lambda x: 1/sum(1/x)) lines_df["line_id"] = lines_df['v_nom'].apply(lambda x: f"{bus0}-{bus1}-{x}") lines_df["bus0"] = bus0 lines_df["bus1"] = bus1 lines_df["length"] = lines_df[['bus0', 'bus1']].apply(lambda x: compute_distance(x.bus0, x.bus1), axis=1) lines_df = lines_df.reset_index().set_index('line_id') all_lines_df = pd.concat((all_lines_df, lines_df)) print(all_lines_df) # --- Links --- # # Remove lines associated to buses that have been removed net.links = net.links.loc[net.links.bus0.isin(old_to_new_buses_map.index) & net.links.bus1.isin(old_to_new_buses_map.index)] net.links.bus0 = net.links.bus0.map(old_to_new_buses_map) net.links.bus1 = net.links.bus1.map(old_to_new_buses_map) # Remove links that have the same starting and end bus net.links = net.links[net.links.bus0 != net.links.bus1] # Merge links with same starting and end bus # Get all links connected to the same bus in the same direction net.links[['bus0', 'bus1']] = [sorted((str(bus0), str(bus1))) for (bus0, bus1) in net.links[['bus0', 'bus1']].values] # Get pairs of connected bus connected_buses = set([(bus0, bus1) for (bus0, bus1) in net.links[['bus0', 'bus1']].values.tolist()]) links_df = pd.DataFrame(index=[f"{bus0}-{bus1}" for (bus0, bus1) in connected_buses], columns=['bus0', 'bus1', 'p_nom', 'length']) for bus0, bus1 in connected_buses: link_index = f"{bus0}-{bus1}" links_df.loc[link_index, ['bus0', 'bus1']] = (bus0, bus1) # Get all links in original network that were connected to bus0 and bus1 old_links_df = net.links[(net.links.bus0 == bus0) & (net.links.bus1 == bus1)] # Add capacities links_df.loc[link_index, 'p_nom'] = old_links_df['p_nom'].sum() links_df["length"] = links_df[['bus0', 'bus1']].apply(lambda x: compute_distance(x.bus0, x.bus1), axis=1) # TODO: Parameters to consider # - name: ok # - bus0: ok # - bus1: ok # - carrier: nope --> all dc # - underground: how? --> affect cost # - underwater_fraction: how? --> affect cost # - under_construction: already dealt with before # - tags: nope # - geometry: nope (replaced by straight line) # - length: how? -> just take fly-distance (times 1.25 like in pypsa-eur?) # - p_nom: how? # - num_parallel: how? # TODO: What about transformers? # print(net.links) print(links_df) clustered_net = pypsa.Network() clustered_net.import_components_from_dataframe(buses_df, 'Bus') clustered_net.import_components_from_dataframe(all_lines_df, 'Line') clustered_net.import_components_from_dataframe(links_df, 'Link') print(clustered_net.buses.onshore_region) return clustered_net