FAILURE MAP
← Case archive

FA-93651 / Solar tracker geometry / Open access

Precomputed tracker angle table: evening range end · case 01

The final scheduled angle is never commanded.

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

ROOT CAUSE

The last table minute is treated as outside the schedule.

VERIFIED REPAIR

Only minutes after the last entry are outside.

Unsuccessful approach: Dropping the evening check leaves the tracker at a stale angle after the schedule ends.

Case contract

table lists [minute, angle] entries in any order; for duplicate minutes the last listed entry wins. Outside [first, last] minute the tracker is flat (0.0). Inside, the angle is linearly interpolated between neighbouring entries and rounded to 2. Return the angle.

Why this case matters

Single-axis and dual-axis solar trackers turn a sun direction into actuator commands; a sign, frame or limit mistake points a whole plant away from the sun or into a mechanical stop.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(table, t):
    pts = {}
    for m, a in table:
        pts[m] = a
    keys = sorted(pts)
    if not keys or t < keys[0] or t >= keys[-1]:
        return 0.0
    for m0, m1 in zip(keys, keys[1:]):
        if m0 <= t <= m1:
            a0, a1 = pts[m0], pts[m1]
            return round(a0 + (a1 - a0) * (t - m0) / (m1 - m0), 2)
    return round(float(pts[keys[0]]), 2)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[540, 60]], 540], 60.0],
  ['regression: evening range end (partial repair)', [[[420, 60]], 1110], 0.0],
  ['control 1', [[[840, 45], [1140, -60]], 300], 0.0],
  ['control 2', [[[540, -60], [600, 60], [480, 45]], 870], 0.0]],
 [['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[360, -30], [1140, 30], [840, -15]], 1140], 30.0],
  ['regression: evening range end (partial repair)',
   [[[600, 15], [420, -60], [360, 30], [540, 60]], 690], 0.0],
  ['control 1', [[[540, 30], [1020, 45]], 945], 42.66],
  ['control 2', [[[1140, -60], [360, -30], [900, 30], [900, 15], [480, 0]], 855], 13.39]],
 [['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[360, -30], [1140, 30], [840, -15]], 1140], 30.0],
  ['regression: evening range end (partial repair)', [[[660, 15], [1020, -45]], 1095], 0.0],
  ['control 1', [[[1080, -45], [540, 45]], 1110], 0.0],
  ['control 2', [[[600, 45], [1020, 45]], 585], 0.0]],
 [['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[540, 60]], 540], 60.0],
  ['regression: evening range end (partial repair)', [[[480, 60], [480, -30], [720, -15]], 840],
   0.0],
  ['control 1', [[[600, -15], [1080, 30], [1080, 0]], 420], 0.0],
  ['control 2', [[[1140, 0], [480, -15]], 1110], -0.68]],
 [['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[540, 60]], 540], 60.0],
  ['regression: evening range end (partial repair)',
   [[[600, 0], [540, 0], [480, -60], [840, -30]], 1200], 0.0],
  ['control 1', [[[1080, 0]], 345], 0.0], ['control 2', [[[1080, 45]], 600], 0.0]]]
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: at last entry0.060.0Failed
normal: midway-30.0-30.0Passed
boundary: before first entry0.00.0Passed
regression: evening range end0.060.0Failed
regression: evening range end (partial repair)0.00.0Passed
control 10.00.0Passed
control 20.00.0Passed

SHA-256 / ef1662104f702f2db5de467e783f44d6acb3c2b4775a7855a571db0db78f7c03

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(table, t):
    pts = {}
    for m, a in table:
        pts[m] = a
    keys = sorted(pts)
    if not keys or t < keys[0]:
        return 0.0
    for m0, m1 in zip(keys, keys[1:]):
        if m0 <= t <= m1:
            a0, a1 = pts[m0], pts[m1]
            return round(a0 + (a1 - a0) * (t - m0) / (m1 - m0), 2)
    return round(float(pts[keys[0]]), 2)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[540, 60]], 540], 60.0],
  ['regression: evening range end (partial repair)', [[[420, 60]], 1110], 0.0],
  ['control 1', [[[840, 45], [1140, -60]], 300], 0.0],
  ['control 2', [[[540, -60], [600, 60], [480, 45]], 870], 0.0]],
 [['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[360, -30], [1140, 30], [840, -15]], 1140], 30.0],
  ['regression: evening range end (partial repair)',
   [[[600, 15], [420, -60], [360, 30], [540, 60]], 690], 0.0],
  ['control 1', [[[540, 30], [1020, 45]], 945], 42.66],
  ['control 2', [[[1140, -60], [360, -30], [900, 30], [900, 15], [480, 0]], 855], 13.39]],
 [['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[360, -30], [1140, 30], [840, -15]], 1140], 30.0],
  ['regression: evening range end (partial repair)', [[[660, 15], [1020, -45]], 1095], 0.0],
  ['control 1', [[[1080, -45], [540, 45]], 1110], 0.0],
  ['control 2', [[[600, 45], [1020, 45]], 585], 0.0]],
 [['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[540, 60]], 540], 60.0],
  ['regression: evening range end (partial repair)', [[[480, 60], [480, -30], [720, -15]], 840],
   0.0],
  ['control 1', [[[600, -15], [1080, 30], [1080, 0]], 420], 0.0],
  ['control 2', [[[1140, 0], [480, -15]], 1110], -0.68]],
 [['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[540, 60]], 540], 60.0],
  ['regression: evening range end (partial repair)',
   [[[600, 0], [540, 0], [480, -60], [840, -30]], 1200], 0.0],
  ['control 1', [[[1080, 0]], 345], 0.0], ['control 2', [[[1080, 45]], 600], 0.0]]]
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: at last entry60.060.0Passed
normal: midway-30.0-30.0Passed
boundary: before first entry0.00.0Passed
regression: evening range end60.060.0Passed
regression: evening range end (partial repair)60.00.0Failed
control 10.00.0Passed
control 245.00.0Failed

SHA-256 / 5eb17668fdbce776714a912e3d11b2d96186bff284212a11c9d6633de90eb5d6

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(table, t):
    pts = {}
    for m, a in table:
        pts[m] = a
    keys = sorted(pts)
    if not keys or t < keys[0] or t > keys[-1]:
        return 0.0
    for m0, m1 in zip(keys, keys[1:]):
        if m0 <= t <= m1:
            a0, a1 = pts[m0], pts[m1]
            return round(a0 + (a1 - a0) * (t - m0) / (m1 - m0), 2)
    return round(float(pts[keys[0]]), 2)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[540, 60]], 540], 60.0],
  ['regression: evening range end (partial repair)', [[[420, 60]], 1110], 0.0],
  ['control 1', [[[840, 45], [1140, -60]], 300], 0.0],
  ['control 2', [[[540, -60], [600, 60], [480, 45]], 870], 0.0]],
 [['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[360, -30], [1140, 30], [840, -15]], 1140], 30.0],
  ['regression: evening range end (partial repair)',
   [[[600, 15], [420, -60], [360, 30], [540, 60]], 690], 0.0],
  ['control 1', [[[540, 30], [1020, 45]], 945], 42.66],
  ['control 2', [[[1140, -60], [360, -30], [900, 30], [900, 15], [480, 0]], 855], 13.39]],
 [['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[360, -30], [1140, 30], [840, -15]], 1140], 30.0],
  ['regression: evening range end (partial repair)', [[[660, 15], [1020, -45]], 1095], 0.0],
  ['control 1', [[[1080, -45], [540, 45]], 1110], 0.0],
  ['control 2', [[[600, 45], [1020, 45]], 585], 0.0]],
 [['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[540, 60]], 540], 60.0],
  ['regression: evening range end (partial repair)', [[[480, 60], [480, -30], [720, -15]], 840],
   0.0],
  ['control 1', [[[600, -15], [1080, 30], [1080, 0]], 420], 0.0],
  ['control 2', [[[1140, 0], [480, -15]], 1110], -0.68]],
 [['boundary: at last entry', [[[360, -60], [720, 0], [1080, 60]], 1080], 60.0],
  ['normal: midway', [[[360, -60], [720, 0], [1080, 60]], 540], -30.0],
  ['boundary: before first entry', [[[360, -60], [720, 0]], 300], 0.0],
  ['regression: evening range end', [[[540, 60]], 540], 60.0],
  ['regression: evening range end (partial repair)',
   [[[600, 0], [540, 0], [480, -60], [840, -30]], 1200], 0.0],
  ['control 1', [[[1080, 0]], 345], 0.0], ['control 2', [[[1080, 45]], 600], 0.0]]]
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: at last entry60.060.0Passed
normal: midway-30.0-30.0Passed
boundary: before first entry0.00.0Passed
regression: evening range end60.060.0Passed
regression: evening range end (partial repair)0.00.0Passed
control 10.00.0Passed
control 20.00.0Passed

SHA-256 / 83d85bf75c5f8610d365b70c2524e381c22b7cf052440b4557c7af42ff7284cd

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

Case digest / 18be045d410149e073a30ce2ce2b8f71b7c1706a42b4d9e2057bccbd72c664e5