Passed
Pull Request — dev (#809)
by
unknown
01:58 queued 14s
created

insert_capacities_per_federal_state_nep()   C

Complexity

Conditions 5

Size

Total Lines 161
Code Lines 86

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 86
dl 0
loc 161
rs 6.9915
c 0
b 0
f 0
cc 5
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
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.9",
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 nuts_mapping():
88
89
    nuts_mapping = {
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 nuts_mapping
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
    # Filter by carrier
245
    updated = insert_data[insert_data["carrier"].isin(carriers)]
246
247
    # Merge to replace capacities
248
    updated = (
249
        updated.merge(capacities_list, on=["carrier", "nuts"], how="left")
250
        .fillna(0)
251
        .drop(["capacity"], axis=1)
252
        .rename(columns={"c2035_capacity": "capacity"})
253
    )
254
255
    # Remove updated entries from df
256
    original = insert_data[~insert_data["carrier"].isin(carriers)]
257
258
    # Join dfs
259
    insert_data = pd.concat([original, updated])
260
261
    # Insert data to db
262
    insert_data.to_sql(
263
        targets["scenario_capacities"]["table"],
264
        engine,
265
        schema=targets["scenario_capacities"]["schema"],
266
        if_exists="append",
267
        index=insert_data.index,
268
    )
269
270
    # Add district heating data accordning to energy and full load hours
271
    district_heating_input()
272
273
274
def population_share():
275
    """Calulate share of population in testmode
276
277
    Returns
278
    -------
279
    float
280
        Share of population in testmode
281
282
    """
283
284
    sources = egon.data.config.datasets()["scenario_input"]["sources"]
285
286
    return (
287
        pd.read_sql(
288
            f"""
289
            SELECT SUM(population)
290
            FROM {sources['zensus_population']['schema']}.{sources['zensus_population']['table']}
291
            WHERE population>0
292
            """,
293
            con=db.engine(),
294
        )["sum"][0]
295
        / 80324282
296
    )
297
298
299
def aggr_nep_capacities(carriers):
300
    """Aggregates capacities from NEP power plants list by carrier and federal
301
    state
302
303
    Returns
304
    -------
305
    pandas.Dataframe
306
        Dataframe with capacities per federal state and carrier
307
308
    """
309
    # Get list of power plants from nep
310
    nep_capacities = insert_nep_list_powerplants(export=False)[
311
        ["federal_state", "carrier", "c2035_capacity"]
312
    ]
313
314
    # Sum up capacities per federal state and carrier
315
    capacities_list = (
316
        nep_capacities.groupby(["federal_state", "carrier"])["c2035_capacity"]
317
        .sum()
318
        .to_frame()
319
        .reset_index()
320
    )
321
322
    # Neglect entries with carriers not in argument
323
    capacities_list = capacities_list[capacities_list.carrier.isin(carriers)]
324
325
    # Include NUTS code
326
    capacities_list["nuts"] = capacities_list.federal_state.map(nuts_mapping())
327
328
    # Drop entries for foreign plants with nan values and federal_state column
329
    capacities_list = capacities_list.dropna(subset=["nuts"]).drop(
330
        columns=["federal_state"]
331
    )
332
333
    return capacities_list
334
335
336
def map_carrier():
337
    """Map carriers from NEP and Marktstammdatenregister to carriers from eGon
338
339
    Returns
340
    -------
341
    pandas.Series
342
        List of mapped carriers
343
344
    """
345
    return pd.Series(
346
        data={
347
            "Abfall": "other_non_renewable",
348
            "Erdgas": "gas",
349
            "Sonstige\nEnergieträger": "other_non_renewable",
350
            "Steinkohle": "coal",
351
            "Kuppelgase": "gas",
352
            "Mineralöl-\nprodukte": "oil",
353
            "Braunkohle": "lignite",
354
            "Waerme": "other_non_renewable",
355
            "Mineraloelprodukte": "oil",
356
            "NichtBiogenerAbfall": "other_non_renewable",
357
            "AndereGase": "gas",
358
            "Sonstige_Energietraeger": "other_non_renewable",
359
            "Kernenergie": "nuclear",
360
            "Pumpspeicher": "pumped_hydro",
361
            "Mineralöl-\nProdukte": "oil",
362
        }
363
    )
364
365
366
def insert_nep_list_powerplants(export=True):
367
    """Insert list of conventional powerplants attached to the approval
368
    of the scenario report by BNetzA
369
370
    Parameters
371
    ----------
372
    export : bool
373
        Choose if nep list should be exported to the data
374
        base. The default is True.
375
        If export=False a data frame will be returned
376
377
    Returns
378
    -------
379
    kw_liste_nep : pandas.DataFrame
380
        List of conventional power plants from nep if export=False
381
    """
382
383
    sources = egon.data.config.datasets()["scenario_input"]["sources"]
384
    targets = egon.data.config.datasets()["scenario_input"]["targets"]
385
386
    # Connect to local database
387
    engine = db.engine()
388
389
    # Read-in data from csv-file
390
    target_file = (
391
        Path(".")
392
        / "data_bundle_egon_data"
393
        / "nep2035_version2021"
394
        / sources["eGon2035"]["list_conv_pp"]
395
    )
396
397
    kw_liste_nep = pd.read_csv(target_file, delimiter=";", decimal=",")
398
399
    # Adjust column names
400
    kw_liste_nep = kw_liste_nep.rename(
401
        columns={
402
            "BNetzA-ID": "bnetza_id",
403
            "Kraftwerksname": "name",
404
            "Blockname": "name_unit",
405
            "Energieträger": "carrier_nep",
406
            "KWK\nJa/Nein": "chp",
407
            "PLZ": "postcode",
408
            "Ort": "city",
409
            "Bundesland/\nLand": "federal_state",
410
            "Inbetrieb-\nnahmejahr": "commissioned",
411
            "Status": "status",
412
            "el. Leistung\n06.02.2020": "capacity",
413
            "A 2035:\nKWK-Ersatz": "a2035_chp",
414
            "A 2035:\nLeistung": "a2035_capacity",
415
            "B 2035\nKWK-Ersatz": "b2035_chp",
416
            "B 2035:\nLeistung": "b2035_capacity",
417
            "C 2035:\nKWK-Ersatz": "c2035_chp",
418
            "C 2035:\nLeistung": "c2035_capacity",
419
            "B 2040:\nKWK-Ersatz": "b2040_chp",
420
            "B 2040:\nLeistung": "b2040_capacity",
421
        }
422
    )
423
424
    # Cut data to federal state if in testmode
425
    boundary = settings()["egon-data"]["--dataset-boundary"]
426
    if boundary != "Everything":
427
        map_states = {
428
            "Baden-Württemberg": "BW",
429
            "Nordrhein-Westfalen": "NW",
430
            "Hessen": "HE",
431
            "Brandenburg": "BB",
432
            "Bremen": "HB",
433
            "Rheinland-Pfalz": "RP",
434
            "Sachsen-Anhalt": "ST",
435
            "Schleswig-Holstein": "SH",
436
            "Mecklenburg-Vorpommern": "MV",
437
            "Thüringen": "TH",
438
            "Niedersachsen": "NI",
439
            "Sachsen": "SN",
440
            "Hamburg": "HH",
441
            "Saarland": "SL",
442
            "Berlin": "BE",
443
            "Bayern": "BY",
444
        }
445
446
        kw_liste_nep = kw_liste_nep[
447
            kw_liste_nep.federal_state.isin([map_states[boundary], np.nan])
448
        ]
449
450
        for col in [
451
            "capacity",
452
            "a2035_capacity",
453
            "b2035_capacity",
454
            "c2035_capacity",
455
            "b2040_capacity",
456
        ]:
457
            kw_liste_nep.loc[
458
                kw_liste_nep[kw_liste_nep.federal_state.isnull()].index, col
459
            ] *= population_share()
460
461
    kw_liste_nep["carrier"] = map_carrier()[kw_liste_nep.carrier_nep].values
462
463
    if export is True:
464
        # Insert data to db
465
        kw_liste_nep.to_sql(
466
            targets["nep_conventional_powerplants"]["table"],
467
            engine,
468
            schema=targets["nep_conventional_powerplants"]["schema"],
469
            if_exists="replace",
470
        )
471
    else:
472
        return kw_liste_nep
473
474
475
def district_heating_input():
476
    """Imports data for district heating networks in Germany
477
478
    Returns
479
    -------
480
    None.
481
482
    """
483
484
    sources = egon.data.config.datasets()["scenario_input"]["sources"]
485
486
    # import data to dataframe
487
    file = (
488
        Path(".")
489
        / "data_bundle_egon_data"
490
        / "nep2035_version2021"
491
        / sources["eGon2035"]["capacities"]
492
    )
493
    df = pd.read_excel(
494
        file, sheet_name="Kurzstudie_KWK", dtype={"Wert": float}
495
    )
496
    df.set_index(["Energietraeger", "Name"], inplace=True)
497
498
    # Scale values to population share in testmode
499
    if settings()["egon-data"]["--dataset-boundary"] != "Everything":
500
        df.loc[
501
            pd.IndexSlice[:, "Fernwaermeerzeugung"], "Wert"
502
        ] *= population_share()
503
504
    # Connect to database
505
    engine = db.engine()
506
    session = sessionmaker(bind=engine)()
507
508
    # insert heatpumps and resistive heater as link
509
    for c in ["Grosswaermepumpe", "Elektrodenheizkessel"]:
510
        entry = EgonScenarioCapacities(
511
            component="link",
512
            scenario_name="eGon2035",
513
            nuts="DE",
514
            carrier="urban_central_"
515
            + ("heat_pump" if c == "Grosswaermepumpe" else "resistive_heater"),
516
            capacity=df.loc[(c, "Fernwaermeerzeugung"), "Wert"]
517
            * 1e6
518
            / df.loc[(c, "Volllaststunden"), "Wert"]
519
            / df.loc[(c, "Wirkungsgrad"), "Wert"],
520
        )
521
522
        session.add(entry)
523
524
    # insert solar- and geothermal as generator
525
    for c in ["Geothermie", "Solarthermie"]:
526
        entry = EgonScenarioCapacities(
527
            component="generator",
528
            scenario_name="eGon2035",
529
            nuts="DE",
530
            carrier="urban_central_"
531
            + (
532
                "solar_thermal_collector"
533
                if c == "Solarthermie"
534
                else "geo_thermal"
535
            ),
536
            capacity=df.loc[(c, "Fernwaermeerzeugung"), "Wert"]
537
            * 1e6
538
            / df.loc[(c, "Volllaststunden"), "Wert"],
539
        )
540
541
        session.add(entry)
542
543
    session.commit()
544
545
546
def insert_data_nep():
547
    """Overall function for importing scenario input data for eGon2035 scenario
548
549
    Returns
550
    -------
551
    None.
552
553
    """
554
555
    insert_nep_list_powerplants(export=True)
556
557
    insert_capacities_per_federal_state_nep()
558
559
560
def eGon100_capacities():
561
    """Inserts installed capacities for the eGon100 scenario
562
563
    Returns
564
    -------
565
    None.
566
567
    """
568
569
    sources = egon.data.config.datasets()["scenario_input"]["sources"]
570
    targets = egon.data.config.datasets()["scenario_input"]["targets"]
571
572
    # read-in installed capacities
573
    execute_pypsa_eur_sec = False
574
    cwd = Path(".")
575
576
    if execute_pypsa_eur_sec:
577
        filepath = cwd / "run-pypsa-eur-sec"
578
        pypsa_eur_sec_repos = filepath / "pypsa-eur-sec"
579
        # Read YAML file
580
        pes_egonconfig = pypsa_eur_sec_repos / "config_egon.yaml"
581
        with open(pes_egonconfig, "r") as stream:
582
            data_config = yaml.safe_load(stream)
583
584
        target_file = (
585
            pypsa_eur_sec_repos
586
            / "results"
587
            / data_config["run"]
588
            / "csvs"
589
            / sources["eGon100RE"]["capacities"]
590
        )
591
592
    else:
593
        target_file = (
594
            cwd
595
            / "data_bundle_egon_data"
596
            / "pypsa_eur_sec"
597
            / "2022-05-04-egondata-integration"
598
            / "csvs"
599
            / sources["eGon100RE"]["capacities"]
600
        )
601
602
    df = pd.read_csv(target_file, skiprows=5)
603
    df.columns = ["component", "country", "carrier", "p_nom"]
604
605
    df.set_index("carrier", inplace=True)
606
607
    df = df[df.country.str[:2] == "DE"]
608
609
    # Drop country column
610
    df.drop("country", axis=1, inplace=True)
611
612
    # Drop copmponents which will be optimized in eGo
613
    unused_carrier = [
614
        "BEV charger",
615
        "DAC",
616
        "H2 Electrolysis",
617
        "electricity distribution grid",
618
        "home battery charger",
619
        "home battery discharger",
620
        "H2",
621
        "Li ion",
622
        "home battery",
623
        "residential rural water tanks charger",
624
        "residential rural water tanks discharger",
625
        "services rural water tanks charger",
626
        "services rural water tanks discharger",
627
        "residential rural water tanks",
628
        "services rural water tanks",
629
        "urban central water tanks",
630
        "urban central water tanks charger",
631
        "urban central water tanks discharger",
632
        "H2 Fuel Cell",
633
    ]
634
635
    df = df[~df.index.isin(unused_carrier)]
636
637
    df.index = df.index.str.replace(" ", "_")
638
639
    # Aggregate offshore wind
640
    df = df.append(
641
        pd.DataFrame(
642
            index=["wind_offshore"],
643
            data={
644
                "p_nom": (df.p_nom["offwind-ac"] + df.p_nom["offwind-dc"]),
645
                "component": df.component["offwind-ac"],
646
            },
647
        )
648
    )
649
    df = df.drop(["offwind-ac", "offwind-dc"])
650
651
    # Aggregate technologies with and without carbon_capture (CC)
652
    for carrier in ["SMR", "urban_central_gas_CHP"]:
653
        df.p_nom[carrier] += df.p_nom[f"{carrier}_CC"]
654
        df = df.drop([f"{carrier}_CC"])
655
656
    # Aggregate residential and services rural heat supply
657
    for merge_carrier in [
658
        "rural_resistive_heater",
659
        "rural_ground_heat_pump",
660
        "rural_gas_boiler",
661
        "rural_solar_thermal",
662
    ]:
663
        df = df.append(
664
            pd.DataFrame(
665
                index=[merge_carrier],
666
                data={
667
                    "p_nom": (
668
                        df.p_nom[f"residential_{merge_carrier}"]
669
                        + df.p_nom[f"services_{merge_carrier}"]
670
                    ),
671
                    "component": df.component[f"residential_{merge_carrier}"],
672
                },
673
            )
674
        )
675
        df = df.drop(
676
            [f"residential_{merge_carrier}", f"services_{merge_carrier}"]
677
        )
678
679
    # Rename carriers
680
    df.rename(
681
        {
682
            "onwind": "wind_onshore",
683
            "ror": "run_of_river",
684
            "PHS": "pumped_hydro",
685
            "OCGT": "gas",
686
            "rural_ground_heat_pump": "residential_rural_heat_pump",
687
            "urban_central_air_heat_pump": "urban_central_heat_pump",
688
            "urban_central_solar_thermal": "urban_central_solar_thermal_collector",
689
        },
690
        inplace=True,
691
    )
692
693
    # Reset index
694
    df = df.reset_index()
695
696
    # Rename columns
697
    df.rename(
698
        {"p_nom": "capacity", "index": "carrier"}, axis="columns", inplace=True
699
    )
700
701
    df["scenario_name"] = "eGon100RE"
702
    df["nuts"] = "DE"
703
704
    db.execute_sql(
705
        f"""
706
        DELETE FROM
707
        {targets['scenario_capacities']['schema']}.{targets['scenario_capacities']['table']}
708
        WHERE scenario_name='eGon100RE'
709
        """
710
    )
711
712
    df.to_sql(
713
        targets["scenario_capacities"]["table"],
714
        schema=targets["scenario_capacities"]["schema"],
715
        con=db.engine(),
716
        if_exists="append",
717
        index=False,
718
    )
719