flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
verify_colocated_taylor_green.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""Phase-3 verification (collocated grid): 2-D Taylor-Green vortex in a triply-periodic box, the canonical
3test of the approximate (MAC) projection. The exact incompressible Navier-Stokes solution is
4
5 u = U0 sin(kx) cos(ky) e^{-2 nu k^2 t}, v = -U0 cos(kx) sin(ky) e^{-2 nu k^2 t}, w = 0,
6
7with the nonlinear term exactly balanced by the pressure gradient. Advection is ON, so the projection has
8to remove the divergence the discrete advection injects each step. We check (a) the projected face field is
9divergence-free (max_open_divergence -> solver tol) and (b) the velocity matches the exact decayed field,
10the L2 error shrinking with resolution. Grid spacing = 1; the vortex wavelength is the box length N (k=2pi/N).
11"""
12import sys
13
14import numpy as np
15
16from _bootstrap import ensure_flow # noqa: E402
17sdflow = ensure_flow()
18
19
20def tg_fields(N, nz, U0, amp):
21 k = 2.0 * np.pi / N
22 ix = np.arange(N)
23 X, Y = np.meshgrid(ix, ix, indexing="ij")
24 u2 = U0 * amp * np.sin(k * X) * np.cos(k * Y)
25 v2 = -U0 * amp * np.cos(k * X) * np.sin(k * Y)
26 u = np.repeat(u2[:, :, None], nz, axis=2)
27 v = np.repeat(v2[:, :, None], nz, axis=2)
28 w = np.zeros((N, N, nz))
29 return (np.asfortranarray(u), np.asfortranarray(v), np.asfortranarray(w))
30
31
32def run(SolverCls, N, nz=4, U0=1.0, rho=1.0, nu=0.05, dt=0.5, steps=100):
33 k = 2.0 * np.pi / N
34 s = SolverCls(N, N, nz)
35 s.set_rho(rho)
36 s.set_mu(rho * nu)
37 s.set_dt(dt)
38 s.set_advection(True) # exercise the projection: advection injects divergence
39 s.diagnostics.set_velocity_solver_params(80)
40 sdf = np.asfortranarray(np.ones((N, N, nz)) * 1e3) # all-fluid SDF -> trivial cut-cell, full projection
41 s.set_solid(sdf, cutcell_pressure=True)
42 u0, v0, w0 = tg_fields(N, nz, U0, 1.0)
43 s.set_state(u0, v0, w0)
44
45 for _ in range(steps):
46 s.step()
47
48 T = dt * steps
49 amp = np.exp(-2.0 * nu * k * k * T) # exact (continuous) decay factor
50 ue, ve, we = tg_fields(N, nz, U0, amp)
51 uu, vv = s.get_u(), s.get_v()
52 num = np.sqrt(np.mean((uu - ue) ** 2 + (vv - ve) ** 2))
53 den = np.sqrt(np.mean(u0 ** 2 + v0 ** 2)) # normalize by the initial field norm
54 l2 = num / den
55 # measured vs analytic energy decay (diagnostic)
56 e_now = float(np.mean(uu ** 2 + vv ** 2))
57 e_ini = float(np.mean(u0 ** 2 + v0 ** 2))
58 ratio_meas = e_now / e_ini
59 ratio_ana = amp * amp
60 div = float(s.max_open_divergence())
61 return l2, div, ratio_meas, ratio_ana
62
63
64def main():
65 print("=== sdflow phase-3: collocated Taylor-Green vortex (approximate/MAC projection, advection ON) ===")
66 print(f"{'N':>5} {'L2_err':>11} {'maxdiv':>11} {'E/E0(meas)':>11} {'E/E0(ana)':>11}")
67 errs = []
68 divs = []
69 for N in (32, 64):
70 l2, div, rm, ra = run(sdflow.SolverColocated, N)
71 print(f"{N:5d} {l2:11.3e} {div:11.3e} {rm:11.5f} {ra:11.5f}")
72 errs.append(l2)
73 divs.append(div)
74 shrinks = errs[1] < errs[0]
75 divfree = max(divs) < 1e-6
76 ok = shrinks and divfree and errs[1] < 0.02
77 print(f" L2 error shrinks 32->64: {shrinks} ({errs[0]:.2e} -> {errs[1]:.2e}, ratio {errs[0]/errs[1]:.1f}x)")
78 print(f" faces divergence-free (<1e-6): {divfree} (max {max(divs):.1e})")
79 print(f" result: {'PASS' if ok else 'FAIL'}")
80 sys.exit(0 if ok else 1)
81
82
83if __name__ == "__main__":
84 main()
run(SolverCls, N, nz=4, U0=1.0, rho=1.0, nu=0.05, dt=0.5, steps=100)