mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
Assume a dual Economy-7 meter for all-electric room-heater dwellings 🟩
Electric room heaters (691) get a Dual meter from the overlay (not single-rate): an all-electric room-heater dwelling realistically bills on Economy 7, and the §12 dispatch then applies a high/low split rather than a single-rate over-penalty. Overlay owns its assumed-meter policy via _ASSUMED_DUAL_METER_CODES (the §12 off-peak systems + room heaters), keeping OFF_PEAK_IMPLYING_HEATING_CODES pure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
13f1981776
commit
4e96ea91a5
2 changed files with 24 additions and 11 deletions
|
|
@ -12,10 +12,11 @@ field-wise with the main_fuel / water_heating overlays.
|
||||||
electricity tariff (meter) and, for storage heaters, its charge control. Rather
|
electricity tariff (meter) and, for storage heaters, its charge control. Rather
|
||||||
than hand-attach those per archetype (easy to forget when a new system is
|
than hand-attach those per archetype (easy to forget when a new system is
|
||||||
added), they are **derived from the SAP code**: the off-peak meter from the
|
added), they are **derived from the SAP code**: the off-peak meter from the
|
||||||
calculator's single off-peak classification (`OFF_PEAK_IMPLYING_HEATING_CODES`,
|
overlay's assumed-Dual classification (`_ASSUMED_DUAL_METER_CODES` — the §12
|
||||||
SAP §12), and the conservative manual charge control for storage heaters. So
|
off-peak systems plus all-electric room-heater dwellings), and the conservative
|
||||||
adding a heating archetype is just adding its code — coherent companions fall
|
manual charge control for storage heaters. So adding a heating archetype is just
|
||||||
out. Synthesis owns coherence; the calculator never normalises a lodged cert.
|
adding its code — coherent companions fall out. Synthesis owns coherence; the
|
||||||
|
calculator never normalises a lodged cert.
|
||||||
|
|
||||||
The SEDBUK A-G efficiency band the Hyde "Heating" column carries is NOT honoured
|
The SEDBUK A-G efficiency band the Hyde "Heating" column carries is NOT honoured
|
||||||
yet (no efficiency slot on the overlay/MainHeatingDetail) -- archetypes map to
|
yet (no efficiency slot on the overlay/MainHeatingDetail) -- archetypes map to
|
||||||
|
|
@ -45,6 +46,19 @@ _OFF_PEAK_METER = "Dual"
|
||||||
# split (the mirror of the storage→Dual drag, ADR-0035).
|
# split (the mirror of the storage→Dual drag, ADR-0035).
|
||||||
_SINGLE_RATE_METER = "Single"
|
_SINGLE_RATE_METER = "Single"
|
||||||
|
|
||||||
|
# Electric room heaters (SAP Table 4a 691). They don't *require* off-peak the way
|
||||||
|
# storage/CPSU do, so they're absent from the calculator's §12
|
||||||
|
# `OFF_PEAK_IMPLYING_HEATING_CODES` (Rules 1-2). But a dwelling heated by them is
|
||||||
|
# all-electric and realistically billed on Economy 7 — its immersion hot water
|
||||||
|
# charges overnight and §12 Rule 3 gives the room heaters a 10-hour off-peak
|
||||||
|
# window. So when the landlord names only the system, the coherent meter to
|
||||||
|
# assume is Dual; the §12 dispatch then applies the realistic high/low split
|
||||||
|
# (not a single-rate over-penalty, nor an all-low over-credit).
|
||||||
|
_ROOM_HEATER_CODES = frozenset({691})
|
||||||
|
# Codes for which the overlay assumes a Dual (off-peak) meter: the §12-mandated
|
||||||
|
# off-peak systems plus the all-electric room-heater dwellings above.
|
||||||
|
_ASSUMED_DUAL_METER_CODES = OFF_PEAK_IMPLYING_HEATING_CODES | _ROOM_HEATER_CODES
|
||||||
|
|
||||||
# SAP Table 4e Group 4 storage charge-control code. Manual charge control is the
|
# SAP Table 4e Group 4 storage charge-control code. Manual charge control is the
|
||||||
# *conservative* assumption when the landlord didn't tell us the control: its
|
# *conservative* assumption when the landlord didn't tell us the control: its
|
||||||
# +0.7 C mean-internal-temperature adjustment is the largest of the storage
|
# +0.7 C mean-internal-temperature adjustment is the largest of the storage
|
||||||
|
|
@ -100,9 +114,10 @@ _MAIN_HEATING_CODES: dict[str, int] = {
|
||||||
|
|
||||||
def _meter_for(code: int) -> str:
|
def _meter_for(code: int) -> str:
|
||||||
"""The coherent meter a heating code implies: an off-peak ("Dual") meter for
|
"""The coherent meter a heating code implies: an off-peak ("Dual") meter for
|
||||||
the calculator's §12 off-peak systems, an explicit single-rate ("Single")
|
the §12 off-peak systems and all-electric room-heater dwellings
|
||||||
meter for every other system. Always set — never left to bleed."""
|
(`_ASSUMED_DUAL_METER_CODES`), an explicit single-rate ("Single") meter for
|
||||||
return _OFF_PEAK_METER if code in OFF_PEAK_IMPLYING_HEATING_CODES else _SINGLE_RATE_METER
|
every other system. Always set — never left to bleed."""
|
||||||
|
return _OFF_PEAK_METER if code in _ASSUMED_DUAL_METER_CODES else _SINGLE_RATE_METER
|
||||||
|
|
||||||
|
|
||||||
def _control_for(code: int) -> Optional[int]:
|
def _control_for(code: int) -> Optional[int]:
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,10 @@ import pytest
|
||||||
from domain.epc.property_overrides.main_heating_system_type import MainHeatingSystemType
|
from domain.epc.property_overrides.main_heating_system_type import MainHeatingSystemType
|
||||||
from domain.epc.property_overlays.main_fuel_overlay import fuel_overlay_for
|
from domain.epc.property_overlays.main_fuel_overlay import fuel_overlay_for
|
||||||
from domain.epc.property_overlays.main_heating_system_overlay import (
|
from domain.epc.property_overlays.main_heating_system_overlay import (
|
||||||
|
_ASSUMED_DUAL_METER_CODES,
|
||||||
_MAIN_HEATING_CODES,
|
_MAIN_HEATING_CODES,
|
||||||
main_heating_overlay_for,
|
main_heating_overlay_for,
|
||||||
)
|
)
|
||||||
from domain.sap10_calculator.tables.table_12a import (
|
|
||||||
OFF_PEAK_IMPLYING_HEATING_CODES,
|
|
||||||
)
|
|
||||||
from domain.epc.property_overlays.water_heating_overlay import (
|
from domain.epc.property_overlays.water_heating_overlay import (
|
||||||
water_heating_overlay_for,
|
water_heating_overlay_for,
|
||||||
)
|
)
|
||||||
|
|
@ -241,7 +239,7 @@ def test_off_peak_archetypes_drag_dual_others_drag_single() -> None:
|
||||||
for value, code in _MAIN_HEATING_CODES.items():
|
for value, code in _MAIN_HEATING_CODES.items():
|
||||||
simulation = main_heating_overlay_for(value, 0)
|
simulation = main_heating_overlay_for(value, 0)
|
||||||
assert simulation is not None and simulation.heating is not None
|
assert simulation is not None and simulation.heating is not None
|
||||||
expected = "Dual" if code in OFF_PEAK_IMPLYING_HEATING_CODES else "Single"
|
expected = "Dual" if code in _ASSUMED_DUAL_METER_CODES else "Single"
|
||||||
assert simulation.heating.meter_type == expected, value
|
assert simulation.heating.meter_type == expected, value
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue