flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
d1_tolerance.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""D1 -- the pressure-tolerance study of doc/vof_step_performance_design.md sec. 5.12 (WO-0 starts
3it, WO-13 analyses it). An instrument, not a gate: it records, it does not judge.
4
5For every pressure-driver rtol in --rtols (default 1e-10 [reference], 1e-9, 1e-8, 1e-7, 1e-6; the
6momentum solve follows the pressure driver's rtol by default) it runs up to three cases and writes
7one JSON per (case, rtol) into --out:
8
9 static tests/study/vof_surface_tension.py's static droplet (all resolution rungs, 60 steps
10 each): max|u| per rung -> static_rtol<R>.json
11 hysing Hysing case 1 through the BLOCK path (tests/study/vof_blocks_ns.py run_hysing, nx 64,
12 T = 3): peak rise velocity, its time, final centroid, volume drift -> hysing_rtol<R>.json
13 column the bubble column from ckpt_t43, --column-steps steps (default 2000), MG-PCG at the
14 rtol, device flux constraint (the production protocol of run_peclet.py): per step dt,
15 pressure iterations, momentum residual and time (the Python API exposes no momentum
16 iteration count), max_open_divergence_projected(), every marker's volume and
17 x-velocity; every 50 steps run_peclet.sample() (gas volume, drift = rise speed)
18 -> column_rtol<R>.json
19
20A (case, rtol) whose JSON already exists is skipped, so re-running resumes.
21
22Usage:
23 PYTHONPATH=<flow build> OMP_NUM_THREADS=8 OMP_PROC_BIND=false \\
24 python tests/study/vof_perf/d1_tolerance.py --out DIR [--cases static,hysing,column]
25 [--rtols 1e-10,1e-9,1e-8,1e-7,1e-6] [--column-steps 2000] [--ckpt PATH] [--case-dir DIR]
26"""
27import json
28import os
29import sys
30import time
31
32import numpy as np
33
34A = list(sys.argv)
35
36
37def arg(name, default, cast=str):
38 return cast(A[A.index(name) + 1]) if name in A else default
39
40
41OUT = arg("--out", "d1")
42CASES = arg("--cases", "static,hysing,column").split(",")
43RTOLS = [float(r) for r in arg("--rtols", "1e-10,1e-9,1e-8,1e-7,1e-6").split(",")]
44NCOL = arg("--column-steps", 2000, int)
45CASE_DIR = arg("--case-dir", os.path.expanduser(
46 "~/Codes/peclet-examples/benchmarks/bubble-column/scripts"))
47CKPT = arg("--ckpt", os.path.expanduser(
48 "~/Codes/bubble_column_perf/ckpt_t43.npz"))
49
50# the two study scripts read sys.argv at import: hand them none of ours
51sys.argv = [sys.argv[0]]
52sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
53import vof_surface_tension as vst # noqa: E402
54import vof_blocks_ns as vbn # noqa: E402
55sys.path.insert(0, CASE_DIR)
56import run_peclet as rp # noqa: E402
57import peclet.flow as pf # noqa: E402
58
59
60def run_static(rtol):
61 vst.RTOL = rtol
62 vst.gate_static()
63 return {"rungs": list(vst.STATIC_RESULTS),
64 "max_u": max(r["max_u"] for r in vst.STATIC_RESULTS)}
65
66
67def run_hysing(rtol):
68 vbn.RTOL = rtol
69 r = vbn.run_hysing(64, True, 3.0, verbose=False)
70 return {"vmax": r["vmax"], "tvmax": r["tvmax"], "yc": r["yc"], "vdrift": r["vdrift"],
71 "steps": r["steps"], "p_iters_max": r["solve"].iters, "div_max": r["solve"].div,
72 "capped": not r["solve"].valid}
73
74
75def run_column(rtol):
76 step, t, blocks, vel, pres, series = rp.load_ckpt(CKPT)
77 s = rp.build(blocks)
78 for c, a in enumerate(vel):
79 s.set_velocity(c, a)
80 s.set_field("p", pres)
81 s.diagnostics.set_vof_step_parity(step)
82 s.set_pressure_pcg(True, 800, rtol)
83 v0 = np.array([b["volume"] for b in s.diagnostics.vof_block_stats()])
84 rec = {k: [] for k in ("t", "dt", "p_iters", "mom_residual", "mom_time", "step_time",
85 "div_proj", "vol", "ux")}
86 samples = [rp.sample(s, t)]
87 for i in range(NCOL):
88 L = s.vof_step_limits()
89 dt = min(0.25 * L["cfl_dt"], 0.25 * L["capillary_dt"])
90 s.set_dt(dt)
91 s.step()
92 t += dt
93 d = s.diagnostics
94 tm = d.last_step_timers()
95 st = d.vof_block_stats()
96 rec["t"].append(t)
97 rec["dt"].append(dt)
98 rec["p_iters"].append(d.last_pressure_iterations())
99 rec["mom_residual"].append(d.last_momentum_residual())
100 rec["mom_time"].append(tm["momentum"])
101 rec["step_time"].append(tm["step"])
102 rec["div_proj"].append(s.max_open_divergence_projected())
103 rec["vol"].append([b["volume"] for b in st])
104 rec["ux"].append([b["velocity"][0] / rp.S for b in st])
105 if (i + 1) % 50 == 0:
106 samples.append(rp.sample(s, t))
107 print(f" column rtol {rtol:g}: step {i+1}/{NCOL} p_iters {rec['p_iters'][-1]} "
108 f"div {rec['div_proj'][-1]:.2e}", flush=True)
109 vol = np.array(rec["vol"])
110 ux = np.array(rec["ux"])
111 half = NCOL // 2
112 rec.update({
113 "vol0": v0.tolist(),
114 "bubble_vol_drift_max": float(np.max(np.abs(vol / v0 - 1.0))),
115 "total_vol_drift_max": float(np.max(np.abs(vol.sum(axis=1) / v0.sum() - 1.0))),
116 "rise_mean_last_half": float(-np.mean(ux[half:])), # + = towards -x (up), per marker
117 "drift_mean_last_half": float(np.mean([q["drift"] for q in samples[len(samples) // 2:]])),
118 "samples": samples})
119 return rec
120
121
122RUN = {"static": run_static, "hysing": run_hysing, "column": run_column}
123os.makedirs(OUT, exist_ok=True)
124print(f"D1: cases {CASES} rtols {RTOLS} module {pf.__file__}", flush=True)
125for case in CASES:
126 for rtol in RTOLS:
127 path = os.path.join(OUT, f"{case}_rtol{rtol:.0e}.json")
128 if os.path.exists(path):
129 print(f" {case} rtol {rtol:g}: exists, skipped", flush=True)
130 continue
131 t0 = time.time()
132 print(f" {case} rtol {rtol:g}: start", flush=True)
133 res = RUN[case](rtol)
134 res.update({"case": case, "rtol": rtol, "wall_s": time.time() - t0,
135 "loadavg": open("/proc/loadavg").read().strip(), "module": pf.__file__})
136 with open(path + ".tmp", "w") as f:
137 json.dump(res, f)
138 os.replace(path + ".tmp", path)
139 print(f" {case} rtol {rtol:g}: done in {res['wall_s']:.0f} s -> {path}", flush=True)
140print("D1 DONE", flush=True)
arg(name, default, cast=str)