"""The wall Recommendation Generator. Detects a treatable main wall on an EpcPropertyData and emits a Recommendation whose Measure Option carries the Simulation Overlay for the intervention. No scoring, no persistence — impact is produced later by scoring (ADR-0016). """ from typing import Optional from datatypes.epc.domain.epc_property_data import ( BuildingPartIdentifier, EpcPropertyData, ) from domain.building_geometry import gross_heat_loss_wall_area from domain.modelling.measure_type import MeasureType from domain.modelling.recommendation import Cost, MeasureOption, Recommendation from domain.modelling.simulation import BuildingPartOverlay, EpcSimulation from repositories.product.product_repository import ProductRepository _CAVITY_MEASURE_TYPE = MeasureType.CAVITY_WALL_INSULATION # RdSAP 10 Table 5 wall_construction: 4 = "Cavity". Table 6 # wall_insulation_type: 4 = "as-built / assumed" (uninsulated), 2 = "Filled # cavity" (the calculator's dedicated filled-cavity U row — see # domain/sap10_ml/rdsap_uvalues.py u_wall). _CAVITY_WALL_CONSTRUCTION = 4 _WALL_UNINSULATED = 4 _FILLED_CAVITY = 2 def recommend_cavity_wall( epc: EpcPropertyData, products: ProductRepository ) -> Optional[Recommendation]: """Return a cavity-fill Recommendation for the main wall when it is an uninsulated cavity wall, else None. The Option's cost is the heat-loss wall 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.wall_construction != _CAVITY_WALL_CONSTRUCTION or main.wall_insulation_type != _WALL_UNINSULATED ): return None product = products.get(_CAVITY_MEASURE_TYPE) wall_area: float = gross_heat_loss_wall_area(epc, BuildingPartIdentifier.MAIN) cost = Cost( total=wall_area * product.unit_cost_per_m2, contingency_rate=product.contingency_rate, ) option = MeasureOption( measure_type=_CAVITY_MEASURE_TYPE, description="Cavity wall insulation (fill the existing cavity)", overlay=EpcSimulation( building_parts={ BuildingPartIdentifier.MAIN: BuildingPartOverlay( wall_insulation_type=_FILLED_CAVITY ) } ), cost=cost, material_id=product.id, ) return Recommendation(surface="Main wall", options=(option,))