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