mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-22 08:48:38 +00:00
Electric immersion water heating (WHC 903) on an off-peak tariff billed 100% at the low rate, under-costing the dwelling and over-rating it (median +0.98 SAP across the off-peak WHC-903 API cohort, n=57). SAP 10.2 Table 12a "Immersion water heater" row (PDF p.191) routes the water-heating column to Table 13 (PDF p.197): the high-rate fraction is a function of cylinder volume V, assumed occupancy N (Appendix J Table 1b) and single-/dual-immersion. The remainder bills at the low rate. Table 13 Note 2 supplies exact equations equivalent to the rounded grid; `electric_dhw_high_rate_fraction` evaluates them (validated against the published 110 L grid cells). Per Note 1 the 10-hour equations cover any tariff with >=10 hours/day low-rate (so 18-/24-hour use that column). Immersion code mapping CONFIRMED 1=dual, 2=single via RdSAP 10 §10.5 (PDF p.54 — an immersion is "assumed dual" on a dual/off-peak meter) cross-checked against the API cohort (code 1 sits 3.6:1 on dual meters; code 2 on single meters). This INVERTS an earlier handover's unverified "1=single, 2=dual" note — the dual code carries Table 13's small fraction, matching the cohort over-rating direction; the single mapping overshot in a prototype. API SAP eval: 47.6% -> 48.6% within 0.5; <1.0 62.6% -> 63.8%; mean|err| 1.586 -> 1.561; 909 computed, 0 raises. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
70 lines
2.9 KiB
Python
70 lines
2.9 KiB
Python
"""SAP 10.2 Table 13 — high-rate fraction for electric DHW heating.
|
|
|
|
Sourced verbatim from `domain/sap10_calculator/docs/specs/sap-10-2-full-
|
|
specification-2025-03-14.pdf`, page 197 (Table 13). RdSAP10 §10.5 (PDF
|
|
p.54) routes electric immersion water heating here via Table 12a's
|
|
"Immersion water heater" row, whose Water-heating column reads "Fraction
|
|
from Table 13".
|
|
|
|
The table gives the fraction of DHW electricity consumed at the HIGH
|
|
rate for a cylinder with a single or dual immersion heater on an
|
|
off-peak tariff; the remainder is at the low rate. Note 2 of the table
|
|
supplies exact equations equivalent to the tabulated (rounded) grid —
|
|
this module evaluates those equations, so no floor-area interpolation
|
|
is needed:
|
|
|
|
7-hour tariff (>= 7 hours/day at the low rate)
|
|
Dual: [(6.8 - 0.024 V) N + 14 - 0.07 V] / 100
|
|
Single: [(14530 - 762 N) / V - 80 + 10 N] / 100
|
|
|
|
10-hour tariff (>= 10 hours/day at the low rate)
|
|
Dual: [(6.8 - 0.036 V) N + 14 - 0.105 V] / 100
|
|
Single: [(14530 - 762 N) / (1.5 V) - 80 + 10 N] / 100
|
|
|
|
where V is the cylinder volume (litres) and N is the assumed occupancy
|
|
(Appendix J Table 1b). Per Note 2 the result is clamped to [0, 1]. Per
|
|
Note 1 the 10-hour equations apply to any tariff providing at least 10
|
|
hours/day at the low rate (so 18-hour and 24-hour use the 10-hour
|
|
column). Heat pumps providing water heating only are treated as dual
|
|
immersion (Note 1) — out of scope of this helper (callers route those
|
|
via Table 12a).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from domain.sap10_calculator.tables.table_12a import Tariff
|
|
|
|
|
|
def electric_dhw_high_rate_fraction(
|
|
*,
|
|
cylinder_volume_l: float,
|
|
occupancy_n: float,
|
|
single_immersion: bool,
|
|
tariff: Tariff,
|
|
) -> float:
|
|
"""SAP 10.2 Table 13 (PDF p.197) high-rate fraction for an electric
|
|
immersion DHW cylinder on an off-peak tariff.
|
|
|
|
`single_immersion` selects the single- vs dual-immersion equation
|
|
(RdSAP10 §10.5 p.54: an immersion is assumed dual on a dual meter).
|
|
The 7-hour tariff uses the 7-hour equations; every other off-peak
|
|
tariff (10/18/24-hour, all >= 10 hours low-rate per Note 1) uses the
|
|
10-hour equations. STANDARD has no off-peak split and is rejected —
|
|
callers must early-return before this fires.
|
|
"""
|
|
if tariff is Tariff.STANDARD:
|
|
raise ValueError("Table 13 high-rate fraction is undefined for STANDARD")
|
|
v = cylinder_volume_l
|
|
n = occupancy_n
|
|
if tariff is Tariff.SEVEN_HOUR:
|
|
if single_immersion:
|
|
fraction = ((14530 - 762 * n) / v - 80 + 10 * n) / 100
|
|
else:
|
|
fraction = ((6.8 - 0.024 * v) * n + 14 - 0.07 * v) / 100
|
|
else:
|
|
# >= 10 hours/day at the low rate (10/18/24-hour) — Note 1.
|
|
if single_immersion:
|
|
fraction = ((14530 - 762 * n) / (1.5 * v) - 80 + 10 * n) / 100
|
|
else:
|
|
fraction = ((6.8 - 0.036 * v) * n + 14 - 0.105 * v) / 100
|
|
return max(0.0, min(1.0, fraction))
|