flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
collocated_zh_hybrid.py
Go to the documentation of this file.
1"""Z&H drag for the collocated cutcell-ghost HYBRID modes (set_face_interp 9/10): mode-0's
2aperture projection with the directional gpCenterGrad -grad(P)/cell correction (9), plus the
3open-centroid wall-aware constraint quadrature (10). Question: how much of the mode-0
4first-order drag was the O(1/h) gradient defect alone (9), and does the a-priori-O(h^2)
5open-centroid flux quadrature finally pay off once paired with a telescoping 2nd-order force
6(10)? Baselines: mode-0 +1.00/+0.69/+0.60/+0.40/+0.30 %, ghost (1,2)
7-0.175/-0.084/-0.056/-0.029/-0.018 % at N=32..128."""
8import os
9import sys
10import time
11
12sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "scripts"))
13from _bootstrap import ensure_flow # noqa: E402
14flow = ensure_flow()
15
16import numpy as np
17
18
19def _collocated_mode(s, mode):
20 """Select a collocated scheme by the integer face-interp mode this script was written with:
21 0/7/9 are the public strings ('plain' / 'embed' / 'gauge-exact'), 5/6 the diagnostics rungs,
22 and every other number was deleted at 1.0.0 (the call then raises)."""
23 names = {0: "plain", 7: "embed", 9: "gauge-exact"}
24 if mode in names:
25 s.set_collocated_scheme(names[mode])
26 else:
27 s.diagnostics.set_face_interp(mode)
28
29def lattice_sdf(N, phi=0.125):
30 R = (3 * phi / (4 * np.pi)) ** (1 / 3) * N
31 g = np.arange(N) + 0.5
32 X, Y, Z = np.meshgrid(g, g, g, indexing="ij")
33 dx = X - 0.5 * N
34 dx -= N * np.round(dx / N)
35 dy = Y - 0.5 * N
36 dy -= N * np.round(dy / N)
37 dz = Z - 0.5 * N
38 dz -= N * np.round(dz / N)
39 return np.sqrt(dx * dx + dy * dy + dz * dz) - R, R
40
41
42def drag(N, mode, mu=0.1, F=1e-3, dt=80.0, warm_tol=1e-7, tail=40, max_steps=4000):
43 sdf, R = lattice_sdf(N)
44 s = flow.SolverColocated(N, N, N)
45 s.set_rho(1.0)
46 s.set_mu(mu)
47 s.set_dt(dt)
48 s.set_body_force((F, 0, 0))
49 s.set_advection(False)
50 s.diagnostics.set_velocity_solver_params(200)
51 s.set_pressure_multigrid(True, levels=max(2, int(np.log2(N)) - 1))
52 s.set_pressure_pcg(True, 400, 1e-10)
53 if mode:
54 _collocated_mode(s, mode)
55 s.set_solid(sdf, cutcell_pressure=True)
56 prev, warm, um = 0.0, None, []
57 for it in range(max_steps):
58 s.step()
59 m = float(s.get_u().mean())
60 um.append(m)
61 if warm is None:
62 if it % 10 == 9:
63 if it > 10 and abs(m - prev) < warm_tol * (abs(m) + 1e-30):
64 warm = it
65 prev = m
66 elif it - warm >= tail:
67 break
68 K = F * N**3 / (6 * np.pi * mu * R * np.mean(um[-tail:]))
69 return K, s.diagnostics.last_pressure_iterations()
70
71
72if __name__ == "__main__":
73 kref = 4.2920
74 M0 = {32: +1.004, 48: +0.685, 64: +0.598, 96: +0.397, 128: +0.299}
75 MG = {32: -0.175, 48: -0.084, 64: -0.056, 96: -0.029, 128: -0.018}
76 print(f"Z&H K={kref}. Collocated hybrids: 9 = aperture proj + gpCenterGrad;"
77 f" 10 = + open-centroid flux quadrature.", flush=True)
78 print(f"{'N':>4} | {'mode0':>7} | {'ghost':>7} | {'hyb9 err%':>9} {'ord':>6} |"
79 f" {'hyb10 err%':>10} {'ord':>6}", flush=True)
80 prev = {}
81 for N in (32, 48, 64, 96, 128):
82 t0 = time.time()
83 K9, i9 = drag(N, 9)
84 K10, i10 = drag(N, 10)
85 e9 = 100 * (K9 - kref) / kref
86 e10 = 100 * (K10 - kref) / kref
87 o9 = np.log(abs(prev["9"]) / abs(e9)) / np.log(N / prev["N"]) if prev else float("nan")
88 o10 = np.log(abs(prev["10"]) / abs(e10)) / np.log(N / prev["N"]) if prev else float("nan")
89 print(f"{N:>4} | {M0[N]:>+7.3f} | {MG[N]:>+7.3f} | {e9:>+9.3f} {o9:>6.2f} |"
90 f" {e10:>+10.3f} {o10:>6.2f} ({time.time() - t0:.0f}s)", flush=True)
91 prev = {"9": e9, "10": e10, "N": N}
drag(N, mode, mu=0.1, F=1e-3, dt=80.0, warm_tol=1e-7, tail=40, max_steps=4000)
static double mean(const std::vector< double > &a)