mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
Recommend loft insulation for a loft below the building-regs depth 🟩
A lodged numeric loft depth below the 270mm building-regs compliance gate is now eligible (topped up to the 300mm install depth), regardless of whether the roof type is lodged, since a measured depth is a positive statement of a real loft. Sentinels keep their ADR-0047 resolution. Fixes property 724702 (50mm loft) which previously returned no recommendation. ADR-0063. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2fe5b79dc4
commit
95a0da4828
1 changed files with 39 additions and 6 deletions
|
|
@ -30,6 +30,12 @@ _SLOPING_CEILING_MEASURE_TYPE = MeasureType.SLOPING_CEILING_INSULATION
|
|||
# measure at 300 mm; pinning the before→after cascade (000490/001431) requires
|
||||
# the overlay to match that depth exactly (see test_elmhurst_cascade_pins).
|
||||
_RECOMMENDED_LOFT_THICKNESS_MM = 300
|
||||
# Building Regulations Approved Document L loft/ceiling compliance gate (mm): a
|
||||
# lodged loft shallower than this is sub-standard and eligible for a top-up to
|
||||
# `_RECOMMENDED_LOFT_THICKNESS_MM` (ADR-0063). The gate (270, "is this at
|
||||
# building regs?") and the install depth (300, "how deep do we lay it?") are
|
||||
# deliberately different numbers.
|
||||
_BUILDING_REGS_LOFT_DEPTH_MM: Final[int] = 270
|
||||
# Recommended sloping-ceiling depth (mm); Elmhurst re-lodges 100 mm (ADR-0021).
|
||||
_RECOMMENDED_SLOPING_CEILING_THICKNESS_MM = 100
|
||||
# Age bands whose as-built pitched roof is uninsulated (ADR-0047: A-D
|
||||
|
|
@ -62,6 +68,23 @@ def _pitched_roof_is_uninsulated(
|
|||
and age_band.upper() in _AS_BUILT_UNINSULATED_AGE_BANDS
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def _lodged_numeric_depth_mm(thickness: Union[str, int, None]) -> Optional[int]:
|
||||
"""The lodged loft depth as a whole number of millimetres, or None when the
|
||||
value is a sentinel or unlodged (``ND`` / ``NI`` / ``"As Built"`` / None).
|
||||
Parses a bare int, an ``"Nmm"`` string (``"50mm"``), and an ``"Nmm+"`` string
|
||||
(``"400mm+"`` → 400). Non-numeric values return None so they fall through to
|
||||
the sentinel resolution (`_pitched_roof_is_uninsulated`), preserving the
|
||||
ADR-0047 split (ADR-0063)."""
|
||||
if isinstance(thickness, int):
|
||||
return thickness
|
||||
if thickness is None:
|
||||
return None
|
||||
digits: str = thickness.strip().lower().removesuffix("+").removesuffix("mm").strip()
|
||||
return int(digits) if digits.isdigit() else None
|
||||
|
||||
|
||||
_FLAT_ROOF_MEASURE_TYPE = MeasureType.FLAT_ROOF_INSULATION
|
||||
# Recommended flat-roof depth (mm); Elmhurst re-lodges 200 mm (ADR-0021).
|
||||
_RECOMMENDED_FLAT_ROOF_THICKNESS_MM = 200
|
||||
|
|
@ -134,15 +157,25 @@ def recommend_roof_insulation(
|
|||
# codes 6/7 ("(another dwelling above)" / insulated thatch) map the type
|
||||
# to None and real code-7 certs lodge "ND" — with zero roof heat loss
|
||||
# (RdSAP 10 Table 18), so only an explicit 0 may trigger it.
|
||||
# A lodged NUMERIC depth is a positive statement of a real, accessible loft
|
||||
# (a party ceiling never lodges a measured roof depth), so it is eligible
|
||||
# whenever it is below the building-regs compliance gate — regardless of
|
||||
# whether the roof type is lodged, and covering the explicit-0 uninsulated
|
||||
# case. A SENTINEL (or nothing) keeps the ADR-0047 resolution: known-pitched
|
||||
# resolves by age band, an unlodged type defers (party-ceiling guard).
|
||||
known_pitched: bool = "pitched" in roof_type or "thatch" in roof_type
|
||||
uninsulated: bool = (
|
||||
_pitched_roof_is_uninsulated(
|
||||
lodged_depth_mm: Optional[int] = _lodged_numeric_depth_mm(
|
||||
main.roof_insulation_thickness
|
||||
)
|
||||
if lodged_depth_mm is not None:
|
||||
eligible: bool = lodged_depth_mm < _BUILDING_REGS_LOFT_DEPTH_MM
|
||||
elif known_pitched:
|
||||
eligible = _pitched_roof_is_uninsulated(
|
||||
main.roof_insulation_thickness, main.construction_age_band
|
||||
)
|
||||
if known_pitched
|
||||
else main.roof_insulation_thickness == 0
|
||||
)
|
||||
if not uninsulated:
|
||||
else:
|
||||
eligible = False
|
||||
if not eligible:
|
||||
return None
|
||||
return _roof_recommendation(
|
||||
epc,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue