diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 7f268796..adfffdf2 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -3618,6 +3618,36 @@ _ELMHURST_GAS_BOILER_FUEL_TYPES: frozenset[str] = frozenset({ "LPG special condition", }) +# Standard-electricity Table 32 fuel code. SAP 10.2 Table 12 lists +# this as code 30 = "Electricity, standard tariff" — the fuel-cost / +# CO2 / PE lookup key for any electric main heating system. +_STANDARD_ELECTRICITY_FUEL_CODE: Final[int] = 30 + +# SAP 10.2 Table 4a main-heating SAP codes whose fuel is electricity. +# Elmhurst §14.0 leaves "Fuel Type" empty for these systems (the SAP +# code identifies the carrier), so the mapper must infer the fuel +# from the SAP code rather than the empty string. First needed by +# cert 000565 Main 1 (SAP code 224 = "Air source heat pump, 2013 or +# later") — without inference, `_main_fuel_code` returns None and the +# Table 32 price lookup defaults to mains gas, charging the HP cert's +# electricity kWh at the gas tariff. +# +# Coverage as Elmhurst-only fixtures land: +# 191-196 — electric boilers (Table 4a "Electric boilers" rows) +# 211-217, 221-227 — heat pumps (Table 4a "Heat pumps" rows; 224 is +# the ASHP 2013+ entry, 1.70 COP — `sap_efficiencies.py:44`) +# 401-409 — electric storage heaters (Table 4a "Storage heaters") +# 421-425 — electric underfloor (Table 4a "Underfloor heating") +# 521-527 — warm-air heat pumps (Table 4a "Warm-air heat pumps") +_ELECTRIC_SAP_MAIN_HEATING_CODES: Final[frozenset[int]] = frozenset( + list(range(191, 197)) + + list(range(211, 218)) + + list(range(221, 228)) + + list(range(401, 410)) + + list(range(421, 426)) + + list(range(521, 528)) +) + class UnmappedElmhurstLabel(ValueError): """An Elmhurst Summary lodged a finite-enum label that the mapper @@ -3835,6 +3865,13 @@ def _map_elmhurst_main_heating_2( ), ) main_fuel_int = _elmhurst_main_fuel_int(mh2.fuel_type) + # Same electric-SAP-code fuel inference as Main 1 — see + # `_ELECTRIC_SAP_MAIN_HEATING_CODES` docstring. + if ( + main_fuel_int is None + and mh2.main_heating_sap_code in _ELECTRIC_SAP_MAIN_HEATING_CODES + ): + main_fuel_int = _STANDARD_ELECTRICITY_FUEL_CODE category: Optional[int] = None if pcdb_index is not None and heat_pump_record(pcdb_index) is not None: category = _ELMHURST_HEATING_CATEGORY_HEAT_PUMP @@ -3882,6 +3919,15 @@ def _map_elmhurst_sap_heating(survey: ElmhurstSiteNotes) -> SapHeating: ) pcdb_index = _elmhurst_pcdb_boiler_index(mh.pcdf_boiler_reference) main_fuel_int = _elmhurst_main_fuel_int(mh.fuel_type) + # Elmhurst §14.0 leaves "Fuel Type" empty for electric main heating + # systems (HP / electric boiler / storage / underfloor); the SAP + # code identifies the carrier. Infer electricity (Table 32 code 30) + # when the mapper can't read it from the `fuel_type` string. + if ( + main_fuel_int is None + and mh.main_heating_sap_code in _ELECTRIC_SAP_MAIN_HEATING_CODES + ): + main_fuel_int = _STANDARD_ELECTRICITY_FUEL_CODE heat_emitter_int = _elmhurst_heat_emitter_int(mh.heat_emitter) sap_control_int = _elmhurst_sap_control_code(sap_control) main_heating_category = _elmhurst_main_heating_category(mh, pcdb_index)