Passed
Pull Request — dev (#809)
by
unknown
02:29
created

data.datasets.scenario_capacities.map_nuts()   A

Complexity

Conditions 1

Size

Total Lines 22
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 22
rs 9.45
c 0
b 0
f 0
cc 1
nop 0
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
7
from sqlalchemy import Column, Float, Integer, String
8
from sqlalchemy.ext.declarative import declarative_base
9
from sqlalchemy.orm import sessionmaker
10
import numpy as np
11
import pandas as pd
12
import yaml
13
14
from egon.data import db
15
from egon.data.config import settings
16
from egon.data.datasets import Dataset
17
import egon.data.config
18
19
### will be later imported from another file ###
20
Base = declarative_base()
21
22
23
class EgonScenarioCapacities(Base):
24
    __tablename__ = "egon_scenario_capacities"
25
    __table_args__ = {"schema": "supply"}
26
    index = Column(Integer, primary_key=True)
27
    component = Column(String(25))
28
    carrier = Column(String(50))
29
    capacity = Column(Float)
30
    nuts = Column(String(12))
31
    scenario_name = Column(String(50))
32
33
34
class NEP2021ConvPowerPlants(Base):
35
    __tablename__ = "egon_nep_2021_conventional_powerplants"
36
    __table_args__ = {"schema": "supply"}
37
    index = Column(String(50), primary_key=True)
38
    bnetza_id = Column(String(50))
39
    name = Column(String(100))
40
    name_unit = Column(String(50))
41
    carrier_nep = Column(String(50))
42
    carrier = Column(String(12))
43
    chp = Column(String(12))
44
    postcode = Column(String(12))
45
    city = Column(String(50))
46
    federal_state = Column(String(12))
47
    commissioned = Column(String(12))
48
    status = Column(String(50))
49
    capacity = Column(Float)
50
    a2035_chp = Column(String(12))
51
    a2035_capacity = Column(Float)
52
    b2035_chp = Column(String(12))
53
    b2035_capacity = Column(Float)
54
    c2035_chp = Column(String(12))
55
    c2035_capacity = Column(Float)
56
    b2040_chp = Column(String(12))
57
    b2040_capacity = Column(Float)
58
59
60
class ScenarioCapacities(Dataset):
61
    def __init__(self, dependencies):
62
        super().__init__(
63
            name="ScenarioCapacities",
64
            version="0.0.8",
65
            dependencies=dependencies,
66
            tasks=(create_table, insert_data_nep, eGon100_capacities),
67
        )
68
69
70
def create_table():
71
    """Create input tables for scenario setup
72
73
    Returns
74
    -------
75
    None.
76
77
    """
78
79
    engine = db.engine()
80
    db.execute_sql("CREATE SCHEMA IF NOT EXISTS supply;")
81
    EgonScenarioCapacities.__table__.drop(bind=engine, checkfirst=True)
82
    NEP2021ConvPowerPlants.__table__.drop(bind=engine, checkfirst=True)
83
    EgonScenarioCapacities.__table__.create(bind=engine, checkfirst=True)
84
    NEP2021ConvPowerPlants.__table__.create(bind=engine, checkfirst=True)
85
86
87
def map_nuts():
88
89
    map_nuts = {
90
        "BW": "DE1",
91
        "NW": "DEA",
92
        "HE": "DE7",
93
        "BB": "DE4",
94
        "HB": "DE5",
95
        "RP": "DEB",
96
        "ST": "DEE",
97
        "SH": "DEF",
98
        "MV": "DE8",
99
        "TH": "DEG",
100
        "NI": "DE9",
101
        "SN": "DED",
102
        "HH": "DE6",
103
        "SL": "DEC",
104
        "BE": "DE3",
105
        "BY": "DE2",
106
    }
107
108
    return map_nuts
109
110
111
def insert_capacities_per_federal_state_nep():
112
    """Inserts installed capacities per federal state accordning to
113
    NEP 2035 (version 2021), scenario 2035 C
114
115
    Returns
116
    -------
117
    None.
118
119
    """
120
121
    sources = egon.data.config.datasets()["scenario_input"]["sources"]
122
    targets = egon.data.config.datasets()["scenario_input"]["targets"]
123
124
    # Connect to local database
125
    engine = db.engine()
126
127
    # Delete rows if already exist
128
    db.execute_sql(
129
        f"""
130
        DELETE FROM
131
        {targets['scenario_capacities']['schema']}.{targets['scenario_capacities']['table']}
132
        WHERE scenario_name = 'eGon2035'
133
        AND nuts != 'DE'
134
        """
135
    )
136
137
    # read-in installed capacities per federal state of germany
138
    target_file = (
139
        Path(".")
140
        / "data_bundle_egon_data"
141
        / "nep2035_version2021"
142
        / sources["eGon2035"]["capacities"]
143
    )
144
145
    df = pd.read_excel(
146
        target_file,
147
        sheet_name="1.Entwurf_NEP2035_V2021",
148
        index_col="Unnamed: 0",
149
    )
150
151
    df_draft = pd.read_excel(
152
        target_file,
153
        sheet_name="Entwurf_des_Szenariorahmens",
154
        index_col="Unnamed: 0",
155
    )
156
157
    # sort NEP-carriers:
158
    rename_carrier = {
159
        "Wind onshore": "wind_onshore",
160
        "Wind offshore": "wind_offshore",
161
        "sonstige Konventionelle": "other_non_renewable",
162
        "Speicherwasser": "reservoir",
163
        "Laufwasser": "run_of_river",
164
        "Biomasse": "biomass",
165
        "Erdgas": "gas",
166
        "Kuppelgas": "gas",
167
        "PV (Aufdach)": "solar_rooftop",
168
        "PV (Freiflaeche)": "solar",
169
        "Pumpspeicher": "pumped_hydro",
170
        "sonstige EE": "other_renewable",
171
        "Oel": "oil",
172
        "Haushaltswaermepumpen": "residential_rural_heat_pump",
173
        "KWK < 10 MW": "small_chp",
174
    }
175
    #'Elektromobilitaet gesamt': 'transport',
176
    # 'Elektromobilitaet privat': 'transport'}
177
178
    # nuts1 to federal state in Germany
179
    map_nuts = pd.read_sql(
180
        f"""
181
        SELECT DISTINCT ON (nuts) gen, nuts
182
        FROM {sources['boundaries']['schema']}.{sources['boundaries']['table']}
183
        """,
184
        engine,
185
        index_col="gen",
186
    )
187
188
    insert_data = pd.DataFrame()
189
190
    scaled_carriers = [
191
        "Haushaltswaermepumpen",
192
        "PV (Aufdach)",
193
        "PV (Freiflaeche)",
194
    ]
195
196
    for bl in map_nuts.index:
197
198
        data = pd.DataFrame(df[bl])
199
200
        # if distribution to federal states is not provided,
201
        # use data from draft of scenario report
202
        for c in scaled_carriers:
203
            data.loc[c, bl] = (
204
                df_draft.loc[c, bl]
205
                / df_draft.loc[c, "Summe"]
206
                * df.loc[c, "Summe"]
207
            )
208
209
        # split hydro into run of river and reservoir
210
        # according to draft of scenario report
211
        if data.loc["Lauf- und Speicherwasser", bl] > 0:
212
            for c in ["Speicherwasser", "Laufwasser"]:
213
                data.loc[c, bl] = (
214
                    data.loc["Lauf- und Speicherwasser", bl]
215
                    * df_draft.loc[c, bl]
216
                    / df_draft.loc[["Speicherwasser", "Laufwasser"], bl].sum()
217
                )
218
219
        data["carrier"] = data.index.map(rename_carrier)
220
        data = data.groupby(data.carrier)[bl].sum().reset_index()
221
        data["component"] = "generator"
222
        data["nuts"] = map_nuts.nuts[bl]
223
        data["scenario_name"] = "eGon2035"
224
225
        # According to NEP, each heatpump has 3kW_el installed capacity
226
        data.loc[data.carrier == "residential_rural_heat_pump", bl] *= 3e-6
227
        data.loc[
228
            data.carrier == "residential_rural_heat_pump", "component"
229
        ] = "link"
230
231
        data = data.rename(columns={bl: "capacity"})
232
233
        # convert GW to MW
234
        data.capacity *= 1e3
235
236
        insert_data = insert_data.append(data)
237
238
    # Get aggregated capacities from nep's power plant list for certain carrier
239
240
    carriers = ["oil", "other_non_renewable", "pumped_hydro"]
241
242
    capacities_list = aggr_nep_capacities(carriers)
243
244
    # Replace capacities for the named carrier
245
246
    updated = insert_data[insert_data["carrier"].isin(carriers)]
247
    updated["capacity"] = capacities_list[
248
        capacities_list["carrier"].isin(updated["carrier"])
249
        & capacities_list["nuts"].isin(updated["nuts"])
250
    ]["c2035_capacity"].values
251
252
    original = insert_data[~insert_data["carrier"].isin(carriers)]
253
254
    insert_data = pd.concat([original, updated])
255
256
    # Insert data to db
257
    insert_data.to_sql(
258
        targets["scenario_capacities"]["table"],
259
        engine,
260
        schema=targets["scenario_capacities"]["schema"],
261
        if_exists="append",
262
        index=insert_data.index,
263
    )
264
265
    # Add district heating data accordning to energy and full load hours
266
    district_heating_input()
267
268
269
def population_share():
270
    """Calulate share of population in testmode
271
272
    Returns
273
    -------
274
    float
275
        Share of population in testmode
276
277
    """
278
279
    sources = egon.data.config.datasets()["scenario_input"]["sources"]
280
281
    return (
282
        pd.read_sql(
283
            f"""
284
            SELECT SUM(population)
285
            FROM {sources['zensus_population']['schema']}.{sources['zensus_population']['table']}
286
            WHERE population>0
287
            """,
288
            con=db.engine(),
289
        )["sum"][0]
290
        / 80324282
291
    )
292
293
294
def aggr_nep_capacities(carriers):
295
    """Aggregates capacities from NEP power plants list by carrier and federal
296
    state
297
298
    Returns
299
    -------
300
    pandas.Dataframe
301
        Dataframe with capacities per federal state and carrier
302
303
    """
304
    # Get list of power plants from nep
305
    nep_capacities = insert_nep_list_powerplants(export=False)[
306
        ["federal_state", "carrier", "c2035_capacity"]
307
    ]
308
309
    # Sum up capacities per federal state and carrier
310
    capacities_list = (
311
        nep_capacities.groupby(["federal_state", "carrier"])["c2035_capacity"]
312
        .sum()
313
        .to_frame()
314
        .reset_index()
315
    )
316
317
    # Neglect entries with carriers not in argument
318
    capacities_list = capacities_list[capacities_list.carrier.isin(carriers)]
319
320
    # Include NUTS code
321
    capacities_list["nuts"] = capacities_list.federal_state.map(map_nuts())
322
323
    # Set new multiindex
324
    capacities_list = capacities_list.drop(columns=["federal_state"])
325
326
    return capacities_list
327
328
329
def map_carrier():
330
    """Map carriers from NEP and Marktstammdatenregister to carriers from eGon
331
332
    Returns
333
    -------
334
    pandas.Series
335
        List of mapped carriers
336
337
    """
338
    return pd.Series(
339
        data={
340
            "Abfall": "other_non_renewable",
341
            "Erdgas": "gas",
342
            "Sonstige\nEnergieträger": "other_non_renewable",
343
            "Steinkohle": "coal",
344
            "Kuppelgase": "gas",
345
            "Mineralöl-\nprodukte": "oil",
346
            "Braunkohle": "lignite",
347
            "Waerme": "other_non_renewable",
348
            "Mineraloelprodukte": "oil",
349
            "NichtBiogenerAbfall": "other_non_renewable",
350
            "AndereGase": "gas",
351
            "Sonstige_Energietraeger": "other_non_renewable",
352
            "Kernenergie": "nuclear",
353
            "Pumpspeicher": "pumped_hydro",
354
            "Mineralöl-\nProdukte": "oil",
355
        }
356
    )
357
358
359
def insert_nep_list_powerplants(export=True):
360
    """Insert list of conventional powerplants attached to the approval
361
    of the scenario report by BNetzA
362
363
    Parameters
364
    ----------
365
    export : bool
366
        Choose if nep list should be exported to the data
367
        base. The default is True.
368
        If export=False a data frame will be returned
369
370
    Returns
371
    -------
372
    kw_liste_nep : pandas.DataFrame
373
        List of conventional power plants from nep if export=False
374
    """
375
376
    sources = egon.data.config.datasets()["scenario_input"]["sources"]
377
    targets = egon.data.config.datasets()["scenario_input"]["targets"]
378
379
    # Connect to local database
380
    engine = db.engine()
381
382
    # Read-in data from csv-file
383
    target_file = (
384
        Path(".")
385
        / "data_bundle_egon_data"
386
        / "nep2035_version2021"
387
        / sources["eGon2035"]["list_conv_pp"]
388
    )
389
390
    kw_liste_nep = pd.read_csv(target_file, delimiter=";", decimal=",")
391
392
    # Adjust column names
393
    kw_liste_nep = kw_liste_nep.rename(
394
        columns={
395
            "BNetzA-ID": "bnetza_id",
396
            "Kraftwerksname": "name",
397
            "Blockname": "name_unit",
398
            "Energieträger": "carrier_nep",
399
            "KWK\nJa/Nein": "chp",
400
            "PLZ": "postcode",
401
            "Ort": "city",
402
            "Bundesland/\nLand": "federal_state",
403
            "Inbetrieb-\nnahmejahr": "commissioned",
404
            "Status": "status",
405
            "el. Leistung\n06.02.2020": "capacity",
406
            "A 2035:\nKWK-Ersatz": "a2035_chp",
407
            "A 2035:\nLeistung": "a2035_capacity",
408
            "B 2035\nKWK-Ersatz": "b2035_chp",
409
            "B 2035:\nLeistung": "b2035_capacity",
410
            "C 2035:\nKWK-Ersatz": "c2035_chp",
411
            "C 2035:\nLeistung": "c2035_capacity",
412
            "B 2040:\nKWK-Ersatz": "b2040_chp",
413
            "B 2040:\nLeistung": "b2040_capacity",
414
        }
415
    )
416
417
    # Cut data to federal state if in testmode
418
    boundary = settings()["egon-data"]["--dataset-boundary"]
419
    if boundary != "Everything":
420
        map_states = {
421
            "Baden-Württemberg": "BW",
422
            "Nordrhein-Westfalen": "NW",
423
            "Hessen": "HE",
424
            "Brandenburg": "BB",
425
            "Bremen": "HB",
426
            "Rheinland-Pfalz": "RP",
427
            "Sachsen-Anhalt": "ST",
428
            "Schleswig-Holstein": "SH",
429
            "Mecklenburg-Vorpommern": "MV",
430
            "Thüringen": "TH",
431
            "Niedersachsen": "NI",
432
            "Sachsen": "SN",
433
            "Hamburg": "HH",
434
            "Saarland": "SL",
435
            "Berlin": "BE",
436
            "Bayern": "BY",
437
        }
438
439
        kw_liste_nep = kw_liste_nep[
440
            kw_liste_nep.federal_state.isin([map_states[boundary], np.nan])
441
        ]
442
443
        for col in [
444
            "capacity",
445
            "a2035_capacity",
446
            "b2035_capacity",
447
            "c2035_capacity",
448
            "b2040_capacity",
449
        ]:
450
            kw_liste_nep.loc[
451
                kw_liste_nep[kw_liste_nep.federal_state.isnull()].index, col
452
            ] *= population_share()
453
454
    kw_liste_nep["carrier"] = map_carrier()[kw_liste_nep.carrier_nep].values
455
456
    if export is True:
457
        # Insert data to db
458
        kw_liste_nep.to_sql(
459
            targets["nep_conventional_powerplants"]["table"],
460
            engine,
461
            schema=targets["nep_conventional_powerplants"]["schema"],
462
            if_exists="replace",
463
        )
464
    else:
465
        return kw_liste_nep
466
467
468
def district_heating_input():
469
    """Imports data for district heating networks in Germany
470
471
    Returns
472
    -------
473
    None.
474
475
    """
476
477
    sources = egon.data.config.datasets()["scenario_input"]["sources"]
478
479
    # import data to dataframe
480
    file = (
481
        Path(".")
482
        / "data_bundle_egon_data"
483
        / "nep2035_version2021"
484
        / sources["eGon2035"]["capacities"]
485
    )
486
    df = pd.read_excel(
487
        file, sheet_name="Kurzstudie_KWK", dtype={"Wert": float}
488
    )
489
    df.set_index(["Energietraeger", "Name"], inplace=True)
490
491
    # Scale values to population share in testmode
492
    if settings()["egon-data"]["--dataset-boundary"] != "Everything":
493
        df.loc[
494
            pd.IndexSlice[:, "Fernwaermeerzeugung"], "Wert"
495
        ] *= population_share()
496
497
    # Connect to database
498
    engine = db.engine()
499
    session = sessionmaker(bind=engine)()
500
501
    # insert heatpumps and resistive heater as link
502
    for c in ["Grosswaermepumpe", "Elektrodenheizkessel"]:
503
        entry = EgonScenarioCapacities(
504
            component="link",
505
            scenario_name="eGon2035",
506
            nuts="DE",
507
            carrier="urban_central_"
508
            + ("heat_pump" if c == "Grosswaermepumpe" else "resistive_heater"),
509
            capacity=df.loc[(c, "Fernwaermeerzeugung"), "Wert"]
510
            * 1e6
511
            / df.loc[(c, "Volllaststunden"), "Wert"]
512
            / df.loc[(c, "Wirkungsgrad"), "Wert"],
513
        )
514
515
        session.add(entry)
516
517
    # insert solar- and geothermal as generator
518
    for c in ["Geothermie", "Solarthermie"]:
519
        entry = EgonScenarioCapacities(
520
            component="generator",
521
            scenario_name="eGon2035",
522
            nuts="DE",
523
            carrier="urban_central_"
524
            + (
525
                "solar_thermal_collector"
526
                if c == "Solarthermie"
527
                else "geo_thermal"
528
            ),
529
            capacity=df.loc[(c, "Fernwaermeerzeugung"), "Wert"]
530
            * 1e6
531
            / df.loc[(c, "Volllaststunden"), "Wert"],
532
        )
533
534
        session.add(entry)
535
536
    session.commit()
537
538
539
def insert_data_nep():
540
    """Overall function for importing scenario input data for eGon2035 scenario
541
542
    Returns
543
    -------
544
    None.
545
546
    """
547
548
    insert_nep_list_powerplants(export=True)
549
550
    insert_capacities_per_federal_state_nep()
551
552
553
def eGon100_capacities():
554
    """Inserts installed capacities for the eGon100 scenario
555
556
    Returns
557
    -------
558
    None.
559
560
    """
561
562
    sources = egon.data.config.datasets()["scenario_input"]["sources"]
563
    targets = egon.data.config.datasets()["scenario_input"]["targets"]
564
565
    # read-in installed capacities
566
    execute_pypsa_eur_sec = False
567
    cwd = Path(".")
568
569
    if execute_pypsa_eur_sec:
570
        filepath = cwd / "run-pypsa-eur-sec"
571
        pypsa_eur_sec_repos = filepath / "pypsa-eur-sec"
572
        # Read YAML file
573
        pes_egonconfig = pypsa_eur_sec_repos / "config_egon.yaml"
574
        with open(pes_egonconfig, "r") as stream:
575
            data_config = yaml.safe_load(stream)
576
577
        target_file = (
578
            pypsa_eur_sec_repos
579
            / "results"
580
            / data_config["run"]
581
            / "csvs"
582
            / sources["eGon100RE"]["capacities"]
583
        )
584
585
    else:
586
        target_file = (
587
            cwd
588
            / "data_bundle_egon_data"
589
            / "pypsa_eur_sec"
590
            / "2022-05-04-egondata-integration"
591
            / "csvs"
592
            / sources["eGon100RE"]["capacities"]
593
        )
594
595
    df = pd.read_csv(target_file, skiprows=5)
596
    df.columns = ["component", "country", "carrier", "p_nom"]
597
598
    df.set_index("carrier", inplace=True)
599
600
    df = df[df.country.str[:2] == "DE"]
601
602
    # Drop country column
603
    df.drop("country", axis=1, inplace=True)
604
605
    # Drop copmponents which will be optimized in eGo
606
    unused_carrier = [
607
        "BEV charger",
608
        "DAC",
609
        "H2 Electrolysis",
610
        "electricity distribution grid",
611
        "home battery charger",
612
        "home battery discharger",
613
        "H2",
614
        "Li ion",
615
        "home battery",
616
        "residential rural water tanks charger",
617
        "residential rural water tanks discharger",
618
        "services rural water tanks charger",
619
        "services rural water tanks discharger",
620
        "residential rural water tanks",
621
        "services rural water tanks",
622
        "urban central water tanks",
623
        "urban central water tanks charger",
624
        "urban central water tanks discharger",
625
        "H2 Fuel Cell",
626
    ]
627
628
    df = df[~df.index.isin(unused_carrier)]
629
630
    df.index = df.index.str.replace(" ", "_")
631
632
    # Aggregate offshore wind
633
    df = df.append(
634
        pd.DataFrame(
635
            index=["wind_offshore"],
636
            data={
637
                "p_nom": (df.p_nom["offwind-ac"] + df.p_nom["offwind-dc"]),
638
                "component": df.component["offwind-ac"],
639
            },
640
        )
641
    )
642
    df = df.drop(["offwind-ac", "offwind-dc"])
643
644
    # Aggregate technologies with and without carbon_capture (CC)
645
    for carrier in ["SMR", "urban_central_gas_CHP"]:
646
        df.p_nom[carrier] += df.p_nom[f"{carrier}_CC"]
647
        df = df.drop([f"{carrier}_CC"])
648
649
    # Aggregate residential and services rural heat supply
650
    for merge_carrier in [
651
        "rural_resistive_heater",
652
        "rural_ground_heat_pump",
653
        "rural_gas_boiler",
654
        "rural_solar_thermal",
655
    ]:
656
        df = df.append(
657
            pd.DataFrame(
658
                index=[merge_carrier],
659
                data={
660
                    "p_nom": (
661
                        df.p_nom[f"residential_{merge_carrier}"]
662
                        + df.p_nom[f"services_{merge_carrier}"]
663
                    ),
664
                    "component": df.component[f"residential_{merge_carrier}"],
665
                },
666
            )
667
        )
668
        df = df.drop(
669
            [f"residential_{merge_carrier}", f"services_{merge_carrier}"]
670
        )
671
672
    # Rename carriers
673
    df.rename(
674
        {
675
            "onwind": "wind_onshore",
676
            "ror": "run_of_river",
677
            "PHS": "pumped_hydro",
678
            "OCGT": "gas",
679
            "rural_ground_heat_pump": "residential_rural_heat_pump",
680
            "urban_central_air_heat_pump": "urban_central_heat_pump",
681
            "urban_central_solar_thermal": "urban_central_solar_thermal_collector",
682
        },
683
        inplace=True,
684
    )
685
686
    # Reset index
687
    df = df.reset_index()
688
689
    # Rename columns
690
    df.rename(
691
        {"p_nom": "capacity", "index": "carrier"}, axis="columns", inplace=True
692
    )
693
694
    df["scenario_name"] = "eGon100RE"
695
    df["nuts"] = "DE"
696
697
    db.execute_sql(
698
        f"""
699
        DELETE FROM
700
        {targets['scenario_capacities']['schema']}.{targets['scenario_capacities']['table']}
701
        WHERE scenario_name='eGon100RE'
702
        """
703
    )
704
705
    df.to_sql(
706
        targets["scenario_capacities"]["table"],
707
        schema=targets["scenario_capacities"]["schema"],
708
        con=db.engine(),
709
        if_exists="append",
710
        index=False,
711
    )
712