From da17ddce8f146983bdaa50237b6c252867471363 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 29 Jun 2026 19:30:25 +0000 Subject: [PATCH] =?UTF-8?q?Stop=20a=20System-built=20landlord=20override?= =?UTF-8?q?=20being=20mis-read=20as=20a=20basement=20=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- domain/modelling/simulation.py | 8 ++++++++ tests/domain/epc/test_wall_type_overlay.py | 24 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/domain/modelling/simulation.py b/domain/modelling/simulation.py index 2f6fc94d..457fa094 100644 --- a/domain/modelling/simulation.py +++ b/domain/modelling/simulation.py @@ -36,6 +36,14 @@ class BuildingPartOverlay: construction_age_band: Optional[str] = None wall_construction: Optional[int] = None wall_insulation_type: Optional[int] = None + # Disambiguates the RdSAP `wall_construction == 6` code collision: gov-EPC + # code 6 = "Basement wall" but RdSAP `WALL_SYSTEM_BUILT` is also 6, and + # `SapBuildingPart.main_wall_is_basement` falls back to the code-6 heuristic + # when the flag is `None`. A Landlord Override that sets a System-built wall + # (construction 6) must therefore set this `False` so the override isn't + # mis-read as a basement — the overlay-path mirror of the gov-API mapper's + # `_clear_basement_flag_when_system_built`. + wall_is_basement: Optional[bool] = None # Added solid-wall insulation depth (mm) — drives the calculator's Table 6 # bucket / §5.8 documentary U-value for EWI (`wall_insulation_type=1`) and # IWI (`wall_insulation_type=3`); λ defaults to 0.04 W/m·K in the calculator. diff --git a/tests/domain/epc/test_wall_type_overlay.py b/tests/domain/epc/test_wall_type_overlay.py index 5b1cc9a8..36015d6f 100644 --- a/tests/domain/epc/test_wall_type_overlay.py +++ b/tests/domain/epc/test_wall_type_overlay.py @@ -25,6 +25,30 @@ def test_solid_brick_with_internal_insulation_overlays_main_wall() -> None: assert overlay.wall_insulation_type == 3 +def test_system_built_override_is_not_mis_read_as_a_basement() -> None: + # A System-built wall is RdSAP code 6, which collides with the gov-EPC + # code-6 basement sentinel. The overlay must set wall_is_basement=False so + # main_wall_is_basement doesn't fire the code-6 heuristic (phantom basement). + simulation = wall_overlay_for( + "System built, as built, no insulation (assumed)", 0 + ) + + assert simulation is not None + overlay = simulation.building_parts[BuildingPartIdentifier.MAIN] + assert overlay.wall_construction == 6 + assert overlay.wall_is_basement is False + + +def test_non_system_built_override_leaves_basement_flag_untouched() -> None: + # Cavity (code 4) can't collide with the basement sentinel, so the overlay + # must not assert a basement verdict either way — leave the flag None. + simulation = wall_overlay_for("Cavity wall, with internal insulation", 0) + + assert simulation is not None + overlay = simulation.building_parts[BuildingPartIdentifier.MAIN] + assert overlay.wall_is_basement is None + + @pytest.mark.parametrize( ("wall_type_value", "construction", "insulation"), [