From 1b43c95ca62caf9ff47e269de23174d08e13c420 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Thu, 21 May 2026 11:22:39 +0000 Subject: [PATCH] pcdb followup: 000490 fixture lodges main_heating_index_number=10328 (Vaillant Ecotec Pro) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PDF "PCDF boiler reference: 10328 Vaillant Ecotec Pro 88.20%" lodgement → fixture now sets `main_heating_index_number=10328` + `main_heating_data_source=1` per the API's standard PCDB-lodgement shape. cert_to_inputs PCDB precedence cascade picks up Table 105 record 10328 (winter eff 88.2%, summer 79.6%) and overrides the Table 4a category-2 default. make_main_heating_detail extended to expose main_heating_index_number / main_heating_data_source / sap_main_heating_code kwargs so fixtures can lodge PCDB pointers without hand-building MainHeatingDetail. 000490 e2e impact: - main_heating_fuel: 14334 → 13001.3 kWh (PDF 13003.85 — gap closes to <0.1%, was +10%) - HW fuel: 3090.47 → 3028.27 kWh (PDF 2850.57 — gap closes +8.4% → +6.2%) - total_fuel_cost: £756.99 → £706.23 (PDF £807.54 — diverges -6.3% → -12.5%, ADR-0010 §3 spec-version artifact) - SAP rating: 60 → 63 (PDF 57 — +3 → +6) The fuel-kWh tightening is the spec-faithful direction. The cost / SAP residuals widen because the cert pre-dates the 14-March-2025 SAP10.2 amendment which lowered gas unit prices ~13%; per ADR-0010 §3 only certs lodged ≥2025-07-01 are spec-comparable on cost-driven outputs. The e2e SAP ceiling is raised 3 → 6 and the cost-rel tolerance 0.10 → 0.15 with a docstring naming the drivers; tightens further when the Validation Cohort filter + Ecodesign/Appendix N adjustments land. 000474 also flagged as Vaillant ecoTEC pro PCDB-lodged; awaiting user's PCDB code lookup for that fixture. Co-Authored-By: Claude Opus 4.7 --- .../domain/src/domain/ml/tests/_fixtures.py | 12 ++++- .../tests/_elmhurst_worksheet_000490.py | 20 ++++++- .../tests/test_e2e_elmhurst_sap_score.py | 52 ++++++++++++------- 3 files changed, 62 insertions(+), 22 deletions(-) diff --git a/packages/domain/src/domain/ml/tests/_fixtures.py b/packages/domain/src/domain/ml/tests/_fixtures.py index f653fee6..14b2013c 100644 --- a/packages/domain/src/domain/ml/tests/_fixtures.py +++ b/packages/domain/src/domain/ml/tests/_fixtures.py @@ -61,8 +61,15 @@ def make_main_heating_detail( boiler_flue_type: Optional[int] = 2, central_heating_pump_age: Optional[int] = 0, main_heating_number: Optional[int] = 1, + main_heating_index_number: Optional[int] = None, + main_heating_data_source: Optional[int] = None, + sap_main_heating_code: Optional[int] = None, ) -> MainHeatingDetail: - """Build a MainHeatingDetail with SAP10 API defaults (mains gas boiler).""" + """Build a MainHeatingDetail with SAP10 API defaults (mains gas boiler). + Pass `main_heating_index_number` to point at a PCDB record (typical + cert convention is `main_heating_data_source=1` + a PCDB pointer for + PCDB-listed systems; `main_heating_data_source=2` + `sap_main_heating_ + code` for Table 4b-lodged systems).""" return MainHeatingDetail( has_fghrs=has_fghrs, main_fuel_type=main_fuel_type, @@ -74,6 +81,9 @@ def make_main_heating_detail( central_heating_pump_age=central_heating_pump_age, main_heating_number=main_heating_number, main_heating_category=main_heating_category, + main_heating_index_number=main_heating_index_number, + main_heating_data_source=main_heating_data_source, + sap_main_heating_code=sap_main_heating_code, ) 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 462cac49..f42c3fa2 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 @@ -30,7 +30,12 @@ from datatypes.epc.domain.epc_property_data import ( SapFloorDimension, SapWindow, ) -from domain.ml.tests._fixtures import make_minimal_sap10_epc, make_window +from domain.ml.tests._fixtures import ( + make_main_heating_detail, + make_minimal_sap10_epc, + make_sap_heating, + make_window, +) from domain.sap.worksheet.solar_gains import RoofWindowInput, RooflightInput from domain.sap.worksheet.ventilation import MechanicalVentilationKind from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C @@ -102,6 +107,11 @@ def build_epc() -> EpcPropertyData: # door_count=2 matches the worksheet's 3.70 m² of total door area: # Elmhurst lodges 1 oversized 3.7 m × 1.0 m door, our cascade uses the # RdSAP default 1.85 m² per door so 2 doors recover the same area. + # PDF lodges "PCDF boiler reference: 10328 Vaillant Ecotec Pro 88.20%". + # The 10328 is the BRE PCDB index_number (Table 105 Vaillant Ecotec Pro + # 28kW, 2004-2012, winter eff 88.2%, summer eff 79.6%). Lodging it on the + # MainHeatingDetail routes `cert_to_inputs` into the PCDB precedence + # cascade rather than the Table 4a category-2 default (80%). return make_minimal_sap10_epc( total_floor_area_m2=66.06, country_code="ENG", @@ -109,6 +119,14 @@ def build_epc() -> EpcPropertyData: habitable_rooms_count=4, heated_rooms_count=4, door_count=2, + sap_heating=make_sap_heating( + main_heating_details=[ + make_main_heating_detail( + main_heating_index_number=10328, + main_heating_data_source=1, + ), + ], + ), ) 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 8e832e51..9c6ea492 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,27 +56,39 @@ _ELMHURST_000474_EXPECTED: Final[ElmhurstExpectedSap] = ElmhurstExpectedSap( ) -def test_elmhurst_000490_end_to_end_sap_score_currently_within_3_points() -> None: - """Mid-terrace combi-gas dwelling with time-clock keep-hot. Before - the §8 summer-clamp fix this fixture matched the worksheet SAP=57 - exactly via fortuitous compensation: the calculator over-predicted - annual space heating by ~+14% (missing Jun-Sep clamp), which roughly - cancelled small under-predictions elsewhere in the §3/§5 chain. +def test_elmhurst_000490_end_to_end_sap_score_currently_within_6_points() -> None: + """Mid-terrace combi-gas dwelling with time-clock keep-hot. After the + PCDB Table 105 integration the fixture lodges `main_heating_index_ + number=10328` (Vaillant Ecotec Pro 28kW, winter eff 88.2%, summer + 79.6%) per the PDF's "PCDF boiler reference: 10328 Vaillant Ecotec + Pro 88.20%" lodgement. Cert path now resolves efficiency via the + spec-faithful PCDB precedence rather than the Table 4a category-2 + default (80%). - Post-§8 (slice 3 of §8 wiring) the summer clamp removes the +1575 - kWh/yr over-prediction and the residual gap from §3 / §5 / §6 / §7 - precision drift surfaces: + Post-PCDB residuals: | metric | actual | PDF | delta | | --------------- | -------- | --------- | ----- | | space heating | 11467.18 | 11183.275 | +2.5% | - | hot water fuel | 3090.47 | 2850.570 | +8.4% | - | total fuel cost | £756.99 | £807.54 | −6.3% | - | SAP rating | 60 | 57 | +3 | + | hot water fuel | 3028.27 | 2850.570 | +6.2% | + | main heating fuel | 13001.3| 13003.85 | -0.02%| + | total fuel cost | £706.23 | £807.54 | −12.5%| + | SAP rating | 63 | 57 | +6 | - Tolerance set at the current gap so future improvements show up as - test tightening, not silent drift. Drop to ≤1 point once the - upstream §3 / §5 cert-pipe precision is closed. + The PCDB efficiency override closes the main-heating-fuel gap to <0.1% + (matches PDF), and the HW kWh gap narrows from +8.4% → +6.2%. The + total fuel cost diverges from -6.3% → -12.5% and the SAP score moves + +3 → +6 because the lodged cert pre-dates the 14-March-2025 SAP10.2 + amendment which lowered gas unit prices ~13% (per ADR-0010 §3 + Validation Cohort: only certs lodged ≥2025-07-01 are spec-comparable + on cost / SAP rating). The fuel-kWh tightening is the spec-faithful + direction; the cost / SAP residuals are documented spec-version + drift, not a calculator regression. + + Ceiling raised 3 → 6 (SAP integer) and 3.0 → 6.0 (continuous) to + reflect the post-PCDB current state. Tightens further when the + Validation Cohort filter is in place and Tables D1/D2/D3 Ecodesign + condensing-boiler corrections + Appendix N adjustments land. """ # Arrange epc = _w000490.build_epc() @@ -86,15 +98,15 @@ def test_elmhurst_000490_end_to_end_sap_score_currently_within_3_points() -> Non # Assert 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 <= 6, ( + f"SAP rating delta {delta} exceeds current-state ceiling of 6. " 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 <= 6.0, ( + f"Continuous SAP delta {continuous_delta:.2f} exceeds ceiling 6.0" ) @@ -156,5 +168,5 @@ def test_elmhurst_000490_end_to_end_kwh_within_15pct() -> None: ) assert result.hot_water_kwh_per_yr == pytest.approx(exp.hot_water_kwh, rel=0.15) assert result.total_fuel_cost_gbp == pytest.approx( - exp.total_energy_cost_gbp, rel=0.10 + exp.total_energy_cost_gbp, rel=0.15 )