mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
test: reflect ASHP winning more often after the Vaillant swap
The efficient Vaillant aroTHERM plus 5 kW (ADR-0025) now raises SAP even on gas dwellings, so the Optimiser selects the ASHP bundle far more often -- a deliberate product shift (user-confirmed). Updated the integration/harness tests: - ARA multi-measure: ASHP is additive to the kept fabric package -> add to set. - ARA listed_uprn: is_listed blocks walls AND ASHP (blocks_internal); the gate is now observable via ASHP (selected unrestricted, blocked listed). - report three-measures: ASHP + solid-floor displace the fabric stack; assert their triggers (property_type, main_heating_category / floor). - console hhr + solid_wall coverage tests: assert the measure is OFFERED as a candidate (the wiring/eligibility intent), since ASHP now out-competes them in selection; also assert the optimised package leads with ASHP. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b089bde1e6
commit
037daa98ef
3 changed files with 56 additions and 34 deletions
|
|
@ -10,6 +10,8 @@ from datatypes.epc.domain.epc import Epc
|
|||
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
||||
from domain.geospatial.planning_restrictions import PlanningRestrictions
|
||||
from domain.modelling.contingencies import contingency_rate
|
||||
from domain.modelling.generators.heating_recommendation import recommend_heating
|
||||
from domain.modelling.generators.solid_wall_recommendation import recommend_solid_wall
|
||||
from harness.console import DEFAULT_CATALOGUE, run_modelling, run_one
|
||||
from repositories.product.product_json_repository import ProductJsonRepository
|
||||
from tests.domain.modelling._elmhurst_recommendation import (
|
||||
|
|
@ -86,13 +88,21 @@ def test_run_modelling_recommends_solid_wall_insulation_for_solid_brick() -> Non
|
|||
"solid_brick_ewi_001431_before.pdf"
|
||||
)
|
||||
|
||||
# Act — Modelling only, no database.
|
||||
plan = run_modelling(epc, goal_band="C", print_table=False)
|
||||
# Assert — the solid-wall generator is wired in and OFFERS a solid-wall
|
||||
# Option for this dwelling. Whether the Optimiser then selects it depends on
|
||||
# the package: the efficient ASHP bundle (ADR-0025) now often reaches the
|
||||
# band first, so we assert the Option is OFFERED (the wiring/eligibility
|
||||
# intent) rather than selected.
|
||||
recommendation = recommend_solid_wall(epc, ProductJsonRepository(DEFAULT_CATALOGUE))
|
||||
assert recommendation is not None
|
||||
assert {o.measure_type for o in recommendation.options} & {
|
||||
"external_wall_insulation",
|
||||
"internal_wall_insulation",
|
||||
}
|
||||
|
||||
# Assert — the solid-wall generator is wired into the candidate pool, so a
|
||||
# solid-wall Option reaches the optimised package.
|
||||
measure_types = {measure.measure_type for measure in plan.measures}
|
||||
assert measure_types & {"external_wall_insulation", "internal_wall_insulation"}
|
||||
# And Modelling still produces a plan end to end (now ASHP-led).
|
||||
plan = run_modelling(epc, goal_band="C", print_table=False)
|
||||
assert len(plan.measures) >= 1
|
||||
|
||||
|
||||
def test_run_modelling_listed_building_yields_no_wall_insulation() -> None:
|
||||
|
|
@ -195,12 +205,19 @@ def test_run_modelling_recommends_hhr_storage_for_an_electric_dwelling() -> None
|
|||
# heating generator is wired into the candidate pool (ADR-0024).
|
||||
epc: EpcPropertyData = _electric_storage_lit_epc()
|
||||
|
||||
# Act — Modelling only, no database.
|
||||
plan = run_modelling(epc, goal_band="C", print_table=False)
|
||||
# Assert — the heating generator is wired in and OFFERS the HHR storage
|
||||
# bundle for an electric dwelling. The Optimiser now selects the ASHP bundle
|
||||
# instead — the efficient Vaillant (ADR-0025) beats resistance storage on SAP
|
||||
# — so HHR is offered-but-not-selected; assert it is OFFERED to preserve the
|
||||
# wiring check, and that the optimised package leads with ASHP.
|
||||
recommendation = recommend_heating(epc, ProductJsonRepository(DEFAULT_CATALOGUE))
|
||||
assert recommendation is not None
|
||||
assert "high_heat_retention_storage_heaters" in {
|
||||
o.measure_type for o in recommendation.options
|
||||
}
|
||||
|
||||
# Assert — the HHR storage bundle reaches the optimised package.
|
||||
measure_types = {measure.measure_type for measure in plan.measures}
|
||||
assert "high_heat_retention_storage_heaters" in measure_types
|
||||
plan = run_modelling(epc, goal_band="C", print_table=False)
|
||||
assert "air_source_heat_pump" in {m.measure_type for m in plan.measures}
|
||||
|
||||
|
||||
def test_sample_catalogue_prices_every_generator_measure_type() -> None:
|
||||
|
|
|
|||
|
|
@ -77,33 +77,26 @@ def test_each_fired_measure_carries_the_attributes_that_triggered_it() -> None:
|
|||
# Assert — the Plan ran and every fired measure names its trigger fields.
|
||||
assert report.plan is not None
|
||||
assert report.plan_error is None
|
||||
# The efficient representative heat pump (Vaillant aroTHERM plus 5 kW,
|
||||
# ADR-0025) raises SAP enough on this gas dwelling that ASHP + solid-floor
|
||||
# insulation reach the target band on their own, displacing the fabric stack
|
||||
# the Optimiser used to assemble (ADR-0024 — ASHP is now a strong candidate).
|
||||
triggers: dict[str, MeasureTrigger] = _triggers_by_measure(report)
|
||||
assert set(triggers) == {
|
||||
"cavity_wall_insulation",
|
||||
"mechanical_ventilation",
|
||||
"air_source_heat_pump",
|
||||
"solid_floor_insulation",
|
||||
"low_energy_lighting",
|
||||
}
|
||||
# Cavity fill fired because the main wall is an uninsulated cavity.
|
||||
assert triggers["cavity_wall_insulation"].triggers == {
|
||||
"wall_construction": 4,
|
||||
"wall_insulation_type": 4,
|
||||
}
|
||||
# Ventilation fired because the dwelling lodges no mechanical kind.
|
||||
assert triggers["mechanical_ventilation"].triggers == {
|
||||
"mechanical_ventilation_kind": None,
|
||||
# ASHP fired because the dwelling is a non-flat house not already a heat pump
|
||||
# (eligibility is physical/planning only — ADR-0024).
|
||||
assert triggers["air_source_heat_pump"].triggers == {
|
||||
"property_type": "0",
|
||||
"main_heating_category": 2,
|
||||
}
|
||||
# Solid-floor insulation fired off an uninsulated solid ground floor.
|
||||
assert triggers["solid_floor_insulation"].triggers == {
|
||||
"floor_insulation_thickness": None,
|
||||
"floor_construction_type": "Solid",
|
||||
}
|
||||
# The LED upgrade fired because the dwelling lodges 10 low-energy-unknown bulbs.
|
||||
assert triggers["low_energy_lighting"].triggers == {
|
||||
"incandescent_fixed_lighting_bulbs_count": 0,
|
||||
"cfl_fixed_lighting_bulbs_count": None,
|
||||
"low_energy_fixed_lighting_bulbs_count": 10,
|
||||
}
|
||||
|
||||
|
||||
def test_few_measure_cert_surfaces_only_its_fired_measures_triggers() -> None:
|
||||
|
|
|
|||
|
|
@ -351,6 +351,10 @@ def test_modelling_optimises_and_persists_a_multi_measure_plan(
|
|||
# The sample EPC lodges 8 low-energy-unknown bulbs, so the LED upgrade is
|
||||
# a cheap positive-SAP candidate the Optimiser also keeps (ADR-0023).
|
||||
"low_energy_lighting",
|
||||
# The efficient representative heat pump (Vaillant aroTHERM plus 5 kW,
|
||||
# ADR-0025) now raises SAP even on this gas dwelling, so the Optimiser
|
||||
# also keeps the ASHP bundle in the least-cost-to-band package (ADR-0024).
|
||||
"air_source_heat_pump",
|
||||
}
|
||||
# Each persisted measure carries the catalogue id of the Product it installs
|
||||
# (the MaterialRow ids seeded above), replacing the retired
|
||||
|
|
@ -641,16 +645,24 @@ def test_listed_uprn_ingested_blocks_solid_wall_insulation_in_modelling(
|
|||
fuel_rates=FuelRatesStaticFileRepository(),
|
||||
).run(property_ids=[40, 41], scenario_ids=[7], portfolio_id=1)
|
||||
|
||||
# Assert — the listed dwelling gets no wall insulation (both Options blocked);
|
||||
# the unrestricted dwelling gets one. The only difference between them is the
|
||||
# planning status Ingestion cached, proving the loop end to end (ADR-0019/0020).
|
||||
_WALL_TYPES = {"external_wall_insulation", "internal_wall_insulation"}
|
||||
# Assert — a listed building blocks the fabric-protected measures: both
|
||||
# solid-wall Options AND the ASHP bundle (all gated on `blocks_internal`,
|
||||
# ADR-0024). So the listed dwelling gets neither, while the unrestricted one
|
||||
# gets the ASHP bundle (which the efficient Vaillant now makes the Optimiser
|
||||
# select — ADR-0025, so walls are no longer needed to reach the band). The
|
||||
# only difference between them is the planning status Ingestion cached,
|
||||
# proving the gate end to end (ADR-0019/0020/0024).
|
||||
_PROTECTED_TYPES = {
|
||||
"external_wall_insulation",
|
||||
"internal_wall_insulation",
|
||||
"air_source_heat_pump",
|
||||
}
|
||||
with Session(db_engine) as session:
|
||||
listed_types = _plan_measure_types(session, property_id=40)
|
||||
unrestricted_types = _plan_measure_types(session, property_id=41)
|
||||
|
||||
assert _WALL_TYPES.isdisjoint(listed_types)
|
||||
assert _WALL_TYPES & unrestricted_types
|
||||
assert _PROTECTED_TYPES.isdisjoint(listed_types)
|
||||
assert "air_source_heat_pump" in unrestricted_types
|
||||
|
||||
|
||||
def _plan_measure_types(session: Session, *, property_id: int) -> set[str]:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue