Passed
Pull Request — dev (#1281)
by
unknown
02:02
created

data.datasets.gas_grid   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 1172
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 49
eloc 597
dl 0
loc 1172
rs 8.48
c 0
b 0
f 0

11 Functions

Rating   Name   Duplication   Size   Complexity  
A ch4_nodes_number_G() 0 22 1
A insert_gas_pipeline_list() 0 60 1
B insert_CH4_nodes_list() 0 99 3
A insert_gas_data() 0 42 4
B download_SciGRID_gas_data() 0 48 6
A insert_gas_data_status() 0 55 1
A insert_gas_buses_abroad() 0 46 1
A define_gas_nodes_list() 0 46 1
A remove_isolated_gas_buses() 0 14 1
F define_gas_pipeline_list() 0 420 25
B define_gas_buses_abroad() 0 182 4

1 Method

Rating   Name   Duplication   Size   Complexity  
A GasNodesAndPipes.__init__() 0 6 1

How to fix   Complexity   

Complexity

Complex classes like data.datasets.gas_grid 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": "eGon2035", "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": (db.next_etrago_id("bus") + len(gdf_abroad_buses) + 1),
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
            # Insert to db
339
            print(gdf_abroad_buses_insert)
340
            gdf_abroad_buses_insert.to_postgis(
341
                "egon_etrago_bus",
342
                engine,
343
                schema="grid",
344
                index=False,
345
                if_exists="append",
346
                dtype={"geom": Geometry()},
347
            )
348
349
            gdf_abroad_buses = pd.concat(
350
                [
351
                    gdf_abroad_buses,
352
                    gdf_abroad_buses_insert
353
                ],
354
                ignore_index=True,
355
            )
356
357
        return gdf_abroad_buses
358
359
    else:
360
        db.execute_sql(
361
            f"""
362
        DELETE FROM grid.egon_etrago_bus WHERE "carrier" = '{gas_carrier}' AND
363
        scn_name = '{scn_name}' AND country != 'DE';
364
        """
365
        )
366
367
        # Select the foreign buses
368
        gdf_abroad_buses = central_buses_pypsaeur(sources, scenario=scn_name)
369
        gdf_abroad_buses = gdf_abroad_buses.drop_duplicates(subset=["country"])
370
371
        # Select next id value
372
        new_id = db.next_etrago_id("bus")
373
374
        gdf_abroad_buses = gdf_abroad_buses.drop(
375
            columns=[
376
                "v_nom",
377
                "v_mag_pu_set",
378
                "v_mag_pu_min",
379
                "v_mag_pu_max",
380
                "geom",
381
                "control",
382
                "generator",
383
                "location",
384
                "unit",
385
                "sub_network",
386
            ],
387
            errors="ignore",
388
        )
389
        gdf_abroad_buses["scn_name"] = scn_name
390
        gdf_abroad_buses["carrier"] = gas_carrier
391
        gdf_abroad_buses["bus_id"] = range(
392
            new_id, new_id + len(gdf_abroad_buses)
393
        )
394
395
        # Add central bus in Russia
396
        gdf_abroad_buses = pd.concat(
397
            [
398
                gdf_abroad_buses,
399
                pd.DataFrame(
400
                    index=["RU"],
401
                    data={
402
                        "scn_name": scn_name,
403
                        "bus_id": (new_id + len(gdf_abroad_buses) + 1),
404
                        "x": 41,
405
                        "y": 55,
406
                        "country": "RU",
407
                        "carrier": gas_carrier,
408
                    },
409
                ),
410
            ],
411
            ignore_index=True,
412
        )
413
        # if in test mode, add bus in center of Germany
414
        boundary = settings()["egon-data"]["--dataset-boundary"]
415
416
        if boundary != "Everything":
417
            gdf_abroad_buses = pd.concat(
418
                [
419
                    gdf_abroad_buses,
420
                    pd.DataFrame(
421
                        index=[gdf_abroad_buses.index.max() + 1],
422
                        data={
423
                            "scn_name": scn_name,
424
                            "bus_id": (new_id + len(gdf_abroad_buses) + 1),
425
                            "x": 10.4234469,
426
                            "y": 51.0834196,
427
                            "country": "DE",
428
                            "carrier": gas_carrier,
429
                        },
430
                    ),
431
                ],
432
                ignore_index=True,
433
            )
434
435
        gdf_abroad_buses = geopandas.GeoDataFrame(
436
            gdf_abroad_buses,
437
            geometry=geopandas.points_from_xy(
438
                gdf_abroad_buses["x"], gdf_abroad_buses["y"]
439
            ),
440
        )
441
        gdf_abroad_buses = gdf_abroad_buses.rename(
442
            columns={"geometry": "geom"}
443
        ).set_geometry("geom", crs=4326)
444
445
    return gdf_abroad_buses
446
447
448
def insert_gas_buses_abroad(scn_name="eGon2035"):
449
    """
450
    Insert CH4 buses in neighbouring countries into database for eGon2035
451
452
    * Definition of the CH4 buses abroad with the function
453
      :py:func:`define_gas_buses_abroad`
454
    * Cleaning of the database table grid.egon_etrago_bus of the
455
      foreign CH4 buses of the specific scenario (eGon2035)
456
    * Insertion of the neighbouring buses into the table grid.egon_etrago_bus.
457
458
    Parameters
459
    ----------
460
    scn_name : str
461
        Name of the scenario
462
463
    Returns
464
    -------
465
    gdf_abroad_buses : dataframe
466
        Dataframe containing the CH4 buses in the neighbouring countries
467
        and one in the center of Germany in test mode
468
469
    """
470
    gas_carrier = "CH4"
471
472
    # Connect to local database
473
    engine = db.engine()
474
    db.execute_sql(
475
        f"""
476
    DELETE FROM grid.egon_etrago_bus WHERE "carrier" = '{gas_carrier}' AND
477
    scn_name = '{scn_name}' AND country != 'DE';
478
    """
479
    )
480
481
    gdf_abroad_buses = define_gas_buses_abroad(scn_name)
482
483
    # Insert to db
484
    print(gdf_abroad_buses)
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
            "diameter",
906
            "pipe_class",
907
            "classification",
908
            "max_transport_capacity_Gwh/d",
909
            "lat",
910
            "long",
911
            "length_km",
912
        ]
913
    )
914
915
    return gas_pipelines_list
916
917
918
def insert_gas_pipeline_list(gas_pipelines_list, scn_name="eGon2035"):
919
    """
920
    Insert list of gas pipelines into the database
921
922
    Receive as argument a list of gas pipelines and insert them into the
923
    database after cleaning it.
924
925
    Parameters
926
    ----------
927
    gas_pipelines_list : pandas.DataFrame
928
        Dataframe containing the gas pipelines in Germany
929
    scn_name : str
930
        Name of the scenario
931
932
    Returns
933
    -------
934
    None
935
936
    """
937
    gas_carrier = "CH4"
938
939
    engine = db.engine()
940
    gas_pipelines_list = gas_pipelines_list.drop(
941
        columns=["country_0", "country_1"]
942
    )
943
944
    # Clean db
945
    db.execute_sql(
946
        f"""DELETE FROM grid.egon_etrago_link
947
        WHERE "carrier" = '{gas_carrier}'
948
        AND scn_name = '{scn_name}'
949
        AND link_id IN(
950
            SELECT link_id FROM grid.egon_etrago_link
951
            WHERE bus0 IN (
952
                SELECT bus_id FROM grid.egon_etrago_bus
953
                WHERE country = 'DE'
954
                AND scn_name = '{scn_name}'
955
                )
956
            AND bus1 IN (
957
                SELECT bus_id FROM grid.egon_etrago_bus
958
                WHERE country = 'DE'
959
                AND scn_name = '{scn_name}'
960
                )
961
            )
962
        """
963
    )
964
965
    print(gas_pipelines_list)
966
    # Insert data to db
967
    gas_pipelines_list.to_postgis(
968
        "egon_etrago_gas_link",
969
        engine,
970
        schema="grid",
971
        index=False,
972
        if_exists="replace",
973
        dtype={"geom": Geometry(), "topo": Geometry()},
974
    )
975
976
    db.execute_sql(
977
        """
978
    select UpdateGeometrySRID('grid', 'egon_etrago_gas_link', 'topo', 4326) ;
979
980
    INSERT INTO grid.egon_etrago_link (scn_name,
981
                                              link_id, carrier,
982
                                              bus0, bus1, p_min_pu,
983
                                              p_nom, p_nom_extendable, length,
984
                                              geom, topo)
985
    SELECT scn_name,
986
                link_id, carrier,
987
                bus0, bus1, p_min_pu,
988
                p_nom, p_nom_extendable, length,
989
                geom, topo
990
991
    FROM grid.egon_etrago_gas_link;
992
993
    DROP TABLE grid.egon_etrago_gas_link;
994
        """
995
    )
996
997
998
def remove_isolated_gas_buses(scn_name="eGon2035"):
999
    """
1000
    Delete CH4 buses which are disconnected of the CH4 grid for the required
1001
    scenario
1002
1003
    Returns
1004
    -------
1005
    None
1006
1007
    """
1008
    targets = config.datasets()["gas_grid"]["targets"]
1009
1010
    db.execute_sql(
1011
        f"""
1012
        DELETE FROM {targets['buses']['schema']}.{targets['buses']['table']}
1013
        WHERE "carrier" = 'CH4'
1014
        AND scn_name = '{scn_name}'
1015
        AND country = 'DE'
1016
        AND "bus_id" NOT IN
1017
            (SELECT bus0 FROM {targets['links']['schema']}.{targets['links']['table']}
1018
            WHERE scn_name = '{scn_name}'
1019
            AND carrier = 'CH4')
1020
        AND "bus_id" NOT IN
1021
            (SELECT bus1 FROM {targets['links']['schema']}.{targets['links']['table']}
1022
            WHERE scn_name = '{scn_name}'
1023
            AND carrier = 'CH4');
1024
    """
1025
    )
1026
1027
1028
def insert_gas_data():
1029
    """
1030
    Overall function for importing methane data for all the scenarios in the
1031
    configuration file.
1032
1033
    This function imports the methane data (buses and pipelines) for
1034
    each required scenario, by executing the following steps:
1035
      * Download the SciGRID_gas datasets with the function :py:func:`download_SciGRID_gas_data`
1036
      * Define CH4 buses with the function :py:func:`define_gas_nodes_list`
1037
      * Insert the CH4 buses in Germany into the database with the
1038
        function :py:func:`insert_CH4_nodes_list`
1039
      * Insert the CH4 buses abroad into the database with the function
1040
        :py:func:`insert_gas_buses_abroad`
1041
      * Insert the CH4 links representing the CH4 pipeline into the
1042
        database with the function :py:func:`insert_gas_pipeline_list`
1043
      * Remove the isolated CH4 buses directly from the database using
1044
        the function :py:func:`remove_isolated_gas_buses`
1045
1046
    Returns
1047
    -------
1048
    None
1049
1050
    """
1051
    s = config.settings()["egon-data"]["--scenarios"]
1052
    scenarios = []
1053
    if "eGon2035" in s:
1054
        scenarios.append("eGon2035")
1055
    if "eGon100RE" in s:
1056
        scenarios.append("eGon100RE")
1057
1058
    download_SciGRID_gas_data()
1059
1060
    for scn_name in scenarios:
1061
        gas_nodes_list = define_gas_nodes_list()
1062
1063
        insert_CH4_nodes_list(gas_nodes_list, scn_name=scn_name)
1064
        abroad_gas_nodes_list = insert_gas_buses_abroad(scn_name=scn_name)
1065
1066
        insert_gas_pipeline_list(
1067
            gas_nodes_list, abroad_gas_nodes_list, scn_name=scn_name
1068
        )
1069
        remove_isolated_gas_buses(scn_name=scn_name)
1070
1071
1072
def insert_gas_data_status(scn_name):
1073
    """
1074
    Function to deal with the gas network for the status2019 scenario.
1075
    For this scenario just one CH4 bus is consider in the center of Germany.
1076
    Since OCGTs in the foreign countries are modelled as generators and not
1077
    as links between the gas and electricity sectors, CH4 foreign buses are
1078
    considered not necessary.
1079
1080
    This function does not require any input.
1081
1082
    Returns
1083
    -------
1084
    None.
1085
1086
    """
1087
1088
    # delete old entries
1089
    db.execute_sql(
1090
        f"""
1091
        DELETE FROM grid.egon_etrago_link
1092
        WHERE carrier = 'CH4' AND scn_name = '{scn_name}'
1093
        """
1094
    )
1095
    db.execute_sql(
1096
        f"""
1097
        DELETE FROM grid.egon_etrago_bus
1098
        WHERE carrier = 'CH4' AND scn_name = '{scn_name}'
1099
        """
1100
    )
1101
1102
    # Select next id value
1103
    new_id = db.next_etrago_id("bus")
1104
1105
    df = pd.DataFrame(
1106
        index=[new_id],
1107
        data={
1108
            "scn_name": scn_name,
1109
            "v_nom": 1,
1110
            "carrier": "CH4",
1111
            "v_mag_pu_set": 1,
1112
            "v_mag_pu_min": 0,
1113
            "v_mag_pu_max": np.inf,
1114
            "x": 10,
1115
            "y": 51,
1116
            "country": "DE",
1117
        },
1118
    )
1119
    gdf = geopandas.GeoDataFrame(
1120
        df, geometry=geopandas.points_from_xy(df.x, df.y, crs=4326)
1121
    ).rename_geometry("geom")
1122
1123
    gdf.index.name = "bus_id"
1124
1125
    gdf.reset_index().to_postgis(
1126
        "egon_etrago_bus", schema="grid", con=db.engine(), if_exists="append"
1127
    )
1128
1129
1130
class GasNodesAndPipes(Dataset):
1131
    """
1132
    Insert the CH4 buses and links into the database.
1133
1134
    Insert the CH4 buses and links, which for the case of gas represent
1135
    pipelines, into the database for the scenarios status2019, eGon2035 and eGon100RE
1136
    with the functions :py:func:`insert_gas_data` and :py:func:`insert_gas_data_eGon100RE`.
1137
1138
    *Dependencies*
1139
      * :py:class:`DataBundle <egon.data.datasets.data_bundle.DataBundle>`
1140
      * :py:class:`ElectricalNeighbours <egon.data.datasets.electrical_neighbours.ElectricalNeighbours>`
1141
      * :py:class:`Osmtgmod <egon.data.datasets.osmtgmod.Osmtgmod>`
1142
      * :py:class:`ScenarioParameters <egon.data.datasets.scenario_parameters.ScenarioParameters>`
1143
      * :py:class:`EtragoSetup <egon.data.datasets.etrago_setup.EtragoSetup>` (more specifically the :func:`create_tables <egon.data.datasets.etrago_setup.create_tables>` task)
1144
1145
    *Resulting tables*
1146
      * :py:class:`grid.egon_etrago_bus <egon.data.datasets.etrago_setup.EgonPfHvBus>` is extended
1147
      * :py:class:`grid.egon_etrago_link <egon.data.datasets.etrago_setup.EgonPfHvLink>` is extended
1148
1149
    """
1150
1151
    #:
1152
    name: str = "GasNodesAndPipes"
1153
    #:
1154
    version: str = "0.0.11"
1155
1156
    tasks = ()
1157
1158
    for scn_name in config.settings()["egon-data"]["--scenarios"]:
1159
        if "status" in scn_name:
1160
            tasks += (wrapped_partial(
1161
                insert_gas_data_status, scn_name=scn_name, postfix=f"_{scn_name[-4:]}"
1162
            ),)
1163
1164
    tasks += (insert_gas_data,)
1165
1166
    def __init__(self, dependencies):
1167
        super().__init__(
1168
            name=self.name,
1169
            version=self.version,
1170
            dependencies=dependencies,
1171
            tasks=self.tasks,
1172
        )
1173