From be076bb9a490f485abb2bff88cc119a6042a1fb7 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Tue, 14 Jul 2026 16:22:23 +0000 Subject: [PATCH] =?UTF-8?q?Resolve=20the=20cohort's=20remaining=20PasHub?= =?UTF-8?q?=20boiler=20control=20labels=20to=20Table=204e=20codes=20?= =?UTF-8?q?=F0=9F=9F=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- datatypes/epc/domain/mapper.py | 10 ++++++++-- .../sap10_calculator/rdsap/test_cert_to_inputs.py | 7 +++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 7dda6b10e..d0ed19edd 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -7086,10 +7086,16 @@ def _pashub_main_fuel_code(fuel_label: str) -> Union[int, str]: # keys off. Copying the raw label onto `main_heating_control` strict-raises # `UnmappedSapCode` deep in the calculator; resolving it here at the boundary # (ADR-0015) hands the cascade the int it expects. Seeded with the boiler -# (Group 1) control labels confirmed in the live cohort — all standard -# time-and-temperature single-zone controls that resolve to control type 2. +# (Group 1) control labels confirmed in the live cohort. Codes are taken +# verbatim from Table 4e Group 1; the control *type* is then the spec's — note +# 2104 ("Programmer and room thermostat") is control type 1, not 2 (type 2 +# needs 2+ room thermostats or TRVs), so the map is not uniformly type 2. _PASHUB_MAIN_HEATING_CONTROL_TO_SAP10: Dict[str, int] = { + "Programmer and room thermostat": 2104, "Programmer, room thermostat and TRVs": 2106, + "Programmer, TRVs and bypass": 2107, + "Room thermostat and TRVs": 2113, + "TRVs and bypass": 2111, } diff --git a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py index 109dabaed..f0a2e14ac 100644 --- a/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py +++ b/tests/domain/sap10_calculator/rdsap/test_cert_to_inputs.py @@ -2559,6 +2559,12 @@ def test_main_heating_control_code_maps_to_sap_control_type() -> None: # by ~0.67 °C → cert 0652 +1.93 SAP / cert 6835 +0.72 SAP. type_2_via_2111 = cert_to_inputs(_epc_with_control(2111)) type_2_via_2113 = cert_to_inputs(_epc_with_control(2113)) + # 2104 ("Programmer and room thermostat") is control type 1 per SAP 10.2 + # Table 4e Group 1 (PDF p.171): a programmer + a single room thermostat is + # type 1, NOT type 2 — type 2 needs more (2+ room thermostats = 2105, or + # TRVs = 2106/2113). Pinned here because the label reads like time-and- + # temperature control; the spec's own classification is the ground truth. + type_1_via_2104 = cert_to_inputs(_epc_with_control(2104)) # Assert assert type_1.control_type == 1 @@ -2566,6 +2572,7 @@ def test_main_heating_control_code_maps_to_sap_control_type() -> None: assert type_3.control_type == 3 assert type_2_via_2111.control_type == 2 assert type_2_via_2113.control_type == 2 + assert type_1_via_2104.control_type == 1 def test_main_heating_control_code_table_4e_full_coverage_groups_0_through_7() -> None: