Passed
Pull Request — dev (#931)
by
unknown
01:41
created

data.datasets.ch4_prod.download_biogas_data()   A

Complexity

Conditions 1

Size

Total Lines 20
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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