48def run(N, RF=0.18, wallgrad=False):
64 f = F * (16.0 / N) ** 2
if (ADVECT
and F_SCALE)
else F
65 CEN = [(0.25*N, 0.25*N, 0.25*N), (0.75*N, 0.75*N, 0.25*N),
66 (0.75*N, 0.25*N, 0.75*N), (0.25*N, 0.75*N, 0.75*N)]
67 node_ints = np.array([1, -1, -1], dtype=np.int32)
68 node_reals = np.zeros(KN_R); node_reals[0] = R
69 node_reals[14] = 1.0; node_reals[15] = 1.0
70 ii = np.zeros((len(CEN), KI_I), dtype=np.int32)
71 ir = np.zeros((len(CEN), KI_R))
72 for m, c
in enumerate(CEN):
73 ii[m] = (0, -1); ir[m, 0:3] = c; ir[m, 6] = 1.0; ir[m, 7] = 1.0
75 s = sdflow.Solver(N, N, N)
76 s.set_rho(RHO); s.set_mu(MU); s.set_dt(DT)
77 s.set_body_force(f, 0.0, 0.0)
78 s.set_advection(ADVECT)
79 s.set_velocity_solver_params(100)
80 s.set_pressure_solver_params(25)
84 s.set_pressure_multigrid(
True, levels=4)
85 s.set_scene(node_ints, node_reals, ii.ravel(), ir.ravel(), periodic=
True)
86 s.set_solid_from_scene(
True)
87 for _
in range(STEPS):
89 u = np.asarray(s.get_u())
90 ft = np.asarray(s.diagnostics.hydro_force_torque_traction())
91 Fh, Th, Fp, Fv = ft[0], ft[1], ft[2], ft[3]
92 fr = np.asarray(s.hydro_force_torque_reaction())
94 Nc = s.fluid_momentum_cells()
97 Vsolid = len(CEN) * 4.0 / 3.0 * np.pi * R**3
98 Vfluid_an = N**3 - Vsolid
100 g = np.arange(N).astype(float)
101 X, Y, Z = np.meshgrid(g, g, g, indexing=
"ij")
102 sd = np.full((N, N, N), 1e30)
103 for (cx, cy, cz)
in CEN:
104 dx = np.abs(X-cx); dx = np.minimum(dx, N-dx)
105 dy = np.abs(Y-cy); dy = np.minimum(dy, N-dy)
106 dz = np.abs(Z-cz); dz = np.minimum(dz, N-dz)
107 sd = np.minimum(sd, np.sqrt(dx*dx+dy*dy+dz*dz) - R)
108 Vfluid_di = float(np.clip(0.5 + sd, 0.0, 1.0).sum())
110 k = MU * float(u.mean()) / f
111 Re = RHO * float(np.abs(u).
mean()) * (2 * R) / MU
113 exp_an, exp_di = f * Vfluid_an, f * Vfluid_di
114 spread = (Fh[:, 0].max() - Fh[:, 0].min()) / abs(Fh[:, 0].
mean())
115 probe = np.asarray(s.wall_area_probe())
116 print(
" N=%3d porosity %.4f k = mu<u>/f = %.4e max|div| %.2e advect=%s Re_d=%.2f"
117 " f=%.4e Re_h=%.2f (semi-implicit limit ~pi)"
118 % (N, Vfluid_an / N**3, k, s.max_open_divergence(), ADVECT, Re, f,
119 RHO * float(np.abs(u).max()) / MU))
120 print(
" A_wall probe / -V_solid = %.5f %.5f %.5f (exactly 1 if the aperture wall-area "
121 "vectors are right)" % tuple(probe / -Vsolid))
122 print(
" sum F_hydro = (%+.6e, %+.6e, %+.6e)" % tuple(tot))
123 print(
" f * V_fluid analytic = %+.6e -> ratio %.5f (err %+.2f%%)"
124 % (exp_an, tot[0] / exp_an, 100 * (tot[0] / exp_an - 1)))
125 print(
" f * V_fluid discrete = %+.6e -> ratio %.5f (err %+.2f%%)"
126 % (exp_di, tot[0] / exp_di, 100 * (tot[0] / exp_di - 1)))
127 print(
" transverse leakage = %.2e (relative to F_x; symmetry says 0)"
128 % (max(abs(tot[1]), abs(tot[2])) / abs(tot[0])))
129 print(
" per-sphere F_x spread = %.2e (4 identical spheres -> owner attribution splits "
130 "the surface evenly)" % spread)
131 print(
" split: pressure %+.6e viscous %+.6e (of the %+.6e needed)"
132 % (Fp[:, 0].sum(), Fv[:, 0].sum(), exp_di))
133 print(
" sum torque = (%+.2e, %+.2e, %+.2e)" % tuple(Th.sum(axis=0)))
134 rtot = FR.sum(axis=0)
135 bt = np.asarray(s.reaction_budget_terms())
136 unst, advs = bt[0], bt[1]
137 exp_re = f * Nc[0] + advs[0] - unst[0]
138 rspread = (FR[:, 0].max() - FR[:, 0].min()) / abs(FR[:, 0].
mean())
139 print(
" REACTION (route b): sum F = (%+.6e, %+.2e, %+.2e)" % tuple(rtot))
140 print(
" identity target = %+.6e ratio-1 = %+.3e (must be solver residual, "
141 "NOT percent -- a percent-level miss means a budget term is missing)"
142 % (exp_re, rtot[0] / exp_re - 1))
143 print(
" terms: f*N_fluid %+.6e sum A (advective wall flux) %+.3e (%+.3f%% of f*N)"
144 " sum unsteady %+.3e (%+.3f%%)"
145 % (f * Nc[0], advs[0], 100 * advs[0] / (f * Nc[0]), unst[0],
146 100 * unst[0] / (f * Nc[0])))
147 print(
" per-sphere spread %.2e transverse %.2e sum torque (%+.2e, %+.2e, %+.2e)"
148 % (rspread, max(abs(rtot[1]), abs(rtot[2])) / abs(rtot[0]), *TR.sum(axis=0)))
149 ok = abs(rtot[0] / exp_re - 1) < 1e-4
150 print(
" REACTION GATE %s" % (
"PASS" if ok
else "FAIL"))
151 return abs(tot[0] / exp_di - 1), spread, abs(rtot[0] / exp_re - 1), ok