26#include <Kokkos_Core.hpp>
38 std::fprintf(stderr, "CHECK failed: %s\n at %s:%d\n", #cond, __FILE__, __LINE__); \
43namespace cs = peclet::core::scheme;
54 const int t = i < 0 ? bcLo : bcHi;
56 return ((i % n) + n) % n;
58 return i < 0 ? -1 - i : 2 * n - 1 - i;
59 return i < 0 ? 0 : n - 1;
64void checkGhosts(
const char* label,
const int bc[6]) {
65 const int nx = 12,
ny = 10, nz = 8;
66 auto f = [&](
int x,
int y,
int z) {
return 1000.0 *
x + 37.0 *
y + 1.25 *
z - 300.0; };
67 std::vector<double>
sdf((std::size_t)nx * ny * nz);
68 for (
int z = 0;
z < nz; ++
z)
69 for (
int y = 0;
y <
ny; ++
y)
70 for (
int x = 0;
x < nx; ++
x)
71 sdf[(std::size_t)
x + (std::size_t)y * nx + (std::size_t)
z * nx *
ny] =
f(x, y, z);
73 for (
int face = 0; face < 6; ++face)
75 s.setDomainBc(face, bc[face], 0.0, 0.0, 0.0);
76 s.setSolid(sdf,
false);
77 auto h = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(),
s.fieldView(
"sdf"));
78 const int ex = nx + 2 *
G, ey =
ny + 2 *
G;
79 const int n[3] = {nx,
ny, nz};
80 long bad = 0, checked = 0;
81 for (
int a = 0;
a < 3; ++
a)
83 for (
int layer = 1; layer <=
G; ++layer) {
84 const int ia =
side ?
n[
a] - 1 + layer : -layer;
85 const int b = (
a + 1) % 3, c = (a + 2) % 3;
86 for (
int jb = 0; jb <
n[
b]; ++jb)
93 h((
long)(g3[0] +
G) + (
long)(g3[1] +
G) * ex + (
long)(g3[2] +
G) * (
long)ex * ey);
94 const int ex3[3] = {
expectIndex(g3[0], nx, bc[0], bc[1]),
98 if (got !=
f(ex3[0], ex3[1], ex3[2]))
102 std::printf(
" ghosts %-28s: %ld / %ld ghost samples (layers 1-2) stale\n", label, bad, checked);
110 double solidVol = 0.0, fluidVol = 0.0, area = 0.0;
111 long cutCells = 0, notSingle = 0, lowRho = 0, sealed = 0, facets = 0, r0 = 0, r2 = 0;
112 double minRho = 1.0, maxSnapRes = 0.0;
113 double sealedMaxKappa = 0.0, sealedVolFrac = 0.0;
114 long kappaMismatch = 0, countMismatch = 0;
115 double maxKappaDiff = 0.0;
119constexpr bool kDeviceBackend =
120 !std::is_same_v<Kokkos::DefaultExecutionSpace::memory_space, Kokkos::HostSpace>;
122Measure measure(
IbmSolver& s,
const int n[3],
const double hp[3]) {
124 const auto& rec =
s.scalarCutGeometry();
125 const auto c =
s.scalarCutCensus();
126 const int ex =
n[0] + 2 *
G, ey =
n[1] + 2 *
G;
127 const long sy = ex, sz = (long)ex * ey;
128 auto sdf = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(),
s.fieldView(
"sdf"));
129 auto kap = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), rec.kappa);
130 auto sax = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), rec.sax);
131 auto say = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), rec.say);
132 auto saz = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), rec.saz);
133 auto alpha = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), rec.fac.alpha);
134 auto rung = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), rec.fac.rungF);
135 const double vol = hp[0] * hp[1] * hp[2];
136 const double maxA = std::fmax(hp[1] * hp[2], std::fmax(hp[0] * hp[2], hp[0] * hp[1]));
138 for (
int z =
G;
z <
n[2] +
G; ++
z)
139 for (
int y =
G;
y <
n[1] +
G; ++
y)
140 for (
int x =
G;
x <
n[0] +
G; ++
x) {
141 const long i =
x +
y * sy +
z * sz;
142 double corner[8], face[6], centre;
144 const cs::CutCellGeometry
g = cs::cutCellGeometryFanTet(corner, face, centre, hp);
145 m.maxKappaDiff = std::fmax(
m.maxKappaDiff, std::fabs(
g.kappa - kap(i)));
146 if (kDeviceBackend ? !(std::fabs(
g.kappa - kap(i)) <= 1e-12)
147 : std::memcmp(&
g.kappa, &kap(
i), sizeof(double)) != 0)
149 m.fluidVol +=
g.kappa *
vol;
150 m.solidVol += (1.0 -
g.kappa) * vol;
153 double area[2][3], cen[2][3];
157 const double rho = std::sqrt(
g.areaPL[0] *
g.areaPL[0] +
g.areaPL[1] *
g.areaPL[1] +
158 g.areaPL[2] *
g.areaPL[2]) /
160 m.minRho = std::fmin(
m.minRho, rho);
161 if (
g.kind != cs::CutCellKind::single)
166 cs::facetAreaVector(ap, hp, aSnap);
167 for (
int d = 0;
d < 3; ++
d)
168 m.maxSnapRes = std::fmax(
m.maxSnapRes, std::fabs(aSnap[d] -
g.areaPL[d]) / maxA);
171 for (
long f = 0;
f < rec.fac.n; ++
f) {
173 if (rung(f) == cs::kProbeR0)
175 if (rung(f) == cs::kProbeR2)
178 m.facets = rec.fac.n;
179 m.sealed =
c.numSealed;
180 m.sealedVolFrac =
c.sealedVolume /
m.fluidVol;
182 auto unk = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), rec.unknown);
183 for (
int z =
G;
z <
n[2] +
G; ++
z)
184 for (
int y =
G;
y <
n[1] +
G; ++
y)
185 for (
int x =
G;
x <
n[0] +
G; ++
x) {
186 const long i =
x +
y * sy +
z * sz;
187 if (kap(i) > 0.0 && unk(i) < 0.5)
188 m.sealedMaxKappa = std::fmax(
m.sealedMaxKappa, kap(i));
191 if (nfacHost != rec.fac.n)
193 CHECK(
c.numFacets == rec.fac.n);
194 CHECK(
c.rungs[0] +
c.rungs[1] +
c.rungs[2] +
c.rungs[3] == rec.fac.n);
200Measure sphereCase(
const int n[3],
const double hp[3],
double R,
const double off[3]) {
201 const std::array<double, 3> extent{
n[0] * hp[0],
n[1] * hp[1],
n[2] * hp[2]};
202 IbmSolver s(n[0], n[1], n[2], extent, {0.0, 0.0, 0.0}, {(long)n[0], (
long)
n[1], (long)n[2]});
203 std::vector<double>
sdf((std::size_t)n[0] * n[1] * n[2]);
204 const double c[3] = {0.5 * extent[0] +
off[0], 0.5 * extent[1] +
off[1],
205 0.5 * extent[2] +
off[2]};
206 for (
int z = 0;
z <
n[2]; ++
z)
207 for (
int y = 0;
y <
n[1]; ++
y)
208 for (
int x = 0;
x <
n[0]; ++
x) {
209 const double dx = (
x + 0.5) * hp[0] - c[0], dy = (y + 0.5) * hp[1] -
c[1],
210 dz = (
z + 0.5) * hp[2] - c[2];
211 sdf[(std::size_t)x + (std::size_t)
y *
n[0] + (std::size_t)z * n[0] * n[1]] =
212 std::sqrt(dx * dx + dy * dy + dz * dz) -
R;
214 s.setSolid(sdf,
false);
215 return measure(s, n, hp);
222void sphereSet(
const char* label,
const double hp[3],
bool strictR0) {
223 const double h = std::fmax(hp[0], std::fmax(hp[1], hp[2]));
224 const double ratios[3] = {8.0, 16.0, 32.0};
225 std::mt19937_64
rng(4242);
226 std::uniform_real_distribution<double>
u(-0.5, 0.5);
227 double rmsV[3], rmsA[3];
228 for (
int r = 0;
r < 3; ++
r) {
229 const double R = ratios[
r] *
h;
231 for (
int a = 0;
a < 3; ++
a)
232 n[a] = (
int)std::lround((2.0 * R + 8.0 * h) / hp[a]);
233 const double Vex = 4.0 / 3.0 * M_PI *
R *
R *
R, Aex = 4.0 * M_PI *
R *
R;
234 const double bound2 = (
h /
R) * (h / R);
235 double sV = 0.0,
sA = 0.0;
236 for (
int o = 0;
o < 3; ++
o) {
237 const double off[3] = {
u(rng) *
h,
u(rng) *
h,
u(rng) *
h};
238 const Measure
m = sphereCase(n, hp, R, off);
239 const double eV =
m.solidVol / Vex - 1.0,
eA =
m.area / Aex - 1.0;
243 " %s R/h=%2.0f off %d: eV %+.3e (bound %.2e) eA %+.3e (bound %.2e) cut %ld "
244 "facets %ld R0 %ld R2 %ld min rho %.4f not-single %ld sealed %ld (max kappa %.1e, "
245 "vol/fluid %.1e) (f) %.2e\n",
246 label, ratios[r], o, eV, 2.0 * bound2, eA, 3.0 * bound2,
m.cutCells,
m.facets,
m.r0,
m.r2,
247 m.minRho,
m.notSingle,
m.sealed,
m.sealedMaxKappa,
m.sealedVolFrac,
m.maxSnapRes);
248 CHECK(std::fabs(eV) <= 2.0 * bound2);
249 CHECK(std::fabs(eA) <= 3.0 * bound2);
250 CHECK(
m.notSingle == 0 &&
m.lowRho == 0);
253 CHECK(
m.sealedVolFrac <= 1e-6);
258 CHECK((
double)
m.r0 >= 0.999 * (
double)
m.facets &&
m.r2 == 0);
259 CHECK(
m.maxSnapRes <= 2e-3);
260 if (
m.kappaMismatch != 0 ||
m.countMismatch != 0)
261 std::printf(
" record vs host: %ld kappa mismatches (max diff %.2e), facet count %s\n",
262 m.kappaMismatch,
m.maxKappaDiff,
m.countMismatch ?
"DIFFERS" :
"equal");
263 CHECK(
m.kappaMismatch == 0 &&
m.countMismatch == 0);
265 rmsV[
r] = std::sqrt(sV / 3.0);
266 rmsA[
r] = std::sqrt(sA / 3.0);
268 for (
int r = 0;
r + 1 < 3; ++
r) {
269 const double oV = std::log2(rmsV[r] / rmsV[r + 1]),
oA = std::log2(rmsA[r] / rmsA[r + 1]);
270 std::printf(
" %s order %2.0f->%2.0f: volume %.3f area %.3f\n", label, ratios[r],
271 ratios[r + 1], oV, oA);
279 std::mt19937_64
rng(77);
280 std::uniform_real_distribution<double>
u(-0.5, 0.5);
281 for (
double R : {16.0, 32.0, 64.0}) {
282 const int nxy = (
int)std::lround(2.0 * R + 8.0);
283 const int n[3] = {nxy, nxy, 4};
284 const double hp[3] = {1.0, 1.0, 1.0};
285 for (
int o = 0;
o < 3; ++
o) {
286 const double cx = 0.5 * nxy +
u(rng), cy = 0.5 * nxy +
u(rng);
287 IbmSolver s(n[0], n[1], n[2], {(double)n[0], (
double)
n[1], (double)n[2]}, {0.0, 0.0, 0.0},
288 {(long)n[0], (
long)
n[1], (long)n[2]});
289 std::vector<double>
sdf((std::size_t)n[0] * n[1] * n[2]);
290 for (
int z = 0;
z <
n[2]; ++
z)
291 for (
int y = 0;
y <
n[1]; ++
y)
292 for (
int x = 0;
x <
n[0]; ++
x) {
293 const double dx =
x + 0.5 - cx,
dy =
y + 0.5 - cy;
294 sdf[(std::size_t)x + (std::size_t)
y *
n[0] + (std::size_t)z * n[0] * n[1]] =
295 R - std::sqrt(dx * dx + dy * dy);
297 s.setSolid(sdf,
false);
298 const Measure
m = measure(s, n, hp);
299 const double eV =
m.fluidVol / (M_PI *
R *
R *
n[2]) - 1.0;
301 " pipe R/h=%2.0f off %d: eV %+.3e facets %ld R0 %ld min rho %.4f sealed %ld "
303 R, o, eV,
m.facets,
m.r0,
m.minRho,
m.sealed,
m.maxSnapRes);
304 CHECK(
m.r0 ==
m.facets &&
m.facets > 0);
305 CHECK(
m.sealed == 0 &&
m.notSingle == 0);
306 CHECK(
m.maxSnapRes <= 2e-3);
307 if (
m.kappaMismatch != 0 ||
m.countMismatch != 0)
308 std::printf(
" record vs host: %ld kappa mismatches (max diff %.2e), facet count %s\n",
309 m.kappaMismatch,
m.maxKappaDiff,
m.countMismatch ?
"DIFFERS" :
"equal");
310 CHECK(
m.kappaMismatch == 0 &&
m.countMismatch == 0);
317int main(
int argc,
char** argv) {
318 Kokkos::ScopeGuard guard(argc, argv);
320 const int periodic[6] = {0, 0, 0, 0, 0, 0};
321 const int walls[6] = {1, 1, 4, 4, 0, 0};
322 const int mixed[6] = {0, 0, 1, 4, 4, 1};
323 checkGhosts(
"periodic wrap", periodic);
324 checkGhosts(
"x wall + y slip", walls);
325 checkGhosts(
"y wall/slip + z slip/wall", mixed);
327 const double iso[3] = {1.0, 1.0, 1.0}, aniso[3] = {1.0, 1.0, 2.0};
328 sphereSet(
"iso ", iso,
true);
329 sphereSet(
"aniso", aniso,
false);
333 std::fprintf(stderr,
"%d failure(s)\n",
failures);
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
void cellSamples(const V &s, long i, long sy, long sz, double corner[8], double face[6], double ¢re)
The 15 PL samples of the cell at extended linear index i (corner index bx + 2 by + 4 bz,...
void cellSnapped(const V &sax, const V &say, const V &saz, long i, long sy, long sz, double ap[6])
The snapped apertures of cell i in face order -x,+x,-y,+y,-z,+z.
int cellFacets(const cs::CutCellGeometry &g, const double ap[6], const double h[3], double area[2][3], double cen[2][3])
The operator facets of one cell (§2.3) from its PL record and snapped apertures: returns the count (0...
Solver< Staggered > IbmSolver
static int expectIndex(int i, int n, int bcLo, int bcHi)