From f863598d39a919a33634ccc832b5b6846fced790 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 25 May 2026 18:15:57 +0000 Subject: [PATCH] Slice 85: bulk-update cohort 000516 hand-built for Cat A diff parity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes 23 of 24 mapper-vs-hand-built load-bearing divergences by populating fields the Elmhurst mapper extracts from Summary_000516. pdf but the original hand-built left at their `make_minimal_sap10_ epc` / dataclass-default values. Every change is cascade-equivalent — all 11 `_FIXTURE_PINS["000516"]` SapResult pins remain GREEN against worksheet `SAP value 62.7937`. 000516-specific deltas: - `wall_thickness_measured=True` on Main (Summary lodges 400 mm). - `floor_type="Above unheated space"` (exposed timber floor, not Ground floor) — matches the cert's `is_exposed_floor=True` for the lowest Main floor. - `roof_insulation_location="None"` — the Summary lodges the literal string "None" for an uninsulated roof; mapper surfaces it verbatim. Standard Cat A additions (per Slice 72/75/78/82 pattern): floor descriptive fields, 6 ventilation zero counts, draught_lobby=True, pressure_test="Not available", top-level descriptive strings + booleans, `number_of_storeys=3` (Main ground + first + RIR), shower_outlets="Non-electric shower", central_heating_pump_age_str="Unknown". Diff count: 24 → **1**. Remaining diff is `sap_windows: LEN 5 vs 2` — closes via Slice 86. Pyright net-zero on the touched fixture. Co-Authored-By: Claude Opus 4.7 --- .../tests/_elmhurst_worksheet_000516.py | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000516.py b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000516.py index e4e32447..28a2247b 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000516.py +++ b/packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_000516.py @@ -33,6 +33,8 @@ from datatypes.epc.domain.epc_property_data import ( SapRoomInRoofSurface, SapVentilation, SapWindow, + ShowerOutlet, + ShowerOutlets, ) from domain.ml.tests._fixtures import ( make_main_heating_detail, @@ -55,13 +57,23 @@ _WC_SOLID_BRICK = 3 # party walls — RdSAP10 maps to U=0.0 (solid masonry) def build_epc() -> EpcPropertyData: - """EpcPropertyData mirroring the Elmhurst 000516 inputs.""" + """EpcPropertyData mirroring the Elmhurst 000516 inputs. + + Field-level parity with `from_elmhurst_site_notes(Summary_000516. + pdf)` for the load-bearing field allow-list — cascade-equivalent + encoding-only fields (descriptive floor/roof strings, ventilation + zero counts) are populated explicitly to eliminate noise without + altering the SAP cascade output (the 11 1e-4 pins in + `test_e2e_elmhurst_sap_score.py` remain GREEN against the worksheet + PDF's `SAP value 62.7937`). + """ main = SapBuildingPart( identifier=BuildingPartIdentifier.MAIN, construction_age_band="A", wall_construction=_WC_CAVITY, wall_insulation_type=4, - wall_thickness_measured=False, + # Summary §7 lodges Wall Thickness 400 mm explicitly; matches mapper. + wall_thickness_measured=True, party_wall_construction=_WC_SOLID_BRICK, # Solid masonry → U=0.0 sap_floor_dimensions=[ SapFloorDimension( @@ -118,8 +130,18 @@ def build_epc() -> EpcPropertyData: # u_roof cascade picks the Table 16 "none" row. roof_insulation_thickness=0, wall_thickness_mm=400, + # Mapper-extracted descriptive fields (cascade reads int codes + # on SapFloorDimension; these carry the lodged Summary text). + # `roof_insulation_location="None"` is literal — the Summary + # lodges "None" for an uninsulated roof, which the mapper + # surfaces as a string field. + floor_type="Above unheated space", + floor_construction_type="Suspended timber", + floor_insulation_type_str="As built", + floor_u_value_known=False, + roof_insulation_location="None", ) - return make_minimal_sap10_epc( + epc = make_minimal_sap10_epc( total_floor_area_m2=90.54, country_code="ENG", postcode="BD4 7JR", @@ -152,11 +174,23 @@ def build_epc() -> EpcPropertyData: percent_draughtproofed=75, low_energy_fixed_lighting_bulbs_count=SECTION_5_BULB_COUNT_LEL, sap_windows=list(SECTION_6_VERTICAL_WINDOWS), + blocked_chimneys_count=0, + dwelling_type="Mid-Terrace house", + built_form="Mid-Terrace", + property_type="House", sap_ventilation=SapVentilation( extract_fans_count=2, sheltered_sides=2, has_suspended_timber_floor=False, has_draught_lobby=False, + open_flues_count=0, + closed_flues_count=0, + boiler_flues_count=0, + other_flues_count=0, + passive_vents_count=0, + flueless_gas_fires_count=0, + draught_lobby=True, + pressure_test="Not available", ), sap_heating=make_sap_heating( # 000516 line 82: PCDF Index 18118 — Vaillant ecoTEC sustain 24 @@ -173,6 +207,15 @@ def build_epc() -> EpcPropertyData: number_baths=1, # 000516 line 117: Total number of baths = 1 ), ) + # Top-level cert-lodgement booleans the mapper surfaces. + epc.has_conservatory = False + epc.any_unheated_rooms = False + epc.number_of_storeys = 3 + epc.sap_heating.shower_outlets = ShowerOutlets( + shower_outlet=ShowerOutlet(shower_outlet_type="Non-electric shower"), + ) + epc.sap_heating.main_heating_details[0].central_heating_pump_age_str = "Unknown" + return epc # ============================================================================