mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
feat(modelling): planning protection picks secondary over double glazing
Slice 3 of the glazing generator (ADR-0022): a conservation/listed/heritage protection (PlanningRestrictions.blocks_external) hard-picks secondary_glazing instead of double_glazing -- an internal second pane, since the external units can't be replaced on a protected building. Each single-glazed window upgrades to the secondary target pinned from cert 001431 (glazing_type=7, u_value=2.90, solar_transmittance=0.85 -- the outer single pane still drives solar gain). The before/after cascade pins for both measures remain deferred behind the glazing-label mapper coverage (owned by another agent). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
8d081cb9d6
commit
276dd1a500
2 changed files with 39 additions and 4 deletions
|
|
@ -56,6 +56,17 @@ _DOUBLE: Final[_GlazingTarget] = _GlazingTarget(
|
|||
u_value=1.40,
|
||||
solar_transmittance=0.72,
|
||||
)
|
||||
# Protected (conservation/listed/heritage): fit an internal secondary pane
|
||||
# (gt=7 "Secondary glazing"; U→2.90, g unchanged at 0.85 — the existing outer
|
||||
# single pane still drives solar gain). The external units can't be replaced on
|
||||
# a protected/over-looked building, so this is the planning-picked Measure.
|
||||
_SECONDARY: Final[_GlazingTarget] = _GlazingTarget(
|
||||
measure_type="secondary_glazing",
|
||||
description="Fit secondary glazing to the single-glazed windows",
|
||||
glazing_type=7,
|
||||
u_value=2.90,
|
||||
solar_transmittance=0.85,
|
||||
)
|
||||
|
||||
|
||||
def recommend_glazing(
|
||||
|
|
@ -63,9 +74,10 @@ def recommend_glazing(
|
|||
products: ProductRepository,
|
||||
restrictions: PlanningRestrictions = PlanningRestrictions(),
|
||||
) -> Optional[Recommendation]:
|
||||
"""Return a glazing Recommendation upgrading every single-glazed window with
|
||||
double glazing (its single Option), else None when the dwelling has no
|
||||
single-glazed windows."""
|
||||
"""Return a glazing Recommendation upgrading every single-glazed window — its
|
||||
single planning-picked Option (double glazing, or secondary glazing where a
|
||||
planning protection blocks replacing the external units) — else None when
|
||||
the dwelling has no single-glazed windows."""
|
||||
single_indices = tuple(
|
||||
index
|
||||
for index, window in enumerate(epc.sap_windows)
|
||||
|
|
@ -74,7 +86,7 @@ def recommend_glazing(
|
|||
if not single_indices:
|
||||
return None
|
||||
|
||||
target: _GlazingTarget = _DOUBLE
|
||||
target: _GlazingTarget = _SECONDARY if restrictions.blocks_external else _DOUBLE
|
||||
product = products.get(target.measure_type)
|
||||
|
||||
overlay = EpcSimulation(
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ consumes those per-window values directly. Detection + pricing only, no scores
|
|||
import copy
|
||||
|
||||
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
||||
from domain.geospatial.planning_restrictions import PlanningRestrictions
|
||||
from domain.modelling.generators.glazing_recommendation import recommend_glazing
|
||||
from domain.modelling.product import Product
|
||||
from domain.modelling.recommendation import Recommendation
|
||||
|
|
@ -101,3 +102,25 @@ def test_recommendation_prices_a_flat_average_per_single_glazed_window() -> None
|
|||
assert cost.total == 3 * 600.0
|
||||
assert cost.contingency_rate == 0.2
|
||||
assert recommendation.options[0].material_id == 42
|
||||
|
||||
|
||||
def test_planning_protection_picks_secondary_glazing_over_double() -> None:
|
||||
# Arrange — a conservation area blocks external work, so the external units
|
||||
# can't be replaced; an internal secondary pane is the picked Measure.
|
||||
baseline: EpcPropertyData = _with_single_glazed(build_epc(), 0, 2)
|
||||
restrictions = PlanningRestrictions(in_conservation_area=True)
|
||||
|
||||
# Act
|
||||
recommendation: Recommendation | None = recommend_glazing(
|
||||
baseline, _StubProducts(), restrictions
|
||||
)
|
||||
|
||||
# Assert — one secondary-glazing Option; each single window upgraded to the
|
||||
# pinned secondary target (gt=7, U=2.90, g unchanged at 0.85).
|
||||
assert recommendation is not None
|
||||
option = recommendation.options[0]
|
||||
assert option.measure_type == "secondary_glazing"
|
||||
assert dict(option.overlay.windows) == {
|
||||
0: WindowOverlay(glazing_type=7, u_value=2.90, solar_transmittance=0.85),
|
||||
2: WindowOverlay(glazing_type=7, u_value=2.90, solar_transmittance=0.85),
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue