From c9f15a2e0ef59c045c273aad87e67084fbb5a8b8 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Thu, 21 May 2026 08:00:01 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20SPEC=5FCOVERAGE=20=C2=A78c=20row=20+=20?= =?UTF-8?q?slice=20progress=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds §8c as a first-class row in the Sections §§1–13 table per Q13 grilling (sub-sections are first-class — §8c, §8f). The §10 spec heading collapses into a pointer at §8c since they describe the same xlsx block. Per-§8c slice progress table mirrors §8's: line refs (100)..(108), commit shorthands, and a Remaining work list naming the three follow-up slices the first cooling-enabled cert triggers (Table 5a exclusion in cooling gains, RdSAP cooled-area defaulting, Table 10c SEER fuel/cost cascade). Co-Authored-By: Claude Opus 4.7 --- docs/sap-spec/SPEC_COVERAGE.md | 37 ++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/docs/sap-spec/SPEC_COVERAGE.md b/docs/sap-spec/SPEC_COVERAGE.md index e0a9b737..0359359b 100644 --- a/docs/sap-spec/SPEC_COVERAGE.md +++ b/docs/sap-spec/SPEC_COVERAGE.md @@ -2,7 +2,7 @@ Tracks which sections of the SAP 10.2 specification are implemented in `packages/domain/src/domain/sap/`. Per ADR-0009 the calculator is built from the spec, not reverse-engineered from cert data. This doc is the worksheet-driven roadmap for what remains. -Updated 2026-05-20 after §8 rebuild (slices `9113f30a`…`f6ab7626`). +Updated 2026-05-21 after §8c rebuild (slices `cf28eec4`…`f3797066`). The canonical SAP10.2 algorithm lives in [`2026-05-19-17-18 RdSap10Worksheet.xlsx`](../../2026-05-19-17-18%20RdSap10Worksheet.xlsx) at the repo root — each line ref `(1)..(486)` maps to a cell. The worksheet sub-modules under `packages/domain/src/domain/sap/worksheet/` implement those line refs directly; Elmhurst worksheets validate end-to-end via `tests/_elmhurst_worksheet_*.py`. @@ -19,8 +19,9 @@ The canonical SAP10.2 algorithm lives in [`2026-05-19-17-18 RdSap10Worksheet.xls | 7 | Mean internal temperature | `worksheet/mean_internal_temperature.py` | **Full** | Worksheet-driven (85)..(94) via `mean_internal_temperature_monthly`. Table 9c steps 1-9 sequential (per-zone η: (86) η_living at Ti=T_h1, (89) η_elsewhere at Ti=T_h2, (94) η_whole at Ti=(93)). Table 9b u-formula consumes weighted R for two-main case 1 (single-main is default). Wired into `calculator.py` + `cert_to_inputs` via two new `CalculatorInputs` fields. Six Elmhurst fixtures conform end-to-end to ≤5e-3 °C on all 9 line tuples + 2 scalars per month (588 assertions). Table 4e adj defaults 0 (cert-side mapping deferred — all 6 fixtures = 0); two-main case 2 (different parts heated separately) deferred. | | 8 | Off-period temperature reduction | inline in `mean_internal_temperature.py` | Full | Table 9b implemented | | 8 | Space heating requirement | `worksheet/space_heating.py` | **Full** | Worksheet-driven (95)..(99) via `space_heating_monthly_kwh`. Includes the Table 9c step 10 spec inclusion rule (Jun..Sep zeroed) on top of the < 1 kWh value clamp. (98b) Appendix H solar space heating defaulted to 0 (no Elmhurst fixture lodges a solar space heating system). Wired into `calculator.py` + `cert_to_inputs` via `CalculatorInputs.space_heating_monthly_kwh`. Six Elmhurst fixtures conform end-to-end on (95)/(97)/(98a)/(98c)/(99)/annual @ 5e-2..1e-1 kWh (looser than §6/§7's 5e-3 because LINE_84/93/94 fixture pins are 4-d.p. display-rounded and §8's 0.024·n_m·(L−ηG) amplifies that rounding). | +| 8c | Space cooling requirement | `worksheet/space_cooling.py` | **Full (no-AC zero-branch)** | Worksheet-driven (100)..(108) via `space_cooling_monthly_kwh`. Table 10a η_loss with 8-dp γ rounding + L=0 sentinel; Table 10b Q_cool with Jun-Aug inclusion mask + post-f_C × f_intermittent 1-kWh clamp per spec line 10321. Internal temp hardcoded 24 °C. Wired into `calculator.py` (`MonthlyEntry.space_cool_requirement_kwh` + `SapResult.space_cooling_kwh_per_yr`) + `cert_to_inputs` via `CalculatorInputs.space_cooling_monthly_kwh`. Six Elmhurst fixtures all `has_fixed_air_conditioning=False` → (107), (108) ≡ 0 — ALL_FIXTURES asserts (101)/(103)/(106)/(107)/(108) per fixture; synthetic positive test (γ=1 closed-form) covers the algebra. **Deferred**: RdSAP cooled-area defaulting rule + `cooling_gains_from_cert` (drops Table 5a items per spec 10280) + Table 10c SEER → cooling fuel kWh + fuel cost cascade (first non-zero-cooling cert triggers this slice). | | 9 | Energy requirements per heating system | `worksheet/space_heating.py` | Partial | Single main system only — **no Table 11 secondary heating allocation** (10% fraction on most boilers — likely big MAE) | -| 10 | Cooling | — | Not implemented | Rare in UK dwellings; defer | +| 10 | Cooling (spec heading — same content as §8c worksheet block) | `worksheet/space_cooling.py` | **Full (no-AC zero-branch)** | See §8c row above. | | 11 | FEE | — | Not implemented | Only for new-build; not required for ratings | | 12 | Total energy + fuel costs | `calculator.py` | Partial | Per-end-use cost split ✓; meter_type tariff routing ✓ (S-B15); PV cost credit ✓ (S-B19); **standing charges not included** (Table 12 note (a) says rating omits standing charge for std electricity tariff) | | 13 | SAP rating | `worksheet/rating.py` | Full | Equations 7-9 verified against SAP 10.2 §13 | @@ -199,3 +200,35 @@ Status now: 100-cert MAE 4.49, 300-cert MAE 5.45, bias near zero (±0.2). Worksh 1. **Appendix H solar space heating (98b)** — orchestrator emits `solar_space_heating_monthly_kwh = (0,)·12` always. No Elmhurst fixture exercises it. Wire when a solar-space-heating cert lands. 2. **000490 SAP-score gap to 57 (currently 60)** — needs upstream §3 (transmission HLC), §5 (internal gains), and the §4 water heating cert path tightened. Likely closes incrementally as the §9–§14 fuel/cost/rating chain is rebuilt + the legacy `predicted_hot_water_kwh` is replaced by `water_heating_from_cert`. + +## §8c — slice progress (xlsx rows 435–466) + +| Line ref | Description | Status | Commit | +|---|---|---|---| +| — | `SpaceCoolingResult` + `space_cooling_monthly_kwh` orchestrator | ✅ | `cf28eec4` | +| — | Table 10a `utilisation_factor_loss` leaf (γ=G/L, 8-dp rounding, L=0 sentinel, γ≤0 / γ=1 / γ>0∧≠1 branches) | ✅ | `cf28eec4` | +| (100)m | Heat loss rate L_m = H·(24 − T_e), W (Ti hardcoded 24 °C) | ✅ | `cf28eec4` | +| (101)m | η_loss per month, Table 10a | ✅ | `cf28eec4` | +| (102)m | Useful loss η·L, W | ✅ | `cf28eec4` | +| (103)m | Cooling gains W (Table 5a exclusions deferred — see below) | ✅ pass-through; Table 5a exclusion ⏸ deferred | `cf28eec4` | +| (104)m | Q_whole continuous = 0.024·(G − η·L)·n_m, kWh; Jun-Aug only | ✅ | `cf28eec4` | +| Σ(104) | Annual Q_whole total kWh | ✅ | `cf28eec4` | +| (105) | Cooled-area fraction f_C — hardcoded 0 in cert_to_inputs | ✅ (zero-branch); RdSAP cooled-area defaulting ⏸ deferred | `cf28eec4` | +| (106)m | Intermittency factor 0.25, Jun-Aug only | ✅ | `cf28eec4` | +| (107)m | Q_cool = (104)·(105)·(106) with negative-or-< 1 kWh clamp per spec 10321 | ✅ | `cf28eec4` | +| Σ(107) | Annual Q_cool kWh | ✅ | `cf28eec4` | +| (108) | Annual per-m² = Σ(107) / TFA | ✅ | `cf28eec4` | +| — | 6-fixture ALL_FIXTURES conformance on (101)/(103)/(106)/(107)/(108) | ✅ | `3b9fa936` | +| — | `CalculatorInputs.space_cooling_monthly_kwh` + `MonthlyEntry.space_cool_requirement_kwh` + `SapResult.space_cooling_kwh_per_yr` + cert_to_inputs wiring | ✅ | `f3797066` | +| (105) Table 10c SEER → cooling fuel kWh | ⏸ deferred | — | + +**Six Elmhurst fixtures conform end-to-end on §8c to exact equality** on (101)/(103)/(106)/(107)/(108) — no tolerance needed because every fixture has `has_fixed_air_conditioning=False` → f_C=0 → (107), (108) ≡ 0 and (101) ≡ 1.0 (γ=0 every month). (100)/(102)/(104) carry computed non-zero values per fixture and are not pinned in the ALL_FIXTURES test; the algebra is exercised by the synthetic-positive leaf/orchestrator tests in `test_space_cooling.py` (γ=1 closed-form branch with hand-computed 4.65 kWh end-to-end + γ ≤ 0 / γ=1 / γ>0∧≠1 / γ≈1 boundary / L=0 sentinel leaf tests). + +**E2e SAP-score impact:** none. All Elmhurst fixtures lodge `has_fixed_air_conditioning=False`; cooling contribution to the rating is structurally zero. The wiring is in place so that the first non-zero-cooling cert triggers the (103) Table 5a exclusion + (105) RdSAP cooled-area defaulting slice atomically. + +### Remaining §8c work + +1. **Table 5a exclusion in cooling gains (103)** — `cert_to_inputs` currently passes `monthly_total_gains_w = (0,)*12` to the orchestrator. Spec line 10280 requires cooling G to drop Table 5a items (pumps/fans, intermittent appliances) and use column (A) of Table 5 throughout. Needs a `cooling_gains_from_cert` helper mirroring `internal_gains_from_cert` + `solar_gains_from_cert`. Triggered by the first cooling-enabled cert. +2. **RdSAP cooled-area defaulting (105)** — `cert_to_inputs` currently passes `cooled_area_fraction = 0.0` always. For `has_fixed_air_conditioning=True` certs the RdSAP 10 spec gives a defaulting rule (cooled area not lodged; cert side derives from dwelling type or assumes whole-dwelling). Needs PDF lookup. Triggered by the first cooling-enabled cert. +3. **Table 10c SEER + cooling fuel kWh + fuel cost cascade** — Q_cool (107) needs to drive an electricity-fuel-kWh path (Q_cool ÷ SEER) and through Table 12 onto `SapResult.total_fuel_cost_gbp`. Currently the cooling kWh sits on `MonthlyEntry.space_cool_requirement_kwh` but doesn't propagate into fuel costs or CO2. Triggered by the first cooling-enabled cert. +4. **§8f Fabric Energy Efficiency (109) = (98a)/TFA + (108)** — only relevant for new-build compliance (FEE replaces SAP rating in some contexts). Picked up by §8f slice.