Passed
Pull Request — dev (#972)
by
unknown
01:45
created

calc_ch4_storage_capacities()   B

Complexity

Conditions 3

Size

Total Lines 86
Code Lines 62

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 62
dl 0
loc 86
rs 8.2436
c 0
b 0
f 0
cc 3
nop 0

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
"""Central module containing code dealing with gas neighbours for eGon2035
2
"""
3
4
from pathlib import Path
5
from urllib.request import urlretrieve
6
import ast
7
import zipfile
8
9
from geoalchemy2.types import Geometry
10
from shapely.geometry import LineString, MultiLineString
11
import geopandas as gpd
12
import numpy as np
13
import pandas as pd
14
import pypsa
15
16
from egon.data import config, db
17
from egon.data.datasets import Dataset
18
from egon.data.datasets.electrical_neighbours import (
19
    get_foreign_bus_id,
20
    get_map_buses,
21
)
22
from egon.data.datasets.gas_neighbours.gas_abroad import (
23
    insert_gas_grid_capacities,
24
)
25
from egon.data.datasets.scenario_parameters import get_sector_parameters
26
27
countries = [
28
    "AT",
29
    "BE",
30
    "CH",
31
    "CZ",
32
    "DK",
33
    "FR",
34
    "GB",
35
    "LU",
36
    "NL",
37
    "NO",
38
    "PL",
39
    "RU",
40
    "SE",
41
    "UK",
42
]
43
44
45 View Code Duplication
def get_foreign_gas_bus_id(carrier="CH4"):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
46
    """Calculate the etrago bus id based on the geometry
47
48
    Mapp node_ids from TYNDP and etragos bus_id
49
50
    Parameters
51
    ----------
52
    carrier : str
53
        Name of the carrier
54
55
    Returns
56
    -------
57
    pandas.Series
58
        List of mapped node_ids from TYNDP and etragos bus_id
59
60
    """
61
    sources = config.datasets()["gas_neighbours"]["sources"]
62
    scn_name = "eGon2035"
63
64
    bus_id = db.select_geodataframe(
65
        f"""
66
        SELECT bus_id, ST_Buffer(geom, 1) as geom, country
67
        FROM grid.egon_etrago_bus
68
        WHERE scn_name = '{scn_name}'
69
        AND carrier = '{carrier}'
70
        AND country != 'DE'
71
        """,
72
        epsg=3035,
73
    )
74
75
    # insert installed capacities
76
    file = zipfile.ZipFile(f"tyndp/{sources['tyndp_capacities']}")
77
78
    # Select buses in neighbouring countries as geodataframe
79
    buses = pd.read_excel(
80
        file.open("TYNDP-2020-Scenario-Datafile.xlsx").read(),
81
        sheet_name="Nodes - Dict",
82
    ).query("longitude==longitude")
83
    buses = gpd.GeoDataFrame(
84
        buses,
85
        crs=4326,
86
        geometry=gpd.points_from_xy(buses.longitude, buses.latitude),
87
    ).to_crs(3035)
88
89
    buses["bus_id"] = 0
90
91
    # Select bus_id from etrago with shortest distance to TYNDP node
92
    for i, row in buses.iterrows():
93
        distance = bus_id.set_index("bus_id").geom.distance(row.geometry)
94
        buses.loc[i, "bus_id"] = distance[
95
            distance == distance.min()
96
        ].index.values[0]
97
98
    return buses.set_index("node_id").bus_id
99
100
101
def read_LNG_capacities():
102
    lng_file = "datasets/gas_data/data/IGGIELGN_LNGs.csv"
103
    IGGIELGN_LNGs = gpd.read_file(lng_file)
104
105
    map_countries_scigrid = {
106
        "BE": "BE00",
107
        "EE": "EE00",
108
        "EL": "GR00",
109
        "ES": "ES00",
110
        "FI": "FI00",
111
        "FR": "FR00",
112
        "GB": "UK00",
113
        "IT": "ITCN",
114
        "LT": "LT00",
115
        "LV": "LV00",
116
        "MT": "MT00",
117
        "NL": "NL00",
118
        "PL": "PL00",
119
        "PT": "PT00",
120
        "SE": "SE01",
121
    }
122
123
    conversion_factor = 437.5  # MCM/day to MWh/h
124
    c2 = 24 / 1000  # MWh/h to GWh/d
125
    p_nom = []
126
127
    for index, row in IGGIELGN_LNGs.iterrows():
128
        param = ast.literal_eval(row["param"])
129
        p_nom.append(
130
            param["max_cap_store2pipe_M_m3_per_d"] * conversion_factor * c2
131
        )
132
133
    IGGIELGN_LNGs["LNG max_cap_store2pipe_M_m3_per_d (in GWh/d)"] = p_nom
134
135
    IGGIELGN_LNGs.drop(
136
        [
137
            "uncertainty",
138
            "method",
139
            "param",
140
            "comment",
141
            "tags",
142
            "source_id",
143
            "lat",
144
            "long",
145
            "geometry",
146
            "id",
147
            "name",
148
            "node_id",
149
        ],
150
        axis=1,
151
        inplace=True,
152
    )
153
154
    IGGIELGN_LNGs["Country"] = IGGIELGN_LNGs["country_code"].map(
155
        map_countries_scigrid
156
    )
157
    IGGIELGN_LNGs = (
158
        IGGIELGN_LNGs.groupby(["Country"])[
159
            "LNG max_cap_store2pipe_M_m3_per_d (in GWh/d)"
160
        ]
161
        .sum()
162
        .sort_index()
163
    )
164
165
    return IGGIELGN_LNGs
166
167
168
def calc_capacities():
169
    """Calculates gas production capacities from TYNDP data
170
171
    Returns
172
    -------
173
    pandas.DataFrame
174
        Gas production capacities per foreign node and energy carrier
175
176
    """
177
178
    sources = config.datasets()["gas_neighbours"]["sources"]
179
180
    # insert installed capacities
181
    file = zipfile.ZipFile(f"tyndp/{sources['tyndp_capacities']}")
182
    df = pd.read_excel(
183
        file.open("TYNDP-2020-Scenario-Datafile.xlsx").read(),
184
        sheet_name="Gas Data",
185
    )
186
187
    df = (
188
        df.query(
189
            'Scenario == "Distributed Energy" & '
190
            '(Case == "Peak" | Case == "Average") &'  # Case: 2 Week/Average/DF/Peak
191
            'Category == "Production"'
192
        )
193
        .drop(
194
            columns=[
195
                "Generator_ID",
196
                "Climate Year",
197
                "Simulation_ID",
198
                "Node 1",
199
                "Path",
200
                "Direct/Indirect",
201
                "Sector",
202
                "Note",
203
                "Category",
204
                "Scenario",
205
            ]
206
        )
207
        .set_index("Node/Line")
208
        .sort_index()
209
    )
210
211
    lng = read_LNG_capacities()
212
    df_2030 = calc_capacity_per_year(df, lng, 2030)
213
    df_2040 = calc_capacity_per_year(df, lng, 2040)
214
215
    # Conversion GWh/d to MWh/h
216
    conversion_factor = 1000 / 24
217
218
    df_2035 = pd.concat([df_2040, df_2030], axis=1)
219
    df_2035 = df_2035.drop(
220
        columns=[
221
            "Value_conv_2040",
222
            "Value_conv_2030",
223
            "Value_bio_2040",
224
            "Value_bio_2030",
225
        ]
226
    )
227
    df_2035["cap_2035"] = (df_2035["CH4_2030"] + df_2035["CH4_2040"]) / 2
228
    df_2035["e_nom_max"] = (
229
        ((df_2035["e_nom_max_2030"] + df_2035["e_nom_max_2040"]) / 2)
230
        * conversion_factor
231
        * 8760
232
    )
233
    df_2035["ratioConv_2035"] = (
234
        df_2035["ratioConv_2030"] + df_2035["ratioConv_2040"]
235
    ) / 2
236
    grouped_capacities = df_2035.drop(
237
        columns=[
238
            "ratioConv_2030",
239
            "ratioConv_2040",
240
            "CH4_2040",
241
            "CH4_2030",
242
            "e_nom_max_2030",
243
            "e_nom_max_2040",
244
        ]
245
    ).reset_index()
246
247
    grouped_capacities["cap_2035"] = (
248
        grouped_capacities["cap_2035"] * conversion_factor
249
    )
250
251
    # Add generator in Russia of infinite capacity
252
    grouped_capacities = grouped_capacities.append(
253
        {
254
            "cap_2035": 1e9,
255
            "e_nom_max": np.inf,
256
            "ratioConv_2035": 1,
257
            "index": "RU",
258
        },
259
        ignore_index=True,
260
    )
261
262
    # choose capacities for considered countries
263
    grouped_capacities = grouped_capacities[
264
        grouped_capacities["index"].str[:2].isin(countries)
265
    ]
266
    return grouped_capacities
267
268
269
def calc_capacity_per_year(df, lng, year):
270
    """Calculates gas production capacities from TYNDP data for a specified year
271
272
    Parameters
273
    ----------
274
    df : pandas.DataFrame
275
        DataFrame containing all TYNDP data.
276
277
    lng : geopandas.GeoDataFrame
278
        Georeferenced LNG terminal capacities.
279
280
    year : int
281
        Year to calculate gas production capacity for.
282
283
    Returns
284
    -------
285
    pandas.DataFrame
286
        Gas production capacities per foreign node and energy carrier
287
    """
288
    df_conv_peak = (
289
        df[
290
            (df["Parameter"] == "Conventional")
291
            & (df["Year"] == year)
292
            & (df["Case"] == "Peak")
293
        ]
294
        .rename(columns={"Value": f"Value_conv_{year}_peak"})
295
        .drop(columns=["Parameter", "Year", "Case"])
296
    )
297
    df_conv_average = (
298
        df[
299
            (df["Parameter"] == "Conventional")
300
            & (df["Year"] == year)
301
            & (df["Case"] == "Average")
302
        ]
303
        .rename(columns={"Value": f"Value_conv_{year}_average"})
304
        .drop(columns=["Parameter", "Year", "Case"])
305
    )
306
    df_bioch4 = (
307
        df[
308
            (df["Parameter"] == "Biomethane")
309
            & (df["Year"] == year)
310
            & (
311
                df["Case"] == "Peak"
312
            )  # Peak and Average have the same valus for biogas production in 2030 and 2040
313
        ]
314
        .rename(columns={"Value": f"Value_bio_{year}"})
315
        .drop(columns=["Parameter", "Year", "Case"])
316
    )
317
318
    # Some values are duplicated (DE00 in 2030)
319
    df_conv_peak = df_conv_peak[~df_conv_peak.index.duplicated(keep="first")]
320
    df_conv_average = df_conv_average[
321
        ~df_conv_average.index.duplicated(keep="first")
322
    ]
323
324
    df_year = pd.concat(
325
        [df_conv_peak, df_conv_average, df_bioch4, lng], axis=1
326
    ).fillna(0)
327
    df_year = df_year[
328
        ~(
329
            (df_year[f"Value_conv_{year}_peak"] == 0)
330
            & (df_year[f"Value_bio_{year}"] == 0)
331
            & (df_year["LNG max_cap_store2pipe_M_m3_per_d (in GWh/d)"] == 0)
332
        )
333
    ]
334
    df_year[f"Value_conv_{year}"] = (
335
        df_year[f"Value_conv_{year}_peak"]
336
        + df_year["LNG max_cap_store2pipe_M_m3_per_d (in GWh/d)"]
337
    )
338
    df_year[f"CH4_{year}"] = (
339
        df_year[f"Value_conv_{year}"] + df_year[f"Value_bio_{year}"]
340
    )
341
    df_year[f"ratioConv_{year}"] = (
342
        df_year[f"Value_conv_{year}_peak"] / df_year[f"CH4_{year}"]
343
    )
344
    df_year[f"e_nom_max_{year}"] = (
345
        df_year[f"Value_conv_{year}_average"] + df_year[f"Value_bio_{year}"]
346
    )
347
    df_year = df_year.drop(
348
        columns=[
349
            "LNG max_cap_store2pipe_M_m3_per_d (in GWh/d)",
350
            f"Value_conv_{year}_average",
351
            f"Value_conv_{year}_peak",
352
        ]
353
    )
354
355
    return df_year
356
357
358
def insert_generators(gen):
359
    """Insert gas generators for foreign countries based on TYNDP-data
360
361
    Parameters
362
    ----------
363
    gen : pandas.DataFrame
364
        Gas production capacities per foreign node and energy carrier
365
366
    Returns
367
    -------
368
    None.
369
370
    """
371
    sources = config.datasets()["gas_neighbours"]["sources"]
372
    targets = config.datasets()["gas_neighbours"]["targets"]
373
    map_buses = get_map_buses()
374
    scn_params = get_sector_parameters("gas", "eGon2035")
375
376
    # Delete existing data
377
    db.execute_sql(
378
        f"""
379
        DELETE FROM
380
        {targets['generators']['schema']}.{targets['generators']['table']}
381
        WHERE bus IN (
382
            SELECT bus_id FROM
383
            {sources['buses']['schema']}.{sources['buses']['table']}
384
            WHERE country != 'DE'
385
            AND scn_name = 'eGon2035')
386
        AND scn_name = 'eGon2035'
387
        AND carrier = 'CH4';
388
        """
389
    )
390
391
    # Set bus_id
392
    gen.loc[gen[gen["index"].isin(map_buses.keys())].index, "index"] = gen.loc[
393
        gen[gen["index"].isin(map_buses.keys())].index, "index"
394
    ].map(map_buses)
395
    gen.loc[:, "bus"] = (
396
        get_foreign_gas_bus_id().loc[gen.loc[:, "index"]].values
397
    )
398
399
    # Add missing columns
400
    c = {"scn_name": "eGon2035", "carrier": "CH4"}
401
    gen = gen.assign(**c)
402
403
    new_id = db.next_etrago_id("generator")
404
    gen["generator_id"] = range(new_id, new_id + len(gen))
405
    gen["p_nom"] = gen["cap_2035"]
406
    gen["marginal_cost"] = (
407
        gen["ratioConv_2035"] * scn_params["marginal_cost"]["CH4"]
408
        + (1 - gen["ratioConv_2035"]) * scn_params["marginal_cost"]["biogas"]
409
    )
410
411
    # Remove useless columns
412
    gen = gen.drop(columns=["index", "ratioConv_2035", "cap_2035"])
413
414
    # Insert data to db
415
    gen.to_sql(
416
        targets["generators"]["table"],
417
        db.engine(),
418
        schema=targets["generators"]["schema"],
419
        index=False,
420
        if_exists="append",
421
    )
422
423
424
def calc_global_ch4_demand(Norway_global_demand_1y):
425
    """Calculates global CH4 demands abroad for eGon2035 scenario
426
427
    The data comes from TYNDP 2020 according to NEP 2021 from the
428
    scenario 'Distributed Energy', linear interpolate between 2030
429
    and 2040.
430
431
    Returns
432
    -------
433
    pandas.DataFrame
434
        Global (yearly) CH4 final demand per foreign node
435
436
    """
437
438
    sources = config.datasets()["gas_neighbours"]["sources"]
439
440
    file = zipfile.ZipFile(f"tyndp/{sources['tyndp_capacities']}")
441
    df = pd.read_excel(
442
        file.open("TYNDP-2020-Scenario-Datafile.xlsx").read(),
443
        sheet_name="Gas Data",
444
    )
445
446
    df = (
447
        df.query(
448
            'Scenario == "Distributed Energy" & '
449
            'Case == "Average" &'
450
            'Category == "Demand"'
451
        )
452
        .drop(
453
            columns=[
454
                "Generator_ID",
455
                "Climate Year",
456
                "Simulation_ID",
457
                "Node 1",
458
                "Path",
459
                "Direct/Indirect",
460
                "Sector",
461
                "Note",
462
                "Category",
463
                "Case",
464
                "Scenario",
465
            ]
466
        )
467
        .set_index("Node/Line")
468
    )
469
470
    df_2030 = (
471
        df[(df["Parameter"] == "Final demand") & (df["Year"] == 2030)]
472
        .rename(columns={"Value": "Value_2030"})
473
        .drop(columns=["Parameter", "Year"])
474
    )
475
476
    df_2040 = (
477
        df[(df["Parameter"] == "Final demand") & (df["Year"] == 2040)]
478
        .rename(columns={"Value": "Value_2040"})
479
        .drop(columns=["Parameter", "Year"])
480
    )
481
482
    # Conversion GWh/d to MWh/h
483
    conversion_factor = 1000 / 24
484
485
    df_2035 = pd.concat([df_2040, df_2030], axis=1)
486
    df_2035["GlobD_2035"] = (
487
        (df_2035["Value_2030"] + df_2035["Value_2040"]) / 2
488
    ) * conversion_factor
489
    df_2035.loc["NOS0"] = [
490
        0,
491
        0,
492
        Norway_global_demand_1y / 8760,
493
    ]  # Manually add Norway demand
494
    grouped_demands = df_2035.drop(
495
        columns=["Value_2030", "Value_2040"]
496
    ).reset_index()
497
498
    # choose demands for considered countries
499
    return grouped_demands[
500
        grouped_demands["Node/Line"].str[:2].isin(countries)
501
    ]
502
503
504
def import_ch4_demandTS():
505
    """Calculate global CH4 demand in Norway and CH4 demand profile
506
507
    Import from the PyPSA-eur-sec run the timeseries of residential
508
    rural heat per neighbor country. This timeserie is used to
509
    calculate:
510
      * the global (yearly) heat demand of Norway
511
        (that will be supplied by CH4)
512
      * the normalized CH4 hourly resolved demand profile
513
514
    Parameters
515
    ----------
516
    None.
517
518
    Returns
519
    -------
520
    Norway_global_demand: Float
521
        Yearly heat demand of Norway in MWh
522
    neighbor_loads_t: pandas.DataFrame
523
        Normalized CH4 hourly resolved demand profiles per neighbor country
524
525
    """
526
527
    cwd = Path(".")
528
    target_file = (
529
        cwd
530
        / "data_bundle_egon_data"
531
        / "pypsa_eur_sec"
532
        / "2022-07-26-egondata-integration"
533
        / "postnetworks"
534
        / "elec_s_37_lv2.0__Co2L0-1H-T-H-B-I-dist1_2050.nc"
535
    )
536
537
    network = pypsa.Network(str(target_file))
538
539
    # Set country tag for all buses
540
    network.buses.country = network.buses.index.str[:2]
541
    neighbors = network.buses[network.buses.country != "DE"]
542
    neighbors = neighbors[
543
        (neighbors["country"].isin(countries))
544
        & (neighbors["carrier"] == "residential rural heat")
545
    ].drop_duplicates(subset="country")
546
547
    neighbor_loads = network.loads[network.loads.bus.isin(neighbors.index)]
548
    neighbor_loads_t_index = neighbor_loads.index[
549
        neighbor_loads.index.isin(network.loads_t.p_set.columns)
550
    ]
551
    neighbor_loads_t = network.loads_t["p_set"][neighbor_loads_t_index]
552
    Norway_global_demand = neighbor_loads_t[
553
        "NO3 0 residential rural heat"
554
    ].sum()
555
556
    for i in neighbor_loads_t.columns:
557
        neighbor_loads_t[i] = neighbor_loads_t[i] / neighbor_loads_t[i].sum()
558
559
    return Norway_global_demand, neighbor_loads_t
560
561
562
def insert_ch4_demand(global_demand, normalized_ch4_demandTS):
563
    """Insert CH4 demands abroad in the database for eGon2035
564
565
    Parameters
566
    ----------
567
    global_demand : pandas.DataFrame
568
        Global CH4 demand per foreign node in 1 year
569
    gas_demandTS : pandas.DataFrame
570
        Normalized time serie of the demand per foreign country
571
572
    Returns
573
    -------
574
    None.
575
576
    """
577
    sources = config.datasets()["gas_neighbours"]["sources"]
578
    targets = config.datasets()["gas_neighbours"]["targets"]
579
    map_buses = get_map_buses()
580
581
    scn_name = "eGon2035"
582
    carrier = "CH4"
583
584
    # Delete existing data
585
    db.execute_sql(
586
        f"""
587
        DELETE FROM 
588
        {targets['load_timeseries']['schema']}.{targets['load_timeseries']['table']}
589
        WHERE "load_id" IN (
590
            SELECT load_id FROM 
591
            {targets['loads']['schema']}.{targets['loads']['table']}
592
            WHERE bus IN (
593
                SELECT bus_id FROM
594
                {sources['buses']['schema']}.{sources['buses']['table']}
595
                WHERE country != 'DE'
596
                AND scn_name = '{scn_name}')
597
            AND scn_name = '{scn_name}'
598
            AND carrier = '{carrier}'
599
        );
600
        """
601
    )
602
603
    db.execute_sql(
604
        f"""
605
        DELETE FROM
606
        {targets['loads']['schema']}.{targets['loads']['table']}
607
        WHERE bus IN (
608
            SELECT bus_id FROM
609
            {sources['buses']['schema']}.{sources['buses']['table']}
610
            WHERE country != 'DE'
611
            AND scn_name = '{scn_name}')
612
        AND scn_name = '{scn_name}'
613
        AND carrier = '{carrier}'
614
        """
615
    )
616
617
    # Set bus_id
618
    global_demand.loc[
619
        global_demand[global_demand["Node/Line"].isin(map_buses.keys())].index,
620
        "Node/Line",
621
    ] = global_demand.loc[
622
        global_demand[global_demand["Node/Line"].isin(map_buses.keys())].index,
623
        "Node/Line",
624
    ].map(
625
        map_buses
626
    )
627
    global_demand.loc[:, "bus"] = (
628
        get_foreign_gas_bus_id().loc[global_demand.loc[:, "Node/Line"]].values
629
    )
630
631
    # Add missing columns
632
    c = {"scn_name": scn_name, "carrier": carrier}
633
    global_demand = global_demand.assign(**c)
634
635
    new_id = db.next_etrago_id("load")
636
    global_demand["load_id"] = range(new_id, new_id + len(global_demand))
637
638
    ch4_demand_TS = global_demand.copy()
639
    # Remove useless columns
640
    global_demand = global_demand.drop(columns=["Node/Line", "GlobD_2035"])
641
642
    # Insert data to db
643
    global_demand.to_sql(
644
        targets["loads"]["table"],
645
        db.engine(),
646
        schema=targets["loads"]["schema"],
647
        index=False,
648
        if_exists="append",
649
    )
650
651
    # Insert time series
652
    ch4_demand_TS["Node/Line"] = ch4_demand_TS["Node/Line"].replace(
653
        ["UK00"], "GB"
654
    )
655
656
    p_set = []
657
    for index, row in ch4_demand_TS.iterrows():
658
        normalized_TS_df = normalized_ch4_demandTS.loc[
659
            :,
660
            normalized_ch4_demandTS.columns.str.contains(row["Node/Line"][:2]),
661
        ]
662
        p_set.append(
663
            (
664
                normalized_TS_df[normalized_TS_df.columns[0]]
665
                * row["GlobD_2035"]
666
            ).tolist()
667
        )
668
669
    ch4_demand_TS["p_set"] = p_set
670
    ch4_demand_TS["temp_id"] = 1
671
    ch4_demand_TS = ch4_demand_TS.drop(
672
        columns=["Node/Line", "GlobD_2035", "bus", "carrier"]
673
    )
674
675
    # Insert data to DB
676
    ch4_demand_TS.to_sql(
677
        targets["load_timeseries"]["table"],
678
        db.engine(),
679
        schema=targets["load_timeseries"]["schema"],
680
        index=False,
681
        if_exists="append",
682
    )
683
684
685
def calc_ch4_storage_capacities():
686
    target_file = (
687
        Path(".") / "datasets" / "gas_data" / "data" / "IGGIELGN_Storages.csv"
688
    )
689
690
    ch4_storage_capacities = pd.read_csv(
691
        target_file,
692
        delimiter=";",
693
        decimal=".",
694
        usecols=["country_code", "param"],
695
    )
696
697
    ch4_storage_capacities = ch4_storage_capacities[
698
        ch4_storage_capacities["country_code"].isin(countries)
699
    ]
700
701
    map_countries_scigrid = {
702
        "AT": "AT00",
703
        "BE": "BE00",
704
        "CZ": "CZ00",
705
        "DK": "DKE1",
706
        "EE": "EE00",
707
        "EL": "GR00",
708
        "ES": "ES00",
709
        "FI": "FI00",
710
        "FR": "FR00",
711
        "GB": "UK00",
712
        "IT": "ITCN",
713
        "LT": "LT00",
714
        "LV": "LV00",
715
        "MT": "MT00",
716
        "NL": "NL00",
717
        "PL": "PL00",
718
        "PT": "PT00",
719
        "SE": "SE01",
720
    }
721
722
    # Define new columns
723
    max_workingGas_M_m3 = []
724
    end_year = []
725
726
    for index, row in ch4_storage_capacities.iterrows():
727
        param = ast.literal_eval(row["param"])
728
        end_year.append(param["end_year"])
729
        max_workingGas_M_m3.append(param["max_workingGas_M_m3"])
730
731
    end_year = [float("inf") if x == None else x for x in end_year]
732
    ch4_storage_capacities = ch4_storage_capacities.assign(end_year=end_year)
733
    ch4_storage_capacities = ch4_storage_capacities[
734
        ch4_storage_capacities["end_year"] >= 2035
735
    ]
736
737
    # Calculate e_nom
738
    conv_factor = (
739
        10830  # M_m3 to MWh - gross calorific value = 39 MJ/m3 (eurogas.org)
740
    )
741
    ch4_storage_capacities["e_nom"] = [
742
        conv_factor * i for i in max_workingGas_M_m3
743
    ]
744
745
    ch4_storage_capacities.drop(
746
        ["param", "end_year"],
747
        axis=1,
748
        inplace=True,
749
    )
750
751
    ch4_storage_capacities["Country"] = ch4_storage_capacities[
752
        "country_code"
753
    ].map(map_countries_scigrid)
754
    ch4_storage_capacities = ch4_storage_capacities.groupby(
755
        ["country_code"]
756
    ).agg(
757
        {
758
            "e_nom": "sum",
759
            "Country": "first",
760
        },
761
    )
762
763
    ch4_storage_capacities = ch4_storage_capacities.drop(["RU"])
764
    ch4_storage_capacities.loc[:, "bus"] = (
765
        get_foreign_gas_bus_id()
766
        .loc[ch4_storage_capacities.loc[:, "Country"]]
767
        .values
768
    )
769
770
    return ch4_storage_capacities
771
772
773
def insert_storage(ch4_storage_capacities):
774
    sources = config.datasets()["gas_neighbours"]["sources"]
775
    targets = config.datasets()["gas_neighbours"]["targets"]
776
777
    # Clean table
778
    db.execute_sql(
779
        f"""
780
        DELETE FROM {targets['stores']['schema']}.{targets['stores']['table']}  
781
        WHERE "carrier" = 'CH4'
782
        AND scn_name = 'eGon2035'
783
        AND bus IN (
784
            SELECT bus_id FROM {sources['buses']['schema']}.{sources['buses']['table']}
785
            WHERE scn_name = 'eGon2035' 
786
            AND country != 'DE'
787
            );
788
        """
789
    )
790
    # Add missing columns
791
    c = {"scn_name": "eGon2035", "carrier": "CH4"}
792
    ch4_storage_capacities = ch4_storage_capacities.assign(**c)
793
794
    new_id = db.next_etrago_id("store")
795
    ch4_storage_capacities["store_id"] = range(
796
        new_id, new_id + len(ch4_storage_capacities)
797
    )
798
799
    ch4_storage_capacities.drop(
800
        ["Country"],
801
        axis=1,
802
        inplace=True,
803
    )
804
805
    ch4_storage_capacities = ch4_storage_capacities.reset_index(drop=True)
806
    # Insert data to db
807
    ch4_storage_capacities.to_sql(
808
        targets["stores"]["table"],
809
        db.engine(),
810
        schema=targets["stores"]["schema"],
811
        index=False,
812
        if_exists="append",
813
    )
814
815
816
def calc_global_power_to_h2_demand():
817
    """Calculates H2 demand abroad for eGon2035 scenario
818
819
    Calculates global power demand abroad linked to H2 production.
820
    The data comes from TYNDP 2020 according to NEP 2021 from the
821
    scenario 'Distributed Energy', linear interpolate between 2030
822
    and 2040.
823
824
    Returns
825
    -------
826
    pandas.DataFrame
827
        Global power-to-h2 demand per foreign node
828
829
    """
830
    sources = config.datasets()["gas_neighbours"]["sources"]
831
832
    file = zipfile.ZipFile(f"tyndp/{sources['tyndp_capacities']}")
833
    df = pd.read_excel(
834
        file.open("TYNDP-2020-Scenario-Datafile.xlsx").read(),
835
        sheet_name="Gas Data",
836
    )
837
838
    df = (
839
        df.query(
840
            'Scenario == "Distributed Energy" & '
841
            'Case == "Average" &'
842
            'Parameter == "P2H2"'
843
        )
844
        .drop(
845
            columns=[
846
                "Generator_ID",
847
                "Climate Year",
848
                "Simulation_ID",
849
                "Node 1",
850
                "Path",
851
                "Direct/Indirect",
852
                "Sector",
853
                "Note",
854
                "Category",
855
                "Case",
856
                "Scenario",
857
                "Parameter",
858
            ]
859
        )
860
        .set_index("Node/Line")
861
    )
862
863
    df_2030 = (
864
        df[df["Year"] == 2030]
865
        .rename(columns={"Value": "Value_2030"})
866
        .drop(columns=["Year"])
867
    )
868
    df_2040 = (
869
        df[df["Year"] == 2040]
870
        .rename(columns={"Value": "Value_2040"})
871
        .drop(columns=["Year"])
872
    )
873
874
    # Conversion GWh/d to MWh/h
875
    conversion_factor = 1000 / 24
876
877
    df_2035 = pd.concat([df_2040, df_2030], axis=1)
878
    df_2035["GlobD_2035"] = (
879
        (df_2035["Value_2030"] + df_2035["Value_2040"]) / 2
880
    ) * conversion_factor
881
882
    global_power_to_h2_demand = df_2035.drop(
883
        columns=["Value_2030", "Value_2040"]
884
    )
885
886
    # choose demands for considered countries
887
    global_power_to_h2_demand = global_power_to_h2_demand[
888
        (global_power_to_h2_demand.index.str[:2].isin(countries))
889
        & (global_power_to_h2_demand["GlobD_2035"] != 0)
890
    ]
891
892
    # Split in two the demands for DK and UK
893
    global_power_to_h2_demand.loc["DKW1"] = (
894
        global_power_to_h2_demand.loc["DKE1"] / 2
895
    )
896
    global_power_to_h2_demand.loc["DKE1"] = (
897
        global_power_to_h2_demand.loc["DKE1"] / 2
898
    )
899
    global_power_to_h2_demand.loc["UKNI"] = (
900
        global_power_to_h2_demand.loc["UK00"] / 2
901
    )
902
    global_power_to_h2_demand.loc["UK00"] = (
903
        global_power_to_h2_demand.loc["UK00"] / 2
904
    )
905
    global_power_to_h2_demand = global_power_to_h2_demand.reset_index()
906
907
    return global_power_to_h2_demand
908
909
910
def insert_power_to_h2_demand(global_power_to_h2_demand):
911
    """Insert H2 demands into database for eGon2035
912
913
    Detailled description
914
    This function insert data in the database and has no return.
915
916
    Parameters
917
    ----------
918
    global_power_to_h2_demand : pandas.DataFrame
919
        Global H2 demand per foreign node in 1 year
920
921
    """
922
    sources = config.datasets()["gas_neighbours"]["sources"]
923
    targets = config.datasets()["gas_neighbours"]["targets"]
924
    map_buses = get_map_buses()
925
926
    scn_name = "eGon2035"
927
    carrier = "H2_for_industry"
928
929
    db.execute_sql(
930
        f"""
931
        DELETE FROM
932
        {targets['loads']['schema']}.{targets['loads']['table']}
933
        WHERE bus IN (
934
            SELECT bus_id FROM
935
            {sources['buses']['schema']}.{sources['buses']['table']}
936
            WHERE country != 'DE'
937
            AND scn_name = '{scn_name}')
938
        AND scn_name = '{scn_name}'
939
        AND carrier = '{carrier}'
940
        """
941
    )
942
943
    # Set bus_id
944
    global_power_to_h2_demand.loc[
945
        global_power_to_h2_demand[
946
            global_power_to_h2_demand["Node/Line"].isin(map_buses.keys())
947
        ].index,
948
        "Node/Line",
949
    ] = global_power_to_h2_demand.loc[
950
        global_power_to_h2_demand[
951
            global_power_to_h2_demand["Node/Line"].isin(map_buses.keys())
952
        ].index,
953
        "Node/Line",
954
    ].map(
955
        map_buses
956
    )
957
    global_power_to_h2_demand.loc[:, "bus"] = (
958
        get_foreign_bus_id()
959
        .loc[global_power_to_h2_demand.loc[:, "Node/Line"]]
960
        .values
961
    )
962
963
    # Add missing columns
964
    c = {"scn_name": scn_name, "carrier": carrier}
965
    global_power_to_h2_demand = global_power_to_h2_demand.assign(**c)
966
967
    new_id = db.next_etrago_id("load")
968
    global_power_to_h2_demand["load_id"] = range(
969
        new_id, new_id + len(global_power_to_h2_demand)
970
    )
971
972
    global_power_to_h2_demand = global_power_to_h2_demand.rename(
973
        columns={"GlobD_2035": "p_set"}
974
    )
975
976
    power_to_h2_demand_TS = global_power_to_h2_demand.copy()
977
    # Remove useless columns
978
    global_power_to_h2_demand = global_power_to_h2_demand.drop(
979
        columns=["Node/Line"]
980
    )
981
982
    # Insert data to db
983
    global_power_to_h2_demand.to_sql(
984
        targets["loads"]["table"],
985
        db.engine(),
986
        schema=targets["loads"]["schema"],
987
        index=False,
988
        if_exists="append",
989
    )
990
991
992
def calculate_ch4_grid_capacities():
993
    """Calculates CH4 grid capacities for foreign countries based on TYNDP-data
994
995
    Parameters
996
    ----------
997
    None.
998
999
    Returns
1000
    -------
1001
    Neighbouring_pipe_capacities_list : pandas.DataFrame
1002
1003
    """
1004
    sources = config.datasets()["gas_neighbours"]["sources"]
1005
1006
    # Download file
1007
    basename = "ENTSOG_TYNDP_2020_Annex_C2_Capacities_per_country.xlsx"
1008
    url = "https://www.entsog.eu/sites/default/files/2021-07/" + basename
1009
    target_file = Path(".") / "datasets" / "gas_data" / basename
1010
1011
    urlretrieve(url, target_file)
1012
    map_pipelines = {
1013
        "NORDSTREAM": "RU00",
1014
        "NORDSTREAM 2": "RU00",
1015
        "OPAL": "DE",
1016
        "YAMAL (BY)": "RU00",
1017
        "Denmark": "DKE1",
1018
        "Belgium": "BE00",
1019
        "Netherlands": "NL00",
1020
        "Norway": "NOM1",
1021
        "Switzerland": "CH00",
1022
        "Poland": "PL00",
1023
        "United Kingdom": "UK00",
1024
        "Germany": "DE",
1025
        "Austria": "AT00",
1026
        "France": "FR00",
1027
        "Czechia": "CZ00",
1028
        "Russia": "RU00",
1029
        "Luxemburg": "LUB1",
1030
    }
1031
1032
    grid_countries = [
1033
        "NORDSTREAM",
1034
        "NORDSTREAM 2",
1035
        "OPAL",
1036
        "YAMAL (BY)",
1037
        "Denmark",
1038
        "Belgium",
1039
        "Netherlands",
1040
        "Norway",
1041
        "Switzerland",
1042
        "Poland",
1043
        "United Kingdom",
1044
        "Germany",
1045
        "Austria",
1046
        "France",
1047
        "Czechia",
1048
        "Russia",
1049
        "Luxemburg",
1050
    ]
1051
1052
    # Read-in data from csv-file
1053
    pipe_capacities_list = pd.read_excel(
1054
        target_file,
1055
        sheet_name="Transmission Peak Capacity",
1056
        skiprows=range(4),
1057
    )
1058
    pipe_capacities_list = pipe_capacities_list[
1059
        ["To Country", "Unnamed: 3", "From Country", 2035]
1060
    ].rename(
1061
        columns={
1062
            "Unnamed: 3": "Scenario",
1063
            "To Country": "To_Country",
1064
            "From Country": "From_Country",
1065
        }
1066
    )
1067
    pipe_capacities_list["To_Country"] = pd.Series(
1068
        pipe_capacities_list["To_Country"]
1069
    ).fillna(method="ffill")
1070
    pipe_capacities_list["From_Country"] = pd.Series(
1071
        pipe_capacities_list["From_Country"]
1072
    ).fillna(method="ffill")
1073
    pipe_capacities_list = pipe_capacities_list[
1074
        pipe_capacities_list["Scenario"] == "Advanced"
1075
    ].drop(columns={"Scenario"})
1076
    pipe_capacities_list = pipe_capacities_list[
1077
        (
1078
            (pipe_capacities_list["To_Country"].isin(grid_countries))
1079
            & (pipe_capacities_list["From_Country"].isin(grid_countries))
1080
        )
1081
        & (pipe_capacities_list[2035] != 0)
1082
    ]
1083
    pipe_capacities_list["To_Country"] = pipe_capacities_list[
1084
        "To_Country"
1085
    ].map(map_pipelines)
1086
    pipe_capacities_list["From_Country"] = pipe_capacities_list[
1087
        "From_Country"
1088
    ].map(map_pipelines)
1089
    pipe_capacities_list["countrycombination"] = pipe_capacities_list[
1090
        ["To_Country", "From_Country"]
1091
    ].apply(
1092
        lambda x: tuple(sorted([str(x.To_Country), str(x.From_Country)])),
1093
        axis=1,
1094
    )
1095
1096
    pipeline_strategies = {
1097
        "To_Country": "first",
1098
        "From_Country": "first",
1099
        2035: sum,
1100
    }
1101
1102
    pipe_capacities_list = pipe_capacities_list.groupby(
1103
        ["countrycombination"]
1104
    ).agg(pipeline_strategies)
1105
1106
    # Add manually DK-SE and AT-CH pipes (Scigrid gas data)
1107
    pipe_capacities_list.loc["(DKE1, SE02)"] = ["DKE1", "SE02", 651]
1108
    pipe_capacities_list.loc["(AT00, CH00)"] = ["AT00", "CH00", 651]
1109
1110
    # Conversion GWh/d to MWh/h
1111
    pipe_capacities_list["p_nom"] = pipe_capacities_list[2035] * (1000 / 24)
1112
1113
    # Border crossing CH4 pipelines between foreign countries
1114
1115
    Neighbouring_pipe_capacities_list = pipe_capacities_list[
1116
        (pipe_capacities_list["To_Country"] != "DE")
1117
        & (pipe_capacities_list["From_Country"] != "DE")
1118
    ].reset_index()
1119
1120
    Neighbouring_pipe_capacities_list.loc[:, "bus0"] = (
1121
        get_foreign_gas_bus_id()
1122
        .loc[Neighbouring_pipe_capacities_list.loc[:, "To_Country"]]
1123
        .values
1124
    )
1125
    Neighbouring_pipe_capacities_list.loc[:, "bus1"] = (
1126
        get_foreign_gas_bus_id()
1127
        .loc[Neighbouring_pipe_capacities_list.loc[:, "From_Country"]]
1128
        .values
1129
    )
1130
1131
    # Adjust columns
1132
    Neighbouring_pipe_capacities_list = Neighbouring_pipe_capacities_list.drop(
1133
        columns=[
1134
            "To_Country",
1135
            "From_Country",
1136
            "countrycombination",
1137
            2035,
1138
        ]
1139
    )
1140
1141
    new_id = db.next_etrago_id("link")
1142
    Neighbouring_pipe_capacities_list["link_id"] = range(
1143
        new_id, new_id + len(Neighbouring_pipe_capacities_list)
1144
    )
1145
1146
    # Border crossing CH4 pipelines between DE and neighbouring countries
1147
    DE_pipe_capacities_list = pipe_capacities_list[
1148
        (pipe_capacities_list["To_Country"] == "DE")
1149
        | (pipe_capacities_list["From_Country"] == "DE")
1150
    ].reset_index()
1151
1152
    dict_cross_pipes_DE = {
1153
        ("AT00", "DE"): "AT",
1154
        ("BE00", "DE"): "BE",
1155
        ("CH00", "DE"): "CH",
1156
        ("CZ00", "DE"): "CZ",
1157
        ("DE", "DKE1"): "DK",
1158
        ("DE", "FR00"): "FR",
1159
        ("DE", "LUB1"): "LU",
1160
        ("DE", "NL00"): "NL",
1161
        ("DE", "NOM1"): "NO",
1162
        ("DE", "PL00"): "PL",
1163
        ("DE", "RU00"): "RU",
1164
    }
1165
1166
    DE_pipe_capacities_list["country_code"] = DE_pipe_capacities_list[
1167
        "countrycombination"
1168
    ].map(dict_cross_pipes_DE)
1169
    DE_pipe_capacities_list = DE_pipe_capacities_list.set_index("country_code")
1170
1171
    for country_code in [e for e in countries if e not in ("GB", "SE", "UK")]:
1172
1173
        # Select cross-bording links
1174
        cap_DE = db.select_dataframe(
1175
            f"""SELECT link_id, bus0, bus1
1176
                FROM {sources['links']['schema']}.{sources['links']['table']}
1177
                    WHERE scn_name = 'eGon2035' 
1178
                    AND carrier = 'CH4'
1179
                    AND (("bus0" IN (
1180
                        SELECT bus_id FROM {sources['buses']['schema']}.{sources['buses']['table']}
1181
                            WHERE country = 'DE'
1182
                            AND carrier = 'CH4'
1183
                            AND scn_name = 'eGon2035')
1184
                        AND "bus1" IN (SELECT bus_id FROM {sources['buses']['schema']}.{sources['buses']['table']}
1185
                            WHERE country = '{country_code}'
1186
                            AND carrier = 'CH4'
1187
                            AND scn_name = 'eGon2035')
1188
                    )
1189
                    OR ("bus0" IN (
1190
                        SELECT bus_id FROM {sources['buses']['schema']}.{sources['buses']['table']}
1191
                            WHERE country = '{country_code}'
1192
                            AND carrier = 'CH4'
1193
                            AND scn_name = 'eGon2035')
1194
                        AND "bus1" IN (SELECT bus_id FROM {sources['buses']['schema']}.{sources['buses']['table']}
1195
                            WHERE country = 'DE'
1196
                            AND carrier = 'CH4'
1197
                            AND scn_name = 'eGon2035'))
1198
                    )
1199
            ;"""
1200
        )
1201
1202
        cap_DE["p_nom"] = DE_pipe_capacities_list.at[
1203
            country_code, "p_nom"
1204
        ] / len(cap_DE.index)
1205
        Neighbouring_pipe_capacities_list = (
1206
            Neighbouring_pipe_capacities_list.append(cap_DE)
1207
        )
1208
1209
    # Add topo, geom and length
1210
    bus_geom = db.select_geodataframe(
1211
        """SELECT bus_id, geom
1212
        FROM grid.egon_etrago_bus
1213
        WHERE scn_name = 'eGon2035'
1214
        AND carrier = 'CH4'
1215
        """,
1216
        epsg=4326,
1217
    ).set_index("bus_id")
1218
1219
    coordinates_bus0 = []
1220
    coordinates_bus1 = []
1221
1222
    for index, row in Neighbouring_pipe_capacities_list.iterrows():
1223
        coordinates_bus0.append(bus_geom["geom"].loc[int(row["bus0"])])
1224
        coordinates_bus1.append(bus_geom["geom"].loc[int(row["bus1"])])
1225
1226
    Neighbouring_pipe_capacities_list["coordinates_bus0"] = coordinates_bus0
1227
    Neighbouring_pipe_capacities_list["coordinates_bus1"] = coordinates_bus1
1228
1229
    Neighbouring_pipe_capacities_list[
1230
        "topo"
1231
    ] = Neighbouring_pipe_capacities_list.apply(
1232
        lambda row: LineString(
1233
            [row["coordinates_bus0"], row["coordinates_bus1"]]
1234
        ),
1235
        axis=1,
1236
    )
1237
    Neighbouring_pipe_capacities_list[
1238
        "geom"
1239
    ] = Neighbouring_pipe_capacities_list.apply(
1240
        lambda row: MultiLineString([row["topo"]]), axis=1
1241
    )
1242
    Neighbouring_pipe_capacities_list[
1243
        "length"
1244
    ] = Neighbouring_pipe_capacities_list.apply(
1245
        lambda row: row["topo"].length, axis=1
1246
    )
1247
1248
    # Remove useless columns
1249
    Neighbouring_pipe_capacities_list = Neighbouring_pipe_capacities_list.drop(
1250
        columns=[
1251
            "coordinates_bus0",
1252
            "coordinates_bus1",
1253
        ]
1254
    )
1255
1256
    # Add missing columns
1257
    c = {"scn_name": "eGon2035", "carrier": "CH4"}
1258
    Neighbouring_pipe_capacities_list = (
1259
        Neighbouring_pipe_capacities_list.assign(**c)
1260
    )
1261
1262
    Neighbouring_pipe_capacities_list = (
1263
        Neighbouring_pipe_capacities_list.set_geometry("geom", crs=4326)
1264
    )
1265
1266
    return Neighbouring_pipe_capacities_list
1267
1268
1269
def tyndp_gas_generation():
1270
    """Insert data from TYNDP 2020 accordning to NEP 2021
1271
    Scenario 'Distributed Energy', linear interpolate between 2030 and 2040
1272
1273
    Returns
1274
    -------
1275
    None.
1276
    """
1277
    capacities = calc_capacities()
1278
    insert_generators(capacities)
1279
1280
    ch4_storage_capacities = calc_ch4_storage_capacities()
1281
    insert_storage(ch4_storage_capacities)
1282
1283
1284
def tyndp_gas_demand():
1285
    """Insert gas demands abroad for eGon2035
1286
1287
    Insert CH4 and H2 demands abroad for eGon2035 by executing the
1288
    following steps:
1289
      * CH4
1290
          * Calculation of the global CH4 demand in Norway and of the
1291
            CH4 demand profile by executing the function
1292
            :py:func:`import_ch4_demandTS`
1293
          * Calculation of the global CH4 demands by executing the
1294
            function :py:func:`calc_global_ch4_demand`
1295
          * Insertion the CH4 loads and their associated time series
1296
            in the database by executing the function
1297
            :py:func:`insert_ch4_demand`
1298
      * H2
1299
          * Calculation of the global power demand abroad linked
1300
            to H2 production by executing the function
1301
            :py:func:`calc_global_power_to_h2_demand`
1302
          * Insertion of these loads in the database by executing the
1303
            function :py:func:`insert_power_to_h2_demand`
1304
    This function insert data in the database and has no return.
1305
1306
    """
1307
    Norway_global_demand_1y, normalized_ch4_demandTS = import_ch4_demandTS()
1308
    global_ch4_demand = calc_global_ch4_demand(Norway_global_demand_1y)
1309
    insert_ch4_demand(global_ch4_demand, normalized_ch4_demandTS)
1310
1311
    global_power_to_h2_demand = calc_global_power_to_h2_demand()
1312
    insert_power_to_h2_demand(global_power_to_h2_demand)
1313
1314
1315
def grid():
1316
    """Insert data from TYNDP 2020 accordning to NEP 2021
1317
    Scenario 'Distributed Energy', linear interpolate between 2030 and 2040
1318
1319
    Returns
1320
    -------
1321
    None.
1322
    """
1323
    Neighbouring_pipe_capacities_list = calculate_ch4_grid_capacities()
1324
    insert_gas_grid_capacities(
1325
        Neighbouring_pipe_capacities_list, scn_name="eGon2035"
1326
    )
1327