diff --git a/domain/sap10_calculator/rdsap/cert_to_inputs.py b/domain/sap10_calculator/rdsap/cert_to_inputs.py index 5f7c2310..fcf2ed75 100644 --- a/domain/sap10_calculator/rdsap/cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/cert_to_inputs.py @@ -412,15 +412,25 @@ SAP_10_2_SPEC_PRICES: Final[PriceTable] = PriceTable( ) -# SAP 10.2 Table 9 main_heating_control codes → control type (1/2/3). +# SAP 10.2 Table 4e (page 171) main_heating_control codes → control type +# (1/2/3 per Table 9 "Heating control type" column). Type drives the +# elsewhere-zone off-hours pattern in Table 9: types 1+2 use (7, 8), +# type 3 uses (9, 8) per footnote (b) "heating 0700-0900 and 1800-2300". +# # Type 1: no time + temp control, or one but not both. -# Type 2: programmer + room thermostat (+/− TRVs). -# Type 3: time-and-temperature zone control (e.g. separate living-zone -# programmer + thermostat). +# Type 2: programmer + room thermostat (+/− TRVs); also bare TRV-class +# controls (2111 "TRVs and bypass", 2113 "Room thermostat and +# TRVs") — these were misclassified as type 3 pre-S0380.25 and +# pushed cert 0652 to +1.93 SAP / cert 6835 to +0.72. +# Type 3: time-and-temperature zone control (separate living-zone +# schedule via plumbing/electrical arrangement or PCDB device). _CONTROL_TYPE_BY_CODE: Final[dict[int, int]] = { 2101: 1, 2102: 1, 2103: 1, 2104: 1, 2105: 2, 2106: 2, 2107: 2, 2108: 2, 2109: 2, - 2110: 3, 2111: 3, 2112: 3, 2113: 3, + 2110: 3, + 2111: 2, # TRVs and bypass — Table 4e row "2 0" + 2112: 3, + 2113: 2, # Room thermostat and TRVs — Table 4e row "2 0" } diff --git a/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py b/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py index c1a0f59a..5f154fc1 100644 --- a/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py +++ b/domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py @@ -707,11 +707,21 @@ def test_main_heating_control_code_maps_to_sap_control_type() -> None: type_1 = cert_to_inputs(_epc_with_control(2103)) type_2 = cert_to_inputs(_epc_with_control(2106)) type_3 = cert_to_inputs(_epc_with_control(2110)) + # Slice S0380.25: SAP 10.2 Table 4e (page 171) classifies codes 2111 + # ("TRVs and bypass") and 2113 ("Room thermostat and TRVs") as + # control type 2, NOT type 3 — they lack the time-zone control that + # type 3 requires. Mis-classifying these as type 3 swapped the + # elsewhere-zone off-hours from (7, 8) to (9, 8), under-counting MIT + # 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)) # Assert assert type_1.control_type == 1 assert type_2.control_type == 2 assert type_3.control_type == 3 + assert type_2_via_2111.control_type == 2 + assert type_2_via_2113.control_type == 2 def test_off_peak_meter_routes_electric_costs_to_low_rate() -> None: