Passed
Pull Request — dev (#936)
by
unknown
01:40
created

insert_power_to_h2_demand()   B

Complexity

Conditions 2

Size

Total Lines 134
Code Lines 73

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 73
dl 0
loc 134
rs 7.8836
c 0
b 0
f 0
cc 2
nop 2

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