Passed
Pull Request — dev (#1052)
by
unknown
01:38
created

data.datasets.DSM_cts_ind.data_export()   B

Complexity

Conditions 1

Size

Total Lines 110
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 69
dl 0
loc 110
rs 8.0145
c 0
b 0
f 0
cc 1
nop 4

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 sqlalchemy import ARRAY, Column, Float, Integer, String
2
from sqlalchemy.ext.declarative import declarative_base
3
import geopandas as gpd
4
import numpy as np
5
import pandas as pd
6
7
from egon.data import config, db
8
from egon.data.datasets import Dataset
9
from egon.data.datasets.electricity_demand.temporal import calc_load_curve
10
from egon.data.datasets.industry.temporal import identify_bus
11
12
# CONSTANTS
13
# TODO: move to datasets.yml
14
CON = db.engine()
15
16
# CTS
17
CTS_COOL_VENT_AC_SHARE = 0.22
18
19
S_FLEX_CTS = 0.5
20
S_UTIL_CTS = 0.67
21
S_INC_CTS = 1
22
S_DEC_CTS = 0
23
DELTA_T_CTS = 1
24
25
# industry
26
IND_VENT_COOL_SHARE = 0.039
27
IND_VENT_SHARE = 0.017
28
29
# OSM
30
S_FLEX_OSM = 0.5
31
S_UTIL_OSM = 0.73
32
S_INC_OSM = 0.9
33
S_DEC_OSM = 0.5
34
DELTA_T_OSM = 1
35
36
# paper
37
S_FLEX_PAPER = 0.15
38
S_UTIL_PAPER = 0.86
39
S_INC_PAPER = 0.95
40
S_DEC_PAPER = 0
41
DELTA_T_PAPER = 3
42
43
# recycled paper
44
S_FLEX_RECYCLED_PAPER = 0.7
45
S_UTIL_RECYCLED_PAPER = 0.85
46
S_INC_RECYCLED_PAPER = 0.95
47
S_DEC_RECYCLED_PAPER = 0
48
DELTA_T_RECYCLED_PAPER = 3
49
50
# pulp
51
S_FLEX_PULP = 0.7
52
S_UTIL_PULP = 0.83
53
S_INC_PULP = 0.95
54
S_DEC_PULP = 0
55
DELTA_T_PULP = 2
56
57
# cement
58
S_FLEX_CEMENT = 0.61
59
S_UTIL_CEMENT = 0.65
60
S_INC_CEMENT = 0.95
61
S_DEC_CEMENT = 0
62
DELTA_T_CEMENT = 4
63
64
# wz 23
65
WZ = 23
66
67
S_FLEX_WZ = 0.5
68
S_UTIL_WZ = 0.8
69
S_INC_WZ = 1
70
S_DEC_WZ = 0.5
71
DELTA_T_WZ = 1
72
73
Base = declarative_base()
74
75
76
class DsmPotential(Dataset):
77
    def __init__(self, dependencies):
78
        super().__init__(
79
            name="DsmPotential",
80
            version="0.0.4.dev",
81
            dependencies=dependencies,
82
            tasks=(dsm_cts_ind_processing),
83
        )
84
85
86
# Datasets
87 View Code Duplication
class EgonEtragoElectricityCtsDsmTimeseries(Base):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
88
    target = config.datasets()["DSM_CTS_industry"]["targets"][
89
        "cts_loadcurves_dsm"
90
    ]
91
92
    __tablename__ = target["table"]
93
    __table_args__ = {"schema": target["schema"]}
94
95
    bus = Column(Integer, primary_key=True, index=True)
96
    scn_name = Column(String, primary_key=True, index=True)
97
    p_nom = Column(Float)
98
    e_nom = Column(Float)
99
    p_set = Column(ARRAY(Float))
100
    p_max_pu = Column(ARRAY(Float))
101
    p_min_pu = Column(ARRAY(Float))
102
    e_max_pu = Column(ARRAY(Float))
103
    e_min_pu = Column(ARRAY(Float))
104
105
106 View Code Duplication
class EgonOsmIndLoadCurvesIndividualDsmTimeseries(Base):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
107
    target = config.datasets()["DSM_CTS_industry"]["targets"][
108
        "ind_osm_loadcurves_individual_dsm"
109
    ]
110
111
    __tablename__ = target["table"]
112
    __table_args__ = {"schema": target["schema"]}
113
114
    osm_id = Column(Integer, primary_key=True, index=True)
115
    scn_name = Column(String, primary_key=True, index=True)
116
    bus = Column(Integer)
117
    p_nom = Column(Float)
118
    e_nom = Column(Float)
119
    p_set = Column(ARRAY(Float))
120
    p_max_pu = Column(ARRAY(Float))
121
    p_min_pu = Column(ARRAY(Float))
122
    e_max_pu = Column(ARRAY(Float))
123
    e_min_pu = Column(ARRAY(Float))
124
125
126 View Code Duplication
class EgonDemandregioSitesIndElectricityDsmTimeseries(Base):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
127
    target = config.datasets()["DSM_CTS_industry"]["targets"][
128
        "demandregio_ind_sites_dsm"
129
    ]
130
131
    __tablename__ = target["table"]
132
    __table_args__ = {"schema": target["schema"]}
133
134
    industrial_sites_id = Column(Integer, primary_key=True, index=True)
135
    scn_name = Column(String, primary_key=True, index=True)
136
    bus = Column(Integer)
137
    application = Column(String)
138
    p_nom = Column(Float)
139
    e_nom = Column(Float)
140
    p_set = Column(ARRAY(Float))
141
    p_max_pu = Column(ARRAY(Float))
142
    p_min_pu = Column(ARRAY(Float))
143
    e_max_pu = Column(ARRAY(Float))
144
    e_min_pu = Column(ARRAY(Float))
145
146
147 View Code Duplication
class EgonSitesIndLoadCurvesIndividualDsmTimeseries(Base):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
148
    target = config.datasets()["DSM_CTS_industry"]["targets"][
149
        "ind_sites_loadcurves_individual"
150
    ]
151
152
    __tablename__ = target["table"]
153
    __table_args__ = {"schema": target["schema"]}
154
155
    site_id = Column(Integer, primary_key=True, index=True)
156
    scn_name = Column(String, primary_key=True, index=True)
157
    bus = Column(Integer)
158
    p_nom = Column(Float)
159
    e_nom = Column(Float)
160
    p_set = Column(ARRAY(Float))
161
    p_max_pu = Column(ARRAY(Float))
162
    p_min_pu = Column(ARRAY(Float))
163
    e_max_pu = Column(ARRAY(Float))
164
    e_min_pu = Column(ARRAY(Float))
165
166
167
# Code
168
def cts_data_import(cts_cool_vent_ac_share):
169
    """
170
    Import CTS data necessary to identify DSM-potential.
171
172
    ----------
173
    cts_share: float
174
        Share of cooling, ventilation and AC in CTS demand
175
    """
176
177
    # import load data
178
179
    sources = config.datasets()["DSM_CTS_industry"]["sources"][
180
        "cts_loadcurves"
181
    ]
182
183
    ts = db.select_dataframe(
184
        f"""SELECT bus_id, scn_name, p_set FROM
185
        {sources['schema']}.{sources['table']}"""
186
    )
187
188
    # identify relevant columns and prepare df to be returned
189
190
    dsm = pd.DataFrame(index=ts.index)
191
192
    dsm["bus"] = ts["bus_id"].copy()
193
    dsm["scn_name"] = ts["scn_name"].copy()
194
    dsm["p_set"] = ts["p_set"].copy()
195
196
    # calculate share of timeseries for air conditioning, cooling and
197
    # ventilation out of CTS-data
198
199
    timeseries = dsm["p_set"].copy()
200
201
    for index, liste in timeseries.items():
202
        share = [float(item) * cts_cool_vent_ac_share for item in liste]
203
        timeseries.loc[index] = share
204
205
    dsm["p_set"] = timeseries.copy()
206
207
    return dsm
208
209
210 View Code Duplication
def ind_osm_data_import(ind_vent_cool_share):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
211
    """
212
    Import industry data per osm-area necessary to identify DSM-potential.
213
        ----------
214
    ind_share: float
215
        Share of considered application in industry demand
216
    """
217
218
    # import load data
219
220
    sources = config.datasets()["DSM_CTS_industry"]["sources"][
221
        "ind_osm_loadcurves"
222
    ]
223
224
    dsm = db.select_dataframe(
225
        f"""
226
        SELECT bus, scn_name, p_set FROM
227
        {sources['schema']}.{sources['table']}
228
        """
229
    )
230
231
    # calculate share of timeseries for cooling and ventilation out of
232
    # industry-data
233
234
    timeseries = dsm["p_set"].copy()
235
236
    for index, liste in timeseries.items():
237
        share = [float(item) * ind_vent_cool_share for item in liste]
238
239
        timeseries.loc[index] = share
240
241
    dsm["p_set"] = timeseries.copy()
242
243
    return dsm
244
245
246 View Code Duplication
def ind_osm_data_import_individual(ind_vent_cool_share):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
247
    """
248
    Import industry data per osm-area necessary to identify DSM-potential.
249
        ----------
250
    ind_share: float
251
        Share of considered application in industry demand
252
    """
253
254
    # import load data
255
256
    sources = config.datasets()["DSM_CTS_industry"]["sources"][
257
        "ind_osm_loadcurves_individual"
258
    ]
259
260
    dsm = db.select_dataframe(
261
        f"""
262
        SELECT osm_id, bus_id as bus, scn_name, p_set FROM
263
        {sources['schema']}.{sources['table']}
264
        """
265
    )
266
267
    # calculate share of timeseries for cooling and ventilation out of
268
    # industry-data
269
270
    timeseries = dsm["p_set"].copy()
271
272
    for index, liste in timeseries.items():
273
        share = [float(item) * ind_vent_cool_share for item in liste]
274
275
        timeseries.loc[index] = share
276
277
    dsm["p_set"] = timeseries.copy()
278
279
    return dsm
280
281
282 View Code Duplication
def ind_sites_vent_data_import(ind_vent_share, wz):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
283
    """
284
    Import industry sites necessary to identify DSM-potential.
285
        ----------
286
    ind_vent_share: float
287
        Share of considered application in industry demand
288
    wz: int
289
        Wirtschaftszweig to be considered within industry sites
290
    """
291
292
    # import load data
293
294
    sources = config.datasets()["DSM_CTS_industry"]["sources"][
295
        "ind_sites_loadcurves"
296
    ]
297
298
    dsm = db.select_dataframe(
299
        f"""
300
        SELECT bus, scn_name, p_set FROM
301
        {sources['schema']}.{sources['table']}
302
        WHERE wz = {wz}
303
        """
304
    )
305
306
    # calculate share of timeseries for ventilation
307
308
    timeseries = dsm["p_set"].copy()
309
310
    for index, liste in timeseries.items():
311
        share = [float(item) * ind_vent_share for item in liste]
312
        timeseries.loc[index] = share
313
314
    dsm["p_set"] = timeseries.copy()
315
316
    return dsm
317
318
319 View Code Duplication
def ind_sites_vent_data_import_individual(ind_vent_share, wz):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
320
    """
321
    Import industry sites necessary to identify DSM-potential.
322
        ----------
323
    ind_vent_share: float
324
        Share of considered application in industry demand
325
    wz: int
326
        Wirtschaftszweig to be considered within industry sites
327
    """
328
329
    # import load data
330
331
    sources = config.datasets()["DSM_CTS_industry"]["sources"][
332
        "ind_sites_loadcurves_individual"
333
    ]
334
335
    dsm = db.select_dataframe(
336
        f"""
337
        SELECT site_id, bus_id as bus, scn_name, p_set FROM
338
        {sources['schema']}.{sources['table']}
339
        WHERE wz = {wz}
340
        """
341
    )
342
343
    # calculate share of timeseries for ventilation
344
345
    timeseries = dsm["p_set"].copy()
346
347
    for index, liste in timeseries.items():
348
        share = [float(item) * ind_vent_share for item in liste]
349
        timeseries.loc[index] = share
350
351
    dsm["p_set"] = timeseries.copy()
352
353
    return dsm
354
355
356
def calc_ind_site_timeseries(scenario):
357
    # calculate timeseries per site
358
    # -> using code from egon.data.datasets.industry.temporal:
359
    # calc_load_curves_ind_sites
360
361
    # select demands per industrial site including the subsector information
362
    source1 = config.datasets()["DSM_CTS_industry"]["sources"][
363
        "demandregio_ind_sites"
364
    ]
365
366
    demands_ind_sites = db.select_dataframe(
367
        f"""SELECT industrial_sites_id, wz, demand
368
            FROM {source1['schema']}.{source1['table']}
369
            WHERE scenario = '{scenario}'
370
            AND demand > 0
371
            """
372
    ).set_index(["industrial_sites_id"])
373
374
    # select industrial sites as demand_areas from database
375
    source2 = config.datasets()["DSM_CTS_industry"]["sources"]["ind_sites"]
376
377
    demand_area = db.select_geodataframe(
378
        f"""SELECT id, geom, subsector FROM
379
            {source2['schema']}.{source2['table']}""",
380
        index_col="id",
381
        geom_col="geom",
382
        epsg=3035,
383
    )
384
385
    # replace entries to bring it in line with demandregio's subsector
386
    # definitions
387
    demands_ind_sites.replace(1718, 17, inplace=True)
388
    share_wz_sites = demands_ind_sites.copy()
389
390
    # create additional df on wz_share per industrial site, which is always set
391
    # to one as the industrial demand per site is subsector specific
392
    share_wz_sites.demand = 1
393
    share_wz_sites.reset_index(inplace=True)
394
395
    share_transpose = pd.DataFrame(
396
        index=share_wz_sites.industrial_sites_id.unique(),
397
        columns=share_wz_sites.wz.unique(),
398
    )
399
    share_transpose.index.rename("industrial_sites_id", inplace=True)
400
    for wz in share_transpose.columns:
401
        share_transpose[wz] = (
402
            share_wz_sites[share_wz_sites.wz == wz]
403
            .set_index("industrial_sites_id")
404
            .demand
405
        )
406
407
    # calculate load curves
408
    load_curves = calc_load_curve(share_transpose, demands_ind_sites["demand"])
409
410
    # identify bus per industrial site
411
    curves_bus = identify_bus(load_curves, demand_area)
412
    curves_bus.index = curves_bus["id"].astype(int)
413
414
    # initialize dataframe to be returned
415
416
    ts = pd.DataFrame(
417
        data=curves_bus["bus_id"], index=curves_bus["id"].astype(int)
418
    )
419
    ts["scenario_name"] = scenario
420
    curves_bus.drop({"id", "bus_id", "geom"}, axis=1, inplace=True)
421
    ts["p_set"] = curves_bus.values.tolist()
422
423
    # add subsector to relate to Schmidt's tables afterwards
424
    ts["application"] = demand_area["subsector"]
425
426
    return ts
427
428
429
def relate_to_schmidt_sites(dsm):
430
    # import industrial sites by Schmidt
431
432
    source = config.datasets()["DSM_CTS_industry"]["sources"][
433
        "ind_sites_schmidt"
434
    ]
435
436
    schmidt = db.select_dataframe(
437
        f"""SELECT application, geom FROM
438
            {source['schema']}.{source['table']}"""
439
    )
440
441
    # relate calculated timeseries (dsm) to Schmidt's industrial sites
442
443
    applications = np.unique(schmidt["application"])
444
    dsm = pd.DataFrame(dsm[dsm["application"].isin(applications)])
445
446
    # initialize dataframe to be returned
447
448
    dsm.rename(
449
        columns={"scenario_name": "scn_name", "bus_id": "bus"},
450
        inplace=True,
451
    )
452
453
    return dsm
454
455
456
def ind_sites_data_import():
457
    """
458
    Import industry sites data necessary to identify DSM-potential.
459
    """
460
    # calculate timeseries per site
461
462
    # scenario eGon2035
463
    dsm_2035 = calc_ind_site_timeseries("eGon2035")
464
    dsm_2035.reset_index(inplace=True)
465
    # scenario eGon100RE
466
    dsm_100 = calc_ind_site_timeseries("eGon100RE")
467
    dsm_100.reset_index(inplace=True)
468
    # bring df for both scenarios together
469
    dsm_100.index = range(len(dsm_2035), (len(dsm_2035) + len((dsm_100))))
470
    dsm = dsm_2035.append(dsm_100)
471
472
    # relate calculated timeseries to Schmidt's industrial sites
473
474
    dsm = relate_to_schmidt_sites(dsm)
475
476
    return dsm[["application", "id", "bus", "scn_name", "p_set"]]
477
478
479
def calculate_potentials(s_flex, s_util, s_inc, s_dec, delta_t, dsm):
480
    """
481
    Calculate DSM-potential per bus using the methods by Heitkoetter et. al.:
482
        https://doi.org/10.1016/j.adapen.2020.100001
483
    Parameters
484
        ----------
485
    s_flex: float
486
        Feasability factor to account for socio-technical restrictions
487
    s_util: float
488
        Average annual utilisation rate
489
    s_inc: float
490
        Shiftable share of installed capacity up to which load can be
491
        increased considering technical limitations
492
    s_dec: float
493
        Shiftable share of installed capacity up to which load can be
494
        decreased considering technical limitations
495
    delta_t: int
496
        Maximum shift duration in hours
497
    dsm: DataFrame
498
        List of existing buses with DSM-potential including timeseries of
499
        loads
500
    """
501
502
    # copy relevant timeseries
503
    timeseries = dsm["p_set"].copy()
504
505
    # calculate scheduled load L(t)
506
507
    scheduled_load = timeseries.copy()
508
509
    for index, liste in scheduled_load.items():
510
        share = []
511
        for item in liste:
512
            share.append(item * s_flex)
513
        scheduled_load.loc[index] = share
514
515
    # calculate maximum capacity Lambda
516
517
    # calculate energy annual requirement
518
    energy_annual = pd.Series(index=timeseries.index, dtype=float)
519
    for index, liste in timeseries.items():
520
        energy_annual.loc[index] = sum(liste)
521
522
    # calculate Lambda
523
    lam = (energy_annual * s_flex) / (8760 * s_util)
524
525
    # calculation of P_max and P_min
526
527
    # P_max
528
    p_max = scheduled_load.copy()
529
    for index, liste in scheduled_load.items():
530
        lamb = lam.loc[index]
531
        p_max.loc[index] = [lamb * s_inc - item for item in liste]
532
533
    # P_min
534
    p_min = scheduled_load.copy()
535
    for index, liste in scheduled_load.items():
536
        lamb = lam.loc[index]
537
538
        p_min.loc[index] = [-(item - lamb * s_dec) for item in liste]
539
540
    # calculation of E_max and E_min
541
542
    e_max = scheduled_load.copy()
543
    e_min = scheduled_load.copy()
544
545
    for index, liste in scheduled_load.items():
546
        emin = []
547
        emax = []
548
        for i in range(len(liste)):
549
            if i + delta_t > len(liste):
550
                emax.append(
551
                    (sum(liste[i:]) + sum(liste[: delta_t - (len(liste) - i)]))
552
                )
553
            else:
554
                emax.append(sum(liste[i : i + delta_t]))
555
            if i - delta_t < 0:
556
                emin.append(
557
                    (
558
                        -1
559
                        * (
560
                            (
561
                                sum(liste[:i])
562
                                + sum(liste[len(liste) - delta_t + i :])
563
                            )
564
                        )
565
                    )
566
                )
567
            else:
568
                emin.append(-1 * sum(liste[i - delta_t : i]))
569
        e_max.loc[index] = emax
570
        e_min.loc[index] = emin
571
572
    return p_max, p_min, e_max, e_min
573
574
575
def create_dsm_components(con, p_max, p_min, e_max, e_min, dsm):
576
    """
577
    Create components representing DSM.
578
    Parameters
579
        ----------
580
    con :
581
        Connection to database
582
    p_max: DataFrame
583
        Timeseries identifying maximum load increase
584
    p_min: DataFrame
585
        Timeseries identifying maximum load decrease
586
    e_max: DataFrame
587
        Timeseries identifying maximum energy amount to be preponed
588
    e_min: DataFrame
589
        Timeseries identifying maximum energy amount to be postponed
590
    dsm: DataFrame
591
        List of existing buses with DSM-potential including timeseries of loads
592
    """
593
594
    # calculate P_nom and P per unit
595
    p_nom = pd.Series(index=p_max.index, dtype=float)
596
    for index, row in p_max.items():
597
        nom = max(max(row), abs(min(p_min.loc[index])))
598
        p_nom.loc[index] = nom
599
        new = [element / nom for element in row]
600
        p_max.loc[index] = new
601
        new = [element / nom for element in p_min.loc[index]]
602
        p_min.loc[index] = new
603
604
    # calculate E_nom and E per unit
605
    e_nom = pd.Series(index=p_min.index, dtype=float)
606
    for index, row in e_max.items():
607
        nom = max(max(row), abs(min(e_min.loc[index])))
608
        e_nom.loc[index] = nom
609
        new = [element / nom for element in row]
610
        e_max.loc[index] = new
611
        new = [element / nom for element in e_min.loc[index]]
612
        e_min.loc[index] = new
613
614
    # add DSM-buses to "original" buses
615
    dsm_buses = gpd.GeoDataFrame(index=dsm.index)
616
    dsm_buses["original_bus"] = dsm["bus"].copy()
617
    dsm_buses["scn_name"] = dsm["scn_name"].copy()
618
619
    # get original buses and add copy of relevant information
620
    target1 = config.datasets()["DSM_CTS_industry"]["targets"]["bus"]
621
    original_buses = db.select_geodataframe(
622
        f"""SELECT bus_id, v_nom, scn_name, x, y, geom FROM
623
            {target1['schema']}.{target1['table']}""",
624
        geom_col="geom",
625
        epsg=4326,
626
    )
627
628
    # copy relevant information from original buses to DSM-buses
629
    dsm_buses["index"] = dsm_buses.index
630
    originals = original_buses[
631
        original_buses["bus_id"].isin(np.unique(dsm_buses["original_bus"]))
632
    ]
633
    dsm_buses = originals.merge(
634
        dsm_buses,
635
        left_on=["bus_id", "scn_name"],
636
        right_on=["original_bus", "scn_name"],
637
    )
638
    dsm_buses.index = dsm_buses["index"]
639
    dsm_buses.drop(["bus_id", "index"], axis=1, inplace=True)
640
641
    # new bus_ids for DSM-buses
642
    max_id = original_buses["bus_id"].max()
643
    if np.isnan(max_id):
644
        max_id = 0
645
    dsm_id = max_id + 1
646
    bus_id = pd.Series(index=dsm_buses.index, dtype=int)
647
648
    # Get number of DSM buses for both scenarios
649
    rows_per_scenario = (
650
        dsm_buses.groupby("scn_name").count().original_bus.to_dict()
651
    )
652
653
    # Assignment of DSM ids
654
    bus_id.iloc[: rows_per_scenario.get("eGon2035", 0)] = range(
655
        dsm_id, dsm_id + rows_per_scenario.get("eGon2035", 0)
656
    )
657
658
    bus_id.iloc[
659
        rows_per_scenario.get("eGon2035", 0) : rows_per_scenario.get(
660
            "eGon2035", 0
661
        )
662
        + rows_per_scenario.get("eGon100RE", 0)
663
    ] = range(dsm_id, dsm_id + rows_per_scenario.get("eGon100RE", 0))
664
665
    dsm_buses["bus_id"] = bus_id
666
667
    # add links from "orignal" buses to DSM-buses
668
669
    dsm_links = pd.DataFrame(index=dsm_buses.index)
670
    dsm_links["original_bus"] = dsm_buses["original_bus"].copy()
671
    dsm_links["dsm_bus"] = dsm_buses["bus_id"].copy()
672
    dsm_links["scn_name"] = dsm_buses["scn_name"].copy()
673
674
    # set link_id
675
    target2 = config.datasets()["DSM_CTS_industry"]["targets"]["link"]
676
    sql = f"""SELECT link_id FROM {target2['schema']}.{target2['table']}"""
677
    max_id = pd.read_sql_query(sql, con)
678
    max_id = max_id["link_id"].max()
679
    if np.isnan(max_id):
680
        max_id = 0
681
    dsm_id = max_id + 1
682
    link_id = pd.Series(index=dsm_buses.index, dtype=int)
683
684
    # Assignment of link ids
685
    link_id.iloc[: rows_per_scenario.get("eGon2035", 0)] = range(
686
        dsm_id, dsm_id + rows_per_scenario.get("eGon2035", 0)
687
    )
688
689
    link_id.iloc[
690
        rows_per_scenario.get("eGon2035", 0) : rows_per_scenario.get(
691
            "eGon2035", 0
692
        )
693
        + rows_per_scenario.get("eGon100RE", 0)
694
    ] = range(dsm_id, dsm_id + rows_per_scenario.get("eGon100RE", 0))
695
696
    dsm_links["link_id"] = link_id
697
698
    # add calculated timeseries to df to be returned
699
    dsm_links["p_nom"] = p_nom
700
    dsm_links["p_min"] = p_min
701
    dsm_links["p_max"] = p_max
702
703
    # add DSM-stores
704
705
    dsm_stores = pd.DataFrame(index=dsm_buses.index)
706
    dsm_stores["bus"] = dsm_buses["bus_id"].copy()
707
    dsm_stores["scn_name"] = dsm_buses["scn_name"].copy()
708
    dsm_stores["original_bus"] = dsm_buses["original_bus"].copy()
709
710
    # set store_id
711
    target3 = config.datasets()["DSM_CTS_industry"]["targets"]["store"]
712
    sql = f"""SELECT store_id FROM {target3['schema']}.{target3['table']}"""
713
    max_id = pd.read_sql_query(sql, con)
714
    max_id = max_id["store_id"].max()
715
    if np.isnan(max_id):
716
        max_id = 0
717
    dsm_id = max_id + 1
718
    store_id = pd.Series(index=dsm_buses.index, dtype=int)
719
720
    # Assignment of store ids
721
    store_id.iloc[: rows_per_scenario.get("eGon2035", 0)] = range(
722
        dsm_id, dsm_id + rows_per_scenario.get("eGon2035", 0)
723
    )
724
725
    store_id.iloc[
726
        rows_per_scenario.get("eGon2035", 0) : rows_per_scenario.get(
727
            "eGon2035", 0
728
        )
729
        + rows_per_scenario.get("eGon100RE", 0)
730
    ] = range(dsm_id, dsm_id + rows_per_scenario.get("eGon100RE", 0))
731
732
    dsm_stores["store_id"] = store_id
733
734
    # add calculated timeseries to df to be returned
735
    dsm_stores["e_nom"] = e_nom
736
    dsm_stores["e_min"] = e_min
737
    dsm_stores["e_max"] = e_max
738
739
    return dsm_buses, dsm_links, dsm_stores
740
741
742
def aggregate_components(df_dsm_buses, df_dsm_links, df_dsm_stores):
743
    # aggregate buses
744
745
    grouper = [df_dsm_buses.original_bus, df_dsm_buses.scn_name]
746
747
    df_dsm_buses = df_dsm_buses.groupby(grouper).first()
748
749
    df_dsm_buses.reset_index(inplace=True)
750
    df_dsm_buses.sort_values("scn_name", inplace=True)
751
752
    # aggregate links
753
754
    df_dsm_links["p_max"] = df_dsm_links["p_max"].apply(lambda x: np.array(x))
755
    df_dsm_links["p_min"] = df_dsm_links["p_min"].apply(lambda x: np.array(x))
756
757
    grouper = [df_dsm_links.original_bus, df_dsm_links.scn_name]
758
    p_nom = df_dsm_links.groupby(grouper)["p_nom"].sum()
759
    p_max = df_dsm_links.groupby(grouper)["p_max"].apply(np.sum)
760
    p_min = df_dsm_links.groupby(grouper)["p_min"].apply(np.sum)
761
762
    df_dsm_links = df_dsm_links.groupby(grouper).first()
763
    df_dsm_links.p_nom = p_nom
764
    df_dsm_links.p_max = p_max
765
    df_dsm_links.p_min = p_min
766
767
    df_dsm_links["p_max"] = df_dsm_links["p_max"].apply(lambda x: list(x))
768
    df_dsm_links["p_min"] = df_dsm_links["p_min"].apply(lambda x: list(x))
769
770
    df_dsm_links.reset_index(inplace=True)
771
    df_dsm_links.sort_values("scn_name", inplace=True)
772
773
    # aggregate stores
774
775
    df_dsm_stores["e_max"] = df_dsm_stores["e_max"].apply(
776
        lambda x: np.array(x)
777
    )
778
    df_dsm_stores["e_min"] = df_dsm_stores["e_min"].apply(
779
        lambda x: np.array(x)
780
    )
781
782
    grouper = [df_dsm_stores.original_bus, df_dsm_stores.scn_name]
783
    e_nom = df_dsm_stores.groupby(grouper)["e_nom"].sum()
784
    e_max = df_dsm_stores.groupby(grouper)["e_max"].apply(np.sum)
785
    e_min = df_dsm_stores.groupby(grouper)["e_min"].apply(np.sum)
786
787
    df_dsm_stores = df_dsm_stores.groupby(grouper).first()
788
    df_dsm_stores.e_nom = e_nom
789
    df_dsm_stores.e_max = e_max
790
    df_dsm_stores.e_min = e_min
791
792
    df_dsm_stores["e_max"] = df_dsm_stores["e_max"].apply(lambda x: list(x))
793
    df_dsm_stores["e_min"] = df_dsm_stores["e_min"].apply(lambda x: list(x))
794
795
    df_dsm_stores.reset_index(inplace=True)
796
    df_dsm_stores.sort_values("scn_name", inplace=True)
797
798
    # select new bus_ids for aggregated buses and add to links and stores
799
    bus_id = db.next_etrago_id("Bus") + df_dsm_buses.index
800
801
    df_dsm_buses["bus_id"] = bus_id
802
    df_dsm_links["dsm_bus"] = bus_id
803
    df_dsm_stores["bus"] = bus_id
804
805
    # select new link_ids for aggregated links
806
    link_id = db.next_etrago_id("Link") + df_dsm_links.index
807
808
    df_dsm_links["link_id"] = link_id
809
810
    # select new store_ids to aggregated stores
811
812
    store_id = db.next_etrago_id("Store") + df_dsm_stores.index
813
814
    df_dsm_stores["store_id"] = store_id
815
816
    return df_dsm_buses, df_dsm_links, df_dsm_stores
817
818
819
def data_export(dsm_buses, dsm_links, dsm_stores, carrier):
820
    """
821
    Export new components to database.
822
823
    Parameters
824
    ----------
825
    dsm_buses: DataFrame
826
        Buses representing locations of DSM-potential
827
    dsm_links: DataFrame
828
        Links connecting DSM-buses and DSM-stores
829
    dsm_stores: DataFrame
830
        Stores representing DSM-potential
831
    carrier: str
832
        Remark to be filled in column 'carrier' identifying DSM-potential
833
    """
834
835
    targets = config.datasets()["DSM_CTS_industry"]["targets"]
836
837
    # dsm_buses
838
839
    insert_buses = gpd.GeoDataFrame(
840
        index=dsm_buses.index,
841
        data=dsm_buses["geom"],
842
        geometry="geom",
843
        crs=dsm_buses.crs,
844
    )
845
    insert_buses["scn_name"] = dsm_buses["scn_name"]
846
    insert_buses["bus_id"] = dsm_buses["bus_id"]
847
    insert_buses["v_nom"] = dsm_buses["v_nom"]
848
    insert_buses["carrier"] = carrier
849
    insert_buses["x"] = dsm_buses["x"]
850
    insert_buses["y"] = dsm_buses["y"]
851
852
    # insert into database
853
    insert_buses.to_postgis(
854
        targets["bus"]["table"],
855
        con=db.engine(),
856
        schema=targets["bus"]["schema"],
857
        if_exists="append",
858
        index=False,
859
        dtype={"geom": "geometry"},
860
    )
861
862
    # dsm_links
863
864
    insert_links = pd.DataFrame(index=dsm_links.index)
865
    insert_links["scn_name"] = dsm_links["scn_name"]
866
    insert_links["link_id"] = dsm_links["link_id"]
867
    insert_links["bus0"] = dsm_links["original_bus"]
868
    insert_links["bus1"] = dsm_links["dsm_bus"]
869
    insert_links["carrier"] = carrier
870
    insert_links["p_nom"] = dsm_links["p_nom"]
871
872
    # insert into database
873
    insert_links.to_sql(
874
        targets["link"]["table"],
875
        con=db.engine(),
876
        schema=targets["link"]["schema"],
877
        if_exists="append",
878
        index=False,
879
    )
880
881
    insert_links_timeseries = pd.DataFrame(index=dsm_links.index)
882
    insert_links_timeseries["scn_name"] = dsm_links["scn_name"]
883
    insert_links_timeseries["link_id"] = dsm_links["link_id"]
884
    insert_links_timeseries["p_min_pu"] = dsm_links["p_min"]
885
    insert_links_timeseries["p_max_pu"] = dsm_links["p_max"]
886
    insert_links_timeseries["temp_id"] = 1
887
888
    # insert into database
889
    insert_links_timeseries.to_sql(
890
        targets["link_timeseries"]["table"],
891
        con=db.engine(),
892
        schema=targets["link_timeseries"]["schema"],
893
        if_exists="append",
894
        index=False,
895
    )
896
897
    # dsm_stores
898
899
    insert_stores = pd.DataFrame(index=dsm_stores.index)
900
    insert_stores["scn_name"] = dsm_stores["scn_name"]
901
    insert_stores["store_id"] = dsm_stores["store_id"]
902
    insert_stores["bus"] = dsm_stores["bus"]
903
    insert_stores["carrier"] = carrier
904
    insert_stores["e_nom"] = dsm_stores["e_nom"]
905
906
    # insert into database
907
    insert_stores.to_sql(
908
        targets["store"]["table"],
909
        con=db.engine(),
910
        schema=targets["store"]["schema"],
911
        if_exists="append",
912
        index=False,
913
    )
914
915
    insert_stores_timeseries = pd.DataFrame(index=dsm_stores.index)
916
    insert_stores_timeseries["scn_name"] = dsm_stores["scn_name"]
917
    insert_stores_timeseries["store_id"] = dsm_stores["store_id"]
918
    insert_stores_timeseries["e_min_pu"] = dsm_stores["e_min"]
919
    insert_stores_timeseries["e_max_pu"] = dsm_stores["e_max"]
920
    insert_stores_timeseries["temp_id"] = 1
921
922
    # insert into database
923
    insert_stores_timeseries.to_sql(
924
        targets["store_timeseries"]["table"],
925
        con=db.engine(),
926
        schema=targets["store_timeseries"]["schema"],
927
        if_exists="append",
928
        index=False,
929
    )
930
931
932
def delete_dsm_entries(carrier):
933
    """
934
    Deletes DSM-components from database if they already exist before creating
935
    new ones.
936
937
    Parameters
938
        ----------
939
     carrier: str
940
        Remark in column 'carrier' identifying DSM-potential
941
    """
942
943
    targets = config.datasets()["DSM_CTS_industry"]["targets"]
944
945
    # buses
946
947
    sql = f"""DELETE FROM {targets["bus"]["schema"]}.{targets["bus"]["table"]} b
948
     WHERE (b.carrier LIKE '{carrier}');"""
949
    db.execute_sql(sql)
950
951
    # links
952
953
    sql = f"""
954
        DELETE FROM {targets["link_timeseries"]["schema"]}.
955
        {targets["link_timeseries"]["table"]} t
956
        WHERE t.link_id IN
957
        (
958
            SELECT l.link_id FROM {targets["link"]["schema"]}.
959
            {targets["link"]["table"]} l
960
            WHERE l.carrier LIKE '{carrier}'
961
        );
962
        """
963
964
    db.execute_sql(sql)
965
966
    sql = f"""
967
        DELETE FROM {targets["link"]["schema"]}.
968
        {targets["link"]["table"]} l
969
        WHERE (l.carrier LIKE '{carrier}');
970
        """
971
972
    db.execute_sql(sql)
973
974
    # stores
975
976
    sql = f"""
977
        DELETE FROM {targets["store_timeseries"]["schema"]}.
978
        {targets["store_timeseries"]["table"]} t
979
        WHERE t.store_id IN
980
        (
981
            SELECT s.store_id FROM {targets["store"]["schema"]}.
982
            {targets["store"]["table"]} s
983
            WHERE s.carrier LIKE '{carrier}'
984
        );
985
        """
986
987
    db.execute_sql(sql)
988
989
    sql = f"""
990
        DELETE FROM {targets["store"]["schema"]}.{targets["store"]["table"]} s
991
        WHERE (s.carrier LIKE '{carrier}');
992
        """
993
994
    db.execute_sql(sql)
995
996
997
def dsm_cts_ind(
998
    con=db.engine(),
999
    cts_cool_vent_ac_share=0.22,
1000
    ind_vent_cool_share=0.039,
1001
    ind_vent_share=0.017,
1002
):
1003
    """
1004
    Execute methodology to create and implement components for DSM considering
1005
    a) CTS per osm-area: combined potentials of cooling, ventilation and air
1006
      conditioning
1007
    b) Industry per osm-are: combined potentials of cooling and ventilation
1008
    c) Industrial Sites: potentials of ventilation in sites of
1009
      "Wirtschaftszweig" (WZ) 23
1010
    d) Industrial Sites: potentials of sites specified by subsectors
1011
      identified by Schmidt (https://zenodo.org/record/3613767#.YTsGwVtCRhG):
1012
      Paper, Recycled Paper, Pulp, Cement
1013
1014
    Modelled using the methods by Heitkoetter et. al.:
1015
    https://doi.org/10.1016/j.adapen.2020.100001
1016
1017
    Parameters
1018
    ----------
1019
    con :
1020
        Connection to database
1021
    cts_cool_vent_ac_share: float
1022
        Share of cooling, ventilation and AC in CTS demand
1023
    ind_vent_cool_share: float
1024
        Share of cooling and ventilation in industry demand
1025
    ind_vent_share: float
1026
        Share of ventilation in industry demand in sites of WZ 23
1027
1028
    """
1029
1030
    # CTS per osm-area: cooling, ventilation and air conditioning
1031
1032
    print(" ")
1033
    print("CTS per osm-area: cooling, ventilation and air conditioning")
1034
    print(" ")
1035
1036
    dsm = cts_data_import(cts_cool_vent_ac_share)
1037
1038
    # calculate combined potentials of cooling, ventilation and air
1039
    # conditioning in CTS using combined parameters by Heitkoetter et. al.
1040
    p_max, p_min, e_max, e_min = calculate_potentials(
1041
        s_flex=S_FLEX_CTS,
1042
        s_util=S_UTIL_CTS,
1043
        s_inc=S_INC_CTS,
1044
        s_dec=S_DEC_CTS,
1045
        delta_t=DELTA_T_CTS,
1046
        dsm=dsm,
1047
    )
1048
1049
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
1050
        con, p_max, p_min, e_max, e_min, dsm
1051
    )
1052
1053
    df_dsm_buses = dsm_buses.copy()
1054
    df_dsm_links = dsm_links.copy()
1055
    df_dsm_stores = dsm_stores.copy()
1056
1057
    # industry per osm-area: cooling and ventilation
1058
1059
    print(" ")
1060
    print("industry per osm-area: cooling and ventilation")
1061
    print(" ")
1062
1063
    dsm = ind_osm_data_import(ind_vent_cool_share)
1064
1065
    # calculate combined potentials of cooling and ventilation in industrial
1066
    # sector using combined parameters by Heitkoetter et. al.
1067
    p_max, p_min, e_max, e_min = calculate_potentials(
1068
        s_flex=S_FLEX_OSM,
1069
        s_util=S_UTIL_OSM,
1070
        s_inc=S_INC_OSM,
1071
        s_dec=S_DEC_OSM,
1072
        delta_t=DELTA_T_OSM,
1073
        dsm=dsm,
1074
    )
1075
1076
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
1077
        con, p_max, p_min, e_max, e_min, dsm
1078
    )
1079
1080
    df_dsm_buses = gpd.GeoDataFrame(
1081
        pd.concat([df_dsm_buses, dsm_buses], ignore_index=True),
1082
        crs="EPSG:4326",
1083
    )
1084
    df_dsm_links = pd.DataFrame(
1085
        pd.concat([df_dsm_links, dsm_links], ignore_index=True)
1086
    )
1087
    df_dsm_stores = pd.DataFrame(
1088
        pd.concat([df_dsm_stores, dsm_stores], ignore_index=True)
1089
    )
1090
1091
    # industry sites
1092
1093
    # industry sites: different applications
1094
1095
    dsm = ind_sites_data_import()
1096
1097
    print(" ")
1098
    print("industry sites: paper")
1099
    print(" ")
1100
1101
    dsm_paper = gpd.GeoDataFrame(
1102
        dsm[
1103
            dsm["application"].isin(
1104
                [
1105
                    "Graphic Paper",
1106
                    "Packing Paper and Board",
1107
                    "Hygiene Paper",
1108
                    "Technical/Special Paper and Board",
1109
                ]
1110
            )
1111
        ]
1112
    )
1113
1114
    # calculate potentials of industrial sites with paper-applications
1115
    # using parameters by Heitkoetter et al.
1116
    p_max, p_min, e_max, e_min = calculate_potentials(
1117
        s_flex=S_FLEX_PAPER,
1118
        s_util=S_UTIL_PAPER,
1119
        s_inc=S_INC_PAPER,
1120
        s_dec=S_DEC_PAPER,
1121
        delta_t=DELTA_T_PAPER,
1122
        dsm=dsm_paper,
1123
    )
1124
1125
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
1126
        con, p_max, p_min, e_max, e_min, dsm_paper
1127
    )
1128
1129
    df_dsm_buses = gpd.GeoDataFrame(
1130
        pd.concat([df_dsm_buses, dsm_buses], ignore_index=True),
1131
        crs="EPSG:4326",
1132
    )
1133
    df_dsm_links = pd.DataFrame(
1134
        pd.concat([df_dsm_links, dsm_links], ignore_index=True)
1135
    )
1136
    df_dsm_stores = pd.DataFrame(
1137
        pd.concat([df_dsm_stores, dsm_stores], ignore_index=True)
1138
    )
1139
1140
    print(" ")
1141
    print("industry sites: recycled paper")
1142
    print(" ")
1143
1144
    # calculate potentials of industrial sites with recycled paper-applications
1145
    # using parameters by Heitkoetter et. al.
1146
    dsm_recycled_paper = gpd.GeoDataFrame(
1147
        dsm[dsm["application"] == "Recycled Paper"]
1148
    )
1149
1150
    p_max, p_min, e_max, e_min = calculate_potentials(
1151
        s_flex=S_FLEX_RECYCLED_PAPER,
1152
        s_util=S_UTIL_RECYCLED_PAPER,
1153
        s_inc=S_INC_RECYCLED_PAPER,
1154
        s_dec=S_DEC_RECYCLED_PAPER,
1155
        delta_t=DELTA_T_RECYCLED_PAPER,
1156
        dsm=dsm_recycled_paper,
1157
    )
1158
1159
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
1160
        con, p_max, p_min, e_max, e_min, dsm_recycled_paper
1161
    )
1162
1163
    df_dsm_buses = gpd.GeoDataFrame(
1164
        pd.concat([df_dsm_buses, dsm_buses], ignore_index=True),
1165
        crs="EPSG:4326",
1166
    )
1167
    df_dsm_links = pd.DataFrame(
1168
        pd.concat([df_dsm_links, dsm_links], ignore_index=True)
1169
    )
1170
    df_dsm_stores = pd.DataFrame(
1171
        pd.concat([df_dsm_stores, dsm_stores], ignore_index=True)
1172
    )
1173
1174
    print(" ")
1175
    print("industry sites: pulp")
1176
    print(" ")
1177
1178
    dsm_pulp = gpd.GeoDataFrame(dsm[dsm["application"] == "Mechanical Pulp"])
1179
1180
    # calculate potentials of industrial sites with pulp-applications
1181
    # using parameters by Heitkoetter et al.
1182
    p_max, p_min, e_max, e_min = calculate_potentials(
1183
        s_flex=S_FLEX_PULP,
1184
        s_util=S_UTIL_PULP,
1185
        s_inc=S_INC_PULP,
1186
        s_dec=S_DEC_PULP,
1187
        delta_t=DELTA_T_PULP,
1188
        dsm=dsm_pulp,
1189
    )
1190
1191
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
1192
        con, p_max, p_min, e_max, e_min, dsm_pulp
1193
    )
1194
1195
    df_dsm_buses = gpd.GeoDataFrame(
1196
        pd.concat([df_dsm_buses, dsm_buses], ignore_index=True),
1197
        crs="EPSG:4326",
1198
    )
1199
    df_dsm_links = pd.DataFrame(
1200
        pd.concat([df_dsm_links, dsm_links], ignore_index=True)
1201
    )
1202
    df_dsm_stores = pd.DataFrame(
1203
        pd.concat([df_dsm_stores, dsm_stores], ignore_index=True)
1204
    )
1205
1206
    # industry sites: cement
1207
1208
    print(" ")
1209
    print("industry sites: cement")
1210
    print(" ")
1211
1212
    dsm_cement = gpd.GeoDataFrame(dsm[dsm["application"] == "Cement Mill"])
1213
1214
    # calculate potentials of industrial sites with cement-applications
1215
    # using parameters by Heitkoetter et al.
1216
    p_max, p_min, e_max, e_min = calculate_potentials(
1217
        s_flex=S_FLEX_CEMENT,
1218
        s_util=S_UTIL_CEMENT,
1219
        s_inc=S_INC_CEMENT,
1220
        s_dec=S_DEC_CEMENT,
1221
        delta_t=DELTA_T_CEMENT,
1222
        dsm=dsm_cement,
1223
    )
1224
1225
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
1226
        con, p_max, p_min, e_max, e_min, dsm_cement
1227
    )
1228
1229
    df_dsm_buses = gpd.GeoDataFrame(
1230
        pd.concat([df_dsm_buses, dsm_buses], ignore_index=True),
1231
        crs="EPSG:4326",
1232
    )
1233
    df_dsm_links = pd.DataFrame(
1234
        pd.concat([df_dsm_links, dsm_links], ignore_index=True)
1235
    )
1236
    df_dsm_stores = pd.DataFrame(
1237
        pd.concat([df_dsm_stores, dsm_stores], ignore_index=True)
1238
    )
1239
1240
    # industry sites: ventilation in WZ23
1241
1242
    print(" ")
1243
    print("industry sites: ventilation in WZ23")
1244
    print(" ")
1245
1246
    dsm = ind_sites_vent_data_import(ind_vent_share, wz=WZ)
1247
1248
    # drop entries of Cement Mills whose DSM-potentials have already been
1249
    # modelled
1250
    cement = np.unique(dsm_cement["bus"].values)
1251
    index_names = np.array(dsm[dsm["bus"].isin(cement)].index)
1252
    dsm.drop(index_names, inplace=True)
1253
1254
    # calculate potentials of ventialtion in industrial sites of WZ 23
1255
    # using parameters by Heitkoetter et al.
1256
    p_max, p_min, e_max, e_min = calculate_potentials(
1257
        s_flex=S_FLEX_WZ,
1258
        s_util=S_UTIL_WZ,
1259
        s_inc=S_INC_WZ,
1260
        s_dec=S_DEC_WZ,
1261
        delta_t=DELTA_T_WZ,
1262
        dsm=dsm,
1263
    )
1264
1265
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
1266
        con, p_max, p_min, e_max, e_min, dsm
1267
    )
1268
1269
    df_dsm_buses = gpd.GeoDataFrame(
1270
        pd.concat([df_dsm_buses, dsm_buses], ignore_index=True),
1271
        crs="EPSG:4326",
1272
    )
1273
    df_dsm_links = pd.DataFrame(
1274
        pd.concat([df_dsm_links, dsm_links], ignore_index=True)
1275
    )
1276
    df_dsm_stores = pd.DataFrame(
1277
        pd.concat([df_dsm_stores, dsm_stores], ignore_index=True)
1278
    )
1279
1280
    # aggregate DSM components per substation
1281
    dsm_buses, dsm_links, dsm_stores = aggregate_components(
1282
        df_dsm_buses, df_dsm_links, df_dsm_stores
1283
    )
1284
1285
    # export aggregated DSM components to database
1286
1287
    delete_dsm_entries("dsm-cts")
1288
    delete_dsm_entries("dsm-ind-osm")
1289
    delete_dsm_entries("dsm-ind-sites")
1290
    delete_dsm_entries("dsm")
1291
1292
    data_export(dsm_buses, dsm_links, dsm_stores, carrier="dsm")
1293
1294
1295
def get_p_nom_e_nom(df: pd.DataFrame):
1296
    p_nom = [
1297
        max(max(val), max(abs(v) for v in df.p_min_pu.at[idx]))
1298
        for idx, val in df.p_max_pu.items()
1299
    ]
1300
1301
    e_nom = [
1302
        max(max(val), max(abs(v) for v in df.e_min_pu.at[idx]))
1303
        for idx, val in df.e_max_pu.items()
1304
    ]
1305
1306
    return df.assign(p_nom=p_nom, e_nom=e_nom)
1307
1308
1309
def calc_per_unit(df):
1310
    df = get_p_nom_e_nom(df)
1311
1312
    for col in ["p_max_pu", "p_min_pu"]:
1313
        rslt = []
1314
1315
        for idx, lst in df[col].items():
1316
            p_nom = df.p_nom.at[idx]
1317
1318
            rslt.append([v / p_nom for v in lst])
1319
1320
        df[col] = rslt
1321
1322
    for col in ["e_max_pu", "e_min_pu"]:
1323
        rslt = []
1324
1325
        for idx, lst in df[col].items():
1326
            e_nom = df.e_nom.at[idx]
1327
1328
            rslt.append([v / e_nom for v in lst])
1329
1330
        df[col] = rslt
1331
1332
    return df
1333
1334
1335
def create_table(df, table, engine=CON):
1336
    """Create table"""
1337
    table.__table__.drop(bind=engine, checkfirst=True)
1338
    table.__table__.create(bind=engine, checkfirst=True)
1339
1340
    df.to_sql(
1341
        name=table.__table__.name,
1342
        schema=table.__table__.schema,
1343
        con=engine,
1344
        if_exists="append",
1345
        index=False,
1346
    )
1347
1348
1349
def dsm_cts_ind_individual(
1350
    cts_cool_vent_ac_share=CTS_COOL_VENT_AC_SHARE,
1351
    ind_vent_cool_share=IND_VENT_COOL_SHARE,
1352
    ind_vent_share=IND_VENT_SHARE,
1353
):
1354
    """
1355
    Execute methodology to create and implement components for DSM considering
1356
    a) CTS per osm-area: combined potentials of cooling, ventilation and air
1357
      conditioning
1358
    b) Industry per osm-are: combined potentials of cooling and ventilation
1359
    c) Industrial Sites: potentials of ventilation in sites of
1360
      "Wirtschaftszweig" (WZ) 23
1361
    d) Industrial Sites: potentials of sites specified by subsectors
1362
      identified by Schmidt (https://zenodo.org/record/3613767#.YTsGwVtCRhG):
1363
      Paper, Recycled Paper, Pulp, Cement
1364
1365
    Modelled using the methods by Heitkoetter et. al.:
1366
    https://doi.org/10.1016/j.adapen.2020.100001
1367
1368
    Parameters
1369
    ----------
1370
    cts_cool_vent_ac_share: float
1371
        Share of cooling, ventilation and AC in CTS demand
1372
    ind_vent_cool_share: float
1373
        Share of cooling and ventilation in industry demand
1374
    ind_vent_share: float
1375
        Share of ventilation in industry demand in sites of WZ 23
1376
1377
    """
1378
1379
    # CTS per osm-area: cooling, ventilation and air conditioning
1380
1381
    print(" ")
1382
    print("CTS per osm-area: cooling, ventilation and air conditioning")
1383
    print(" ")
1384
1385
    dsm = cts_data_import(cts_cool_vent_ac_share)
1386
1387
    # calculate combined potentials of cooling, ventilation and air
1388
    # conditioning in CTS using combined parameters by Heitkoetter et. al.
1389
    vals = calculate_potentials(
1390
        s_flex=S_FLEX_CTS,
1391
        s_util=S_UTIL_CTS,
1392
        s_inc=S_INC_CTS,
1393
        s_dec=S_DEC_CTS,
1394
        delta_t=DELTA_T_CTS,
1395
        dsm=dsm,
1396
    )
1397
1398
    base_columns = [
1399
        "bus",
1400
        "scn_name",
1401
        "p_set",
1402
        "p_max_pu",
1403
        "p_min_pu",
1404
        "e_max_pu",
1405
        "e_min_pu",
1406
    ]
1407
1408
    cts_df = pd.concat([dsm, *vals], axis=1, ignore_index=True)
1409
    cts_df.columns = base_columns
1410
    cts_df = calc_per_unit(cts_df)
1411
1412
    print(" ")
1413
    print("industry per osm-area: cooling and ventilation")
1414
    print(" ")
1415
1416
    dsm = ind_osm_data_import_individual(ind_vent_cool_share)
1417
1418
    # calculate combined potentials of cooling and ventilation in industrial
1419
    # sector using combined parameters by Heitkoetter et al.
1420
    vals = calculate_potentials(
1421
        s_flex=S_FLEX_OSM,
1422
        s_util=S_UTIL_OSM,
1423
        s_inc=S_INC_OSM,
1424
        s_dec=S_DEC_OSM,
1425
        delta_t=DELTA_T_OSM,
1426
        dsm=dsm,
1427
    )
1428
1429
    columns = ["osm_id"] + base_columns
1430
1431
    osm_df = pd.concat([dsm, *vals], axis=1, ignore_index=True)
1432
    osm_df.columns = columns
1433
    osm_df = calc_per_unit(osm_df)
1434
1435
    # industry sites
1436
1437
    # industry sites: different applications
1438
1439
    dsm = ind_sites_data_import()
1440
1441
    print(" ")
1442
    print("industry sites: paper")
1443
    print(" ")
1444
1445
    dsm_paper = gpd.GeoDataFrame(
1446
        dsm[
1447
            dsm["application"].isin(
1448
                [
1449
                    "Graphic Paper",
1450
                    "Packing Paper and Board",
1451
                    "Hygiene Paper",
1452
                    "Technical/Special Paper and Board",
1453
                ]
1454
            )
1455
        ]
1456
    )
1457
1458
    # calculate potentials of industrial sites with paper-applications
1459
    # using parameters by Heitkoetter et al.
1460
    vals = calculate_potentials(
1461
        s_flex=S_FLEX_PAPER,
1462
        s_util=S_UTIL_PAPER,
1463
        s_inc=S_INC_PAPER,
1464
        s_dec=S_DEC_PAPER,
1465
        delta_t=DELTA_T_PAPER,
1466
        dsm=dsm_paper,
1467
    )
1468
1469
    columns = ["application", "industrial_sites_id"] + base_columns
1470
1471
    paper_df = pd.concat([dsm_paper, *vals], axis=1, ignore_index=True)
1472
    paper_df.columns = columns
1473
    paper_df = calc_per_unit(paper_df)
1474
1475
    print(" ")
1476
    print("industry sites: recycled paper")
1477
    print(" ")
1478
1479
    # calculate potentials of industrial sites with recycled paper-applications
1480
    # using parameters by Heitkoetter et. al.
1481
    dsm_recycled_paper = gpd.GeoDataFrame(
1482
        dsm[dsm["application"] == "Recycled Paper"]
1483
    )
1484
1485
    vals = calculate_potentials(
1486
        s_flex=S_FLEX_RECYCLED_PAPER,
1487
        s_util=S_UTIL_RECYCLED_PAPER,
1488
        s_inc=S_INC_RECYCLED_PAPER,
1489
        s_dec=S_DEC_RECYCLED_PAPER,
1490
        delta_t=DELTA_T_RECYCLED_PAPER,
1491
        dsm=dsm_recycled_paper,
1492
    )
1493
1494
    recycled_paper_df = pd.concat(
1495
        [dsm_recycled_paper, *vals], axis=1, ignore_index=True
1496
    )
1497
    recycled_paper_df.columns = columns
1498
    recycled_paper_df = calc_per_unit(recycled_paper_df)
1499
1500
    print(" ")
1501
    print("industry sites: pulp")
1502
    print(" ")
1503
1504
    dsm_pulp = gpd.GeoDataFrame(dsm[dsm["application"] == "Mechanical Pulp"])
1505
1506
    # calculate potentials of industrial sites with pulp-applications
1507
    # using parameters by Heitkoetter et al.
1508
    vals = calculate_potentials(
1509
        s_flex=S_FLEX_PULP,
1510
        s_util=S_UTIL_PULP,
1511
        s_inc=S_INC_PULP,
1512
        s_dec=S_DEC_PULP,
1513
        delta_t=DELTA_T_PULP,
1514
        dsm=dsm_pulp,
1515
    )
1516
1517
    pulp_df = pd.concat([dsm_pulp, *vals], axis=1, ignore_index=True)
1518
    pulp_df.columns = columns
1519
    pulp_df = calc_per_unit(pulp_df)
1520
1521
    # industry sites: cement
1522
1523
    print(" ")
1524
    print("industry sites: cement")
1525
    print(" ")
1526
1527
    dsm_cement = gpd.GeoDataFrame(dsm[dsm["application"] == "Cement Mill"])
1528
1529
    # calculate potentials of industrial sites with cement-applications
1530
    # using parameters by Heitkoetter et al.
1531
    vals = calculate_potentials(
1532
        s_flex=S_FLEX_CEMENT,
1533
        s_util=S_UTIL_CEMENT,
1534
        s_inc=S_INC_CEMENT,
1535
        s_dec=S_DEC_CEMENT,
1536
        delta_t=DELTA_T_CEMENT,
1537
        dsm=dsm_cement,
1538
    )
1539
1540
    cement_df = pd.concat([dsm_cement, *vals], axis=1, ignore_index=True)
1541
    cement_df.columns = columns
1542
    cement_df = calc_per_unit(cement_df)
1543
1544
    ind_df = pd.concat(
1545
        [paper_df, recycled_paper_df, pulp_df, cement_df], ignore_index=True
1546
    )
1547
1548
    # industry sites: ventilation in WZ23
1549
1550
    print(" ")
1551
    print("industry sites: ventilation in WZ23")
1552
    print(" ")
1553
1554
    dsm = ind_sites_vent_data_import_individual(ind_vent_share, wz=WZ)
1555
1556
    # drop entries of Cement Mills whose DSM-potentials have already been
1557
    # modelled
1558
    cement = np.unique(dsm_cement["bus"].values)
1559
    index_names = np.array(dsm[dsm["bus"].isin(cement)].index)
1560
    dsm.drop(index_names, inplace=True)
1561
1562
    # calculate potentials of ventialtion in industrial sites of WZ 23
1563
    # using parameters by Heitkoetter et al.
1564
    vals = calculate_potentials(
1565
        s_flex=S_FLEX_WZ,
1566
        s_util=S_UTIL_WZ,
1567
        s_inc=S_INC_WZ,
1568
        s_dec=S_DEC_WZ,
1569
        delta_t=DELTA_T_WZ,
1570
        dsm=dsm,
1571
    )
1572
1573
    columns = ["site_id"] + base_columns
1574
1575
    ind_sites_df = pd.concat([dsm, *vals], axis=1, ignore_index=True)
1576
    ind_sites_df.columns = columns
1577
    ind_sites_df = calc_per_unit(ind_sites_df)
1578
1579
    # create tables
1580
    create_table(
1581
        df=cts_df, table=EgonEtragoElectricityCtsDsmTimeseries, engine=CON
1582
    )
1583
    create_table(
1584
        df=osm_df,
1585
        table=EgonOsmIndLoadCurvesIndividualDsmTimeseries,
1586
        engine=CON,
1587
    )
1588
    create_table(
1589
        df=ind_df,
1590
        table=EgonDemandregioSitesIndElectricityDsmTimeseries,
1591
        engine=CON,
1592
    )
1593
    create_table(
1594
        df=ind_sites_df,
1595
        table=EgonSitesIndLoadCurvesIndividualDsmTimeseries,
1596
        engine=CON,
1597
    )
1598
1599
1600
def dsm_cts_ind_processing():
1601
    dsm_cts_ind()
1602
1603
    dsm_cts_ind_individual()
1604