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