From 9153fa2523a5887b36ab5868d5c454d024b12f1f Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Fri, 3 Jul 2026 16:35:08 +0000 Subject: [PATCH] =?UTF-8?q?A=20community=20override=20keeps=20an=20assesso?= =?UTF-8?q?r-observed=20Group=203=20control=20=F0=9F=9F=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The heat-network mirror of keep_existing_off_peak_meter: the override's 2313 default applies only when the replaced system's control is cross-family (stale); an observed community control survives. Co-Authored-By: Claude Opus 4.8 --- .../main_heating_system_overlay.py | 5 ++++ .../modelling/scoring/overlay_applicator.py | 24 +++++++++++++++++-- domain/modelling/simulation.py | 9 +++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/domain/epc/property_overlays/main_heating_system_overlay.py b/domain/epc/property_overlays/main_heating_system_overlay.py index 243326cfd..946c648ca 100644 --- a/domain/epc/property_overlays/main_heating_system_overlay.py +++ b/domain/epc/property_overlays/main_heating_system_overlay.py @@ -445,5 +445,10 @@ def main_heating_overlay_for( # (e.g. a 24-hour all-low tariff). Measures, which re-meter for real, # build their HeatingOverlay directly and leave this False. keep_existing_off_peak_meter=True, + # Likewise a heat-network override's control default (2313) must + # not overwrite an assessor-OBSERVED Group 3 control on a community + # cert being confirmed — only a stale cross-family control is + # replaced (ADR-0053). + keep_existing_heat_network_control=code in _HEAT_NETWORK_CODES, ) ) diff --git a/domain/modelling/scoring/overlay_applicator.py b/domain/modelling/scoring/overlay_applicator.py index 216463dab..1a9357033 100644 --- a/domain/modelling/scoring/overlay_applicator.py +++ b/domain/modelling/scoring/overlay_applicator.py @@ -152,6 +152,13 @@ _SAP_HEATING_FIELDS: tuple[str, ...] = ( _ENERGY_SOURCE_FIELDS: tuple[str, ...] = ("meter_type", "gas_connection_available") +def _is_heat_network_control(control: object) -> bool: + """True iff the lodged control is a SAP Table 4e Group 3 (heat network) + code — 2301-2314 — i.e. an assessor-observed community control a + heat-network override's default must not overwrite (ADR-0053).""" + return isinstance(control, int) and 2301 <= control <= 2314 + + def _is_off_peak_meter(meter_type: object) -> bool: """True iff the meter resolves to an off-peak Table 12a tariff (not the STANDARD single-rate column). Unparseable / absent meters count as not @@ -177,8 +184,21 @@ def _fold_heating(epc: EpcPropertyData, overlay: HeatingOverlay) -> None: main = epc.sap_heating.main_heating_details[0] for field_name in _MAIN_HEATING_FIELDS: value = getattr(overlay, field_name) - if value is not None: - setattr(main, field_name, value) + if value is None: + continue + # A community override's control is a coherent default, not an observed + # one: when it opts in (`keep_existing_heat_network_control`) and the + # cert already lodges a Table 4e GROUP 3 control (assessor-observed on + # a community cert being confirmed), keep the cert's. A cross-family + # control (boiler 2110, storage 2401) is stale and is still replaced + # (ADR-0053). + if ( + field_name == "main_heating_control" + and overlay.keep_existing_heat_network_control + and _is_heat_network_control(main.main_heating_control) + ): + continue + setattr(main, field_name, value) # `main_heating_index_number` (PCDB-resolved, e.g. a heat pump) and # `sap_main_heating_code` (Table 4a-resolved, e.g. storage heaters) are # mutually-exclusive efficiency anchors: a whole-system replacement to one diff --git a/domain/modelling/simulation.py b/domain/modelling/simulation.py index 79855940e..55c52855e 100644 --- a/domain/modelling/simulation.py +++ b/domain/modelling/simulation.py @@ -200,6 +200,15 @@ class HeatingOverlay: # MEASURE re-meters for real (Elmhurst re-lodges 18-hour → Dual on a storage # install), so it leaves this False. `_fold_heating` reads it. keep_existing_off_peak_meter: bool = False + # The community-heating mirror of the flag above (ADR-0053): a heat-network + # override's `main_heating_control` is a coherent DEFAULT, not an observed + # control — when the cert already lodges a Table 4e Group 3 control the + # assessor observed it (a community cert being confirmed, not replaced), so + # the default must not overwrite it (e.g. a usage-linked 2306 downgraded to + # flat-rate 2313 destroys ~1-2 real SAP). A cross-family control (a boiler + # 2110, storage 2401) is stale by definition and is still replaced. + # `_fold_heating` reads it. + keep_existing_heat_network_control: bool = False @dataclass(frozen=True)