S0380.231: Dual-meter electric room heaters resolve to 10-hour tariff (RdSAP 10 §12 Rule 3)

RdSAP 10 §12 (PDF p.62) Dual-meter dispatch: "the choice between 7-hour
and 10-hour is made by the main heating type ... if the main system is a
direct-acting electric boiler (191), or electric room heaters ... it is
10-hour tariff." The electric room-heater codes — Table 4a 691 (panel/
convector/radiant), 692 (fan), 693 (portable), 694 (water-/oil-filled),
699 (assumed) — were missing from `_RULE_3_TEN_HOUR_CODES` (the long-
standing TODO there), so a Dual-meter room-heater cert fell through to
Rule 4 (7-hour default).

Compounded with S0380.230 (which routes room heaters to Table 12a
OTHER_DIRECT_ACTING_ELECTRIC): at 7-hour the high-rate fraction is 1.00
(all at 15.29 p), but at the correct 10-hour it is 0.50 split over the
10-hour rates (14.68 / 7.50 p) → blended ~11 p. Without this fix .230
over-charged and flipped the cluster from over- to under-rating.

1,000-cert 2026 API sample: cat-10 mean |err| 7.11 → 5.26, signed mean
+5.08 → -0.86 (now balanced, 22 over / 26 under — the systematic
directional bias is gone). Overall mean |err| 2.16 → 2.04. Full §4 suite
green (2406 passed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-04 20:51:09 +00:00
parent 0476b4b235
commit 3684a142ac
2 changed files with 29 additions and 3 deletions

View file

@ -250,13 +250,16 @@ _RULE_2_STORAGE_CODES: Final[frozenset[int]] = frozenset(
# Rule 3: direct-acting electric + heat pumps + electric room heaters
# → 10-hour. §12 lists "heat pump (211 to 224, 521 to 524, or
# database)" — the "database" branch fires when the cert lodges a
# PCDB Table 362 heat-pump index regardless of SAP code.
# PCDB Table 362 heat-pump index regardless of SAP code. §12 also
# names "electric room heaters" verbatim (RdSAP 10 PDF p.62) — Table 4a
# electric room-heater codes 691 (panel/convector/radiant), 692 (fan),
# 693 (portable), 694 (water-/oil-filled), 699 (assumed). Without these
# a Dual-meter room-heater cert fell through to Rule 4 (7-hour default).
_RULE_3_TEN_HOUR_CODES: Final[frozenset[int]] = frozenset(
[191] # direct-acting electric boiler
+ list(range(211, 225)) # heat pumps 211-224
+ list(range(521, 525)) # warm-air heat pumps 521-524
# TODO: electric room heater codes (SAP Table 4a row 6xx for
# electric panel / radiant heaters) when a fixture surfaces them.
+ [691, 692, 693, 694, 699] # electric room heaters (Table 4a)
)

View file

@ -17,12 +17,35 @@ from domain.sap10_calculator.tables.table_12a import (
Table12aSystem,
Tariff,
other_use_high_rate_fraction,
rdsap_tariff_for_cert,
space_heating_high_rate_fraction,
tariff_from_meter_type,
water_heating_high_rate_fraction,
)
def test_dual_meter_electric_room_heater_resolves_to_ten_hour_tariff() -> None:
# Arrange — RdSAP 10 §12 (PDF p.62) Dual-meter tariff dispatch: for a
# Dual meter the choice between 7-hour and 10-hour is made by the main
# heating type. Rule 3 verbatim: "if the main system ... is a direct-
# acting electric boiler (191), or electric room heaters ... it is
# 10-hour tariff." The electric room-heater codes are Table 4a 691
# (panel/convector/radiant), 692 (fan), 693 (portable), 694 (water-/
# oil-filled), 699 (assumed). Pre-slice these fell through to Rule 4
# (7-hour default), so a Dual-meter room-heater cert was billed at the
# 7-hour rates with Table 12a high-rate fraction 1.00 instead of the
# 10-hour rates with fraction 0.50 — over-charging direct-acting heat
# once S0380.230 routed it to OTHER_DIRECT_ACTING_ELECTRIC.
# Act / Assert — every electric room-heater code on a Dual meter → 10-hour.
for code in (691, 692, 693, 694, 699):
assert rdsap_tariff_for_cert(1, main_1_sap_code=code) is Tariff.TEN_HOUR
# Storage heaters (Rule 2) stay 7-hour; a gas room heater (non-Rule-3
# code) keeps the Rule 4 Dual default of 7-hour.
assert rdsap_tariff_for_cert(1, main_1_sap_code=401) is Tariff.SEVEN_HOUR
assert rdsap_tariff_for_cert(1, main_1_sap_code=601) is Tariff.SEVEN_HOUR
def test_tariff_enum_has_five_members() -> None:
"""Table 12a columns: standard (no off-peak split), 7-hour, 10-hour,
18-hour, 24-hour. Worksheet-shape fidelity: TEN_HOUR is included for