Passed
Pull Request — dev (#1129)
by
unknown
01:48
created

data.datasets.gas_grid.define_gas_buses_abroad()   B

Complexity

Conditions 2

Size

Total Lines 96
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 45
dl 0
loc 96
rs 8.8
c 0
b 0
f 0
cc 2
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 containing code aiming to insert the methane grid into the database
4
5
The central module containing all code dealing with the import of data
6
from SciGRID_gas (IGGIELGN dataset) and with the insertion fo the CH4
7
buses and links 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 are also used by other modules.
11
12
In this module, only the IGGIELGN_Nodes and IGGIELGN_PipeSegments cvs 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 are 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 these 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 red from the IGGIELGN_Nodes cvs file previously downloaded in the
129
    function :py:func:`download_SciGRID_gas_data`, corrected (erroneous country),
130
    and returned as 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
def define_gas_buses_abroad(scn_name="eGon2035"):
297
    """
298
    Define central CH4 buses in foreign countries for eGon2035
299
300
    For the scenario eGon2035, define central CH4 buses in foreign
301
    countries. The considered foreign countries are the direct
302
    neighbouring countries, with the addition of Russia that is
303
    considered as a source of fossil CH4.
304
    Therefore, the following steps are executed:
305
      * Definition of the foreign buses with the function
306
        :py:func:`central_buses_egon100 <egon.data.datasets.electrical_neighbours.central_buses_egon100>` from
307
        the module :py:mod:`electrical_neighbours <egon.data.datasets.electrical_neighbours>`
308
      * Removal of the superfluous buses in order to have only one bus
309
        in each neighbouring country
310
      * Removal of the the irrelevant columns
311
      * Addition of the missing information: scn_name and carrier
312
      * Attribution of an id to each bus
313
314
    Parameters
315
    ----------
316
    scn_name : str
317
        Name of the scenario
318
319
    Returns
320
    -------
321
    gdf_abroad_buses : pandas.DataFrame
322
        Dataframe containing the gas buses in the neighbouring countries
323
        and one in the center of Germany in test mode
324
325
    """
326
    # Select sources and targets from dataset configuration
327
    sources = config.datasets()["electrical_neighbours"]["sources"]
328
329
    main_gas_carrier = get_sector_parameters("gas", scenario=scn_name)[
330
        "main_gas_carrier"
331
    ]
332
333
    # Select the foreign buses
334
    gdf_abroad_buses = central_buses_egon100(sources)
335
    gdf_abroad_buses = gdf_abroad_buses.drop_duplicates(subset=["country"])
336
337
    # Select next id value
338
    new_id = db.next_etrago_id("bus")
339
340
    gdf_abroad_buses = gdf_abroad_buses.drop(
341
        columns=[
342
            "v_nom",
343
            "v_mag_pu_set",
344
            "v_mag_pu_min",
345
            "v_mag_pu_max",
346
            "geom",
347
        ]
348
    )
349
    gdf_abroad_buses["scn_name"] = "eGon2035"
350
    gdf_abroad_buses["carrier"] = main_gas_carrier
351
    gdf_abroad_buses["bus_id"] = range(new_id, new_id + len(gdf_abroad_buses))
352
353
    # Add central bus in Russia
354
    gdf_abroad_buses = gdf_abroad_buses.append(
355
        {
356
            "scn_name": scn_name,
357
            "bus_id": (new_id + len(gdf_abroad_buses) + 1),
358
            "x": 41,
359
            "y": 55,
360
            "country": "RU",
361
            "carrier": main_gas_carrier,
362
        },
363
        ignore_index=True,
364
    )
365
    # if in test mode, add bus in center of Germany
366
    boundary = settings()["egon-data"]["--dataset-boundary"]
367
368
    if boundary != "Everything":
369
        gdf_abroad_buses = gdf_abroad_buses.append(
370
            {
371
                "scn_name": scn_name,
372
                "bus_id": (new_id + len(gdf_abroad_buses) + 1),
373
                "x": 10.4234469,
374
                "y": 51.0834196,
375
                "country": "DE",
376
                "carrier": main_gas_carrier,
377
            },
378
            ignore_index=True,
379
        )
380
381
    gdf_abroad_buses = geopandas.GeoDataFrame(
382
        gdf_abroad_buses,
383
        geometry=geopandas.points_from_xy(
384
            gdf_abroad_buses["x"], gdf_abroad_buses["y"]
385
        ),
386
    )
387
    gdf_abroad_buses = gdf_abroad_buses.rename(
388
        columns={"geometry": "geom"}
389
    ).set_geometry("geom", crs=4326)
390
391
    return gdf_abroad_buses
392
393
394
def insert_gas_buses_abroad(scn_name="eGon2035"):
395
    """
396
        Insert CH4 buses in neighbouring countries to database for eGon2035
397
398
      * Definition of the CH4 buses abroad with the function
399
        :py:func:`define_gas_buses_abroad`
400
      * Cleaning of the data base table grid.egon_etrago_bus of the
401
        CH4 buses of the specific scenario (eGon2035) out of Germany
402
      * Insertion of the neighbouring buses in the table grid.egon_etrago_bus.
403
404
    Parameters
405
    ----------
406
    scn_name : str
407
        Name of the scenario
408
409
    Returns
410
    -------
411
    gdf_abroad_buses : dataframe
412
        Dataframe containing the CH4 buses in the neighbouring countries
413
        and one in the center of Germany in test mode
414
    """
415
    main_gas_carrier = get_sector_parameters("gas", scenario=scn_name)[
416
        "main_gas_carrier"
417
    ]
418
419
    # Connect to local database
420
    engine = db.engine()
421
    db.execute_sql(
422
        f"""
423
    DELETE FROM grid.egon_etrago_bus WHERE "carrier" = '{main_gas_carrier}' AND
424
    scn_name = '{scn_name}' AND country != 'DE';
425
    """
426
    )
427
428
    gdf_abroad_buses = define_gas_buses_abroad(scn_name)
429
430
    # Insert to db
431
    print(gdf_abroad_buses)
432
    gdf_abroad_buses.to_postgis(
433
        "egon_etrago_bus",
434
        engine,
435
        schema="grid",
436
        index=False,
437
        if_exists="append",
438
        dtype={"geom": Geometry()},
439
    )
440
    return gdf_abroad_buses
441
442
443
def define_gas_pipeline_list(
444
    gas_nodes_list, abroad_gas_nodes_list, scn_name="eGon2035"
445
):
446
    """
447
    Define gas pipelines in Germany from SciGRID_gas IGGIELGN data
448
449
    The gas pipelines, modelled as Pypsa links are red from the IGGIELGN_PipeSegments
450
    csv file previously downloded in the function :py:func:`download_SciGRID_gas_data`.
451
452
    The capacities of the pipelines are determined by the correspondance
453
    table given by the Parameters for the classification of gas pipelines
454
    in `Electricity, heat, and gas sector data for modeling the German system
455
    <https://www.econstor.eu/bitstream/10419/173388/1/1011162628.pdf>`_
456
    related to the pipeline diameter given in the SciGRID_gas dataset.
457
458
    The manual corrections allows to:
459
      * Delete gas pipelines disconnected of the rest of the gas grid
460
      * Connect one pipeline (also connected to Norway) disconnected of
461
        the rest of the gas grid
462
      * Correct erroneous country of some pipelines
463
464
    Parameters
465
    ----------
466
    gas_nodes_list : dataframe
467
        Dataframe containing the gas nodes in Europe
468
    abroad_gas_nodes_list: dataframe
469
        Dataframe containing the gas buses in the neighbouring countries
470
        and one in the center of Germany in test mode
471
    scn_name : str
472
        Name of the scenario
473
474
    Returns
475
    -------
476
    gas_pipelines_list : pandas.DataFrame
477
        Dataframe containing the gas pipelines in Germany
478
479
    """
480
    abroad_gas_nodes_list = abroad_gas_nodes_list.set_index("country")
481
482
    main_gas_carrier = get_sector_parameters("gas", scenario=scn_name)[
483
        "main_gas_carrier"
484
    ]
485
486
    # Select next id value
487
    new_id = db.next_etrago_id("link")
488
489
    classifiaction_file = (
490
        Path(".")
491
        / "data_bundle_egon_data"
492
        / "pipeline_classification_gas"
493
        / "pipeline_classification.csv"
494
    )
495
496
    classification = pd.read_csv(
497
        classifiaction_file,
498
        delimiter=",",
499
        usecols=["classification", "max_transport_capacity_Gwh/d"],
500
    )
501
502
    target_file = (
503
        Path(".")
504
        / "datasets"
505
        / "gas_data"
506
        / "data"
507
        / "IGGIELGN_PipeSegments.csv"
508
    )
509
510
    gas_pipelines_list = pd.read_csv(
511
        target_file,
512
        delimiter=";",
513
        decimal=".",
514
        usecols=["id", "node_id", "lat", "long", "country_code", "param"],
515
    )
516
517
    # Select the links having at least one bus in Germany
518
    gas_pipelines_list = gas_pipelines_list[
519
        gas_pipelines_list["country_code"].str.contains("DE")
520
    ]
521
    # Remove links disconnected of the rest of the grid
522
    # Remove manually for disconnected link EntsoG_Map__ST_195 and EntsoG_Map__ST_108
523
    gas_pipelines_list = gas_pipelines_list[
524
        gas_pipelines_list["node_id"] != "['SEQ_11790_p', 'Stor_EU_107']"
525
    ]
526
    gas_pipelines_list = gas_pipelines_list[
527
        ~gas_pipelines_list["id"].str.match("EntsoG_Map__ST_108")
528
    ]
529
530
    # Manually add pipeline to artificially connect isolated pipeline
531
    gas_pipelines_list.at["new_pipe", "param"] = gas_pipelines_list[
532
        gas_pipelines_list["id"] == "NO_PS_8_Seg_0_Seg_23"
533
    ]["param"].values[0]
534
    gas_pipelines_list.at[
535
        "new_pipe", "node_id"
536
    ] = "['SEQ_12442_p', 'LKD_N_200']"
537
    gas_pipelines_list.at["new_pipe", "lat"] = "[53.358536, 53.412719]"
538
    gas_pipelines_list.at["new_pipe", "long"] = "[7.041677, 7.093251]"
539
    gas_pipelines_list.at["new_pipe", "country_code"] = "['DE', 'DE']"
540
541
    gas_pipelines_list["link_id"] = range(
542
        new_id, new_id + len(gas_pipelines_list)
543
    )
544
    gas_pipelines_list["link_id"] = gas_pipelines_list["link_id"].astype(int)
545
546
    # Cut data to federal state if in testmode
547
    NUTS1 = []
548
    for index, row in gas_pipelines_list.iterrows():
549
        param = ast.literal_eval(row["param"])
550
        NUTS1.append(param["nuts_id_1"])
551
    gas_pipelines_list["NUTS1"] = NUTS1
552
553
    map_states = {
554
        "Baden-Württemberg": "DE1",
555
        "Nordrhein-Westfalen": "DEA",
556
        "Hessen": "DE7",
557
        "Brandenburg": "DE4",
558
        "Bremen": "DE5",
559
        "Rheinland-Pfalz": "DEB",
560
        "Sachsen-Anhalt": "DEE",
561
        "Schleswig-Holstein": "DEF",
562
        "Mecklenburg-Vorpommern": "DE8",
563
        "Thüringen": "DEG",
564
        "Niedersachsen": "DE9",
565
        "Sachsen": "DED",
566
        "Hamburg": "DE6",
567
        "Saarland": "DEC",
568
        "Berlin": "DE3",
569
        "Bayern": "DE2",
570
        "Everything": "Nan",
571
    }
572
    gas_pipelines_list["NUTS1_0"] = [x[0] for x in gas_pipelines_list["NUTS1"]]
573
    gas_pipelines_list["NUTS1_1"] = [x[1] for x in gas_pipelines_list["NUTS1"]]
574
575
    boundary = settings()["egon-data"]["--dataset-boundary"]
576
577
    if boundary != "Everything":
578
579
        gas_pipelines_list = gas_pipelines_list[
580
            gas_pipelines_list["NUTS1_0"].str.contains(map_states[boundary])
581
            | gas_pipelines_list["NUTS1_1"].str.contains(map_states[boundary])
582
        ]
583
584
    # Add missing columns
585
    gas_pipelines_list["scn_name"] = scn_name
586
    gas_pipelines_list["carrier"] = main_gas_carrier
587
    gas_pipelines_list["p_nom_extendable"] = False
588
    gas_pipelines_list["p_min_pu"] = -1.0
589
590
    diameter = []
591
    geom = []
592
    topo = []
593
    length_km = []
594
595
    for index, row in gas_pipelines_list.iterrows():
596
597
        param = ast.literal_eval(row["param"])
598
        diameter.append(param["diameter_mm"])
599
        length_km.append(param["length_km"])
600
601
        long_e = json.loads(row["long"])
602
        lat_e = json.loads(row["lat"])
603
        crd_e = list(zip(long_e, lat_e))
604
        topo.append(geometry.LineString(crd_e))
605
606
        long_path = param["path_long"]
607
        lat_path = param["path_lat"]
608
        crd = list(zip(long_path, lat_path))
609
        crd.insert(0, crd_e[0])
610
        crd.append(crd_e[1])
611
        lines = []
612
        for i in range(len(crd) - 1):
613
            lines.append(geometry.LineString([crd[i], crd[i + 1]]))
614
        geom.append(geometry.MultiLineString(lines))
615
616
    gas_pipelines_list["diameter"] = diameter
617
    gas_pipelines_list["geom"] = geom
618
    gas_pipelines_list["topo"] = topo
619
    gas_pipelines_list["length_km"] = length_km
620
    gas_pipelines_list = gas_pipelines_list.set_geometry("geom", crs=4326)
621
622
    country_0 = []
623
    country_1 = []
624
    for index, row in gas_pipelines_list.iterrows():
625
        c = ast.literal_eval(row["country_code"])
626
        country_0.append(c[0])
627
        country_1.append(c[1])
628
629
    gas_pipelines_list["country_0"] = country_0
630
    gas_pipelines_list["country_1"] = country_1
631
632
    # Correct non valid neighbouring country nodes
633
    gas_pipelines_list.loc[
634
        gas_pipelines_list["country_0"] == "XX", "country_0"
635
    ] = "NO"
636
    gas_pipelines_list.loc[
637
        gas_pipelines_list["country_1"] == "FI", "country_1"
638
    ] = "RU"
639
    gas_pipelines_list.loc[
640
        gas_pipelines_list["id"] == "ST_2612_Seg_0_Seg_0", "country_0"
641
    ] = "AT"  # bus "INET_N_1182" DE -> AT
642
    gas_pipelines_list.loc[
643
        gas_pipelines_list["id"] == "INET_PL_385_EE_3_Seg_0_Seg_1", "country_1"
644
    ] = "AT"  # "INET_N_1182" DE -> AT
645
    gas_pipelines_list.loc[
646
        gas_pipelines_list["id"] == "LKD_PS_0_Seg_0_Seg_3", "country_0"
647
    ] = "NL"  # bus "SEQ_10608_p" DE -> NL
648
649
    # Remove uncorrect pipelines
650
    gas_pipelines_list = gas_pipelines_list[
651
        (gas_pipelines_list["id"] != "PLNG_2637_Seg_0_Seg_0_Seg_0")
652
        & (gas_pipelines_list["id"] != "NSG_6650_Seg_2_Seg_0")
653
        & (gas_pipelines_list["id"] != "NSG_6734_Seg_2_Seg_0")
654
    ]
655
656
    # Remove link test if length = 0
657
    gas_pipelines_list = gas_pipelines_list[
658
        gas_pipelines_list["length_km"] != 0
659
    ]
660
661
    # Adjust columns
662
    bus0 = []
663
    bus1 = []
664
    geom_adjusted = []
665
    topo_adjusted = []
666
    length_adjusted = []
667
    pipe_class = []
668
669
    for index, row in gas_pipelines_list.iterrows():
670
        buses = row["node_id"].strip("][").split(", ")
671
672
        if (
673
            (boundary != "Everything")
674
            & (row["NUTS1_0"] != map_states[boundary])
675
            & (row["country_0"] == "DE")
676
        ):
677
            bus0.append(abroad_gas_nodes_list.loc["DE", "bus_id"])
678
            bus1.append(gas_nodes_list.loc[buses[1][1:-1], "bus_id"])
679
            long_e = [
680
                abroad_gas_nodes_list.loc["DE", "x"],
681
                json.loads(row["long"])[1],
682
            ]
683
            lat_e = [
684
                abroad_gas_nodes_list.loc["DE", "y"],
685
                json.loads(row["lat"])[1],
686
            ]
687
            geom_pipe = geometry.MultiLineString(
688
                [geometry.LineString(list(zip(long_e, lat_e)))]
689
            )
690
            topo_adjusted.append(geometry.LineString(list(zip(long_e, lat_e))))
691
692
        elif row["country_0"] != "DE":
693
            country = str(row["country_0"])
694
            bus0.append(abroad_gas_nodes_list.loc[country, "bus_id"])
695
            bus1.append(gas_nodes_list.loc[buses[1][1:-1], "bus_id"])
696
            long_e = [
697
                abroad_gas_nodes_list.loc[country, "x"],
698
                json.loads(row["long"])[1],
699
            ]
700
            lat_e = [
701
                abroad_gas_nodes_list.loc[country, "y"],
702
                json.loads(row["lat"])[1],
703
            ]
704
            geom_pipe = geometry.MultiLineString(
705
                [geometry.LineString(list(zip(long_e, lat_e)))]
706
            )
707
            topo_adjusted.append(geometry.LineString(list(zip(long_e, lat_e))))
708
709
        elif (
710
            (boundary != "Everything")
711
            & (row["NUTS1_1"] != map_states[boundary])
712
            & (row["country_1"] == "DE")
713
        ):
714
            bus0.append(gas_nodes_list.loc[buses[0][1:-1], "bus_id"])
715
            bus1.append(abroad_gas_nodes_list.loc["DE", "bus_id"])
716
            long_e = [
717
                json.loads(row["long"])[0],
718
                abroad_gas_nodes_list.loc["DE", "x"],
719
            ]
720
            lat_e = [
721
                json.loads(row["lat"])[0],
722
                abroad_gas_nodes_list.loc["DE", "y"],
723
            ]
724
            geom_pipe = geometry.MultiLineString(
725
                [geometry.LineString(list(zip(long_e, lat_e)))]
726
            )
727
            topo_adjusted.append(geometry.LineString(list(zip(long_e, lat_e))))
728
729
        elif row["country_1"] != "DE":
730
            country = str(row["country_1"])
731
            bus0.append(gas_nodes_list.loc[buses[0][1:-1], "bus_id"])
732
            bus1.append(abroad_gas_nodes_list.loc[country, "bus_id"])
733
            long_e = [
734
                json.loads(row["long"])[0],
735
                abroad_gas_nodes_list.loc[country, "x"],
736
            ]
737
            lat_e = [
738
                json.loads(row["lat"])[0],
739
                abroad_gas_nodes_list.loc[country, "y"],
740
            ]
741
            geom_pipe = geometry.MultiLineString(
742
                [geometry.LineString(list(zip(long_e, lat_e)))]
743
            )
744
            topo_adjusted.append(geometry.LineString(list(zip(long_e, lat_e))))
745
746
        else:
747
            bus0.append(gas_nodes_list.loc[buses[0][1:-1], "bus_id"])
748
            bus1.append(gas_nodes_list.loc[buses[1][1:-1], "bus_id"])
749
            geom_pipe = row["geom"]
750
            topo_adjusted.append(row["topo"])
751
752
        geom_adjusted.append(geom_pipe)
753
        length_adjusted.append(geom_pipe.length)
754
755
        if row["diameter"] >= 1000:
756
            pipe_class = "A"
757
        elif 700 <= row["diameter"] <= 1000:
758
            pipe_class = "B"
759
        elif 500 <= row["diameter"] <= 700:
760
            pipe_class = "C"
761
        elif 350 <= row["diameter"] <= 500:
762
            pipe_class = "D"
763
        elif 200 <= row["diameter"] <= 350:
764
            pipe_class = "E"
765
        elif 100 <= row["diameter"] <= 200:
766
            pipe_class = "F"
767
        elif row["diameter"] <= 100:
768
            pipe_class = "G"
769
770
    gas_pipelines_list["bus0"] = bus0
771
    gas_pipelines_list["bus1"] = bus1
772
    gas_pipelines_list["geom"] = geom_adjusted
773
    gas_pipelines_list["topo"] = topo_adjusted
774
    gas_pipelines_list["length"] = length_adjusted
775
    gas_pipelines_list["pipe_class"] = pipe_class
776
777
    # Remove pipes having the same node for start and end
778
    gas_pipelines_list = gas_pipelines_list[
779
        gas_pipelines_list["bus0"] != gas_pipelines_list["bus1"]
780
    ]
781
782
    gas_pipelines_list = gas_pipelines_list.merge(
783
        classification,
784
        how="left",
785
        left_on="pipe_class",
786
        right_on="classification",
787
    )
788
    gas_pipelines_list["p_nom"] = gas_pipelines_list[
789
        "max_transport_capacity_Gwh/d"
790
    ] * (1000 / 24)
791
792
    # Remove useless columns
793
    gas_pipelines_list = gas_pipelines_list.drop(
794
        columns=[
795
            "id",
796
            "node_id",
797
            "param",
798
            "NUTS1",
799
            "NUTS1_0",
800
            "NUTS1_1",
801
            "country_code",
802
            "diameter",
803
            "pipe_class",
804
            "classification",
805
            "max_transport_capacity_Gwh/d",
806
            "lat",
807
            "long",
808
            "length_km",
809
        ]
810
    )
811
812
    return gas_pipelines_list
813
814
815
def insert_gas_pipeline_list(gas_pipelines_list, scn_name="eGon2035"):
816
    """
817
    Insert list of gas pipelines in the database
818
819
    Receive as argument a list of gas pipelines and insert them into the
820
    data base after cleaning it.
821
822
    Parameters
823
    ----------
824
    gas_pipelines_list : pandas.DataFrame
825
        Dataframe containing the gas pipelines in Germany
826
    scn_name : str
827
        Name of the scenario
828
829
    """
830
    main_gas_carrier = get_sector_parameters("gas", scenario=scn_name)[
831
        "main_gas_carrier"
832
    ]
833
    engine = db.engine()
834
    gas_pipelines_list = gas_pipelines_list.drop(
835
        columns=[
836
            "country_0",
837
            "country_1",
838
        ]
839
    )
840
841
    # Clean db
842
    db.execute_sql(
843
        f"""DELETE FROM grid.egon_etrago_link
844
        WHERE "carrier" = '{main_gas_carrier}'
845
        AND scn_name = '{scn_name}';
846
        """
847
    )
848
849
    print(gas_pipelines_list)
850
    # Insert data to db
851
    gas_pipelines_list.to_postgis(
852
        "egon_etrago_gas_link",
853
        engine,
854
        schema="grid",
855
        index=False,
856
        if_exists="replace",
857
        dtype={"geom": Geometry(), "topo": Geometry()},
858
    )
859
860
    db.execute_sql(
861
        """
862
    select UpdateGeometrySRID('grid', 'egon_etrago_gas_link', 'topo', 4326) ;
863
864
    INSERT INTO grid.egon_etrago_link (scn_name,
865
                                              link_id, carrier,
866
                                              bus0, bus1, p_min_pu,
867
                                              p_nom, p_nom_extendable, length,
868
                                              geom, topo)
869
    SELECT scn_name,
870
                link_id, carrier,
871
                bus0, bus1, p_min_pu,
872
                p_nom, p_nom_extendable, length,
873
                geom, topo
874
875
    FROM grid.egon_etrago_gas_link;
876
877
    DROP TABLE grid.egon_etrago_gas_link;
878
        """
879
    )
880
881
882
def remove_isolated_gas_buses():
883
    """
884
    Delete CH4 buses which are disconnected of the CH4 grid for the eGon2035 scenario
885
886
    This function deletes directly in the database and has no return.
887
888
    """
889
    targets = config.datasets()["gas_grid"]["targets"]
890
891
    db.execute_sql(
892
        f"""
893
        DELETE FROM {targets['buses']['schema']}.{targets['buses']['table']}
894
        WHERE "carrier" = 'CH4'
895
        AND scn_name = 'eGon2035'
896
        AND country = 'DE'
897
        AND "bus_id" NOT IN
898
            (SELECT bus0 FROM {targets['links']['schema']}.{targets['links']['table']}
899
            WHERE scn_name = 'eGon2035'
900
            AND carrier = 'CH4')
901
        AND "bus_id" NOT IN
902
            (SELECT bus1 FROM {targets['links']['schema']}.{targets['links']['table']}
903
            WHERE scn_name = 'eGon2035'
904
            AND carrier = 'CH4');
905
    """
906
    )
907
908
909
def insert_gas_data():
910
    """
911
    Overall function for importing methane data for eGon2035
912
913
    This function import the methane data (buses and pipelines) for
914
    eGon2035, by executing the following steps:
915
      * Download the SciGRID_gas datasets with the function :py:func:`download_SciGRID_gas_data`
916
      * Define CH4 buses with the function :py:func:`define_gas_nodes_list`
917
      * Insert the CH4 buses in Germany into the database with the
918
        function :py:func:`insert_CH4_nodes_list`
919
      * Insert the CH4 buses abroad into the database with the function
920
        :py:func:`insert_gas_buses_abroad`
921
      * Insert the CH4 links representing the CH4 pipeline into the
922
        database with the function :py:func:`insert_gas_pipeline_list`
923
      * Remove the isolated CH4 buses directly from the database using
924
        the function :py:func:`remove_isolated_gas_buses`
925
926
    This function inserts data into the database and has no return.
927
928
    """
929
    download_SciGRID_gas_data()
930
931
    gas_nodes_list = define_gas_nodes_list()
932
933
    insert_CH4_nodes_list(gas_nodes_list)
934
    abroad_gas_nodes_list = insert_gas_buses_abroad()
935
936
    gas_pipeline_list = define_gas_pipeline_list(
937
        gas_nodes_list, abroad_gas_nodes_list
938
    )
939
    insert_gas_pipeline_list(gas_pipeline_list)
940
    remove_isolated_gas_buses()
941
942
943
def insert_gas_data_eGon100RE():
944
    """
945
    Overall function for importing methane data for eGon100RE
946
947
    This function import the methane data (buses and pipelines) for
948
    eGon100RE, by copying the CH4 buses from the eGon2035 scenario using
949
    the function :py:func:`copy_and_modify_buses <egon.data.datasets.etrago_helpers.copy_and_modify_buses>`
950
    from the module :py:mod:`etrago_helpers <egon.data.datasets.etrago_helpers>`. The methane
951
    pipelines are also copied and their capacities are adapted: one
952
    share of the methane grid is retroffited into an hydrogen grid, so
953
    the methane pieplines nominal capacities are reduced from this share
954
    (calculated in the pyspa-eur-sec run).
955
956
    This function inserts data into the database and has no return.
957
958
    """
959
    # copy buses
960
    copy_and_modify_buses("eGon2035", "eGon100RE", {"carrier": ["CH4"]})
961
962
    # get CH4 pipelines and modify their nominal capacity with the
963
    # retrofitting factor
964
    gdf = db.select_geodataframe(
965
        f"""
966
        SELECT * FROM grid.egon_etrago_link
967
        WHERE carrier = 'CH4' AND scn_name = 'eGon2035' AND
968
        bus0 IN (
969
            SELECT bus_id FROM grid.egon_etrago_bus
970
            WHERE scn_name = 'eGon2035' AND country = 'DE'
971
        ) AND bus1 IN (
972
            SELECT bus_id FROM grid.egon_etrago_bus
973
            WHERE scn_name = 'eGon2035' AND country = 'DE'
974
        );
975
        """,
976
        epsg=4326,
977
        geom_col="topo",
978
    )
979
980
    # Update scenario specific information
981
    scn_name = "eGon100RE"
982
    gdf["scn_name"] = scn_name
983
    scn_params = get_sector_parameters("gas", scn_name)
984
985
    for param in ["capital_cost", "marginal_cost", "efficiency"]:
986
        try:
987
            gdf.loc[:, param] = scn_params[param]["CH4"]
988
        except KeyError:
989
            pass
990
991
    # remaining CH4 share is 1 - retroffited pipeline share
992
    gdf["p_nom"] *= (
993
        1 - scn_params["retrofitted_CH4pipeline-to-H2pipeline_share"]
994
    )
995
996
    # delete old entries
997
    db.execute_sql(
998
        f"""
999
        DELETE FROM grid.egon_etrago_link
1000
        WHERE carrier = 'CH4' AND scn_name = '{scn_name}' AND
1001
        bus0 NOT IN (
1002
            SELECT bus_id FROM grid.egon_etrago_bus
1003
            WHERE scn_name = '{scn_name}' AND country != 'DE'
1004
        ) AND bus1 NOT IN (
1005
            SELECT bus_id FROM grid.egon_etrago_bus
1006
            WHERE scn_name = '{scn_name}' AND country != 'DE'
1007
        );
1008
        """
1009
    )
1010
1011
    gdf.to_postgis(
1012
        "egon_etrago_link",
1013
        schema="grid",
1014
        if_exists="append",
1015
        con=db.engine(),
1016
        index=False,
1017
        dtype={"geom": Geometry(), "topo": Geometry()},
1018
    )
1019