mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
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:
parent
dae2a6f3fe
commit
9dc6073bd3
1 changed files with 37 additions and 22 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue