22#include <Kokkos_Core.hpp>
33 std::fprintf(stderr, "CHECK failed: %s\n at %s:%d\n", #cond, __FILE__, __LINE__); \
38double maxAbsDiff(
const std::vector<double>& a,
const std::vector<double>& b) {
40 for (std::size_t i = 0; i < a.size(); ++i) {
41 const double d = std::fabs(a[i] - b[i]);
47double maxAbs(
const std::vector<double>& a) {
50 if (!(std::fabs(
v) <=
m))
60constexpr double RHO = 1.0,
MU = 0.1,
DT = 50.0,
F = 0.01;
63std::vector<double> runChannel(
int nx,
int ny,
int nz,
double ylo,
double yhi,
int slipFace,
64 bool cutcellPressure,
int& stepsTaken) {
71 s.setBodyForce(
F, 0.0, 0.0);
72 s.setVelocityIterations(400);
73 s.setVelocityResidualTolerance(1e-12);
74 s.setPressurePcg(
true, 50, 1e-11);
76 s.setDomainBc(slipFace, 4, 0.0, 0.0, 0.0);
77 std::vector<double>
sdf((std::size_t)nx * ny * nz);
78 for (
int z = 0;
z < nz; ++
z)
79 for (
int y = 0;
y <
ny; ++
y)
80 for (
int x = 0;
x < nx; ++
x) {
83 d = std::min(y - ylo, yhi - y);
84 else if (slipFace == 3)
88 sdf[(std::size_t)x + (std::size_t)
y * nx + (std::size_t)z * nx * ny] = d;
90 s.setSolid(sdf, cutcellPressure);
93 for (
int it = 0;
it < 800; ++
it) {
96 const double now =
maxAbs(
s.getVelocity(0));
97 if (it > 5 && std::fabs(now - prev) < 1e-12 * (now + 1e-300))
101 std::vector<double>
out =
s.getVelocity(0);
102 std::vector<double>
v =
s.getVelocity(1);
109void gateChannel(
const char* grid,
bool cutcellPressure) {
110 const int nx = 8, nz = 8, nyH = 16, nyF = 32;
111 const double ylo = 3.5, yhi = 2.0 * (nyH - 0.5) - ylo;
112 int stF = 0, stT = 0,
stB = 0;
114 const std::vector<double> uF = runChannel<Grid>(nx, nyF, nz, ylo, yhi, -1, cutcellPressure, stF);
116 const std::vector<double> uT = runChannel<Grid>(nx, nyH, nz, ylo, yhi, 3, cutcellPressure, stT);
118 const std::vector<double>
uB =
119 runChannel<Grid>(nx, nyH, nz, ylo, yhi - 16.0, 2, cutcellPressure, stB);
120 auto at = [&](
const std::vector<double>&
u,
int ny,
int x,
int y,
int z) {
121 return u[(std::size_t)x + (std::size_t)
y * nx + (std::size_t)z * nx * ny];
123 const double c =
F / (2.0 *
MU);
124 double errF = 0, errT = 0, errB = 0, dT = 0, dB = 0,
umax = 0;
125 for (
int z = 0;
z < nz; ++
z)
126 for (
int x = 0;
x < nx; ++
x) {
127 for (
int y = 0;
y < nyF; ++
y) {
128 if (y <= ylo || y >= yhi)
130 const double ana =
c * (
y - ylo) * (yhi - y);
131 errF = std::max(errF, std::fabs(at(uF, nyF, x, y, z) - ana));
132 umax = std::max(umax, ana);
134 errT = std::max(errT, std::fabs(at(uT, nyH, x, y, z) - ana));
135 dT = std::max(dT, std::fabs(at(uT, nyH, x, y, z) - at(uF, nyF, x, y, z)));
137 errB = std::max(errB, std::fabs(at(uB, nyH, x, y - 16, z) - ana));
138 dB = std::max(dB, std::fabs(at(uB, nyH, x, y - 16, z) - at(uF, nyF, x, y, z)));
143 "[A %s] u_max %.4f node error vs parabola: full %.2e (steps %d) top-slip half %.2e (%d) "
144 "bottom-slip half %.2e (%d); half-vs-full pointwise: top %.2e bottom %.2e\n",
145 grid, umax, errF, stF, errT, stT, errB, stB, dT, dB);
150 const double tol = 3
e-5 *
umax;
163void gateUniform(
const char* grid,
bool advection) {
164 const int n = 8,
steps = 20;
165 const double dt = 1.0,
f = 0.01;
170 s.setBodyForce(f, 0.0, 0.0);
171 s.setAdvection(advection);
172 s.setVelocityIterations(200);
173 s.setPressurePcg(
true, 50, 1e-11);
174 for (
int face = 2; face < 6; ++face)
175 s.setDomainBc(face, 4, 0.0, 0.0, 0.0);
176 s.setPressureGeometry(std::vector<double>((std::size_t)n * n * n, 1e30));
179 const std::vector<double>
u =
s.getVelocity(0),
v =
s.getVelocity(1),
w =
s.getVelocity(2);
183 if (!(std::fabs(
x - uex) <=
du))
184 du = std::fabs(
x - uex);
185 const double div =
s.maxOpenDivergence();
187 "[B %s advection=%d] max|u - F t/rho| = %.2e (u = %.3f) max|v| = %.2e max|w| = %.2e "
189 grid, (
int)advection, du, uex,
maxAbs(v),
maxAbs(w), div);
196 const bool dbl = !Grid::collocated && !advection;
197 CHECK(du < (dbl ? 1e-10 : 1
e-6 * uex));
209 const int nx = 16,
ny = 16, nz = 16,
steps = 8;
210 const double R = 4.3, cx = 8.0, cy = 8.0, cz = 8.0;
212 std::vector<double>
sdf((std::size_t)nx *
NY * nz);
213 for (
int z = 0;
z < nz; ++
z)
214 for (
int y = 0;
y <
NY; ++
y)
215 for (
int x = 0;
x < nx; ++
x) {
216 const double dx =
x - cx,
dz =
z - cz;
217 double d = std::sqrt(dx * dx + (y - cy) * (y - cy) + dz * dz) -
R;
219 const double yi = 2.0 *
ny - 1.0 - cy;
220 d = std::min(d, std::sqrt(dx * dx + (y - yi) * (y - yi) + dz * dz) - R);
222 sdf[(std::size_t)x + (std::size_t)
y * nx + (std::size_t)z * nx *
NY] = d;
230 s.setBodyForce(1.0, 0.0, 0.0);
231 s.setAdvection(
false);
232 s.setVelocityIterations(2000);
233 s.setVelocityResidualTolerance(1e-12);
234 s.setPressureLevels(3);
235 s.setPressurePcg(
true, 200, 1e-12);
239 sh.setDomainBc(2, 4, 0.0, 0.0, 0.0);
240 sh.setDomainBc(3, 4, 0.0, 0.0, 0.0);
249 double d[3] = {0, 0, 0},
umax = 0;
250 for (
int c = 0;
c < 3; ++
c) {
251 const std::vector<double> uh =
sh.getVelocity(c), uf =
sf.getVelocity(c);
252 for (
int z = 0;
z < nz; ++
z)
253 for (
int y = 0;
y <
ny; ++
y)
254 for (
int x = 0;
x < nx; ++
x) {
255 const std::size_t ih = (std::size_t)x + (std::size_t)
y * nx + (std::size_t)z * nx * ny;
256 const std::size_t iF =
257 (std::size_t)x + (std::size_t)
y * nx + (std::size_t)z * nx * (2 * ny);
258 const double dd = std::fabs(uh[ih] - uf[iF]);
261 umax = std::max(umax, std::fabs(uf[iF]));
264 const double divh =
sh.maxOpenDivergence(),
divf =
sf.maxOpenDivergence();
266 "[C] sphere in a slip box vs mirror-periodic twin: max|du| %.2e |dv| %.2e |dw| %.2e "
267 "(u_max %.3e, rel %.2e) div %.1e / %.1e\n",
268 d[0], d[1], d[2], umax, std::max({
d[0],
d[1],
d[2]}) / umax, divh, divf);
269 CHECK(std::max({
d[0],
d[1],
d[2]}) / umax < 1e-7);
271 CHECK(divh / umax < 1e-6 && divf / umax < 1e-6);
275int main(
int argc,
char** argv) {
276 Kokkos::initialize(argc, argv);
278 std::printf(
"[freeslip] exec %s\n", Kokkos::DefaultExecutionSpace::name());
279 gateChannel<peclet::flow::Staggered>(
"staggered",
true);
280 gateChannel<peclet::flow::Colocated>(
"collocated",
false);
281 gateUniform<peclet::flow::Staggered>(
"staggered",
false);
282 gateUniform<peclet::flow::Staggered>(
"staggered",
true);
283 gateUniform<peclet::flow::Colocated>(
"collocated",
false);
284 gateUniform<peclet::flow::Colocated>(
"collocated",
true);
287 std::printf(
"[freeslip] PASS: type-4 free-slip faces reproduce the symmetric problems\n");
289 std::fprintf(stderr,
"[freeslip] FAILED (%d checks)\n",
failures);
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
static std::vector< double > sphereSdf()
static void configure(IbmSolver &s)
static double maxAbs(const std::vector< double > &a)
static double maxAbsDiff(const std::vector< double > &a, const std::vector< double > &b)
static constexpr double DT
static constexpr double RHO
static constexpr double MU
static constexpr double F