diff --git a/packages/domain/src/domain/sap/calculator.py b/packages/domain/src/domain/sap/calculator.py index 0c156691..7b221780 100644 --- a/packages/domain/src/domain/sap/calculator.py +++ b/packages/domain/src/domain/sap/calculator.py @@ -43,7 +43,12 @@ from domain.sap.worksheet.dimensions import Dimensions from domain.sap.worksheet.heat_transmission import HeatTransmission from domain.sap.worksheet.internal_gains import internal_gains_w from domain.sap.worksheet.mean_internal_temperature import mean_internal_temperature_c -from domain.sap.worksheet.rating import energy_cost_factor, sap_rating, sap_rating_integer +from domain.sap.worksheet.rating import ( + ENERGY_COST_DEFLATOR, + energy_cost_factor, + sap_rating, + sap_rating_integer, +) from domain.sap.worksheet.solar_gains import ( Orientation, surface_solar_flux_w_per_m2, @@ -364,6 +369,8 @@ def calculate_sap_from_inputs(inputs: CalculatorInputs) -> SapResult: "hot_water_cost_gbp": hot_water_cost, "pumps_fans_cost_gbp": pumps_fans_cost, "lighting_cost_gbp": lighting_cost, + "ecf": ecf, + "deflator": ENERGY_COST_DEFLATOR, } return SapResult( diff --git a/packages/domain/src/domain/sap/tests/test_calculator.py b/packages/domain/src/domain/sap/tests/test_calculator.py index 609b30cb..af44af50 100644 --- a/packages/domain/src/domain/sap/tests/test_calculator.py +++ b/packages/domain/src/domain/sap/tests/test_calculator.py @@ -262,6 +262,22 @@ def test_calculate_exposes_per_end_use_fuel_costs() -> None: assert result.intermediate["lighting_cost_gbp"] == pytest.approx(lighting_cost, rel=1e-9) +def test_calculate_exposes_ecf_and_deflator() -> None: + # Arrange — P5 trace mode: ECF (the rating denominator) and the §13 + # Table 12 deflator (0.36) surface on `intermediate`. ECF mirrors the + # top-level field; deflator is the only fixed worksheet constant the + # SAP rating depends on, so naming it lets future rating-equation + # sweep slices reference it explicitly. + inputs = _baseline_inputs() + + # Act + result = calculate_sap_from_inputs(inputs) + + # Assert + assert result.intermediate["ecf"] == pytest.approx(result.ecf, rel=1e-9) + assert result.intermediate["deflator"] == pytest.approx(0.36, rel=1e-12) + + 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. diff --git a/packages/domain/src/domain/sap/worksheet/rating.py b/packages/domain/src/domain/sap/worksheet/rating.py index 85ae0aa5..0463c57b 100644 --- a/packages/domain/src/domain/sap/worksheet/rating.py +++ b/packages/domain/src/domain/sap/worksheet/rating.py @@ -18,7 +18,7 @@ from math import log10 from typing import Final -_ENERGY_COST_DEFLATOR: Final[float] = 0.36 +ENERGY_COST_DEFLATOR: Final[float] = 0.36 _FLOOR_AREA_OFFSET_M2: Final[float] = 45.0 _ECF_LOG_THRESHOLD: Final[float] = 3.5 _SAP_LINEAR_INTERCEPT: Final[float] = 100.0 @@ -38,7 +38,7 @@ def energy_cost_factor( total_floor_area_m2: float, ) -> float: """SAP 10.3 §13 equation (7): ECF = 0.36 × cost / (TFA + 45).""" - return _ENERGY_COST_DEFLATOR * total_cost_gbp / (total_floor_area_m2 + _FLOOR_AREA_OFFSET_M2) + return ENERGY_COST_DEFLATOR * total_cost_gbp / (total_floor_area_m2 + _FLOOR_AREA_OFFSET_M2) def sap_rating(*, ecf: float) -> float: