39 """Exact signed distance to the slab boundary; > 0 in the fluid (z0 < z < z1)."""
40 z = (np.arange(nz) + 0.5)[
None,
None, :]
41 d = np.minimum(z - z0, z1 - z)
42 return np.asfortranarray(np.broadcast_to(d, (NX, NY, nz)).astype(float).copy())
58def poiseuille(nz, z0, z1, lam, steps=4000, verbose=True):
60 s = pf.Solver(NX, NY, nz)
64 s.set_advection(
False)
65 s.set_body_force((G, 0.0, 0.0))
66 s.set_pressure_multigrid(
True, levels=3)
67 s.set_pressure_solver_params(400)
68 s.diagnostics.set_velocity_solver_params(400)
69 s.set_solid(
slab_sdf(nz, z0, z1), cutcell_pressure=
True)
71 s.set_wall_slip_length(lam)
73 u = np.asarray(s.get_u())[NX // 2, NY // 2, :]
74 zc = np.arange(nz) + 0.5
75 fluid = (zc > z0) & (zc < z1)
76 ue = (G / (2 * MU)) * ((zc - z0) * (z1 - zc) + lam * H)
77 err = np.abs(u[fluid] - ue[fluid]).max()
78 scale = np.abs(ue[fluid]).max()
80 us = (G / (2 * MU)) * lam * H
82 print(f
" lambda {lam:<10.4g} H {H:g} steps {n:5d} (dU {d:.2e}) "
83 f
"max|u - u_exact| {err:.4e} rel {err/scale:.4e} u_slip(exact) {us:.6e} "
84 f
"u_centre {u[fluid][len(u[fluid])//2]:.8e}")
85 return dict(lam=lam, err=err, rel=err / scale, u=u.copy(), ue=ue, fluid=fluid,
86 iters=s.diagnostics.last_pressure_iterations(), sandwich=s.diagnostics.wall_slip_sandwich_cells(),
87 div=s.max_open_divergence_projected(), u_slip=us, steps=n)
90def couette(nz, z0, lam, U=1.0, steps=200):
91 """The work order's gate 1: plane COUETTE over a flat SDF wall carrying the Navier slip.
93 The bottom boundary is an SDF wall at `z0` with slip lambda; the top is the +z DOMAIN face
94 driven at U. A type-1 domain wall IGNORES its tangential velocity (measured: a plain channel
95 with `set_domain_bc(5, 1, (U, 0, 0))` stays identically 0 after 400 steps) -- the moving lid has
96 to be an INFLOW face (type 2) whose velocity is purely tangential, which is what
97 `tests/kokkos_mpi/test_varmu_mpi.cpp` uses. Exact steady Stokes profile:
99 u(z) = U (z - z0 + lambda) / (H + lambda), H = nz - z0
101 (no-slip at the lid, Navier slip at the SDF wall), a LINEAR profile, so the closure has to be
105 s = pf.Solver(NX, NY, nz)
109 s.set_advection(
False)
110 s.set_domain_bc(
"-z",
"wall", (0.0, 0.0, 0.0))
111 s.set_domain_bc(
"+z",
"inflow", (U, 0.0, 0.0))
112 s.set_pressure_pcg(
True, 400, 1e-12)
113 s.diagnostics.set_velocity_solver_params(400)
114 z = (np.arange(nz) + 0.5)[
None,
None, :]
115 d = np.broadcast_to(z - z0, (NX, NY, nz)).astype(float).copy()
116 s.set_solid(np.asfortranarray(d), cutcell_pressure=
True)
118 s.set_wall_slip_length(lam)
120 u = np.asarray(s.get_u())[NX // 2, NY // 2, :]
121 zc = np.arange(nz) + 0.5
123 ue = U * (zc - z0 + lam) / (H + lam)
124 err = np.abs(u[fluid] - ue[fluid]).max()
125 print(f
" lambda {lam:<10.4g} H {H:g} steps {n:5d} (dU {dd:.2e}) "
126 f
"max|u - u_exact| {err:.4e} rel {err/U:.4e} "
127 f
"u(wall,exact) {U*lam/(H+lam):.6e} u(first fluid cell) {u[fluid][0]:.8e} "
128 f
"vs exact {ue[fluid][0]:.8e} press {s.diagnostics.last_pressure_iterations()}")
129 return dict(lam=lam, err=err, rel=err / U)
133 which = sys.argv[1]
if len(sys.argv) > 1
else "all"
134 nz, z0, z1 = 40, 8.25, 32.25
135 if which
in (
"poiseuille",
"all"):
137 print(f
"GATE 1a slip POISEUILLE, {NX}x{NY}x{nz}, SDF walls at z = {z0} / {z1} "
138 f
"(quarter-integer), H = {z1-z0:g}, mu = {MU:g}, G = {G:g}")
139 print(
" exact: u(z) = (G/2mu)((z-z0)(z1-z) + lambda H); theta = 0.25 (low wall) / "
143 for lam
in (0.0, 0.02, 0.05, 0.1, 0.3, 0.5, 1.0):
147 print(f
" sandwich cells (must be 0,0,0): {base['sandwich']}, "
148 f
"max|div| {base['div']:.2e}, pressure iterations {base['iters']}")
149 if which
in (
"couette",
"all"):
150 print(
"\n" +
"=" * 96)
151 print(f
"GATE 1b slip COUETTE, SDF wall at z = {z0}, +z domain wall moving at U")
153 for lam
in (0.0, 0.02, 0.05, 0.1, 0.3, 0.5):
155 if which
in (
"floor",
"all"):
156 print(
"\n" +
"=" * 96)
157 print(
"THE FLOAT FLOOR: the closure is stored in FLOAT, so lambda vanishes from")
158 print(
"D = theta(1+theta) + lambda(1+2 theta) once lambda(1+2 theta) < eps_f32 * D.")
159 print(
"Reported as the measured slip velocity u(z0) - the analytic no-slip profile,")
160 print(
"i.e. how much of the imposed slip actually survives the storage.")
162 r0 =
poiseuille(nz, z0, z1, 0.0, verbose=
False)
163 for lam
in (1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10):
164 r =
poiseuille(nz, z0, z1, lam, verbose=
False)
165 d = np.abs(r[
"u"][r[
"fluid"]] - r0[
"u"][r0[
"fluid"]]).max()
166 expect = (G / (2 * MU)) * lam * (z1 - z0)
167 print(f
" lambda {lam:<9.1e} max|u(lambda) - u(0)| {d:.6e} "
168 f
"expected slip {expect:.6e} ratio {d/expect if expect else float('nan'):8.4f}")