FAILURE MAP
← Case archive

FA-93281 / EV charging session scheduling / Open access

Arrival charger matching: feasible charger preference · case 01

Short-stay vehicles take fast DC chargers that slower AC points could serve in time.

Verified by executionVariant 1 · 7 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

Feasible chargers are ranked by finish time instead of lowest sufficient power.

VERIFIED REPAIR

Rank feasible chargers by power, then finish, then id.

Unsuccessful approach: Ranking by power alone leaves equal-power ties in list order.

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 = 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, key=lambda o: (o[1], o[2]))
        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: feasible charger preference',
   [[[1, 3.7, 30], [2, 3.7, 60], [3, 11, 30], [4, 22, 120]], [10, 332, 11, True], 57],
   [1, 220, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 50, 30], [2, 22, 30], [3, 50, 120], [4, 22, 0]], [5, 150, 7.4, True], 10], [4, 51, '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: feasible charger preference',
   [[[1, 150, 0], [2, 7.4, 120], [3, 50, 0]], [20, 67, 11, True], 8], [3, 32, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 3.7, 10], [2, 7.4, 0], [3, 50, 60], [4, 50, 30], [5, 7.4, 10]], [20, 252, 3.7, True], 34],
   [4, 58, '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: feasible charger preference',
   [[[1, 150, 0], [2, 7.4, 30], [3, 7.4, 0], [4, 3.7, 60], [5, 7.4, 60]], [5, 300, 3.7, True], 48],
   [2, 130, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 150, 120], [2, 50, 0], [3, 50, 10], [4, 22, 60], [5, 22, 0]], [20, 374, 7.4, True], 19],
   [5, 182, '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: feasible charger preference',
   [[[1, 22, 120], [2, 7.4, 10], [3, 50, 60], [4, 7.4, 0]], [20, 375, 7.4, True], 37],
   [2, 200, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 150, 0], [2, 150, 10], [3, 22, 30], [4, 7.4, 10]], [10, 278, 7.4, False], 17],
   [4, 99, '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: feasible charger preference',
   [[[1, 50, 60], [2, 11, 0], [3, 7.4, 0]], [45, 352, 11, True], 50], [2, 296, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 50, 30], [2, 50, 60], [3, 50, 0]], [10, 216, 3.7, True], 29], [3, 41, '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 fixtureActualExpectedOutcome
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 supportNoneNonePassed
regression: feasible charger preference[3, 112, 'ok'][1, 220, 'ok']Failed
regression: feasible charger preference (partial repair)[1, 36, 'ok'][4, 51, 'ok']Failed
control 1[1, 88, 'ok'][1, 88, 'ok']Passed
control 2NoneNonePassed

SHA-256 / bf6fd0f98e95111182603fca23c107f42fc5d030954c3b932f3549bef8e5ffa0

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 = 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, key=lambda o: o[0])
        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: feasible charger preference',
   [[[1, 3.7, 30], [2, 3.7, 60], [3, 11, 30], [4, 22, 120]], [10, 332, 11, True], 57],
   [1, 220, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 50, 30], [2, 22, 30], [3, 50, 120], [4, 22, 0]], [5, 150, 7.4, True], 10], [4, 51, '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: feasible charger preference',
   [[[1, 150, 0], [2, 7.4, 120], [3, 50, 0]], [20, 67, 11, True], 8], [3, 32, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 3.7, 10], [2, 7.4, 0], [3, 50, 60], [4, 50, 30], [5, 7.4, 10]], [20, 252, 3.7, True], 34],
   [4, 58, '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: feasible charger preference',
   [[[1, 150, 0], [2, 7.4, 30], [3, 7.4, 0], [4, 3.7, 60], [5, 7.4, 60]], [5, 300, 3.7, True], 48],
   [2, 130, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 150, 120], [2, 50, 0], [3, 50, 10], [4, 22, 60], [5, 22, 0]], [20, 374, 7.4, True], 19],
   [5, 182, '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: feasible charger preference',
   [[[1, 22, 120], [2, 7.4, 10], [3, 50, 60], [4, 7.4, 0]], [20, 375, 7.4, True], 37],
   [2, 200, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 150, 0], [2, 150, 10], [3, 22, 30], [4, 7.4, 10]], [10, 278, 7.4, False], 17],
   [4, 99, '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: feasible charger preference',
   [[[1, 50, 60], [2, 11, 0], [3, 7.4, 0]], [45, 352, 11, True], 50], [2, 296, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 50, 30], [2, 50, 60], [3, 50, 0]], [10, 216, 3.7, True], 29], [3, 41, '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 fixtureActualExpectedOutcome
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 supportNoneNonePassed
regression: feasible charger preference[1, 220, 'ok'][1, 220, 'ok']Passed
regression: feasible charger preference (partial repair)[2, 71, 'ok'][4, 51, 'ok']Failed
control 1[1, 88, 'ok'][1, 88, 'ok']Passed
control 2NoneNonePassed

SHA-256 / 8f6dbe16a150fa461fb2f69236b1039e2bbfafc4ff1196bb4d4ab0df28c4d074

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: feasible charger preference',
   [[[1, 3.7, 30], [2, 3.7, 60], [3, 11, 30], [4, 22, 120]], [10, 332, 11, True], 57],
   [1, 220, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 50, 30], [2, 22, 30], [3, 50, 120], [4, 22, 0]], [5, 150, 7.4, True], 10], [4, 51, '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: feasible charger preference',
   [[[1, 150, 0], [2, 7.4, 120], [3, 50, 0]], [20, 67, 11, True], 8], [3, 32, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 3.7, 10], [2, 7.4, 0], [3, 50, 60], [4, 50, 30], [5, 7.4, 10]], [20, 252, 3.7, True], 34],
   [4, 58, '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: feasible charger preference',
   [[[1, 150, 0], [2, 7.4, 30], [3, 7.4, 0], [4, 3.7, 60], [5, 7.4, 60]], [5, 300, 3.7, True], 48],
   [2, 130, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 150, 120], [2, 50, 0], [3, 50, 10], [4, 22, 60], [5, 22, 0]], [20, 374, 7.4, True], 19],
   [5, 182, '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: feasible charger preference',
   [[[1, 22, 120], [2, 7.4, 10], [3, 50, 60], [4, 7.4, 0]], [20, 375, 7.4, True], 37],
   [2, 200, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 150, 0], [2, 150, 10], [3, 22, 30], [4, 7.4, 10]], [10, 278, 7.4, False], 17],
   [4, 99, '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: feasible charger preference',
   [[[1, 50, 60], [2, 11, 0], [3, 7.4, 0]], [45, 352, 11, True], 50], [2, 296, 'ok']],
  ['regression: feasible charger preference (partial repair)',
   [[[1, 50, 30], [2, 50, 60], [3, 50, 0]], [10, 216, 3.7, True], 29], [3, 41, '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 fixtureActualExpectedOutcome
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 supportNoneNonePassed
regression: feasible charger preference[1, 220, 'ok'][1, 220, 'ok']Passed
regression: feasible charger preference (partial repair)[4, 51, 'ok'][4, 51, 'ok']Passed
control 1[1, 88, 'ok'][1, 88, 'ok']Passed
control 2NoneNonePassed

SHA-256 / ec205f6017e8ce9581d219ea12d8ae6e4b15e77afbd2440ab8df5a834c606b4d

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.680046+00:00.

Case digest / 56a2069850fc04827c4704595c857f5726a1adc1792c6cd9d810e415839dc626