mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
test(elmhurst): party-ceiling BP window is vertical, not a rooflight 🟥
_is_elmhurst_roof_window mis-routes a vertical window (Location "External wall") to sap_roof_windows when the building part's roof is a PARTY ceiling (A "Another dwelling above" / NR "Non-residential space above"). A party ceiling has no external roof, so it cannot host a rooflight. Surfaced by simulated case 53 (cert 000565 re-keyed as a mid-floor electric- storage flat, roof "A Another dwelling above"): its External-wall window (U 2.00) routed to sap_roof_windows -> window area not deducted from the wall (wall over-counted ~7 W/K) + priced as a roof window -> our SAP 74.0 vs Elmhurst worksheet 75. Elmhurst lodges it Type "Window", Location "External wall". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6303343575
commit
23245a085c
1 changed files with 89 additions and 0 deletions
|
|
@ -0,0 +1,89 @@
|
|||
"""Mapper boundary: Elmhurst §11 window vs roof window classification.
|
||||
|
||||
`_is_elmhurst_roof_window` must NOT route a vertical window (lodged Location
|
||||
"External wall") to `sap_roof_windows` when the building part's roof is a
|
||||
PARTY ceiling — "A Another dwelling above" / "NR Non-residential space above"
|
||||
/ "S Same dwelling above". A party ceiling has no external roof structure, so
|
||||
it cannot host a rooflight (RdSAP 10 §3.7.1: a rooflight is glazing in a roof;
|
||||
party codes carry no heat-loss roof per `_ELMHURST_PARTY_ROOF_CODES`).
|
||||
|
||||
Surfaced by "simulated case 53" (cert 000565 re-keyed as a mid-floor electric-
|
||||
storage flat, roof "A Another dwelling above"): its one External-wall window
|
||||
(U 2.00) was mis-routed to `sap_roof_windows`, so the window area was never
|
||||
deducted from the wall (wall over-counted ~7 W/K) and it was priced as a roof
|
||||
window — our SAP 74.0 vs Elmhurst worksheet 75. Elmhurst's own entry lodges it
|
||||
Type "Window", Location "External wall".
|
||||
"""
|
||||
|
||||
from datatypes.epc.surveys.elmhurst_site_notes import (
|
||||
RoofDetails,
|
||||
Window as ElmhurstWindow,
|
||||
)
|
||||
from datatypes.epc.surveys.elmhurst_site_notes import ElmhurstSiteNotes
|
||||
from datatypes.epc.domain.mapper import (
|
||||
_is_elmhurst_roof_window, # pyright: ignore[reportPrivateUsage]
|
||||
)
|
||||
|
||||
|
||||
def _external_wall_window(*, glazing: str, u_value: float) -> ElmhurstWindow:
|
||||
return ElmhurstWindow(
|
||||
width_m=15.14,
|
||||
height_m=1.0,
|
||||
area_m2=15.14,
|
||||
glazing_type=glazing,
|
||||
frame_factor=0.7,
|
||||
building_part="Main",
|
||||
location="External wall",
|
||||
orientation="North",
|
||||
data_source="Manufacturer",
|
||||
u_value=u_value,
|
||||
g_value=0.72,
|
||||
draught_proofed=True,
|
||||
permanent_shutters="None",
|
||||
frame_type="PVC",
|
||||
)
|
||||
|
||||
|
||||
def _survey_with_main_roof(roof_type: str) -> ElmhurstSiteNotes:
|
||||
"""Partial `ElmhurstSiteNotes` carrying only what the roof-window
|
||||
classifier reads: `roof`, `room_in_roof`, `extensions`."""
|
||||
survey = object.__new__(ElmhurstSiteNotes)
|
||||
survey.roof = RoofDetails(
|
||||
roof_type=roof_type, insulation="", u_value_known=False
|
||||
)
|
||||
survey.room_in_roof = None
|
||||
survey.extensions = []
|
||||
return survey
|
||||
|
||||
|
||||
def test_external_wall_window_on_party_ceiling_is_vertical() -> None:
|
||||
# Arrange — mid-floor flat: Main BP roof is "Another dwelling above"
|
||||
# (party ceiling, no external roof). Window lodged on External wall.
|
||||
window = _external_wall_window(
|
||||
glazing="Double between 2002 and 2021", u_value=2.0
|
||||
)
|
||||
survey = _survey_with_main_roof("A Another dwelling above")
|
||||
|
||||
# Act / Assert — vertical, not a rooflight.
|
||||
assert _is_elmhurst_roof_window(window, survey) is False
|
||||
|
||||
|
||||
def test_external_wall_window_under_non_residential_space_is_vertical() -> None:
|
||||
window = _external_wall_window(
|
||||
glazing="Double between 2002 and 2021", u_value=2.0
|
||||
)
|
||||
survey = _survey_with_main_roof("NR Non-residential space above")
|
||||
|
||||
assert _is_elmhurst_roof_window(window, survey) is False
|
||||
|
||||
|
||||
def test_roof_of_room_location_still_classified_as_rooflight() -> None:
|
||||
# Guard: an explicit "Roof of Room" location stays a rooflight
|
||||
# (unaffected by the party-ceiling fix).
|
||||
window = _external_wall_window(
|
||||
glazing="Double between 2002 and 2021", u_value=2.0
|
||||
)
|
||||
window.location = "Roof of Room"
|
||||
survey = _survey_with_main_roof("A Another dwelling above")
|
||||
|
||||
assert _is_elmhurst_roof_window(window, survey) is True
|
||||
Loading…
Add table
Reference in a new issue