slice S-B17: Unknown meter_type → off-peak when fuel is electric

Refines S-B16 with a fuel-conditional rule for the Unknown tariff code
(RdSAP energy_tariff=3): all-electric dwellings whose meter_type the
assessor couldn't pin down are almost always E7-eligible (gas dwellings
default to Single). For non-electric end-uses (gas main heating), the
meter_type doesn't affect cost, so Unknown stays standard for them.

Hand-trace confirmation: 3 of the 4 worst residuals (0800-1364,
0036-1125, 0340-2394) all have meter_type=3 AND electric main fuel —
applying off-peak to these recovers the parity loss S-B16 introduced.

100-cert parity probe:
  MAE 5.04 → 4.39   (recovered to S-B15 best state)
  bias -1.20 → -0.17
  within ±10: 93% → 96%

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-05-18 16:42:38 +00:00
parent dae2a6f3fe
commit 9dc6073bd3

View file

@ -368,36 +368,51 @@ def _fuel_cost_gbp_per_kwh(
# RdSAP energy_tariff enum (per datatypes/epc/domain/epc_codes.csv):
# 1 = dual (off-peak / Economy-7)
# 2 = Single (standard tariff)
# 3 = Unknown (treated as Single — verified against
# Elmhurst assessor software with one
# real property; SAP assessor behavior is
# to default Unknown tariffs to Single)
# 3 = Unknown (Elmhurst-on-gas-property test says Single;
# corpus signal for electric dwellings says
# off-peak — see _is_off_peak_meter)
# 4 = dual (24 hour) (off-peak / 24h heating)
# 5 = off-peak 18 hour (off-peak)
#
# Different from the SAP-Schema enum which is 1=standard, 2=off-peak.
# Our corpus is RdSAP so we use RdSAP codes.
_RDSAP_OFF_PEAK_METER_CODES: Final[frozenset[int]] = frozenset({1, 4, 5})
_RDSAP_DEFINITELY_OFF_PEAK: Final[frozenset[int]] = frozenset({1, 4, 5})
_RDSAP_UNKNOWN_METER: Final[frozenset[int]] = frozenset({3})
def _is_off_peak_meter(meter_type: object) -> bool:
"""True when the cert's `sap_energy_source.meter_type` lodges an
off-peak / dual-rate tariff per the RdSAP enum (codes 1, 4, 5).
Unknown (3) defaults to standard since that's the cert software's
behaviour. Trusts whatever meter_type the cert lodges regardless of
whether the heating system makes that tariff "natural" per user
guidance the cert's lodged meter_type is the source of truth."""
def _is_off_peak_meter(meter_type: object, *, fuel_is_electric: bool) -> bool:
"""Whether the dwelling bills the given end-use (fuel_is_electric) at
the off-peak rate. RdSAP codes 1/4/5 are explicit off-peak. Code 3
(Unknown) defers to the fuel: electric end-uses on Unknown meters
typically come from E7-eligible dwellings whose tariff the assessor
couldn't pin down, so we apply off-peak. Non-electric end-uses on
Unknown meters are unaffected. Per user guidance + Elmhurst test on
a single gas-heated property, code 2 (Single) is always standard."""
if meter_type is None:
return False
code: Optional[int]
if isinstance(meter_type, int):
return meter_type in _RDSAP_OFF_PEAK_METER_CODES
if isinstance(meter_type, str):
stripped = meter_type.strip().lower()
if stripped in {"dual", "off-peak", "1", "4", "5"}:
return True
if stripped in {"dual (24 hour)", "off-peak 18 hour"}:
return True
code = meter_type
elif isinstance(meter_type, str):
s = meter_type.strip().lower()
if s in {"single", "standard", "2"}:
return False
if s in {"dual", "1"}:
code = 1
elif s in {"unknown", "3", ""}:
code = 3
elif s in {"dual (24 hour)", "4"}:
code = 4
elif s in {"off-peak 18 hour", "5"}:
code = 5
else:
return False
else:
return False
if code in _RDSAP_DEFINITELY_OFF_PEAK:
return True
if code in _RDSAP_UNKNOWN_METER and fuel_is_electric:
return True
return False
@ -435,7 +450,7 @@ def _space_heating_fuel_cost_gbp_per_kwh(
on an off-peak tariff (meter_type != standard) AND the main fuel is
electricity, bill at the off-peak rate instead. Trusts the cert's
meter_type rather than inferring tariff from heating code."""
if _is_electric_main(main) and _is_off_peak_meter(meter_type):
if _is_electric_main(main) and _is_off_peak_meter(meter_type, fuel_is_electric=True):
return prices.e7_low_rate_p_per_kwh * _PENCE_TO_GBP
return _fuel_cost_gbp_per_kwh(main, prices)
@ -450,8 +465,8 @@ def _hot_water_fuel_cost_gbp_per_kwh(
dwelling is on an off-peak tariff AND the water-heating fuel is
electricity (immersion etc.), bill HW at the off-peak rate too
the cert assessor treats the immersion as running on the timer."""
is_off_peak = _is_off_peak_meter(meter_type)
if is_off_peak and _is_electric_water(water_heating_fuel):
water_electric = _is_electric_water(water_heating_fuel)
if water_electric and _is_off_peak_meter(meter_type, fuel_is_electric=True):
return prices.e7_low_rate_p_per_kwh * _PENCE_TO_GBP
if water_heating_fuel is not None:
return prices.unit_price_p_per_kwh(water_heating_fuel) * _PENCE_TO_GBP