Passed
Pull Request — dev (#1052)
by
unknown
02:51 queued 01:09
created

data.datasets.DSM_cts_ind.DsmPotential.__init__()   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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