No single rolling budget met both cases in the sweep (8 false-alarmed on 2 s seed 25 and warned in 1 to 3 of 7 quarters at 15 s). The budget is now 10 cycles up to a 3.5 s cycle, 8 up to 11 s, 6 up to 16 s, 4 beyond, per rolling 15 minutes; 8 while the cycle length is unknown. A stop or a gap over two minutes is not a cycle length. The unsourced "owner accepted N=8" paragraph is removed from DECISIONS. test_safety119 part 4 now runs 2 s seed 25 and shows a fixed budget of 8 warns there; the never case runs at 2 s and checks every other quarter at 15 s. Co-Authored-By: Claude <noreply@anthropic.com>
5.0 KiB
SAFETY-119 - no "not keeping up" when the loop takes the wire back
Status: for review. Tests: test_safety119.py.
Cause. When another owner (the peak guard, a maintenance charge) has set
the wire and the loop wins again, cycle() re-seeds the integrator from that
owner's command, and compute() adds gain * error from a meter that does
not show that command yet. On the TEST-23 rig (SAFETY-118 I-2) the guard
stepped from -2000 W to +429 W with 5 s left. A cycle later the loop's ask
crossed the +2500 W rail and beat the guard's floor, so SAFETY-85 rule 1
counted it as the loop's own bound and raised goodwe_integrator_bound. The
battery gave what it was told, and the cap held.
Decision. After a hand-back, the loop's integrator bound is not counted
until the plant's settle has passed (SHORT_CYCLES cycles and
SHORT_SPAN_S, the window SAFETY-85 grades a battery on). After that it
counts as before. An over-rating (clamped, a bug) on a cycle the loop
decided always counts. No command changes, and no text was added or changed.
A rolling budget (round 3). The settle above is per hand-back, and
losing the wire restarts it. Across all hand-backs the bound is hidden on at
most a budget of cycles in any 15 minutes (ROLLING_BUDGET, ROLLING_S); a
cycle over the budget counts. Round 1 restarted the settle at every
hand-back, so a guard and a charge taking turns hid the bound for good (0
warnings in 2 h where 28 were raised before). Round 2 gave each run of
hand-backs its own budget; at 10 s and 15 s meters each quarter then started
a fresh one and the loop's bound was never counted. A quarter-edge kick is a
few bound cycles in one quarter, a real bound that stays is many: a budget
per 15 minutes tells them apart.
The budget follows the cycle length. 10 cycles up to a 3.5 s cycle, 8 up to 11 s, 6 up to 16 s, 4 beyond. The cycle length is the time between two decided cycles (a stop, or a gap over two minutes, is not one); until it is known, and without a monotonic clock, the budget is 8. Without a monotonic clock the budget never refills.
Why not one number. Measured over 2 h (goodwe-addon-checks:1), the
quarter-edge case (house 800 W, cap 2500 W, no forecast, 30 seeds) and the
case below. No single budget held both at 1 to 15 s: 3 to 6 let the false
alarm back at 2 s; 7 on 10 of 30 seeds at 2 s; 8 on 1 of 30 (seed 25) at
2 s, and warned in only 1 to 3 of 7 quarters at 15 s; 9 and 10 miss quarters
at 10 s. A slow cycle needs a small budget to warn, a fast one a large budget
to stay quiet. Per band, the largest budget that still warns: up to 3.5 s,
10 (9 and 10 had no false alarm at 1 and 2 s; above 10 was not swept); up to
11 s, 8 (9 misses quarters at 10 s); up to 16 s, 6 (7 leaves two quiet
quarters in a row at 15 s, 4 false-alarms at 12 s on 7 of 30 seeds); beyond,
4 (3 false-alarms at 20 and 30 s on 14 of 30, 5 leaves five quiet quarters at
20 s).
What holds. Quarter-edge case, 30 seeds each: 0 notifications at 1, 2, 3, 4, 5, 10, 12, 15, 20 and 30 s. Repeated hand-backs (2 x 2500 W, cheap-window charge, no usable forecast, cap 5000 W, house 5050 and 5100 W, constant and with noise, 8 runs per meter):
| meter | warnings in 2 h | quarters warned (of 7 after the first) | without the fix |
|---|---|---|---|
| 1 s | 56 to 63 | 7 | 56 to 63 |
| 2 s | 49 to 56 | 7 | 54 to 56 |
| 5 s | 19 to 23 | 7 | 28 |
| 10 s | 7 to 10 | 7 | 14 |
| 15 s | 4 to 10 | 4 to 7, never two quiet in a row | 14 |
| 30 s | 3 to 7 | 3 to 5, up to two quiet in a row | 7 to 11 |
At 30 s no budget does better: 4 leaves two quiet quarters, 3 brings back the false alarm. Single hand-back, the loop at its rail, first warning after the hand-back compared with no fix: 10 s at 1 s, 20 s at 2 s, 40 s at 5 s (default), 80 s at 10 s, 90 s at 15 s, 80 s at 20 s, 120 s at 30 s, 240 s at 60 s. At 60 s the rig is a different regime (the house is not over the cap in the case above), so nothing more is claimed there. The cap held at 1 to 10 s in the quarter-edge case and at 1 to 5 s in the case above; at slower meters the meter's lag puts quarters over it, the same with and without the fix (commands are identical).
Kept. The loop in charge at its rail warns on the same cycle as before.
The start and a restart after a stop are not hand-backs. The quarter-edge
kick of test_safety119.py part 1 stays hidden (see above).
Re-pinned. test_safety84 (all six) and test_safety87 (n1_crossing,
n1_above, n1_charge_low): only OUTPUT_CLAMP log rows differ; commands and
claims are the same (test_safety119.py part 6).
Not hidden. A battery that gives less than another owner told it still alarms through SAFETY-85 rule 2. "Always counts" for an over-rating holds on cycles the loop decided; on others SAFETY-85 rule 1 already dropped it.
Not changed. The kick itself is control behaviour: for a few seconds at
a quarter's edge the loop asks up to its rail. In test_safety119.py part 1
that is more discharge than the guard asked at a quarter's end, and a few
seconds of up to 500 W more charge at the next quarter's start.