Skip to content

Commit 32ff08a

Browse files
authored
fix: restore correct assigned countries for virtual tyndp nodes (#794)
* fix: restore correct assigned countries for virtual tyndp nodes in base_network.py * doc: add release note * feat: generalise adding virtual nodes in build_tyndp_network.py * doc: clarify docstring
1 parent 8ce80c5 commit 32ff08a

3 files changed

Lines changed: 82 additions & 22 deletions

File tree

doc/release_notes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141

4242
* Extend `tyndp-archive` support to integrate three new datasets (`desnz_electricity_consumption`, `jrc_energy_atlas`, `ons_lad`) and two new versions (`nitrogen_statistics`, `synthetic_electricity_demand`) ([#758](https://github.com/open-energy-transition/open-tyndp/pull/758)).
4343

44+
* Restore correct assigned countries for virtual TYNDP nodes ([#794](https://github.com/open-energy-transition/open-tyndp/pull/794)).
45+
4446

4547
**Documentation**
4648

scripts/base_network.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
get_snapshots,
4343
set_scenario_config,
4444
)
45+
from scripts.build_tyndp_network import AC_VIRTUAL_NODES_IT
4546

4647
PD_GE_2_2 = parse(pd.__version__) >= Version("2.2")
4748

@@ -560,6 +561,25 @@ def prefer_voltage(x, which):
560561
return buses
561562

562563

564+
def _restore_virtual_node_countries_tyndp(
565+
n: pypsa.Network, buses: pd.DataFrame
566+
) -> None:
567+
"""
568+
Restore the declared country of TYNDP virtual nodes instead of coordinate-based country assignment
569+
from `_set_countries_and_substations`.
570+
571+
Parameters
572+
----------
573+
n : pypsa.Network
574+
Network whose `buses` country assignment is corrected in place.
575+
buses : pd.DataFrame
576+
Buses as loaded in `_load_buses`, holding the countries declared in
577+
build_tyndp_network before the coordinate-based reassignment.
578+
"""
579+
nodes = n.buses.index.intersection(AC_VIRTUAL_NODES_IT)
580+
n.buses.loc[nodes, "country"] = buses.loc[nodes, "country"]
581+
582+
563583
def _replace_b2b_converter_at_country_border_by_link(n):
564584
# Affects only the B2B converter in Lithuania at the Polish border at the moment
565585
buscntry = n.buses.country
@@ -752,6 +772,8 @@ def base_network(
752772

753773
_set_countries_and_substations(n, config, country_shapes, offshore_shapes)
754774

775+
_restore_virtual_node_countries_tyndp(n, buses)
776+
755777
_set_links_underwater_fraction(n, offshore_shapes)
756778

757779
_replace_b2b_converter_at_country_border_by_link(n)

scripts/build_tyndp_network.py

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@
7878
"UK": "GB",
7979
}
8080

81+
AC_VIRTUAL_NODES_IT = {
82+
"ITCO": "FR15",
83+
"ITVI": "ITSI",
84+
}
85+
8186
IBFI_COORD = (63.0, 25.0)
8287

8388

@@ -223,6 +228,41 @@ def build_shapes(
223228
return bidding_shapes, country_shapes
224229

225230

231+
def _add_virtual_node(
232+
target_gdf: gpd.GeoDataFrame,
233+
new_bus: str,
234+
ref_bus: str,
235+
source_gdf: gpd.GeoDataFrame | None = None,
236+
**overrides,
237+
) -> None:
238+
"""
239+
Add a virtual node to target Dataframe as a copy of an existing reference bus.
240+
241+
The virtual node inherits every attribute of the reference bus and takes its
242+
own name as ``station_id`` and ``tags``. Any remaining attribute is set
243+
through ``overrides``.
244+
245+
Parameters
246+
----------
247+
target_gdf : gpd.GeoDataFrame
248+
Bus GeoDataFrame the virtual node is appended to, modified in place.
249+
new_bus : str
250+
Name of the virtual node.
251+
ref_bus : str
252+
Name of the reference bus whose attributes are copied.
253+
source_gdf : gpd.GeoDataFrame, optional
254+
Bus GeoDataFrame holding the reference bus. Defaults to None in which case ``target_gdf`` is used.
255+
**overrides, optional
256+
Attribute values overriding those inherited from the reference bus.
257+
"""
258+
source_gdf = target_gdf if source_gdf is None else source_gdf
259+
target_gdf.loc[new_bus] = (
260+
source_gdf.loc[[ref_bus]]
261+
.assign(station_id=new_bus, tags=new_bus, **overrides)
262+
.loc[ref_bus]
263+
)
264+
265+
226266
def build_buses(
227267
buses_fn: str,
228268
countries: list[str],
@@ -281,14 +321,10 @@ def build_buses(
281321

282322
# Manually add Italian virtual nodes # TODO Refine assumptions
283323
if "IT" in countries:
284-
buses.loc["ITCO"] = (
285-
buses.loc[["FR15"]]
286-
.assign(station_id="ITCO", country="IT", tags="ITCO")
287-
.loc["FR15"]
288-
)
289-
buses.loc["ITVI"] = (
290-
buses.loc[["ITSI"]].assign(station_id="ITVI", tags="ITVI").loc["ITSI"]
291-
)
324+
for node, location in AC_VIRTUAL_NODES_IT.items():
325+
_add_virtual_node(
326+
target_gdf=buses, new_bus=node, ref_bus=location, country="IT"
327+
)
292328

293329
buses_h2 = (
294330
country_shapes[["node", "x", "y"]]
@@ -309,23 +345,23 @@ def build_buses(
309345

310346
# Manually add IBIT and IBFI nodes # TODO Refine assumptions
311347
if "IT" in countries:
312-
buses_h2.loc["IBIT H2"] = (
313-
buses.loc[["ITN1"]]
314-
.assign(station_id="IBIT H2", voltage=None, dc="f", tags="IBIT H2")
315-
.loc["ITN1"]
348+
_add_virtual_node(
349+
target_gdf=buses_h2,
350+
new_bus="IBIT H2",
351+
ref_bus="ITN1",
352+
source_gdf=buses,
353+
voltage=None,
354+
dc="f",
316355
)
317356
if "FI" in countries:
318357
ibfi_lat, ibfi_long = IBFI_COORD
319-
buses_h2.loc["IBFI H2"] = (
320-
buses_h2.loc[["FI H2"]]
321-
.assign(
322-
station_id="IBFI H2",
323-
tags="IBFI H2",
324-
x=ibfi_long,
325-
y=ibfi_lat,
326-
geometry=Point(ibfi_long, ibfi_lat),
327-
)
328-
.loc["FI H2"]
358+
_add_virtual_node(
359+
target_gdf=buses_h2,
360+
new_bus="IBFI H2",
361+
ref_bus="FI H2",
362+
x=ibfi_long,
363+
y=ibfi_lat,
364+
geometry=Point(ibfi_long, ibfi_lat),
329365
)
330366

331367
return buses, buses_h2

0 commit comments

Comments
 (0)