diff --git a/domain/epc/property_overlays/main_heating_system_overlay.py b/domain/epc/property_overlays/main_heating_system_overlay.py index af334f41c..b8c945393 100644 --- a/domain/epc/property_overlays/main_heating_system_overlay.py +++ b/domain/epc/property_overlays/main_heating_system_overlay.py @@ -188,6 +188,14 @@ _CONSERVATIVE_HEAT_PUMP_CONTROL = 2201 _HEAT_NETWORK_CATEGORY = 6 _HEAT_NETWORK_CODES = frozenset({301, 302, 303, 304}) _COMMUNITY_BOILER_CODES = frozenset({301, 302, 303}) +# SAP Table 4e Group 3 heat-network control: 2301 ("flat rate charging, no +# thermostatic control of room temperature") is the conservative default when +# the landlord named only the scheme. It is never-over-crediting on BOTH axes +# the control drives: the largest Group 3 temperature adjustment (+0.3 C, tied +# with 2302) and the worst Table 4c(3) charging factors (space 1.10, DHW 1.05 +# — an unobserved charging arrangement is never assumed usage-linked). The +# community mirror of storage 2401 / boiler 2101 / heat-pump 2201 (ADR-0053). +_CONSERVATIVE_HEAT_NETWORK_CONTROL = 2301 _COMMUNITY_GAS_FUEL = 20 # SAP Table 4c full boiler-control code: programmer + room thermostat + TRVs. The @@ -318,6 +326,8 @@ def _control_for(code: int) -> Optional[int]: return _CONSERVATIVE_BOILER_CONTROL if code in _WET_HEAT_PUMP_CODES: return _CONSERVATIVE_HEAT_PUMP_CONTROL + if code in _HEAT_NETWORK_CODES: + return _CONSERVATIVE_HEAT_NETWORK_CONTROL return None diff --git a/tests/domain/epc/test_main_heating_system_overlay.py b/tests/domain/epc/test_main_heating_system_overlay.py index c092c5114..fb856b75c 100644 --- a/tests/domain/epc/test_main_heating_system_overlay.py +++ b/tests/domain/epc/test_main_heating_system_overlay.py @@ -94,18 +94,24 @@ def test_electric_room_heaters_overlay_the_room_heater_charge_control() -> None: def test_overlay_logs_when_it_cannot_complete_a_companion_set( caplog: pytest.LogCaptureFixture, + monkeypatch: pytest.MonkeyPatch, ) -> None: - # An archetype the overlay cannot yet fully companion — here community - # heating, whose Table 4e Group 3 conservative control is not yet mapped — - # is still overlaid (log-and-continue), but the incomplete companion set is - # logged as an error so the known gap is visible (CloudWatch) rather than - # silently inheriting the replaced system's stale value. + # Every real archetype now resolves a complete companion set (ADR-0053), + # so the incomplete case is simulated: a hypothetical warm-air heat-pump + # archetype (SAP 521 — category 4 but its Table 4e Group 5 control is + # deliberately unmapped). The overlay still applies (log-and-continue), + # but the incomplete companion set is reported rather than silently + # inheriting the replaced system's stale value. + from domain.epc.property_overlays import main_heating_system_overlay as mod + + monkeypatch.setitem(mod._MAIN_HEATING_CODES, "Warm-air heat pump", 521) + # Act with caplog.at_level(logging.ERROR): - main_heating_overlay_for("Community heating, boilers", 0) + main_heating_overlay_for("Warm-air heat pump", 0) # Assert — the gap is reported and names the archetype. - assert "Community heating, boilers" in caplog.text + assert "Warm-air heat pump" in caplog.text @pytest.mark.parametrize(