25KN_R, KI_I, KI_R = 16, 2, 17
28def make(N, R, dt, om, advect=False):
30 ni = np.array([1, -1, -1], dtype=np.int32)
31 nr = np.zeros(KN_R); nr[0] = R; nr[14] = 1.0; nr[15] = 1.0
32 ii = np.zeros((1, KI_I), dtype=np.int32); ii[0] = (0, -1)
33 ir = np.zeros((1, KI_R)); ir[0, 0:3] = (x0, x0, x0); ir[0, 6] = 1.0; ir[0, 7] = 1.0
34 s = sdflow.Solver(N, N, N)
35 s.set_rho(RHO); s.set_mu(MU); s.set_dt(dt); s.set_advection(advect)
36 s.set_velocity_solver_params(100); s.set_pressure_solver_params(25)
40 if hasattr(s,
"set_velocity_residual_tolerance"):
41 s.set_velocity_residual_tolerance(0.0)
42 s.set_pressure_multigrid(
True, levels=4)
43 s.set_scene(ni, nr, ii.ravel(), ir.ravel(), periodic=
True)
44 s.set_instance_motion(0, lin_vel=[0, 0, 0], ang_vel=[0, 0, om], center=[x0, x0, x0])
45 s.set_solid_from_scene(
True)
50 s =
make(N, R, dt, OM0)
51 for _
in range(steps):
53 T = abs(np.asarray(s.hydro_force_torque_reaction())[1][0][2])
54 F = np.linalg.norm(np.asarray(s.hydro_force_torque_reaction())[0][0])
55 return T / (8 * np.pi * MU * R ** 3 * OM0), F / (MU * R ** 2 * OM0)
59 print(
"A. static spin vs T = 8 pi mu a^3 Omega (lambda_rot = T_measured / T_exact)")
60 rungs = [(64, 9.6), (96, 9.6), (96, 14.4)]
61 if os.environ.get(
"ROT_FULL",
"0") ==
"1":
62 rungs += [(128, 9.6), (128, 19.2)]
69 print(
" N=%3d R=%5.2f lambda_rot = %.5f (err %+5.2f%%) |F|net/(mu R^2 Om) = %.1e"
70 % (N, R, l, 100 * (l - 1), fnet))
71 ok = ok
and abs(l - 1) < 0.05
and fnet < 1e-9
73 e64, e96 = abs(lam[(64, 9.6)] - 1), abs(lam[(96, 9.6)] - 1)
75 print(
" GATE A %s [|err| < 5%% at every rung; box growth moves it toward the exact "
76 "answer (%.2f%% -> %.2f%%); the net force stays at round-off]"
77 % (
"PASS" if (ok
and conv)
else "FAIL", 100 * e64, 100 * e96))
78 return ok
and conv, lam[(64, 9.6)]
82 from peclet
import dem
as pdem
83 from peclet_coupling.resolved
import ResolvedCfdDem
84 N, R, DT = 64, 9.6, 5.0
86 m = RHO_P * 4 / 3 * np.pi * R ** 3
88 tau_pred = I / (8 * np.pi * MU * R ** 3 * lam)
89 nstep = int(1.2 * tau_pred / DT)
90 print(
"B. spin-decay through ResolvedCfdDem (apply_torque=True): I dOmega/dt = -T(Omega)")
91 print(
" tau_pred = I/(8 pi mu R^3 lambda_rot) = %.1f (%d steps at dt=%.0f)"
92 % (tau_pred, nstep, DT))
93 d = pdem.Simulation(8)
94 d.set_gravity(0.0, 0.0, 0.0)
95 d.set_sphere_shape(1.0)
97 d.set_positions(np.array([[0.5 * N, 0.5 * N, 0.5 * N]], dtype=np.float32))
98 d.set_inv_mass(np.array([1.0 / m], dtype=np.float32))
99 d.set_inv_inertia(np.array([[1.0 / I] * 3], dtype=np.float32))
100 d.set_angular_velocities(np.array([[0.0, 0.0, OM0]], dtype=np.float32))
101 s = sdflow.Solver(N, N, N)
102 s.set_rho(RHO); s.set_mu(MU); s.set_dt(DT); s.set_advection(
False)
103 s.set_velocity_solver_params(100); s.set_pressure_solver_params(25)
107 if hasattr(s,
"set_velocity_residual_tolerance"):
108 s.set_velocity_residual_tolerance(0.0)
109 s.set_pressure_multigrid(
True, levels=4)
110 drv = ResolvedCfdDem(s, d, radius=R, mu=MU, rho_f=RHO, fluid_dt=DT, dem_substeps=10,
111 periodic=
True, rho_p=RHO_P, buoyancy=
False, move=
True,
114 for k
in range(nstep):
116 ts.append((k + 1) * DT)
117 oms.append(float(np.asarray(d.get_angular_velocities())[0, 2]))
118 ts = np.array(ts); oms = np.array(oms)
120 mfit = ts <= tau_pred
121 rate = -np.polyfit(ts[mfit], np.log(oms[mfit] / OM0), 1)[0]
122 ratio = rate * tau_pred
123 print(
" Omega decayed %.4f -> %.4f of Omega0 over %.0f time units"
124 % (1.0, oms[-1] / OM0, ts[-1]))
125 print(
" fitted decay rate * tau_pred = %.4f (1 = the same box's own calibrated drag)"
127 ok = abs(ratio - 1) < 0.10
128 print(
" GATE B %s [claim: the dem-integrated decay matches the same box's calibrated "
129 "rotational drag within 10%%; the co-rotating-fluid feedback makes the tail slower "
130 "than exponential, which is physics, so the fit window is the first tau]"
131 % (
"PASS" if ok
else "FAIL"))