Passed
Pull Request — dev (#1089)
by
unknown
07:31 queued 05:55
created

data.datasets.DSM_cts_ind   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 1575
Duplicated Lines 12.83 %

Importance

Changes 0
Metric Value
wmc 51
eloc 806
dl 202
loc 1575
rs 7.714
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A DsmPotential.__init__() 0 6 1

18 Functions

Rating   Name   Duplication   Size   Complexity  
A ind_sites_data_import() 0 21 1
A cts_data_import() 0 40 2
A relate_to_schmidt_sites() 0 25 1
A calc_ind_site_timeseries() 0 71 2
A ind_sites_vent_data_import() 35 35 2
A ind_sites_vent_data_import_individual() 35 35 2
A delete_dsm_entries() 0 65 1
A dsm_cts_ind_processing() 0 4 1
A create_table() 0 11 1
B data_export() 0 110 1
C calculate_potentials() 0 91 9
A div_list() 0 2 1
A ind_osm_data_import_individual() 34 34 2
A ind_osm_data_import() 32 34 2
B dsm_cts_ind() 0 296 1
D create_dsm_components() 0 169 9
B dsm_cts_ind_individual() 0 247 1
C aggregate_components() 0 88 11

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like data.datasets.DSM_cts_ind often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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