mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
50 lines
2 KiB
Python
50 lines
2 KiB
Python
"""Scenario — the named retrofit brief the Modelling stage scores against.
|
|
|
|
Built by a user in the scenario-builder UI and persisted before any modelling
|
|
fires; the pipeline is handed only its id and reads it back via a
|
|
`ScenarioRepository`. This is the thin slice the Modelling stage uses today:
|
|
the goal + budget that the Optimiser will consume (#1160) and `is_default`
|
|
(which drives `plan.is_default`). The legacy file-path / portfolio-aggregate
|
|
columns are not modelled. Carries no phases — multi-phase is deferred
|
|
(ADR-0005). See CONTEXT.md.
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
|
|
from domain.modelling.measure_type import MeasureType
|
|
|
|
_NO_EXCLUSIONS: frozenset[MeasureType] = frozenset()
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Scenario:
|
|
"""A retrofit brief: its goal, optional budget, and whether it is the
|
|
Property's default Scenario. `goal` / `goal_value` are the lodged target
|
|
(e.g. "INCREASING_EPC" → band "C"); carried for the Optimiser, not yet
|
|
enforced.
|
|
|
|
`exclusions` are the measure types the brief bars from the run (the only
|
|
measure-scoping the live ``scenario`` table persists — there is no
|
|
inclusions column). Empty means nothing is barred.
|
|
|
|
`fabric_first` constrains the Optimiser to treat the building envelope
|
|
first: fabric measures are optimised with the full budget, and heating /
|
|
renewables are only considered on top of the committed fabric when the
|
|
fabric alone does not meet the brief's target."""
|
|
|
|
id: int
|
|
goal: str
|
|
goal_value: str
|
|
budget: Optional[float]
|
|
is_default: bool
|
|
exclusions: frozenset[MeasureType] = _NO_EXCLUSIONS
|
|
fabric_first: bool = False
|
|
|
|
def considered_measures(self) -> Optional[frozenset[MeasureType]]:
|
|
"""The measure-type allowlist the Scenario's exclusions imply: every
|
|
modelled measure minus the excluded ones, or None (consider every
|
|
measure) when nothing is excluded."""
|
|
if not self.exclusions:
|
|
return None
|
|
return frozenset(MeasureType) - self.exclusions
|