flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
verify_colocated_channel.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""Phase-5b verification (collocated grid): developing plane channel -- the canonical INFLOW/OUTFLOW
3benchmark. A uniform stream enters at -x (inflow Dirichlet), leaves at +x (outflow: zero-gradient velocity
4+ Dirichlet p=0), between no-slip walls at +-y; quasi-2D (periodic z). Exercises the collocated open-boundary
5machinery: the inflow reflection ghost (carrying the prescribed normal velocity through the open face),
6the zero-gradient outflow velocity ghost, and the outflow face correction on the MAC field that lets mass
7leave (the shared operator/flux openness split is grid-agnostic).
8
9Develops into the parabolic Poiseuille profile (u_max/U_mean -> 1.5). We check global mass conservation,
10the developed profile, and incompressibility. NOTE on the divergence: the *face* field is divergence-free
11to machine precision in the interior; at the open outflow the approximate projection leaves a small O(h^2)
12residual (vs the staggered exact projection), so the divergence tolerance here is looser than the staggered
131e-6 and is checked to SHRINK with resolution. Global mass conservation (flux_in == flux_out) is the
14primary continuity check.
15"""
16import sys
17
18import numpy as np
19
20from _bootstrap import ensure_flow # noqa: E402
21sdflow = ensure_flow()
22
23
24def run(H, L, Re=100.0, U=1.0, nz=4, max_steps=8000, dt=0.5):
25 nu = U * H / Re
26 s = sdflow.SolverColocated(L, H, nz)
27 s.set_rho(1.0); s.set_mu(nu); s.set_dt(dt); s.set_advection(True)
28 s.set_domain_bc("-x", "inflow", (U, 0.0, 0.0)) # -x inflow: uniform stream
29 s.set_domain_bc("+x", "outflow") # +x outflow
30 s.set_domain_bc("-y", "wall"); s.set_domain_bc("+y", "wall") # -y, +y no-slip walls
31 s.diagnostics.set_velocity_solver_params(60)
32 s.set_pressure_pcg(True, 400, 1e-9)
33 s.set_pressure_geometry(np.asfortranarray(np.full((L, H, nz), 1e30)))
34
35 prev = 0.0
36 steps = max_steps
37 for it in range(max_steps):
38 s.step()
39 if it % 50 == 49:
40 m = float(s.get_u()[L - 4, H // 2, nz // 2])
41 if it > 1000 and abs(m - prev) < 1e-5 * (abs(m) + 1e-30):
42 steps = it + 1
43 break
44 prev = m
45 u = s.get_u(); div = float(s.max_open_divergence())
46 flux_in = float(u[2, :, nz // 2].sum())
47 flux_out = float(u[L - 3, :, nz // 2].sum())
48 mass_err = abs(flux_out - flux_in) / (abs(flux_in) + 1e-30)
49 prof = u[L - 4, :, nz // 2]
50 U_mean = float(prof.mean())
51 eta = (np.arange(H) + 0.5) / H
52 parab = 6.0 * U_mean * eta * (1.0 - eta)
53 prof_rms = float(np.sqrt(np.mean((prof - parab) ** 2)) / (abs(U_mean) + 1e-30))
54 ratio = float(prof.max() / (U_mean + 1e-30))
55 return dict(steps=steps, mass_err=mass_err, div=div, ratio=ratio, prof_rms=prof_rms, H=H, L=L)
56
57
58def main():
59 print("=== sdflow phase-5b: collocated developing channel (inflow/outflow) ===")
60 print(f"{'H':>4} {'L':>5} {'steps':>6} {'mass_err':>10} {'maxdiv':>10} {'u_max/Um':>9} {'prof_rms':>9}")
61 rows = []
62 for H, L in ((16, 112), (32, 224)):
63 r = run(H, L)
64 print(f"{r['H']:4d} {r['L']:5d} {r['steps']:6d} {r['mass_err']:10.2e} {r['div']:10.2e} {r['ratio']:9.4f} {r['prof_rms']:9.4f}")
65 rows.append(r)
66 fine = rows[-1]
67 mass_ok = fine["mass_err"] < 1e-3
68 div_shrinks = rows[1]["div"] < rows[0]["div"]
69 div_small = fine["div"] < 1e-4
70 developed = 1.45 < fine["ratio"] < 1.55 and fine["prof_rms"] < 0.03
71 ok = mass_ok and div_small and div_shrinks and developed
72 print(f" mass conservation (flux_out==flux_in, <1e-3): {mass_ok} ({fine['mass_err']:.2e})")
73 print(f" outflow divergence small (<1e-4) and shrinks with N: {div_small and div_shrinks} "
74 f"({rows[0]['div']:.1e} -> {rows[1]['div']:.1e})")
75 print(f" developed Poiseuille (u_max/U_mean~1.5, rms<0.03): {developed} "
76 f"({fine['ratio']:.4f}, {fine['prof_rms']:.4f})")
77 print(f" result: {'PASS' if ok else 'FAIL'}")
78 sys.exit(0 if ok else 1)
79
80
81if __name__ == "__main__":
82 main()
run(H, L, Re=100.0, U=1.0, nz=4, max_steps=8000, dt=0.5)