Passed
Pull Request — dev (#1054)
by
unknown
05:31 queued 04:10
created

data.datasets.power_plants.insert_hydro_biomass()   A

Complexity

Conditions 2

Size

Total Lines 19
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nop 0
dl 0
loc 19
rs 10
c 0
b 0
f 0
1
"""The central module containing all code dealing with power plant data.
2
"""
3
from geoalchemy2 import Geometry
4
from sqlalchemy import (
5
    BigInteger, Column, Float, Integer, Sequence, String, DateTime, Boolean
6
)
7
from sqlalchemy.dialects.postgresql import JSONB
8
from sqlalchemy.ext.declarative import declarative_base
9
from sqlalchemy.orm import sessionmaker
10
import geopandas as gpd
11
import numpy as np
12
import pandas as pd
13
14
from egon.data import db
15
from egon.data.datasets import Dataset
16
from egon.data.datasets.power_plants.conventional import (
17
    match_nep_no_chp,
18
    select_nep_power_plants,
19
    select_no_chp_combustion_mastr,
20
)
21
from egon.data.datasets.power_plants.mastr import import_mastr
22
from egon.data.datasets.power_plants.pv_rooftop import pv_rooftop_per_mv_grid
23
from egon.data.datasets.power_plants.pv_rooftop_buildings import (
24
    geocode_mastr_data,
25
    pv_rooftop_to_buildings,
26
)
27
import egon.data.config
28
import egon.data.datasets.power_plants.assign_weather_data as assign_weather_data  # noqa: E501
29
import egon.data.datasets.power_plants.pv_ground_mounted as pv_ground_mounted
30
import egon.data.datasets.power_plants.wind_farms as wind_onshore
31
import egon.data.datasets.power_plants.wind_offshore as wind_offshore
32
33
Base = declarative_base()
34
35
36
class EgonPowerPlantsPv(Base):
37
    __tablename__ = "egon_power_plants_pv"
38
    __table_args__ = {"schema": "supply"}
39
40
    id = Column(Integer, Sequence("pp_pv_seq"), primary_key=True)
41
    bus_id = Column(Integer, nullable=True) # Grid district id
42
    gens_id = Column(String, nullable=True) # EinheitMastrNummer
43
44
    status = Column(String, nullable=True)  # EinheitBetriebsstatus
45
    commissioning_date = Column(DateTime, nullable=True) # Inbetriebnahmedatum
46
    postcode = Column(String(5), nullable=True) # Postleitzahl
47
    city = Column(String(50), nullable=True) # Ort
48
    federal_state = Column(String(21), nullable=True) # Bundesland
49
50
    site_type = Column(String(69), nullable=True)  # Lage
51
    usage_sector = Column(String(36), nullable=True) # Nutzungsbereich
52
    orientation_primary = Column(String(11), nullable=True) # Hauptausrichtung
53
    orientation_primary_angle = Column(String(18), nullable=True)  # HauptausrichtungNeigungswinkel
54
    orientation_secondary = Column(String(11), nullable=True) # Nebenausrichtung
55
    orientation_secondary_angle = Column(String(18), nullable=True)  # NebenausrichtungNeigungswinkel
56
    orientation_uniform = Column(Boolean, nullable=True) # EinheitlicheAusrichtungUndNeigungswinkel
57
    module_count = Column(Float, nullable=True)  # AnzahlModule
58
59
    capacity = Column(Float) # Nettonennleistung
60
    capacity_inverter = Column(Float, nullable=True) # ZugeordneteWirkleistungWechselrichter in MW
61
    feedin_type = Column(String(47), nullable=True) # Einspeisungsart
62
    voltage_level = Column(Integer, nullable=True)
63
64
    geom = Column(Geometry("POINT", 4326), index=True, nullable=True)
65
66
67
class EgonPowerPlantsWind(Base):
68
    __tablename__ = "egon_power_plants_wind"
69
    __table_args__ = {"schema": "supply"}
70
71
    id = Column(Integer, Sequence("pp_wind_seq"), primary_key=True)
72
    bus_id = Column(Integer, nullable=True) # Grid district id
73
    gens_id = Column(String, nullable=True) # EinheitMastrNummer
74
75
    status = Column(String, nullable=True)  # EinheitBetriebsstatus
76
    commissioning_date = Column(DateTime, nullable=True) # Inbetriebnahmedatum
77
    postcode = Column(String(5), nullable=True) # Postleitzahl
78
    city = Column(String(50), nullable=True) # Ort
79
    federal_state = Column(String(21), nullable=True) # Bundesland
80
81
    site_type = Column(String(17), nullable=True) # Lage
82
    manufacturer_name = Column(String(100), nullable=True) # Hersteller
83
    type_name = Column(String(100), nullable=True) # Typenbezeichnung
84
    hub_height = Column(Float, nullable=True) # Nabenhoehe
85
    rotor_diameter = Column(Float, nullable=True) # Rotordurchmesser
86
87
    capacity = Column(Float) # Nettonennleistung
88
    feedin_type = Column(String(47), nullable=True)  # Einspeisungsart
89
    voltage_level = Column(Integer, nullable=True)
90
91
    geom = Column(Geometry("POINT", 4326), index=True, nullable=True)
92
93
94
class EgonPowerPlantsBiomass(Base):
95
    __tablename__ = "egon_power_plants_biomass"
96
    __table_args__ = {"schema": "supply"}
97
98
    id = Column(Integer, Sequence("pp_biomass_seq"), primary_key=True)
99
    bus_id = Column(Integer, nullable=True) # Grid district id
100
    gens_id = Column(String, nullable=True) # EinheitMastrNummer
101
102
    status = Column(String, nullable=True)  # EinheitBetriebsstatus
103
    commissioning_date = Column(DateTime, nullable=True) # Inbetriebnahmedatum
104
    postcode = Column(String(5), nullable=True) # Postleitzahl
105
    city = Column(String(50), nullable=True) # Ort
106
    federal_state = Column(String(21), nullable=True) # Bundesland
107
108
    technology = Column(String(45), nullable=True) # Technologie
109
    fuel_name = Column(String(52), nullable=True) # Hauptbrennstoff
110
    fuel_type = Column(String(19), nullable=True)  # Biomasseart
111
112
    capacity = Column(Float) # Nettonennleistung
113
    th_capacity = Column(Float, nullable=True) # ThermischeNutzleistung
114
    feedin_type = Column(String(47), nullable=True)  # Einspeisungsart
115
    voltage_level = Column(Integer, nullable=True)
116
117
    geom = Column(Geometry("POINT", 4326), index=True, nullable=True)
118
119
120
class EgonPowerPlantsHydro(Base):
121
    __tablename__ = "egon_power_plants_hydro"
122
    __table_args__ = {"schema": "supply"}
123
124
    id = Column(Integer, Sequence("pp_hydro_seq"), primary_key=True)
125
    bus_id = Column(Integer, nullable=True) # Grid district id
126
    gens_id = Column(String, nullable=True) # EinheitMastrNummer
127
128
    status = Column(String, nullable=True)  # EinheitBetriebsstatus
129
    commissioning_date = Column(DateTime, nullable=True) # Inbetriebnahmedatum
130
    postcode = Column(String(5), nullable=True) # Postleitzahl
131
    city = Column(String(50), nullable=True) # Ort
132
    federal_state = Column(String(21), nullable=True) # Bundesland
133
134
    type = Column(Integer, nullable=True) # ArtDerWasserkraftanlage
135
    water_origin = Column(String(20), nullable=True)  # ArtDesZuflusses
136
137
    capacity = Column(Float, nullable=False) # Nettonennleistung
138
    feedin_type = Column(String(47), nullable=True)  # Einspeisungsart
139
    voltage_level = Column(Integer, nullable=True)
140
141
    geom = Column(Geometry("POINT", 4326), index=True, nullable=True)
142
143
144 View Code Duplication
class EgonPowerPlants(Base):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
145
    __tablename__ = "egon_power_plants"
146
    __table_args__ = {"schema": "supply"}
147
    id = Column(BigInteger, Sequence("pp_seq"), primary_key=True)
148
    sources = Column(JSONB)
149
    source_id = Column(JSONB)
150
    carrier = Column(String)
151
    el_capacity = Column(Float)
152
    bus_id = Column(Integer)
153
    voltage_level = Column(Integer)
154
    weather_cell_id = Column(Integer)
155
    scenario = Column(String)
156
    geom = Column(Geometry("POINT", 4326), index=True)
157
158
159
class PowerPlants(Dataset):
160
    def __init__(self, dependencies):
161
        super().__init__(
162
            name="PowerPlants",
163
            version="0.0.15",
164
            dependencies=dependencies,
165
            tasks=(
166
                create_tables,
167
                import_mastr,
168
                insert_hydro_biomass,
169
                allocate_conventional_non_chp_power_plants,
170
                allocate_other_power_plants,
171
                {
172
                    wind_onshore.insert,
173
                    pv_ground_mounted.insert,
174
                    (
175
                        pv_rooftop_per_mv_grid,
176
                        geocode_mastr_data,
177
                        pv_rooftop_to_buildings,
178
                    ),
179
                },
180
                wind_offshore.insert,
181
                assign_weather_data.weatherId_and_busId,
182
            ),
183
        )
184
185
186
def create_tables():
187
    """Create tables for power plant data
188
    Returns
189
    -------
190
    None.
191
    """
192
193
    # Tables for future scenarios
194
    cfg = egon.data.config.datasets()["power_plants"]
195
    db.execute_sql(f"CREATE SCHEMA IF NOT EXISTS {cfg['target']['schema']};")
196
    engine = db.engine()
197
    db.execute_sql(
198
        f"""DROP TABLE IF EXISTS
199
        {cfg['target']['schema']}.{cfg['target']['table']}"""
200
    )
201
202
    db.execute_sql("""DROP SEQUENCE IF EXISTS pp_seq""")
203
    EgonPowerPlants.__table__.create(bind=engine, checkfirst=True)
204
205
    # Tables for status quo
206
    tables = [
207
        EgonPowerPlantsWind,
208
        EgonPowerPlantsPv,
209
        EgonPowerPlantsBiomass,
210
        EgonPowerPlantsHydro,
211
    ]
212
    for t in tables:
213
        db.execute_sql(
214
            f"DROP TABLE IF EXISTS {t.schema}.{t.name} CASCADE;"
215
        )
216
        t.__table__.create(bind=engine, checkfirst=True)
217
218
219
def scale_prox2now(df, target, level="federal_state"):
220
    """Scale installed capacities linear to status quo power plants
221
222
    Parameters
223
    ----------
224
    df : pandas.DataFrame
225
        Status Quo power plants
226
    target : pandas.Series
227
        Target values for future scenario
228
    level : str, optional
229
        Scale per 'federal_state' or 'country'. The default is 'federal_state'.
230
231
    Returns
232
    -------
233
    df : pandas.DataFrame
234
        Future power plants
235
236
    """
237
238
    if level == "federal_state":
239
        df.loc[:, "Nettonennleistung"] = (
240
            df.groupby(df.Bundesland)
241
            .Nettonennleistung.apply(lambda grp: grp / grp.sum())
242
            .mul(target[df.Bundesland.values].values)
243
        )
244
    else:
245
        df.loc[:, "Nettonennleistung"] = df.Nettonennleistung.apply(
246
            lambda x: x / x.sum()
247
        ).mul(target.values)
248
249
    df = df[df.Nettonennleistung > 0]
250
251
    return df
252
253
254
def select_target(carrier, scenario):
255
    """Select installed capacity per scenario and carrier
256
257
    Parameters
258
    ----------
259
    carrier : str
260
        Name of energy carrier
261
    scenario : str
262
        Name of scenario
263
264
    Returns
265
    -------
266
    pandas.Series
267
        Target values for carrier and scenario
268
269
    """
270
    cfg = egon.data.config.datasets()["power_plants"]
271
272
    return (
273
        pd.read_sql(
274
            f"""SELECT DISTINCT ON (b.gen)
275
                         REPLACE(REPLACE(b.gen, '-', ''), 'ü', 'ue') as state,
276
                         a.capacity
277
                         FROM {cfg['sources']['capacities']} a,
278
                         {cfg['sources']['geom_federal_states']} b
279
                         WHERE a.nuts = b.nuts
280
                         AND scenario_name = '{scenario}'
281
                         AND carrier = '{carrier}'
282
                         AND b.gen NOT IN ('Baden-Württemberg (Bodensee)',
283
                                           'Bayern (Bodensee)')""",
284
            con=db.engine(),
285
        )
286
        .set_index("state")
287
        .capacity
288
    )
289
290
291
def filter_mastr_geometry(mastr, federal_state=None):
292
    """Filter data from MaStR by geometry
293
294
    Parameters
295
    ----------
296
    mastr : pandas.DataFrame
297
        All power plants listed in MaStR
298
    federal_state : str or None
299
        Name of federal state whoes power plants are returned.
300
        If None, data for Germany is returned
301
302
    Returns
303
    -------
304
    mastr_loc : pandas.DataFrame
305
        Power plants listed in MaStR with geometry inside German boundaries
306
307
    """
308
    cfg = egon.data.config.datasets()["power_plants"]
309
310
    if type(mastr) == pd.core.frame.DataFrame:
311
        # Drop entries without geometry for insert
312
        mastr_loc = mastr[
313
            mastr.Laengengrad.notnull() & mastr.Breitengrad.notnull()
314
        ]
315
316
        # Create geodataframe
317
        mastr_loc = gpd.GeoDataFrame(
318
            mastr_loc,
319
            geometry=gpd.points_from_xy(
320
                mastr_loc.Laengengrad, mastr_loc.Breitengrad, crs=4326
321
            ),
322
        )
323
    else:
324
        mastr_loc = mastr.copy()
325
326
    # Drop entries outside of germany or federal state
327
    if not federal_state:
328
        sql = f"SELECT geometry as geom FROM {cfg['sources']['geom_germany']}"
329
    else:
330
        sql = f"""
331
        SELECT geometry as geom
332
        FROM boundaries.vg250_lan_union
333
        WHERE REPLACE(REPLACE(gen, '-', ''), 'ü', 'ue') = '{federal_state}'"""
334
335
    mastr_loc = (
336
        gpd.sjoin(
337
            gpd.read_postgis(sql, con=db.engine()).to_crs(4326),
338
            mastr_loc,
339
            how="right",
340
        )
341
        .query("index_left==0")
342
        .drop("index_left", axis=1)
343
    )
344
345
    return mastr_loc
346
347
348
def insert_biomass_plants(scenario):
349
    """Insert biomass power plants of future scenario
350
351
    Parameters
352
    ----------
353
    scenario : str
354
        Name of scenario.
355
356
    Returns
357
    -------
358
    None.
359
360
    """
361
    cfg = egon.data.config.datasets()["power_plants"]
362
363
    # import target values from NEP 2021, scneario C 2035
364
    target = select_target("biomass", scenario)
365
366
    # import data for MaStR
367
    mastr = pd.read_csv(cfg["sources"]["mastr_biomass"]).query(
368
        "EinheitBetriebsstatus=='InBetrieb'"
369
    )
370
371
    # Drop entries without federal state or 'AusschließlichWirtschaftszone'
372
    mastr = mastr[
373
        mastr.Bundesland.isin(
374
            pd.read_sql(
375
                f"""SELECT DISTINCT ON (gen)
376
        REPLACE(REPLACE(gen, '-', ''), 'ü', 'ue') as states
377
        FROM {cfg['sources']['geom_federal_states']}""",
378
                con=db.engine(),
379
            ).states.values
380
        )
381
    ]
382
383
    # Scaling will be done per federal state in case of eGon2035 scenario.
384
    if scenario == "eGon2035":
385
        level = "federal_state"
386
    else:
387
        level = "country"
388
389
    # Choose only entries with valid geometries inside DE/test mode
390
    mastr_loc = filter_mastr_geometry(mastr).set_geometry("geometry")
391
392
    # Scale capacities to meet target values
393
    mastr_loc = scale_prox2now(mastr_loc, target, level=level)
394
395
    # Assign bus_id
396
    if len(mastr_loc) > 0:
397
        mastr_loc["voltage_level"] = assign_voltage_level(mastr_loc, cfg)
398
        mastr_loc = assign_bus_id(mastr_loc, cfg)
399
400
    # Insert entries with location
401
    session = sessionmaker(bind=db.engine())()
402
403
    for i, row in mastr_loc.iterrows():
404
        if not row.ThermischeNutzleistung > 0:
405
            entry = EgonPowerPlants(
406
                sources={"el_capacity": "MaStR scaled with NEP 2021"},
407
                source_id={"MastrNummer": row.EinheitMastrNummer},
408
                carrier="biomass",
409
                el_capacity=row.Nettonennleistung,
410
                scenario=scenario,
411
                bus_id=row.bus_id,
412
                voltage_level=row.voltage_level,
413
                geom=f"SRID=4326;POINT({row.Laengengrad} {row.Breitengrad})",
414
            )
415
            session.add(entry)
416
417
    session.commit()
418
419
420
def insert_hydro_plants(scenario):
421
    """Insert hydro power plants of future scenario.
422
423
    Hydro power plants are diveded into run_of_river and reservoir plants
424
    according to Marktstammdatenregister.
425
    Additional hydro technologies (e.g. turbines inside drinking water
426
    systems) are not considered.
427
428
    Parameters
429
    ----------
430
    scenario : str
431
        Name of scenario.
432
433
    Returns
434
    -------
435
    None.
436
437
    """
438
    cfg = egon.data.config.datasets()["power_plants"]
439
440
    # Map MaStR carriers to eGon carriers
441
    map_carrier = {
442
        "run_of_river": ["Laufwasseranlage"],
443
        "reservoir": ["Speicherwasseranlage"],
444
    }
445
446
    for carrier in map_carrier.keys():
447
448
        # import target values
449
        target = select_target(carrier, scenario)
450
451
        # import data for MaStR
452
        mastr = pd.read_csv(cfg["sources"]["mastr_hydro"]).query(
453
            "EinheitBetriebsstatus=='InBetrieb'"
454
        )
455
456
        # Choose only plants with specific carriers
457
        mastr = mastr[mastr.ArtDerWasserkraftanlage.isin(map_carrier[carrier])]
458
459
        # Drop entries without federal state or 'AusschließlichWirtschaftszone'
460
        mastr = mastr[
461
            mastr.Bundesland.isin(
462
                pd.read_sql(
463
                    f"""SELECT DISTINCT ON (gen)
464
            REPLACE(REPLACE(gen, '-', ''), 'ü', 'ue') as states
465
            FROM {cfg['sources']['geom_federal_states']}""",
466
                    con=db.engine(),
467
                ).states.values
468
            )
469
        ]
470
471
        # Scaling will be done per federal state in case of eGon2035 scenario.
472
        if scenario == "eGon2035":
473
            level = "federal_state"
474
        else:
475
            level = "country"
476
477
        # Scale capacities to meet target values
478
        mastr = scale_prox2now(mastr, target, level=level)
479
480
        # Choose only entries with valid geometries inside DE/test mode
481
        mastr_loc = filter_mastr_geometry(mastr).set_geometry("geometry")
482
        # TODO: Deal with power plants without geometry
483
484
        # Assign bus_id and voltage level
485
        if len(mastr_loc) > 0:
486
            mastr_loc["voltage_level"] = assign_voltage_level(mastr_loc, cfg)
487
            mastr_loc = assign_bus_id(mastr_loc, cfg)
488
489
        # Insert entries with location
490
        session = sessionmaker(bind=db.engine())()
491
        for i, row in mastr_loc.iterrows():
492
            entry = EgonPowerPlants(
493
                sources={"el_capacity": "MaStR scaled with NEP 2021"},
494
                source_id={"MastrNummer": row.EinheitMastrNummer},
495
                carrier=carrier,
496
                el_capacity=row.Nettonennleistung,
497
                scenario=scenario,
498
                bus_id=row.bus_id,
499
                voltage_level=row.voltage_level,
500
                geom=f"SRID=4326;POINT({row.Laengengrad} {row.Breitengrad})",
501
            )
502
            session.add(entry)
503
504
        session.commit()
505
506
507
def assign_voltage_level(mastr_loc, cfg):
508
    """Assigns voltage level to power plants.
509
510
    If location data inluding voltage level is available from
511
    Marktstammdatenregister, this is used. Otherwise the voltage level is
512
    assigned according to the electrical capacity.
513
514
    Parameters
515
    ----------
516
    mastr_loc : pandas.DataFrame
517
        Power plants listed in MaStR with geometry inside German boundaries
518
519
    Returns
520
    -------
521
    pandas.DataFrame
522
        Power plants including voltage_level
523
524
    """
525
    mastr_loc["Spannungsebene"] = np.nan
526
    mastr_loc["voltage_level"] = np.nan
527
528
    if "LokationMastrNummer" in mastr_loc.columns:
529
        location = pd.read_csv(
530
            cfg["sources"]["mastr_location"],
531
            usecols=["LokationMastrNummer", "Spannungsebene"],
532
        ).set_index("LokationMastrNummer")
533
534
        location = location[~location.index.duplicated(keep="first")]
535
536
        mastr_loc.loc[
537
            mastr_loc[
538
                mastr_loc.LokationMastrNummer.isin(location.index)
539
            ].index,
540
            "Spannungsebene",
541
        ] = location.Spannungsebene[
542
            mastr_loc[
543
                mastr_loc.LokationMastrNummer.isin(location.index)
544
            ].LokationMastrNummer
545
        ].values
546
547
        # Transfer voltage_level as integer from Spanungsebene
548
        map_voltage_levels = pd.Series(
549
            data={
550
                "Höchstspannung": 1,
551
                "Hoechstspannung": 1,
552
                "UmspannungZurHochspannung": 2,
553
                "Hochspannung": 3,
554
                "UmspannungZurMittelspannung": 4,
555
                "Mittelspannung": 5,
556
                "UmspannungZurNiederspannung": 6,
557
                "Niederspannung": 7,
558
            }
559
        )
560
561
        mastr_loc.loc[
562
            mastr_loc[mastr_loc["Spannungsebene"].notnull()].index,
563
            "voltage_level",
564
        ] = map_voltage_levels[
565
            mastr_loc.loc[
566
                mastr_loc[mastr_loc["Spannungsebene"].notnull()].index,
567
                "Spannungsebene",
568
            ].values
569
        ].values
570
571
    else:
572
        print(
573
            "No information about MaStR location available. "
574
            "All voltage levels are assigned using threshold values."
575
        )
576
577
    # If no voltage level is available from mastr, choose level according
578
    # to threshold values
579
580
    mastr_loc.voltage_level = assign_voltage_level_by_capacity(mastr_loc)
581
582
    return mastr_loc.voltage_level
583
584
585
def assign_voltage_level_by_capacity(mastr_loc):
586
587
    for i, row in mastr_loc[mastr_loc.voltage_level.isnull()].iterrows():
588
589
        if row.Nettonennleistung > 120:
590
            level = 1
591
        elif row.Nettonennleistung > 20:
592
            level = 3
593
        elif row.Nettonennleistung > 5.5:
594
            level = 4
595
        elif row.Nettonennleistung > 0.2:
596
            level = 5
597
        elif row.Nettonennleistung > 0.1:
598
            level = 6
599
        else:
600
            level = 7
601
602
        mastr_loc.loc[i, "voltage_level"] = level
603
604
    mastr_loc.voltage_level = mastr_loc.voltage_level.astype(int)
605
606
    return mastr_loc.voltage_level
607
608
609
def assign_bus_id(power_plants, cfg):
610
    """Assigns bus_ids to power plants according to location and voltage level
611
612
    Parameters
613
    ----------
614
    power_plants : pandas.DataFrame
615
        Power plants including voltage level
616
617
    Returns
618
    -------
619
    power_plants : pandas.DataFrame
620
        Power plants including voltage level and bus_id
621
622
    """
623
624
    mv_grid_districts = db.select_geodataframe(
625
        f"""
626
        SELECT * FROM {cfg['sources']['egon_mv_grid_district']}
627
        """,
628
        epsg=4326,
629
    )
630
631
    ehv_grid_districts = db.select_geodataframe(
632
        f"""
633
        SELECT * FROM {cfg['sources']['ehv_voronoi']}
634
        """,
635
        epsg=4326,
636
    )
637
638
    # Assign power plants in hv and below to hvmv bus
639
    power_plants_hv = power_plants[power_plants.voltage_level >= 3].index
640
    if len(power_plants_hv) > 0:
641
        power_plants.loc[power_plants_hv, "bus_id"] = gpd.sjoin(
642
            power_plants[power_plants.index.isin(power_plants_hv)],
643
            mv_grid_districts,
644
        ).bus_id
645
646
    # Assign power plants in ehv to ehv bus
647
    power_plants_ehv = power_plants[power_plants.voltage_level < 3].index
648
649
    if len(power_plants_ehv) > 0:
650
        ehv_join = gpd.sjoin(
651
            power_plants[power_plants.index.isin(power_plants_ehv)],
652
            ehv_grid_districts,
653
        )
654
655
        if "bus_id_right" in ehv_join.columns:
656
            power_plants.loc[power_plants_ehv, "bus_id"] = gpd.sjoin(
657
                power_plants[power_plants.index.isin(power_plants_ehv)],
658
                ehv_grid_districts,
659
            ).bus_id_right
660
661
        else:
662
            power_plants.loc[power_plants_ehv, "bus_id"] = gpd.sjoin(
663
                power_plants[power_plants.index.isin(power_plants_ehv)],
664
                ehv_grid_districts,
665
            ).bus_id
666
667
    # Assert that all power plants have a bus_id
668
    assert power_plants.bus_id.notnull().all(), f"""Some power plants are
669
    not attached to a bus: {power_plants[power_plants.bus_id.isnull()]}"""
670
671
    return power_plants
672
673
674
def insert_hydro_biomass():
675
    """Insert hydro and biomass power plants in database
676
677
    Returns
678
    -------
679
    None.
680
681
    """
682
    cfg = egon.data.config.datasets()["power_plants"]
683
    db.execute_sql(
684
        f"""
685
        DELETE FROM {cfg['target']['schema']}.{cfg['target']['table']}
686
        WHERE carrier IN ('biomass', 'reservoir', 'run_of_river')
687
        """
688
    )
689
690
    for scenario in ["eGon2035"]:
691
        insert_biomass_plants(scenario)
692
        insert_hydro_plants(scenario)
693
694
695
def allocate_conventional_non_chp_power_plants():
696
697
    carrier = ["oil", "gas"]
698
699
    cfg = egon.data.config.datasets()["power_plants"]
700
701
    # Delete existing plants in the target table
702
    db.execute_sql(
703
        f"""
704
         DELETE FROM {cfg ['target']['schema']}.{cfg ['target']['table']}
705
         WHERE carrier IN ('gas', 'oil')
706
         AND scenario='eGon2035';
707
         """
708
    )
709
710
    for carrier in carrier:
711
712
        nep = select_nep_power_plants(carrier)
713
714
        if nep.empty:
715
            print(f"DataFrame from NEP for carrier {carrier} is empty!")
716
717
        else:
718
719
            mastr = select_no_chp_combustion_mastr(carrier)
720
721
            # Assign voltage level to MaStR
722
            mastr["voltage_level"] = assign_voltage_level(
723
                mastr.rename({"el_capacity": "Nettonennleistung"}, axis=1), cfg
724
            )
725
726
            # Initalize DataFrame for matching power plants
727
            matched = gpd.GeoDataFrame(
728
                columns=[
729
                    "carrier",
730
                    "el_capacity",
731
                    "scenario",
732
                    "geometry",
733
                    "MaStRNummer",
734
                    "source",
735
                    "voltage_level",
736
                ]
737
            )
738
739
            # Match combustion plants of a certain carrier from NEP list
740
            # using PLZ and capacity
741
            matched, mastr, nep = match_nep_no_chp(
742
                nep,
743
                mastr,
744
                matched,
745
                buffer_capacity=0.1,
746
                consider_carrier=False,
747
            )
748
749
            # Match plants from NEP list using city and capacity
750
            matched, mastr, nep = match_nep_no_chp(
751
                nep,
752
                mastr,
753
                matched,
754
                buffer_capacity=0.1,
755
                consider_carrier=False,
756
                consider_location="city",
757
            )
758
759
            # Match plants from NEP list using plz,
760
            # neglecting the capacity
761
            matched, mastr, nep = match_nep_no_chp(
762
                nep,
763
                mastr,
764
                matched,
765
                consider_location="plz",
766
                consider_carrier=False,
767
                consider_capacity=False,
768
            )
769
770
            # Match plants from NEP list using city,
771
            # neglecting the capacity
772
            matched, mastr, nep = match_nep_no_chp(
773
                nep,
774
                mastr,
775
                matched,
776
                consider_location="city",
777
                consider_carrier=False,
778
                consider_capacity=False,
779
            )
780
781
            # Match remaining plants from NEP using the federal state
782
            matched, mastr, nep = match_nep_no_chp(
783
                nep,
784
                mastr,
785
                matched,
786
                buffer_capacity=0.1,
787
                consider_location="federal_state",
788
                consider_carrier=False,
789
            )
790
791
            # Match remaining plants from NEP using the federal state
792
            matched, mastr, nep = match_nep_no_chp(
793
                nep,
794
                mastr,
795
                matched,
796
                buffer_capacity=0.7,
797
                consider_location="federal_state",
798
                consider_carrier=False,
799
            )
800
801
            print(f"{matched.el_capacity.sum()} MW of {carrier} matched")
802
            print(f"{nep.c2035_capacity.sum()} MW of {carrier} not matched")
803
804
            matched.crs = "EPSG:4326"
805
806
            # Assign bus_id
807
            # Load grid district polygons
808
            mv_grid_districts = db.select_geodataframe(
809
                f"""
810
            SELECT * FROM {cfg['sources']['egon_mv_grid_district']}
811
            """,
812
                epsg=4326,
813
            )
814
815
            ehv_grid_districts = db.select_geodataframe(
816
                f"""
817
            SELECT * FROM {cfg['sources']['ehv_voronoi']}
818
            """,
819
                epsg=4326,
820
            )
821
822
            # Perform spatial joins for plants in ehv and hv level seperately
823
            power_plants_hv = gpd.sjoin(
824
                matched[matched.voltage_level >= 3],
825
                mv_grid_districts[["bus_id", "geom"]],
826
                how="left",
827
            ).drop(columns=["index_right"])
828
            power_plants_ehv = gpd.sjoin(
829
                matched[matched.voltage_level < 3],
830
                ehv_grid_districts[["bus_id", "geom"]],
831
                how="left",
832
            ).drop(columns=["index_right"])
833
834
            # Combine both dataframes
835
            power_plants = pd.concat([power_plants_hv, power_plants_ehv])
836
837
            # Insert into target table
838
            session = sessionmaker(bind=db.engine())()
839
            for i, row in power_plants.iterrows():
840
                entry = EgonPowerPlants(
841
                    sources={"el_capacity": row.source},
842
                    source_id={"MastrNummer": row.MaStRNummer},
843
                    carrier=row.carrier,
844
                    el_capacity=row.el_capacity,
845
                    voltage_level=row.voltage_level,
846
                    bus_id=row.bus_id,
847
                    scenario=row.scenario,
848
                    geom=f"SRID=4326;POINT({row.geometry.x} {row.geometry.y})",
849
                )
850
                session.add(entry)
851
            session.commit()
852
853
854
def allocate_other_power_plants():
855
856
    # Get configuration
857
    cfg = egon.data.config.datasets()["power_plants"]
858
    boundary = egon.data.config.settings()["egon-data"]["--dataset-boundary"]
859
860
    db.execute_sql(
861
        f"""
862
        DELETE FROM {cfg['target']['schema']}.{cfg['target']['table']}
863
        WHERE carrier ='others'
864
        """
865
    )
866
867
    # Define scenario, carrier 'others' is only present in 'eGon2035'
868
    scenario = "eGon2035"
869
870
    # Select target values for carrier 'others'
871
    target = db.select_dataframe(
872
        f"""
873
        SELECT sum(capacity) as capacity, carrier, scenario_name, nuts
874
            FROM {cfg['sources']['capacities']}
875
            WHERE scenario_name = '{scenario}'
876
            AND carrier = 'others'
877
            GROUP BY carrier, nuts, scenario_name;
878
        """
879
    )
880
881
    # Assign name of federal state
882
883
    map_states = {
884
        "DE1": "BadenWuerttemberg",
885
        "DEA": "NordrheinWestfalen",
886
        "DE7": "Hessen",
887
        "DE4": "Brandenburg",
888
        "DE5": "Bremen",
889
        "DEB": "RheinlandPfalz",
890
        "DEE": "SachsenAnhalt",
891
        "DEF": "SchleswigHolstein",
892
        "DE8": "MecklenburgVorpommern",
893
        "DEG": "Thueringen",
894
        "DE9": "Niedersachsen",
895
        "DED": "Sachsen",
896
        "DE6": "Hamburg",
897
        "DEC": "Saarland",
898
        "DE3": "Berlin",
899
        "DE2": "Bayern",
900
    }
901
902
    target = (
903
        target.replace({"nuts": map_states})
904
        .rename(columns={"nuts": "Bundesland"})
905
        .set_index("Bundesland")
906
    )
907
    target = target.capacity
908
909
    # Select 'non chp' power plants from mastr table
910
    mastr_combustion = select_no_chp_combustion_mastr("others")
911
912
    # Rename columns
913
    mastr_combustion = mastr_combustion.rename(
914
        columns={
915
            "carrier": "Energietraeger",
916
            "plz": "Postleitzahl",
917
            "city": "Ort",
918
            "federal_state": "Bundesland",
919
            "el_capacity": "Nettonennleistung",
920
        }
921
    )
922
923
    # Select power plants representing carrier 'others' from MaStR files
924
    mastr_sludge = pd.read_csv(cfg["sources"]["mastr_gsgk"]).query(
925
        """EinheitBetriebsstatus=='InBetrieb'and Energietraeger=='Klaerschlamm'"""  # noqa: E501
926
    )
927
    mastr_geothermal = pd.read_csv(cfg["sources"]["mastr_gsgk"]).query(
928
        "EinheitBetriebsstatus=='InBetrieb' and Energietraeger=='Geothermie' "
929
        "and Technologie == 'ORCOrganicRankineCycleAnlage'"
930
    )
931
932
    mastr_sg = mastr_sludge.append(mastr_geothermal)
933
934
    # Insert geometry column
935
    mastr_sg = mastr_sg[~(mastr_sg["Laengengrad"].isnull())]
936
    mastr_sg = gpd.GeoDataFrame(
937
        mastr_sg,
938
        geometry=gpd.points_from_xy(
939
            mastr_sg["Laengengrad"], mastr_sg["Breitengrad"], crs=4326
940
        ),
941
    )
942
943
    # Exclude columns which are not essential
944
    mastr_sg = mastr_sg.filter(
945
        [
946
            "EinheitMastrNummer",
947
            "Nettonennleistung",
948
            "geometry",
949
            "Energietraeger",
950
            "Postleitzahl",
951
            "Ort",
952
            "Bundesland",
953
        ],
954
        axis=1,
955
    )
956
    # Rename carrier
957
    mastr_sg.Energietraeger = "others"
958
959
    # Change data type
960
    mastr_sg["Postleitzahl"] = mastr_sg["Postleitzahl"].astype(int)
961
962
    # Capacity in MW
963
    mastr_sg.loc[:, "Nettonennleistung"] *= 1e-3
964
965
    # Merge different sources to one df
966
    mastr_others = mastr_sg.append(mastr_combustion).reset_index()
967
968
    # Delete entries outside Schleswig-Holstein for test mode
969
    if boundary == "Schleswig-Holstein":
970
        mastr_others = mastr_others[
971
            mastr_others["Bundesland"] == "SchleswigHolstein"
972
        ]
973
974
    # Scale capacities prox to now to meet target values
975
    mastr_prox = scale_prox2now(mastr_others, target, level="federal_state")
976
977
    # Assign voltage_level based on scaled capacity
978
    mastr_prox["voltage_level"] = np.nan
979
    mastr_prox["voltage_level"] = assign_voltage_level_by_capacity(mastr_prox)
980
981
    # Rename columns
982
    mastr_prox = mastr_prox.rename(
983
        columns={
984
            "Energietraeger": "carrier",
985
            "Postleitzahl": "plz",
986
            "Ort": "city",
987
            "Bundesland": "federal_state",
988
            "Nettonennleistung": "el_capacity",
989
        }
990
    )
991
992
    # Assign bus_id
993
    mastr_prox = assign_bus_id(mastr_prox, cfg)
994
    mastr_prox = mastr_prox.set_crs(4326, allow_override=True)
995
996
    # Insert into target table
997
    session = sessionmaker(bind=db.engine())()
998
    for i, row in mastr_prox.iterrows():
999
        entry = EgonPowerPlants(
1000
            sources=row.el_capacity,
1001
            source_id={"MastrNummer": row.EinheitMastrNummer},
1002
            carrier=row.carrier,
1003
            el_capacity=row.el_capacity,
1004
            voltage_level=row.voltage_level,
1005
            bus_id=row.bus_id,
1006
            scenario=scenario,
1007
            geom=f"SRID=4326; {row.geometry}",
1008
        )
1009
        session.add(entry)
1010
    session.commit()
1011