Two tests were spinning up real CBC solves over 96-half-hour windows with long runs of exactly-repeated prices (e.g. [0.0] * 40). That gives the LP relaxation a huge set of economically indistinguishable ways to spread a trade, which the MILP fallback's branch-and-bound then wastes enormous effort disambiguating (47k+ nodes without closing the gap, confirmed by running CBC verbosely). Real Attachment 2 data has no such flat runs and solves in ~0.1s/window; a synthetic sine wiggle wasn't enough either, since neighbouring half-hours stayed too similar. Fixes, matched to what each test actually needs: - The 9 validator tests only need *some* structurally valid schedule to mutate; they were deriving it by running the real optimiser once per test. Replaced with make_valid_schedule(), built directly from the model's own energy-balance formulas -- no solver involved, and it's now a true unit test of validate_schedule() in isolation. - The rolling-horizon carry-forward test was exercising the mechanism at full production scale (48h window / 24h commit) when a 2h/1h window proves the same boundary-carrying behaviour with a trivial MILP, regardless of price structure. - Fixed a genuine tie in test_optimum_uses_whichever_market_pays_more: two equal-price hours with just enough stored energy for one meant either market was a valid optimum. Sized the charge phase so delivery must split across both hours, pinning a unique answer. Full suite: 38 passed in ~1.3s (previously hung indefinitely on CI and locally within seconds of the same wall-clock variance CBC shows on degenerate MIPs). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,22 +5,17 @@ from __future__ import annotations
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from conftest import prices_frame
|
||||
from conftest import make_valid_schedule
|
||||
|
||||
from battery_dispatch.config import RunConfig
|
||||
from battery_dispatch.optimiser import run_rolling_horizon
|
||||
from battery_dispatch.validation import validate_schedule
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def schedule(spec) -> pd.DataFrame:
|
||||
"""A genuine, valid two-day schedule to mutate in each test."""
|
||||
prices = prices_frame(
|
||||
[0.0] * 20 + [90.0] * 8 + [10.0] * 20 + [5.0] * 16 + [120.0] * 8 + [40.0] * 24,
|
||||
[30.0] * 48,
|
||||
)
|
||||
result, _, _ = run_rolling_horizon(prices, spec, RunConfig())
|
||||
return result
|
||||
"""A genuine, valid schedule to mutate in each test. See
|
||||
``conftest.make_valid_schedule`` for why this isn't a real optimiser run.
|
||||
"""
|
||||
return make_valid_schedule(spec)
|
||||
|
||||
|
||||
def test_a_valid_schedule_passes_every_check(schedule, spec):
|
||||
|
||||
Reference in New Issue
Block a user