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

Complexity

Conditions 18

Size

Total Lines 370
Code Lines 250

Duplication

Lines 0
Ratio 0 %

Importance

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