flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
verify_implicit_advection_sdflow.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""Verification of the implicit-FOU deferred-correction advection (dcfd.set_implicit_advection). The
3distributed solver is a full Navier-Stokes solver (Koren TVD advection). The default advection is
4EXPLICIT (Picard-lagged) and therefore CFL-limited: at high Reynolds number / large dt it goes unstable.
5The implicit-FOU mode solves the first-order-upwind part of advection implicitly (diagonally dominant ->
6unconditionally stable for advection) and keeps the (Koren - FOU) correction explicit, so the scheme is
7still Koren TVD at convergence but is robust at high Re, matching the production solver's deferred
8correction. This checks both:
9 (1) high Re / large dt: explicit BLOWS UP, implicit-FOU stays finite and bounded;
10 (2) moderate Re (where explicit is stable): the two agree -> same Koren scheme at convergence.
11Flow around a sphere in a periodic box (full 3-D NS + cut-cell IBM + cut-cell pressure). One GPU.
12"""
13import sys
14
15import numpy as np
16
17from _bootstrap import ensure_flow # noqa: E402
18sdflow = ensure_flow()
19
20N = 32
21NU = 0.1
22
23
24def sphere_sdf(rfrac=0.3): # sdf[x,y,z], negative inside the sphere
25 X, Y, Z = np.meshgrid(np.arange(N), np.arange(N), np.arange(N), indexing="ij")
26 return np.sqrt((X - N / 2.0) ** 2 + (Y - N / 2.0) ** 2 + (Z - N / 2.0) ** 2) - N * rfrac
27
28
29def run(implicit, dt, fx, n_steps, to_steady=False):
30 sdf = sphere_sdf()
31 s = sdflow.Solver(N, N, N)
32 s.set_rho(1.0) # grid units: rho=1, mu=NU -> nu=NU
33 s.set_mu(NU)
34 s.set_dt(dt)
35 s.set_body_force((fx, 0.0, 0.0))
36 s.set_advection(True)
37 s.set_implicit_advection(implicit)
38 s.diagnostics.set_outer_iterations(3)
39 s.diagnostics.set_velocity_solver_params(80) # implicit-FOU uses RB-GS (n_diff sweeps), not vel-MG
40 s.set_pressure_pcg(True, max_iter=120, rtol=1e-9)
41 s.set_solid(sdf, cutcell_pressure=True)
42 prev = 0.0
43 for it in range(n_steps): # np=1 demo
44 s.step()
45 u = s.get_u()
46 if not np.isfinite(u).all():
47 return None # blew up
48 um = float(u.mean())
49 if to_steady and it > 8 and abs(um - prev) < 1e-6 * (abs(um) + 1e-15):
50 break
51 prev = um
52 return s.get_u()
53
54
55def main():
56 print("=== implicit-FOU advection: high-Re stability + moderate-Re correctness ===")
57
58 # (1) high Re / large dt: explicit should blow up, implicit-FOU should not
59 print(f" (1) high Re: dt=5, fx=0.02 (velocity reaches U~2 -> CFL = U*dt/dx >> 1)")
60 ue = run(implicit=False, dt=5.0, fx=0.02, n_steps=30)
61 ui = run(implicit=True, dt=5.0, fx=0.02, n_steps=30)
62 expl_blew = ue is None
63 impl_ok = ui is not None and np.isfinite(ui).all() and ui.max() < 1e3
64 print(f" explicit advection : {'BLEW UP (NaN/Inf)' if expl_blew else f'finite, U_max={ue.max():.3f}'}")
65 print(f" implicit-FOU : {'finite, U_max=%.3f' % ui.max() if impl_ok else 'unstable'}")
66
67 # (2) moderate Re (explicit stable): the two should agree -> same Koren scheme
68 print(f" (2) moderate Re: dt=5, fx=2e-4 (steady, both stable)")
69 ue2 = run(implicit=False, dt=5.0, fx=2e-4, n_steps=300, to_steady=True)
70 ui2 = run(implicit=True, dt=5.0, fx=2e-4, n_steps=300, to_steady=True)
71 rel = abs(ui2.max() - ue2.max()) / ue2.max()
72 print(f" explicit U_max={ue2.max():.5f} implicit-FOU U_max={ui2.max():.5f} diff={rel*100:.2f}%")
73
74 ok = expl_blew and impl_ok and rel < 0.03
75 print(f" result: {'PASS' if ok else 'FAIL'} (implicit-FOU stable at high Re where explicit fails; "
76 f"agrees with explicit at moderate Re)")
77 sys.exit(0 if ok else 1)
78
79
80if __name__ == "__main__":
81 main()
run(implicit, dt, fx, n_steps, to_steady=False)