flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
verify_velocity_mg_staircase_zh_sdflow.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""Z&H gate for the STAIRCASE velocity-MG coarse operator (IBM momentum solve).
3
4Simple-cubic sphere array (the Zick & Homsy Stokes-drag ground truth). The IBM velocity diffusion is solved
5with plain RB-GS (the exact reference) vs the staircase velocity multigrid (set_velocity_multigrid): the fine
6level is the sharp row-based IBM stencil, the coarse levels use the volume fraction only to CLASSIFY cells
7(theta>=0.5 fluid / <0.5 solid-pinned) and a plain const-coeff Helmholtz at fluid cells, with the IBM-cell
8residuals filtered before restriction. See doc/velocity_mg_plan.md.
9
10Two checks:
11 (1) drag: |K_vmg - K_rbgs| / K_rbgs < 0.1% at a fixed V-cycle budget;
12 (2) stiff stability: at a large dt (beta=nu*dt=80) where the old geometry-blind const coarse op diverged,
13 the staircase stays finite and exact.
14"""
15import os
16import sys
17import time
18
19import numpy as np
20
21from _bootstrap import ensure_flow # noqa: E402
22sdflow = ensure_flow()
23
24ZH_PHI = [0.000125, 0.001, 0.008, 0.027, 0.064, 0.125, 0.216, 0.343, 0.45, 0.5236]
25ZH_K = [1.096, 1.212, 1.525, 2.008, 2.810, 4.292, 7.442, 15.4, 28.1, 42.1]
26
27
28def sphere_radius(phi, N):
29 return (phi * 3.0 / (4.0 * np.pi)) ** (1.0 / 3.0) * N
30
31
32def sc_sdf(N, phi):
33 R = sphere_radius(phi, N)
34 g = np.arange(N) + 0.5
35 X, Y, Z = np.meshgrid(g, g, g, indexing="ij")
36 c = N / 2.0
37 return np.sqrt((X - c) ** 2 + (Y - c) ** 2 + (Z - c) ** 2) - R, R
38
39
40def drag_K(umean, R, N, f, mu):
41 return f * N ** 3 / (6.0 * np.pi * mu * R * umean)
42
43
44def run(N, phi, mode, vel_iter=200, vlevels=4, vcycles=12, mu=0.1, f=1e-3, dt=60.0,
45 max_steps=600, tol=1e-7):
46 """mode: 'rbgs' (plain RB-GS reference) | 'vmg' (the staircase velocity-MG, via set_velocity_multigrid)."""
47 sdf, R = sc_sdf(N, phi)
48 s = sdflow.Solver(N, N, N)
49 s.set_rho(1.0); s.set_mu(mu); s.set_dt(dt); s.set_body_force((f, 0, 0)); s.set_advection(False)
50 if mode == "rbgs":
51 s.diagnostics.set_velocity_solver_params(vel_iter)
52 else:
53 s.set_velocity_multigrid(True, vlevels, vcycles) # IBM -> staircase coarse op (the default)
54 lv = max(2, int(np.log2(N)) - 1)
55 s.set_pressure_multigrid(True, levels=lv)
56 s.set_pressure_pcg(True, max_iter=200, rtol=1e-8)
57 s.set_solid(sdf, cutcell_pressure=True)
58 prev = 0.0; t0 = time.time()
59 for it in range(max_steps):
60 s.step()
61 u = s.get_u()
62 if not np.isfinite(u).all():
63 return None, None
64 if it % 5 == 4:
65 m = float(u.mean())
66 if it > 10 and abs(m - prev) < tol * (abs(m) + 1e-30):
67 break
68 prev = m
69 return drag_K(float(s.get_u().mean()), R, N, f, mu), time.time() - t0
70
71
72def main():
73 N = int(os.environ.get("VMG_N", 64))
74 phi = float(os.environ.get("VMG_PHI", 0.216))
75 vcyc = int(os.environ.get("VMG_VCYC", 12))
76 kref = float(np.interp(phi, ZH_PHI, ZH_K))
77 print(f"=== staircase velocity-MG Z&H gate: SC sphere N={N} phi={phi} (Z&H K~{kref:.3f}) ===")
78 Krb, wrb = run(N, phi, "rbgs")
79 Kvm, wvm = run(N, phi, "vmg", vcycles=vcyc)
80 e = abs(Kvm - Krb) / Krb if (Kvm and Krb) else float("nan")
81 print(f" RB-GS : K={Krb:.5f} wall={wrb:.1f}s")
82 print(f" vel-MG (v={vcyc:2d}) : K={'NaN' if Kvm is None else f'{Kvm:.5f}'} wall={wvm:.1f}s "
83 f"err vs RB-GS {e*100:.3f}%")
84 ok1 = (Kvm is not None) and e < 0.001
85
86 # stiff regime: the geometry-blind const coarse op diverged here (dt=800, beta=80); staircase stays exact.
87 dt_stiff = float(os.environ.get("VMG_DT_STIFF", 800.0))
88 print(f" stiff dt={dt_stiff:.0f} (beta={mu_b(dt_stiff):.0f}; geometry-blind const coarse diverges):")
89 Krb_s, _ = run(N, phi, "rbgs", dt=dt_stiff)
90 Kv_s, _ = run(N, phi, "vmg", dt=dt_stiff, vcycles=vcyc)
91 e_s = abs(Kv_s - Krb_s) / Krb_s if (Kv_s and Krb_s) else float("nan")
92 print(f" RB-GS K={Krb_s:.5f} vel-MG={'BLEW UP' if Kv_s is None else f'{Kv_s:.5f} (err {e_s*100:.3f}%)'}")
93 ok2 = (Kv_s is not None) and e_s < 0.005
94
95 print(f" result: {'PASS' if ok1 and ok2 else 'FAIL'} (staircase vel-MG == RB-GS drag, and stable+exact "
96 f"at a stiff dt)")
97 sys.exit(0 if ok1 and ok2 else 1)
98
99
100def mu_b(dt, mu=0.1):
101 return mu * dt
102
103
104if __name__ == "__main__":
105 main()
run(N, phi, mode, vel_iter=200, vlevels=4, vcycles=12, mu=0.1, f=1e-3, dt=60.0, max_steps=600, tol=1e-7)
static double mean(const std::vector< double > &a)