Passed
Pull Request — dev (#936)
by
unknown
01:53
created

overwrite_max_gas_generation_overtheyear()   A

Complexity

Conditions 2

Size

Total Lines 38
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 38
rs 9.7
c 0
b 0
f 0
cc 2
nop 1
1
# -*- coding: utf-8 -*-
2
"""
3
The central module containing code dealing with importing CH4 production data for eGon2035.
4
5
For eGon2035, the gas produced in Germany can be natural gas or biogas.
6
The source productions are geolocalised potentials described as PyPSA
7
generators. These generators are not extendable and their overall
8
production over the year is limited directly in eTraGo by values from
9
the Netzentwicklungsplan Gas 2020–2030 (36 TWh natural gas and 10 TWh
10
biogas), also stored in the table
11
:py:class:`scenario.egon_scenario_parameters <egon.data.datasets.scenario_parameters.EgonScenario>`.
12
13
"""
14
from pathlib import Path
15
from urllib.request import urlretrieve
16
import ast
17
import json
18
19
import geopandas as gpd
20
import numpy as np
21
import pandas as pd
22
23
from egon.data import config, db
24
from egon.data.config import settings
25
from egon.data.datasets import Dataset
26
from egon.data.datasets.pypsaeursec import read_network
27
from egon.data.datasets.scenario_parameters import get_sector_parameters
28
29
30
class CH4Production(Dataset):
31
    """
32
    Insert the CH4 productions into the database for eGon2035
33
34
    Insert the CH4 productions into the database for eGon2035 by using
35
    the function :py:func:`import_gas_generators`.
36
37
    *Dependencies*
38
      * :py:class:`GasAreaseGon2035 <egon.data.datasets.gas_areas.GasAreaseGon2035>`
39
      * :py:class:`GasNodesAndPipes <egon.data.datasets.gas_grid.GasNodesAndPipes>`
40
41
    *Resulting tables*
42
      * :py:class:`grid.egon_etrago_generator <egon.data.datasets.etrago_setup.EgonPfHvGenerator>` is extended
43
44
    """
45
46
    #:
47
    name: str = "CH4Production"
48
    #:
49
50
    version: str = "0.0.8"
51
52
    def __init__(self, dependencies):
53
        super().__init__(
54
            name=self.name,
55
            version=self.version,
56
            dependencies=dependencies,
57
            tasks=(insert_ch4_generators),
58
        )
59
60
61
def load_NG_generators(scn_name="eGon2035"):
62
    """
63
    Define the fossil CH4 production units in Germany
64
65
    This function reads from the SciGRID_gas dataset the fossil CH4
66
    production units in Germany, adjuts and returns them.
67
    Natural gas production reference: SciGRID_gas dataset (datasets/gas_data/data/IGGIELGN_Production.csv
68
    downloaded in :func:`download_SciGRID_gas_data <egon.data.datasets.gas_grid.download_SciGRID_gas_data>`).
69
    For more information on these data, refer to the
70
    `SciGRID_gas IGGIELGN documentation <https://zenodo.org/record/4767098>`_.
71
72
    Parameters
73
    ----------
74
    scn_name : str
75
        Name of the scenario.
76
77
    Returns
78
    -------
79
    CH4_generators_list : pandas.DataFrame
80
        Dataframe containing the natural gas production units in Germany
81
82
    """
83
    # read carrier information from scnario parameter data
84
    scn_params = get_sector_parameters("gas", scn_name)
85
86
    target_file = (
87
        Path(".")
88
        / "datasets"
89
        / "gas_data"
90
        / "data"
91
        / "IGGIELGN_Productions.csv"
92
    )
93
94
    NG_generators_list = pd.read_csv(
95
        target_file,
96
        delimiter=";",
97
        decimal=".",
98
        usecols=["lat", "long", "country_code", "param"],
99
    )
100
101
    NG_generators_list = NG_generators_list[
102
        NG_generators_list["country_code"].str.match("DE")
103
    ]
104
105
    # Cut data to federal state if in testmode
106
    NUTS1 = []
107
    for index, row in NG_generators_list.iterrows():
108
        param = ast.literal_eval(row["param"])
109
        NUTS1.append(param["nuts_id_1"])
110
    NG_generators_list = NG_generators_list.assign(NUTS1=NUTS1)
111
112
    boundary = settings()["egon-data"]["--dataset-boundary"]
113
    if boundary != "Everything":
114
        map_states = {
115
            "Baden-Württemberg": "DE1",
116
            "Nordrhein-Westfalen": "DEA",
117
            "Hessen": "DE7",
118
            "Brandenburg": "DE4",
119
            "Bremen": "DE5",
120
            "Rheinland-Pfalz": "DEB",
121
            "Sachsen-Anhalt": "DEE",
122
            "Schleswig-Holstein": "DEF",
123
            "Mecklenburg-Vorpommern": "DE8",
124
            "Thüringen": "DEG",
125
            "Niedersachsen": "DE9",
126
            "Sachsen": "DED",
127
            "Hamburg": "DE6",
128
            "Saarland": "DEC",
129
            "Berlin": "DE3",
130
            "Bayern": "DE2",
131
        }
132
133
        NG_generators_list = NG_generators_list[
134
            NG_generators_list["NUTS1"].isin([map_states[boundary], np.nan])
135
        ]
136
137
    NG_generators_list = NG_generators_list.rename(
138
        columns={"lat": "y", "long": "x"}
139
    )
140
    NG_generators_list = gpd.GeoDataFrame(
141
        NG_generators_list,
142
        geometry=gpd.points_from_xy(
143
            NG_generators_list["x"], NG_generators_list["y"]
144
        ),
145
    )
146
    NG_generators_list = NG_generators_list.rename(
147
        columns={"geometry": "geom"}
148
    ).set_geometry("geom", crs=4326)
149
150
    # Insert p_nom
151
    p_nom = []
152
    for index, row in NG_generators_list.iterrows():
153
        param = ast.literal_eval(row["param"])
154
        p_nom.append(param["max_supply_M_m3_per_d"])
155
156
    conversion_factor = 437.5  # MCM/day to MWh/h
157
    NG_generators_list["p_nom"] = [i * conversion_factor for i in p_nom]
158
159
    # Add missing columns
160
    NG_generators_list["marginal_cost"] = scn_params["marginal_cost"]["CH4"]
161
162
    # Remove useless columns
163
    NG_generators_list = NG_generators_list.drop(
164
        columns=["x", "y", "param", "country_code", "NUTS1"]
165
    )
166
167
    return NG_generators_list
168
169
170
def download_biogas_data():
171
    """Download the biogas production units data in Germany
172
173
    Parameters
174
    ----------
175
    None
176
177
    Returns
178
    -------
179
    None
180
181
    """
182
    basename = "Biogaspartner_Einspeiseatlas_Deutschland_2021.xlsx"
183
    url = (
184
        "https://www.biogaspartner.de/fileadmin/Biogaspartner/Dokumente/Einspeiseatlas/"
185
        + basename
186
    )
187
    target_file = Path(".") / "datasets" / "gas_data" / basename
188
189
    urlretrieve(url, target_file)
190
191
192
def load_biogas_generators(scn_name):
193
    """
194
    Define the biogas production units in Germany
195
196
    This function download the Biogaspartner Einspeiseatlas into
197
    (datasets/gas_data/Biogaspartner_Einspeiseatlas_Deutschland_2021.xlsx),
198
    reads the biogas production units in Germany data, adjuts and
199
    returns them.
200
    For more information on these data refer, to the
201
    `Einspeiseatlas website <https://www.biogaspartner.de/einspeiseatlas/>`_.
202
203
    Parameters
204
    ----------
205
    scn_name : str
206
        Name of the scenario
207
208
    Returns
209
    -------
210
    CH4_generators_list : pandas.DataFrame
211
        Dataframe containing the biogas production units in Germany
212
213
    """
214
    # read carrier information from scnario parameter data
215
    scn_params = get_sector_parameters("gas", scn_name)
216
217
    basename = "Biogaspartner_Einspeiseatlas_Deutschland_2021.xlsx"
218
    target_file = Path(".") / "datasets" / "gas_data" / basename
219
220
    # Read-in data from csv-file
221
    biogas_generators_list = pd.read_excel(
222
        target_file,
223
        usecols=["Koordinaten", "Einspeisung Biomethan [(N*m^3)/h)]"],
224
    )
225
226
    x = []
227
    y = []
228
    for index, row in biogas_generators_list.iterrows():
229
        coordinates = row["Koordinaten"].split(",")
230
        y.append(coordinates[0])
231
        x.append(coordinates[1])
232
    biogas_generators_list["x"] = x
233
    biogas_generators_list["y"] = y
234
235
    biogas_generators_list = gpd.GeoDataFrame(
236
        biogas_generators_list,
237
        geometry=gpd.points_from_xy(
238
            biogas_generators_list["x"], biogas_generators_list["y"]
239
        ),
240
    )
241
    biogas_generators_list = biogas_generators_list.rename(
242
        columns={"geometry": "geom"}
243
    ).set_geometry("geom", crs=4326)
244
245
    # Connect to local database
246
    engine = db.engine()
247
248
    # Cut data to federal state if in testmode
249
    boundary = settings()["egon-data"]["--dataset-boundary"]
250
    if boundary != "Everything":
251
        db.execute_sql(
252
            """
253
              DROP TABLE IF EXISTS grid.egon_biogas_generator CASCADE;
254
            """
255
        )
256
        biogas_generators_list.to_postgis(
257
            "egon_biogas_generator",
258
            engine,
259
            schema="grid",
260
            index=False,
261
            if_exists="replace",
262
        )
263
264
        sql = """SELECT *
265
            FROM grid.egon_biogas_generator, boundaries.vg250_sta_union as vg
266
            WHERE ST_Transform(vg.geometry,4326) && egon_biogas_generator.geom
267
            AND ST_Contains(ST_Transform(vg.geometry,4326), egon_biogas_generator.geom)"""
268
269
        biogas_generators_list = gpd.GeoDataFrame.from_postgis(
270
            sql, con=engine, geom_col="geom", crs=4326
271
        )
272
        biogas_generators_list = biogas_generators_list.drop(
273
            columns=["id", "bez", "area_ha", "geometry"]
274
        )
275
        db.execute_sql(
276
            """
277
              DROP TABLE IF EXISTS grid.egon_biogas_generator CASCADE;
278
            """
279
        )
280
281
    # Insert p_nom
282
    conversion_factor = 0.01083  # m^3/h to MWh/h
283
    biogas_generators_list["p_nom"] = [
284
        i * conversion_factor
285
        for i in biogas_generators_list["Einspeisung Biomethan [(N*m^3)/h)]"]
286
    ]
287
288
    # Add missing columns
289
    biogas_generators_list["marginal_cost"] = scn_params["marginal_cost"][
290
        "biogas"
291
    ]
292
293
    # Remove useless columns
294
    biogas_generators_list = biogas_generators_list.drop(
295
        columns=["x", "y", "Koordinaten", "Einspeisung Biomethan [(N*m^3)/h)]"]
296
    )
297
    return biogas_generators_list
298
299
300
def import_gas_generators(scn_name):
301
    """
302
    Insert list of gas production units into the database
303
304
    To insert the gas production units into the database, the following
305
    steps are followed:
306
307
      * cleaning of the database table grid.egon_etrago_generator of the
308
        CH4 generators of the specific scenario (eGon2035),
309
      * call of the functions :py:func:`load_NG_generators` and
310
        :py:func:`load_biogas_generators` that respectively return
311
        dataframes containing the natural- an bio-gas production units
312
        in Germany,
313
      * attribution of the bus_id to which each generator is connected
314
        (call the function :func:`assign_gas_bus_id <egon.data.db.assign_gas_bus_id>`
315
        from :py:mod:`egon.data.db <egon.data.db>`),
316
      * aggregation of the CH4 productions with same properties at the
317
        same bus. The properties that should be the same in order that
318
        different generators are aggregated are:
319
          * scenario
320
          * carrier
321
          * marginal cost: this parameter differentiates the natural gas
322
            generators from the biogas generators,
323
      * addition of the missing columns: scn_name, carrier and
324
        generator_id,
325
      * insertion of the generators into the database.
326
327
    Parameters
328
    ----------
329
    scn_name : str
330
        Name of the scenario.
331
332
    Returns
333
    -------
334
    None
335
336
    """
337
    carrier = "CH4"
338
339
    # Connect to local database
340
    engine = db.engine()
341
342
    # Select source and target from dataset configuration
343
    source = config.datasets()["gas_prod"]["source"]
344
    target = config.datasets()["gas_prod"]["target"]
345
346
    # Clean table
347
    db.execute_sql(
348
        f"""
349
        DELETE FROM {target['stores']['schema']}.{target['stores']['table']}
350
        WHERE "carrier" = '{carrier}' AND
351
        scn_name = '{scn_name}' AND bus not IN (
352
            SELECT bus_id FROM {source['buses']['schema']}.{source['buses']['table']}
353
            WHERE scn_name = '{scn_name}' AND country != 'DE'
354
        );
355
        """
356
    )
357
358
    if scn_name == "eGon2035":
359
        CH4_generators_list = pd.concat(
360
            [load_NG_generators(scn_name), load_biogas_generators(scn_name)]
361
        )
362
363
    elif scn_name == "eGon100RE":
364
        CH4_generators_list = load_biogas_generators(scn_name)
365
        overwrite_max_gas_generation_overtheyear(scn_name)
366
367
    # Add missing columns
368
    c = {"scn_name": scn_name, "carrier": carrier}
369
    CH4_generators_list = CH4_generators_list.assign(**c)
0 ignored issues
show
introduced by
The variable CH4_generators_list does not seem to be defined for all execution paths.
Loading history...
370
371
    # Match to associated CH4 bus
372
    CH4_generators_list = db.assign_gas_bus_id(
373
        CH4_generators_list, scn_name, carrier
374
    )
375
376
    # Remove useless columns
377
    CH4_generators_list = CH4_generators_list.drop(columns=["geom", "bus_id"])
378
379
    # Aggregate ch4 productions with same properties at the same bus
380
    CH4_generators_list = (
381
        CH4_generators_list.groupby(
382
            ["bus", "carrier", "scn_name", "marginal_cost"]
383
        )
384
        .agg({"p_nom": "sum"})
385
        .reset_index(drop=False)
386
    )
387
388
    new_id = db.next_etrago_id("generator")
389
    CH4_generators_list["generator_id"] = range(
390
        new_id, new_id + len(CH4_generators_list)
391
    )
392
393
    # Insert data to db
394
    CH4_generators_list.to_sql(
395
        target["stores"]["table"],
396
        engine,
397
        schema=target["stores"]["schema"],
398
        index=False,
399
        if_exists="append",
400
    )
401
402
403
def overwrite_max_gas_generation_overtheyear(scn_name):
404
    """Overright max_gas_generation_overtheyear in scenario parameter table
405
406
    Overright max_gas_generation_overtheyear in scenario parameter
407
    table if the value of this parameter has changed in the p-e-s run.
408
409
    Parameters
410
    ----------
411
    scn_name : str
412
        Name of the scenario
413
414
    """
415
    execute_pypsa_eur_sec = True  # False
416
417
    # Select source and target from dataset configuration
418
    target = config.datasets()["gas_prod"]["target"]
419
420
    if execute_pypsa_eur_sec:
421
        n = read_network()
422
        max_value = n.stores[n.stores["carrier"] == "biogas"].loc[
423
            "DE0 0 biogas", "e_initial"
424
        ]
425
426
        parameters = db.select_dataframe(
427
            f"""
428
            SELECT *
429
            FROM {target['scenario_parameters']['schema']}.{target['scenario_parameters']['table']}
430
            WHERE name = '{scn_name}'
431
            """
432
        )
433
434
        gas_param = parameters.loc[0, "gas_parameters"]
435
        gas_param["max_gas_generation_overtheyear"] = {"biogas": max_value}
436
        gas_param = json.dumps(gas_param)
437
438
        # Update data in db
439
        db.execute_sql(
440
            f"""
441
        UPDATE {target['scenario_parameters']['schema']}.{target['scenario_parameters']['table']}
442
        SET gas_parameters = '{gas_param}'
443
        WHERE name = '{scn_name}';
444
        """
445
        )
446
447
448
def insert_ch4_generators():
449
    """Insert gas production units in database for both scenarios
450
451
    Parameters
452
    ----------
453
    None
454
455
    Returns
456
    -------
457
    None
458
    """
459
    import_gas_generators("eGon2035")
460
    import_gas_generators("eGon100RE")
461