72#ifndef PECLET_FLOW_VOF_ADVECT_WY_HPP
73#define PECLET_FLOW_VOF_ADVECT_WY_HPP
78#include <Kokkos_Core.hpp>
89using UCField = Kokkos::View<unsigned char*, SMem>;
90using LField = Kokkos::View<long*, SMem>;
96 return c > 0.0 && c < 1.0;
112 return c > eps && c < 1.0 - eps;
124 const double d = a - b;
125 return (d > eps) || (d < -eps);
130inline constexpr int kWySweepPerm[6][3] = {{0, 1, 2}, {1, 2, 0}, {2, 0, 1},
131 {0, 2, 1}, {2, 1, 0}, {1, 0, 2}};
142 for (
int kk = -1;
kk <= 1; ++
kk)
143 for (
int jj = -1;
jj <= 1; ++
jj)
144 for (
int ii = -1;
ii <= 1; ++
ii)
151 alpha(i) = plicAlpha(m[0], m[1], m[2], c(i));
158 const SF& mx,
const SF& my,
const SF& mz,
const SF& alpha,
161 const double cd = c(p);
166 const long q = p +
sd;
167 const double cd = c(q),
aa = -a;
187template <
class SF,
class MF>
189 const SF& mx,
const SF& my,
const SF& mz,
190 const SF& alpha,
const MF& outside,
double eps = 0.0) {
192 const double cd = c(p);
193 return (
wyIsMixed(cd, eps) && !outside(p))
194 ?
plicSlabVolume(mx(p), my(p), mz(p), alpha(p), dir, 1.0 - a, 1.0)
198 const long q = p +
sd;
199 const double cd = c(q),
aa = -a;
200 return -((
wyIsMixed(cd, eps) && !outside(q))
244 throw std::invalid_argument(
"peclet::flow::vof::WyAdvector: ghost width must be >= 2");
245 if (nx < 1 || ny < 1 || nz < 1)
246 throw std::invalid_argument(
"peclet::flow::vof::WyAdvector: empty block");
251 len_ =
static_cast<long>(e_.
x) * e_.
y * e_.
z;
252 c_ =
SField(
"vof::C", len_);
253 mx_ =
SField(
"vof::mx", len_);
254 my_ =
SField(
"vof::my", len_);
255 mz_ =
SField(
"vof::mz", len_);
256 alpha_ =
SField(
"vof::alpha", len_);
257 flux_ =
SField(
"vof::flux", len_);
258 uf_ =
SField(
"vof::uf", len_);
259 vf_ =
SField(
"vof::vf", len_);
260 wf_ =
SField(
"vof::wf", len_);
261 cc_ =
UCField(
"vof::cc", len_);
262 listCap_ =
static_cast<long>(n_.
x + 2) * (n_.
y + 2) * (n_.
z + 2);
263 list_ =
LField(
"vof::worklist", listCap_);
270 double h()
const {
return h_; }
271 long size()
const {
return len_; }
273 long index(
int x,
int y,
int z)
const {
return L3(x + g_, y + g_, z + g_, e_); }
303 throw std::runtime_error(
"peclet::flow::vof::WyAdvector: enableGeometry before init()");
305 for (
int d = 0; d < 3; ++d)
306 of_[d] =
SField(
"vof::openness", len_);
307 eps_ =
SField(
"vof::eps", len_);
308 kindD_ =
SField(
"vof::kindD", len_);
309 kind_ =
UCField(
"vof::kind", len_);
310 fill_ =
UCField(
"vof::fillState", len_);
311 mark_ =
UCField(
"vof::fillMark", len_);
344 const long sx = 1, sy = e_.
x, sz =
static_cast<long>(e_.
x) * e_.
y;
345 SField ox = of_[0], oy = of_[1], oz = of_[2],
ep = eps_,
kd = kindD_;
346 Kokkos::parallel_for(
349 const long i =
L3(x, y, z, e);
350 if (x == 0 || y == 0 || z == 0) {
354 const double oLo[3] = {ox(i -
sx), oy(i - sy), oz(i - sz)};
355 const double oHi[3] = {ox(i), oy(i), oz(i)};
366 Kokkos::parallel_for(
367 "vof::wy::finalize_geom", Kokkos::RangePolicy<SExec>(
SExec(), 0, len_),
389 throw std::runtime_error(
"peclet::flow::vof::WyAdvector: enableWetting needs geometry");
391 sdfB_ =
SField(
"vof::wallSdf", len_);
392 thetaB_ =
SField(
"vof::contactAngle", len_);
393 for (
int d = 0; d < 3; ++d)
394 mfl_[d] =
SField(
"vof::fluidNormal", len_);
395 appB_ =
SField(
"vof::apparentAngle", len_);
396 wetB_ =
UCField(
"vof::wetBranch", len_);
430 const I3 e = e_, n = n_;
432 SField c = c_, mx = mfl_[0], my = mfl_[1], mz = mfl_[2];
434 Kokkos::parallel_for(
435 "vof::wy::wetting_normals",
438 const long i =
L3(x, y, z, e);
445 unsigned char fl[27];
446 for (
int kz = -1; kz <= 1; ++kz)
447 for (
int ky = -1;
ky <= 1; ++
ky)
448 for (
int kx = -1;
kx <= 1; ++
kx) {
450 const long j =
L3(x +
kx, y +
ky, z + kz, e);
481 const I3 e = e_, n = n_;
490 const unsigned char bb =
static_cast<unsigned char>(b);
491 Kokkos::parallel_reduce(
492 "vof::wy::wet_census",
pol,
494 const long i =
L3(x, y, z, e);
503 Kokkos::parallel_reduce(
504 "vof::wy::wet_apparent",
pol,
506 const long i =
L3(x, y, z, e);
515 meanAppDeg = cnt ? sum /
static_cast<double>(cnt) * (180.0 / 3.14159265358979323846) : 0.0;
525 UCField fs = fill_, mk = mark_,
kk = kind_;
526 Kokkos::parallel_for(
527 "vof::wy::fill_reset", Kokkos::RangePolicy<SExec>(
SExec(), 0, len_),
535 Kokkos::parallel_for(
536 "vof::wy::wet_reset", Kokkos::RangePolicy<SExec>(
SExec(), 0, len_),
539 for (
int k = 1; k <= 3; ++k) {
540 if (hasWet_ && k == 1)
566 bool hasOutsideMask()
const {
return outside_.extent(0) ==
static_cast<std::size_t
>(len_); }
579 for (
int f = 0; f < 6; ++f)
614 return std::chrono::duration<double>(std::chrono::steady_clock::now().
time_since_epoch())
623 void addT_(
double& acc,
double t0) {
634 const double t0 =
tick_();
676 const double dth = dt / h_;
685 clippedVolume_ = 0.0;
686 clippedSigned_ = 0.0;
692 for (
int s = 0; s < 3; ++s) {
693 const int d =
perm[s];
722 SField planeM(
int d)
const {
return d == 0 ? mx_ : (d == 1 ? my_ : mz_); }
741 const double dth = dt / h_;
759 std::snprintf(
msg,
sizeof(
msg),
760 "peclet::flow::vof::WyAdvector: CFL = max|uf| dt/h = %.6g exceeds the "
761 "Weymouth-Yue boundedness cap %.6g (dt = %.6g, h = %.6g) - reduce dt",
763 throw std::runtime_error(
msg);
779 const I3 e = e_, n = n_;
783 double sum = 0.0,
mn = 0.0, mx = 0.0;
784 long mixed = 0, wisps = 0;
785 MD pol(
SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z});
786 Kokkos::parallel_reduce(
787 "vof::wy::diag_sum",
pol,
788 KOKKOS_LAMBDA(
int x,
int y,
int z,
double& acc) { acc += c(
L3(x, y, z, e)); }, sum);
789 Kokkos::parallel_reduce(
790 "vof::wy::diag_min",
pol,
792 acc = Kokkos::fmin(acc, c(
L3(x, y, z, e)));
794 Kokkos::Min<double>(
mn));
795 Kokkos::parallel_reduce(
796 "vof::wy::diag_max",
pol,
798 acc = Kokkos::fmax(acc, c(
L3(x, y, z, e)));
800 Kokkos::Max<double>(mx));
801 Kokkos::parallel_reduce(
802 "vof::wy::diag_counts",
pol,
804 const double v = c(
L3(x, y, z, e));
827 const I3 e = e_, n = n_;
829 SField u = uf_, v = vf_, w = wf_;
831 Kokkos::parallel_reduce(
834 const long i =
L3(x, y, z, e);
835 acc = Kokkos::fmax(acc, Kokkos::fabs(u(i)));
836 acc = Kokkos::fmax(acc, Kokkos::fabs(v(i)));
837 acc = Kokkos::fmax(acc, Kokkos::fabs(w(i)));
839 Kokkos::Max<double>(m));
867 const I3 e = e_, n = n_;
869 const long sx = 1, sy = e_.
x, sz =
static_cast<long>(e_.
x) * e_.
y;
870 SField c = c_, u = uf_, v = vf_, w = wf_;
873 Kokkos::parallel_reduce(
876 const long i =
L3(x, y, z, e);
877 const double ci = c(i);
885 acc = Kokkos::fmax(acc, Kokkos::fabs(u(i)));
886 acc = Kokkos::fmax(acc, Kokkos::fabs(u(i -
sx)));
887 acc = Kokkos::fmax(acc, Kokkos::fabs(v(i)));
888 acc = Kokkos::fmax(acc, Kokkos::fabs(v(i - sy)));
889 acc = Kokkos::fmax(acc, Kokkos::fabs(w(i)));
890 acc = Kokkos::fmax(acc, Kokkos::fabs(w(i - sz)));
892 Kokkos::Max<double>(m));
896 return Kokkos::fmax(m, 0.0) * dth;
909 const I3 e = e_, n = n_;
911 const long sy = e_.
x, sz =
static_cast<long>(e_.
x) * e_.
y;
912 SField u = uf_, v = vf_, w = wf_;
914 Kokkos::parallel_reduce(
917 const long i =
L3(x, y, z, e);
918 const double d = (u(i) - u(i - 1)) + (v(i) - v(i - sy)) + (w(i) - w(i - sz));
919 acc = Kokkos::fmax(acc, Kokkos::fabs(d) * dth);
921 Kokkos::Max<double>(m));
933 throw std::runtime_error(
"peclet::flow::vof::WyAdvector: the `exchange` hook is not set");
940 Kokkos::parallel_for(
941 "vof::wy::freeze", Kokkos::RangePolicy<SExec>(
SExec(), 0, len_),
959 const int rx = n_.
x + 2,
ry = n_.
y + 2,
rz = n_.
z + 2;
961 const long sy = e_.
x, sz =
static_cast<long>(e_.
x) * e_.
y;
962 SField c = c_, mx = mx_, my = my_, mz = mz_, al = alpha_;
968 Kokkos::parallel_scan(
969 "vof::wy::worklist", Kokkos::RangePolicy<SExec>(
SExec(), 0,
region),
971 const int ix =
static_cast<int>(r %
rx);
972 const int iy =
static_cast<int>((r /
rx) %
ry);
973 const int iz =
static_cast<int>(r / (
static_cast<long>(
rx) *
ry));
974 const long i =
L3(g - 1 +
ix, g - 1 +
iy, g - 1 +
iz, e);
984 Kokkos::parallel_for(
985 "vof::wy::plic", Kokkos::RangePolicy<SExec>(
SExec(), 0, cnt),
988 Kokkos::parallel_for(
989 "vof::wy::plic_dense", Kokkos::RangePolicy<SExec>(
SExec(), 0,
region),
991 const int ix =
static_cast<int>(r %
rx);
992 const int iy =
static_cast<int>((r /
rx) %
ry);
993 const int iz =
static_cast<int>(r / (
static_cast<long>(
rx) *
ry));
994 const long i =
L3(g - 1 +
ix, g - 1 +
iy, g - 1 +
iz, e);
1020 d == 0 ? 1 : (d == 1 ?
static_cast<long>(e_.
x) :
static_cast<long>(e_.
x) * e_.
y);
1023 int lo[3] = {g, g, g};
1024 const int hi[3] = {g + n_.
x, g + n_.
y, g + n_.
z};
1026 SField c = c_, mx = mx_, my = my_, mz = mz_, al = alpha_, fl = flux_, u =
faceVel(d);
1030 Kokkos::parallel_for(
1034 const long p =
L3(x, y, z, e);
1035 fl(p) =
wyFaceFluxBc(u(p) * dth, p,
sd, d, c, mx, my, mz, al,
ob, weps);
1040 Kokkos::parallel_for(
1043 const long p =
L3(x, y, z, e);
1044 fl(p) =
wyFaceFlux(u(p) * dth, p,
sd, d, c, mx, my, mz, al, weps);
1054 const I3 e = e_, n = n_;
1056 const int b = (d + 1) % 3, c2 = (d + 2) % 3;
1057 const long st[3] = {1, e_.
x,
static_cast<long>(e_.
x) * e_.
y};
1058 const long sd = st[d],
sb = st[b], sc = st[c2];
1059 const int nb = (b == 0) ? n.x : (b == 1) ? n.y : n.z;
1060 const int nc = (c2 == 0) ? n.x : (c2 == 1) ? n.y : n.z;
1061 const int nd = (d == 0) ? n.x : (d == 1) ? n.y : n.z;
1063 for (
int s = 0; s < 2; ++s) {
1064 if (!bcOwn_[2 * d + s])
1066 const int fa = (s == 0) ? (g - 1) : (g +
nd - 1);
1068 Kokkos::parallel_reduce(
1071 r += fl(
static_cast<long>(p0) *
sb +
static_cast<long>(
p1) * sc +
1072 static_cast<long>(
fa) *
sd);
1076 if (bcOwn_[2 * d + s])
1077 bcVol_[2 * d + s] += (s == 0) ? acc : -acc;
1093 const I3 e = e_, n = n_;
1096 d == 0 ? 1 : (d == 1 ?
static_cast<long>(e_.
x) :
static_cast<long>(e_.
x) * e_.
y);
1099 Kokkos::parallel_for(
1102 const long i =
L3(x, y, z, e);
1105 const double aP = u(i) * dth,
aM = u(i -
sd) * dth;
1106 const double dil = cc(i) ? (
aP -
aM) : 0.0;
1107 c(i) = c(i) + (fl(i -
sd) - fl(i)) +
dil;
1122 d == 0 ? 1 : (d == 1 ?
static_cast<long>(e_.
x) :
static_cast<long>(e_.
x) * e_.
y);
1123 int lo[3] = {g, g, g};
1124 const int hi[3] = {g + n_.
x, g + n_.
y, g + n_.
z};
1126 SField c = c_, mx = mx_, my = my_, mz = mz_, al = alpha_, fl = flux_, u =
faceVel(d),
1127 o = of_[d],
ep = eps_;
1142 Kokkos::parallel_reduce(
1145 const long p =
L3(x, y, z, e);
1146 const double op = o(p);
1151 const double a = u(p) * dth;
1153 vofCutFlux(op, bc ?
wyFaceFluxBc(a, p,
sd, d, c, mx, my, mz, al,
ob, weps)
1155 if (!
clamp || a == 0.0) {
1159 const long q = a > 0.0 ? p : p +
sd;
1160 const double cq = c(q);
1167 const double aa = a > 0.0 ? a : -a;
1168 const double mag = raw > 0.0 ? raw : -raw;
1172 fl(p) = a > 0.0 ?
cl : -
cl;
1184 const I3 e = e_, n = n_;
1187 d == 0 ? 1 : (d == 1 ?
static_cast<long>(e_.
x) :
static_cast<long>(e_.
x) * e_.
y);
1190 Kokkos::parallel_for(
1193 const long i =
L3(x, y, z, e);
1203 const double aP = o(i) * (u(i) * dth),
aM = o(i -
sd) * (u(i -
sd) * dth);
1204 const double dil = cc(i) ? (
aP -
aM) : 0.0;
1205 c(i) = c(i) + ((fl(i -
sd) - fl(i)) +
dil) /
epsEff;
1218 const I3 e = e_, n = n_;
1222 double acc = 0.0, sgn = 0.0;
1223 Kokkos::parallel_reduce(
1226 const long i =
L3(x, y, z, e);
1232 const double cv = c(i);
1233 if (
cv >= 0.0 &&
cv <= 1.0)
1237 a += Kokkos::fabs(
moved);
1242 clippedVolume_ += acc;
1243 clippedSigned_ += sgn;
1250 const I3 e = e_, n = n_;
1252 const long sx = 1, sy = e_.
x, sz =
static_cast<long>(e_.
x) * e_.
y;
1253 SField c = c_, u = uf_, v = vf_, w = wf_, ox = of_[0], oy = of_[1], oz = of_[2],
ep = eps_;
1257 Kokkos::parallel_reduce(
1258 "vof::wy::cfl_interface_cut",
1261 const long i =
L3(x, y, z, e);
1264 const double ci = c(i);
1272 const double ei =
ep(i);
1276 acc = Kokkos::fmax(acc,
vofCutCourant(oy(i - sy), v(i - sy) * dth,
ei));
1278 acc = Kokkos::fmax(acc,
vofCutCourant(oz(i - sz), w(i - sz) * dth,
ei));
1280 Kokkos::Max<double>(m));
1282 return Kokkos::fmax(m, 0.0);
1289 const I3 e = e_, n = n_;
1290 const int g = g_,
maxDepth = 3 - k;
1291 const long sx = 1, sy = e_.
x, sz =
static_cast<long>(e_.
x) * e_.
y;
1293 UCField fs = fill_, mk = mark_;
1294 Kokkos::parallel_for(
1299 const long i =
L3(x, y, z, e);
1302 const long nb[6] = {i -
sx, i +
sx, i - sy, i + sy, i - sz, i + sz};
1305 for (
int q = 0; q < 6; ++q)
1316 Kokkos::parallel_for(
1317 "vof::wy::band_fill_commit", Kokkos::RangePolicy<SExec>(
SExec(), 0, len_),
1332 const I3 e = e_, n = n_;
1334 const long sx = 1, sy = e_.
x, sz =
static_cast<long>(e_.
x) * e_.
y;
1335 SField c = c_, sdf = sdfB_, th = thetaB_, ap = appB_;
1336 SField mx = mfl_[0], my = mfl_[1], mz = mfl_[2];
1337 UCField fs = fill_, mk = mark_,
kk = kind_,
wb = wetB_;
1341 Kokkos::parallel_for(
1346 const long i =
L3(x, y, z, e);
1353 double nw[3] = {0.5 * (sdf(i +
sx) - sdf(i -
sx)), 0.5 * (sdf(i + sy) - sdf(i - sy)),
1354 0.5 * (sdf(i + sz) - sdf(i - sz))};
1361 const double nn = Kokkos::sqrt(
wk[0] *
wk[0] +
wk[1] *
wk[1] +
wk[2] *
wk[2]);
1366 for (
int step = 1; step <= 4; ++step) {
1367 const int fx = x +
static_cast<int>(Kokkos::round(step *
wk[0]));
1368 const int fy = y +
static_cast<int>(Kokkos::round(step *
wk[1]));
1369 const int fz = z +
static_cast<int>(Kokkos::round(step *
wk[2]));
1375 const double cf = c(
fi);
1384 fx >= 1 &&
fy >= 1 &&
fz >= 1 &&
fx + 1 < e.x &&
fy + 1 < e.y &&
fz + 1 < e.z;
1386 for (
int kz = -1; kz <= 1; ++kz)
1387 for (
int ky = -1;
ky <= 1; ++
ky)
1388 for (
int kx = -1;
kx <= 1; ++
kx) {
1392 const double cg = c(
gi);
1395 const double mg[3] = {mx(
gi), my(
gi), mz(
gi)};
1396 if (Kokkos::fabs(mg[0]) + Kokkos::fabs(mg[1]) + Kokkos::fabs(mg[2]) <= 0.0)
1399 const double t2 = th(i);
1402 const int ds2[3] = {x - (
fx +
kx), y - (
fy +
ky), z - (
fz + kz)};
1411 const double mf[3] = {mx(
fi), my(
fi), mz(
fi)};
1412 if (Kokkos::fabs(
mf[0]) + Kokkos::fabs(
mf[1]) + Kokkos::fabs(
mf[2]) <= 0.0)
1415 const double t0 = th(i);
1418 const int ds[3] = {x -
fx, y -
fy, z -
fz};
1422 wb(i) =
static_cast<unsigned char>(br);
1427 const long nb[6] = {i -
sx, i +
sx, i - sy, i + sy, i - sz, i + sz};
1430 for (
int q = 0; q < 6; ++q)
1442 Kokkos::parallel_for(
1443 "vof::wy::band_fill_theta_commit", Kokkos::RangePolicy<SExec>(
SExec(), 0, len_),
1455 const I3 e = e_, n = n_;
1460 MD pol(
SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z});
1461 double vol = 0.0, raw = 0.0, solidC = 0.0;
1463 Kokkos::parallel_reduce(
1464 "vof::wy::cut_sums",
pol,
1465 KOKKOS_LAMBDA(
int x,
int y,
int z,
double& v,
double& r,
double& sc) {
1466 const long i =
L3(x, y, z, e);
1475 Kokkos::parallel_reduce(
1476 "vof::wy::cut_counts",
pol,
1478 const long i =
L3(x, y, z, e);
1489 Kokkos::parallel_reduce(
1490 "vof::wy::cut_minfluid",
pol,
1492 const long i =
L3(x, y, z, e);
1495 a = Kokkos::fmin(a, c(i));
1497 Kokkos::Min<double>(
mn));
1498 Kokkos::parallel_reduce(
1499 "vof::wy::cut_maxfluid",
pol,
1501 const long i =
L3(x, y, z, e);
1504 a = Kokkos::fmax(a, c(i));
1506 Kokkos::Max<double>(mx));
1510 dg.solidFillSum = solidC;
1514 dg.maxCFluid = mx < -1
e299 ? 0.0 : mx;
1515 dg.clippedVolume = clippedVolume_;
1516 dg.clippedSigned = clippedSigned_;
1517 dg.clampedFaces = clampedFaces_;
1521 I3 n_{0, 0, 0}, e_{0, 0, 0};
1524 long len_ = 0, listCap_ = 0;
1525 SField c_, mx_, my_, mz_, alpha_, flux_, uf_, vf_, wf_;
1527 double bcVol_[6] = {0, 0, 0, 0, 0, 0};
1528 unsigned char bcOwn_[6] = {0, 0, 0, 0, 0, 0};
1530 long mixedCount_ = 0, steps_ = 0;
1531 double lastCfl_ = 0.0;
1533 bool hasGeom_ =
false;
1534 SField of_[3], eps_, kindD_;
1537 bool hasWet_ =
false;
1538 SField sdfB_, thetaB_, mfl_[3], appB_;
1540 double clippedVolume_ = 0.0, clippedSigned_ = 0.0;
1541 long clampedFaces_ = 0;
Weymouth-Yue split advection of a colour field on an extended (inner + ghost) block.
double clippedVolume() const
|liquid volume| the cut-cell clip moved during the last advect() (0 without geometry).
void requireExchange() const
SField contactAngle() const
The prescribed contact angle per cell, in RADIANS, measured through the liquid.
double bcFaceVolume(int f) const
Signed liquid volume that crossed domain face f (0..5 = −x,+x,−y,+y,−z,+z) since the last resetBcFace...
void accumulateBcFaceVolume(int d)
Sum the just-computed d-fluxes on the two block-boundary d-faces into bcVol_, signed so that POSITIVE...
double maxCourantInterfaceAuto(double dth) const
The interface-local Courant number for this dt/h, using the CUT-CELL rule when geometry is attached (...
UCField cellKind() const
The cell classification (VofCellKind).
bool debugRecomputeDilation
DIAGNOSTIC ONLY — recompute the dilation flag before every sweep instead of freezing it once.
void applySweepImpl(int d, double dth)
void cutDiagnostics(Diagnostics &dg) const
The cut-cell half of diagnostics() (zero-cost when no geometry is attached).
bool hasOutsideMask() const
SField wettingNormal(int d) const
The fluid-only Youngs normal of each fluid cell (buildWettingNormals, then exchanged).
double maxCourant(double dth) const
Max |uf| dt / h over this block's owned faces (the + face of every inner cell; the - face of the firs...
double maxCourantInterface(double dth) const
Max |uf| dt/h over the faces of INTERFACE-ADJACENT cells only.
bool cutFluxClamp
Weymouth's admissible-interval clamp on the openness-weighted flux (cutcell.hpp vofCutFluxClamp).
void classifyGeometry()
Classify every cell from eps and the six face openness values (cutcell.hpp rule 1) into kindDouble().
double maxDiscreteDivergence(double dth) const
Max |discrete face divergence| * dt/h over inner cells — the exact quantity the conservation floor is...
void freezeDilationFlag()
void applySweep(int d, double dth)
C_i += (F_{i-} - F_{i+}) + c_i (a_{i+} - a_{i-}), over inner cells.
UCField dilationFlag() const
The frozen dilation indicator H(C^n - 1/2) of the current step (pressure cells).
SField colour() const
Colour field on the extended block (x-fastest).
void buildWettingNormals()
The fluid-only Youngs normal at every FLUID cell of the INNER region (the block's own ghost policy th...
void applySweepCut(int d, double dth)
eps_i C_i += (F_- - F_+) + c_i (o_+ a_+ - o_- a_-), then divide by eps_eff.
SField faceFlux() const
The geometric LIQUID volume flux of the sweep computeFluxes last ran, stored at the face's - side (so...
void wettingCensusGhost(long counts[kVofWetCount], double &meanAppDeg, long &nApp, int ghost) const
long index(int x, int y, int z) const
Linear index of the inner-block cell (x,y,z), 0-based within the inner region.
void enforceCfl(double cfl, double dt)
Record cfl as this step's Courant number and enforce the Weymouth-Yue cap – the one guard advect(),...
double wettingPureEps
A donor with C <= wettingPureEps or >= 1 - wettingPureEps is pure phase: the band gets the plain cont...
void exchangeTimed(SField f)
SField planeM(int d) const
PLIC normal component d of the current reconstruction (valid on the inner region grown by 1,...
bool interfaceLocalCfl
Take the CFL over INTERFACE-ADJACENT cells only (mixed cells and their face neighbours) instead of ov...
double cflLimit
Abort threshold on max |uf| dt / h.
void solidBandFillPassWetting()
Pass 1 of the band fill with the theta-consistent rule (WO-S).
void init(int nx, int ny, int nz, double h, int ghost=3)
void wettingCensus(long counts[kVofWetCount], double &meanAppDeg, long &nApp) const
Band-fill branch census over the INNER region (VofWettingBranch counts) and the mean APPARENT contact...
double checkCourant(double dt)
The interface-local (or global) Courant number for this dt, all-reduced, with the Weymouth-Yue bounde...
bool useWorklist
Compaction of the reconstruction pass onto the mixed cells.
SField wallSdf() const
The SDF on this block, in cells, > 0 in fluid (the solver embeds + exchanges it).
static double wallSeconds()
SField faceOpenness(int d) const
Face openness of the +d face of each cell (the advector's own high-face convention — the SAME convent...
bool bcFaceOwned(int f) const
The WO-R boundary ledger, for the batched step: whether face f is a global domain face this block own...
SField planeAlpha() const
PLIC plane offset of the current reconstruction.
void computeFluxesImpl(int d, double dth)
double wispEps
WO-R2 item 4 — the wisp tolerance on the mixed-cell predicate (wyIsMixed(c, wispEps)),...
void clipCutCells()
Rule 3: clip C into [0,1] in CUT cells only (eps_eff < 1), accumulating the liquid volume the clip mo...
std::function< double(double)> globalMax
All-reduce max across ranks; identity when unset (single block).
void solidBandFill()
The three-pass neutral (90 deg) solid-band fill of cutcell.hpp.
double wettingTangentEps
Below |sin(theta_apparent)| = wettingTangentEps the interface is parallel to the wall and no rotation...
void syncGhosts()
Bring the colour ghosts up to date.
long clampedFaces() const
Faces the clamp had to bind, last advect().
void addT_(double &acc, double t0)
std::function< void(SField)> exchange
Refresh EVERY ghost layer of the given cell field (MPI exchange + domain BC fill)....
SField kindDouble() const
The cell classification as a DOUBLE (1 = solid), so it can ride the block's own halo exchange.
void setOutsideMask(UCField m)
Install the OUT-OF-DOMAIN mask (vof/colour_bc.hppbuildOutsideMask): 1 on every ghost cell outside the...
void finalizeGeometry()
kindDouble() (ghost-exchanged) -> the unsigned-char classification every kernel reads.
void disableGeometry()
Drop the geometry (back to the uncut kernels, byte-identically).
void advect(double dt, long step)
One Weymouth-Yue step: freeze the dilation flag, then three sweeps in the permutation selected by ste...
void reconstruct()
PLIC over the inner region grown by one cell in every direction: the donor of a face of an inner cell...
void computeFluxes(int d, double dth)
One flux per d-face touched by an inner cell, stored at the cell on the face's - side.
UCField outsideMask() const
double clippedSigned() const
void addBcFaceVolume(int f, double v)
void setBcFaceOwned(int f, bool on)
Which of the six BLOCK boundary faces are GLOBAL domain faces this block owns.
long lastMixedCount() const
void solidBandFillPass(int k)
One pass of the neutral solid-band fill.
void timedExchange(F &&fn)
Run fn (a ghost exchange) under the exchange accumulator.
VofMetric metric
Which VofWettingPivot anchors the theta-plane (a measured ablation; see wetting.hpp).
SField epsFraction() const
Fluid volume fraction of each cell (buildCellFraction, a multiple of 1/64).
void noteBatchedStep()
Bookkeeping of a step the block container ran on this advector's views in its batched launches (VofBl...
void computeFluxesCut(int d, double dth)
F_f = o_f * wyFaceFlux(a_f, ...).
Diagnostics diagnostics() const
Local census over the inner region.
const Timing & timing() const
SField faceVel(int d) const
SField faceU() const
Face velocity in direction d at the +d face of each cell: uf(i,j,k) sits at (i+1/2,...
void enableGeometry()
Allocate the geometry views. Idempotent; does not classify.
double maxCourantInterfaceCut(double dth) const
Rule 2: the interface-local Courant number in cut cells is o_f |a_f| / max(eps_i, 0....
flow — thin shim: the container-free VoF cut-cell colour-transport rules (rung V5a) now live in core.
flow — portable (Kokkos) MAC stencil operators: Red-Black Gauss-Seidel smoothers + divergence.
Kokkos::View< long *, SMem > LField
double wyFaceFluxBc(double a, long p, long sd, int dir, const SF &c, const SF &mx, const SF &my, const SF &mz, const SF &alpha, const MF &outside, double eps=0.0)
WO-R sibling of wyFaceFlux for a face whose DONOR lies outside the global domain.
constexpr int kWySweepPerm[6][3]
The six permutations of (x, y, z), cycled by step index so no direction is systematically favoured (p...
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.
bool wyColourJump(double a, double b, double eps)
The interface-BAND jump test of maxCourantInterface: two neighbouring cells carry a DIFFERENT colour.
bool wyIsMixed(double c)
The mixed-cell predicate.
double wyFaceFlux(double a, long p, long sd, int dir, const SF &c, const SF &mx, const SF &my, const SF &mz, const SF &alpha, double eps=0.0)
Signed Eulerian donor-cell flux through the dir-face between cell p and cell p + sd,...
Kokkos::View< unsigned char *, SMem > UCField
long L3(int x, int y, int z, I3 e)
Kokkos::DefaultExecutionSpace SExec
Kokkos::View< double *, SMem > SField
flow — thin shim: the container-free VoF PLIC geometric toolbox (rung V0) now live in core.
flow — the multidimensional iteration policies: iteration order follows storage.
long solidCells
inner cells classified SOLID
double maxC
max C over inner cells (may be > 1)
double solidFillSum
Sum of C over inner SOLID cells ON THE WORKING BLOCK, i.e.
long clampedFaces
faces Weymouth's admissible interval had to clamp, last step
double rawVolume
sum of eps*C (raw buildCellFraction eps) over the same cells; differs from volume only on eps == 0 ce...
double minCFluid
min C over inner fluid cells with eps == 1 (uncut fluid)
double clippedSigned
the same, signed (created positive, destroyed negative)
double clippedVolume
|liquid volume| the cut-cell clip moved during the last step
long cutCells
inner cells with 0 < eps_eff < 1
double sumC
sum of C over inner cells (cell-volume units, not scaled by h^3)
double maxCFluid
max C over the same set
long wisps
cells with 0 < C < 1e-8 or 1-1e-8 < C < 1
long mixed
cells with 0 < C < 1
double minC
min C over inner cells (may be < 0: no clipping at this rung)
double solidSumC
filled by the SOLVER from the canonical G=2 colour field
double volume
sum of eps_eff*C over inner FLUID cells: the EXACTLY conserved functional of the cut-cell scheme (cut...
double reconstruct
the MYC normal + plicAlpha pass (once per sweep)
double exchange
the g = 3 ghost exchange (once per sweep, plus the sibling fields of the momentum/energy advectors)
long sweeps
sweeps counted (3 per advect() call)
double sweep
the colour/momentum update pass (once per sweep)
double freeze
the frozen dilation flag (once per step)
double clip
the cut-cell [0,1] clip + census (once per sweep, V5a only)
double fluxes
the per-face geometric flux pass (once per sweep)
static void cg(CCField x, CCConst b, OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB, OpV AT, C3 e, Exch &&exchange, const Reduce &red, long gCells, int iters)
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)
flow — thin shim: the container-free VoF theta-consistent solid-band fill (rung V5b) now live in core...