Passed
Push — dev ( 7cf077...0e9721 )
by
unknown
07:11 queued 04:45
created

data.datasets.heat_supply.individual_heating()   A

Complexity

Conditions 1

Size

Total Lines 28
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 12
nop 0
dl 0
loc 28
rs 9.8
c 0
b 0
f 0
1
"""The central module containing all code dealing with heat supply data
2
3
"""
4
5
import datetime
6
import json
7
import time
8
9
from geoalchemy2.types import Geometry
10
from sqlalchemy import Column, Float, ForeignKey, Integer, String
11
from sqlalchemy.ext.declarative import declarative_base
12
import pandas as pd
13
14
from egon.data import config, db
15
from egon.data.datasets import Dataset
16
from egon.data.datasets.district_heating_areas import EgonDistrictHeatingAreas
17
from egon.data.datasets.heat_supply.district_heating import (
18
    backup_gas_boilers,
19
    backup_resistive_heaters,
20
    cascade_heat_supply,
21
)
22
from egon.data.datasets.heat_supply.geothermal import potential_germany
23
from egon.data.datasets.heat_supply.individual_heating import (
24
    cascade_heat_supply_indiv,
25
)
26
from egon.data.metadata import (
27
    context,
28
    generate_resource_fields_from_sqla_model,
29
    license_ccby,
30
    license_egon_data_odbl,
31
    meta_metadata,
32
    sources,
33
)
34
35
# Will later be imported from another file.
36
Base = declarative_base()
37
38
39
class EgonDistrictHeatingSupply(Base):
40
    __tablename__ = "egon_district_heating"
41
    __table_args__ = {"schema": "supply"}
42
    index = Column(Integer, primary_key=True, autoincrement=True)
43
    district_heating_id = Column(
44
        Integer, ForeignKey(EgonDistrictHeatingAreas.id)
45
    )
46
    carrier = Column(String(25))
47
    category = Column(String(25))
48
    capacity = Column(Float)
49
    geometry = Column(Geometry("POINT", 3035))
50
    scenario = Column(String(50))
51
52
53
class EgonIndividualHeatingSupply(Base):
54
    __tablename__ = "egon_individual_heating"
55
    __table_args__ = {"schema": "supply"}
56
    index = Column(Integer, primary_key=True, autoincrement=True)
57
    mv_grid_id = Column(Integer)
58
    carrier = Column(String(25))
59
    category = Column(String(25))
60
    capacity = Column(Float)
61
    geometry = Column(Geometry("POINT", 3035))
62
    scenario = Column(String(50))
63
64
65
def create_tables():
66
    """Create tables for district heating areas
67
68
    Returns
69
    -------
70
        None
71
    """
72
73
    engine = db.engine()
74
    EgonDistrictHeatingSupply.__table__.drop(bind=engine, checkfirst=True)
75
    EgonDistrictHeatingSupply.__table__.create(bind=engine, checkfirst=True)
76
    EgonIndividualHeatingSupply.__table__.drop(bind=engine, checkfirst=True)
77
    EgonIndividualHeatingSupply.__table__.create(bind=engine, checkfirst=True)
78
79
80
def district_heating():
81
    """Insert supply for district heating areas
82
83
    Returns
84
    -------
85
    None.
86
87
    """
88
    sources = config.datasets()["heat_supply"]["sources"]
89
    targets = config.datasets()["heat_supply"]["targets"]
90
91
    db.execute_sql(
92
        f"""
93
        DELETE FROM {targets['district_heating_supply']['schema']}.
94
        {targets['district_heating_supply']['table']}
95
        """
96
    )
97
98
    supply_2035 = cascade_heat_supply("eGon2035", plotting=False)
99
100
    supply_2035["scenario"] = "eGon2035"
101
102
    supply_2035.to_postgis(
103
        targets["district_heating_supply"]["table"],
104
        schema=targets["district_heating_supply"]["schema"],
105
        con=db.engine(),
106
        if_exists="append",
107
    )
108
109
    # Compare target value with sum of distributed heat supply
110
    df_check = db.select_dataframe(
111
        f"""
112
        SELECT a.carrier,
113
        (SUM(a.capacity) - b.capacity) / SUM(a.capacity) as deviation
114
        FROM {targets['district_heating_supply']['schema']}.
115
        {targets['district_heating_supply']['table']} a,
116
        {sources['scenario_capacities']['schema']}.
117
        {sources['scenario_capacities']['table']} b
118
        WHERE a.scenario = 'eGon2035'
119
        AND b.scenario_name = 'eGon2035'
120
        AND b.carrier = CONCAT('urban_central_', a.carrier)
121
        GROUP BY (a.carrier,  b.capacity);
122
        """
123
    )
124
    # If the deviation is > 1%, throw an error
125
    assert (
126
        df_check.deviation.abs().max() < 1
127
    ), f"""Unexpected deviation between target value and distributed
128
        heat supply: {df_check}
129
        """
130
131
    # Add gas boilers as conventional backup capacities
132
    backup = backup_gas_boilers("eGon2035")
133
134
    backup.to_postgis(
135
        targets["district_heating_supply"]["table"],
136
        schema=targets["district_heating_supply"]["schema"],
137
        con=db.engine(),
138
        if_exists="append",
139
    )
140
141
    backup_rh = backup_resistive_heaters("eGon2035")
142
143
    if not backup_rh.empty:
144
        backup_rh.to_postgis(
145
            targets["district_heating_supply"]["table"],
146
            schema=targets["district_heating_supply"]["schema"],
147
            con=db.engine(),
148
            if_exists="append",
149
        )
150
151
152
def individual_heating():
153
    """Insert supply for individual heating
154
155
    Returns
156
    -------
157
    None.
158
159
    """
160
    targets = config.datasets()["heat_supply"]["targets"]
161
162
    db.execute_sql(
163
        f"""
164
        DELETE FROM {targets['individual_heating_supply']['schema']}.
165
        {targets['individual_heating_supply']['table']}
166
        """
167
    )
168
169
    supply_2035 = cascade_heat_supply_indiv(
170
        "eGon2035", distribution_level="federal_states", plotting=False
171
    )
172
173
    supply_2035["scenario"] = "eGon2035"
174
175
    supply_2035.to_postgis(
176
        targets["individual_heating_supply"]["table"],
177
        schema=targets["individual_heating_supply"]["schema"],
178
        con=db.engine(),
179
        if_exists="append",
180
    )
181
182
183
def metadata():
184
    """Write metadata for heat supply tables
185
186
    Returns
187
    -------
188
    None.
189
190
    """
191
192
    fields = generate_resource_fields_from_sqla_model(
193
        EgonDistrictHeatingSupply
194
    )
195
196
    fields_df = pd.DataFrame(data=fields).set_index("name")
197
    fields_df.loc["index", "description"] = "Unique identifyer"
198
    fields_df.loc[
199
        "district_heating_id", "description"
200
    ] = "Index of the corresponding district heating grid"
201
    fields_df.loc["carrier", "description"] = "Name of energy carrier"
202
    fields_df.loc[
203
        "category", "description"
204
    ] = "Size-category of district heating grid"
205
    fields_df.loc["capacity", "description"] = "Installed heating capacity"
206
    fields_df.loc[
207
        "geometry", "description"
208
    ] = "Location of thermal power plant"
209
    fields_df.loc["scenario", "description"] = "Name of corresponing scenario"
210
211
    fields_df.loc["capacity", "unit"] = "MW_th"
212
    fields_df.unit.fillna("none", inplace=True)
213
214
    fields = fields_df.reset_index().to_dict(orient="records")
215
216
    meta_district = {
217
        "name": "supply.egon_district_heating",
218
        "title": "eGon heat supply for district heating grids",
219
        "id": "WILL_BE_SET_AT_PUBLICATION",
220
        "description": "Heat supply technologies for district heating grids",
221
        "language": ["EN"],
222
        "publicationDate": datetime.date.today().isoformat(),
223
        "context": context(),
224
        "spatial": {
225
            "location": None,
226
            "extent": "Germany",
227
            "resolution": None,
228
        },
229
        "sources": [
230
            sources()["era5"],
231
            sources()["vg250"],
232
            sources()["egon-data"],
233
            sources()["egon-data_bundle"],
234
            sources()["openstreetmap"],
235
            sources()["mastr"],
236
            sources()["peta"],
237
        ],
238
        "licenses": [license_egon_data_odbl()],
239
        "contributors": [
240
            {
241
                "title": "Clara Büttner",
242
                "email": "http://github.com/ClaraBuettner",
243
                "date": time.strftime("%Y-%m-%d"),
244
                "object": None,
245
                "comment": "Imported data",
246
            },
247
        ],
248
        "resources": [
249
            {
250
                "profile": "tabular-data-resource",
251
                "name": "supply.egon_district_heating",
252
                "path": None,
253
                "format": "PostgreSQL",
254
                "encoding": "UTF-8",
255
                "schema": {
256
                    "fields": fields,
257
                    "primaryKey": ["index"],
258
                    "foreignKeys": [],
259
                },
260
                "dialect": {"delimiter": None, "decimalSeparator": "."},
261
            }
262
        ],
263
        "metaMetadata": meta_metadata(),
264
    }
265
266
    # Add metadata as a comment to the table
267
    db.submit_comment(
268
        "'" + json.dumps(meta_district) + "'",
269
        EgonDistrictHeatingSupply.__table__.schema,
270
        EgonDistrictHeatingSupply.__table__.name,
271
    )
272
273
    fields = generate_resource_fields_from_sqla_model(
274
        EgonIndividualHeatingSupply
275
    )
276
277
    fields_df = pd.DataFrame(data=fields).set_index("name")
278
    fields_df.loc["index", "description"] = "Unique identifyer"
279
    fields_df.loc[
280
        "mv_grid_id", "description"
281
    ] = "Index of the corresponding mv grid district"
282
    fields_df.loc["carrier", "description"] = "Name of energy carrier"
283
    fields_df.loc["category", "description"] = "Size-category"
284
    fields_df.loc["capacity", "description"] = "Installed heating capacity"
285
    fields_df.loc[
286
        "geometry", "description"
287
    ] = "Location of thermal power plant"
288
    fields_df.loc["scenario", "description"] = "Name of corresponing scenario"
289
290
    fields_df.loc["capacity", "unit"] = "MW_th"
291
    fields_df.unit.fillna("none", inplace=True)
292
293
    fields = fields_df.reset_index().to_dict(orient="records")
294
295
    meta_district = {
296
        "name": "supply.egon_individual_heating",
297
        "title": "eGon heat supply for individual supplied buildings",
298
        "id": "WILL_BE_SET_AT_PUBLICATION",
299
        "description": "Heat supply technologies for individual supplied buildings",
300
        "language": ["EN"],
301
        "publicationDate": datetime.date.today().isoformat(),
302
        "context": context(),
303
        "spatial": {
304
            "location": None,
305
            "extent": "Germany",
306
            "resolution": None,
307
        },
308
        "sources": [
309
            sources()["era5"],
310
            sources()["vg250"],
311
            sources()["egon-data"],
312
            sources()["egon-data_bundle"],
313
            sources()["openstreetmap"],
314
            sources()["mastr"],
315
            sources()["peta"],
316
        ],
317
        "licenses": [license_egon_data_odbl()],
318
        "contributors": [
319
            {
320
                "title": "Clara Büttner",
321
                "email": "http://github.com/ClaraBuettner",
322
                "date": time.strftime("%Y-%m-%d"),
323
                "object": None,
324
                "comment": "Imported data",
325
            },
326
        ],
327
        "resources": [
328
            {
329
                "profile": "tabular-data-resource",
330
                "name": "supply.egon_individual_heating",
331
                "path": None,
332
                "format": "PostgreSQL",
333
                "encoding": "UTF-8",
334
                "schema": {
335
                    "fields": fields,
336
                    "primaryKey": ["index"],
337
                    "foreignKeys": [],
338
                },
339
                "dialect": {"delimiter": None, "decimalSeparator": "."},
340
            }
341
        ],
342
        "metaMetadata": meta_metadata(),
343
    }
344
345
    # Add metadata as a comment to the table
346
    db.submit_comment(
347
        "'" + json.dumps(meta_district) + "'",
348
        EgonIndividualHeatingSupply.__table__.schema,
349
        EgonIndividualHeatingSupply.__table__.name,
350
    )
351
352
353
class HeatSupply(Dataset):
354
    """
355
    Select and store heat supply technologies for inidvidual and district heating
356
357
    This dataset distributes heat supply technologies to each district heating grid
358
    and individual supplies buildings per medium voltage grid district.
359
    National installed capacities are predefined from external sources within
360
    :py:class:`ScenarioCapacities <egon.data.datasets.scenario_capacities.ScenarioCapacities>`.
361
    The further distribution is done using a cascade that follows a specific order of supply technologies
362
    and the heat demand.
363
364
365
    *Dependencies*
366
      * :py:class:`DataBundle <egon.data.datasets.data_bundle.DataBundle>`
367
      * :py:class:`DistrictHeatingAreas <egon.data.datasets.district_heating_areas.DistrictHeatingAreas>`
368
      * :py:class:`ZensusMvGridDistricts <egon.data.datasets.zensus_mv_grid_districts.ZensusMvGridDistricts>`
369
      * :py:class:`Chp <egon.data.datasets.chp.Chp>`
370
371
372
    *Resulting tables*
373
      * :py:class:`demand.egon_district_heating <egon.data.datasets.heat_supply.EgonDistrictHeatingSupply>` is created and filled
374
      * :py:class:`demand.egon_individual_heating <egon.data.datasets.heat_supply.EgonIndividualHeatingSupply>` is created and filled
375
376
    """
377
378
    #:
379
    name: str = "HeatSupply"
380
    #:
381
    version: str = "0.0.9"
382
383
    def __init__(self, dependencies):
384
        super().__init__(
385
            name=self.name,
386
            version=self.version,
387
            dependencies=dependencies,
388
            tasks=(
389
                create_tables,
390
                {
391
                    district_heating,
392
                    individual_heating,
393
                    potential_germany,
394
                },
395
                metadata,
396
            ),
397
        )
398