data.datasets.electrical_neighbours   F
last analyzed

Complexity

Total Complexity 110

Size/Duplication

Total Lines 2204
Duplicated Lines 5.54 %

Importance

Changes 0
Metric Value
wmc 110
eloc 1189
dl 122
loc 2204
rs 0.844
c 0
b 0
f 0

29 Functions

Rating   Name   Duplication   Size   Complexity  
A get_cross_border_lines() 0 27 1
A get_cross_border_buses() 0 34 1
A central_buses_pypsaeur() 0 37 1
B calc_capacities() 0 73 1
A get_foreign_bus_id() 50 50 2
B lines_between_foreign_countries() 0 112 4
C insert_storage_units_sq() 14 175 8
B foreign_dc_lines() 0 126 2
A grid() 0 28 3
A get_map_buses() 0 23 1
C cross_border_lines() 0 141 8
A map_carriers_entsoe() 0 28 1
B map_carriers_tyndp() 0 47 1
B insert_loads_sq() 0 95 7
B entsoe_historic_demand() 0 60 6
A fill_by_backup_data_from_former_runs() 0 23 2
C entsoe_historic_generation_capacities() 0 84 7
B insert_generators_tyndp() 0 101 2
F choose_transformer() 0 49 14
A save_entsoe_data() 0 6 2
A tyndp_generation() 0 14 1
C tyndp_demand() 0 125 6
A entsoe_to_bus_etrago() 0 22 1
B renewable_timeseries_pypsaeur() 0 84 2
B insert_generators_sq() 14 120 7
A get_entsoe_token() 0 14 4
C buses() 44 162 7
B central_transformer() 0 84 4
B insert_storage_tyndp() 0 92 3

1 Method

Rating   Name   Duplication   Size   Complexity  
A ElectricalNeighbours.__init__() 0 6 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like data.datasets.electrical_neighbours often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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