diff --git a/domain/epc_prediction/comparable_properties.py b/domain/epc_prediction/comparable_properties.py index 13e33cf73..47b762346 100644 --- a/domain/epc_prediction/comparable_properties.py +++ b/domain/epc_prediction/comparable_properties.py @@ -21,6 +21,10 @@ from domain.geospatial.coordinates import Coordinates # else it is relaxed (ADR-0029 filter-then-relax ladder). _DEFAULT_MINIMUM_COHORT = 5 +# Half-width of the floor-area conditioning band: an expired Historic EPC's +# observed floor area keeps comparables within ±5% of it (ADR-0054). +_FLOOR_AREA_TOLERANCE = 0.05 + @dataclass(frozen=True) class ComparableProperty: @@ -88,6 +92,27 @@ def select_comparables( active=target.wall_construction is not None, minimum_cohort=minimum_cohort, ) + cohort = _maybe_filter( + cohort, + lambda c: _main_construction_age_band(c) == target.construction_age_band, + active=target.construction_age_band is not None, + minimum_cohort=minimum_cohort, + ) + cohort = _maybe_filter( + cohort, + lambda c: _main_fuel_type(c) == target.main_fuel, + active=target.main_fuel is not None, + minimum_cohort=minimum_cohort, + ) + target_area = target.total_floor_area_m2 + cohort = _maybe_filter( + cohort, + lambda c: target_area is not None + and abs(c.epc.total_floor_area_m2 - target_area) + <= _FLOOR_AREA_TOLERANCE * target_area, + active=target_area is not None, + minimum_cohort=minimum_cohort, + ) return ComparableProperties(members=tuple(cohort)) @@ -124,3 +149,15 @@ def _main_wall_construction(comparable: ComparableProperty) -> object: """The main building part's wall construction, or None when no part lodged.""" parts = comparable.epc.sap_building_parts return parts[0].wall_construction if parts else None + + +def _main_construction_age_band(comparable: ComparableProperty) -> object: + """The main building part's Table S1 band letter, or None when no part lodged.""" + parts = comparable.epc.sap_building_parts + return parts[0].construction_age_band if parts else None + + +def _main_fuel_type(comparable: ComparableProperty) -> object: + """The primary heating fuel code, or None when no main heating lodged.""" + details = comparable.epc.sap_heating.main_heating_details + return details[0].main_fuel_type if details else None