Passed
Pull Request — dev (#1181)
by
unknown
05:34
created

data.datasets.chp   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 858
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 39
eloc 487
dl 0
loc 858
rs 9.28
c 0
b 0
f 0

24 Functions

Rating   Name   Duplication   Size   Complexity  
B insert_biomass_chp() 0 77 5
A extension_HH() 0 2 1
A extension_BY() 0 2 1
B metadata() 0 97 1
A extension_HE() 0 2 1
A create_tables() 0 14 1
A extension_NS() 0 2 1
A extension_BE() 0 2 1
B assign_heat_bus() 0 102 5
A extension_SH() 0 2 1
A extension_ST() 0 2 1
B insert_chp_egon100re() 0 83 2
A extension_SN() 0 2 1
A nearest() 0 46 2
A extension_RP() 0 2 1
A extension_HB() 0 2 1
A insert_chp_egon2035() 0 38 1
A extension_BB() 0 2 1
A extension_SL() 0 2 1
A extension_NW() 0 2 1
A extension_MV() 0 2 1
C insert_chp_statusquo() 0 137 5
A extension_TH() 0 2 1
A extension_BW() 0 2 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A Chp.__init__() 0 6 1
1
"""
2
The central module containing all code dealing with combined heat and power
3
(CHP) plants.
4
"""
5
6
from pathlib import Path
7
import datetime
8
import json
9
import time
10
11
from geoalchemy2 import Geometry
12
from shapely.ops import nearest_points
13
from sqlalchemy import Boolean, Column, Float, Integer, Sequence, String
14
from sqlalchemy.dialects.postgresql import JSONB
15
from sqlalchemy.ext.declarative import declarative_base
16
from sqlalchemy.orm import sessionmaker
17
import geopandas as gpd
18
import pandas as pd
19
import pypsa
20
21
from egon.data import config, db
22
from egon.data.datasets import Dataset, wrapped_partial
23
from egon.data.datasets.chp.match_nep import insert_large_chp, map_carrier
24
from egon.data.datasets.chp.small_chp import (
25
    assign_use_case,
26
    existing_chp_smaller_10mw,
27
    extension_per_federal_state,
28
    extension_to_areas,
29
    select_target,
30
)
31
from egon.data.datasets.mastr import (
32
    WORKING_DIR_MASTR_OLD,
33
    WORKING_DIR_MASTR_NEW,
34
)
35
from egon.data.datasets.power_plants import (
36
    assign_bus_id,
37
    assign_voltage_level,
38
    filter_mastr_geometry,
39
    scale_prox2now,
40
)
41
from egon.data.metadata import (
42
    context,
43
    generate_resource_fields_from_sqla_model,
44
    license_egon_data_odbl,
45
    meta_metadata,
46
    sources,
47
)
48
49
Base = declarative_base()
50
51
52
class EgonChp(Base):
53
    __tablename__ = "egon_chp_plants"
54
    __table_args__ = {"schema": "supply"}
55
    id = Column(Integer, Sequence("chp_seq"), primary_key=True)
56
    sources = Column(JSONB)
57
    source_id = Column(JSONB)
58
    carrier = Column(String)
59
    district_heating = Column(Boolean)
60
    el_capacity = Column(Float)
61
    th_capacity = Column(Float)
62
    electrical_bus_id = Column(Integer)
63
    district_heating_area_id = Column(Integer)
64
    ch4_bus_id = Column(Integer)
65
    voltage_level = Column(Integer)
66
    scenario = Column(String)
67
    geom = Column(Geometry("POINT", 4326))
68
69
70
class EgonMaStRConventinalWithoutChp(Base):
71
    __tablename__ = "egon_mastr_conventional_without_chp"
72
    __table_args__ = {"schema": "supply"}
73
    id = Column(Integer, Sequence("mastr_conventional_seq"), primary_key=True)
74
    EinheitMastrNummer = Column(String)
75
    carrier = Column(String)
76
    el_capacity = Column(Float)
77
    plz = Column(Integer)
78
    city = Column(String)
79
    federal_state = Column(String)
80
    geometry = Column(Geometry("POINT", 4326))
81
82
83
def metadata():
84
    """Write metadata for heat supply tables
85
86
    Returns
87
    -------
88
    None.
89
90
    """
91
92
    fields = generate_resource_fields_from_sqla_model(EgonChp)
93
94
    fields_df = pd.DataFrame(data=fields).set_index("name")
95
    fields_df.loc["id", "description"] = "Unique identifyer"
96
    fields_df.loc["sources", "description"] = "List of sources"
97
    fields_df.loc[
98
        "source_id", "description"
99
    ] = "Names of sources, e.g. MaStr_id"
100
    fields_df.loc["carrier", "description"] = "Energy carrier"
101
    fields_df.loc[
102
        "district_heating", "description"
103
    ] = "Used in district heating or not"
104
    fields_df.loc[
105
        "el_capacity", "description"
106
    ] = "Installed electrical capacity"
107
    fields_df.loc["th_capacity", "description"] = "Installed thermal capacity"
108
    fields_df.loc[
109
        "electrical_bus_id", "description"
110
    ] = "Index of corresponding electricity bus"
111
    fields_df.loc[
112
        "district_heating_area_id", "description"
113
    ] = "Index of corresponding district heating bus"
114
    fields_df.loc[
115
        "ch4_bus_id", "description"
116
    ] = "Index of corresponding methane bus"
117
    fields_df.loc["voltage_level", "description"] = "Voltage level"
118
    fields_df.loc["scenario", "description"] = "Name of scenario"
119
    fields_df.loc["geom", "description"] = "Location of CHP plant"
120
121
    fields_df.loc["el_capacity", "unit"] = "MW_el"
122
    fields_df.loc["th_capacity", "unit"] = "MW_th"
123
    fields_df.unit.fillna("none", inplace=True)
124
125
    fields = fields_df.reset_index().to_dict(orient="records")
126
127
    meta_district = {
128
        "name": "supply.egon_chp_plants",
129
        "title": "eGon combined heat and power plants",
130
        "id": "WILL_BE_SET_AT_PUBLICATION",
131
        "description": "Combined heat and power plants",
132
        "language": ["EN"],
133
        "publicationDate": datetime.date.today().isoformat(),
134
        "context": context(),
135
        "spatial": {
136
            "location": None,
137
            "extent": "Germany",
138
            "resolution": None,
139
        },
140
        "sources": [
141
            sources()["vg250"],
142
            sources()["egon-data"],
143
            sources()["egon-data_bundle"],
144
            sources()["openstreetmap"],
145
            sources()["mastr"],
146
        ],
147
        "licenses": [license_egon_data_odbl()],
148
        "contributors": [
149
            {
150
                "title": "Clara Büttner",
151
                "email": "http://github.com/ClaraBuettner",
152
                "date": time.strftime("%Y-%m-%d"),
153
                "object": None,
154
                "comment": "Imported data",
155
            },
156
        ],
157
        "resources": [
158
            {
159
                "profile": "tabular-data-resource",
160
                "name": "supply.egon_chp_plants",
161
                "path": None,
162
                "format": "PostgreSQL",
163
                "encoding": "UTF-8",
164
                "schema": {
165
                    "fields": fields,
166
                    "primaryKey": ["index"],
167
                    "foreignKeys": [],
168
                },
169
                "dialect": {"delimiter": None, "decimalSeparator": "."},
170
            }
171
        ],
172
        "metaMetadata": meta_metadata(),
173
    }
174
175
    # Add metadata as a comment to the table
176
    db.submit_comment(
177
        "'" + json.dumps(meta_district) + "'",
178
        EgonChp.__table__.schema,
179
        EgonChp.__table__.name,
180
    )
181
182
183
def create_tables():
184
    """Create tables for chp data
185
    Returns
186
    -------
187
    None.
188
    """
189
190
    db.execute_sql("CREATE SCHEMA IF NOT EXISTS supply;")
191
    engine = db.engine()
192
    EgonChp.__table__.drop(bind=engine, checkfirst=True)
193
    EgonChp.__table__.create(bind=engine, checkfirst=True)
194
    EgonMaStRConventinalWithoutChp.__table__.drop(bind=engine, checkfirst=True)
195
    EgonMaStRConventinalWithoutChp.__table__.create(
196
        bind=engine, checkfirst=True
197
    )
198
199
200
def nearest(
201
    row,
202
    df,
203
    centroid=False,
204
    row_geom_col="geometry",
205
    df_geom_col="geometry",
206
    src_column=None,
207
):
208
    """
209
    Finds the nearest point and returns the specified column values
210
211
    Parameters
212
    ----------
213
    row : pandas.Series
214
        Data to which the nearest data of df is assigned.
215
    df : pandas.DataFrame
216
        Data which includes all options for the nearest neighbor alogrithm.
217
    centroid : boolean
218
        Use centroid geoemtry. The default is False.
219
    row_geom_col : str, optional
220
        Name of row's geometry column. The default is 'geometry'.
221
    df_geom_col : str, optional
222
        Name of df's geometry column. The default is 'geometry'.
223
    src_column : str, optional
224
        Name of returned df column. The default is None.
225
226
    Returns
227
    -------
228
    value : pandas.Series
229
        Values of specified column of df
230
231
    """
232
233
    if centroid:
234
        unary_union = df.centroid.unary_union
235
    else:
236
        unary_union = df[df_geom_col].unary_union
237
    # Find the geometry that is closest
238
    nearest = (
239
        df[df_geom_col] == nearest_points(row[row_geom_col], unary_union)[1]
240
    )
241
242
    # Get the corresponding value from df (matching is based on the geometry)
243
    value = df[nearest][src_column].values[0]
244
245
    return value
246
247
248
def assign_heat_bus():
249
    """Selects heat_bus for chps used in district heating.
250
251
    Parameters
252
    ----------
253
    scenario : str, optional
254
        Name of the corresponding scenario. The default is 'eGon2035'.
255
256
    Returns
257
    -------
258
    None.
259
260
    """
261
    sources = config.datasets()["chp_location"]["sources"]
262
    target = config.datasets()["chp_location"]["targets"]["chp_table"]
263
264
    for scenario in config.settings()["egon-data"]["--scenarios"]:
265
        # Select CHP with use_case = 'district_heating'
266
        chp = db.select_geodataframe(
267
            f"""
268
            SELECT * FROM
269
            {target['schema']}.{target['table']}
270
            WHERE scenario = '{scenario}'
271
            AND district_heating = True
272
            """,
273
            index_col="id",
274
            epsg=4326,
275
        )
276
277
        if chp.empty:
278
            print(f"No CHP for district heating in scenario {scenario}")
279
            return
280
281
        # Select district heating areas and their centroid
282
        district_heating = db.select_geodataframe(
283
            f"""
284
            SELECT area_id, ST_Centroid(geom_polygon) as geom
285
            FROM
286
            {sources['district_heating_areas']['schema']}.
287
            {sources['district_heating_areas']['table']}
288
            WHERE scenario = '{scenario}'
289
            """,
290
            epsg=4326,
291
        )
292
293
        # Assign district heating area_id to district_heating_chp
294
        # According to nearest centroid of district heating area
295
        chp["district_heating_area_id"] = chp.apply(
296
            nearest,
297
            df=district_heating,
298
            row_geom_col="geom",
299
            df_geom_col="geom",
300
            centroid=True,
301
            src_column="area_id",
302
            axis=1,
303
        )
304
305
        # Drop district heating CHP without heat_bus_id
306
        db.execute_sql(
307
            f"""
308
            DELETE FROM {target['schema']}.{target['table']}
309
            WHERE scenario = '{scenario}'
310
            AND district_heating = True
311
            """
312
        )
313
314
        # Insert district heating CHP with heat_bus_id
315
        session = sessionmaker(bind=db.engine())()
316
        for i, row in chp.iterrows():
317
            if row.carrier != "biomass":
318
                entry = EgonChp(
319
                    id=i,
320
                    sources=row.sources,
321
                    source_id=row.source_id,
322
                    carrier=row.carrier,
323
                    el_capacity=row.el_capacity,
324
                    th_capacity=row.th_capacity,
325
                    electrical_bus_id=row.electrical_bus_id,
326
                    ch4_bus_id=row.ch4_bus_id,
327
                    district_heating_area_id=row.district_heating_area_id,
328
                    district_heating=row.district_heating,
329
                    voltage_level=row.voltage_level,
330
                    scenario=scenario,
331
                    geom=f"SRID=4326;POINT({row.geom.x} {row.geom.y})",
332
                )
333
            else:
334
                entry = EgonChp(
335
                    id=i,
336
                    sources=row.sources,
337
                    source_id=row.source_id,
338
                    carrier=row.carrier,
339
                    el_capacity=row.el_capacity,
340
                    th_capacity=row.th_capacity,
341
                    electrical_bus_id=row.electrical_bus_id,
342
                    district_heating_area_id=row.district_heating_area_id,
343
                    district_heating=row.district_heating,
344
                    voltage_level=row.voltage_level,
345
                    scenario=scenario,
346
                    geom=f"SRID=4326;POINT({row.geom.x} {row.geom.y})",
347
                )
348
            session.add(entry)
349
        session.commit()
350
351
352
def insert_biomass_chp(scenario):
353
    """Insert biomass chp plants of future scenario
354
355
    Parameters
356
    ----------
357
    scenario : str
358
        Name of scenario.
359
360
    Returns
361
    -------
362
    None.
363
364
    """
365
    cfg = config.datasets()["chp_location"]
366
367
    # import target values from NEP 2021, scneario C 2035
368
    target = select_target("biomass", scenario)
369
370
    # import data for MaStR
371
    mastr = pd.read_csv(
372
        WORKING_DIR_MASTR_OLD / cfg["sources"]["mastr_biomass"]
373
    ).query("EinheitBetriebsstatus=='InBetrieb'")
374
375
    # Drop entries without federal state or 'AusschließlichWirtschaftszone'
376
    mastr = mastr[
377
        mastr.Bundesland.isin(
378
            pd.read_sql(
379
                f"""SELECT DISTINCT ON (gen)
380
        REPLACE(REPLACE(gen, '-', ''), 'ü', 'ue') as states
381
        FROM {cfg['sources']['vg250_lan']['schema']}.
382
        {cfg['sources']['vg250_lan']['table']}""",
383
                con=db.engine(),
384
            ).states.values
385
        )
386
    ]
387
388
    # Scaling will be done per federal state in case of eGon2035 scenario.
389
    if scenario == "eGon2035":
390
        level = "federal_state"
391
    else:
392
        level = "country"
393
    # Choose only entries with valid geometries inside DE/test mode
394
    mastr_loc = filter_mastr_geometry(mastr).set_geometry("geometry")
395
396
    # Scale capacities to meet target values
397
    mastr_loc = scale_prox2now(mastr_loc, target, level=level)
398
399
    # Assign bus_id
400
    if len(mastr_loc) > 0:
401
        mastr_loc["voltage_level"] = assign_voltage_level(
402
            mastr_loc, cfg, WORKING_DIR_MASTR_OLD
403
        )
404
        mastr_loc = assign_bus_id(mastr_loc, cfg)
405
    mastr_loc = assign_use_case(mastr_loc, cfg["sources"], scenario)
406
407
    # Insert entries with location
408
    session = sessionmaker(bind=db.engine())()
409
    for i, row in mastr_loc.iterrows():
410
        if row.ThermischeNutzleistung > 0:
411
            entry = EgonChp(
412
                sources={
413
                    "chp": "MaStR",
414
                    "el_capacity": "MaStR scaled with NEP 2021",
415
                    "th_capacity": "MaStR",
416
                },
417
                source_id={"MastrNummer": row.EinheitMastrNummer},
418
                carrier="biomass",
419
                el_capacity=row.Nettonennleistung,
420
                th_capacity=row.ThermischeNutzleistung / 1000,
421
                scenario=scenario,
422
                district_heating=row.district_heating,
423
                electrical_bus_id=row.bus_id,
424
                voltage_level=row.voltage_level,
425
                geom=f"SRID=4326;POINT({row.Laengengrad} {row.Breitengrad})",
426
            )
427
            session.add(entry)
428
    session.commit()
429
430
431
def insert_chp_statusquo(scn="status2019"):
432
    cfg = config.datasets()["chp_location"]
433
434
    # import data for MaStR
435
    mastr = pd.read_csv(
436
        WORKING_DIR_MASTR_NEW / "bnetza_mastr_combustion_cleaned.csv"
437
    )
438
439
    mastr_biomass = pd.read_csv(
440
        WORKING_DIR_MASTR_NEW / "bnetza_mastr_biomass_cleaned.csv"
441
    )
442
443
    mastr = pd.concat([mastr, mastr_biomass]).reset_index(drop=True)
444
445
    mastr = mastr.loc[mastr.ThermischeNutzleistung > 0]
446
447
    mastr = mastr.loc[
448
        mastr.Energietraeger.isin(
449
            [
450
                "Erdgas",
451
                "Mineralölprodukte",
452
                "andere Gase",
453
                "nicht biogener Abfall",
454
                "Braunkohle",
455
                "Steinkohle",
456
                "Biomasse",
457
            ]
458
        )
459
    ]
460
461
    mastr.Inbetriebnahmedatum = pd.to_datetime(mastr.Inbetriebnahmedatum)
462
    mastr.DatumEndgueltigeStilllegung = pd.to_datetime(
463
        mastr.DatumEndgueltigeStilllegung
464
    )
465
    mastr = mastr.loc[
466
        mastr.Inbetriebnahmedatum
467
        <= config.datasets()["mastr_new"][f"{scn}_date_max"]
468
    ]
469
470
    mastr = mastr.loc[
471
        (
472
            mastr.DatumEndgueltigeStilllegung
473
            >= config.datasets()["mastr_new"][f"{scn}_date_max"]
474
        )
475
        | (mastr.DatumEndgueltigeStilllegung.isnull())
476
    ]
477
478
    mastr.groupby("Energietraeger").Nettonennleistung.sum().mul(1e-6)
479
480
    geom_municipalities = db.select_geodataframe(
481
        """
482
        SELECT gen, ST_UNION(geometry) as geom
483
        FROM boundaries.vg250_gem
484
        GROUP BY gen
485
        """
486
    ).set_index("gen")
487
488
    # Assing Laengengrad and Breitengrad to chps without location data
489
    # based on the centroid of the municipaltiy
490
    idx_no_location = mastr[
491
        (mastr.Laengengrad.isnull())
492
        & (mastr.Gemeinde.isin(geom_municipalities.index))
493
    ].index
494
495
    mastr.loc[idx_no_location, "Laengengrad"] = (
496
        geom_municipalities.to_crs(epsg="4326").centroid.x.loc[
497
            mastr.Gemeinde[idx_no_location]
498
        ]
499
    ).values
500
501
    mastr.loc[idx_no_location, "Breitengrad"] = (
502
        geom_municipalities.to_crs(epsg="4326").centroid.y.loc[
503
            mastr.Gemeinde[idx_no_location]
504
        ]
505
    ).values
506
507
    if (
508
        config.settings()["egon-data"]["--dataset-boundary"]
509
        == "Schleswig-Holstein"
510
    ):
511
        dropped_capacity = mastr[
512
            (mastr.Laengengrad.isnull())
513
            & (mastr.Bundesland == "SchleswigHolstein")
514
        ].Nettonennleistung.sum()
515
516
    else:
517
        dropped_capacity = mastr[
518
            (mastr.Laengengrad.isnull())
519
        ].Nettonennleistung.sum()
520
521
    print(
522
        f"""
523
          CHPs with a total installed electrical capacity of {dropped_capacity} kW are dropped
524
          because of missing or wrong location data
525
          """
526
    )
527
528
    mastr = mastr[~mastr.Laengengrad.isnull()]
529
    mastr = filter_mastr_geometry(mastr).set_geometry("geometry")
530
531
    # Assign bus_id
532
    if len(mastr) > 0:
533
        mastr["voltage_level"] = assign_voltage_level(
534
            mastr, cfg, WORKING_DIR_MASTR_NEW
535
        )
536
537
        gas_bus_id = db.assign_gas_bus_id(mastr, scn, "CH4").bus
538
539
        mastr = assign_bus_id(mastr, cfg, drop_missing=True)
540
541
        mastr["gas_bus_id"] = gas_bus_id
542
543
    mastr = assign_use_case(mastr, cfg["sources"], scn)
544
545
    # Insert entries with location
546
    session = sessionmaker(bind=db.engine())()
547
    for i, row in mastr.iterrows():
548
        if row.ThermischeNutzleistung > 0:
549
            entry = EgonChp(
550
                sources={
551
                    "chp": "MaStR",
552
                    "el_capacity": "MaStR",
553
                    "th_capacity": "MaStR",
554
                },
555
                source_id={"MastrNummer": row.EinheitMastrNummer},
556
                carrier=map_carrier().loc[row.Energietraeger],
557
                el_capacity=row.Nettonennleistung / 1000,
558
                th_capacity=row.ThermischeNutzleistung / 1000,
559
                scenario=scn,
560
                district_heating=row.district_heating,
561
                electrical_bus_id=row.bus_id,
562
                ch4_bus_id=row.gas_bus_id,
563
                voltage_level=row.voltage_level,
564
                geom=f"SRID=4326;POINT({row.Laengengrad} {row.Breitengrad})",
565
            )
566
            session.add(entry)
567
    session.commit()
568
569
570
def insert_chp_egon2035():
571
    """Insert CHP plants for eGon2035 considering NEP and MaStR data
572
573
    Returns
574
    -------
575
    None.
576
577
    """
578
579
    sources = config.datasets()["chp_location"]["sources"]
580
581
    targets = config.datasets()["chp_location"]["targets"]
582
583
    insert_biomass_chp("eGon2035")
584
585
    # Insert large CHPs based on NEP's list of conventional power plants
586
    MaStR_konv = insert_large_chp(sources, targets["chp_table"], EgonChp)
587
588
    # Insert smaller CHPs (< 10MW) based on existing locations from MaStR
589
    existing_chp_smaller_10mw(sources, MaStR_konv, EgonChp)
590
591
    gpd.GeoDataFrame(
592
        MaStR_konv[
593
            [
594
                "EinheitMastrNummer",
595
                "el_capacity",
596
                "geometry",
597
                "carrier",
598
                "plz",
599
                "city",
600
                "federal_state",
601
            ]
602
        ]
603
    ).to_postgis(
604
        targets["mastr_conventional_without_chp"]["table"],
605
        schema=targets["mastr_conventional_without_chp"]["schema"],
606
        con=db.engine(),
607
        if_exists="replace",
608
    )
609
610
611
def extension_BW():
612
    extension_per_federal_state("BadenWuerttemberg", EgonChp)
613
614
615
def extension_BY():
616
    extension_per_federal_state("Bayern", EgonChp)
617
618
619
def extension_HB():
620
    extension_per_federal_state("Bremen", EgonChp)
621
622
623
def extension_BB():
624
    extension_per_federal_state("Brandenburg", EgonChp)
625
626
627
def extension_HH():
628
    extension_per_federal_state("Hamburg", EgonChp)
629
630
631
def extension_HE():
632
    extension_per_federal_state("Hessen", EgonChp)
633
634
635
def extension_MV():
636
    extension_per_federal_state("MecklenburgVorpommern", EgonChp)
637
638
639
def extension_NS():
640
    extension_per_federal_state("Niedersachsen", EgonChp)
641
642
643
def extension_NW():
644
    extension_per_federal_state("NordrheinWestfalen", EgonChp)
645
646
647
def extension_SN():
648
    extension_per_federal_state("Sachsen", EgonChp)
649
650
651
def extension_TH():
652
    extension_per_federal_state("Thueringen", EgonChp)
653
654
655
def extension_SL():
656
    extension_per_federal_state("Saarland", EgonChp)
657
658
659
def extension_ST():
660
    extension_per_federal_state("SachsenAnhalt", EgonChp)
661
662
663
def extension_RP():
664
    extension_per_federal_state("RheinlandPfalz", EgonChp)
665
666
667
def extension_BE():
668
    extension_per_federal_state("Berlin", EgonChp)
669
670
671
def extension_SH():
672
    extension_per_federal_state("SchleswigHolstein", EgonChp)
673
674
675
def insert_chp_egon100re():
676
    """Insert CHP plants for eGon100RE considering results from pypsa-eur-sec
677
678
    Returns
679
    -------
680
    None.
681
682
    """
683
684
    sources = config.datasets()["chp_location"]["sources"]
685
686
    db.execute_sql(
687
        f"""
688
        DELETE FROM {EgonChp.__table__.schema}.{EgonChp.__table__.name}
689
        WHERE scenario = 'eGon100RE'
690
        """
691
    )
692
693
    # select target values from pypsa-eur-sec
694
    additional_capacity = db.select_dataframe(
695
        """
696
        SELECT capacity
697
        FROM supply.egon_scenario_capacities
698
        WHERE scenario_name = 'eGon100RE'
699
        AND carrier = 'urban_central_gas_CHP'
700
        """
701
    ).capacity[0]
702
703
    if config.settings()["egon-data"]["--dataset-boundary"] != "Everything":
704
        additional_capacity /= 16
705
    target_file = (
706
        Path(".")
707
        / "data_bundle_egon_data"
708
        / "pypsa_eur_sec"
709
        / "2022-07-26-egondata-integration"
710
        / "postnetworks"
711
        / "elec_s_37_lv2.0__Co2L0-1H-T-H-B-I-dist1_2050.nc"
712
    )
713
714
    network = pypsa.Network(str(target_file))
715
    chp_index = "DE0 0 urban central gas CHP"
716
717
    standard_chp_th = 10
718
    standard_chp_el = (
719
        standard_chp_th
720
        * network.links.loc[chp_index, "efficiency"]
721
        / network.links.loc[chp_index, "efficiency2"]
722
    )
723
724
    areas = db.select_geodataframe(
725
        f"""
726
            SELECT
727
            residential_and_service_demand as demand, area_id,
728
            ST_Transform(ST_PointOnSurface(geom_polygon), 4326)  as geom
729
            FROM
730
            {sources['district_heating_areas']['schema']}.
731
            {sources['district_heating_areas']['table']}
732
            WHERE scenario = 'eGon100RE'
733
            """
734
    )
735
736
    existing_chp = pd.DataFrame(
737
        data={
738
            "el_capacity": standard_chp_el,
739
            "th_capacity": standard_chp_th,
740
            "voltage_level": 5,
741
        },
742
        index=range(1),
743
    )
744
745
    flh = (
746
        network.links_t.p0[chp_index].sum()
747
        / network.links.p_nom_opt[chp_index]
748
    )
749
750
    extension_to_areas(
751
        areas,
752
        additional_capacity,
753
        existing_chp,
754
        flh,
755
        EgonChp,
756
        district_heating=True,
757
        scenario="eGon100RE",
758
    )
759
760
761
tasks = (create_tables,)
762
763
insert_per_scenario = set()
764
765
if "status2019" in config.settings()["egon-data"]["--scenarios"]:
766
    insert_per_scenario.add(
767
        wrapped_partial(insert_chp_statusquo, scn="status2019", postfix="_2019")
768
    )
769
770
if "status2023" in config.settings()["egon-data"]["--scenarios"]:
771
    insert_per_scenario.add(
772
        wrapped_partial(insert_chp_statusquo, scn="status2023", postfix="_2023")
773
    )
774
775
if "eGon2035" in config.settings()["egon-data"]["--scenarios"]:
776
    insert_per_scenario.add(insert_chp_egon2035)
777
778
if "eGon100RE" in config.settings()["egon-data"]["--scenarios"]:
779
    insert_per_scenario.add(insert_chp_egon100re)
780
781
tasks = tasks + (insert_per_scenario, assign_heat_bus)
782
783
extension = set()
784
785
if "eGon2035" in config.settings()["egon-data"]["--scenarios"]:
786
    # Add one task per federal state for small CHP extension
787
    if (
788
        config.settings()["egon-data"]["--dataset-boundary"]
789
        == "Schleswig-Holstein"
790
    ):
791
        extension = extension_SH
792
    else:
793
        extension = {
794
            extension_BW,
795
            extension_BY,
796
            extension_HB,
797
            extension_BB,
798
            extension_HE,
799
            extension_MV,
800
            extension_NS,
801
            extension_NW,
802
            extension_SH,
803
            extension_HH,
804
            extension_RP,
805
            extension_SL,
806
            extension_SN,
807
            extension_ST,
808
            extension_TH,
809
            extension_BE,
810
        }
811
812
if extension != set():
813
    tasks = tasks + (extension,)
814
815
tasks += (metadata,)
816
817
818
class Chp(Dataset):
819
    """
820
    Extract combined heat and power plants for each scenario
821
822
    This dataset creates combined heat and power (CHP) plants for each scenario and defines their use case.
823
    The method bases on existing CHP plants from Marktstammdatenregister. For the eGon2035 scenario,
824
    a list of CHP plans from the grid operator is used for new largescale CHP plants. CHP < 10MW are
825
    randomly distributed.
826
    Depending on the distance to a district heating grid, it is decided if the CHP is used to
827
    supply a district heating grid or used by an industrial site.
828
829
830
    *Dependencies*
831
      * :py:class:`GasAreaseGon100RE <egon.data.datasets.gas_areas.GasAreaseGon100RE>`
832
      * :py:class:`GasAreaseGon2035 <egon.data.datasets.gas_areas.GasAreaseGon2035>`
833
      * :py:class:`DistrictHeatingAreas <egon.data.datasets.district_heating_areas.DistrictHeatingAreas>`
834
      * :py:class:`IndustrialDemandCurves <egon.data.datasets.industry.IndustrialDemandCurves>`
835
      * :py:class:`OsmLanduse <egon.data.datasets.loadarea.OsmLanduse>`
836
      * :py:func:`download_mastr_data <egon.data.datasets.mastr.download_mastr_data>`
837
      * :py:func:`define_mv_grid_districts <egon.data.datasets.mv_grid_districts.define_mv_grid_districts>`
838
      * :py:class:`ScenarioCapacities <egon.data.datasets.scenario_capacities.ScenarioCapacities>`
839
840
841
    *Resulting tables*
842
      * :py:class:`supply.egon_chp_plants <egon.data.datasets.chp.EgonChp>` is created and filled
843
      * :py:class:`supply.egon_mastr_conventional_without_chp <egon.data.datasets.chp.EgonMaStRConventinalWithoutChp>` is created and filled
844
845
    """
846
847
    #:
848
    name: str = "Chp"
849
    #:
850
    version: str = "0.0.10"
851
852
    def __init__(self, dependencies):
853
        super().__init__(
854
            name=self.name,
855
            version=self.version,
856
            dependencies=dependencies,
857
            tasks=tasks,
858
        )
859