core 1.5.0
Shared MPI block decomposition, asynchronous ghost-layer exchange, SDF geometry, load balancing (header-only C++20)
Loading...
Searching...
No Matches
plic.hpp
Go to the documentation of this file.
1
50#ifndef PECLET_CORE_VOF_PLIC_HPP
51#define PECLET_CORE_VOF_PLIC_HPP
52
53#include <Kokkos_Core.hpp>
54#include <Kokkos_MathematicalFunctions.hpp>
55
56namespace peclet::core::vof {
57
58// ---------------------------------------------------------------------------------------------
59// small local helpers
60// ---------------------------------------------------------------------------------------------
61
63 return x * x;
64}
66 return x * x * x;
67}
68
71 const double s = Kokkos::fabs(m[0]) + Kokkos::fabs(m[1]) + Kokkos::fabs(m[2]);
72 if (s > 0.0) {
73 const double inv = 1.0 / s;
74 m[0] *= inv;
75 m[1] *= inv;
76 m[2] *= inv;
77 }
78 return s;
79}
80
81// ---------------------------------------------------------------------------------------------
82// the anisotropic cell metric (Phase 3 of suite/docs/PHYSICAL_UNITS_PLAN.md;
83// flow/doc/anisotropic_vof.md §2)
84// ---------------------------------------------------------------------------------------------
85
108struct VofMetric {
109 double h[3] = {1.0, 1.0, 1.0};
110
111 KOKKOS_INLINE_FUNCTION bool isotropic() const { return h[0] == h[1] && h[1] == h[2]; }
112 KOKKOS_INLINE_FUNCTION double det() const { return h[0] * h[1] * h[2]; }
114 const double m = h[0] > h[1] ? h[0] : h[1];
115 return m > h[2] ? m : h[2];
116 }
118 const double m = h[0] < h[1] ? h[0] : h[1];
119 return m < h[2] ? m : h[2];
120 }
122 KOKKOS_INLINE_FUNCTION void toPhys(const double X[3], double out[3]) const {
123 out[0] = X[0] * h[0];
124 out[1] = X[1] * h[1];
125 out[2] = X[2] * h[2];
126 }
127};
128
143KOKKOS_INLINE_FUNCTION double vofPhysNormal(const double m[3], const VofMetric& g, double n[3]) {
144 const double q = Kokkos::sqrt(m[0] * m[0] + m[1] * m[1] + m[2] * m[2]);
145 if (!(q > 0.0))
146 return 0.0;
147 const double p[3] = {m[0] / g.h[0], m[1] / g.h[1], m[2] / g.h[2]};
148 const double pn = Kokkos::sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
149 if (!(pn > 0.0))
150 return 0.0;
151 n[0] = p[0] / pn;
152 n[1] = p[1] / pn;
153 n[2] = p[2] / pn;
154 return pn / q;
155}
156
160KOKKOS_INLINE_FUNCTION double vofPhysNormalInv(const double m[3], const VofMetric& g, double n[3]) {
161 const double q = Kokkos::sqrt(m[0] * m[0] + m[1] * m[1] + m[2] * m[2]);
162 if (!(q > 0.0))
163 return 0.0;
164 const double p[3] = {m[0] / g.h[0], m[1] / g.h[1], m[2] / g.h[2]};
165 const double n2 = p[0] * p[0] + p[1] * p[1] + p[2] * p[2];
166 if (!(n2 > 0.0))
167 return 0.0;
168 const double invn = 1.0 / Kokkos::sqrt(n2);
169 n[0] = p[0] * invn;
170 n[1] = p[1] * invn;
171 n[2] = p[2] * invn;
172 return Kokkos::sqrt(n2) / q;
173}
174
177KOKKOS_INLINE_FUNCTION void vofIndexNormal(const double n[3], const VofMetric& g, double m[3]) {
178 m[0] = n[0] * g.h[0];
179 m[1] = n[1] * g.h[1];
180 m[2] = n[2] * g.h[2];
181}
182
183// ---------------------------------------------------------------------------------------------
184// (1) forward: plane -> volume
185// ---------------------------------------------------------------------------------------------
186
194KOKKOS_INLINE_FUNCTION double plicVolumeReduced(double w, double n1, double n2, double n3) {
195 const double n12 = n1 + n2;
196 // case (5) — the slab regime (the plane exits through both faces normal to axis 3), no division
197 // by n1/n2. NOTE the condition is `n1+n2 <= w`, NOT Lehmann & Gekle's Listing 1 shortcut
198 // `min(n1+n2,n3) <= w`: eq. (11) defines case (5) as "the remaining free sector mutually excluded
199 // by the other four cases", which is the interval [n1+n2, n3] and is empty unless n3 >= n1+n2.
200 // Hoisting the test to the front (as the listing does, for speed) makes `min(n1+n2,n3) <= w`
201 // additionally fire at the single point w == n3 when n3 < n1+n2, where the correct case is (3):
202 // e.g. m = (1/3,1/3,1/3), alpha = 1/3 gives 0 instead of the exact 1/6. Using the true case-(5)
203 // interval keeps the front position (and its n1 = 0 protection) without the boundary defect.
204 if (n12 <= w && w <= n3)
205 return (w - 0.5 * n12) / n3;
206 if (w < n1) // case (1) — corner tetrahedron (implies n1 > 0)
207 return plicCube(w) / (6.0 * n1 * n2 * n3);
208 if (w <= n2) // case (2) — one corner clipped; the n1 factor cancels, so n1 = 0 is safe
209 return (3.0 * w * (w - n1) + plicSq(n1)) / (6.0 * n2 * n3);
210 // cases (3) and (4) — two or three corners clipped; fdim() merges the two branches. Reachable
211 // only with n1 > 0 (n1 = 0 is fully covered by cases (2) and (5) above).
212 const double t3 = Kokkos::fmax(w - n3, 0.0);
213 return (plicCube(w) - plicCube(w - n1) - plicCube(w - n2) - plicCube(t3)) / (6.0 * n1 * n2 * n3);
214}
215
221KOKKOS_INLINE_FUNCTION double plicVolume(double mx, double my, double mz, double alpha) {
222 const double ax = Kokkos::fabs(mx), ay = Kokkos::fabs(my), az = Kokkos::fabs(mz);
223 const double s = ax + ay + az;
224 if (!(s > 0.0))
225 return alpha > 0.0 ? 1.0 : 0.0; // degenerate normal: the half-space is trivial
226
227 // Mirror the negative components into the positive octant. Substituting x_i -> 1 - x_i turns
228 // m_i x_i into m_i - m_i x_i', so the offset picks up |m_i| for every negative component.
229 double beta = alpha;
230 if (mx < 0.0)
231 beta += ax;
232 if (my < 0.0)
233 beta += ay;
234 if (mz < 0.0)
235 beta += az;
236
237 const double a1 = beta / s; // offset in the L1-normalized, positive-octant frame
238 if (a1 <= 0.0)
239 return 0.0;
240 if (a1 >= 1.0)
241 return 1.0;
242
243 // Sort ascending. n2 from the L1 identity (never from a third min/max) so n1+n2+n3 = 1 holds.
244 const double n1 = Kokkos::fmin(Kokkos::fmin(ax, ay), az) / s;
245 const double n3 = Kokkos::fmax(Kokkos::fmax(ax, ay), az) / s;
246 const double n2 = Kokkos::fmax(1.0 - n1 - n3, 0.0);
247
248 // V(alpha) + V(1-alpha) = 1 (point reflection x -> 1-x), so only w = min(a1, 1-a1) is computed.
249 const double v = plicVolumeReduced(Kokkos::fmin(a1, 1.0 - a1), n1, n2, n3);
250 return (a1 <= 0.5) ? v : 1.0 - v;
251}
252
253// ---------------------------------------------------------------------------------------------
254// (2) inverse: volume -> plane
255// ---------------------------------------------------------------------------------------------
256
263KOKKOS_INLINE_FUNCTION double plicAlphaReduced(double v, double n1, double n2, double n3) {
264 const double n12 = n1 + n2, n3v = n3 * v;
265 if (n12 <= 2.0 * n3v)
266 return n3v + 0.5 * n12; // case (5); also catches n1 = n2 = 0 (1D normal)
267
268 // past case (5), n2 > 0 (n2 = 0 would force n1 = 0 and hence n12 = 0 <= 2 n3 V)
269 const double sqn1 = plicSq(n1), n26 = 6.0 * n2, v1 = sqn1 / n26;
270 if (v1 <= n3v && n3v < v1 + 0.5 * (n2 - n1)) // case (2); also catches n1 = 0 (2D normal)
271 return 0.5 * (n1 + Kokkos::sqrt(sqn1 + 8.0 * n2 * (n3v - v1)));
272
273 const double v6 = n1 * n26 * n3v; // = 6 n1 n2 n3 V
274 if (n3v < v1)
275 return Kokkos::cbrt(v6); // case (1)
276
277 // past case (2), n1 > 0. v3 is n3 * V at the (3)|(4) boundary w = n3 (or at the case (5)
278 // boundary w = n1+n2 when the plane can never reach w = n3).
279 const double v3 = (n3 < n12) ? (plicSq(n3) * (3.0 * n12 - n3) + sqn1 * (n1 - 3.0 * n3) +
280 plicSq(n2) * (n2 - 3.0 * n3)) /
281 (n1 * n26)
282 : 0.5 * n12;
283 const double sqn12 = sqn1 + plicSq(n2), v6cb = v6 - plicCube(n1) - plicCube(n2);
284 const bool case3 = (n3v < v3);
285 // Both cubic cases reduce to w^3 - 3 c w^2 + 3 b w + a = 0 :
286 const double a = case3 ? v6cb : 0.5 * (v6cb - plicCube(n3));
287 const double b = case3 ? sqn12 : 0.5 * (sqn12 + plicSq(n3));
288 const double c = case3 ? n12 : 0.5;
289 // c^2 - b > 0 is guaranteed here: 2 n1 n2 > 0 in case (3); 1/4 - (n1^2+n2^2+n3^2)/2 > 0 in case
290 // (4), which is reachable only when n3 < n1+n2, i.e. n3 < 1/2.
291 const double t = Kokkos::sqrt(c * c - b);
292 // Casus irreducibilis: three real roots, |arg| <= 1 analytically. The clamp is a floating-point
293 // guard on the case boundaries only (an out-of-range arg would otherwise return NaN).
294 const double arg =
295 Kokkos::fmin(Kokkos::fmax((plicCube(c) - 0.5 * a - 1.5 * b * c) / plicCube(t), -1.0), 1.0);
296 return c - 2.0 * t * Kokkos::sin((1.0 / 3.0) * Kokkos::asin(arg));
297}
298
303KOKKOS_INLINE_FUNCTION double plicAlpha(double mx, double my, double mz, double v) {
304 const double ax = Kokkos::fabs(mx), ay = Kokkos::fabs(my), az = Kokkos::fabs(mz);
305 const double s = ax + ay + az;
306 if (!(s > 0.0))
307 return 0.0;
308
309 const double n1 = Kokkos::fmin(Kokkos::fmin(ax, ay), az) / s;
310 const double n3 = Kokkos::fmax(Kokkos::fmax(ax, ay), az) / s;
311 const double n2 = Kokkos::fmax(1.0 - n1 - n3, 0.0);
312
313 const double vc = Kokkos::fmin(Kokkos::fmax(v, 0.0), 1.0);
314 const double w = plicAlphaReduced(Kokkos::fmin(vc, 1.0 - vc), n1, n2, n3);
315 const double a1 = (vc <= 0.5) ? w : 1.0 - w; // undo the V -> 1-V fold
316
317 double alpha = s * a1; // undo the L1 normalization
318 if (mx < 0.0)
319 alpha -= ax; // undo the octant mirroring
320 if (my < 0.0)
321 alpha -= ay;
322 if (mz < 0.0)
323 alpha -= az;
324 return alpha;
325}
326
327// ---------------------------------------------------------------------------------------------
328// (5) slab truncation — the geometric face flux
329// ---------------------------------------------------------------------------------------------
330
339KOKKOS_INLINE_FUNCTION double plicSlabVolume(double mx, double my, double mz, double alpha, int dir,
340 double a, double b) {
341 const double len = b - a;
342 if (!(len > 0.0))
343 return 0.0;
344 double m[3] = {mx, my, mz};
345 const double md = m[dir];
346 m[dir] = md * len;
347 return len * plicVolume(m[0], m[1], m[2], alpha - md * a);
348}
349
361KOKKOS_INLINE_FUNCTION double plicBoxVolume(double mx, double my, double mz, double alpha,
362 double a0, double b0, double a1, double b1, double a2,
363 double b2) {
364 const double len[3] = {b0 - a0, b1 - a1, b2 - a2};
365 if (!(len[0] > 0.0) || !(len[1] > 0.0) || !(len[2] > 0.0))
366 return 0.0;
367 const double m[3] = {mx, my, mz};
368 const double off = alpha - (m[0] * a0 + m[1] * a1 + m[2] * a2);
369 return len[0] * len[1] * len[2] * plicVolume(m[0] * len[0], m[1] * len[1], m[2] * len[2], off);
370}
371
374KOKKOS_INLINE_FUNCTION double faceFluxVolume(double mx, double my, double mz, double alpha, int dir,
375 double f) {
376 return plicSlabVolume(mx, my, mz, alpha, dir, 0.0, f);
377}
378
379// ---------------------------------------------------------------------------------------------
380// (3)/(4) interface normals from a 3x3x3 colour stencil
381// ---------------------------------------------------------------------------------------------
382
385KOKKOS_INLINE_FUNCTION int plicSt(int i, int j, int k) {
386 return i + 3 * j + 9 * k;
387}
388
389#define PECLET_VOF_C(dx, dy, dz) c[plicSt(1 + (dx), 1 + (dy), 1 + (dz))]
390
394KOKKOS_INLINE_FUNCTION void youngsNormal(const double c[27], double m[3]) {
395 double m1, m2;
396
397 m1 = PECLET_VOF_C(-1, -1, -1) + PECLET_VOF_C(-1, 1, -1) + PECLET_VOF_C(-1, -1, 1) +
398 PECLET_VOF_C(-1, 1, 1) +
399 2.0 * (PECLET_VOF_C(-1, -1, 0) + PECLET_VOF_C(-1, 1, 0) + PECLET_VOF_C(-1, 0, -1) +
400 PECLET_VOF_C(-1, 0, 1)) +
401 4.0 * PECLET_VOF_C(-1, 0, 0);
402 m2 = PECLET_VOF_C(1, -1, -1) + PECLET_VOF_C(1, 1, -1) + PECLET_VOF_C(1, -1, 1) +
403 PECLET_VOF_C(1, 1, 1) +
404 2.0 * (PECLET_VOF_C(1, -1, 0) + PECLET_VOF_C(1, 1, 0) + PECLET_VOF_C(1, 0, -1) +
405 PECLET_VOF_C(1, 0, 1)) +
406 4.0 * PECLET_VOF_C(1, 0, 0);
407 m[0] = m1 - m2;
408
409 m1 = PECLET_VOF_C(-1, -1, -1) + PECLET_VOF_C(-1, -1, 1) + PECLET_VOF_C(1, -1, -1) +
410 PECLET_VOF_C(1, -1, 1) +
411 2.0 * (PECLET_VOF_C(-1, -1, 0) + PECLET_VOF_C(1, -1, 0) + PECLET_VOF_C(0, -1, -1) +
412 PECLET_VOF_C(0, -1, 1)) +
413 4.0 * PECLET_VOF_C(0, -1, 0);
414 m2 = PECLET_VOF_C(-1, 1, -1) + PECLET_VOF_C(-1, 1, 1) + PECLET_VOF_C(1, 1, -1) +
415 PECLET_VOF_C(1, 1, 1) +
416 2.0 * (PECLET_VOF_C(-1, 1, 0) + PECLET_VOF_C(1, 1, 0) + PECLET_VOF_C(0, 1, -1) +
417 PECLET_VOF_C(0, 1, 1)) +
418 4.0 * PECLET_VOF_C(0, 1, 0);
419 m[1] = m1 - m2;
420
421 m1 = PECLET_VOF_C(-1, -1, -1) + PECLET_VOF_C(-1, 1, -1) + PECLET_VOF_C(1, -1, -1) +
422 PECLET_VOF_C(1, 1, -1) +
423 2.0 * (PECLET_VOF_C(-1, 0, -1) + PECLET_VOF_C(1, 0, -1) + PECLET_VOF_C(0, -1, -1) +
424 PECLET_VOF_C(0, 1, -1)) +
425 4.0 * PECLET_VOF_C(0, 0, -1);
426 m2 = PECLET_VOF_C(-1, -1, 1) + PECLET_VOF_C(-1, 1, 1) + PECLET_VOF_C(1, -1, 1) +
427 PECLET_VOF_C(1, 1, 1) +
428 2.0 * (PECLET_VOF_C(-1, 0, 1) + PECLET_VOF_C(1, 0, 1) + PECLET_VOF_C(0, -1, 1) +
429 PECLET_VOF_C(0, 1, 1)) +
430 4.0 * PECLET_VOF_C(0, 0, 1);
431 m[2] = m1 - m2;
432
433 if (plicNormalizeL1(m) < 1e-30) {
434 m[0] = 1.0;
435 m[1] = 0.0;
436 m[2] = 0.0;
437 }
438}
439
451KOKKOS_INLINE_FUNCTION void mycNormal(const double c[27], double m[3]) {
452 double m1, m2, mm[4][3], t0, t1, t2;
453 int cn;
454
455 // plane as sgn(mx) X = m01 Y + m02 Z + alpha
456 m1 = PECLET_VOF_C(-1, 0, -1) + PECLET_VOF_C(-1, 0, 1) + PECLET_VOF_C(-1, -1, 0) +
457 PECLET_VOF_C(-1, 1, 0) + PECLET_VOF_C(-1, 0, 0);
458 m2 = PECLET_VOF_C(1, 0, -1) + PECLET_VOF_C(1, 0, 1) + PECLET_VOF_C(1, -1, 0) +
459 PECLET_VOF_C(1, 1, 0) + PECLET_VOF_C(1, 0, 0);
460 mm[0][0] = m1 > m2 ? 1.0 : -1.0;
461
462 m1 = PECLET_VOF_C(-1, -1, 0) + PECLET_VOF_C(1, -1, 0) + PECLET_VOF_C(0, -1, 0);
463 m2 = PECLET_VOF_C(-1, 1, 0) + PECLET_VOF_C(1, 1, 0) + PECLET_VOF_C(0, 1, 0);
464 mm[0][1] = 0.5 * (m1 - m2);
465
466 m1 = PECLET_VOF_C(-1, 0, -1) + PECLET_VOF_C(1, 0, -1) + PECLET_VOF_C(0, 0, -1);
467 m2 = PECLET_VOF_C(-1, 0, 1) + PECLET_VOF_C(1, 0, 1) + PECLET_VOF_C(0, 0, 1);
468 mm[0][2] = 0.5 * (m1 - m2);
469
470 // plane as sgn(my) Y = m10 X + m12 Z + alpha
471 m1 = PECLET_VOF_C(-1, -1, 0) + PECLET_VOF_C(-1, 1, 0) + PECLET_VOF_C(-1, 0, 0);
472 m2 = PECLET_VOF_C(1, -1, 0) + PECLET_VOF_C(1, 1, 0) + PECLET_VOF_C(1, 0, 0);
473 mm[1][0] = 0.5 * (m1 - m2);
474
475 m1 = PECLET_VOF_C(0, -1, -1) + PECLET_VOF_C(0, -1, 1) + PECLET_VOF_C(1, -1, 0) +
476 PECLET_VOF_C(-1, -1, 0) + PECLET_VOF_C(0, -1, 0);
477 m2 = PECLET_VOF_C(0, 1, -1) + PECLET_VOF_C(0, 1, 1) + PECLET_VOF_C(1, 1, 0) +
478 PECLET_VOF_C(-1, 1, 0) + PECLET_VOF_C(0, 1, 0);
479 mm[1][1] = m1 > m2 ? 1.0 : -1.0;
480
481 m1 = PECLET_VOF_C(0, -1, -1) + PECLET_VOF_C(0, 0, -1) + PECLET_VOF_C(0, 1, -1);
482 m2 = PECLET_VOF_C(0, -1, 1) + PECLET_VOF_C(0, 0, 1) + PECLET_VOF_C(0, 1, 1);
483 mm[1][2] = 0.5 * (m1 - m2);
484
485 // plane as sgn(mz) Z = m20 X + m21 Y + alpha
486 m1 = PECLET_VOF_C(-1, 0, -1) + PECLET_VOF_C(-1, 0, 1) + PECLET_VOF_C(-1, 0, 0);
487 m2 = PECLET_VOF_C(1, 0, -1) + PECLET_VOF_C(1, 0, 1) + PECLET_VOF_C(1, 0, 0);
488 mm[2][0] = 0.5 * (m1 - m2);
489
490 m1 = PECLET_VOF_C(0, -1, -1) + PECLET_VOF_C(0, -1, 1) + PECLET_VOF_C(0, -1, 0);
491 m2 = PECLET_VOF_C(0, 1, -1) + PECLET_VOF_C(0, 1, 1) + PECLET_VOF_C(0, 1, 0);
492 mm[2][1] = 0.5 * (m1 - m2);
493
494 m1 = PECLET_VOF_C(-1, 0, -1) + PECLET_VOF_C(1, 0, -1) + PECLET_VOF_C(0, -1, -1) +
495 PECLET_VOF_C(0, 1, -1) + PECLET_VOF_C(0, 0, -1);
496 m2 = PECLET_VOF_C(-1, 0, 1) + PECLET_VOF_C(1, 0, 1) + PECLET_VOF_C(0, -1, 1) +
497 PECLET_VOF_C(0, 1, 1) + PECLET_VOF_C(0, 0, 1);
498 mm[2][2] = m1 > m2 ? 1.0 : -1.0;
499
503
504 // pick the centred candidate with the largest dominant component
505 t0 = Kokkos::fabs(mm[0][0]);
506 t1 = Kokkos::fabs(mm[1][1]);
507 t2 = Kokkos::fabs(mm[2][2]);
508 cn = 0;
509 if (t1 > t0) {
510 t0 = t1;
511 cn = 1;
512 }
513 if (t2 > t0)
514 cn = 2;
515
516 youngsNormal(c, mm[3]); // already L1-normalized; (1,0,0) on a degenerate stencil
517
518 t0 = Kokkos::fabs(mm[3][0]);
519 t1 = Kokkos::fabs(mm[3][1]);
520 t2 = Kokkos::fabs(mm[3][2]);
521 if (t1 > t0)
522 t0 = t1;
523 if (t2 > t0)
524 t0 = t2;
525 if (Kokkos::fabs(mm[cn][cn]) > t0)
526 cn = 3;
527
528 m[0] = mm[cn][0];
529 m[1] = mm[cn][1];
530 m[2] = mm[cn][2];
531}
532
533#undef PECLET_VOF_C
534
535// ---------------------------------------------------------------------------------------------
536// (6) test helpers — exact per-cell fractions for manufactured interfaces
537// ---------------------------------------------------------------------------------------------
538
542KOKKOS_INLINE_FUNCTION double planeCellFraction(double mx, double my, double mz, double alphaGlobal,
543 double x0, double y0, double z0, double h) {
544 const double aLoc = alphaGlobal - (mx * x0 + my * y0 + mz * z0);
545 return plicVolume(mx * h, my * h, mz * h, aLoc);
546}
547
553 double alphaGlobal, double x0, double y0,
554 double z0, const VofMetric& g) {
555 const double aLoc = alphaGlobal - (mx * x0 + my * y0 + mz * z0);
556 return plicVolume(mx * g.h[0], my * g.h[1], mz * g.h[2], aLoc);
557}
558
569KOKKOS_INLINE_FUNCTION double sphereCellFraction(double cx, double cy, double cz, double r,
570 double x0, double y0, double z0, double h,
571 int levels) {
572 if (levels < 0)
573 levels = 0;
574 if (levels > 8)
575 levels = 8;
576 struct Box {
577 double x, y, z, s;
578 int lev;
579 };
580 Box stack[64]; // DFS over an octree of depth L keeps at most 7L+1 boxes live
581 int top = 0;
582 stack[top++] = Box{x0, y0, z0, h, 0};
583 double vol = 0.0;
584 const double kHalfDiag = 0.86602540378443865; // sqrt(3)/2
585
586 while (top > 0) {
587 const Box b = stack[--top];
588 const double half = 0.5 * b.s;
589 const double dx = b.x + half - cx, dy = b.y + half - cy, dz = b.z + half - cz;
590 const double d = Kokkos::sqrt(dx * dx + dy * dy + dz * dz);
591 const double hd = kHalfDiag * b.s;
592 if (r - d >= hd) { // wholly inside the sphere
593 vol += b.s * b.s * b.s;
594 continue;
595 }
596 if (d - r >= hd)
597 continue; // wholly outside
598 if (b.lev >= levels || !(d > 0.0)) {
599 if (d > 0.0) {
600 const double inv = 1.0 / d;
601 const double nx = dx * inv, ny = dy * inv, nz = dz * inv;
602 // fluid where (X - centre).n < r, i.e. n.X < r + n.centre
603 vol += b.s * b.s * b.s *
604 planeCellFraction(nx, ny, nz, r + nx * cx + ny * cy + nz * cz, b.x, b.y, b.z, b.s);
605 }
606 continue;
607 }
608 for (int q = 0; q < 8; ++q)
609 stack[top++] = Box{b.x + ((q & 1) ? half : 0.0), b.y + ((q & 2) ? half : 0.0),
610 b.z + ((q & 4) ? half : 0.0), half, b.lev + 1};
611 }
612 return vol / (h * h * h);
613}
614
615} // namespace peclet::core::vof
616
617#endif // PECLET_CORE_VOF_PLIC_HPP
double faceFluxVolume(double mx, double my, double mz, double alpha, int dir, double f)
Donor-cell geometric flux volume: the fluid volume swept out of the slab [0, f] along dir,...
Definition plic.hpp:374
void youngsNormal(const double c[27], double m[3])
Youngs' normal: the 27-point weighted gradient (corner 1, edge 2, face 4 on each of the two opposing ...
Definition plic.hpp:394
double sphereCellFraction(double cx, double cy, double cz, double r, double x0, double y0, double z0, double h, int levels)
Fluid fraction of a cell for a sphere (fluid = INSIDE the sphere), by recursive octree subdivision wi...
Definition plic.hpp:569
void vofIndexNormal(const double n[3], const VofMetric &g, double m[3])
The INDEX-space normal of a physical normal n (any scale): m = H n, returned unnormalized (plicAlpha ...
Definition plic.hpp:177
double plicBoxVolume(double mx, double my, double mz, double alpha, double a0, double b0, double a1, double b1, double a2, double b2)
Fluid volume of the PLIC polyhedron inside the axis-aligned BOX [a0,b0] x [a1,b1] x [a2,...
Definition plic.hpp:361
double plicSlabVolume(double mx, double my, double mz, double alpha, int dir, double a, double b)
Fluid volume of the PLIC polyhedron inside the axis-aligned slab a <= x_dir <= b, expressed as a frac...
Definition plic.hpp:339
double plicAlphaReduced(double v, double n1, double n2, double n3)
Reduced-symmetry analytic inverse of plicVolumeReduced.
Definition plic.hpp:263
double vofPhysNormal(const double m[3], const VofMetric &g, double n[3])
The PHYSICAL unit normal of an index-space normal m (any scale), i.e.
Definition plic.hpp:143
double planeCellFractionAniso(double mx, double my, double mz, double alphaGlobal, double x0, double y0, double z0, const VofMetric &g)
The same for an anisotropic cell [x0, x0+g.h[0]] x [y0, y0+g.h[1]] x [z0, z0+g.h[2]]: the index norma...
Definition plic.hpp:552
int plicSt(int i, int j, int k)
Stencil index: x-fastest, offsets (i-1, j-1, k-1) with i,j,k in {0,1,2} (suite/docs/CONVENTIONS....
Definition plic.hpp:385
double plicCube(double x)
Definition plic.hpp:65
double planeCellFraction(double mx, double my, double mz, double alphaGlobal, double x0, double y0, double z0, double h)
Exact fluid fraction of the axis-aligned cell [x0,x0+h] x [y0,y0+h] x [z0,z0+h] under the GLOBAL plan...
Definition plic.hpp:542
double plicVolumeReduced(double w, double n1, double n2, double n3)
Reduced-symmetry forward relation.
Definition plic.hpp:194
double plicAlpha(double mx, double my, double mz, double v)
Plane offset alpha such that plicVolume(mx,my,mz,alpha) == V, exactly (analytic inverse).
Definition plic.hpp:303
double plicNormalizeL1(double m[3])
L1-normalize a normal in place. Returns the L1 norm before normalization (0 => untouched).
Definition plic.hpp:70
double plicVolume(double mx, double my, double mz, double alpha)
Volume fraction of the unit cell [0,1]^3 lying on the fluid side m .
Definition plic.hpp:221
void mycNormal(const double c[27], double m[3])
MYC — Mixed Youngs-Centred normal (Aulisa et al.
Definition plic.hpp:451
double vofPhysNormalInv(const double m[3], const VofMetric &g, double n[3])
vofPhysNormal in the RECIPROCAL-MULTIPLY form — n = (H^-1 m) * (1/|H^-1 m|), which is what the curvat...
Definition plic.hpp:160
double plicSq(double x)
Definition plic.hpp:62
Kokkos::View< T *, MemSpace > View
1D device array.
Definition view.hpp:26
#define PECLET_VOF_C(dx, dy, dz)
Definition plic.hpp:389
A physical, axis-aligned box with per-axis periodicity. Continuous — no cells.
Definition domain.hpp:34
The per-axis cell size, in the solver's reference length (hRef), of the cell these kernels map to the...
Definition plic.hpp:108
void toPhys(const double X[3], double out[3]) const
Index displacement -> physical displacement (in hRef): ‘X’_a = h[a] X_a`.
Definition plic.hpp:122