data.datasets.gas_grid.insert_gas_data_status()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 53
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 23
dl 0
loc 53
rs 9.328
c 0
b 0
f 0
cc 1
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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