Release 0.20.0: release/1.0 -> master #227

Merged
admin merged 63 commits from release/1.0 into master 2026-11-01 18:58:05 +01:00
2 changed files with 31 additions and 3 deletions
Showing only changes of commit 4fde0b177c - Show all commits
+25 -1
View File
@@ -672,9 +672,24 @@ check("Cheap hours with a sun forecast charges as with none (P3)",
and top > 80.0)
# The forecast goes away mid-run: Leftover solar stops at once (P3 option A).
# OPT-27: a sensor that is unavailable is covered by its last fresh reading
# for up to 3 h (`solarfc.Hold`), so "lost" here is a hold that never covers;
# the next scenario is the same loss with the hold (test_opt27.py).
DROP = UTC0 + timedelta(hours=1, minutes=30)
class _NoHold(solarfc.Hold):
def stand_in(self, *a, **k):
return None
def seed_profile_no_hold(c):
seed_profile(c)
c.fc_hold = _NoHold()
rows, st_, c = run_day(SF_LS, fc=lambda now: 1.0 if now < DROP else None,
soc0={"a": 60.0, "b": 10.0})
soc0={"a": 60.0, "b": 10.0}, patch=seed_profile_no_hold)
invariants("forecast lost mid-run", rows, c)
before = [r for r in rows if r["now"] < DROP and r["action"]
== arbitrage.CHARGE]
@@ -682,6 +697,15 @@ late = [r for r in rows if r["now"] > DROP + timedelta(
seconds=2 * 60 + 10) and r["action"] == arbitrage.CHARGE]
check("forecast lost mid-run: Leftover solar was charging, and stops within "
"a forecast read (60 s) and a cycle", before and not late)
rows, st_, c = run_day(SF_LS, fc=lambda now: 1.0 if now < DROP else None,
soc0={"a": 60.0, "b": 10.0})
invariants("forecast unavailable mid-run, covered", rows, c)
check("forecast unavailable mid-run (OPT-27): the last reading covers it, so "
"Leftover solar goes on charging past the point where it was lost",
[r for r in rows if r["now"] < DROP and r["action"]
== arbitrage.CHARGE]
and [r for r in rows if r["now"] > DROP + timedelta(
seconds=2 * 60 + 10) and r["action"] == arbitrage.CHARGE])
# The forecast is far too high (40 kWh, and there is no sun at all): no grid
# charge, so B covers less of the evening - a money risk, never a safety one.
+6 -2
View File
@@ -100,7 +100,7 @@ check("22:00 with the sensors stale since 17:05: the 17:05 figures are used",
fc is not None and fc.kept and fc.tomorrow.kwh == 12.0
and fc.at == D1_READ and gaps == ())
check("... today has no sun left in it, tomorrow's is counted from 06:00",
fc.between(EVE, at(4, 7, 0)) is not None
fc is not None and fc.between(EVE, at(4, 7, 0)) is not None
and 0.0 < fc.between(EVE, at(4, 7, 0)) < 0.5
and fc.today.kwh == 0.4)
fc2, _w, _g = rs(GONE, GONE, EVE, h)
@@ -146,12 +146,16 @@ check("02:59: tomorrow's 12.0 kWh is now today's, there is no tomorrow",
fmid is not None and fmid.today.kwh == 12.0 and fmid.tomorrow is None
and fmid.kept and fmid.today.day0 == at(4, 0, 0))
check("... its sun is counted on the day it is for (06:00-20:00 on the 4th)",
fmid.between(at(4, 3, 0), at(4, 7, 0)) is not None
fmid is not None and fmid.between(at(4, 3, 0), at(4, 7, 0)) is not None
and fmid.between(at(4, 3, 0), at(4, 7, 0)) > 0.0)
check("03:01 (midnight + 3 h): not used any more",
rs(GONE, GONE, at(4, 3, 1), h2)[0] is None)
check("... and not the next evening either (older than one night)",
rs(GONE, GONE, at(4, 22, 0), h2)[0] is None)
fe = rs(st(0.1, at(4, 21, 50)), GONE, at(4, 22, 0), h2)[0]
check("... the next evening, with today fresh and tomorrow's sensor down, "
"there is no tomorrow: the 4th's figure is not the 5th's",
fe is not None and fe.today.kwh == 0.1 and fe.tomorrow is None)
# Never today's figure for tomorrow.
h4 = solarfc.Hold()
rs(st(3.0, at(3, 17, 0)), st(12.0, at(3, 17, 0)), at(3, 17, 5), h4)