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