From 537e18bc2e2b2de7198f01e826e3ea4192b2500c Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 19 May 2026 10:32:59 +0000 Subject: [PATCH] P5.8: SapResult.intermediate exposes CO2 chain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds delivered_fuel_kwh_per_yr (sum of all five end-use kWh) and co2_factor_kg_per_kwh (mirrors the SAP10 input). Together with the top-level co2_kg_per_yr they make the §15 equation traceable: co2 = delivered_fuel × factor. --- packages/domain/src/domain/sap/calculator.py | 2 ++ .../src/domain/sap/tests/test_calculator.py | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/packages/domain/src/domain/sap/calculator.py b/packages/domain/src/domain/sap/calculator.py index 7b221780..f17d23fc 100644 --- a/packages/domain/src/domain/sap/calculator.py +++ b/packages/domain/src/domain/sap/calculator.py @@ -371,6 +371,8 @@ def calculate_sap_from_inputs(inputs: CalculatorInputs) -> SapResult: "lighting_cost_gbp": lighting_cost, "ecf": ecf, "deflator": ENERGY_COST_DEFLATOR, + "delivered_fuel_kwh_per_yr": delivered_fuel_kwh, + "co2_factor_kg_per_kwh": inputs.co2_factor_kg_per_kwh, } 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 af44af50..22915dd2 100644 --- a/packages/domain/src/domain/sap/tests/test_calculator.py +++ b/packages/domain/src/domain/sap/tests/test_calculator.py @@ -278,6 +278,36 @@ def test_calculate_exposes_ecf_and_deflator() -> None: assert result.intermediate["deflator"] == pytest.approx(0.36, rel=1e-12) +def test_calculate_exposes_co2_chain() -> None: + # Arrange — P5 trace mode: CO2 = delivered_fuel × co2_factor. Both + # inputs surface on `intermediate` so the top-level co2_kg_per_yr is + # auditable. Delivered fuel is the sum of every end-use kWh; the + # factor mirrors the SAP10 inputs.co2_factor_kg_per_kwh. + inputs = _baseline_inputs() + + # Act + result = calculate_sap_from_inputs(inputs) + + # Assert + expected_delivered = ( + result.main_heating_fuel_kwh_per_yr + + result.secondary_heating_fuel_kwh_per_yr + + result.hot_water_kwh_per_yr + + result.pumps_fans_kwh_per_yr + + result.lighting_kwh_per_yr + ) + assert result.intermediate["delivered_fuel_kwh_per_yr"] == pytest.approx( + expected_delivered, rel=1e-9 + ) + assert result.intermediate["co2_factor_kg_per_kwh"] == pytest.approx( + inputs.co2_factor_kg_per_kwh, rel=1e-12 + ) + assert ( + result.intermediate["delivered_fuel_kwh_per_yr"] + * result.intermediate["co2_factor_kg_per_kwh"] + ) == pytest.approx(result.co2_kg_per_yr, 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.