1"""RCP permeability A/B: {staggered, collocated} x {cutcell, ghost} on the SAME random close
2packing (the peclet-examples random-packed-bed geometry: dem pack_bed, N=180, phi~0.63, seed 3;
3cached in rcp_pack_seed3.npz — regenerate with the example's pack_bed if absent).
5Context: the staggered examples-check found the ghost projection inflates the RCP permeability
6(+21/+15/+12% at Ng=32/44/56 vs the cutcell reference) because point-based closure faces cannot
7throttle sub-cell throats the way apertures do, and the binary COUPLED graph fragments (guard
8decouples the pockets). This study measures whether the collocated ghost inherits the same
9defect, with the collocated cutcell (mode 0) and both staggered variants as anchors. Protocol =
10the example's steady loop with a tighter stop (tol 1e-6) so the scheme, not the stop criterion,
16sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..",
"..",
"scripts"))
17from _bootstrap
import ensure_flow
21HERE = os.path.dirname(os.path.abspath(__file__))
26 """Select a collocated scheme by the integer face-interp mode this script was written with:
27 0/7/9 are the public strings ('plain' / 'embed' / 'gauge-exact'), 5/6 the diagnostics rungs,
28 and every other number was deleted at 1.0.0 (the call then raises)."""
29 names = {0:
"plain", 7:
"embed", 9:
"gauge-exact"}
31 s.set_collocated_scheme(names[mode])
33 s.diagnostics.set_face_interp(mode)
36 g = (np.arange(Ng) + 0.5) / Ng * side
37 X, Y, Z = np.meshgrid(g, g, g, indexing=
"ij")
38 best = np.full((Ng, Ng, Ng), 1e30)
39 for k
in range(len(pos)):
40 dx = X - (pos[k, 0] + side / 2)
41 dx -= side * np.round(dx / side)
42 dy = Y - (pos[k, 1] + side / 2)
43 dy -= side * np.round(dy / side)
44 dz = Z - (pos[k, 2] + side / 2)
45 dz -= side * np.round(dz / side)
46 best = np.minimum(best, np.sqrt(dx * dx + dy * dy + dz * dz) - r[k])
50def permeability(Ng, sdf, side, colloc, ghost, mode=0, mu=0.1, F=1e-3, dt=80.0, max_steps=3000,
52 lv = max(2, int(np.log2(Ng)) - 1)
53 s = (flow.SolverColocated
if colloc
else flow.Solver)(Ng, Ng, Ng)
57 s.set_body_force((F, 0, 0))
58 s.set_advection(
False)
59 s.diagnostics.set_velocity_solver_params(150)
60 s.set_pressure_multigrid(
True, levels=lv)
61 s.set_pressure_pcg(
True, 400, 1e-9)
63 s.diagnostics.set_ghost_projection(
True, 1, 2)
66 s.set_solid(np.asfortranarray(sdf), cutcell_pressure=
True)
68 for it
in range(max_steps):
71 m = float(s.get_u().
mean())
72 if it > 10
and abs(m - prev) < tol * (abs(m) + 1e-30):
75 umean = float(s.get_u().
mean())
76 return dict(k=mu * umean / F * (side / Ng) ** 2, steps=it + 1,
77 pit=s.diagnostics.last_pressure_iterations(), div=s.max_open_divergence())
80if __name__ ==
"__main__":
81 d = np.load(os.path.join(HERE,
"rcp_pack_seed3.npz"))
82 pos, r, side, phi = d[
"pos"], d[
"r"], float(d[
"side"]), float(d[
"phi"])
83 print(f
"RCP: N={len(pos)} phi={phi:.4f} side={side:.3f}", flush=
True)
86 variants = [(
"stag cutcell",
False,
False, 0), (
"stag ghost",
False,
True, 0),
87 (
"col cutcell",
True,
False, 0), (
"col ghost",
True,
True, 0),
88 (
"col hyb9",
True,
False, 9)]
89 print(
"columns: " +
" | ".join(n
for n, *_
in variants)
90 +
" (k, % vs stag-cutcell, PCG/BiCGStab iters)", flush=
True)
91 for Ng
in (32, 44, 56):
95 for name, colloc, ghost, mode
in variants:
100 row += (f
" {out['k']:.4e} {100 * (out['k'] / kref - 1):+6.1f}%"
101 f
" i{out['pit']:>3} |")
102 print(row, flush=
True)
_collocated_mode(s, mode)
permeability(Ng, sdf, side, colloc, ghost, mode=0, mu=0.1, F=1e-3, dt=80.0, max_steps=3000, tol=1e-6)
sdf_from_pack(Ng, pos, r, side)
static double mean(const std::vector< double > &a)