FA-93261 / EV charging session scheduling / Open access
Arrival charger matching: onboard AC charger limit · case 01
Finish times on 22 kW AC points assume the car can take all 22 kW.
ROOT CAUSE
The AC power ignores the vehicle onboard charger limit.
VERIFIED REPAIR
Limit AC power to min(charger kw, vehicle max_ac).
Unsuccessful approach: Applying the onboard limit to DC instead of AC throttles DC sessions and leaves AC wrong.
Case contract
chargers are [id, kw, free_at]; kw > 22 is DC (usable only if dc_ok, full kw), otherwise AC limited to the vehicle onboard max_ac. vehicle is [need_kwh, depart, max_ac, dc_ok]. Finish = max(now, free_at) + ceil(need*60/power) minutes. Among chargers finishing by depart pick the lowest power, then earliest finish, then id; otherwise the earliest finish (then id) with status 'late'. Return [id, finish, status] or None when no charger is usable.
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
import math
N = 1
observations = []
def solve(chargers, vehicle, now):
need, depart, max_ac, dc_ok = vehicle
opts = []
for cid, kw, free_at in chargers:
if kw > 22:
if not dc_ok:
continue
pw = kw
else:
pw = kw
start = max(now, free_at)
finish = start + math.ceil(need * 60 / pw)
opts.append([pw, finish, cid])
if not opts:
return None
ok = [o for o in opts if o[1] <= depart]
if ok:
pw, finish, cid = min(ok)
return [cid, finish, 'ok']
pw, finish, cid = min(opts, key=lambda o: (o[1], o[2]))
return [cid, finish, 'late']
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit',
[[[1, 150, 10], [2, 22, 120], [3, 150, 30], [4, 3.7, 120]], [20, 111, 3.7, False], 6],
[2, 445, 'late']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 150, 0], [2, 7.4, 10]], [45, 104, 7.4, True], 19], [1, 37, 'ok']],
['control 1', [[[1, 22, 60]], [5, 244, 11, False], 8], [1, 88, 'ok']],
['control 2', [[[1, 50, 0], [2, 50, 0]], [5, 160, 11, False], 39], None]],
[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit', [[[1, 22, 120]], [20, 345, 3.7, True], 47],
[1, 445, 'late']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 150, 0], [2, 7.4, 120], [3, 50, 0]], [20, 67, 11, True], 8], [3, 32, 'ok']],
['control 1', [[[1, 50, 0]], [45, 63, 7.4, True], 12], [1, 66, 'late']],
['control 2',
[[[1, 7.4, 60], [2, 7.4, 10], [3, 22, 10], [4, 11, 120], [5, 50, 120]], [45, 353, 7.4, True],
45],
[5, 174, 'ok']]],
[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit',
[[[1, 11, 60], [2, 150, 30], [3, 7.4, 0], [4, 3.7, 0], [5, 7.4, 60]], [10, 35, 3.7, False], 25],
[3, 188, 'late']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 150, 30], [2, 7.4, 60], [3, 22, 0]], [45, 136, 11, True], 41], [1, 59, 'ok']],
['control 1', [[[1, 7.4, 10], [2, 7.4, 0]], [45, 88, 7.4, True], 40], [1, 405, 'late']],
['control 2', [[[1, 3.7, 10], [2, 3.7, 120], [3, 11, 10], [4, 50, 0]], [5, 42, 7.4, True], 27],
[4, 33, 'ok']]],
[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit',
[[[1, 50, 0], [2, 7.4, 0], [3, 7.4, 120], [4, 22, 10]], [45, 278, 3.7, True], 26],
[1, 80, 'ok']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 50, 60], [2, 50, 60]], [45, 130, 7.4, True], 48], [1, 114, 'ok']],
['control 1', [[[1, 7.4, 60]], [30, 240, 3.7, True], 21], [1, 547, 'late']],
['control 2', [[[1, 3.7, 60], [2, 3.7, 120], [3, 150, 30], [4, 150, 0]], [45, 67, 11, False], 57],
[1, 790, 'late']]],
[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit',
[[[1, 22, 0], [2, 3.7, 10], [3, 50, 30]], [20, 64, 3.7, False], 19], [1, 344, 'late']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 50, 0], [2, 50, 30]], [10, 89, 7.4, True], 45], [1, 57, 'ok']],
['control 1', [[[1, 50, 60], [2, 50, 60]], [45, 130, 7.4, True], 48], [1, 114, 'ok']],
['control 2', [[[1, 150, 0], [2, 7.4, 10]], [10, 89, 3.7, True], 57], [1, 61, '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: 22 kW AC charger | [1, 30, 'ok'] | [1, 60, 'ok'] | Failed |
| boundary: finish exactly at departure | [1, 300, 'ok'] | [1, 300, 'ok'] | Passed |
| boundary: only DC and no DC support | None | None | Passed |
| regression: onboard AC charger limit | [2, 175, 'late'] | [2, 445, 'late'] | Failed |
| regression: onboard AC charger limit (partial repair) | [1, 37, 'ok'] | [1, 37, 'ok'] | Passed |
| control 1 | [1, 74, 'ok'] | [1, 88, 'ok'] | Failed |
| control 2 | None | None | Passed |
SHA-256 / a99e4a0261af20595c8fe13492c5fb2c13324fc1380034aeef66fe3fd28f2c31
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(chargers, vehicle, now):
need, depart, max_ac, dc_ok = vehicle
opts = []
for cid, kw, free_at in chargers:
if kw > 22:
if not dc_ok:
continue
pw = min(kw, max_ac)
else:
pw = min(kw, max_ac)
start = max(now, free_at)
finish = start + math.ceil(need * 60 / pw)
opts.append([pw, finish, cid])
if not opts:
return None
ok = [o for o in opts if o[1] <= depart]
if ok:
pw, finish, cid = min(ok)
return [cid, finish, 'ok']
pw, finish, cid = min(opts, key=lambda o: (o[1], o[2]))
return [cid, finish, 'late']
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit',
[[[1, 150, 10], [2, 22, 120], [3, 150, 30], [4, 3.7, 120]], [20, 111, 3.7, False], 6],
[2, 445, 'late']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 150, 0], [2, 7.4, 10]], [45, 104, 7.4, True], 19], [1, 37, 'ok']],
['control 1', [[[1, 22, 60]], [5, 244, 11, False], 8], [1, 88, 'ok']],
['control 2', [[[1, 50, 0], [2, 50, 0]], [5, 160, 11, False], 39], None]],
[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit', [[[1, 22, 120]], [20, 345, 3.7, True], 47],
[1, 445, 'late']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 150, 0], [2, 7.4, 120], [3, 50, 0]], [20, 67, 11, True], 8], [3, 32, 'ok']],
['control 1', [[[1, 50, 0]], [45, 63, 7.4, True], 12], [1, 66, 'late']],
['control 2',
[[[1, 7.4, 60], [2, 7.4, 10], [3, 22, 10], [4, 11, 120], [5, 50, 120]], [45, 353, 7.4, True],
45],
[5, 174, 'ok']]],
[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit',
[[[1, 11, 60], [2, 150, 30], [3, 7.4, 0], [4, 3.7, 0], [5, 7.4, 60]], [10, 35, 3.7, False], 25],
[3, 188, 'late']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 150, 30], [2, 7.4, 60], [3, 22, 0]], [45, 136, 11, True], 41], [1, 59, 'ok']],
['control 1', [[[1, 7.4, 10], [2, 7.4, 0]], [45, 88, 7.4, True], 40], [1, 405, 'late']],
['control 2', [[[1, 3.7, 10], [2, 3.7, 120], [3, 11, 10], [4, 50, 0]], [5, 42, 7.4, True], 27],
[4, 33, 'ok']]],
[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit',
[[[1, 50, 0], [2, 7.4, 0], [3, 7.4, 120], [4, 22, 10]], [45, 278, 3.7, True], 26],
[1, 80, 'ok']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 50, 60], [2, 50, 60]], [45, 130, 7.4, True], 48], [1, 114, 'ok']],
['control 1', [[[1, 7.4, 60]], [30, 240, 3.7, True], 21], [1, 547, 'late']],
['control 2', [[[1, 3.7, 60], [2, 3.7, 120], [3, 150, 30], [4, 150, 0]], [45, 67, 11, False], 57],
[1, 790, 'late']]],
[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit',
[[[1, 22, 0], [2, 3.7, 10], [3, 50, 30]], [20, 64, 3.7, False], 19], [1, 344, 'late']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 50, 0], [2, 50, 30]], [10, 89, 7.4, True], 45], [1, 57, 'ok']],
['control 1', [[[1, 50, 60], [2, 50, 60]], [45, 130, 7.4, True], 48], [1, 114, 'ok']],
['control 2', [[[1, 150, 0], [2, 7.4, 10]], [10, 89, 3.7, True], 57], [1, 61, '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: 22 kW AC charger | [1, 60, 'ok'] | [1, 60, 'ok'] | Passed |
| boundary: finish exactly at departure | [1, 300, 'ok'] | [1, 300, 'ok'] | Passed |
| boundary: only DC and no DC support | None | None | Passed |
| regression: onboard AC charger limit | [2, 445, 'late'] | [2, 445, 'late'] | Passed |
| regression: onboard AC charger limit (partial repair) | [1, 384, 'late'] | [1, 37, 'ok'] | Failed |
| control 1 | [1, 88, 'ok'] | [1, 88, 'ok'] | Passed |
| control 2 | None | None | Passed |
SHA-256 / 10539def5bd0e2bec81178b87aefe745ee63b78a984e7fe3cf744f028f156725
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(chargers, vehicle, now):
need, depart, max_ac, dc_ok = vehicle
opts = []
for cid, kw, free_at in chargers:
if kw > 22:
if not dc_ok:
continue
pw = kw
else:
pw = min(kw, max_ac)
start = max(now, free_at)
finish = start + math.ceil(need * 60 / pw)
opts.append([pw, finish, cid])
if not opts:
return None
ok = [o for o in opts if o[1] <= depart]
if ok:
pw, finish, cid = min(ok)
return [cid, finish, 'ok']
pw, finish, cid = min(opts, key=lambda o: (o[1], o[2]))
return [cid, finish, 'late']
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit',
[[[1, 150, 10], [2, 22, 120], [3, 150, 30], [4, 3.7, 120]], [20, 111, 3.7, False], 6],
[2, 445, 'late']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 150, 0], [2, 7.4, 10]], [45, 104, 7.4, True], 19], [1, 37, 'ok']],
['control 1', [[[1, 22, 60]], [5, 244, 11, False], 8], [1, 88, 'ok']],
['control 2', [[[1, 50, 0], [2, 50, 0]], [5, 160, 11, False], 39], None]],
[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit', [[[1, 22, 120]], [20, 345, 3.7, True], 47],
[1, 445, 'late']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 150, 0], [2, 7.4, 120], [3, 50, 0]], [20, 67, 11, True], 8], [3, 32, 'ok']],
['control 1', [[[1, 50, 0]], [45, 63, 7.4, True], 12], [1, 66, 'late']],
['control 2',
[[[1, 7.4, 60], [2, 7.4, 10], [3, 22, 10], [4, 11, 120], [5, 50, 120]], [45, 353, 7.4, True],
45],
[5, 174, 'ok']]],
[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit',
[[[1, 11, 60], [2, 150, 30], [3, 7.4, 0], [4, 3.7, 0], [5, 7.4, 60]], [10, 35, 3.7, False], 25],
[3, 188, 'late']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 150, 30], [2, 7.4, 60], [3, 22, 0]], [45, 136, 11, True], 41], [1, 59, 'ok']],
['control 1', [[[1, 7.4, 10], [2, 7.4, 0]], [45, 88, 7.4, True], 40], [1, 405, 'late']],
['control 2', [[[1, 3.7, 10], [2, 3.7, 120], [3, 11, 10], [4, 50, 0]], [5, 42, 7.4, True], 27],
[4, 33, 'ok']]],
[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit',
[[[1, 50, 0], [2, 7.4, 0], [3, 7.4, 120], [4, 22, 10]], [45, 278, 3.7, True], 26],
[1, 80, 'ok']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 50, 60], [2, 50, 60]], [45, 130, 7.4, True], 48], [1, 114, 'ok']],
['control 1', [[[1, 7.4, 60]], [30, 240, 3.7, True], 21], [1, 547, 'late']],
['control 2', [[[1, 3.7, 60], [2, 3.7, 120], [3, 150, 30], [4, 150, 0]], [45, 67, 11, False], 57],
[1, 790, 'late']]],
[['boundary: 22 kW AC charger', [[[1, 22, 0]], [11, 300, 11, False], 0], [1, 60, 'ok']],
['boundary: finish exactly at departure', [[[1, 7.4, 0]], [37, 300, 7.4, True], 0],
[1, 300, 'ok']],
['boundary: only DC and no DC support', [[[1, 50, 0]], [10, 100, 11, False], 0], None],
['regression: onboard AC charger limit',
[[[1, 22, 0], [2, 3.7, 10], [3, 50, 30]], [20, 64, 3.7, False], 19], [1, 344, 'late']],
['regression: onboard AC charger limit (partial repair)',
[[[1, 50, 0], [2, 50, 30]], [10, 89, 7.4, True], 45], [1, 57, 'ok']],
['control 1', [[[1, 50, 60], [2, 50, 60]], [45, 130, 7.4, True], 48], [1, 114, 'ok']],
['control 2', [[[1, 150, 0], [2, 7.4, 10]], [10, 89, 3.7, True], 57], [1, 61, '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: 22 kW AC charger | [1, 60, 'ok'] | [1, 60, 'ok'] | Passed |
| boundary: finish exactly at departure | [1, 300, 'ok'] | [1, 300, 'ok'] | Passed |
| boundary: only DC and no DC support | None | None | Passed |
| regression: onboard AC charger limit | [2, 445, 'late'] | [2, 445, 'late'] | Passed |
| regression: onboard AC charger limit (partial repair) | [1, 37, 'ok'] | [1, 37, 'ok'] | Passed |
| control 1 | [1, 88, 'ok'] | [1, 88, 'ok'] | Passed |
| control 2 | None | None | Passed |
SHA-256 / dacdf457d1dcbdd95a2da3498f561d22ae24a84f2e0f8e1397e1c5baed5a8ae9
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:53.603871+00:00.
Case digest / b4156a4b04feb8375aa5a5d84ee99e4601fbc65cef5d01c465fd8cc20dce6337