entsoe_historic_generation_capacities()   C
last analyzed

Complexity

Conditions 7

Size

Total Lines 84
Code Lines 66

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 66
dl 0
loc 84
rs 6.7127
c 0
b 0
f 0
cc 7
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
"""The central module containing all code dealing with electrical neighbours"""
2
3
from os import path
4
from pathlib import Path
5
import datetime
6
import logging
7
import os.path
8
import zipfile
9
10
from shapely.geometry import LineString
11
from sqlalchemy.orm import sessionmaker
12
import entsoe
13
import geopandas as gpd
14
import pandas as pd
15
import requests
16
17
from egon.data import config, db, logger
18
from egon.data.datasets import Dataset, wrapped_partial
19
from egon.data.datasets.fill_etrago_gen import add_marginal_costs
20
from egon.data.datasets.fix_ehv_subnetworks import select_bus_id
21
from egon.data.datasets.pypsaeur import prepared_network
22
from egon.data.datasets.scenario_parameters import get_sector_parameters
23
from egon.data.db import session_scope
24
import egon.data.datasets.etrago_setup as etrago
25
import egon.data.datasets.scenario_parameters.parameters as scenario_parameters
26
27
28
def get_cross_border_buses(scenario, sources):
29
    """Returns buses from osmTGmod which are outside of Germany.
30
31
    Parameters
32
    ----------
33
    sources : dict
34
        List of sources
35
36
    Returns
37
    -------
38
    geopandas.GeoDataFrame
39
        Electricity buses outside of Germany
40
41
    """
42
    return db.select_geodataframe(
43
        f"""
44
        SELECT *
45
        FROM {sources['electricity_buses']['schema']}.
46
            {sources['electricity_buses']['table']}
47
        WHERE
48
        NOT ST_INTERSECTS (
49
            geom,
50
            (SELECT ST_Transform(ST_Buffer(geometry, 5), 4326) FROM
51
             {sources['german_borders']['schema']}.
52
            {sources['german_borders']['table']}))
53
        AND (bus_id IN (
54
            SELECT bus0 FROM
55
            {sources['lines']['schema']}.{sources['lines']['table']})
56
            OR bus_id IN (
57
            SELECT bus1 FROM
58
            {sources['lines']['schema']}.{sources['lines']['table']}))
59
        AND scn_name = '{scenario}';
60
        """,
61
        epsg=4326,
62
    )
63
64
65
def get_cross_border_lines(scenario, sources):
66
    """Returns lines from osmTGmod which end or start outside of Germany.
67
68
    Parameters
69
    ----------
70
    sources : dict
71
        List of sources
72
73
    Returns
74
    -------
75
    geopandas.GeoDataFrame
76
        AC-lines outside of Germany
77
78
    """
79
    return db.select_geodataframe(
80
        f"""
81
    SELECT *
82
    FROM {sources['lines']['schema']}.{sources['lines']['table']} a
83
    WHERE
84
    ST_INTERSECTS (
85
        a.topo,
86
        (SELECT ST_Transform(ST_boundary(geometry), 4326)
87
         FROM {sources['german_borders']['schema']}.
88
        {sources['german_borders']['table']}))
89
    AND scn_name = '{scenario}';
90
    """,
91
        epsg=4326,
92
    )
93
94
95
def central_buses_pypsaeur(sources, scenario):
96
    """Returns buses in the middle of foreign countries based on prepared pypsa-eur network
97
98
    Parameters
99
    ----------
100
    sources : dict
101
        List of sources
102
103
    Returns
104
    -------
105
    pandas.DataFrame
106
        Buses in the center of foreign countries
107
108
    """
109
110
    wanted_countries = [
111
        "AT",
112
        "CH",
113
        "CZ",
114
        "PL",
115
        "SE",
116
        "NO",
117
        "DK",
118
        "GB",
119
        "NL",
120
        "BE",
121
        "FR",
122
        "LU",
123
    ]
124
    network = prepared_network()
125
126
    df = network.buses[
127
        (network.buses.carrier == "AC")
128
        & (network.buses.country.isin(wanted_countries))
129
    ]
130
131
    return df
132
133
134
def buses(scenario, sources, targets):
135
    """Insert central buses in foreign countries per scenario
136
137
    Parameters
138
    ----------
139
    sources : dict
140
        List of dataset sources
141
    targets : dict
142
        List of dataset targets
143
144
    Returns
145
    -------
146
    central_buses : geoapndas.GeoDataFrame
147
        Buses in the center of foreign countries
148
149
    """
150
    sql_delete = f"""
151
        DELETE FROM {sources['electricity_buses']['schema']}.
152
            {sources['electricity_buses']['table']}
153
        WHERE country != 'DE' AND scn_name = '{scenario}'
154
        AND carrier = 'AC'
155
        AND bus_id NOT IN (
156
            SELECT bus_i
157
            FROM  {sources['osmtgmod_bus']['schema']}.
158
            {sources['osmtgmod_bus']['table']})
159
        """
160
161
    # Delete existing buses
162
    db.execute_sql(sql_delete)
163
164
    central_buses = central_buses_pypsaeur(sources, scenario)
165
166
    next_bus_id = db.next_etrago_id("bus") + 1
167
168
    central_buses["bus_id"] = central_buses.reset_index().index + next_bus_id
169
170
    next_bus_id += len(central_buses)
171
172
    # if in test mode, add bus in center of Germany
173
    if config.settings()["egon-data"]["--dataset-boundary"] != "Everything":
174
        central_buses = pd.concat(
175
            [
176
                central_buses,
177
                pd.DataFrame(
178
                    index=[central_buses.bus_id.max() + 1],
179
                    data={
180
                        "scn_name": scenario,
181
                        "bus_id": next_bus_id,
182
                        "x": 10.4234469,
183
                        "y": 51.0834196,
184
                        "country": "DE",
185
                        "carrier": "AC",
186
                        "v_nom": 380.0,
187
                    },
188
                ),
189
            ],
190
            ignore_index=True,
191
        )
192
        next_bus_id += 1
193
194
    # Add buses for other voltage levels
195
    foreign_buses = get_cross_border_buses(scenario, sources)
196
    if config.settings()["egon-data"]["--dataset-boundary"] == "Everything":
197
        foreign_buses = foreign_buses[foreign_buses.country != "DE"]
198
    vnom_per_country = foreign_buses.groupby("country").v_nom.unique().copy()
199
    for cntr in vnom_per_country.index:
200
        print(cntr)
201 View Code Duplication
        if 110.0 in vnom_per_country[cntr]:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
202
            central_buses = pd.concat(
203
                [
204
                    central_buses,
205
                    pd.DataFrame(
206
                        index=[next_bus_id],
207
                        data={
208
                            "scn_name": scenario,
209
                            "bus_id": next_bus_id,
210
                            "x": central_buses[
211
                                central_buses.country == cntr
212
                            ].x.unique()[0],
213
                            "y": central_buses[
214
                                central_buses.country == cntr
215
                            ].y.unique()[0],
216
                            "country": cntr,
217
                            "carrier": "AC",
218
                            "v_nom": 110.0,
219
                        },
220
                    ),
221
                ],
222
                ignore_index=True,
223
            )
224
            next_bus_id += 1
225
226 View Code Duplication
        if 220.0 in vnom_per_country[cntr]:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
227
            central_buses = pd.concat(
228
                [
229
                    central_buses,
230
                    pd.DataFrame(
231
                        index=[next_bus_id],
232
                        data={
233
                            "scn_name": scenario,
234
                            "bus_id": next_bus_id,
235
                            "x": central_buses[
236
                                central_buses.country == cntr
237
                            ].x.unique()[0],
238
                            "y": central_buses[
239
                                central_buses.country == cntr
240
                            ].y.unique()[0],
241
                            "country": cntr,
242
                            "carrier": "AC",
243
                            "v_nom": 220.0,
244
                        },
245
                    ),
246
                ],
247
                ignore_index=True,
248
            )
249
            next_bus_id += 1
250
251
    # Add geometry column
252
    central_buses = gpd.GeoDataFrame(
253
        central_buses,
254
        geometry=gpd.points_from_xy(central_buses.x, central_buses.y),
255
        crs="EPSG:4326",
256
    )
257
    central_buses["geom"] = central_buses.geometry.copy()
258
    central_buses = central_buses.set_geometry("geom").drop(
259
        "geometry", axis="columns"
260
    )
261
    central_buses.scn_name = scenario
262
263
    central_buses.drop(
264
        [
265
            "control",
266
            "generator",
267
            "location",
268
            "unit",
269
            "sub_network",
270
            "substation_off",
271
            "substation_lv",
272
        ],
273
        axis="columns",
274
        inplace=True,
275
        errors="ignore",
276
    )
277
278
    # Insert all central buses for eGon2035
279
    if scenario in [
280
        "eGon2035",
281
        "status2019",
282
        "status2023",
283
    ]:  # TODO: status2023 this is hardcoded shit
284
        central_buses.to_postgis(
285
            targets["buses"]["table"],
286
            schema=targets["buses"]["schema"],
287
            if_exists="append",
288
            con=db.engine(),
289
            index=False,
290
        )
291
    # Insert only buses for eGon100RE that are not coming from pypsa-eur-sec
292
    # (buses with another voltage_level or inside Germany in test mode)
293
    else:
294
        central_buses[central_buses.carrier == "AC"].to_postgis(
295
            targets["buses"]["table"],
296
            schema=targets["buses"]["schema"],
297
            if_exists="append",
298
            con=db.engine(),
299
            index=False,
300
        )
301
302
    return central_buses
303
304
305
def lines_between_foreign_countries(scenario, sorces, targets, central_buses):
306
    # import network from pypsa-eur
307
    network = prepared_network()
308
309
    gdf_buses = gpd.GeoDataFrame(
310
        network.buses,
311
        geometry=gpd.points_from_xy(network.buses.x, network.buses.y),
312
    )
313
314
    central_buses_pypsaeur = gpd.sjoin(
315
        gdf_buses[gdf_buses.carrier == "AC"], central_buses
316
    )
317
318
    central_buses_pypsaeur = central_buses_pypsaeur[
319
        central_buses_pypsaeur.v_nom_right == 380
320
    ]
321
322
    lines_to_add = network.lines[
323
        (network.lines.bus0.isin(central_buses_pypsaeur.index))
324
        & (network.lines.bus1.isin(central_buses_pypsaeur.index))
325
    ]
326
327
    lines_to_add.loc[:, "lifetime"] = get_sector_parameters(
328
        "electricity", scenario
329
    )["lifetime"]["ac_ehv_overhead_line"]
330
    lines_to_add.loc[:, "line_id"] = (
331
        lines_to_add.reset_index().index.astype(int)
332
        + db.next_etrago_id("line")
333
        + 1
334
    )
335
336
    links_to_add = network.links[
337
        (network.links.bus0.isin(central_buses_pypsaeur.index))
338
        & (network.links.bus1.isin(central_buses_pypsaeur.index))
339
    ]
340
341
    links_to_add.loc[:, "lifetime"] = get_sector_parameters(
342
        "electricity", scenario
343
    )["lifetime"]["dc_overhead_line"]
344
    links_to_add.loc[:, "link_id"] = (
345
        links_to_add.reset_index().index.astype(int)
346
        + db.next_etrago_id("link")
347
        + 1
348
    )
349
350
    for df in [lines_to_add, links_to_add]:
351
        df.loc[:, "scn_name"] = scenario
352
        gdf = gpd.GeoDataFrame(df)
353
        gdf["geom_bus0"] = gdf_buses.geometry[df.bus0].values
354
        gdf["geom_bus1"] = gdf_buses.geometry[df.bus1].values
355
        gdf["geometry"] = gdf.apply(
356
            lambda x: LineString([x["geom_bus0"], x["geom_bus1"]]),
357
            axis=1,
358
        )
359
360
        gdf = gdf.set_geometry("geometry")
361
        gdf = gdf.set_crs(4326)
362
363
        gdf = gdf.rename_geometry("topo")
364
365
        gdf.loc[:, "bus0"] = central_buses_pypsaeur.bus_id.loc[df.bus0].values
366
        gdf.loc[:, "bus1"] = central_buses_pypsaeur.bus_id.loc[df.bus1].values
367
368
        gdf.drop(["geom_bus0", "geom_bus1"], inplace=True, axis="columns")
369
        if "link_id" in df.columns:
370
            table_name = "link"
371
            gdf.drop(
372
                [
373
                    "tags",
374
                    "under_construction",
375
                    "underground",
376
                    "underwater_fraction",
377
                    "bus2",
378
                    "efficiency2",
379
                    "length_original",
380
                    "bus4",
381
                    "efficiency4",
382
                    "reversed",
383
                    "ramp_limit_up",
384
                    "ramp_limit_down",
385
                    "p_nom_opt",
386
                    "bus3",
387
                    "efficiency3",
388
                    "location",
389
                    "project_status",
390
                    "dc",
391
                    "voltage",
392
                ],
393
                axis="columns",
394
                inplace=True,
395
            )
396
        else:
397
            table_name = "line"
398
            gdf.drop(
399
                [
400
                    "i_nom",
401
                    "sub_network",
402
                    "x_pu",
403
                    "r_pu",
404
                    "g_pu",
405
                    "b_pu",
406
                    "x_pu_eff",
407
                    "r_pu_eff",
408
                    "s_nom_opt",
409
                    "dc",
410
                ],
411
                axis="columns",
412
                inplace=True,
413
            )
414
415
        gdf = gdf.set_index(f"{table_name}_id")
416
        gdf.to_postgis(
417
            f"egon_etrago_{table_name}",
418
            db.engine(),
419
            schema="grid",
420
            if_exists="append",
421
            index=True,
422
            index_label=f"{table_name}_id",
423
        )
424
425
426
def cross_border_lines(scenario, sources, targets, central_buses):
427
    """Adds lines which connect border-crossing lines from osmtgmod
428
    to the central buses in the corresponding neigbouring country
429
430
    Parameters
431
    ----------
432
    sources : dict
433
        List of dataset sources
434
    targets : dict
435
        List of dataset targets
436
    central_buses : geopandas.GeoDataFrame
437
        Buses in the center of foreign countries
438
439
    Returns
440
    -------
441
    new_lines : geopandas.GeoDataFrame
442
        Lines that connect cross-border lines to central bus per country
443
444
    """
445
    # Delete existing data
446
    db.execute_sql(
447
        f"""
448
        DELETE FROM {targets['lines']['schema']}.
449
        {targets['lines']['table']}
450
        WHERE scn_name = '{scenario}'
451
        AND line_id NOT IN (
452
            SELECT branch_id
453
            FROM  {sources['osmtgmod_branch']['schema']}.
454
            {sources['osmtgmod_branch']['table']}
455
              WHERE result_id = 1 and (link_type = 'line' or
456
                                       link_type = 'cable'))
457
        AND bus0 IN (
458
            SELECT bus_i
459
            FROM  {sources['osmtgmod_bus']['schema']}.
460
            {sources['osmtgmod_bus']['table']})
461
        AND bus1 NOT IN (
462
            SELECT bus_i
463
            FROM  {sources['osmtgmod_bus']['schema']}.
464
            {sources['osmtgmod_bus']['table']})
465
        """
466
    )
467
468
    # Calculate cross-border busses and lines from osmtgmod
469
    foreign_buses = get_cross_border_buses(scenario, sources)
470
    foreign_buses.dropna(subset="country", inplace=True)
471
472
    if config.settings()["egon-data"]["--dataset-boundary"] == "Everything":
473
        foreign_buses = foreign_buses[foreign_buses.country != "DE"]
474
    lines = get_cross_border_lines(scenario, sources)
475
476
    # Select bus outside of Germany from border-crossing lines
477
    lines.loc[
478
        lines[lines.bus0.isin(foreign_buses.bus_id)].index, "foreign_bus"
479
    ] = lines.loc[lines[lines.bus0.isin(foreign_buses.bus_id)].index, "bus0"]
480
    lines.loc[
481
        lines[lines.bus1.isin(foreign_buses.bus_id)].index, "foreign_bus"
482
    ] = lines.loc[lines[lines.bus1.isin(foreign_buses.bus_id)].index, "bus1"]
483
484
    # Drop lines with start and endpoint in Germany
485
    lines = lines[lines.foreign_bus.notnull()]
486
    lines.loc[:, "foreign_bus"] = lines.loc[:, "foreign_bus"].astype(int)
487
488
    # Copy all parameters from border-crossing lines
489
    new_lines = lines.copy().set_crs(4326)
490
491
    # Set bus0 as foreign_bus from osmtgmod
492
    new_lines.bus0 = new_lines.foreign_bus.copy()
493
    new_lines.bus0 = new_lines.bus0.astype(int)
494
495
    # Add country tag and set index
496
    new_lines["country"] = (
497
        foreign_buses.set_index("bus_id")
498
        .loc[lines.foreign_bus, "country"]
499
        .values
500
    )
501
502
    if config.settings()["egon-data"]["--dataset-boundary"] == "Everything":
503
        new_lines = new_lines[~new_lines.country.isnull()]
504
    new_lines.line_id = range(
505
        db.next_etrago_id("line"), db.next_etrago_id("line") + len(new_lines)
506
    )
507
508
    # Set bus in center of foreign countries as bus1
509
    for i, row in new_lines.iterrows():
510
        print(row)
511
        new_lines.loc[i, "bus1"] = central_buses.bus_id[
512
            (central_buses.country == row.country)
513
            & (central_buses.v_nom == row.v_nom)
514
        ].values[0]
515
516
    # Create geometry for new lines
517
    new_lines["geom_bus0"] = (
518
        foreign_buses.set_index("bus_id").geom[new_lines.bus0].values
519
    )
520
    new_lines["geom_bus1"] = (
521
        central_buses.set_index("bus_id").geom[new_lines.bus1].values
522
    )
523
    new_lines["topo"] = new_lines.apply(
524
        lambda x: LineString([x["geom_bus0"], x["geom_bus1"]]), axis=1
525
    )
526
527
    # Set topo as geometry column
528
    new_lines = new_lines.set_geometry("topo").set_crs(4326)
529
    # Calcultae length of lines based on topology
530
    old_length = new_lines["length"].copy()
531
    new_lines["length"] = new_lines.to_crs(3035).length / 1000
532
533
    if (new_lines["length"] == 0).any():
534
        print("WARNING! THERE ARE LINES WITH LENGTH = 0")
535
        condition = new_lines["length"] != 0
536
        new_lines["length"] = new_lines["length"].where(condition, 1)
537
538
    # Set electrical parameters based on lines from osmtgmod
539
    for parameter in ["x", "r"]:
540
        new_lines[parameter] = (
541
            new_lines[parameter] / old_length * new_lines["length"]
542
        )
543
    for parameter in ["b", "g"]:
544
        new_lines[parameter] = (
545
            new_lines[parameter] * old_length / new_lines["length"]
546
        )
547
548
    # Drop intermediate columns
549
    new_lines.drop(
550
        ["foreign_bus", "country", "geom_bus0", "geom_bus1", "geom"],
551
        axis="columns",
552
        inplace=True,
553
    )
554
555
    new_lines = new_lines[new_lines.bus0 != new_lines.bus1]
556
557
    new_lines["cables"] = new_lines["cables"].apply(int)
558
559
    # Insert lines to the database
560
    new_lines.to_postgis(
561
        targets["lines"]["table"],
562
        schema=targets["lines"]["schema"],
563
        if_exists="append",
564
        con=db.engine(),
565
        index=False,
566
    )
567
568
    return new_lines
569
570
571
def choose_transformer(s_nom):
572
    """Select transformer and parameters from existing data in the grid model
573
574
    It is assumed that transformers in the foreign countries are not limiting
575
    the electricity flow, so the capacitiy s_nom is set to the minimum sum
576
    of attached AC-lines.
577
    The electrical parameters are set according to already inserted
578
    transformers in the grid model for Germany.
579
580
    Parameters
581
    ----------
582
    s_nom : float
583
        Minimal sum of nominal power of lines at one side
584
585
    Returns
586
    -------
587
    int
588
        Selected transformer nominal power
589
    float
590
        Selected transformer nominal impedance
591
592
    """
593
594
    if s_nom <= 600:
595
        return 600, 0.0002
596
    elif (s_nom > 600) & (s_nom <= 1200):
597
        return 1200, 0.0001
598
    elif (s_nom > 1200) & (s_nom <= 1600):
599
        return 1600, 0.000075
600
    elif (s_nom > 1600) & (s_nom <= 2100):
601
        return 2100, 0.00006667
602
    elif (s_nom > 2100) & (s_nom <= 2600):
603
        return 2600, 0.0000461538
604
    elif (s_nom > 2600) & (s_nom <= 4800):
605
        return 4800, 0.000025
606
    elif (s_nom > 4800) & (s_nom <= 6000):
607
        return 6000, 0.0000225
608
    elif (s_nom > 6000) & (s_nom <= 7200):
609
        return 7200, 0.0000194444
610
    elif (s_nom > 7200) & (s_nom <= 8000):
611
        return 8000, 0.000016875
612
    elif (s_nom > 8000) & (s_nom <= 9000):
613
        return 9000, 0.000015
614
    elif (s_nom > 9000) & (s_nom <= 13000):
615
        return 13000, 0.0000103846
616
    elif (s_nom > 13000) & (s_nom <= 20000):
617
        return 20000, 0.00000675
618
    elif (s_nom > 20000) & (s_nom <= 33000):
619
        return 33000, 0.00000409091
620
621
622
def central_transformer(scenario, sources, targets, central_buses, new_lines):
623
    """Connect central foreign buses with different voltage levels
624
625
    Parameters
626
    ----------
627
    sources : dict
628
        List of dataset sources
629
    targets : dict
630
        List of dataset targets
631
    central_buses : geopandas.GeoDataFrame
632
        Buses in the center of foreign countries
633
    new_lines : geopandas.GeoDataFrame
634
        Lines that connect cross-border lines to central bus per country
635
636
    Returns
637
    -------
638
    None.
639
640
    """
641
    # Delete existing transformers in foreign countries
642
    db.execute_sql(
643
        f"""
644
        DELETE FROM {targets['transformers']['schema']}.
645
        {targets['transformers']['table']}
646
        WHERE scn_name = '{scenario}'
647
        AND trafo_id NOT IN (
648
            SELECT branch_id
649
            FROM {sources['osmtgmod_branch']['schema']}.
650
            {sources['osmtgmod_branch']['table']}
651
              WHERE result_id = 1 and link_type = 'transformer')
652
        """
653
    )
654
655
    # Initalize the dataframe for transformers
656
    trafo = gpd.GeoDataFrame(
657
        columns=["trafo_id", "bus0", "bus1", "s_nom"], dtype=int
658
    )
659
    trafo_id = db.next_etrago_id("transformer")
660
661
    # Add one transformer per central foreign bus with v_nom != 380
662
    for i, row in central_buses[central_buses.v_nom != 380].iterrows():
663
        s_nom_0 = new_lines[new_lines.bus0 == row.bus_id].s_nom.sum()
664
        s_nom_1 = new_lines[new_lines.bus1 == row.bus_id].s_nom.sum()
665
        if s_nom_0 == 0.0:
666
            s_nom = s_nom_1
667
        elif s_nom_1 == 0.0:
668
            s_nom = s_nom_0
669
        else:
670
            s_nom = min([s_nom_0, s_nom_1])
671
672
        s_nom, x = choose_transformer(s_nom)
673
674
        trafo = pd.concat(
675
            [
676
                trafo,
677
                pd.DataFrame(
678
                    index=[trafo.index.max() + 1],
679
                    data={
680
                        "trafo_id": trafo_id,
681
                        "bus0": row.bus_id,
682
                        "bus1": central_buses[
683
                            (central_buses.v_nom == 380)
684
                            & (central_buses.country == row.country)
685
                        ].bus_id.values[0],
686
                        "s_nom": s_nom,
687
                        "x": x,
688
                    },
689
                ),
690
            ],
691
            ignore_index=True,
692
        )
693
        trafo_id += 1
694
695
    # Set data type
696
    trafo = trafo.astype({"trafo_id": "int", "bus0": "int", "bus1": "int"})
697
    trafo["scn_name"] = scenario
698
699
    # Insert transformers to the database
700
    trafo.to_sql(
701
        targets["transformers"]["table"],
702
        schema=targets["transformers"]["schema"],
703
        if_exists="append",
704
        con=db.engine(),
705
        index=False,
706
    )
707
708
709
def foreign_dc_lines(scenario, sources, targets, central_buses):
710
    """Insert DC lines to foreign countries manually
711
712
    Parameters
713
    ----------
714
    sources : dict
715
        List of dataset sources
716
    targets : dict
717
        List of dataset targets
718
    central_buses : geopandas.GeoDataFrame
719
        Buses in the center of foreign countries
720
721
    Returns
722
    -------
723
    None.
724
725
    """
726
    # Delete existing dc lines to foreign countries
727
    db.execute_sql(
728
        f"""
729
        DELETE FROM {targets['links']['schema']}.
730
        {targets['links']['table']}
731
        WHERE scn_name = '{scenario}'
732
        AND carrier = 'DC'
733
        AND bus0 IN (
734
            SELECT bus_id
735
            FROM {sources['electricity_buses']['schema']}.
736
            {sources['electricity_buses']['table']}
737
              WHERE scn_name = '{scenario}'
738
              AND carrier = 'AC'
739
              AND country = 'DE')
740
        AND bus1 IN (
741
            SELECT bus_id
742
            FROM {sources['electricity_buses']['schema']}.
743
            {sources['electricity_buses']['table']}
744
              WHERE scn_name = '{scenario}'
745
              AND carrier = 'AC'
746
              AND country != 'DE')
747
        """
748
    )
749
    capital_cost = get_sector_parameters("electricity", scenario)[
750
        "capital_cost"
751
    ]
752
753
    # Add DC line from Lübeck to Sweden
754
    converter_luebeck = select_bus_id(
755
        10.802358024202768,
756
        53.897547401787,
757
        380,
758
        scenario,
759
        "AC",
760
        find_closest=True,
761
    )
762
763
    foreign_links = pd.DataFrame(
764
        index=[0],
765
        data={
766
            "link_id": db.next_etrago_id("link"),
767
            "bus0": converter_luebeck,
768
            "bus1": central_buses[
769
                (central_buses.country == "SE") & (central_buses.v_nom == 380)
770
            ]
771
            .iloc[0]
772
            .squeeze()
773
            .bus_id,
774
            "p_nom": 600,
775
            "length": 262,
776
        },
777
    )
778
779
    # When not in test-mode, add DC line from Bentwisch to Denmark
780
    if config.settings()["egon-data"]["--dataset-boundary"] == "Everything":
781
        converter_bentwisch = select_bus_id(
782
            12.213671694775988,
783
            54.09974494662279,
784
            380,
785
            scenario,
786
            "AC",
787
            find_closest=True,
788
        )
789
790
        foreign_links = pd.concat(
791
            [
792
                foreign_links,
793
                pd.DataFrame(
794
                    index=[1],
795
                    data={
796
                        "link_id": db.next_etrago_id("link") + 1,
797
                        "bus0": converter_bentwisch,
798
                        "bus1": central_buses[
799
                            (central_buses.country == "DK")
800
                            & (central_buses.v_nom == 380)
801
                            & (central_buses.x > 10)
802
                        ]
803
                        .iloc[0]
804
                        .squeeze()
805
                        .bus_id,
806
                        "p_nom": 600,
807
                        "length": 170,
808
                    },
809
                ),
810
            ]
811
        )
812
813
    # Set parameters for all DC lines
814
    foreign_links["capital_cost"] = (
815
        capital_cost["dc_cable"] * foreign_links.length
816
        + 2 * capital_cost["dc_inverter"]
817
    )
818
    foreign_links["p_min_pu"] = -1
819
    foreign_links["p_nom_extendable"] = True
820
    foreign_links["p_nom_min"] = foreign_links["p_nom"]
821
    foreign_links["scn_name"] = scenario
822
    foreign_links["carrier"] = "DC"
823
    foreign_links["efficiency"] = 1
824
825
    # Add topology
826
    foreign_links = etrago.link_geom_from_buses(foreign_links, scenario)
827
828
    # Insert DC lines to the database
829
    foreign_links.to_postgis(
830
        targets["links"]["table"],
831
        schema=targets["links"]["schema"],
832
        if_exists="append",
833
        con=db.engine(),
834
        index=False,
835
    )
836
837
838
def grid():
839
    """Insert electrical grid compoenents for neighbouring countries
840
841
    Returns
842
    -------
843
    None.
844
845
    """
846
    # Select sources and targets from dataset configuration
847
    sources = config.datasets()["electrical_neighbours"]["sources"]
848
    targets = config.datasets()["electrical_neighbours"]["targets"]
849
850
    for scenario in config.settings()["egon-data"]["--scenarios"]:
851
        central_buses = buses(scenario, sources, targets)
852
853
        foreign_lines = cross_border_lines(
854
            scenario, sources, targets, central_buses
855
        )
856
857
        central_transformer(
858
            scenario, sources, targets, central_buses, foreign_lines
859
        )
860
861
        foreign_dc_lines(scenario, sources, targets, central_buses)
862
863
        if scenario != "eGon100RE":
864
            lines_between_foreign_countries(
865
                scenario, sources, targets, central_buses
866
            )
867
868
869
def map_carriers_tyndp():
870
    """Map carriers from TYNDP-data to carriers used in eGon
871
    Returns
872
    -------
873
    dict
874
        Carrier from TYNDP and eGon
875
    """
876
    return {
877
        "Battery": "battery",
878
        "DSR": "demand_side_response",
879
        "Gas CCGT new": "gas",
880
        "Gas CCGT old 2": "gas",
881
        "Gas CCGT present 1": "gas",
882
        "Gas CCGT present 2": "gas",
883
        "Gas conventional old 1": "gas",
884
        "Gas conventional old 2": "gas",
885
        "Gas OCGT new": "gas",
886
        "Gas OCGT old": "gas",
887
        "Gas CCGT old 1": "gas",
888
        "Gas CCGT old 2 Bio": "biogas",
889
        "Gas conventional old 2 Bio": "biogas",
890
        "Hard coal new": "coal",
891
        "Hard coal old 1": "coal",
892
        "Hard coal old 2": "coal",
893
        "Hard coal old 2 Bio": "coal",
894
        "Heavy oil old 1": "oil",
895
        "Heavy oil old 1 Bio": "oil",
896
        "Heavy oil old 2": "oil",
897
        "Light oil": "oil",
898
        "Lignite new": "lignite",
899
        "Lignite old 1": "lignite",
900
        "Lignite old 2": "lignite",
901
        "Lignite old 1 Bio": "lignite",
902
        "Lignite old 2 Bio": "lignite",
903
        "Nuclear": "nuclear",
904
        "Offshore Wind": "wind_offshore",
905
        "Onshore Wind": "wind_onshore",
906
        "Other non-RES": "others",
907
        "Other RES": "others",
908
        "P2G": "power_to_gas",
909
        "PS Closed": "pumped_hydro",
910
        "PS Open": "reservoir",
911
        "Reservoir": "reservoir",
912
        "Run-of-River": "run_of_river",
913
        "Solar PV": "solar",
914
        "Solar Thermal": "others",
915
        "Waste": "Other RES",
916
    }
917
918
919 View Code Duplication
def get_foreign_bus_id(scenario):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
920
    """Calculte the etrago bus id from Nodes of TYNDP based on the geometry
921
922
    Returns
923
    -------
924
    pandas.Series
925
        List of mapped node_ids from TYNDP and etragos bus_id
926
927
    """
928
929
    sources = config.datasets()["electrical_neighbours"]["sources"]
930
931
    bus_id = db.select_geodataframe(
932
        f"""SELECT bus_id, ST_Buffer(geom, 1) as geom, country
933
        FROM grid.egon_etrago_bus
934
        WHERE scn_name = '{scenario}'
935
        AND carrier = 'AC'
936
        AND v_nom = 380.
937
        AND country != 'DE'
938
        AND bus_id NOT IN (
939
            SELECT bus_i
940
            FROM osmtgmod_results.bus_data)
941
        """,
942
        epsg=3035,
943
    )
944
945
    # insert installed capacities
946
    file = zipfile.ZipFile(f"tyndp/{sources['tyndp_capacities']}")
947
948
    # Select buses in neighbouring countries as geodataframe
949
    buses = pd.read_excel(
950
        file.open("TYNDP-2020-Scenario-Datafile.xlsx").read(),
951
        sheet_name="Nodes - Dict",
952
    ).query("longitude==longitude")
953
    buses = gpd.GeoDataFrame(
954
        buses,
955
        crs=4326,
956
        geometry=gpd.points_from_xy(buses.longitude, buses.latitude),
957
    ).to_crs(3035)
958
959
    buses["bus_id"] = 0
960
961
    # Select bus_id from etrago with shortest distance to TYNDP node
962
    for i, row in buses.iterrows():
963
        distance = bus_id.set_index("bus_id").geom.distance(row.geometry)
964
        buses.loc[i, "bus_id"] = distance[
965
            distance == distance.min()
966
        ].index.values[0]
967
968
    return buses.set_index("node_id").bus_id
969
970
971
def calc_capacities():
972
    """Calculates installed capacities from TYNDP data
973
974
    Returns
975
    -------
976
    pandas.DataFrame
977
        Installed capacities per foreign node and energy carrier
978
979
    """
980
981
    sources = config.datasets()["electrical_neighbours"]["sources"]
982
983
    countries = [
984
        "AT",
985
        "BE",
986
        "CH",
987
        "CZ",
988
        "DK",
989
        "FR",
990
        "NL",
991
        "NO",
992
        "SE",
993
        "PL",
994
        "UK",
995
    ]
996
997
    # insert installed capacities
998
    file = zipfile.ZipFile(f"tyndp/{sources['tyndp_capacities']}")
999
    df = pd.read_excel(
1000
        file.open("TYNDP-2020-Scenario-Datafile.xlsx").read(),
1001
        sheet_name="Capacity",
1002
    )
1003
1004
    # differneces between different climate years are very small (<1MW)
1005
    # choose 1984 because it is the mean value
1006
    df_2030 = (
1007
        df.rename({"Climate Year": "Climate_Year"}, axis="columns")
1008
        .query(
1009
            'Scenario == "Distributed Energy" & Year == 2030 & '
1010
            "Climate_Year == 1984"
1011
        )
1012
        .set_index(["Node/Line", "Generator_ID"])
1013
    )
1014
1015
    df_2040 = (
1016
        df.rename({"Climate Year": "Climate_Year"}, axis="columns")
1017
        .query(
1018
            'Scenario == "Distributed Energy" & Year == 2040 & '
1019
            "Climate_Year == 1984"
1020
        )
1021
        .set_index(["Node/Line", "Generator_ID"])
1022
    )
1023
1024
    # interpolate linear between 2030 and 2040 for 2035 accordning to
1025
    # scenario report of TSO's and the approval by BNetzA
1026
    df_2035 = pd.DataFrame(index=df_2030.index)
1027
    df_2035["cap_2030"] = df_2030.Value
1028
    df_2035["cap_2040"] = df_2040.Value
1029
    df_2035.fillna(0.0, inplace=True)
1030
    df_2035["cap_2035"] = (
1031
        df_2035["cap_2030"] + (df_2035["cap_2040"] - df_2035["cap_2030"]) / 2
1032
    )
1033
    df_2035 = df_2035.reset_index()
1034
    df_2035["carrier"] = df_2035.Generator_ID.map(map_carriers_tyndp())
1035
1036
    # group capacities by new carriers
1037
    grouped_capacities = (
1038
        df_2035.groupby(["carrier", "Node/Line"]).cap_2035.sum().reset_index()
1039
    )
1040
1041
    # choose capacities for considered countries
1042
    return grouped_capacities[
1043
        grouped_capacities["Node/Line"].str[:2].isin(countries)
1044
    ]
1045
1046
1047
def insert_generators_tyndp(capacities):
1048
    """Insert generators for foreign countries based on TYNDP-data
1049
1050
    Parameters
1051
    ----------
1052
    capacities : pandas.DataFrame
1053
        Installed capacities per foreign node and energy carrier
1054
1055
    Returns
1056
    -------
1057
    None.
1058
1059
    """
1060
    targets = config.datasets()["electrical_neighbours"]["targets"]
1061
    map_buses = get_map_buses()
1062
1063
    # Delete existing data
1064
    db.execute_sql(
1065
        f"""
1066
        DELETE FROM
1067
        {targets['generators']['schema']}.{targets['generators']['table']}
1068
        WHERE bus IN (
1069
            SELECT bus_id FROM
1070
            {targets['buses']['schema']}.{targets['buses']['table']}
1071
            WHERE country != 'DE'
1072
            AND scn_name = 'eGon2035')
1073
        AND scn_name = 'eGon2035'
1074
        AND carrier != 'CH4'
1075
        """
1076
    )
1077
1078
    db.execute_sql(
1079
        f"""
1080
        DELETE FROM
1081
        {targets['generators_timeseries']['schema']}.
1082
        {targets['generators_timeseries']['table']}
1083
        WHERE generator_id NOT IN (
1084
            SELECT generator_id FROM
1085
            {targets['generators']['schema']}.{targets['generators']['table']}
1086
        )
1087
        AND scn_name = 'eGon2035'
1088
        """
1089
    )
1090
1091
    # Select generators from TYNDP capacities
1092
    gen = capacities[
1093
        capacities.carrier.isin(
1094
            [
1095
                "others",
1096
                "wind_offshore",
1097
                "wind_onshore",
1098
                "solar",
1099
                "reservoir",
1100
                "run_of_river",
1101
                "lignite",
1102
                "coal",
1103
                "oil",
1104
                "nuclear",
1105
            ]
1106
        )
1107
    ]
1108
1109
    # Set bus_id
1110
    gen.loc[
1111
        gen[gen["Node/Line"].isin(map_buses.keys())].index, "Node/Line"
1112
    ] = gen.loc[
1113
        gen[gen["Node/Line"].isin(map_buses.keys())].index, "Node/Line"
1114
    ].map(
1115
        map_buses
1116
    )
1117
1118
    gen.loc[:, "bus"] = (
1119
        get_foreign_bus_id(scenario="eGon2035")
1120
        .loc[gen.loc[:, "Node/Line"]]
1121
        .values
1122
    )
1123
1124
    # Add scenario column
1125
    gen["scenario"] = "eGon2035"
1126
1127
    # Add marginal costs
1128
    gen = add_marginal_costs(gen)
1129
1130
    # insert generators data
1131
    session = sessionmaker(bind=db.engine())()
1132
    for i, row in gen.iterrows():
1133
        entry = etrago.EgonPfHvGenerator(
1134
            scn_name=row.scenario,
1135
            generator_id=int(db.next_etrago_id("generator")),
1136
            bus=row.bus,
1137
            carrier=row.carrier,
1138
            p_nom=row.cap_2035,
1139
            marginal_cost=row.marginal_cost,
1140
        )
1141
1142
        session.add(entry)
1143
        session.commit()
1144
1145
    # assign generators time-series data
1146
1147
    renewable_timeseries_pypsaeur("eGon2035")
1148
1149
1150
def insert_storage_tyndp(capacities):
1151
    """Insert storage units for foreign countries based on TYNDP-data
1152
1153
    Parameters
1154
    ----------
1155
    capacities : pandas.DataFrame
1156
        Installed capacities per foreign node and energy carrier
1157
1158
1159
    Returns
1160
    -------
1161
    None.
1162
1163
    """
1164
    targets = config.datasets()["electrical_neighbours"]["targets"]
1165
    map_buses = get_map_buses()
1166
1167
    # Delete existing data
1168
    db.execute_sql(
1169
        f"""
1170
        DELETE FROM {targets['storage']['schema']}.{targets['storage']['table']}
1171
        WHERE bus IN (
1172
            SELECT bus_id FROM
1173
            {targets['buses']['schema']}.{targets['buses']['table']}
1174
            WHERE country != 'DE'
1175
            AND scn_name = 'eGon2035')
1176
        AND scn_name = 'eGon2035'
1177
        """
1178
    )
1179
1180
    # Add missing information suitable for eTraGo selected from scenario_parameter table
1181
    parameters_pumped_hydro = scenario_parameters.electricity("eGon2035")[
1182
        "efficiency"
1183
    ]["pumped_hydro"]
1184
1185
    parameters_battery = scenario_parameters.electricity("eGon2035")[
1186
        "efficiency"
1187
    ]["battery"]
1188
1189
    # Select storage capacities from TYNDP-data
1190
    store = capacities[capacities.carrier.isin(["battery", "pumped_hydro"])]
1191
1192
    # Set bus_id
1193
    store.loc[
1194
        store[store["Node/Line"].isin(map_buses.keys())].index, "Node/Line"
1195
    ] = store.loc[
1196
        store[store["Node/Line"].isin(map_buses.keys())].index, "Node/Line"
1197
    ].map(
1198
        map_buses
1199
    )
1200
1201
    store.loc[:, "bus"] = (
1202
        get_foreign_bus_id(scenario="eGon2035")
1203
        .loc[store.loc[:, "Node/Line"]]
1204
        .values
1205
    )
1206
1207
    # Add columns for additional parameters to df
1208
    (
1209
        store["dispatch"],
1210
        store["store"],
1211
        store["standing_loss"],
1212
        store["max_hours"],
1213
    ) = (None, None, None, None)
1214
1215
    # Insert carrier specific parameters
1216
1217
    parameters = ["dispatch", "store", "standing_loss", "max_hours"]
1218
1219
    for x in parameters:
1220
        store.loc[store["carrier"] == "battery", x] = parameters_battery[x]
1221
        store.loc[store["carrier"] == "pumped_hydro", x] = (
1222
            parameters_pumped_hydro[x]
1223
        )
1224
1225
    # insert data
1226
    session = sessionmaker(bind=db.engine())()
1227
    for i, row in store.iterrows():
1228
        entry = etrago.EgonPfHvStorage(
1229
            scn_name="eGon2035",
1230
            storage_id=int(db.next_etrago_id("storage")),
1231
            bus=row.bus,
1232
            max_hours=row.max_hours,
1233
            efficiency_store=row.store,
1234
            efficiency_dispatch=row.dispatch,
1235
            standing_loss=row.standing_loss,
1236
            carrier=row.carrier,
1237
            p_nom=row.cap_2035,
1238
        )
1239
1240
        session.add(entry)
1241
        session.commit()
1242
1243
1244
def get_map_buses():
1245
    """Returns a dictonary of foreign regions which are aggregated to another
1246
1247
    Returns
1248
    -------
1249
    Combination of aggregated regions
1250
1251
1252
    """
1253
    return {
1254
        "DK00": "DKW1",
1255
        "DKKF": "DKE1",
1256
        "FR15": "FR00",
1257
        "NON1": "NOM1",
1258
        "NOS0": "NOM1",
1259
        "NOS1": "NOM1",
1260
        "PLE0": "PL00",
1261
        "PLI0": "PL00",
1262
        "SE00": "SE02",
1263
        "SE01": "SE02",
1264
        "SE03": "SE02",
1265
        "SE04": "SE02",
1266
        "RU": "RU00",
1267
    }
1268
1269
1270
def tyndp_generation():
1271
    """Insert data from TYNDP 2020 accordning to NEP 2021
1272
    Scenario 'Distributed Energy', linear interpolate between 2030 and 2040
1273
1274
    Returns
1275
    -------
1276
    None.
1277
    """
1278
1279
    capacities = calc_capacities()
1280
1281
    insert_generators_tyndp(capacities)
1282
1283
    insert_storage_tyndp(capacities)
1284
1285
1286
def tyndp_demand():
1287
    """Copy load timeseries data from TYNDP 2020.
1288
    According to NEP 2021, the data for 2030 and 2040 is interpolated linearly.
1289
1290
    Returns
1291
    -------
1292
    None.
1293
1294
    """
1295
    map_buses = get_map_buses()
1296
1297
    sources = config.datasets()["electrical_neighbours"]["sources"]
1298
    targets = config.datasets()["electrical_neighbours"]["targets"]
1299
1300
    # Delete existing data
1301
    db.execute_sql(
1302
        f"""
1303
        DELETE FROM {targets['loads']['schema']}.
1304
        {targets['loads']['table']}
1305
        WHERE
1306
        scn_name = 'eGon2035'
1307
        AND carrier = 'AC'
1308
        AND bus NOT IN (
1309
            SELECT bus_i
1310
            FROM  {sources['osmtgmod_bus']['schema']}.
1311
            {sources['osmtgmod_bus']['table']})
1312
        """
1313
    )
1314
1315
    # Connect to database
1316
    engine = db.engine()
1317
    session = sessionmaker(bind=engine)()
1318
1319
    nodes = [
1320
        "AT00",
1321
        "BE00",
1322
        "CH00",
1323
        "CZ00",
1324
        "DKE1",
1325
        "DKW1",
1326
        "FR00",
1327
        "NL00",
1328
        "LUB1",
1329
        "LUF1",
1330
        "LUG1",
1331
        "NOM1",
1332
        "NON1",
1333
        "NOS0",
1334
        "SE01",
1335
        "SE02",
1336
        "SE03",
1337
        "SE04",
1338
        "PL00",
1339
        "UK00",
1340
        "UKNI",
1341
    ]
1342
    # Assign etrago bus_id to TYNDP nodes
1343
    buses = pd.DataFrame({"nodes": nodes})
1344
    buses.loc[buses[buses.nodes.isin(map_buses.keys())].index, "nodes"] = (
1345
        buses[buses.nodes.isin(map_buses.keys())].nodes.map(map_buses)
1346
    )
1347
    buses.loc[:, "bus"] = (
1348
        get_foreign_bus_id(scenario="eGon2035")
1349
        .loc[buses.loc[:, "nodes"]]
1350
        .values
1351
    )
1352
    buses.set_index("nodes", inplace=True)
1353
    buses = buses[~buses.index.duplicated(keep="first")]
1354
1355
    # Read in data from TYNDP for 2030 and 2040
1356
    dataset_2030 = pd.read_excel(
1357
        f"tyndp/{sources['tyndp_demand_2030']}", sheet_name=nodes, skiprows=10
1358
    )
1359
1360
    dataset_2040 = pd.read_excel(
1361
        f"tyndp/{sources['tyndp_demand_2040']}", sheet_name=None, skiprows=10
1362
    )
1363
1364
    # Transform map_buses to pandas.Series and select only used values
1365
    map_series = pd.Series(map_buses)
1366
    map_series = map_series[map_series.index.isin(nodes)]
1367
1368
    # Calculate and insert demand timeseries per etrago bus_id
1369
    for bus in buses.index:
1370
        nodes = [bus]
1371
1372
        if bus in map_series.values:
1373
            nodes.extend(list(map_series[map_series == bus].index.values))
1374
1375
        load_id = db.next_etrago_id("load")
1376
1377
        # Some etrago bus_ids represent multiple TYNDP nodes,
1378
        # in this cases the loads are summed
1379
        data_2030 = pd.Series(index=range(8760), data=0.0)
1380
        for node in nodes:
1381
            data_2030 = dataset_2030[node][2011] + data_2030
1382
1383
        try:
1384
            data_2040 = pd.Series(index=range(8760), data=0.0)
1385
1386
            for node in nodes:
1387
                data_2040 = dataset_2040[node][2011] + data_2040
1388
        except:
1389
            data_2040 = data_2030
1390
1391
        # According to the NEP, data for 2030 and 2040 is linear interpolated
1392
        data_2035 = ((data_2030 + data_2040) / 2)[:8760]
1393
1394
        entry = etrago.EgonPfHvLoad(
1395
            scn_name="eGon2035",
1396
            load_id=int(load_id),
1397
            carrier="AC",
1398
            bus=int(buses.bus[bus]),
1399
        )
1400
1401
        entry_ts = etrago.EgonPfHvLoadTimeseries(
1402
            scn_name="eGon2035",
1403
            load_id=int(load_id),
1404
            temp_id=1,
1405
            p_set=list(data_2035.values),
1406
        )
1407
1408
        session.add(entry)
1409
        session.add(entry_ts)
1410
        session.commit()
1411
1412
1413
def get_entsoe_token():
1414
    """Check for token in home dir. If not exists, check in working dir"""
1415
    token_path = path.join(path.expanduser("~"), ".entsoe-token")
1416
    if not os.path.isfile(token_path):
1417
        logger.info(
1418
            f"Token file not found at {token_path}. Will check in working directory."
1419
        )
1420
        token_path = Path(".entsoe-token")
1421
        if os.path.isfile(token_path):
1422
            logger.info(f"Token found at {token_path}")
1423
    entsoe_token = open(token_path, "r").read(36)
1424
    if entsoe_token is None:
1425
        raise FileNotFoundError("No entsoe-token found.")
1426
    return entsoe_token
1427
1428
1429
def entsoe_historic_generation_capacities(
1430
    year_start="20190101", year_end="20200101"
1431
):
1432
    entsoe_token = get_entsoe_token()
1433
    client = entsoe.EntsoePandasClient(api_key=entsoe_token)
1434
1435
    start = pd.Timestamp(year_start, tz="Europe/Brussels")
1436
    end = pd.Timestamp(year_end, tz="Europe/Brussels")
1437
    start_gb = pd.Timestamp(year_start, tz="Europe/London")
1438
    end_gb = pd.Timestamp(year_end, tz="Europe/London")
1439
    countries = [
1440
        "LU",
1441
        "AT",
1442
        "FR",
1443
        "NL",
1444
        "CZ",
1445
        "DK_1",
1446
        "DK_2",
1447
        "PL",
1448
        "CH",
1449
        "NO",
1450
        "BE",
1451
        "SE",
1452
        "GB",
1453
    ]
1454
    # No GB data after Brexit
1455
    if int(year_start[:4]) > 2021:
1456
        logger.warning(
1457
            "No GB data after Brexit. GB is dropped from entsoe query!"
1458
        )
1459
        countries = [c for c in countries if c != "GB"]
1460
    # todo: define wanted countries
1461
1462
    not_retrieved = []
1463
    dfs = []
1464
    for country in countries:
1465
        if country == "GB":
1466
            kwargs = dict(start=start_gb, end=end_gb)
1467
        else:
1468
            kwargs = dict(start=start, end=end)
1469
        try:
1470
            country_data = client.query_installed_generation_capacity(
1471
                country, **kwargs
1472
            )
1473
            dfs.append(country_data)
1474
        except (entsoe.exceptions.NoMatchingDataError, requests.HTTPError):
1475
            logger.warning(
1476
                f"Data for country: {country} could not be retrieved."
1477
            )
1478
            not_retrieved.append(country)
1479
            pass
1480
1481
    if dfs:
1482
        df = pd.concat(dfs)
1483
        df["country"] = [c for c in countries if c not in not_retrieved]
1484
        df.set_index("country", inplace=True)
1485
        if int(year_start[:4]) == 2023:
1486
            # https://www.bmreports.com/bmrs/?q=foregeneration/capacityaggregated
1487
            # could probably somehow be automised
1488
            # https://www.elexonportal.co.uk/category/view/178
1489
            # in MW
1490
            installed_capacity_gb = pd.Series(
1491
                {
1492
                    "Biomass": 4438,
1493
                    "Fossil Gas": 37047,
1494
                    "Fossil Hard coal": 1491,
1495
                    "Hydro Pumped Storage": 5603,
1496
                    "Hydro Run-of-river and poundage": 2063,
1497
                    "Nuclear": 4950,
1498
                    "Other": 3313,
1499
                    "Other renewable": 1462,
1500
                    "Solar": 14518,
1501
                    "Wind Offshore": 13038,
1502
                    "Wind Onshore": 13907,
1503
                },
1504
                name="GB",
1505
            )
1506
            df = pd.concat([df.T, installed_capacity_gb], axis=1).T
1507
            logger.info("Manually added generation capacities for GB 2023.")
1508
            not_retrieved = [c for c in not_retrieved if c != "GB"]
1509
        df.fillna(0, inplace=True)
1510
    else:
1511
        df = pd.DataFrame()
1512
    return df, not_retrieved
1513
1514
1515
def entsoe_historic_demand(year_start="20190101", year_end="20200101"):
1516
    entsoe_token = get_entsoe_token()
1517
    client = entsoe.EntsoePandasClient(api_key=entsoe_token)
1518
1519
    start = pd.Timestamp(year_start, tz="Europe/Brussels")
1520
    end = pd.Timestamp(year_end, tz="Europe/Brussels")
1521
    start_gb = start.tz_convert("Europe/London")
1522
    end_gb = end.tz_convert("Europe/London")
1523
1524
    countries = [
1525
        "LU",
1526
        "AT",
1527
        "FR",
1528
        "NL",
1529
        "CZ",
1530
        "DK_1",
1531
        "DK_2",
1532
        "PL",
1533
        "CH",
1534
        "NO",
1535
        "BE",
1536
        "SE",
1537
        "GB",
1538
    ]
1539
1540
    # todo: define wanted countries
1541
1542
    not_retrieved = []
1543
    dfs = []
1544
1545
    for country in countries:
1546
        if country == "GB":
1547
            kwargs = dict(start=start_gb, end=end_gb)
1548
        else:
1549
            kwargs = dict(start=start, end=end)
1550
        try:
1551
            country_data = (
1552
                client.query_load(country, **kwargs)
1553
                .resample("H")["Actual Load"]
1554
                .mean()
1555
            )
1556
            if country == "GB":
1557
                country_data.index = country_data.index.tz_convert(
1558
                    "Europe/Brussels"
1559
                )
1560
            dfs.append(country_data)
1561
        except (entsoe.exceptions.NoMatchingDataError, requests.HTTPError):
1562
            not_retrieved.append(country)
1563
            logger.warning(
1564
                f"Data for country: {country} could not be retrieved."
1565
            )
1566
            pass
1567
1568
    if dfs:
1569
        df = pd.concat(dfs, axis=1)
1570
        df.columns = [c for c in countries if c not in not_retrieved]
1571
        df.index = pd.date_range(year_start, periods=8760, freq="H")
1572
    else:
1573
        df = pd.DataFrame()
1574
    return df, not_retrieved
1575
1576
1577
def map_carriers_entsoe():
1578
    """Map carriers from entsoe-data to carriers used in eGon
1579
    Returns
1580
    -------
1581
    dict
1582
        Carrier from entsoe to eGon
1583
    """
1584
    return {
1585
        "Biomass": "biomass",
1586
        "Fossil Brown coal/Lignite": "lignite",
1587
        "Fossil Coal-derived gas": "coal",
1588
        "Fossil Gas": "OCGT",
1589
        "Fossil Hard coal": "coal",
1590
        "Fossil Oil": "oil",
1591
        "Fossil Oil shale": "oil",
1592
        "Fossil Peat": "others",
1593
        "Geothermal": "geo_thermal",
1594
        "Hydro Pumped Storage": "Hydro Pumped Storage",
1595
        "Hydro Run-of-river and poundage": "run_of_river",
1596
        "Hydro Water Reservoir": "reservoir",
1597
        "Marine": "others",
1598
        "Nuclear": "nuclear",
1599
        "Other": "others",
1600
        "Other renewable": "others",
1601
        "Solar": "solar",
1602
        "Waste": "others",
1603
        "Wind Offshore": "wind_offshore",
1604
        "Wind Onshore": "wind_onshore",
1605
    }
1606
1607
1608
def entsoe_to_bus_etrago(scenario="status2019"):
1609
    map_entsoe = pd.Series(
1610
        {
1611
            "LU": "LU00",
1612
            "AT": "AT00",
1613
            "FR": "FR00",
1614
            "NL": "NL00",
1615
            "DK_1": "DK00",
1616
            "DK_2": "DKE1",
1617
            "PL": "PL00",
1618
            "CH": "CH00",
1619
            "NO": "NO00",
1620
            "BE": "BE00",
1621
            "SE": "SE00",
1622
            "GB": "UK00",
1623
            "CZ": "CZ00",
1624
        }
1625
    )
1626
1627
    for_bus = get_foreign_bus_id(scenario=scenario)
1628
1629
    return map_entsoe.map(for_bus)
1630
1631
1632
def save_entsoe_data(df: pd.DataFrame, file_path: Path):
1633
    os.makedirs(file_path.parent, exist_ok=True)
1634
    if not df.empty:
1635
        df.to_csv(file_path, index_label="Index")
1636
        logger.info(
1637
            f"Saved entsoe data for {file_path.stem} "
1638
            f"to {file_path.parent} for countries: {df.index}"
1639
        )
1640
1641
1642
def fill_by_backup_data_from_former_runs(df_sq, file_path, not_retrieved):
1643
    """
1644
    Fills missing data from former runs
1645
    Parameters
1646
    ----------
1647
    df_sq: pd.DataFrame
1648
    file_path: str, Path
1649
    not_retrieved: list
1650
1651
    Returns
1652
    -------
1653
    df_sq, not_retrieved
1654
1655
    """
1656
    sq_backup = pd.read_csv(file_path, index_col="Index")
1657
    # check for missing columns in backup (former runs)
1658
    c_backup = [c for c in sq_backup.columns if c in not_retrieved]
1659
    # remove columns, if found in backup
1660
    not_retrieved = [c for c in not_retrieved if c not in c_backup]
1661
    if c_backup:
1662
        df_sq = pd.concat([df_sq, sq_backup.loc[:, c_backup]], axis=1)
1663
        logger.info(f"Appended data from former runs for {c_backup}")
1664
    return df_sq, not_retrieved
1665
1666
1667
def insert_storage_units_sq(scn_name="status2019"):
1668
    """
1669
    Insert storage_units for foreign countries based on ENTSO-E data
1670
1671
    Parameters
1672
    ----------
1673
    scn_name : str
1674
        Scenario to which the foreign storage units will be assigned.
1675
        The default is "status2019".
1676
1677
    Returns
1678
    -------
1679
    None.
1680
1681
    """
1682
    if "status" in scn_name:
1683
        year = int(scn_name.split("status")[-1])
1684
        year_start_end = {
1685
            "year_start": f"{year}0101",
1686
            "year_end": f"{year+1}0101",
1687
        }
1688
    else:
1689
        raise ValueError("No valid scenario name!")
1690
1691
    df_gen_sq, not_retrieved = entsoe_historic_generation_capacities(
1692
        **year_start_end
1693
    )
1694
1695 View Code Duplication
    if not_retrieved:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1696
        logger.warning("Generation data from entsoe could not be retrieved.")
1697
        # check for generation backup from former runs
1698
        file_path = Path(
1699
            "./",
1700
            "data_bundle_egon_data",
1701
            "entsoe",
1702
            f"gen_entsoe_{scn_name}.csv",
1703
        ).resolve()
1704
        if os.path.isfile(file_path):
1705
            df_gen_sq, not_retrieved = fill_by_backup_data_from_former_runs(
1706
                df_gen_sq, file_path, not_retrieved
1707
            )
1708
        save_entsoe_data(df_gen_sq, file_path=file_path)
1709
1710
    sto_sq = df_gen_sq.loc[:, df_gen_sq.columns == "Hydro Pumped Storage"]
1711
    sto_sq.rename(columns={"Hydro Pumped Storage": "p_nom"}, inplace=True)
1712
1713
    targets = config.datasets()["electrical_neighbours"]["targets"]
1714
1715
    # Delete existing data
1716
    db.execute_sql(
1717
        f"""
1718
        DELETE FROM {targets['storage']['schema']}.{targets['storage']['table']}
1719
        WHERE bus IN (
1720
            SELECT bus_id FROM
1721
            {targets['buses']['schema']}.{targets['buses']['table']}
1722
            WHERE country != 'DE'
1723
            AND scn_name = '{scn_name}')
1724
        AND scn_name = '{scn_name}'
1725
        """
1726
    )
1727
1728
    # Add missing information suitable for eTraGo selected from scenario_parameter table
1729
    parameters_pumped_hydro = get_sector_parameters(
1730
        sector="electricity", scenario=scn_name
1731
    )["efficiency"]["pumped_hydro"]
1732
1733
    # Set bus_id
1734
    entsoe_to_bus = entsoe_to_bus_etrago(scenario=scn_name)
1735
    sto_sq["bus"] = sto_sq.index.map(entsoe_to_bus)
1736
1737
    # Insert carrier specific parameters
1738
    sto_sq["carrier"] = "pumped_hydro"
1739
    sto_sq["scn_name"] = scn_name
1740
    sto_sq["dispatch"] = parameters_pumped_hydro["dispatch"]
1741
    sto_sq["store"] = parameters_pumped_hydro["store"]
1742
    sto_sq["standing_loss"] = parameters_pumped_hydro["standing_loss"]
1743
    sto_sq["max_hours"] = parameters_pumped_hydro["max_hours"]
1744
    sto_sq["cyclic_state_of_charge"] = parameters_pumped_hydro[
1745
        "cyclic_state_of_charge"
1746
    ]
1747
1748
    next_id = int(db.next_etrago_id("storage"))
1749
    sto_sq["storage_id"] = range(next_id, next_id + len(sto_sq))
1750
1751
    # Delete entrances without any installed capacity
1752
    sto_sq = sto_sq[sto_sq["p_nom"] > 0]
1753
1754
    # insert data pumped_hydro storage
1755
1756
    with session_scope() as session:
1757
        for i, row in sto_sq.iterrows():
1758
            entry = etrago.EgonPfHvStorage(
1759
                scn_name=scn_name,
1760
                storage_id=row.storage_id,
1761
                bus=row.bus,
1762
                max_hours=row.max_hours,
1763
                efficiency_store=row.store,
1764
                efficiency_dispatch=row.dispatch,
1765
                standing_loss=row.standing_loss,
1766
                carrier=row.carrier,
1767
                p_nom=row.p_nom,
1768
                cyclic_state_of_charge=row.cyclic_state_of_charge,
1769
            )
1770
            session.add(entry)
1771
            session.commit()
1772
1773
    # big scale batteries
1774
    # info based on EASE data. https://ease-storage.eu/publication/emmes-7-0-march-2023/
1775
    # batteries smaller than 100MW are neglected
1776
1777
    # TODO: include capacities between 2020 and 2023
1778
    bat_per_country = {
1779
        "LU": [0, pd.NA, pd.NA, pd.NA, pd.NA],
1780
        "AT": [0, pd.NA, pd.NA, pd.NA, pd.NA],
1781
        "FR": [0, pd.NA, pd.NA, pd.NA, pd.NA],
1782
        "NL": [0, pd.NA, pd.NA, pd.NA, pd.NA],
1783
        "DK_1": [0, pd.NA, pd.NA, pd.NA, pd.NA],
1784
        "DK_2": [0, pd.NA, pd.NA, pd.NA, pd.NA],
1785
        "PL": [0, pd.NA, pd.NA, pd.NA, pd.NA],
1786
        "CH": [0, pd.NA, pd.NA, pd.NA, pd.NA],
1787
        "NO": [0, pd.NA, pd.NA, pd.NA, pd.NA],
1788
        "BE": [0, pd.NA, pd.NA, pd.NA, pd.NA],
1789
        "SE": [0, pd.NA, pd.NA, pd.NA, pd.NA],
1790
        "GB": [723.8, 952.3, 1380.9, 2333.3, 3928.5],
1791
        "CZ": [0, pd.NA, pd.NA, pd.NA, pd.NA],
1792
    }
1793
    bat_sq = pd.DataFrame(bat_per_country).T.set_axis(
1794
        ["2019", "2020", "2021", "2022", "2023"], axis=1
1795
    )
1796
1797
    # Select year of interest
1798
    bat_sq = bat_sq[[str(year)]]
1799
    bat_sq.rename(columns={str(year): "p_nom"}, inplace=True)
1800
1801
    # Add missing information suitable for eTraGo selected from scenario_parameter table
1802
    parameters_batteries = get_sector_parameters(
1803
        sector="electricity", scenario=scn_name
1804
    )["efficiency"]["battery"]
1805
1806
    # Set bus_id
1807
    entsoe_to_bus = entsoe_to_bus_etrago()
1808
    bat_sq["bus"] = bat_sq.index.map(entsoe_to_bus)
1809
1810
    # Insert carrier specific parameters
1811
    bat_sq["carrier"] = "battery"
1812
    bat_sq["scn_name"] = scn_name
1813
    bat_sq["dispatch"] = parameters_batteries["dispatch"]
1814
    bat_sq["store"] = parameters_batteries["store"]
1815
    bat_sq["standing_loss"] = parameters_batteries["standing_loss"]
1816
    bat_sq["max_hours"] = parameters_batteries["max_hours"]
1817
    bat_sq["cyclic_state_of_charge"] = parameters_batteries[
1818
        "cyclic_state_of_charge"
1819
    ]
1820
1821
    next_id = int(db.next_etrago_id("storage"))
1822
    bat_sq["storage_id"] = range(next_id, next_id + len(bat_sq))
1823
1824
    # Delete entrances without any installed capacity
1825
    bat_sq = bat_sq[bat_sq["p_nom"] > 0]
1826
1827
    # insert data pumped_hydro storage
1828
    with db.session_scope() as session:
1829
        for i, row in bat_sq.iterrows():
1830
            entry = etrago.EgonPfHvStorage(
1831
                scn_name=scn_name,
1832
                storage_id=row.storage_id,
1833
                bus=row.bus,
1834
                max_hours=row.max_hours,
1835
                efficiency_store=row.store,
1836
                efficiency_dispatch=row.dispatch,
1837
                standing_loss=row.standing_loss,
1838
                carrier=row.carrier,
1839
                p_nom=row.p_nom,
1840
                cyclic_state_of_charge=row.cyclic_state_of_charge,
1841
            )
1842
            session.add(entry)
1843
            session.commit()
1844
1845
1846
def insert_generators_sq(scn_name="status2019"):
1847
    """
1848
    Insert generators for foreign countries based on ENTSO-E data
1849
1850
    Parameters
1851
    ----------
1852
    gen_sq : pandas dataframe
1853
        df with all the foreign generators produced by the function
1854
        entsoe_historic_generation_capacities
1855
    scn_name : str
1856
        The default is "status2019".
1857
1858
    Returns
1859
    -------
1860
    None.
1861
1862
    """
1863
    if "status" in scn_name:
1864
        year = int(scn_name.split("status")[-1])
1865
        year_start_end = {
1866
            "year_start": f"{year}0101",
1867
            "year_end": f"{year+1}0101",
1868
        }
1869
    else:
1870
        raise ValueError("No valid scenario name!")
1871
1872
    df_gen_sq, not_retrieved = entsoe_historic_generation_capacities(
1873
        **year_start_end
1874
    )
1875
1876 View Code Duplication
    if not_retrieved:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1877
        logger.warning("Generation data from entsoe could not be retrieved.")
1878
        # check for generation backup from former runs
1879
        file_path = Path(
1880
            "./",
1881
            "data_bundle_egon_data",
1882
            "entsoe",
1883
            f"gen_entsoe_{scn_name}.csv",
1884
        ).resolve()
1885
        if os.path.isfile(file_path):
1886
            df_gen_sq, not_retrieved = fill_by_backup_data_from_former_runs(
1887
                df_gen_sq, file_path, not_retrieved
1888
            )
1889
        save_entsoe_data(df_gen_sq, file_path=file_path)
1890
1891
    targets = config.datasets()["electrical_neighbours"]["targets"]
1892
    # Delete existing data
1893
    db.execute_sql(
1894
        f"""
1895
        DELETE FROM
1896
        {targets['generators']['schema']}.{targets['generators']['table']}
1897
        WHERE bus IN (
1898
            SELECT bus_id FROM
1899
            {targets['buses']['schema']}.{targets['buses']['table']}
1900
            WHERE country != 'DE'
1901
            AND scn_name = '{scn_name}')
1902
        AND scn_name = '{scn_name}'
1903
        AND carrier != 'CH4'
1904
        """
1905
    )
1906
1907
    db.execute_sql(
1908
        f"""
1909
        DELETE FROM
1910
        {targets['generators_timeseries']['schema']}.
1911
        {targets['generators_timeseries']['table']}
1912
        WHERE generator_id NOT IN (
1913
            SELECT generator_id FROM
1914
            {targets['generators']['schema']}.{targets['generators']['table']}
1915
        )
1916
        AND scn_name = '{scn_name}'
1917
        """
1918
    )
1919
    entsoe_to_bus = entsoe_to_bus_etrago(scn_name)
1920
    carrier_entsoe = map_carriers_entsoe()
1921
    df_gen_sq = df_gen_sq.groupby(axis=1, by=carrier_entsoe).sum()
1922
1923
    # Filter generators modeled as storage and geothermal
1924
    df_gen_sq = df_gen_sq.loc[
1925
        :, ~df_gen_sq.columns.isin(["Hydro Pumped Storage", "geo_thermal"])
1926
    ]
1927
1928
    list_gen_sq = pd.DataFrame(
1929
        dtype=int, columns=["carrier", "country", "capacity"]
1930
    )
1931
    for carrier in df_gen_sq.columns:
1932
        gen_carry = df_gen_sq[carrier]
1933
        for country, cap in gen_carry.items():
1934
            gen = pd.DataFrame(
1935
                {"carrier": carrier, "country": country, "capacity": cap},
1936
                index=[1],
1937
            )
1938
            # print(gen)
1939
            list_gen_sq = pd.concat([list_gen_sq, gen], ignore_index=True)
1940
1941
    list_gen_sq = list_gen_sq[list_gen_sq.capacity > 0]
1942
    list_gen_sq["scenario"] = scn_name
1943
1944
    # Add marginal costs
1945
    list_gen_sq = add_marginal_costs(list_gen_sq)
1946
1947
    # Find foreign bus to assign the generator
1948
    list_gen_sq["bus"] = list_gen_sq.country.map(entsoe_to_bus)
1949
1950
    # insert generators data
1951
    session = sessionmaker(bind=db.engine())()
1952
    for i, row in list_gen_sq.iterrows():
1953
        entry = etrago.EgonPfHvGenerator(
1954
            scn_name=row.scenario,
1955
            generator_id=int(db.next_etrago_id("generator")),
1956
            bus=row.bus,
1957
            carrier=row.carrier,
1958
            p_nom=row.capacity,
1959
            marginal_cost=row.marginal_cost,
1960
        )
1961
1962
        session.add(entry)
1963
        session.commit()
1964
1965
    renewable_timeseries_pypsaeur(scn_name)
1966
1967
1968
def renewable_timeseries_pypsaeur(scn_name):
1969
    # select generators from database to get index values
1970
    foreign_re_generators = db.select_dataframe(
1971
        f"""
1972
        SELECT generator_id, a.carrier, country, x, y
1973
        FROM grid.egon_etrago_generator a
1974
        JOIN  grid.egon_etrago_bus b
1975
        ON a.bus = b.bus_id
1976
        WHERE a.scn_name = '{scn_name}'
1977
        AND  b.scn_name = '{scn_name}'
1978
        AND b.carrier = 'AC'
1979
        AND b.country != 'DE'
1980
        AND a.carrier IN ('wind_onshore', 'wind_offshore', 'solar')
1981
        """
1982
    )
1983
1984
    # Import prepared network from pypsa-eur
1985
    network = prepared_network()
1986
1987
    # Select fluctuating renewable generators
1988
    generators_pypsa_eur = network.generators.loc[
1989
        network.generators[
1990
            network.generators.carrier.isin(["onwind", "offwind-ac", "solar"])
1991
        ].index,
1992
        ["bus", "carrier"],
1993
    ]
1994
1995
    # Align carrier names for wind turbines
1996
    generators_pypsa_eur.loc[
1997
        generators_pypsa_eur[generators_pypsa_eur.carrier == "onwind"].index,
1998
        "carrier",
1999
    ] = "wind_onshore"
2000
    generators_pypsa_eur.loc[
2001
        generators_pypsa_eur[
2002
            generators_pypsa_eur.carrier == "offwind-ac"
2003
        ].index,
2004
        "carrier",
2005
    ] = "wind_offshore"
2006
2007
    # Set coordinates from bus table
2008
    generators_pypsa_eur["x"] = network.buses.loc[
2009
        generators_pypsa_eur.bus.values, "x"
2010
    ].values
2011
    generators_pypsa_eur["y"] = network.buses.loc[
2012
        generators_pypsa_eur.bus.values, "y"
2013
    ].values
2014
2015
    # Get p_max_pu time series from pypsa-eur
2016
    generators_pypsa_eur["p_max_pu"] = network.generators_t.p_max_pu[
2017
        generators_pypsa_eur.index
2018
    ].T.values.tolist()
2019
2020
    session = sessionmaker(bind=db.engine())()
2021
2022
    # Insert p_max_pu timeseries based on geometry and carrier
2023
    for gen in foreign_re_generators.index:
2024
        entry = etrago.EgonPfHvGeneratorTimeseries(
2025
            scn_name=scn_name,
2026
            generator_id=foreign_re_generators.loc[gen, "generator_id"],
2027
            temp_id=1,
2028
            p_max_pu=generators_pypsa_eur[
2029
                (
2030
                    (
2031
                        generators_pypsa_eur.x
2032
                        - foreign_re_generators.loc[gen, "x"]
2033
                    ).abs()
2034
                    < 0.01
2035
                )
2036
                & (
2037
                    (
2038
                        generators_pypsa_eur.y
2039
                        - foreign_re_generators.loc[gen, "y"]
2040
                    ).abs()
2041
                    < 0.01
2042
                )
2043
                & (
2044
                    generators_pypsa_eur.carrier
2045
                    == foreign_re_generators.loc[gen, "carrier"]
2046
                )
2047
            ].p_max_pu.iloc[0],
2048
        )
2049
2050
        session.add(entry)
2051
        session.commit()
2052
2053
2054
def insert_loads_sq(scn_name="status2019"):
2055
    """
2056
    Copy load timeseries data from entso-e.
2057
2058
    Returns
2059
    -------
2060
    None.
2061
2062
    """
2063
    sources = config.datasets()["electrical_neighbours"]["sources"]
2064
    targets = config.datasets()["electrical_neighbours"]["targets"]
2065
2066
    if scn_name == "status2019":
2067
        year_start_end = {"year_start": "20190101", "year_end": "20200101"}
2068
    elif scn_name == "status2023":
2069
        year_start_end = {"year_start": "20230101", "year_end": "20240101"}
2070
    else:
2071
        raise ValueError("No valid scenario name!")
2072
2073
    df_load_sq, not_retrieved = entsoe_historic_demand(**year_start_end)
2074
2075
    if not_retrieved:
2076
        logger.warning("Demand data from entsoe could not be retrieved.")
2077
        # check for generation backup from former runs
2078
        file_path = Path(
2079
            "./",
2080
            "data_bundle_egon_data",
2081
            "entsoe",
2082
            f"load_entsoe_{scn_name}.csv",
2083
        ).resolve()
2084
        if os.path.isfile(file_path):
2085
            df_load_sq, not_retrieved = fill_by_backup_data_from_former_runs(
2086
                df_load_sq, file_path, not_retrieved
2087
            )
2088
        save_entsoe_data(df_load_sq, file_path=file_path)
2089
2090
    # Delete existing data
2091
    db.execute_sql(
2092
        f"""
2093
        DELETE FROM {targets['load_timeseries']['schema']}.
2094
        {targets['load_timeseries']['table']}
2095
        WHERE
2096
        scn_name = '{scn_name}'
2097
        AND load_id IN (
2098
        SELECT load_id FROM {targets['loads']['schema']}.
2099
        {targets['loads']['table']}
2100
        WHERE
2101
        scn_name = '{scn_name}'
2102
        AND carrier = 'AC'
2103
        AND bus NOT IN (
2104
            SELECT bus_i
2105
            FROM  {sources['osmtgmod_bus']['schema']}.
2106
            {sources['osmtgmod_bus']['table']}))
2107
        """
2108
    )
2109
2110
    db.execute_sql(
2111
        f"""
2112
        DELETE FROM {targets['loads']['schema']}.
2113
        {targets['loads']['table']}
2114
        WHERE
2115
        scn_name = '{scn_name}'
2116
        AND carrier = 'AC'
2117
        AND bus NOT IN (
2118
            SELECT bus_i
2119
            FROM  {sources['osmtgmod_bus']['schema']}.
2120
            {sources['osmtgmod_bus']['table']})
2121
        """
2122
    )
2123
2124
    # get the corresponding bus per foreign country
2125
    entsoe_to_bus = entsoe_to_bus_etrago(scn_name)
2126
2127
    # Calculate and insert demand timeseries per etrago bus_id
2128
    with session_scope() as session:
2129
        for country in df_load_sq.columns:
2130
            load_id = db.next_etrago_id("load")
2131
2132
            entry = etrago.EgonPfHvLoad(
2133
                scn_name=scn_name,
2134
                load_id=int(load_id),
2135
                carrier="AC",
2136
                bus=int(entsoe_to_bus[country]),
2137
            )
2138
2139
            entry_ts = etrago.EgonPfHvLoadTimeseries(
2140
                scn_name=scn_name,
2141
                load_id=int(load_id),
2142
                temp_id=1,
2143
                p_set=list(df_load_sq[country]),
2144
            )
2145
2146
            session.add(entry)
2147
            session.add(entry_ts)
2148
            session.commit()
2149
2150
2151
tasks = (grid,)
2152
2153
insert_per_scenario = set()
2154
2155
for scn_name in config.settings()["egon-data"]["--scenarios"]:
2156
2157
    if scn_name == "eGon2035":
2158
        insert_per_scenario.update([tyndp_generation, tyndp_demand])
2159
2160
    if "status" in scn_name:
2161
        postfix = f"_{scn_name.split('status')[-1]}"
2162
        insert_per_scenario.update(
2163
            [
2164
                wrapped_partial(
2165
                    insert_generators_sq, scn_name=scn_name, postfix=postfix
2166
                ),
2167
                wrapped_partial(
2168
                    insert_loads_sq, scn_name=scn_name, postfix=postfix
2169
                ),
2170
                wrapped_partial(
2171
                    insert_storage_units_sq, scn_name=scn_name, postfix=postfix
2172
                ),
2173
            ]
2174
        )
2175
2176
tasks = tasks + (insert_per_scenario,)
2177
2178
2179
class ElectricalNeighbours(Dataset):
2180
    """
2181
    Add lines, loads, generation and storage for electrical neighbours
2182
2183
    This dataset creates data for modelling the considered foreign countries and writes
2184
    that data into the database tables that can be read by the eTraGo tool.
2185
    Neighbouring countries are modelled in a lower spatial resolution, in general one node per
2186
    country is considered.
2187
    Defined load timeseries as well as generatrion and storage capacities are connected to these nodes.
2188
    The nodes are connected by AC and DC transmission lines with the German grid and other neighbouring countries
2189
    considering the grid topology from ENTSO-E.
2190
2191
2192
    *Dependencies*
2193
      * :py:class:`Tyndp <egon.data.datasets.tyndp.Tyndp>`
2194
      * :py:class:`PypsaEurSec <egon.data.datasets.pypsaeursec.PypsaEurSec>`
2195
2196
2197
    *Resulting tables*
2198
      * :py:class:`grid.egon_etrago_bus <egon.data.datasets.etrago_setup.EgonPfHvBus>` is extended
2199
      * :py:class:`grid.egon_etrago_link <egon.data.datasets.etrago_setup.EgonPfHvLink>` is extended
2200
      * :py:class:`grid.egon_etrago_line <egon.data.datasets.etrago_setup.EgonPfHvLine>` is extended
2201
      * :py:class:`grid.egon_etrago_load <egon.data.datasets.etrago_setup.EgonPfHvLoad>` is extended
2202
      * :py:class:`grid.egon_etrago_load_timeseries <egon.data.datasets.etrago_setup.EgonPfHvLoadTimeseries>` is extended
2203
      * :py:class:`grid.egon_etrago_storage <egon.data.datasets.etrago_setup.EgonPfHvStorageUnit>` is extended
2204
      * :py:class:`grid.egon_etrago_generator <egon.data.datasets.etrago_setup.EgonPfHvGenerator>` is extended
2205
      * :py:class:`grid.egon_etrago_generator_timeseries <egon.data.datasets.etrago_setup.EgonPfHvGeneratorTimeseries>` is extended
2206
      * :py:class:`grid.egon_etrago_transformer <egon.data.datasets.etrago_setup.EgonPfHvTransformer>` is extended
2207
2208
    """
2209
2210
    #:
2211
    name: str = "ElectricalNeighbours"
2212
    #:
2213
    version: str = "0.0.11"
2214
2215
    def __init__(self, dependencies):
2216
        super().__init__(
2217
            name=self.name,
2218
            version=self.version,
2219
            dependencies=dependencies,
2220
            tasks=tasks,
2221
        )
2222