flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
staggered_zh_ghostproj.py
Go to the documentation of this file.
1"""THE ghost-projection experiment: staggered Zick & Homsy drag, cut-cell vs directional
2ghost-cell projection (set_ghost_projection). Success = ghost-mode order ~2 with K converging to
3the same Z&H limit; also logs BiCGStab vs PCG iteration counts (solver health / compatibility
4floor). Pattern: tests/study/collocated_zh_ab.py."""
5import os
6import sys
7import time
8
9sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "scripts"))
10from _bootstrap import ensure_flow # noqa: E402
11flow = ensure_flow()
12
13import numpy as np
14
15def lattice_sdf(N, phi=0.125):
16 R = (3 * phi / (4 * np.pi)) ** (1 / 3) * N
17 g = np.arange(N) + 0.5
18 X, Y, Z = np.meshgrid(g, g, g, indexing="ij")
19 dx = X - 0.5 * N
20 dx -= N * np.round(dx / N)
21 dy = Y - 0.5 * N
22 dy -= N * np.round(dy / N)
23 dz = Z - 0.5 * N
24 dz -= N * np.round(dz / N)
25 return np.sqrt(dx * dx + dy * dy + dz * dz) - R, R
26
27
28def drag(N, ghost, mu=0.1, F=1e-3, dt=80.0, warm_tol=1e-7, tail=40, max_steps=4000):
29 sdf, R = lattice_sdf(N)
30 lv = max(2, int(np.log2(N)) - 1)
31 s = flow.Solver(N, N, N)
32 s.set_rho(1.0)
33 s.set_mu(mu)
34 s.set_dt(dt)
35 s.set_body_force((F, 0, 0))
36 s.set_advection(False)
37 s.diagnostics.set_velocity_solver_params(200)
38 s.set_pressure_multigrid(True, levels=lv)
39 s.set_pressure_pcg(True, 400, 1e-10)
40 if ghost:
41 s.diagnostics.set_ghost_projection(True)
42 s.set_solid(sdf, cutcell_pressure=True)
43 prev, warm, um, t0 = 0.0, None, [], time.time()
44 for it in range(max_steps):
45 s.step()
46 m = float(s.get_u().mean())
47 um.append(m)
48 if warm is None:
49 if it % 10 == 9:
50 if it > 10 and abs(m - prev) < warm_tol * (abs(m) + 1e-30):
51 warm = it
52 prev = m
53 elif it - warm >= tail:
54 break
55 K = F * N**3 / (6 * np.pi * mu * R * np.mean(um[-tail:]))
56 return K, it + 1, s.diagnostics.last_pressure_iterations(), s.max_open_divergence(), time.time() - t0
57
58
59kref = 4.2920
60print(f"Z&H K={kref}. Staggered: cut-cell projection (baseline) vs directional ghost-cell "
61 f"projection (set_ghost_projection).", flush=True)
62print(f"{'N':>4} | {'cutcell err%':>12} {'ord':>6} | {'ghost err%':>11} {'ord':>6} | "
63 f"{'it_p':>4} {'it_g':>4} | {'div_g':>9} | secs", flush=True)
64prev = {}
65for N in (32, 48, 64, 96, 128):
66 Kc, sc, ipc, dvc, tc = drag(N, ghost=False)
67 Kg, sg, ipg, dvg, tg = drag(N, ghost=True)
68 ec = 100 * (Kc - kref) / kref
69 eg = 100 * (Kg - kref) / kref
70 oc = np.log(abs(prev["c"]) / abs(ec)) / np.log(N / prev["N"]) if prev else float("nan")
71 og = np.log(abs(prev["g"]) / abs(eg)) / np.log(N / prev["N"]) if prev else float("nan")
72 print(f"{N:>4} | {ec:>+12.3f} {oc:>6.2f} | {eg:>+11.3f} {og:>6.2f} | "
73 f"{ipc:>4d} {ipg:>4d} | {dvg:>9.2e} | {tc + tg:>4.0f}", flush=True)
74 prev = {"c": ec, "g": eg, "N": N}
drag(N, ghost, 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)