mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
Add domain/modelling/considered_measures.py::restrict_to_considered_measures —
the pure allowlist that limits a run to a chosen set of MeasureType (mirroring
the legacy engine's `inclusions`). It filters at the Option level, so a
multi-option Recommendation (e.g. Heating & Hot Water competing HHRSH against
an ASHP bundle) is kept with only its allowed Options; a Recommendation left
with none is dropped. None = consider everything (unrestricted default).
Thread `considered_measures: frozenset[MeasureType] | None` through
ModellingOrchestrator.run -> _plan_for -> _scored_candidate_groups /
_candidate_recommendations (applies the filter) and _measure_dependencies
(suppresses a forced dependency whose required measure is outside the
allowlist, so a restricted run forces nothing it is not considering). The
local-run seam (harness.console.run_modelling) gains the same param.
The Optimiser still freely chooses among survivors — including none. Tests:
the pure filter (3 cases) + an orchestrator-seam test proving a
{solar_pv}-restricted run yields only solar_pv options. 257 pass + 3 xfail;
pyright clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
83 lines
2.8 KiB
Python
83 lines
2.8 KiB
Python
"""Slice 3 — `restrict_to_considered_measures`, the pure allowlist that limits a
|
|
run to a chosen set of measure types (mirroring the legacy engine's
|
|
`inclusions`).
|
|
|
|
It filters at the Option level, so a multi-option Recommendation (e.g. Heating &
|
|
Hot Water offering both HHRSH and an ASHP bundle) is kept with only its allowed
|
|
Options; a Recommendation left with no allowed Option is dropped entirely. A
|
|
None allowlist means "consider everything" (today's unrestricted behaviour).
|
|
"""
|
|
|
|
from domain.modelling.considered_measures import restrict_to_considered_measures
|
|
from domain.modelling.measure_type import MeasureType
|
|
from domain.modelling.recommendation import MeasureOption, Recommendation
|
|
from domain.modelling.simulation import EpcSimulation
|
|
|
|
|
|
def _option(measure_type: MeasureType) -> MeasureOption:
|
|
return MeasureOption(
|
|
measure_type=measure_type, description=str(measure_type), overlay=EpcSimulation()
|
|
)
|
|
|
|
|
|
def _heating_rec() -> Recommendation:
|
|
# Heating & Hot Water competes HHRSH against an ASHP bundle in one rec.
|
|
return Recommendation(
|
|
surface="Heating & Hot Water",
|
|
options=(
|
|
_option(MeasureType.HIGH_HEAT_RETENTION_STORAGE_HEATERS),
|
|
_option(MeasureType.AIR_SOURCE_HEAT_PUMP),
|
|
),
|
|
)
|
|
|
|
|
|
def _solar_rec() -> Recommendation:
|
|
return Recommendation(surface="Solar PV", options=(_option(MeasureType.SOLAR_PV),))
|
|
|
|
|
|
def _wall_rec() -> Recommendation:
|
|
return Recommendation(
|
|
surface="Wall", options=(_option(MeasureType.CAVITY_WALL_INSULATION),)
|
|
)
|
|
|
|
|
|
def test_none_allowlist_keeps_everything() -> None:
|
|
# Arrange
|
|
recommendations = [_heating_rec(), _solar_rec(), _wall_rec()]
|
|
|
|
# Act
|
|
kept = restrict_to_considered_measures(recommendations, None)
|
|
|
|
# Assert
|
|
assert kept == recommendations
|
|
|
|
|
|
def test_drops_recommendations_with_no_allowed_option() -> None:
|
|
# Arrange
|
|
considered = frozenset(
|
|
{MeasureType.HIGH_HEAT_RETENTION_STORAGE_HEATERS, MeasureType.SOLAR_PV}
|
|
)
|
|
|
|
# Act
|
|
kept = restrict_to_considered_measures(
|
|
[_heating_rec(), _solar_rec(), _wall_rec()], considered
|
|
)
|
|
|
|
# Assert — the wall rec is gone; heating + solar survive.
|
|
surfaces = {rec.surface for rec in kept}
|
|
assert surfaces == {"Heating & Hot Water", "Solar PV"}
|
|
|
|
|
|
def test_filters_options_within_a_kept_recommendation() -> None:
|
|
# Arrange — HHRSH is allowed but the competing ASHP bundle is not.
|
|
considered = frozenset(
|
|
{MeasureType.HIGH_HEAT_RETENTION_STORAGE_HEATERS, MeasureType.SOLAR_PV}
|
|
)
|
|
|
|
# Act
|
|
kept = restrict_to_considered_measures([_heating_rec()], considered)
|
|
|
|
# Assert — the heating rec keeps ONLY its HHRSH option, ASHP is dropped.
|
|
assert len(kept) == 1
|
|
kept_types = [option.measure_type for option in kept[0].options]
|
|
assert kept_types == [MeasureType.HIGH_HEAT_RETENTION_STORAGE_HEATERS]
|