1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env python
"""E30a: does making wind set ignition *time* instead of ignition *chance*
give a spread kernel whose elongation survives at any fire size?
Runs `cella_lib/examples/wildfire_ros` (a synthetic-grid example, no
scenario needed) in its three E30a modes and combines their JSON reports
into one file:
- `arrival_flat`: the E19 flat-grid front-speed table, both spread rules
(`bernoulli`/`arrival`), wind 0/2/5/8 m/s, p0 0.12/0.22/0.44, burn
duration 5/10, 3 seeds.
- `illuminate`: point-ignition elongation (E12's second-moment measure) at
2/5/10/20 % burned on a 400x400 uniform grid, 8 m/s toward +x, both
rules, 3 seeds.
- `lb`: length-to-breadth table on the arrival rule at 10,000-/20,000-cell
checkpoints for 2/5/8 m/s, `c2` in {0.131, 0.2, 0.3, 0.45} under the
exponential wind law plus the rear-focus law, against Anderson (1983)'s
`LB(U)`, the closed-form head:back ratio at 0.6 m/s for both laws, and
(fix round 3) two extra jitter-0 rear-focus template points bisected to
hit Anderson LB 1.2 and 2.0 exactly (tagged with a `template_lb` field;
`null` on every other row).
- `head_speed`: measured vs. closed-form head speed (arrival rule, jitter
0), both wind laws, added in fix round 2.
Each mode's own `binary_git`/`binary_built_utc` (stamped by
`cella_lib/build.rs`) must agree; the script fails loudly if they don't
(a rebuild happened mid-run) rather than silently mixing binaries.
Output: `validation/results/experiments/exp30a_arrival_flat.json`, one
object with `binary_git`, `binary_built_utc`, `speed_table`,
`illuminate`, `lb`, `head_speed`. Running this script regenerates the
whole file in one command (all four modes, always).
Usage (from repo root): `python3 validation/scripts/experiments/exp_r6_arrival_flat.py`
"""
import json
import subprocess
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parents[3]
VAL = REPO / "validation"
OUT = VAL / "results" / "experiments" / "exp30a_arrival_flat.json"
BIN = REPO / "cella_lib" / "target" / "release" / "examples" / "wildfire_ros"
def run_mode(mode: str) -> dict:
proc = subprocess.run([str(BIN), mode], check=True, capture_output=True, text=True)
sys.stderr.write(proc.stderr)
return json.loads(proc.stdout)
def main() -> None:
if not BIN.exists():
raise SystemExit(
f"{BIN} not built — run: cd cella_lib && cargo build --release --examples"
)
modes = ["arrival_flat", "illuminate", "lb", "head_speed"]
reports = {m: run_mode(m) for m in modes}
gits = {r["binary_git"] for r in reports.values()}
builts = {r["binary_built_utc"] for r in reports.values()}
if len(gits) != 1 or len(builts) != 1:
raise SystemExit(f"binary_git/binary_built_utc disagree across modes: {reports}")
binary_git = gits.pop()
if binary_git.endswith("-dirty"):
print(f"WARNING: binary_git is dirty ({binary_git}) — not a reproducible run", file=sys.stderr)
combined = {
"binary_git": binary_git,
"binary_built_utc": builts.pop(),
"speed_table": reports["arrival_flat"]["results"],
"illuminate": reports["illuminate"]["results"],
"lb": reports["lb"]["results"],
"head_speed": reports["head_speed"]["results"],
}
OUT.parent.mkdir(parents=True, exist_ok=True)
OUT.write_text(json.dumps(combined, indent=1))
print(
f"wrote {OUT} (binary_git {combined['binary_git']}): "
f"{len(combined['speed_table'])} speed rows, "
f"{len(combined['illuminate'])} illuminate rows, "
f"{len(combined['lb'])} lb rows, "
f"{len(combined['head_speed'])} head_speed rows",
flush=True,
)
if __name__ == "__main__":
main()