21 """Random periodic sphere packing: greedily place non-overlapping spheres (min surface gap `gap` cells)
22 until the solid fraction reaches target_phi. SDF = min over spheres of (dist - R), periodic, <0 in solid."""
23 rng = np.random.default_rng(seed)
24 g = np.arange(N) + 0.5
25 X, Y, Z = np.meshgrid(g, g, g, indexing=
"ij")
26 sdf = np.full((N, N, N), 1e30)
28 vsph = 4.0 / 3.0 * np.pi * R ** 3
29 while (len(centers) * vsph) / N ** 3 < target_phi:
35 d = c - cc; d -= N * np.round(d / N)
36 if np.linalg.norm(d) < 2 * R + gap:
39 centers.append(c); placed =
True;
break
43 dx = X - c[0]; dx -= N * np.round(dx / N)
44 dy = Y - c[1]; dy -= N * np.round(dy / N)
45 dz = Z - c[2]; dz -= N * np.round(dz / N)
46 sdf = np.minimum(sdf, np.sqrt(dx * dx + dy * dy + dz * dz) - R)
47 return sdf, len(centers), float((sdf < 0).mean())
50def run(sdf, mode, dt, vcyc=16, levels=4, mu=0.1, f=1e-3, steps=600, tol=1e-7):
51 """mode: 'rbgs' (plain RB-GS reference) | 'vmg' (the staircase velocity-MG, via set_velocity_multigrid)."""
53 s = sdflow.Solver(N, N, N)
54 s.set_rho(1.0); s.set_mu(mu); s.set_dt(dt); s.set_body_force(f, 0, 0); s.set_advection(
False)
56 s.set_velocity_solver_params(400)
58 s.set_velocity_multigrid(
True, levels, vcyc)
59 plv = max(2, int(np.log2(N)) - 1)
60 s.set_pressure_multigrid(
True, levels=plv); s.set_pressure_pcg(
True, max_iter=300, rtol=1e-9)
61 s.set_solid(sdf, cutcell_pressure=
True, pressure_coarse=
"rediscretized")
63 for it
in range(steps):
65 if not np.isfinite(s.get_u()).all():
68 m = float(s.get_u().mean())
69 if it > 10
and abs(m - prev) < tol * (abs(m) + 1e-30):
72 return float(s.get_u().mean())
76 N = int(os.environ.get(
"VMG_N", 64))
77 R = float(os.environ.get(
"VMG_R", 9.0))
78 phi = float(os.environ.get(
"VMG_PHI", 0.35))
79 gap = float(os.environ.get(
"VMG_GAP", 2.0))
80 sdf, nsph, phi_act =
packing_sdf(N, R, phi, gap=gap, seed=int(os.environ.get(
"VMG_SEED", 1)))
81 print(f
"=== packed bed: {nsph} spheres R={R:.0f} in {N}^3, solid phi={phi_act:.3f}, gap~{gap:g} cells ===")
83 uref =
run(sdf,
"rbgs", dt=60.0)
84 print(f
" RB-GS reference: U_sup={uref:.6e}")
85 print(
"--- staircase velocity-MG (set_velocity_multigrid): exact vs RB-GS + large-dt stability ---")
86 for dt
in [60.0, 800.0, 3200.0]:
87 u =
run(sdf,
"vmg", dt=dt, levels=4)
88 e =
"NaN" if u
is None else f
"{abs(u - uref) / abs(uref) * 100:.3f}%"
89 print(f
" vmg dt={dt:6.0f} (beta={mu_b(dt):4.0f}): {'BLEW UP' if u is None else f'U_sup={u:.6e} err {e}'}")
91 print(
"--- coarsening-level robustness (deep levels dissolve thin necks), dt=60 ---")
92 for lv
in [2, 3, 4, 5, 6]:
93 u =
run(sdf,
"vmg", dt=60.0, levels=lv)
94 e =
"NaN" if u
is None else f
"{abs(u - uref) / abs(uref) * 100:.3f}%"
95 print(f
" levels={lv}: {'BLEW UP' if u is None else f'err {e}'}")