mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-27 23:35:01 +00:00
Map oil and solid-fuel boilers to their SAP codes and companion set 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
55ab5bfb80
commit
04774a8888
2 changed files with 119 additions and 0 deletions
|
|
@ -19,6 +19,15 @@ class MainHeatingSystemType(Enum):
|
|||
GAS_COMBI = "Gas boiler, combi"
|
||||
GAS_REGULAR = "Gas boiler, regular"
|
||||
GAS_CPSU = "Gas CPSU"
|
||||
# Oil / solid-fuel wet boilers off the fuel-agnostic gas default (ADR-0067).
|
||||
# A fuel-blind "Boiler … Regular/Combi" description on a non-gas main_fuel was
|
||||
# funnelled onto the gas archetype; these give it a home, with the fuel column
|
||||
# (`resolve_boiler_archetype`) re-pointing at resolution. Solid fuel is a single
|
||||
# conservative member (SAP 151, 0.60); sub-fuels deferred. Lock-step with
|
||||
# assessment-model#455 / migration 0279 (ADR-0002).
|
||||
OIL_BOILER_REGULAR = "Oil boiler, regular"
|
||||
OIL_BOILER_COMBI = "Oil boiler, combi"
|
||||
SOLID_FUEL_BOILER = "Solid fuel boiler"
|
||||
ELECTRIC_STORAGE_OLD = "Electric storage heaters, old"
|
||||
ELECTRIC_STORAGE_SLIMLINE = "Electric storage heaters, slimline"
|
||||
ELECTRIC_STORAGE_CONVECTOR = "Electric storage heaters, convector"
|
||||
|
|
|
|||
|
|
@ -817,6 +817,116 @@ def test_every_resolvable_main_heating_value_decodes(
|
|||
assert simulation is not None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("main_heating_value", "code"),
|
||||
[
|
||||
# Oil / solid-fuel wet boilers off the fuel-agnostic gas default (ADR-0067).
|
||||
# Modern/condensing codes (A-G deferred, like gas): SAP Table 4b 127
|
||||
# condensing oil regular, 130 condensing oil combi; Table 4a 151 manual-feed
|
||||
# independent solid-fuel boiler (the conservative single member).
|
||||
("Oil boiler, regular", 127),
|
||||
("Oil boiler, combi", 130),
|
||||
("Solid fuel boiler", 151),
|
||||
],
|
||||
)
|
||||
def test_oil_and_solid_fuel_boilers_decode_to_their_boiler_codes(
|
||||
main_heating_value: str, code: int
|
||||
) -> None:
|
||||
# Act
|
||||
simulation = main_heating_overlay_for(main_heating_value, 0)
|
||||
|
||||
# Assert — its own boiler code, not the mains-gas default.
|
||||
assert simulation is not None
|
||||
assert simulation.heating is not None
|
||||
assert simulation.heating.sap_main_heating_code == code
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("main_heating_value", "fuel"),
|
||||
[
|
||||
("Oil boiler, regular", 28), # heating oil
|
||||
("Oil boiler, combi", 28),
|
||||
("Solid fuel boiler", 33), # house coal — the conservative solid default
|
||||
],
|
||||
)
|
||||
def test_oil_and_solid_fuel_boilers_drag_their_natural_fuel(
|
||||
main_heating_value: str, fuel: int
|
||||
) -> None:
|
||||
# The archetype implies a fuel so a system-only override is self-coherent; the
|
||||
# property's `main_fuel` override still wins by last-wins composition (ADR-0041).
|
||||
|
||||
# Act
|
||||
simulation = main_heating_overlay_for(main_heating_value, 0)
|
||||
|
||||
# Assert
|
||||
assert simulation is not None
|
||||
assert simulation.heating is not None
|
||||
assert simulation.heating.main_fuel_type == fuel
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"main_heating_value", ["Oil boiler, regular", "Oil boiler, combi", "Solid fuel boiler"]
|
||||
)
|
||||
def test_fuel_boilers_drag_the_gas_pattern_boiler_companions(
|
||||
main_heating_value: str,
|
||||
) -> None:
|
||||
# Modelled on the gas boiler (ADR-0067): SAP Table 4a category 2, full modern
|
||||
# controls (Table 4e 2106), a single-rate meter — but NOT a forced gas
|
||||
# connection (an oil/solid boiler needs no gas main, so it inherits the lodged
|
||||
# EPC: `gas_connection_available` stays unset).
|
||||
|
||||
# Act
|
||||
simulation = main_heating_overlay_for(main_heating_value, 0)
|
||||
|
||||
# Assert
|
||||
assert simulation is not None
|
||||
assert simulation.heating is not None
|
||||
assert simulation.heating.main_heating_category == 2
|
||||
assert simulation.heating.main_heating_control == 2106
|
||||
assert simulation.heating.meter_type == "Single"
|
||||
assert simulation.heating.gas_connection_available is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("main_heating_value", "has_cylinder"),
|
||||
[
|
||||
("Oil boiler, regular", True),
|
||||
("Oil boiler, combi", False), # a combi heats hot water instantaneously
|
||||
("Solid fuel boiler", True), # no solid-fuel combi exists
|
||||
],
|
||||
)
|
||||
def test_fuel_boiler_cylinder_follows_combi(
|
||||
main_heating_value: str, has_cylinder: bool
|
||||
) -> None:
|
||||
# Act
|
||||
simulation = main_heating_overlay_for(main_heating_value, 0)
|
||||
|
||||
# Assert — hot water from the main system, with a cylinder unless a combi.
|
||||
assert simulation is not None
|
||||
assert simulation.heating is not None
|
||||
assert simulation.heating.has_hot_water_cylinder is has_cylinder
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("main_heating_value", "fan_flue"),
|
||||
[
|
||||
("Oil boiler, regular", True), # modern condensing oil: room-sealed/fan flue
|
||||
("Oil boiler, combi", True),
|
||||
("Solid fuel boiler", False), # solid fuel: conventional (non-fanned) flue
|
||||
],
|
||||
)
|
||||
def test_fuel_boiler_fan_flue_follows_fuel(
|
||||
main_heating_value: str, fan_flue: bool
|
||||
) -> None:
|
||||
# Act
|
||||
simulation = main_heating_overlay_for(main_heating_value, 0)
|
||||
|
||||
# Assert
|
||||
assert simulation is not None
|
||||
assert simulation.heating is not None
|
||||
assert simulation.heating.fan_flue_present is fan_flue
|
||||
|
||||
|
||||
def test_solid_fuel_room_heater_decodes_to_the_closed_room_heater_code() -> None:
|
||||
# A landlord-named solid-fuel room heater (e.g. a closed stove) is a
|
||||
# recognised archetype, not a gas wet system — it must decode to its SAP
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue