flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
verify_velocity_mg_upwind_sdflow.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""Validation of the upwind-convective velocity-multigrid coarse operator (task #56, Phase 4 of
3doc/velocity_mg_plan.md). When implicit-FOU advection is on, the velocity (momentum) solve is an
4advection-dominated, non-symmetric Helmholtz system (I - nu*dt*Lap + dt*FOU(u^k)). The opt-in
5upwind-convective vel-MG builds the coarse operators as anisotropic const-coeff diffusion + a coarse
6first-order-upwind advection from the restricted advecting velocity, keeping every level an M-matrix.
7
8This checks, on a sphere in a periodic box (full 3-D NS + cut-cell IBM + cut-cell pressure, one GPU):
9 (1) the vel-MG path stays finite/bounded at high Re where the operator is advection-dominated;
10 (2) at steady state it matches the RB-GS implicit-FOU solve (same operator, MG just sets the rate)
11 -> drag-equivalent: U_max and U_mean agree to a tight tolerance.
12The fine residual + smoother guarantee the exact fine (sharp-IBM) answer regardless of the coarse op,
13so MG-on must converge to the same field as RB-GS.
14"""
15import sys
16
17import numpy as np
18
19from _bootstrap import ensure_flow # noqa: E402
20sdflow = ensure_flow()
21
22N = 32
23NU = 0.1
24
25
26def sphere_sdf(rfrac=0.3): # sdf[x,y,z], negative inside the sphere
27 X, Y, Z = np.meshgrid(np.arange(N), np.arange(N), np.arange(N), indexing="ij")
28 return np.sqrt((X - N / 2.0) ** 2 + (Y - N / 2.0) ** 2 + (Z - N / 2.0) ** 2) - N * rfrac
29
30
31def run(vmg, dt, fx, n_steps, vlevels=3, vcycles=10, to_steady=False):
32 sdf = sphere_sdf()
33 s = sdflow.Solver(N, N, N)
34 s.set_rho(1.0)
35 s.set_mu(NU)
36 s.set_dt(dt)
37 s.set_body_force((fx, 0.0, 0.0))
38 s.set_advection(True)
39 s.set_implicit_advection(True)
40 s.diagnostics.set_outer_iterations(3)
41 s.diagnostics.set_velocity_solver_params(80) # RB-GS sweeps when vmg off
42 if vmg:
43 s.set_velocity_multigrid(True, vlevels, vcycles)
44 s.set_pressure_pcg(True, max_iter=120, rtol=1e-9)
45 s.set_solid(sdf, cutcell_pressure=True)
46 prev = 0.0
47 for it in range(n_steps):
48 s.step()
49 u = s.get_u()
50 if not np.isfinite(u).all():
51 return None
52 um = float(u.mean())
53 if to_steady and it > 8 and abs(um - prev) < 1e-7 * (abs(um) + 1e-15):
54 break
55 prev = um
56 return s.get_u()
57
58
59def main():
60 print("=== upwind-convective velocity-MG: high-Re stability + RB-GS equivalence ===")
61
62 # (1) high Re / large dt: advection-dominated; the upwind coarse op must stay an M-matrix (no NaN)
63 print(" (1) high Re: dt=5, fx=0.02 (U~2 -> CFL >> 1, operator advection-dominated)")
64 ur = run(vmg=False, dt=5.0, fx=0.02, n_steps=30)
65 uv = run(vmg=True, dt=5.0, fx=0.02, n_steps=30)
66 rbgs_ok = ur is not None and np.isfinite(ur).all()
67 vmg_ok = uv is not None and np.isfinite(uv).all() and uv.max() < 1e3
68 print(f" RB-GS implicit-FOU : {'finite, U_max=%.3f' % ur.max() if rbgs_ok else 'unstable'}")
69 print(f" vel-MG implicit-FOU: {'finite, U_max=%.3f' % uv.max() if vmg_ok else 'unstable'}")
70
71 # (2) moderate Re, steady: MG-on must converge to the SAME field as RB-GS (fine residual is identical)
72 print(" (2) moderate Re: dt=5, fx=2e-4 (steady) -> RB-GS vs vel-MG agree")
73 ur2 = run(vmg=False, dt=5.0, fx=2e-4, n_steps=400, to_steady=True)
74 uv2 = run(vmg=True, dt=5.0, fx=2e-4, n_steps=400, to_steady=True)
75 rel_max = abs(uv2.max() - ur2.max()) / ur2.max()
76 rel_mean = abs(uv2.mean() - ur2.mean()) / abs(ur2.mean())
77 print(f" RB-GS U_max={ur2.max():.6f} U_mean={ur2.mean():.6e}")
78 print(f" vel-MG U_max={uv2.max():.6f} U_mean={uv2.mean():.6e}")
79 print(f" diff: U_max {rel_max*100:.3f}% U_mean {rel_mean*100:.3f}%")
80
81 ok = rbgs_ok and vmg_ok and rel_max < 0.01 and rel_mean < 0.01
82 print(f" result: {'PASS' if ok else 'FAIL'} (upwind vel-MG stable at high Re; "
83 f"matches RB-GS at steady to <1%)")
84 sys.exit(0 if ok else 1)
85
86
87if __name__ == "__main__":
88 main()
run(vmg, dt, fx, n_steps, vlevels=3, vcycles=10, to_steady=False)