From d8cdee4e5301ebd7b654945138bc7f52070f21c1 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Sat, 30 May 2026 23:51:47 +0000 Subject: [PATCH] Slice S0380.125: map Elmhurst Summary "18 Hour" meter_type to EIGHTEEN_HOUR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Elmhurst Summary §14.2 Meters section lodges the electricity meter type as the bare RdSAP enum form "18 Hour", but `_METER_STR_TO_INT` only carried the legacy "off-peak 18 hour" alias. All 41 P960-format heating-system fixtures at `sap worksheets/heating systems examples/` lodge meter_type "18 Hour", so `cert_to_inputs` strict-raised on every one of them before this slice. Per RdSAP 10 Specification §17 page 85 (Electricity meter row 10-2): > "Electricity meter: Dual/single/10-hour/18-hour/24-hour/unknown" Per RdSAP 10 §12 page 62: > "if the meter is dual 18-hour/24-hour it is 18-hour/24-hour tariff" So the bare "18 Hour" lodging routes directly to enum 5 (Off-peak 18 hour) → `Tariff.EIGHTEEN_HOUR`, bypassing the §12 Rules 1-4 dispatch (which only fires for Dual meters that aren't 18-hour or 24-hour). After this slice the heating-system corpus probe (`/tmp/probe_*.py` across 41 variants of the same property × different heating systems) shifts from "32 raises + 7 mapper gaps + 2 emitter gaps" to "32 cascade-OK + 7 community-heating + 2 underfloor-emitter + 1 cylinder-size 'No Access'". The 32 newly-OK variants surface a positive ΔSAP cluster (cascade SAP_c > worksheet SAP_c by +0.87..+30 across boiler types) — that residual layer is queued for the next slice. Extended handover suite at HEAD post-slice: **829 pass, 0 fail** (baseline 775 + test_table_12a.py's 54 incl. the new "18 Hour" entry). Co-Authored-By: Claude Opus 4.7 --- domain/sap10_calculator/tables/table_12a.py | 5 +++++ domain/sap10_calculator/tests/test_table_12a.py | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/domain/sap10_calculator/tables/table_12a.py b/domain/sap10_calculator/tables/table_12a.py index e4a07f1b..f98e1c27 100644 --- a/domain/sap10_calculator/tables/table_12a.py +++ b/domain/sap10_calculator/tables/table_12a.py @@ -83,6 +83,11 @@ _METER_STR_TO_INT: Final[dict[str, int]] = { "dual": 1, "dual (24 hour)": 4, "off-peak 18 hour": 5, + # RdSAP 10 §17 page 85 row 10-2 lodging form: "18-hour" (Elmhurst + # Summary §14.2 surfaces this as the bare "18 Hour"). Per §12 + # page 62: "if the meter is dual 18-hour/24-hour it is 18-hour/ + # 24-hour tariff" → enum 5 (EIGHTEEN_HOUR). + "18 hour": 5, "unknown": 3, "": 3, } diff --git a/domain/sap10_calculator/tests/test_table_12a.py b/domain/sap10_calculator/tests/test_table_12a.py index 3135881e..2b294ca2 100644 --- a/domain/sap10_calculator/tests/test_table_12a.py +++ b/domain/sap10_calculator/tests/test_table_12a.py @@ -51,6 +51,13 @@ def test_tariff_enum_has_five_members() -> None: ("Dual", Tariff.SEVEN_HOUR), ("Dual (24 hour)", Tariff.TWENTY_FOUR_HOUR), ("Off-peak 18 hour", Tariff.EIGHTEEN_HOUR), + # RdSAP 10 §17 page 85 (Electricity meter row 10-2): + # "Dual/single/10-hour/18-hour/24-hour/unknown". The Elmhurst + # Summary §14.2 lodges the bare form "18 Hour" (not the + # "Off-peak 18 hour" alias above). Per §12 page 62: "if the + # meter is dual 18-hour/24-hour it is 18-hour/24-hour tariff", + # so the bare lodging routes directly to EIGHTEEN_HOUR. + ("18 Hour", Tariff.EIGHTEEN_HOUR), # Per Q11b: "Unknown" maps to STANDARD (no off-peak heuristic). ("Unknown", Tariff.STANDARD), ("", Tariff.STANDARD),