flow 0.4.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
tgv_nosolid_control.py
Go to the documentation of this file.
1#!/usr/bin/env python
2"""No-solid periodic control (isolation ladder rung 0): 2D Taylor-Green on an all-fluid domain.
3
4With no solid cells every collocated cut-cell device degenerates to its textbook form
5(gpCenterGrad -> central difference, centerToFace -> plain 1/2-1/2, openness == 1), so
6staggered-vs-collocated here tests ONLY the core scheme pair (exact vs ABC approximate
7projection + advection). Expected: both 2nd order in space, col-stag field difference
8converging ~2nd order. If this control FAILS the whole cut-cell diagnosis is re-scoped.
9
102D TGV is an exact NS solution: u = U0 sin(kx)cos(ky) F(t), v = -U0 cos(kx)sin(ky) F(t),
11F = exp(-2 nu k^2 t). Fixed Re = U0*N/nu and fixed CFL per rung; measured at t* = T_TRANSITS
12tile transits. Backward-Euler time error is O(dt) and shared by both solvers; the col-stag
13difference cancels most of it, so ITS order is the clean spatial readout.
14
15 SDFLOW_BUILD=build_omp3 python tests/study/tgv_nosolid_control.py [N ...] # default 16 32 64
16"""
17import os
18import sys
19
20import numpy as np
21
22sys.path.insert(0, os.path.abspath(os.path.join(
23 os.path.dirname(__file__), "..", "..", os.environ.get("SDFLOW_BUILD", "build"))))
24from peclet import flow # noqa: E402
25
26U0, RE, CFL, T_TRANSITS, NZ = 1.0, 100.0, 0.2, 1.0, 4
27
28
29def run(kind, N):
30 nu = U0 * N / RE
31 dt = CFL / U0
32 steps = int(round(T_TRANSITS * N / (U0 * dt)))
33 k = 2 * np.pi / N
34 s = (flow.Solver if kind == "stag" else flow.SolverColocated)(N, N, NZ)
35 s.set_rho(1.0); s.set_mu(nu); s.set_dt(dt)
36 s.set_advection(True)
37 s.set_velocity_solver_params(200)
38 s.set_pressure_multigrid(True, max(2, int(np.log2(N)) - 2))
39 s.set_pressure_pcg(True, 300, 1e-10)
40 if kind != "stag":
41 if hasattr(s, "set_collocated_scheme"):
42 s.set_collocated_scheme("gauge-exact")
43 else:
44 s.set_face_interp(9)
45 s.set_solid(np.full((N, N, NZ), 1e3, order="F"), cutcell_pressure=True,
46 pressure_coarse="rediscretized")
47 # initial condition sampled at each solver's own u/v locations
48 cc = np.arange(N) + 0.5
49 fc = np.arange(N) * 1.0 # low-face coordinate of cell i
50 xu, yu = (fc, cc) if kind == "stag" else (cc, cc)
51 xv, yv = (cc, fc) if kind == "stag" else (cc, cc)
52 u0 = np.asfortranarray(np.broadcast_to(
53 (U0 * np.sin(k * xu)[:, None] * np.cos(k * yu)[None, :])[:, :, None], (N, N, NZ)).copy())
54 v0 = np.asfortranarray(np.broadcast_to(
55 (-U0 * np.cos(k * xv)[:, None] * np.sin(k * yv)[None, :])[:, :, None], (N, N, NZ)).copy())
56 s.set_state(u0, v0, np.zeros((N, N, NZ), order="F"))
57 for _ in range(steps):
58 s.step()
59 t = steps * dt
60 F = np.exp(-2 * nu * k * k * t)
61 uex = (U0 * np.sin(k * xu)[:, None] * np.cos(k * yu)[None, :]) * F
62 U = np.asarray(s.get_u())[:, :, NZ // 2]
63 err = float(np.sqrt(np.mean((U - uex) ** 2)) / (U0 * F))
64 # center-sampled u for the cross-solver difference (staggered averaged to centers)
65 Uc = 0.5 * (U + np.roll(U, -1, axis=0)) if kind == "stag" else U
66 return err, Uc, F
67
68
69if __name__ == "__main__":
70 Ns = [int(x) for x in (sys.argv[1:] or [16, 32, 64])]
71 print(f"{'N':>5} {'err_stag':>11} {'ord':>6} {'err_col9':>11} {'ord':>6} "
72 f"{'|col-stag|':>11} {'ord':>6}")
73 prev = None
74 for N in Ns:
75 es, Us, F = run("stag", N)
76 ec, Uc, _ = run("gauge-exact", N)
77 d = float(np.sqrt(np.mean((Uc - Us) ** 2)) / (U0 * F))
78 o = ["", "", ""]
79 if prev:
80 lr = np.log(N / prev[0])
81 o = [f"{np.log(a / b) / lr:+.2f}" if a > 0 and b > 0 else ""
82 for a, b in zip(prev[1:], (es, ec, d))]
83 print(f"{N:>5} {es:>11.4e} {o[0]:>6} {ec:>11.4e} {o[1]:>6} {d:>11.4e} {o[2]:>6}",
84 flush=True)
85 prev = (N, es, ec, d)