50#include <Kokkos_Core.hpp>
66 std::fprintf(stderr, "CHECK failed: %s\n at %s:%d\n", #cond, __FILE__, __LINE__); \
71std::size_t idx(
int x,
int y,
int z,
int nx,
int ny) {
72 return (std::size_t)x + (std::size_t)y * nx + (std::size_t)z * (std::size_t)nx * ny;
74double maxAbs(
const std::vector<double>& v) {
77 m = std::fmax(
m, std::fabs(
x));
80double maxAbsDiff(
const std::vector<double>& a,
const std::vector<double>& b) {
85 for (std::size_t i = 0;
i <
a.size(); ++
i) {
86 const double d = std::fabs(a[i] - b[i]);
93double sum(
const std::vector<double>& v) {
110 const double h = 1.0 /
N,
dt = 0.1 *
h,
T = 3.0;
113 const double dtSolver =
dt /
h;
114 const int steps = 20;
123 const I3 eR =
ref.extent();
124 const int gR =
ref.ghost();
129 for (
int s = 0;
s <
steps; ++
s) {
133 auto refHost = Kokkos::create_mirror_view(
ref.colour());
134 Kokkos::deep_copy(refHost,
ref.colour());
146 s.setVelocityResidualTolerance(0.0);
147 s.setPressureGeometry(std::vector<double>((std::size_t)
N *
N *
N, 10.0));
152 const vofscene::Block bS{seed.inner(), seed.extent(), seed.ghost(), I3{0, 0, 0}};
154 auto sh = Kokkos::create_mirror_view(seed.colour());
155 Kokkos::deep_copy(sh, seed.colour());
156 const I3 eS = seed.extent();
157 const int gS = seed.ghost();
158 std::vector<double> c0((std::size_t)
N *
N *
N);
159 for (
int z = 0;
z <
N; ++
z)
160 for (
int y = 0;
y <
N; ++
y)
161 for (
int x = 0;
x <
N; ++
x)
162 c0[idx(x, y, z,
N,
N)] =
sh(
L3(x + gS, y + gS, z + gS, eS));
168 const I3 eP =
probe.extent();
169 const int gP =
probe.ghost();
171 std::vector<double> uu((std::size_t)
N *
N *
N), vv(uu.size()), ww(uu.size());
174 auto hu = Kokkos::create_mirror_view(
probe.faceU());
175 auto hv = Kokkos::create_mirror_view(
probe.faceV());
176 auto hw = Kokkos::create_mirror_view(
probe.faceW());
177 Kokkos::deep_copy(hu,
probe.faceU());
178 Kokkos::deep_copy(hv,
probe.faceV());
179 Kokkos::deep_copy(hw,
probe.faceW());
180 for (
int z = 0;
z <
N; ++
z)
181 for (
int y = 0;
y <
N; ++
y)
182 for (
int x = 0;
x <
N; ++
x) {
184 const int xm = (
x +
N - 1) %
N, ym = (y +
N - 1) %
N, zm = (
z +
N - 1) %
N;
185 uu[idx(x, y, z,
N,
N)] = hu(
L3(xm + gP, y + gP, z + gP, eP));
186 vv[idx(x, y, z,
N,
N)] = hv(
L3(x + gP, ym + gP, z + gP, eP));
187 ww[idx(x, y, z,
N,
N)] = hw(
L3(x + gP, y + gP, zm + gP, eP));
194 const auto cs =
s.getVof();
195 double dmax = 0.0, l1 = 0.0;
196 for (
int z = 0;
z <
N; ++
z)
197 for (
int y = 0;
y <
N; ++
y)
198 for (
int x = 0;
x <
N; ++
x) {
199 const double d =
cs[idx(x, y, z,
N,
N)] - refHost(
L3(x + gR, y + gR, z + gR, eR));
200 dmax = std::fmax(dmax, std::fabs(d));
203 std::printf(
"A bridge (LeVeque, %d steps): max|C_solver - C_standalone| %.3e L1 %.3e\n", steps,
241double hydrostaticC(
double ratio,
bool freeze,
bool harmonic,
int steps,
double g,
double dt) {
242 const int N = 8,
NZ = 24;
248 s.setVelocityResidualTolerance(0.0);
250 s.setDomainBc(4, 1, 0, 0, 0);
251 s.setDomainBc(5, 1, 0, 0, 0);
252 s.setPressureGeometry(std::vector<double>((std::size_t)
N *
N *
NZ, 10.0));
253 std::vector<double> c0((std::size_t)
N *
N *
NZ);
254 for (
int z = 0;
z <
NZ; ++
z)
255 for (
int y = 0;
y <
N; ++
y)
256 for (
int x = 0;
x <
N; ++
x)
257 c0[idx(x, y, z,
N,
N)] = (
z <
NZ / 2) ? 1.0 : 0.0;
261 s.exchangeField(
"C");
269 s.setRhoFaceHarmonic(
true);
273 last = std::fmax(
maxAbs(
s.getVelocity(0)),
275 CHECK(!std::isnan(last));
276 if (std::isnan(last))
280 const auto cNow = freeze ? c0 :
s.getVof();
281 const auto p =
s.getPressure();
283 const int xc =
N / 2, yc =
N / 2;
284 for (
int z = 1;
z <
NZ; ++
z) {
285 const double dp =
p[idx(xc, yc, z,
N,
N)] -
p[idx(xc, yc, z - 1,
N,
N)];
286 const double ra = 1.0 + (
ratio - 1.0) * cNow[idx(xc, yc, z,
N,
N)];
287 const double rb = 1.0 + (
ratio - 1.0) * cNow[idx(xc, yc, z - 1,
N,
N)];
288 perr = std::fmax(perr, std::fabs(dp + g * 0.5 * (ra + rb)) / (g * ratio));
290 std::printf(
"B ratio %-6g %-14s: steady max|u| %.3e dP/dz rel-err %.3e\n", ratio,
291 harmonic ?
"HARMONIC rho_f" : (freeze ?
"frozen C" :
"free C"), last,
perr);
294 return freeze ? last :
perr;
299double hydrostaticRhoRef(
double ratio,
int steps,
double g,
double dt) {
300 const int N = 8,
NZ = 24;
305 s.setDomainBc(4, 1, 0, 0, 0);
306 s.setDomainBc(5, 1, 0, 0, 0);
307 s.setPressureGeometry(std::vector<double>((std::size_t)
N *
N *
NZ, 10.0));
308 std::vector<double>
rho((std::size_t)
N *
N *
NZ);
309 for (
int z = 0;
z <
NZ; ++
z)
310 for (
int y = 0;
y <
N; ++
y)
311 for (
int x = 0;
x <
N; ++
x)
312 rho[idx(x, y, z,
N,
N)] = (
z <
NZ / 2) ? ratio : 1.0;
314 s.setField(
"rho", rho);
315 s.setDensityMode(
true);
320 last = std::fmax(
maxAbs(
s.getVelocity(0)),
327void conservation(
int steps) {
329 const double dt = 1.0, amp = 5
e-4,
ratio = 10.0;
334 s.setAdvection(
true);
335 s.setPressureGeometry(std::vector<double>((std::size_t)
N *
N *
N, 10.0));
337 std::vector<double> c0((std::size_t)
N *
N *
N), fx(c0.size());
338 for (
int z = 0;
z <
N; ++
z)
339 for (
int y = 0;
y <
N; ++
y)
340 for (
int x = 0;
x <
N; ++
x) {
341 const double dx =
x + 0.5 -
N / 2.0,
dy =
y + 0.5 -
N / 2.0,
dz =
z + 0.5 -
N / 2.0;
342 const double r = std::sqrt(dx * dx + dy * dy + dz * dz);
343 c0[idx(x, y, z,
N,
N)] = std::fmin(1.0, std::fmax(0.0, 0.5 - (r -
N / 5.0)));
344 fx[idx(x, y, z,
N,
N)] = amp * std::sin(2 * M_PI * (z + 0.5) /
N);
350 s.setField(
"force_x", fx);
351 const double v0 = sum(
s.getVof());
352 double dmax = 0.0, cfl = 0.0, peak = 0.0, drift = 0.0;
355 dmax = std::fmax(dmax, std::fabs(
s.maxOpenDivergence()));
356 cfl = std::fmax(cfl,
s.vofLastCourant());
357 drift = (sum(
s.getVof()) - v0) / v0;
358 peak = std::fmax(peak, std::fabs(drift));
360 const auto d =
s.vofDiagnostics();
362 "C conservation (%d coupled steps, ratio %g): dV/V final %+.3e PEAK %.3e "
363 "max|div(open u)| %.2e max cfl %.4f C in [%.2e, %.10f] mixed %ld\n",
364 steps, ratio, drift, peak, dmax, cfl,
d.minC,
d.maxC,
d.mixed);
371 CHECK(
d.maxC <= 1.0 + 1e-12 &&
d.minC > -1e-12);
375void constantColour() {
377 const std::size_t
n = (std::size_t)
N *
N *
N;
381 std::vector<double> fx(n), ones(n, 1.0);
382 for (
int z = 0;
z <
N; ++
z)
383 for (
int y = 0;
y <
N; ++
y)
384 for (
int x = 0;
x <
N; ++
x)
385 fx[idx(x, y, z,
N,
N)] = 1
e-3 * std::sin(2 * M_PI * (z + 0.5) /
N);
390 s.setVelocityResidualTolerance(0.0);
391 s.setAdvection(
true);
392 s.setPressureGeometry(std::vector<double>(n, 10.0));
394 s.setField(
"force_x", fx);
399 std::vector<std::vector<double>>
u(2),
p(2);
400 std::vector<double> cEnd;
401 for (
int vofOn = 0; vofOn < 2; ++vofOn) {
406 for (
int it = 0;
it < 30; ++
it)
408 u[vofOn] =
s.getVelocity(0);
409 p[vofOn] =
s.getPressure();
414 for (
double c : cEnd)
415 dC = std::fmax(dC, std::fabs(
c - 1.0));
418 "D1 uniform C stationary: max|C-1| %.3e\nD2 VoF inert (no closure): du %.3e dp %.3e "
420 dC, du, dp,
maxAbs(u[0]));
422 CHECK(du == 0.0 && dp == 0.0);
425 std::vector<std::vector<double>> uv(2), pv(2);
426 for (
int viaC = 0; viaC < 2; ++viaC) {
434 s.setField(
"rho", std::vector<double>(n, 2.0));
435 s.setDensityMode(
true);
437 for (
int it = 0;
it < 30; ++
it)
439 uv[viaC] =
s.getVelocity(0);
440 pv[viaC] =
s.getPressure();
443 std::printf(
"D3 rho(C==1) == hand-set uniform rho: du %.3e dp %.3e\n", duv, dpv);
444 CHECK(duv == 0.0 && dpv == 0.0);
453void interfaceLocalCfl() {
455 const double dt = 1.0;
460 s.setPressureGeometry(std::vector<double>((std::size_t)
N *
N *
N, 10.0));
462 std::vector<double> c0((std::size_t)
N *
N *
N), fx(c0.size());
463 for (
int z = 0;
z <
N; ++
z)
464 for (
int y = 0;
y <
N; ++
y)
465 for (
int x = 0;
x <
N; ++
x) {
466 const double dx =
x + 0.5 -
N / 2.0,
dy =
y + 0.5 -
N / 2.0,
dz =
z + 0.5 -
N / 2.0;
467 c0[idx(x, y, z,
N,
N)] =
468 std::fmin(1.0, std::fmax(0.0, 0.5 - (std::sqrt(dx * dx + dy * dy + dz * dz) - 3.0)));
470 const double a = (
z + 0.5 - 3.0) / 1.5;
471 fx[idx(x, y, z,
N,
N)] = 2
e-3 * std::exp(-a * a);
476 s.setField(
"force_x", fx);
477 for (
int it = 0;
it < 40; ++
it)
479 const double local =
s.vofLastCourant();
481 for (
int c = 0;
c < 3; ++
c)
482 global = std::fmax(global,
maxAbs(
s.getVelocity(c)) * dt);
483 std::printf(
"F CFL: interface-local %.4f global %.4f (over-throttle factor %.2fx)\n", local,
484 global, global / std::fmax(local, 1e-300));
485 CHECK(local < 0.5 * global);
490int main(
int argc,
char** argv) {
491 Kokkos::initialize(argc, argv);
497 for (
double ratio : {3.0, 1000.0}) {
498 const double frozen = hydrostaticC(ratio,
true,
false, 100, 0.1, 1.0);
499 const double ref = hydrostaticRhoRef(ratio, 100, 0.1, 1.0);
500 std::printf(
"B2 ratio %-6g frozen-C chain vs hand-set rho: %.17g vs %.17g (%s)\n", ratio,
501 frozen, ref, frozen == ref ?
"BITWISE EQUAL" :
"DIFFER");
502 CHECK(frozen == ref);
503 CHECK(frozen < 1e-12);
504 hydrostaticC(ratio,
false,
false, 100, 0.1, 1.0);
519 perr = hydrostaticC(1000.0,
false,
true, 100, 0.1, 1.0);
520 std::printf(
"E harmonic rho_f dP/dz rel-err %.3e (arithmetic is the consistent mean)\n",
522 }
catch (
const std::exception& ex) {
524 std::printf(
"E harmonic rho_f: the ablation left the boundedness cap altogether — %s\n",
527 CHECK(blewUp || perr > 1e-3);
531 conservation(std::getenv(
"PECLET_VOF_TWOPHASE_LONG") ? 1000 : 200);
538 std::fprintf(stderr,
"%d failure(s)\n",
failures);
static constexpr int kVofG
static constexpr double defaultVofWispEps()
The value enableVof starts from.
Weymouth-Yue split advection of a colour field on an extended (inner + ghost) block.
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
long L3(int x, int y, int z, I3 e)
Kokkos::View< double *, SMem > SField
void fillLeVeque(WyAdvector &a, Block b, double h, double phase)
void periodicFill(SField f, I3 e, int g, bool px, bool py, bool pz)
Periodic wrap of a single-block extended field (serial reference path; the MPI path uses core's GridH...
void initSphere(SField c, Block b, double h, double cx, double cy, double cz, double r, int levels=4)
Sphere of fluid (C = 1 inside), exact to ~5e-7 relative volume at R/h = 16 via the octree helper in p...
Where this block sits in the global grid: o is the global index of inner cell (0,0,...
static double maxAbs(const std::vector< double > &a)
static double maxAbsDiff(const std::vector< double > &a, const std::vector< double > &b)