import json
import os
import shutil
import subprocess
import sys
from pathlib import Path
import numpy as np
REPO = Path(__file__).resolve().parents[3]
VAL = REPO / "validation"
EXP = VAL / "results" / "experiments"
OUT = EXP / "exp16_stopping"
BIN = REPO / "cella_lib/target/release/examples/wildfire_experiment"
BEST = {"Bear_2020": (0.12, 10), "Brattain_2020": (0.22, 10),
"Buck_2017": (0.16, 5), "Chimney_2016": (0.30, 5)}
SEEDS = 3
def run(fire, p0, dur, label, density=None, p0_scale=None):
tmp = EXP / "tmp_stopping"
if tmp.exists():
shutil.rmtree(tmp)
shutil.copytree(VAL / "data" / "scenarios" / fire, tmp)
cfg = json.loads((tmp / "config.json").read_text())
cfg["model"]["wildfire"]["params"]["p0"] = p0
cfg["model"]["wildfire"]["params"]["burn_duration"] = dur
if density is not None:
cfg["model"]["wildfire"]["env"]["density"] = density
(tmp / "config.json").write_text(json.dumps(cfg))
env = dict(os.environ)
if p0_scale is not None:
sc = json.loads((tmp / "scenario.json").read_text())
assert len(p0_scale) == len(sc["wind"]) - 1
(tmp / "p0_scale.json").write_text(json.dumps(p0_scale))
env["EXP_P0_SCALE"] = str(tmp / "p0_scale.json")
rep = OUT / f"{fire}_{label}.json"
subprocess.run([str(BIN), str(tmp), str(SEEDS), str(rep)], check=True,
capture_output=True, env=env)
r = json.loads(rep.read_text())
s, rad = r["model"][1:], r["radial"][1:]
row = {"fire": fire, "variant": label, "seeds": SEEDS, "p0": p0, "dur": dur,
"mean_iou": sum(x["iou"] for x in s) / len(s), "final_iou": r["final_iou_model"],
"mean_iou_radial": sum(x["iou"] for x in rad) / len(rad),
"area_ratio": s[-1]["sim_burned"] / s[-1]["obs_burned"],
"arrival_mae": r["model_arrival_mae_hours"]}
beat = "BEATS" if row["mean_iou"] > row["mean_iou_radial"] else "loses"
print(f"{fire:14s} {label:18s} mean IoU {row['mean_iou']:.3f} final {row['final_iou']:.3f} "
f"(circle {row['mean_iou_radial']:.3f} {beat}) area x{row['area_ratio']:.2f} "
f"MAE {row['arrival_mae']:.0f}h", flush=True)
return row
def main():
OUT.mkdir(parents=True, exist_ok=True)
rows = []
for fire, (p0, dur) in BEST.items():
sc = json.loads((VAL / "data" / "scenarios" / fire / "scenario.json").read_text())
n_cells = sc["grid"]["width"] * sc["grid"]["height"]
windows = sc["wind"][:-1]
rows.append(run(fire, p0, dur, "ctrl"))
rng = np.random.default_rng(7)
for sigma in (0.3, 0.6, 1.0):
dens = rng.lognormal(-sigma * sigma / 2.0, sigma, n_cells).astype(np.float32)
dens_list = [float(x) for x in dens]
for mult in (1.0, 1.5, 2.0):
rows.append(run(fire, p0 * mult, dur, f"hetero{sigma:g}_p{mult:g}", density=dens_list))
for tau_days in (5, 10, 20):
scale = [float(np.exp(-w["hours"] / (24.0 * tau_days))) for w in windows]
for mult in (1.0, 1.5, 2.0):
rows.append(run(fire, p0 * mult, dur, f"contain{tau_days}_p{mult:g}", p0_scale=scale))
(EXP / "exp16_stopping.json").write_text(json.dumps(rows, indent=1))
if __name__ == "__main__":
main()