Updates SPEC_COVERAGE.md with the 9 §4 slices landed since the last doc sweep, and lays out the remaining work in priority order: 1. §4 orchestrator (water_heating_from_cert) 2. Wire calculator.py to the new worksheet module 3. End-to-end SAP score validation against Elmhurst worksheets 4. Cylinder + solar + renewables branches (population coverage) 5. PCDB-backed Table 3b/3c combi loss (000474 sits here) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
8.8 KiB
SAP 10.2 / RdSAP 10 Coverage Map
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 §3 close (slices 344a9c9d…cf244762).
The canonical SAP10.2 algorithm lives in 2026-05-19-17-18 RdSap10Worksheet.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.
Sections §§1–13 (the SAP worksheet)
| § | xlsx rows | Topic | Module | Status | Notes / gaps |
|---|---|---|---|---|---|
| 1 | 1–120 (approx) | Dimensions | worksheet/dimensions.py |
Full | Porches, conservatories, RIR deferred per ADR-0009 |
| 2 | 121–206 (approx) | Ventilation | worksheet/ventilation.py |
Partial | No mechanical ventilation (MVHR/MEV), no wind-shelter factor, no pressure-test override (worksheet lines 17-18), no AP4 override (worksheet line 19) |
| 3 | 121–207 | Heat transmission | worksheet/heat_transmission.py |
Full (non-RR) | LINE_31/33/36/37 exact for both non-RR Elmhurst fixtures (000474, 000490). Suspended-timber + Table 20 exposed-floor routes wired. RR sub-areas (gable/slope/stud-wall) deferred until SapRoomInRoof carries them. Global y-factor (Table R2 per-junction deferred). |
| 4 | 207–304 | Hot water + Appendix J | worksheet/water_heating.py (new) + legacy domain.ml.demand.predicted_hot_water_kwh (calculator still calls this) |
Happy-path done — see slice progress below. Worksheet-driven module lands line refs (42)..(65) for the combi-gas-boiler population (~70% of corpus). Cylinder + solar + renewables branches deferred. Calculator.py not yet wired to new module — next step. | |
| 5 | Internal gains + Appendix L | worksheet/internal_gains.py |
Full | Default occupancy + Appendix L lighting fallback | |
| 6 | Solar gains + Tables 6b/6c/6d + Appendix U | worksheet/solar_gains.py |
Partial | Per-orientation/pitch ✓; Tables 6b/6c lookups ✓; Z (overshading) hardcoded to 0.77 average; roof-lights treated as vertical | |
| 7 | Mean internal temperature | worksheet/mean_internal_temperature.py |
Partial | Living area fraction from Table 27 ✓ (per S-A7b); control_type from main_heating_control code ✓ (S-B5); control_temperature_adjustment_c always 0 | |
| 8 | Off-period temperature reduction | inline in mean_internal_temperature.py |
Full | Table 9b implemented | |
| 9 | Space heating | 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 | |
| 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 | |
| 14 | CO2 + primary energy | calculator.py SapResult.co2_kg_per_yr |
Partial | Single CO2 factor on main fuel; no per-end-use CO2 mixing; no primary energy calculation | |
| 15 | Building regs | n/a | n/a | Not relevant to ratings |
Appendices
| Appendix | Topic | Status | Notes |
|---|---|---|---|
| A | Main + secondary heating identification | Not implemented | First main only; Table 11 secondary fraction missing — high-value gap |
| B | Gas/oil boilers + boiler interlock | Partial | Via Table 4b in domain.ml.sap_efficiencies.seasonal_efficiency |
| C | Community heating | Not implemented | Heat network certs handled by Table 4a codes 301-304 (efficiency only) |
| D | PCDB | Stubbed | Per ADR-0009 grill: NoOpPcdbLookup returning None; Session C bundles a CSV PCDB extract |
| E | Heat pumps | Partial | Category fallback to 2.30 for cat=4; no MCS installation factor (×1.39 GSHP); no flow temp adjustment |
| F | Electric CPSU | Not implemented | Rare |
| G | FGHRS / WWHRS / PV-diverter | Not implemented | Rare |
| H | Solar water heating | Partial | Boolean has_solar_water_heating reduces HW by 250 kWh; no actual collector calc |
| J | Hot water demand | Partial | See §4 row above |
| K | Thermal bridging | Partial | Using global y per age band (per ADR-0009 grill: per-junction Table R2 deferred) |
| L | Lighting | Full | Existing-dwelling fallback ✓ |
| M | PV / wind / hydro generation | Partial | PV ✓ (S-B19); wind / hydro / micro-CHP not implemented |
| N | Micro-CHP | Not implemented | Rare |
| P | Electric storage heaters detail | Partial | Identified via codes 401-409; Table 12a high-rate fractions not exact (we use 100% off-peak per cert-calibration heuristic) |
| Q | Special features | Not implemented | One-off energy-use additions; rare |
| R | Reference dwelling | Not implemented | Only needed for compliance, not ratings |
| S | RdSAP procedures | Separate PDF (114pp) | Cert→inputs mapper in domain.sap.rdsap.cert_to_inputs ports key rules; many sections of RdSAP 10 not yet read |
| T | Improvement measures | Not relevant | Recommendation engine, not rating |
| U | Climate data | Full | Tables U1/U2/U3 + solar declination + Table U5 k1-k9 |
Prioritised gap list (by likely MAE impact)
- Table 11 Secondary heating allocation — most boiler-main certs allocate 10% of space heating to a secondary system (often a less-efficient room heater on a different fuel). We model 0%. Likely +1-2 SAP-point bias on affected certs.
- Wind-shelter factor on infiltration (§2 worksheet lines 19-21) — multiplies infiltration by
1 - 0.075 × sheltered_sides. We have no shelter input; assume 2 sheltered sides default. Net effect on infiltration ACH probably ~10%. - Table 12a high-rate fraction for off-peak dwellings — we currently bill 100% of E7 space heating at the low rate. Real spec says e.g. heat pumps on 7h tariff at 80% high-rate. Affects ~5% of certs.
- Cylinder-loss factor cascade — currently uses simplified buckets in
domain.ml.demand._STORAGE_LOSS_FACTOR. Spec has more precise interpolation rules from cylinder volume + insulation thickness. - Standing charges in cost — Table 12 note (a) gives the rule for when standing charges are included (energy use vs rating). May affect bias.
- Per-junction thermal bridging (Table R2) — only relevant when assessor lodged junction-count data, otherwise global y is the spec answer for RdSAP-driven assessments.
Status now: 100-cert MAE 4.49, 300-cert MAE 5.45, bias near zero (±0.2). Worksheet-driven phase begins with Secondary heating Table 11 as the next slice.
§4 — slice progress (xlsx rows 207–304)
| Line ref | Description | Status | Commit |
|---|---|---|---|
| (42) | Assumed occupancy N from Appendix J | ✅ | aff678e8 |
| (42a)m | Mixer showers monthly | ✅ | 1dcbdb28 |
| (42b)m | Baths monthly | ✅ | dad7fbf3 |
| (42c)m | Other uses monthly | ✅ | 5cc68ab3 |
| (43) | Annual avg | ✅ | 702b1c6c |
| (44)m | Daily monthly total | ✅ | 702b1c6c |
| (45)m | Energy content | ✅ | a3c687f1 |
| (46)m | Distribution loss | ✅ | a3c687f1 |
| (47)–(56) | Storage / HIU loss | Combi zero-branch only; cylinder paths deferred | — |
| (57)m | Dedicated solar storage | Combi zero-branch only; solar HW deferred | — |
| (59)m | Primary loss | Combi zero-branch only; boiler+cylinder deferred | — |
| (61)m | Combi loss | Table 3a row "time-clock keep-hot" only | bfba610b |
| (62)m | Total demand | ✅ | bfba610b |
| (63a-d) | WWHRS/PV/Solar/FGHRS reductions | Zero-only — non-zero paths deferred | feef8198 |
| (64)m | Output from water heater | ✅ (with max-clamp) | feef8198 |
| (64a)m | Electric shower energy | Zero-only — non-zero path deferred | — |
| (65)m | Heat gains | ✅ | 43da3ea0 |
Happy path closes both non-RR Elmhurst fixtures to <1e-3 kWh end-to-end on lines (42)..(65) (000474's PCDB-backed Table 3b combi loss is the one hand-lodged value).
Remaining §4 work
- Orchestrator
water_heating_from_cert(epc, ...)that wires the leaf functions, mirroringheat_transmission_from_certfor §3. - Integrate with
calculator.py— replace the legacydomain.ml.demand.predicted_hot_water_kwhcall. - End-to-end SAP score validation against the worksheets — the real e2e test the user has been asking for.
- Cylinder + solar + renewables paths for full population coverage (cylinders, FGHRS, WWHRS, PV-diverter).
- PCDB-backed Table 3b/3c combi loss for tested boilers (000474 sits here).