27#include <Kokkos_Core.hpp>
31#include "peclet/core/common/types.hpp"
32#include "peclet/core/decomp/block_decomposer.hpp"
40static constexpr int NX = 16,
NY = 8,
NZ = 32;
42static constexpr double ZL = 8.5,
ZR = 24.5;
45static double sdfAt(
double x,
double,
double) {
49 const double lo = std::fmax((
double)
iz,
ZL), hi = std::fmin((
double)
iz + 1.0,
ZR);
50 return std::fmax(hi - lo, 0.0);
55 std::vector<double> v((std::size_t)
lnx *
lny *
lnz);
56 for (
int z = 0; z <
lnz; ++z)
57 for (
int y = 0; y <
lny; ++y)
58 for (
int x = 0; x <
lnx; ++x)
59 v[(std::size_t)x + (std::size_t)y *
lnx + (std::size_t)z *
lnx *
lny] =
60 f(x + ox, y + oy, z + oz);
63static std::vector<double>
sliceOf(
const std::vector<double>& g,
int ox,
int oy,
int oz,
int lnx,
66 [&](
int x,
int y,
int z) {
67 return g[(std::size_t)x + (std::size_t)y *
NX + (std::size_t)z *
NX *
NY];
71static std::vector<double>
gatherGlobal(
const std::vector<double>&
local,
int ox,
int oy,
int oz,
72 int lnx,
int lny,
int lnz,
int rank,
int size) {
73 std::vector<double>
global;
76 for (
int r = 0; r < size; ++r) {
80 for (
int z = 0; z <
lnz; ++z)
81 for (
int y = 0; y <
lny; ++y)
82 std::memcpy(&
global[(std::size_t)ox + (std::size_t)(y + oy) *
NX +
83 (std::size_t)(z + oz) *
NX *
NY],
85 (std::size_t)
lnx *
sizeof(
double));
91 }
else if (rank == 0) {
96 for (
int z = 0; z <
meta[5]; ++z)
97 for (
int y = 0; y <
meta[4]; ++y)
99 (std::size_t)(z +
meta[2]) *
NX *
NY],
101 (std::size_t)
meta[3] *
sizeof(
double));
107static double maxAbsDiff(
const std::vector<double>& a,
const std::vector<double>& b) {
109 for (std::size_t i = 0; i < b.size(); ++i) {
110 const double d = std::fabs(a[i] - b[i]);
120 Kokkos::initialize(
argc, argv);
123 int rank = 0, size = 1;
126 auto dec = peclet::flow::CutcellMG::decomposition(
static_cast<std::size_t
>(size),
NX,
NY,
NZ);
127 auto blk =
dec.block(rank);
128 const int ox = (int)blk.origin[0], oy = (
int)blk.origin[1], oz = (int)blk.origin[2];
129 const int lnx = (int)blk.size[0],
lny = (
int)blk.size[1],
lnz = (int)blk.size[2];
130 const int gn[3] = {
NX,
NY,
NZ};
131 bool cut[3] = {
false,
false,
false};
132 for (
const auto& sz :
dec.sizes())
133 for (
int a = 0; a < 3; ++a)
134 if ((
int)sz[a] != gn[a])
138 "VOF DYNAMIC WETTING MPI np=%d grid %dx%dx%d block %dx%dx%d cut axes: %s%s%s "
139 "theta_e = %.0f deg, lambda = %.2f cells, u = %.3g\n",
140 size,
NX,
NY,
NZ,
lnx,
lny,
lnz, cut[0] ?
"x" :
"", cut[1] ?
"y" :
"", cut[2] ?
"z" :
"",
142 if (size > 1 && !cut[2]) {
144 std::printf(
" FAIL — the decomposition does not cut z, so no contact line is cut\n");
148 [](
int x,
int y,
int z) {
return sdfAt(x + 0.5, y + 0.5, z + 0.5); }, 0, 0, 0,
NX,
NY,
NZ);
173 long loc[4] = {
cdLoc.dynamicCells,
cdLoc.pinnedCells,
cdLoc.advancingCells,
174 cdLoc.recedingCells};
175 long tot[4] = {0, 0, 0, 0};
177 std::vector<double> g[5];
178 for (
int q = 0; q < 5; ++q)
184 const auto rd = ref.contactAngleDiagnostics();
186 for (
int q = 0; q < 5; ++q)
187 d[q] =
maxAbsDiff(g[q], ref.getVofDynamicField(q));
190 " [%s np=%d] imposed %.3e apparent %.3e U_cl %.3e Ca_cl %.3e state %.3e "
191 "band fill %.3e (all bitwise required)\n"
192 " census dynamic/pinned/advancing/receding %ld/%ld/%ld/%ld "
193 "(ref %ld/%ld/%ld/%ld) mean imposed %.6f mean apparent %.6f max|Ca_cl| %.6e\n",
194 hyst ?
"hyst " :
"dyn ", size, d[0], d[1], d[2], d[3], d[4],
df, tot[0], tot[1],
195 tot[2], tot[3],
rd.dynamicCells,
rd.pinnedCells,
rd.advancingCells,
rd.recedingCells,
196 rd.meanImposedTheta,
rd.meanApparentTheta,
rd.maxCaCl);
197 for (
int q = 0; q < 5; ++q)
198 if (!(d[q] == 0.0)) {
199 std::printf(
" [np=%d] FAIL — dynamic field %d is not bitwise (%.3e)\n", size, q, d[q]);
203 std::printf(
" [np=%d] FAIL — the theta band fill is not bitwise (%.3e)\n", size,
df);
206 if (tot[0] !=
rd.dynamicCells || tot[1] !=
rd.pinnedCells || tot[2] !=
rd.advancingCells ||
207 tot[3] !=
rd.recedingCells) {
208 std::printf(
" [np=%d] FAIL — the state census differs from the reference\n", size);
216 const int steps = 20;
224 for (
int i = 0; i < steps; ++i) {
229 double v1 = 0.0, solid = 0.0;
234 std::vector<double>
gu[3];
235 for (
int c = 0; c < 3; ++c)
240 for (
int i = 0; i < steps; ++i)
243 for (
int c = 0; c < 3; ++c)
244 du = std::fmax(du,
maxAbsDiff(
gu[c], ref.getVelocity(c)));
247 const double drift = (
v1 -
v0) /
v0;
248 const double tol = size == 1 ? 0.0 : 1e-9;
250 " [coupled np=%d] colour %.3e velocity %.3e imposed theta %.3e drift %.3e "
251 "solid sum C %.3e pressure iters max %ld\n",
253 if (!(
dc <=
tol) || !(du <=
tol) || !(
di <= 1e-9)) {
254 std::printf(
" [coupled np=%d] FAIL — above tol %.1e\n", size,
tol);
257 if (!(solid == 0.0) || !(std::fabs(drift) <= 1e-9)) {
258 std::printf(
" [coupled np=%d] FAIL — solid sum %.3e drift %.3e\n", size, solid, drift);
268 std::printf(
fail ?
"VOF DYNAMIC WETTING MPI: FAILED\n"
269 :
"VOF DYNAMIC WETTING MPI: all checks passed\n");
void setSolid(const std::vector< double > &sdfInner, bool cutcellPressure)
Host entry point: upload the inner SDF once and delegate.
std::vector< double > getVofFilledColour()
void setContactAngleDynamic(double thetaEDeg, double slipCells, double muLiquid, double sigma=0.0)
std::vector< double > getVof()
void setSurfaceTension(double sigma)
double capillaryDt()
The Brackbill capillary limit in the CALLER's time unit.
void setVof(const std::vector< double > &c)
void setContactAngleHysteresis(double thetaADeg, double thetaRDeg)
ContactAngleDiagnostics contactAngleDiagnostics()
std::vector< double > getVofDynamicField(int which)
void setVelocity(int c, const std::vector< double > &v)
Write a component's inner velocity from a host vector (x-fastest, inner region) and re-impose the sol...
std::vector< double > getVelocity(int c)
long lastPressureIterations() const
void setMu(double m)
Dynamic viscosity, in the caller's units.
void setRho(double r)
Fluid density, in the caller's units.
vof::WyAdvector::Diagnostics vofDiagnostics()
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
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...
static constexpr double XW
static constexpr double SLIP
static std::vector< double > blockOf(Fn f, int ox, int oy, int oz, int lnx, int lny, int lnz)
static constexpr double SIGMA
static constexpr double UVEL
static double colourAt(int, int, int iz)
static constexpr double THETA_E
static std::vector< double > gatherGlobal(const std::vector< double > &local, int ox, int oy, int oz, int lnx, int lny, int lnz, int rank, int size)
static constexpr double ZR
static constexpr double MU
static std::vector< double > sliceOf(const std::vector< double > &g, int ox, int oy, int oz, int lnx, int lny, int lnz)
static double maxAbsDiff(const std::vector< double > &a, const std::vector< double > &b)
static constexpr double ZL
static constexpr std::size_t GCELLS
static double sdfAt(double x, double, double)