Passed
Pull Request — dev (#1375)
by
unknown
02:18
created

data.datasets.chp.insert_chp_egon2035()   A

Complexity

Conditions 1

Size

Total Lines 38
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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