flow
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
validate_zick_homsy_sdflow.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""GROUND TRUTH: simple-cubic (SC) array of spheres, Stokes drag factor K vs Zick & Homsy (1982).
3
4A single sphere centred in a periodic cube is the classic SC lattice. Z&H give the semi-analytic Stokes
5drag factor K(c) (c = sphere solid fraction) to high accuracy -- the external reference for the sdflow
6cut-cell IBM. (Historically this also re-ran the retired pnm_backend reference solver; sdflow was validated
7bit-identical to it before its retirement, so only the external Z&H comparison remains.)
8
9 K = F_total / (6*pi*mu*R*U_sup), F_total = f * V_cell, U_sup = mean(u) over the whole cell.
10
11K is dimensionless and unit-invariant.
12
13Usage: python scripts/validate_zick_homsy_sdflow.py [N1,N2,...] [phi1,phi2,...]
14"""
15import os
16import sys
17import time
18
19import numpy as np
20
21sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "build")))
22sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..",
23 os.environ.get("SDFLOW_BUILD", "build_mpi"))))
24
25# Zick & Homsy (1982), simple cubic: solid fraction c -> drag factor K.
26ZH_PHI = [0.000125, 0.001, 0.008, 0.027, 0.064, 0.125, 0.216, 0.343, 0.45, 0.5236]
27ZH_K = [1.096, 1.212, 1.525, 2.008, 2.810, 4.292, 7.442, 15.4, 28.1, 42.1]
28
29
30def zh_ref(phi):
31 return float(np.interp(phi, ZH_PHI, ZH_K))
32
33
34def sphere_radius(phi, N):
35 """Sphere radius (grid units) for solid fraction phi in an N^3 cube."""
36 return (phi * 3.0 / (4.0 * np.pi)) ** (1.0 / 3.0) * N
37
38
39def sc_sdf_xyz(N, phi):
40 """SC single sphere, SDF as 3-D array sdf[x,y,z] in grid units (dx=1); negative inside the sphere."""
41 R = sphere_radius(phi, N)
42 g = np.arange(N) + 0.5 # cell centres
43 X, Y, Z = np.meshgrid(g, g, g, indexing="ij")
44 c = N / 2.0
45 return np.sqrt((X - c) ** 2 + (Y - c) ** 2 + (Z - c) ** 2) - R, R
46
47
48def drag_K(umean, R, N, f, mu):
49 """K = F_total/(6 pi mu R U_sup), F_total = f*N^3 (grid units), U_sup = mean(u)."""
50 return f * N ** 3 / (6.0 * np.pi * mu * R * umean)
51
52
54 """2x nearest upsample x4 (self-similar packing: u_2N(2x) ~ 4*u_N(x)) -> near-steady seed."""
55 return np.repeat(np.repeat(np.repeat(a, 2, 0), 2, 1), 2, 2) * 4.0
56
57
58def run_sdflow(N, phi, mu=0.1, f=1e-3, dt=None, max_steps=600, tol=1e-6, coarse="rediscretized",
59 seed=None):
60 """Multilevel MG-PCG pressure solve; `coarse` selects the coarse-operator mode
61 ('rediscretized' = the geometric per-level cut-cell operator, the recommended default;
62 'galerkin' = the inconsistent aggregation path; 'const' = geometry-blind coarse).
63 `seed` = (u,v,w) from the next-coarser N, upsampled here, to start the march near steady."""
64 from peclet import flow as sdflow
65 if dt is None:
66 dt = 60.0 if N <= 64 else 120.0
67 lv = max(2, int(np.log2(N)) - 1) # coarsen to ~4^3
68 sdf, R = sc_sdf_xyz(N, phi)
69 s = sdflow.Solver(N, N, N)
70 s.set_rho(1.0); s.set_mu(mu); s.set_dt(dt); s.set_body_force(f, 0, 0); s.set_advection(False)
71 s.set_velocity_solver_params(200) # velocity RB-GS (the velocity MG under-
72 # converges the IBM diffusion -- separate bug)
73 s.set_pressure_multigrid(True, levels=lv)
74 s.set_pressure_pcg(True, max_iter=200, rtol=1e-8)
75 s.set_solid(sdf, cutcell_pressure=True, pressure_coarse=coarse)
76 if seed is not None:
77 u, v, w = (np.asfortranarray(_upsample2(c)) for c in seed)
78 s.set_state(u, v, w)
79 prev = 0.0; t0 = time.time()
80 for it in range(max_steps):
81 s.step()
82 if it % 5 == 4:
83 m = float(s.get_u().mean())
84 if it > 10 and abs(m - prev) < tol * (abs(m) + 1e-30):
85 break
86 prev = m
87 m = float(s.get_u().mean())
88 return drag_K(m, R, N, f, mu), (s.get_u(), s.get_v(), s.get_w()), time.time() - t0
89
90
91def main():
92 Ns = [int(x) for x in sys.argv[1].split(",")] if len(sys.argv) > 1 else [32, 64, 128]
93 phis = [float(x) for x in sys.argv[2].split(",")] if len(sys.argv) > 2 else [0.064, 0.125, 0.216]
94 print("=== Zick & Homsy (1982) SC drag factor K: sdflow ===")
95 print(f"{'phi':>7} {'N':>4} {'K_ZH':>8} {'K_sdflow':>9} {'err%':>7}")
96 for phi in phis:
97 kref = zh_ref(phi)
98 seed = None # coarse->fine continuation per phi
99 for N in sorted(Ns):
100 ks, fld, ts = run_sdflow(N, phi, seed=seed)
101 seed = fld
102 print(f"{phi:7.4f} {N:4d} {kref:8.3f} {ks:9.3f} {100*(ks-kref)/kref:7.2f}", flush=True)
103
104
105if __name__ == "__main__":
106 main()
run_sdflow(N, phi, mu=0.1, f=1e-3, dt=None, max_steps=600, tol=1e-6, coarse="rediscretized", seed=None)