mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
10 modelling_e2e properties failed with "unmapped SAP code in fuel_code: 10": the billing layer (`sap_code_to_fuel`) had no carrier for Table-32 code 10 (dual fuel, mineral + wood) and raised rather than guess one. SAP 10.2 treats dual fuel as its OWN fuel (its own Table-12 factors), so model it as its own billing carrier rather than collapsing onto wood or coal: - New `Fuel.DUAL_FUEL_MINERAL_AND_WOOD`. - `_CODE_TO_FUEL[10]` -> that carrier. - Fuel Rates snapshot prices it at 7.69 p/kWh — the midpoint of the COAL proxy (7.13) and WOOD_LOGS (8.25). This mirrors SAP's own construction: Table-32 dual fuel (3.99) ~= midpoint of house coal (3.67) and wood logs (4.23). Marked `derived` with a documented _note/_gap/_assumption (like the COAL and HEAT_NETWORK proxies), since there is no retail blend price. A dedicated carrier + rate (vs a one-line map to an existing carrier) keeps the fuel identity faithful to SAP and avoids mispricing dual fuel as pure wood/coal. Tests: code 10 -> DUAL_FUEL carrier; snapshot prices it at 7.69; grid-export codes (36/60) still raise (the genuine no-carrier case). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
47 lines
1.8 KiB
Python
47 lines
1.8 KiB
Python
from __future__ import annotations
|
|
|
|
from enum import Enum
|
|
|
|
|
|
class Fuel(Enum):
|
|
"""A canonical billing fuel — the join key between the calculator's
|
|
per-end-use fuel (mapped from SAP fuel codes) and the Fuel Rates snapshot
|
|
(ADR-0014). Member names match the snapshot's keys.
|
|
|
|
``COAL`` (traditional house coal) and ``HEAT_NETWORK`` are carried as
|
|
members so a cert lodging them maps to a Fuel, but they have no national
|
|
rate — pricing them raises ``UnpricedFuel`` (house coal's domestic sale is
|
|
illegal in England; heat networks are scheme-specific).
|
|
"""
|
|
|
|
MAINS_GAS = "MAINS_GAS"
|
|
ELECTRICITY = "ELECTRICITY"
|
|
ELECTRICITY_OFF_PEAK = "ELECTRICITY_OFF_PEAK"
|
|
OIL = "OIL"
|
|
LPG = "LPG"
|
|
COAL = "COAL"
|
|
SMOKELESS = "SMOKELESS"
|
|
WOOD_LOGS = "WOOD_LOGS"
|
|
WOOD_PELLETS = "WOOD_PELLETS"
|
|
# SAP 10.2 dual-fuel appliance (mineral + wood) — its own SAP fuel, so kept
|
|
# as its own billing carrier rather than collapsed onto wood or coal. Priced
|
|
# as the mineral+wood midpoint (see the Fuel Rates snapshot note).
|
|
DUAL_FUEL_MINERAL_AND_WOOD = "DUAL_FUEL_MINERAL_AND_WOOD"
|
|
HEAT_NETWORK = "HEAT_NETWORK"
|
|
|
|
|
|
class UnpricedFuel(ValueError):
|
|
"""Bill Derivation was asked for a rate on a fuel the current Fuel Rates
|
|
snapshot does not price (ADR-0014).
|
|
|
|
Raised rather than billing at a wrong default so the gap surfaces
|
|
immediately — house coal and heat networks have no national rate, and
|
|
off-peak electricity needs the day/night split that a later slice adds.
|
|
"""
|
|
|
|
def __init__(self, fuel: Fuel) -> None:
|
|
super().__init__(
|
|
f"no rate for fuel {fuel.name} in the current Fuel Rates snapshot; "
|
|
f"add it to the snapshot or map this end use to a priced fuel"
|
|
)
|
|
self.fuel = fuel
|