SAFETY-87: grade the surviving mutants (dropped battery, own-floor naming, pair permission, latched row)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DBUfvndgHwaV339Fvd2gr
This commit is contained in:
@@ -33,6 +33,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import types
|
||||||
|
|
||||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
sys.path.insert(0, HERE)
|
sys.path.insert(0, HERE)
|
||||||
@@ -393,6 +394,33 @@ check("...and at N=1 the floor-limited claim is unchanged: 0 W, 'SoC floor'",
|
|||||||
peakguard.claim(_lim).lo == 0.0
|
peakguard.claim(_lim).lo == 0.0
|
||||||
and peakguard.claim(_lim).reason == "SoC floor")
|
and peakguard.claim(_lim).reason == "SoC floor")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def three(socs, cuts):
|
||||||
|
"""`_peak_on_able()` on a stub roster of three (SIMULATED packs)."""
|
||||||
|
names = ("x", "y", "z")
|
||||||
|
invs = [types.SimpleNamespace(name=n, refused=0, rating_w=lambda: 2500.0)
|
||||||
|
for n in names]
|
||||||
|
stub = types.SimpleNamespace(
|
||||||
|
inverters=invs, o={"peak_soc_floor": 20},
|
||||||
|
packs=[peakguard.Pack(n, so, c) for n, so, c in zip(names, socs, cuts)],
|
||||||
|
live_units=lambda: names)
|
||||||
|
return Controller._peak_on_able(stub, _lim, [MaintResult(
|
||||||
|
IDLE, False, None, False, [])] * 3)
|
||||||
|
|
||||||
|
|
||||||
|
# x 25 % (cut-out 10 -> floor 20), y 40 % (cut-out 30), z 15 % (held). x and
|
||||||
|
# y each clear their own floor, but 25 % is not above y's 30 %: the pair is
|
||||||
|
# graded min SoC against max floor (SAFETY-32), so nothing is asked.
|
||||||
|
_t3 = three((25.0, 40.0, 15.0), (10, 30, 10))
|
||||||
|
_t3ok = three((35.0, 40.0, 15.0), (10, 30, 10))
|
||||||
|
check("⭐ three batteries, z held: the two others are graded as a pair, the "
|
||||||
|
"EMPTIEST of them (25 %) against the HIGHEST of their floors (30 %), "
|
||||||
|
"so nothing is asked; at 35 % they are asked for the need",
|
||||||
|
_t3.held == ("z",) and _t3.floor_limited and _t3.floor_w == 0.0
|
||||||
|
and _t3ok.held == ("z",) and not _t3ok.floor_limited
|
||||||
|
and _t3ok.floor_w == 2000.0)
|
||||||
|
|
||||||
# ============================================================================ #
|
# ============================================================================ #
|
||||||
print("\n6. the allocator: a held battery gives last")
|
print("\n6. the allocator: a held battery gives last")
|
||||||
|
|
||||||
@@ -474,6 +502,13 @@ check("...the row names A and says the others cannot hold the limit alone, "
|
|||||||
len(_row) == 1 and _row[0]["text"].startswith(
|
len(_row) == 1 and _row[0]["text"].startswith(
|
||||||
"inverter-a battery at its peak reserve. The others cannot hold "
|
"inverter-a battery at its peak reserve. The others cannot hold "
|
||||||
"the 3 500 W peak limit alone."))
|
"the 3 500 W peak limit alone."))
|
||||||
|
_c65._peak_floor_limited = False
|
||||||
|
_blip = [r for r in _c65.checks() if r.get("id") == "peak_guard"]
|
||||||
|
_c65._peak_floor_limited = True
|
||||||
|
check("...and while the alarm is not latched (a one-cycle miss) the row is "
|
||||||
|
"the ordinary amber guard row, not the red reserve row",
|
||||||
|
len(_blip) == 1 and _blip[0]["warn"] and not _blip[0].get("runs")
|
||||||
|
and "peak reserve" not in _blip[0]["text"])
|
||||||
_c65.auto = False
|
_c65.auto = False
|
||||||
_stopped = _c65.status_payload()
|
_stopped = _c65.status_payload()
|
||||||
_c65.auto = True
|
_c65.auto = True
|
||||||
|
|||||||
Reference in New Issue
Block a user