Slice 97: API glazing_type=2 → RdSAP 10 Table 24 (DG 2002-2021)

Cert 0330 API path was at Δ +1.68 SAP after Slice 96 because all 11
windows (`sap_windows[*].glazing_type = 2`) fell through
`_API_GLAZING_TYPE_TO_TRANSMISSION` (which only covered codes 3 +
13) to the cascade's `u_window` default (~U=2.5). The cert's actual
glazing is "Double, England/Wales 2002 or later (before 2022)" per
RdSAP 10 Table 24 page 79 → U=2.0, g=0.72 (PVC/wooden frame).

RdSAP 10 Table 24 verbatim:
  Glazing       Installed                       Gap       U-value   g
  Double or     England/Wales: 2002 or later                2.0    0.72
  triple        Scotland: 2003 or later         any
  glazed        N. Ireland: 2006 or later

The cascade's curtain-transform path (`U_eff = 1/(1/U + 0.04)`)
takes U_raw=2.0 to U_eff=1.8519 — matching the worksheet's per-
window (27) U value column to 4 d.p. across all 11 windows.

Effect on cert 0330 API path:
- Windows HLC 36.4545 → 29.7407 (= worksheet exact)
- (37) total fabric heat loss 244.48 → 237.77 (≈ worksheet 237.75)
- SAP Δ +1.68 → +2.12 (windows fix unmasks the standalone HW gap,
  which the next slice closes)

Re-pinned residuals (5 affected golden certs):
- 0240: PE +17.85 → +15.69; CO2 +1.01 → +0.90; SAP unchanged at -15
- 0300: PE +7.76 → +7.52; CO2 -0.25 → -0.27; SAP unchanged at +0
- 0390-2954: PE -26.46 → -28.68; CO2 -2.56 → -2.76; SAP unchanged
- 7536: SAP +0 → +1; PE -3.45 → -6.51; CO2 -0.09 → -0.17
- 8135: PE -2.41 → -5.31; CO2 -0.02 → -0.07; SAP unchanged at +0

The PE/CO2 widening on some certs (vs lodged GOV.UK values) reflects
the cascade now using the spec table U=2.0 where those certs may have
lodged a higher project-specific U — the spec-table is the right
floor for the API path; per-window measured U overrides would belong
on the cert's window_transmission_details.u_value field, which the
API JSON doesn't surface uniformly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-05-26 18:22:04 +00:00
parent da5e7196c4
commit aa6645e3f1
2 changed files with 40 additions and 26 deletions

View file

@ -2192,12 +2192,15 @@ def _api_sheltered_sides(built_form: object) -> Optional[int]:
# Ext1 lodges glazing_type=13 → manufacturer DG post-2022 Argon
# U=1.4 / g=0.72, vs cascade default U=2.5).
#
# Codes observed across the 10 golden fixtures: 3 (Main DG pre-2002)
# and 13 (Ext1 post-2022 Argon). The wider SAP10.2 glazing-type enum
# (4-12, 14+) is not yet mapped — incremental coverage as new
# fixtures surface them.
# Codes observed across the 10 golden fixtures: 2 (DG England/Wales
# 2002 or later, pre-2022), 3 (Main DG pre-2002), 13 (Ext1 post-2022
# Argon). The wider SAP10.2 glazing-type enum (4-12, 14+) is not yet
# mapped — incremental coverage as new fixtures surface them.
#
# Spec source: RdSAP 10 Table 24 "Window characteristics" page 79.
_API_GLAZING_TYPE_TO_TRANSMISSION: Dict[int, tuple[float, float, float]] = {
# (u_value, solar_transmittance/g_⊥, frame_factor)
2: (2.0, 0.72, 0.70), # Double glazed, England/Wales 2002+ (pre-2022)
3: (2.8, 0.76, 0.70), # Double glazed, pre-2002
13: (1.4, 0.72, 0.70), # Double glazed, Argon-filled post-2022
}

View file

@ -75,8 +75,8 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = (
cert_number="0240-0200-5706-2365-8010",
actual_sap=73,
expected_sap_resid=-15,
expected_pe_resid_kwh_per_m2=+17.8450,
expected_co2_resid_tonnes_per_yr=+1.0097,
expected_pe_resid_kwh_per_m2=+15.6873,
expected_co2_resid_tonnes_per_yr=+0.8999,
notes=(
"Detached house, TFA 118, age J, oil boiler PCDB-listed + PV + "
"RR on BP[0]. Mapper DOES extract sap_room_in_roof.room_in_roof_"
@ -85,22 +85,20 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = (
"handover claim of 'gable_wall_lengths not extracted' is stale. "
"Subsystem diff against the cascade: walls 22.95 / roof 76.93 / "
"floor 29.43 / windows 41.55 / doors 11.10 / bridging 39.64 "
"(total HLC 221.6 W/K). Biggest leverage is windows: 11 windows "
"× 18.28 m² × U_default≈2.27 because cert lodges glazing_type=2 "
"and Slice 93's _API_GLAZING_TYPE_TO_TRANSMISSION only covers "
"codes 3 and 13. Surfacing code 2 → measurable U≈1.8-2.0 would "
"close several W/K. Other candidates: BP[0] non-RR ceiling lodges "
"'Pitched, 400+ mm loft insulation' — verify cascade U; possibly "
"RR description-implied insulation nuance (spec basis unclear "
"for RR — unlike regular roofs which have the §5.11.4 50mm rule)."
"(total HLC 221.6 W/K). Slice 97 added glazing_type=2 "
"(RdSAP 10 Table 24 DG England/Wales 2002+, U=2.0, g=0.72) — "
"PE residual +17.85 → +15.69 and CO2 +1.01 → +0.90. SAP "
"residual still -15: the residual sits in subsystems other than "
"the windows lookup (PV cascade, RR description-implied "
"insulation nuance, possibly oil-tariff secondary)."
),
),
_GoldenExpectation(
cert_number="0300-2747-7640-2526-2135",
actual_sap=78,
expected_sap_resid=+0,
expected_pe_resid_kwh_per_m2=+7.7553,
expected_co2_resid_tonnes_per_yr=-0.2526,
expected_pe_resid_kwh_per_m2=+7.5229,
expected_co2_resid_tonnes_per_yr=-0.2726,
notes=(
"Large semi-detached, TFA 526, age D, gas boiler PCDB-listed "
"(no Table 4b code). Cert lodges open_flues_count=1 + "
@ -119,9 +117,15 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = (
cert_number="0390-2954-3640-2196-4175",
actual_sap=60,
expected_sap_resid=-6,
expected_pe_resid_kwh_per_m2=-26.4584,
expected_co2_resid_tonnes_per_yr=-2.5618,
notes="Large detached, TFA 360, age F, oil PCDB-listed. Cert lodges has_draught_lobby=true.",
expected_pe_resid_kwh_per_m2=-28.6783,
expected_co2_resid_tonnes_per_yr=-2.7640,
notes=(
"Large detached, TFA 360, age F, oil PCDB-listed. Cert lodges "
"has_draught_lobby=true. Slice 97 added glazing_type=2 — "
"windows now drop to spec U=2.0, widening PE -26.46 → -28.68 "
"and CO2 -2.56 → -2.76 (the cert's lodged U for this glazing "
"type appears to be higher than the spec's table-24 default)."
),
),
_GoldenExpectation(
cert_number="6035-7729-2309-0879-2296",
@ -140,28 +144,35 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = (
_GoldenExpectation(
cert_number="7536-3827-0600-0600-0276",
actual_sap=68,
expected_sap_resid=+0,
expected_pe_resid_kwh_per_m2=-3.4482,
expected_co2_resid_tonnes_per_yr=-0.0907,
expected_sap_resid=+1,
expected_pe_resid_kwh_per_m2=-6.5135,
expected_co2_resid_tonnes_per_yr=-0.1724,
notes=(
"Detached + 2 extensions, TFA 152. Multi-age bps (Main=D, "
"Ext1=L, Ext2=F). Slice 59 (per-bp window apportionment) and "
"Slice 60 (dwelling-wide thermal bridging y from primary bp's "
"age band, not per-bp) jointly tightened: SAP +4 → +3, PE "
"-27.17 → -22.53, CO2 -0.72 → -0.60."
"-27.17 → -22.53, CO2 -0.72 → -0.60. Slice 97 added "
"glazing_type=2 (Table 24 spec U=2.0): SAP 0 → +1, PE/CO2 "
"widened. The cert's actual lodged U for glazing_type=2 "
"appears higher than the spec's table default — multi-age "
"geometry probably surfaces a per-bp U-value the spec table "
"doesn't capture exactly."
),
),
_GoldenExpectation(
cert_number="8135-1728-8500-0511-3296",
actual_sap=72,
expected_sap_resid=+0,
expected_pe_resid_kwh_per_m2=-2.4072,
expected_co2_resid_tonnes_per_yr=-0.0195,
expected_pe_resid_kwh_per_m2=-5.3103,
expected_co2_resid_tonnes_per_yr=-0.0744,
notes=(
"Semi-detached, TFA 102, age C, gas PCDB-listed. Cert lodges "
"blocked_chimneys_count=1. Slice 59 per-bp window apportionment "
"tightens PE -16.98 → -16.51 and CO2 -0.30 → -0.29; SAP "
"residual unchanged at +1."
"residual unchanged at +1. Slice 97 added glazing_type=2 — "
"SAP residual unchanged (cert rounds to 72 either way); PE "
"-2.41 → -5.31 and CO2 -0.02 → -0.07."
),
),
_GoldenExpectation(