diff --git a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000490.py b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000490.py index f8c28aa1..9d87c8f6 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000490.py +++ b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000490.py @@ -128,6 +128,7 @@ def build_epc() -> EpcPropertyData: main_heating_data_source=1, ), ], + secondary_heating_type=691, ), ) @@ -265,6 +266,12 @@ SECTION_5_PUMP_AGE_STR: str = "Unknown" # inputs.lighting_kwh_per_yr on the cost side. LINE_232_LIGHTING_KWH_PER_YR: float = 171.4217 +# Secondary heating fuel kWh (215) — Σ (215)m monthly tuple. +# 000490 lodges secondary heating system "Electricity Electric Panel, +# convector or radiant heaters" (SAP Code 691) at 100% efficiency. +# Table 11 fraction 0.1000 of total space heat goes to secondary. +LINE_215_SECONDARY_HEATING_FUEL_KWH: float = 1118.3275 + LINE_66_M_METABOLIC_W: tuple[float, ...] = (128.8087,) * 12 LINE_67_M_LIGHTING_W: tuple[float, ...] = ( 24.2665, 21.5533, 17.5283, 13.2701, 9.9195, 8.3745, diff --git a/packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py b/packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py index 0600df26..a8601430 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py +++ b/packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py @@ -56,19 +56,6 @@ _ELMHURST_000474_EXPECTED: Final[ElmhurstExpectedSap] = ElmhurstExpectedSap( ) -@pytest.mark.xfail( - reason=( - "Appendix L closure on 000490: lighting kWh closes 614→171 (spec-faithful " - "U985 (232)=171.4217). Cost drops by ~£60 → £703 vs PDF £807; SAP integer " - "climbs 60→63 → delta widens 3→6. Per the e2e validation philosophy " - "(feedback-e2e-validation-philosophy): don't widen the ceiling, hunt the " - "next broken component. Suspects: fuel pricing for pre-2025-07-01 certs " - "(ADR-0010 §3 Validation Cohort), main heating kWh +2.5% overshoot, " - "Table D1/D2/D3 Ecodesign corrections. Re-enable when those land and " - "SAP integer = PDF integer (delta=0)." - ), - strict=True, -) def test_elmhurst_000490_end_to_end_sap_score_currently_within_3_points() -> None: """Mid-terrace combi-gas dwelling with time-clock keep-hot. After the PCDB Table 105 integration the fixture lodges `main_heating_index_ @@ -119,17 +106,19 @@ def test_elmhurst_000490_end_to_end_sap_score_currently_within_3_points() -> Non # Act result = Sap10Calculator().calculate(epc) - # Assert + # Assert — secondary heating cascade closed the £104 cost gap; SAP + # integer is now 58 vs PDF 57 (delta 1). The residual delta is from + # the +0.7% upstream useful space heating overshoot — next ticket. delta = abs(result.sap_score - _ELMHURST_000490_EXPECTED.sap_rating) - assert delta <= 3, ( - f"SAP rating delta {delta} exceeds current-state ceiling of 3. " + assert delta <= 1, ( + f"SAP rating delta {delta} exceeds current-state ceiling of 1. " f"Actual={result.sap_score}, expected={_ELMHURST_000490_EXPECTED.sap_rating}." ) continuous_delta = abs( result.sap_score_continuous - _ELMHURST_000490_EXPECTED.sap_score_continuous ) - assert continuous_delta <= 3.0, ( - f"Continuous SAP delta {continuous_delta:.2f} exceeds ceiling 3.0" + assert continuous_delta <= 0.5, ( + f"Continuous SAP delta {continuous_delta:.2f} exceeds ceiling 0.5" ) @@ -221,6 +210,37 @@ def test_elmhurst_end_to_end_lighting_kwh_per_yr_matches_u985_worksheet( assert result.lighting_kwh_per_yr == pytest.approx(expected_kwh, abs=1e-4) +def test_elmhurst_000490_end_to_end_secondary_heating_fuel_kwh_matches_u985_worksheet() -> None: + """Component-level e2e pin on `SapResult.secondary_heating_fuel_kwh_per_yr` + for 000490 — cert lodges secondary heating system "Electricity Electric + Panel, convector or radiant heaters" (SAP Code 691, 100% efficiency). + Table 11 fraction 0.10 of total space heat goes to the secondary + system → (215) = 1118.3275 kWh. + + Closes the next 000490 residual after Appendix L: secondary fuel was + silently 0 because build_epc didn't lodge secondary_heating_type, so + `_secondary_fraction` early-returned 0.0 → all useful space heat + routed to main 1 → main_fuel_kwh +1357 kWh over PDF, secondary -1118 + under PDF. Cost gap was £147 secondary missing minus £47 main + overshoot = -£104 (the dominant residual after Appendix L closure). + """ + # Arrange + epc = _w000490.build_epc() + + # Act + result = Sap10Calculator().calculate(epc) + + # Assert — tolerance abs=10 absorbs the +0.7% (78 kWh) overshoot in + # `result.space_heating_kwh_per_yr` (useful demand) that propagates + # proportionally to (215) = useful × 0.1 / 1.0. The secondary cascade + # itself is exact (Table 11 fraction lookup + 100% efficiency); the + # residual is upstream. Tightens to abs=1e-3 when the useful bias + # closes (next ticket — see project memory). + assert result.secondary_heating_fuel_kwh_per_yr == pytest.approx( + _w000490.LINE_215_SECONDARY_HEATING_FUEL_KWH, abs=10.0 + ) + + def test_elmhurst_000490_end_to_end_kwh_within_15pct() -> None: """Per-end-use kWh sanity check for 000490. Closer-fitting than the SAP score because intermediate values aren't compressed through the diff --git a/packages/domain/src/domain/sap/worksheet/tests/test_fuel_cost.py b/packages/domain/src/domain/sap/worksheet/tests/test_fuel_cost.py index a831a9ec..e8b4c9a1 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/test_fuel_cost.py +++ b/packages/domain/src/domain/sap/worksheet/tests/test_fuel_cost.py @@ -368,17 +368,6 @@ def test_000474_cert_to_inputs_fuel_cost_within_existing_e2e_tolerance() -> None assert inputs.fuel_cost.total_cost_gbp == pytest.approx(655.6949, rel=0.15) -@pytest.mark.xfail( - reason=( - "Appendix L closure on 000490: lighting kWh closes 614→171 (spec-faithful " - "U985 (232)=171.4217). Cost drops ~£60 → £703 vs PDF £807 (-12.9%). The " - "Appendix L direction is correct; the residual is a non-lighting broken " - "component (suspects per feedback-e2e-validation-philosophy: fuel pricing " - "for pre-2025-07-01 certs, main-heating-fuel +2.5% overshoot, Table D1-3 " - "Ecodesign). Re-enable when next component closes." - ), - strict=True, -) def test_000490_cert_to_inputs_fuel_cost_closes_to_within_5pct() -> None: """Cert-round-trip conformance: 000490 mid-terrace combi-gas with PV (PDF total fuel cost £807.54). Pre-§10a was £706.23 (-12.5%) —