flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
vof_advect_scenes.hpp
Go to the documentation of this file.
1// Test-only scene builders shared by tests/kokkos/test_vof_advect.cpp and
2// tests/kokkos_mpi/test_vof_advect_mpi.cpp (VoF rung V1, WO-E).
3//
4// Everything here is a pure function of GLOBAL cell/face indices, so a block that owns a sub-range
5// of the global grid gets bit-identical data to the corresponding part of a single-block run. That
6// is what makes the np 1/2/4 bitwise gate meaningful rather than a tautology.
7//
8// The one piece with real content is `fillLeVeque`: the 3D deformation field is sampled as the
9// DISCRETE CURL of an edge-based vector potential rather than pointwise, so the discrete face
10// divergence is round-off rather than O(h^2). Weymouth-Yue conserves volume to exactly the accuracy
11// with which that divergence vanishes -- the dilation term contributes `H(C-1/2) * div * dt/h` to
12// the volume budget of EVERY cell, interior full cells included -- so pointwise sampling would put
13// the conservation floor at O(h^2), not at 1e-15.
14#ifndef PECLET_FLOW_TESTS_VOF_ADVECT_SCENES_HPP
15#define PECLET_FLOW_TESTS_VOF_ADVECT_SCENES_HPP
16
17#include <cmath>
18#include <Kokkos_Core.hpp>
19
20#include "vof/advect_wy.hpp"
21#include "vof/plic.hpp"
22
23namespace vofscene {
24
30
32struct Block {
33 I3 n{0, 0, 0};
34 I3 e{0, 0, 0};
35 int g = 3;
36 I3 o{0, 0, 0};
37};
38
39inline Block blockOf(const WyAdvector& a, I3 origin) {
40 return Block{a.inner(), a.extent(), a.ghost(), origin};
41}
42
43// ------------------------------------------------------------------------------ ghost fills
44
47inline void periodicFill(SField f, I3 e, int g, bool px, bool py, bool pz) {
48 const long st[3] = {1, e.x, static_cast<long>(e.x) * e.y};
49 const int dims[3] = {e.x, e.y, e.z};
50 const bool per[3] = {px, py, pz};
51 for (int a = 0; a < 3; ++a) {
52 if (!per[a])
53 continue;
54 const int b = (a + 1) % 3, c = (a + 2) % 3;
55 const long sa = st[a], sb = st[b], sc = st[c];
56 const int N = dims[a] - 2 * g;
57 Kokkos::parallel_for(
58 "vofscene::periodicFill",
59 peclet::flow::MDRange2<SExec>(SExec(), {0, 0}, {dims[b], dims[c]}),
60 KOKKOS_LAMBDA(int p0, int p1) {
61 const long base = static_cast<long>(p0) * sb + static_cast<long>(p1) * sc;
62 for (int gl = 0; gl < g; ++gl) {
63 f(base + static_cast<long>(gl) * sa) = f(base + static_cast<long>(gl + N) * sa);
64 f(base + static_cast<long>(g + N + gl) * sa) = f(base + static_cast<long>(g + gl) * sa);
65 }
66 });
67 Kokkos::fence();
68 }
69}
70
86inline void clampFill(SField f, Block b, I3 gs, bool px, bool py, bool pz) {
87 if (px && py && pz)
88 return;
89 const I3 e = b.e, o = b.o;
90 const int g = b.g;
91 const bool per[3] = {px, py, pz};
92 const int gsz[3] = {gs.x, gs.y, gs.z};
93 const bool p0 = per[0], p1 = per[1], p2 = per[2];
94 const int q0 = gsz[0], q1 = gsz[1], q2 = gsz[2];
95 Kokkos::parallel_for(
96 "vofscene::clampFill", peclet::flow::MDRange3<SExec>(SExec(), {0, 0, 0}, {e.x, e.y, e.z}),
97 KOKKOS_LAMBDA(int x, int y, int z) {
98 const int gx = x - g + o.x, gy = y - g + o.y, gz = z - g + o.z;
99 const int cx = p0 ? gx : (gx < 0 ? 0 : (gx >= q0 ? q0 - 1 : gx));
100 const int cy = p1 ? gy : (gy < 0 ? 0 : (gy >= q1 ? q1 - 1 : gy));
101 const int cz = p2 ? gz : (gz < 0 ? 0 : (gz >= q2 ? q2 - 1 : gz));
102 if (cx != gx || cy != gy || cz != gz)
103 f(L3(x, y, z, e)) = f(L3(cx - o.x + g, cy - o.y + g, cz - o.z + g, e));
104 });
105 Kokkos::fence();
106}
107
108// ------------------------------------------------------------------------------ colour fields
109
111template <typename F>
112inline void forEachExtended(Block b, F fn) {
113 const I3 e = b.e;
114 const int g = b.g;
115 const I3 o = b.o;
116 Kokkos::parallel_for(
117 "vofscene::forEachExtended",
118 peclet::flow::MDRange3<SExec>(SExec(), {0, 0, 0}, {e.x, e.y, e.z}),
119 KOKKOS_LAMBDA(int x, int y, int z) {
120 fn(L3(x, y, z, e), x - g + o.x, y - g + o.y, z - g + o.z);
121 });
122 Kokkos::fence();
123}
124
127inline void initSphere(SField c, Block b, double h, double cx, double cy, double cz, double r,
128 int levels = 4) {
130 b, KOKKOS_LAMBDA(long i, int gx, int gy, int gz) {
131 c(i) =
132 peclet::flow::vof::sphereCellFraction(cx, cy, cz, r, gx * h, gy * h, gz * h, h, levels);
133 });
134}
135
137inline void initSlab(SField c, Block b, double h, int axis, double lo, double hi) {
139 b, KOKKOS_LAMBDA(long i, int gx, int gy, int gz) {
140 const int gi[3] = {gx, gy, gz};
141 const double a = gi[axis] * h, bb = a + h;
142 const double cover = Kokkos::fmax(0.0, Kokkos::fmin(bb, hi) - Kokkos::fmax(a, lo));
143 c(i) = cover / h;
144 });
145}
146
151inline void initZalesak(SField c, Block b, double h, double cx, double cy, double r, double sw,
152 double stop, int nsub = 64) {
153 const double w = 1.0 / nsub;
155 b, KOKKOS_LAMBDA(long i, int gx, int gy, int) {
156 const double x0 = gx * h, y0 = gy * h;
157 int in = 0;
158 for (int q = 0; q < nsub; ++q) {
159 const double y = y0 + (q + 0.5) * w * h;
160 for (int p = 0; p < nsub; ++p) {
161 const double x = x0 + (p + 0.5) * w * h;
162 const double dx = x - cx, dy = y - cy;
163 const bool inCircle = dx * dx + dy * dy < r * r;
164 const bool inSlot = Kokkos::fabs(dx) < sw && y < stop;
165 if (inCircle && !inSlot)
166 ++in;
167 }
168 }
169 c(i) = static_cast<double>(in) / (static_cast<double>(nsub) * nsub);
170 });
171}
172
173// ------------------------------------------------------------------------------ velocity fields
174
176inline void fillUniform(WyAdvector& a, Block b, double ux, double uy, double uz) {
177 SField u = a.faceU(), v = a.faceV(), w = a.faceW();
179 b, KOKKOS_LAMBDA(long i, int, int, int) {
180 u(i) = ux;
181 v(i) = uy;
182 w(i) = uz;
183 });
184}
185
188inline void fillRotation(WyAdvector& a, Block b, double h, double cx, double cy, double omega) {
189 SField u = a.faceU(), v = a.faceV(), w = a.faceW();
191 b, KOKKOS_LAMBDA(long i, int gx, int gy, int) {
192 u(i) = -omega * ((gy + 0.5) * h - cy);
193 v(i) = omega * ((gx + 1.0) * h - cx);
194 w(i) = 0.0;
195 });
196}
197
207KOKKOS_INLINE_FUNCTION double lvSin2(double t) {
208 const double s = Kokkos::sin(3.14159265358979323846 * t);
209 return s * s;
210}
212KOKKOS_INLINE_FUNCTION double lvAy(double x, double y, double z, double phase) {
213 const double PI = 3.14159265358979323846;
214 return -(lvSin2(x) * lvSin2(z) / PI) * Kokkos::sin(2.0 * PI * y) * phase;
215}
217KOKKOS_INLINE_FUNCTION double lvAz(double x, double y, double z, double phase) {
218 const double PI = 3.14159265358979323846;
219 return (lvSin2(x) * lvSin2(y) / PI) * Kokkos::sin(2.0 * PI * z) * phase;
220}
221
222inline void fillLeVeque(WyAdvector& a, Block b, double h, double phase) {
223 SField u = a.faceU(), v = a.faceV(), w = a.faceW();
224 const double invh = 1.0 / h;
226 b, KOKKOS_LAMBDA(long i, int gx, int gy, int gz) {
227 const double xn = gx * h, yn = gy * h, zn = gz * h; // node
228 const double yc = (gy + 0.5) * h, zc = (gz + 0.5) * h;
229 const double xp = (gx + 1) * h, yp = (gy + 1) * h, zp = (gz + 1) * h; // next node
230 // u = dAz/dy - dAy/dz at (xp, yc, zc)
231 u(i) = (lvAz(xp, yp, zc, phase) - lvAz(xp, yn, zc, phase)) * invh -
232 (lvAy(xp, yc, zp, phase) - lvAy(xp, yc, zn, phase)) * invh;
233 // v = -dAz/dx at (xc, yp, zc) (A_x = 0)
234 v(i) = -(lvAz(xp, yp, zc, phase) - lvAz(xn, yp, zc, phase)) * invh;
235 // w = dAy/dx at (xc, yc, zp)
236 w(i) = (lvAy(xp, yc, zp, phase) - lvAy(xn, yc, zp, phase)) * invh;
237 });
238}
239
240// ------------------------------------------------------------------------------ metrics
241
243inline double l1Diff(SField a, SField b, I3 e, I3 n, int g) {
244 double s = 0.0;
245 Kokkos::parallel_reduce(
246 "vofscene::l1",
247 peclet::flow::MDRange3<SExec>(SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z}),
248 KOKKOS_LAMBDA(int x, int y, int z, double& acc) {
249 const long i = L3(x, y, z, e);
250 acc += Kokkos::fabs(a(i) - b(i));
251 },
252 s);
253 Kokkos::fence();
254 return s;
255}
256
258inline double lInfDiff(SField a, SField b, I3 e, I3 n, int g) {
259 double s = 0.0;
260 Kokkos::parallel_reduce(
261 "vofscene::linf",
262 peclet::flow::MDRange3<SExec>(SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z}),
263 KOKKOS_LAMBDA(int x, int y, int z, double& acc) {
264 const long i = L3(x, y, z, e);
265 acc = Kokkos::fmax(acc, Kokkos::fabs(a(i) - b(i)));
266 },
267 Kokkos::Max<double>(s));
268 Kokkos::fence();
269 return s;
270}
271
272inline SField copyOf(SField src, const char* name) {
273 SField dst(name, src.extent(0));
274 Kokkos::deep_copy(dst, src);
275 return dst;
276}
277
278} // namespace vofscene
279
280#endif // PECLET_FLOW_TESTS_VOF_ADVECT_SCENES_HPP
flow — VoF rung V1: Weymouth & Yue (2010) directionally-split, exactly volume-conserving geometric ad...
Weymouth-Yue split advection of a colour field on an extended (inner + ghost) block.
long L3(int x, int y, int z, I3 e)
Kokkos::DefaultExecutionSpace SExec
Kokkos::View< double *, SMem > SField
double lInfDiff(SField a, SField b, I3 e, I3 n, int g)
max |a - b| over the inner region.
void fillLeVeque(WyAdvector &a, Block b, double h, double phase)
double l1Diff(SField a, SField b, I3 e, I3 n, int g)
sum |a - b| over the inner region (cell-fraction units).
void forEachExtended(Block b, F fn)
Visit every cell of the extended block with (local linear index, global cell index).
SField copyOf(SField src, const char *name)
void initZalesak(SField c, Block b, double h, double cx, double cy, double r, double sw, double stop, int nsub=64)
Zalesak's slotted disk in the (x,y) plane, uniform in z.
void periodicFill(SField f, I3 e, int g, bool px, bool py, bool pz)
Periodic wrap of a single-block extended field (serial reference path; the MPI path uses core's GridH...
void fillUniform(WyAdvector &a, Block b, double ux, double uy, double uz)
Uniform translation. Discrete divergence is EXACTLY zero (identical values differenced).
void initSphere(SField c, Block b, double h, double cx, double cy, double cz, double r, int levels=4)
Sphere of fluid (C = 1 inside), exact to ~5e-7 relative volume at R/h = 16 via the octree helper in p...
double lvAz(double x, double y, double z, double phase)
A_z of the LeVeque vector potential (lives on z-edges: node x, node y, centre z).
void clampFill(SField f, Block b, I3 gs, bool px, bool py, bool pz)
Zero-gradient (Neumann) fill of the ghost cells that lie OUTSIDE the global domain on a non-periodic ...
double lvSin2(double t)
The LeVeque 3D deformation field u = 2 sin^2(pi x) sin(2 pi y) sin(2 pi z) cos(pi t / T) v = - sin(2 ...
Block blockOf(const WyAdvector &a, I3 origin)
void fillRotation(WyAdvector &a, Block b, double h, double cx, double cy, double omega)
Solid-body rotation about (cx,cy) in the (x,y) plane, w = 0.
double lvAy(double x, double y, double z, double phase)
A_y of the LeVeque vector potential (lives on y-edges: node x, centre y, node z).
void initSlab(SField c, Block b, double h, int axis, double lo, double hi)
Slab zlo < x < zhi along axis axis, exact fractions (axis-aligned planes).
flow — thin shim: the container-free VoF PLIC geometric toolbox (rung V0) now live in core.
Where this block sits in the global grid: o is the global index of inner cell (0,0,...
I3 e
extended (inner + 2g) counts
I3 o
global index of the first inner cell
I3 n
inner cell counts
static constexpr int N
static constexpr double F