103 """Trilinear resample of one staggered component.
105 `src_pos[d]` and `dst_pos[d]` are the PHYSICAL coordinates of the sample points along axis d,
106 normalised to [0,1) of the respective box. x and z wrap (periodic), y clamps (walls).
109 for d, wrap
in enumerate((
True,
False,
True)):
110 sp, dp = src_pos[d], dst_pos[d]
113 spx = np.concatenate([sp - 1.0, sp, sp + 1.0])
114 idx = np.searchsorted(spx, dp) - 1
115 idx = np.clip(idx, 0, len(spx) - 2)
116 w = (dp - spx[idx]) / (spx[idx + 1] - spx[idx])
117 i0, i1 = idx % n, (idx + 1) % n
119 idx = np.clip(np.searchsorted(sp, dp) - 1, 0, n - 2)
120 w = np.clip((dp - sp[idx]) / (sp[idx + 1] - sp[idx]), 0.0, 1.0)
121 i0, i1 = idx, idx + 1
122 sl = [
slice(
None)] * 3
123 sl0, sl1 = list(sl), list(sl)
124 sl0[d], sl1[d] = i0, i1
127 out = out[tuple(sl0)] * (1.0 - w.reshape(shape)) + out[tuple(sl1)] * w.reshape(shape)
128 return np.asfortranarray(out)
132 """TBFsolver's turbulent snapshot, resampled onto our grid, in CELL units (u' = S u)."""
133 nxt, nyt, nzt = 192, 160, 96
134 ux =
tbf_read(os.path.join(TBF,
"ux"), (nxt + 1, nyt, nzt))
135 uy =
tbf_read(os.path.join(TBF,
"uy"), (nxt, nyt + 1, nzt))
136 uz =
tbf_read(os.path.join(TBF,
"uz"), (nxt, nyt, nzt + 1))
138 fx = np.arange(nxt + 1) / nxt
139 cx = (np.arange(nxt) + 0.5) / nxt
140 fy = np.arange(nyt + 1) / nyt
141 cy = (np.arange(nyt) + 0.5) / nyt
142 fz = np.arange(nzt + 1) / nzt
143 cz = (np.arange(nzt) + 0.5) / nzt
145 dfx = np.arange(NX) / NX
146 dcx = (np.arange(NX) + 0.5) / NX
147 dfy = np.arange(NY) / NY
148 dcy = (np.arange(NY) + 0.5) / NY
149 dfz = np.arange(NZ) / NZ
150 dcz = (np.arange(NZ) + 0.5) / NZ
154 return (np.asfortranarray(u * S), np.asfortranarray(v * S), np.asfortranarray(w * S))
158 """Perturbed parabolic fallback (mean flow set to the shipped snapshot's bulk, U_b = 0.6113)."""
159 y = (np.arange(NY) + 0.5) / NY * LY
160 prof = 1.5 * 0.6113 * (1.0 - (y - 1.0) ** 2)
161 rng = np.random.default_rng(7)
162 u = np.broadcast_to(prof[
None, :,
None], (NX, NY, NZ)).copy()
163 u += 0.15 * 0.6113 * rng.standard_normal((NX, NY, NZ))
164 v = 0.05 * 0.6113 * rng.standard_normal((NX, NY, NZ))
165 w = 0.05 * 0.6113 * rng.standard_normal((NX, NY, NZ))
169 return (np.asfortranarray(u * S), np.asfortranarray(v * S), np.asfortranarray(w * S))
174 ax = (np.arange(nx)[:,
None] + (np.arange(sub)[
None, :] + 0.5) / sub).ravel()
175 ay = (np.arange(ny)[:,
None] + (np.arange(sub)[
None, :] + 0.5) / sub).ravel()
176 X, Y = ax[:,
None], ay[
None, :]
177 half = np.sqrt(np.maximum(R * R - (X - c[0]) ** 2 - (Y - c[1]) ** 2, 0.0))
178 z0, z1 = c[2] - half, c[2] + half
179 C = np.zeros((nx, ny, nz))
181 seg = np.maximum(np.minimum(z1, k + 1) - np.maximum(z0, k), 0.0)
182 C[:, :, k] = seg.reshape(nx, sub, ny, sub).
mean(axis=(1, 3))
183 return np.asfortranarray(C)
189 print(
"TBFsolver channel_18 on the peclet BLOCK VoF container (WO-W12 gate 4)")
191 print(f
" grid {NX} x {NY} x {NZ} isotropic (TBFsolver: 192 x 160 x 96 anisotropic)")
192 print(f
" box {LXC:.4f} x {LY:.4f} x {LZC:.4f} h (case: {LX:.4f} x {LY:.4f} x {LZ:.4f} h; "
193 f
"the cubic-cell rounding costs {100*(LXC/LX-1):+.1f} % in x and "
194 f
"{100*(LZC/LZ-1):+.1f} % in z)")
195 print(f
" h = {NY/2:.0f} cells, delta_nu = h/Re_tau = {NY/2/RET:.4f} cells -> "
196 f
"Delta+ = {RET/(NY/2):.2f} (TBFsolver: 2.08 / 1.59 / 2.08)")
197 print(f
" D = {2*Rc:.1f} cells (D/Delta = {2*Rc:.1f}, D+ = {2*RBUB*RET:.1f}); 18 bubbles")
198 print(f
" u_tau = {UTAU:.6f}, tau_w = {TAUW:.6e}, Re_tau = {RET}")
200 s = pf.Solver(NX, NY, NZ)
203 s.set_domain_bc(
"-y",
"wall", (0, 0, 0))
204 s.set_domain_bc(
"+y",
"wall", (0, 0, 0))
205 s.set_pressure_geometry(np.full((NX, NY, NZ), 10.0, order=
"F"))
206 s.set_pressure_chebyshev(
True, 800, 1e-10)
208 if os.path.isdir(TBF):
210 src = f
"TBFsolver's own snapshot, resampled ({TBF})"
213 src =
"a perturbed parabolic profile (TBFsolver's snapshot not found)"
214 for c, a
in enumerate((u, v, w)):
216 print(f
" initial condition: {src}")
217 ub = float(u.mean()) / S
218 print(f
" bulk velocity of the IC: U_b = {ub:.4f} (Re_b = {ub/MU:.0f}, U_b+ = {ub/UTAU:.2f})")
222 C = np.zeros((NX, NY, NZ), order=
"F")
226 cx = (i + 0.5) * NX / NBX
228 cz = (k + 0.5) * NZ / NBZ
231 lo = [int(math.floor(q - Rc)) - 1
for q
in (cx, cy, cz)]
232 hi = [int(math.ceil(q + Rc)) + 1
for q
in (cx, cy, cz)]
233 boxes.append([lo[0], lo[1], lo[2], hi[0], hi[1], hi[2]])
234 s.set_vof(np.asfortranarray(C))
235 alpha = float(C.sum()) / (NX * NY * NZ)
236 rho_av = RHO_L + (RHO_G - RHO_L) * alpha
237 print(f
" void fraction {100*alpha:.3f} % (case: 1.492 %), <rho> = {rho_av:.6f}")
239 f0 = S * (TAUW + (RHO_L - rho_av) * GCH)
240 f1 = S * (RHO_G - RHO_L) * GCH
241 s.set_property_model(
"rho",
"linear",
"C", [RHO_L, RHO_G - RHO_L])
242 s.set_property_model(
"mu",
"linear",
"C", [S * S * MU, 0.0])
243 s.set_property_model(
"force_x",
"linear",
"C", [f0, f1])
244 s.set_surface_tension(S ** 3 * SIGMA)
245 s.enable_vof_blocks_from_field(boxes)
246 s.enable_vof_block_csf()
247 print(f
" markers seeded: {len(s.diagnostics.vof_block_stats())}; force_x = {f0:.6e} {f1:+.6e} C")
249 s.set_property_model(
"force_x",
"const",
"C", [S * TAUW])
250 print(f
" SINGLE PHASE: force_x = {S*TAUW:.6e}")
253 yq = (np.arange(NY) + 0.5) / S
257 t, wall0 = 0.0, time.time()
258 maxit, maxdiv, dt = 0, 0.0, 0.0
259 for i
in range(NSTEP):
261 L = s.vof_step_limits()
if not SINGLE
else None
262 dt = 0.2 / max(np.abs(s.get_u()).max(), np.abs(s.get_v()).max(),
263 np.abs(s.get_w()).max(), 1e-30)
265 dt = min(dt, 0.4 * L[
"cfl_dt"], 0.4 * L[
"capillary_dt"])
269 maxit = max(maxit, s.diagnostics.last_pressure_iterations())
270 maxdiv = max(maxdiv, s.max_open_divergence())
272 accU += s.get_u().
mean(axis=(0, 2))
274 accC += s.get_vof().
mean(axis=(0, 2))
277 um = s.get_u().
mean() / S
278 print(f
" step {i:6d} t u_tau/h = {t*UTAU:7.4f} dt = {dt:.3e} U_b = {um:.4f} "
279 f
"press {s.diagnostics.last_pressure_iterations():3d}/800 ({time.time()-wall0:.0f} s)")
281 st = s.diagnostics.vof_block_stats()
282 v = [b[
"volume"]
for b
in st]
283 print(f
" markers {len(st)}: V min {min(v):.2f} max {max(v):.2f} "
284 f
"(seed {4/3*math.pi*Rc**3:.2f}), total area "
285 f
"{sum(b['area'] for b in st):.0f} cells^2")
286 U = accU / max(nacc, 1) / S
287 Cp = accC / max(nacc, 1)
288 print(f
"\n {NSTEP} steps to t u_tau/h = {t*UTAU:.4f} in {time.time()-wall0:.0f} s; "
289 f
"pressure {maxit}/800 "
290 f
"{'OK' if maxit < 800 else '*** CAPPED -> RUN INVALID ***'}, "
291 f
"max|div(open u)| {maxdiv:.2e}")
292 utau_m = math.sqrt(MU * abs(U[0]) / (yq[0]))
293 print(f
" wall-gradient u_tau (first cell) {utau_m:.6f} vs the imposed {UTAU:.6f} "
294 f
"({100*(utau_m/UTAU-1):+.1f} %)")
295 print(
"\n y/h y+ <u>/u_tau void fraction")
296 for j
in range(0, NY // 2):
297 print(f
" {yq[j]:7.4f} {yq[j]/ (MU/UTAU):8.2f} {U[j]/UTAU:12.4f} {Cp[j]:14.5f}")
299 np.savetxt(OUT, np.c_[yq, U / UTAU, Cp],
300 header=
"y/h <u>/u_tau void_fraction (peclet block VoF, channel_18)")
301 print(f
"\n written {OUT}")