2"""Turn a `run_channel_18.py` CHECKPOINT into the profiles NPZ, without waiting for a chunk to end.
4The running sums live in the checkpoint (`acc_*`), so the campaign can be read at any moment —
5which is the point: `stats.npz` is only written when a chunk finishes, and a chunk is hours.
7 python profiles_from_ckpt.py runs/prod/ckpt.npz [--out stats_now.npz]
9It reuses `run_channel_18.py`'s own `profiles()` so there is one definition of what the profiles
10are; that import needs `peclet.flow` on the path (any build — the transformation touches no solver
18CK = sys.argv[1]
if len(sys.argv) > 1
else "runs/prod/ckpt.npz"
19OUT = sys.argv[sys.argv.index(
"--out") + 1]
if "--out" in sys.argv
else "stats_now.npz"
23sys.argv = [sys.argv[0],
"--ny", str(ny)]
24sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
25import run_channel_18
as R
29 acc[k] = np.array(z[
"acc_" + k])
30acc[
"_n"] = float(z[
"acc_n"])
31acc[
"_t0"] = float(z[
"acc_t0"])
32acc[
"_t1"] = float(z[
"acc_t1"])
34 raise SystemExit(f
"{CK}: no samples accumulated yet (still inside the discarded transient)")
38np.savez(OUT, **{k: np.asarray(v)
for k, v
in pr.items()},
39 turnovers=np.float64(t * R.UTAU), step=np.int64(z[
"step"]),
40 grid=np.asarray([R.NX, R.NY, R.NZ]), utau=np.float64(R.UTAU))
41print(f
"wrote {OUT}: {int(acc['_n'])} samples over t u_tau/h "
42 f
"{pr['window_turnovers'][0]:.3f} .. {pr['window_turnovers'][1]:.3f}, "
43 f
"run at {t*R.UTAU:.3f} turnovers (step {int(z['step'])})")
44print(f
" u_tau: imposed {R.UTAU:.6f}, wall gradient {pr['utau_wall']:.6f} "
45 f
"({100*(pr['utau_wall']/R.UTAU-1):+.1f} %; walls {pr['utau_lo']:.6f} / {pr['utau_hi']:.6f})")
47print(f
" centreline: <alpha> {pr['alpha'][j]:.5f}, <u>_liq/u_tau {pr['u_liq'][j]/R.UTAU:.3f}")
48k = int(np.argmax(pr[
'u_liq']))
49print(f
" peak <u>_liq/u_tau {pr['u_liq'][k]/R.UTAU:.3f} at y/h = {pr['y'][k]:.3f}")