Passed
Pull Request — dev (#1273)
by
unknown
02:20
created

data.datasets.fix_ehv_subnetworks.add_line()   B

Complexity

Conditions 3

Size

Total Lines 54
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 40
dl 0
loc 54
rs 8.92
c 0
b 0
f 0
cc 3
nop 7

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
"""The central module containing all code dealing with fixing ehv subnetworks
2
"""
3
import geopandas as gpd
4
import numpy as np
5
import pandas as pd
6
7
from egon.data import config, db, logger
8
from egon.data.config import settings
9
from egon.data.datasets import Dataset
10
from egon.data.datasets.etrago_setup import link_geom_from_buses
11
from egon.data.datasets.scenario_parameters import get_sector_parameters
12
13
14
class FixEhvSubnetworks(Dataset):
15
    """
16
    Manually fix grid topology in the extra high voltage grid to avoid subnetworks
17
18
    This dataset includes fixes for the topology of the German extra high voltage grid.
19
    The initial grid topology from openstreetmap resp. osmTGmod includes some issues,  eg. because of
20
    incomplete data. Thsi dataset does not fix all those issues, but deals only with subnetworks
21
    in the extra high voltage grid that would result into problems in the grid optimisation.
22
23
24
    *Dependencies*
25
      * :py:class:`Osmtgmod <egon.data.datasets.osmtgmod.Osmtgmod>`
26
27
28
    *Resulting tables*
29
      * :py:class:`grid.egon_etrago_bus <egon.data.datasets.etrago_setup.EgonPfHvBus>` is updated
30
      * :py:class:`grid.egon_etrago_line <egon.data.datasets.etrago_setup.EgonPfHvLine>` is updated
31
      * :py:class:`grid.egon_etrago_transformer <egon.data.datasets.etrago_setup.EgonPfHvTransformer>` is updated
32
33
    """
34
35
    #:
36
    name: str = "FixEhvSubnetworks"
37
    #:
38
    version: str = "0.0.2"
39
40
    def __init__(self, dependencies):
41
        super().__init__(
42
            name=self.name,
43
            version=self.version,
44
            dependencies=dependencies,
45
            tasks=run,
46
        )
47
48
49
def select_bus_id(x, y, v_nom, scn_name, carrier, find_closest=False):
50
    bus_id = db.select_dataframe(
51
        f"""
52
        SELECT bus_id
53
        FROM grid.egon_etrago_bus
54
        WHERE x = {x}
55
        AND y = {y}
56
        AND v_nom = {v_nom}
57
        AND scn_name = '{scn_name}'
58
        AND carrier = '{carrier}'
59
        """
60
    )
61
62
    if bus_id.empty:
63
        logger.info("No bus found")
64
        if find_closest:
65
            logger.info(f"Finding closest to x = {x}, y = {y}")
66
            bus_id = db.select_dataframe(
67
                f"""
68
            SELECT bus_id, st_distance(geom, 'SRID=4326;POINT({x} {y})'::geometry)
69
            FROM grid.egon_etrago_bus
70
            WHERE v_nom = {v_nom}
71
            AND scn_name = '{scn_name}'
72
            AND carrier = '{carrier}'
73
            ORDER BY st_distance
74
            Limit 1
75
            """
76
            )
77
            logger.info(f"Bus ID = {bus_id.bus_id[0]} selected")
78
            return bus_id.bus_id[0]
79
        else:
80
            logger.info("Find closest == False.")
81
            return None
82
    else:
83
        logger.info(f"Exact match with bus ID = {bus_id.bus_id[0]} found.")
84
        return bus_id.bus_id[0]
85
86
87
def add_bus(x, y, v_nom, scn_name):
88
    df = pd.DataFrame(
89
        index=[db.next_etrago_id("bus")],
90
        data={
91
            "scn_name": scn_name,
92
            "v_nom": v_nom,
93
            "x": x,
94
            "y": y,
95
            "carrier": "AC",
96
        },
97
    )
98
    gdf = gpd.GeoDataFrame(
99
        df, geometry=gpd.points_from_xy(df.x, df.y, crs=4326)
100
    ).rename_geometry("geom")
101
102
    gdf.index.name = "bus_id"
103
104
    gdf.reset_index().to_postgis(
105
        "egon_etrago_bus", schema="grid", con=db.engine(), if_exists="append"
106
    )
107
108
109
def drop_bus(x, y, v_nom, scn_name):
110
    bus = select_bus_id(x, y, v_nom, scn_name, carrier="AC")
111
112
    if bus is not None:
113
        db.execute_sql(
114
            f"""
115
            DELETE FROM grid.egon_etrago_bus
116
            WHERE
117
            scn_name = '{scn_name}'
118
            AND bus_id = {bus}
119
            AND v_nom = {v_nom}
120
            AND carrier = 'AC'
121
            """
122
        )
123
124
125
def add_line(x0, y0, x1, y1, v_nom, scn_name, cables):
126
    parameters = get_sector_parameters("electricity", scenario=scn_name)
127
    bus0 = select_bus_id(
128
        x0, y0, v_nom, scn_name, carrier="AC", find_closest=True
129
    )
130
    bus1 = select_bus_id(
131
        x1, y1, v_nom, scn_name, carrier="AC", find_closest=True
132
    )
133
134
    df = pd.DataFrame(
135
        index=[db.next_etrago_id("line")],
136
        data={
137
            "bus0": bus0,
138
            "bus1": bus1,
139
            "scn_name": scn_name,
140
            "v_nom": v_nom,
141
            "cables": cables,
142
            "carrier": "AC",
143
        },
144
    )
145
146
    gdf = link_geom_from_buses(df, scn_name)
147
148
    gdf["length"] = gdf.to_crs(3035).topo.length.mul(1e-3)
149
150
    # all the values used for x, r and b are taken from the electrical values
151
    # reference table from oemtgmod: github.com/wupperinst/osmTGmod
152
    if v_nom == 220:
153
        s_nom = 520
154
        x_per_km = 0.001 * 2 * np.pi * 50
155
        r_per_km = 0.05475
156
        b_per_km = 11 * 2 * np.pi * 50 * 1e-9
157
        cost_per_km = parameters["capital_cost"]["ac_ehv_overhead_line"]
158
159
    elif v_nom == 380:
160
        s_nom = 1790
161
        x_per_km = 0.0008 * 2 * np.pi * 50
162
        r_per_km = 0.027375
163
        b_per_km = 14 * 2 * np.pi * 50 * 1e-9
164
        cost_per_km = parameters["capital_cost"]["ac_ehv_overhead_line"]
165
166
    gdf["s_nom"] = s_nom * gdf["cables"] / 3
0 ignored issues
show
introduced by
The variable s_nom does not seem to be defined for all execution paths.
Loading history...
167
    gdf["s_nom_extendable"] = True
168
    gdf["lifetime"] = parameters["lifetime"]["ac_ehv_overhead_line"]
169
    gdf["s_nom_min"] = s_nom * gdf["cables"] / 3
170
171
    gdf["x"] = (x_per_km * gdf["length"]) / (gdf["cables"] / 3)
0 ignored issues
show
introduced by
The variable x_per_km does not seem to be defined for all execution paths.
Loading history...
172
    gdf["r"] = (r_per_km * gdf["length"]) / (gdf["cables"] / 3)
0 ignored issues
show
introduced by
The variable r_per_km does not seem to be defined for all execution paths.
Loading history...
173
    gdf["b"] = (b_per_km * gdf["length"]) * (gdf["cables"] / 3)
0 ignored issues
show
introduced by
The variable b_per_km does not seem to be defined for all execution paths.
Loading history...
174
175
    gdf["capital_cost"] = (cost_per_km * gdf["length"]) * (gdf["cables"] / 3)
0 ignored issues
show
introduced by
The variable cost_per_km does not seem to be defined for all execution paths.
Loading history...
176
    gdf.index.name = "line_id"
177
    gdf.reset_index().to_postgis(
178
        "egon_etrago_line", schema="grid", con=db.engine(), if_exists="append"
179
    )
180
181
182
def drop_line(x0, y0, x1, y1, v_nom, scn_name):
183
    bus0 = select_bus_id(x0, y0, v_nom, scn_name, carrier="AC")
184
    bus1 = select_bus_id(x1, y1, v_nom, scn_name, carrier="AC")
185
186
    if (bus0 is not None) and (bus1 is not None):
187
        db.execute_sql(
188
            f"""
189
            DELETE FROM grid.egon_etrago_line
190
            WHERE
191
            scn_name = '{scn_name}'
192
            AND bus0 = {bus0}
193
            AND bus1 = {bus1}
194
            AND v_nom = {v_nom}
195
            """
196
        )
197
198
199
def add_trafo(x, y, v_nom0, v_nom1, scn_name, n=1):
200
    bus0 = select_bus_id(
201
        x, y, v_nom0, scn_name, carrier="AC", find_closest=True
202
    )
203
    bus1 = select_bus_id(
204
        x, y, v_nom1, scn_name, carrier="AC", find_closest=True
205
    )
206
207
    df = pd.DataFrame(
208
        index=[db.next_etrago_id("line")],
209
        data={
210
            "bus0": bus0,
211
            "bus1": bus1,
212
            "scn_name": scn_name,
213
        },
214
    )
215
216
    gdf = link_geom_from_buses(df, scn_name)
217
218
    if (v_nom0 == 220) & (v_nom1 == 380):
219
        s_nom = 600
220
        x = 0.0002
221
222
    gdf["s_nom"] = s_nom * n
0 ignored issues
show
introduced by
The variable s_nom does not seem to be defined in case v_nom0 == 220 & v_nom1 == 380 on line 218 is False. Are you sure this can never be the case?
Loading history...
223
224
    gdf["x"] = x / n
225
226
    gdf.index.name = "trafo_id"
227
228
    gdf.reset_index().to_postgis(
229
        "egon_etrago_transformer",
230
        schema="grid",
231
        con=db.engine(),
232
        if_exists="append",
233
    )
234
235
236
def drop_trafo(x, y, v_nom0, v_nom1, scn_name):
237
    bus0 = select_bus_id(x, y, v_nom0, scn_name, carrier="AC")
238
    bus1 = select_bus_id(x, y, v_nom1, scn_name, carrier="AC")
239
240
    if (bus0 is not None) and (bus1 is not None):
241
        db.execute_sql(
242
            f"""
243
            DELETE FROM grid.egon_etrago_transformer
244
            WHERE
245
            scn_name = '{scn_name}'
246
            AND bus0 = {bus0}
247
            AND bus1 = {bus1}
248
            """
249
        )
250
251
252
def fix_subnetworks(scn_name):
253
    # Missing 220kV line to Lübeck Siems
254
    # add 220kV bus at substation Lübeck Siems
255
    add_bus(10.760835327266625, 53.90974536547805, 220, scn_name)
256
    # add 220/380kV transformer at substation Lübeck Siems
257
    add_trafo(10.760835327266625, 53.90974536547805, 220, 380, scn_name)
258
259
    # add 220kV line from Umspannwerk Lübeck to Lübeck Siems
260
    add_line(
261
        10.760835327266625,  # Lübeck Siems
262
        53.90974536547805,
263
        10.641467804496818,  # Umspannwerk Lübeck
264
        53.91916269128779,
265
        220,
266
        scn_name,
267
        3,
268
    )
269
270
    if settings()["egon-data"]["--dataset-boundary"] == "Everything":
271
        # Missing line from USW Uchtelfangen to 'Kraftwerk Weiher'
272
        add_line(
273
            7.032657738999395,  # Kraftwerk Weiher
274
            49.33473737285781,
275
            6.996454674906,  # Umspannwerk Uchtelfangen
276
            49.3754149606116,
277
            220,
278
            scn_name,
279
            6,
280
        )
281
        if (
282
            select_bus_id(
283
                12.85381530378627, 48.764209444817745, 380, scn_name, "AC"
284
            )
285
            != None
286
        ):
287
            # Missing line from Umspannwerk Plottling to Gänsdorf UW
288
            add_line(
289
                12.85381530378627,  # Umspannwerk Plottling
290
                48.764209444817745,
291
                12.769768646403532,  # Gänsdorf UW
292
                48.80533685376445,
293
                380,
294
                scn_name,
295
                3,
296
            )
297
298
        # Missing line inside Ottstedt
299
        add_line(
300
            11.4295305,  # Ottstedt
301
            50.9115176,
302
            11.4299277,  # Ottstedt
303
            50.911449600000005,
304
            380,
305
            scn_name,
306
            3,
307
        )
308
309
310
def run():
311
    for scenario in config.settings()["egon-data"]["--scenarios"]:
312
        fix_subnetworks(scenario)
313