mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
recommend_floor_insulation(epc, products) detects an uninsulated ground floor (SapBuildingPart.floor_insulation_thickness blank/zero) and its construction from floor_construction_type — 'Suspended timber' -> suspended_floor_insulation, 'Solid' -> solid_floor_insulation — emitting the matching single Option (a floor is one construction, like a cavity wall) with the overlay (floor_insulation_thickness = 100 mm) and a priced Cost (ground-floor area x the Product's fully-loaded unit cost + contingency). - building_geometry.ground_floor_area(epc, identifier): the lowest floor's (floor == 0) area. Pinned 14.85 m^2 on 000490 MAIN. - BuildingPartOverlay gains floor_insulation_thickness (generic Applicator writes it unchanged). suspended (0.20) / solid (0.26) floor contingencies. Progress on #1159 (generator + geometry); end-to-end + Elmhurst pin pending the orchestrator (#1157) and parser. Four behaviour tests (suspended / solid / none / cost) + geometry pin. pyright strict clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40 lines
1.4 KiB
Python
40 lines
1.4 KiB
Python
"""The Simulation Overlay (`EpcSimulation`) — the change a single Measure
|
|
Option makes to a Property's EpcPropertyData.
|
|
|
|
An all-optional partial mirror of EpcPropertyData / SapBuildingPart, covering
|
|
the retrofit-relevant surface only (wall fields first). It is *not* an
|
|
EpcPropertyData — composition, not inheritance — and carries no scores.
|
|
Building parts are targeted by `BuildingPartIdentifier` so a measure addresses
|
|
the exact `SapBuildingPart` (the main wall vs an extension). See CONTEXT.md.
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Mapping, Optional
|
|
|
|
from datatypes.epc.domain.epc_property_data import BuildingPartIdentifier
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class BuildingPartOverlay:
|
|
"""All-optional partial of `SapBuildingPart` (wall surface first).
|
|
|
|
A `None` field means "leave the baseline value unchanged".
|
|
"""
|
|
|
|
wall_insulation_type: Optional[int] = None
|
|
roof_insulation_thickness: Optional[int] = None
|
|
floor_insulation_thickness: Optional[int] = None
|
|
|
|
|
|
def _no_building_parts() -> dict[BuildingPartIdentifier, BuildingPartOverlay]:
|
|
return {}
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class EpcSimulation:
|
|
"""A Simulation Overlay: the per-building-part changes a Measure Option
|
|
makes, keyed by `BuildingPartIdentifier`."""
|
|
|
|
building_parts: Mapping[BuildingPartIdentifier, BuildingPartOverlay] = field(
|
|
default_factory=_no_building_parts
|
|
)
|