flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
duct_re_gate.py
Go to the documentation of this file.
1"""Confinement at finite Re, static twin of the ten Cate tank.
2
3Lab frame: tank at rest, fluid at rest, sphere falls at -U. Sphere frame: sphere FIXED, duct walls
4translate at +U, far fluid moves at +U -- plug flow, zero wall shear, exactly the quiescent lab
5state. No moving geometry (the duct is y-invariant, so its translation is a pure wall-velocity
6datum), so the finite-Re Galilean gate's clean bill applies and what is measured here is the
7solver's confinement physics alone.
8
9K(Re) = Cd_duct(Re) / Cd_periodic(Re) is the confinement factor. Creeping expectation for a sphere
10on the axis of a square duct with d/W = 0.15 (Faxen): K ~ 1.39. At Re = 1.5 with the wall at
113.3 d >> nu/U = 0.67 d the Stokeslet is screened and the experiment implies K ~ 1.05-1.15. A solver
12that returns ~1.39 at Re = 1.5 is not screening.
13
14Usage: PYTHONPATH=<flow build> python duct_re_gate.py [DH=8] [Re=1.5] [STEPS=800]
15"""
16import sys
17import numpy as np
18from peclet import flow as sdflow
19from peclet import geom
20
21DH = float(sys.argv[1]) if len(sys.argv) > 1 else 8.0
22RE = float(sys.argv[2]) if len(sys.argv) > 2 else 1.5
23STEPS = int(sys.argv[3]) if len(sys.argv) > 3 else 800
24U = 0.02
25NU = U * DH / RE
26DT = 3.2 if RE > 0.5 else 8.0
27KI_I, KI_R = 2, 17
28OFF = 0.3
29WALL = 4.3
30L = int(np.ceil((100 / 15 * DH + 2 * WALL) / 8) * 8) # the ten Cate cross-section
31WX = (L - 100 / 15 * DH) / 2
32NY = int(np.ceil(12 * DH / 8) * 8) # periodic streamwise length 12 d
33
34
35def abraham_cd(re):
36 return 24.0 / 9.06 ** 2 * (9.06 / np.sqrt(re) + 1.0) ** 2
37
38
39def run(duct):
40 b = geom.SceneBuilder()
41 sph = b.add_leaf("sphere", [DH / 2])
42 if duct:
43 slab = b.add_leaf("box", [L / 2 + 1.0, NY * 2.0, L / 2 + 1.0])
44 cav = b.add_leaf("box", [(L - 2 * WX) / 2, NY * 2.0, (L - 2 * WX) / 2])
45 duc = b.add_difference(slab, cav)
46 ni, nr, _, _ = b.encode()
47 nI = 2 if duct else 1
48 ii = np.zeros((nI, KI_I), dtype=np.int32); ir = np.zeros((nI, KI_R))
49 x0 = 0.5 * L + OFF
50 ii[0] = (sph, -1); ir[0, 0:3] = (x0, 0.5 * NY + OFF, x0); ir[0, 6] = 1.0; ir[0, 7] = 1.0
51 if duct:
52 ii[1] = (duc, -1); ir[1, 0:3] = (x0, 0.5 * NY, x0); ir[1, 6] = 1.0; ir[1, 7] = 1.0
53 s = sdflow.Solver(L, NY, L)
54 s.set_rho(1.0); s.set_mu(NU); s.set_dt(DT); s.set_advection(True)
55 s.set_velocity_solver_params(60); s.set_pressure_solver_params(20)
56 s.set_pressure_multigrid(True, levels=4)
57 s.set_scene(np.asarray(ni, np.int32), np.asarray(nr, float), ii.ravel(), ir.ravel(),
58 periodic=True)
59 if duct:
60 s.set_instance_motion(1, lin_vel=[0.0, U, 0.0]) # walls move WITH the plug: zero shear
61 s.set_solid_from_scene(True)
62 s.set_velocity(1, np.full((L, NY, L), U, dtype=np.float64, order="F"))
63 # Hold the mean: body force = estimated drag / fluid volume (creeping duct: Faxen x Stokes).
64 fest = abraham_cd(RE) * 0.5 * U ** 2 * np.pi * (DH / 2) ** 2 * (1.4 if duct else 1.05)
65 vfl = ((L - 2 * WX) ** 2 * NY if duct else L * L * NY) - np.pi / 6 * DH ** 3
66 import os
67 if os.environ.get("DUCT_NOBF", "0") != "1":
68 s.set_body_force(0.0, fest / vfl, 0.0)
69 v0 = float(np.asarray(s.get_v()).sum())
70 print(" [%s] initial sum(v)/vfl = %.5f (fluid v-points with |v|>0: %d, vfl=%d)"
71 % ("duct" if duct else "periodic", v0 / vfl, int((np.abs(np.asarray(s.get_v())) > 0).sum()), int(vfl)))
72 Fh = []; vm = []
73 for k in range(STEPS):
74 s.step()
75 if k < 100 and k % 10 == 9:
76 print(" k=%3d momentum fraction %.4f F_y=%.4e" % (k + 1, float(np.asarray(s.get_v()).sum()) / v0,
77 float(np.asarray(s.hydro_force_torque_reaction())[0][0][1])))
78 Fh.append(float(np.asarray(s.hydro_force_torque_reaction())[0][0][1]))
79 if k % 100 == 99 or k == STEPS - 1:
80 vm.append((k + 1, float(np.asarray(s.get_v()).sum()) / vfl))
81 Fh = np.array(Fh)
82 n4 = STEPS * 3 // 4
83 return Fh[n4:].mean(), Fh, vm, vfl
84
85
86print("Duct confinement gate: DH=%g Re=%g duct %dx%dx%d (width %.2f d), periodic ref %dx%dx%d"
87 % (DH, RE, L, NY, L, (L - 2 * WX) / DH, L, NY, L))
88fP, FP, vP, _ = run(False)
89fD, FD, vD, _ = run(True)
90for tag, f, vm in (("periodic", fP, vP), ("duct", fD, vD)):
91 u = vm[-1][1]
92 re = u * DH / NU
93 cd = f / (0.5 * u ** 2 * np.pi * (DH / 2) ** 2)
94 print(" %-8s F_y=%.5e fluid-mean v=%.5f (drift %s) Re=%.3f Cd=%.4f Cd/Abraham=%.4f"
95 % (tag, f, u, "/".join("%.5f" % t[1] for t in vm[::2]), re, cd, cd / abraham_cd(re)))
96print(" quasi-steadiness (last-quarter slope / mean): periodic %.4f duct %.4f"
97 % ((FP[-1] - FP[STEPS * 3 // 4]) / fP, (FD[-1] - FD[STEPS * 3 // 4]) / fD))
98K = fD / fP * (vP[-1][1] / vD[-1][1])
99print(" K(Re=%g) = Cd_duct / Cd_periodic = %.4f [creeping Faxen ~1.39; screened experiment ~1.05-1.15]"
100 % (RE, K))
static double mean(const std::vector< double > &a)