SAFETY-126 round 2: state what the 10 W tolerance costs; test the latch reset
Text only (DECISIONS, DOCS, CHANGELOG): a step load alarms on the same cycle as before; a steadily rising load alarms a little later; with peak_margin_w 0 a period can end up to 10 W over the limit without this alarm, as for 'Load too high' (SAFETY-81). Owner accepted 2026-10-31. test_safety126.py part 2: after a real overrun ends, _peak_landing()'s latch is reset and the night's later landings are quiet (review F3). Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
8e2463bc32
commit
a66a7f1a0f
@@ -4,7 +4,7 @@
|
||||
|
||||
### Not in a release yet
|
||||
|
||||
- The night grid charge no longer raises "Battery stopped limiting the peak" when the 15-minute period stays on the aim ([[SAFETY-126]]).
|
||||
- The night grid charge no longer raises "Battery stopped limiting the peak" when the 15-minute period stays on the aim. A steadily rising load now alarms a little later, and with a peak margin of 0 a period can end up to 10 W over the limit without this alarm ([[SAFETY-126]]).
|
||||
|
||||
**A patch bump: one fix for charging from the grid under the peak limit, and no option changes.**
|
||||
No option was added, removed or renamed, and no default changed (86 options).
|
||||
|
||||
@@ -25,16 +25,21 @@ no "Battery stopped limiting the peak" event hint. The claim, the floor and the
|
||||
commands are unchanged.
|
||||
|
||||
**Still alarms.** House load the batteries cannot cover under the reserve:
|
||||
the period would end over the aim with the batteries at 0 W, so the alarm
|
||||
fires on the same cycle as before (both under the reserve; a 6 500 W house
|
||||
during the night charge). SAFETY-87's case (others able, graded on the limit
|
||||
for `ALARM_HOLD_S`) and SAFETY-81's 'Load too high' notification are unchanged.
|
||||
A period heading at most 10 W over the aim no longer alarms with nothing able
|
||||
to give. That is SAFETY-81's landing tolerance. `misses_aim()` takes the house
|
||||
as meter plus batteries, so an export nets off. `decide()`'s projection counts
|
||||
offtake only. So a re-alarm while the loop's draw on held batteries covers the
|
||||
house with some export, and the period lands under the aim, is gone too
|
||||
(test_safety126.py part 2, modes off: 2 notifications, was 4).
|
||||
the period would end over the aim with the batteries at 0 W. A step load
|
||||
alarms on the same cycle as before. SAFETY-87's case and SAFETY-81's 'Load too
|
||||
high' notification are unchanged.
|
||||
|
||||
**What the 10 W tolerance costs** (review, owner accepted 2026-10-31):
|
||||
- A steadily rising load alarms a little later, once the projected miss passes
|
||||
10 W (3 000 -> 4 000 W over 45 min: 56 s later). With the default 50 W
|
||||
margin the alarm still comes before the limit.
|
||||
- With `peak_margin_w` 0, a period can end up to 10 W over the limit without
|
||||
this alarm, the tolerance 'Load too high' already accepts (SAFETY-81).
|
||||
|
||||
`misses_aim()` takes the house as meter plus batteries, so an export nets
|
||||
off (`decide()`'s projection counts offtake only). A re-alarm while the loop's
|
||||
draw on held batteries covers the house is gone too (test_safety126.py part 2,
|
||||
modes off: 2 notifications, was 4).
|
||||
|
||||
**Not changed.** The one-cycle floor itself: at a period's last instant the
|
||||
guard still asks an able battery for its rating, or stops a charge, for a
|
||||
|
||||
@@ -3458,7 +3458,7 @@ Charging from the grid stays blocked meanwhile. With several inverters the
|
||||
notification names the battery at its limit (SAFETY-32), each graded against
|
||||
its own cut-out level. That battery is held (weight 0 while the site gives)
|
||||
and the guard's need goes to the others, up to their rating; the alarm fires
|
||||
only if they miss the limit at full power for `ALARM_HOLD_S` (SAFETY-87). With no battery able to give it fires at once, but only if the period would end more than 10 W over the aim with the batteries at 0 W. A grid charge the add-on runs counts as one it can stop, and the last second's landing error on the aim is not an alarm (SAFETY-126). ⚠️ **The reserve binds the peak guard only
|
||||
only if they miss the limit at full power for `ALARM_HOLD_S` (SAFETY-87). With no battery able to give it fires at once, but only if the period would end more than 10 W over the aim with the batteries at 0 W. A grid charge the add-on runs counts as one it can stop, and the last second's landing error on the aim is not an alarm. A steadily rising load therefore alarms a little later. With `peak_margin_w` 0 a period can end up to 10 W over the limit without this alarm, as for “Load too high” (SAFETY-126). ⚠️ **The reserve binds the peak guard only
|
||||
(SAFETY-25).** Self-consumption has no such floor. See *What `peak_soc_floor`
|
||||
does not protect*.
|
||||
|
||||
|
||||
@@ -18,8 +18,9 @@ quarter's edge as on the rig.
|
||||
red peak row, no "Why" peak, every quarter on the aim; the guard did want
|
||||
power at the quarter's end (not vacuous); with the fix reverted the same
|
||||
run alarms. The commands are the same with and without the fix.
|
||||
2. A real overrun under the reserve still alarms, at the same cycle as
|
||||
before, and names both batteries.
|
||||
2. A real overrun under the reserve still alarms, a step load at the same
|
||||
cycle as before, and names both batteries. After it, the latch is reset:
|
||||
the night's later landings are quiet again.
|
||||
3. `misses_aim()` on its own.
|
||||
4. The page and the "Why" on a decision within the aim.
|
||||
|
||||
@@ -345,6 +346,48 @@ for tag, onset, kw in (
|
||||
any(s[4] for s in head["seen"]))
|
||||
|
||||
|
||||
def burst(t):
|
||||
"""600 W, a 6 500 W load from 20 to 25 minutes, then 600 W again while
|
||||
the night charge goes on."""
|
||||
return 6500.0 if 1200 <= t < 1500 else 600.0
|
||||
|
||||
|
||||
# F3 (review): `_peak_landing()` resets its latch when the guard lets go, so
|
||||
# a real overrun does not keep the later landing errors alarming.
|
||||
_bh = drive(opts(), house=burst, off=0.99, minutes=61)
|
||||
_bb = without_fix(lambda: drive(opts(), house=burst, off=0.99,
|
||||
minutes=61))
|
||||
show("head real overrun, then the night again", _bh)
|
||||
show("fix reverted", _bb)
|
||||
# The burst banked a new month peak, which the later quarters stay under.
|
||||
_new_peak = _bh["c"].peak_guard.ceiling_w
|
||||
|
||||
|
||||
def _late(r):
|
||||
"""From the next quarter on: the overrun's own quarter ends over."""
|
||||
return [x for x in r["seen"] if x[0] >= 1800]
|
||||
|
||||
|
||||
print(f" notifications at {[t for t, _n in _bh['notes']]} s; with the fix "
|
||||
f"reverted {[t for t, _n in _bb['notes']]} s; limit after the burst "
|
||||
f"{_new_peak:.0f} W")
|
||||
check("⭐ after a real overrun ends, the latch is reset: the alarm came "
|
||||
"during the overrun, and the later quarters' landings are quiet "
|
||||
"(no notification, no 'Why' peak, no red row) and under the new peak",
|
||||
any(1200 <= t < 1500 for t, _n in _bh["notes"])
|
||||
and not [t for t, _n in _bh["notes"] if t >= 1800]
|
||||
and not any(x[3] or x[4] or x[5] for x in _late(_bh))
|
||||
and present(nth([v for k, v in _bh["quarters"].items()
|
||||
if k >= "22:30"], 0))
|
||||
and all(v <= _new_peak for k, v in _bh["quarters"].items()
|
||||
if k >= "22:30"))
|
||||
check("...not vacuous: the guard wanted power at those landings, and with "
|
||||
"the fix reverted they show the 'Why' peak or an alarm",
|
||||
any(x[2] for x in _late(_bh))
|
||||
and (any(x[3] or x[4] or x[5] for x in _late(_bb))
|
||||
or bool([t for t, _n in _bb["notes"] if t >= 1800])))
|
||||
|
||||
|
||||
# ============================================================================ #
|
||||
print("\n3. misses_aim() on its own")
|
||||
START = datetime(2026, 10, 30, 21, 15, tzinfo=timezone.utc)
|
||||
|
||||
Reference in New Issue
Block a user