data.datasets.gas_grid.define_gas_pipeline_list()   F
last analyzed

Complexity

Conditions 25

Size

Total Lines 422
Code Lines 287

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 287
dl 0
loc 422
rs 0
c 0
b 0
f 0
cc 25
nop 3

How to fix   Long Method    Complexity   

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:

Complexity

Complex classes like data.datasets.gas_grid.define_gas_pipeline_list() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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