diff --git a/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py b/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py index 6dcde6fb..400dab5d 100644 --- a/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py +++ b/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py @@ -1847,6 +1847,33 @@ def test_summary_000565_ext2_floor_routes_to_u_value_0p22_via_table_20_per_rdsap assert bp_2.floor_insulation_thickness == "200mm" +def test_summary_000565_main_1_ashp_sap_code_224_routes_to_main_heating_category_4_per_sap_table_4a() -> None: + # Arrange — SAP 10.2 Table 4a (PDF p.165) "Main heating systems": + # the category column lists "Heat pumps" as category 4. Codes in + # rows 211-217 (ground/water source HP) and 221-227 (air source HP) + # and 521-527 (warm-air HP) all map to category 4. + # + # Cert 000565 Main 1 lodges `Main Heating SAP Code = 224` (Air + # source heat pump, 2013 or later — SAP 10.2 Appendix N efficiency + # row 224, COP 1.70). Without a PCDB Table 362 record (cert lodges + # `PCDF boiler Reference = 0`) the existing mapper's `_elmhurst_ + # main_heating_category` returns None, which falls through to the + # cascade's `_DEFAULT_PUMPS_FANS_KWH_PER_YR = 130` (incorrect — HP + # circulation pump's electricity is inside the system COP per + # SAP 10.2 Table 4f, so the category 4 row is 0 kWh/year). + pages = _summary_pdf_to_textract_style_pages(_SUMMARY_000565_PDF) + site_notes = ElmhurstSiteNotesExtractor(pages).extract() + + # Act + epc = EpcPropertyDataMapper.from_elmhurst_site_notes(site_notes) + + # Assert + assert epc.sap_heating is not None + main_1 = epc.sap_heating.main_heating_details[0] + assert main_1.sap_main_heating_code == 224 + assert main_1.main_heating_category == 4 + + def test_summary_000565_ext1_floor_above_partially_heated_routes_to_u_value_0p7_per_rdsap_10_section_5_14() -> None: # Arrange — RdSAP 10 §5.14 (PDF p.47) "U-value of floor above a # partially heated space": diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index ef2c16d6..ed1e2bb1 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -3853,6 +3853,22 @@ _ELECTRIC_SAP_MAIN_HEATING_CODES: Final[frozenset[int]] = frozenset( + list(range(521, 528)) ) +# SAP 10.2 Table 4a "Heat pumps" rows — codes whose `main_heating_ +# category` is 4 per the table's category column. Used when the cert +# lodges a Table 4a SAP code but no PCDB Table 362 record (the +# preferred identifier per `_elmhurst_main_heating_category`). Subset +# of `_ELECTRIC_SAP_MAIN_HEATING_CODES`. +# +# 211-217 — ground/water source heat pumps +# 221-227 — air source heat pumps (224 = ASHP 2013+, the cert 000565 +# Main 1 lodging) +# 521-527 — warm-air heat pumps +_HEAT_PUMP_SAP_MAIN_HEATING_CODES: Final[frozenset[int]] = frozenset( + list(range(211, 218)) + + list(range(221, 228)) + + list(range(521, 528)) +) + class UnmappedElmhurstLabel(ValueError): """An Elmhurst Summary lodged a finite-enum label that the mapper @@ -4030,22 +4046,22 @@ def _elmhurst_main_heating_category( lodged data. A PCDB index that resolves to a Table 362 record is a heat pump (category 4) — Table 362 lists heat pumps only, so membership is the authoritative signal. A PCDB-referenced boiler on - mains/LPG gas is category 2 (gas-fired boilers). Other system types - fall through to None so the cascade applies its default pumps_fans - 130 kWh/yr until extended. + mains/LPG gas is category 2 (gas-fired boilers). - TODO: route Table 4a HP SAP codes (211-227, 521-527) to - category=4 when no PCDB Table 362 record is lodged. Currently - deferred because the correct dispatch needs (a) Table 12a - high/low rate split for HP-on-E7 cost cascade and (b) Table 4f - MEV / flue-fan / solar HW pump components for pumps_fans — - without both, naive category=4 dispatch overshoots cost by - £1.3k and undershoots pumps_fans by 252 kWh on cert 000565. + SAP 10.2 Table 4a (PDF p.165) lists "Heat pumps" as category 4 for + SAP codes 211-217 (ground/water source), 221-227 (air source) and + 521-527 (warm-air). When a cert lodges one of these codes WITHOUT + a PCDB Table 362 record (e.g. cert 000565 Main 1: SAP code 224 + + `PCDF boiler Reference = 0`), category 4 still applies — the + cascade's pumps_fans path then routes through the HP-specific 0 + kWh/yr row (Table 4f) rather than the 130 kWh/yr default base. """ if pcdb_index is not None and heat_pump_record(pcdb_index) is not None: return _ELMHURST_HEATING_CATEGORY_HEAT_PUMP if pcdb_index is not None and mh.fuel_type in _ELMHURST_GAS_BOILER_FUEL_TYPES: return _ELMHURST_HEATING_CATEGORY_GAS_BOILER + if mh.main_heating_sap_code in _HEAT_PUMP_SAP_MAIN_HEATING_CODES: + return _ELMHURST_HEATING_CATEGORY_HEAT_PUMP return None