124#ifndef PECLET_FLOW_VOF_MOMENTUM_ADVECT_HPP
125#define PECLET_FLOW_VOF_MOMENTUM_ADVECT_HPP
127#include <Kokkos_Core.hpp>
140 return rhoG * (1.0 - c) + rhoL * c;
146 return Kokkos::fabs(a) < Kokkos::fabs(b) ? a : b;
154 const double lo[3],
const double hi[3],
double eps = 0.0) {
155 const double cq = c(q);
156 const double vol = (hi[0] - lo[0]) * (hi[1] - lo[1]) * (hi[2] - lo[2]);
161 return plicBoxVolume(mx(q), my(q), mz(q), al(q), lo[0], hi[0], lo[1], hi[1], lo[2], hi[2]);
183 if (!(rhoG > 0.0) || !(rhoL > 0.0))
184 throw std::invalid_argument(
185 "peclet::flow::vof::MomentumConsistentAdvector: both phase densities must be > 0");
189 len_ =
static_cast<long>(e_.
x) * e_.
y * e_.
z;
192 for (
int c = 0; c < 3; ++c) {
193 cc_[c] =
SField(
"vof::Cc", len_);
194 vel_[c] =
SField(
"vof::uAdv", len_);
195 flag_[c] =
UCField(
"vof::ccFlag", len_);
197 fluxC_ =
SField(
"vof::momFluxC", len_);
198 fluxU_ =
SField(
"vof::momFluxU", len_);
199 aFace_ =
SField(
"vof::momAFace", len_);
252 throw std::runtime_error(
253 "peclet::flow::vof::MomentumConsistentAdvector: init() was never called");
254 if (!(w.cflLimit < 0.5))
255 throw std::runtime_error(
256 "peclet::flow::vof::MomentumConsistentAdvector: the shifted control volume needs "
257 "|u| dt/h < 1/2 strictly (its flux slab lives inside ONE half cell), so the "
258 "Weymouth-Yue cap must be < 0.5 - the default 0.25 is the proven 3D bound");
261 const double dth = dt / w.h();
265 w.freezeDilationFlag();
274 for (
int s = 0; s < 3; ++s) {
275 const int d =
perm[s];
279 for (
int e = 0; e < 3; ++e) {
280 w.exchangeTimed(cc_[e]);
281 w.exchangeTimed(vel_[e]);
284 w.computeFluxes(d, dth);
285 for (
int e = 0; e < 3; ++e) {
286 if (w.hasGeometry()) {
294 w.applySweep(d, dth);
295 w.exchangeTimed(w.colour());
301 const I3 e = e_, n = n_;
303 const double rg = rhoG_,
rl = rhoL_;
305 MD pol(
SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z});
307 for (
int c = 0; c < 3; ++c) {
309 double mn = 0.0, mx = 0.0,
sm = 0.0,
rmin = 0.0;
310 Kokkos::parallel_reduce(
311 "vof::mom::min",
pol,
313 acc = Kokkos::fmin(acc, cf(
L3(x, y, z, e)));
315 Kokkos::Min<double>(
mn));
316 Kokkos::parallel_reduce(
317 "vof::mom::max",
pol,
319 acc = Kokkos::fmax(acc, cf(
L3(x, y, z, e)));
321 Kokkos::Max<double>(mx));
322 Kokkos::parallel_reduce(
323 "vof::mom::sum",
pol,
325 const long i =
L3(x, y, z, e);
329 Kokkos::parallel_reduce(
330 "vof::mom::rhomin",
pol,
334 Kokkos::Min<double>(
rmin));
342 dg.floored = floored_;
343 dg.clamped = clamped_;
344 for (
int c = 0; c < 3; ++c)
345 dg.sumM0[c] = seedM_[c];
354 const I3 e = e_, n = n_;
356 SField c = w.colour(), mx = w.planeM(0), my = w.planeM(1), mz = w.planeM(2),
358 const double weps = w.wispEps;
360 for (
int comp = 0; comp < 3; ++comp) {
364 Kokkos::parallel_for(
365 "vof::mom::shifted_colour",
MD(
SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z}),
367 const long i =
L3(x, y, z, e);
368 double lo[3] = {0.0, 0.0, 0.0}, hi[3] = {1.0, 1.0, 1.0};
383 const I3 e = e_, n = n_;
388 const double rg = rhoG_,
rl = rhoL_;
389 for (
int comp = 0; comp < 3; ++comp) {
391 SField cf = cc_[comp],
mf = vel_[comp],
uf = w.faceVel(comp);
393 Kokkos::parallel_for(
394 "vof::mom::freeze",
MD(
SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z}),
396 const long i =
L3(x, y, z, e);
405 Kokkos::parallel_reduce(
406 "vof::mom::seedsum",
MD(
SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z}),
408 const long i =
L3(x, y, z, e);
426 const int dd = d, ec = comp;
427 const double rg = rhoG_,
rl = rhoL_;
429 SField c = w.colour(), mx = w.planeM(0), my = w.planeM(1), mz = w.planeM(2),
431 const double weps = w.wispEps;
434 int lo3[3] = {g, g, g};
435 const int hi3[3] = {g + n_.
x, g + n_.
y, g + n_.
z};
438 Kokkos::parallel_reduce(
442 const long p =
L3(x, y, z, e);
445 const double a = 0.5 * (
ud(p -
se) +
ud(p)) * dth;
452 const double aa = a > 0.0 ? a : -a;
453 double lo[3] = {0.0, 0.0, 0.0}, hi[3] = {1.0, 1.0, 1.0};
465 F =
vofCellBox(c, mx, my, mz, al, p, lo, hi, weps);
491 const double cd =
cvC(a > 0.0 ? p : p +
sd);
492 const double up =
aa < cd ?
aa : cd;
493 const double dn =
aa - (1.0 - cd);
494 const double lob =
dn > 0.0 ?
dn : 0.0;
504 const long don = a > 0.0 ? p : p +
sd;
508 const double sgn = a > 0.0 ? 1.0 : -1.0;
523 const I3 e = e_, n = n_;
526 const double rg = rhoG_,
rl = rhoL_,
dr = rhoL_ - rhoG_;
527 const double rmin = rhoG_ < rhoL_ ? rhoG_ : rhoL_;
530 SField cf = cc_[comp],
vf = vel_[comp],
uf = w.faceVel(comp);
534 Kokkos::parallel_reduce(
537 const long i =
L3(x, y, z, e);
539 const unsigned char k = fl(i);
540 const double dil = k ? (
aP -
aM) : 0.0;
544 const double dC = (fc(i -
sd) - fc(i)) +
dil;
545 const double cNew = cf(i) +
dC;
575 const double uo =
vf(i);
609 const I3 e = e_, n = n_;
611 SField c = w.colour(), mx = w.planeM(0), my = w.planeM(1), mz = w.planeM(2),
612 al = w.planeAlpha(),
ep = w.epsFraction();
614 const double weps = w.wispEps;
616 for (
int comp = 0; comp < 3; ++comp) {
620 Kokkos::parallel_for(
621 "vof::mom::shifted_colour_cut",
MD(
SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z}),
623 const long i =
L3(x, y, z, e);
626 const double ecv = 0.5 * (ea +
eb);
631 double lo[3] = {0.0, 0.0, 0.0}, hi[3] = {1.0, 1.0, 1.0};
657 const int dd = d, ec = comp;
659 SField c = w.colour(), mx = w.planeM(0), my = w.planeM(1), mz = w.planeM(2),
660 al = w.planeAlpha(),
ep = w.epsFraction(),
od = w.faceOpenness(d);
662 const double weps = w.wispEps;
665 int lo3[3] = {g, g, g};
666 const int hi3[3] = {g + n_.
x, g + n_.
y, g + n_.
z};
669 Kokkos::parallel_reduce(
670 "vof::mom::flux_cut",
673 const long p =
L3(x, y, z, e);
679 const double a = 0.5 * (
ud(p -
se) +
ud(p)) * dth;
681 if (a == 0.0 ||
ofac == 0.0) {
686 const double aa = a > 0.0 ? a : -a;
687 double lo[3] = {0.0, 0.0, 0.0}, hi[3] = {1.0, 1.0, 1.0};
697 F =
vofCellBox(c, mx, my, mz, al, p, lo, hi, weps);
713 const double ecv = 0.5 * (ea +
eb);
724 const long don = a > 0.0 ? p : p +
sd;
725 const double cd =
cvC(
don);
726 const double sweep =
ofac *
aa;
728 const double Fc = vofCutFluxClamp(
F, sweep,
ecvDon, cd);
735 const long don = a > 0.0 ? p : p +
sd;
739 const double sgn = a > 0.0 ? 1.0 : -1.0;
752 const I3 e = e_, n = n_;
755 const double rg = rhoG_,
rl = rhoL_,
dr = rhoL_ - rhoG_;
756 const double rmin = rhoG_ < rhoL_ ? rhoG_ : rhoL_;
759 SField cf = cc_[comp],
vf = vel_[comp],
uf = w.faceVel(comp),
ep = w.epsFraction();
764 Kokkos::parallel_reduce(
767 const long i =
L3(x, y, z, e);
772 const unsigned char k = fl(i);
773 const double dil = k ? (
aP -
aM) : 0.0;
774 const double dC = ((fc(i -
sd) - fc(i)) +
dil) /
ecv;
775 const double cNew = cf(i) +
dC;
777 const double uo =
vf(i);
793 return d == 0 ? 1 : (d == 1 ?
static_cast<long>(e_.
x) :
static_cast<long>(e_.
x) * e_.
y);
797 I3 n_{0, 0, 0}, e_{0, 0, 0};
800 double rhoG_ = 1.0, rhoL_ = 1.0, floorUsed_ = 0.0;
801 long floored_ = 0, clamped_ = 0;
802 double seedM_[3] = {0, 0, 0};
803 SField cc_[3], vel_[3], fluxC_, fluxU_, aFace_;
flow — VoF rung V1: Weymouth & Yue (2010) directionally-split, exactly volume-conserving geometric ad...
Momentum-consistent companion to WyAdvector.
bool clampFluxes
Ablation: drop the Weymouth flux clamp of point 3 (the raw geometric flux).
void momentumUpdate(const WyAdvector &w, int d, int comp)
C^e_i += (F_- - F_+) + flag (a_+ - a_-) and M_i += (G_- - G_+) + rho^ u^n_i (a_+ - a_-),...
double rhoFloor
Floor on rho^e in the recovery divide, in the same units as the phase densities.
void freezeShiftedState(const WyAdvector &w)
Freeze flag^e = H(C^{e,n} - 1/2) and seed M = rho^e u^n_e.
long clampedFluxes() const
void momentumFluxesCut(const WyAdvector &w, int d, int comp, double dth)
Face Courant number, geometric liquid flux and momentum flux on a CUT shifted CV.
SField advectedVelocity(int e) const
The advected velocity of component e, indexed exactly like the solver's u_e (inner cells).
void momentumFluxes(const WyAdvector &w, int d, int comp, double dth)
Face Courant number, geometric liquid flux and momentum flux of the shifted CV of component comp,...
bool momentumMuscl
Opt-in MinMod-limited linear reconstruction of the donor velocity in the momentum flux,...
bool useCellDilationFlag
Ablation: use the PRESSURE-cell frozen dilation flag H(C^n - 1/2) on the shifted CV instead of the st...
void momentumUpdateCut(const WyAdvector &w, int d, int comp)
The cut update: both increments divided by the CV's fluid volume, everything else identical — in part...
void buildShiftedColour(const WyAdvector &w)
C^e(i) = slab(cell i-s_e, e, [1/2,1]) + slab(cell i, e, [0,1/2]), over the inner region.
void setPhaseDensities(double rhoG, double rhoL)
static double cvEps(const SField &ep, const UCField &kd, long i, long se)
The CV's fluid volume eps^e(i) (0 when the whole CV is solid).
double lastRhoFloor() const
Diagnostics diagnostics() const
void advect(WyAdvector &w, double dt, long step)
One momentum-consistent Weymouth-Yue step: advances BOTH the colour field inside w and the half-shift...
void init(const WyAdvector &w, double rhoG, double rhoL)
SField shiftedColour(int e) const
The half-shifted colour of component e, same indexing.
void buildShiftedColourCut(const WyAdvector &w)
C^e = [eps(i-s_e) slabHi(i-s_e) + eps(i) slabLo(i)] / eps^e(i) — each half cell's PLIC slab weighted ...
long strideOf(int d) const
Weymouth-Yue split advection of a colour field on an extended (inner + ghost) block.
flow — thin shim: the container-free VoF cut-cell colour-transport rules (rung V5a) now live in core.
constexpr int kWySweepPerm[6][3]
The six permutations of (x, y, z), cycled by step index so no direction is systematically favoured (p...
bool wyIsMixed(double c)
The mixed-cell predicate.
double vofPhaseRho(double rhoG, double rhoL, double c)
Two-phase density from a fraction, written so that C = 1 returns rho_l and C = 0 returns rho_g EXACTL...
double vofMinmod(double a, double b)
Kokkos::View< unsigned char *, SMem > UCField
double vofCellBox(const SField &c, const SField &mx, const SField &my, const SField &mz, const SField &al, long q, const double lo[3], const double hi[3], double eps=0.0)
Liquid volume of the PLIC polyhedron of cell q inside a box given as per-axis intervals,...
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.
long clamped
fluxes Weymouth's admissible interval had to clamp, last step
double maxCc[3]
max C^e over inner momentum CVs
long floored
inner CVs where the rho^e floor bit in the last recovery
double minCc[3]
min C^e over inner momentum CVs
double sumM0[3]
the same sum at the START of the last advection (seeding)
double sumM[3]
sum of rho^e u_e over inner CVs AFTER the last advection
double minRhoC
min rho^e before the floor
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)