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