1
|
|
|
"""The central module containing all code dealing with processing |
2
|
|
|
data from demandRegio |
3
|
|
|
|
4
|
|
|
""" |
5
|
|
|
from sqlalchemy import Column, Float, ForeignKey, Integer, String |
6
|
|
|
from sqlalchemy.ext.declarative import declarative_base |
7
|
|
|
import pandas as pd |
8
|
|
|
|
9
|
|
|
from egon.data import db |
10
|
|
|
from egon.data.datasets import Dataset |
11
|
|
|
from egon.data.datasets.electricity_demand.temporal import insert_cts_load |
12
|
|
|
from egon.data.datasets.electricity_demand_timeseries.hh_buildings import ( |
13
|
|
|
HouseholdElectricityProfilesOfBuildings, |
14
|
|
|
get_iee_hh_demand_profiles_raw, |
15
|
|
|
) |
16
|
|
|
from egon.data.datasets.electricity_demand_timeseries.hh_profiles import ( |
17
|
|
|
HouseholdElectricityProfilesInCensusCells, |
18
|
|
|
) |
19
|
|
|
from egon.data.datasets.zensus_vg250 import DestatisZensusPopulationPerHa |
20
|
|
|
import egon.data.config |
21
|
|
|
|
22
|
|
|
# will be later imported from another file ### |
23
|
|
|
Base = declarative_base() |
24
|
|
|
engine = db.engine() |
25
|
|
|
|
26
|
|
|
|
27
|
|
|
class HouseholdElectricityDemand(Dataset): |
28
|
|
|
""" |
29
|
|
|
Create table and store data on household electricity demands per census cell |
30
|
|
|
|
31
|
|
|
Create a table to store the annual electricity demand for households on census cell level. |
32
|
|
|
In a next step the annual electricity demand per cell is determined by |
33
|
|
|
executing function :py:func:`get_annual_household_el_demand_cells` |
34
|
|
|
|
35
|
|
|
*Dependencies* |
36
|
|
|
* :py:class:`DemandRegio <egon.data.datasets.demandregio>` |
37
|
|
|
* :py:class:`DataBundle <egon.data.datasets.data_bundle.DataBundle>` |
38
|
|
|
|
39
|
|
|
*Resulting tables* |
40
|
|
|
* :py:class:`demand.egon_demandregio_zensus_electricity <egon.data.datasets.electricity_demand.EgonDemandRegioZensusElectricity>` is created and filled |
41
|
|
|
|
42
|
|
|
""" |
43
|
|
|
|
44
|
|
|
#: |
45
|
|
|
name: str = "HouseholdElectricityDemand" |
46
|
|
|
#: |
47
|
|
|
version: str = "0.0.5" |
48
|
|
|
|
49
|
|
|
def __init__(self, dependencies): |
50
|
|
|
super().__init__( |
51
|
|
|
name=self.name, |
52
|
|
|
version=self.version, |
53
|
|
|
dependencies=dependencies, |
54
|
|
|
tasks=(create_tables, get_annual_household_el_demand_cells), |
55
|
|
|
) |
56
|
|
|
|
57
|
|
|
|
58
|
|
|
class CtsElectricityDemand(Dataset): |
59
|
|
|
""" |
60
|
|
|
Create table and store data on cts electricity demands per census cell |
61
|
|
|
|
62
|
|
|
Creates a table to store data on electricity demands from the cts sector on |
63
|
|
|
census cell level. For a spatial distribution of electricity demands data |
64
|
|
|
from DemandRegio, which provides the data on NUT3-level, is used and |
65
|
|
|
distributed to census cells according to heat demand data from Peta. |
66
|
|
|
Annual demands are then aggregated per MV grid district and a corresponding |
67
|
|
|
time series is created taking the shares of different cts branches and their |
68
|
|
|
specific standard load profiles into account. |
69
|
|
|
|
70
|
|
|
|
71
|
|
|
*Dependencies* |
72
|
|
|
* :py:class:`MapZensusGridDistricts <egon.data.datasets.zensus_mv_grid_districts.MapZensusGridDistricts>` |
73
|
|
|
* :py:class:`DemandRegio <egon.data.datasets.demandregio.DemandRegio>` |
74
|
|
|
* :py:class:`HeatDemandImport <egon.data.datasets.heat_demand.HeatDemandImport>` |
75
|
|
|
* :py:class:`HouseholdElectricityDemand <egon.data.datasets.electricity_demand.HouseholdElectricityDemand>` |
76
|
|
|
* :py:class:`EtragoSetup <egon.data.datasets.etrago_setup.EtragoSetup>` |
77
|
|
|
* :py:class:`ZensusMvGridDistricts <egon.data.datasets.zensus_mv_grid_districts.ZensusMvGridDistricts>` |
78
|
|
|
* :py:class:`ZensusVg250 <egon.data.datasets.zensus_vg250.ZensusVg250>` |
79
|
|
|
|
80
|
|
|
|
81
|
|
|
*Resulting tables* |
82
|
|
|
* :py:class:`demand.egon_etrago_electricity_cts <egon.data.datasets.electricity_demand.temporal.EgonEtragoElectricityCts>` is created and filled |
83
|
|
|
|
84
|
|
|
|
85
|
|
|
""" |
86
|
|
|
|
87
|
|
|
#: |
88
|
|
|
name: str = "CtsElectricityDemand" |
89
|
|
|
#: |
90
|
|
|
version: str = "0.0.2" |
91
|
|
|
|
92
|
|
|
def __init__(self, dependencies): |
93
|
|
|
super().__init__( |
94
|
|
|
name=self.name, |
95
|
|
|
version=self.version, |
96
|
|
|
dependencies=dependencies, |
97
|
|
|
tasks=(distribute_cts_demands, insert_cts_load), |
98
|
|
|
) |
99
|
|
|
|
100
|
|
|
|
101
|
|
|
class EgonDemandRegioZensusElectricity(Base): |
102
|
|
|
__tablename__ = "egon_demandregio_zensus_electricity" |
103
|
|
|
__table_args__ = {"schema": "demand", "extend_existing": True} |
104
|
|
|
zensus_population_id = Column( |
105
|
|
|
Integer, ForeignKey(DestatisZensusPopulationPerHa.id), primary_key=True |
106
|
|
|
) |
107
|
|
|
scenario = Column(String(50), primary_key=True) |
108
|
|
|
sector = Column(String, primary_key=True) |
109
|
|
|
demand = Column(Float) |
110
|
|
|
|
111
|
|
|
|
112
|
|
|
def create_tables(): |
113
|
|
|
"""Create tables for demandregio data |
114
|
|
|
Returns |
115
|
|
|
------- |
116
|
|
|
None. |
117
|
|
|
""" |
118
|
|
|
db.execute_sql("CREATE SCHEMA IF NOT EXISTS demand;") |
119
|
|
|
db.execute_sql("CREATE SCHEMA IF NOT EXISTS society;") |
120
|
|
|
engine = db.engine() |
121
|
|
|
EgonDemandRegioZensusElectricity.__table__.drop( |
122
|
|
|
bind=engine, checkfirst=True |
123
|
|
|
) |
124
|
|
|
EgonDemandRegioZensusElectricity.__table__.create( |
125
|
|
|
bind=engine, checkfirst=True |
126
|
|
|
) |
127
|
|
|
|
128
|
|
|
|
129
|
|
|
def get_annual_household_el_demand_cells(): |
130
|
|
|
""" |
131
|
|
|
Annual electricity demand per cell is determined |
132
|
|
|
|
133
|
|
|
Timeseries for every cell are accumulated, the maximum value |
134
|
|
|
determined and with the respective nuts3 factor scaled for 2035 and 2050 |
135
|
|
|
scenario. |
136
|
|
|
|
137
|
|
|
Note |
138
|
|
|
---------- |
139
|
|
|
In test-mode 'SH' the iteration takes place by 'cell_id' to avoid |
140
|
|
|
intensive RAM usage. For whole Germany 'nuts3' are taken and |
141
|
|
|
RAM > 32GB is necessary. |
142
|
|
|
""" |
143
|
|
|
|
144
|
|
|
with db.session_scope() as session: |
145
|
|
|
cells_query = ( |
146
|
|
|
session.query( |
147
|
|
|
HouseholdElectricityProfilesOfBuildings, |
148
|
|
|
HouseholdElectricityProfilesInCensusCells.nuts3, |
149
|
|
|
HouseholdElectricityProfilesInCensusCells.factor_2019, |
150
|
|
|
HouseholdElectricityProfilesInCensusCells.factor_2023, |
151
|
|
|
HouseholdElectricityProfilesInCensusCells.factor_2035, |
152
|
|
|
HouseholdElectricityProfilesInCensusCells.factor_2050, |
153
|
|
|
) |
154
|
|
|
.filter( |
155
|
|
|
HouseholdElectricityProfilesOfBuildings.cell_id |
156
|
|
|
== HouseholdElectricityProfilesInCensusCells.cell_id |
157
|
|
|
) |
158
|
|
|
.order_by(HouseholdElectricityProfilesOfBuildings.id) |
159
|
|
|
) |
160
|
|
|
|
161
|
|
|
df_buildings_and_profiles = pd.read_sql( |
162
|
|
|
cells_query.statement, cells_query.session.bind, index_col="id" |
163
|
|
|
) |
164
|
|
|
|
165
|
|
|
# Read demand profiles from egon-data-bundle |
166
|
|
|
df_profiles = get_iee_hh_demand_profiles_raw() |
167
|
|
|
|
168
|
|
|
def ve(s): |
169
|
|
|
raise (ValueError(s)) |
170
|
|
|
|
171
|
|
|
dataset = egon.data.config.settings()["egon-data"]["--dataset-boundary"] |
172
|
|
|
scenarios = egon.data.config.settings()["egon-data"]["--scenarios"] |
173
|
|
|
|
174
|
|
|
iterate_over = ( |
175
|
|
|
"nuts3" |
176
|
|
|
if dataset == "Everything" |
177
|
|
|
else "cell_id" |
178
|
|
|
if dataset == "Schleswig-Holstein" |
179
|
|
|
else ve(f"'{dataset}' is not a valid dataset boundary.") |
180
|
|
|
) |
181
|
|
|
|
182
|
|
|
df_annual_demand = pd.DataFrame( |
183
|
|
|
columns=scenarios + ["zensus_population_id"] |
184
|
|
|
) |
185
|
|
|
|
186
|
|
|
for _, df in df_buildings_and_profiles.groupby(by=iterate_over): |
187
|
|
|
df_annual_demand_iter = pd.DataFrame( |
188
|
|
|
columns=scenarios + ["zensus_population_id"] |
189
|
|
|
) |
190
|
|
|
|
191
|
|
|
if "eGon2035" in scenarios: |
192
|
|
|
df_annual_demand_iter["eGon2035"] = ( |
193
|
|
|
df_profiles.loc[:, df["profile_id"]].sum(axis=0) |
194
|
|
|
* df["factor_2035"].values |
195
|
|
|
) |
196
|
|
|
if "eGon100RE" in scenarios: |
197
|
|
|
df_annual_demand_iter["eGon100RE"] = ( |
198
|
|
|
df_profiles.loc[:, df["profile_id"]].sum(axis=0) |
199
|
|
|
* df["factor_2050"].values |
200
|
|
|
) |
201
|
|
|
if "status2019" in scenarios: |
202
|
|
|
df_annual_demand_iter["status2019"] = ( |
203
|
|
|
df_profiles.loc[:, df["profile_id"]].sum(axis=0) |
204
|
|
|
* df["factor_2019"].values |
205
|
|
|
) |
206
|
|
|
|
207
|
|
|
if "status2023" in scenarios: |
208
|
|
|
df_annual_demand_iter["status2023"] = ( |
209
|
|
|
df_profiles.loc[:, df["profile_id"]].sum(axis=0) |
210
|
|
|
* df["factor_2023"].values |
211
|
|
|
) |
212
|
|
|
df_annual_demand_iter["zensus_population_id"] = df["cell_id"].values |
213
|
|
|
df_annual_demand = pd.concat([df_annual_demand, df_annual_demand_iter]) |
214
|
|
|
|
215
|
|
|
df_annual_demand = ( |
216
|
|
|
df_annual_demand.groupby("zensus_population_id").sum().reset_index() |
217
|
|
|
) |
218
|
|
|
df_annual_demand["sector"] = "residential" |
219
|
|
|
df_annual_demand = df_annual_demand.melt( |
220
|
|
|
id_vars=["zensus_population_id", "sector"], |
221
|
|
|
var_name="scenario", |
222
|
|
|
value_name="demand", |
223
|
|
|
) |
224
|
|
|
# convert from Wh to MWh |
225
|
|
|
df_annual_demand["demand"] = df_annual_demand["demand"] / 1e6 |
226
|
|
|
|
227
|
|
|
# delete all cells for residentials |
228
|
|
|
with db.session_scope() as session: |
229
|
|
|
session.query(EgonDemandRegioZensusElectricity).filter( |
230
|
|
|
EgonDemandRegioZensusElectricity.sector == "residential" |
231
|
|
|
).delete() |
232
|
|
|
|
233
|
|
|
# Insert data to target table |
234
|
|
|
df_annual_demand.to_sql( |
235
|
|
|
name=EgonDemandRegioZensusElectricity.__table__.name, |
236
|
|
|
schema=EgonDemandRegioZensusElectricity.__table__.schema, |
237
|
|
|
con=db.engine(), |
238
|
|
|
index=False, |
239
|
|
|
if_exists="append", |
240
|
|
|
) |
241
|
|
|
|
242
|
|
|
|
243
|
|
|
def distribute_cts_demands(): |
244
|
|
|
"""Distribute electrical demands for cts to zensus cells. |
245
|
|
|
|
246
|
|
|
The demands on nuts3-level from demandregio are linear distributed |
247
|
|
|
to the heat demand of cts in each zensus cell. |
248
|
|
|
|
249
|
|
|
Returns |
250
|
|
|
------- |
251
|
|
|
None. |
252
|
|
|
|
253
|
|
|
""" |
254
|
|
|
|
255
|
|
|
sources = egon.data.config.datasets()["electrical_demands_cts"]["sources"] |
256
|
|
|
|
257
|
|
|
target = egon.data.config.datasets()["electrical_demands_cts"]["targets"][ |
258
|
|
|
"cts_demands_zensus" |
259
|
|
|
] |
260
|
|
|
|
261
|
|
|
db.execute_sql( |
262
|
|
|
f"""DELETE FROM {target['schema']}.{target['table']} |
263
|
|
|
WHERE sector = 'service'""" |
264
|
|
|
) |
265
|
|
|
|
266
|
|
|
# Select match between zensus cells and nuts3 regions of vg250 |
267
|
|
|
map_nuts3 = db.select_dataframe( |
268
|
|
|
f"""SELECT zensus_population_id, vg250_nuts3 as nuts3 FROM |
269
|
|
|
{sources['map_zensus_vg250']['schema']}. |
270
|
|
|
{sources['map_zensus_vg250']['table']}""", |
271
|
|
|
index_col="zensus_population_id", |
272
|
|
|
) |
273
|
|
|
|
274
|
|
|
# Insert data per scenario |
275
|
|
|
for scn in egon.data.config.settings()["egon-data"]["--scenarios"]: |
276
|
|
|
# Select heat_demand per zensus cell |
277
|
|
|
peta = db.select_dataframe( |
278
|
|
|
f"""SELECT zensus_population_id, demand as heat_demand, |
279
|
|
|
sector, scenario FROM |
280
|
|
|
{sources['heat_demand_cts']['schema']}. |
281
|
|
|
{sources['heat_demand_cts']['table']} |
282
|
|
|
WHERE scenario = '{scn}' |
283
|
|
|
AND sector = 'service'""", |
284
|
|
|
index_col="zensus_population_id", |
285
|
|
|
) |
286
|
|
|
|
287
|
|
|
# Add nuts3 key to zensus cells |
288
|
|
|
peta["nuts3"] = map_nuts3.nuts3 |
289
|
|
|
|
290
|
|
|
# Calculate share of nuts3 heat demand per zensus cell |
291
|
|
|
for nuts3, df in peta.groupby("nuts3"): |
292
|
|
|
peta.loc[df.index, "share"] = ( |
293
|
|
|
df["heat_demand"] / df["heat_demand"].sum() |
294
|
|
|
) |
295
|
|
|
|
296
|
|
|
# Select forecasted electrical demands from demandregio table |
297
|
|
|
demand_nuts3 = db.select_dataframe( |
298
|
|
|
f"""SELECT nuts3, SUM(demand) as demand FROM |
299
|
|
|
{sources['demandregio']['schema']}. |
300
|
|
|
{sources['demandregio']['table']} |
301
|
|
|
WHERE scenario = '{scn}' |
302
|
|
|
AND wz IN ( |
303
|
|
|
SELECT wz FROM |
304
|
|
|
{sources['demandregio_wz']['schema']}. |
305
|
|
|
{sources['demandregio_wz']['table']} |
306
|
|
|
WHERE sector = 'CTS') |
307
|
|
|
GROUP BY nuts3""", |
308
|
|
|
index_col="nuts3", |
309
|
|
|
) |
310
|
|
|
|
311
|
|
|
# Scale demands on nuts3 level linear to heat demand share |
312
|
|
|
peta["demand"] = peta["share"].mul( |
313
|
|
|
demand_nuts3.demand[peta["nuts3"]].values |
314
|
|
|
) |
315
|
|
|
|
316
|
|
|
# Rename index |
317
|
|
|
peta.index = peta.index.rename("zensus_population_id") |
318
|
|
|
|
319
|
|
|
# Insert data to target table |
320
|
|
|
peta[["scenario", "demand", "sector"]].to_sql( |
321
|
|
|
target["table"], |
322
|
|
|
schema=target["schema"], |
323
|
|
|
con=db.engine(), |
324
|
|
|
if_exists="append", |
325
|
|
|
) |
326
|
|
|
|