flow
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
21sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..",
22 os.environ.get("SDFLOW_BUILD", "build_mpi"))))
23from peclet import flow as sdflow # noqa: E402
24
25ZH_PHI = [0.000125, 0.001, 0.008, 0.027, 0.064, 0.125, 0.216, 0.343, 0.45, 0.5236]
26ZH_K = [1.096, 1.212, 1.525, 2.008, 2.810, 4.292, 7.442, 15.4, 28.1, 42.1]
27
28
29def sphere_radius(phi, N):
30 return (phi * 3.0 / (4.0 * np.pi)) ** (1.0 / 3.0) * N
31
32
33def sc_sdf(N, phi):
34 R = sphere_radius(phi, N)
35 g = np.arange(N) + 0.5
36 X, Y, Z = np.meshgrid(g, g, g, indexing="ij")
37 c = N / 2.0
38 return np.sqrt((X - c) ** 2 + (Y - c) ** 2 + (Z - c) ** 2) - R, R
39
40
41def drag_K(umean, R, N, f, mu):
42 return f * N ** 3 / (6.0 * np.pi * mu * R * umean)
43
44
45def run(N, phi, mode, vel_iter=200, vlevels=4, vcycles=12, mu=0.1, f=1e-3, dt=60.0,
46 max_steps=600, tol=1e-7):
47 """mode: 'rbgs' (plain RB-GS reference) | 'vmg' (the staircase velocity-MG, via set_velocity_multigrid)."""
48 sdf, R = sc_sdf(N, phi)
49 s = sdflow.Solver(N, N, N)
50 s.set_rho(1.0); s.set_mu(mu); s.set_dt(dt); s.set_body_force(f, 0, 0); s.set_advection(False)
51 if mode == "rbgs":
52 s.set_velocity_solver_params(vel_iter)
53 else:
54 s.set_velocity_multigrid(True, vlevels, vcycles) # IBM -> staircase coarse op (the default)
55 lv = max(2, int(np.log2(N)) - 1)
56 s.set_pressure_multigrid(True, levels=lv)
57 s.set_pressure_pcg(True, max_iter=200, rtol=1e-8)
58 s.set_solid(sdf, cutcell_pressure=True, pressure_coarse="rediscretized")
59 prev = 0.0; t0 = time.time()
60 for it in range(max_steps):
61 s.step()
62 u = s.get_u()
63 if not np.isfinite(u).all():
64 return None, None
65 if it % 5 == 4:
66 m = float(u.mean())
67 if it > 10 and abs(m - prev) < tol * (abs(m) + 1e-30):
68 break
69 prev = m
70 return drag_K(float(s.get_u().mean()), R, N, f, mu), time.time() - t0
71
72
73def main():
74 N = int(os.environ.get("VMG_N", 64))
75 phi = float(os.environ.get("VMG_PHI", 0.216))
76 vcyc = int(os.environ.get("VMG_VCYC", 12))
77 kref = float(np.interp(phi, ZH_PHI, ZH_K))
78 print(f"=== staircase velocity-MG Z&H gate: SC sphere N={N} phi={phi} (Z&H K~{kref:.3f}) ===")
79 Krb, wrb = run(N, phi, "rbgs")
80 Kvm, wvm = run(N, phi, "vmg", vcycles=vcyc)
81 e = abs(Kvm - Krb) / Krb if (Kvm and Krb) else float("nan")
82 print(f" RB-GS : K={Krb:.5f} wall={wrb:.1f}s")
83 print(f" vel-MG (v={vcyc:2d}) : K={'NaN' if Kvm is None else f'{Kvm:.5f}'} wall={wvm:.1f}s "
84 f"err vs RB-GS {e*100:.3f}%")
85 ok1 = (Kvm is not None) and e < 0.001
86
87 # stiff regime: the geometry-blind const coarse op diverged here (dt=800, beta=80); staircase stays exact.
88 dt_stiff = float(os.environ.get("VMG_DT_STIFF", 800.0))
89 print(f" stiff dt={dt_stiff:.0f} (beta={mu_b(dt_stiff):.0f}; geometry-blind const coarse diverges):")
90 Krb_s, _ = run(N, phi, "rbgs", dt=dt_stiff)
91 Kv_s, _ = run(N, phi, "vmg", dt=dt_stiff, vcycles=vcyc)
92 e_s = abs(Kv_s - Krb_s) / Krb_s if (Kv_s and Krb_s) else float("nan")
93 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}%)'}")
94 ok2 = (Kv_s is not None) and e_s < 0.005
95
96 print(f" result: {'PASS' if ok1 and ok2 else 'FAIL'} (staircase vel-MG == RB-GS drag, and stable+exact "
97 f"at a stiff dt)")
98 sys.exit(0 if ok1 and ok2 else 1)
99
100
101def mu_b(dt, mu=0.1):
102 return mu * dt
103
104
105if __name__ == "__main__":
106 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)