diff --git a/packages/domain/src/domain/sap/rdsap/cert_to_inputs.py b/packages/domain/src/domain/sap/rdsap/cert_to_inputs.py index cb8032bd..690d2368 100644 --- a/packages/domain/src/domain/sap/rdsap/cert_to_inputs.py +++ b/packages/domain/src/domain/sap/rdsap/cert_to_inputs.py @@ -50,7 +50,7 @@ from datatypes.epc.domain.epc_property_data import ( from domain.ml.demand import predicted_hot_water_kwh, predicted_lighting_kwh from domain.ml.sap_efficiencies import ( seasonal_efficiency, - water_heating_efficiency, + water_heating_efficiency as _legacy_water_heating_efficiency, ) from domain.sap.calculator import CalculatorInputs, WindowInput from domain.sap.tables.table_12 import co2_factor_kg_per_kwh, unit_price_p_per_kwh @@ -411,6 +411,35 @@ def _other_fuel_cost_gbp_per_kwh(prices: PriceTable) -> float: return prices.standard_electricity_p_per_kwh * _PENCE_TO_GBP +# Water-heating codes that say "inherit from the main system" — the +# `seasonal_efficiency` cascade returns 0 as a sentinel for these in the +# legacy `domain.ml.sap_efficiencies` module. We need to inherit through +# the SAME cascade the main heating uses, including the main_heating_ +# category fallback (e.g. heat pumps return 2.30 via category 4). +_WATER_INHERIT_FROM_MAIN_CODES: Final[frozenset[int]] = frozenset({901, 902, 914}) + + +def _water_efficiency_with_category_inherit( + *, + water_heating_code: Optional[int], + main_code: Optional[int], + main_category: Optional[int], + main_fuel: Optional[int], +) -> float: + """When the cert says "hot water comes from the main system" (codes + 901 / 902 / 914), inherit the main system's efficiency — and crucially + inherit the cascade that maps `main_heating_category` to a default + when `sap_main_heating_code` is None. The legacy water_heating_efficiency + only passes main_code through and so collapses heat pumps (cat 4) + + no-code lodgements into the 0.80 gas-boiler default. + """ + if water_heating_code is None: + return _legacy_water_heating_efficiency(None, main_code) + if water_heating_code in _WATER_INHERIT_FROM_MAIN_CODES: + return seasonal_efficiency(main_code, main_category, main_fuel) + return _legacy_water_heating_efficiency(water_heating_code, main_code) + + def _co2_factor_kg_per_kwh(main: Optional[MainHeatingDetail]) -> float: @@ -499,7 +528,12 @@ def cert_to_inputs( ) eff = seasonal_efficiency(main_code, main_category, main_fuel) - water_eff = water_heating_efficiency(epc.sap_heating.water_heating_code, main_code) + water_eff = _water_efficiency_with_category_inherit( + water_heating_code=epc.sap_heating.water_heating_code, + main_code=main_code, + main_category=main_category, + main_fuel=main_fuel, + ) hw_kwh = predicted_hot_water_kwh( total_floor_area_m2=epc.total_floor_area_m2, seasonal_efficiency_water=water_eff,