flow
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_scalar_transport.cpp
Go to the documentation of this file.
1// Scalar transport on the flow Solver: openness-weighted implicit diffusion (2nd-order operator via
2// the discrete-eigenvalue convergence) + explicit conservative advection (machine-precision
3// conservation on a periodic all-fluid box).
4#include <cmath>
5#include <cstdio>
6#include <Kokkos_Core.hpp>
7#include <vector>
8
9#include "flow_ibm.hpp"
10
11namespace {
12int failures = 0;
13#define CHECK(cond) \
14 do { \
15 if (!(cond)) { \
16 std::fprintf(stderr, "CHECK failed: %s\n at %s:%d\n", #cond, __FILE__, __LINE__); \
17 ++failures; \
18 } \
19 } while (0)
20
21std::vector<double> allFluid(int nx, int ny, int nz) {
22 return std::vector<double>((std::size_t)nx * ny * nz, 10.0);
23}
24
25// Discrete Laplacian eigenvalue from one backward-Euler diffusion step of a cosine mode.
26double measureEigen(int N) {
27 const double D = 1.0, dt = 0.5;
29 s.setDt(dt);
30 s.setPressureGeometry(allFluid(N, 4, 4));
31 s.addScalar("c", D, 0, 600);
32 const double k = 2.0 * M_PI / N;
33 std::vector<double> c0((std::size_t)N * 4 * 4);
34 for (int z = 0; z < 4; ++z)
35 for (int y = 0; y < 4; ++y)
36 for (int x = 0; x < N; ++x)
37 c0[(std::size_t)x + (std::size_t)y * N + (std::size_t)z * N * 4] = std::cos(k * x);
38 s.setField("c", c0);
39 s.advanceScalars(); // velocity zero -> pure diffusion
40 auto c1 = s.getField("c");
41 const double peak0 = c0[0], peak1 = c1[0]; // x=y=z=0
42 return (peak0 / peak1 - 1.0) / (dt * D); // mu_h (eigenmode: c1 = c0/(1+dt*D*mu_h))
43}
44
45void run() {
46 // 2nd-order diffusion operator: |mu_h - k^2| ~ O(k^4) => relative error halves^2 per refinement.
47 const double e16 =
48 std::fabs(measureEigen(16) - std::pow(2 * M_PI / 16, 2)) / std::pow(2 * M_PI / 16, 2);
49 const double e32 =
50 std::fabs(measureEigen(32) - std::pow(2 * M_PI / 32, 2)) / std::pow(2 * M_PI / 32, 2);
51 const double order = std::log(e16 / e32) / std::log(2.0);
52 std::printf("diffusion rel-errs %.3e %.3e order %.2f\n", e16, e32, order);
53 CHECK(order > 1.9);
54
55 // Uniform-velocity advection conserves the scalar to machine precision (periodic, openness 1).
56 const int N = 32;
57 const double U = 0.4, dt = 0.5;
59 s.setDt(dt);
60 s.setPressureGeometry(allFluid(N, 4, 4));
61 s.addScalar("c", 0.0, 1, 1); // pure advection, Koren
62 std::vector<double> u((std::size_t)N * 4 * 4, U), zero((std::size_t)N * 4 * 4, 0.0);
63 s.uploadVelocity(u, zero, zero);
64 std::vector<double> c0((std::size_t)N * 4 * 4);
65 double tot0 = 0.0;
66 for (int z = 0; z < 4; ++z)
67 for (int y = 0; y < 4; ++y)
68 for (int x = 0; x < N; ++x) {
69 const double b = std::exp(-std::pow(x - N / 2.0, 2) / (2 * std::pow(N / 12.0, 2)));
70 c0[(std::size_t)x + (std::size_t)y * N + (std::size_t)z * N * 4] = b;
71 tot0 += b;
72 }
73 s.setField("c", c0);
74 for (int it = 0; it < 20; ++it)
75 s.advanceScalars();
76 auto c = s.getField("c");
77 double tot = 0.0, mx = -1e30;
78 for (double v : c) {
79 tot += v;
80 mx = std::fmax(mx, v);
81 }
82 const double cons = std::fabs(tot - tot0) / tot0;
83 std::printf("advection conservation rel %.2e max %.4f (init 1.0)\n", cons, mx);
84 CHECK(cons < 1e-10);
85 CHECK(mx < 1.0 + 1e-7); // no overshoot
86}
87} // namespace
88
89int main(int argc, char** argv) {
90 Kokkos::initialize(argc, argv);
91 run();
92 Kokkos::finalize();
93 if (failures == 0) {
94 std::printf("OK\n");
95 return 0;
96 }
97 std::fprintf(stderr, "%d failure(s)\n", failures);
98 return 1;
99}
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
static constexpr int N
static int run(int bc)
int main(int argc, char **argv)
#define CHECK(cond)