flow 0.4.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
collocated_zh_ghostproj.py
Go to the documentation of this file.
1"""THE collocated ghost-projection experiment: Zick & Homsy drag, SolverColocated mode-0
2(plain average + central grad(P), 1st order) vs the directional ghost-cell projection
3(set_ghost_projection: ghost-closed divergence of the face-averaged field + gpCenterGrad
4predictor/correction). Success = ghost order ~2 converging to the same Z&H limit — closing the
5open problem of doc/collocated_second_order_open_problem.md. Pattern:
6tests/study/staggered_zh_ghostproj.py (same warm-detector + tail protocol as the staggered
7experiment and the mode-0 baselines)."""
8import os
9import sys
10import time
11
12sys.path.insert(0, os.path.abspath(os.environ.get("SDFLOW_BUILD", "build_cuda2")))
13import numpy as np
14from peclet import flow
15
16
17def lattice_sdf(N, phi=0.125):
18 R = (3 * phi / (4 * np.pi)) ** (1 / 3) * N
19 g = np.arange(N) + 0.5
20 X, Y, Z = np.meshgrid(g, g, g, indexing="ij")
21 dx = X - 0.5 * N
22 dx -= N * np.round(dx / N)
23 dy = Y - 0.5 * N
24 dy -= N * np.round(dy / N)
25 dz = Z - 0.5 * N
26 dz -= N * np.round(dz / N)
27 return np.sqrt(dx * dx + dy * dy + dz * dz) - R, R
28
29
30GPORD = tuple(int(v) for v in os.environ.get("GPORD", "1,2").split(","))
31
32def drag(N, ghost, orders=None, mu=0.1, F=1e-3, dt=80.0, warm_tol=1e-7, tail=40,
33 max_steps=4000):
34 sdf, R = lattice_sdf(N)
35 lv = max(2, int(np.log2(N)) - 1)
36 s = flow.SolverColocated(N, N, N)
37 s.set_rho(1.0)
38 s.set_mu(mu)
39 s.set_dt(dt)
40 s.set_body_force(F, 0, 0)
41 s.set_advection(False)
42 s.set_velocity_solver_params(200)
43 s.set_pressure_multigrid(True, levels=lv)
44 s.set_pressure_pcg(True, 400, 1e-10)
45 if ghost:
46 s.set_ghost_projection(True, *(orders or GPORD))
47 s.set_solid(sdf, cutcell_pressure=True, pressure_coarse="rediscretized")
48 prev, warm, um, t0 = 0.0, None, [], time.time()
49 for it in range(max_steps):
50 s.step()
51 m = float(s.get_u().mean())
52 um.append(m)
53 if warm is None:
54 if it % 10 == 9:
55 if it > 10 and abs(m - prev) < warm_tol * (abs(m) + 1e-30):
56 warm = it
57 prev = m
58 elif it - warm >= tail:
59 break
60 K = F * N**3 / (6 * np.pi * mu * R * np.mean(um[-tail:]))
61 return K, it + 1, s.last_pressure_iterations(), s.max_open_divergence(), time.time() - t0
62
63
64if __name__ == "__main__":
65 kref = 4.2920
66 print(f"Z&H K={kref}. Collocated: mode-0 (baseline, 1st order) vs directional ghost-cell "
67 f"projection (1,2).", flush=True)
68 print(f"{'N':>4} | {'mode0 err%':>10} {'ord':>6} | {'ghost err%':>11} {'ord':>6} | "
69 f"{'it_0':>4} {'it_g':>4} | {'div_g':>9} | secs", flush=True)
70 prev = {}
71 for N in (32, 48, 64, 96, 128):
72 Kc, sc, ipc, dvc, tc = drag(N, ghost=False)
73 Kg, sg, ipg, dvg, tg = drag(N, ghost=True)
74 ec = 100 * (Kc - kref) / kref
75 eg = 100 * (Kg - kref) / kref
76 oc = np.log(abs(prev["c"]) / abs(ec)) / np.log(N / prev["N"]) if prev else float("nan")
77 og = np.log(abs(prev["g"]) / abs(eg)) / np.log(N / prev["N"]) if prev else float("nan")
78 print(f"{N:>4} | {ec:>+10.3f} {oc:>6.2f} | {eg:>+11.3f} {og:>6.2f} | "
79 f"{ipc:>4d} {ipg:>4d} | {dvg:>9.2e} | {tc + tg:>4.0f}", flush=True)
80 prev = {"c": ec, "g": eg, "N": N}
drag(N, ghost, orders=None, mu=0.1, F=1e-3, dt=80.0, warm_tol=1e-7, tail=40, max_steps=4000)