Passed
Pull Request — dev (#931)
by
unknown
01:31
created

get_foreign_gas_bus_id()   A

Complexity

Conditions 2

Size

Total Lines 54
Code Lines 23

Duplication

Lines 54
Ratio 100 %

Importance

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