mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-27 23:35:01 +00:00
Document oil and solid-fuel boiler archetypes (ADR-0067)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6f8724aa04
commit
e0478cfa5d
1 changed files with 130 additions and 0 deletions
|
|
@ -0,0 +1,130 @@
|
|||
# Oil and solid-fuel boiler archetypes, with the fuel resolved at resolution not in the classifier
|
||||
|
||||
## Status
|
||||
|
||||
accepted
|
||||
|
||||
## Context
|
||||
|
||||
A portfolio-796 re-audit found ~43 properties whose `main_heating` override is a
|
||||
**gas** boiler archetype (`Gas boiler, regular/combi`) sitting on a **non-gas**
|
||||
`main_fuel` (oil 24+3, dual-fuel 7, house coal 5, wood logs 4). Same root cause as
|
||||
the electric-boiler cohort (ADR-0052) one fuel over: the landlord's heating
|
||||
description is a **fuel-agnostic** "Boiler … Regular/Combi Boiler", and with no
|
||||
oil/solid wet-boiler member in `MainHeatingSystemType` the LLM funnelled them all
|
||||
onto the gas archetype. LPG-on-gas is deliberately excluded — RdSAP models LPG on
|
||||
the gas archetype, so those are correct.
|
||||
|
||||
The app side is ready (`assessment-model#455` adds the members to
|
||||
`MainHeatingSystemValues`; migration 0279), inert until this repo lands the
|
||||
counterpart (lock-step, ADR-0002).
|
||||
|
||||
The issue framed the fix as "make the classifier resolve the boiler description
|
||||
**using the fuel column**". That conflates the *rule* with the *layer*: the
|
||||
`ColumnClassifier` port classifies a **deduped, portfolio-wide set of description
|
||||
strings** (`classify(descriptions: set[str]) -> {description: archetype}`), with
|
||||
no access to another column, and stores the result keyed by description. Fuel
|
||||
varies **per property**, not per description — so two dwellings sharing the
|
||||
description `"regular boiler"` on different fuels cannot map to different
|
||||
archetypes in that model. A fuel-aware *classifier* would need a composite
|
||||
`(description, fuel)` key threaded through the port, the orchestrator dedup, and
|
||||
the description-keyed override table — the first cross-column classifier in the
|
||||
system.
|
||||
|
||||
But `ResolvedPropertyOverrides` already carries every override component for a
|
||||
property together (`main_heating_system` **and** `main_fuel`), so the fuel is in
|
||||
hand at **resolution** — where `overlays_from` builds the overlays.
|
||||
|
||||
## Decision
|
||||
|
||||
Add three members to `MainHeatingSystemType` (byte-for-byte with #455):
|
||||
|
||||
```
|
||||
Oil boiler, regular
|
||||
Oil boiler, combi
|
||||
Solid fuel boiler
|
||||
```
|
||||
|
||||
**1. Overlay — model them on the gas-boiler pattern** (a dedicated path that
|
||||
forces a complete, coherent companion set; ADR-0048/0052/0053). Representative
|
||||
SAP codes map to the modern/condensing variant (A–G efficiency deferred, as for
|
||||
gas):
|
||||
|
||||
| archetype | SAP code | fuel | fan flue | cylinder | control | category | meter |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| Oil boiler, regular | 127 (condensing oil, 84%) | oil (28) | yes | yes | 2106 | 2 | Single |
|
||||
| Oil boiler, combi | 130 (condensing combi oil, 82%) | oil (28) | yes | no | 2106 | 2 | Single |
|
||||
| Solid fuel boiler | 151 (manual-feed independent, 0.60) | house coal (33) | no | yes | 2106 | 2 | Single |
|
||||
|
||||
- **Control 2106** (full modern), **not** the electric-boiler's conservative 2101
|
||||
— decided with Khalim: model the boiler modern, exactly as the gas archetype
|
||||
does (the whole install is treated as a modern condensing system).
|
||||
- **`gas_connection_available` is left unset (`None`)**, not forced. Unlike a gas
|
||||
boiler (which *requires* the connection, so forces `True`), an oil/solid boiler
|
||||
has no such requirement — a `None` overlay field inherits the lodged EPC's
|
||||
value ("default to the existing EPC"), and an absent lodgement reads as no gas
|
||||
main. Forcing `False` would wrongly erase a real gas cooking connection.
|
||||
- **Solid fuel is single-member, conservative code 151** (0.60, the lowest-
|
||||
efficiency independent solid-fuel boiler). The `main_fuel` override drives
|
||||
carbon, but it does **not** change `sap_main_heating_code`, so one member fixes
|
||||
one efficiency: 151 under-credits a wood dwelling (its true 155 is 0.70) rather
|
||||
than over-crediting a coal one — the safe direction. Splitting into
|
||||
mineral/biomass sub-members is deferred until demand and app-side members
|
||||
justify it. `main_fuel = house coal` is the coherent default, always overridden
|
||||
by the property's actual `main_fuel` (last-wins).
|
||||
|
||||
**2. Fuel-awareness is a pure fuel-join at *resolution*, not a classifier
|
||||
change.** A function `resolve_boiler_archetype(heating_value, fuel_value)`
|
||||
re-points a **gas** wet-boiler archetype by the property's `main_fuel` family
|
||||
(reusing the existing `_FUEL_FAMILY` map): oil → `Oil boiler, regular/combi`
|
||||
(preserving regular/combi); solid (coal/wood/dual) → `Solid fuel boiler`;
|
||||
gas/LPG/electric/none → unchanged. `overlays_from` applies it before building the
|
||||
`main_heating_system` overlay, reading the sibling `main_fuel` row. The LLM
|
||||
classifier and its deduped port are **untouched**.
|
||||
|
||||
This is the same rule the issue's per-property backfill describes, so
|
||||
recurrence-prevention and the 43-row correction are **one rule**. It also fixes
|
||||
modelling for the existing 43 at model-time (their stored `Gas boiler` snapshot
|
||||
re-points to `Oil`/`Solid` when scored) — the persisted-label backfill (for UI
|
||||
truthfulness) is a separate, deferred step.
|
||||
|
||||
**Composition (unchanged, ADR-0041):** the archetype's forced companions are
|
||||
*defaults*; `main_fuel` is in `_APPLY_LAST`, so a fuel override applies **after**
|
||||
the archetype and wins on `main_fuel_type` (never on `sap_main_heating_code`) —
|
||||
efficiency from the boiler code, carbon from the fuel. No contradiction by
|
||||
construction: the join *picked* the archetype *from* the fuel. Category/control
|
||||
stay archetype-forced (no override column, and a stale inherited one silently
|
||||
mis-bills, ADR-0048); a future user-tunable control would add its own column and
|
||||
join into `_APPLY_LAST`, not change this.
|
||||
|
||||
## Considered options
|
||||
|
||||
- **Fuel-aware classifier via a composite `(description, fuel)` key.** Rejected:
|
||||
wrong layer — forces a schema + shared-port change and the first cross-column
|
||||
classifier, when `ResolvedPropertyOverrides` already hands resolution both
|
||||
values for free.
|
||||
- **Split `Solid fuel boiler` into mineral vs biomass now.** Rejected: doubles the
|
||||
enum + app-side members for a 16-row cohort; the conservative single code (151)
|
||||
bounds the error to the safe (under-credit) direction. Revisit on demand.
|
||||
- **Conservative control 2101 (the electric-boiler choice).** Rejected: these are
|
||||
wet boilers modelled modern like gas (decided with Khalim); 2101 is for the
|
||||
unknown-vintage electric-boiler case (ADR-0052).
|
||||
- **Force `gas_connection_available=False`.** Rejected: erases a real gas cooking
|
||||
connection; inherit the lodged EPC instead (`None`).
|
||||
- **Re-point only at a one-off backfill.** Rejected alone: leaves the *engine*
|
||||
emitting gas-on-oil for every future portfolio; the resolution join prevents
|
||||
recurrence and the backfill becomes cosmetic (label truthfulness).
|
||||
|
||||
## Consequences
|
||||
|
||||
- The 43 rows score on the correct boiler efficiency + fuel at model-time with no
|
||||
data migration; the persisted-snapshot/vocab backfill (label truthfulness) is a
|
||||
deferred follow-up, after #455 is live.
|
||||
- `resolve_boiler_archetype` is extensible to the planned `Boiler, unspecified
|
||||
fuel` member — its resolution order (fuel override → EPC → default gas) is the
|
||||
same shape; the EPC-fuel fallback (resolution has no EPC today) lands with that
|
||||
member.
|
||||
- Efficiency A–G bands remain deferred for these as for gas (modern/condensing
|
||||
code); solid sub-fuel efficiency (155/153) is bounded by the conservative 151.
|
||||
- Lock-step obligation (ADR-0002): the three member strings are the contract with
|
||||
#455 / migration 0279.
|
||||
Loading…
Add table
Reference in a new issue