70#ifndef PECLET_FLOW_VOF_WETTING_DYNAMIC_HPP
71#define PECLET_FLOW_VOF_WETTING_DYNAMIC_HPP
75#include <Kokkos_Core.hpp>
76#include <Kokkos_MathematicalFunctions.hpp>
100 double thetaMin,
double thetaMax) {
102 double th =
t3 > 0.0 ? Kokkos::pow(
t3, 1.0 / 3.0) : thetaMin;
105 return th < thetaMin ? thetaMin : (th > thetaMax ? thetaMax : th);
136 double sigma,
double logRatio,
bool hyst,
137 double thetaA,
double thetaR,
double thetaE,
138 double thetaMin,
double thetaMax,
148 thetaOut = base < thetaMin ? thetaMin : (base > thetaMax ? thetaMax : base);
161 double that[3],
double&
cosApp,
const VofMetric& g) {
168 if (!(vofPhysNormal(
mfIn, g,
mh) > 0.0)) {
174 if (!(vofPhysNormal(
nwIn, g,
nw) > 0.0)) {
181 const double tn = Kokkos::sqrt(t[0] * t[0] + t[1] * t[1] + t[2] * t[2]);
186 for (
int d = 0; d < 3; ++d)
210 return u[0] *
that[0] + u[1] *
that[1] + u[2] *
that[2];
231 double thetaMin = 1.0 * 3.14159265358979323846 / 180.0;
232 double thetaMax = 179.0 * 3.14159265358979323846 / 180.0;
257 if (base_.extent(0) == (std::size_t)len)
259 base_ =
SField(
"vof::dyn::base", len);
260 imposed_ =
SField(
"vof::dyn::imposed", len);
261 app_ =
SField(
"vof::dyn::apparent", len);
262 ucl_ =
SField(
"vof::dyn::uclRaw", len);
263 uclS_ =
SField(
"vof::dyn::ucl", len);
264 wgt_ =
SField(
"vof::dyn::valid", len);
265 ca_ =
SField(
"vof::dyn::ca", len);
266 stateD_ =
SField(
"vof::dyn::state", len);
267 for (
int d = 0; d < 3; ++d) {
268 uc_[d] =
SField(
"vof::dyn::cellVel", len);
269 th_[d] =
SField(
"vof::dyn::tangent", len);
280 const int g = adv.
ghost();
281 const long sx = 1, sy = e.x, sz =
static_cast<long>(e.x) * e.y;
285 SField ucl = ucl_, wg = wgt_, ap = app_,
tx = th_[0],
ty = th_[1],
tz = th_[2];
289 Kokkos::parallel_for(
292 const long i =
L3(x, y, z, e);
296 tx(i) =
ty(i) =
tz(i) = 0.0;
301 double nw[3] = {0.5 * (sdf(i +
sx) - sdf(i -
sx)), 0.5 * (sdf(i + sy) - sdf(i - sy)),
302 0.5 * (sdf(i + sz) - sdf(i - sz))};
308 const double nn = Kokkos::sqrt(
wk[0] *
wk[0] +
wk[1] *
wk[1] +
wk[2] *
wk[2]);
311 for (
int d = 0; d < 3; ++d)
313 for (
int step = 1; step <= 4; ++step) {
314 const int fx = x +
static_cast<int>(Kokkos::round(step *
wk[0]));
315 const int fy = y +
static_cast<int>(Kokkos::round(step *
wk[1]));
316 const int fz = z +
static_cast<int>(Kokkos::round(step *
wk[2]));
322 double mAcc[3] = {0.0, 0.0, 0.0},
uAcc[3] = {0.0, 0.0, 0.0};
324 const double cf = c(
fi);
325 const double ml = Kokkos::fabs(mx(
fi)) + Kokkos::fabs(my(
fi)) + Kokkos::fabs(mz(
fi));
338 fx >= 1 &&
fy >= 1 &&
fz >= 1 &&
fx + 1 < e.x &&
fy + 1 < e.y &&
fz + 1 < e.z;
340 for (
int kz = -1; kz <= 1; ++kz)
341 for (
int ky = -1;
ky <= 1; ++
ky)
342 for (
int kx = -1;
kx <= 1; ++
kx) {
346 const double cg = c(
gi);
349 const double gm[3] = {mx(
gi), my(
gi), mz(
gi)};
351 Kokkos::fabs(gm[0]) + Kokkos::fabs(gm[1]) + Kokkos::fabs(gm[2]);
354 const double g2 = Kokkos::sqrt(gm[0] * gm[0] + gm[1] * gm[1] + gm[2] * gm[2]);
355 for (
int d = 0; d < 3; ++d)
356 mAcc[d] += gm[d] / g2;
365 const double inv = 1.0 /
static_cast<double>(cnt);
384 if (!
gme.isotropic()) {
385 for (
int d = 0; d < 3; ++d)
387 const double tin = Kokkos::sqrt(
ti[0] *
ti[0] +
ti[1] *
ti[1] +
ti[2] *
ti[2]);
389 for (
int d = 0; d < 3; ++d)
408 const int g = adv.
ghost();
410 SField bs = base_, ap = app_,
ucl = ucl_, wg = wgt_, us = uclS_, ca = ca_, st = stateD_;
416 Kokkos::parallel_for(
419 const long i =
L3(x, y, z, e);
430 if (!(wg(i) > 0.0)) {
435 const double td[3] = {
tx(i),
ty(i),
tz(i)};
436 if (
sm && (td[0] != 0.0 || td[1] != 0.0 || td[2] != 0.0)) {
437 for (
int s = -1; s <= 1; s += 2) {
438 const int dx =
static_cast<int>(Kokkos::round(s * td[0]));
439 const int dy =
static_cast<int>(Kokkos::round(s * td[1]));
440 const int dz =
static_cast<int>(Kokkos::round(s * td[2]));
441 if (dx == 0 && dy == 0 && dz == 0)
443 const int px = x + dx, py = y + dy, pz = z + dz;
444 if (px < 0 || py < 0 ||
pz < 0 || px >= e.x || py >= e.y || pz >= e.z)
446 const long j =
L3(px, py, pz, e);
453 const double u =
su /
sw;
455 const int state =
vofDynamicContactAngle(ap(i), u, mu, sg, lr,
hyst, ta, tr,
bs(i),
tmin,
459 st(i) =
static_cast<double>(state);
481 const int g = adv.
ghost();
482 SField wg = wgt_,
imp = imposed_, ap = app_, ca = ca_, st = stateD_, us = uclS_;
485 MD pol(
SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z});
488 const double sv =
static_cast<double>(s);
489 Kokkos::parallel_reduce(
490 "vof::dyn::census_state",
pol,
492 const long i =
L3(x, y, z, e);
493 if (
kk(i) ==
kVofSolid && wg(i) > 0.0 && st(i) == sv)
499 double si = 0.0,
sa = 0.0,
mc = 0.0, ms = 0.0;
501 Kokkos::parallel_reduce(
502 "vof::dyn::census_mean",
pol,
504 const long i =
L3(x, y, z, e);
512 Kokkos::parallel_reduce(
513 "vof::dyn::census_maxca",
pol,
515 const long i =
L3(x, y, z, e);
517 const double v = Kokkos::fabs(ca(i));
522 Kokkos::Max<double>(
mc));
523 Kokkos::parallel_reduce(
524 "vof::dyn::census_maxu",
pol,
526 const long i =
L3(x, y, z, e);
528 const double w = Kokkos::fabs(us(i));
533 Kokkos::Max<double>(ms));
535 const double toDeg = 180.0 / 3.14159265358979323846;
536 r.contactCells = cnt;
537 r.meanImposedDeg = cnt ?
si /
static_cast<double>(cnt) *
toDeg : 0.0;
538 r.meanApparentDeg = cnt ?
sa /
static_cast<double>(cnt) *
toDeg : 0.0;
545 SField base_, imposed_, app_, ucl_, uclS_, wgt_, ca_, stateD_;
flow — VoF rung V1: Weymouth & Yue (2010) directionally-split, exactly volume-conserving geometric ad...
Weymouth-Yue split advection of a colour field on an extended (inner + ghost) block.
SField contactAngle() const
The prescribed contact angle per cell, in RADIANS, measured through the liquid.
UCField cellKind() const
The cell classification (VofCellKind).
SField wettingNormal(int d) const
The fluid-only Youngs normal of each fluid cell (buildWettingNormals, then exchanged).
SField colour() const
Colour field on the extended block (x-fastest).
SField wallSdf() const
The SDF on this block, in cells, > 0 in fluid (the solver embeds + exchanges it).
int vofHysteresisBase(double thetaApp, double thetaA, double thetaR, double &base)
The hysteresis selector: which base angle the Cox-Voinov correction is applied to,...
bool vofWallTangent(const double mfIn[3], const double nwIn[3], double tEps, double that[3], double &cosApp, const VofMetric &g)
The in-wall (tangential) unit direction of the interface and its apparent cosine.
double vofContactLineSpeed(const double u[3], const double that[3])
U_cl = +u .
double coxVoinovAngle(double thetaE, double ca, double logRatio, double thetaMin, double thetaMax)
Cox-Voinov / Afkhami-Zaleski-Bussmann grid-scale apparent angle, in RADIANS.
int vofDynamicContactAngle(double thetaApp, double uCl, double muL, double sigma, double logRatio, bool hyst, double thetaA, double thetaR, double thetaE, double thetaMin, double thetaMax, double &thetaOut, double &caOut)
The whole rung in one pure function: hysteresis selector, then Cox-Voinov on the selected base.
VofDynamicState
Which rule produced a band cell's imposed angle (vof_dynamic_field(4)).
@ kVofDynNone
not a contact cell (no anchor / no usable normal): theta = the base
@ kVofDynReceding
theta_app < theta_r: theta_r (+ Cox-Voinov)
@ kVofDynPinned
theta_r <= theta_app <= theta_a: the apparent angle is imposed
@ kVofDynAdvancing
theta_app > theta_a: theta_a (+ Cox-Voinov)
@ kVofDynStatic
Cox-Voinov applied to the static base (no hysteresis configured)
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 — the multidimensional iteration policies: iteration order follows storage.
Census over the INNER region: cells per state, mean imposed / apparent angle (degrees) over the conta...
long cells[kVofDynStateCount]
The driver: produces WyAdvector::contactAngle() from the current colour, geometry,...
SField stateField() const
VofDynamicState, as a double
SField cellVel(int d) const
cell-centre velocity (the solver fills it)
SField capillary() const
Ca_cl.
bool smooth
the 3-point in-wall mean of U_cl (ablation)
SField base() const
The STATIC per-cell base angle (radians) the selector starts from.
double muLiquid
the LIQUID dynamic viscosity used in Ca_cl
void measure(const WyAdvector &adv)
Pass A — the measurement.
SField speed() const
the SMOOTHED U_cl
bool hysteresis
theta_a / theta_r are configured
double pureEps
a donor this close to 0/1 carries no interface
double logRatio() const
ln(Delta/lambda) at Delta = 1 cell.
VofMetric metric
The anisotropic cell metric (Phase 3, V4.3); {1,1,1} == the pre-Phase-3 arithmetic.
double tanEps
below this the interface is parallel to the wall
double slip
lambda, in CELLS (Delta = 1 cell), so logRatio = -ln(slip)
Census census(const WyAdvector &adv) const
bool dynamic
the Cox-Voinov correction is configured
SField imposed() const
the angle written this fill (rad)
double sigma
the surface tension used in Ca_cl (the solver resolves it)
SField apparent() const
the measured apparent angle (rad)
void impose(const WyAdvector &adv)
Pass B — the 3-point in-wall smoothing of U_cl, the selector and the write of theta into adv....
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)