Passed
Pull Request — dev (#568)
by Stephan
01:45
created

data.datasets.heat_demand_timeseries.metadata()   B

Complexity

Conditions 1

Size

Total Lines 75
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 55
dl 0
loc 75
rs 8.4727
c 0
b 0
f 0
cc 1
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
from datetime import date, datetime
2
from pathlib import Path
3
import json
4
import os
5
import time
6
7
from sqlalchemy import ARRAY, Column, Float, Integer, String, Text
8
from sqlalchemy.ext.declarative import declarative_base
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.heat_demand_timeseries.daily import (
15
    daily_demand_shares_per_climate_zone,
16
    map_climate_zones_to_zensus,
17
)
18
from egon.data.datasets.heat_demand_timeseries.idp_pool import create, select
19
from egon.data.datasets.heat_demand_timeseries.service_sector import (
20
    CTS_demand_scale,
21
)
22
from egon.data.metadata import (
23
    context,
24
    license_egon_data_odbl,
25
    meta_metadata,
26
    sources,
27
)
28
29
Base = declarative_base()
30
31
32
class EgonTimeseriesDistrictHeating(Base):
33
    __tablename__ = "egon_timeseries_district_heating"
34
    __table_args__ = {"schema": "demand"}
35
    area_id = Column(Integer, primary_key=True)
36
    scenario = Column(Text, primary_key=True)
37
    dist_aggregated_mw = Column(ARRAY(Float(53)))
38
39
40
class EgonEtragoTimeseriesIndividualHeating(Base):
41
    __tablename__ = "egon_etrago_timeseries_individual_heating"
42
    __table_args__ = {"schema": "demand"}
43
    bus_id = Column(Integer, primary_key=True)
44
    scenario = Column(Text, primary_key=True)
45
    dist_aggregated_mw = Column(ARRAY(Float(53)))
46
47
48
class EgonIndividualHeatingPeakLoads(Base):
49
    __tablename__ = "egon_individual_heating_peak_loads"
50
    __table_args__ = {"schema": "demand"}
51
    building_id = Column(Integer, primary_key=True)
52
    scenario = Column(Text, primary_key=True)
53
    w_th = Column(Float(53))
54
55
56
class EgonEtragoHeatCts(Base):
57
    __tablename__ = "egon_etrago_heat_cts"
58
    __table_args__ = {"schema": "demand"}
59
60
    bus_id = Column(Integer, primary_key=True)
61
    scn_name = Column(String, primary_key=True)
62
    p_set = Column(ARRAY(Float))
63
64
65
def create_timeseries_for_building(building_id, scenario):
66
    """Generates final heat demand timeseries for a specific building
67
68
    Parameters
69
    ----------
70
    building_id : int
71
        Index of the selected building
72
    scenario : str
73
        Name of the selected scenario.
74
75
    Returns
76
    -------
77
    pd.DataFrame
78
        Hourly heat demand timeseries in MW for the selected building
79
80
    """
81
82
    return db.select_dataframe(
83
        f"""
84
        SELECT building_demand * UNNEST(idp) as demand
85
        FROM
86
        (
87
        SELECT
88
            demand.demand
89
            / building.count
90
            * daily_demand.daily_demand_share as building_demand,
91
            daily_demand.day_of_year
92
        FROM
93
94
        (SELECT demand FROM
95
        demand.egon_peta_heat
96
        WHERE scenario = '{scenario}'
97
        AND sector = 'residential'
98
        AND zensus_population_id IN(
99
        SELECT zensus_population_id FROM
100
        demand.egon_heat_timeseries_selected_profiles
101
        WHERE building_id  = {building_id})) as demand,
102
103
        (SELECT COUNT(building_id)
104
        FROM demand.egon_heat_timeseries_selected_profiles
105
        WHERE zensus_population_id IN(
106
        SELECT zensus_population_id FROM
107
        demand.egon_heat_timeseries_selected_profiles
108
        WHERE building_id  = {building_id})) as building,
109
110
        (SELECT daily_demand_share, day_of_year FROM
111
        demand.egon_daily_heat_demand_per_climate_zone
112
        WHERE climate_zone = (
113
            SELECT climate_zone FROM boundaries.egon_map_zensus_climate_zones
114
            WHERE zensus_population_id =
115
            (
116
                SELECT zensus_population_id
117
                FROM demand.egon_heat_timeseries_selected_profiles
118
                WHERE building_id = {building_id}
119
            )
120
        )) as daily_demand) as daily_demand
121
122
        JOIN (SELECT b.idp, ordinality as day
123
        FROM demand.egon_heat_timeseries_selected_profiles a,
124
        UNNEST (a.selected_idp_profiles) WITH ORDINALITY as selected_idp
125
        JOIN demand.egon_heat_idp_pool b
126
        ON selected_idp = b.index
127
        WHERE a.building_id = {building_id}) as demand_profile
128
        ON demand_profile.day = daily_demand.day_of_year
129
        """
130
    )
131
132
133
def create_district_heating_profile(scenario, area_id):
134
    """Create a heat demand profile for a district heating grid.
135
136
    The created heat demand profile includes the demands of households
137
    and the service sector.
138
139
    Parameters
140
    ----------
141
    scenario : str
142
        The name of the selected scenario.
143
    area_id : int
144
        The index of the selected district heating grid.
145
146
    Returns
147
    -------
148
    pd.DataFrame
149
        An hourly heat demand timeseries in MW for the selected district
150
        heating grid.
151
152
    """
153
154
    start_time = datetime.now()
155
156
    df = db.select_dataframe(
157
        f"""
158
159
        SELECT SUM(building_demand_per_hour) as demand_profile, hour_of_year
160
        FROM
161
162
        (
163
        SELECT demand.demand  *
164
        c.daily_demand_share * hourly_demand as building_demand_per_hour,
165
        ordinality + 24* (c.day_of_year-1) as hour_of_year,
166
        demand_profile.building_id,
167
        c.day_of_year,
168
        ordinality
169
170
        FROM
171
172
        (SELECT zensus_population_id, demand FROM
173
        demand.egon_peta_heat
174
        WHERE scenario = '{scenario}'
175
        AND sector = 'residential'
176
        AND zensus_population_id IN(
177
        SELECT zensus_population_id FROM
178
        demand.egon_map_zensus_district_heating_areas
179
        WHERE scenario = '{scenario}'
180
        AND area_id = {area_id}
181
        )) as demand
182
183
        JOIN boundaries.egon_map_zensus_climate_zones b
184
        ON demand.zensus_population_id = b.zensus_population_id
185
186
        JOIN demand.egon_daily_heat_demand_per_climate_zone c
187
        ON c.climate_zone = b.climate_zone
188
189
        JOIN (
190
        SELECT e.idp, ordinality as day, zensus_population_id, building_id
191
        FROM demand.egon_heat_timeseries_selected_profiles d,
192
        UNNEST (d.selected_idp_profiles) WITH ORDINALITY as selected_idp
193
        JOIN demand.egon_heat_idp_pool e
194
        ON selected_idp = e.index
195
        WHERE zensus_population_id IN (
196
        SELECT zensus_population_id FROM
197
        demand.egon_map_zensus_district_heating_areas
198
        WHERE scenario = '{scenario}'
199
        AND area_id = {area_id}
200
        ))  demand_profile
201
        ON (demand_profile.day = c.day_of_year AND
202
            demand_profile.zensus_population_id = b.zensus_population_id)
203
204
        JOIN (SELECT COUNT(building_id), zensus_population_id
205
        FROM demand.egon_heat_timeseries_selected_profiles
206
        WHERE zensus_population_id IN(
207
        SELECT zensus_population_id FROM
208
        demand.egon_heat_timeseries_selected_profiles
209
       WHERE zensus_population_id IN (
210
       SELECT zensus_population_id FROM
211
       demand.egon_map_zensus_district_heating_areas
212
       WHERE scenario = '{scenario}'
213
       AND area_id = {area_id}
214
       ))
215
        GROUP BY zensus_population_id) building
216
        ON building.zensus_population_id = b.zensus_population_id,
217
218
        UNNEST(demand_profile.idp) WITH ORDINALITY as hourly_demand
219
        )   result
220
221
222
        GROUP BY hour_of_year
223
224
        """
225
    )
226
227
    print(
228
        f"Time to create time series for district heating grid {scenario}"
229
        f" {area_id}:\n{datetime.now() - start_time}"
230
    )
231
232
    return df
233
234
235
def create_district_heating_profile_python_like(scenario="eGon2035"):
236
    """Creates profiles for all district heating grids in one scenario.
237
    Similar to create_district_heating_profile but faster and needs more RAM.
238
    The results are directly written into the database.
239
240
    Parameters
241
    ----------
242
    scenario : str
243
        Name of the selected scenario.
244
245
    Returns
246
    -------
247
    None.
248
249
    """
250
251
    start_time = datetime.now()
252
253
    idp_df = db.select_dataframe(
254
        """
255
        SELECT index, idp FROM demand.egon_heat_idp_pool
256
        """,
257
        index_col="index",
258
    )
259
260
    district_heating_grids = db.select_dataframe(
261
        f"""
262
        SELECT area_id
263
        FROM demand.egon_district_heating_areas
264
        WHERE scenario = '{scenario}'
265
        """
266
    )
267
268
    annual_demand = db.select_dataframe(
269
        f"""
270
        SELECT
271
            a.zensus_population_id,
272
            demand / c.count as per_building,
273
            area_id,
274
            demand as demand_total
275
        FROM
276
        demand.egon_peta_heat a
277
        INNER JOIN (
278
            SELECT * FROM demand.egon_map_zensus_district_heating_areas
279
            WHERE scenario = '{scenario}'
280
        ) b ON a.zensus_population_id = b.zensus_population_id
281
282
        JOIN (SELECT COUNT(building_id), zensus_population_id
283
        FROM demand.egon_heat_timeseries_selected_profiles
284
        WHERE zensus_population_id IN(
285
        SELECT zensus_population_id FROM
286
        demand.egon_heat_timeseries_selected_profiles
287
        WHERE zensus_population_id IN (
288
        SELECT zensus_population_id FROM
289
        boundaries.egon_map_zensus_grid_districts
290
       ))
291
        GROUP BY zensus_population_id)c
292
        ON a.zensus_population_id = c.zensus_population_id
293
294
        WHERE a.scenario = '{scenario}'
295
        AND a.sector = 'residential'
296
297
        """,
298
        index_col="zensus_population_id",
299
    )
300
301
    annual_demand = annual_demand[
302
        ~annual_demand.index.duplicated(keep="first")
303
    ]
304
305
    daily_demand_shares = db.select_dataframe(
306
        """
307
        SELECT climate_zone, day_of_year as day, daily_demand_share FROM
308
        demand.egon_daily_heat_demand_per_climate_zone
309
        """
310
    )
311
312
    CTS_demand_dist, CTS_demand_grid, CTS_demand_zensus = CTS_demand_scale(
313
        aggregation_level="district"
314
    )
315
316
    # TODO: use session_scope!
317
    from sqlalchemy.orm import sessionmaker
318
319
    session = sessionmaker(bind=db.engine())()
320
321
    print(datetime.now() - start_time)
322
323
    start_time = datetime.now()
324
    for area in district_heating_grids.area_id.unique():
325
        selected_profiles = db.select_dataframe(
326
            f"""
327
            SELECT a.zensus_population_id, building_id, c.climate_zone,
328
            selected_idp, ordinality as day, b.area_id
329
            FROM demand.egon_heat_timeseries_selected_profiles a
330
            INNER JOIN boundaries.egon_map_zensus_climate_zones c
331
            ON a.zensus_population_id = c.zensus_population_id
332
            INNER JOIN (
333
                SELECT * FROM demand.egon_map_zensus_district_heating_areas
334
                WHERE scenario = '{scenario}'
335
                AND area_id = '{area}'
336
            ) b ON a.zensus_population_id = b.zensus_population_id        ,
337
338
            UNNEST (selected_idp_profiles) WITH ORDINALITY as selected_idp
339
340
            """
341
        )
342
343
        if not selected_profiles.empty:
344
            df = pd.merge(
345
                selected_profiles,
346
                daily_demand_shares,
347
                on=["day", "climate_zone"],
348
            )
349
350
            slice_df = pd.merge(
351
                df[df.area_id == area],
352
                idp_df,
353
                left_on="selected_idp",
354
                right_on="index",
355
            )
356
357
            for hour in range(24):
358
                slice_df[hour] = (
359
                    slice_df.idp.str[hour]
360
                    .mul(slice_df.daily_demand_share)
361
                    .mul(
362
                        annual_demand.loc[
363
                            slice_df.zensus_population_id.values,
364
                            "per_building",
365
                        ].values
366
                    )
367
                )
368
369
            diff = (
370
                slice_df.groupby("day").sum()[range(24)].sum().sum()
371
                - annual_demand[
372
                    annual_demand.area_id == area
373
                ].demand_total.sum()
374
            ) / (
375
                annual_demand[annual_demand.area_id == area].demand_total.sum()
376
            )
377
378
            assert abs(diff) < 0.03, (
379
                "Deviation of residential heat demand time series for"
380
                f" district heating grid {str(area)} is {diff}"
381
            )
382
383
            hh = np.concatenate(
384
                slice_df.groupby("day").sum()[range(24)].values
385
            ).ravel()
386
387
        cts = CTS_demand_dist[
388
            (CTS_demand_dist.scenario == scenario)
389
            & (CTS_demand_dist.index == area)
390
        ].drop("scenario", axis="columns")
391
392
        if (not selected_profiles.empty) and not cts.empty:
393
            entry = EgonTimeseriesDistrictHeating(
394
                area_id=int(area),
395
                scenario=scenario,
396
                dist_aggregated_mw=(hh + cts.values[0]).tolist(),
0 ignored issues
show
introduced by
The variable hh does not seem to be defined for all execution paths.
Loading history...
397
            )
398
        elif (not selected_profiles.empty) and cts.empty:
399
            entry = EgonTimeseriesDistrictHeating(
400
                area_id=int(area),
401
                scenario=scenario,
402
                dist_aggregated_mw=(hh).tolist(),
403
            )
404
        elif not cts.empty:
405
            entry = EgonTimeseriesDistrictHeating(
406
                area_id=int(area),
407
                scenario=scenario,
408
                dist_aggregated_mw=(cts.values[0]).tolist(),
409
            )
410
411
        session.add(entry)
0 ignored issues
show
introduced by
The variable entry does not seem to be defined for all execution paths.
Loading history...
412
    session.commit()
413
414
    print(
415
        f"Time to create time series for district heating scenario {scenario}"
416
    )
417
    print(datetime.now() - start_time)
418
419
420
def create_individual_heat_per_mv_grid(scenario="eGon2035", mv_grid_id=1564):
421
    start_time = datetime.now()
422
    df = db.select_dataframe(
423
        f"""
424
425
        SELECT SUM(building_demand_per_hour) as demand_profile, hour_of_year
426
        FROM
427
428
        (
429
        SELECT demand.demand  *
430
        c.daily_demand_share * hourly_demand as building_demand_per_hour,
431
        ordinality + 24* (c.day_of_year-1) as hour_of_year,
432
        demand_profile.building_id,
433
        c.day_of_year,
434
        ordinality
435
436
        FROM
437
438
        (SELECT zensus_population_id, demand FROM
439
        demand.egon_peta_heat
440
        WHERE scenario = '{scenario}'
441
        AND sector = 'residential'
442
        AND zensus_population_id IN (
443
        SELECT zensus_population_id FROM
444
        boundaries.egon_map_zensus_grid_districts
445
        WHERE bus_id = {mv_grid_id}
446
        )) as demand
447
448
        JOIN boundaries.egon_map_zensus_climate_zones b
449
        ON demand.zensus_population_id = b.zensus_population_id
450
451
        JOIN demand.egon_daily_heat_demand_per_climate_zone c
452
        ON c.climate_zone = b.climate_zone
453
454
        JOIN (
455
        SELECT
456
            e.idp, ordinality as day, zensus_population_id, building_id
457
        FROM demand.egon_heat_timeseries_selected_profiles d,
458
        UNNEST (d.selected_idp_profiles) WITH ORDINALITY as selected_idp
459
        JOIN demand.egon_heat_idp_pool e
460
        ON selected_idp = e.index
461
        WHERE zensus_population_id IN (
462
        SELECT zensus_population_id FROM
463
        boundaries.egon_map_zensus_grid_districts
464
        WHERE bus_id = {mv_grid_id}
465
        ))  demand_profile
466
        ON (demand_profile.day = c.day_of_year AND
467
            demand_profile.zensus_population_id = b.zensus_population_id)
468
469
        JOIN (SELECT COUNT(building_id), zensus_population_id
470
        FROM demand.egon_heat_timeseries_selected_profiles
471
        WHERE zensus_population_id IN(
472
        SELECT zensus_population_id FROM
473
        demand.egon_heat_timeseries_selected_profiles
474
        WHERE zensus_population_id IN (
475
        SELECT zensus_population_id FROM
476
        boundaries.egon_map_zensus_grid_districts
477
        WHERE bus_id = {mv_grid_id}
478
       ))
479
        GROUP BY zensus_population_id) building
480
        ON building.zensus_population_id = b.zensus_population_id,
481
482
        UNNEST(demand_profile.idp) WITH ORDINALITY as hourly_demand
483
        )   result
484
485
486
        GROUP BY hour_of_year
487
488
        """
489
    )
490
491
    print(f"Time to create time series for mv grid {scenario} {mv_grid_id}:")
492
    print(datetime.now() - start_time)
493
494
    return df
495
496
497
def calulate_peak_load(df, scenario):
498
    # peat load in W_th
499
    data = (
500
        df.groupby("building_id")
501
        .max()[range(24)]
502
        .max(axis=1)
503
        .mul(1000000)
504
        .astype(int)
505
        .reset_index()
506
    )
507
508
    data["scenario"] = scenario
509
510
    data.rename({0: "w_th"}, axis="columns", inplace=True)
511
512
    data.to_sql(
513
        EgonIndividualHeatingPeakLoads.__table__.name,
514
        schema=EgonIndividualHeatingPeakLoads.__table__.schema,
515
        con=db.engine(),
516
        if_exists="append",
517
        index=False,
518
    )
519
520
521
def create_individual_heating_peak_loads(scenario="eGon2035"):
522
    engine = db.engine()
523
524
    EgonIndividualHeatingPeakLoads.__table__.drop(bind=engine, checkfirst=True)
525
526
    EgonIndividualHeatingPeakLoads.__table__.create(
527
        bind=engine, checkfirst=True
528
    )
529
530
    start_time = datetime.now()
531
532
    idp_df = db.select_dataframe(
533
        """
534
        SELECT index, idp FROM demand.egon_heat_idp_pool
535
        """,
536
        index_col="index",
537
    )
538
539
    annual_demand = db.select_dataframe(
540
        f"""
541
        SELECT a.zensus_population_id, demand/c.count as per_building, bus_id
542
        FROM demand.egon_peta_heat a
543
544
545
        JOIN (SELECT COUNT(building_id), zensus_population_id
546
        FROM demand.egon_heat_timeseries_selected_profiles
547
        WHERE zensus_population_id IN(
548
        SELECT zensus_population_id FROM
549
        demand.egon_heat_timeseries_selected_profiles
550
        WHERE zensus_population_id IN (
551
        SELECT zensus_population_id FROM
552
        boundaries.egon_map_zensus_grid_districts
553
       ))
554
        GROUP BY zensus_population_id)c
555
        ON a.zensus_population_id = c.zensus_population_id
556
557
        JOIN boundaries.egon_map_zensus_grid_districts d
558
        ON a.zensus_population_id = d.zensus_population_id
559
560
        WHERE a.scenario = '{scenario}'
561
        AND a.sector = 'residential'
562
        AND a.zensus_population_id NOT IN (
563
            SELECT zensus_population_id
564
            FROM demand.egon_map_zensus_district_heating_areas
565
            WHERE scenario = '{scenario}'
566
        )
567
568
        """,
569
        index_col="zensus_population_id",
570
    )
571
572
    daily_demand_shares = db.select_dataframe(
573
        """
574
        SELECT climate_zone, day_of_year as day, daily_demand_share FROM
575
        demand.egon_daily_heat_demand_per_climate_zone
576
        """
577
    )
578
579
    start_time = datetime.now()
580
    for grid in annual_demand.bus_id.unique():
581
        selected_profiles = db.select_dataframe(
582
            f"""
583
            SELECT a.zensus_population_id, building_id, c.climate_zone,
584
            selected_idp, ordinality as day
585
            FROM demand.egon_heat_timeseries_selected_profiles a
586
            INNER JOIN boundaries.egon_map_zensus_climate_zones c
587
            ON a.zensus_population_id = c.zensus_population_id
588
            ,
589
590
            UNNEST (selected_idp_profiles) WITH ORDINALITY as selected_idp
591
592
            WHERE a.zensus_population_id NOT IN (
593
                SELECT zensus_population_id
594
                FROM demand.egon_map_zensus_district_heating_areas
595
                WHERE scenario = '{scenario}'
596
            )
597
            AND a.zensus_population_id IN (
598
                SELECT zensus_population_id
599
                FROM boundaries.egon_map_zensus_grid_districts
600
                WHERE bus_id = '{grid}'
601
            )
602
603
            """
604
        )
605
606
        df = pd.merge(
607
            selected_profiles, daily_demand_shares, on=["day", "climate_zone"]
608
        )
609
610
        slice_df = pd.merge(
611
            df, idp_df, left_on="selected_idp", right_on="index"
612
        )
613
614
        for hour in range(24):
615
            slice_df[hour] = (
616
                slice_df.idp.str[hour]
617
                .mul(slice_df.daily_demand_share)
618
                .mul(
619
                    annual_demand.loc[
620
                        slice_df.zensus_population_id.values, "per_building"
621
                    ].values
622
                )
623
            )
624
625
        calulate_peak_load(slice_df, scenario)
626
627
    print(f"Time to create peak loads per building for {scenario}")
628
    print(datetime.now() - start_time)
629
630
631
def create_individual_heating_profile_python_like(scenario="eGon2035"):
632
    start_time = datetime.now()
633
634
    idp_df = db.select_dataframe(
635
        "SELECT index, idp FROM demand.egon_heat_idp_pool",
636
        index_col="index",
637
    )
638
639
    annual_demand = db.select_dataframe(
640
        f"""
641
        SELECT
642
            a.zensus_population_id,
643
            demand / c.count as per_building,
644
            demand as demand_total,
645
            bus_id
646
        FROM demand.egon_peta_heat a
647
648
649
        JOIN (SELECT COUNT(building_id), zensus_population_id
650
        FROM demand.egon_heat_timeseries_selected_profiles
651
        WHERE zensus_population_id IN(
652
        SELECT zensus_population_id FROM
653
        demand.egon_heat_timeseries_selected_profiles
654
        WHERE zensus_population_id IN (
655
        SELECT zensus_population_id FROM
656
        boundaries.egon_map_zensus_grid_districts
657
       ))
658
        GROUP BY zensus_population_id)c
659
        ON a.zensus_population_id = c.zensus_population_id
660
661
        JOIN boundaries.egon_map_zensus_grid_districts d
662
        ON a.zensus_population_id = d.zensus_population_id
663
664
        WHERE a.scenario = '{scenario}'
665
        AND a.sector = 'residential'
666
        AND a.zensus_population_id NOT IN (
667
            SELECT zensus_population_id
668
            FROM demand.egon_map_zensus_district_heating_areas
669
            WHERE scenario = '{scenario}'
670
        )
671
672
        """,
673
        index_col="zensus_population_id",
674
    )
675
676
    daily_demand_shares = db.select_dataframe(
677
        """
678
        SELECT climate_zone, day_of_year as day, daily_demand_share FROM
679
        demand.egon_daily_heat_demand_per_climate_zone
680
        """
681
    )
682
683
    CTS_demand_dist, CTS_demand_grid, CTS_demand_zensus = CTS_demand_scale(
684
        aggregation_level="district"
685
    )
686
687
    # TODO: use session_scope!
688
    from sqlalchemy.orm import sessionmaker
689
690
    session = sessionmaker(bind=db.engine())()
691
692
    print(
693
        "Time to create overhead for time series for district heating"
694
        f" scenario {scenario}:\n{datetime.now() - start_time}"
695
    )
696
697
    start_time = datetime.now()
698
    for grid in annual_demand.bus_id.unique():
699
        selected_profiles = db.select_dataframe(
700
            f"""
701
            SELECT a.zensus_population_id, building_id, c.climate_zone,
702
            selected_idp, ordinality as day
703
            FROM demand.egon_heat_timeseries_selected_profiles a
704
            INNER JOIN boundaries.egon_map_zensus_climate_zones c
705
            ON a.zensus_population_id = c.zensus_population_id
706
            ,
707
708
            UNNEST (selected_idp_profiles) WITH ORDINALITY as selected_idp
709
710
            WHERE a.zensus_population_id NOT IN (
711
                SELECT zensus_population_id
712
                FROM demand.egon_map_zensus_district_heating_areas
713
                WHERE scenario = '{scenario}'
714
            )
715
            AND a.zensus_population_id IN (
716
                SELECT zensus_population_id
717
                FROM boundaries.egon_map_zensus_grid_districts
718
                WHERE bus_id = '{grid}'
719
            )
720
721
            """
722
        )
723
724
        df = pd.merge(
725
            selected_profiles, daily_demand_shares, on=["day", "climate_zone"]
726
        )
727
728
        slice_df = pd.merge(
729
            df, idp_df, left_on="selected_idp", right_on="index"
730
        )
731
732
        for hour in range(24):
733
            slice_df[hour] = (
734
                slice_df.idp.str[hour]
735
                .mul(slice_df.daily_demand_share)
736
                .mul(
737
                    annual_demand.loc[
738
                        slice_df.zensus_population_id.values, "per_building"
739
                    ].values
740
                )
741
            )
742
743
        cts = CTS_demand_grid[
744
            (CTS_demand_grid.scenario == scenario)
745
            & (CTS_demand_grid.index == grid)
746
        ].drop("scenario", axis="columns")
747
748
        hh = np.concatenate(
749
            slice_df.groupby("day").sum()[range(24)].values
750
        ).ravel()
751
752
        diff = (
753
            slice_df.groupby("day").sum()[range(24)].sum().sum()
754
            - annual_demand[annual_demand.bus_id == grid].demand_total.sum()
755
        ) / (annual_demand[annual_demand.bus_id == grid].demand_total.sum())
756
757
        assert abs(diff) < 0.03, (
758
            "Deviation of residential heat demand time series for mv"
759
            f" grid {grid} is {diff}"
760
        )
761
762
        if not (slice_df[hour].empty or cts.empty):
0 ignored issues
show
introduced by
The variable hour does not seem to be defined for all execution paths.
Loading history...
763
            entry = EgonEtragoTimeseriesIndividualHeating(
764
                bus_id=int(grid),
765
                scenario=scenario,
766
                dist_aggregated_mw=(hh + cts.values[0]).tolist(),
767
            )
768
        elif not slice_df[hour].empty:
769
            entry = EgonEtragoTimeseriesIndividualHeating(
770
                bus_id=int(grid),
771
                scenario=scenario,
772
                dist_aggregated_mw=(hh).tolist(),
773
            )
774
        elif not cts.empty:
775
            entry = EgonEtragoTimeseriesIndividualHeating(
776
                bus_id=int(grid),
777
                scenario=scenario,
778
                dist_aggregated_mw=(cts).tolist(),
779
            )
780
781
        session.add(entry)
0 ignored issues
show
introduced by
The variable entry does not seem to be defined for all execution paths.
Loading history...
782
783
    session.commit()
784
785
    print(
786
        f"Time to create time series for district heating scenario {scenario}"
787
    )
788
    print(datetime.now() - start_time)
789
790
791
def district_heating(method="python"):
792
    engine = db.engine()
793
    EgonTimeseriesDistrictHeating.__table__.drop(bind=engine, checkfirst=True)
794
    EgonTimeseriesDistrictHeating.__table__.create(
795
        bind=engine, checkfirst=True
796
    )
797
798
    if method == "python":
799
        create_district_heating_profile_python_like("eGon2035")
800
        create_district_heating_profile_python_like("eGon100RE")
801
802
    else:
803
        CTS_demand_dist, CTS_demand_grid, CTS_demand_zensus = CTS_demand_scale(
804
            aggregation_level="district"
805
        )
806
807
        ids = db.select_dataframe(
808
            """
809
            SELECT area_id, scenario
810
            FROM demand.egon_district_heating_areas
811
            """
812
        )
813
814
        df = pd.DataFrame(
815
            columns=["area_id", "scenario", "dist_aggregated_mw"]
816
        )
817
818
        for index, row in ids.iterrows():
819
            series = create_district_heating_profile(
820
                scenario=row.scenario, area_id=row.area_id
821
            )
822
823
            cts = (
824
                CTS_demand_dist[
825
                    (CTS_demand_dist.scenario == row.scenario)
826
                    & (CTS_demand_dist.index == row.area_id)
827
                ]
828
                .drop("scenario", axis="columns")
829
                .transpose()
830
            )
831
832
            if not cts.empty:
833
                data = (
834
                    cts[row.area_id] + series.demand_profile
835
                ).values.tolist()
836
            else:
837
                data = series.demand_profile.values.tolist()
838
839
            df = df.append(
840
                pd.Series(
841
                    data={
842
                        "area_id": row.area_id,
843
                        "scenario": row.scenario,
844
                        "dist_aggregated_mw": data,
845
                    },
846
                ),
847
                ignore_index=True,
848
            )
849
850
        df.to_sql(
851
            "egon_timeseries_district_heating",
852
            schema="demand",
853
            con=db.engine(),
854
            if_exists="append",
855
            index=False,
856
        )
857
858
859
def individual_heating_per_mv_grid_tables(method="python"):
860
    engine = db.engine()
861
    EgonEtragoTimeseriesIndividualHeating.__table__.drop(
862
        bind=engine, checkfirst=True
863
    )
864
    EgonEtragoTimeseriesIndividualHeating.__table__.create(
865
        bind=engine, checkfirst=True
866
    )
867
868
869
def individual_heating_per_mv_grid_2035(method="python"):
870
    create_individual_heating_profile_python_like("eGon2035")
871
872
873
def individual_heating_per_mv_grid_100(method="python"):
874
    create_individual_heating_profile_python_like("eGon100RE")
875
876
877
def individual_heating_per_mv_grid(method="python"):
878
    if method == "python":
879
        engine = db.engine()
880
        EgonEtragoTimeseriesIndividualHeating.__table__.drop(
881
            bind=engine, checkfirst=True
882
        )
883
        EgonEtragoTimeseriesIndividualHeating.__table__.create(
884
            bind=engine, checkfirst=True
885
        )
886
887
        create_individual_heating_profile_python_like("eGon2035")
888
        create_individual_heating_profile_python_like("eGon100RE")
889
890
    else:
891
        engine = db.engine()
892
        EgonEtragoTimeseriesIndividualHeating.__table__.drop(
893
            bind=engine, checkfirst=True
894
        )
895
        EgonEtragoTimeseriesIndividualHeating.__table__.create(
896
            bind=engine, checkfirst=True
897
        )
898
899
        CTS_demand_dist, CTS_demand_grid, CTS_demand_zensus = CTS_demand_scale(
900
            aggregation_level="district"
901
        )
902
        df = pd.DataFrame(columns=["bus_id", "scenario", "dist_aggregated_mw"])
903
904
        ids = db.select_dataframe(
905
            """
906
            SELECT bus_id
907
            FROM grid.egon_mv_grid_district
908
            """
909
        )
910
911
        for index, row in ids.iterrows():
912
            for scenario in ["eGon2035", "eGon100RE"]:
913
                series = create_individual_heat_per_mv_grid(
914
                    scenario, row.bus_id
915
                )
916
                cts = (
917
                    CTS_demand_grid[
918
                        (CTS_demand_grid.scenario == scenario)
919
                        & (CTS_demand_grid.index == row.bus_id)
920
                    ]
921
                    .drop("scenario", axis="columns")
922
                    .transpose()
923
                )
924
                if not cts.empty:
925
                    data = (
926
                        cts[row.bus_id] + series.demand_profile
927
                    ).values.tolist()
928
                else:
929
                    data = series.demand_profile.values.tolist()
930
931
                df = df.append(
932
                    pd.Series(
933
                        data={
934
                            "bus_id": row.bus_id,
935
                            "scenario": scenario,
936
                            "dist_aggregated_mw": data,
937
                        },
938
                    ),
939
                    ignore_index=True,
940
                )
941
942
        df.to_sql(
943
            "egon_etrago_timeseries_individual_heating",
944
            schema="demand",
945
            con=db.engine(),
946
            if_exists="append",
947
            index=False,
948
        )
949
950
951
def store_national_profiles():
952
    scenario = "eGon100RE"
953
954
    df = db.select_dataframe(
955
        f"""
956
957
        SELECT SUM(building_demand_per_hour) as "residential rural"
958
        FROM
959
960
        (
961
        SELECT demand.demand  *
962
        c.daily_demand_share * hourly_demand as building_demand_per_hour,
963
        ordinality + 24* (c.day_of_year-1) as hour_of_year,
964
        demand_profile.building_id,
965
        c.day_of_year,
966
        ordinality
967
968
        FROM
969
970
        (SELECT zensus_population_id, demand FROM
971
        demand.egon_peta_heat
972
        WHERE scenario = '{scenario}'
973
        AND sector = 'residential'
974
       ) as demand
975
976
        JOIN boundaries.egon_map_zensus_climate_zones b
977
        ON demand.zensus_population_id = b.zensus_population_id
978
979
        JOIN demand.egon_daily_heat_demand_per_climate_zone c
980
        ON c.climate_zone = b.climate_zone
981
982
        JOIN (
983
        SELECT e.idp, ordinality as day, zensus_population_id, building_id
984
        FROM demand.egon_heat_timeseries_selected_profiles d,
985
        UNNEST (d.selected_idp_profiles) WITH ORDINALITY as selected_idp
986
        JOIN demand.egon_heat_idp_pool e
987
        ON selected_idp = e.index
988
        )  demand_profile
989
        ON (demand_profile.day = c.day_of_year AND
990
            demand_profile.zensus_population_id = b.zensus_population_id)
991
992
        JOIN (SELECT COUNT(building_id), zensus_population_id
993
        FROM demand.egon_heat_timeseries_selected_profiles
994
        WHERE zensus_population_id IN(
995
        SELECT zensus_population_id FROM
996
        demand.egon_heat_timeseries_selected_profiles
997
        )
998
        GROUP BY zensus_population_id) building
999
        ON building.zensus_population_id = b.zensus_population_id,
1000
1001
        UNNEST(demand_profile.idp) WITH ORDINALITY as hourly_demand
1002
        )   result
1003
1004
1005
        GROUP BY hour_of_year
1006
1007
        """
1008
    )
1009
1010
    CTS_demand_dist, CTS_demand_grid, CTS_demand_zensus = CTS_demand_scale(
1011
        aggregation_level="district"
1012
    )
1013
1014
    df["service rural"] = (
1015
        CTS_demand_dist.loc[CTS_demand_dist.scenario == scenario]
1016
        .drop("scenario", axis=1)
1017
        .sum()
1018
    )
1019
1020
    df["urban central"] = db.select_dataframe(
1021
        f"""
1022
        SELECT sum(demand) as "urban central"
1023
1024
        FROM demand.egon_timeseries_district_heating,
1025
        UNNEST (dist_aggregated_mw) WITH ORDINALITY as demand
1026
1027
        WHERE scenario = '{scenario}'
1028
1029
        GROUP BY ordinality
1030
1031
        """
1032
    )
1033
1034
    folder = Path(".") / "input-pypsa-eur-sec"
1035
    # Create the folder, if it does not exists already
1036
    if not os.path.exists(folder):
1037
        os.mkdir(folder)
1038
1039
    df.to_csv(folder / f"heat_demand_timeseries_DE_{scenario}.csv")
1040
1041
1042
def export_etrago_cts_heat_profiles():
1043
    """Export heat cts load profiles at mv substation level
1044
    to etrago-table in the database
1045
1046
    Returns
1047
    -------
1048
    None.
1049
1050
    """
1051
1052
    # Calculate cts heat profiles at substation
1053
    _, CTS_grid, _ = CTS_demand_scale("district")
1054
1055
    # Change format
1056
    data = CTS_grid.drop(columns="scenario")
1057
    df_etrago_cts_heat_profiles = pd.DataFrame(
1058
        index=data.index, columns=["scn_name", "p_set"]
1059
    )
1060
    df_etrago_cts_heat_profiles.p_set = data.values.tolist()
1061
    df_etrago_cts_heat_profiles.scn_name = CTS_grid["scenario"]
1062
    df_etrago_cts_heat_profiles.reset_index(inplace=True)
1063
1064
    # Drop and recreate Table if exists
1065
    EgonEtragoHeatCts.__table__.drop(bind=db.engine(), checkfirst=True)
1066
    EgonEtragoHeatCts.__table__.create(bind=db.engine(), checkfirst=True)
1067
1068
    # Write heat ts into db
1069
    with db.session_scope() as session:
1070
        session.bulk_insert_mappings(
1071
            EgonEtragoHeatCts,
1072
            df_etrago_cts_heat_profiles.to_dict(orient="records"),
1073
        )
1074
1075
1076
def metadata():
1077
    fields = [
1078
        {
1079
            "description": "Index of corresponding district heating area",
1080
            "name": "area_id",
1081
            "type": "integer",
1082
            "unit": "none",
1083
        },
1084
        {
1085
            "description": "Name of scenario",
1086
            "name": "scenario",
1087
            "type": "str",
1088
            "unit": "none",
1089
        },
1090
        {
1091
            "description": "Heat demand time series",
1092
            "name": "dist_aggregated_mw",
1093
            "type": "array of floats",
1094
            "unit": "MW",
1095
        },
1096
    ]
1097
1098
    meta_district = {
1099
        "name": "demand.egon_timeseries_district_heating",
1100
        "title": "eGon heat demand time series for district heating grids",
1101
        "id": "WILL_BE_SET_AT_PUBLICATION",
1102
        "description": "Heat demand time series for district heating grids",
1103
        "language": ["EN"],
1104
        "publicationDate": date.today().isoformat(),
1105
        "context": context(),
1106
        "spatial": {
1107
            "location": None,
1108
            "extent": "Germany",
1109
            "resolution": None,
1110
        },
1111
        "sources": [
1112
            sources()["era5"],
1113
            sources()["vg250"],
1114
            sources()["egon-data"],
1115
            sources()["egon-data_bundle"],
1116
            sources()["peta"],
1117
        ],
1118
        "licenses": [license_egon_data_odbl()],
1119
        "contributors": [
1120
            {
1121
                "title": "Clara Büttner",
1122
                "email": "http://github.com/ClaraBuettner",
1123
                "date": time.strftime("%Y-%m-%d"),
1124
                "object": None,
1125
                "comment": "Imported data",
1126
            },
1127
        ],
1128
        "resources": [
1129
            {
1130
                "profile": "tabular-data-resource",
1131
                "name": "demand.egon_timeseries_district_heating",
1132
                "path": None,
1133
                "format": "PostgreSQL",
1134
                "encoding": "UTF-8",
1135
                "schema": {
1136
                    "fields": fields,
1137
                    "primaryKey": ["index"],
1138
                    "foreignKeys": [],
1139
                },
1140
                "dialect": {"delimiter": None, "decimalSeparator": "."},
1141
            }
1142
        ],
1143
        "metaMetadata": meta_metadata(),
1144
    }
1145
1146
    # Add metadata as a comment to the table
1147
    db.submit_comment(
1148
        "'" + json.dumps(meta_district) + "'",
1149
        EgonTimeseriesDistrictHeating.__table__.schema,
1150
        EgonTimeseriesDistrictHeating.__table__.name,
1151
    )
1152
1153
1154
class HeatTimeSeries(Dataset):
1155
    def __init__(self, dependencies):
1156
        super().__init__(
1157
            name="HeatTimeSeries",
1158
            version="0.0.8",
1159
            dependencies=dependencies,
1160
            tasks=(
1161
                {
1162
                    export_etrago_cts_heat_profiles,
1163
                    map_climate_zones_to_zensus,
1164
                    daily_demand_shares_per_climate_zone,
1165
                    create,
1166
                },
1167
                select,
1168
                district_heating,
1169
                metadata,
1170
                # store_national_profiles,
1171
            ),
1172
        )
1173