From aa6645e3f146c7e96aa981d83ea4c726ddd0f89a Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 26 May 2026 18:22:04 +0000 Subject: [PATCH] =?UTF-8?q?Slice=2097:=20API=20glazing=5Ftype=3D2=20?= =?UTF-8?q?=E2=86=92=20RdSAP=2010=20Table=2024=20(DG=202002-2021)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- datatypes/epc/domain/mapper.py | 11 ++-- .../rdsap/tests/test_golden_fixtures.py | 55 +++++++++++-------- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 69e557a6..e8c9af93 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -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 } diff --git a/domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py b/domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py index d654a776..e347b3de 100644 --- a/domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py +++ b/domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py @@ -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(