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>
42 lines
1.6 KiB
Python
42 lines
1.6 KiB
Python
"""Restricting a modelling run to a chosen set of measure types.
|
|
|
|
The allowlist a run "considers" — mirroring the legacy engine's `inclusions`
|
|
(`backend/app/plan/schemas.py`). It filters the candidate Recommendations 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 no allowed Option is dropped. The Optimiser still
|
|
freely chooses among what survives — including choosing nothing.
|
|
|
|
A `None` allowlist means "consider every modelled measure" (the unrestricted
|
|
default).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Iterable
|
|
from typing import Optional
|
|
|
|
from domain.modelling.measure_type import MeasureType
|
|
from domain.modelling.recommendation import Recommendation
|
|
|
|
|
|
def restrict_to_considered_measures(
|
|
recommendations: Iterable[Recommendation],
|
|
considered_measures: Optional[frozenset[MeasureType]],
|
|
) -> list[Recommendation]:
|
|
"""Keep only the Options whose measure type is in ``considered_measures``,
|
|
dropping any Recommendation left with none. ``None`` keeps everything."""
|
|
if considered_measures is None:
|
|
return list(recommendations)
|
|
restricted: list[Recommendation] = []
|
|
for recommendation in recommendations:
|
|
kept = tuple(
|
|
option
|
|
for option in recommendation.options
|
|
if option.measure_type in considered_measures
|
|
)
|
|
if kept:
|
|
restricted.append(
|
|
Recommendation(surface=recommendation.surface, options=kept)
|
|
)
|
|
return restricted
|