flow
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_vmg_cavity.py
Go to the documentation of this file.
1"""Velocity-MG vs RB-GS on the lid-driven cavity (Ghia Re=100). Checks (1) no regression with vmg OFF,
2(2) vmg ON reaches the same Ghia centreline match + incompressibility. Run: PYTHONPATH=build_mpi python ..."""
3import sys, pathlib, time
4import numpy as np
5sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
6from peclet import flow as sdflow
7from verify_lid_cavity_sdflow import GHIA_Y, GHIA_U, GHIA_X, GHIA_V
8
9
10def run(N=128, Re=100.0, U=1.0, nz=4, max_steps=4000, vmg=False, vlevels=3, vcycles=4, vel_iter=60, dt=1.0):
11 nu = U * N / Re
12 s = sdflow.Solver(N, N, nz)
13 s.set_rho(1.0); s.set_mu(nu); s.set_dt(dt); s.set_advection(True)
14 s.set_domain_bc(0, 1); s.set_domain_bc(1, 1); s.set_domain_bc(2, 1)
15 s.set_domain_bc(3, 2, U, 0.0, 0.0)
16 s.set_velocity_solver_params(vel_iter)
17 if vmg:
18 s.set_velocity_multigrid(True, vlevels, vcycles)
19 s.set_pressure_multigrid(True, levels=8)
20 s.set_pressure_solver_params(80)
21 s.set_pressure_geometry(np.full((N, N, nz), 1e30))
22 t0 = time.time()
23 prev = None
24 steps = max_steps
25 for it in range(max_steps):
26 s.step()
27 if it % 100 == 0:
28 u = np.asarray(s.get_u()).reshape((N, N, nz), order="F")
29 cur = u[:, :, nz // 2].copy()
30 if prev is not None:
31 d = np.max(np.abs(cur - prev)) / (np.max(np.abs(cur)) + 1e-30)
32 if d < 2e-5:
33 steps = it + 1; break
34 prev = cur
35 dt_wall = time.time() - t0
36 u = np.asarray(s.get_u()).reshape((N, N, nz), order="F")
37 v = np.asarray(s.get_v()).reshape((N, N, nz), order="F")
38 uc = 0.5 * (u[N // 2 - 1, :, nz // 2] + u[N // 2, :, nz // 2])
39 vc = 0.5 * (v[:, N // 2 - 1, nz // 2] + v[:, N // 2, nz // 2])
40 yc = (np.arange(N) + 0.5) / N
41 u_rms = float(np.sqrt(np.mean((np.interp(GHIA_Y, yc, uc) - GHIA_U) ** 2)))
42 v_rms = float(np.sqrt(np.mean((np.interp(GHIA_X, yc, vc) - GHIA_V) ** 2)))
43 try:
44 div = float(s.max_flux_divergence())
45 except Exception:
46 div = float("nan")
47 return dict(u_rms=u_rms, v_rms=v_rms, umin=float(uc.min()), div=div, steps=steps, wall=dt_wall)
48
49
50if __name__ == "__main__":
51 import os
52 N = int(os.environ.get("VMG_N", 128))
53 print(f"=== Velocity-MG (semi-coarsening) vs RB-GS: lid cavity Re=100, N={N} (quasi-2D nz=4) ===")
54 # under-solved RB-GS (few sweeps) vs few-cycle deep vmg: the MG should hold accuracy where RB-GS lags.
55 cfgs = [("RB-GS 60 sweeps ", dict(vmg=False, vel_iter=60)),
56 ("RB-GS 20 sweeps ", dict(vmg=False, vel_iter=20)),
57 ("vmg L=8 v=2 (semi) ", dict(vmg=True, vlevels=8, vcycles=2)),
58 ("vmg L=8 v=4 (semi) ", dict(vmg=True, vlevels=8, vcycles=4))]
59 for label, kw in cfgs:
60 r = run(N=N, **kw)
61 print(f" {label}: u_rms={r['u_rms']:.4f} v_rms={r['v_rms']:.4f} umin={r['umin']:.4f} "
62 f"(Ghia -0.2058) div={r['div']:.1e} steps={r['steps']} wall={r['wall']:.1f}s")
run(N=128, Re=100.0, U=1.0, nz=4, max_steps=4000, vmg=False, vlevels=3, vcycles=4, vel_iter=60, dt=1.0)