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
|
### 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.**
|
**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).
|
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.
|
commands are unchanged.
|
||||||
|
|
||||||
**Still alarms.** House load the batteries cannot cover under the reserve:
|
**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
|
the period would end over the aim with the batteries at 0 W. A step load
|
||||||
fires on the same cycle as before (both under the reserve; a 6 500 W house
|
alarms on the same cycle as before. SAFETY-87's case and SAFETY-81's 'Load too
|
||||||
during the night charge). SAFETY-87's case (others able, graded on the limit
|
high' notification are unchanged.
|
||||||
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
|
**What the 10 W tolerance costs** (review, owner accepted 2026-10-31):
|
||||||
to give. That is SAFETY-81's landing tolerance. `misses_aim()` takes the house
|
- A steadily rising load alarms a little later, once the projected miss passes
|
||||||
as meter plus batteries, so an export nets off. `decide()`'s projection counts
|
10 W (3 000 -> 4 000 W over 45 min: 56 s later). With the default 50 W
|
||||||
offtake only. So a re-alarm while the loop's draw on held batteries covers the
|
margin the alarm still comes before the limit.
|
||||||
house with some export, and the period lands under the aim, is gone too
|
- With `peak_margin_w` 0, a period can end up to 10 W over the limit without
|
||||||
(test_safety126.py part 2, modes off: 2 notifications, was 4).
|
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
|
**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
|
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
|
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)
|
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
|
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`
|
(SAFETY-25).** Self-consumption has no such floor. See *What `peak_soc_floor`
|
||||||
does not protect*.
|
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
|
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
|
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.
|
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
|
2. A real overrun under the reserve still alarms, a step load at the same
|
||||||
before, and names both batteries.
|
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.
|
3. `misses_aim()` on its own.
|
||||||
4. The page and the "Why" on a decision within the aim.
|
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"]))
|
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")
|
print("\n3. misses_aim() on its own")
|
||||||
START = datetime(2026, 10, 30, 21, 15, tzinfo=timezone.utc)
|
START = datetime(2026, 10, 30, 21, 15, tzinfo=timezone.utc)
|
||||||
|
|||||||
Reference in New Issue
Block a user