mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
feat(modelling): room-in-roof safety guard defers the roof generator
A room-in-roof carries its insulation on its own sloping/stud/gable surfaces (RdSAP 10 §3.10, Table 17/18), which the roof overlay's flat roof_insulation_thickness bump cannot model. Without a guard a RR with an uninsulated loft fell through to the loft fallback and mis-recommended 300 mm loft insulation. Return None when the main part lodges a sap_room_in_roof, deferring until a dedicated RR branch lands (ADR-0021). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
8323d9cf07
commit
f33bb9d52d
2 changed files with 30 additions and 0 deletions
|
|
@ -49,6 +49,16 @@ def recommend_roof_insulation(
|
|||
for part in epc.sap_building_parts
|
||||
if part.identifier is BuildingPartIdentifier.MAIN
|
||||
)
|
||||
|
||||
# Room-in-roof safety guard (ADR-0021): a room-in-roof carries its
|
||||
# insulation on its own sloping/stud/gable surfaces (RdSAP 10 §3.10, Table
|
||||
# 17/18), which the loft/sloping overlay's flat `roof_insulation_thickness`
|
||||
# bump cannot model. Without this guard a RR with an uninsulated loft would
|
||||
# fall through to the loft fallback and mis-recommend loft insulation.
|
||||
# Defer until a dedicated RR branch lands.
|
||||
if main.sap_room_in_roof is not None:
|
||||
return None
|
||||
|
||||
roof_type: str = (main.roof_construction_type or "").lower()
|
||||
|
||||
# Dispatch by roof type (ADR-0021). Order matters: a sloping ceiling is
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from datatypes.epc.domain.epc_property_data import (
|
|||
BuildingPartIdentifier,
|
||||
EpcPropertyData,
|
||||
SapBuildingPart,
|
||||
SapRoomInRoof,
|
||||
)
|
||||
from domain.modelling.scoring.overlay_applicator import apply_simulations
|
||||
from domain.modelling.product import Product
|
||||
|
|
@ -63,6 +64,25 @@ def test_already_insulated_loft_yields_no_recommendation() -> None:
|
|||
assert recommendation is None
|
||||
|
||||
|
||||
def test_room_in_roof_yields_no_recommendation_pending_a_dedicated_branch() -> None:
|
||||
# Arrange — an uninsulated loft the fallback would otherwise top up, but the
|
||||
# part is a room-in-roof. The simple loft/sloping overlay can't model RR
|
||||
# insulation (its sloping/stud/gable surfaces carry their own U-values via
|
||||
# Table 17/18), so the generator must defer rather than mis-fire loft.
|
||||
baseline: EpcPropertyData = build_epc()
|
||||
main: SapBuildingPart = _part(baseline, BuildingPartIdentifier.MAIN)
|
||||
main.roof_insulation_thickness = 0
|
||||
main.sap_room_in_roof = SapRoomInRoof(floor_area=9.0, construction_age_band="D")
|
||||
|
||||
# Act
|
||||
recommendation: Recommendation | None = recommend_roof_insulation(
|
||||
baseline, _StubProducts()
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert recommendation is None
|
||||
|
||||
|
||||
def test_loft_option_carries_cost_from_roof_area_and_product() -> None:
|
||||
# Arrange
|
||||
baseline: EpcPropertyData = build_epc() # MAIN roof area 14.85 m^2
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue