57#ifndef PECLET_FLOW_VOF_BLOCK_CONTAINER_HPP
58#define PECLET_FLOW_VOF_BLOCK_CONTAINER_HPP
64#include <Kokkos_Core.hpp>
72#include "peclet/core/decomp/block_decomposer.hpp"
92 int n(
int d)
const {
return hi[d] -
lo[d]; }
93 long cells()
const {
return static_cast<long>(
n(0)) *
n(1) *
n(2); }
94 bool empty()
const {
return n(0) <= 0 ||
n(1) <= 0 ||
n(2) <= 0; }
99 for (
int d = 0; d < 3; ++d)
100 if (o.lo[d] <
lo[d] || o.hi[d] >
hi[d])
105 for (
int d = 0; d < 3; ++d)
106 if (
lo[d] != o.lo[d] ||
hi[d] != o.hi[d])
114 for (
int d = 0; d < 3; ++d) {
123 for (
int d = 0; d < 3; ++d) {
124 r.lo[d] = a.lo[d] > b.lo[d] ? a.lo[d] : b.lo[d];
125 r.hi[d] = a.hi[d] < b.hi[d] ? a.hi[d] : b.hi[d];
155 const int s = a < -outside ? -outside : a;
156 const int e = b > gs + outside ? gs + outside : b;
158 out[n++] =
VofRun{s, e - s, s - a};
162 throw std::runtime_error(
"peclet::flow::vof: block range exceeds three periodic images");
164 while (k < b && n < 5) {
165 const int w = ((k % gs) + gs) % gs;
169 out[n++] =
VofRun{w, len, k - a};
178 const int g[3] = {gs.x, gs.y, gs.z};
179 for (
int d = 0; d < 3; ++d) {
190 if (b.hi[d] <= b.lo[d]) {
192 b.hi[d] = g[d] < 1 ? 1 : g[d];
217inline void vofAssignLpt(
const std::vector<long>& w,
int size, std::vector<int>& master) {
218 const std::size_t n = w.size();
220 if (n == 0 || size <= 1)
222 std::vector<std::size_t>
ord(n);
223 for (std::size_t i = 0; i < n; ++i)
227 std::stable_sort(
ord.begin(),
ord.end(),
228 [&](std::size_t a, std::size_t b) { return w[a] > w[b]; });
229 std::vector<long> load(
static_cast<std::size_t
>(size), 0);
230 for (std::size_t k = 0; k < n; ++k) {
232 for (
int r = 1; r < size; ++r)
233 if (load[
static_cast<std::size_t
>(r)] < load[
static_cast<std::size_t
>(
best)])
236 load[
static_cast<std::size_t
>(
best)] += w[
ord[k]];
246inline void vofAssignOrb(
const std::vector<long>& w,
int size, std::vector<int>& master) {
247 const std::size_t n = w.size();
249 if (n == 0 || size <= 1)
251 std::vector<peclet::core::Real>
wr(n);
252 for (std::size_t i = 0; i < n; ++i)
253 wr[i] =
static_cast<peclet::core::Real
>(w[i]);
254 peclet::core::decomp::BlockDecomposer<1>
dec(
255 static_cast<std::size_t
>(size),
256 peclet::core::IVec<1>{
static_cast<peclet::core::Index
>(n)},
wr);
257 for (
int r = 0; r < size; ++r) {
258 const auto b =
dec.block(
static_cast<std::size_t
>(r));
259 for (
auto i = b.origin[0]; i < b.origin[0] + b.size[0]; ++i)
260 master[
static_cast<std::size_t
>(i)] = r;
322 bool mine()
const {
return mine_; }
340 for (
int d = 0; d < 3; ++d)
341 out[d] = prevCentroid_[d];
342 out[3] = hasPrev_ ? 1.0 : 0.0;
347 out[8] =
static_cast<double>(st_.
fullAxis);
351 for (
int d = 0; d < 3; ++d)
352 prevCentroid_[d] =
in[d];
353 hasPrev_ = (
in[3] != 0.0);
366 bool allocated_ =
false;
371 double prevCentroid_[3]{0, 0, 0};
372 bool hasPrev_ =
false;
398 virtual void syncTable(std::vector<VofBlock>& blocks) = 0;
411 throw std::runtime_error(
"peclet::flow::vof: this block exchange has no colour SUM scatter");
452 for (
auto& b : blocks_) {
457 const VofMetric&
metric()
const {
return metric_; }
459 void setExchange(std::shared_ptr<VofBlockExchangeBase> x) { exch_ = std::move(x); }
463 int ghost()
const {
return ghost_; }
533 for (
auto& b : blocks_)
535 b.adv_.wispEps = eps;
536 b.curv_.pureEps = eps;
576 std::size_t
count()
const {
return blocks_.size(); }
583 const std::vector<VofBlock>&
blocks()
const {
return blocks_; }
584 long step()
const {
return step_; }
590 int masterOf(
long id)
const {
return static_cast<int>(
id % size_); }
601 const double c[3] = {cx, cy, cz};
602 for (
int d = 0; d < 3; ++d) {
603 bb.lo[d] =
static_cast<int>(std::floor((c[d] - r) / h_)) - 1;
604 bb.hi[d] =
static_cast<int>(std::ceil((c[d] + r) / h_)) + 1;
607 b.id =
static_cast<long>(blocks_.size());
610 b.mine_ = (b.master == rank_);
611 blocks_.push_back(std::move(b));
612 const std::size_t
idx = blocks_.size() - 1;
615 for (std::size_t k = 0; k + 1 < blocks_.size(); ++k)
616 if (blocks_[k].mine_ && blocks_[k].allocated_)
621 const I3 e = nb.adv_.extent();
622 const int g = ghost_;
623 const I3 o = nb.box.origin();
624 const double hh = h_;
625 SField cf = nb.adv_.colour();
626 Kokkos::parallel_for(
627 "vof::block::seed_sphere",
630 const double gx = (x - g + o.x) *
hh,
gy = (y - g + o.y) *
hh,
gz = (z - g + o.z) *
hh;
631 cf(
L3(x, y, z, e)) =
sphereCellFraction(cx, cy, cz, r,
gx,
gy,
gz,
hh,
subLevels);
646 throw std::runtime_error(
"peclet::flow::vof::VofBlockSet: no exchange installed");
647 exch_->gatherFaceVel(blocks_, ghost_);
648 std::vector<VofBlock*>
deb;
654 for (
auto& b : blocks_) {
658 b.adv_.advect(dt, step_);
659 b.st_.recentred =
false;
675 for (std::size_t k = 0; k < blocks_.size(); ++k)
676 if (blocks_[k].mine_)
679 exch_->syncTable(blocks_);
680 for (
auto& b : blocks_)
681 b.mine_ = (b.master == rank_);
688 exch_->scatterColourMax(blocks_,
cLocal);
692 for (
auto& b : blocks_)
710 for (std::size_t i = 0; i < blocks_.size(); ++i)
711 if (blocks_[i].mine_ && blocks_[i].allocated_)
719 throw std::runtime_error(
"peclet::flow::vof::VofBlockSet: no exchange installed");
723 exch_->scatterForceSum(blocks_,
fx,
fy,
fz);
729 for (
auto& b : blocks_)
730 if (b.mine_ && !b.curv_.useWorklist)
733 std::vector<VofCurvFallbackJob>
jobs;
734 for (
auto& b : blocks_)
736 b.curv_.computeBegin(b.adv_.colour());
737 jobs.push_back(b.curv_.fallbackJob(b.adv_.colour()));
741 for (
auto& b : blocks_) {
745 batch ? b.curv_.computeEnd() : b.curv_.compute(b.adv_.colour());
747 curvStats_.
hf += st.hf;
748 curvStats_.
hfMixed += st.hfMixed;
749 curvStats_.
hfFit += st.hfFit;
750 curvStats_.
pv += st.pv;
753 curvStats_.
clipped += st.clipped;
756 exch_->scatterForceSum(blocks_,
fx,
fy,
fz);
764 for (
auto& b : blocks_)
765 b.curv_.kappaMax = km;
770 const I3 n = b.adv_.inner();
771 b.curv_.init(n.x, n.y, n.z, ghost_);
777 const long len =
static_cast<long>(b.adv_.extent().x) * b.adv_.extent().y * b.adv_.extent().z;
778 for (
int c = 0; c < 3; ++c)
779 b.f_[c] =
SField(
"vof::block::csf", len);
789 const I3 e = b.adv_.extent(), n = b.adv_.inner();
790 const int g = ghost_;
793 const double sig =
sigma;
794 const double wgt[3] = {metric_.h[0] * metric_.h[0], metric_.h[1] * metric_.h[1],
795 metric_.h[2] * metric_.h[2]};
796 SField cv = b.adv_.colour(), kp = b.curv_.kappa(), kb = b.curv_.branch();
797 const long sy = e.x, sz =
static_cast<long>(e.x) * e.y;
798 for (
int c = 0; c < 3; ++c) {
800 const double wc =
wgt[c];
801 const long strd = (c == 0) ? 1 : (c == 1 ? sy : sz);
802 Kokkos::parallel_for(
803 "vof::block::csf_force",
805 {g + n.x, g + n.y, g + n.z}),
807 const long i =
L3(x, y, z, e);
823 throw std::runtime_error(
"peclet::flow::vof::VofBlockSet: no exchange installed");
824 exch_->scatterColourSum(blocks_,
sLocal);
830 throw std::runtime_error(
"peclet::flow::vof::VofBlockSet: no exchange installed");
831 exch_->syncTable(blocks_);
832 exch_->scatterColourMax(blocks_,
cLocal);
841 std::vector<long> w(blocks_.size());
842 for (std::size_t i = 0; i < blocks_.size(); ++i)
843 w[i] = blocks_[i].box.cells();
854 m.assign(blocks_.size(), 0);
855 for (std::size_t i = 0; i < blocks_.size(); ++i)
856 m[i] =
static_cast<int>(blocks_[i].
id % size_);
867 if (blocks_.empty() || size_ <= 1)
870 std::vector<int>
old(blocks_.size());
872 for (std::size_t i = 0; i < blocks_.size(); ++i) {
873 old[i] = blocks_[i].master;
874 if (
old[i] != want[i])
879 for (std::size_t i = 0; i < blocks_.size(); ++i) {
880 blocks_[i].master = want[i];
881 blocks_[i].st_.master = want[i];
884 for (std::size_t i = 0; i < blocks_.size(); ++i)
885 if (want[i] == rank_ &&
old[i] != rank_) {
886 blocks_[i].mine_ =
true;
890 exch_->migrateColour(blocks_,
old);
891 for (std::size_t i = 0; i < blocks_.size(); ++i) {
892 if (
old[i] == rank_ && want[i] != rank_) {
894 blocks_[i].mine_ =
false;
895 }
else if (want[i] == rank_ &&
old[i] != rank_) {
898 blocks_[i].mine_ =
true;
901 blocks_[i].serializeAux(
aux);
903 blocks_[i].deserializeAux(
aux);
919 b.id =
static_cast<long>(blocks_.size());
920 b.master =
static_cast<int>(b.id % size_);
923 b.mine_ = (b.master == rank_);
924 blocks_.push_back(std::move(b));
925 const std::size_t
idx = blocks_.size() - 1;
926 for (std::size_t k = 0; k + 1 < blocks_.size(); ++k)
927 if (blocks_[k].mine_ && blocks_[k].allocated_)
929 if (blocks_[
idx].mine_)
936 throw std::runtime_error(
"peclet::flow::vof::VofBlockSet: no exchange installed");
938 exch_->gatherColour(blocks_,
cLocal);
939 for (
auto& b : blocks_)
961 if (!b.mine_ || !b.allocated_)
963 const I3 n = b.adv_.inner();
964 SField c = b.adv_.colour();
965 auto hc = Kokkos::create_mirror_view(c);
966 Kokkos::deep_copy(
hc, c);
967 std::vector<double> out(
static_cast<std::size_t
>(n.x) *
static_cast<std::size_t
>(n.y) *
968 static_cast<std::size_t
>(n.z));
970 for (
int z = 0; z < n.z; ++z)
971 for (
int y = 0; y < n.y; ++y)
972 for (
int x = 0; x < n.x; ++x)
973 out[q++] =
hc(b.adv_.index(x, y, z));
983 const I3 n = b.adv_.inner();
984 SField k = b.curv_.kappa();
985 auto hk = Kokkos::create_mirror_view(k);
986 Kokkos::deep_copy(
hk, k);
987 std::vector<double> out(
static_cast<std::size_t
>(n.x) *
static_cast<std::size_t
>(n.y) *
988 static_cast<std::size_t
>(n.z));
990 for (
int z = 0; z < n.z; ++z)
991 for (
int y = 0; y < n.y; ++y)
992 for (
int x = 0; x < n.x; ++x)
993 out[q++] =
hk(b.adv_.index(x, y, z));
1003 b.id =
static_cast<long>(blocks_.size());
1006 b.mine_ = (b.master == rank_);
1007 blocks_.push_back(std::move(b));
1008 const std::size_t
idx = blocks_.size() - 1;
1009 for (std::size_t k = 0; k + 1 < blocks_.size(); ++k)
1010 if (blocks_[k].mine_ && blocks_[k].allocated_)
1012 if (!blocks_[
idx].mine_)
1016 const I3 n = nb.adv_.inner();
1017 const std::size_t want =
static_cast<std::size_t
>(n.x) *
static_cast<std::size_t
>(n.y) *
1018 static_cast<std::size_t
>(n.z);
1019 if (
colour.size() != want)
1020 throw std::runtime_error(
1021 "peclet::flow::vof::VofBlockSet::seedBoxWithColour: colour size does not match the box");
1022 SField c = nb.adv_.colour();
1023 auto hc = Kokkos::create_mirror_view(c);
1024 Kokkos::deep_copy(
hc, c);
1026 for (
int z = 0; z < n.z; ++z)
1027 for (
int y = 0; y < n.y; ++y)
1028 for (
int x = 0; x < n.x; ++x)
1029 hc(nb.adv_.index(x, y, z)) =
colour[q++];
1030 Kokkos::deep_copy(c,
hc);
1043 const I3 e = b.adv_.extent(), n = b.adv_.inner(), o = b.box.origin();
1044 const int g = ghost_;
1045 const int lx = b.seed_.lo[0],
hx = b.seed_.hi[0],
ly = b.seed_.lo[1],
hy = b.seed_.hi[1],
1046 lz = b.seed_.lo[2],
hz = b.seed_.hi[2];
1049 SField c = b.adv_.colour();
1050 Kokkos::parallel_for(
1051 "vof::block::clip_seed",
1054 const int gx = x + o.x,
gy = y + o.y,
gz = z + o.z;
1056 c(
L3(x + g, y + g, z + g, e)) = 0.0;
1064 const I3 e = b.adv_.extent(), n = b.adv_.inner(), o = b.box.origin();
1065 const int g = ghost_;
1066 SField f = b.adv_.colour();
1068 Kokkos::parallel_for(
1069 "vof::block::ghost_zero",
1074 f(
L3(x, y, z, e)) = 0.0;
1077 const bool sx = per_[0] && b.box.n(0) == gs_.
x;
1078 const bool sy = per_[1] && b.box.n(1) == gs_.
y;
1079 const bool sz = per_[2] && b.box.n(2) == gs_.
z;
1084 if (!per_[0] || !per_[1] || !per_[2])
1095 std::vector<VofBlockStats> v;
1096 v.reserve(blocks_.size());
1097 for (
const auto& b : blocks_) {
1100 q.master = b.master;
1101 for (
int d = 0; d < 3; ++d) {
1102 q.lo[d] = b.box.lo[d];
1103 q.hi[d] = b.box.hi[d];
1105 q.cells = b.box.cells();
1116 cells.assign(size_, 0);
1117 for (
const auto& b : blocks_) {
1124 std::vector<long> m, c;
1126 long tot = 0, mx = 0;
1129 mx = v > mx ? v : mx;
1133 return static_cast<double>(mx) * size_ /
static_cast<double>(tot);
1145 void installHook(std::size_t
idx) {
1150 void allocate(std::size_t idx) {
1151 VofBlock& b = blocks_[idx];
1152 b.adv_ = acquireAdvector(b.box.n(0), b.box.n(1), b.box.n(2));
1154 b.allocated_ =
true;
1156 b.st_.master = b.master;
1163 void release(std::size_t idx) {
1164 VofBlock& b = blocks_[idx];
1165 retireAdvector(std::move(b.adv_));
1166 b.adv_ = WyAdvector();
1167 b.curv_ = VofCurvature();
1168 for (
int c = 0; c < 3; ++c)
1170 b.allocated_ =
false;
1180 WyAdvector acquireAdvector(
int nx,
int ny,
int nz) {
1181 const std::array<int, 3>
key{nx,
ny, nz};
1183 auto it = pool_.find(key);
1184 if (it != pool_.end() && !
it->second.empty()) {
1185 WyAdvector
a = std::move(
it->second.back());
1186 it->second.pop_back();
1188 Kokkos::deep_copy(
a.colour(), 0.0);
1189 for (
int d = 0;
d < 3; ++
d)
1190 Kokkos::deep_copy(
a.faceVel(d), 0.0);
1195 a.globalMax =
nullptr;
1196 a.exchange =
nullptr;
1202 a.init(nx, ny, nz, h_, ghost_);
1206 a.globalMax =
nullptr;
1210 void retireAdvector(WyAdvector&& a) {
1211 if (!
usePool ||
a.colour().extent(0) == 0)
1213 const I3
n =
a.inner();
1214 const std::array<int, 3>
key{
n.x,
n.y,
n.z};
1215 auto&
v = pool_[
key];
1218 a.exchange =
nullptr;
1219 v.push_back(std::move(a));
1235 const I3 e = b.adv_.extent(), n = b.adv_.inner();
1236 const int g = ghost_;
1238 SField c = b.adv_.colour();
1239 int lo[3] = {n.x, n.y, n.z}, hi[3] = {-1, -1, -1};
1241 MD pol(
SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z});
1242 Kokkos::parallel_reduce(
1243 "vof::block::bbox_lo",
pol,
1245 if (!(Kokkos::fabs(c(
L3(x, y, z, e))) > eps))
1247 const int px = x - g, py = y - g, pz = z - g;
1252 Kokkos::Min<int>(lo[0]), Kokkos::Min<int>(lo[1]), Kokkos::Min<int>(lo[2]));
1253 Kokkos::parallel_reduce(
1254 "vof::block::bbox_hi",
pol,
1256 if (!(Kokkos::fabs(c(
L3(x, y, z, e))) > eps))
1258 const int px = x - g, py = y - g, pz = z - g;
1263 Kokkos::Max<int>(hi[0]), Kokkos::Max<int>(hi[1]), Kokkos::Max<int>(hi[2]));
1267 for (
int d = 0; d < 3; ++d) {
1268 out.lo[d] = lo[d] + b.box.lo[d];
1269 out.hi[d] = hi[d] + 1 + b.box.lo[d];
1288 for (
int d = 0; d < 3; ++d)
1300 const int L[3] = {gs_.
x, gs_.
y, gs_.
z};
1303 for (
int d = 0; d < 3; ++d) {
1304 wrap[d] = per_[d] && nb.n(d) == L[d];
1313 const I3 se = b.adv_.extent(),
de =
fresh.extent();
1315 const int g = ghost_;
1316 const int sx = b.box.lo[0], sy = b.box.lo[1], sz = b.box.lo[2];
1317 const int snx = b.box.n(0),
sny = b.box.n(1),
snz = b.box.n(2);
1318 const int dx = nb.lo[0], dy = nb.lo[1], dz = nb.lo[2];
1319 const bool wx = wrap[0],
wy = wrap[1],
wz = wrap[2];
1320 const int Lx = L[0],
Ly = L[1],
Lz = L[2];
1321 SField src = b.adv_.colour(), dst =
fresh.colour();
1322 Kokkos::parallel_for(
1323 "vof::block::recentre_copy",
1326 const int gx = x + dx,
gy = y + dy,
gz = z + dz;
1337 dst(
L3(x + g, y + g, z + g,
de)) = v;
1341 retireAdvector(std::move(b.adv_));
1342 b.adv_ = std::move(
fresh);
1344 b.adv_.globalMax =
nullptr;
1349 blocks_[
idx].st_.recentred =
true;
1350 blocks_[
idx].st_.discarded += dropped;
1352 ++blocks_[
idx].st_.fullAxis;
1373 std::vector<VofBlock*> one{&b};
1389 Kokkos::deep_copy(pkHost_, pk_);
1414 const int g = ghost_;
1422 std::vector<long> off(
bs.size() + 1, 0);
1423 for (std::size_t k = 0; k <
bs.size(); ++k) {
1424 const I3 n =
bs[k]->adv_.inner();
1425 off[k + 1] = off[k] +
static_cast<long>(n.x) * n.y * n.z;
1428 if (
static_cast<long>(f.extent(0)) < off.back())
1429 f =
LField(Kokkos::view_alloc(std::string(name), Kokkos::WithoutInitializing), off.back());
1432 ensure(listD_,
"vof::block::debris_list");
1434 ensure(listR_,
"vof::block::residue_list");
1437 long k,
nD, nR, nA, nDr;
1440 std::vector<Pending>
pend;
1441 std::vector<DebrisJob>
jobs;
1442 for (std::size_t k = 0; k <
bs.size(); ++k) {
1444 b.st_.debrisCells = 0;
1445 b.st_.debrisVolume = 0.0;
1446 const I3 e = b.adv_.extent(), n = b.adv_.inner();
1447 const long region =
static_cast<long>(n.x) * n.y * n.z, o = off[k];
1448 const long sy = e.x, sz =
static_cast<long>(e.x) * e.y;
1449 const int nx = n.x, ny = n.y;
1450 SField c = b.adv_.colour();
1451 long nD = 0, nR = 0;
1454 Kokkos::parallel_scan(
1455 "vof::block::debris_mark", Kokkos::RangePolicy<SExec>(
SExec(), 0,
region),
1457 const int ix =
static_cast<int>(r % nx);
1458 const int iy =
static_cast<int>((r / nx) % ny);
1459 const int iz =
static_cast<int>(r / (
static_cast<long>(nx) * ny));
1460 const long i =
L3(g +
ix, g +
iy, g +
iz, e);
1471 Kokkos::parallel_scan(
1472 "vof::block::residue_mark", Kokkos::RangePolicy<SExec>(
SExec(), 0,
region),
1474 const int ix =
static_cast<int>(r % nx);
1475 const int iy =
static_cast<int>((r / nx) % ny);
1476 const int iz =
static_cast<int>(r / (
static_cast<long>(nx) * ny));
1477 const double ci = c(
L3(g +
ix, g +
iy, g +
iz, e));
1488 if (doDebris &&
nD > 0) {
1491 Kokkos::parallel_reduce(
1492 "vof::block::debris_census", Kokkos::RangePolicy<SExec>(
SExec(), 0, 1),
1495 for (
long t = 0; t <
nD; ++t)
1496 a += c(listD(o + t));
1500 b.st_.debrisCells =
nD;
1501 b.st_.debrisVolume =
dVd;
1505 ensure(listA_,
"vof::block::attached_list");
1508 Kokkos::parallel_scan(
1509 "vof::block::debris_attached", Kokkos::RangePolicy<SExec>(
SExec(), 0,
region),
1511 const int ix =
static_cast<int>(r % nx);
1512 const int iy =
static_cast<int>((r / nx) % ny);
1513 const int iz =
static_cast<int>(r / (
static_cast<long>(nx) * ny));
1514 const long i =
L3(g +
ix, g +
iy, g +
iz, e);
1526 j.listD = doDebris ? listD_.data() + o :
nullptr;
1527 j.listR = doResidue ? listR_.data() + o :
nullptr;
1528 j.listA = listA_.data() + o;
1529 j.nDc = doDebris ?
nD : 0;
1543 const long np =
static_cast<long>(
pend.size());
1544 if (
static_cast<long>(debrisOut_.extent(0)) < 6 *
np) {
1545 debrisOut_ =
SField(
"vof::block::debris_out", 6 *
np);
1546 debrisOutHost_ = Kokkos::create_mirror_view(debrisOut_);
1553 for (
int q = 0; q < T.nj; ++q)
1554 T.job[q] =
jobs[j0 + q];
1555 const long base = 6 * j0;
1556 Kokkos::parallel_for(
1557 "vof::block::debris_sums", Kokkos::RangePolicy<SExec>(
SExec(), 0, T.nj),
1561 const long nDc =
J.nDc, nDr =
J.nDr, nR =
J.nR, nA =
J.nA;
1562 double sD = 0.0,
sR = 0.0, W = 0.0,
VA = 0.0;
1563 for (
long t = 0; t < nDc; ++t)
1564 sD += c(
J.listD[t]);
1565 for (
long t = 0; t < nR; ++t)
1566 sR += c(
J.listR[t]);
1567 for (
long t = 0; t < nA; ++t) {
1568 const double ca = c(
J.listA[t]);
1569 W += ca * (1.0 - ca);
1572 const long ob = base + 6 * q;
1578 out(
ob + 5) = (
VA < vaMin || !(W > 0.0)) ? 0.0 : 1.0;
1581 for (
long t = 0; t < nDr; ++t)
1582 dV += c(
J.listD[t]);
1583 for (
long t = 0; t < nR; ++t)
1584 dV += c(
J.listR[t]);
1588 Kokkos::deep_copy(debrisOutHost_, out);
1589 auto hoAll = debrisOutHost_;
1590 for (
long p = 0; p <
np; ++p) {
1593 const long o = off[
pb.k];
1594 const I3 n = b.adv_.inner();
1595 const long region =
static_cast<long>(n.x) * n.y * n.z;
1597 for (
int q = 0; q < 6; ++q)
1599 const long nD =
pb.nD, nR =
pb.nR, nA =
pb.nA, nDr =
pb.nDr;
1602 const double dV =
ho[4], W =
ho[2];
1603 if (
static_cast<long>(debrisEx_.extent(0)) <
region)
1604 debrisEx_ =
SField(Kokkos::view_alloc(
"vof::block::debris_excess",
1605 Kokkos::WithoutInitializing),
1608 SField c = b.adv_.colour();
1609 LField listD = listD_, listR = listR_, listA = listA_;
1610 const long nZ = nDr + nR,
nTot =
nZ + nA;
1612 Kokkos::parallel_reduce(
1613 "vof::block::debris_write", Kokkos::RangePolicy<SExec>(
SExec(), 0,
nTot),
1616 c(listD(o + t)) = 0.0;
1620 c(listR(o + t - nDr)) = 0.0;
1623 const long ta = t -
nZ;
1624 const long i = listA(o + ta);
1625 const double ca = c(i);
1626 const double d = dV * (ca * (1.0 - ca)) / W;
1640 Kokkos::parallel_reduce(
1641 "vof::block::debris_lost", Kokkos::RangePolicy<SExec>(
SExec(), 0, 1),
1644 for (
long t = 0; t < nA; ++t)
1653 b.st_.debrisCells =
nD;
1654 b.st_.debrisVolume =
ho[0];
1658 b.st_.debrisUnresolved +=
nD;
1663 b.st_.debrisCells =
nD;
1664 b.st_.debrisVolume =
ho[0];
1665 b.st_.debrisReturned +=
ho[0];
1667 b.st_.residueReturned +=
ho[1];
1668 b.st_.debrisLost +=
ho[4];
1674 const bool nowrap[3] = {
false,
false,
false};
1680 const I3 e = b.adv_.extent(), n = b.adv_.inner();
1681 const int g = ghost_;
1682 const int sx = b.box.lo[0], sy = b.box.lo[1], sz = b.box.lo[2];
1683 const int lx = nb.lo[0],
ly = nb.lo[1],
lz = nb.lo[2];
1684 const int hx = nb.hi[0],
hy = nb.hi[1],
hz = nb.hi[2];
1685 const bool wx = wrap[0],
wy = wrap[1],
wz = wrap[2];
1686 SField c = b.adv_.colour();
1688 Kokkos::parallel_reduce(
1689 "vof::block::out_of_box",
1691 {g + n.x, g + n.y, g + n.z}),
1693 const int gx = x - g +
sx,
gy = y - g + sy,
gz = z - g + sz;
1698 a += c(
L3(x, y, z, e));
1706 const I3 e = b.adv_.extent(), n = b.adv_.inner();
1707 const int g = ghost_;
1708 SField c = b.adv_.colour();
1710 Kokkos::parallel_reduce(
1711 "vof::block::inner_sum",
1713 {g + n.x, g + n.y, g + n.z}),
1714 KOKKOS_LAMBDA(
int x,
int y,
int z,
double& a) { a += c(
L3(x, y, z, e)); }, v);
1726 const I3 e = b.adv_.extent(), n = b.adv_.inner();
1727 const int g = ghost_;
1729 SField c = b.adv_.colour();
1730 const long sy = e.x, sz =
static_cast<long>(e.x) * e.y;
1732 Kokkos::parallel_reduce(
1735 {g + n.x, g + n.y, g + n.z}),
1737 const long i =
L3(x, y, z, e);
1738 const double ci = c(i);
1742 for (
int dz = -1; dz <= 1; ++dz)
1743 for (
int dy = -1; dy <= 1; ++dy)
1744 for (
int dx = -1; dx <= 1; ++dx)
1745 st[vof::plicSt(dx + 1, dy + 1, dz + 1)] = c(i + dx + dy * sy + dz * sz);
1747 vof::mycNormal(st, m);
1748 const double al = vof::plicAlpha(m[0], m[1], m[2],
ci);
1749 double v[8][3],
ctr[3],
ar = 0.0;
1750 const int nv = vof::plicPolygon(m[0], m[1], m[2], al, v);
1751 vof::polygonAreaCentroid(v,
nv,
ctr,
ar);
1762 const I3 e = b.adv_.extent(), n = b.adv_.inner(), o = b.box.origin();
1763 const int g = ghost_;
1764 const double hh = h_;
1765 SField c = b.adv_.colour();
1767 MD pol(
SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z});
1768 double v = 0.0, mx = 0.0, my = 0.0, mz = 0.0;
1769 Kokkos::parallel_reduce(
1770 "vof::block::moments1",
pol,
1771 KOKKOS_LAMBDA(
int x,
int y,
int z,
double&
av,
double& ax,
double& ay,
double&
az) {
1772 const double q = c(
L3(x, y, z, e));
1774 ax += q * (x - g + o.x + 0.5) *
hh;
1775 ay += q * (y - g + o.y + 0.5) *
hh;
1776 az += q * (z - g + o.z + 0.5) *
hh;
1781 b.st_.master = b.master;
1782 for (
int d = 0; d < 3; ++d) {
1783 b.st_.lo[d] = b.box.lo[d];
1784 b.st_.hi[d] = b.box.hi[d];
1786 b.st_.cells = b.box.cells();
1790 for (
int d = 0; d < 3; ++d)
1791 b.st_.centroid[d] = b.st_.velocity[d] = 0.0;
1792 for (
int d = 0; d < 6; ++d)
1793 b.st_.moment[d] = 0.0;
1796 const double cx = mx / v, cy = my / v, cz = mz / v;
1798 Kokkos::parallel_reduce(
1799 "vof::block::moments2",
pol,
1801 double&
axz,
double&
ayz) {
1802 const double q = c(
L3(x, y, z, e));
1803 const double px = (x - g + o.x + 0.5) *
hh - cx, py = (y - g + o.y + 0.5) *
hh - cy,
1804 pz = (z - g + o.z + 0.5) *
hh - cz;
1814 const double nc[3] = {cx, cy, cz};
1815 for (
int d = 0; d < 3; ++d) {
1816 b.st_.velocity[d] = (b.hasPrev_ && dt > 0.0) ? (nc[d] - b.prevCentroid_[d]) / dt : 0.0;
1817 b.prevCentroid_[d] = nc[d];
1818 b.st_.centroid[d] = nc[d];
1822 const double iv = 1.0 / v;
1823 b.st_.moment[0] =
sxx *
iv;
1824 b.st_.moment[1] =
syy *
iv;
1825 b.st_.moment[2] =
szz *
iv;
1826 b.st_.moment[3] =
sxy *
iv;
1827 b.st_.moment[4] =
sxz *
iv;
1828 b.st_.moment[5] =
syz *
iv;
1839 for (
const auto& b : blocks_) {
1842 if (!b.curv_.ready() || !b.curv_.useWorklist || b.curv_.timingOn || !b.f_[0].extent(0))
1856 std::vector<VofBlock*>
mb;
1857 for (
auto& b : blocks_)
1860 const std::size_t nb =
mb.size();
1863 std::vector<VofCurvJob>
jobs(nb);
1865 for (std::size_t k = 0; k < nb; ++k) {
1877 J.slot =
cv.slotMap().data();
1878 for (
int c = 0; c < 3; ++c)
1879 J.f[c] = b.f_[c].data();
1883 J.ieps =
cv.interfaceEps;
1884 J.mtol =
cv.monoTol;
1885 J.ptW =
cv.ptWeightWidth *
cv.metric.maxH();
1886 J.peps =
cv.pureEps;
1889 J.km =
cv.kappaClipValue();
1890 J.forceFb =
cv.debugForceFallback;
1891 J.oneDir =
cv.debugSingleDirection;
1892 J.useFit =
cv.useMixedHeightFit;
1897 if (
static_cast<std::size_t
>(cStartG_.extent(0)) < nb) {
1898 cStartG_ =
LField(
"vof::block::batch_curv_sG", nb);
1899 cEndG_ =
LField(
"vof::block::batch_curv_eG", nb);
1900 cStartI_ =
LField(
"vof::block::batch_curv_sI", nb);
1901 cEndI_ =
LField(
"vof::block::batch_curv_eI", nb);
1902 cCnt_ =
LField(
"vof::block::batch_curv_counts", 8 * nb);
1903 cCntHost_ = Kokkos::create_mirror_view(cCnt_);
1905 Kokkos::deep_copy(
SExec(), cCnt_, 0L);
1906 const double wgt[3] = {metric_.h[0] * metric_.h[0], metric_.h[1] * metric_.h[1],
1907 metric_.h[2] * metric_.h[2]};
1910 T.nj =
static_cast<int>(std::min<std::size_t>(
kVofBlockBatch, nb - j0));
1911 T.base =
static_cast<int>(j0);
1912 for (
int k = 0; k < T.nj; ++k)
1913 T.job[k] =
jobs[j0 + k];
1915 if (
static_cast<long>(cListG_.extent(0)) <
rg)
1917 Kokkos::view_alloc(std::string(
"vof::block::batch_curv_listG"), Kokkos::WithoutInitializing),
1921 if (
rg >= (1L << 31))
1922 throw std::runtime_error(
1923 "peclet::flow::vof::VofBlockSet: grown list capacity >= 2^31 (the slot map is int)");
1924 if (
static_cast<long>(cCacheG_.extent(0)) <
rg)
1925 cCacheG_ = Kokkos::View<PvMoments*, SMem>(
1926 Kokkos::view_alloc(std::string(
"vof::block::batch_curv_cacheG"), Kokkos::WithoutInitializing),
1928 T.cache = cCacheG_.data();
1933 if (
static_cast<long>(cListI_.extent(0)) <
ri)
1935 Kokkos::view_alloc(std::string(
"vof::block::batch_curv_listI"), Kokkos::WithoutInitializing),
1942 Kokkos::deep_copy(cCntHost_, cCnt_);
1943 for (std::size_t k = 0; k < nb; ++k) {
1944 const long*
ct = &cCntHost_(8 * k);
1946 curvStats_.
hf +=
ct[1];
1949 curvStats_.
pv +=
ct[4];
1963 for (
const auto& b : blocks_) {
1967 if (!b.allocated_ || a.hasGeometry() || a.debugRecomputeDilation || !a.useWorklist ||
1968 a.timingOn || a.globalMax || !a.exchange || a.ghost() != ghost_)
1978 J.c = a.colour().data();
1979 J.mx = a.planeM(0).data();
1980 J.my = a.planeM(1).data();
1981 J.mz = a.planeM(2).data();
1982 J.al = a.planeAlpha().data();
1983 J.fl = a.faceFlux().data();
1984 for (
int d = 0; d < 3; ++d)
1985 J.u[d] = a.faceVel(d).data();
1986 J.cc = a.dilationFlag().data();
1987 J.outside = a.hasOutsideMask() ? a.outsideMask().data() :
nullptr;
1990 J.o = b.box.origin();
1994 J.interfaceCfl = a.interfaceLocalCfl;
1995 const int L[3] = {gs_.
x, gs_.
y, gs_.
z};
1996 for (
int d = 0; d < 3; ++d)
1997 J.span[d] = per_[d] && b.box.n(d) == L[d];
1998 for (
int f = 0; f < 6; ++f)
1999 J.bcOwn[f] = a.bcFaceOwned(f) ? 1u : 0u;
2006 T.base =
static_cast<int>(j0);
2007 for (
int k = 0; k < T.nj; ++k)
2008 T.job[k] =
jobs[j0 + k];
2017 for (
int a = 0; a < 3; ++a) {
2021 for (
int k = 0; k < T.nj; ++k)
2022 if (T.job[k].span[a])
2023 P.
job[P.
nj++] = T.job[k];
2026 const int b = (a + 1) % 3, c = (a + 2) % 3;
2028 const int dims[3] = {
J.e.x,
J.e.y,
J.e.z};
2029 return static_cast<long>(
dims[b]) *
dims[c];
2033 if (!per_[0] || !per_[1] || !per_[2])
2040 if (
static_cast<std::size_t
>(bRead1_.extent(0)) >= len)
2042 if (bRead1_.extent(0) == 0)
2043 bRead1_ =
SField(
"vof::block::batch_read1", len);
2045 Kokkos::resize(bRead1_, len);
2046 bRead1Host_ = Kokkos::create_mirror_view(bRead1_);
2058 std::vector<std::size_t>
idx;
2059 for (std::size_t k = 0; k < blocks_.size(); ++k)
2060 if (blocks_[k].mine_)
2062 const std::size_t nb =
idx.size();
2065 std::vector<VofBlockJob>
jobs(nb);
2066 for (std::size_t k = 0; k < nb; ++k) {
2070 b.st_.master = b.master;
2071 for (
int d = 0; d < 3; ++d) {
2072 b.st_.lo[d] = b.box.lo[d];
2073 b.st_.hi[d] = b.box.hi[d];
2075 b.st_.cells = b.box.cells();
2081 for (
int k = 0; k < T.nj; ++k) {
2083 for (
int d = 0; d < 3; ++d)
2084 P.
c[k][d] = b.prevCentroid_[d];
2085 P.
valid[k] = b.hasPrev_ ? 1u : 0u;
2089 statsPend_ = std::move(
idx);
2095 for (std::size_t k = 0; k < statsPend_.size(); ++k) {
2096 VofBlock& b = blocks_[statsPend_[k]];
2098 b.st_.volume = q[0];
2101 for (
int d = 0; d < 3; ++d)
2102 b.st_.centroid[d] = b.st_.velocity[d] = 0.0;
2103 for (
int d = 0; d < 6; ++d)
2104 b.st_.moment[d] = 0.0;
2107 for (
int d = 0; d < 3; ++d) {
2108 b.st_.velocity[d] = q[4 + d];
2109 b.prevCentroid_[d] = q[1 + d];
2110 b.st_.centroid[d] = q[1 + d];
2114 for (
int d = 0; d < 6; ++d)
2115 b.st_.moment[d] = q[8 + d];
2123 if (statsPend_.empty())
2125 const std::size_t len =
kVofStats * statsPend_.size();
2126 Kokkos::deep_copy(Kokkos::subview(bRead1Host_, std::make_pair(std::size_t(0), len)),
2127 Kokkos::subview(bRead1_, std::make_pair(std::size_t(0), len)));
2137 std::vector<VofBlock*>
mb;
2138 for (
auto& b : blocks_)
2145 const std::size_t nb =
mb.size();
2146 std::vector<VofBlockJob>
jobs(nb);
2148 for (std::size_t k = 0; k < nb; ++k) {
2152 if (
static_cast<std::size_t
>(bStart_.extent(0)) < nb) {
2153 bStart_ =
LField(
"vof::block::batch_start", nb);
2154 bEnd_ =
LField(
"vof::block::batch_end", nb);
2158 const std::size_t off =
kVofStats * statsPend_.size();
2160 auto cfl = Kokkos::subview(bRead1_, std::make_pair(off, off + nb));
2165 Kokkos::deep_copy(Kokkos::subview(bRead1Host_, std::make_pair(std::size_t(0), off + nb)),
2166 Kokkos::subview(bRead1_, std::make_pair(std::size_t(0), off + nb)));
2168 for (std::size_t k = 0; k < nb; ++k)
2169 mb[k]->adv_.enforceCfl(bRead1Host_(off + k), dt);
2170 Kokkos::deep_copy(
SExec(), pk_, 0.0);
2178 for (
int s = 0; s < 3; ++s) {
2179 const int d =
perm[s];
2181 if (
static_cast<long>(bList_.extent(0)) <
region)
2182 bList_ =
LField(Kokkos::view_alloc(std::string(
"vof::block::batch_list"),
2183 Kokkos::WithoutInitializing),
2196 for (std::size_t k = 0; k < nb; ++k)
2197 mb[k]->adv_.noteBatchedStep();
2209 const std::size_t nb =
bs.size();
2220 std::vector<VofBlockJob>
jobs(nb);
2221 for (std::size_t k = 0; k < nb; ++k) {
2222 bs[k]->st_.debrisCells = 0;
2223 bs[k]->st_.debrisVolume = 0.0;
2226 if (
static_cast<std::size_t
>(dCap_.extent(0)) < nb) {
2227 for (
int q = 0; q < 6; ++q)
2228 dRange_[q] =
LField(
"vof::block::batch_debris_range", nb);
2229 dCap_ = Kokkos::View<int*, SMem>(
"vof::block::batch_debris_cap", nb);
2232 Kokkos::deep_copy(
SExec(), dCap_, 0);
2233 const LField sD = dRange_[0],
eD = dRange_[1],
sR = dRange_[2],
eR = dRange_[3],
2234 sA = dRange_[4], eA = dRange_[5];
2239 if (
static_cast<long>(f.extent(0)) <
region)
2240 f =
LField(Kokkos::view_alloc(std::string(name), Kokkos::WithoutInitializing),
region);
2243 ensure(listD_,
"vof::block::debris_list");
2247 ensure(listR_,
"vof::block::residue_list");
2250 ensure(listA_,
"vof::block::attached_list");
2252 vofBatchDebrisSums(T,
F, listD_, listR_, listA_,
sD,
eD,
sR,
eR, sA, eA, pk_,
kPk, 0);
2253 if (
static_cast<long>(debrisEx_.extent(0)) <
region)
2255 Kokkos::view_alloc(
"vof::block::debris_excess", Kokkos::WithoutInitializing),
region);
2256 vofBatchDebrisWrite(T,
F, listD_, listR_, listA_,
sD,
sR, sA, eA, pk_,
kPk, 0, debrisEx_,
2265 for (std::size_t k = 0; k <
bs.size(); ++k) {
2267 const double*
ho = &pkHost_(
kPk * k);
2268 const long nD =
static_cast<long>(
ho[
kDbND]);
2272 if (doDebris &&
nD > 0) {
2273 st.debrisCells =
nD;
2279 st.debrisCells =
nD;
2284 st.debrisUnresolved +=
nD;
2288 st.debrisCells =
nD;
2301 if (
static_cast<std::size_t
>(pk_.extent(0)) <
kPk * nb) {
2302 pk_ =
SField(
"vof::block::batch_packet",
kPk * nb);
2303 pkHost_ = Kokkos::create_mirror_view(pk_);
2312 std::vector<std::size_t>
idx;
2313 std::vector<VofBlock*>
mb;
2314 for (std::size_t k = 0; k < blocks_.size(); ++k)
2315 if (blocks_[k].mine_) {
2317 mb.push_back(&blocks_[k]);
2319 const std::size_t nb =
idx.size();
2322 std::vector<VofBlockJob>
jobs(nb);
2323 for (std::size_t k = 0; k < nb; ++k)
2329 Kokkos::deep_copy(pkHost_, pk_);
2332 for (std::size_t k = 0; k < nb; ++k) {
2333 if (
jobs[k].outside ==
nullptr)
2335 for (
int f = 0; f < 6; ++f)
2336 if (
jobs[k].bcOwn[f])
2337 mb[k]->adv_.addBcFaceVolume(f, pkHost_(
kPk * k +
kPkBcv + f));
2339 for (std::size_t k = 0; k < nb; ++k) {
2340 const double* hb = &pkHost_(
kPk * k +
kPkBox);
2341 const int lo[3] = {
static_cast<int>(hb[0]),
static_cast<int>(hb[1]),
static_cast<int>(hb[2])};
2342 const int hi[3] = {
static_cast<int>(hb[3]),
static_cast<int>(hb[4]),
static_cast<int>(hb[5])};
2347 for (
int d = 0; d < 3; ++d) {
2348 bb.lo[d] = lo[d] + b.box.lo[d];
2349 bb.hi[d] = hi[d] + 1 + b.box.lo[d];
2359 std::array<bool, 3> per_{
true,
true,
true};
2360 int rank_ = 0, size_ = 1;
2365 std::vector<VofBlock> blocks_;
2366 std::shared_ptr<VofBlockExchangeBase> exch_;
2367 std::map<std::array<int, 3>, std::vector<WyAdvector>> pool_;
2368 long poolHits_ = 0, poolMisses_ = 0;
2369 long lastReassigned_ = 0;
2370 VofCurvature::Stats curvStats_{};
2371 LField listD_, listA_, listR_;
2374 SField::host_mirror_type debrisOutHost_;
2377 SField::host_mirror_type bRead1Host_;
2378 std::vector<std::size_t> statsPend_;
2379 LField bList_, bStart_, bEnd_;
2381 Kokkos::View<int*, SMem> dCap_;
2382 LField cListG_, cListI_, cStartG_, cEndG_, cStartI_, cEndI_;
2383 Kokkos::View<PvMoments*, SMem> cCacheG_;
2385 LField::host_mirror_type cCntHost_;
2387 SField::host_mirror_type pkHost_;
flow — VoF rung V1: Weymouth & Yue (2010) directionally-split, exactly volume-conserving geometric ad...
flow — batched execution of the VoF block container's stages (C1, doc/vof_step_performance_design....
The container: a replicated table of blocks, the per-step orchestration (gather → advect → re-centre ...
void scatterSum(SField sLocal)
S = sum_k C_k of the CURRENT (replicated) table into sLocal's inner region (§5.6).
VofBlockJob makeJob(VofBlock &b, double dt) const
The raw-pointer job of one master block for the batched launches.
VofCurvature::Stats csfCurvatureStats() const
long assignMasters()
Apply plannedMasters(): allocate on the gaining ranks, migrate the colour, free on the losing ranks.
bool bubbleBox(const VofBlock &b, VofBox &out) const
Tight bounding box of C > 0 over the inner region, in GLOBAL indices.
void recentreTo(std::size_t idx, const VofBox &bb)
recentre for a bubble box already measured (the batched path's read #2).
void enableCsf(double sigmaValue)
Turn on the block CSF.
void recentre(std::size_t idx)
Move / resize the block when the bubble has spent its margin (or when the box has grown wastefully la...
double bubbleEps
The colour threshold that defines the BUBBLE EXTENT (and hence the box).
void applyDebrisLedger(std::vector< VofBlock * > &bs, bool doDebris)
The host half of the debris pass: the ledger of debrisPassBatchImpl, from the packet.
void setExchange(std::shared_ptr< VofBlockExchangeBase > x)
VofBlockExchangeBase * exchange() const
long lastReassigned() const
Blocks that changed master at the last advect() (rung W1 item a).
static constexpr double kDebrisMaxC
Debris predicate threshold (doc/vof_overlap_design.md §11): an interfacial cell is debris when NO cel...
void debrisBatched(std::vector< VofBlock * > &bs)
Stage 4 of §5.9, the debris/residue pass of debrisPassBatchImpl on every block of bs in batched launc...
int recentrePad
Cells of slack added beyond the margin when a block is re-centred, so a translating bubble re-allocat...
void flushStats()
Bring deferred statistics to the host now (a separate read; the step itself folds it into read #1).
void debrisPassBatchImpl(std::vector< VofBlock * > &bs)
bool csfEnabled
Rung W2: form the CSF face force on each block (its own curvature cascade, the V4 balanced -force rul...
bool batched
C1 (doc/vof_step_performance_design.md §4.6, §5.9): run each stage of the container step for ALL mast...
double interfaceArea(const VofBlock &b) const
Rung W1 item (d): the marker's interface area, in CELL units squared (the same convention volume uses...
std::vector< VofBlock > & blocks()
The block table.
void debrisPassBatch(std::vector< VofBlock * > &bs, bool deferLedger=false)
The pass on several master blocks at once.
bool timingOn
Instrument: fence-bounded wall time of the debris/residue pass (all blocks, all calls) while timingOn...
void debrisPass(VofBlock &b)
Debris + residue pass on one master block (doc/vof_overlap_design.md §5.2-5.3, predicate of §11,...
double areaEps
Wisp guard on the INTERFACE-AREA predicate (VofBlockStats::area), the same threshold and the same rea...
void computeCsf(SField fx, SField fy, SField fz)
Per-block curvature cascade + the CSF face force, summed into the caller's three face-force patches.
void clipToSeed(VofBlock &b)
A seeded block's colour is its OWN bubble's: the gather copies the whole INNER box (bubble extent + m...
std::array< bool, 3 > periodic() const
VofCurvature curvProto
The curvature cascade's TUNABLES for every block, copied into each block's own VofCurvature when it i...
double sigma
surface-tension coefficient of the block CSF (cell units)
std::vector< double > blockColourHost(std::size_t idx)
One block's INNER colour, copied to the host in x-fastest order over blocks_[idx]....
void setKappaMax(double km)
Set the curvature admissibility clip (VofCurvature::kappaMax, index units) on the prototype AND on ev...
void seedSphere(double cx, double cy, double cz, double r, int subLevels=4, int extraPad=0)
Seed a spherical bubble (centre and radius in CELL units, global).
void finishSeeding(SField cLocal)
Gather the colour of every seedBox-seeded block from cLocal, fill its ghosts and measure.
bool debrisCensus
Run the debris pass in advect() (only while the block CSF is on – a kinematic block run never pays fo...
void advectBatched(double dt)
Stages 1-3 of §5.9 for every master block: the Courant numbers in one launch and ONE host read (the t...
bool debrisRemove
Remove the debris and return its volume (§5.3).
void seedBoxWithColour(const VofBox &bb, const std::vector< double > &colour)
Seed a block whose INNER box is EXACTLY bb (not grown by the margin: bb is what blockColourHost was p...
void buildCsfForce(VofBlock &b)
The V4 balanced-force CSF on the BLOCK's faces — the same csfFaceCurvature + csfFaceForce pair Solver...
void seedBox(const VofBox &bb, int extraPad=0)
Seed one block on the global index box bb (the BUBBLE extent; the inner box is bb grown by margin),...
void recentreBatched(bool ranDebris)
Stage 5 of §5.9 on the batched path: every master block's bubble box in one launch,...
const std::vector< VofBlock > & blocks() const
void init(I3 gs, std::array< bool, 3 > per, int rank, int size, double h=1.0)
void masterCensus(std::vector< long > &masters, std::vector< long > &cells) const
Load-balance census of the CURRENT master assignment: masters[r] = blocks mastered by rank r,...
void ensureRead1(std::size_t len)
Grow read #1's packet to len doubles, KEEPING its contents (deferred statistics may wait in the front...
const VofMetric & metric() const
void measureBatched(double dt)
C2 (§5.10): measure() for every master block in one launch (vofBatchStats), with NO host read – the v...
double cellImbalance() const
max/mean of the per-rank block-cell load (1.0 = perfect). 0 blocks -> 1.0.
static VofCurvature blockCurvProto()
The block prototype: the class defaults, plus the admissibility clip ON at 1/Delta_min (doc/vof_overl...
void measure(VofBlock &b, double dt)
Volume, centroid, centroid velocity and the central second moments over the inner box.
void ensurePacket(std::size_t nb)
void applyStats(const double *hs)
Apply the deferred statistics in hs (host, kVofStats doubles per pending block) exactly as measure() ...
void scatter(SField cLocal)
Union the current colour into cLocal without advecting (used right after seeding).
void advect(double dt, SField cLocal)
One kinematic block step: gather the face velocity, advect every local block, re-centre,...
void setMetric(const VofMetric &g)
The anisotropic cell metric (Phase 3).
std::vector< VofBlockStats > statsAll()
Per-bubble census on the master (empty entries on a non-master).
int poolCapacity
advectors kept per extent; beyond it the retired one is freed
static constexpr double kDebrisMinAttached
Guard of the volume return (§5.3 step 3): a marker whose attached interface holds less than this much...
void fillBlockGhosts(VofBlock &b)
The block's own colour ghost policy — see the file header.
bool csfBatchEligible() const
The batched cascade applies where the container step is batched and every master block's cascade runs...
double outOfBoxSum(const VofBlock &b, const VofBox &nb) const
Sum of the block's colour over inner cells whose GLOBAL index falls outside nb.
std::vector< int > plannedMasters() const
The master each block WOULD get under assignMode, from the current block cell counts.
void computeCsfBatched()
Stage 6 of §5.9: the curvature cascade (compaction, planes, tiers 1-2, tier 3, the clip,...
double cflLimit
The Weymouth-Yue cap of every block advector.
double innerSum(const VofBlock &b) const
std::vector< double > blockKappaHost(std::size_t idx)
One marker's OWN curvature over its inner box, x-fastest (empty unless this rank masters it and the b...
VofMasterAssign assignMode
Rung W1 item (a): how masters are chosen, and how often the choice is revisited.
static constexpr int kDebrisBatch
void setWispEps(double eps)
Set wispEps on the set AND on every live advector (pooled ones take it on reuse).
void batchGhostFill(VofBlockTable T)
fillBlockGhosts of every job of T, pass by pass in the per-block pass order: zero,...
bool usePool
Rung W1 item (c): recycle the advectors a re-centring retires, keyed by the EXACT extent.
static constexpr int kPkBox
The per-job packet of read #2 (§5.9): the debris slots (VofDebrisSlot), the bubble box,...
bool batchEligible() const
The batched path applies to the plain advect() configuration: every master block an uncut all-fluid a...
double wispEps
The Weymouth-Yue wisp tolerance of every block advector (WyAdvector::wispEps); the solver sets it to ...
double outOfBoxSum(const VofBlock &b, const VofBox &nb, const bool wrap[3]) const
... where an axis flagged in wrap spans the whole periodic domain in nb and is copied with the wrap: ...
bool allowShrink
Let a block SHRINK when it has grown much larger than the bubble needs.
static constexpr int kPkBcv
static VofBlockTable batchTable(const std::vector< VofBlockJob > &jobs, std::size_t j0)
int masterOf(long id) const
Master assignment: round robin by block id (VOF_PLAN §10 W0; the weighted-ORB assignment is W1).
void allocateCsf(std::size_t idx)
std::size_t count() const
VofBox extended(int ghost) const
const VofCurvature & curvature() const
SField csfForce(int c) const
void deserializeAux(const double in[kAuxLen])
void serializeAux(double out[kAuxLen]) const
static constexpr int kAuxLen
The non-colour state a master carries between steps: the previous centroid (so a migrated block's rep...
VofBox seed_
The BUBBLE extent this block was seeded from (empty for a sphere seed, which paints its own colour).
VofCurvature & curvature()
Rung W2: the block's own curvature cascade and the CSF face force it forms.
const WyAdvector & advector() const
VofBox box
the INNER box, global
const VofBlockStats & stats() const
Curvature of the colour field on an extended (inner + ghost) block.
double interfaceEps
Wisp threshold on the interfacial predicate.
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...
double pureEps
Tolerance of the PURITY test that ends a height-function column (hfIsFull/hfIsEmpty),...
double kappaMax
Curvature admissibility clip (doc/vof_overlap_design.md §5.1, §11): after the cascade,...
VofMetric metric
The anisotropic cell metric (Phase 3).
Weymouth-Yue split advection of a colour field on an extended (inner + ghost) block.
static double wallSeconds()
flow — VoF rung V2a (WO-J): the bridge between the solver's G = 2 block and the colour field's own g ...
flow — VoF rung V3 (WO-O): the view-level driver for the curvature cascade.
flow — portable (Kokkos) MAC stencil operators: Red-Black Gauss-Seidel smoothers + divergence.
long vofJobGrown1(const VofBlockJob &J)
The inner region grown by one cell per side: WyAdvector::reconstructImpl's range.
void vofCurvCompact(const VofCurvTable &T, bool grown, LField list, LField start, LField end)
VofCurvature::compact: the interfacial cells of the grown (grown) or inner region,...
bool vofDebrisCell(const SF &c, long i, long sy, long sz, double ieps, double cfull)
The debris predicate of doc/vof_overlap_design.md §11 on cell i: interfacial, and NO cell of the mark...
void vofBatchFlux(const VofBlockTable &T, int d)
WyAdvector::computeFluxesImpl (uncut): one flux per d-face, wyFaceFlux (or, for a job carrying the WO...
Kokkos::View< long *, SMem > LField
void vofCurvListPasses(const VofCurvTable &T, bool anyClip, LField list, LField start, LField end, LField cnt)
The whole cascade list stage of a chunk: tiers 1-2, tier 3, the clip (only if some job clips),...
void vofBatchCfl(const VofBlockTable &T, SField out)
One team per job: WyAdvector::maxCourant or maxCourantInterface (per the job's interfaceCfl),...
long vofSetOffsets(VofBlockTable &T, F size)
Fill T.off from a per-job range size.
void vofBatchClamp(const VofBlockTable &T, I3 gs, bool px, bool py, bool pz)
Pass 3 (colour_field.hpp::clampFill, skip = 0): the part of each extended block outside a NON-periodi...
void vofBatchPeriodic(const VofBlockTable &T, int a)
Pass 2 on axis a (colour_field.hpp::periodicFill, one axis): only the jobs whose block spans the peri...
constexpr int kWySweepPerm[6][3]
The six permutations of (x, y, z), cycled by step index so no direction is systematically favoured (p...
void vofBatchGhostZero(const VofBlockTable &T)
Pass 1: every ghost cell of every job -> 0.
void vofAssignOrb(const std::vector< long > &w, int size, std::vector< int > &master)
Weighted ORB over a 1-D block space: the blocks are laid out as a 1-D "grid" of N cells in block-id o...
constexpr int kVofStats
Doubles per job in the statistics packet: volume, centroid[3], velocity[3], area, moment[6].
constexpr int kVofBlockBatch
Jobs per launch; a container with more master blocks runs each stage in chunks.
@ kDbActed
1 when the return ran (pending and VA >= kDebrisMinAttached and W > 0)
@ kDbSumR
sum_R C in list order (signed residue)
@ kDbPending
1 when there was something to remove (removeD or nR > 0)
@ kDbSumD
sum_D C in list order (the removed / census debris volume)
@ kDbDV
the volume to return (D if removing, then R); replaced by lost once acted
void vofBatchDebrisSums(const VofBlockTable &T, VofDebrisFlags F, LField listD, LField listR, LField listA, LField sD0, LField eD0, LField sR0, LField eR0, LField sA0, LField eA0, SField out, int stride, int slot0)
Step 2 of the pass: one thread per job, every sum in list order (the per-block kernel's single thread...
long vofJobFaces(const VofBlockJob &J, int d)
A sweep's face range of one job: the inner region with one extra layer on the low side of d.
void vofCurvHfReset(const VofCurvTable &T)
heightPass (worklist mode), part 1: every inner cell to kappa = 0, branch = kCurvNone.
long vofJobInner(const VofBlockJob &J)
void vofCurvPlanesList(const VofCurvTable &T, LField list, LField start, LField end)
Part 2: wyReconstructCell over the grown interfacial list (upper bound: the grown region),...
void vofBatchMark(const VofBlockTable &T, int kind, double ieps, double cfull, double weps, LField list, LField start, LField end)
One mark scan over the concatenated inner regions (T.off = vofJobInner), in each block's inner-box in...
void vofCsfForceBatch(const VofCurvTable &T, double sig, double w0, double w1, double w2)
VofBlockSet::buildCsfForce for every job, the three components per cell: the V4 balanced-force CSF at...
void vofBatchBox(const VofBlockTable &T, double eps, SField out, int stride, int slot0)
One team per job: the tight box of |C| > eps over the inner region, in the block's LOCAL inner frame ...
VofBox vofClampBox(VofBox b, I3 gs, const std::array< bool, 3 > &per)
Clamp a block box so it is representable: at most the whole axis on a periodic axis,...
void vofBatchPlic(const VofBlockTable &T, LField list, LField start, LField end)
wyReconstructCell over each job's compacted list: launched over the upper bound (the job's region),...
void vofAssignLpt(const std::vector< long > &w, int size, std::vector< int > &master)
Longest-processing-time greedy (Graham 1969): heaviest block first onto the currently least-loaded ra...
void vofCurvPlanesZero(const VofCurvTable &T)
reconstructPlanes (worklist mode), part 1: zero the four plane fields and clear the slot map (design ...
void vofBatchDebrisLost(const VofBlockTable &T, LField sA0, LField eA0, SField out, int stride, int slot0, SField ex, Kokkos::View< int *, SMem > capf)
lost: the capped excess of an acted job summed in list order on one thread (only where a cap fired,...
int vofAxisRuns(int a, int b, int gs, bool periodic, VofRun out[5], int outside=0)
Decompose [a, b) on one axis into global runs.
void vofBatchBcLedger(const VofBlockTable &T, int d, SField out, int stride, int slot0)
The WO-R boundary ledger (WyAdvector::accumulateBcFaceVolume) of the jobs carrying the mask: one thre...
long vofJobLen(const VofBlockJob &J)
VofMasterAssign
How a block's MASTER rank is chosen.
@ Lpt
longest-processing-time greedy on the block cell counts (rung W1 default)
@ RoundRobin
id % size — rung W0's assignment; blind to block SIZE
@ WeightedOrb
weighted ORB over a 1-D "block space" (core::decomp::BlockDecomposer<1>)
bool vofAttachedCell(const SF &c, long i, long sy, long sz, double ieps, double cfull)
The ATTACHED predicate (§5.3 step 2): interfacial with a cell at or above cfull within two.
void clampFill(SField f, I3 e, int g, I3 o, I3 gs, bool px, bool py, bool pz, int skip=0)
Zero-gradient (Neumann) fill of the ghost cells that lie OUTSIDE the global domain on a non-periodic ...
void vofBatchWorklist(const VofBlockTable &T, LField list, LField start, LField end)
WyAdvector::reconstructImpl's worklist scan, over the concatenated grown-by-one regions (T....
void periodicFill(SField f, I3 e, int g, bool px, bool py, bool pz)
Periodic wrap of a single-block extended field at ghost width g (the single-rank ghost path; under MP...
long vofCurvInner(const VofCurvJob &J)
void vofBatchStats(const VofBlockTable &T, const VofStatsPrev &P, double hh, double aeps, double dt, SField out, long off)
VofBlockSet::measure for every job in one launch, one team per job: the first moments,...
double csfFaceForce(double sigma, double kf, double dC, double hGrad)
The balanced-force CSF contribution to the momentum RHS at one staggered velocity unknown,...
void vofBatchFreeze(const VofBlockTable &T)
WyAdvector::freezeDilationFlag over every job's extended block.
void vofBatchUpdate(const VofBlockTable &T, int d)
WyAdvector::applySweepImpl (uncut) over the inner regions (T.off = vofJobInner); the expression verba...
bool vofResidueCell(double ci, double weps)
The residue predicate (§13): 0 < |C| <= wispEps.
bool csfFaceCurvature(double k0, double b0, double k1, double b1, double &kf)
Face curvature for the balanced-force CSF, from the two cells sharing the face.
long vofCurvGrown(const VofCurvJob &J)
The inner region grown by kPvHalf: every cell VofCurvature::reconstructPlanes writes.
void vofBatchDebrisWrite(const VofBlockTable &T, VofDebrisFlags F, LField listD, LField listR, LField listA, LField sD0, LField sR0, LField sA0, LField eA0, SField out, int stride, int slot0, SField ex, Kokkos::View< int *, SMem > capf)
Step 4, the act: removed cells -> 0, attached C += dV C(1-C)/W capped at 1 (the per-block write kerne...
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.
Gather / scatter / table replication.
virtual void scatterForceSum(std::vector< VofBlock > &blocks, SField fx, SField fy, SField fz)=0
SUM every master block's CSF face force into the caller's three face-force patches (TBFsolver's UNPAC...
virtual void migrateColour(std::vector< VofBlock > &blocks, const std::vector< int > &oldMaster)=0
Move a block's state from oldMaster[i] to blocks[i].master after a re-assignment.
virtual void scatterColourSum(std::vector< VofBlock > &, SField)
SUM every master block's inner colour into the caller's colour patch: S = sum_k C_k,...
virtual void scatterColourMax(std::vector< VofBlock > &blocks, SField cLocal)=0
UNION the masters' inner colour into the caller's local colour patch: zero the inner region,...
virtual ~VofBlockExchangeBase()=default
virtual void syncTable(std::vector< VofBlock > &blocks)=0
Replicate the (possibly re-centred) boxes so every rank's table agrees again.
virtual long scatterBytes() const
virtual void gatherColour(std::vector< VofBlock > &blocks, SField cLocal)=0
Fill every MASTER block's INNER colour from the caller's colour patch (the mirror of scatterColourMax...
virtual long gatherBytes() const
Bytes moved by the last gather / scatter on THIS rank (sent + received).
virtual void gatherFaceVel(std::vector< VofBlock > &blocks, int ghost)=0
Fill every MASTER block's faceU/V/W over its EXTENDED box, from the face velocity the ranks own.
One master block's share of a batched container launch.
One block's share of the batched sums (raw pointers: the table rides in the functor).
DebrisJob job[kDebrisBatch]
Per-block Lagrangian census, computed on the master (VOF_PLAN §10 W1 outputs, available at W0).
long debrisUnresolved
cumulative debris cells left for want of an attached interface
double debrisReturned
cumulative sum of debrisVolume over the removals
double area
Interface area of the marker, in cell-size units squared: the sum of the PLIC polygon areas over the ...
double moment[6]
Central second moments Σ C (x−x̄)(y−ȳ)/Σ C, order xx, yy, zz, xy, xz, yz (the deformation).
long fullAxis
Cumulative re-centrings whose box had to span a whole periodic axis (vof_overlap_design §12....
long cells
inner-box cell count (the master's work)
double debrisLost
cumulative volume the return could not place (C capped at 1)
double debrisVolume
their colour, summed in list order (cell volumes), this step
double volume
Σ C over the inner box, in cell volumes.
double residueReturned
Cumulative SIGNED volume of the sub-wispEps residue cleared after each advection and returned to the ...
double discarded
Cumulative colour dropped by re-centrings, i.e.
bool recentred
the box moved at the end of the last step
double velocity[3]
d(centroid)/dt of the last advect
long debrisCells
Marker DEBRIS (doc/vof_overlap_design.md §5.2 as amended by §11): an interfacial cell (vofIsInterface...
The job table of one launch, by value.
VofBlockJob job[kVofBlockBatch]
Half-open global cell box [lo, hi).
bool operator!=(const VofBox &o) const
bool contains(const VofBox &o) const
static VofBox intersect(const VofBox &a, const VofBox &b)
Intersection in global coordinates (may be empty).
bool operator==(const VofBox &o) const
static VofBox grown(const VofBox &b, int m)
One block's share of a batched tier-3 launch (VofCurvature::fallbackBatch): its fields,...
One master block's cascade (VofCurvature, worklist mode) for the batched launches: its planes,...
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
Flags of the pass, uniform over the blocks.
A raw-pointer field accessor with the View<double*> call syntax, for kernels that walk SEVERAL blocks...
One contiguous run of GLOBAL indices covered by a block's index range on one axis,...
int loc0
block-local index (0-based within [a, b)) the run starts at
int g0
first GLOBAL index of the run
What measure() reads besides the colour: each job's previous centroid and whether it is valid (the ho...
double c[kVofBlockBatch][3]
unsigned char valid[kVofBlockBatch]
flow — VoF rung V4 (WO-P): balanced-force continuum surface force + the capillary Δt.
static constexpr double F
static double perm(const std::vector< double > &gsdf, const BlockDecomposer< 3 > &dec, int rank, bool amg, int levels, MPI_Comm comm)
static std::vector< double > colour(int m)