Passed
Pull Request — dev (#1052)
by
unknown
24:07
created

data.datasets.DSM_cts_ind.dsm_cts_ind_processing()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
cc 1
nop 0
1
import geopandas as gpd
2
import numpy as np
3
import pandas as pd
4
5
from egon.data import config, db
6
from egon.data.datasets import Dataset
7
from egon.data.datasets.electricity_demand.temporal import calc_load_curve
8
from egon.data.datasets.industry.temporal import identify_bus
9
10
11
class dsm_Potential(Dataset):
12
    def __init__(self, dependencies):
13
        super().__init__(
14
            name="DSM_potentials",
15
            version="0.0.4.dev",
16
            dependencies=dependencies,
17
            tasks=(dsm_cts_ind_processing),
18
        )
19
20
21
def cts_data_import(cts_cool_vent_ac_share):
22
23
    """
24
    Import CTS data necessary to identify DSM-potential.
25
        ----------
26
    cts_share: float
27
        Share of cooling, ventilation and AC in CTS demand
28
    """
29
30
    # import load data
31
32
    sources = config.datasets()["DSM_CTS_industry"]["sources"][
33
        "cts_loadcurves"
34
    ]
35
36
    ts = db.select_dataframe(
37
        f"""SELECT bus_id, scn_name, p_set FROM
38
        {sources['schema']}.{sources['table']}"""
39
    )
40
41
    # identify relevant columns and prepare df to be returned
42
43
    dsm = pd.DataFrame(index=ts.index)
44
45
    dsm["bus"] = ts["bus_id"].copy()
46
    dsm["scn_name"] = ts["scn_name"].copy()
47
    dsm["p_set"] = ts["p_set"].copy()
48
49
    # calculate share of timeseries for air conditioning, cooling and
50
    # ventilation out of CTS-data
51
52
    timeseries = dsm["p_set"].copy()
53
54
    for index, liste in timeseries.iteritems():
55
        share = [float(item) * cts_cool_vent_ac_share for item in liste]
56
        timeseries.loc[index] = share
57
58
    dsm["p_set"] = timeseries.copy()
59
60
    return dsm
61
62
63
def ind_osm_data_import(ind_vent_cool_share):
64
65
    """
66
    Import industry data per osm-area necessary to identify DSM-potential.
67
        ----------
68
    ind_share: float
69
        Share of considered application in industry demand
70
    """
71
72
    # import load data
73
74
    sources = config.datasets()["DSM_CTS_industry"]["sources"][
75
        "ind_osm_loadcurves"
76
    ]
77
78
    dsm = db.select_dataframe(
79
        f"""SELECT bus, scn_name, p_set FROM
80
        {sources['schema']}.{sources['table']}"""
81
    )
82
83
    # calculate share of timeseries for cooling and ventilation out of
84
    # industry-data
85
86
    timeseries = dsm["p_set"].copy()
87
88
    for index, liste in timeseries.iteritems():
89
        share = [float(item) * ind_vent_cool_share for item in liste]
90
91
        timeseries.loc[index] = share
92
93
    dsm["p_set"] = timeseries.copy()
94
95
    return dsm
96
97
98
def ind_sites_vent_data_import(ind_vent_share, wz):
99
100
    """
101
    Import industry sites necessary to identify DSM-potential.
102
        ----------
103
    ind_vent_share: float
104
        Share of considered application in industry demand
105
    wz: int
106
        Wirtschaftszweig to be considered within industry sites
107
    """
108
109
    # import load data
110
111
    sources = config.datasets()["DSM_CTS_industry"]["sources"][
112
        "ind_sites_loadcurves"
113
    ]
114
115
    dsm = db.select_dataframe(
116
        f"""SELECT bus, scn_name, p_set, wz FROM
117
        {sources['schema']}.{sources['table']}"""
118
    )
119
120
    # select load for considered applications
121
122
    dsm = dsm[dsm["wz"] == wz]
123
124
    # calculate share of timeseries for ventilation
125
126
    timeseries = dsm["p_set"].copy()
127
128
    for index, liste in timeseries.iteritems():
129
        share = [float(item) * ind_vent_share for item in liste]
130
        timeseries.loc[index] = share
131
132
    dsm["p_set"] = timeseries.copy()
133
134
    return dsm
135
136
137
def calc_ind_site_timeseries(scenario):
138
139
    # calculate timeseries per site
140
    # -> using code from egon.data.datasets.industry.temporal:
141
    # calc_load_curves_ind_sites
142
143
    # select demands per industrial site including the subsector information
144
    source1 = config.datasets()["DSM_CTS_industry"]["sources"][
145
        "demandregio_ind_sites"
146
    ]
147
148
    demands_ind_sites = db.select_dataframe(
149
        f"""SELECT industrial_sites_id, wz, demand
150
            FROM {source1['schema']}.{source1['table']}
151
            WHERE scenario = '{scenario}'
152
            AND demand > 0
153
            """
154
    ).set_index(["industrial_sites_id"])
155
156
    # select industrial sites as demand_areas from database
157
    source2 = config.datasets()["DSM_CTS_industry"]["sources"]["ind_sites"]
158
159
    demand_area = db.select_geodataframe(
160
        f"""SELECT id, geom, subsector FROM
161
            {source2['schema']}.{source2['table']}""",
162
        index_col="id",
163
        geom_col="geom",
164
        epsg=3035,
165
    )
166
167
    # replace entries to bring it in line with demandregio's subsector
168
    # definitions
169
    demands_ind_sites.replace(1718, 17, inplace=True)
170
    share_wz_sites = demands_ind_sites.copy()
171
172
    # create additional df on wz_share per industrial site, which is always set
173
    # to one as the industrial demand per site is subsector specific
174
    share_wz_sites.demand = 1
175
    share_wz_sites.reset_index(inplace=True)
176
177
    share_transpose = pd.DataFrame(
178
        index=share_wz_sites.industrial_sites_id.unique(),
179
        columns=share_wz_sites.wz.unique(),
180
    )
181
    share_transpose.index.rename("industrial_sites_id", inplace=True)
182
    for wz in share_transpose.columns:
183
        share_transpose[wz] = (
184
            share_wz_sites[share_wz_sites.wz == wz]
185
            .set_index("industrial_sites_id")
186
            .demand
187
        )
188
189
    # calculate load curves
190
    load_curves = calc_load_curve(share_transpose, demands_ind_sites["demand"])
191
192
    # identify bus per industrial site
193
    curves_bus = identify_bus(load_curves, demand_area)
194
    curves_bus.index = curves_bus["id"].astype(int)
195
196
    # initialize dataframe to be returned
197
198
    ts = pd.DataFrame(
199
        data=curves_bus["bus_id"], index=curves_bus["id"].astype(int)
200
    )
201
    ts["scenario_name"] = scenario
202
    curves_bus.drop({"id", "bus_id", "geom"}, axis=1, inplace=True)
203
    ts["p_set"] = curves_bus.values.tolist()
204
205
    # add subsector to relate to Schmidt's tables afterwards
206
    ts["application"] = demand_area["subsector"]
207
208
    return ts
209
210
211
def relate_to_schmidt_sites(dsm):
212
213
    # import industrial sites by Schmidt
214
215
    source = config.datasets()["DSM_CTS_industry"]["sources"][
216
        "ind_sites_schmidt"
217
    ]
218
219
    schmidt = db.select_dataframe(
220
        f"""SELECT application, geom FROM
221
            {source['schema']}.{source['table']}"""
222
    )
223
224
    # relate calculated timeseries (dsm) to Schmidt's industrial sites
225
226
    applications = np.unique(schmidt["application"])
227
    dsm = pd.DataFrame(dsm[dsm["application"].isin(applications)])
228
229
    # initialize dataframe to be returned
230
231
    dsm.rename(
232
        columns={"scenario_name": "scn_name", "bus_id": "bus"},
233
        inplace=True,
234
    )
235
236
    return dsm
237
238
239
def ind_sites_data_import():
240
    """
241
    Import industry sites data necessary to identify DSM-potential.
242
    """
243
    # calculate timeseries per site
244
245
    # scenario eGon2035
246
    dsm_2035 = calc_ind_site_timeseries("eGon2035")
247
    dsm_2035.reset_index(inplace=True)
248
    # scenario eGon100RE
249
    dsm_100 = calc_ind_site_timeseries("eGon100RE")
250
    dsm_100.reset_index(inplace=True)
251
    # bring df for both scenarios together
252
    dsm_100.index = range(len(dsm_2035), (len(dsm_2035) + len((dsm_100))))
253
    dsm = dsm_2035.append(dsm_100)
254
255
    # relate calculated timeseries to Schmidt's industrial sites
256
257
    dsm = relate_to_schmidt_sites(dsm)
258
259
    return dsm
260
261
262
def calculate_potentials(s_flex, s_util, s_inc, s_dec, delta_t, dsm):
263
264
    """
265
    Calculate DSM-potential per bus using the methods by Heitkoetter et. al.:
266
        https://doi.org/10.1016/j.adapen.2020.100001
267
    Parameters
268
        ----------
269
    s_flex: float
270
        Feasability factor to account for socio-technical restrictions
271
    s_util: float
272
        Average annual utilisation rate
273
    s_inc: float
274
        Shiftable share of installed capacity up to which load can be
275
        increased considering technical limitations
276
    s_dec: float
277
        Shiftable share of installed capacity up to which load can be
278
        decreased considering technical limitations
279
    delta_t: int
280
        Maximum shift duration in hours
281
    dsm: DataFrame
282
        List of existing buses with DSM-potential including timeseries of
283
        loads
284
    """
285
286
    # copy relevant timeseries
287
    timeseries = dsm["p_set"].copy()
288
289
    # calculate scheduled load L(t)
290
291
    scheduled_load = timeseries.copy()
292
293
    for index, liste in scheduled_load.iteritems():
294
        share = []
295
        for item in liste:
296
            share.append(item * s_flex)
297
        scheduled_load.loc[index] = share
298
299
    # calculate maximum capacity Lambda
300
301
    # calculate energy annual requirement
302
    energy_annual = pd.Series(index=timeseries.index, dtype=float)
303
    for index, liste in timeseries.iteritems():
304
        energy_annual.loc[index] = sum(liste)
305
306
    # calculate Lambda
307
    lam = (energy_annual * s_flex) / (8760 * s_util)
308
309
    # calculation of P_max and P_min
310
311
    # P_max
312
    p_max = scheduled_load.copy()
313
    for index, liste in scheduled_load.iteritems():
314
        lamb = lam.loc[index]
315
        p = []
316
        for item in liste:
317
            value = lamb * s_inc - item
318
            if value < 0:
319
                value = 0
320
            p.append(value)
321
        p_max.loc[index] = p
322
323
    # P_min
324
    p_min = scheduled_load.copy()
325
    for index, liste in scheduled_load.iteritems():
326
        lamb = lam.loc[index]
327
        p = []
328
        for item in liste:
329
            value = -(item - lamb * s_dec)
330
            if value > 0:
331
                value = 0
332
            p.append(value)
333
        p_min.loc[index] = p
334
335
    # calculation of E_max and E_min
336
337
    e_max = scheduled_load.copy()
338
    e_min = scheduled_load.copy()
339
340
    for index, liste in scheduled_load.iteritems():
341
        emin = []
342
        emax = []
343
        for i in range(len(liste)):
344
            if i + delta_t > len(liste):
345
                emax.append(
346
                    (sum(liste[i:]) + sum(liste[: delta_t - (len(liste) - i)]))
347
                )
348
            else:
349
                emax.append(sum(liste[i : i + delta_t]))
350
            if i - delta_t < 0:
351
                emin.append(
352
                    (
353
                        -1
354
                        * (
355
                            (
356
                                sum(liste[:i])
357
                                + sum(liste[len(liste) - delta_t + i :])
358
                            )
359
                        )
360
                    )
361
                )
362
            else:
363
                emin.append(-1 * sum(liste[i - delta_t : i]))
364
        e_max.loc[index] = emax
365
        e_min.loc[index] = emin
366
367
    return p_max, p_min, e_max, e_min
368
369
370
def create_dsm_components(con, p_max, p_min, e_max, e_min, dsm):
371
372
    """
373
    Create components representing DSM.
374
    Parameters
375
        ----------
376
    con :
377
        Connection to database
378
    p_max: DataFrame
379
        Timeseries identifying maximum load increase
380
    p_min: DataFrame
381
        Timeseries identifying maximum load decrease
382
    e_max: DataFrame
383
        Timeseries identifying maximum energy amount to be preponed
384
    e_min: DataFrame
385
        Timeseries identifying maximum energy amount to be postponed
386
    dsm: DataFrame
387
        List of existing buses with DSM-potential including timeseries of loads
388
    """
389
390
    # calculate P_nom and P per unit
391
    p_nom = pd.Series(index=p_max.index, dtype=float)
392
    for index, row in p_max.iteritems():
393
        nom = max(max(row), abs(min(p_min.loc[index])))
394
        p_nom.loc[index] = nom
395
        new = [element / nom for element in row]
396
        p_max.loc[index] = new
397
        new = [element / nom for element in p_min.loc[index]]
398
        p_min.loc[index] = new
399
400
    # calculate E_nom and E per unit
401
    e_nom = pd.Series(index=p_min.index, dtype=float)
402
    for index, row in e_max.iteritems():
403
        nom = max(max(row), abs(min(e_min.loc[index])))
404
        e_nom.loc[index] = nom
405
        new = [element / nom for element in row]
406
        e_max.loc[index] = new
407
        new = [element / nom for element in e_min.loc[index]]
408
        e_min.loc[index] = new
409
410
    # add DSM-buses to "original" buses
411
    dsm_buses = gpd.GeoDataFrame(index=dsm.index)
412
    dsm_buses["original_bus"] = dsm["bus"].copy()
413
    dsm_buses["scn_name"] = dsm["scn_name"].copy()
414
415
    # get original buses and add copy of relevant information
416
    target1 = config.datasets()["DSM_CTS_industry"]["targets"]["bus"]
417
    original_buses = db.select_geodataframe(
418
        f"""SELECT bus_id, v_nom, scn_name, x, y, geom FROM
419
            {target1['schema']}.{target1['table']}""",
420
        geom_col="geom",
421
        epsg=4326,
422
    )
423
424
    # copy relevant information from original buses to DSM-buses
425
    dsm_buses["index"] = dsm_buses.index
426
    originals = original_buses[
427
        original_buses["bus_id"].isin(np.unique(dsm_buses["original_bus"]))
428
    ]
429
    dsm_buses = originals.merge(
430
        dsm_buses,
431
        left_on=["bus_id", "scn_name"],
432
        right_on=["original_bus", "scn_name"],
433
    )
434
    dsm_buses.index = dsm_buses["index"]
435
    dsm_buses.drop(["bus_id", "index"], axis=1, inplace=True)
436
437
    # new bus_ids for DSM-buses
438
    max_id = original_buses["bus_id"].max()
439
    if np.isnan(max_id):
440
        max_id = 0
441
    dsm_id = max_id + 1
442
    bus_id = pd.Series(index=dsm_buses.index, dtype=int)
443
444
    # Get number of DSM buses for both scenarios
445
    rows_per_scenario = (
446
        dsm_buses.groupby("scn_name").count().original_bus.to_dict()
447
    )
448
449
    # Assignment of DSM ids
450
    bus_id.iloc[: rows_per_scenario.get("eGon2035", 0)] = range(
451
        dsm_id, dsm_id + rows_per_scenario.get("eGon2035", 0)
452
    )
453
454
    bus_id.iloc[
455
        rows_per_scenario.get("eGon2035", 0) : rows_per_scenario.get(
456
            "eGon2035", 0
457
        )
458
        + rows_per_scenario.get("eGon100RE", 0)
459
    ] = range(dsm_id, dsm_id + rows_per_scenario.get("eGon100RE", 0))
460
461
    dsm_buses["bus_id"] = bus_id
462
463
    # add links from "orignal" buses to DSM-buses
464
465
    dsm_links = pd.DataFrame(index=dsm_buses.index)
466
    dsm_links["original_bus"] = dsm_buses["original_bus"].copy()
467
    dsm_links["dsm_bus"] = dsm_buses["bus_id"].copy()
468
    dsm_links["scn_name"] = dsm_buses["scn_name"].copy()
469
470
    # set link_id
471
    target2 = config.datasets()["DSM_CTS_industry"]["targets"]["link"]
472
    sql = f"""SELECT link_id FROM {target2['schema']}.{target2['table']}"""
473
    max_id = pd.read_sql_query(sql, con)
474
    max_id = max_id["link_id"].max()
475
    if np.isnan(max_id):
476
        max_id = 0
477
    dsm_id = max_id + 1
478
    link_id = pd.Series(index=dsm_buses.index, dtype=int)
479
480
    # Assignment of link ids
481
    link_id.iloc[: rows_per_scenario.get("eGon2035", 0)] = range(
482
        dsm_id, dsm_id + rows_per_scenario.get("eGon2035", 0)
483
    )
484
485
    link_id.iloc[
486
        rows_per_scenario.get("eGon2035", 0) : rows_per_scenario.get(
487
            "eGon2035", 0
488
        )
489
        + rows_per_scenario.get("eGon100RE", 0)
490
    ] = range(dsm_id, dsm_id + rows_per_scenario.get("eGon100RE", 0))
491
492
    dsm_links["link_id"] = link_id
493
494
    # add calculated timeseries to df to be returned
495
    dsm_links["p_nom"] = p_nom
496
    dsm_links["p_min"] = p_min
497
    dsm_links["p_max"] = p_max
498
499
    # add DSM-stores
500
501
    dsm_stores = pd.DataFrame(index=dsm_buses.index)
502
    dsm_stores["bus"] = dsm_buses["bus_id"].copy()
503
    dsm_stores["scn_name"] = dsm_buses["scn_name"].copy()
504
    dsm_stores["original_bus"] = dsm_buses["original_bus"].copy()
505
506
    # set store_id
507
    target3 = config.datasets()["DSM_CTS_industry"]["targets"]["store"]
508
    sql = f"""SELECT store_id FROM {target3['schema']}.{target3['table']}"""
509
    max_id = pd.read_sql_query(sql, con)
510
    max_id = max_id["store_id"].max()
511
    if np.isnan(max_id):
512
        max_id = 0
513
    dsm_id = max_id + 1
514
    store_id = pd.Series(index=dsm_buses.index, dtype=int)
515
516
    # Assignment of store ids
517
    store_id.iloc[: rows_per_scenario.get("eGon2035", 0)] = range(
518
        dsm_id, dsm_id + rows_per_scenario.get("eGon2035", 0)
519
    )
520
521
    store_id.iloc[
522
        rows_per_scenario.get("eGon2035", 0) : rows_per_scenario.get(
523
            "eGon2035", 0
524
        )
525
        + rows_per_scenario.get("eGon100RE", 0)
526
    ] = range(dsm_id, dsm_id + rows_per_scenario.get("eGon100RE", 0))
527
528
    dsm_stores["store_id"] = store_id
529
530
    # add calculated timeseries to df to be returned
531
    dsm_stores["e_nom"] = e_nom
532
    dsm_stores["e_min"] = e_min
533
    dsm_stores["e_max"] = e_max
534
535
    return dsm_buses, dsm_links, dsm_stores
536
537
538
def aggregate_components(df_dsm_buses, df_dsm_links, df_dsm_stores):
539
540
    # aggregate buses
541
542
    grouper = [df_dsm_buses.original_bus, df_dsm_buses.scn_name]
543
544
    df_dsm_buses = df_dsm_buses.groupby(grouper).first()
545
546
    df_dsm_buses.reset_index(inplace=True)
547
    df_dsm_buses.sort_values("scn_name", inplace=True)
548
549
    # aggregate links
550
551
    df_dsm_links["p_max"] = df_dsm_links["p_max"].apply(lambda x: np.array(x))
552
    df_dsm_links["p_min"] = df_dsm_links["p_min"].apply(lambda x: np.array(x))
553
554
    grouper = [df_dsm_links.original_bus, df_dsm_links.scn_name]
555
    p_nom = df_dsm_links.groupby(grouper)["p_nom"].sum()
556
    p_max = df_dsm_links.groupby(grouper)["p_max"].apply(np.sum)
557
    p_min = df_dsm_links.groupby(grouper)["p_min"].apply(np.sum)
558
559
    df_dsm_links = df_dsm_links.groupby(grouper).first()
560
    df_dsm_links.p_nom = p_nom
561
    df_dsm_links.p_max = p_max
562
    df_dsm_links.p_min = p_min
563
564
    df_dsm_links["p_max"] = df_dsm_links["p_max"].apply(lambda x: list(x))
565
    df_dsm_links["p_min"] = df_dsm_links["p_min"].apply(lambda x: list(x))
566
567
    df_dsm_links.reset_index(inplace=True)
568
    df_dsm_links.sort_values("scn_name", inplace=True)
569
570
    # aggregate stores
571
572
    df_dsm_stores["e_max"] = df_dsm_stores["e_max"].apply(
573
        lambda x: np.array(x)
574
    )
575
    df_dsm_stores["e_min"] = df_dsm_stores["e_min"].apply(
576
        lambda x: np.array(x)
577
    )
578
579
    grouper = [df_dsm_stores.original_bus, df_dsm_stores.scn_name]
580
    e_nom = df_dsm_stores.groupby(grouper)["e_nom"].sum()
581
    e_max = df_dsm_stores.groupby(grouper)["e_max"].apply(np.sum)
582
    e_min = df_dsm_stores.groupby(grouper)["e_min"].apply(np.sum)
583
584
    df_dsm_stores = df_dsm_stores.groupby(grouper).first()
585
    df_dsm_stores.e_nom = e_nom
586
    df_dsm_stores.e_max = e_max
587
    df_dsm_stores.e_min = e_min
588
589
    df_dsm_stores["e_max"] = df_dsm_stores["e_max"].apply(lambda x: list(x))
590
    df_dsm_stores["e_min"] = df_dsm_stores["e_min"].apply(lambda x: list(x))
591
592
    df_dsm_stores.reset_index(inplace=True)
593
    df_dsm_stores.sort_values("scn_name", inplace=True)
594
595
    # select new bus_ids for aggregated buses and add to links and stores
596
    bus_id = db.next_etrago_id("Bus") + df_dsm_buses.index
597
598
    df_dsm_buses["bus_id"] = bus_id
599
    df_dsm_links["dsm_bus"] = bus_id
600
    df_dsm_stores["bus"] = bus_id
601
602
    # select new link_ids for aggregated links
603
    link_id = db.next_etrago_id("Link") + df_dsm_links.index
604
605
    df_dsm_links["link_id"] = link_id
606
607
    # select new store_ids to aggregated stores
608
609
    store_id = db.next_etrago_id("Store") + df_dsm_stores.index
610
611
    df_dsm_stores["store_id"] = store_id
612
613
    return df_dsm_buses, df_dsm_links, df_dsm_stores
614
615
616
def data_export(dsm_buses, dsm_links, dsm_stores, carrier):
617
618
    """
619
    Export new components to database.
620
621
    Parameters
622
    ----------
623
    dsm_buses: DataFrame
624
        Buses representing locations of DSM-potential
625
    dsm_links: DataFrame
626
        Links connecting DSM-buses and DSM-stores
627
    dsm_stores: DataFrame
628
        Stores representing DSM-potential
629
    carrier: String
630
        Remark to be filled in column 'carrier' identifying DSM-potential
631
    """
632
633
    targets = config.datasets()["DSM_CTS_industry"]["targets"]
634
635
    # dsm_buses
636
637
    insert_buses = gpd.GeoDataFrame(
638
        index=dsm_buses.index,
639
        data=dsm_buses["geom"],
640
        geometry="geom",
641
        crs=dsm_buses.crs,
642
    )
643
    insert_buses["scn_name"] = dsm_buses["scn_name"]
644
    insert_buses["bus_id"] = dsm_buses["bus_id"]
645
    insert_buses["v_nom"] = dsm_buses["v_nom"]
646
    insert_buses["carrier"] = carrier
647
    insert_buses["x"] = dsm_buses["x"]
648
    insert_buses["y"] = dsm_buses["y"]
649
650
    # insert into database
651
    insert_buses.to_postgis(
652
        targets["bus"]["table"],
653
        con=db.engine(),
654
        schema=targets["bus"]["schema"],
655
        if_exists="append",
656
        index=False,
657
        dtype={"geom": "geometry"},
658
    )
659
660
    # dsm_links
661
662
    insert_links = pd.DataFrame(index=dsm_links.index)
663
    insert_links["scn_name"] = dsm_links["scn_name"]
664
    insert_links["link_id"] = dsm_links["link_id"]
665
    insert_links["bus0"] = dsm_links["original_bus"]
666
    insert_links["bus1"] = dsm_links["dsm_bus"]
667
    insert_links["carrier"] = carrier
668
    insert_links["p_nom"] = dsm_links["p_nom"]
669
670
    # insert into database
671
    insert_links.to_sql(
672
        targets["link"]["table"],
673
        con=db.engine(),
674
        schema=targets["link"]["schema"],
675
        if_exists="append",
676
        index=False,
677
    )
678
679
    insert_links_timeseries = pd.DataFrame(index=dsm_links.index)
680
    insert_links_timeseries["scn_name"] = dsm_links["scn_name"]
681
    insert_links_timeseries["link_id"] = dsm_links["link_id"]
682
    insert_links_timeseries["p_min_pu"] = dsm_links["p_min"]
683
    insert_links_timeseries["p_max_pu"] = dsm_links["p_max"]
684
    insert_links_timeseries["temp_id"] = 1
685
686
    # insert into database
687
    insert_links_timeseries.to_sql(
688
        targets["link_timeseries"]["table"],
689
        con=db.engine(),
690
        schema=targets["link_timeseries"]["schema"],
691
        if_exists="append",
692
        index=False,
693
    )
694
695
    # dsm_stores
696
697
    insert_stores = pd.DataFrame(index=dsm_stores.index)
698
    insert_stores["scn_name"] = dsm_stores["scn_name"]
699
    insert_stores["store_id"] = dsm_stores["store_id"]
700
    insert_stores["bus"] = dsm_stores["bus"]
701
    insert_stores["carrier"] = carrier
702
    insert_stores["e_nom"] = dsm_stores["e_nom"]
703
704
    # insert into database
705
    insert_stores.to_sql(
706
        targets["store"]["table"],
707
        con=db.engine(),
708
        schema=targets["store"]["schema"],
709
        if_exists="append",
710
        index=False,
711
    )
712
713
    insert_stores_timeseries = pd.DataFrame(index=dsm_stores.index)
714
    insert_stores_timeseries["scn_name"] = dsm_stores["scn_name"]
715
    insert_stores_timeseries["store_id"] = dsm_stores["store_id"]
716
    insert_stores_timeseries["e_min_pu"] = dsm_stores["e_min"]
717
    insert_stores_timeseries["e_max_pu"] = dsm_stores["e_max"]
718
    insert_stores_timeseries["temp_id"] = 1
719
720
    # insert into database
721
    insert_stores_timeseries.to_sql(
722
        targets["store_timeseries"]["table"],
723
        con=db.engine(),
724
        schema=targets["store_timeseries"]["schema"],
725
        if_exists="append",
726
        index=False,
727
    )
728
729
730
def delete_dsm_entries(carrier):
731
732
    """
733
    Deletes DSM-components from database if they already exist before creating
734
    new ones.
735
736
    Parameters
737
        ----------
738
     carrier: String
739
        Remark in column 'carrier' identifying DSM-potential
740
    """
741
742
    targets = config.datasets()["DSM_CTS_industry"]["targets"]
743
744
    # buses
745
746
    sql = f"""DELETE FROM {targets["bus"]["schema"]}.{targets["bus"]["table"]} b
747
     WHERE (b.carrier LIKE '{carrier}');"""
748
    db.execute_sql(sql)
749
750
    # links
751
752
    sql = f"""
753
        DELETE FROM {targets["link_timeseries"]["schema"]}.
754
        {targets["link_timeseries"]["table"]} t
755
        WHERE t.link_id IN
756
        (
757
            SELECT l.link_id FROM {targets["link"]["schema"]}.
758
            {targets["link"]["table"]} l
759
            WHERE l.carrier LIKE '{carrier}'
760
        );
761
        """
762
763
    db.execute_sql(sql)
764
765
    sql = f"""
766
        DELETE FROM {targets["link"]["schema"]}.
767
        {targets["link"]["table"]} l
768
        WHERE (l.carrier LIKE '{carrier}');
769
        """
770
771
    db.execute_sql(sql)
772
773
    # stores
774
775
    sql = f"""
776
        DELETE FROM {targets["store_timeseries"]["schema"]}.
777
        {targets["store_timeseries"]["table"]} t
778
        WHERE t.store_id IN
779
        (
780
            SELECT s.store_id FROM {targets["store"]["schema"]}.
781
            {targets["store"]["table"]} s
782
            WHERE s.carrier LIKE '{carrier}'
783
        );
784
        """
785
786
    db.execute_sql(sql)
787
788
    sql = f"""
789
        DELETE FROM {targets["store"]["schema"]}.{targets["store"]["table"]} s
790
        WHERE (s.carrier LIKE '{carrier}');
791
        """
792
793
    db.execute_sql(sql)
794
795
796
def dsm_cts_ind(
797
    con=db.engine(),
798
    cts_cool_vent_ac_share=0.22,
799
    ind_cool_vent_share=0.039,
800
    ind_vent_share=0.017,
801
):
802
803
    """
804
    Execute methodology to create and implement components for DSM considering
805
    a) CTS per osm-area: combined potentials of cooling, ventilation and air
806
      conditioning
807
    b) Industry per osm-are: combined potentials of cooling and ventilation
808
    c) Industrial Sites: potentials of ventilation in sites of
809
      "Wirtschaftszweig" (WZ) 23
810
    d) Industrial Sites: potentials of sites specified by subsectors
811
      identified by Schmidt (https://zenodo.org/record/3613767#.YTsGwVtCRhG):
812
      Paper, Recycled Paper, Pulp, Cement
813
814
    Modelled using the methods by Heitkoetter et. al.:
815
    https://doi.org/10.1016/j.adapen.2020.100001
816
817
    Parameters
818
    ----------
819
    con :
820
        Connection to database
821
    cts_cool_vent_ac_share: float
822
        Share of cooling, ventilation and AC in CTS demand
823
    ind_cool_vent_share: float
824
        Share of cooling and ventilation in industry demand
825
    ind_vent_share: float
826
        Share of ventilation in industry demand in sites of WZ 23
827
828
    """
829
830
    # CTS per osm-area: cooling, ventilation and air conditioning
831
832
    print(" ")
833
    print("CTS per osm-area: cooling, ventilation and air conditioning")
834
    print(" ")
835
836
    dsm = cts_data_import(cts_cool_vent_ac_share)
837
838
    # calculate combined potentials of cooling, ventilation and air
839
    # conditioning in CTS using combined parameters by Heitkoetter et. al.
840
    p_max, p_min, e_max, e_min = calculate_potentials(
841
        s_flex=0.5, s_util=0.67, s_inc=1, s_dec=0, delta_t=1, dsm=dsm
842
    )
843
844
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
845
        con, p_max, p_min, e_max, e_min, dsm
846
    )
847
848
    df_dsm_buses = dsm_buses.copy()
849
    df_dsm_links = dsm_links.copy()
850
    df_dsm_stores = dsm_stores.copy()
851
852
    # industry per osm-area: cooling and ventilation
853
854
    print(" ")
855
    print("industry per osm-area: cooling and ventilation")
856
    print(" ")
857
858
    dsm = ind_osm_data_import(ind_cool_vent_share)
859
860
    # calculate combined potentials of cooling and ventilation in industrial
861
    # sector using combined parameters by Heitkoetter et. al.
862
    p_max, p_min, e_max, e_min = calculate_potentials(
863
        s_flex=0.5, s_util=0.73, s_inc=0.9, s_dec=0.5, delta_t=1, dsm=dsm
864
    )
865
866
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
867
        con, p_max, p_min, e_max, e_min, dsm
868
    )
869
870
    df_dsm_buses = gpd.GeoDataFrame(
871
        pd.concat([df_dsm_buses, dsm_buses], ignore_index=True),
872
        crs="EPSG:4326",
873
    )
874
    df_dsm_links = pd.DataFrame(
875
        pd.concat([df_dsm_links, dsm_links], ignore_index=True)
876
    )
877
    df_dsm_stores = pd.DataFrame(
878
        pd.concat([df_dsm_stores, dsm_stores], ignore_index=True)
879
    )
880
881
    # industry sites
882
883
    # industry sites: different applications
884
885
    dsm = ind_sites_data_import()
886
887
    print(" ")
888
    print("industry sites: paper")
889
    print(" ")
890
891
    dsm_paper = gpd.GeoDataFrame(
892
        dsm[
893
            dsm["application"].isin(
894
                [
895
                    "Graphic Paper",
896
                    "Packing Paper and Board",
897
                    "Hygiene Paper",
898
                    "Technical/Special Paper and Board",
899
                ]
900
            )
901
        ]
902
    )
903
904
    # calculate potentials of industrial sites with paper-applications
905
    # using parameters by Heitkoetter et al.
906
    p_max, p_min, e_max, e_min = calculate_potentials(
907
        s_flex=0.15,
908
        s_util=0.86,
909
        s_inc=0.95,
910
        s_dec=0,
911
        delta_t=3,
912
        dsm=dsm_paper,
913
    )
914
915
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
916
        con, p_max, p_min, e_max, e_min, dsm_paper
917
    )
918
919
    df_dsm_buses = gpd.GeoDataFrame(
920
        pd.concat([df_dsm_buses, dsm_buses], ignore_index=True),
921
        crs="EPSG:4326",
922
    )
923
    df_dsm_links = pd.DataFrame(
924
        pd.concat([df_dsm_links, dsm_links], ignore_index=True)
925
    )
926
    df_dsm_stores = pd.DataFrame(
927
        pd.concat([df_dsm_stores, dsm_stores], ignore_index=True)
928
    )
929
930
    print(" ")
931
    print("industry sites: recycled paper")
932
    print(" ")
933
934
    # calculate potentials of industrial sites with recycled paper-applications
935
    # using parameters by Heitkoetter et. al.
936
    dsm_recycled_paper = gpd.GeoDataFrame(
937
        dsm[dsm["application"] == "Recycled Paper"]
938
    )
939
940
    p_max, p_min, e_max, e_min = calculate_potentials(
941
        s_flex=0.7,
942
        s_util=0.85,
943
        s_inc=0.95,
944
        s_dec=0,
945
        delta_t=3,
946
        dsm=dsm_recycled_paper,
947
    )
948
949
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
950
        con, p_max, p_min, e_max, e_min, dsm_recycled_paper
951
    )
952
953
    df_dsm_buses = gpd.GeoDataFrame(
954
        pd.concat([df_dsm_buses, dsm_buses], ignore_index=True),
955
        crs="EPSG:4326",
956
    )
957
    df_dsm_links = pd.DataFrame(
958
        pd.concat([df_dsm_links, dsm_links], ignore_index=True)
959
    )
960
    df_dsm_stores = pd.DataFrame(
961
        pd.concat([df_dsm_stores, dsm_stores], ignore_index=True)
962
    )
963
964
    print(" ")
965
    print("industry sites: pulp")
966
    print(" ")
967
968
    dsm_pulp = gpd.GeoDataFrame(dsm[dsm["application"] == "Mechanical Pulp"])
969
970
    # calculate potentials of industrial sites with pulp-applications
971
    # using parameters by Heitkoetter et. al.
972
    p_max, p_min, e_max, e_min = calculate_potentials(
973
        s_flex=0.7,
974
        s_util=0.83,
975
        s_inc=0.95,
976
        s_dec=0,
977
        delta_t=2,
978
        dsm=dsm_pulp,
979
    )
980
981
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
982
        con, p_max, p_min, e_max, e_min, dsm_pulp
983
    )
984
985
    df_dsm_buses = gpd.GeoDataFrame(
986
        pd.concat([df_dsm_buses, dsm_buses], ignore_index=True),
987
        crs="EPSG:4326",
988
    )
989
    df_dsm_links = pd.DataFrame(
990
        pd.concat([df_dsm_links, dsm_links], ignore_index=True)
991
    )
992
    df_dsm_stores = pd.DataFrame(
993
        pd.concat([df_dsm_stores, dsm_stores], ignore_index=True)
994
    )
995
996
    # industry sites: cement
997
998
    print(" ")
999
    print("industry sites: cement")
1000
    print(" ")
1001
1002
    dsm_cement = gpd.GeoDataFrame(dsm[dsm["application"] == "Cement Mill"])
1003
1004
    # calculate potentials of industrial sites with cement-applications
1005
    # using parameters by Heitkoetter et. al.
1006
    p_max, p_min, e_max, e_min = calculate_potentials(
1007
        s_flex=0.61,
1008
        s_util=0.65,
1009
        s_inc=0.95,
1010
        s_dec=0,
1011
        delta_t=4,
1012
        dsm=dsm_cement,
1013
    )
1014
1015
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
1016
        con, p_max, p_min, e_max, e_min, dsm_cement
1017
    )
1018
1019
    df_dsm_buses = gpd.GeoDataFrame(
1020
        pd.concat([df_dsm_buses, dsm_buses], ignore_index=True),
1021
        crs="EPSG:4326",
1022
    )
1023
    df_dsm_links = pd.DataFrame(
1024
        pd.concat([df_dsm_links, dsm_links], ignore_index=True)
1025
    )
1026
    df_dsm_stores = pd.DataFrame(
1027
        pd.concat([df_dsm_stores, dsm_stores], ignore_index=True)
1028
    )
1029
1030
    # industry sites: ventilation in WZ23
1031
1032
    print(" ")
1033
    print("industry sites: ventilation in WZ23")
1034
    print(" ")
1035
1036
    dsm = ind_sites_vent_data_import(ind_vent_share, wz=23)
1037
1038
    # drop entries of Cement Mills whose DSM-potentials have already been
1039
    # modelled
1040
    cement = np.unique(dsm_cement["bus"].values)
1041
    index_names = np.array(dsm[dsm["bus"].isin(cement)].index)
1042
    dsm.drop(index_names, inplace=True)
1043
1044
    # calculate potentials of ventialtion in industrial sites of WZ 23
1045
    # using parameters by Heitkoetter et. al.
1046
    p_max, p_min, e_max, e_min = calculate_potentials(
1047
        s_flex=0.5, s_util=0.8, s_inc=1, s_dec=0.5, delta_t=1, dsm=dsm
1048
    )
1049
1050
    dsm_buses, dsm_links, dsm_stores = create_dsm_components(
1051
        con, p_max, p_min, e_max, e_min, dsm
1052
    )
1053
1054
    df_dsm_buses = gpd.GeoDataFrame(
1055
        pd.concat([df_dsm_buses, dsm_buses], ignore_index=True),
1056
        crs="EPSG:4326",
1057
    )
1058
    df_dsm_links = pd.DataFrame(
1059
        pd.concat([df_dsm_links, dsm_links], ignore_index=True)
1060
    )
1061
    df_dsm_stores = pd.DataFrame(
1062
        pd.concat([df_dsm_stores, dsm_stores], ignore_index=True)
1063
    )
1064
1065
    # aggregate DSM components per substation
1066
1067
    dsm_buses, dsm_links, dsm_stores = aggregate_components(
1068
        df_dsm_buses, df_dsm_links, df_dsm_stores
1069
    )
1070
1071
    # export aggregated DSM components to database
1072
1073
    delete_dsm_entries("dsm-cts")
1074
    delete_dsm_entries("dsm-ind-osm")
1075
    delete_dsm_entries("dsm-ind-sites")
1076
    delete_dsm_entries("dsm")
1077
1078
    data_export(dsm_buses, dsm_links, dsm_stores, carrier="dsm")
1079
1080
1081
def dsm_cts_ind_processing():
1082
    dsm_cts_ind()
1083