mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice S0380.57: Elmhurst mapper infers electricity fuel for electric SAP main heating codes
Elmhurst §14.0 leaves "Fuel Type" empty for electric main heating
systems (heat pumps, electric boilers, storage heaters, electric
underfloor, warm-air HPs) — the SAP code identifies the carrier
directly. The mapper was reading the empty string via
`_elmhurst_main_fuel_int(mh.fuel_type)` → None, and downstream
`_main_fuel_code` returned None, so Table 32 unit-price lookups
defaulted to mains gas. Cert 000565 (HP Main 1, SAP code 224) was
being charged 29,353 kWh/yr of electricity at the gas tariff —
£0.0364/kWh instead of £0.165/kWh.
New `_ELECTRIC_SAP_MAIN_HEATING_CODES` frozen set covers the Table
4a electric carrier rows:
191-196 Electric boilers
211-217, 221-227 Heat pumps (224 = ASHP 2013+, 1.70 COP)
401-409 Electric storage heaters
421-425 Electric underfloor heating
521-527 Warm-air heat pumps
Inference fires in both Main 1 (`_map_elmhurst_sap_heating`) and
Main 2 (`_map_elmhurst_main_heating_2`) construction paths — when
`_elmhurst_main_fuel_int(fuel_type)` returns None AND the SAP code
is in the electric set, fall back to `_STANDARD_ELECTRICITY_FUEL_
CODE = 30` (Table 12 row "Electricity, standard tariff").
Cert 000565 cascade impact (compounding with S0380.56):
- sap_score: 71 → 30 (target 29 → Δ +1.7; was Δ +44)
- sap_score_continuous: 71.42 → 30.21 (target 28.51 → Δ +1.70; was Δ +42.91)
- ecf: 2.05 → 5.22 (target 5.39 → Δ −0.17; was Δ −3.34)
- total_fuel_cost_gbp: 1,423.80 → 3,624.64 (target 4,680.26 → Δ −1,055.62; was Δ −3,256.46)
- co2_kg_per_yr: 7,181.62 → 5,009.47 (target 6,447.63 → Δ −1,438.16; was Δ +733.99)
(now undershooting — independent cascade gap
around Table 12d monthly electric CO2 factor
interpolation; separate slice)
Single-main non-HP certs: no behavioural change (`fuel_type` lodged
explicitly for gas/oil boilers → `_elmhurst_main_fuel_int` returns
non-None → inference branch not entered). Cohort regression check:
472 pass + 10 expected 000565 fails — no regression.
Spec source: SAP 10.2 Table 4a main heating SAP codes + Table 12 fuel
codes (electricity, standard tariff = 30). Heat-pump cohort efficiency
values cross-referenced in `domain/sap10_ml/sap_efficiencies.py:42-44`.
Pyright net-zero on mapper.py (32 / 32).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
35d2648ae6
commit
358b4dcd01
1 changed files with 46 additions and 0 deletions
|
|
@ -3618,6 +3618,36 @@ _ELMHURST_GAS_BOILER_FUEL_TYPES: frozenset[str] = frozenset({
|
|||
"LPG special condition",
|
||||
})
|
||||
|
||||
# Standard-electricity Table 32 fuel code. SAP 10.2 Table 12 lists
|
||||
# this as code 30 = "Electricity, standard tariff" — the fuel-cost /
|
||||
# CO2 / PE lookup key for any electric main heating system.
|
||||
_STANDARD_ELECTRICITY_FUEL_CODE: Final[int] = 30
|
||||
|
||||
# SAP 10.2 Table 4a main-heating SAP codes whose fuel is electricity.
|
||||
# Elmhurst §14.0 leaves "Fuel Type" empty for these systems (the SAP
|
||||
# code identifies the carrier), so the mapper must infer the fuel
|
||||
# from the SAP code rather than the empty string. First needed by
|
||||
# cert 000565 Main 1 (SAP code 224 = "Air source heat pump, 2013 or
|
||||
# later") — without inference, `_main_fuel_code` returns None and the
|
||||
# Table 32 price lookup defaults to mains gas, charging the HP cert's
|
||||
# electricity kWh at the gas tariff.
|
||||
#
|
||||
# Coverage as Elmhurst-only fixtures land:
|
||||
# 191-196 — electric boilers (Table 4a "Electric boilers" rows)
|
||||
# 211-217, 221-227 — heat pumps (Table 4a "Heat pumps" rows; 224 is
|
||||
# the ASHP 2013+ entry, 1.70 COP — `sap_efficiencies.py:44`)
|
||||
# 401-409 — electric storage heaters (Table 4a "Storage heaters")
|
||||
# 421-425 — electric underfloor (Table 4a "Underfloor heating")
|
||||
# 521-527 — warm-air heat pumps (Table 4a "Warm-air heat pumps")
|
||||
_ELECTRIC_SAP_MAIN_HEATING_CODES: Final[frozenset[int]] = frozenset(
|
||||
list(range(191, 197))
|
||||
+ list(range(211, 218))
|
||||
+ list(range(221, 228))
|
||||
+ list(range(401, 410))
|
||||
+ list(range(421, 426))
|
||||
+ list(range(521, 528))
|
||||
)
|
||||
|
||||
|
||||
class UnmappedElmhurstLabel(ValueError):
|
||||
"""An Elmhurst Summary lodged a finite-enum label that the mapper
|
||||
|
|
@ -3835,6 +3865,13 @@ def _map_elmhurst_main_heating_2(
|
|||
),
|
||||
)
|
||||
main_fuel_int = _elmhurst_main_fuel_int(mh2.fuel_type)
|
||||
# Same electric-SAP-code fuel inference as Main 1 — see
|
||||
# `_ELECTRIC_SAP_MAIN_HEATING_CODES` docstring.
|
||||
if (
|
||||
main_fuel_int is None
|
||||
and mh2.main_heating_sap_code in _ELECTRIC_SAP_MAIN_HEATING_CODES
|
||||
):
|
||||
main_fuel_int = _STANDARD_ELECTRICITY_FUEL_CODE
|
||||
category: Optional[int] = None
|
||||
if pcdb_index is not None and heat_pump_record(pcdb_index) is not None:
|
||||
category = _ELMHURST_HEATING_CATEGORY_HEAT_PUMP
|
||||
|
|
@ -3882,6 +3919,15 @@ def _map_elmhurst_sap_heating(survey: ElmhurstSiteNotes) -> SapHeating:
|
|||
)
|
||||
pcdb_index = _elmhurst_pcdb_boiler_index(mh.pcdf_boiler_reference)
|
||||
main_fuel_int = _elmhurst_main_fuel_int(mh.fuel_type)
|
||||
# Elmhurst §14.0 leaves "Fuel Type" empty for electric main heating
|
||||
# systems (HP / electric boiler / storage / underfloor); the SAP
|
||||
# code identifies the carrier. Infer electricity (Table 32 code 30)
|
||||
# when the mapper can't read it from the `fuel_type` string.
|
||||
if (
|
||||
main_fuel_int is None
|
||||
and mh.main_heating_sap_code in _ELECTRIC_SAP_MAIN_HEATING_CODES
|
||||
):
|
||||
main_fuel_int = _STANDARD_ELECTRICITY_FUEL_CODE
|
||||
heat_emitter_int = _elmhurst_heat_emitter_int(mh.heat_emitter)
|
||||
sap_control_int = _elmhurst_sap_control_code(sap_control)
|
||||
main_heating_category = _elmhurst_main_heating_category(mh, pcdb_index)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue