Goal-objective test uses the shared fixtures and a real boiler code 🟪

Review findings on PR #1527:

- The overlay constants, ScoredOption builder, ventilation dependency and
  selected_types helper come from the shared _optimiser_fixtures module
  (landed on the fabric-first base) instead of local copies; the boiler
  overlay is the shared BOILER_OVERLAY (SAP Table 4a code 104, a mains-gas
  combi) rather than code 201, which is neither a boiler nor a heat pump.
  _IWI_OVERLAY (solid-wall internal, type 3) stays local — no shared
  equivalent — and the carbon stubs stay bespoke (the shared StubScorer has
  no CO2 knob).
- The optimise_package_fabric_first import is lifted to module scope.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-10 11:17:27 +00:00
parent 98d2f7aa16
commit 7996eedf55

View file

@ -16,46 +16,25 @@ from datatypes.epc.domain.epc_property_data import (
)
from domain.modelling.measure_type import MeasureType
from domain.modelling.optimisation.optimiser import (
MeasureDependency,
OptimisedPackage,
ScoredOption,
optimise_package,
optimise_package_fabric_first,
)
from domain.modelling.recommendation import Cost, MeasureOption
from domain.modelling.scoring.package_scorer import Score
from domain.modelling.simulation import (
BuildingPartOverlay,
EpcSimulation,
HeatingOverlay,
VentilationOverlay,
from domain.modelling.simulation import BuildingPartOverlay, EpcSimulation
from tests.domain.modelling._optimiser_fixtures import (
ASHP_OVERLAY,
BOILER_OVERLAY,
WALL_OVERLAY,
scored_option,
selected_types,
ventilation_dependency,
)
from tests.domain.sap10_calculator.worksheet._elmhurst_worksheet_000490 import (
build_epc,
)
_WALL_OVERLAY = EpcSimulation(
building_parts={
BuildingPartIdentifier.MAIN: BuildingPartOverlay(wall_insulation_type=2)
}
)
_VENT_OVERLAY = EpcSimulation(
ventilation=VentilationOverlay(mechanical_ventilation_kind="EXTRACT_OR_PIV_OUTSIDE")
)
def _scored(
measure_type: str, *, gain: float, cost: float, overlay: EpcSimulation
) -> ScoredOption:
return ScoredOption(
option=MeasureOption(
measure_type=MeasureType(measure_type),
description=measure_type,
overlay=overlay,
cost=Cost(total=cost, contingency_rate=0.0),
),
sap_gain=gain,
)
class _CarbonScorer:
"""A stub where the wall is a small carbon win (20 kg/yr) and a large SAP
@ -89,19 +68,10 @@ def test_dependency_signals_are_priced_in_the_objective_currency() -> None:
# pricing the ventilation's 30 SAP would outweigh the wall's +20 signal
# and the package would collapse to nothing.
groups: list[list[ScoredOption]] = [
[_scored("cavity_wall_insulation", gain=20.0, cost=1000.0, overlay=_WALL_OVERLAY)],
[scored_option("cavity_wall_insulation", gain=20.0, cost=1000.0, overlay=WALL_OVERLAY)],
]
dependency = MeasureDependency(
triggers=frozenset({MeasureType.CAVITY_WALL_INSULATION}),
required=ScoredOption(
option=MeasureOption(
measure_type=MeasureType.MECHANICAL_VENTILATION,
description="mechanical_ventilation",
overlay=_VENT_OVERLAY,
cost=Cost(total=300.0, contingency_rate=0.0),
),
sap_gain=0.0,
),
dependency = ventilation_dependency(
cost=300.0, triggers=frozenset({MeasureType.CAVITY_WALL_INSULATION})
)
# Act — a Reducing-CO2 brief: maximise carbon reduction within budget.
@ -117,22 +87,21 @@ def test_dependency_signals_are_priced_in_the_objective_currency() -> None:
# Assert — the wall survives with its ventilation: the dependency is worth
# 0 kg CO2, not 30 SAP, so the package is a net +20 kg saving.
assert {s.option.measure_type for s in package.selected} == {
assert selected_types(package.selected) == {
"cavity_wall_insulation",
"mechanical_ventilation",
}
assert abs(package.score.co2_kg_per_yr - 480.0) <= 1e-9
# Internal wall insulation — a distinct fabric overlay so the fabric-first
# phase-1 pick is unambiguous. No shared fixture (the shared WALL_OVERLAY is a
# cavity fill, type 2); this is a solid-wall internal treatment, type 3.
_IWI_OVERLAY = EpcSimulation(
building_parts={
BuildingPartIdentifier.MAIN: BuildingPartOverlay(wall_insulation_type=3)
}
)
_BOILER_OVERLAY = EpcSimulation(heating=HeatingOverlay(sap_main_heating_code=201))
_ASHP_OVERLAY = EpcSimulation(
heating=HeatingOverlay(main_heating_index_number=13000)
)
class _CarbonHeatingScorer:
@ -166,15 +135,11 @@ def test_fabric_first_phase_two_rescores_in_the_objective_currency() -> None:
# Arrange — a fabric-first Reducing-CO2 brief. Phase 1 commits the wall;
# phase 2 must choose the heating on its post-fabric *carbon* worth, not
# its SAP worth. Signals are supplied in kg CO2 saved (the caller's job).
from domain.modelling.optimisation.optimiser import (
optimise_package_fabric_first,
)
groups: list[list[ScoredOption]] = [
[_scored("internal_wall_insulation", gain=10.0, cost=1000.0, overlay=_IWI_OVERLAY)],
[scored_option("internal_wall_insulation", gain=10.0, cost=1000.0, overlay=_IWI_OVERLAY)],
[
_scored("gas_boiler_upgrade", gain=5.0, cost=2000.0, overlay=_BOILER_OVERLAY),
_scored("air_source_heat_pump", gain=50.0, cost=6000.0, overlay=_ASHP_OVERLAY),
scored_option("gas_boiler_upgrade", gain=5.0, cost=2000.0, overlay=BOILER_OVERLAY),
scored_option("air_source_heat_pump", gain=50.0, cost=6000.0, overlay=ASHP_OVERLAY),
],
]
@ -190,7 +155,7 @@ def test_fabric_first_phase_two_rescores_in_the_objective_currency() -> None:
# Assert — the wall plus the heat pump (50 kg), not the SAP-favoured
# boiler; the truthful package carbon is 500 10 50 = 440.
assert {s.option.measure_type for s in package.selected} == {
assert selected_types(package.selected) == {
"internal_wall_insulation",
"air_source_heat_pump",
}