mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
Records the four load-bearing design decisions from the grill-with-docs session: standalone co-selectable rec; eligibility = lodged-only (no effectiveness gate, electric-storage §A.2.2 no-op is the Optimiser's call); dedicated clearing SecondaryHeatingOverlay; flat per-dwelling cost (a lodged secondary is fixed per RdSAP, so a real decommission job, not room-scaled). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
33 lines
4.5 KiB
Markdown
33 lines
4.5 KiB
Markdown
# Secondary Heating Removal — standalone recommendation, eligibility, overlay, costing
|
||
|
||
We model "removal of secondary heating" — stripping a dwelling's lodged secondary heating system so the main system serves 100% of space heating — as a **standalone, co-selectable Recommendation** (`MeasureType.SECONDARY_HEATING_REMOVAL`), built API-inputs-first. This records the four load-bearing, non-obvious choices made designing it.
|
||
|
||
## Status
|
||
|
||
Accepted.
|
||
|
||
## Decisions
|
||
|
||
### 1. Standalone Recommendation, not an Option in the Heating & Hot Water rec
|
||
|
||
The heating expansion (ADR-0024) consolidated heating into one rec whose Options are **mutually-exclusive** (the Optimiser picks ≤1) because they are whole-system replacements you would never combine. Secondary-heating removal is different in kind: it is independent of, and freely combinable with, a tune-up or a boiler upgrade (you can remove a panel heater *and* upgrade the controls). Making it an Option would falsely force mutual exclusivity with the partial heating upgrades. As a standalone rec it composes additively like loft/cavity/glazing.
|
||
|
||
The redundancy risk with a whole-system bundle is self-resolving: an ASHP is calculator-category 4 → secondary fraction is already 0.00, so removal adds **zero marginal SAP** on top of it, and a SAP-maximising Optimiser never pays for a zero-gain measure. The two overlays touch disjoint fields, so there is no double-*SAP*-counting either.
|
||
|
||
### 2. Eligibility is physical only — offer iff a secondary is lodged; no effectiveness gate
|
||
|
||
Offered **iff `sap_heating.secondary_heating_type is not None`** (a surveyor-lodged secondary exists to remove). Per ADR-0024's principle — *eligibility encodes only physical/planning installability; the Optimiser owns the economics* — we deliberately do **not** gate out the cases where removal cannot move SAP.
|
||
|
||
The surprising case: on an **electric-storage main** (SAP codes 401–407/409/421), RdSAP §A.2.2 **forces a default secondary (693, portable electric) back** even after the lodged one is removed, so removal is a guaranteed zero-SAP no-op. That is an *effectiveness* fact, not an installability one — so eligibility still offers it, and the Optimiser de-selects it (zero gain, real cost). This is why our only example cert (001431, main 402) shows F35→F35 unchanged, matching Elmhurst exactly.
|
||
|
||
### 3. A dedicated `SecondaryHeatingOverlay` that *clears* fields
|
||
|
||
Every other Simulation Overlay obeys "a `None` field means leave the baseline unchanged" and writes **target states**. Removal is the opposite — it must set `secondary_heating_type` and `secondary_fuel_type` *to* `None`, which that convention structurally cannot express. Rather than wart the absolute-target-state `HeatingOverlay` with a remove-flag, removal gets its own minimal overlay surface (`SecondaryHeatingOverlay`, with an explicit `remove` flag) + `EpcSimulation` slot + `_fold_secondary_heating`, mirroring the one-overlay-per-measure-family pattern. It is the one overlay that sets a value to *absent*.
|
||
|
||
### 4. Flat per-dwelling decommission cost, not room-scaled
|
||
|
||
Legacy `recommendations/SecondaryHeating.py` scaled cost by a room count (`habitable − heated`). We price a **flat per-dwelling scalar (~£250, contingency 0.25)** instead. Two reasons: (a) the EPC lodges a *single* secondary system with **no heater count**, so the legacy room proxy is unfounded; (b) because RdSAP only records a secondary when a **fixed** emitter is present (portable plug-in heaters are ignored), a lodged secondary is by definition a fixed unit — its removal is a real but roughly-fixed job (one electrician visit to disconnect/isolate a hard-wired panel/convector/radiant heater + a blanking plate + localised making-good of the wall). The contingency absorbs the unknown count / hard-wire status / repaint extent. No composite `Products` machinery and no `rates.json` entry — a future data-only upgrade if a real per-unit rate sheet arrives.
|
||
|
||
## Validation
|
||
|
||
The before/after example (cert 001431, main 402) is a forced-secondary delta-0 case, and shares the boiler-fixtures' Summary-path roof-fidelity gap, so the cascade pin asserts the **field-delta** (`score(before + remove-overlay) == score(before)` at delta 0), proving the overlay clears the fields and the calculator correctly re-forces the §A.2.2 default. A **synthetic unit test** recasts 001431's main to a non-forced gas-boiler code and asserts removal yields a *positive* SAP delta (Table 11 fraction → 0), exercising the value path without a second real cert.
|