flow 0.4.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.abspath(os.environ.get("SDFLOW_BUILD", "build_cuda2")))
10import numpy as np
11from peclet import flow
12
13
14def lattice_sdf(N, phi=0.125):
15 R = (3 * phi / (4 * np.pi)) ** (1 / 3) * N
16 g = np.arange(N) + 0.5
17 X, Y, Z = np.meshgrid(g, g, g, indexing="ij")
18 dx = X - 0.5 * N
19 dx -= N * np.round(dx / N)
20 dy = Y - 0.5 * N
21 dy -= N * np.round(dy / N)
22 dz = Z - 0.5 * N
23 dz -= N * np.round(dz / N)
24 return np.sqrt(dx * dx + dy * dy + dz * dz) - R, R
25
26
27def drag(N, ghost, mu=0.1, F=1e-3, dt=80.0, warm_tol=1e-7, tail=40, max_steps=4000):
28 sdf, R = lattice_sdf(N)
29 lv = max(2, int(np.log2(N)) - 1)
30 s = flow.Solver(N, N, N)
31 s.set_rho(1.0)
32 s.set_mu(mu)
33 s.set_dt(dt)
34 s.set_body_force(F, 0, 0)
35 s.set_advection(False)
36 s.set_velocity_solver_params(200)
37 s.set_pressure_multigrid(True, levels=lv)
38 s.set_pressure_pcg(True, 400, 1e-10)
39 if ghost:
40 s.set_ghost_projection(True)
41 s.set_solid(sdf, cutcell_pressure=True, pressure_coarse="rediscretized")
42 prev, warm, um, t0 = 0.0, None, [], time.time()
43 for it in range(max_steps):
44 s.step()
45 m = float(s.get_u().mean())
46 um.append(m)
47 if warm is None:
48 if it % 10 == 9:
49 if it > 10 and abs(m - prev) < warm_tol * (abs(m) + 1e-30):
50 warm = it
51 prev = m
52 elif it - warm >= tail:
53 break
54 K = F * N**3 / (6 * np.pi * mu * R * np.mean(um[-tail:]))
55 return K, it + 1, s.last_pressure_iterations(), s.max_open_divergence(), time.time() - t0
56
57
58kref = 4.2920
59print(f"Z&H K={kref}. Staggered: cut-cell projection (baseline) vs directional ghost-cell "
60 f"projection (set_ghost_projection).", flush=True)
61print(f"{'N':>4} | {'cutcell err%':>12} {'ord':>6} | {'ghost err%':>11} {'ord':>6} | "
62 f"{'it_p':>4} {'it_g':>4} | {'div_g':>9} | secs", flush=True)
63prev = {}
64for N in (32, 48, 64, 96, 128):
65 Kc, sc, ipc, dvc, tc = drag(N, ghost=False)
66 Kg, sg, ipg, dvg, tg = drag(N, ghost=True)
67 ec = 100 * (Kc - kref) / kref
68 eg = 100 * (Kg - kref) / kref
69 oc = np.log(abs(prev["c"]) / abs(ec)) / np.log(N / prev["N"]) if prev else float("nan")
70 og = np.log(abs(prev["g"]) / abs(eg)) / np.log(N / prev["N"]) if prev else float("nan")
71 print(f"{N:>4} | {ec:>+12.3f} {oc:>6.2f} | {eg:>+11.3f} {og:>6.2f} | "
72 f"{ipc:>4d} {ipg:>4d} | {dvg:>9.2e} | {tc + tg:>4.0f}", flush=True)
73 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)