33#include <Kokkos_Core.hpp>
37#include "peclet/core/common/types.hpp"
38#include "peclet/core/decomp/block_decomposer.hpp"
42static constexpr int NX = 48,
NY = 32,
NZ = 32;
44static constexpr double R = 5.0,
SIGMA = 320.0,
AX = 16.3,
BX = 32.3,
CY = 16.2,
CZ = 16.1;
45static constexpr int SX = 21,
SY0 = 15,
SZ = 16;
46static constexpr double SPECK[3] = {1e-5, 1e-2, 3e-3};
47static constexpr double GAMMA = 0.25,
DT = 0.05;
48static const std::array<int, 6>
BOX[2] = {{7, 7, 7, 26, 26, 26}, {18, 7, 7, 41, 26, 26}};
50static double sphereFrac(
double cx,
int x,
int y,
int z) {
53 for (
int k = 0; k < NS; ++k)
54 for (
int j = 0; j < NS; ++j)
55 for (
int i = 0; i < NS; ++i) {
56 const double px = x + (i + 0.5) / NS - cx, py = y + (j + 0.5) / NS -
CY,
57 pz = z + (k + 0.5) / NS -
CZ;
58 in += (px * px + py * py + pz * pz <
R *
R) ? 1 : 0;
60 return static_cast<double>(
in) / (NS * NS * NS);
63static std::vector<double>
colour(
int m) {
64 const auto& b =
BOX[m];
65 std::vector<double> c;
66 for (
int z = b[2]; z < b[5]; ++z)
67 for (
int y = b[1]; y < b[4]; ++y)
68 for (
int x = b[0]; x < b[3]; ++x) {
70 if (m == 1 && x ==
SX && z ==
SZ && y >=
SY0 && y <
SY0 + 3)
78 const std::size_t loc = (std::size_t)
lnx *
lny *
lnz;
83 s.
setVof(std::vector<double>(loc, 0.0));
88 std::vector<double> u(loc, 0.0), zero(loc, 0.0);
89 for (
int z = 0; z <
lnz; ++z)
90 for (
int y = 0; y <
lny; ++y)
91 for (
int x = 0; x <
lnx; ++x)
92 u[(std::size_t)x + (std::size_t)y *
lnx + (std::size_t)z *
lnx *
lny] =
106static const std::array<int, 6>
PBOX[2] = {{9, 7, 7, 28, 26, 26}, {17, 7, 7, 36, 26, 26}};
108 const auto& b =
PBOX[m];
109 std::vector<double> c;
110 for (
int z = b[2]; z < b[5]; ++z)
111 for (
int y = b[1]; y < b[4]; ++y)
112 for (
int x = b[0]; x < b[3]; ++x)
117 const std::size_t loc = (std::size_t)
lnx *
lny *
lnz;
122 s.
setVof(std::vector<double>(loc, 0.0));
132static constexpr double WR[2] = {5.0, 9.0},
WC[2][3] = {{40.3, 22.2, 16.1}, {20.3, 3.2, 16.1}};
133static constexpr double WA = 0.5,
WDT = 0.5;
135static double sphereFrac3(
const double* c,
double r,
int x,
int y,
int z) {
138 for (
int k = 0; k < NS; ++k)
139 for (
int j = 0; j < NS; ++j)
140 for (
int i = 0; i < NS; ++i) {
141 const double px = x + (i + 0.5) / NS - c[0], py = y + (j + 0.5) / NS - c[1],
142 pz = z + (k + 0.5) / NS - c[2];
143 in += (px * px + py * py + pz * pz < r * r) ? 1 : 0;
145 return static_cast<double>(
in) / (NS * NS * NS);
148 std::array<int, 6> q;
149 for (
int d = 0; d < 3; ++d) {
150 q[d] = (int)std::floor(
WC[m][d] -
WR[m]) - 3;
151 q[3 + d] = (int)std::ceil(
WC[m][d] +
WR[m]) + 3;
153 q[1] = std::max(q[1], 0);
154 q[4] = std::min(q[4],
NY);
159 std::vector<double> c;
160 for (
int z = b[2]; z < b[5]; ++z)
161 for (
int y = b[1]; y < b[4]; ++y)
162 for (
int x = b[0]; x < b[3]; ++x)
168 const double PI = 3.14159265358979323846;
169 const int xm = ((x %
NX) +
NX) %
NX;
170 return WA * (
NX / (2.0 *
PI)) * std::sin(2.0 *
PI * xm /
NX) * 4.0 * y * (
NY - y) /
174 const std::size_t loc = (std::size_t)
lnx *
lny *
lnz;
181 s.
setVof(std::vector<double>(loc, 0.0));
188 std::vector<double> u(loc, 0.0), v(loc, 0.0), w(loc, 0.0);
189 for (
int z = 0; z <
lnz; ++z)
190 for (
int y = 0; y <
lny; ++y)
191 for (
int x = 0; x <
lnx; ++x) {
192 const int gx = x + ox,
gy = y + oy;
193 const std::size_t i = (std::size_t)x + (std::size_t)y *
lnx + (std::size_t)z *
lnx *
lny;
202static std::vector<double>
gatherGlobal(
const std::vector<double>&
local,
int ox,
int oy,
int oz,
203 int lnx,
int lny,
int lnz,
int rank,
int size) {
204 std::vector<double>
global;
207 for (
int r = 0; r < size; ++r) {
211 for (
int z = 0; z <
lnz; ++z)
212 for (
int y = 0; y <
lny; ++y)
213 std::memcpy(&
global[(std::size_t)ox + (std::size_t)(y + oy) *
NX +
214 (std::size_t)(z + oz) *
NX *
NY],
216 (std::size_t)
lnx *
sizeof(
double));
222 }
else if (rank == 0) {
227 for (
int z = 0; z <
meta[5]; ++z)
228 for (
int y = 0; y <
meta[4]; ++y)
229 std::memcpy(&
global[(std::size_t)
meta[0] + (std::size_t)(y +
meta[1]) *
NX +
230 (std::size_t)(z +
meta[2]) *
NX *
NY],
232 (std::size_t)
meta[3] *
sizeof(
double));
240static constexpr int NF = 7;
242 std::vector<double> v;
244 v.push_back(q.volume);
245 v.push_back(
static_cast<double>(q.debrisCells));
246 v.push_back(q.debrisVolume);
247 v.push_back(q.debrisReturned);
248 v.push_back(q.debrisLost);
249 v.push_back(
static_cast<double>(q.debrisUnresolved));
250 v.push_back(q.residueReturned);
257 Kokkos::initialize(
argc, argv);
260 int rank = 0, size = 1;
263 auto dec = peclet::flow::CutcellMG::decomposition(
static_cast<std::size_t
>(size),
NX,
NY,
NZ);
264 auto blk =
dec.block(rank);
265 const int ox = (int)blk.origin[0], oy = (
int)blk.origin[1], oz = (int)blk.origin[2];
266 const int lnx = (int)blk.size[0],
lny = (
int)blk.size[1],
lnz = (int)blk.size[2];
268 for (
const auto& sz :
dec.sizes())
269 if ((
int)sz[0] !=
NX)
273 "VOF BLOCK DEBRIS MPI (vof_overlap_design G5) np=%d grid %dx%dx%d block "
274 "%dx%dx%d x cut: %s\n",
276 if (size > 1 && !
cutX) {
278 std::printf(
" FAIL - the decomposition does not cut x, so the markers share a rank\n");
281 const int STEPS = (
argc > 1) ? std::atoi(argv[1]) : 20;
285 for (
int k = 0; k <
STEPS; ++k)
295 const std::vector<double> r0 =
ledger(ref);
296 for (
int k = 0; k <
STEPS; ++k)
297 ref.advectVofBlocks(
DT,
false);
298 const std::vector<double>
rl =
ledger(ref),
rC = ref.getVof();
300 for (std::size_t i = 0; i <
GCELLS; ++i)
301 nC += (
gC[i] ==
rC[i]) ? 0 : 1;
303 for (std::size_t i = 0; i <
rl.size(); ++i)
305 const char* fn[
NF] = {
"volume",
"cells",
"vol",
"returned",
"lost",
"unresolved",
"residue"};
306 for (std::size_t m = 0; m <
rl.size() /
NF; ++m) {
307 std::printf(
" marker %zu:", m);
308 for (
int f = 0; f <
NF; ++f)
309 std::printf(
" %s %.17g%s", fn[f],
lAll[m *
NF + f],
310 lAll[m *
NF + f] ==
rl[m *
NF + f] ?
"" :
" (!= np1)");
315 " after %d steps: union colour cells differing from np=1: %ld; ledger fields "
316 "differing: %ld; B returned %.17g (painted %.17g)\n",
319 for (std::size_t m = 0; m <
rl.size() /
NF; ++m)
320 dv = std::fmax(dv, std::fabs(
rl[m *
NF] - r0[m *
NF]) / r0[m *
NF]);
321 std::printf(
" max marker volume rel change over the run (np=1): %.3e\n", dv);
322 if (!(dv <= 1e-12)) {
323 std::printf(
" FAIL: marker volume not conserved\n");
326 if (
nC != 0 ||
nL != 0) {
327 std::printf(
" FAIL: not bitwise decomposition-independent\n");
331 std::printf(
" FAIL: the debris removal did not fire (returned %.3e)\n",
rl[
NF + 3]);
334 if (!(
rl[3] == 0.0 &&
rl[
NF + 4] == 0.0)) {
335 std::printf(
" FAIL: marker A removed debris, or the return lost volume\n");
354 const auto cr = ref.vofBlockOverlapCensus();
355 const double dtR = ref.capillaryDt();
357 " phantom bound, overlapping pair: max S %.17g, excess %.17g, cells %ld, "
358 "active %d (off-switch run: active %d); capillary dt ratio %.17g (want "
359 "%.17g); np=1: max S %.17g cells %ld active %d dt %.17g\n",
360 cb.maxSum, cb.excess, cb.cells, (
int)cb.active, (
int)
cn.active,
dtB /
dtN, want,
361 cr.maxSum,
cr.cells, (
int)
cr.active,
dtR);
362 if (!(cb.active && !
cn.active)) {
363 std::printf(
" FAIL: the phantom bound did not activate (or the off switch failed)\n");
366 if (!(std::fabs(
dtB /
dtN / want - 1.0) <= 1e-14)) {
367 std::printf(
" FAIL: capillary dt ratio is not sqrt(2 rho_min / (rho_min + rho_max))\n");
372 if (!(cb.maxSum ==
cr.maxSum && cb.cells ==
cr.cells && cb.active ==
cr.active &&
373 dtB ==
dtR && std::fabs(cb.excess -
cr.excess) <= 1e-12 *
cr.excess)) {
374 std::printf(
" FAIL: the overlap census / bound differs from np = 1\n");
383 for (
const auto& sz :
dec.sizes())
384 if ((
int)sz[1] !=
NY)
391 v0Loc.push_back(q.volume);
398 for (
int k = 0; k <
WSTEPS; ++k) {
401 for (std::size_t m = 0; m < st.size(); ++m)
402 vLoc[m] = st[m].volume;
405 for (std::size_t m = 0; m <
vAll.size(); ++m)
406 worst = std::fmax(worst, std::fabs(
vAll[m] /
v0All[m] - 1.0));
408 const std::vector<double>
gC =
413 for (
int k = 0; k <
WSTEPS; ++k)
414 ref.advectVofBlocks(
WDT,
false);
415 const std::vector<double>
rC = ref.getVof();
416 const auto rs = ref.vofBlockStats();
418 for (std::size_t i = 0; i <
GCELLS; ++i)
419 nC += (
gC[i] ==
rC[i]) ? 0 : 1;
420 for (std::size_t m = 0; m <
rs.size(); ++m)
421 nV += (
vAll[m] ==
rs[m].volume) ? 0 : 1;
426 for (
int z = 0; z <
wb[5] -
wb[2]; ++z)
427 for (
int x = 0; x <
wb[3] -
wb[0]; ++x)
428 layer +=
wc[(std::size_t)x + (std::size_t)z * (
wb[3] -
wb[0]) * (
wb[4] -
wb[1])];
431 " low-wall marker: box y [%d, %d), wall-layer colour %.3f cells, |v| one face in "
432 "%.3e; master rank %d; y cut %s\n",
435 " after %d steps: max marker |V/V0-1| %.3e (volumes %.17g, %.17g); union cells "
436 "differing from np=1: %ld; volumes differing: %ld\n",
438 if (!(layer > 1.0 &&
wb[1] == 0 &&
vIn > 1e-3)) {
439 std::printf(
" FAIL: the low-wall scene does not put colour on the wall layer\n");
442 if (size > 1 &&
wMaster == 0) {
443 std::printf(
" FAIL: the wall marker's master is rank 0 (no MPI gather exercised)\n");
446 if (!(worst <= 1e-12)) {
447 std::printf(
" FAIL: a marker at the low wall does not conserve its volume\n");
450 if (
nC != 0 ||
nV != 0) {
451 std::printf(
" FAIL: the low-wall scene is not bitwise decomposition-independent\n");
458 std::printf(
"%s\n",
fail ?
"FAILED" :
"PASSED");
void setDomainBc(int face, int type, double vx, double vy, double vz)
std::vector< double > getVof()
void setPropertyModel(const std::string &target, ClosureKind kind, const std::string &in0, const std::string &in1, const std::vector< double > ¶ms)
void setSurfaceTension(double sigma)
double capillaryDt()
The Brackbill capillary limit in the CALLER's time unit.
void setVof(const std::vector< double > &c)
void setPressureGeometry(const std::vector< double > &sdfInner)
void setVofPhantomCapillaryBound(bool enabled)
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...
VofOverlapCensus vofBlockOverlapCensus() const
void setMu(double m)
Dynamic viscosity, in the caller's units.
void enableVofBlocksFromColours(const std::vector< std::array< int, 6 > > &boxes, const std::vector< std::vector< double > > &colours)
void setRho(double r)
Fluid density, in the caller's units.
std::vector< vof::VofBlockStats > vofBlockStats() const
void advectVofBlocks(double dtPhysArg, bool requireSolenoidal=true)
dt is in the caller's time unit.
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
static constexpr int STEPS
static const std::array< int, 6 > BOX[2]
static constexpr double SIGMA
static constexpr double WA
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 void configure(IbmSolver &s, int ox, int oy, int oz, int lnx, int lny, int lnz)
static constexpr double PAX
static constexpr double CY
static constexpr double R
static constexpr double GAMMA
static std::array< int, 6 > wallBox(int m)
static constexpr double RHO_G
static std::vector< double > colour(int m)
static constexpr double WR[2]
static std::vector< double > pairColour(int m)
static constexpr double DT
static void configureWall(IbmSolver &s, int ox, int oy, int lnx, int lny, int lnz)
static constexpr double SPECK[3]
static void configurePair(IbmSolver &s, int lnx, int lny, int lnz, bool bound)
static constexpr double BX
static constexpr double WC[2][3]
static constexpr double PBX
static constexpr int WSTEPS
static constexpr double AX
static constexpr double WDT
static constexpr double RHO_L
static double sphereFrac3(const double *c, double r, int x, int y, int z)
static constexpr double CZ
static std::vector< double > wallColour(int m)
static double sphereFrac(double cx, int x, int y, int z)
static double wallPsi(int x, int y)
psi on the z-edge (x, y): zero on both walls, periodic in x with an EXACT wrap (x mod NX).
static constexpr std::size_t GCELLS
static std::vector< double > ledger(IbmSolver &s)
static const std::array< int, 6 > PBOX[2]