flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_vof_blocks_debris_mpi.cpp
Go to the documentation of this file.
1// flow — doc/vof_overlap_design.md gate G5: the block container's DEBRIS removal, distributed.
2//
3// The G1 scene (two R = 5 markers 16 cells apart along x, plus a speck of B's colour painted into
4// A's interface band -- three cells with no cell of B above 1/2 within two cells) under a linear
5// shear u = gamma (y - y_c), advected 20 steps by the block container with the block CSF on (so
6// the debris pass runs). The speck is removed at the first step and its volume returned to B's
7// attached interface; the removal's sums are sequential in index order on the master, so the
8// result must be BITWISE independent of the decomposition.
9//
10// Compared bitwise against a single-rank run of the same scene (rank 0): the union colour, every
11// marker's volume, and the seven ledger fields (with residueReturned). At np >= 2 the ORB cuts x
12// and the masters are round-robin, so A and B live on different ranks.
13//
14// Second scene (review finding 3c): an OVERLAPPING pair with variable density, split between ranks
15// -- the phantom capillary bound activates, cuts the capillary dt by exactly
16// sqrt(2 rho_min / (rho_min + rho_max)) against the set_vof_phantom_capillary_bound(False) run, and
17// max S, the overlap cell count, the flag and the capillary dt are identical to np = 1.
18//
19// Third scene (vof_overlap_design §15): a marker CUT BY THE LOW WALL y = 0 (walls +-y), colour in
20// the wall-adjacent layer, in a solenoidal field with v = 0 exactly on the wall face and v != 0 one
21// face in. The block's wall-face velocity lives at global index -1, outside the domain, and is
22// gathered from the wall rank's patch ghosts. np 2 cuts x through the marker and its master is
23// rank 1 (round robin, marker id 1), so the out-of-domain pieces travel by MPI; np 4 also cuts y
24// through its box, so ranks that own NO wall face take part. Per-marker volume <= 1e-12 and union
25// colour + volumes bitwise np = 1. (With the pre-§15 zero-gradient clamp: volume drift ~1e-3.)
26#include <mpi.h>
27
28#include <algorithm>
29#include <array>
30#include <cmath>
31#include <cstdio>
32#include <cstring>
33#include <Kokkos_Core.hpp>
34#include <vector>
35
36#include "flow_ibm.hpp"
37#include "peclet/core/common/types.hpp"
38#include "peclet/core/decomp/block_decomposer.hpp"
39
41
42static constexpr int NX = 48, NY = 32, NZ = 32;
43static constexpr std::size_t GCELLS = (std::size_t)NX * NY * NZ;
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}};
49
50static double sphereFrac(double cx, int x, int y, int z) {
51 const int NS = 8;
52 int in = 0;
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;
59 }
60 return static_cast<double>(in) / (NS * NS * NS);
61}
62
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) {
69 double v = sphereFrac(m == 0 ? AX : BX, x, y, z);
70 if (m == 1 && x == SX && z == SZ && y >= SY0 && y < SY0 + 3)
71 v = SPECK[y - SY0];
72 c.push_back(v);
73 }
74 return c;
75}
76
77static void configure(IbmSolver& s, int ox, int oy, int oz, int lnx, int lny, int lnz) {
78 const std::size_t loc = (std::size_t)lnx * lny * lnz;
79 s.setRho(1.0);
80 s.setMu(0.5);
81 s.setPressureGeometry(std::vector<double>(loc, 10.0));
82 s.enableVof();
83 s.setVof(std::vector<double>(loc, 0.0));
85 s.enableVofBlocksFromColours({BOX[0], BOX[1]}, {colour(0), colour(1)});
86 s.enableVofBlockCsf(); // the debris pass runs under the block CSF (removal default ON)
87 // u = gamma (y - y_c) at the u faces (x + 1/2, y, z): divergence-free, periodic in x.
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] =
93 GAMMA * ((y + oy) + 0.5 - CY);
94 (void)ox;
95 (void)oz;
96 s.setVelocity(0, u);
97 s.setVelocity(1, zero);
98 s.setVelocity(2, zero);
99}
100
101// ---- the PHANTOM capillary bound (vof_overlap_design §5.6/§11.3; review finding 3c): two
102// markers 8 cells apart (R = 5: 2 cells of overlap), each carrying only its own sphere, with a
103// variable density so the bound has a density range to act on. The x cut of np 2 / 4 falls
104// between the two centres, so the S = sum_k C_k census is assembled from both masters.
105static constexpr double PAX = 18.3, PBX = 26.3, RHO_L = 1.0, RHO_G = 0.1;
106static const std::array<int, 6> PBOX[2] = {{9, 7, 7, 28, 26, 26}, {17, 7, 7, 36, 26, 26}};
107static std::vector<double> pairColour(int m) {
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)
113 c.push_back(sphereFrac(m == 0 ? PAX : PBX, x, y, z));
114 return c;
115}
116static void configurePair(IbmSolver& s, int lnx, int lny, int lnz, bool bound) {
117 const std::size_t loc = (std::size_t)lnx * lny * lnz;
118 s.setRho(RHO_L);
119 s.setMu(0.5);
120 s.setPressureGeometry(std::vector<double>(loc, 10.0));
121 s.enableVof();
122 s.setVof(std::vector<double>(loc, 0.0));
125 if (!bound)
128 s.enableVofBlockCsf(); // forms S and the census for the first step
129}
130
131// ---- a marker at the LOW wall y = 0 (§15)
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;
134static constexpr int WSTEPS = 40;
135static double sphereFrac3(const double* c, double r, int x, int y, int z) {
136 const int NS = 8;
137 int in = 0;
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;
144 }
145 return static_cast<double>(in) / (NS * NS * NS);
146}
147static std::array<int, 6> wallBox(int m) { // bubble box + the margin 3, inside the y walls
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;
152 }
153 q[1] = std::max(q[1], 0);
154 q[4] = std::min(q[4], NY);
155 return q;
156}
157static std::vector<double> wallColour(int m) {
158 const auto b = wallBox(m);
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)
163 c.push_back(sphereFrac3(WC[m], WR[m], x, y, z));
164 return c;
165}
167static double wallPsi(int x, int y) {
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) /
171 (double)(NY * NY);
172}
173static void configureWall(IbmSolver& s, int ox, int oy, int lnx, int lny, int lnz) {
174 const std::size_t loc = (std::size_t)lnx * lny * lnz;
175 s.setRho(1.0);
176 s.setMu(0.5);
177 s.setDomainBc(2, 1, 0, 0, 0);
178 s.setDomainBc(3, 1, 0, 0, 0); // walls +-y
179 s.setPressureGeometry(std::vector<double>(loc, 10.0));
180 s.enableVof();
181 s.setVof(std::vector<double>(loc, 0.0));
184 s.enableVofBlockCsf(); // the production configuration: debris + residue pass on
185 // flow's LOW-face convention: u(i, j) on the x face i between z-edges (i, j) and (i, j + 1),
186 // v(i, j) on the y face j between (i, j) and (i + 1, j) -- the discrete curl, so the discrete
187 // divergence telescopes; v(., 0) = 0 exactly (psi(., 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;
194 u[i] = wallPsi(gx, gy + 1) - wallPsi(gx, gy);
195 v[i] = -(wallPsi(gx + 1, gy) - wallPsi(gx, gy));
196 }
197 s.setVelocity(0, u);
198 s.setVelocity(1, v);
199 s.setVelocity(2, w);
200}
201
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;
205 if (rank == 0)
206 global.assign(GCELLS, 0.0);
207 for (int r = 0; r < size; ++r) {
208 int meta[6] = {ox, oy, oz, lnx, lny, lnz};
209 if (r == 0) {
210 if (rank == 0)
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],
215 &local[(std::size_t)y * lnx + (std::size_t)z * lnx * lny],
216 (std::size_t)lnx * sizeof(double));
217 continue;
218 }
219 if (rank == r) {
220 MPI_Send(meta, 6, MPI_INT, 0, 100 + r, MPI_COMM_WORLD);
221 MPI_Send(local.data(), (int)local.size(), MPI_DOUBLE, 0, 200 + r, MPI_COMM_WORLD);
222 } else if (rank == 0) {
224 std::vector<double> buf((std::size_t)meta[3] * meta[4] * meta[5]);
225 MPI_Recv(buf.data(), (int)buf.size(), MPI_DOUBLE, r, 200 + r, MPI_COMM_WORLD,
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],
231 &buf[(std::size_t)y * meta[3] + (std::size_t)z * meta[3] * meta[4]],
232 (std::size_t)meta[3] * sizeof(double));
233 }
234 }
235 return global;
236}
237
238// per marker: volume, debrisCells, debrisVolume, debrisReturned, debrisLost, debrisUnresolved,
239// residueReturned (§13)
240static constexpr int NF = 7;
241static std::vector<double> ledger(IbmSolver& s) {
242 std::vector<double> v;
243 for (const auto& q : s.vofBlockStats()) {
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);
251 }
252 return v;
253}
254
255int main(int argc, char** argv) {
256 MPI_Init(&argc, &argv);
257 Kokkos::initialize(argc, argv);
258 int fail = 0;
259 {
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];
267 bool cutX = false;
268 for (const auto& sz : dec.sizes())
269 if ((int)sz[0] != NX)
270 cutX = true;
271 if (rank == 0)
272 std::printf(
273 "VOF BLOCK DEBRIS MPI (vof_overlap_design G5) np=%d grid %dx%dx%d block "
274 "%dx%dx%d x cut: %s\n",
275 size, NX, NY, NZ, lnx, lny, lnz, cutX ? "yes" : "no");
276 if (size > 1 && !cutX) {
277 if (rank == 0)
278 std::printf(" FAIL - the decomposition does not cut x, so the markers share a rank\n");
279 fail = 1;
280 }
281 const int STEPS = (argc > 1) ? std::atoi(argv[1]) : 20;
283 sd.initMpi(dec, MPI_COMM_WORLD);
284 configure(sd, ox, oy, oz, lnx, lny, lnz);
285 for (int k = 0; k < STEPS; ++k)
286 sd.advectVofBlocks(DT, /*requireSolenoidal=*/false);
287 // the measured entries live on each block's master (zero elsewhere): a SUM is the census,
288 // and x + 0 is exact, so the sum carries the master's double bit for bit
289 std::vector<double> lLoc = ledger(sd), lAll(lLoc.size(), 0.0);
290 MPI_Allreduce(lLoc.data(), lAll.data(), (int)lLoc.size(), MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
291 const std::vector<double> gC = gatherGlobal(sd.getVof(), ox, oy, oz, lnx, lny, lnz, rank, size);
292 if (rank == 0) {
293 IbmSolver ref(NX, NY, NZ);
294 configure(ref, 0, 0, 0, NX, NY, NZ);
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();
299 long nC = 0;
300 for (std::size_t i = 0; i < GCELLS; ++i)
301 nC += (gC[i] == rC[i]) ? 0 : 1;
302 long nL = 0;
303 for (std::size_t i = 0; i < rl.size(); ++i)
304 nL += (lAll[i] == rl[i]) ? 0 : 1;
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)");
311 std::printf("\n");
312 }
313 const double painted = SPECK[0] + SPECK[1] + SPECK[2];
314 std::printf(
315 " after %d steps: union colour cells differing from np=1: %ld; ledger fields "
316 "differing: %ld; B returned %.17g (painted %.17g)\n",
317 STEPS, nC, nL, rl[NF + 3], painted);
318 double dv = 0.0;
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");
324 fail = 1;
325 }
326 if (nC != 0 || nL != 0) {
327 std::printf(" FAIL: not bitwise decomposition-independent\n");
328 fail = 1;
329 }
330 if (!(rl[NF + 3] > 0.9 * painted)) { // the gate must not be vacuous: the speck was removed
331 std::printf(" FAIL: the debris removal did not fire (returned %.3e)\n", rl[NF + 3]);
332 fail = 1;
333 }
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");
336 fail = 1;
337 }
338 }
340
341 // ---- the phantom capillary bound on an OVERLAPPING pair split between ranks
342 {
343 IbmSolver pb(lnx, lny, lnz), pn(lnx, lny, lnz);
344 pb.initMpi(dec, MPI_COMM_WORLD);
345 pn.initMpi(dec, MPI_COMM_WORLD);
346 configurePair(pb, lnx, lny, lnz, true);
347 configurePair(pn, lnx, lny, lnz, false);
348 const auto cb = pb.vofBlockOverlapCensus(), cn = pn.vofBlockOverlapCensus();
349 const double dtB = pb.capillaryDt(), dtN = pn.capillaryDt(); // collective
350 const double want = std::sqrt(2.0 * RHO_G / (RHO_G + RHO_L));
351 if (rank == 0) {
352 IbmSolver ref(NX, NY, NZ);
353 configurePair(ref, NX, NY, NZ, true);
354 const auto cr = ref.vofBlockOverlapCensus();
355 const double dtR = ref.capillaryDt();
356 std::printf(
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");
364 fail = 1;
365 }
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");
368 fail = 1;
369 }
370 // max S and the cell count are exact across np (a two-term sum is order-independent and
371 // the reductions are MAX / integer SUM); the excess is a floating SUM over ranks
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");
375 fail = 1;
376 }
377 }
379 }
380 // ---- a marker at the LOW wall (§15), split between ranks
381 {
382 bool cutY = false;
383 for (const auto& sz : dec.sizes())
384 if ((int)sz[1] != NY)
385 cutY = true;
387 sw.initMpi(dec, MPI_COMM_WORLD);
388 configureWall(sw, ox, oy, lnx, lny, lnz);
389 std::vector<double> v0Loc, v0All;
390 for (const auto& q : sw.vofBlockStats())
391 v0Loc.push_back(q.volume);
392 v0All.assign(v0Loc.size(), 0.0);
393 MPI_Allreduce(v0Loc.data(), v0All.data(), (int)v0Loc.size(), MPI_DOUBLE, MPI_SUM,
395 const int wMaster = sw.vofBlockStats()[1].master;
396 double worst = 0.0;
397 std::vector<double> vLoc(v0Loc.size()), vAll(v0Loc.size());
398 for (int k = 0; k < WSTEPS; ++k) {
399 sw.advectVofBlocks(WDT, /*requireSolenoidal=*/false);
400 const auto st = sw.vofBlockStats();
401 for (std::size_t m = 0; m < st.size(); ++m)
402 vLoc[m] = st[m].volume;
403 MPI_Allreduce(vLoc.data(), vAll.data(), (int)vLoc.size(), MPI_DOUBLE, MPI_SUM,
405 for (std::size_t m = 0; m < vAll.size(); ++m)
406 worst = std::fmax(worst, std::fabs(vAll[m] / v0All[m] - 1.0));
407 }
408 const std::vector<double> gC =
409 gatherGlobal(sw.getVof(), ox, oy, oz, lnx, lny, lnz, rank, size);
410 if (rank == 0) {
411 IbmSolver ref(NX, NY, NZ);
412 configureWall(ref, 0, 0, NX, NY, NZ);
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();
417 long nC = 0, nV = 0;
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;
422 // the triggering condition, so the gate cannot go vacuous
423 const auto wb = wallBox(1);
424 const std::vector<double> wc = wallColour(1);
425 double layer = 0.0;
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])];
429 const double vIn = std::fabs(wallPsi(20, 1) - wallPsi(21, 1));
430 std::printf(
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",
433 wb[1], wb[4], layer, vIn, wMaster, cutY ? "yes" : "no");
434 std::printf(
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",
437 WSTEPS, worst, vAll[0], vAll[1], nC, nV);
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");
440 fail = 1;
441 }
442 if (size > 1 && wMaster == 0) {
443 std::printf(" FAIL: the wall marker's master is rank 0 (no MPI gather exercised)\n");
444 fail = 1;
445 }
446 if (!(worst <= 1e-12)) {
447 std::printf(" FAIL: a marker at the low wall does not conserve its volume\n");
448 fail = 1;
449 }
450 if (nC != 0 || nV != 0) {
451 std::printf(" FAIL: the low-wall scene is not bitwise decomposition-independent\n");
452 fail = 1;
453 }
454 }
456 }
457 if (rank == 0)
458 std::printf("%s\n", fail ? "FAILED" : "PASSED");
459 }
460 Kokkos::finalize();
461 MPI_Finalize();
462 return fail;
463}
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 > &params)
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.
int main()
Definition dbg_scene.cpp:13
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 int NY
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 int NF
static constexpr double CY
static constexpr int SZ
static constexpr double R
static constexpr double GAMMA
static std::array< int, 6 > wallBox(int m)
static constexpr double RHO_G
static constexpr int SX
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 int SY0
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 NX
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]
static constexpr int NZ