mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice 1 of solid-wall insulation. BuildingPartOverlay gains a wall_insulation_thickness field; the generic applicator already folds it onto SapBuildingPart by name. With wall_insulation_type=1 (EWI) / 3 (IWI) + 100 mm, the calculator derives the post-insulation U-value (§5.8 documentary path, λ=0.04 default) — and for IWI also lowers the thermal-mass parameter. Two new Elmhurst before/after cascade pins (solid-brick EWI + IWI, cert 001431) reproduce the re-lodged after at abs(diff) <= 1e-4 across SAP/CO2/PE. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
63 lines
2.5 KiB
Python
63 lines
2.5 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
|
|
# 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.
|
|
wall_insulation_thickness: Optional[int] = None
|
|
roof_insulation_thickness: Optional[int] = None
|
|
floor_insulation_thickness: Optional[int] = None
|
|
floor_insulation_type_str: Optional[str] = None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class VentilationOverlay:
|
|
"""All-optional partial of `SapVentilation` — the whole-dwelling ventilation
|
|
change a Measure Option makes (e.g. retrofit MEV). Unlike a
|
|
`BuildingPartOverlay` this targets no building part; it folds onto the
|
|
dwelling's single `sap_ventilation`.
|
|
|
|
`mechanical_ventilation_kind` names the SAP10.2 §2 mechanical-ventilation
|
|
kind (the `MechanicalVentilationKind` enum name, e.g.
|
|
``"EXTRACT_OR_PIV_OUTSIDE"`` for decentralised MEV). A `None` field means
|
|
"leave the baseline value unchanged".
|
|
"""
|
|
|
|
mechanical_ventilation_kind: Optional[str] = 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`, plus an optional whole-dwelling
|
|
`ventilation` change (the Measure Dependency surface — ADR-0016)."""
|
|
|
|
building_parts: Mapping[BuildingPartIdentifier, BuildingPartOverlay] = field(
|
|
default_factory=_no_building_parts
|
|
)
|
|
ventilation: Optional[VentilationOverlay] = None
|