diff --git a/src/pluginbattery/sim.py b/src/pluginbattery/sim.py index 6371014..964baca 100644 --- a/src/pluginbattery/sim.py +++ b/src/pluginbattery/sim.py @@ -238,9 +238,20 @@ def oracle_daily_schedule(df: pd.DataFrame, battery: Battery) -> np.ndarray: # Cost = sum(import_price * gI - export_price * gE). # When import_price > export_price, the LP automatically picks # min(gI, gE) = 0 because keeping both > 0 costs (imp - exp) > 0. + # + # Tiny tie-breakers: when several surplus hours are equally good for + # charging (true whenever export_price is constant and we're capturing + # surplus), the simplex would otherwise pick an arbitrary subset. + # Adding a microscopic penalty proportional to hour-index makes early + # charging strictly cheaper than late charging by a fraction of a + # cent — so the battery visibly fills as soon as surplus appears. + # Symmetric for discharge: prefer late hours (when prices are + # typically highest anyway, but ties happen during evening plateaus). + EPS = 1e-6 + hr = np.arange(m) f = np.concatenate([ - np.zeros(m), # c — already paid for via gI - np.zeros(m), # d — earnings reach us via reduced gI / increased gE + EPS * hr, # c: prefer earlier charging (smaller h ⇒ less cost) + EPS * (m - 1 - hr), # d: prefer later discharging import_price, # pay for imports -export_price, # earn export credit ]) diff --git a/src/pluginbattery/templates/index.html b/src/pluginbattery/templates/index.html index 01dd5e6..dcce9cf 100644 --- a/src/pluginbattery/templates/index.html +++ b/src/pluginbattery/templates/index.html @@ -5,7 +5,7 @@