Skip to content

Commit 7f7d4fe

Browse files
committed
refactor: refactor clean_projects to reduce redundancy in outputs
1 parent 0a209a3 commit 7f7d4fe

3 files changed

Lines changed: 65 additions & 38 deletions

File tree

scripts/cba/clean_projects.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
}
7272

7373

74-
def read_tyndp_electricity_buses(buses_fn: str):
74+
def read_tyndp_electricity_buses(buses_fn: str) -> pd.Index:
7575
"""
7676
Read node list for electricity from tyndp data input.
7777
@@ -392,12 +392,29 @@ def normalize_yes_no(value: str) -> str:
392392

393393

394394
def compute_method(flag: str) -> str:
395-
return "TOOT" if flag == "yes" else "PINT"
395+
return "toot" if flag == "yes" else "pint"
396396

397397

398398
def build_method_assignments(
399399
guidelines: pd.DataFrame, projects: pd.DataFrame
400400
) -> pd.DataFrame:
401+
"""
402+
Define the method to apply on a project. The method can be PINT (default) or TOOT and can verify on
403+
the planning horizon (2030 or 2040).
404+
405+
Parameters
406+
----------
407+
guidelines : pd.DataFrame
408+
Overview of the projects included in the reference grid, as defined in the Implementation Guidelines.
409+
projects : pd.DataFrame
410+
List of projects.
411+
412+
Returns
413+
-------
414+
pd.DataFrame
415+
List of the method to apply on projects.
416+
417+
"""
401418
guidelines = guidelines.rename(
402419
columns={
403420
"ID": "project_id",
@@ -441,14 +458,14 @@ def build_method_assignments(
441458
"in_ref_grid_2030": "no",
442459
"in_ref_grid_2040": "no",
443460
"planning_horizon": horizon,
444-
"method": "PINT",
461+
"method": "pint",
445462
}
446463
)
447464
rows = pd.concat([rows, missing_rows], ignore_index=True)
448465
assigned.append(rows)
449466

450467
assigned = pd.concat(assigned, ignore_index=True)
451-
return projects.merge(assigned, on="project_id", how="left")
468+
return assigned
452469

453470

454471
if __name__ == "__main__":

scripts/cba/make_indicators.py

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import pypsa
3636

3737
from scripts._helpers import configure_logging, set_scenario_config
38+
from scripts.cba.prepare_project import load_method
3839
from scripts.prepare_sector_network import get
3940

4041
logger = logging.getLogger(__name__)
@@ -130,18 +131,6 @@ def calculate_total_system_cost(
130131
}
131132

132133

133-
def check_method(method: str) -> str:
134-
"""
135-
Normalize and validate the CBA method name.
136-
137-
If the method is not recognized as either "pint" or "toot", a ValueError is raised.
138-
"""
139-
method = method.lower()
140-
if method not in ["pint", "toot"]:
141-
raise ValueError(f"Method must be 'pint' or 'toot', got: {method}")
142-
return method
143-
144-
145134
def difference_by_method(reference: float, project: float, method: str) -> float:
146135
"""
147136
Calculate differences for indicator values by method (TOOT / PINT).
@@ -999,16 +988,7 @@ def load_benchmark_rows(
999988
# Detect method from assignments (toot or pint)
1000989
cba_project = snakemake.wildcards.cba_project
1001990
project_id = int(cba_project[1:])
1002-
methods = pd.read_csv(snakemake.input.methods)
1003-
method_row = methods[
1004-
(methods["project_id"] == project_id)
1005-
& (methods["planning_horizon"] == planning_horizon)
1006-
]
1007-
if method_row.empty:
1008-
raise ValueError(
1009-
f"Missing CBA method for project {project_id} and horizon {planning_horizon}"
1010-
)
1011-
method = check_method(method_row["method"].iloc[0])
991+
method = load_method(snakemake.input.methods, project_id, planning_horizon)
1012992

1013993
# Calculate indicators
1014994
indicators = {}

scripts/cba/prepare_project.py

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,38 @@
2020
logger = logging.getLogger(__name__)
2121

2222

23-
def load_method(methods: pd.DataFrame, project_id: int, planning_horizon: int) -> str:
23+
def check_method(method: str) -> str:
24+
"""
25+
Normalize and validate the CBA method name.
26+
27+
If the method is not recognized as either "pint" or "toot", a ValueError is raised.
28+
"""
29+
method = method.lower().strip()
30+
if method not in ["pint", "toot"]:
31+
raise ValueError(f"Method must be 'pint' or 'toot', got: {method}")
32+
return method
33+
34+
35+
def load_method(methods_fn: str, project_id: int, planning_horizon: int) -> str:
36+
"""
37+
Load the method for a specific project and planning horizon.
38+
39+
Parameters
40+
----------
41+
methods_fn : str
42+
Path to the file defining the methods.
43+
project_id : int
44+
Project reference ID.
45+
planning_horizon : int
46+
Planning horizon
47+
48+
Returns
49+
-------
50+
str
51+
Method to be used to assess a project at a planning horizon.
52+
53+
"""
54+
methods = pd.read_csv(methods_fn)
2455
row = methods[
2556
(methods["project_id"] == project_id)
2657
& (methods["planning_horizon"] == planning_horizon)
@@ -29,7 +60,7 @@ def load_method(methods: pd.DataFrame, project_id: int, planning_horizon: int) -
2960
raise ValueError(
3061
f"Missing CBA method for project {project_id} and horizon {planning_horizon}"
3162
)
32-
return str(row["method"].iloc[0]).strip().upper()
63+
return check_method(row["method"].iloc[0])
3364

3465

3566
def apply_toot(
@@ -145,38 +176,37 @@ def apply_pint(
145176
configure_logging(snakemake)
146177
set_scenario_config(snakemake)
147178

148-
n = pypsa.Network(snakemake.input.network)
179+
cba_project = snakemake.wildcards.cba_project
180+
planning_horizon = int(snakemake.wildcards.planning_horizons)
181+
methods_fn = snakemake.input.methods
149182
transmission_projects = pd.read_csv(snakemake.input.transmission_projects)
150-
methods = pd.read_csv(snakemake.input.methods)
183+
n = pypsa.Network(snakemake.input.network)
184+
hurdle_costs = snakemake.params.hurdle_costs
185+
costs = pd.read_csv(snakemake.input.costs, index_col=0)
151186

152-
cba_project = snakemake.wildcards.cba_project
153187
project_id = int(cba_project[1:])
154-
planning_horizon = int(snakemake.wildcards.planning_horizons)
155188
if planning_horizon not in [2030, 2040]:
156189
logger.warning(
157190
"CBA methods are only available for 2030 or 2040. Using 2040 for planning horizon %s.",
158191
snakemake.wildcards.planning_horizons,
159192
)
160193
planning_horizon = 2040
161194

162-
method = load_method(methods, project_id, planning_horizon)
163-
hurdle_costs = snakemake.params.hurdle_costs
195+
method = load_method(methods_fn, project_id, planning_horizon)
164196
negative_toot_capacity = snakemake.config["cba"].get(
165197
"negative_toot_capacity", "zero"
166198
)
167199

168-
costs = pd.read_csv(snakemake.input.costs, index_col=0)
169-
170200
transmission_project = transmission_projects[
171201
transmission_projects["project_id"] == project_id
172202
]
173203
assert not transmission_project.empty, (
174204
f"Transmission project {project_id} not found."
175205
)
176206

177-
if method == "TOOT":
207+
if method == "toot":
178208
apply_toot(n, transmission_project, negative_toot_capacity)
179-
elif method == "PINT":
209+
elif method == "pint":
180210
apply_pint(n, transmission_project, hurdle_costs, costs)
181211
else:
182212
raise ValueError(f"Unknown method {method} for project {project_id}")

0 commit comments

Comments
 (0)