Passed
Pull Request — dev (#1375)
by
unknown
02:18
created

eGon100_capacities()   C

Complexity

Conditions 7

Size

Total Lines 216
Code Lines 139

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 139
dl 0
loc 216
rs 5.6
c 0
b 0
f 0
cc 7
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
"""The central module containing all code dealing with importing data from
2
Netzentwicklungsplan 2035, Version 2031, Szenario C
3
"""
4
5
from pathlib import Path
6
import datetime
7
import json
8
import time
9
10
from sqlalchemy import Column, Float, Integer, String
11
from sqlalchemy.ext.declarative import declarative_base
12
from sqlalchemy.orm import sessionmaker
13
import numpy as np
14
import pandas as pd
15
import yaml
16
17
from egon.data import config, db
18
from egon.data.datasets import Dataset, wrapped_partial
19
from egon.data.metadata import (
20
    context,
21
    generate_resource_fields_from_sqla_model,
22
    license_ccby,
23
    meta_metadata,
24
    sources,
25
)
26
27
from egon_validation import(
28
    RowCountValidation,
29
    DataTypeValidation,
30
    NotNullAndNotNaNValidation,
31
    WholeTableNotNullAndNotNaNValidation,
32
    ValueSetValidation
33
)
34
35
Base = declarative_base()
36
37
38
class EgonScenarioCapacities(Base):
39
    __tablename__ = "egon_scenario_capacities"
40
    __table_args__ = {"schema": "supply"}
41
    index = Column(Integer, primary_key=True)
42
    component = Column(String(25))
43
    carrier = Column(String(50))
44
    capacity = Column(Float)
45
    nuts = Column(String(12))
46
    scenario_name = Column(String(50))
47
48
49
class NEP2021ConvPowerPlants(Base):
50
    __tablename__ = "egon_nep_2021_conventional_powerplants"
51
    __table_args__ = {"schema": "supply"}
52
    index = Column(String(50), primary_key=True)
53
    bnetza_id = Column(String(50))
54
    name = Column(String(100))
55
    name_unit = Column(String(50))
56
    carrier_nep = Column(String(50))
57
    carrier = Column(String(12))
58
    chp = Column(String(12))
59
    postcode = Column(String(12))
60
    city = Column(String(50))
61
    federal_state = Column(String(12))
62
    commissioned = Column(String(12))
63
    status = Column(String(50))
64
    capacity = Column(Float)
65
    a2035_chp = Column(String(12))
66
    a2035_capacity = Column(Float)
67
    b2035_chp = Column(String(12))
68
    b2035_capacity = Column(Float)
69
    c2035_chp = Column(String(12))
70
    c2035_capacity = Column(Float)
71
    b2040_chp = Column(String(12))
72
    b2040_capacity = Column(Float)
73
74
75
def create_table():
76
    """Create input tables for scenario setup
77
78
    Returns
79
    -------
80
    None.
81
82
    """
83
84
    engine = db.engine()
85
    db.execute_sql("CREATE SCHEMA IF NOT EXISTS supply;")
86
    EgonScenarioCapacities.__table__.drop(bind=engine, checkfirst=True)
87
    NEP2021ConvPowerPlants.__table__.drop(bind=engine, checkfirst=True)
88
    EgonScenarioCapacities.__table__.create(bind=engine, checkfirst=True)
89
    NEP2021ConvPowerPlants.__table__.create(bind=engine, checkfirst=True)
90
91
92
def nuts_mapping():
93
    nuts_mapping = {
94
        "BW": "DE1",
95
        "NW": "DEA",
96
        "HE": "DE7",
97
        "BB": "DE4",
98
        "HB": "DE5",
99
        "RP": "DEB",
100
        "ST": "DEE",
101
        "SH": "DEF",
102
        "MV": "DE8",
103
        "TH": "DEG",
104
        "NI": "DE9",
105
        "SN": "DED",
106
        "HH": "DE6",
107
        "SL": "DEC",
108
        "BE": "DE3",
109
        "BY": "DE2",
110
    }
111
112
    return nuts_mapping
113
114
115
def insert_capacities_status_quo(scenario: str) -> None:
116
    """Insert capacity of rural heat pumps for status quo
117
118
    Returns
119
    -------
120
    None.
121
122
    """
123
124
    targets = config.datasets()["scenario_input"]["targets"]
125
126
    # Delete rows if already exist
127
    db.execute_sql(
128
        f"""
129
        DELETE FROM
130
        {targets['scenario_capacities']['schema']}.
131
        {targets['scenario_capacities']['table']}
132
        WHERE scenario_name = '{scenario}'
133
        """
134
    )
135
136
    rural_heat_capacity = {
137
        # Rural heat capacity for 2019 according to NEP 2035, version 2021
138
        "status2019": 1e6 * 5e-3,
139
        # Rural heat capacity for 2023 according to NEP 2037, version 2023
140
        # 1.2 Mio. for 2020
141
        # https://www.netzentwicklungsplan.de/sites/default/files/2023-07/
142
        # NEP_2037_2045_V2023_2_Entwurf_Teil1_1.pdf#page=25
143
        # and 3 kW per heat pump
144
        # https://www.netzentwicklungsplan.de/sites/default/files/2022-11/
145
        # NEP_2035_V2021_2_Entwurf_Teil1.pdf#page=33
146
        # plus 0.15 Mio. 2021 and 0.24 Mio. in 2022
147
        # https://www.enercity.de/magazin/unsere-welt/waermepumpen-boom
148
        # plus 0.2 Mio. in H1 2023 -> Assumption 2023: 2 * 0.2 Mio = 0.4 Mio.
149
        "status2023": (1.2 + 0.15 + 0.24 + 0.4) * 1e6 * 3e-3,
150
    }[scenario]
151
152
    if config.settings()["egon-data"]["--dataset-boundary"] != "Everything":
153
        rural_heat_capacity *= population_share()
154
155
    db.execute_sql(
156
        f"""
157
        INSERT INTO
158
        {targets['scenario_capacities']['schema']}.
159
        {targets['scenario_capacities']['table']}
160
        (component, carrier, capacity, nuts, scenario_name)
161
        VALUES (
162
            'link',
163
            'rural_heat_pump',
164
            {rural_heat_capacity},
165
            'DE',
166
            '{scenario}'
167
            )
168
        """
169
    )
170
171
    # Include small storages for scenario2019
172
    small_storages = {
173
        # MW for Germany
174
        "status2019": 600,
175
        # 1.3 GW in 2020/2021
176
        # https://www.netzentwicklungsplan.de/sites/default/files/2023-07/
177
        # NEP_2037_2045_V2023_2_Entwurf_Teil1_1.pdf#page=25
178
        # Installed quantity 2020: 272,000
179
        # Installed quantity 2023: 1,197,000
180
        # https://www.photovoltaik.eu/solarspeicher/
181
        # bsw-speicherkapazitaet-von-heimspeichern-2023-verdoppelt
182
        "status2023": 1300 * 1197 / 272,
183
    }[scenario]
184
185
    db.execute_sql(
186
        f"""
187
        INSERT INTO
188
        {targets['scenario_capacities']['schema']}.
189
        {targets['scenario_capacities']['table']}
190
        (component, carrier, capacity, nuts, scenario_name)
191
        VALUES (
192
            'storage_units',
193
            'battery',
194
            {small_storages},
195
            'DE',
196
            '{scenario}'
197
            )
198
        """
199
    )
200
201
202
def insert_capacities_per_federal_state_nep():
203
    """Inserts installed capacities per federal state accordning to
204
    NEP 2035 (version 2021), scenario 2035 C
205
206
    Returns
207
    -------
208
    None.
209
210
    """
211
212
    sources = config.datasets()["scenario_input"]["sources"]
213
    targets = config.datasets()["scenario_input"]["targets"]
214
215
    # Connect to local database
216
    engine = db.engine()
217
218
    # Delete rows if already exist
219
    db.execute_sql(
220
        f"""
221
        DELETE FROM
222
        {targets['scenario_capacities']['schema']}.
223
        {targets['scenario_capacities']['table']}
224
        WHERE scenario_name = 'eGon2035'
225
        AND nuts != 'DE'
226
        """
227
    )
228
229
    # read-in installed capacities per federal state of germany
230
    target_file = (
231
        Path(".")
232
        / "data_bundle_egon_data"
233
        / "nep2035_version2021"
234
        / sources["eGon2035"]["capacities"]
235
    )
236
237
    df = pd.read_excel(
238
        target_file,
239
        sheet_name="1.Entwurf_NEP2035_V2021",
240
        index_col="Unnamed: 0",
241
    )
242
243
    df_draft = pd.read_excel(
244
        target_file,
245
        sheet_name="Entwurf_des_Szenariorahmens",
246
        index_col="Unnamed: 0",
247
    )
248
249
    # Import data on wind offshore capacities
250
    df_windoff = pd.read_excel(
251
        target_file,
252
        sheet_name="WInd_Offshore_NEP",
253
    ).dropna(subset=["Bundesland", "Netzverknuepfungspunkt"])
254
255
    # Remove trailing whitespace from column Bundesland
256
    df_windoff["Bundesland"] = df_windoff["Bundesland"].str.strip()
257
258
    # Group and sum capacities per federal state
259
    df_windoff_fs = (
260
        df_windoff[["Bundesland", "C 2035"]].groupby(["Bundesland"]).sum()
261
    )
262
263
    # List federal state with an assigned wind offshore capacity
264
    index_list = list(df_windoff_fs.index.values)
265
266
    # Overwrite capacities in df_windoff with more accurate values from
267
    # df_windoff_fs
268
269
    for state in index_list:
270
        df.at["Wind offshore", state] = (
271
            df_windoff_fs.at[state, "C 2035"] / 1000
272
        )
273
274
    # sort NEP-carriers:
275
    rename_carrier = {
276
        "Wind onshore": "wind_onshore",
277
        "Wind offshore": "wind_offshore",
278
        "sonstige Konventionelle": "others",
279
        "Speicherwasser": "reservoir",
280
        "Laufwasser": "run_of_river",
281
        "Biomasse": "biomass",
282
        "Erdgas": "gas",
283
        "Kuppelgas": "gas",
284
        "PV (Aufdach)": "solar_rooftop",
285
        "PV (Freiflaeche)": "solar",
286
        "Pumpspeicher": "pumped_hydro",
287
        "sonstige EE": "others",
288
        "Oel": "oil",
289
        "Haushaltswaermepumpen": "residential_rural_heat_pump",
290
        "KWK < 10 MW": "small_chp",
291
    }
292
    # 'Elektromobilitaet gesamt': 'transport',
293
    # 'Elektromobilitaet privat': 'transport'}
294
295
    # nuts1 to federal state in Germany
296
    map_nuts = pd.read_sql(
297
        f"""
298
        SELECT DISTINCT ON (nuts) gen, nuts
299
        FROM {sources['boundaries']['schema']}.{sources['boundaries']['table']}
300
        """,
301
        engine,
302
        index_col="gen",
303
    )
304
305
    insert_data = pd.DataFrame()
306
307
    scaled_carriers = [
308
        "Haushaltswaermepumpen",
309
        "PV (Aufdach)",
310
        "PV (Freiflaeche)",
311
    ]
312
313
    for bl in map_nuts.index:
314
        data = pd.DataFrame(df[bl])
315
316
        # if distribution to federal states is not provided,
317
        # use data from draft of scenario report
318
        for c in scaled_carriers:
319
            data.loc[c, bl] = (
320
                df_draft.loc[c, bl]
321
                / df_draft.loc[c, "Summe"]
322
                * df.loc[c, "Summe"]
323
            )
324
325
        # split hydro into run of river and reservoir
326
        # according to draft of scenario report
327
        if data.loc["Lauf- und Speicherwasser", bl] > 0:
328
            for c in ["Speicherwasser", "Laufwasser"]:
329
                data.loc[c, bl] = (
330
                    data.loc["Lauf- und Speicherwasser", bl]
331
                    * df_draft.loc[c, bl]
332
                    / df_draft.loc[["Speicherwasser", "Laufwasser"], bl].sum()
333
                )
334
335
        data["carrier"] = data.index.map(rename_carrier)
336
        data = data.groupby(data.carrier)[bl].sum().reset_index()
337
        data["component"] = "generator"
338
        data["nuts"] = map_nuts.nuts[bl]
339
        data["scenario_name"] = "eGon2035"
340
341
        # According to NEP, each heatpump has 5kW_el installed capacity
342
        # source: Entwurf des Szenariorahmens NEP 2035, version 2021, page 47
343
        data.loc[data.carrier == "residential_rural_heat_pump", bl] *= 5e-6
344
        data.loc[
345
            data.carrier == "residential_rural_heat_pump", "component"
346
        ] = "link"
347
348
        data = data.rename(columns={bl: "capacity"})
349
350
        # convert GW to MW
351
        data.capacity *= 1e3
352
353
        insert_data = pd.concat([insert_data, data])
354
355
    # Get aggregated capacities from nep's power plant list for certain carrier
356
357
    carriers = ["oil", "other_non_renewable", "pumped_hydro"]
358
359
    capacities_list = aggr_nep_capacities(carriers)
360
361
    # Filter by carrier
362
    updated = insert_data[insert_data["carrier"].isin(carriers)]
363
364
    # Merge to replace capacities for carriers "oil", "other_non_renewable" and
365
    # "pumped_hydro"
366
    updated = (
367
        updated.merge(capacities_list, on=["carrier", "nuts"], how="left")
368
        .fillna(0)
369
        .drop(["capacity"], axis=1)
370
        .rename(columns={"c2035_capacity": "capacity"})
371
    )
372
373
    # Remove updated entries from df
374
    original = insert_data[~insert_data["carrier"].isin(carriers)]
375
376
    # Join dfs
377
    insert_data = pd.concat([original, updated])
378
379
    # Insert data to db
380
    insert_data.to_sql(
381
        targets["scenario_capacities"]["table"],
382
        engine,
383
        schema=targets["scenario_capacities"]["schema"],
384
        if_exists="append",
385
        index=insert_data.index,
386
    )
387
388
    # Add district heating data accordning to energy and full load hours
389
    district_heating_input()
390
391
392
def population_share():
393
    """Calulate share of population in testmode
394
395
    Returns
396
    -------
397
    float
398
        Share of population in testmode
399
400
    """
401
402
    sources = config.datasets()["scenario_input"]["sources"]
403
404
    return (
405
        pd.read_sql(
406
            f"""
407
            SELECT SUM(population)
408
            FROM {sources['zensus_population']['schema']}.
409
            {sources['zensus_population']['table']}
410
            WHERE population>0
411
            """,
412
            con=db.engine(),
413
        )["sum"][0]
414
        / 80324282
415
    )
416
417
418
def aggr_nep_capacities(carriers):
419
    """Aggregates capacities from NEP power plants list by carrier and federal
420
    state
421
422
    Returns
423
    -------
424
    pandas.Dataframe
425
        Dataframe with capacities per federal state and carrier
426
427
    """
428
    # Get list of power plants from nep
429
    nep_capacities = insert_nep_list_powerplants(export=False)[
430
        ["federal_state", "carrier", "c2035_capacity"]
431
    ]
432
433
    # Sum up capacities per federal state and carrier
434
    capacities_list = (
435
        nep_capacities.groupby(["federal_state", "carrier"])["c2035_capacity"]
436
        .sum()
437
        .to_frame()
438
        .reset_index()
439
    )
440
441
    # Neglect entries with carriers not in argument
442
    capacities_list = capacities_list[capacities_list.carrier.isin(carriers)]
443
444
    # Include NUTS code
445
    capacities_list["nuts"] = capacities_list.federal_state.map(nuts_mapping())
446
447
    # Drop entries for foreign plants with nan values and federal_state column
448
    capacities_list = capacities_list.dropna(subset=["nuts"]).drop(
449
        columns=["federal_state"]
450
    )
451
452
    return capacities_list
453
454
455
def map_carrier():
456
    """Map carriers from NEP and Marktstammdatenregister to carriers from eGon
457
458
    Returns
459
    -------
460
    pandas.Series
461
        List of mapped carriers
462
463
    """
464
    return pd.Series(
465
        data={
466
            "Abfall": "others",
467
            "Erdgas": "gas",
468
            "Sonstige\nEnergieträger": "others",
469
            "Steinkohle": "coal",
470
            "Kuppelgase": "gas",
471
            "Mineralöl-\nprodukte": "oil",
472
            "Braunkohle": "lignite",
473
            "Waerme": "others",
474
            "Mineraloelprodukte": "oil",
475
            "Mineralölprodukte": "oil",
476
            "NichtBiogenerAbfall": "others",
477
            "nicht biogener Abfall": "others",
478
            "AndereGase": "gas",
479
            "andere Gase": "gas",
480
            "Sonstige_Energietraeger": "others",
481
            "Kernenergie": "nuclear",
482
            "Pumpspeicher": "pumped_hydro",
483
            "Mineralöl-\nProdukte": "oil",
484
            "Biomasse": "biomass",
485
        }
486
    )
487
488
489
def insert_nep_list_powerplants(export=True):
490
    """Insert list of conventional powerplants attached to the approval
491
    of the scenario report by BNetzA
492
493
    Parameters
494
    ----------
495
    export : bool
496
        Choose if nep list should be exported to the data
497
        base. The default is True.
498
        If export=False a data frame will be returned
499
500
    Returns
501
    -------
502
    kw_liste_nep : pandas.DataFrame
503
        List of conventional power plants from nep if export=False
504
    """
505
506
    sources = config.datasets()["scenario_input"]["sources"]
507
    targets = config.datasets()["scenario_input"]["targets"]
508
509
    # Connect to local database
510
    engine = db.engine()
511
512
    # Read-in data from csv-file
513
    target_file = (
514
        Path(".")
515
        / "data_bundle_egon_data"
516
        / "nep2035_version2021"
517
        / sources["eGon2035"]["list_conv_pp"]
518
    )
519
520
    kw_liste_nep = pd.read_csv(target_file, delimiter=";", decimal=",")
521
522
    # Adjust column names
523
    kw_liste_nep = kw_liste_nep.rename(
524
        columns={
525
            "BNetzA-ID": "bnetza_id",
526
            "Kraftwerksname": "name",
527
            "Blockname": "name_unit",
528
            "Energieträger": "carrier_nep",
529
            "KWK\nJa/Nein": "chp",
530
            "PLZ": "postcode",
531
            "Ort": "city",
532
            "Bundesland/\nLand": "federal_state",
533
            "Inbetrieb-\nnahmejahr": "commissioned",
534
            "Status": "status",
535
            "el. Leistung\n06.02.2020": "capacity",
536
            "A 2035:\nKWK-Ersatz": "a2035_chp",
537
            "A 2035:\nLeistung": "a2035_capacity",
538
            "B 2035\nKWK-Ersatz": "b2035_chp",
539
            "B 2035:\nLeistung": "b2035_capacity",
540
            "C 2035:\nKWK-Ersatz": "c2035_chp",
541
            "C 2035:\nLeistung": "c2035_capacity",
542
            "B 2040:\nKWK-Ersatz": "b2040_chp",
543
            "B 2040:\nLeistung": "b2040_capacity",
544
        }
545
    )
546
547
    # Cut data to federal state if in testmode
548
    boundary = config.settings()["egon-data"]["--dataset-boundary"]
549
    if boundary != "Everything":
550
        map_states = {
551
            "Baden-Württemberg": "BW",
552
            "Nordrhein-Westfalen": "NW",
553
            "Hessen": "HE",
554
            "Brandenburg": "BB",
555
            "Bremen": "HB",
556
            "Rheinland-Pfalz": "RP",
557
            "Sachsen-Anhalt": "ST",
558
            "Schleswig-Holstein": "SH",
559
            "Mecklenburg-Vorpommern": "MV",
560
            "Thüringen": "TH",
561
            "Niedersachsen": "NI",
562
            "Sachsen": "SN",
563
            "Hamburg": "HH",
564
            "Saarland": "SL",
565
            "Berlin": "BE",
566
            "Bayern": "BY",
567
        }
568
569
        kw_liste_nep = kw_liste_nep[
570
            kw_liste_nep.federal_state.isin([map_states[boundary], np.nan])
571
        ]
572
573
        for col in [
574
            "capacity",
575
            "a2035_capacity",
576
            "b2035_capacity",
577
            "c2035_capacity",
578
            "b2040_capacity",
579
        ]:
580
            kw_liste_nep.loc[
581
                kw_liste_nep[kw_liste_nep.federal_state.isnull()].index, col
582
            ] *= population_share()
583
584
    kw_liste_nep["carrier"] = map_carrier()[kw_liste_nep.carrier_nep].values
585
586
    if export is True:
587
        # Insert data to db
588
        kw_liste_nep.to_sql(
589
            targets["nep_conventional_powerplants"]["table"],
590
            engine,
591
            schema=targets["nep_conventional_powerplants"]["schema"],
592
            if_exists="replace",
593
        )
594
    else:
595
        return kw_liste_nep
596
597
598
def district_heating_input():
599
    """Imports data for district heating networks in Germany
600
601
    Returns
602
    -------
603
    None.
604
605
    """
606
607
    sources = config.datasets()["scenario_input"]["sources"]
608
609
    # import data to dataframe
610
    file = (
611
        Path(".")
612
        / "data_bundle_egon_data"
613
        / "nep2035_version2021"
614
        / sources["eGon2035"]["capacities"]
615
    )
616
    df = pd.read_excel(
617
        file, sheet_name="Kurzstudie_KWK", dtype={"Wert": float}
618
    )
619
    df.set_index(["Energietraeger", "Name"], inplace=True)
620
621
    # Scale values to population share in testmode
622
    if config.settings()["egon-data"]["--dataset-boundary"] != "Everything":
623
        df.loc[
624
            pd.IndexSlice[:, "Fernwaermeerzeugung"], "Wert"
625
        ] *= population_share()
626
627
    # Connect to database
628
    engine = db.engine()
629
    session = sessionmaker(bind=engine)()
630
631
    # insert heatpumps and resistive heater as link
632
    for c in ["Grosswaermepumpe", "Elektrodenheizkessel"]:
633
        entry = EgonScenarioCapacities(
634
            component="link",
635
            scenario_name="eGon2035",
636
            nuts="DE",
637
            carrier="urban_central_"
638
            + ("heat_pump" if c == "Grosswaermepumpe" else "resistive_heater"),
639
            capacity=df.loc[(c, "Fernwaermeerzeugung"), "Wert"]
640
            * 1e6
641
            / df.loc[(c, "Volllaststunden"), "Wert"]
642
            / df.loc[(c, "Wirkungsgrad"), "Wert"],
643
        )
644
645
        session.add(entry)
646
647
    # insert solar- and geothermal as generator
648
    for c in ["Geothermie", "Solarthermie"]:
649
        entry = EgonScenarioCapacities(
650
            component="generator",
651
            scenario_name="eGon2035",
652
            nuts="DE",
653
            carrier="urban_central_"
654
            + (
655
                "solar_thermal_collector"
656
                if c == "Solarthermie"
657
                else "geo_thermal"
658
            ),
659
            capacity=df.loc[(c, "Fernwaermeerzeugung"), "Wert"]
660
            * 1e6
661
            / df.loc[(c, "Volllaststunden"), "Wert"],
662
        )
663
664
        session.add(entry)
665
666
    session.commit()
667
668
669
def insert_data_nep():
670
    """Overall function for importing scenario input data for eGon2035 scenario
671
672
    Returns
673
    -------
674
    None.
675
676
    """
677
678
    insert_nep_list_powerplants(export=True)
679
680
    insert_capacities_per_federal_state_nep()
681
682
683
def eGon100_capacities():
684
    """Inserts installed capacities for the eGon100 scenario
685
686
    Returns
687
    -------
688
    None.
689
690
    """
691
692
    sources = config.datasets()["scenario_input"]["sources"]
693
    targets = config.datasets()["scenario_input"]["targets"]
694
695
    # read-in installed capacities
696
    cwd = Path(".")
697
698
    if config.settings()["egon-data"]["--run-pypsa-eur"]:
699
        filepath = cwd / "run-pypsa-eur"
700
        pypsa_eur_repos = filepath / "pypsa-eur"
701
        # Read YAML file
702
        pes_egonconfig = pypsa_eur_repos / "config" / "config.yaml"
703
        with open(pes_egonconfig, "r") as stream:
704
            data_config = yaml.safe_load(stream)
705
706
        target_file = (
707
            pypsa_eur_repos
708
            / "results"
709
            / data_config["run"]["name"]
710
            / "csvs"
711
            / sources["eGon100RE"]["capacities"]
712
        )
713
714
    else:
715
        target_file = (
716
            cwd
717
            / "data_bundle_egon_data"
718
            / "pypsa_eur"
719
            / "csvs"
720
            / sources["eGon100RE"]["capacities"]
721
        )
722
723
    df = pd.read_csv(target_file, delimiter=",", skiprows=3)
724
    df.columns = [
725
        "component",
726
        "country",
727
        "carrier",
728
        "p_nom_2025",
729
        "p_nom_2030",
730
        "p_nom_2035",
731
        "p_nom_2045",
732
    ]
733
734
    df.set_index("carrier", inplace=True)
735
736
    df = df[df.country.str[:2] == "DE"]
737
738
    # Drop country column
739
    df.drop("country", axis=1, inplace=True)
740
741
    # Drop copmponents which will be optimized in eGo
742
    unused_carrier = [
743
        "BEV charger",
744
        "DAC",
745
        "H2 Electrolysis",
746
        "electricity distribution grid",
747
        "home battery charger",
748
        "home battery discharger",
749
        "H2",
750
        "Li ion",
751
        "rural water tanks charger",
752
        "rural water tanks discharger",
753
        "urban decentral water tanks charger",
754
        "urban decentral water tanks discharger",
755
        "rural water tanks",
756
        "urban decentral water tanks",
757
        "urban central water tanks",
758
        "urban central water tanks charger",
759
        "urban central water tanks discharger",
760
        "H2 Fuel Cell",
761
        "gas",
762
        "SMR",
763
        "SMR CC",
764
        "Sabatier",
765
        "biogas to gas",
766
        "biogas to gas CC",
767
        "gas for industry",
768
        "gas for industry CC",
769
        "methanolisation",
770
        "EV battery",
771
        "H2 Store",
772
        "battery",
773
        "battery charger",
774
        "battery discharger",
775
        "unsustainable biogas",
776
        "biogas",
777
        "Fischer-Tropsch",
778
    ]
779
780
    df = df[~df.index.isin(unused_carrier)]
781
782
    df.index = df.index.str.replace(" ", "_")
783
784
    # Aggregate offshore wind
785
    df.loc["wind_offshore"] = df[df.index.str.startswith("offwind")].sum(
786
        numeric_only=True
787
    )
788
    df.loc["wind_offshore", "component"] = "generators"
789
    df = df.drop(df.index[df.index.str.startswith("offwind")])
790
791
    # Aggregate OCGT and CCGT
792
    df.loc["OCGT", df.columns != "component"] = (
793
        df.loc["OCGT", df.columns != "component"] * 0.425
794
    )
795
    df.loc["CCGT", df.columns != "component"] = (
796
        df.loc["CCGT", df.columns != "component"] * 0.570
797
    )
798
    df.loc["gas"] = df[df.index.str.endswith("CGT")].sum(numeric_only=True)
799
    df.loc["gas", "component"] = "links"
800
    df = df.drop(df.index[df.index.str.endswith("CGT")])
801
802
    # Aggregate hydro and pumped_hydro
803
    df.loc["pumped_hydro"] = df.loc["PHS"] + df.loc["hydro"]
804
    df.loc["pumped_hydro", "component"] = "storage_units"
805
    df = df.drop(["PHS", "hydro"])
806
807
    # Aggregate solar and solar-hsat
808
    df.loc["solar"] = df.loc["solar"] + df.loc["solar-hsat"]
809
    df.loc["solar", "component"] = "generators"
810
    df = df.drop(["solar-hsat"])
811
812
    # Aggregate technologies with and without carbon_capture (CC)
813
    for carrier in [
814
        "urban_central_gas_CHP",
815
        "urban_central_solid_biomass_CHP",
816
    ]:
817
        df.loc[
818
            carrier, ["p_nom_2025", "p_nom_2030", "p_nom_2035", "p_nom_2045"]
819
        ] += df.loc[
820
            f"{carrier}_CC",
821
            ["p_nom_2025", "p_nom_2030", "p_nom_2035", "p_nom_2045"],
822
        ]
823
        df = df.drop([f"{carrier}_CC"])
824
825
    # Aggregate urban decentral and rural heat supply
826
    for merge_carrier in df.index[df.index.str.startswith("urban_decentral")]:
827
828
        # Add capacity of urban decentral to coresponding rural technology
829
        df.loc[merge_carrier.replace("urban_decentral", "rural")] += df.loc[
830
            merge_carrier
831
        ]
832
833
        # Avoid summing up of component names
834
        df.loc[
835
            merge_carrier.replace("urban_decentral", "rural"), "component"
836
        ] = df.loc[merge_carrier, "component"]
837
838
        # Drop urban decentral technology
839
        df = df.drop(merge_carrier)
840
841
    # Aggregate rural air and rural ground heat pump
842
    df.loc["rural_heat_pump"] = (
843
        df.loc["rural_air_heat_pump"] + df.loc["rural_ground_heat_pump"]
844
    )
845
    df.loc["rural_heat_pump", "component"] = "links"
846
    df = df.drop(["rural_air_heat_pump", "rural_ground_heat_pump"])
847
848
    # Rename carriers
849
    df.rename(
850
        {
851
            "onwind": "wind_onshore",
852
            "ror": "run_of_river",
853
            "urban_central_air_heat_pump": "urban_central_heat_pump",
854
            "urban_central_solar_thermal": (
855
                "urban_central_solar_thermal_collector"
856
            ),
857
            "home_battery": "battery",
858
        },
859
        inplace=True,
860
    )
861
862
    # Reset index
863
    df = df.reset_index()
864
865
    # Insert target capacities for all years
866
    for year in ["2025", "2030", "2035", "2045"]:
867
        df_year = df.rename(
868
            {f"p_nom_{year}": "capacity", "index": "carrier"}, axis="columns"
869
        )
870
        df_year.drop(
871
            df_year.columns[
872
                ~df_year.columns.isin(["carrier", "component", "capacity"])
873
            ],
874
            axis="columns",
875
            inplace=True,
876
        )
877
878
        if year == "2045":
879
            df_year["scenario_name"] = "eGon100RE"
880
        else:
881
            df_year["scenario_name"] = f"powerd{year}"
882
883
        df_year["nuts"] = "DE"
884
885
        db.execute_sql(
886
            f"""
887
            DELETE FROM
888
            {targets['scenario_capacities']['schema']}.{targets['scenario_capacities']['table']}
889
            WHERE scenario_name='{df_year["scenario_name"].unique()[0]}'
890
            """
891
        )
892
893
        df_year.to_sql(
894
            targets["scenario_capacities"]["table"],
895
            schema=targets["scenario_capacities"]["schema"],
896
            con=db.engine(),
897
            if_exists="append",
898
            index=False,
899
        )
900
901
902
def add_metadata():
903
    """Add metdata to supply.egon_scenario_capacities
904
905
    Returns
906
    -------
907
    None.
908
909
    """
910
911
    # Import column names and datatypes
912
    fields = pd.DataFrame(
913
        generate_resource_fields_from_sqla_model(EgonScenarioCapacities)
914
    ).set_index("name")
915
916
    # Set descriptions and units
917
    fields.loc["index", "description"] = "Index"
918
    fields.loc["component", "description"] = (
919
        "Name of representative PyPSA component"
920
    )
921
    fields.loc["carrier", "description"] = "Name of carrier"
922
    fields.loc["capacity", "description"] = "Installed capacity"
923
    fields.loc["capacity", "unit"] = "MW"
924
    fields.loc["nuts", "description"] = (
925
        "NUTS region, either federal state or Germany"
926
    )
927
    fields.loc["scenario_name", "description"] = (
928
        "Name of corresponding eGon scenario"
929
    )
930
931
    # Reformat pandas.DataFrame to dict
932
    fields = fields.reset_index().to_dict(orient="records")
933
934
    meta = {
935
        "name": "supply.egon_scenario_capacities",
936
        "title": "eGon scenario capacities",
937
        "id": "WILL_BE_SET_AT_PUBLICATION",
938
        "description": (
939
            "Installed capacities of scenarios used in the eGon project"
940
        ),
941
        "language": ["de-DE"],
942
        "publicationDate": datetime.date.today().isoformat(),
943
        "context": context(),
944
        "spatial": {
945
            "location": None,
946
            "extent": "Germany",
947
            "resolution": None,
948
        },
949
        "sources": [
950
            sources()["nep2021"],
951
            sources()["vg250"],
952
            sources()["zensus"],
953
            sources()["egon-data"],
954
        ],
955
        "licenses": [
956
            license_ccby(
957
                "© Übertragungsnetzbetreiber; "
958
                "© Bundesamt für Kartographie und Geodäsie 2020 (Daten verändert); "
959
                "© Statistische Ämter des Bundes und der Länder 2014; "
960
                "© Jonathan Amme, Clara Büttner, Ilka Cußmann, Julian Endres, Carlos Epia, Stephan Günther, Ulf Müller, Amélia Nadal, Guido Pleßmann, Francesco Witte",
961
            )
962
        ],
963
        "contributors": [
964
            {
965
                "title": "Clara Büttner",
966
                "email": "http://github.com/ClaraBuettner",
967
                "date": time.strftime("%Y-%m-%d"),
968
                "object": None,
969
                "comment": "Imported data",
970
            },
971
        ],
972
        "resources": [
973
            {
974
                "profile": "tabular-data-resource",
975
                "name": "supply.egon_scenario_capacities",
976
                "path": None,
977
                "format": "PostgreSQL",
978
                "encoding": "UTF-8",
979
                "schema": {
980
                    "fields": fields,
981
                    "primaryKey": ["index"],
982
                    "foreignKeys": [],
983
                },
984
                "dialect": {"delimiter": None, "decimalSeparator": "."},
985
            }
986
        ],
987
        "metaMetadata": meta_metadata(),
988
    }
989
990
    # Create json dump
991
    meta_json = "'" + json.dumps(meta) + "'"
992
993
    # Add metadata as a comment to the table
994
    db.submit_comment(
995
        meta_json,
996
        EgonScenarioCapacities.__table__.schema,
997
        EgonScenarioCapacities.__table__.name,
998
    )
999
1000
1001
tasks = (create_table,)
1002
1003
scenarios = config.settings()["egon-data"]["--scenarios"]
1004
1005
status_quo = False
1006
1007
for scenario in scenarios:
1008
    if "status" in scenario:
1009
        tasks += (
1010
            wrapped_partial(
1011
                insert_capacities_status_quo,
1012
                scenario=scenario,
1013
                postfix=f"_{scenario[-2:]}",
1014
            ),
1015
        )
1016
        status_quo = True
1017
1018
if status_quo or ("eGon2035" in scenarios):
1019
    tasks += (insert_data_nep,)
1020
1021
if "eGon100RE" in scenarios:
1022
    tasks += (eGon100_capacities,)
1023
1024
tasks += (add_metadata,)
1025
1026
1027
class ScenarioCapacities(Dataset):
1028
    """
1029
    Create and fill table with installed generation capacities in Germany
1030
1031
    This dataset creates and fills a table with the installed generation capacities in
1032
    Germany in a lower spatial resolution (either per federal state or on national level).
1033
    This data is coming from external sources (e.g. German grid developement plan for scenario eGon2035).
1034
    The table is in downstream datasets used to define target values for the installed capacities.
1035
1036
1037
    *Dependencies*
1038
      * :py:func:`Setup <egon.data.datasets.database.setup>`
1039
      * :py:class:`PypsaEurSec <egon.data.datasets.pypsaeursec.PypsaEurSec>`
1040
      * :py:class:`Vg250 <egon.data.datasets.vg250.Vg250>`
1041
      * :py:class:`DataBundle <egon.data.datasets.data_bundle.DataBundle>`
1042
      * :py:class:`ZensusPopulation <egon.data.datasets.zensus.ZensusPopulation>`
1043
1044
1045
    *Resulting tables*
1046
      * :py:class:`supply.egon_scenario_capacities <egon.data.datasets.scenario_capacities.EgonScenarioCapacities>` is created and filled
1047
      * :py:class:`supply.egon_nep_2021_conventional_powerplants <egon.data.datasets.scenario_capacities.NEP2021ConvPowerPlants>` is created and filled
1048
1049
    """
1050
1051
    #:
1052
    name: str = "ScenarioCapacities"
1053
    #:
1054
    version: str = "0.0.19"
1055
1056
    def __init__(self, dependencies):
1057
        super().__init__(
1058
            name=self.name,
1059
            version=self.version,
1060
            dependencies=dependencies,
1061
            tasks=tasks,
1062
            validation={
1063
                "data-quality": [
1064
                    RowCountValidation(
1065
                        table="supply.egon_nep_2021_conventional_powerplants",
1066
                        rule_id="TEST_ROW_COUNT.egon_nep_2021_conventional_powerplants",
1067
                        expected_count={"Schleswig-Holstein": 40, "Everything": 737}
1068
                    ),
1069
                    DataTypeValidation(
1070
                        table="supply.egon_nep_2021_conventional_powerplants",
1071
                        rule_id="TEST_DATA_MULTIPLE_TYPES.egon_nep_2021_conventional_powerplants",
1072
                        column_types={
1073
                            "index": "bigint",
1074
                            "bnetza_id": "text",
1075
                            "name": "text",
1076
                            "name_unit": "text",
1077
                            "carrier_nep": "text",
1078
                            "chp": "text",
1079
                            "postcode": "text",
1080
                            "city": "text",
1081
                            "federal_state": "text",
1082
                            "commissioned": "double precision",
1083
                            "status": "text",
1084
                            "capacity": "double precision",
1085
                            "a2035_chp": "text",
1086
                            "a2035_capacity": "double precision",
1087
                            "b2035_chp": "text",
1088
                            "b2035_capacity": "double precision",
1089
                            "c2035_chp": "text",
1090
                            "c2035_capacity": "double precision",
1091
                            "b2040_chp": "text",
1092
                            "b2040_capacity": "double precision",
1093
                            "carrier": "text"
1094
                        }
1095
                    ),
1096
                    NotNullAndNotNaNValidation(
1097
                        table="supply.egon_nep_2021_conventional_powerplants",
1098
                        rule_id="TEST_NOT_NAN.egon_nep_2021_conventional_powerplants",
1099
                        columns=[
1100
                            "index",
1101
                            "bnetza_id",
1102
                            "name",
1103
                            "name_unit",
1104
                            "carrier_nep",
1105
                            "chp",
1106
                            "postcode",
1107
                            "city",
1108
                            "federal_state",
1109
                            "commissioned",
1110
                            "status",
1111
                            "capacity",
1112
                            "a2035_chp",
1113
                            "a2035_capacity",
1114
                            "b2035_chp",
1115
                            "b2035_capacity",
1116
                            "c2035_chp",
1117
                            "c2035_capacity",
1118
                            "b2040_chp",
1119
                            "b2040_capacity",
1120
                            "carrier"
1121
                        ]
1122
                    ),
1123
                    WholeTableNotNullAndNotNaNValidation(
1124
                        table="supply.egon_nep_2021_conventional_powerplants",
1125
                        rule_id="TEST_WHOLE_TABLE_NOT_NAN.egon_nep_2021_conventional_powerplants"
1126
                    ),
1127
                    RowCountValidation(
1128
                        table="supply.egon_scenario_capacities",
1129
                        rule_id="TEST_ROW_COUNT.egon_scenario_capacities",
1130
                        expected_count={"Schleswig-Holstein": 17, "Everything": 236}
1131
                    ),
1132
                    DataTypeValidation(
1133
                        table="supply.egon_scenario_capacities",
1134
                        rule_id="TEST_DATA_MULTIPLE_TYPES.egon_scenario_capacities",
1135
                        column_types={
1136
                            "index": "integer",
1137
                            "component": "character varying",
1138
                            "carrier": "character varying",
1139
                            "capacity": "double precision",
1140
                            "nuts": "character varying",
1141
                            "scenario_name": "character varying"
1142
                        }
1143
                    ),
1144
                    NotNullAndNotNaNValidation(
1145
                        table="supply.egon_scenario_capacities",
1146
                        rule_id="TEST_NOT_NAN.egon_scenario_capacities",
1147
                        columns=[
1148
                            "index",
1149
                            "component",
1150
                            "carrier",
1151
                            "capacity",
1152
                            "nuts",
1153
                            "scenario_name"
1154
                        ]
1155
                    ),
1156
                    WholeTableNotNullAndNotNaNValidation(
1157
                        table="supply.egon_scenario_capacities",
1158
                        rule_id="TEST_WHOLE_TABLE_NOT_NAN.egon_scenario_capacities"
1159
                    ),
1160
                    ValueSetValidation(
1161
                        table="supply.egon_scenario_capacities",
1162
                        rule_id="VALUE_SET_VALIDATION_CARRIER.egon_scenario_capacities",
1163
                        column="carrier",
1164
                        expected_values=["pumped_hydro",
1165
                            "gas_for_industry",
1166
                            "gas_for_industry_CC",
1167
                            "biogas_to_gas",
1168
                            "Sabatier",
1169
                            "urban_central_gas_CHP",
1170
                            "solar",
1171
                            "reservoir",
1172
                            "biogas",
1173
                            "residential_rural_heat_pump",
1174
                            "urban_central_solar_thermal_collector",
1175
                            "oil",
1176
                            "urban_central_resistive_heater",
1177
                            "wind_offshore",
1178
                            "battery",
1179
                            "others",
1180
                            "gas",
1181
                            "wind_onshore",
1182
                            "small_chp",
1183
                            "Li_ion",
1184
                            "urban_central_heat_pump",
1185
                            "urban_central_geo_thermal",
1186
                            "SMR",
1187
                            "biomass",
1188
                            "hydro",
1189
                            "run_of_river",
1190
                            "rural_solar_thermal",
1191
                            "solar_rooftop",
1192
                            "BEV_charger"]
1193
                    ),
1194
                    ValueSetValidation(
1195
                        table="supply.egon_scenario_capacities",
1196
                        rule_id="VALUE_SET_VALIDATION_SCENARIO_NAME.egon_scenario_capacities",
1197
                        column="scenario_name",
1198
                        expected_values=["eGon2035", "eGon100RE"]
1199
                    ),
1200
1201
                ]
1202
            },
1203
            on_validation_failure="continue"
1204
        )
1205