mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
P5.6: SapResult.intermediate exposes per-end-use fuel costs
Per-end-use £/yr costs (main heating, secondary heating, hot water, pumps_fans, lighting) lifted from the inlined total_cost sum into named locals and populated on `intermediate`. §12 sweep slices can now diff each line against the spec (Table 12 unit prices, future Table 12a fractional blending, Table 12c heat-network DLF) without re-deriving the cost decomposition. Behaviour-preserving — `total_fuel_cost_gbp` reconciles bit-for-bit. 136 SAP tests pass.
This commit is contained in:
parent
44b1d0d923
commit
2104c8c2da
2 changed files with 50 additions and 5 deletions
|
|
@ -302,13 +302,20 @@ def calculate_sap_from_inputs(inputs: CalculatorInputs) -> SapResult:
|
|||
+ inputs.lighting_kwh_per_yr
|
||||
)
|
||||
pv_credit = inputs.pv_generation_kwh_per_yr * inputs.pv_export_credit_gbp_per_kwh
|
||||
main_heating_cost = main_fuel_kwh * inputs.space_heating_fuel_cost_gbp_per_kwh
|
||||
secondary_heating_cost = (
|
||||
secondary_fuel_kwh * inputs.secondary_heating_fuel_cost_gbp_per_kwh
|
||||
)
|
||||
hot_water_cost = inputs.hot_water_kwh_per_yr * inputs.hot_water_fuel_cost_gbp_per_kwh
|
||||
pumps_fans_cost = inputs.pumps_fans_kwh_per_yr * inputs.other_fuel_cost_gbp_per_kwh
|
||||
lighting_cost = inputs.lighting_kwh_per_yr * inputs.other_fuel_cost_gbp_per_kwh
|
||||
total_cost = max(
|
||||
0.0,
|
||||
main_fuel_kwh * inputs.space_heating_fuel_cost_gbp_per_kwh
|
||||
+ secondary_fuel_kwh * inputs.secondary_heating_fuel_cost_gbp_per_kwh
|
||||
+ inputs.hot_water_kwh_per_yr * inputs.hot_water_fuel_cost_gbp_per_kwh
|
||||
+ (inputs.pumps_fans_kwh_per_yr + inputs.lighting_kwh_per_yr)
|
||||
* inputs.other_fuel_cost_gbp_per_kwh
|
||||
main_heating_cost
|
||||
+ secondary_heating_cost
|
||||
+ hot_water_cost
|
||||
+ pumps_fans_cost
|
||||
+ lighting_cost
|
||||
- pv_credit,
|
||||
)
|
||||
ecf = energy_cost_factor(total_cost_gbp=total_cost, total_floor_area_m2=tfa)
|
||||
|
|
@ -352,6 +359,11 @@ def calculate_sap_from_inputs(inputs: CalculatorInputs) -> SapResult:
|
|||
"internal_gains_annual_avg_w": sum(e.internal_gains_w for e in monthly) / 12.0,
|
||||
"mean_internal_temp_annual_avg_c": sum(e.internal_temp_c for e in monthly) / 12.0,
|
||||
"useful_space_heating_kwh_per_yr": space_heating_kwh,
|
||||
"main_heating_cost_gbp": main_heating_cost,
|
||||
"secondary_heating_cost_gbp": secondary_heating_cost,
|
||||
"hot_water_cost_gbp": hot_water_cost,
|
||||
"pumps_fans_cost_gbp": pumps_fans_cost,
|
||||
"lighting_cost_gbp": lighting_cost,
|
||||
}
|
||||
|
||||
return SapResult(
|
||||
|
|
|
|||
|
|
@ -229,6 +229,39 @@ def test_calculate_exposes_useful_space_heating_kwh() -> None:
|
|||
)
|
||||
|
||||
|
||||
def test_calculate_exposes_per_end_use_fuel_costs() -> None:
|
||||
# Arrange — P5 trace mode: per-end-use fuel costs (§12 / Table 12) break
|
||||
# out on `intermediate` so the §12 sweep can diff main vs hot water vs
|
||||
# pumps/fans vs lighting individually rather than against the bundled
|
||||
# `total_fuel_cost_gbp`. Secondary heating cost is also surfaced even
|
||||
# though §11 omitted it — the field exists on the calculator and is a
|
||||
# named worksheet variable.
|
||||
inputs = _baseline_inputs()
|
||||
|
||||
# Act
|
||||
result = calculate_sap_from_inputs(inputs)
|
||||
|
||||
# Assert
|
||||
main_cost = (
|
||||
result.main_heating_fuel_kwh_per_yr * inputs.space_heating_fuel_cost_gbp_per_kwh
|
||||
)
|
||||
secondary_cost = (
|
||||
result.secondary_heating_fuel_kwh_per_yr
|
||||
* inputs.secondary_heating_fuel_cost_gbp_per_kwh
|
||||
)
|
||||
hot_water_cost = inputs.hot_water_kwh_per_yr * inputs.hot_water_fuel_cost_gbp_per_kwh
|
||||
pumps_cost = inputs.pumps_fans_kwh_per_yr * inputs.other_fuel_cost_gbp_per_kwh
|
||||
lighting_cost = inputs.lighting_kwh_per_yr * inputs.other_fuel_cost_gbp_per_kwh
|
||||
|
||||
assert result.intermediate["main_heating_cost_gbp"] == pytest.approx(main_cost, rel=1e-9)
|
||||
assert result.intermediate["secondary_heating_cost_gbp"] == pytest.approx(
|
||||
secondary_cost, rel=1e-9
|
||||
)
|
||||
assert result.intermediate["hot_water_cost_gbp"] == pytest.approx(hot_water_cost, rel=1e-9)
|
||||
assert result.intermediate["pumps_fans_cost_gbp"] == pytest.approx(pumps_cost, rel=1e-9)
|
||||
assert result.intermediate["lighting_cost_gbp"] == pytest.approx(lighting_cost, rel=1e-9)
|
||||
|
||||
|
||||
def test_higher_main_heating_efficiency_reduces_fuel_use() -> None:
|
||||
# Arrange — Direction check: doubling the boiler efficiency must halve
|
||||
# the main-heating fuel kWh, holding everything else constant.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue