FA-71041 / Weather index computation / Open access
Keetch-Byram style drought index: interception per spell · case 01
Several consecutive light showers never lower the index.
ROOT CAUSE
The 0.20 in interception is applied to each day instead of once per wet spell.
VERIFIED REPAIR
Reduce Q by the spell excess over 0.20 not already applied.
Unsuccessful approach: Using the cumulative spell excess re-applies earlier days of the spell.
Case contract
State Q in hundredths of an inch (0..800). Each day [tmax_F, rain_in]: rain days extend the current wet spell; the first 0.20 in of each spell is intercepted and only newly exceeding rain reduces Q by 100*net (floor 0). A dry day ends the spell. If tmax > 50, Q increases by (800-Q)*(0.968exp(0.0486T)-8.30)*0.001/(1+10.88exp(-0.0441*annual_in)), capped at 800. Return daily Q rounded to integers.
Why this case matters
Wildfire danger ratings and burn bans use the daily drought index.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(q0, annual_in, days):
q = float(q0)
spell = 0.0
out = []
for tmax, rain in days:
if rain > 0:
before = max(spell - 0.20, 0.0)
spell += rain
net = max(rain - 0.20, 0.0)
q = max(q - 100.0 * net, 0.0)
else:
spell = 0.0
if tmax > 50:
dq = (800.0 - q) * (0.968 * math.exp(0.0486 * tmax) - 8.30) * 0.001 / (1 + 10.88 * math.exp(-0.0441 * annual_in))
q = min(q + max(dq, 0.0), 800.0)
out.append(round(q))
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['station 0', [700, 50, [[70, 1.2], [85, 0.6], [55, 0.3], [50, 0], [55, 0], [45, 0.05], [55, 0.05]]], [602, 548, 519, 519, 519, 519, 520]], ['station 1', [150, 20, [[95, 0.1], [95, 0.15], [50, 0.15], [55, 1.2], [55, 0], [95, 0.15], [55, 0.6]]], [161, 166, 151, 32, 33, 45, 1]], ['station 2', [0, 20, [[85, 0.15], [50, 0.15], [95, 0], [70, 0], [95, 0.3], [85, 0], [50, 0.15]]], [8, 0, 13, 16, 19, 26, 26]], ['station 3', [150, 20, [[50, 0], [55, 0.05], [70, 0.1], [50, 0], [45, 0.3], [95, 0], [45, 0]]], [150, 151, 153, 153, 143, 154, 154]], ['station 4', [150, 20, [[70, 0.15], [45, 0.05], [45, 0], [50, 0.15], [95, 1.2], [85, 1.2], [55, 0]]], [152, 152, 152, 152, 50, 8, 8]], ['station 5', [400, 70, [[50, 0.1], [55, 0.1], [55, 0.6], [55, 1.2], [85, 0.15], [45, 0.3], [85, 0.3]]], [400, 402, 343, 225, 231, 201, 193]], ['station 6', [0, 70, [[70, 0.1], [70, 0.3], [55, 0.05], [85, 0], [95, 0.3], [55, 0.6], [50, 0]]], [11, 11, 9, 37, 73, 16, 16]], ['station 7', [400, 50, [[70, 0], [70, 0.05], [70, 0.15], [95, 0], [55, 0.05], [45, 0.6], [50, 0]]], [404, 408, 411, 427, 428, 383, 383]]], [['station 4', [150, 20, [[70, 0.15], [45, 0.05], [45, 0], [50, 0.15], [95, 1.2], [85, 1.2], [55, 0]]], [152, 152, 152, 152, 50, 8, 8]], ['station 7', [400, 50, [[70, 0], [70, 0.05], [70, 0.15], [95, 0], [55, 0.05], [45, 0.6], [50, 0]]], [404, 408, 411, 427, 428, 383, 383]], ['station 8', [150, 20, [[85, 0.3], [70, 0], [45, 0.1], [55, 0], [95, 1.2], [55, 0.6], [85, 0.15]]], [146, 149, 149, 149, 62, 2, 8]], ['station 9', [700, 50, [[95, 0.3], [50, 0], [95, 0], [45, 0.1], [85, 0.1], [70, 0.1], [45, 0.1]]], [694, 694, 699, 699, 701, 692, 682]], ['station 10', [0, 70, [[95, 1.2], [55, 0.3], [70, 0], [45, 0], [95, 0.15], [95, 0], [85, 0]]], [48, 21, 32, 32, 78, 121, 145]], ['station 11', [700, 50, [[45, 0.6], [45, 0], [50, 0.6], [95, 0.05], [95, 0.1], [85, 0], [50, 1.2]]], [660, 660, 620, 623, 620, 624, 524]], ['station 12', [700, 50, [[45, 0.6], [50, 0.15], [70, 0.15], [85, 0.3], [50, 0.05], [50, 0.05], [45, 0]]], [660, 645, 632, 606, 601, 596, 596]], ['station 13', [400, 50, [[45, 0.05], [50, 0], [95, 0.3], [85, 0.1], [55, 0.3], [45, 0], [95, 0.1]]], [400, 400, 407, 406, 377, 377, 395]]], [['station 7', [400, 50, [[70, 0], [70, 0.05], [70, 0.15], [95, 0], [55, 0.05], [45, 0.6], [50, 0]]], [404, 408, 411, 427, 428, 383, 383]], ['station 14', [0, 20, [[55, 0], [95, 0.05], [50, 0.3], [70, 0.15], [70, 0.1], [85, 0.15], [55, 0.15]]], [1, 14, 0, 3, 3, 8, 1]], ['station 15', [150, 20, [[50, 0.1], [55, 0], [45, 0], [70, 0.1], [85, 0], [45, 0], [70, 0.05]]], [150, 151, 151, 153, 159, 159, 162]], ['station 16', [400, 50, [[95, 0.05], [85, 1.2], [45, 0], [55, 0], [55, 0], [50, 1.2], [95, 1.2]]], [416, 323, 323, 324, 325, 225, 134]], ['station 17', [700, 70, [[70, 0.3], [50, 0.3], [50, 0.6], [55, 1.2], [50, 0], [45, 0], [70, 1.2]]], [692, 662, 602, 483, 483, 483, 389]], ['station 18', [150, 20, [[55, 1.2], [55, 0.1], [85, 0.1], [85, 0.6], [95, 0.05], [85, 0.6], [45, 0.6]]], [51, 42, 39, 8, 16, 8, 0]], ['station 19', [0, 70, [[85, 0.6], [45, 0.15], [50, 0], [55, 1.2], [50, 0], [85, 0.3], [95, 0.05]]], [28, 13, 13, 3, 3, 28, 69]], ['station 20', [150, 70, [[45, 0], [70, 0.3], [50, 0.6], [70, 0.3], [50, 0.05], [70, 0.3], [50, 0.3]]], [150, 149, 89, 69, 64, 45, 15]]], [['station 0', [700, 50, [[70, 1.2], [85, 0.6], [55, 0.3], [50, 0], [55, 0], [45, 0.05], [55, 0.05]]], [602, 548, 519, 519, 519, 519, 520]], ['station 10', [0, 70, [[95, 1.2], [55, 0.3], [70, 0], [45, 0], [95, 0.15], [95, 0], [85, 0]]], [48, 21, 32, 32, 78, 121, 145]], ['station 21', [150, 20, [[45, 0], [55, 0.1], [70, 0], [55, 0.15], [45, 0.15], [55, 0.1], [85, 0]]], [150, 151, 153, 154, 144, 134, 141]], ['station 22', [150, 20, [[45, 0.05], [70, 0], [50, 0], [85, 1.2], [50, 0], [95, 0], [95, 1.2]]], [150, 152, 152, 60, 60, 72, 13]], ['station 23', [400, 50, [[95, 0.1], [85, 0.3], [50, 0.6], [50, 1.2], [85, 1.2], [45, 0.3], [95, 0.3]]], [416, 406, 346, 226, 122, 92, 92]], ['wet spell of small showers', [300, 50, [[80, 0.1], [80, 0.1], [80, 0.1], [80, 0], [80, 0.15]]], [309, 318, 316, 325, 333]], ['exactly 50F day', [200, 40, [[50, 0], [50, 0]]], [200, 200]], ['downpour on dry soil', [40, 50, [[70, 1.0]]], [8]]], [['station 1', [150, 20, [[95, 0.1], [95, 0.15], [50, 0.15], [55, 1.2], [55, 0], [95, 0.15], [55, 0.6]]], [161, 166, 151, 32, 33, 45, 1]], ['station 2', [0, 20, [[85, 0.15], [50, 0.15], [95, 0], [70, 0], [95, 0.3], [85, 0], [50, 0.15]]], [8, 0, 13, 16, 19, 26, 26]], ['station 3', [150, 20, [[50, 0], [55, 0.05], [70, 0.1], [50, 0], [45, 0.3], [95, 0], [45, 0]]], [150, 151, 153, 153, 143, 154, 154]], ['station 4', [150, 20, [[70, 0.15], [45, 0.05], [45, 0], [50, 0.15], [95, 1.2], [85, 1.2], [55, 0]]], [152, 152, 152, 152, 50, 8, 8]], ['station 5', [400, 70, [[50, 0.1], [55, 0.1], [55, 0.6], [55, 1.2], [85, 0.15], [45, 0.3], [85, 0.3]]], [400, 402, 343, 225, 231, 201, 193]], ['station 6', [0, 70, [[70, 0.1], [70, 0.3], [55, 0.05], [85, 0], [95, 0.3], [55, 0.6], [50, 0]]], [11, 11, 9, 37, 73, 16, 16]], ['station 8', [150, 20, [[85, 0.3], [70, 0], [45, 0.1], [55, 0], [95, 1.2], [55, 0.6], [85, 0.15]]], [146, 149, 149, 149, 62, 2, 8]], ['station 13', [400, 50, [[45, 0.05], [50, 0], [95, 0.3], [85, 0.1], [55, 0.3], [45, 0], [95, 0.1]]], [400, 400, 407, 406, 377, 377, 395]]]]
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 |
|---|---|---|---|
| station 0 | [602, 568, 558, 558, 559, 559, 559] | [602, 548, 519, 519, 519, 519, 520] | Failed |
| station 1 | [161, 171, 171, 72, 73, 84, 45] | [161, 166, 151, 32, 33, 45, 1] | Failed |
| station 2 | [8, 8, 20, 23, 26, 34, 34] | [8, 0, 13, 16, 19, 26, 26] | Failed |
| station 3 | [150, 151, 153, 153, 143, 154, 154] | [150, 151, 153, 153, 143, 154, 154] | Passed |
| station 4 | [152, 152, 152, 152, 65, 8, 8] | [152, 152, 152, 152, 50, 8, 8] | Failed |
| station 5 | [400, 402, 363, 265, 284, 274, 282] | [400, 402, 343, 225, 231, 201, 193] | Failed |
| station 6 | [11, 12, 15, 42, 78, 41, 41] | [11, 11, 9, 37, 73, 16, 16] | Failed |
| station 7 | [404, 408, 411, 427, 428, 388, 388] | [404, 408, 411, 427, 428, 383, 383] | Failed |
SHA-256 / b8b43017e29703e68dbebca97f880e6ae37a18b8e8069ac3c499d5fcd4d88651
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(q0, annual_in, days):
q = float(q0)
spell = 0.0
out = []
for tmax, rain in days:
if rain > 0:
before = max(spell - 0.20, 0.0)
spell += rain
net = max(spell - 0.20, 0.0)
q = max(q - 100.0 * net, 0.0)
else:
spell = 0.0
if tmax > 50:
dq = (800.0 - q) * (0.968 * math.exp(0.0486 * tmax) - 8.30) * 0.001 / (1 + 10.88 * math.exp(-0.0441 * annual_in))
q = min(q + max(dq, 0.0), 800.0)
out.append(round(q))
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['station 0', [700, 50, [[70, 1.2], [85, 0.6], [55, 0.3], [50, 0], [55, 0], [45, 0.05], [55, 0.05]]], [602, 548, 519, 519, 519, 519, 520]], ['station 1', [150, 20, [[95, 0.1], [95, 0.15], [50, 0.15], [55, 1.2], [55, 0], [95, 0.15], [55, 0.6]]], [161, 166, 151, 32, 33, 45, 1]], ['station 2', [0, 20, [[85, 0.15], [50, 0.15], [95, 0], [70, 0], [95, 0.3], [85, 0], [50, 0.15]]], [8, 0, 13, 16, 19, 26, 26]], ['station 3', [150, 20, [[50, 0], [55, 0.05], [70, 0.1], [50, 0], [45, 0.3], [95, 0], [45, 0]]], [150, 151, 153, 153, 143, 154, 154]], ['station 4', [150, 20, [[70, 0.15], [45, 0.05], [45, 0], [50, 0.15], [95, 1.2], [85, 1.2], [55, 0]]], [152, 152, 152, 152, 50, 8, 8]], ['station 5', [400, 70, [[50, 0.1], [55, 0.1], [55, 0.6], [55, 1.2], [85, 0.15], [45, 0.3], [85, 0.3]]], [400, 402, 343, 225, 231, 201, 193]], ['station 6', [0, 70, [[70, 0.1], [70, 0.3], [55, 0.05], [85, 0], [95, 0.3], [55, 0.6], [50, 0]]], [11, 11, 9, 37, 73, 16, 16]], ['station 7', [400, 50, [[70, 0], [70, 0.05], [70, 0.15], [95, 0], [55, 0.05], [45, 0.6], [50, 0]]], [404, 408, 411, 427, 428, 383, 383]]], [['station 4', [150, 20, [[70, 0.15], [45, 0.05], [45, 0], [50, 0.15], [95, 1.2], [85, 1.2], [55, 0]]], [152, 152, 152, 152, 50, 8, 8]], ['station 7', [400, 50, [[70, 0], [70, 0.05], [70, 0.15], [95, 0], [55, 0.05], [45, 0.6], [50, 0]]], [404, 408, 411, 427, 428, 383, 383]], ['station 8', [150, 20, [[85, 0.3], [70, 0], [45, 0.1], [55, 0], [95, 1.2], [55, 0.6], [85, 0.15]]], [146, 149, 149, 149, 62, 2, 8]], ['station 9', [700, 50, [[95, 0.3], [50, 0], [95, 0], [45, 0.1], [85, 0.1], [70, 0.1], [45, 0.1]]], [694, 694, 699, 699, 701, 692, 682]], ['station 10', [0, 70, [[95, 1.2], [55, 0.3], [70, 0], [45, 0], [95, 0.15], [95, 0], [85, 0]]], [48, 21, 32, 32, 78, 121, 145]], ['station 11', [700, 50, [[45, 0.6], [45, 0], [50, 0.6], [95, 0.05], [95, 0.1], [85, 0], [50, 1.2]]], [660, 660, 620, 623, 620, 624, 524]], ['station 12', [700, 50, [[45, 0.6], [50, 0.15], [70, 0.15], [85, 0.3], [50, 0.05], [50, 0.05], [45, 0]]], [660, 645, 632, 606, 601, 596, 596]], ['station 13', [400, 50, [[45, 0.05], [50, 0], [95, 0.3], [85, 0.1], [55, 0.3], [45, 0], [95, 0.1]]], [400, 400, 407, 406, 377, 377, 395]]], [['station 7', [400, 50, [[70, 0], [70, 0.05], [70, 0.15], [95, 0], [55, 0.05], [45, 0.6], [50, 0]]], [404, 408, 411, 427, 428, 383, 383]], ['station 14', [0, 20, [[55, 0], [95, 0.05], [50, 0.3], [70, 0.15], [70, 0.1], [85, 0.15], [55, 0.15]]], [1, 14, 0, 3, 3, 8, 1]], ['station 15', [150, 20, [[50, 0.1], [55, 0], [45, 0], [70, 0.1], [85, 0], [45, 0], [70, 0.05]]], [150, 151, 151, 153, 159, 159, 162]], ['station 16', [400, 50, [[95, 0.05], [85, 1.2], [45, 0], [55, 0], [55, 0], [50, 1.2], [95, 1.2]]], [416, 323, 323, 324, 325, 225, 134]], ['station 17', [700, 70, [[70, 0.3], [50, 0.3], [50, 0.6], [55, 1.2], [50, 0], [45, 0], [70, 1.2]]], [692, 662, 602, 483, 483, 483, 389]], ['station 18', [150, 20, [[55, 1.2], [55, 0.1], [85, 0.1], [85, 0.6], [95, 0.05], [85, 0.6], [45, 0.6]]], [51, 42, 39, 8, 16, 8, 0]], ['station 19', [0, 70, [[85, 0.6], [45, 0.15], [50, 0], [55, 1.2], [50, 0], [85, 0.3], [95, 0.05]]], [28, 13, 13, 3, 3, 28, 69]], ['station 20', [150, 70, [[45, 0], [70, 0.3], [50, 0.6], [70, 0.3], [50, 0.05], [70, 0.3], [50, 0.3]]], [150, 149, 89, 69, 64, 45, 15]]], [['station 0', [700, 50, [[70, 1.2], [85, 0.6], [55, 0.3], [50, 0], [55, 0], [45, 0.05], [55, 0.05]]], [602, 548, 519, 519, 519, 519, 520]], ['station 10', [0, 70, [[95, 1.2], [55, 0.3], [70, 0], [45, 0], [95, 0.15], [95, 0], [85, 0]]], [48, 21, 32, 32, 78, 121, 145]], ['station 21', [150, 20, [[45, 0], [55, 0.1], [70, 0], [55, 0.15], [45, 0.15], [55, 0.1], [85, 0]]], [150, 151, 153, 154, 144, 134, 141]], ['station 22', [150, 20, [[45, 0.05], [70, 0], [50, 0], [85, 1.2], [50, 0], [95, 0], [95, 1.2]]], [150, 152, 152, 60, 60, 72, 13]], ['station 23', [400, 50, [[95, 0.1], [85, 0.3], [50, 0.6], [50, 1.2], [85, 1.2], [45, 0.3], [95, 0.3]]], [416, 406, 346, 226, 122, 92, 92]], ['wet spell of small showers', [300, 50, [[80, 0.1], [80, 0.1], [80, 0.1], [80, 0], [80, 0.15]]], [309, 318, 316, 325, 333]], ['exactly 50F day', [200, 40, [[50, 0], [50, 0]]], [200, 200]], ['downpour on dry soil', [40, 50, [[70, 1.0]]], [8]]], [['station 1', [150, 20, [[95, 0.1], [95, 0.15], [50, 0.15], [55, 1.2], [55, 0], [95, 0.15], [55, 0.6]]], [161, 166, 151, 32, 33, 45, 1]], ['station 2', [0, 20, [[85, 0.15], [50, 0.15], [95, 0], [70, 0], [95, 0.3], [85, 0], [50, 0.15]]], [8, 0, 13, 16, 19, 26, 26]], ['station 3', [150, 20, [[50, 0], [55, 0.05], [70, 0.1], [50, 0], [45, 0.3], [95, 0], [45, 0]]], [150, 151, 153, 153, 143, 154, 154]], ['station 4', [150, 20, [[70, 0.15], [45, 0.05], [45, 0], [50, 0.15], [95, 1.2], [85, 1.2], [55, 0]]], [152, 152, 152, 152, 50, 8, 8]], ['station 5', [400, 70, [[50, 0.1], [55, 0.1], [55, 0.6], [55, 1.2], [85, 0.15], [45, 0.3], [85, 0.3]]], [400, 402, 343, 225, 231, 201, 193]], ['station 6', [0, 70, [[70, 0.1], [70, 0.3], [55, 0.05], [85, 0], [95, 0.3], [55, 0.6], [50, 0]]], [11, 11, 9, 37, 73, 16, 16]], ['station 8', [150, 20, [[85, 0.3], [70, 0], [45, 0.1], [55, 0], [95, 1.2], [55, 0.6], [85, 0.15]]], [146, 149, 149, 149, 62, 2, 8]], ['station 13', [400, 50, [[45, 0.05], [50, 0], [95, 0.3], [85, 0.1], [55, 0.3], [45, 0], [95, 0.1]]], [400, 400, 407, 406, 377, 377, 395]]]]
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 |
|---|---|---|---|
| station 0 | [602, 450, 262, 262, 263, 263, 265] | [602, 548, 519, 519, 519, 519, 520] | Failed |
| station 1 | [161, 166, 146, 7, 8, 21, 1] | [161, 166, 151, 32, 33, 45, 1] | Failed |
| station 2 | [8, 0, 13, 16, 19, 26, 26] | [8, 0, 13, 16, 19, 26, 26] | Passed |
| station 3 | [150, 151, 153, 153, 143, 154, 154] | [150, 151, 153, 153, 143, 154, 154] | Passed |
| station 4 | [152, 152, 152, 152, 50, 8, 8] | [152, 152, 152, 152, 50, 8, 8] | Passed |
| station 5 | [400, 402, 343, 166, 28, 0, 28] | [400, 402, 343, 225, 231, 201, 193] | Failed |
| station 6 | [11, 11, 3, 31, 67, 3, 3] | [11, 11, 9, 37, 73, 16, 16] | Failed |
| station 7 | [404, 408, 411, 427, 428, 383, 383] | [404, 408, 411, 427, 428, 383, 383] | Passed |
SHA-256 / 7cbe2f0e19d32fcc581932d9a25a8485acc8e3760caa2f9e0b5edf31aeb9dfcd
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(q0, annual_in, days):
q = float(q0)
spell = 0.0
out = []
for tmax, rain in days:
if rain > 0:
before = max(spell - 0.20, 0.0)
spell += rain
net = max(spell - 0.20, 0.0) - before
q = max(q - 100.0 * net, 0.0)
else:
spell = 0.0
if tmax > 50:
dq = (800.0 - q) * (0.968 * math.exp(0.0486 * tmax) - 8.30) * 0.001 / (1 + 10.88 * math.exp(-0.0441 * annual_in))
q = min(q + max(dq, 0.0), 800.0)
out.append(round(q))
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['station 0', [700, 50, [[70, 1.2], [85, 0.6], [55, 0.3], [50, 0], [55, 0], [45, 0.05], [55, 0.05]]], [602, 548, 519, 519, 519, 519, 520]], ['station 1', [150, 20, [[95, 0.1], [95, 0.15], [50, 0.15], [55, 1.2], [55, 0], [95, 0.15], [55, 0.6]]], [161, 166, 151, 32, 33, 45, 1]], ['station 2', [0, 20, [[85, 0.15], [50, 0.15], [95, 0], [70, 0], [95, 0.3], [85, 0], [50, 0.15]]], [8, 0, 13, 16, 19, 26, 26]], ['station 3', [150, 20, [[50, 0], [55, 0.05], [70, 0.1], [50, 0], [45, 0.3], [95, 0], [45, 0]]], [150, 151, 153, 153, 143, 154, 154]], ['station 4', [150, 20, [[70, 0.15], [45, 0.05], [45, 0], [50, 0.15], [95, 1.2], [85, 1.2], [55, 0]]], [152, 152, 152, 152, 50, 8, 8]], ['station 5', [400, 70, [[50, 0.1], [55, 0.1], [55, 0.6], [55, 1.2], [85, 0.15], [45, 0.3], [85, 0.3]]], [400, 402, 343, 225, 231, 201, 193]], ['station 6', [0, 70, [[70, 0.1], [70, 0.3], [55, 0.05], [85, 0], [95, 0.3], [55, 0.6], [50, 0]]], [11, 11, 9, 37, 73, 16, 16]], ['station 7', [400, 50, [[70, 0], [70, 0.05], [70, 0.15], [95, 0], [55, 0.05], [45, 0.6], [50, 0]]], [404, 408, 411, 427, 428, 383, 383]]], [['station 4', [150, 20, [[70, 0.15], [45, 0.05], [45, 0], [50, 0.15], [95, 1.2], [85, 1.2], [55, 0]]], [152, 152, 152, 152, 50, 8, 8]], ['station 7', [400, 50, [[70, 0], [70, 0.05], [70, 0.15], [95, 0], [55, 0.05], [45, 0.6], [50, 0]]], [404, 408, 411, 427, 428, 383, 383]], ['station 8', [150, 20, [[85, 0.3], [70, 0], [45, 0.1], [55, 0], [95, 1.2], [55, 0.6], [85, 0.15]]], [146, 149, 149, 149, 62, 2, 8]], ['station 9', [700, 50, [[95, 0.3], [50, 0], [95, 0], [45, 0.1], [85, 0.1], [70, 0.1], [45, 0.1]]], [694, 694, 699, 699, 701, 692, 682]], ['station 10', [0, 70, [[95, 1.2], [55, 0.3], [70, 0], [45, 0], [95, 0.15], [95, 0], [85, 0]]], [48, 21, 32, 32, 78, 121, 145]], ['station 11', [700, 50, [[45, 0.6], [45, 0], [50, 0.6], [95, 0.05], [95, 0.1], [85, 0], [50, 1.2]]], [660, 660, 620, 623, 620, 624, 524]], ['station 12', [700, 50, [[45, 0.6], [50, 0.15], [70, 0.15], [85, 0.3], [50, 0.05], [50, 0.05], [45, 0]]], [660, 645, 632, 606, 601, 596, 596]], ['station 13', [400, 50, [[45, 0.05], [50, 0], [95, 0.3], [85, 0.1], [55, 0.3], [45, 0], [95, 0.1]]], [400, 400, 407, 406, 377, 377, 395]]], [['station 7', [400, 50, [[70, 0], [70, 0.05], [70, 0.15], [95, 0], [55, 0.05], [45, 0.6], [50, 0]]], [404, 408, 411, 427, 428, 383, 383]], ['station 14', [0, 20, [[55, 0], [95, 0.05], [50, 0.3], [70, 0.15], [70, 0.1], [85, 0.15], [55, 0.15]]], [1, 14, 0, 3, 3, 8, 1]], ['station 15', [150, 20, [[50, 0.1], [55, 0], [45, 0], [70, 0.1], [85, 0], [45, 0], [70, 0.05]]], [150, 151, 151, 153, 159, 159, 162]], ['station 16', [400, 50, [[95, 0.05], [85, 1.2], [45, 0], [55, 0], [55, 0], [50, 1.2], [95, 1.2]]], [416, 323, 323, 324, 325, 225, 134]], ['station 17', [700, 70, [[70, 0.3], [50, 0.3], [50, 0.6], [55, 1.2], [50, 0], [45, 0], [70, 1.2]]], [692, 662, 602, 483, 483, 483, 389]], ['station 18', [150, 20, [[55, 1.2], [55, 0.1], [85, 0.1], [85, 0.6], [95, 0.05], [85, 0.6], [45, 0.6]]], [51, 42, 39, 8, 16, 8, 0]], ['station 19', [0, 70, [[85, 0.6], [45, 0.15], [50, 0], [55, 1.2], [50, 0], [85, 0.3], [95, 0.05]]], [28, 13, 13, 3, 3, 28, 69]], ['station 20', [150, 70, [[45, 0], [70, 0.3], [50, 0.6], [70, 0.3], [50, 0.05], [70, 0.3], [50, 0.3]]], [150, 149, 89, 69, 64, 45, 15]]], [['station 0', [700, 50, [[70, 1.2], [85, 0.6], [55, 0.3], [50, 0], [55, 0], [45, 0.05], [55, 0.05]]], [602, 548, 519, 519, 519, 519, 520]], ['station 10', [0, 70, [[95, 1.2], [55, 0.3], [70, 0], [45, 0], [95, 0.15], [95, 0], [85, 0]]], [48, 21, 32, 32, 78, 121, 145]], ['station 21', [150, 20, [[45, 0], [55, 0.1], [70, 0], [55, 0.15], [45, 0.15], [55, 0.1], [85, 0]]], [150, 151, 153, 154, 144, 134, 141]], ['station 22', [150, 20, [[45, 0.05], [70, 0], [50, 0], [85, 1.2], [50, 0], [95, 0], [95, 1.2]]], [150, 152, 152, 60, 60, 72, 13]], ['station 23', [400, 50, [[95, 0.1], [85, 0.3], [50, 0.6], [50, 1.2], [85, 1.2], [45, 0.3], [95, 0.3]]], [416, 406, 346, 226, 122, 92, 92]], ['wet spell of small showers', [300, 50, [[80, 0.1], [80, 0.1], [80, 0.1], [80, 0], [80, 0.15]]], [309, 318, 316, 325, 333]], ['exactly 50F day', [200, 40, [[50, 0], [50, 0]]], [200, 200]], ['downpour on dry soil', [40, 50, [[70, 1.0]]], [8]]], [['station 1', [150, 20, [[95, 0.1], [95, 0.15], [50, 0.15], [55, 1.2], [55, 0], [95, 0.15], [55, 0.6]]], [161, 166, 151, 32, 33, 45, 1]], ['station 2', [0, 20, [[85, 0.15], [50, 0.15], [95, 0], [70, 0], [95, 0.3], [85, 0], [50, 0.15]]], [8, 0, 13, 16, 19, 26, 26]], ['station 3', [150, 20, [[50, 0], [55, 0.05], [70, 0.1], [50, 0], [45, 0.3], [95, 0], [45, 0]]], [150, 151, 153, 153, 143, 154, 154]], ['station 4', [150, 20, [[70, 0.15], [45, 0.05], [45, 0], [50, 0.15], [95, 1.2], [85, 1.2], [55, 0]]], [152, 152, 152, 152, 50, 8, 8]], ['station 5', [400, 70, [[50, 0.1], [55, 0.1], [55, 0.6], [55, 1.2], [85, 0.15], [45, 0.3], [85, 0.3]]], [400, 402, 343, 225, 231, 201, 193]], ['station 6', [0, 70, [[70, 0.1], [70, 0.3], [55, 0.05], [85, 0], [95, 0.3], [55, 0.6], [50, 0]]], [11, 11, 9, 37, 73, 16, 16]], ['station 8', [150, 20, [[85, 0.3], [70, 0], [45, 0.1], [55, 0], [95, 1.2], [55, 0.6], [85, 0.15]]], [146, 149, 149, 149, 62, 2, 8]], ['station 13', [400, 50, [[45, 0.05], [50, 0], [95, 0.3], [85, 0.1], [55, 0.3], [45, 0], [95, 0.1]]], [400, 400, 407, 406, 377, 377, 395]]]]
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 |
|---|---|---|---|
| station 0 | [602, 548, 519, 519, 519, 519, 520] | [602, 548, 519, 519, 519, 519, 520] | Passed |
| station 1 | [161, 166, 151, 32, 33, 45, 1] | [161, 166, 151, 32, 33, 45, 1] | Passed |
| station 2 | [8, 0, 13, 16, 19, 26, 26] | [8, 0, 13, 16, 19, 26, 26] | Passed |
| station 3 | [150, 151, 153, 153, 143, 154, 154] | [150, 151, 153, 153, 143, 154, 154] | Passed |
| station 4 | [152, 152, 152, 152, 50, 8, 8] | [152, 152, 152, 152, 50, 8, 8] | Passed |
| station 5 | [400, 402, 343, 225, 231, 201, 193] | [400, 402, 343, 225, 231, 201, 193] | Passed |
| station 6 | [11, 11, 9, 37, 73, 16, 16] | [11, 11, 9, 37, 73, 16, 16] | Passed |
| station 7 | [404, 408, 411, 427, 428, 383, 383] | [404, 408, 411, 427, 428, 383, 383] | Passed |
SHA-256 / 5d8f2929a5c1cc970ce9b5409d3f6845dcaa3bab182326d94fa194fedd6f9c08
Verification & scope
Stipulated deterministic teaching model of an operational weather index; coefficients and thresholds are fixed by the contract and no claim of standards conformance is made. 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:48:26.135079+00:00.
Case digest / ef28b690299116086e280fa2218ff7acbc8c68e9290f4920890ee2389c18d867