data.datasets.gas_grid.define_gas_buses_abroad()   B
last analyzed

Complexity

Conditions 4

Size

Total Lines 170
Code Lines 91

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 91
dl 0
loc 170
rs 7.2945
c 0
b 0
f 0
cc 4
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 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
            ],
375
            errors="ignore",
376
        )
377
        gdf_abroad_buses["scn_name"] = scn_name
378
        gdf_abroad_buses["carrier"] = gas_carrier
379
        gdf_abroad_buses["bus_id"] = range(
380
            new_id, new_id + len(gdf_abroad_buses)
381
        )
382
383
        # Add central bus in Russia
384
        gdf_abroad_buses = pd.concat(
385
            [
386
                gdf_abroad_buses,
387
                pd.DataFrame(
388
                    index=["RU"],
389
                    data={
390
                        "scn_name": scn_name,
391
                        "bus_id": (new_id + len(gdf_abroad_buses) + 1),
392
                        "x": 41,
393
                        "y": 55,
394
                        "country": "RU",
395
                        "carrier": gas_carrier,
396
                    },
397
                ),
398
            ],
399
            ignore_index=True,
400
        )
401
        # if in test mode, add bus in center of Germany
402
        boundary = settings()["egon-data"]["--dataset-boundary"]
403
404
        if boundary != "Everything":
405
            gdf_abroad_buses = pd.concat(
406
                [
407
                    gdf_abroad_buses,
408
                    pd.DataFrame(
409
                        index=[gdf_abroad_buses.index.max() + 1],
410
                        data={
411
                            "scn_name": scn_name,
412
                            "bus_id": (new_id + len(gdf_abroad_buses) + 1),
413
                            "x": 10.4234469,
414
                            "y": 51.0834196,
415
                            "country": "DE",
416
                            "carrier": gas_carrier,
417
                        },
418
                    ),
419
                ],
420
                ignore_index=True,
421
            )
422
423
        gdf_abroad_buses = geopandas.GeoDataFrame(
424
            gdf_abroad_buses,
425
            geometry=geopandas.points_from_xy(
426
                gdf_abroad_buses["x"], gdf_abroad_buses["y"]
427
            ),
428
        )
429
        gdf_abroad_buses = gdf_abroad_buses.rename(
430
            columns={"geometry": "geom"}
431
        ).set_geometry("geom", crs=4326)
432
433
    return gdf_abroad_buses
434
435
436
def insert_gas_buses_abroad(scn_name="eGon2035"):
437
    """
438
    Insert CH4 buses in neighbouring countries into database for eGon2035
439
440
    * Definition of the CH4 buses abroad with the function
441
      :py:func:`define_gas_buses_abroad`
442
    * Cleaning of the database table grid.egon_etrago_bus of the
443
      foreign CH4 buses of the specific scenario (eGon2035)
444
    * Insertion of the neighbouring buses into the table grid.egon_etrago_bus.
445
446
    Parameters
447
    ----------
448
    scn_name : str
449
        Name of the scenario
450
451
    Returns
452
    -------
453
    gdf_abroad_buses : dataframe
454
        Dataframe containing the CH4 buses in the neighbouring countries
455
        and one in the center of Germany in test mode
456
457
    """
458
    gas_carrier = "CH4"
459
460
    # Connect to local database
461
    engine = db.engine()
462
463
    gdf_abroad_buses = define_gas_buses_abroad(scn_name)
464
465
    print(gdf_abroad_buses)
466
467
    # Insert to db
468
    if scn_name == "eGon100RE":
469
        gdf_abroad_buses[gdf_abroad_buses["country"] == "DE"].to_postgis(
470
            "egon_etrago_bus",
471
            engine,
472
            schema="grid",
473
            index=False,
474
            if_exists="append",
475
            dtype={"geom": Geometry()},
476
        )
477
478
    else:
479
        db.execute_sql(
480
            f"""
481
        DELETE FROM grid.egon_etrago_bus WHERE "carrier" = '{gas_carrier}' AND
482
        scn_name = '{scn_name}' AND country != 'DE';
483
        """
484
        )
485
        gdf_abroad_buses.to_postgis(
486
            "egon_etrago_bus",
487
            engine,
488
            schema="grid",
489
            index=False,
490
            if_exists="append",
491
            dtype={"geom": Geometry()},
492
        )
493
    return gdf_abroad_buses
494
495
496
def define_gas_pipeline_list(
497
    gas_nodes_list, abroad_gas_nodes_list, scn_name="eGon2035"
498
):
499
    """
500
    Define gas pipelines in Germany from SciGRID_gas IGGIELGN data
501
502
    The gas pipelines, modelled as PyPSA links are read from the IGGIELGN_PipeSegments
503
    csv file previously downloded in the function :py:func:`download_SciGRID_gas_data`.
504
505
    The capacities of the pipelines are determined by the correspondance
506
    table given by the parameters for the classification of gas pipelines
507
    in `Electricity, heat, and gas sector data for modeling the German system
508
    <https://www.econstor.eu/bitstream/10419/173388/1/1011162628.pdf>`_
509
    related to the pipeline diameter given in the SciGRID_gas dataset.
510
511
    The manual corrections allow to:
512
513
    * Delete gas pipelines disconnected of the rest of the gas grid
514
    * Connect one pipeline (also connected to Norway) disconnected of
515
      the rest of the gas grid
516
    * Correct countries of some erroneous pipelines
517
518
    Parameters
519
    ----------
520
    gas_nodes_list : dataframe
521
        Dataframe containing the gas nodes in Europe
522
    abroad_gas_nodes_list: dataframe
523
        Dataframe containing the gas buses in the neighbouring countries
524
        and one in the center of Germany in test mode
525
    scn_name : str
526
        Name of the scenario
527
528
    Returns
529
    -------
530
    gas_pipelines_list : pandas.DataFrame
531
        Dataframe containing the gas pipelines in Germany
532
533
    """
534
    scn_params = get_sector_parameters("gas", scn_name)
535
536
    abroad_gas_nodes_list = abroad_gas_nodes_list.set_index("country")
537
538
    gas_carrier = "CH4"
539
540
    # Select next id value
541
    new_id = db.next_etrago_id("link")
542
543
    classifiaction_file = (
544
        Path(".")
545
        / "data_bundle_egon_data"
546
        / "pipeline_classification_gas"
547
        / "pipeline_classification.csv"
548
    )
549
550
    classification = pd.read_csv(
551
        classifiaction_file,
552
        delimiter=",",
553
        usecols=["classification", "max_transport_capacity_Gwh/d"],
554
    )
555
556
    target_file = (
557
        Path(".")
558
        / "datasets"
559
        / "gas_data"
560
        / "data"
561
        / "IGGIELGN_PipeSegments.csv"
562
    )
563
564
    gas_pipelines_list = pd.read_csv(
565
        target_file,
566
        delimiter=";",
567
        decimal=".",
568
        usecols=["id", "node_id", "lat", "long", "country_code", "param"],
569
    )
570
571
    # Correct some country codes (also changed in define_gas_nodes_list())
572
    gas_pipelines_list["bus0"] = gas_pipelines_list["node_id"].apply(
573
        lambda x: x.split(",")[0]
574
    )
575
    gas_pipelines_list["bus1"] = gas_pipelines_list["node_id"].apply(
576
        lambda x: x.split(",")[1]
577
    )
578
    gas_pipelines_list["country0"] = gas_pipelines_list["country_code"].apply(
579
        lambda x: x.split(",")[0]
580
    )
581
    gas_pipelines_list["country1"] = gas_pipelines_list["country_code"].apply(
582
        lambda x: x.split(",")[1]
583
    )
584
585
    gas_pipelines_list.loc[
586
        gas_pipelines_list["bus0"].str.contains("INET_N_1182"), "country0"
587
    ] = "['AT'"
588
    gas_pipelines_list.loc[
589
        gas_pipelines_list["bus1"].str.contains("INET_N_1182"), "country1"
590
    ] = "'AT']"
591
    gas_pipelines_list.loc[
592
        gas_pipelines_list["bus0"].str.contains("SEQ_10608_p"), "country0"
593
    ] = "['NL'"
594
    gas_pipelines_list.loc[
595
        gas_pipelines_list["bus1"].str.contains("SEQ_10608_p"), "country1"
596
    ] = "'NL']"
597
    gas_pipelines_list.loc[
598
        gas_pipelines_list["bus0"].str.contains("N_88_NS_LMGN"), "country0"
599
    ] = "['XX'"
600
    gas_pipelines_list.loc[
601
        gas_pipelines_list["bus1"].str.contains("N_88_NS_LMGN"), "country1"
602
    ] = "'XX']"
603
604
    gas_pipelines_list["country_code"] = gas_pipelines_list.apply(
605
        lambda x: x["country0"] + "," + x["country1"], axis=1
606
    )
607
    gas_pipelines_list.drop(
608
        columns=["bus0", "bus1", "country0", "country1"], inplace=True
609
    )
610
611
    # Select the links having at least one bus in Germany
612
    gas_pipelines_list = gas_pipelines_list[
613
        gas_pipelines_list["country_code"].str.contains("DE")
614
    ]
615
    # Remove links disconnected of the rest of the grid
616
    # Remove manually for disconnected link EntsoG_Map__ST_195 and EntsoG_Map__ST_108
617
    gas_pipelines_list = gas_pipelines_list[
618
        gas_pipelines_list["node_id"] != "['SEQ_11790_p', 'Stor_EU_107']"
619
    ]
620
    gas_pipelines_list = gas_pipelines_list[
621
        ~gas_pipelines_list["id"].str.match("EntsoG_Map__ST_108")
622
    ]
623
624
    # Manually add pipeline to artificially connect isolated pipeline
625
    gas_pipelines_list.at["new_pipe", "param"] = gas_pipelines_list[
626
        gas_pipelines_list["id"] == "NO_PS_8_Seg_0_Seg_23"
627
    ]["param"].values[0]
628
    gas_pipelines_list.at["new_pipe", "node_id"] = (
629
        "['SEQ_12442_p', 'LKD_N_200']"
630
    )
631
    gas_pipelines_list.at["new_pipe", "lat"] = "[53.358536, 53.412719]"
632
    gas_pipelines_list.at["new_pipe", "long"] = "[7.041677, 7.093251]"
633
    gas_pipelines_list.at["new_pipe", "country_code"] = "['DE', 'DE']"
634
635
    gas_pipelines_list["link_id"] = range(
636
        new_id, new_id + len(gas_pipelines_list)
637
    )
638
    gas_pipelines_list["link_id"] = gas_pipelines_list["link_id"].astype(int)
639
640
    # Cut data to federal state if in testmode
641
    NUTS1 = []
642
    for index, row in gas_pipelines_list.iterrows():
643
        param = ast.literal_eval(row["param"])
644
        NUTS1.append(param["nuts_id_1"])
645
    gas_pipelines_list["NUTS1"] = NUTS1
646
647
    map_states = {
648
        "Baden-Württemberg": "DE1",
649
        "Nordrhein-Westfalen": "DEA",
650
        "Hessen": "DE7",
651
        "Brandenburg": "DE4",
652
        "Bremen": "DE5",
653
        "Rheinland-Pfalz": "DEB",
654
        "Sachsen-Anhalt": "DEE",
655
        "Schleswig-Holstein": "DEF",
656
        "Mecklenburg-Vorpommern": "DE8",
657
        "Thüringen": "DEG",
658
        "Niedersachsen": "DE9",
659
        "Sachsen": "DED",
660
        "Hamburg": "DE6",
661
        "Saarland": "DEC",
662
        "Berlin": "DE3",
663
        "Bayern": "DE2",
664
        "Everything": "Nan",
665
    }
666
    gas_pipelines_list["NUTS1_0"] = [x[0] for x in gas_pipelines_list["NUTS1"]]
667
    gas_pipelines_list["NUTS1_1"] = [x[1] for x in gas_pipelines_list["NUTS1"]]
668
669
    boundary = settings()["egon-data"]["--dataset-boundary"]
670
671
    if boundary != "Everything":
672
        gas_pipelines_list = gas_pipelines_list[
673
            gas_pipelines_list["NUTS1_0"].str.contains(map_states[boundary])
674
            | gas_pipelines_list["NUTS1_1"].str.contains(map_states[boundary])
675
        ]
676
677
    # Add missing columns
678
    gas_pipelines_list["scn_name"] = scn_name
679
    gas_pipelines_list["carrier"] = gas_carrier
680
    gas_pipelines_list["p_nom_extendable"] = False
681
    gas_pipelines_list["p_min_pu"] = -1.0
682
683
    diameter = []
684
    geom = []
685
    topo = []
686
    length_km = []
687
688
    for index, row in gas_pipelines_list.iterrows():
689
        param = ast.literal_eval(row["param"])
690
        diameter.append(param["diameter_mm"])
691
        length_km.append(param["length_km"])
692
693
        long_e = json.loads(row["long"])
694
        lat_e = json.loads(row["lat"])
695
        crd_e = list(zip(long_e, lat_e))
696
        topo.append(geometry.LineString(crd_e))
697
698
        long_path = param["path_long"]
699
        lat_path = param["path_lat"]
700
        crd = list(zip(long_path, lat_path))
701
        crd.insert(0, crd_e[0])
702
        crd.append(crd_e[1])
703
        lines = []
704
        for i in range(len(crd) - 1):
705
            lines.append(geometry.LineString([crd[i], crd[i + 1]]))
706
        geom.append(geometry.MultiLineString(lines))
707
708
    gas_pipelines_list["diameter"] = diameter
709
    gas_pipelines_list["geom"] = geom
710
    gas_pipelines_list["topo"] = topo
711
    gas_pipelines_list["length_km"] = length_km
712
    gas_pipelines_list = gas_pipelines_list.set_geometry("geom", crs=4326)
713
714
    country_0 = []
715
    country_1 = []
716
    for index, row in gas_pipelines_list.iterrows():
717
        c = ast.literal_eval(row["country_code"])
718
        country_0.append(c[0])
719
        country_1.append(c[1])
720
721
    gas_pipelines_list["country_0"] = country_0
722
    gas_pipelines_list["country_1"] = country_1
723
724
    # Correct non valid neighbouring country nodes
725
    gas_pipelines_list.loc[
726
        gas_pipelines_list["country_0"] == "XX", "country_0"
727
    ] = "NO"
728
    gas_pipelines_list.loc[
729
        gas_pipelines_list["country_1"] == "FI", "country_1"
730
    ] = "RU"
731
    gas_pipelines_list.loc[
732
        gas_pipelines_list["id"] == "ST_2612_Seg_0_Seg_0", "country_0"
733
    ] = "AT"  # bus "INET_N_1182" DE -> AT
734
    gas_pipelines_list.loc[
735
        gas_pipelines_list["id"] == "INET_PL_385_EE_3_Seg_0_Seg_1", "country_1"
736
    ] = "AT"  # "INET_N_1182" DE -> AT
737
    gas_pipelines_list.loc[
738
        gas_pipelines_list["id"] == "LKD_PS_0_Seg_0_Seg_3", "country_0"
739
    ] = "NL"  # bus "SEQ_10608_p" DE -> NL
740
741
    if scn_name == "eGon100RE":
742
        gas_pipelines_list = gas_pipelines_list[
743
            gas_pipelines_list["country_1"] != "RU"
744
        ]
745
746
    # Remove uncorrect pipelines
747
    gas_pipelines_list = gas_pipelines_list[
748
        (gas_pipelines_list["id"] != "PLNG_2637_Seg_0_Seg_0_Seg_0")
749
        & (gas_pipelines_list["id"] != "NSG_6650_Seg_2_Seg_0")
750
        & (gas_pipelines_list["id"] != "NSG_6734_Seg_2_Seg_0")
751
    ]
752
753
    # Remove link test if length = 0
754
    gas_pipelines_list = gas_pipelines_list[
755
        gas_pipelines_list["length_km"] != 0
756
    ]
757
758
    # Adjust columns
759
    bus0 = []
760
    bus1 = []
761
    geom_adjusted = []
762
    topo_adjusted = []
763
    length_adjusted = []
764
    pipe_class = []
765
766
    for index, row in gas_pipelines_list.iterrows():
767
        buses = row["node_id"].strip("][").split(", ")
768
769
        if (
770
            (boundary != "Everything")
771
            & (row["NUTS1_0"] != map_states[boundary])
772
            & (row["country_0"] == "DE")
773
        ):
774
            bus0.append(abroad_gas_nodes_list.loc["DE", "bus_id"])
775
            bus1.append(gas_nodes_list.loc[buses[1][1:-1], "bus_id"])
776
            long_e = [
777
                abroad_gas_nodes_list.loc["DE", "x"],
778
                json.loads(row["long"])[1],
779
            ]
780
            lat_e = [
781
                abroad_gas_nodes_list.loc["DE", "y"],
782
                json.loads(row["lat"])[1],
783
            ]
784
            geom_pipe = geometry.MultiLineString(
785
                [geometry.LineString(list(zip(long_e, lat_e)))]
786
            )
787
            topo_adjusted.append(geometry.LineString(list(zip(long_e, lat_e))))
788
789
        elif row["country_0"] != "DE":
790
            country = str(row["country_0"])
791
            bus0.append(abroad_gas_nodes_list.loc[country, "bus_id"])
792
            bus1.append(gas_nodes_list.loc[buses[1][1:-1], "bus_id"])
793
            long_e = [
794
                abroad_gas_nodes_list.loc[country, "x"],
795
                json.loads(row["long"])[1],
796
            ]
797
            lat_e = [
798
                abroad_gas_nodes_list.loc[country, "y"],
799
                json.loads(row["lat"])[1],
800
            ]
801
            geom_pipe = geometry.MultiLineString(
802
                [geometry.LineString(list(zip(long_e, lat_e)))]
803
            )
804
            topo_adjusted.append(geometry.LineString(list(zip(long_e, lat_e))))
805
806
        elif (
807
            (boundary != "Everything")
808
            & (row["NUTS1_1"] != map_states[boundary])
809
            & (row["country_1"] == "DE")
810
        ):
811
            bus0.append(gas_nodes_list.loc[buses[0][1:-1], "bus_id"])
812
            bus1.append(abroad_gas_nodes_list.loc["DE", "bus_id"])
813
            long_e = [
814
                json.loads(row["long"])[0],
815
                abroad_gas_nodes_list.loc["DE", "x"],
816
            ]
817
            lat_e = [
818
                json.loads(row["lat"])[0],
819
                abroad_gas_nodes_list.loc["DE", "y"],
820
            ]
821
            geom_pipe = geometry.MultiLineString(
822
                [geometry.LineString(list(zip(long_e, lat_e)))]
823
            )
824
            topo_adjusted.append(geometry.LineString(list(zip(long_e, lat_e))))
825
826
        elif row["country_1"] != "DE":
827
            country = str(row["country_1"])
828
            bus0.append(gas_nodes_list.loc[buses[0][1:-1], "bus_id"])
829
            bus1.append(abroad_gas_nodes_list.loc[country, "bus_id"])
830
            long_e = [
831
                json.loads(row["long"])[0],
832
                abroad_gas_nodes_list.loc[country, "x"],
833
            ]
834
            lat_e = [
835
                json.loads(row["lat"])[0],
836
                abroad_gas_nodes_list.loc[country, "y"],
837
            ]
838
            geom_pipe = geometry.MultiLineString(
839
                [geometry.LineString(list(zip(long_e, lat_e)))]
840
            )
841
            topo_adjusted.append(geometry.LineString(list(zip(long_e, lat_e))))
842
843
        else:
844
            bus0.append(gas_nodes_list.loc[buses[0][1:-1], "bus_id"])
845
            bus1.append(gas_nodes_list.loc[buses[1][1:-1], "bus_id"])
846
            geom_pipe = row["geom"]
847
            topo_adjusted.append(row["topo"])
848
849
        geom_adjusted.append(geom_pipe)
850
        length_adjusted.append(geom_pipe.length)
851
852
        if row["diameter"] >= 1000:
853
            pipe_class = "A"
854
        elif 700 <= row["diameter"] <= 1000:
855
            pipe_class = "B"
856
        elif 500 <= row["diameter"] <= 700:
857
            pipe_class = "C"
858
        elif 350 <= row["diameter"] <= 500:
859
            pipe_class = "D"
860
        elif 200 <= row["diameter"] <= 350:
861
            pipe_class = "E"
862
        elif 100 <= row["diameter"] <= 200:
863
            pipe_class = "F"
864
        elif row["diameter"] <= 100:
865
            pipe_class = "G"
866
867
    gas_pipelines_list["bus0"] = bus0
868
    gas_pipelines_list["bus1"] = bus1
869
    gas_pipelines_list["geom"] = geom_adjusted
870
    gas_pipelines_list["topo"] = topo_adjusted
871
    gas_pipelines_list["length"] = length_adjusted
872
    gas_pipelines_list["pipe_class"] = pipe_class
873
874
    # Remove pipes having the same node for start and end
875
    gas_pipelines_list = gas_pipelines_list[
876
        gas_pipelines_list["bus0"] != gas_pipelines_list["bus1"]
877
    ]
878
879
    gas_pipelines_list = gas_pipelines_list.merge(
880
        classification,
881
        how="left",
882
        left_on="pipe_class",
883
        right_on="classification",
884
    )
885
    gas_pipelines_list["p_nom"] = gas_pipelines_list[
886
        "max_transport_capacity_Gwh/d"
887
    ] * (1000 / 24)
888
889
    if scn_name == "eGon100RE":
890
        # remaining CH4 share is 1 - retroffited pipeline share
891
        gas_pipelines_list["p_nom"] *= (
892
            1 - scn_params["retrofitted_CH4pipeline-to-H2pipeline_share"]
893
        )
894
895
    # Remove useless columns
896
    gas_pipelines_list = gas_pipelines_list.drop(
897
        columns=[
898
            "id",
899
            "node_id",
900
            "param",
901
            "NUTS1",
902
            "NUTS1_0",
903
            "NUTS1_1",
904
            "country_code",
905
            "country_0",
906
            "country_1",
907
            "diameter",
908
            "pipe_class",
909
            "classification",
910
            "max_transport_capacity_Gwh/d",
911
            "lat",
912
            "long",
913
            "length_km",
914
        ]
915
    )
916
917
    return gas_pipelines_list
918
919
920
def insert_gas_pipeline_list(gas_pipelines_list, scn_name="eGon2035"):
921
    """
922
    Insert list of gas pipelines into the database
923
924
    Receive as argument a list of gas pipelines and insert them into the
925
    database after cleaning it.
926
927
    Parameters
928
    ----------
929
    gas_pipelines_list : pandas.DataFrame
930
        Dataframe containing the gas pipelines in Germany
931
    scn_name : str
932
        Name of the scenario
933
934
    Returns
935
    -------
936
    None
937
938
    """
939
    gas_carrier = "CH4"
940
941
    engine = db.engine()
942
943
    # Clean db
944
    db.execute_sql(
945
        f"""DELETE FROM grid.egon_etrago_link
946
        WHERE "carrier" = '{gas_carrier}'
947
        AND scn_name = '{scn_name}'
948
        AND link_id IN(
949
            SELECT link_id FROM grid.egon_etrago_link
950
            WHERE bus0 IN (
951
                SELECT bus_id FROM grid.egon_etrago_bus
952
                WHERE country = 'DE'
953
                AND scn_name = '{scn_name}'
954
                )
955
            AND bus1 IN (
956
                SELECT bus_id FROM grid.egon_etrago_bus
957
                WHERE country = 'DE'
958
                AND scn_name = '{scn_name}'
959
                )
960
            )
961
        """
962
    )
963
964
    print(gas_pipelines_list)
965
    # Insert data to db
966
    gas_pipelines_list.to_postgis(
967
        "egon_etrago_gas_link",
968
        engine,
969
        schema="grid",
970
        index=False,
971
        if_exists="replace",
972
        dtype={"geom": Geometry(), "topo": Geometry()},
973
    )
974
975
    db.execute_sql(
976
        """
977
    select UpdateGeometrySRID('grid', 'egon_etrago_gas_link', 'topo', 4326) ;
978
979
    INSERT INTO grid.egon_etrago_link (scn_name,
980
                                              link_id, carrier,
981
                                              bus0, bus1, p_min_pu,
982
                                              p_nom, p_nom_extendable, length,
983
                                              geom, topo)
984
    SELECT scn_name,
985
                link_id, carrier,
986
                bus0, bus1, p_min_pu,
987
                p_nom, p_nom_extendable, length,
988
                geom, topo
989
990
    FROM grid.egon_etrago_gas_link;
991
992
    DROP TABLE grid.egon_etrago_gas_link;
993
        """
994
    )
995
996
997
def remove_isolated_gas_buses(scn_name="eGon2035"):
998
    """
999
    Delete CH4 buses which are disconnected of the CH4 grid for the required
1000
    scenario
1001
1002
    Returns
1003
    -------
1004
    None
1005
1006
    """
1007
    targets = config.datasets()["gas_grid"]["targets"]
1008
1009
    db.execute_sql(
1010
        f"""
1011
        DELETE FROM {targets['buses']['schema']}.{targets['buses']['table']}
1012
        WHERE "carrier" = 'CH4'
1013
        AND scn_name = '{scn_name}'
1014
        AND country = 'DE'
1015
        AND "bus_id" NOT IN
1016
            (SELECT bus0 FROM {targets['links']['schema']}.{targets['links']['table']}
1017
            WHERE scn_name = '{scn_name}'
1018
            AND carrier = 'CH4')
1019
        AND "bus_id" NOT IN
1020
            (SELECT bus1 FROM {targets['links']['schema']}.{targets['links']['table']}
1021
            WHERE scn_name = '{scn_name}'
1022
            AND carrier = 'CH4');
1023
    """
1024
    )
1025
1026
1027
def insert_gas_data():
1028
    """
1029
    Overall function for importing methane data for all the scenarios in the
1030
    configuration file.
1031
1032
    This function imports the methane data (buses and pipelines) for
1033
    each required scenario, by executing the following steps:
1034
      * Download the SciGRID_gas datasets with the function :py:func:`download_SciGRID_gas_data`
1035
      * Define CH4 buses with the function :py:func:`define_gas_nodes_list`
1036
      * Insert the CH4 buses in Germany into the database with the
1037
        function :py:func:`insert_CH4_nodes_list`
1038
      * Insert the CH4 buses abroad into the database with the function
1039
        :py:func:`insert_gas_buses_abroad`
1040
      * Insert the CH4 links representing the CH4 pipeline into the
1041
        database with the function :py:func:`insert_gas_pipeline_list`
1042
      * Remove the isolated CH4 buses directly from the database using
1043
        the function :py:func:`remove_isolated_gas_buses`
1044
1045
    Returns
1046
    -------
1047
    None
1048
1049
    """
1050
    s = config.settings()["egon-data"]["--scenarios"]
1051
    scenarios = []
1052
    if "eGon2035" in s:
1053
        scenarios.append("eGon2035")
1054
    if "eGon100RE" in s:
1055
        scenarios.append("eGon100RE")
1056
1057
    download_SciGRID_gas_data()
1058
1059
    for scn_name in scenarios:
1060
        gas_nodes_list = define_gas_nodes_list()
1061
1062
        insert_CH4_nodes_list(gas_nodes_list, scn_name=scn_name)
1063
        abroad_gas_nodes_list = insert_gas_buses_abroad(scn_name=scn_name)
1064
1065
        gas_pipeline_list = define_gas_pipeline_list(
1066
            gas_nodes_list, abroad_gas_nodes_list, scn_name=scn_name
1067
        )
1068
        insert_gas_pipeline_list(gas_pipeline_list, scn_name=scn_name)
1069
1070
        remove_isolated_gas_buses(scn_name=scn_name)
1071
1072
1073
def insert_gas_data_status(scn_name):
1074
    """
1075
    Function to deal with the gas network for the status2019 scenario.
1076
    For this scenario just one CH4 bus is consider in the center of Germany.
1077
    Since OCGTs in the foreign countries are modelled as generators and not
1078
    as links between the gas and electricity sectors, CH4 foreign buses are
1079
    considered not necessary.
1080
1081
    This function does not require any input.
1082
1083
    Returns
1084
    -------
1085
    None.
1086
1087
    """
1088
1089
    # delete old entries
1090
    db.execute_sql(
1091
        f"""
1092
        DELETE FROM grid.egon_etrago_link
1093
        WHERE carrier = 'CH4' AND scn_name = '{scn_name}'
1094
        """
1095
    )
1096
    db.execute_sql(
1097
        f"""
1098
        DELETE FROM grid.egon_etrago_bus
1099
        WHERE carrier = 'CH4' AND scn_name = '{scn_name}'
1100
        """
1101
    )
1102
1103
    # Select next id value
1104
    new_id = db.next_etrago_id("bus")
1105
1106
    df = pd.DataFrame(
1107
        index=[new_id],
1108
        data={
1109
            "scn_name": scn_name,
1110
            "v_nom": 1,
1111
            "carrier": "CH4",
1112
            "v_mag_pu_set": 1,
1113
            "v_mag_pu_min": 0,
1114
            "v_mag_pu_max": np.inf,
1115
            "x": 10,
1116
            "y": 51,
1117
            "country": "DE",
1118
        },
1119
    )
1120
    gdf = geopandas.GeoDataFrame(
1121
        df, geometry=geopandas.points_from_xy(df.x, df.y, crs=4326)
1122
    ).rename_geometry("geom")
1123
1124
    gdf.index.name = "bus_id"
1125
1126
    gdf.reset_index().to_postgis(
1127
        "egon_etrago_bus", schema="grid", con=db.engine(), if_exists="append"
1128
    )
1129
1130
1131
class GasNodesAndPipes(Dataset):
1132
    """
1133
    Insert the CH4 buses and links into the database.
1134
1135
    Insert the CH4 buses and links, which for the case of gas represent
1136
    pipelines, into the database for the scenarios status2019, eGon2035 and eGon100RE
1137
    with the functions :py:func:`insert_gas_data` and :py:func:`insert_gas_data_eGon100RE`.
1138
1139
    *Dependencies*
1140
      * :py:class:`DataBundle <egon.data.datasets.data_bundle.DataBundle>`
1141
      * :py:class:`ElectricalNeighbours <egon.data.datasets.electrical_neighbours.ElectricalNeighbours>`
1142
      * :py:class:`Osmtgmod <egon.data.datasets.osmtgmod.Osmtgmod>`
1143
      * :py:class:`ScenarioParameters <egon.data.datasets.scenario_parameters.ScenarioParameters>`
1144
      * :py:class:`EtragoSetup <egon.data.datasets.etrago_setup.EtragoSetup>` (more specifically the :func:`create_tables <egon.data.datasets.etrago_setup.create_tables>` task)
1145
1146
    *Resulting tables*
1147
      * :py:class:`grid.egon_etrago_bus <egon.data.datasets.etrago_setup.EgonPfHvBus>` is extended
1148
      * :py:class:`grid.egon_etrago_link <egon.data.datasets.etrago_setup.EgonPfHvLink>` is extended
1149
1150
    """
1151
1152
    #:
1153
    name: str = "GasNodesAndPipes"
1154
    #:
1155
    version: str = "0.0.11"
1156
1157
    tasks = ()
1158
1159
    for scn_name in config.settings()["egon-data"]["--scenarios"]:
1160
        if "status" in scn_name:
1161
            tasks += (
1162
                wrapped_partial(
1163
                    insert_gas_data_status,
1164
                    scn_name=scn_name,
1165
                    postfix=f"_{scn_name[-4:]}",
1166
                ),
1167
            )
1168
1169
    tasks += (insert_gas_data,)
1170
1171
    def __init__(self, dependencies):
1172
        super().__init__(
1173
            name=self.name,
1174
            version=self.version,
1175
            dependencies=dependencies,
1176
            tasks=self.tasks,
1177
        )
1178