Skip to content

Commit 828366c

Browse files
committed
Merge branch 'main' into initial_capex_bugfix
2 parents 2bc56e1 + ef93de4 commit 828366c

File tree

5 files changed

+166
-47
lines changed

5 files changed

+166
-47
lines changed

run_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
for scenario_name, scenario_df in results_df.items():
4949
if scenario_name == "bau":
5050
continue # Skip BAU for absolute value plotting
51-
filtered_results[scenario_name] = scenario_df.select(COMPARE_COLS)
51+
filtered_results[scenario_name] = scenario_df.select(["year", *COMPARE_COLS])
5252

5353
# Concatenate and transform to long format
5454
combined_df = pl.concat(

src/npa_howtopay/model.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ def run_model(scenario_params: ScenarioParams, input_params: InputParams, ts_par
478478

479479

480480
def create_delta_bau_df(results_df: dict[str, pl.DataFrame], compare_cols: list[str]) -> pl.DataFrame:
481-
bau_df = results_df["bau"].select(compare_cols)
481+
bau_df = results_df["bau"].select(["year"] + compare_cols)
482482

483483
# Create comparison DataFrames for each scenario
484484
comparison_dfs = {}
@@ -488,11 +488,9 @@ def create_delta_bau_df(results_df: dict[str, pl.DataFrame], compare_cols: list[
488488

489489
# Join with BAU to subtract values
490490
comparison_df = scenario_df.join(
491-
bau_df.select(["year"] + [col for col in compare_cols[1:]]).rename({
492-
col: f"bau_{col}" for col in compare_cols[1:]
493-
}),
491+
bau_df.select(["year"] + [col for col in compare_cols]).rename({col: f"bau_{col}" for col in compare_cols}),
494492
on="year",
495-
).select(["year", *[pl.col(col).sub(pl.col(f"bau_{col}")) for col in compare_cols[1:]]])
493+
).select(["year", *[pl.col(col).sub(pl.col(f"bau_{col}")) for col in compare_cols]])
496494

497495
comparison_dfs[scenario_name] = comparison_df
498496
delta_bau_df = pl.concat(

src/npa_howtopay/params.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
KWH_PER_THERM = 29.3071
1515

1616
COMPARE_COLS = [
17-
"year",
1817
# revenue requirement
1918
"gas_inflation_adjusted_revenue_requirement",
2019
"electric_inflation_adjusted_revenue_requirement",
@@ -214,11 +213,13 @@ def load_time_series_params_from_yaml(run_name: str, data_dir: str = "data") ->
214213
return _load_time_series_params_from_yaml(str(yaml_path))
215214

216215

217-
def load_time_series_params_from_web_params(web_params: dict, start_year: int, end_year: int) -> TimeSeriesParams:
216+
def load_time_series_params_from_web_params(
217+
web_params: dict, start_year: int, end_year: int, cost_inflation_rate: float = 0.0
218+
) -> TimeSeriesParams:
218219
"""Load time series parameters from web parameters (scalar values)"""
219220

220221
web_params_obj = WebParams(**web_params)
221-
generated_data = create_time_series_from_web_params(web_params_obj, start_year, end_year)
222+
generated_data = create_time_series_from_web_params(web_params_obj, start_year, end_year, cost_inflation_rate)
222223

223224
return TimeSeriesParams(
224225
npa_projects=generated_data["npa_projects"],

src/npa_howtopay/web_params.py

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from attrs import define
22
import polars as pl
3+
from typing import Optional
34

45

56
@define
@@ -32,34 +33,64 @@ def create_npa_projects(web_params: WebParams, start_year: int, end_year: int) -
3233
})
3334

3435

35-
def create_gas_fixed_overhead_costs(web_params: WebParams, start_year: int, end_year: int) -> pl.DataFrame:
36+
def create_gas_fixed_overhead_costs(
37+
web_params: WebParams, start_year: int, end_year: int, cost_inflation_rate: float = 0.0
38+
) -> pl.DataFrame:
39+
years = list(range(start_year, end_year + 1))
40+
base_cost = web_params.gas_fixed_overhead_costs
41+
42+
# Apply compound inflation: cost * (1 + rate)^(year - start_year)
43+
costs = [base_cost * (1 + cost_inflation_rate) ** (year - start_year) for year in years]
44+
3645
return pl.DataFrame({
37-
"year": list(range(start_year, end_year + 1)),
38-
"cost": [web_params.gas_fixed_overhead_costs] * (end_year - start_year + 1),
46+
"year": years,
47+
"cost": costs,
3948
})
4049

4150

42-
def create_electric_fixed_overhead_costs(web_params: WebParams, start_year: int, end_year: int) -> pl.DataFrame:
51+
def create_electric_fixed_overhead_costs(
52+
web_params: WebParams, start_year: int, end_year: int, cost_inflation_rate: float = 0.0
53+
) -> pl.DataFrame:
54+
years = list(range(start_year, end_year + 1))
55+
base_cost = web_params.electric_fixed_overhead_costs
56+
57+
# Apply compound inflation: cost * (1 + rate)^(year - start_year)
58+
costs = [base_cost * (1 + cost_inflation_rate) ** (year - start_year) for year in years]
59+
4360
return pl.DataFrame({
44-
"year": list(range(start_year, end_year + 1)),
45-
"cost": [web_params.electric_fixed_overhead_costs] * (end_year - start_year + 1),
61+
"year": years,
62+
"cost": costs,
4663
})
4764

4865

49-
def create_gas_bau_lpp_costs_per_year(web_params: WebParams, start_year: int, end_year: int) -> pl.DataFrame:
66+
def create_gas_bau_lpp_costs_per_year(
67+
web_params: WebParams, start_year: int, end_year: int, cost_inflation_rate: float = 0.0
68+
) -> pl.DataFrame:
69+
years = list(range(start_year, end_year + 1))
70+
base_cost = web_params.gas_bau_lpp_costs_per_year
71+
72+
# Apply compound inflation: cost * (1 + rate)^(year - start_year)
73+
costs = [base_cost * (1 + cost_inflation_rate) ** (year - start_year) for year in years]
74+
5075
return pl.DataFrame({
51-
"year": list(range(start_year, end_year + 1)),
52-
"cost": [web_params.gas_bau_lpp_costs_per_year] * (end_year - start_year + 1),
76+
"year": years,
77+
"cost": costs,
5378
})
5479

5580

5681
def create_time_series_from_web_params(
57-
web_params: WebParams, start_year: int, end_year: int
82+
web_params: WebParams, start_year: int, end_year: int, cost_inflation_rate: float = 0.0
5883
) -> dict[str, pl.DataFrame]:
5984
"""Create all time series DataFrames from web parameters"""
6085
return {
6186
"npa_projects": create_npa_projects(web_params, start_year, end_year),
62-
"gas_fixed_overhead_costs": create_gas_fixed_overhead_costs(web_params, start_year, end_year),
63-
"electric_fixed_overhead_costs": create_electric_fixed_overhead_costs(web_params, start_year, end_year),
64-
"gas_bau_lpp_costs_per_year": create_gas_bau_lpp_costs_per_year(web_params, start_year, end_year),
87+
"gas_fixed_overhead_costs": create_gas_fixed_overhead_costs(
88+
web_params, start_year, end_year, cost_inflation_rate
89+
),
90+
"electric_fixed_overhead_costs": create_electric_fixed_overhead_costs(
91+
web_params, start_year, end_year, cost_inflation_rate
92+
),
93+
"gas_bau_lpp_costs_per_year": create_gas_bau_lpp_costs_per_year(
94+
web_params, start_year, end_year, cost_inflation_rate
95+
),
6596
}

tests/test_input_load.py

Lines changed: 114 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import polars as pl
12
import pytest
3+
from polars.testing import assert_frame_equal
24

35
from npa_howtopay.params import (
46
ScenarioParams,
@@ -26,6 +28,75 @@ def web_params():
2628
}
2729

2830

31+
@pytest.fixture
32+
def expected_gas_bau_lpp_costs_no_inflation():
33+
"""Expected gas BAU LPP costs for 2025-2030 with no inflation"""
34+
return pl.DataFrame({
35+
"year": [2025, 2026, 2027, 2028, 2029, 2030],
36+
"cost": [100.0, 100.0, 100.0, 100.0, 100.0, 100.0],
37+
})
38+
39+
40+
@pytest.fixture
41+
def expected_gas_fixed_overhead_costs_no_inflation():
42+
"""Expected gas fixed overhead costs for 2025-2030 with no inflation"""
43+
return pl.DataFrame({
44+
"year": [2025, 2026, 2027, 2028, 2029, 2030],
45+
"cost": [100.0, 100.0, 100.0, 100.0, 100.0, 100.0],
46+
})
47+
48+
49+
@pytest.fixture
50+
def expected_electric_fixed_overhead_costs_no_inflation():
51+
"""Expected electric fixed overhead costs for 2025-2030 with no inflation"""
52+
return pl.DataFrame({
53+
"year": [2025, 2026, 2027, 2028, 2029, 2030],
54+
"cost": [100.0, 100.0, 100.0, 100.0, 100.0, 100.0],
55+
})
56+
57+
58+
@pytest.fixture
59+
def expected_gas_bau_lpp_costs_with_inflation():
60+
"""Expected gas BAU LPP costs for 2025-2030 with 5% inflation"""
61+
return pl.DataFrame({
62+
"year": [2025, 2026, 2027, 2028, 2029, 2030],
63+
"cost": [100.0, 105.0, 110.25, 115.7625, 121.550625, 127.62815625],
64+
})
65+
66+
67+
@pytest.fixture
68+
def expected_gas_fixed_overhead_costs_with_inflation():
69+
"""Expected gas fixed overhead costs for 2025-2030 with 5% inflation"""
70+
return pl.DataFrame({
71+
"year": [2025, 2026, 2027, 2028, 2029, 2030],
72+
"cost": [100.0, 105.0, 110.25, 115.7625, 121.550625, 127.62815625],
73+
})
74+
75+
76+
@pytest.fixture
77+
def expected_electric_fixed_overhead_costs_with_inflation():
78+
"""Expected electric fixed overhead costs for 2025-2030 with 5% inflation"""
79+
return pl.DataFrame({
80+
"year": [2025, 2026, 2027, 2028, 2029, 2030],
81+
"cost": [100.0, 105.0, 110.25, 115.7625, 121.550625, 127.62815625],
82+
})
83+
84+
85+
@pytest.fixture
86+
def expected_npa_projects():
87+
"""Expected NPA projects for 2025-2030"""
88+
return pl.DataFrame({
89+
"project_year": [2025, 2026, 2027, 2028, 2029, 2030],
90+
"num_converts": [100, 100, 100, 100, 100, 100],
91+
"pipe_value_per_user": [1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0],
92+
"pipe_decomm_cost_per_user": [100.0, 100.0, 100.0, 100.0, 100.0, 100.0],
93+
"peak_kw_winter_headroom": [10.0, 10.0, 10.0, 10.0, 10.0, 10.0],
94+
"peak_kw_summer_headroom": [10.0, 10.0, 10.0, 10.0, 10.0, 10.0],
95+
"aircon_percent_adoption_pre_npa": [0.8, 0.8, 0.8, 0.8, 0.8, 0.8],
96+
"is_scattershot": [False, False, False, False, False, False],
97+
})
98+
99+
29100
def test_load_sample_yaml():
30101
"""Test loading the sample.yaml file"""
31102
# Load the sample scenario
@@ -34,36 +105,54 @@ def test_load_sample_yaml():
34105
assert params is not None
35106

36107

37-
def test_load_web_params(web_params):
38-
"""Test loading the web params"""
39-
params = load_time_series_params_from_web_params(web_params, 2025, 2050)
40-
# Verify params has expected structure
108+
def test_load_web_params_no_inflation(
109+
web_params,
110+
expected_gas_bau_lpp_costs_no_inflation,
111+
expected_gas_fixed_overhead_costs_no_inflation,
112+
expected_electric_fixed_overhead_costs_no_inflation,
113+
expected_npa_projects,
114+
):
115+
"""Test loading the web params without inflation"""
116+
params = load_time_series_params_from_web_params(web_params, 2025, 2030)
117+
118+
# Verify params is not None
41119
assert params is not None
42120

43-
# Verify gas_bau_lpp_costs df has correct shape and columns
44-
assert params.gas_bau_lpp_costs_per_year.shape == (26, 2)
45-
assert set(params.gas_bau_lpp_costs_per_year.columns) == {"year", "cost"}
121+
# Test each DataFrame against expected values
122+
assert params.gas_bau_lpp_costs_per_year.equals(expected_gas_bau_lpp_costs_no_inflation)
123+
assert params.gas_fixed_overhead_costs.equals(expected_gas_fixed_overhead_costs_no_inflation)
124+
assert params.electric_fixed_overhead_costs.equals(expected_electric_fixed_overhead_costs_no_inflation)
125+
assert params.npa_projects.equals(expected_npa_projects)
46126

47-
# Verify gas_fixed_overhead_costs df has correct shape and columns
48-
assert params.gas_fixed_overhead_costs.shape == (26, 2)
49-
assert set(params.gas_fixed_overhead_costs.columns) == {"year", "cost"}
50127

51-
# Verify electric_fixed_overhead_costs df has correct shape and columns
52-
assert params.electric_fixed_overhead_costs.shape == (26, 2)
53-
assert set(params.electric_fixed_overhead_costs.columns) == {"year", "cost"}
128+
def test_load_web_params_with_inflation(
129+
web_params,
130+
expected_gas_bau_lpp_costs_with_inflation,
131+
expected_gas_fixed_overhead_costs_with_inflation,
132+
expected_electric_fixed_overhead_costs_with_inflation,
133+
expected_npa_projects,
134+
):
135+
"""Test loading the web params with 5% inflation"""
136+
params = load_time_series_params_from_web_params(web_params, 2025, 2030, cost_inflation_rate=0.05)
54137

55-
# Verify npa_projects df has correct shape and columns
56-
assert params.npa_projects.shape == (26, 8)
57-
assert set(params.npa_projects.columns) == {
58-
"project_year",
59-
"num_converts",
60-
"pipe_value_per_user",
61-
"pipe_decomm_cost_per_user",
62-
"peak_kw_winter_headroom",
63-
"peak_kw_summer_headroom",
64-
"aircon_percent_adoption_pre_npa",
65-
"is_scattershot",
66-
}
138+
# Verify params is not None
139+
assert params is not None
140+
141+
# Test each DataFrame against expected values with proper float tolerance
142+
assert_frame_equal(
143+
params.gas_bau_lpp_costs_per_year, expected_gas_bau_lpp_costs_with_inflation, check_exact=False, atol=1e-10
144+
)
145+
assert_frame_equal(
146+
params.gas_fixed_overhead_costs, expected_gas_fixed_overhead_costs_with_inflation, check_exact=False, atol=1e-10
147+
)
148+
assert_frame_equal(
149+
params.electric_fixed_overhead_costs,
150+
expected_electric_fixed_overhead_costs_with_inflation,
151+
check_exact=False,
152+
atol=1e-10,
153+
)
154+
# NPA projects should match exactly (no floats)
155+
assert params.npa_projects.equals(expected_npa_projects)
67156

68157

69158
def test_load_time_series_params_from_yaml():

0 commit comments

Comments
 (0)