FA-71051 / Weather index computation / Open access
Keetch-Byram style drought index: drying temperature gate · case 01
Exactly 50 degF days dry the fuels.
ROOT CAUSE
The drying gate includes 50 degF.
VERIFIED REPAIR
Apply the drought increment only above 50 degF.
Unsuccessful approach: Raising the gate to 60 degF skips drying on 51 to 60 degF days.
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(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 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 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 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 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]]]]
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, 520, 520, 521] | [602, 548, 519, 519, 519, 519, 520] | Failed |
| station 1 | [161, 166, 151, 32, 33, 45, 1] | [161, 166, 151, 32, 33, 45, 1] | Passed |
| station 2 | [8, 0, 13, 16, 19, 27, 27] | [8, 0, 13, 16, 19, 26, 26] | Failed |
| station 3 | [150, 151, 153, 154, 144, 154, 154] | [150, 151, 153, 153, 143, 154, 154] | Failed |
| station 4 | [152, 152, 152, 153, 50, 8, 8] | [152, 152, 152, 152, 50, 8, 8] | Failed |
| station 5 | [401, 402, 344, 226, 232, 202, 193] | [400, 402, 343, 225, 231, 201, 193] | Failed |
| station 6 | [11, 11, 9, 37, 73, 16, 17] | [11, 11, 9, 37, 73, 16, 16] | Failed |
| station 7 | [404, 408, 411, 427, 428, 383, 384] | [404, 408, 411, 427, 428, 383, 383] | Failed |
SHA-256 / 248681a7be5c802e0addf506a24d4325c3da235a8eb505c857bbe3993024f00c
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) - before
q = max(q - 100.0 * net, 0.0)
else:
spell = 0.0
if tmax > 60:
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 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 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 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 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]]]]
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, 518, 518, 518, 518, 518] | [602, 548, 519, 519, 519, 519, 520] | Failed |
| station 1 | [161, 166, 151, 31, 31, 44, 0] | [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, 150, 152, 152, 142, 153, 153] | [150, 151, 153, 153, 143, 154, 154] | Failed |
| station 4 | [152, 152, 152, 152, 50, 8, 8] | [152, 152, 152, 152, 50, 8, 8] | Passed |
| station 5 | [400, 400, 340, 220, 226, 196, 188] | [400, 402, 343, 225, 231, 201, 193] | Failed |
| station 6 | [11, 11, 6, 34, 70, 10, 10] | [11, 11, 9, 37, 73, 16, 16] | Failed |
| station 7 | [404, 408, 411, 427, 427, 382, 382] | [404, 408, 411, 427, 428, 383, 383] | Failed |
SHA-256 / f89073932b4c890e24ece3ffd17ade92922265f3a4005a9115031725f1c2bf67
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 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 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 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 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]]]]
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 / 17e8c28f1a966469a3c10d6bf18d053711f15b4bfed42f519388707f49124a3b
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.222076+00:00.
Case digest / f79e2aea4d068aee532cb40542d5e7375645a3b9285181e1f2c01a257888c10d