58 if name ==
"production":
59 return dict(pcg=(200, 1e-8), vel_rtol=
None, rtol=1e-4, max_steps=5000)
61 return dict(pcg=(400, 1e-12), vel_rtol=1e-12, rtol=1e-10, max_steps=20000)
62 raise ValueError(name)
172 return dict(label=a.label, case=a.case, scheme=a.scheme, N=a.N, phi=a.phi, mu=a.mu,
173 dt=case.dt(), F=case.F, advection=a.advection, settings=a.settings,
174 threads=os.environ.get(
"OMP_NUM_THREADS"))
181 st[
"max_steps"] = a.max_steps
185 s = case.build(flow, a.scheme, st)
189 def cb(steps, phase, s=s, holder=holder, trace=trace):
190 acc = holder.get(
"acc")
191 trace.append((phase, int(s.diagnostics.last_pressure_iterations()),
192 acc.residual
if acc
is not None else None,
193 acc.status
if acc
is not None else None))
195 t0 = time.perf_counter()
196 res = flow.march_to_steady(
Proxy(s, holder),
lambda s=s:
umean(s), rtol=st[
"rtol"],
197 max_steps=st[
"max_steps"], accelerate=(w > 0),
198 window=max(w, 1), callback=cb)
199 wall = time.perf_counter() - t0
200 acc = holder.get(
"acc")
202 for ph
in (
"accelerate",
"certify",
"plain"):
203 its = [t[1]
for t
in trace
if t[0] == ph]
204 piters[ph] = (sum(its), len(its))
207 if phases
and phases[-1][0] == t[0]:
210 phases.append([t[0], 1])
212 rec.update(window=w, converged=res.converged, reason=res.reason, steps=res.steps,
213 accelerated_steps=res.accelerated_steps, num_restarts=res.num_restarts,
214 monitor=res.monitor, K=case.K(res.monitor), wall_s=wall,
215 pressure_iterations=sum(v[0]
for v
in piters.values()),
216 piters_by_phase=piters,
217 status=acc.status
if acc
else None, acc_reason=acc.reason
if acc
else None,
218 num_resets=acc.num_resets
if acc
else None,
219 acc_seconds=acc.seconds
if acc
else None,
220 memory_bytes=acc.memory_bytes
if acc
else None,
221 phases=
"".join(f
"{p[0][0]}{p[1]} " for p
in phases).strip())
224 pi = {k: (f
"{v[0] / v[1]:.2f}" if v[1]
else "-")
for k, v
in piters.items()}
225 print(f
"{a.label:>8s} {a.case} {a.scheme:10s} N={a.N} {a.settings:10s} m={w} "
226 f
"conv={res.converged} {res.reason} steps={res.steps} acc={res.accelerated_steps} "
227 f
"K={rec['K']:.10f} wall={wall:.2f}s piters={rec['pressure_iterations']} "
228 f
"pit/step a/c/p={pi['accelerate']}/{pi['certify']}/{pi['plain']} "
229 f
"rst={res.num_restarts} "
230 f
"status={rec['status']} accsec={rec['acc_seconds']} [{rec['phases']}]",
233 conv[w] = res.converged
238 if 0
in conv
and len(conv) > 1:
239 accw = {w: c
for w, c
in conv.items()
if w > 0}
240 ok = (
not conv[0])
or all(accw.values())
241 print(f
"G7c {a.case} {a.scheme} N={a.N} {a.settings}: plain converged={conv[0]}; "
242 +
" ".join(f
"m={w} converged={c}" for w, c
in accw.items())
243 + f
" -> {'PASS' if ok else 'FAIL'}", flush=
True)
245 rec.update(gate=
"g7c", plain_converged=conv[0],
246 accelerated_converged={str(w): c
for w, c
in accw.items()}, ok=ok)
260 if a.case !=
"bed" or a.scheme !=
"staggered" or a.settings !=
"tight":
262 kp = Ks.get(0, a.k_plain)
263 if kp
is None or not (0
in conv
or a.k_plain
is not None):
267 uncert = (0
in conv
and not conv[0])
or a.plain_uncertified
268 for w, K
in Ks.items():
271 ends = [(abs(K / ki - 1.0), abs(kp / ki - 1.0))
for ki
in BED_K_INF]
273 ok = conv[w]
and all(ea < ep
for ea, ep
in ends)
274 rule =
"acc closer than the uncertified plain K at max_steps"
276 ok = conv[w]
and all(ea <= ep + 1e-8
for ea, ep
in ends)
277 rule =
"acc <= plain + 1e-8"
278 print(f
"G1 bed staggered dt={case.dt():.6g} m={w}: |K_acc/K_inf-1| "
279 f
"{ends[0][0]:.2e}..{ends[1][0]:.2e}, |K_plain/K_inf-1| {ends[0][1]:.2e}.."
280 f
"{ends[1][1]:.2e} (K_plain {kp!r}) -> {'PASS' if ok else 'FAIL'} ({rule})",
283 rec.update(gate=
"g1_bed", window=w, K_acc=K, K_plain=kp, K_inf=list(BED_K_INF),
284 err_acc=[e[0]
for e
in ends], err_plain=[e[1]
for e
in ends], ok=ok)
290 sys.path.insert(0, HERE)
291 import anderson_oracle
as O
296 s1 = case.build(flow, a.scheme, st)
297 acc = s1.diagnostics.anderson_accelerator(window=m)
299 for _
in range(a.steps):
301 rc.append(acc.residual)
303 s2 = case.build(flow, a.scheme, st)
305 tau = st[
"vel_rtol"]
if st[
"vel_rtol"]
else st[
"pcg"][1]
306 okw = dict(signature=(case.dt() * us[
"time_to_internal"], case.rho * us[
"density_to_internal"],
307 case.mu * us[
"viscosity_to_internal"],
308 case.F * us[
"force_density_to_internal"][0], 0.0, 0.0),
309 c_p=1.0, gauged=
True, collocated_advection=
False, cells=(a.N,) * 3, metric=
"V",
310 ritz_scope=
"mixed", inner_tolerance=tau, ritz_action=
"none")
311 orc = O.AndersonOracle(s2, window=m, **okw)
313 for _
in range(a.steps):
315 ro.append(orc.residual)
316 rel = [abs(x - y) / abs(y)
if y != 0
else abs(x - y)
for x, y
in zip(rc, ro)]
318 print(f
"oracle {a.case} {a.scheme} N={a.N} m={m} {a.settings}: max rel diff of the residual "
319 f
"over {a.steps} steps = {worst:.3e} (step {rel.index(worst) + 1}); C++ "
320 f
"{rc[0]:.6e} .. {rc[-1]:.6e}, oracle {ro[0]:.6e} .. {ro[-1]:.6e}", flush=
True)
321 for k, (x, y, r)
in enumerate(zip(rc, ro, rel)):
323 print(f
" {k + 1:3d} {x:.15e} {y:.15e} {r:.2e}")
325 rec.update(gate=
"oracle", window=m, steps=a.steps, max_rel=worst, cpp=rc, oracle=ro)
340 depth = G3_DEPTH[a.case]
343 s = case.build(flow, a.scheme, st, beta=beta)
344 acc = s.diagnostics.anderson_accelerator(window=a.window[0])
345 res, rmin, rst_steps = [], math.inf, []
346 u, u_call = math.nan, 0
347 t0 = time.perf_counter()
348 for k
in range(max(a.steps, a.extend_to)):
349 if k >= a.steps
and depth
is not None and rmin <= depth:
351 r0 = acc.num_restarts
353 if acc.num_restarts > r0:
354 rst_steps.append(k + 1)
356 u, u_call =
umean(s), k + 1
357 res.append(acc.residual)
358 rmin = min(rmin, acc.residual)
359 if acc.status !=
"active":
361 wall = time.perf_counter() - t0
364 per100 = max([sum(1
for q
in rst_steps
if lo < q <= lo + 100)
365 for lo
in range(0, len(res), 100)]
or [0])
366 deep = depth
is None or rmin <= depth
367 ok = (acc.status ==
"active" and per100 <= 1
and deep
and res[-1] <= 10 * rmin)
368 print(f
"g3 {a.case} {a.scheme} N={a.N} beta={beta:g} m={a.window[0]}: status={acc.status} "
369 f
"({acc.reason}) steps={len(res)} restarts={acc.num_restarts} (max/100 {per100}) "
370 f
"min_res={rmin:.3e} (bar {depth if depth is not None else 'none'}) "
371 f
"final_res={res[-1]:.3e} K={K:.12f} "
372 f
"(at call {u_call}) wall={wall:.1f}s -> {'PASS' if ok else 'FAIL'}",
375 rec.update(gate=
"g3", beta=beta, window=a.window[0], status=acc.status,
376 reason=acc.reason, steps=len(res), restarts=acc.num_restarts,
377 restart_calls=rst_steps, restarts_max_per_100=per100, min_res=rmin,
378 depth=depth, final_res=res[-1], K=K, u=u, K_call=u_call, ok=ok, wall_s=wall)
382 vals = [v
for v
in Ks.values()
if math.isfinite(v)]
383 spread = (max(vals) - min(vals)) / abs(min(vals))
if vals
else math.nan
384 print(f
"g3 {a.case} {a.scheme} N={a.N}: K over betas {sorted(Ks)} spread {spread:.3e} "
385 f
"-> {'PASS' if spread <= 1e-8 else 'FAIL'} (<= 1e-8)", flush=
True)
387 rec.update(gate=
"g3_spread", betas=list(Ks), Ks=list(Ks.values()), spread=spread)
396 s0 = case.build(flow, a.scheme, st)
397 full = flow.march_to_steady(s0,
lambda:
umean(s0), rtol=st[
"rtol"],
398 max_steps=st[
"max_steps"], accelerate=
True, window=m)
399 K0 = case.K(full.monitor)
400 for mode
in (
"field",
"state"):
401 s1 = case.build(flow, a.scheme, st)
402 acc = s1.diagnostics.anderson_accelerator(window=m)
403 for _
in range(a.at):
405 ck = {f: np.array(s1.get_field(f))
for f
in (
"u",
"v",
"w",
"p")}
406 vel = [np.array(s1.get_u()), np.array(s1.get_v()), np.array(s1.get_w())]
408 s2 = case.build(flow, a.scheme, st)
410 for f, v
in ck.items():
411 s2.set_field(f, np.asfortranarray(v))
413 s2.set_state(*[np.asfortranarray(v)
for v
in vel])
414 res = flow.march_to_steady(s2,
lambda s2=s2:
umean(s2), rtol=st[
"rtol"],
415 max_steps=st[
"max_steps"], accelerate=
True, window=m)
416 K = case.K(res.monitor)
417 rel = abs(K / K0 - 1.0)
418 total = a.at + res.steps
419 print(f
"g5 {a.case} {a.scheme} N={a.N} {a.settings} m={m} restore={mode}: uninterrupted "
420 f
"{full.steps} steps K={K0:.12f}; interrupted at {a.at} + {res.steps} = {total} "
421 f
"steps K={K:.12f} |dK|/K={rel:.2e} extra={total - full.steps} "
422 f
"-> {'PASS' if rel <= 1e-8 and res.converged else 'FAIL'}", flush=
True)
424 rec.update(gate=
"g5", restore=mode, window=m, at=a.at, full_steps=full.steps,
425 K_full=K0, steps_after=res.steps, total=total, K=K, rel=rel,
426 converged=res.converged)
435 s = case.build(flow, a.scheme, st)
436 for _
in range(a.warmup):
439 for _
in range(a.steps):
441 plain.append(s.diagnostics.last_step_timers()[
"step"])
442 acc = s.diagnostics.anderson_accelerator(window=m)
447 for _
in range(a.steps):
449 steps.append(s.diagnostics.last_step_timers()[
"step"])
450 over = (acc.seconds - t_acc0) / a.steps
451 mp = float(np.mean(plain))
452 ms = float(np.mean(steps))
453 ns = 7
if (a.scheme ==
"collocated" and a.advection)
else 4
454 npad = (a.N + 4) ** 3
455 formula = (2 * m + 3) * ns * 8 * npad
456 print(f
"g8 {a.case} {a.scheme} N={a.N} m={m}: mean plain step {mp * 1e3:.3f} ms, mean step "
457 f
"under acceleration {ms * 1e3:.3f} ms, accelerator {over * 1e3:.3f} ms/step = "
458 f
"{100 * over / mp:.2f} % of the plain step ({100 * over / ms:.2f} % of the step under "
459 f
"acceleration; {a.warmup} plain warm-up steps); memory_bytes {acc.memory_bytes} vs formula "
460 f
"{formula} ({'equal' if acc.memory_bytes == formula else 'DIFFERENT'}); "
461 f
"status {acc.status} engaged columns {acc.num_columns}", flush=
True)
463 rec.update(gate=
"g8", window=m, steps=a.steps, plain_ms=mp * 1e3, acc_step_ms=ms * 1e3,
464 overhead_ms=over * 1e3, overhead_pct=100 * over / mp, warmup=a.warmup,
465 overhead_pct_of_acc_step=100 * over / ms,
466 memory_bytes=acc.memory_bytes, formula=formula)
472 from mpi4py
import MPI
473 comm = MPI.COMM_WORLD
474 rank, size = comm.Get_rank(), comm.Get_size()
478 Cls = flow.Solver
if a.scheme ==
"staggered" else flow.SolverColocated
480 s = case.build(flow, a.scheme, st)
482 origin, bsize = flow.mpi_block(N, N, N)
483 s = Cls(tuple(bsize), extent=(L, L, L), global_cells=(N, N, N),
484 origin=(0.0, 0.0, 0.0))
486 case.configure(s, st)
487 g = (np.arange(N) + 0.5) * (L / N)
488 gsdf = case.sdf_global(g, g, g)
491 s.set_solid(np.asfortranarray(gsdf[ox:ox + lx, oy:oy + ly, oz:oz + lz]),
492 cutcell_pressure=
True)
495 u = np.asarray(s.get_u())
496 tot = comm.allreduce(float(u.sum()), op=MPI.SUM)
497 return tot / float(N * N * N)
500 acc = s.diagnostics.anderson_accelerator(window=a.window[0])
501 for _
in range(a.steps):
503 loc = {f: np.ascontiguousarray(getattr(s,
"get_" + f)(), dtype=np.float64)
504 for f
in (
"u",
"v",
"w",
"p")}
505 allf = comm.gather(loc, root=0)
507 hs = {f: hashlib.sha256(np.concatenate([np.asarray(r[f]).ravel()
for r
in allf])
508 .tobytes()).hexdigest()
for f
in loc}
509 tot = hashlib.sha256(
"".join(hs[f]
for f
in sorted(hs)).encode()).hexdigest()
510 print(f
"mpi-hash {a.scheme} N={N} np={size} serial={a.serial} m={a.window[0]} "
511 f
"{a.steps} accelerated steps: {tot} status={acc.status} "
512 f
"res={acc.residual:.6e}", flush=
True)
514 rec.update(gate=
"g4_hash", np=size, serial=a.serial, steps=a.steps, hash=tot)
519 raise SystemExit(
"mpi: one window per process")
520 t0 = time.perf_counter()
521 res = flow.march_to_steady(s, gmean, rtol=st[
"rtol"], max_steps=st[
"max_steps"],
522 accelerate=(w > 0), window=max(w, 1))
523 wall = time.perf_counter() - t0
525 K = case.K(res.monitor)
526 print(f
"mpi {a.case} {a.scheme} N={N} np={size} {a.settings} m={w}: "
527 f
"conv={res.converged} {res.reason} steps={res.steps} "
528 f
"acc={res.accelerated_steps} K={K:.14f} wall={wall:.1f}s", flush=
True)
530 rec.update(gate=
"g4", np=size, window=w, converged=res.converged, reason=res.reason,
531 steps=res.steps, accelerated_steps=res.accelerated_steps, K=K,
532 monitor=res.monitor, wall_s=wall)
537 ap = argparse.ArgumentParser()
538 ap.add_argument(
"cmd", choices=(
"run",
"oracle",
"g3",
"g5",
"g8",
"mpi"))
539 ap.add_argument(
"case", choices=(
"sphere",
"bed"))
540 ap.add_argument(
"--scheme", choices=(
"staggered",
"collocated"), required=
True)
541 ap.add_argument(
"--N", type=int, default=16)
542 ap.add_argument(
"--phi", type=float, default=0.125)
543 ap.add_argument(
"--mu", type=float, default=1.0)
544 ap.add_argument(
"--beta", type=float, default=6.0, help=
"nu dt / h^2 (ignored with --dt)")
545 ap.add_argument(
"--betas", type=float, nargs=
"+", default=[6.0, 60.0, 600.0, 1e4])
546 ap.add_argument(
"--dt", type=float, default=
None)
547 ap.add_argument(
"--force", type=float, default=
None)
548 ap.add_argument(
"--advection", default=
None)
549 ap.add_argument(
"--implicit-advection", action=
"store_true")
550 ap.add_argument(
"--arrangement", default=
None)
551 ap.add_argument(
"--settings", choices=(
"production",
"tight"), default=
"production")
552 ap.add_argument(
"--window", type=int, nargs=
"+", default=[0, 5])
553 ap.add_argument(
"--max-steps", type=int, default=
None)
554 ap.add_argument(
"--k-plain", type=float, default=
None,
555 help=
"run, tight staggered bed: the plain tight certificate's K at this dt "
556 "(from the log) for the G1 bed criterion, when window 0 is not run")
557 ap.add_argument(
"--plain-uncertified", action=
"store_true",
558 help=
"run, G1 bed: --k-plain is the plain march's K at max_steps (it did not "
559 "certify); the accelerated K must then be closer to K_inf")
560 ap.add_argument(
"--steps", type=int, default=30)
561 ap.add_argument(
"--at", type=int, default=25)
562 ap.add_argument(
"--warmup", type=int, default=100, help=
"g8: plain steps before timing")
563 ap.add_argument(
"--extend-to", type=int, default=0,
564 help=
"g3: past --steps, continue until the running-min residual reaches the "
565 "depth bar (sphere 1e-9; the bed has none) "
566 "or this many steps in total (0 = no extension)")
567 ap.add_argument(
"--serial", action=
"store_true")
568 ap.add_argument(
"--hash", action=
"store_true")
569 ap.add_argument(
"--trace", action=
"store_true")
570 ap.add_argument(
"--json", default=
None)
571 ap.add_argument(
"--label", default=
"")
573 sys.path.insert(0, os.path.join(HERE,
"..",
"..",
"scripts"))
574 from _bootstrap
import ensure_flow
576 {
"run": cmd_run,
"oracle": cmd_oracle,
"g3": cmd_g3,
"g5": cmd_g5,
"g8": cmd_g8,
577 "mpi": cmd_mpi}[a.cmd](flow, a)