flow
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 os
13import sys
14
15import numpy as np
16
17sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", os.environ.get("SDFLOW_BUILD", "build"))))
18from peclet import flow as sdflow # noqa: E402
19
20
21def tg_fields(N, nz, U0, amp):
22 k = 2.0 * np.pi / N
23 ix = np.arange(N)
24 X, Y = np.meshgrid(ix, ix, indexing="ij")
25 u2 = U0 * amp * np.sin(k * X) * np.cos(k * Y)
26 v2 = -U0 * amp * np.cos(k * X) * np.sin(k * Y)
27 u = np.repeat(u2[:, :, None], nz, axis=2)
28 v = np.repeat(v2[:, :, None], nz, axis=2)
29 w = np.zeros((N, N, nz))
30 return (np.asfortranarray(u), np.asfortranarray(v), np.asfortranarray(w))
31
32
33def run(SolverCls, N, nz=4, U0=1.0, rho=1.0, nu=0.05, dt=0.5, steps=100):
34 k = 2.0 * np.pi / N
35 s = SolverCls(N, N, nz)
36 s.set_rho(rho)
37 s.set_mu(rho * nu)
38 s.set_dt(dt)
39 s.set_advection(True) # exercise the projection: advection injects divergence
40 s.set_velocity_solver_params(80)
41 sdf = np.asfortranarray(np.ones((N, N, nz)) * 1e3) # all-fluid SDF -> trivial cut-cell, full projection
42 s.set_solid(sdf, cutcell_pressure=True)
43 u0, v0, w0 = tg_fields(N, nz, U0, 1.0)
44 s.set_state(u0, v0, w0)
45
46 for _ in range(steps):
47 s.step()
48
49 T = dt * steps
50 amp = np.exp(-2.0 * nu * k * k * T) # exact (continuous) decay factor
51 ue, ve, we = tg_fields(N, nz, U0, amp)
52 uu, vv = s.get_u(), s.get_v()
53 num = np.sqrt(np.mean((uu - ue) ** 2 + (vv - ve) ** 2))
54 den = np.sqrt(np.mean(u0 ** 2 + v0 ** 2)) # normalize by the initial field norm
55 l2 = num / den
56 # measured vs analytic energy decay (diagnostic)
57 e_now = float(np.mean(uu ** 2 + vv ** 2))
58 e_ini = float(np.mean(u0 ** 2 + v0 ** 2))
59 ratio_meas = e_now / e_ini
60 ratio_ana = amp * amp
61 div = float(s.max_open_divergence())
62 return l2, div, ratio_meas, ratio_ana
63
64
65def main():
66 print("=== sdflow phase-3: collocated Taylor-Green vortex (approximate/MAC projection, advection ON) ===")
67 print(f"{'N':>5} {'L2_err':>11} {'maxdiv':>11} {'E/E0(meas)':>11} {'E/E0(ana)':>11}")
68 errs = []
69 divs = []
70 for N in (32, 64):
71 l2, div, rm, ra = run(sdflow.SolverColocated, N)
72 print(f"{N:5d} {l2:11.3e} {div:11.3e} {rm:11.5f} {ra:11.5f}")
73 errs.append(l2)
74 divs.append(div)
75 shrinks = errs[1] < errs[0]
76 divfree = max(divs) < 1e-6
77 ok = shrinks and divfree and errs[1] < 0.02
78 print(f" L2 error shrinks 32->64: {shrinks} ({errs[0]:.2e} -> {errs[1]:.2e}, ratio {errs[0]/errs[1]:.1f}x)")
79 print(f" faces divergence-free (<1e-6): {divfree} (max {max(divs):.1e})")
80 print(f" result: {'PASS' if ok else 'FAIL'}")
81 sys.exit(0 if ok else 1)
82
83
84if __name__ == "__main__":
85 main()
run(SolverCls, N, nz=4, U0=1.0, rho=1.0, nu=0.05, dt=0.5, steps=100)