29#include <Kokkos_Core.hpp>
46 std::fprintf(stderr, "CHECK failed: %s\n at %s:%d\n", #cond, __FILE__, __LINE__); \
51std::size_t idx(
int x,
int y,
int z,
int nx,
int ny) {
52 return (std::size_t)x + (std::size_t)y * nx + (std::size_t)z * (std::size_t)nx * ny;
54double maxAbs(
const std::vector<double>& v) {
57 m = std::fmax(
m, std::fabs(
x));
61double maxFaceVel(S& s) {
62 return std::fmax(std::fmax(
maxAbs(
s.getFaceVelocity(0)),
maxAbs(
s.getFaceVelocity(1))),
67std::vector<double> sphereC(
int n,
double R,
double cx,
double cy,
double cz,
int sub = 24) {
68 std::vector<double>
C((std::size_t)n * n * n, 0.0);
69 const double w = 1.0 / sub;
70 for (
int k = 0;
k <
n; ++
k)
71 for (
int j = 0;
j <
n; ++
j)
72 for (
int i = 0;
i <
n; ++
i) {
74 for (
int b = 0;
b < sub; ++
b)
75 for (
int a = 0;
a < sub; ++
a) {
76 const double px =
i + (
a + 0.5) * w, py = j + (b + 0.5) *
w;
77 const double r2 =
R *
R - (px - cx) * (px - cx) - (py - cy) * (py - cy);
80 const double h = std::sqrt(r2);
81 const double lo = std::fmax(cz - h, (
double)k), hi = std::fmin(cz + h, (
double)k + 1);
85 C[idx(i, j, k, n, n)] =
acc / (sub * sub);
92 double ratio = 1.0, mu = 0.1, sigma = 1.0;
93 bool walls =
false, plane =
false;
97double runDrop(
const DropCfg& c,
bool on,
long* bfIters =
nullptr) {
99 const double R = 8.0, cx =
n / 2 + 0.13, cy =
n / 2 + 0.27, cz =
n / 2 + 0.11;
104 s.setVelocityResidualTolerance(0.0);
106 for (
int f = 0;
f < 6; ++
f)
107 s.setDomainBc(f, 1, 0, 0, 0);
109 std::vector<double>
sdf((std::size_t)n * n * n);
110 for (
int z = 0;
z <
n; ++
z)
111 for (
int y = 0;
y <
n; ++
y)
112 for (
int x = 0;
x <
n; ++
x)
113 sdf[idx(x, y, z, n, n)] = (
z + 0.5) - (cz - 0.5 * R);
114 s.setSolid(sdf,
true);
116 s.setPressureGeometry(std::vector<double>((std::size_t)n * n * n, 10.0));
118 s.setPressureChebyshev(
true, 500, 1e-14);
120 s.setVof(sphereC(n, R, cx, cy, cz));
121 s.setPropertyModel(
"rho", ClosureKind::LinearMix,
"C",
"", {1.0,
c.ratio - 1.0});
122 s.setSurfaceTension(
c.sigma);
123 s.setVofKappaConstant(0.25);
124 s.setDt(0.5 *
s.capillaryDt());
126 s.setBalancedForceProjection(
true);
127 s.setPressureChebyshev(
true, 500, 1e-14);
129 for (
int k = 0;
k <
c.steps; ++
k) {
131 it = std::max(it,
s.lastBalancedForceIterations());
135 return maxFaceVel(s);
140 double faceU = 0, pErr = 0;
143HydroResult
hydrostatic(
double ratio,
double mu,
int steps,
bool sphere,
bool on) {
145 const double g = 0.1;
150 s.setVelocityResidualTolerance(0.0);
151 s.setDomainBc(4, 1, 0, 0, 0);
152 s.setDomainBc(5, 1, 0, 0, 0);
153 std::vector<double>
sdf((std::size_t)
N *
N *
NZ, 10.0);
155 for (
int z = 0;
z <
NZ; ++
z)
156 for (
int y = 0;
y <
N; ++
y)
157 for (
int x = 0;
x <
N; ++
x) {
158 const double dx =
x + 0.5 - 8.2,
dy =
y + 0.5 - 7.9,
dz =
z + 0.5 - 16.3;
159 sdf[idx(x, y, z,
N,
N)] = std::sqrt(dx * dx + dy * dy + dz * dz) - 5.0;
162 s.setSolid(sdf,
true);
164 s.setPressureGeometry(sdf);
165 auto heavy = [&](
int z) {
return z <
NZ / 2; };
166 std::vector<double> fld((std::size_t)
N *
N *
NZ);
167 for (
int z = 0;
z <
NZ; ++
z)
168 for (
int y = 0;
y <
N; ++
y)
169 for (
int x = 0;
x <
N; ++
x)
170 fld[idx(x, y, z,
N,
N)] = heavy(z) ?
ratio : 1.0;
172 s.setField(
"rho", fld);
173 s.setDensityMode(
true);
174 s.setPropertyModel(
"force_z", ClosureKind::LinearMix,
"rho",
"", std::vector<double>{0.0, -
g});
176 s.setBalancedForceProjection(
true);
177 for (
int k = 0;
k <
steps; ++
k)
180 r.faceU = maxFaceVel(s);
181 const auto p =
s.getPressure();
182 for (
int z = 1;
z <
NZ; ++
z)
183 for (
int y = 0;
y <
N; ++
y)
184 for (
int x = 0;
x <
N; ++
x) {
185 if (sdf[idx(x, y, z,
N,
N)] < 0.0 || sdf[idx(x, y, z - 1,
N,
N)] < 0.0)
187 const double dp =
p[idx(x, y, z,
N,
N)] -
p[idx(x, y, z - 1,
N,
N)];
188 const double rf = 0.5 * ((heavy(z) ?
ratio : 1.0) + (heavy(z - 1) ?
ratio : 1.0));
189 r.pErr = std::fmax(
r.pErr, std::fabs(dp + g * rf) / (g * ratio));
195 std::printf(
"\n=== (b) staggered drop, constant kappa, 30 steps: face |u| OFF -> ON\n");
196 for (
double ratio : {1.0, 10.0, 100.0, 1000.0}) {
200 const double off = runDrop(c,
false), on = runDrop(c,
true, &it);
201 std::printf(
" ratio %6g OFF %.3e ON %.3e (balanced-force iters max %ld)\n", ratio, off, on,
205 for (
double mu : {0.0, 0.01, 0.1}) {
209 const double off = runDrop(c,
false), on = runDrop(c,
true);
210 std::printf(
" ratio 1000 mu %-5g OFF %.3e ON %.3e\n", mu, off, on);
213 std::printf(
"\n=== (d) low-Ca walled drop (sigma 0.01, mu 10, walls on every face), 30 steps\n");
214 for (
double ratio : {1.0, 1000.0}) {
220 const double off = runDrop(c,
false), on = runDrop(c,
true);
221 std::printf(
" ratio %6g OFF %.3e (reported) ON %.3e\n", ratio, off, on);
225 "\n=== (e)(ii) constant-kappa sessile cap: the drop cut by an immersed plane, mu 0.1\n");
226 for (
double ratio : {1.0, 1000.0}) {
230 const double off = runDrop(c,
false), on = runDrop(c,
true);
231 std::printf(
" ratio %6g OFF %.3e (reported) ON %.3e\n", ratio, off, on);
238 "\n=== (c) hydrostatic column, walled, ratio 1000: ON after ONE step (OFF 100 steps)\n");
239 for (
double mu : {0.0, 1
e-3, 1
e-2, 1
e-1}) {
241 const auto on =
hydrostatic(1000.0, mu, 1,
false,
true);
242 std::printf(
" mu %-6g OFF face %.3e dP/dz %.3e | ON(1 step) face %.3e dP/dz %.3e\n", mu,
243 off.faceU,
off.pErr, on.faceU, on.pErr);
244 CHECK(on.faceU < 1e-12);
245 CHECK(on.pErr < 1e-10);
248 "\n=== (e)(i) ratio-1000 column with a solid sphere (R = 5) across the interface, "
249 "mu 0.1, 30 steps\n");
251 const auto on =
hydrostatic(1000.0, 0.1, 30,
true,
true);
252 std::printf(
" OFF face %.3e dP/dz %.3e (reported) | ON face %.3e dP/dz %.3e\n",
off.faceU,
253 off.pErr, on.faceU, on.pErr);
254 CHECK(on.faceU < 1e-12);
255 CHECK(on.pErr < 1e-10);
258void expectThrow(
const char* what,
const std::function<
void()>& f) {
263 }
catch (
const std::exception& e) {
267 std::printf(
" %-34s -> throws: %s%s%.90s\n", what, threw ?
"yes" :
"NO", threw ?
" " :
"",
273 std::printf(
"\n=== (f) refusals\n");
275 const std::vector<double>
fluid((std::size_t)
N *
N *
N, 10.0);
276 auto base = [&](
auto&
s) {
281 expectThrow(
"porous continuity", [&] {
284 s.setPressureGeometry(fluid);
285 s.setPorousContinuity(
true);
286 s.setBalancedForceProjection(
true);
289 expectThrow(
"ghost projection", [&] {
292 s.setGhostProjection(
true);
293 s.setBalancedForceProjection(
true);
294 s.setPressureGeometry(fluid);
297 expectThrow(
"block CSF", [&] {
300 s.setPressureGeometry(fluid);
302 s.enableVofBlocks({{{
N / 2.0,
N / 2.0,
N / 2.0, 4.0}}});
303 s.setSurfaceTension(0.01);
304 s.enableVofBlockCsf();
305 s.setBalancedForceProjection(
true);
308 expectThrow(
"inflow/outflow face", [&] {
311 s.setDomainBc(0, 2, 0.1, 0.0, 0.0);
312 s.setDomainBc(1, 3, 0, 0, 0);
313 s.setPressureGeometry(fluid);
314 s.setBalancedForceProjection(
true);
322 s.setDomainBc(0, 2, 0.1, 0.0, 0.0);
323 s.setDomainBc(1, 3, 0, 0, 0);
324 s.setPressureGeometry(fluid);
325 s.setDensityMode(
true);
330 }
catch (
const std::exception& e) {
332 std::printf(
" V8 default + open face threw: %s\n",
e.what());
334 std::printf(
" V8 default + inflow/outflow: %s, option %s\n", threw ?
"THREW" :
"ran",
335 s.balancedForceProjection() ?
"ON" :
"OFF");
337 CHECK(!
s.balancedForceProjection());
339 expectThrow(
"V8 + inflow/outflow face, explicit ON", [&] {
342 s.setDomainBc(0, 2, 0.1, 0.0, 0.0);
343 s.setDomainBc(1, 3, 0, 0, 0);
344 s.setPressureGeometry(fluid);
345 s.setDensityMode(
true);
346 s.setBalancedForceProjection(
true);
349 expectThrow(
"non-incremental pressure", [&] {
352 s.setPressureGeometry(fluid);
353 s.setIncrementalPressure(
false);
354 s.setBalancedForceProjection(
true);
361int main(
int argc,
char** argv) {
362 Kokkos::initialize(argc, argv);
370 std::fprintf(stderr,
"\n%d CHECK(s) failed\n",
failures);
373 std::printf(
"\nAll staggered balanced-force projection gates passed.\n");
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
hydrostatic(ratio, mu=0.0, steps=100, g=0.1, dt=1.0, N=8, NZ=24)
static double maxAbs(const std::vector< double > &a)