mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
Resolve a landlord double-glazing override to its glazing code 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fd922a26c2
commit
f1c6825cae
3 changed files with 66 additions and 0 deletions
23
domain/epc/property_overlays/glazing_overlay.py
Normal file
23
domain/epc/property_overlays/glazing_overlay.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
"""Map a Landlord-Override glazing value to a glazing Simulation Overlay.
|
||||
|
||||
A glazing value is one canonical glazing description carrying type + era
|
||||
("Double glazing, 2002 or later", "Single glazing", "Triple glazing, 2002 or
|
||||
later"). The calculator derives each window's U-value from its SAP10
|
||||
`glazing_type` code via the RdSAP Table 24 cascade, so the overlay decomposes
|
||||
the value into that code and emits a whole-dwelling `GlazingOverlay` (a landlord
|
||||
describes the dwelling's glazing as a whole, with no per-window geometry, so
|
||||
`building_part` is ignored). `_fold_glazing` expands it across every window.
|
||||
Unresolvable values produce no overlay.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from domain.modelling.simulation import EpcSimulation
|
||||
|
||||
|
||||
def glazing_overlay_for(
|
||||
glazing_value: str, building_part: int
|
||||
) -> Optional[EpcSimulation]:
|
||||
raise NotImplementedError
|
||||
|
|
@ -73,6 +73,28 @@ class WindowOverlay:
|
|||
solar_transmittance: Optional[float] = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GlazingOverlay:
|
||||
"""All-optional partial of the dwelling's whole-glazing state — the
|
||||
correction a Landlord Override makes when the lodged glazing is wrong.
|
||||
|
||||
Unlike a per-window `WindowOverlay` (keyed by `sap_windows` index), this
|
||||
targets no single window: a landlord describes the dwelling's glazing as a
|
||||
whole ("Double glazing, 2002 or later") with no per-window geometry, so the
|
||||
overlay builder (which never sees the baseline window list) emits one of
|
||||
these and `_fold_glazing` expands it across every `sap_windows` entry.
|
||||
|
||||
`glazing_type` is the SAP10 glazing-type code (Table 24 / `u_window`
|
||||
cascade: 1=single, 2=double 2002-2021, 3=double pre-2002, 9=triple 2002+,
|
||||
…). The fold sets it on every window AND clears each window's lodged
|
||||
transmission U-value, so the Table-24 cascade re-derives the corrected U
|
||||
from the new type (the lodged U was for the OLD, mis-recorded glazing).
|
||||
A `None` field means "leave the baseline value unchanged".
|
||||
"""
|
||||
|
||||
glazing_type: Optional[int] = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LightingOverlay:
|
||||
"""All-optional partial of the dwelling's fixed-lighting bulb counts — the
|
||||
|
|
@ -220,6 +242,7 @@ class EpcSimulation:
|
|||
windows: Mapping[int, WindowOverlay] = field(default_factory=_no_windows)
|
||||
ventilation: Optional[VentilationOverlay] = None
|
||||
lighting: Optional[LightingOverlay] = None
|
||||
glazing: Optional[GlazingOverlay] = None
|
||||
heating: Optional[HeatingOverlay] = None
|
||||
secondary_heating: Optional[SecondaryHeatingOverlay] = None
|
||||
solar: Optional[SolarOverlay] = None
|
||||
|
|
|
|||
20
tests/domain/epc/test_glazing_overlay.py
Normal file
20
tests/domain/epc/test_glazing_overlay.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
"""The Landlord-Override glazing → glazing Simulation Overlay mapping.
|
||||
|
||||
A glazing value resolves to the SAP10 `glazing_type` code the calculator's
|
||||
Table-24 cascade reads; the overlay is whole-dwelling (expanded across every
|
||||
window by `_fold_glazing`).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from domain.epc.property_overlays.glazing_overlay import glazing_overlay_for
|
||||
|
||||
|
||||
def test_double_glazing_post_2002_overlays_its_glazing_code() -> None:
|
||||
# Act
|
||||
simulation = glazing_overlay_for("Double glazing, 2002 or later", 0)
|
||||
|
||||
# Assert — double glazing 2002-2021 is SAP10 glazing_type code 2.
|
||||
assert simulation is not None
|
||||
assert simulation.glazing is not None
|
||||
assert simulation.glazing.glazing_type == 2
|
||||
Loading…
Add table
Reference in a new issue