Passed
Pull Request — dev (#1054)
by
unknown
01:38
created

data.datasets.power_plants.scale_prox2now()   A

Complexity

Conditions 4

Size

Total Lines 33
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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