27#ifndef PECLET_FLOW_VOF_CURVATURE_FIELD_HPP
28#define PECLET_FLOW_VOF_CURVATURE_FIELD_HPP
32#include <Kokkos_Core.hpp>
48 return c > eps && c < 1.0 - eps;
60 long s0,
long s1,
long s2,
double mtol,
double ptW,
61 double ieps,
bool forceFb,
bool oneDir,
bool useFit,
62 VofMetric g,
double peps) {
63 const long st[3] = {s0, s1, s2};
75 const double am[3] = {Kokkos::fabs(mx(i)), Kokkos::fabs(my(i)), Kokkos::fabs(mz(i))};
76 int ord[3] = {0, 1, 2};
77 for (
int p = 1; p < 3; ++p)
78 for (
int q = p; q > 0 && am[
ord[q]] > am[
ord[q - 1]]; --q) {
83 const int ntry = oneDir ? 1 : 3;
84 for (
int t = 0; t <
ntry; ++t) {
86 const int d1 = (d + 1) % 3,
d2 = (d + 2) % 3;
87 const long sd = st[d], s1 = st[
d1], s2 = st[
d2];
91 for (
int q = 0; q < 3 && ok; ++q)
92 for (
int p = 0; p < 3 && ok; ++p) {
93 const long base = i + (p - 1) * s1 + (q - 1) * s2;
125 const double m0 = mx(i), m1 = my(i), m2 = mz(i);
126 const double mi[3] = {
m0, m1, m2};
127 double nn[3] = {0.0, 0.0, 0.0};
132 double vtx[8][3],
ctr[3], area;
135 const double org[3] = {
ctr[0] - 0.5,
ctr[1] - 0.5,
ctr[2] - 0.5};
138 for (
int d = 0; d < 3; ++d) {
139 const int d1 = (d + 1) % 3,
d2 = (d + 2) % 3;
140 const long sd = st[d], s1 = st[
d1], s2 = st[
d2];
141 for (
int q = -1; q <= 1; ++q)
142 for (
int p = -1; p <= 1; ++p) {
143 const long base = i + p * s1 + q * s2;
152 X[
d1] =
static_cast<double>(p);
153 X[
d2] =
static_cast<double>(q);
191#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
192#define PECLET_VOF_DEVICE_NOINLINE __noinline__
194#define PECLET_VOF_DEVICE_NOINLINE
200 double mz,
double alpha,
245template <
class SF,
class Acc>
247 long sz,
int gr,
double dW,
double cmin, VofMetric g) {
251 double nn[3],
t1[3],
t2[3], org[3];
260 for (
int oz = -gr; oz <= gr; ++oz)
261 for (
int oy = -gr; oy <= gr; ++oy)
262 for (
int ox = -gr; ox <= gr; ++ox) {
263 const long j = i + (
long)ox + (
long)oy * sy + (
long)oz * sz;
266 const double off[3] = {
static_cast<double>(ox),
static_cast<double>(oy),
267 static_cast<double>(oz)};
277using VofPvTerms = Kokkos::View<PvTerm*, SExec::scratch_memory_space, Kokkos::MemoryUnmanaged>;
278using VofPvInts = Kokkos::View<int*, SExec::scratch_memory_space, Kokkos::MemoryUnmanaged>;
279using VofPvDbls = Kokkos::View<double*, SExec::scratch_memory_space, Kokkos::MemoryUnmanaged>;
286 return VofPvInts::shmem_size(
nk) + VofPvTerms::shmem_size(
nk) +
289 template <
class Member>
310template <
class Member,
class SF,
class Acc>
313 long sz,
int gr,
double dW,
double cmin, VofMetric g) {
316 double nn[3],
t1[3],
t2[3], org[3];
318 const int side = 2 * gr + 1,
nk = side * side * side;
321 Kokkos::parallel_scan(
322 Kokkos::TeamThreadRange(tm,
nk),
323 [&](
const int k,
int&
upd,
const bool final) {
324 const int ox = k % side - gr, oy = (k / side) % side - gr, oz = k / (side * side) - gr;
325 const long j = i + (
long)ox + (
long)oy * sy + (
long)oz * sz;
326 if (acc.present(j)) {
334 Kokkos::parallel_for(Kokkos::TeamThreadRange(tm, n), [&](
const int p) {
335 const int k = sc.kidx(p);
336 const int ox = k % side - gr, oy = (k / side) % side - gr, oz = k / (side * side) - gr;
337 const long j = i + (
long)ox + (
long)oy * sy + (
long)oz * sz;
338 const double off[3] = {
static_cast<double>(ox),
static_cast<double>(oy),
339 static_cast<double>(oz)};
345 Kokkos::parallel_for(Kokkos::TeamThreadRange(tm,
kPvEntries), [&](
const int e) {
347 for (
int p = 0; p < n; ++p)
354 Kokkos::single(Kokkos::PerTeam(tm), [&]() {
361 for (
int p = 0; p < n; ++p)
362 npoly += sc.term(p).ok ? 1 : 0;
373#if defined(KOKKOS_ENABLE_HIP)
435 throw std::invalid_argument(
436 "peclet::flow::vof::VofCurvature: ghost width must be >= 3 (the height-function column "
437 "reaches 3 cells and the 5^3 PV stencil's MYC normals reach 3 cells)");
438 if (nx < 1 || ny < 1 || nz < 1)
439 throw std::invalid_argument(
"peclet::flow::vof::VofCurvature: empty block");
443 len_ =
static_cast<long>(e_.
x) * e_.
y * e_.
z;
444 mx_ =
SField(
"vof::curv::mx", len_);
445 my_ =
SField(
"vof::curv::my", len_);
446 mz_ =
SField(
"vof::curv::mz", len_);
447 alpha_ =
SField(
"vof::curv::alpha", len_);
448 kappa_ =
SField(
"vof::curv::kappa", len_);
449 branch_ =
SField(
"vof::curv::branch", len_);
454 listG_ =
LField(
"vof::curv::listG", grown);
455 listI_ =
LField(
"vof::curv::listI", (
long)n_.
x * n_.
y * n_.
z);
460 slot_ =
IField(
"vof::curv::slot", len_);
464 bool ready()
const {
return kappa_.extent(0) != 0; }
494 return std::chrono::duration<double>(std::chrono::steady_clock::now().
time_since_epoch())
497 void addT_(
double& acc,
double t0) {
502 std::chrono::duration<double>(std::chrono::steady_clock::now().
time_since_epoch()).count() -
512 SField planeM(
int d)
const {
return d == 0 ? mx_ : (d == 1 ? my_ : mz_); }
656 throw std::runtime_error(
"peclet::flow::vof::VofCurvature::compute: init() not called");
657 const double t0 =
tick_();
687 j.al = alpha_.data();
688 j.kap = kappa_.data();
689 j.br = branch_.data();
690 j.slot = slot_.data();
691 j.cache = cache_.data();
692 j.list = listI_.data();
695 j.sz =
static_cast<long>(e_.
x) * e_.
y;
716 for (
int k = 0; k < T.nj; ++k) {
717 T.job[k] =
jobs[j0 + k];
718 T.off[k + 1] = T.off[k] + T.job[k].n;
721 T.off[k + 1] = T.off[T.nj];
722 if (T.off[T.nj] == 0)
724 if constexpr (!Kokkos::SpaceAccessibility<Kokkos::HostSpace,
725 SField::memory_space>::accessible) {
728 using Policy = Kokkos::TeamPolicy<SExec>;
730 for (
int k = 0; k < T.nj; ++k)
731 gr = std::max(gr, T.job[k].gr);
732 const int nk = (2 * gr + 1) * (2 * gr + 1) * (2 * gr + 1);
735 Kokkos::parallel_for(
736 "vof::curv::pv_batch_team",
pol,
KOKKOS_LAMBDA(
const typename Policy::member_type&
tm) {
737 const long t =
tm.league_rank();
739 while (t >= T.off[b + 1])
742 const int side = 2 *
J.gr + 1;
750 Kokkos::parallel_for(
751 "vof::curv::pv_batch", Kokkos::RangePolicy<SExec>(
SExec(), 0, T.off[T.nj]),
754 while (t >= T.off[b + 1])
768 const I3 e = e_, n = n_;
769 const int g = g_, gr =
kPvHalf;
772 const int rx = n.x + 2 * gr,
ry = n.y + 2 * gr,
rz = n.z + 2 * gr;
776 Kokkos::parallel_scan(
777 "vof::curv::compactG", Kokkos::RangePolicy<SExec>(
SExec(), 0,
region),
779 const int ix = (int)(r %
rx);
780 const int iy = (int)((r /
rx) %
ry);
781 const int iz = (int)(r / ((
long)
rx *
ry));
782 const long i =
L3(g - gr +
ix, g - gr +
iy, g - gr +
iz, e);
795 const int nx = n.x, ny = n.y;
798 Kokkos::parallel_scan(
799 "vof::curv::compactI", Kokkos::RangePolicy<SExec>(
SExec(), 0,
region),
801 const int ix = (int)(r % nx);
802 const int iy = (int)((r / nx) % ny);
803 const int iz = (int)(r / ((
long)nx * ny));
804 const long i =
L3(g +
ix, g +
iy, g +
iz, e);
825 const I3 e = e_, n = n_;
826 const int g = g_, gr =
kPvHalf;
827 const long sy = e_.
x, sz =
static_cast<long>(e_.
x) * e_.
y;
828 SField mx = mx_, my = my_, mz = mz_, al = alpha_;
830 const VofMetric gm =
metric;
840 Kokkos::parallel_for(
841 "vof::curv::planes_zero",
843 {g + n.x + gr, g + n.y + gr, g + n.z + gr}),
845 const long i =
L3(x, y, z, e);
853 Kokkos::parallel_for(
854 "vof::curv::planes_list", Kokkos::RangePolicy<SExec>(
SExec(), 0, nG_),
856 const long i = list(t);
859 slot(i) =
static_cast<int>(t);
863 Kokkos::parallel_for(
866 {g + n.x + gr, g + n.y + gr, g + n.z + gr}),
868 const long i =
L3(x, y, z, e);
883 const I3 e = e_, n = n_;
885 const long st[3] = {1, e_.
x,
static_cast<long>(e_.
x) * e_.
y};
886 SField mx = mx_, my = my_, mz = mz_, al = alpha_, kap = kappa_, br = branch_;
889 const VofMetric gm =
metric;
892 const long s0 = st[0], s1 = st[1], s2 = st[2];
898 Kokkos::parallel_for(
901 const long i =
L3(x, y, z, e);
906 Kokkos::parallel_for(
907 "vof::curv::hf_list", Kokkos::RangePolicy<SExec>(
SExec(), 0, nI_),
KOKKOS_LAMBDA(
long t) {
908 curvHeightCell(list(t), c, mx, my, mz, al, kap, br, s0, s1, s2, mtol, ptW, ieps,
909 forceFb, oneDir, useFit, gm, peps);
913 Kokkos::parallel_for(
916 curvHeightCell(
L3(x, y, z, e), c, mx, my, mz, al, kap, br, s0, s1, s2, mtol, ptW, ieps,
917 forceFb, oneDir, useFit, gm, peps);
925 const I3 e = e_, n = n_;
926 const int g = g_, gr =
kPvHalf;
927 SField mx = mx_, my = my_, mz = mz_, al = alpha_, kap = kappa_, br = branch_;
929 const VofMetric gm =
metric;
930 const long sy = e_.
x, sz =
static_cast<long>(e_.
x) * e_.
y;
936 const VofPvCached acc{slot_.data(), cache_.data()};
937 Kokkos::parallel_for(
938 "vof::curv::pv_list", Kokkos::RangePolicy<SExec>(
SExec(), 0, nI_),
KOKKOS_LAMBDA(
long t) {
944 Kokkos::parallel_for(
947 curvFallbackCell(
L3(x, y, z, e), acc, kap, br, sy, sz, gr, dW, cmin, gm);
958 const I3 e = e_, n = n_;
960 SField kap = kappa_, br = branch_;
964 Kokkos::parallel_reduce(
965 "vof::curv::clip_list", Kokkos::RangePolicy<SExec>(
SExec(), 0, nI_),
967 const long i = list(t);
971 kap(i) = (kap(i) == kap(i)) ? Kokkos::copysign(km, kap(i)) : km;
977 Kokkos::parallel_reduce(
980 const long i =
L3(x, y, z, e);
984 kap(i) = (kap(i) == kap(i)) ? Kokkos::copysign(km, kap(i)) : km;
995 const I3 e = e_, n = n_;
999 Kokkos::parallel_reduce(
1002 long& n4,
long&
n5) {
1003 const int b =
static_cast<int>(br(
L3(x, y, z, e)));
1020 s.interfacial, s.hf, s.hfMixed, s.hfFit, s.pv, s.pvReduced, s.noEstimate);
1026 I3 n_{0, 0, 0}, e_{0, 0, 0};
1029 SField mx_, my_, mz_, alpha_, kappa_, branch_;
1031 long nG_ = 0, nI_ = 0;
1037 void ensureCache_() {
1038 const long cap =
static_cast<long>(listG_.extent(0));
1039 if (cap >= (1L << 31))
1040 throw std::runtime_error(
1041 "peclet::flow::vof::VofCurvature: grown list capacity >= 2^31 (the slot map is int)");
1042 if (
static_cast<long>(cache_.extent(0)) < cap)
1044 Kokkos::view_alloc(std::string(
"vof::curv::cache"), Kokkos::WithoutInitializing), cap);
flow — VoF rung V1: Weymouth & Yue (2010) directionally-split, exactly volume-conserving geometric ad...
Curvature of the colour field on an extended (inner + ghost) block.
double interfaceEps
Wisp threshold on the interfacial predicate.
double monoTol
Tolerance of the column monotonicity test (hfColumnHeight).
static void fallbackBatch(const std::vector< VofCurvFallbackJob > &jobs)
Tier 3 of many cascades in as few launches as possible: one kernel per kFallbackBatch jobs over their...
bool debugSingleDirection
DIAGNOSTIC ONLY — disable tier 2 (the non-preferred column directions), so the branch census separate...
void fallbackPass(SField c)
Tier 3, over the cells the height pass could not serve.
double pureEps
Tolerance of the PURITY test that ends a height-function column (hfIsFull/hfIsEmpty),...
void copyTunablesFrom(const VofCurvature &src)
Copy EVERY tunable above (the metric, the estimator knobs, the predicates, the clip,...
Kokkos::View< PvMoments *, SMem > MomCache
double kappaMax
Curvature admissibility clip (doc/vof_overlap_design.md §5.1, §11): after the cascade,...
long clipPass()
The admissibility clip (see kappaMax), over the same cells the cascade ran on: the compacted interfac...
static constexpr int kFallbackBatch
Jobs per launch of fallbackBatch (the table rides in the kernel's functor by value).
Kokkos::View< int *, SMem > IField
VofCurvFallbackJob fallbackJob(SField c) const
This cascade's tier-3 share for fallbackBatch (worklist mode only: the job walks the compacted interf...
bool useMixedHeightFit
Tier 2b, and it ships OFF — a measured decision, see the WO-O findings entry.
VofMetric metric
The anisotropic cell metric (Phase 3).
bool debugForceFallback
DIAGNOSTIC ONLY — disable tiers 1 and 2 so every interfacial cell goes to the PV fit.
SField planeM(int d) const
The PLIC planes the cascade reconstructed (planeM(d), planeAlpha()): for the block container's batche...
IField slotMap() const
The slot map of the moment cache (design G §5.2), for the batched cascade, which writes it with posit...
double weightWidth
Wendland support width d of the PV fit, in cell units (Han et al. §5: 2.5 with S = 5).
const Timing & timing() const
void computeBegin(SField c)
compute() in three parts, for a caller that runs MANY cascades (the block container): computeBegin (c...
void reconstructPlanes(SField c)
PLIC planes over the inner region grown by kPvHalf — every cell the 5^3 fit can read.
Stats compute(SField c)
Compute the curvature over the inner region from a colour field on the SAME extended block.
double kappaClipValue() const
The bound the clip applies this call (0 = off).
void heightPass(SField c)
Tiers 1 and 2.
void addT_(double &acc, double t0)
SField planeAlpha() const
void init(int nx, int ny, int nz, int ghost)
double ptWeightWidth
Wendland support width of the tier-2b height-position fit, in cell units.
double cosMin
Minimum n_p . n for a stencil polygon to enter the PV fit (pvFitAdd).
void compact(SField c)
The two compaction scans: listG_ = every interfacial cell of the GROWN region (what reconstructPlanes...
flow — thin shim: the container-free VoF height-function / paraboloid curvature kernels (rung V3) now...
#define PECLET_VOF_DEVICE_NOINLINE
ONE compiled device body per tier-3 piece (C1 ruling, design G WO-3).
flow — portable (Kokkos) MAC stencil operators: Red-Black Gauss-Seidel smoothers + divergence.
Kokkos::View< double *, SExec::scratch_memory_space, Kokkos::MemoryUnmanaged > VofPvDbls
Kokkos::View< long *, SMem > LField
bool csfKappaDefined(double branch)
Is the curvature of a cell carrying branch code branch usable?
constexpr int kVofWarp
The team size of a device tier-3 launch: one warp / wavefront.
bool vofIsInterface(double c, double eps)
wyIsMixed with a wisp threshold: the cell carries an interface only while eps < C < 1 - eps.
void wyReconstructCell(const SF &c, long i, long sy, long sz, SF mx, SF my, SF mz, SF alpha)
PLIC reconstruction of one cell: MYC normal from the 3^3 colour stencil + the analytic plane offset.
Kokkos::View< int *, SExec::scratch_memory_space, Kokkos::MemoryUnmanaged > VofPvInts
void curvFallbackCell(long i, const Acc &acc, SF kap, SF br, long sy, long sz, int gr, double dW, double cmin, VofMetric g)
Tier 3 for one target, one thread (host): the frame from the target's entry (pvFrameMoments: the phys...
Kokkos::View< PvTerm *, SExec::scratch_memory_space, Kokkos::MemoryUnmanaged > VofPvTerms
Team scratch of the device tier-3 body: the compacted stencil's canonical offset indices,...
void curvHeightCell(long i, SF c, SF mx, SF my, SF mz, SF al, SF kap, SF br, long s0, long s1, long s2, double mtol, double ptW, double ieps, bool forceFb, bool oneDir, bool useFit, VofMetric g, double peps)
WO-V9 — the per-cell bodies of the two cascade tiers, lifted VERBATIM out of the kernels they used to...
PECLET_VOF_DEVICE_NOINLINE void curvMomentsBuild(double mx, double my, double mz, double alpha, const VofMetric &g, PvMoments &P)
pvMomentsBuild, the one call every planes pass and the dense accessor make (out of line on a device,...
PECLET_VOF_DEVICE_NOINLINE void curvFallbackTeam(const Member &tm, const VofPvTeamScratch &sc, long i, const Acc &acc, SF kap, SF br, long sy, long sz, int gr, double dW, double cmin, VofMetric g)
curvFallbackCell for one target on a whole team (design G §5.4; the team shape of C3,...
void curvFallbackStore(long i, const PvFit &fit, SF kap, SF br)
Tier 3's last step: solve the accumulated PV system and write the target's kappa and branch.
long L3(int x, int y, int z, I3 e)
Kokkos::DefaultExecutionSpace SExec
Kokkos::View< double *, SMem > SField
flow — the multidimensional iteration policies: iteration order follows storage.
One block's share of a batched tier-3 launch (VofCurvature::fallbackBatch): its fields,...
const PvMoments * cache
... and its per-cell moment cache
const int * slot
the cascade's slot map (design G §5.2)
The job table of one batched launch, passed to the kernel BY VALUE (namespace scope: nvcc's extended ...
VofCurvFallbackJob job[N]
Per-call census of the cascade over this block's inner region (LOCAL; a distributed caller sums them ...
long pvReduced
tier 3 with the rank-deficient 3-parameter model
long hfMixed
tier 2a: HF in one of the other two directions
long hfFit
tier 2b: mixed HF – paraboloid through the consistent columns
long interfacial
cells carrying an interface (vofIsInterface, i.e.
long pv
tier 3: PV paraboloid fit, full 6-parameter model
long noEstimate
NO estimate produced (must be 0 on every gated case)
long clipped
Cells whose curvature the admissibility clip (kappaMax) bounded this call.
long hf
tier 1: HF in the preferred direction
double clip
the curvature admissibility clip
double fallback
tier 3, the PLIC-volumetric paraboloid
double compact
the two parallel_scans (0 when the compaction is off)
double planes
the PLIC pass over the grown region
double height
tiers 1/2, the height-function cascade
double census
the branch census reduction
The cache-backed accessor: slot[j] >= 0 iff cell j of the grown region is interfacial,...
bool present(long j) const
const PvMoments & entry(long j) const
The on-the-fly accessor of the dense mode (useWorklist = false, no list and no slot): the interfacial...
bool present(long j) const
PvMoments entry(long j) const
static std::size_t shmem(int nk)
Per-team level-0 scratch bytes for a stencil of nk offsets.
VofPvTeamScratch(const Member &tm, int nk)
A raw-pointer field accessor with the View<double*> call syntax, for kernels that walk SEVERAL blocks...
double & operator()(long i) const
flow — VoF rung V4 (WO-P): balanced-force continuum surface force + the capillary Δt.