mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
fix(glazing): map single/secondary/triple glazing per RdSAP 10 Table 24
The API glazing-transmission table mapped only the double-glazing codes [1,2,3,13,14]; single (5/15), secondary (4/11/12) and triple (6/8/9/10) glazing codes returned None from _api_glazing_transmission, so the cascade silently routed them to the u_window all-None default U=2.5 instead of their RdSAP 10 Table 24 (spec p.50) value. Single glazing (U=4.8) was the worst: modelled at half its true heat loss → systematic over-rate (cert 0370-2933, 7 single-glazed windows, +17 SAP). Extended _API_GLAZING_TYPE_TO_TRANSMISSION + the gap-keyed override table with the Table 24 (U, g, frame-factor) rows for every RdSAP-21 glazing code (single 4.8/g0.85; secondary normal-E 2.9 / low-E 2.2 /g0.85; triple pre-2002 2.4/2.1/2.0 by gap, 2002-2022 2.0, all g0.68/0.72; known-data codes 7/8 alias their family default). 94 corpus certs carry an unmapped glazing code (code 5 = 79); they sat at 32% within-0.5 vs 54.9% baseline. Eval: within-0.5 54.90% -> 56.66% (net +16 certs: 22 in, 6 offsetting-error out), within-1.0 70.2 -> 71.9%, mean|err| 1.224 -> 1.203, 909 computed / 0 raises. Spec-applied uniformly per the determinism principle. 7 AAA tests, goldens + gate green, pyright net-zero (38=38). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c8f0753142
commit
a04329770d
2 changed files with 135 additions and 3 deletions
|
|
@ -2865,9 +2865,10 @@ def _api_mechanical_ventilation_kind(mechanical_ventilation: object) -> Optional
|
|||
# "Fully double glazed" with a worksheet-resolved U=2.7. Per Table 24
|
||||
# row 2 (DG pre-2002, gap 16+, PVC/wooden) the spec answer is U=2.7,
|
||||
# so GOV.UK API code 1 is a schema sibling of code 3 (both alias the
|
||||
# "DG pre-2002 / unknown install date" row). The wider SAP10.2
|
||||
# glazing-type enum (4-12, 15+) is not yet mapped — incremental
|
||||
# coverage as new fixtures surface them.
|
||||
# "DG pre-2002 / unknown install date" row). The full RdSAP-21
|
||||
# glazing-type enum (single / secondary / triple, codes 4-12 + 15) is
|
||||
# now mapped from Table 24 below — single-glazed windows previously fell
|
||||
# through to the cascade default U=2.5 instead of their spec 4.8.
|
||||
#
|
||||
# Spec source: RdSAP 10 Table 24 "Window characteristics" page 49 —
|
||||
# DG pre-2002 spec U varies by gap (6mm=3.1, 12mm=2.8, 16+=2.7); the
|
||||
|
|
@ -2889,6 +2890,34 @@ _API_GLAZING_TYPE_TO_TRANSMISSION: Dict[int, tuple[float, float, float]] = {
|
|||
# product family. Cert 0380 lodges code
|
||||
# 14 on all windows; worksheet uses U=1.4
|
||||
# = post-curtain 1.3258.)
|
||||
#
|
||||
# SINGLE / SECONDARY / TRIPLE glazing — RdSAP 10 Table 24 (spec p.50).
|
||||
# Previously unmapped (`_api_glazing_transmission` returned None) so the
|
||||
# cascade silently routed e.g. a single-glazed window to the u_window
|
||||
# all-None default 2.5 instead of its true 4.8 — over-rating single-
|
||||
# glazed dwellings (cert 0370-2933, 7 single windows, +17 SAP). Codes
|
||||
# per datatypes/epc/domain/epc_codes.csv `glazed_type` (RdSAP-Schema-21).
|
||||
4: (2.9, 0.85, 0.70), # Secondary glazing, unknown data → Table 24
|
||||
# Secondary "Normal emissivity" default (2.9).
|
||||
5: (4.8, 0.85, 0.70), # Single glazing — Table 24 "Single / Any
|
||||
# period" (PVC/wooden 4.8, g 0.85).
|
||||
6: (2.1, 0.68, 0.70), # Triple glazed, unknown install date — Table
|
||||
# 24 Triple pre-2002 12mm-gap default (2.1).
|
||||
7: (2.8, 0.76, 0.70), # Double glazed, known data — no measured U on
|
||||
# the reduced-data path → double pre-2002 /
|
||||
# unknown-date family default (2.8), as code 3.
|
||||
8: (2.1, 0.68, 0.70), # Triple glazed, known data → triple unknown-
|
||||
# date family default (2.1), as code 6.
|
||||
9: (2.0, 0.72, 0.70), # Triple glazed, 2002-2022 — Table 24 "Double
|
||||
# or triple, 2002+ (pre-2022), any gap" (2.0).
|
||||
10: (2.1, 0.68, 0.70), # Triple glazed, pre-2002 — Table 24 Triple
|
||||
# pre-2002 12mm-gap default (2.1).
|
||||
11: (2.9, 0.85, 0.70), # Secondary glazing, normal emissivity —
|
||||
# Table 24 Secondary "Normal emissivity" (2.9).
|
||||
12: (2.2, 0.85, 0.70), # Secondary glazing, low emissivity — Table 24
|
||||
# Secondary "Low emissivity" (2.2).
|
||||
15: (4.8, 0.85, 0.70), # Single glazing, known data → Single row
|
||||
# (4.8) when no measured U is lodged, as code 5.
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2908,6 +2937,22 @@ _API_GLAZING_TYPE_GAP_TO_TRANSMISSION: Dict[
|
|||
(3, 6): (3.1, 0.76, 0.70),
|
||||
(3, 12): (2.8, 0.76, 0.70),
|
||||
(3, "16+"): (2.7, 0.76, 0.70),
|
||||
# Double glazed, known data (code 7) — aliases the double pre-2002 /
|
||||
# unknown-date Table 24 row (same as codes 1/3) when no measured U.
|
||||
(7, 6): (3.1, 0.76, 0.70),
|
||||
(7, 12): (2.8, 0.76, 0.70),
|
||||
(7, "16+"): (2.7, 0.76, 0.70),
|
||||
# Triple glazed pre-2002 / unknown / known-data (codes 6/8/10) —
|
||||
# Table 24 Triple pre-2002 row varies by gap (6mm=2.4, 12mm=2.1, 16+=2.0).
|
||||
(6, 6): (2.4, 0.68, 0.70),
|
||||
(6, 12): (2.1, 0.68, 0.70),
|
||||
(6, "16+"): (2.0, 0.68, 0.70),
|
||||
(8, 6): (2.4, 0.68, 0.70),
|
||||
(8, 12): (2.1, 0.68, 0.70),
|
||||
(8, "16+"): (2.0, 0.68, 0.70),
|
||||
(10, 6): (2.4, 0.68, 0.70),
|
||||
(10, 12): (2.1, 0.68, 0.70),
|
||||
(10, "16+"): (2.0, 0.68, 0.70),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1101,3 +1101,90 @@ class TestApiRoofConstructionCode:
|
|||
|
||||
# Assert
|
||||
assert result == "Pitched, sloping ceiling"
|
||||
|
||||
|
||||
class TestApiGlazingTransmissionTable24:
|
||||
"""`_api_glazing_transmission` must resolve the SINGLE / SECONDARY /
|
||||
TRIPLE glazing RdSAP-21 enum codes to their RdSAP 10 Table 24 (spec
|
||||
page 50) (U, g, frame-factor) — not leave them unmapped (None), which
|
||||
silently routed single-glazed windows to the cascade default U=2.5
|
||||
instead of their true 4.8, over-rating single-glazed dwellings (cert
|
||||
0370-2933, 7 single-glazed windows, +17 SAP)."""
|
||||
|
||||
def test_single_glazing_code_5_is_table_24_u_4p8(self) -> None:
|
||||
# Arrange — RdSAP 21 glazing_type 5 = "single glazing"; Table 24
|
||||
# row "Single / Any period" → U 4.8 (PVC/wooden), g 0.85.
|
||||
from datatypes.epc.domain.mapper import _api_glazing_transmission # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
# Act
|
||||
result = _api_glazing_transmission(5, None)
|
||||
|
||||
# Assert
|
||||
assert result == (4.8, 0.85, 0.70)
|
||||
|
||||
def test_single_glazing_known_data_code_15_is_table_24_u_4p8(self) -> None:
|
||||
# Arrange — code 15 = "single glazing, known data"; same Table 24
|
||||
# Single row when no measured U is lodged on the reduced-data path.
|
||||
from datatypes.epc.domain.mapper import _api_glazing_transmission # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
# Act
|
||||
result = _api_glazing_transmission(15, None)
|
||||
|
||||
# Assert
|
||||
assert result == (4.8, 0.85, 0.70)
|
||||
|
||||
def test_secondary_glazing_normal_emissivity_code_11_is_u_2p9(self) -> None:
|
||||
# Arrange — code 11 = "secondary glazing, normal emissivity";
|
||||
# Table 24 Secondary "Normal emissivity" row → U 2.9, g 0.85.
|
||||
from datatypes.epc.domain.mapper import _api_glazing_transmission # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
# Act
|
||||
result = _api_glazing_transmission(11, None)
|
||||
|
||||
# Assert
|
||||
assert result == (2.9, 0.85, 0.70)
|
||||
|
||||
def test_secondary_glazing_low_emissivity_code_12_is_u_2p2(self) -> None:
|
||||
# Arrange — code 12 = "secondary glazing, low emissivity"; Table 24
|
||||
# Secondary "Low emissivity" row → U 2.2, g 0.85.
|
||||
from datatypes.epc.domain.mapper import _api_glazing_transmission # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
# Act
|
||||
result = _api_glazing_transmission(12, None)
|
||||
|
||||
# Assert
|
||||
assert result == (2.2, 0.85, 0.70)
|
||||
|
||||
def test_triple_glazing_2002_to_2022_code_9_is_u_2p0(self) -> None:
|
||||
# Arrange — code 9 = "triple glazing, installed 2002-2022"; Table 24
|
||||
# "Double or triple, 2002+ (pre-2022), any gap" → U 2.0, g 0.72.
|
||||
from datatypes.epc.domain.mapper import _api_glazing_transmission # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
# Act
|
||||
result = _api_glazing_transmission(9, None)
|
||||
|
||||
# Assert
|
||||
assert result == (2.0, 0.72, 0.70)
|
||||
|
||||
def test_triple_glazing_pre_2002_code_10_default_gap_is_u_2p1(self) -> None:
|
||||
# Arrange — code 10 = "triple glazing, pre-2002"; Table 24 Triple
|
||||
# pre-2002 12mm-gap default → U 2.1, g 0.68.
|
||||
from datatypes.epc.domain.mapper import _api_glazing_transmission # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
# Act
|
||||
result = _api_glazing_transmission(10, None)
|
||||
|
||||
# Assert
|
||||
assert result == (2.1, 0.68, 0.70)
|
||||
|
||||
def test_double_glazing_2002_plus_code_2_unchanged(self) -> None:
|
||||
# Arrange — regression guard: the already-mapped double-glazing
|
||||
# 2002+ entry (U 2.0, g 0.72) is untouched by the single/secondary/
|
||||
# triple extension.
|
||||
from datatypes.epc.domain.mapper import _api_glazing_transmission # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
# Act
|
||||
result = _api_glazing_transmission(2, None)
|
||||
|
||||
# Assert
|
||||
assert result == (2.0, 0.72, 0.70)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue