Skip to content

Commit 79ab8b5

Browse files
committed
fix: change input to bus csv instead of pypsa network
1 parent c75fd3e commit 79ab8b5

2 files changed

Lines changed: 68 additions & 21 deletions

File tree

rules/cba.smk

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -155,22 +155,12 @@ def input_clustered_network(w):
155155
return fill_wildcards(rules.cluster_network.output.network, clusters=clusters)
156156

157157

158-
def input_sb_network(w, run=None, planning_horizons=None):
159-
"""
160-
Return the path to the SB network used as CBA input.
161-
162-
`planning_horizons` overrides `w.planning_horizons`, needed for rules
163-
(e.g. `clean_projects`) that have no `planning_horizons` wildcard of
164-
their own and must pin one explicitly.
165-
"""
158+
def input_sb_network(w, run=None):
166159
scenario = config_provider("scenario")(w)
167160
(clusters,) = scenario["clusters"]
168161
(opts,) = scenario["opts"]
169162
(sector_opts,) = scenario["sector_opts"]
170163

171-
if planning_horizons is None:
172-
planning_horizons = int(w.planning_horizons)
173-
174164
if config_provider("cba", "cba_scenario_input", "use_presolved", default=False)(w):
175165
scenario_name = config_provider("tyndp_scenario")(w)
176166
if scenario_name != "NT":
@@ -185,7 +175,7 @@ def input_sb_network(w, run=None, planning_horizons=None):
185175
"the Zenodo network naming (base_s_all___{planning_horizons}.nc)."
186176
)
187177
horizon = _effective_horizon(
188-
planning_horizons,
178+
int(w.planning_horizons),
189179
warn_fn=logger.warning,
190180
msg=(
191181
"Pre-solved SB networks are only available for 2030 and 2040. "
@@ -207,7 +197,7 @@ def input_sb_network(w, run=None, planning_horizons=None):
207197
expanded_wildcards["planning_horizons"] = "all"
208198
case "myopic":
209199
expanded_wildcards["planning_horizons"] = _effective_horizon(
210-
planning_horizons,
200+
int(w.planning_horizons),
211201
warn_fn=logger.warning,
212202
msg=(
213203
"CBA planning horizon %s is not supported for SB inputs. "
@@ -231,9 +221,8 @@ def input_sb_network(w, run=None, planning_horizons=None):
231221
checkpoint clean_projects:
232222
input:
233223
dir=rules.retrieve_tyndp_cba_projects.output.dir,
234-
network=lambda w: input_sb_network(
235-
w, planning_horizons=config_provider("scenario", "planning_horizons")(w)[0]
236-
),
224+
buses=rules.retrieve_tyndp.output.nodes,
225+
offshore_buses=rules.retrieve_tyndp.output.offshore_nodes,
237226
guidelines=rules.retrieve_cba_guidelines_reference_projects.output.file,
238227
offshore_hub_corrections="data/cba/offshore_hub_projects_corrections.csv",
239228
output:

scripts/cba/clean_projects.py

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
from pathlib import Path
4040

4141
import pandas as pd
42-
import pypsa
4342

4443
from scripts._helpers import configure_logging, set_scenario_config
4544

@@ -99,6 +98,64 @@ def extract_investment_attributes(excel_path: Path) -> pd.DataFrame:
9998
return agg
10099

101100

101+
def read_tyndp_electricity_buses(
102+
buses_fn: str, col: str, virtual_buses: list[str] | None = None
103+
) -> pd.Index:
104+
"""
105+
Read node list for electricity from tyndp data input.
106+
107+
Parameters
108+
----------
109+
- buses_fn (str): Path to a TYNDP node list Excel file ("LIST OF NODES.xlsx"
110+
or offshore hubs "NODE.xlsx")
111+
- col (str): Column which is selected from the dataframe
112+
- virtual_buses (list): list of virtual buses to add, not present in
113+
the raw node list (e.g. added later in build_tyndp_network.py)
114+
115+
Returns
116+
-------
117+
- buses: Index of electricity buses as used in Open-TYNDP
118+
119+
See Also
120+
--------
121+
build_tyndp_network.py : build_buses
122+
build_tyndp_offshore_hubs.py : load_offshore_hubs
123+
"""
124+
virtual_buses = virtual_buses if virtual_buses is not None else []
125+
126+
buses = pd.read_excel(buses_fn).replace("UK", "GB", regex=True).set_index(col)
127+
128+
if "OFFSHORE_NODE_TYPE" in buses.columns:
129+
# drop radial offshore nodes, which are not built as hub buses
130+
buses = buses[buses.OFFSHORE_NODE_TYPE != "Radial"]
131+
132+
return buses.index.union(virtual_buses)
133+
134+
135+
def get_existing_buses(buses_fn: str, offshore_buses_fn: str | list) -> pd.Index:
136+
"""
137+
Return the electricity bus universe used to validate CBA project borders.
138+
139+
Offshore hub buses (AC_OH carrier) only get created inside
140+
prepare_sector_network.py, so they can't be read off a built network here
141+
without depending on the (downstream) SB network and creating a cycle
142+
with fix_reference_sb_to_cba's use of clean_projects's own output. They
143+
are instead read directly from the raw offshore hub node list, mirroring
144+
build_tyndp_offshore_hubs.py's own node filtering.
145+
"""
146+
existing_buses = read_tyndp_electricity_buses(
147+
buses_fn, col="NODE", virtual_buses=["ITCO", "ITVI"]
148+
)
149+
150+
if offshore_buses_fn:
151+
existing_oh_buses = read_tyndp_electricity_buses(
152+
offshore_buses_fn, col="OFFSHORE_NODE"
153+
)
154+
existing_buses = existing_buses.union(existing_oh_buses)
155+
156+
return existing_buses
157+
158+
102159
def apply_offshore_hub_corrections(
103160
hub_corrections_path: Path, projects: pd.DataFrame
104161
) -> pd.DataFrame:
@@ -198,10 +255,9 @@ def extract_transmission_projects(
198255

199256
# Several projects have capacities with "Up to ..."
200257
cols = ["p_nom 0->1", "p_nom 1->0"]
201-
# Several projects have capacities with "Up to ..."
202258
up_to_projects = set()
203259
for col in cols:
204-
up_to = projects[col].str.startswith("Up to ")
260+
up_to = projects[col].str.startswith("Up to ", na=False)
205261
if up_to.any():
206262
projects.loc[up_to, col] = projects.loc[up_to, col].str[len("Up to ") :]
207263
up_to_projects.update(projects.loc[up_to, "project_name"])
@@ -296,6 +352,7 @@ def build_method_assignments(
296352
return projects.merge(assigned, on="project_id", how="left")
297353

298354

355+
# %%
299356
if __name__ == "__main__":
300357
if "snakemake" not in globals():
301358
from scripts._helpers import mock_snakemake
@@ -308,8 +365,9 @@ def build_method_assignments(
308365
set_scenario_config(snakemake)
309366

310367
# get existing buses
311-
n = pypsa.Network(snakemake.input.network)
312-
existing_buses = n.buses[n.buses.carrier.isin(["AC", "AC_OH"])].index
368+
existing_buses = get_existing_buses(
369+
snakemake.input.buses, snakemake.input.offshore_buses
370+
)
313371

314372
excel_path = Path(snakemake.input.dir) / "20250312_export_transmission.xlsx"
315373
hub_corrections_path = snakemake.input.offshore_hub_corrections

0 commit comments

Comments
 (0)