FA-93036 / EV charging session scheduling / Open access
Ready-by departure latest start: efficiency direction · case 01
Cars finish short of their target because charging starts too late.
ROOT CAUSE
Charger efficiency multiplies the energy instead of dividing it, underestimating duration.
VERIFIED REPAIR
Divide by efficiency: duration = ceil(need*6000/(rate*eff)).
Unsuccessful approach: Ignoring efficiency altogether still underestimates duration below 100 percent.
Case contract
Minutes. The battery must receive need_wh; the charger draws rate_w at eff_pct percent efficiency, so duration = ceil(need_wh*6000/(rate_w*eff_pct)) minutes. Latest start = depart - buffer_min - duration, rounded down to a 15-minute boundary. If that is before now, start now with status 'late'. need_wh <= 0 returns [None, now, 'done']; rate_w or eff_pct <= 0 returns [None, None, 'no-power']. Return [start, finish, status].
Why this case matters
Depot, workplace and public EV chargers schedule sessions against prices, circuit limits and departure deadlines; a wrong decision silently strands a driver or overloads a feeder.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(now, depart, need_wh, rate_w, eff_pct, buffer_min):
if need_wh <= 0:
return [None, now, 'done']
if rate_w <= 0 or eff_pct <= 0:
return [None, None, 'no-power']
dur = -(-need_wh * 60 * eff_pct // (rate_w * 100))
latest = depart - buffer_min - dur
latest -= latest % 15
if latest < now:
return [now, now + dur, 'late']
return [latest, latest + dur, 'ok']
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [153, 434, 2000, 11000, 85, 30], [390, 403, 'ok']],
['regression: efficiency direction (partial repair)', [387, 1026, 30000, 11000, 95, 30],
[810, 983, 'ok']],
['control 1', [117, 637, 11000, 11000, 95, 10], [555, 619, 'ok']],
['control 2', [448, 1142, 0, 3700, 85, 0], [None, 448, 'done']]],
[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [351, 1109, 18000, 3700, 95, 15], [780, 1088, 'ok']],
['regression: efficiency direction (partial repair)', [153, 726, 30000, 3700, 85, 0],
[153, 726, 'late']],
['control 1', [416, 1003, 5000, 0, 85, 15], [None, None, 'no-power']],
['control 2', [221, 503, 5000, 0, 85, 30], [None, None, 'no-power']]],
[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [84, 334, 30000, 7400, 95, 30], [84, 341, 'late']],
['regression: efficiency direction (partial repair)', [12, 862, 30000, 11000, 95, 15],
[660, 833, 'ok']],
['control 1', [381, 632, 18000, 3700, 95, 30], [381, 689, 'late']],
['control 2', [19, 584, 30000, 11000, 90, 15], [375, 557, 'ok']]],
[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [496, 868, 11000, 3700, 95, 30], [645, 833, 'ok']],
['regression: efficiency direction (partial repair)', [342, 397, 18000, 11000, 95, 10],
[342, 446, 'late']],
['control 1', [66, 229, 11000, 0, 90, 15], [None, None, 'no-power']],
['control 2', [44, 377, 2000, 0, 100, 0], [None, None, 'no-power']]],
[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [522, 1168, 30000, 3700, 85, 30], [555, 1128, 'ok']],
['regression: efficiency direction (partial repair)', [546, 1243, 11000, 7400, 85, 30],
[1095, 1200, 'ok']],
['control 1', [342, 397, 18000, 11000, 95, 10], [342, 446, 'late']],
['control 2', [577, 1400, 11000, 7400, 85, 15], [1275, 1380, 'ok']]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| boundary: nothing to charge | [None, 100, 'done'] | [None, 100, 'done'] | Passed |
| boundary: latest start exactly now | [330, 390, 'ok'] | [330, 390, 'ok'] | Passed |
| boundary: no charger power | [None, None, 'no-power'] | [None, None, 'no-power'] | Passed |
| regression: efficiency direction | [390, 400, 'ok'] | [390, 403, 'ok'] | Failed |
| regression: efficiency direction (partial repair) | [840, 996, 'ok'] | [810, 983, 'ok'] | Failed |
| control 1 | [570, 627, 'ok'] | [555, 619, 'ok'] | Failed |
| control 2 | [None, 448, 'done'] | [None, 448, 'done'] | Passed |
SHA-256 / f97b8041385c614042761c0af114a9b1a0dd090f6fbe40fd21c36d856547f9d2
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(now, depart, need_wh, rate_w, eff_pct, buffer_min):
if need_wh <= 0:
return [None, now, 'done']
if rate_w <= 0 or eff_pct <= 0:
return [None, None, 'no-power']
dur = -(-need_wh * 60 // rate_w)
latest = depart - buffer_min - dur
latest -= latest % 15
if latest < now:
return [now, now + dur, 'late']
return [latest, latest + dur, 'ok']
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [153, 434, 2000, 11000, 85, 30], [390, 403, 'ok']],
['regression: efficiency direction (partial repair)', [387, 1026, 30000, 11000, 95, 30],
[810, 983, 'ok']],
['control 1', [117, 637, 11000, 11000, 95, 10], [555, 619, 'ok']],
['control 2', [448, 1142, 0, 3700, 85, 0], [None, 448, 'done']]],
[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [351, 1109, 18000, 3700, 95, 15], [780, 1088, 'ok']],
['regression: efficiency direction (partial repair)', [153, 726, 30000, 3700, 85, 0],
[153, 726, 'late']],
['control 1', [416, 1003, 5000, 0, 85, 15], [None, None, 'no-power']],
['control 2', [221, 503, 5000, 0, 85, 30], [None, None, 'no-power']]],
[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [84, 334, 30000, 7400, 95, 30], [84, 341, 'late']],
['regression: efficiency direction (partial repair)', [12, 862, 30000, 11000, 95, 15],
[660, 833, 'ok']],
['control 1', [381, 632, 18000, 3700, 95, 30], [381, 689, 'late']],
['control 2', [19, 584, 30000, 11000, 90, 15], [375, 557, 'ok']]],
[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [496, 868, 11000, 3700, 95, 30], [645, 833, 'ok']],
['regression: efficiency direction (partial repair)', [342, 397, 18000, 11000, 95, 10],
[342, 446, 'late']],
['control 1', [66, 229, 11000, 0, 90, 15], [None, None, 'no-power']],
['control 2', [44, 377, 2000, 0, 100, 0], [None, None, 'no-power']]],
[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [522, 1168, 30000, 3700, 85, 30], [555, 1128, 'ok']],
['regression: efficiency direction (partial repair)', [546, 1243, 11000, 7400, 85, 30],
[1095, 1200, 'ok']],
['control 1', [342, 397, 18000, 11000, 95, 10], [342, 446, 'late']],
['control 2', [577, 1400, 11000, 7400, 85, 15], [1275, 1380, 'ok']]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| boundary: nothing to charge | [None, 100, 'done'] | [None, 100, 'done'] | Passed |
| boundary: latest start exactly now | [330, 390, 'ok'] | [330, 390, 'ok'] | Passed |
| boundary: no charger power | [None, None, 'no-power'] | [None, None, 'no-power'] | Passed |
| regression: efficiency direction | [390, 401, 'ok'] | [390, 403, 'ok'] | Failed |
| regression: efficiency direction (partial repair) | [825, 989, 'ok'] | [810, 983, 'ok'] | Failed |
| control 1 | [555, 615, 'ok'] | [555, 619, 'ok'] | Failed |
| control 2 | [None, 448, 'done'] | [None, 448, 'done'] | Passed |
SHA-256 / bc72f055c5bb2166d07c0fce586afee1c4c75dd93105db82bf630111ef671495
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(now, depart, need_wh, rate_w, eff_pct, buffer_min):
if need_wh <= 0:
return [None, now, 'done']
if rate_w <= 0 or eff_pct <= 0:
return [None, None, 'no-power']
dur = -(-need_wh * 6000 // (rate_w * eff_pct))
latest = depart - buffer_min - dur
latest -= latest % 15
if latest < now:
return [now, now + dur, 'late']
return [latest, latest + dur, 'ok']
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [153, 434, 2000, 11000, 85, 30], [390, 403, 'ok']],
['regression: efficiency direction (partial repair)', [387, 1026, 30000, 11000, 95, 30],
[810, 983, 'ok']],
['control 1', [117, 637, 11000, 11000, 95, 10], [555, 619, 'ok']],
['control 2', [448, 1142, 0, 3700, 85, 0], [None, 448, 'done']]],
[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [351, 1109, 18000, 3700, 95, 15], [780, 1088, 'ok']],
['regression: efficiency direction (partial repair)', [153, 726, 30000, 3700, 85, 0],
[153, 726, 'late']],
['control 1', [416, 1003, 5000, 0, 85, 15], [None, None, 'no-power']],
['control 2', [221, 503, 5000, 0, 85, 30], [None, None, 'no-power']]],
[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [84, 334, 30000, 7400, 95, 30], [84, 341, 'late']],
['regression: efficiency direction (partial repair)', [12, 862, 30000, 11000, 95, 15],
[660, 833, 'ok']],
['control 1', [381, 632, 18000, 3700, 95, 30], [381, 689, 'late']],
['control 2', [19, 584, 30000, 11000, 90, 15], [375, 557, 'ok']]],
[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [496, 868, 11000, 3700, 95, 30], [645, 833, 'ok']],
['regression: efficiency direction (partial repair)', [342, 397, 18000, 11000, 95, 10],
[342, 446, 'late']],
['control 1', [66, 229, 11000, 0, 90, 15], [None, None, 'no-power']],
['control 2', [44, 377, 2000, 0, 100, 0], [None, None, 'no-power']]],
[['boundary: nothing to charge', [100, 400, 0, 0, 90, 10], [None, 100, 'done']],
['boundary: latest start exactly now', [300, 390, 7400, 7400, 100, 0], [330, 390, 'ok']],
['boundary: no charger power', [100, 400, 5000, 0, 90, 10], [None, None, 'no-power']],
['regression: efficiency direction', [522, 1168, 30000, 3700, 85, 30], [555, 1128, 'ok']],
['regression: efficiency direction (partial repair)', [546, 1243, 11000, 7400, 85, 30],
[1095, 1200, 'ok']],
['control 1', [342, 397, 18000, 11000, 95, 10], [342, 446, 'late']],
['control 2', [577, 1400, 11000, 7400, 85, 15], [1275, 1380, 'ok']]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| boundary: nothing to charge | [None, 100, 'done'] | [None, 100, 'done'] | Passed |
| boundary: latest start exactly now | [330, 390, 'ok'] | [330, 390, 'ok'] | Passed |
| boundary: no charger power | [None, None, 'no-power'] | [None, None, 'no-power'] | Passed |
| regression: efficiency direction | [390, 403, 'ok'] | [390, 403, 'ok'] | Passed |
| regression: efficiency direction (partial repair) | [810, 983, 'ok'] | [810, 983, 'ok'] | Passed |
| control 1 | [555, 619, 'ok'] | [555, 619, 'ok'] | Passed |
| control 2 | [None, 448, 'done'] | [None, 448, 'done'] | Passed |
SHA-256 / 7355d8749cbb056c92fd491eccfe20c2d4f4c79f14c8cf04159651dbe4b70243
Verification & scope
Deterministic stipulated toy contract for teaching; no claim of conformance with any standard, vendor protocol or production controller. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:51:51.418022+00:00.
Case digest / ccdd0c4c608086f7013437c06c6871fc2a93eaec52a7052e14e08f7725f90e71