diff --git a/orchestration/modelling_orchestrator.py b/orchestration/modelling_orchestrator.py index 69e88feac..ed324e975 100644 --- a/orchestration/modelling_orchestrator.py +++ b/orchestration/modelling_orchestrator.py @@ -491,10 +491,17 @@ def _objective_for( def _target_sap(scenario: Scenario) -> Optional[float]: """The SAP rating the Optimiser repairs toward — the floor of the goal - band for an INCREASING_EPC goal, else None (no SAP target).""" + band for an INCREASING_EPC goal, else None (no SAP target). + + On the **continuous** scale, because that is the currency every figure the + Optimiser compares it against is in. The dwelling is in the goal band once + its *published* rating is, which happens half a point below the integer + floor — target the integer and a dwelling publishing at the floor reads as a + fraction short, and the Optimiser buys works toward a band it is already in + (#1654).""" if scenario.goal != PortfolioGoal.INCREASING_EPC.value: return None - return float(Epc(scenario.goal_value).sap_lower_bound()) + return Epc(scenario.goal_value).sap_lower_bound_continuous() def _best_practice_key(option: MeasureOption) -> int: diff --git a/tests/orchestration/test_modelling_goal_band_floor.py b/tests/orchestration/test_modelling_goal_band_floor.py index 3238fa477..63210345b 100644 --- a/tests/orchestration/test_modelling_goal_band_floor.py +++ b/tests/orchestration/test_modelling_goal_band_floor.py @@ -40,3 +40,18 @@ def test_dwelling_published_at_the_goal_band_is_sold_no_works() -> None: # Assert — nothing to buy: the goal is already met. assert plan.measures == () assert plan.cost_of_works == 0.0 + + +def test_dwelling_below_the_goal_band_is_still_taken_to_it() -> None: + # Arrange — the same cert with an uninsulated loft scores a continuous + # 37.349, published SAP 37: band F, genuinely short of band D. Lowering the + # target by half a point must not let the package stop short of the band. + below_band_d = _dwelling("loft_001431_before.pdf") + + # Act + plan: Plan = run_modelling(below_band_d, goal_band="D", print_table=False) + + # Assert — real works, and they carry the dwelling into band D. + assert plan.measures != () + assert plan.cost_of_works > 0.0 + assert plan.post_retrofit.sap_continuous >= 54.5