From 52e214a3b25d9624993dcd10829cb8950aeef7b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Mon, 24 Sep 2018 20:19:47 +0200 Subject: [PATCH] base_network: Add links from TYNDP Manually collected from https://tyndp.entsoe.eu/. Deactivated by default. --- Snakefile | 1 + config.yaml | 11 +++--- data/links_tyndp.csv | 26 +++++++++++++ scripts/base_network.py | 82 ++++++++++++++++++++++++++++++++--------- 4 files changed, 98 insertions(+), 22 deletions(-) create mode 100644 data/links_tyndp.csv diff --git a/Snakefile b/Snakefile index 4da244ba..9b0e14e1 100644 --- a/Snakefile +++ b/Snakefile @@ -43,6 +43,7 @@ rule base_network: eg_transformers='data/entsoegridkit/transformers.csv', parameter_corrections='data/parameter_corrections.yaml', links_p_nom='data/links_p_nom.csv', + links_tyndp='data/links_tyndp.csv', country_shapes='resources/country_shapes.geojson', offshore_shapes='resources/offshore_shapes.geojson', europe_shape='resources/europe_shape.geojson' diff --git a/config.yaml b/config.yaml index 69326e32..727d22bc 100644 --- a/config.yaml +++ b/config.yaml @@ -5,7 +5,7 @@ scenario: sectors: [E] # ,E+EV,E+BEV,E+BEV+V2G] # [ E+EV, E+BEV, E+BEV+V2G ] simpl: [''] lv: [1.0, 1.125, 1.25, 1.5, 2.0, 3.0] - clusters: [45, 64, 90, 128, 181, 256] #, 362] # (2**np.r_[5.5:9:.5]).astype(int) + clusters: [45, 64, 90, 128, 181] #, 256, 362] # (2**np.r_[5.5:9:.5]).astype(int) opts: [Co2L-3H] #, LC-FL, LC-T, Ep-T, Co2L-T] countries: ['AL', 'AT', 'BA', 'BE', 'BG', 'CH', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'ME', 'MK', 'NL', 'NO', 'PL', 'PT', 'RO', 'RS', 'SE', 'SI', 'SK'] @@ -32,7 +32,7 @@ electricity: battery: 6 H2: 168 - conventional_carriers: [nuclear, oil, OCGT, CCGT, coal, lignite, geothermal, biomass] + conventional_carriers: [] # nuclear, oil, OCGT, CCGT, coal, lignite, geothermal, biomass] atlite: nprocesses: 4 @@ -114,6 +114,7 @@ lines: links: p_max_pu: 1.0 + include_tyndp: false under_construction: 'zero' # 'zero': set capacity to zero, 'remove': remove, 'keep': with full capacity transformers: @@ -150,9 +151,9 @@ solving: load_shedding: true noisy_costs: true min_iterations: 4 - max_iterations: 6 - # max_iterations: 1 - # nhours: 10 + # max_iterations: 6 + max_iterations: 3 + #nhours: 10 solver: name: gurobi threads: 4 diff --git a/data/links_tyndp.csv b/data/links_tyndp.csv new file mode 100644 index 00000000..1d736eb8 --- /dev/null +++ b/data/links_tyndp.csv @@ -0,0 +1,26 @@ +Name,Converterstation 1,Converterstation 2,Length (given) (km),Length (distance*1.2) (km),Power (MW),status,in links_p_nom.csv ?,Ref,x1,y1,x2,y2 +Biscay Gulf,Gatica (ES),Cubnezais (FR),370,,2200,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/16,-2.867,43.367,-0.408943,45.074191 +Italy-France,Piossasco (IT),Grand Ile (FR),190,,1000,under construction,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/21,7.468,44.9898,6.045,45.472 +IFA2,Tourbe (FR),Chilling (GB),,247.2,1000,under construction,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/25,-0.172042,49.083593,-1.277269,50.839338 +Italy-Montenegro,Villanova (IT),Latsva (MT),445,,1200,under construction,HVDC MON.ITA Project?,https://tyndp.entsoe.eu/tyndp2018/projects/projects/28,14.125,42.3947222222222,18.7947222222222,42.3175 +NordLink,Tonstad (NO),Wilster (DE),514,,1400,under construction,NORD.LINK,https://tyndp.entsoe.eu/tyndp2018/projects/projects/37,6.716948,58.662631,9.373979,53.922479 +COBRA cable,Endrup (DK),Eemshaven (NL),325,,700,under construction,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/71,8.718392,55.523115,6.835494,53.438589 +Thames Estuary Cluster (NEMO-Link),Richborough (GB),Gezelle (BE),140,,1000,under construction,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/74,1.324854,51.295891,3.23043,51.24902 +Anglo-Scottish -1,Hunterston (UK),Deeside (UK),422,,2400,under construction,Yes (Western HVDC Link ),https://tyndp.entsoe.eu/tyndp2018/projects/projects/77,-4.898329,55.723331,-3.032972,53.199735 +ALEGrO,Lixhe (BE),Oberzier (DE),100,,1000,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/92,5.67933,50.7567965,6.474704,50.867532 +North Sea Link,Kvilldal (NO),Blythe (GB),720,,1400,under construction,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/110,6.637527,59.515096,-1.510277,55.126957 +HVDC SuedOstLink,Wolmirstedt (DE),Isar (DE),,557,2000,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/130,11.629014,52.252137,12.091596,48.080837 +HVDC Line A-North,Emden East (DE),Osterath (DE),,284,2000,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/132,7.206009,53.359403,6.619451,51.272935 +France-Alderney-Britain,Exeter (UK),Menuel (FR),220,,1400,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/153,-3.533899,50.718412,-1.469216,49.509594 +Viking DKW-GB,Bicker Fen (GB),Revsing (DK),,807,1400,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/167,-0.203587,52.93979,9.178363,55.509166 +ElecLink,Sellindge (UK),Mandarins (FR),,72,1000,under construction,Yes (Interconnexion France Angleterre (new)),https://tyndp.entsoe.eu/tyndp2018/projects/projects/172,0.975555555555556,51.1058333333333,1.78472222222222,50.9030555555556 +Greenconnector,Verderio (IT),Sils i.D. (CH),150,,1000,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/174,9.439781,45.668539,9.76569,46.432156 +Hansa PowerBridge I,Hurva (SE),Guestrow (DE),,283,700,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/176,13.6022222222222,55.8330555555556,12.189538,53.803155 +NorthConnect,Simadalen (NO),Peterhead (UK),650,,1400,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/190,7.16027,60.500527,-1.784066,57.508123 +HVDC SuedLink,Wilster (DE),Großgartach (DE),,637,4000,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/235,9.373979,53.922479,9.117193,49.145157 +AQUIND Interconnector,Lovedean (GB),Barnabos (FR),254,,2000,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/247,-1.020512,50.908244,0.991736,49.656631 +HVDC Ultranet,Osterath (DE),Philippsburg (DE),,314,600,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/254,6.619451,51.272935,8.458036,49.235253 +Gridlink,Kingsnorth (UK),Warande (FR),160,,1400,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/285,0.596111111111111,51.41972,2.376776,51.034368 +SACOI 3 -1,Codrongianos (IT),Lucciana (FR),,268,400,in permitting,replace SACOI 2,https://tyndp.entsoe.eu/tyndp2018/projects/projects/299,8.679351,40.65765,9.417275,42.547134 +SACOI 3 -2,Lucciana (FR),Suvereto (IT),,139,400,in permitting,replace SACOI 2,https://tyndp.entsoe.eu/tyndp2018/projects/projects/299,9.417275,42.547134,10.678444,43.076683 +NeuConnect,Grain (UK),Fedderwarden (DE),680,,1400,in permitting,,https://tyndp.entsoe.eu/tyndp2018/projects/projects/309,0.716666666666667,51.44,8.046524,53.562763 diff --git a/scripts/base_network.py b/scripts/base_network.py index 2afe15a0..9d18b97c 100644 --- a/scripts/base_network.py +++ b/scripts/base_network.py @@ -9,7 +9,7 @@ from scipy.sparse import csgraph from six import iteritems from six.moves import filter -from shapely.geometry import Point +from shapely.geometry import Point, LineString import shapely, shapely.prepared, shapely.wkt from vresutils.graph import BreadthFirstLevels @@ -19,6 +19,23 @@ logger = logging.getLogger(__name__) import pypsa +def _find_closest_links(links, new_links, distance_upper_bound=1.5): + tree = sp.spatial.KDTree(np.vstack([ + new_links[['x1', 'y1', 'x2', 'y2']], + new_links[['x2', 'y2', 'x1', 'y1']] + ])) + + dist, ind = tree.query( + np.asarray([np.asarray(shapely.wkt.loads(s))[[0, -1]].flatten() + for s in links.geometry]), + distance_upper_bound=distance_upper_bound + ) + + return ( + pd.DataFrame(dict(D=dist, i=new_links.index[ind % len(new_links)]), index=links.index) + .groupby('i').D.idxmin() + ) + def _load_buses_from_eg(): buses = (pd.read_csv(snakemake.input.eg_buses, quotechar="'", true_values='t', false_values='f', @@ -77,6 +94,49 @@ def _load_links_from_eg(buses): return links + +def _add_links_from_tyndp(buses, links): + links_tyndp = pd.read_csv(snakemake.input.links_tyndp) + links_tyndp["j"] = _find_closest_links(links, links_tyndp, distance_upper_bound=0.8) + # Corresponds approximately to 60km tolerances + + if links_tyndp["j"].notnull().any(): + logger.info("The following TYNDP links were already in the dataset (skipping): " + ", ".join(links_tyndp.loc[links_tyndp["j"].notnull(), "Name"])) + links_tyndp = links_tyndp.loc[links_tyndp["j"].isnull()] + + tree = sp.spatial.KDTree(buses[['x', 'y']]) + _, ind0 = tree.query(links_tyndp[["x1", "y1"]]) + ind0_b = ind0 < len(buses) + links_tyndp.loc[ind0_b, "bus0"] = buses.index[ind0[ind0_b]] + + _, ind1 = tree.query(links_tyndp[["x2", "y2"]]) + ind1_b = ind1 < len(buses) + links_tyndp.loc[ind1_b, "bus1"] = buses.index[ind1[ind1_b]] + + links_tyndp_located_b = links_tyndp["bus0"].notnull() & links_tyndp["bus1"].notnull() + if not links_tyndp_located_b.all(): + logger.warn("Did not find connected buses for TYNDP links (skipping): " + ", ".join(links_tyndp.loc[~links_tyndp_located_b, "Name"])) + links_tyndp = links_tyndp.loc[links_tyndp_located_b] + + logger.info("Adding the following TYNDP links: " + ", ".join(links_tyndp["Name"])) + + links_tyndp = links_tyndp[["bus0", "bus1"]].assign( + carrier='DC', + p_nom=links_tyndp["Power (MW)"], + length=links_tyndp["Length (given) (km)"].fillna(links_tyndp["Length (distance*1.2) (km)"]), + under_construction=True, + underground=False, + geometry=(links_tyndp[["x1", "y1", "x2", "y2"]] + .apply(lambda s: str(LineString([[s.x1, s.y1], [s.x2, s.y2]])), axis=1)), + tags=('"name"=>"' + links_tyndp["Name"] + '", ' + + '"ref"=>"' + links_tyndp["Ref"] + '", ' + + '"status"=>"' + links_tyndp["status"] + '"') + ) + + links_tyndp.index = "T" + links_tyndp.index.astype(str) + + return links.append(links_tyndp) + def _load_lines_from_eg(buses): lines = (pd.read_csv(snakemake.input.eg_lines, quotechar="'", true_values='t', false_values='f', dtype=dict(line_id='str', bus0='str', bus1='str', @@ -134,22 +194,7 @@ def _set_electrical_parameters_links(links): links['p_min_pu'] = -p_max_pu links_p_nom = pd.read_csv(snakemake.input.links_p_nom) - - tree = sp.spatial.KDTree(np.vstack([ - links_p_nom[['x1', 'y1', 'x2', 'y2']], - links_p_nom[['x2', 'y2', 'x1', 'y1']] - ])) - - dist, ind = tree.query( - np.asarray([np.asarray(shapely.wkt.loads(s))[[0, -1]].flatten() - for s in links.geometry]), - distance_upper_bound=1.5 - ) - - links_p_nom["j"] =( - pd.DataFrame(dict(D=dist, i=links_p_nom.index[ind % len(links_p_nom)]), index=links.index) - .groupby('i').D.idxmin() - ) + links_p_nom["j"] = _find_closest_links(links, links_p_nom) p_nom = links_p_nom.dropna(subset=["j"]).set_index("j")["Power (MW)"] links.loc[p_nom.index, "p_nom"] = p_nom @@ -321,6 +366,9 @@ def base_network(): buses = _load_buses_from_eg() links = _load_links_from_eg(buses) + if snakemake.config['links'].get('include_tyndp'): + links = _add_links_from_tyndp(buses, links) + converters = _load_converters_from_eg(buses) lines = _load_lines_from_eg(buses)