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

3.2 KiB
Raw Blame History

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.