SAFETY-84: maintenance charge at N=2 no longer stalls
At N>=2 a cheap-window maintenance charge files a site `hi` (`charge`, P_MAINT_SHAPE) equal to what the charging pack's own `hi` places, so the other pack never has to give against it. The charge is sized to the quarter's headroom under the peak aim so it yields to the peak guard, and to 0 W under a site safety stop. N=1 byte-identical (safety84_identity). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9d5b7d129a
commit
43a52e7f60
@@ -40,4 +40,4 @@ direction. Opposite splits stay forbidden.
|
||||
- At N=2 a maintenance CHARGE in a cheap window has the same stall: the
|
||||
charging pack's `hi = -floor` against the loop asking the other to give is
|
||||
refused on 141 of 261 cycles. The mirror fix is a site `hi`, which can meet
|
||||
the peak floor's `lo` (0 W), so it is not this card.
|
||||
the peak floor's `lo` (0 W), so it is not this card. Fixed by [[SAFETY-84]].
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# SAFETY-84 - a maintenance charge at two inverters
|
||||
|
||||
**Status: DECIDED 2026-09-30.** Follows SAFETY-78's "Not changed" list.
|
||||
Tests: `test_safety84.py`; identity: `tools/safety84_identity.py`.
|
||||
|
||||
**The defect.** In a cheap window the charging pack files its own
|
||||
`hi = -charge` (P_MAINT_SHAPE). At N>=2 the loop's site target sat above it
|
||||
(a house to cover, or its 400 W slew step from 0 W), so the other pack had to
|
||||
give while this one charged. The allocator refuses opposite directions
|
||||
(SAFETY-31), so the site sat at 0 W whatever the house
|
||||
(`tools/safety84_identity.py` on 9d5b7d1: 436 of 480 cycles refused). N=1
|
||||
was fine.
|
||||
|
||||
**The fix.**
|
||||
- While a pack's own limits make it charge at N>=2, `main.py` files a site
|
||||
limit `charge`, `hi` = what that pack's own `hi` places
|
||||
(`allocator.hi_w()`, whole W as the allocator rounds). The site target is
|
||||
never above it, so the other pack only adds power in the same direction.
|
||||
Opposite splits stay forbidden. Mirror of SAFETY-78's `drain` floor.
|
||||
- Peak guard first (owner order, SAFETY-83). The charge is sized to the
|
||||
quarter's headroom under the aim (`charge_headroom_w()`, cap or banked peak
|
||||
minus `peak_margin_w`), so it charges less instead of stopping: the meter
|
||||
sits on the aim and the guard does not need to step in. Where the house
|
||||
alone is over the aim the guard engages, the shaping is suspended as before
|
||||
(`shaping_suspended()`) and no `charge` is filed, so the guard's `lo` and
|
||||
this `hi` never meet in one cycle (no `lo > hi`, no park at 0 W).
|
||||
Without the headroom sizing the aim still held (the suspension did it), but
|
||||
a battery gave power back on 285 of 540 cycles at a 3000 W house
|
||||
(test_safety84.py part 2).
|
||||
- A site stop (a safety SET above P_MAINT_SHAPE) sizes the charge to 0 W:
|
||||
the unit `hi` is a hard limit, so a charge under a 0 W stop needs the other
|
||||
pack to give. It was refused, with an ARBITER line, on most cycles of the
|
||||
stop (test_safety84.py part 4). The stop still parked the site at 0 W.
|
||||
- A pack that cannot take the charge (a refusal, a full pack, a stale link)
|
||||
files no ceiling; the other pack follows the loop.
|
||||
- Not filed at N=1: byte-identical (`tools/safety84_identity.py`, which hashes
|
||||
every claim, in and out of a cheap window and under a banked peak).
|
||||
|
||||
**Not changed.**
|
||||
- At N=1 the charge is not sized to the headroom: the guard still suspends it
|
||||
and the battery gives until the quarter is safe. The same sizing would fit
|
||||
there; that is an owner question, not this fix.
|
||||
- Outside a cheap window (`hi = 0`, solar only) nothing is filed, at any N.
|
||||
- A HOLD (SAFETY-86) and the "Battery is not keeping up" alarm (SAFETY-85)
|
||||
are untouched. In the identity runs that alarm's log line comes as often at
|
||||
N=2 as at N=1 (2 in 480 cycles).
|
||||
Reference in New Issue
Block a user