From 7d40cddf3b5f17cc0cccc4336b18b90f22fe2d00 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Thu, 4 Jun 2026 21:12:01 +0000 Subject: [PATCH] feat(modelling): fold loft into the roof dispatcher; thatch routes to loft MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Slice 2 (ADR-0021). `recommend_roof_insulation` now owns the loft branch as the fallback — a plain pitched loft, a thatched roof (the covering doesn't block insulating the loft floor), or an unlodged roof type all take loft (joist) insulation at 300 mm when `roof_insulation_thickness == 0`. Sloping is tested first; a no-access roof gets nothing. Retired the standalone `recommend_loft_insulation`; the orchestrator and its tests now call the dispatcher. Pinned: thatch before→after (None→300) reproduces at 1e-4; the existing loft pin still holds through the dispatcher. Behaviour-preserving on the golden cohort (roof measure unchanged: none across all 57) — the dispatch is strictly more precise (won't fire loft on a sloping/no-access roof). Co-Authored-By: Claude Opus 4.8 --- .../generators/roof_recommendation.py | 77 +++++++----------- orchestration/modelling_orchestrator.py | 4 +- .../fixtures/loft_thatched_001431_after.pdf | Bin 0 -> 80022 bytes .../fixtures/loft_thatched_001431_before.pdf | Bin 0 -> 80004 bytes .../modelling/test_elmhurst_cascade_pins.py | 30 ++++++- .../modelling/test_roof_recommendation.py | 8 +- 6 files changed, 63 insertions(+), 56 deletions(-) create mode 100644 tests/domain/modelling/fixtures/loft_thatched_001431_after.pdf create mode 100644 tests/domain/modelling/fixtures/loft_thatched_001431_before.pdf diff --git a/domain/modelling/generators/roof_recommendation.py b/domain/modelling/generators/roof_recommendation.py index 8bbbd34b..c7ab223e 100644 --- a/domain/modelling/generators/roof_recommendation.py +++ b/domain/modelling/generators/roof_recommendation.py @@ -1,9 +1,13 @@ """The roof Recommendation Generator. -Detects an uninsulated loft on an EpcPropertyData and emits a Recommendation -whose Measure Option carries the loft-insulation Simulation Overlay and a priced +Dispatches the MAIN roof to its single applicable insulation Measure by roof +type (ADR-0021): a sloping ceiling (rafters, 100 mm), or — the fallback — a +loft / thatched / unlodged pitched roof (joists, 300 mm); a no-access roof gets +nothing. Each emits one "Roof" Recommendation whose Option carries the +insulation Simulation Overlay (raising `roof_insulation_thickness`) and a priced Cost (roof area x the Product's fully-loaded unit cost, with its contingency). -No scoring, no persistence — impact is produced later by scoring (ADR-0016). +Flat-roof and room-in-roof branches land in later slices. No scoring, no +persistence — impact is produced later by scoring (ADR-0016). """ from typing import Optional @@ -19,12 +23,9 @@ from repositories.product.product_repository import ProductRepository _LOFT_MEASURE_TYPE = "loft_insulation" _SLOPING_CEILING_MEASURE_TYPE = "sloping_ceiling_insulation" -# RdSAP 10 Table 16: 0 mm lodged roof insulation is an uninsulated loft. +# RdSAP 10 Table 16: 0 mm lodged roof insulation is an uninsulated loft. The +# Elmhurst mapper resolves "As Built" to 0 for pitched/sloping/loft roofs. _ROOF_UNINSULATED_MM = 0 -# A roof is uninsulated when its lodged insulation thickness is 0 or absent — -# the Elmhurst mapper resolves "As Built" to 0 for pitched/sloping roofs and to -# None for flat roofs (ADR-0021). -_ROOF_UNINSULATED_THICKNESSES: tuple[Optional[int], ...] = (None, 0) # Recommended loft-insulation depth (mm). Elmhurst re-lodges a loft-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). @@ -33,44 +34,6 @@ _RECOMMENDED_LOFT_THICKNESS_MM = 300 _RECOMMENDED_SLOPING_CEILING_THICKNESS_MM = 100 -def recommend_loft_insulation( - epc: EpcPropertyData, products: ProductRepository -) -> Optional[Recommendation]: - """Return a loft-insulation Recommendation for the main roof when it is - uninsulated, else None. The Option's cost is the roof area priced at the - Product's fully-loaded unit cost, with its contingency.""" - main = next( - part - for part in epc.sap_building_parts - if part.identifier is BuildingPartIdentifier.MAIN - ) - - if main.roof_insulation_thickness != _ROOF_UNINSULATED_MM: - return None - - product = products.get(_LOFT_MEASURE_TYPE) - area: float = roof_area(epc, BuildingPartIdentifier.MAIN) - cost = Cost( - total=area * product.unit_cost_per_m2, - contingency_rate=product.contingency_rate, - ) - - option = MeasureOption( - measure_type=_LOFT_MEASURE_TYPE, - description="Loft insulation (top up to recommended depth)", - overlay=EpcSimulation( - building_parts={ - BuildingPartIdentifier.MAIN: BuildingPartOverlay( - roof_insulation_thickness=_RECOMMENDED_LOFT_THICKNESS_MM - ) - } - ), - cost=cost, - material_id=product.id, - ) - return Recommendation(surface="Roof", options=(option,)) - - def recommend_roof_insulation( epc: EpcPropertyData, products: ProductRepository ) -> Optional[Recommendation]: @@ -85,8 +48,14 @@ def recommend_roof_insulation( ) roof_type: str = (main.roof_construction_type or "").lower() + # Dispatch by roof type (ADR-0021). Order matters: a sloping ceiling is + # tested before the loft fallback, and "no access" before it too, because + # "no access to loft" contains "loft". Loft is the fallback — it covers a + # plain pitched loft, a thatched roof (the covering doesn't block insulating + # the loft floor), and an unlodged roof type (the modal UK case), matching + # the pre-dispatcher behaviour of firing on `roof_insulation_thickness == 0`. if "sloping ceiling" in roof_type: - if main.roof_insulation_thickness not in _ROOF_UNINSULATED_THICKNESSES: + if main.roof_insulation_thickness != _ROOF_UNINSULATED_MM: return None return _roof_recommendation( epc, @@ -95,7 +64,19 @@ def recommend_roof_insulation( description="Sloping-ceiling insulation (insulate at the rafters)", thickness_mm=_RECOMMENDED_SLOPING_CEILING_THICKNESS_MM, ) - return None + + if "no access" in roof_type: + return None # the roof void can't be reached to insulate it + + if main.roof_insulation_thickness != _ROOF_UNINSULATED_MM: + return None + return _roof_recommendation( + epc, + products, + measure_type=_LOFT_MEASURE_TYPE, + description="Loft insulation (top up to recommended depth)", + thickness_mm=_RECOMMENDED_LOFT_THICKNESS_MM, + ) def _roof_recommendation( diff --git a/orchestration/modelling_orchestrator.py b/orchestration/modelling_orchestrator.py index e8e5d042..795f012e 100644 --- a/orchestration/modelling_orchestrator.py +++ b/orchestration/modelling_orchestrator.py @@ -18,7 +18,7 @@ from domain.modelling.optimisation.optimiser import ( from domain.modelling.scoring.package_scorer import PackageScorer, Score from domain.modelling.plan import Plan, PlanMeasure from domain.modelling.recommendation import MeasureOption, Recommendation -from domain.modelling.generators.roof_recommendation import recommend_loft_insulation +from domain.modelling.generators.roof_recommendation import recommend_roof_insulation from domain.modelling.scenario import Scenario from domain.modelling.scoring.scoring import ( MeasureImpact, @@ -203,7 +203,7 @@ def _candidate_recommendations( found = ( recommend_cavity_wall(effective_epc, products), recommend_solid_wall(effective_epc, products, planning_restrictions), - recommend_loft_insulation(effective_epc, products), + recommend_roof_insulation(effective_epc, products), recommend_floor_insulation(effective_epc, products), ) return [recommendation for recommendation in found if recommendation is not None] diff --git a/tests/domain/modelling/fixtures/loft_thatched_001431_after.pdf b/tests/domain/modelling/fixtures/loft_thatched_001431_after.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a568b1e515faf4bc4b05dc4a622fd01e4c367c59 GIT binary patch literal 80022 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 J&W==<{~z$;$4dYJ literal 0 HcmV?d00001 diff --git a/tests/domain/modelling/test_elmhurst_cascade_pins.py b/tests/domain/modelling/test_elmhurst_cascade_pins.py index 6bffc3e2..d6e38983 100644 --- a/tests/domain/modelling/test_elmhurst_cascade_pins.py +++ b/tests/domain/modelling/test_elmhurst_cascade_pins.py @@ -28,7 +28,6 @@ from domain.modelling.product import Product from domain.modelling.recommendation import Recommendation from domain.modelling.generators.floor_recommendation import recommend_floor_insulation from domain.modelling.generators.roof_recommendation import ( - recommend_loft_insulation, recommend_roof_insulation, ) from domain.modelling.simulation import BuildingPartOverlay, EpcSimulation @@ -328,7 +327,7 @@ def test_loft_overlay_reproduces_the_relodged_after() -> None: after: EpcPropertyData = parse_recommendation_summary( "loft_001431_after.pdf" ) - recommendation: Recommendation | None = recommend_loft_insulation( + recommendation: Recommendation | None = recommend_roof_insulation( before, _AnyProduct() ) assert recommendation is not None @@ -366,6 +365,33 @@ def test_roof_generator_insulates_a_sloping_ceiling_pinning_its_after() -> None: ) +def test_roof_generator_insulates_a_thatched_roof_as_loft_pinning_its_after() -> None: + # Arrange — a thatched pitched roof. Thatch is NOT excluded: the covering + # doesn't block insulating the loft floor, so it takes loft (joist) + # insulation, re-lodged None → 300 mm (ADR-0021). + before: EpcPropertyData = parse_recommendation_summary( + "loft_thatched_001431_before.pdf" + ) + after: EpcPropertyData = parse_recommendation_summary( + "loft_thatched_001431_after.pdf" + ) + + # Act — the dispatcher routes a thatched roof to the loft branch. + recommendation: Recommendation | None = recommend_roof_insulation( + before, _AnyProduct() + ) + assert recommendation is not None + options: dict[str, MeasureOption] = { + option.measure_type: option for option in recommendation.options + } + + # Assert — one loft Option whose overlay reproduces the after. + assert set(options) == {"loft_insulation"} + _assert_overlay_reproduces_after( + before, after, options["loft_insulation"].overlay + ) + + def test_solid_floor_overlay_reproduces_the_relodged_after() -> None: # Arrange before: EpcPropertyData = parse_recommendation_summary( diff --git a/tests/domain/modelling/test_roof_recommendation.py b/tests/domain/modelling/test_roof_recommendation.py index baa2845b..fa27d03b 100644 --- a/tests/domain/modelling/test_roof_recommendation.py +++ b/tests/domain/modelling/test_roof_recommendation.py @@ -11,7 +11,7 @@ from datatypes.epc.domain.epc_property_data import ( from domain.modelling.scoring.overlay_applicator import apply_simulations from domain.modelling.product import Product from domain.modelling.recommendation import Recommendation -from domain.modelling.generators.roof_recommendation import recommend_loft_insulation +from domain.modelling.generators.roof_recommendation import recommend_roof_insulation from repositories.product.product_repository import ProductRepository from tests.domain.sap10_calculator.worksheet._elmhurst_worksheet_000490 import ( build_epc, @@ -35,7 +35,7 @@ def test_uninsulated_loft_yields_a_loft_insulation_recommendation() -> None: _part(baseline, BuildingPartIdentifier.MAIN).roof_insulation_thickness = 0 # Act - recommendation: Recommendation | None = recommend_loft_insulation( + recommendation: Recommendation | None = recommend_roof_insulation( baseline, _StubProducts() ) @@ -55,7 +55,7 @@ def test_already_insulated_loft_yields_no_recommendation() -> None: _part(baseline, BuildingPartIdentifier.MAIN).roof_insulation_thickness = 300 # Act - recommendation: Recommendation | None = recommend_loft_insulation( + recommendation: Recommendation | None = recommend_roof_insulation( baseline, _StubProducts() ) @@ -69,7 +69,7 @@ def test_loft_option_carries_cost_from_roof_area_and_product() -> None: _part(baseline, BuildingPartIdentifier.MAIN).roof_insulation_thickness = 0 # Act - recommendation: Recommendation | None = recommend_loft_insulation( + recommendation: Recommendation | None = recommend_roof_insulation( baseline, _StubProducts() )