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

allocate_other_power_plants()   B

Complexity

Conditions 3

Size

Total Lines 157
Code Lines 90

Duplication

Lines 0
Ratio 0 %

Importance

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