Passed
Pull Request — dev (#1068)
by
unknown
01:54
created

data.datasets.gas_grid.insert_gas_buses_abroad()   A

Complexity

Conditions 1

Size

Total Lines 48
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 48
rs 9.6
c 0
b 0
f 0
cc 1
nop 1
1
# -*- coding: utf-8 -*-
2
"""
3
The module containing code aiming to insert the methane grid into the database
4
5
The central module containing all code dealing with the import of data
6
from SciGRID_gas (IGGIELGN dataset) and with the insertion fo the CH4
7
buses and links into the database for the scenarios eGon2035 and eGon100RE.
8
9
The SciGRID_gas data downloaded with :py:func:`download_SciGRID_gas_data`
10
into the folder ./datasets/gas_data/data are also used by other modules.
11
12
In this module, only the IGGIELGN_Nodes and IGGIELGN_PipeSegments cvs files
13
are used in the function :py:func:`insert_gas_data` that inserts the CH4
14
buses and links, which for the case of gas represent pipelines, into the
15
database.
16
17
"""
18
from pathlib import Path
19
from urllib.request import urlretrieve
20
from zipfile import ZipFile
21
import ast
22
import json
23
import os
24
25
from geoalchemy2.types import Geometry
26
from shapely import geometry
27
import geopandas
28
import numpy as np
29
import pandas as pd
30
31
from egon.data import config, db
32
from egon.data.config import settings
33
from egon.data.datasets import Dataset
34
from egon.data.datasets.electrical_neighbours import central_buses_egon100
35
from egon.data.datasets.etrago_helpers import copy_and_modify_buses
36
from egon.data.datasets.scenario_parameters import get_sector_parameters
37
38
39
class GasNodesAndPipes(Dataset):
40
    """
41
    Insert the CH4 buses and links into the database.
42
43
    Insert the CH4 buses and links, which for the case of gas represent
44
    pipelines, into the database for the scenarios eGon2035 and eGon100RE
45
    with the functions :py:func:`insert_gas_data` and :py:func:`insert_gas_data_eGon100RE`.
46
47
    *Dependencies*
48
      * :py:class:`DataBundle <egon.data.datasets.data_bundle.DataBundle>`
49
      * :py:class:`ElectricalNeighbours <egon.data.datasets.electrical_neighbours.ElectricalNeighbours>`
50
      * :py:class:`Osmtgmod <egon.data.datasets.osmtgmod.Osmtgmod>`
51
      * :py:class:`ScenarioParameters <egon.data.datasets.scenario_parameters.ScenarioParameters>`
52
      * :py:class:`EtragoSetup <egon.data.datasets.etrago_setup.EtragoSetup>` (more specifically the :func:`create_tables <egon.data.datasets.etrago_setup.create_tables>` task)
53
54
    *Resulting tables*
55
      * :py:class:`grid.egon_etrago_bus <egon.data.datasets.etrago_setup.EgonPfHvBus>` is extended
56
      * :py:class:`grid.egon_etrago_link <egon.data.datasets.etrago_setup.EgonPfHvLink>` is extended
57
58
    """
59
60
    #:
61
    name: str = "GasNodesAndPipes"
62
    #:
63
    version: str = "0.0.9"
64
65
    def __init__(self, dependencies):
66
        super().__init__(
67
            name=self.name,
68
            version=self.version,
69
            dependencies=dependencies,
70
            tasks=(insert_gas_data, insert_gas_data_eGon100RE),
71
        )
72
73
74
def download_SciGRID_gas_data():
75
    """
76
    Download SciGRID_gas IGGIELGN data from Zenodo
77
78
    The following data for CH4 are downloaded into the folder
79
    ./datasets/gas_data/data:
80
      * Buses (file IGGIELGN_Nodes.csv),
81
      * Pipelines (file IGGIELGN_PipeSegments.csv),
82
      * Productions (file IGGIELGN_Productions.csv),
83
      * Storages (file IGGIELGN_Storages.csv),
84
      * LNG terminals (file IGGIELGN_LNGs.csv).
85
86
    For more information on these data refer, to the
87
    `SciGRID_gas IGGIELGN documentation <https://zenodo.org/record/4767098>`_.
88
89
    Returns
90
    -------
91
    None
92
93
    """
94
    path = Path(".") / "datasets" / "gas_data"
95
    os.makedirs(path, exist_ok=True)
96
97
    basename = "IGGIELGN"
98
    zip_file = Path(".") / "datasets" / "gas_data" / "IGGIELGN.zip"
99
    zenodo_zip_file_url = (
100
        "https://zenodo.org/record/4767098/files/" + basename + ".zip"
101
    )
102
    if not os.path.isfile(zip_file):
103
        urlretrieve(zenodo_zip_file_url, zip_file)
104
105
    components = [
106
        "Nodes",
107
        "PipeSegments",
108
        "Productions",
109
        "Storages",
110
        "LNGs",
111
    ]  #'Compressors'
112
    files = []
113
    for i in components:
114
        files.append("data/" + basename + "_" + i + ".csv")
115
116
    with ZipFile(zip_file, "r") as zipObj:
117
        listOfFileNames = zipObj.namelist()
118
        for fileName in listOfFileNames:
119
            if fileName in files:
120
                zipObj.extract(fileName, path)
121
122
123
def define_gas_nodes_list():
124
    """
125
    Define list of CH4 buses from SciGRID_gas IGGIELGN data
126
127
    The CH4 nodes are modelled as buses. Therefore the SciGRID_gas nodes
128
    are red from the IGGIELGN_Nodes cvs file previously downloaded in the
129
    function :py:func:`download_SciGRID_gas_data`, corrected (erroneous country),
130
    and returned as dataframe.
131
132
    Returns
133
    -------
134
    gas_nodes_list : pandas.DataFrame
135
        Dataframe containing the gas nodes in Europe
136
137
    """
138
    # Select next id value
139
    new_id = db.next_etrago_id("bus")
140
141
    target_file = (
142
        Path(".") / "datasets" / "gas_data" / "data" / "IGGIELGN_Nodes.csv"
143
    )
144
145
    gas_nodes_list = pd.read_csv(
146
        target_file,
147
        delimiter=";",
148
        decimal=".",
149
        usecols=["lat", "long", "id", "country_code", "param"],
150
    )
151
152
    # Correct non valid neighbouring country nodes
153
    gas_nodes_list.loc[
154
        gas_nodes_list["id"] == "INET_N_1182", "country_code"
155
    ] = "AT"
156
    gas_nodes_list.loc[
157
        gas_nodes_list["id"] == "SEQ_10608_p", "country_code"
158
    ] = "NL"
159
    gas_nodes_list.loc[
160
        gas_nodes_list["id"] == "N_88_NS_LMGN", "country_code"
161
    ] = "XX"
162
163
    gas_nodes_list = gas_nodes_list.rename(columns={"lat": "y", "long": "x"})
164
165
    gas_nodes_list["bus_id"] = range(new_id, new_id + len(gas_nodes_list))
166
    gas_nodes_list = gas_nodes_list.set_index("id")
167
168
    return gas_nodes_list
169
170
171
def ch4_nodes_number_G(gas_nodes_list):
172
    """
173
    Return the number of CH4 buses in Germany
174
175
    Parameters
176
    ----------
177
    gas_nodes_list : pandas.DataFrame
178
        Dataframe containing the gas nodes in Europe
179
180
    Returns
181
    -------
182
    N_ch4_nodes_G : int
183
        Number of CH4 buses in Germany
184
185
    """
186
187
    ch4_nodes_list = gas_nodes_list[
188
        gas_nodes_list["country_code"].str.match("DE")
189
    ]
190
    N_ch4_nodes_G = len(ch4_nodes_list)
191
192
    return N_ch4_nodes_G
193
194
195
def insert_CH4_nodes_list(gas_nodes_list):
196
    """
197
    Insert list of German CH4 nodes into the database for eGon2035
198
199
    Insert the list of German CH4 nodes into the database by executing
200
    the following steps:
201
      * Receive the buses as parameter (from SciGRID_gas IGGIELGN data)
202
      * Add the missing information: scn_name and carrier
203
      * Clean the database table grid.egon_etrago_bus of the
204
        CH4 buses of the specific scenario (eGon2035) in Germany
205
      * Insert the buses in the table grid.egon_etrago_bus
206
207
    Parameters
208
    ----------
209
    gas_nodes_list : pandas.DataFrame
210
        Dataframe containing the gas nodes in Europe
211
212
    Returns
213
    -------
214
    None
215
216
    """
217
    # Connect to local database
218
    engine = db.engine()
219
220
    gas_nodes_list = gas_nodes_list[
221
        gas_nodes_list["country_code"].str.match("DE")
222
    ]  # To eventually replace with a test if the nodes are in the german boundaries.
223
224
    # Cut data to federal state if in testmode
225
    NUTS1 = []
226
    for index, row in gas_nodes_list.iterrows():
227
        param = ast.literal_eval(row["param"])
228
        NUTS1.append(param["nuts_id_1"])
229
    gas_nodes_list = gas_nodes_list.assign(NUTS1=NUTS1)
230
231
    boundary = settings()["egon-data"]["--dataset-boundary"]
232
    if boundary != "Everything":
233
        map_states = {
234
            "Baden-Württemberg": "DE1",
235
            "Nordrhein-Westfalen": "DEA",
236
            "Hessen": "DE7",
237
            "Brandenburg": "DE4",
238
            "Bremen": "DE5",
239
            "Rheinland-Pfalz": "DEB",
240
            "Sachsen-Anhalt": "DEE",
241
            "Schleswig-Holstein": "DEF",
242
            "Mecklenburg-Vorpommern": "DE8",
243
            "Thüringen": "DEG",
244
            "Niedersachsen": "DE9",
245
            "Sachsen": "DED",
246
            "Hamburg": "DE6",
247
            "Saarland": "DEC",
248
            "Berlin": "DE3",
249
            "Bayern": "DE2",
250
        }
251
252
        gas_nodes_list = gas_nodes_list[
253
            gas_nodes_list["NUTS1"].isin([map_states[boundary], np.nan])
254
        ]
255
256
        # A completer avec nodes related to pipelines which have an end in the selected area et evt deplacer ds define_gas_nodes_list
257
258
    # Add missing columns
259
    c = {"scn_name": "eGon2035", "carrier": "CH4"}
260
    gas_nodes_list = gas_nodes_list.assign(**c)
261
262
    gas_nodes_list = geopandas.GeoDataFrame(
263
        gas_nodes_list,
264
        geometry=geopandas.points_from_xy(
265
            gas_nodes_list["x"], gas_nodes_list["y"]
266
        ),
267
    )
268
    gas_nodes_list = gas_nodes_list.rename(
269
        columns={"geometry": "geom"}
270
    ).set_geometry("geom", crs=4326)
271
272
    gas_nodes_list = gas_nodes_list.reset_index(drop=True)
273
    gas_nodes_list = gas_nodes_list.drop(
274
        columns=["NUTS1", "param", "country_code"]
275
    )
276
277
    # Insert data to db
278
    db.execute_sql(
279
        f"""
280
    DELETE FROM grid.egon_etrago_bus WHERE "carrier" = 'CH4' AND
281
    scn_name = '{c['scn_name']}' AND country = 'DE';
282
    """
283
    )
284
285
    # Insert CH4 data to db
286
    print(gas_nodes_list)
287
    gas_nodes_list.to_postgis(
288
        "egon_etrago_bus",
289
        engine,
290
        schema="grid",
291
        index=False,
292
        if_exists="append",
293
        dtype={"geom": Geometry()},
294
    )
295
296
def define_gas_buses_abroad(scn_name="eGon2035"):
297
    """
298
    Define central CH4 buses in foreign countries for eGon2035
299
300
    For the scenario eGon2035, define central CH4 buses in foreign
301
    countries. The considered foreign countries are the direct 
302
    neighbouring countries, with the addition of Russia that is
303
    considered as a source of fossil CH4.
304
    Therefore, the following steps are executed:
305
      * Definition of the foreign buses with the function
306
        :py:func:`central_buses_egon100 <egon.data.datasets.electrical_neighbours.central_buses_egon100>` from
307
        the module :py:mod:`electrical_neighbours <egon.data.datasets.electrical_neighbours>`
308
      * Removal of the superfluous buses in order to have only one bus
309
        in each neighbouring country
310
      * Removal of the the irrelevant columns
311
      * Addition of the missing information: scn_name and carrier
312
      * Attribution of an id to each bus
313
314
    Parameters
315
    ----------
316
    scn_name : str
317
        Name of the scenario
318
319
    Returns
320
    -------
321
    gdf_abroad_buses : pandas.DataFrame
322
        Dataframe containing the gas buses in the neighbouring countries
323
        and one in the center of Germany in test mode
324
325
    """
326
    # Select sources and targets from dataset configuration
327
    sources = config.datasets()["electrical_neighbours"]["sources"]
328
329
    main_gas_carrier = get_sector_parameters("gas", scenario=scn_name)[
330
        "main_gas_carrier"
331
    ]
332
333
    # Select the foreign buses
334
    gdf_abroad_buses = central_buses_egon100(sources)
335
    gdf_abroad_buses = gdf_abroad_buses.drop_duplicates(subset=["country"])
336
337
    # Select next id value
338
    new_id = db.next_etrago_id("bus")
339
340
    gdf_abroad_buses = gdf_abroad_buses.drop(
341
        columns=[
342
            "v_nom",
343
            "v_mag_pu_set",
344
            "v_mag_pu_min",
345
            "v_mag_pu_max",
346
            "geom",
347
        ]
348
    )
349
    gdf_abroad_buses["scn_name"] = "eGon2035"
350
    gdf_abroad_buses["carrier"] = main_gas_carrier
351
    gdf_abroad_buses["bus_id"] = range(new_id, new_id + len(gdf_abroad_buses))
352
353
    # Add central bus in Russia
354
    gdf_abroad_buses = gdf_abroad_buses.append(
355
        {
356
            "scn_name": scn_name,
357
            "bus_id": (new_id + len(gdf_abroad_buses) + 1),
358
            "x": 41,
359
            "y": 55,
360
            "country": "RU",
361
            "carrier": main_gas_carrier,
362
        },
363
        ignore_index=True,
364
    )
365
    # if in test mode, add bus in center of Germany
366
    boundary = settings()["egon-data"]["--dataset-boundary"]
367
368
    if boundary != "Everything":
369
        gdf_abroad_buses = gdf_abroad_buses.append(
370
            {
371
                "scn_name": scn_name,
372
                "bus_id": (new_id + len(gdf_abroad_buses) + 1),
373
                "x": 10.4234469,
374
                "y": 51.0834196,
375
                "country": "DE",
376
                "carrier": main_gas_carrier,
377
            },
378
            ignore_index=True,
379
        )
380
381
    gdf_abroad_buses = geopandas.GeoDataFrame(
382
        gdf_abroad_buses,
383
        geometry=geopandas.points_from_xy(
384
            gdf_abroad_buses["x"], gdf_abroad_buses["y"]
385
        ),
386
    )
387
    gdf_abroad_buses = gdf_abroad_buses.rename(
388
        columns={"geometry": "geom"}
389
    ).set_geometry("geom", crs=4326)
390
391
    return gdf_abroad_buses
392
393
394
def insert_gas_buses_abroad(scn_name="eGon2035"):
395
    """
396
    Insert CH4 buses in neighbouring countries to database for eGon2035
397
398
      * Definition of the CH4 buses abroad with the function 
399
        :py:func:`define_gas_buses_abroad`
400
      * Cleaning of the data base table grid.egon_etrago_bus of the
401
        CH4 buses of the specific scenario (eGon2035) out of Germany
402
      * Insertion of the neighbouring buses in the table grid.egon_etrago_bus.   
403
404
    Parameters
405
    ----------
406
    scn_name : str
407
        Name of the scenario
408
409
    Returns
410
    -------
411
    gdf_abroad_buses : dataframe
412
        Dataframe containing the CH4 buses in the neighbouring countries
413
        and one in the center of Germany in test mode
414
415
    """
416
    main_gas_carrier = get_sector_parameters("gas", scenario=scn_name)[
417
        "main_gas_carrier"
418
    ]
419
420
    # Connect to local database
421
    engine = db.engine()
422
    db.execute_sql(
423
        f"""
424
    DELETE FROM grid.egon_etrago_bus WHERE "carrier" = '{main_gas_carrier}' AND
425
    scn_name = '{scn_name}' AND country != 'DE';
426
    """
427
    )
428
429
    gdf_abroad_buses = define_gas_buses_abroad(scn_name)
430
431
    # Insert to db
432
    print(gdf_abroad_buses)
433
    gdf_abroad_buses.to_postgis(
434
        "egon_etrago_bus",
435
        engine,
436
        schema="grid",
437
        index=False,
438
        if_exists="append",
439
        dtype={"geom": Geometry()},
440
    )
441
    return gdf_abroad_buses
442
443
444
def define_gas_pipeline_list(
445
    gas_nodes_list, abroad_gas_nodes_list, scn_name="eGon2035"
446
):
447
    """
448
    Define gas pipelines in Germany from SciGRID_gas IGGIELGN data
449
450
    The gas pipelines, modelled as Pypsa links are red from the IGGIELGN_PipeSegments
451
    csv file previously downloded in the function :py:func:`download_SciGRID_gas_data`.
452
453
    The capacities of the pipelines are determined by the correspondance
454
    table given by the Parameters for the classification of gas pipelines
455
    in `Electricity, heat, and gas sector data for modeling the German system
456
    <https://www.econstor.eu/bitstream/10419/173388/1/1011162628.pdf>`_
457
    related to the pipeline diameter given in the SciGRID_gas dataset.
458
459
    The manual corrections allows to:
460
      * Delete gas pipelines disconnected of the rest of the gas grid
461
      * Connect one pipeline (also connected to Norway) disconnected of
462
        the rest of the gas grid
463
      * Correct erroneous country of some pipelines
464
465
    Parameters
466
    ----------
467
    gas_nodes_list : dataframe
468
        Dataframe containing the gas nodes in Europe
469
    abroad_gas_nodes_list: dataframe
470
        Dataframe containing the gas buses in the neighbouring countries
471
        and one in the center of Germany in test mode
472
    scn_name : str
473
        Name of the scenario
474
475
    Returns
476
    -------
477
    gas_pipelines_list : pandas.DataFrame
478
        Dataframe containing the gas pipelines in Germany
479
480
    """
481
    abroad_gas_nodes_list = abroad_gas_nodes_list.set_index("country")
482
483
    main_gas_carrier = get_sector_parameters("gas", scenario=scn_name)[
484
        "main_gas_carrier"
485
    ]
486
487
    # Select next id value
488
    new_id = db.next_etrago_id("link")
489
490
    classifiaction_file = (
491
        Path(".")
492
        / "data_bundle_egon_data"
493
        / "pipeline_classification_gas"
494
        / "pipeline_classification.csv"
495
    )
496
497
    classification = pd.read_csv(
498
        classifiaction_file,
499
        delimiter=",",
500
        usecols=["classification", "max_transport_capacity_Gwh/d"],
501
    )
502
503
    target_file = (
504
        Path(".")
505
        / "datasets"
506
        / "gas_data"
507
        / "data"
508
        / "IGGIELGN_PipeSegments.csv"
509
    )
510
511
    gas_pipelines_list = pd.read_csv(
512
        target_file,
513
        delimiter=";",
514
        decimal=".",
515
        usecols=["id", "node_id", "lat", "long", "country_code", "param"],
516
    )
517
518
    # Select the links having at least one bus in Germany
519
    gas_pipelines_list = gas_pipelines_list[
520
        gas_pipelines_list["country_code"].str.contains("DE")
521
    ]
522
    # Remove links disconnected of the rest of the grid
523
    # Remove manually for disconnected link EntsoG_Map__ST_195 and EntsoG_Map__ST_108
524
    gas_pipelines_list = gas_pipelines_list[
525
        gas_pipelines_list["node_id"] != "['SEQ_11790_p', 'Stor_EU_107']"
526
    ]
527
    gas_pipelines_list = gas_pipelines_list[
528
        ~gas_pipelines_list["id"].str.match("EntsoG_Map__ST_108")
529
    ]
530
531
    # Manually add pipeline to artificially connect isolated pipeline
532
    gas_pipelines_list.at["new_pipe", "param"] = gas_pipelines_list[
533
        gas_pipelines_list["id"] == "NO_PS_8_Seg_0_Seg_23"
534
    ]["param"].values[0]
535
    gas_pipelines_list.at[
536
        "new_pipe", "node_id"
537
    ] = "['SEQ_12442_p', 'LKD_N_200']"
538
    gas_pipelines_list.at["new_pipe", "lat"] = "[53.358536, 53.412719]"
539
    gas_pipelines_list.at["new_pipe", "long"] = "[7.041677, 7.093251]"
540
    gas_pipelines_list.at["new_pipe", "country_code"] = "['DE', 'DE']"
541
542
    gas_pipelines_list["link_id"] = range(
543
        new_id, new_id + len(gas_pipelines_list)
544
    )
545
    gas_pipelines_list["link_id"] = gas_pipelines_list["link_id"].astype(int)
546
547
    # Cut data to federal state if in testmode
548
    NUTS1 = []
549
    for index, row in gas_pipelines_list.iterrows():
550
        param = ast.literal_eval(row["param"])
551
        NUTS1.append(param["nuts_id_1"])
552
    gas_pipelines_list["NUTS1"] = NUTS1
553
554
    map_states = {
555
        "Baden-Württemberg": "DE1",
556
        "Nordrhein-Westfalen": "DEA",
557
        "Hessen": "DE7",
558
        "Brandenburg": "DE4",
559
        "Bremen": "DE5",
560
        "Rheinland-Pfalz": "DEB",
561
        "Sachsen-Anhalt": "DEE",
562
        "Schleswig-Holstein": "DEF",
563
        "Mecklenburg-Vorpommern": "DE8",
564
        "Thüringen": "DEG",
565
        "Niedersachsen": "DE9",
566
        "Sachsen": "DED",
567
        "Hamburg": "DE6",
568
        "Saarland": "DEC",
569
        "Berlin": "DE3",
570
        "Bayern": "DE2",
571
        "Everything": "Nan",
572
    }
573
    gas_pipelines_list["NUTS1_0"] = [x[0] for x in gas_pipelines_list["NUTS1"]]
574
    gas_pipelines_list["NUTS1_1"] = [x[1] for x in gas_pipelines_list["NUTS1"]]
575
576
    boundary = settings()["egon-data"]["--dataset-boundary"]
577
578
    if boundary != "Everything":
579
580
        gas_pipelines_list = gas_pipelines_list[
581
            gas_pipelines_list["NUTS1_0"].str.contains(map_states[boundary])
582
            | gas_pipelines_list["NUTS1_1"].str.contains(map_states[boundary])
583
        ]
584
585
    # Add missing columns
586
    gas_pipelines_list["scn_name"] = scn_name
587
    gas_pipelines_list["carrier"] = main_gas_carrier
588
    gas_pipelines_list["p_nom_extendable"] = False
589
    gas_pipelines_list["p_min_pu"] = -1.0
590
591
    diameter = []
592
    geom = []
593
    topo = []
594
    length_km = []
595
596
    for index, row in gas_pipelines_list.iterrows():
597
598
        param = ast.literal_eval(row["param"])
599
        diameter.append(param["diameter_mm"])
600
        length_km.append(param["length_km"])
601
602
        long_e = json.loads(row["long"])
603
        lat_e = json.loads(row["lat"])
604
        crd_e = list(zip(long_e, lat_e))
605
        topo.append(geometry.LineString(crd_e))
606
607
        long_path = param["path_long"]
608
        lat_path = param["path_lat"]
609
        crd = list(zip(long_path, lat_path))
610
        crd.insert(0, crd_e[0])
611
        crd.append(crd_e[1])
612
        lines = []
613
        for i in range(len(crd) - 1):
614
            lines.append(geometry.LineString([crd[i], crd[i + 1]]))
615
        geom.append(geometry.MultiLineString(lines))
616
617
    gas_pipelines_list["diameter"] = diameter
618
    gas_pipelines_list["geom"] = geom
619
    gas_pipelines_list["topo"] = topo
620
    gas_pipelines_list["length_km"] = length_km
621
    gas_pipelines_list = gas_pipelines_list.set_geometry("geom", crs=4326)
622
623
    country_0 = []
624
    country_1 = []
625
    for index, row in gas_pipelines_list.iterrows():
626
        c = ast.literal_eval(row["country_code"])
627
        country_0.append(c[0])
628
        country_1.append(c[1])
629
630
    gas_pipelines_list["country_0"] = country_0
631
    gas_pipelines_list["country_1"] = country_1
632
633
    # Correct non valid neighbouring country nodes
634
    gas_pipelines_list.loc[
635
        gas_pipelines_list["country_0"] == "XX", "country_0"
636
    ] = "NO"
637
    gas_pipelines_list.loc[
638
        gas_pipelines_list["country_1"] == "FI", "country_1"
639
    ] = "RU"
640
    gas_pipelines_list.loc[
641
        gas_pipelines_list["id"] == "ST_2612_Seg_0_Seg_0", "country_0"
642
    ] = "AT"  # bus "INET_N_1182" DE -> AT
643
    gas_pipelines_list.loc[
644
        gas_pipelines_list["id"] == "INET_PL_385_EE_3_Seg_0_Seg_1", "country_1"
645
    ] = "AT"  # "INET_N_1182" DE -> AT
646
    gas_pipelines_list.loc[
647
        gas_pipelines_list["id"] == "LKD_PS_0_Seg_0_Seg_3", "country_0"
648
    ] = "NL"  # bus "SEQ_10608_p" DE -> NL
649
650
    # Remove uncorrect pipelines
651
    gas_pipelines_list = gas_pipelines_list[
652
        (gas_pipelines_list["id"] != "PLNG_2637_Seg_0_Seg_0_Seg_0")
653
        & (gas_pipelines_list["id"] != "NSG_6650_Seg_2_Seg_0")
654
        & (gas_pipelines_list["id"] != "NSG_6734_Seg_2_Seg_0")
655
    ]
656
657
    # Remove link test if length = 0
658
    gas_pipelines_list = gas_pipelines_list[
659
        gas_pipelines_list["length_km"] != 0
660
    ]
661
662
    # Adjust columns
663
    bus0 = []
664
    bus1 = []
665
    geom_adjusted = []
666
    topo_adjusted = []
667
    length_adjusted = []
668
    pipe_class = []
669
670
    for index, row in gas_pipelines_list.iterrows():
671
        buses = row["node_id"].strip("][").split(", ")
672
673
        if (
674
            (boundary != "Everything")
675
            & (row["NUTS1_0"] != map_states[boundary])
676
            & (row["country_0"] == "DE")
677
        ):
678
            bus0.append(abroad_gas_nodes_list.loc["DE", "bus_id"])
679
            bus1.append(gas_nodes_list.loc[buses[1][1:-1], "bus_id"])
680
            long_e = [
681
                abroad_gas_nodes_list.loc["DE", "x"],
682
                json.loads(row["long"])[1],
683
            ]
684
            lat_e = [
685
                abroad_gas_nodes_list.loc["DE", "y"],
686
                json.loads(row["lat"])[1],
687
            ]
688
            geom_pipe = geometry.MultiLineString(
689
                [geometry.LineString(list(zip(long_e, lat_e)))]
690
            )
691
            topo_adjusted.append(geometry.LineString(list(zip(long_e, lat_e))))
692
693
        elif row["country_0"] != "DE":
694
            country = str(row["country_0"])
695
            bus0.append(abroad_gas_nodes_list.loc[country, "bus_id"])
696
            bus1.append(gas_nodes_list.loc[buses[1][1:-1], "bus_id"])
697
            long_e = [
698
                abroad_gas_nodes_list.loc[country, "x"],
699
                json.loads(row["long"])[1],
700
            ]
701
            lat_e = [
702
                abroad_gas_nodes_list.loc[country, "y"],
703
                json.loads(row["lat"])[1],
704
            ]
705
            geom_pipe = geometry.MultiLineString(
706
                [geometry.LineString(list(zip(long_e, lat_e)))]
707
            )
708
            topo_adjusted.append(geometry.LineString(list(zip(long_e, lat_e))))
709
710
        elif (
711
            (boundary != "Everything")
712
            & (row["NUTS1_1"] != map_states[boundary])
713
            & (row["country_1"] == "DE")
714
        ):
715
            bus0.append(gas_nodes_list.loc[buses[0][1:-1], "bus_id"])
716
            bus1.append(abroad_gas_nodes_list.loc["DE", "bus_id"])
717
            long_e = [
718
                json.loads(row["long"])[0],
719
                abroad_gas_nodes_list.loc["DE", "x"],
720
            ]
721
            lat_e = [
722
                json.loads(row["lat"])[0],
723
                abroad_gas_nodes_list.loc["DE", "y"],
724
            ]
725
            geom_pipe = geometry.MultiLineString(
726
                [geometry.LineString(list(zip(long_e, lat_e)))]
727
            )
728
            topo_adjusted.append(geometry.LineString(list(zip(long_e, lat_e))))
729
730
        elif row["country_1"] != "DE":
731
            country = str(row["country_1"])
732
            bus0.append(gas_nodes_list.loc[buses[0][1:-1], "bus_id"])
733
            bus1.append(abroad_gas_nodes_list.loc[country, "bus_id"])
734
            long_e = [
735
                json.loads(row["long"])[0],
736
                abroad_gas_nodes_list.loc[country, "x"],
737
            ]
738
            lat_e = [
739
                json.loads(row["lat"])[0],
740
                abroad_gas_nodes_list.loc[country, "y"],
741
            ]
742
            geom_pipe = geometry.MultiLineString(
743
                [geometry.LineString(list(zip(long_e, lat_e)))]
744
            )
745
            topo_adjusted.append(geometry.LineString(list(zip(long_e, lat_e))))
746
747
        else:
748
            bus0.append(gas_nodes_list.loc[buses[0][1:-1], "bus_id"])
749
            bus1.append(gas_nodes_list.loc[buses[1][1:-1], "bus_id"])
750
            geom_pipe = row["geom"]
751
            topo_adjusted.append(row["topo"])
752
753
        geom_adjusted.append(geom_pipe)
754
        length_adjusted.append(geom_pipe.length)
755
756
        if row["diameter"] >= 1000:
757
            pipe_class = "A"
758
        elif 700 <= row["diameter"] <= 1000:
759
            pipe_class = "B"
760
        elif 500 <= row["diameter"] <= 700:
761
            pipe_class = "C"
762
        elif 350 <= row["diameter"] <= 500:
763
            pipe_class = "D"
764
        elif 200 <= row["diameter"] <= 350:
765
            pipe_class = "E"
766
        elif 100 <= row["diameter"] <= 200:
767
            pipe_class = "F"
768
        elif row["diameter"] <= 100:
769
            pipe_class = "G"
770
771
    gas_pipelines_list["bus0"] = bus0
772
    gas_pipelines_list["bus1"] = bus1
773
    gas_pipelines_list["geom"] = geom_adjusted
774
    gas_pipelines_list["topo"] = topo_adjusted
775
    gas_pipelines_list["length"] = length_adjusted
776
    gas_pipelines_list["pipe_class"] = pipe_class
777
778
    # Remove pipes having the same node for start and end
779
    gas_pipelines_list = gas_pipelines_list[
780
        gas_pipelines_list["bus0"] != gas_pipelines_list["bus1"]
781
    ]
782
783
    gas_pipelines_list = gas_pipelines_list.merge(
784
        classification,
785
        how="left",
786
        left_on="pipe_class",
787
        right_on="classification",
788
    )
789
    gas_pipelines_list["p_nom"] = gas_pipelines_list[
790
        "max_transport_capacity_Gwh/d"
791
    ] * (1000 / 24)
792
793
    # Remove useless columns
794
    gas_pipelines_list = gas_pipelines_list.drop(
795
        columns=[
796
            "id",
797
            "node_id",
798
            "param",
799
            "NUTS1",
800
            "NUTS1_0",
801
            "NUTS1_1",
802
            "country_code",
803
            "diameter",
804
            "pipe_class",
805
            "classification",
806
            "max_transport_capacity_Gwh/d",
807
            "lat",
808
            "long",
809
            "length_km",
810
        ]
811
    )
812
813
    return gas_pipelines_list
814
815
816
def insert_gas_pipeline_list(gas_pipelines_list, scn_name="eGon2035"):
817
    """
818
    Insert list of gas pipelines into the database
819
820
    Receive as argument a list of gas pipelines and insert them into the
821
    data base after cleaning it.
822
823
    Parameters
824
    ----------
825
    gas_pipelines_list : pandas.DataFrame
826
        Dataframe containing the gas pipelines in Germany
827
    scn_name : str
828
        Name of the scenario
829
830
    """
831
    main_gas_carrier = get_sector_parameters("gas", scenario=scn_name)[
832
        "main_gas_carrier"
833
    ]
834
    engine = db.engine()
835
    gas_pipelines_list = gas_pipelines_list.drop(
836
        columns=[
837
            "country_0",
838
            "country_1",
839
        ]
840
    )
841
842
    # Clean db
843
    db.execute_sql(
844
        f"""DELETE FROM grid.egon_etrago_link
845
        WHERE "carrier" = '{main_gas_carrier}'
846
        AND scn_name = '{scn_name}';
847
        """
848
    )
849
850
    print(gas_pipelines_list)
851
    # Insert data to db
852
    gas_pipelines_list.to_postgis(
853
        "egon_etrago_gas_link",
854
        engine,
855
        schema="grid",
856
        index=False,
857
        if_exists="replace",
858
        dtype={"geom": Geometry(), "topo": Geometry()},
859
    )
860
861
    db.execute_sql(
862
        """
863
    select UpdateGeometrySRID('grid', 'egon_etrago_gas_link', 'topo', 4326) ;
864
865
    INSERT INTO grid.egon_etrago_link (scn_name,
866
                                              link_id, carrier,
867
                                              bus0, bus1, p_min_pu,
868
                                              p_nom, p_nom_extendable, length,
869
                                              geom, topo)
870
    SELECT scn_name,
871
                link_id, carrier,
872
                bus0, bus1, p_min_pu,
873
                p_nom, p_nom_extendable, length,
874
                geom, topo
875
876
    FROM grid.egon_etrago_gas_link;
877
878
    DROP TABLE grid.egon_etrago_gas_link;
879
        """
880
    )
881
882
883
def remove_isolated_gas_buses():
884
    """
885
    Delete CH4 buses which are disconnected of the CH4 grid for the eGon2035 scenario
886
887
    This function deletes directly in the database and has no return.
888
889
    """
890
    targets = config.datasets()["gas_grid"]["targets"]
891
892
    db.execute_sql(
893
        f"""
894
        DELETE FROM {targets['buses']['schema']}.{targets['buses']['table']}
895
        WHERE "carrier" = 'CH4'
896
        AND scn_name = 'eGon2035'
897
        AND country = 'DE'
898
        AND "bus_id" NOT IN
899
            (SELECT bus0 FROM {targets['links']['schema']}.{targets['links']['table']}
900
            WHERE scn_name = 'eGon2035'
901
            AND carrier = 'CH4')
902
        AND "bus_id" NOT IN
903
            (SELECT bus1 FROM {targets['links']['schema']}.{targets['links']['table']}
904
            WHERE scn_name = 'eGon2035'
905
            AND carrier = 'CH4');
906
    """
907
    )
908
909
910
def insert_gas_data():
911
    """
912
    Overall function for importing methane data for eGon2035
913
914
    This function import the methane data (buses and pipelines) for
915
    eGon2035, by executing the following steps:
916
      * Download the SciGRID_gas datasets with the function :py:func:`download_SciGRID_gas_data`
917
      * Define CH4 buses with the function :py:func:`define_gas_nodes_list`
918
      * Insert the CH4 buses in Germany into the database with the
919
        function :py:func:`insert_CH4_nodes_list`
920
      * Insert the CH4 buses abroad into the database with the function
921
        :py:func:`insert_gas_buses_abroad`
922
      * Insert the CH4 links representing the CH4 pipeline into the
923
        database with the function :py:func:`insert_gas_pipeline_list`
924
      * Remove the isolated CH4 buses directly from the database using
925
        the function :py:func:`remove_isolated_gas_buses`
926
927
    This function inserts data into the database and has no return.
928
929
    """
930
    download_SciGRID_gas_data()
931
932
    gas_nodes_list = define_gas_nodes_list()
933
934
    insert_CH4_nodes_list(gas_nodes_list)
935
    abroad_gas_nodes_list = insert_gas_buses_abroad()
936
937
    gas_pipeline_list = define_gas_pipeline_list(
938
        gas_nodes_list, abroad_gas_nodes_list
939
    )
940
    insert_gas_pipeline_list(gas_pipeline_list)
941
    remove_isolated_gas_buses()
942
943
944
def insert_gas_data_eGon100RE():
945
    """
946
    Overall function for importing methane data for eGon100RE
947
948
    This function import the methane data (buses and pipelines) for
949
    eGon100RE, by copying the CH4 buses from the eGon2035 scenario using
950
    the function :py:func:`copy_and_modify_buses <egon.data.datasets.etrago_helpers.copy_and_modify_buses>`
951
    from the module :py:mod:`etrago_helpers <egon.data.datasets.etrago_helpers>`. The methane
952
    pipelines are also copied and their capacities are adapted: one
953
    share of the methane grid is retroffited into an hydrogen grid, so
954
    the methane pieplines nominal capacities are reduced from this share
955
    (calculated in the pyspa-eur-sec run).
956
957
    This function inserts data into the database and has no return.
958
959
    """
960
    # copy buses
961
    copy_and_modify_buses("eGon2035", "eGon100RE", {"carrier": ["CH4"]})
962
963
    # get CH4 pipelines and modify their nominal capacity with the
964
    # retrofitting factor
965
    gdf = db.select_geodataframe(
966
        f"""
967
        SELECT * FROM grid.egon_etrago_link
968
        WHERE carrier = 'CH4' AND scn_name = 'eGon2035' AND
969
        bus0 IN (
970
            SELECT bus_id FROM grid.egon_etrago_bus
971
            WHERE scn_name = 'eGon2035' AND country = 'DE'
972
        ) AND bus1 IN (
973
            SELECT bus_id FROM grid.egon_etrago_bus
974
            WHERE scn_name = 'eGon2035' AND country = 'DE'
975
        );
976
        """,
977
        epsg=4326,
978
        geom_col="topo",
979
    )
980
981
    # Update scenario specific information
982
    scn_name = "eGon100RE"
983
    gdf["scn_name"] = scn_name
984
    scn_params = get_sector_parameters("gas", scn_name)
985
986
    for param in ["capital_cost", "marginal_cost", "efficiency"]:
987
        try:
988
            gdf.loc[:, param] = scn_params[param]["CH4"]
989
        except KeyError:
990
            pass
991
992
    # remaining CH4 share is 1 - retroffited pipeline share
993
    gdf["p_nom"] *= (
994
        1 - scn_params["retrofitted_CH4pipeline-to-H2pipeline_share"]
995
    )
996
997
    # delete old entries
998
    db.execute_sql(
999
        f"""
1000
        DELETE FROM grid.egon_etrago_link
1001
        WHERE carrier = 'CH4' AND scn_name = '{scn_name}' AND
1002
        bus0 NOT IN (
1003
            SELECT bus_id FROM grid.egon_etrago_bus
1004
            WHERE scn_name = '{scn_name}' AND country != 'DE'
1005
        ) AND bus1 NOT IN (
1006
            SELECT bus_id FROM grid.egon_etrago_bus
1007
            WHERE scn_name = '{scn_name}' AND country != 'DE'
1008
        );
1009
        """
1010
    )
1011
1012
    gdf.to_postgis(
1013
        "egon_etrago_link",
1014
        schema="grid",
1015
        if_exists="append",
1016
        con=db.engine(),
1017
        index=False,
1018
        dtype={"geom": Geometry(), "topo": Geometry()},
1019
    )
1020