diff --git a/domain/modelling/optimisation/optimiser.py b/domain/modelling/optimisation/optimiser.py index aa220dc07..43a87dfce 100644 --- a/domain/modelling/optimisation/optimiser.py +++ b/domain/modelling/optimisation/optimiser.py @@ -272,7 +272,7 @@ def optimise_package_fabric_first( None if budget is None else budget - _package_cost(fabric_package.selected) ) top_up: OptimisedPackage = optimise_package( - groups=remaining_groups, + groups=_rescored_groups(remaining_groups, post_fabric_scorer, baseline_epc), scorer=post_fabric_scorer, baseline_epc=baseline_epc, budget=leftover_budget, @@ -293,6 +293,31 @@ def _is_fabric_group(group: list[ScoredOption]) -> bool: ) +def _rescored_groups( + groups: list[list[ScoredOption]], + scorer: Scorer, + baseline_epc: EpcPropertyData, +) -> list[list[ScoredOption]]: + """The groups with every Option's role-1 warm-start signal re-scored + through ``scorer`` — for phase 2, its independent gain on the post-fabric + dwelling rather than the raw baseline, so options whose worth changes once + the envelope is treated (a boiler on an insulated home) are re-ranked.""" + start_sap: float = scorer.score(baseline_epc, []).sap_continuous + return [ + [ + ScoredOption( + option=scored.option, + sap_gain=scorer.score( + baseline_epc, [scored.option.overlay] + ).sap_continuous + - start_sap, + ) + for scored in group + ] + for group in groups + ] + + class _PrefixedScorer: """A Scorer view of the dwelling with a committed package already applied: every score call sees the ``prefix`` overlays before the candidate's own.