flow
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 os
14import sys
15
16import numpy as np
17
18sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", os.environ.get("SDFLOW_BUILD", "build_mpi"))))
19from peclet import flow as sdflow # noqa: E402
20
21N = 32
22NU = 0.1
23
24
25def sphere_sdf(rfrac=0.3): # sdf[x,y,z], negative inside the sphere
26 X, Y, Z = np.meshgrid(np.arange(N), np.arange(N), np.arange(N), indexing="ij")
27 return np.sqrt((X - N / 2.0) ** 2 + (Y - N / 2.0) ** 2 + (Z - N / 2.0) ** 2) - N * rfrac
28
29
30def run(implicit, dt, fx, n_steps, to_steady=False):
31 sdf = sphere_sdf()
32 s = sdflow.Solver(N, N, N)
33 s.set_rho(1.0) # grid units: rho=1, mu=NU -> nu=NU
34 s.set_mu(NU)
35 s.set_dt(dt)
36 s.set_body_force(fx, 0.0, 0.0)
37 s.set_advection(True)
38 s.set_implicit_advection(implicit)
39 s.set_outer_iterations(3)
40 s.set_velocity_solver_params(80) # implicit-FOU uses RB-GS (n_diff sweeps), not vel-MG
41 s.set_pressure_pcg(True, max_iter=120, rtol=1e-9)
42 s.set_solid(sdf, cutcell_pressure=True, pressure_coarse="galerkin")
43 prev = 0.0
44 for it in range(n_steps): # np=1 demo
45 s.step()
46 u = s.get_u()
47 if not np.isfinite(u).all():
48 return None # blew up
49 um = float(u.mean())
50 if to_steady and it > 8 and abs(um - prev) < 1e-6 * (abs(um) + 1e-15):
51 break
52 prev = um
53 return s.get_u()
54
55
56def main():
57 print("=== implicit-FOU advection: high-Re stability + moderate-Re correctness ===")
58
59 # (1) high Re / large dt: explicit should blow up, implicit-FOU should not
60 print(f" (1) high Re: dt=5, fx=0.02 (velocity reaches U~2 -> CFL = U*dt/dx >> 1)")
61 ue = run(implicit=False, dt=5.0, fx=0.02, n_steps=30)
62 ui = run(implicit=True, dt=5.0, fx=0.02, n_steps=30)
63 expl_blew = ue is None
64 impl_ok = ui is not None and np.isfinite(ui).all() and ui.max() < 1e3
65 print(f" explicit advection : {'BLEW UP (NaN/Inf)' if expl_blew else f'finite, U_max={ue.max():.3f}'}")
66 print(f" implicit-FOU : {'finite, U_max=%.3f' % ui.max() if impl_ok else 'unstable'}")
67
68 # (2) moderate Re (explicit stable): the two should agree -> same Koren scheme
69 print(f" (2) moderate Re: dt=5, fx=2e-4 (steady, both stable)")
70 ue2 = run(implicit=False, dt=5.0, fx=2e-4, n_steps=300, to_steady=True)
71 ui2 = run(implicit=True, dt=5.0, fx=2e-4, n_steps=300, to_steady=True)
72 rel = abs(ui2.max() - ue2.max()) / ue2.max()
73 print(f" explicit U_max={ue2.max():.5f} implicit-FOU U_max={ui2.max():.5f} diff={rel*100:.2f}%")
74
75 ok = expl_blew and impl_ok and rel < 0.03
76 print(f" result: {'PASS' if ok else 'FAIL'} (implicit-FOU stable at high Re where explicit fails; "
77 f"agrees with explicit at moderate Re)")
78 sys.exit(0 if ok else 1)
79
80
81if __name__ == "__main__":
82 main()
run(implicit, dt, fx, n_steps, to_steady=False)