mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
SAP 10.2 Table 4d (PDF p.170) "Heating type and responsiveness ...
depending on heat emitter":
Heat emitter R
------------------------------------------------- -----
Systems with radiators 1.0
Underfloor (wet) — pipes in insulated timber floor 1.0
Underfloor (wet) — pipes in screed above insulation 0.75
Underfloor (wet) — pipes in concrete slab 0.25
Warm air via fan coil units 1.0
Pre-S0380.89 the cascade `_responsiveness` had:
if isinstance(emitter, int) and emitter == 2:
return 0.25
return 1.0
But the Elmhurst cert-side enum (`_ELMHURST_HEAT_EMITTER_TO_SAP10` at
`datatypes/epc/domain/mapper.py:3646`) maps:
1 = Radiators
2 = Underfloor (in screed) ← spec R=0.75, NOT 0.25
3 = Underfloor (timber floor)
4 = Warm air
5 = Fan coils
The cascade silently treated screed UFH (Elmhurst code 2) as
concrete-slab UFH (R=0.25 — Table 4d's most thermally massive UFH
variant). The bug halved the actual spec responsiveness — for a screed
UFH cert, off-period temperature reduction was computed with R=0.25
instead of R=0.75, materially under-counting MIT drop and over-counting
SH demand.
The bug is latent on cohort + golden because `_first_main_heating`
picks main[0] and almost all certs lodge radiators (emitter=1) there.
Corpus audit (full JSON sweep): emitter=2 appears on 2 records and
in both cases on a secondary main slot (e.g. golden cert
0240-0200-5706-2365-8010 main[1]) — never on the selected first main.
The fix preempts the next cert that lodges screed UFH on main[0].
Fix:
- New `_RESPONSIVENESS_BY_EMITTER_CODE` dispatch dict reflecting
Table 4d per the Elmhurst cert-side enum (1: 1.0, 2: 0.75, 3: 1.0,
4: 1.0, 5: 1.0). "Concrete slab UFH" (Table 4d R=0.25) has no
cert-side enum entry — that variant would need a new mapper code
before the cascade can dispatch it.
- `_responsiveness` flipped to strict-raise per [[reference-
unmapped-sap-code]]: absent lodging (None / 0 / "") returns modal
R=1.0 default; lodging present but unmapped raises
`UnmappedSapCode("heat_emitter_type", value)`.
Tests (4 new, AAA-structure):
- `test_heat_emitter_code_2_underfloor_in_screed_routes_to_responsiveness_0p75_per_table_4d`
pins the bug fix: emitter=2 → R=0.75 (was 0.25)
- `test_heat_emitter_code_dispatch_table_4d_full_coverage`
pins all 5 Elmhurst emitter codes to spec-correct R
- `test_responsiveness_raises_unmapped_sap_code_on_unknown_emitter`
pins the strict-raise contract (hypothetical code 99 raises)
- `test_responsiveness_default_1p0_when_emitter_lodging_absent`
pins the absent-lodging contract (None / 0 / "" → 1.0)
Test baseline: 568 pass (was 564 + 4 new) + 9 expected
`test_sap_result_pin[000565-*]` fails unchanged. Cohort + golden
unaffected (all use emitter=1 on main[0]).
Pyright net-zero per touched file (one `pyright: ignore` added on the
absent-lodging test where `main_heating_control=None` is passed to a
dataclass declaring `Union[int, str]` — runtime data exhibits None
on certs lacking space-heating controls, so the test covers a real
codepath the type system doesn't model).
Per the user-requested "we keep debugging silent fallbacks" mandate,
this is the second slice (after S0380.88) in the strict-raise series.
Next candidates per [[reference-unmapped-sap-code]]: PV pitch +
overshading, meter→tariff, heat-network DLF, secondary-heating
fraction by category.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|---|---|---|
| .. | ||
| addresses | ||
| data_transformation | ||
| epc | ||
| sap10_calculator | ||
| sap10_ml | ||
| tasks | ||
| postcode.py | ||