Model/docs/baseline-downgrade-followups.md
Khalim Conn-Kowlessar 3f69c4be02 docs: full-SAP baseline-downgrade follow-ups + Case A population sweep tool
Captures the calc-facing ventilation-type gap (older RdSAP mappers drop
mechanical_ventilation_kind), the FE-side Main Fuel display, the sweep survivor
clusters, and the predicted-property display path — all separate from ADR-0037.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 21:45:44 +00:00

56 lines
3.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Follow-ups from the full-SAP baseline-downgrade work (ADR-0037)
Open items surfaced while fixing the full-SAP mapper / portfolio-796 downgrades.
Each is **separate from** the full-SAP fix (`fix/baseline-downgrades`).
## 1. Older RdSAP mappers drop `mechanical_ventilation_kind` (calc-facing)
**Severity: accuracy bug for MEV/MVHR dwellings.**
`mechanical_ventilation_kind` (the natural / MEV / MVHR ventilation **type**) is
mapped in **only `from_rdsap_schema_21_0_1`** ([mapper.py:2650], via
`_api_mechanical_ventilation_kind`). The other six API mappers —
`from_rdsap_schema_17_0/17_1/18_0/19_0/20_0_0/21_0_0` — build
`SapVentilation(sheltered_sides=…)` and **drop** the cert's `mechanical_ventilation`
field. (The full-SAP path maps it via `_sap_17_1_ventilation` — not affected.)
- For **natural-ventilation** certs (`mechanical_ventilation = 0`, e.g. UPRN
100020603823 / property 726605) it's **benign** — unmapped → `None` defaults to
natural in the §2 cascade.
- For **MEV/MVHR** certs (`mechanical_ventilation ≠ 0`) the calculator treats the
dwelling as **naturally ventilated**, getting the SAP §2 ventilation cascade
(and any heat recovery) wrong → mis-scored SAP.
The granular **counts** (fans/flues/vents) are *not* a bug: older RdSAP open-data
certs don't lodge them, and the calc correctly uses RdSAP Table-5 age defaults.
`percent_draughtproofed` is mapped (top-level) and read by the calc.
**Fix:** mirror `mechanical_ventilation_kind=_api_mechanical_ventilation_kind(schema.mechanical_ventilation)`
into the six older RdSAP mappers. **Calc-facing → validate** with the RdSAP-21.0.1
corpus (must hold 73.3% / MAE 0.774) plus an Elmhurst-anchored MEV/MVHR cert (the
corpus is natural-vent-dominated, so add a mechanical-vent `RealCertExpectation`).
Quantify blast radius: count older-RdSAP certs with `mechanical_ventilation ≠ 0`.
## 2. FE "Main Fuel: Unknown" is FE-side, not a Model mapper gap
`main_fuel_type` (the gov fuel **code**) **is** populated Model-side — UPRN
10093412452 (709791) → `1`, UPRN 100020603823 (726605) → `26` — and is persisted
(`epc_main_heating_detail.main_fuel_type`). So the panel's "Main Fuel: Unknown"
is the **front-end** either not mapping the code → fuel name or reading a field we
don't populate. Needs an FE-repo (Drizzle/Next) check, not a Model change.
## 3. Survivor clusters from the post-fix population sweep
`scripts/hyde/case_a_population_sweep.py`: the representative sample rebaselined
cleanly (0 survivors), but the worst-old-drop sample held **28 survivors**
(lodged A/B → new C/D, 1525 SAP) in tight UPRN clusters (new-build blocks),
spanning multiple schemas (16.0/16.1/17.1/18.0.0) and heating types. No single
mapper-gap signature → likely genuine SAP-2012→10.2 drops for very-high-lodged
new-builds, but **triage one in Elmhurst** to confirm genuine vs a residual calc
issue before trusting the cohort.
## 4. Predicted-property display path (e.g. property 721167)
721167 has **no lodged EPC** (predicted). Its Heating-Control / Main-Fuel /
Ventilation Unknowns come from the prediction + landlord-override **overlay** not
populating the display fields — a separate path from the lodged-cert mappers.