Completed
Push — dev ( 733e9e...770207 )
by
unknown
23s queued 17s
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
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 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 to 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 data base table grid.egon_etrago_bus of the
402
        CH4 buses of the specific scenario (eGon2035) out of Germany
403
      * Insertion of the neighbouring buses in 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 red 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 allows 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 erroneous country of some 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
    data base 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
    """
832
    main_gas_carrier = get_sector_parameters("gas", scenario=scn_name)[
833
        "main_gas_carrier"
834
    ]
835
    engine = db.engine()
836
    gas_pipelines_list = gas_pipelines_list.drop(
837
        columns=["country_0", "country_1"]
838
    )
839
840
    # Clean db
841
    db.execute_sql(
842
        f"""DELETE FROM grid.egon_etrago_link
843
        WHERE "carrier" = '{main_gas_carrier}'
844
        AND scn_name = '{scn_name}';
845
        """
846
    )
847
848
    print(gas_pipelines_list)
849
    # Insert data to db
850
    gas_pipelines_list.to_postgis(
851
        "egon_etrago_gas_link",
852
        engine,
853
        schema="grid",
854
        index=False,
855
        if_exists="replace",
856
        dtype={"geom": Geometry(), "topo": Geometry()},
857
    )
858
859
    db.execute_sql(
860
        """
861
    select UpdateGeometrySRID('grid', 'egon_etrago_gas_link', 'topo', 4326) ;
862
863
    INSERT INTO grid.egon_etrago_link (scn_name,
864
                                              link_id, carrier,
865
                                              bus0, bus1, p_min_pu,
866
                                              p_nom, p_nom_extendable, length,
867
                                              geom, topo)
868
    SELECT scn_name,
869
                link_id, carrier,
870
                bus0, bus1, p_min_pu,
871
                p_nom, p_nom_extendable, length,
872
                geom, topo
873
874
    FROM grid.egon_etrago_gas_link;
875
876
    DROP TABLE grid.egon_etrago_gas_link;
877
        """
878
    )
879
880
881
def remove_isolated_gas_buses():
882
    """
883
    Delete CH4 buses which are disconnected of the CH4 grid for the eGon2035 scenario
884
885
    This function deletes directly in the database and has no return.
886
887
    """
888
    targets = config.datasets()["gas_grid"]["targets"]
889
890
    db.execute_sql(
891
        f"""
892
        DELETE FROM {targets['buses']['schema']}.{targets['buses']['table']}
893
        WHERE "carrier" = 'CH4'
894
        AND scn_name = 'eGon2035'
895
        AND country = 'DE'
896
        AND "bus_id" NOT IN
897
            (SELECT bus0 FROM {targets['links']['schema']}.{targets['links']['table']}
898
            WHERE scn_name = 'eGon2035'
899
            AND carrier = 'CH4')
900
        AND "bus_id" NOT IN
901
            (SELECT bus1 FROM {targets['links']['schema']}.{targets['links']['table']}
902
            WHERE scn_name = 'eGon2035'
903
            AND carrier = 'CH4');
904
    """
905
    )
906
907
908
def insert_gas_data():
909
    """
910
    Overall function for importing methane data for eGon2035
911
912
    This function import the methane data (buses and pipelines) for
913
    eGon2035, by executing the following steps:
914
      * Download the SciGRID_gas datasets with the function :py:func:`download_SciGRID_gas_data`
915
      * Define CH4 buses with the function :py:func:`define_gas_nodes_list`
916
      * Insert the CH4 buses in Germany into the database with the
917
        function :py:func:`insert_CH4_nodes_list`
918
      * Insert the CH4 buses abroad into the database with the function
919
        :py:func:`insert_gas_buses_abroad`
920
      * Insert the CH4 links representing the CH4 pipeline into the
921
        database with the function :py:func:`insert_gas_pipeline_list`
922
      * Remove the isolated CH4 buses directly from the database using
923
        the function :py:func:`remove_isolated_gas_buses`
924
925
    This function inserts data into the database and has no return.
926
927
    """
928
    download_SciGRID_gas_data()
929
930
    gas_nodes_list = define_gas_nodes_list()
931
932
    insert_CH4_nodes_list(gas_nodes_list)
933
    abroad_gas_nodes_list = insert_gas_buses_abroad()
934
935
    gas_pipeline_list = define_gas_pipeline_list(
936
        gas_nodes_list, abroad_gas_nodes_list
937
    )
938
    insert_gas_pipeline_list(gas_pipeline_list)
939
    remove_isolated_gas_buses()
940
941
942
def insert_gas_data_eGon100RE():
943
    """
944
    Overall function for importing methane data for eGon100RE
945
946
    This function import the methane data (buses and pipelines) for
947
    eGon100RE, by copying the CH4 buses from the eGon2035 scenario using
948
    the function :py:func:`copy_and_modify_buses <egon.data.datasets.etrago_helpers.copy_and_modify_buses>`
949
    from the module :py:mod:`etrago_helpers <egon.data.datasets.etrago_helpers>`. The methane
950
    pipelines are also copied and their capacities are adapted: one
951
    share of the methane grid is retroffited into an hydrogen grid, so
952
    the methane pieplines nominal capacities are reduced from this share
953
    (calculated in the pyspa-eur-sec run).
954
955
    This function inserts data into the database and has no return.
956
957
    """
958
    # copy buses
959
    copy_and_modify_buses("eGon2035", "eGon100RE", {"carrier": ["CH4"]})
960
961
    # get CH4 pipelines and modify their nominal capacity with the
962
    # retrofitting factor
963
    gdf = db.select_geodataframe(
964
        f"""
965
        SELECT * FROM grid.egon_etrago_link
966
        WHERE carrier = 'CH4' AND scn_name = 'eGon2035' AND
967
        bus0 IN (
968
            SELECT bus_id FROM grid.egon_etrago_bus
969
            WHERE scn_name = 'eGon2035' AND country = 'DE'
970
        ) AND bus1 IN (
971
            SELECT bus_id FROM grid.egon_etrago_bus
972
            WHERE scn_name = 'eGon2035' AND country = 'DE'
973
        );
974
        """,
975
        epsg=4326,
976
        geom_col="topo",
977
    )
978
979
    # Update scenario specific information
980
    scn_name = "eGon100RE"
981
    gdf["scn_name"] = scn_name
982
    scn_params = get_sector_parameters("gas", scn_name)
983
984
    for param in ["capital_cost", "marginal_cost", "efficiency"]:
985
        try:
986
            gdf.loc[:, param] = scn_params[param]["CH4"]
987
        except KeyError:
988
            pass
989
990
    # remaining CH4 share is 1 - retroffited pipeline share
991
    gdf["p_nom"] *= (
992
        1 - scn_params["retrofitted_CH4pipeline-to-H2pipeline_share"]
993
    )
994
995
    # delete old entries
996
    db.execute_sql(
997
        f"""
998
        DELETE FROM grid.egon_etrago_link
999
        WHERE carrier = 'CH4' AND scn_name = '{scn_name}' AND
1000
        bus0 NOT IN (
1001
            SELECT bus_id FROM grid.egon_etrago_bus
1002
            WHERE scn_name = '{scn_name}' AND country != 'DE'
1003
        ) AND bus1 NOT IN (
1004
            SELECT bus_id FROM grid.egon_etrago_bus
1005
            WHERE scn_name = '{scn_name}' AND country != 'DE'
1006
        );
1007
        """
1008
    )
1009
1010
    gdf.to_postgis(
1011
        "egon_etrago_link",
1012
        schema="grid",
1013
        if_exists="append",
1014
        con=db.engine(),
1015
        index=False,
1016
        dtype={"geom": Geometry(), "topo": Geometry()},
1017
    )
1018