From cf244762d5d7c9de1a9cddbbf9427750092133bc Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 20 May 2026 14:14:08 +0000 Subject: [PATCH] =?UTF-8?q?Elmhurst=20000474:=20=C2=A73=20LINE=5F33=20+=20?= =?UTF-8?q?LINE=5F37=20close=20exactly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the second non-RR Elmhurst worksheet (mid-terrace, 3 parts). LINE_33 (209.1084) and LINE_37 (232.1169) reproduce to 0.1 W/K. Cert inputs lodged on the fixture: - Ext1 SapFloorDimension(is_exposed_floor=True) — Table 20 route - Ext2 ground floor (tiny 1.35 m², P=3.30) stays on Table 19 fn 1 suspended-timber default for age B (cascade → U≈1.25, worksheet 1.25) - door_count=2 → 3.70 m² total door area - WINDOW_TOTAL_AREA_M2=11.72 split across two glazing types (Type 1: 6.22 m² post-2002 raw U=2.0, Type 2: 5.50 m² pre-2002 raw U=2.8). Area-weighted aggregate raw U=2.37 reproduces the worksheet's 25.37 W/K through the curtain-resistance transform. Non-RR §3 scope closed: - LINE_31 exact (existing test) - LINE_33 exact ← this slice + the 000490 slice - LINE_36 exact (existing test, y × LINE_31) - LINE_37 exact ← this slice + the 000490 slice Co-Authored-By: Claude Opus 4.7 --- .../tests/_elmhurst_worksheet_000474.py | 18 ++++++++++++ .../worksheet/tests/test_heat_transmission.py | 28 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000474.py b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000474.py index 19a2c65e..a605fda3 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000474.py +++ b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000474.py @@ -64,11 +64,14 @@ def build_epc() -> EpcPropertyData: wall_thickness_measured=False, party_wall_construction=0, sap_floor_dimensions=[ + # Ext1 hangs off the main from the first storey upward — its + # lowest dimension is an exposed timber floor (Table 20). SapFloorDimension( room_height_m=2.81, # lowest of ext, internal total_floor_area_m2=15.04, party_wall_length_m=3.56, heat_loss_perimeter_m=8.46, floor=0, + is_exposed_floor=True, ), SapFloorDimension( room_height_m=3.13, # = 2.88 + 0.25 @@ -163,3 +166,18 @@ LINE_31_TOTAL_EXTERNAL_AREA_M2: float = 153.3900 LINE_33_FABRIC_HEAT_LOSS_W_PER_K: float = 209.1084 LINE_36_THERMAL_BRIDGING_W_PER_K: float = 23.0085 LINE_37_TOTAL_FABRIC_HEAT_LOSS_W_PER_K: float = 232.1169 + +# §3 windows + doors — values from the Elmhurst worksheet §3 table. +# Two window types share the dwelling: +# Type 1 (post-2002 double-glazed, PVC, g=0.72): 6.22 m² at raw U=2.0, +# U_eff=1.8519 +# Type 2 (pre-2002 double-glazed, PVC, g=0.76): 5.50 m² at raw U=2.8, +# U_eff=2.5180 +# Worksheet windows_w_per_k = 25.3675. Aggregating to a single area- +# weighted raw U with the curtain-resistance transform applied reproduces +# the total to better than 0.05 W/K: +# U_eff_target = 25.3675 / 11.72 = 2.1645 +# raw U = 1/(1/2.1645 - 0.04) ≈ 2.37 +WINDOW_TOTAL_AREA_M2: float = 11.72 +WINDOW_AVG_RAW_U_VALUE: float = 2.37 +DOOR_COUNT: int = 2 # cascade default 1.85 m²/door → 3.70 m² matches worksheet diff --git a/packages/domain/src/domain/sap/worksheet/tests/test_heat_transmission.py b/packages/domain/src/domain/sap/worksheet/tests/test_heat_transmission.py index ea4e31df..c2e48d72 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/test_heat_transmission.py +++ b/packages/domain/src/domain/sap/worksheet/tests/test_heat_transmission.py @@ -1179,6 +1179,34 @@ def test_section_3_non_rr_line_31_and_36_match_elmhurst_worksheet( ) +def test_section_3_line_33_and_line_37_match_elmhurst_worksheet_000474() -> None: + """Full §3 fabric heat loss for Elmhurst U985-0001-000474. Mid-terrace + with 3 building parts (Main + 2 extensions). Ext1 is the exposed-timber + upper floor (same shape as 000490 Ext1). Two window types share an + area-weighted raw U-value when aggregated (post-2002 raw 2.0 mixed + with pre-2002 raw 2.8); reproducing the worksheet's per-type sum of + 25.37 W/K to 0.1 W/K is sufficient given the curtain-resistance + transform is non-linear.""" + # Arrange + epc = _w000474.build_epc() + + # Act + result = heat_transmission_from_cert( + epc, + window_total_area_m2=_w000474.WINDOW_TOTAL_AREA_M2, + window_avg_u_value=_w000474.WINDOW_AVG_RAW_U_VALUE, + door_count=_w000474.DOOR_COUNT, + ) + + # Assert + assert result.fabric_heat_loss_w_per_k == pytest.approx( + _w000474.LINE_33_FABRIC_HEAT_LOSS_W_PER_K, abs=0.1 + ) + assert result.total_w_per_k == pytest.approx( + _w000474.LINE_37_TOTAL_FABRIC_HEAT_LOSS_W_PER_K, abs=0.1 + ) + + def test_section_3_line_33_and_line_37_match_elmhurst_worksheet_000490() -> None: """Full §3 fabric heat loss for Elmhurst U985-0001-000490. Once the suspended/exposed floor routes are wired and the cert lodges the