numerics 0.1.0
Loading...
Searching...
No Matches
stencil.hpp
Go to the documentation of this file.
1/// @file pde/stencil.hpp
2/// @brief Higher-order stencil and grid-sweep utilities.
3///
4/// The second-order 2D stencil stores \f$h^2\Delta_h u\f$:
5/// \f[
6/// y_{ij}=u_{i+1,j}+u_{i-1,j}+u_{i,j+1}+u_{i,j-1}-4u_{ij}.
7/// \f]
8/// @todo Add boundary-condition-aware stencil operators and compact higher-
9/// order derivative operators for gradient, divergence, curl, and Laplacian.
10#pragma once
11
12#include "core/vector.hpp"
13#include "fields/field3d.hpp"
14#include "fields/grid2d.hpp"
16#include <algorithm>
17#include <cmath>
18#include <vector>
19
20namespace num {
21
22template<typename T>
24 for (int i = 0; i < N; ++i) {
25 for (int j = 0; j < N; ++j) {
26 int k = (i * N) + j;
27 T val = T(-4) * x[k];
28 if (i > 0)
29 val += x[k - N];
30 if (i < N - 1)
31 val += x[k + N];
32 if (j > 0)
33 val += x[k - 1];
34 if (j < N - 1)
35 val += x[k + 1];
36 y[k] = val;
37 }
38 }
39}
40
41/// @brief Periodic second-order 2D Laplacian stencil.
42template<typename T>
44 for (int i = 0; i < N; ++i) {
45 int ip = (i + 1) % N, im = (i + N - 1) % N;
46 const T* row = x.data() + (i * N);
47 const T* row_p = x.data() + (ip * N);
48 const T* row_m = x.data() + (im * N);
49 T* d = y.data() + (i * N);
50
51 d[0] = row_p[0] + row_m[0] + row[1] + row[N - 1] - (T(4) * row[0]);
52 for (int j = 1; j < N - 1; ++j) {
53 d[j] = row_p[j] + row_m[j] + row[j + 1] + row[j - 1] - (T(4) * row[j]);
54 }
55 d[N - 1] = row_p[N - 1] + row_m[N - 1] + row[0] + row[N - 2] - (T(4) * row[N - 1]);
56 }
57}
58
59/// @brief Fourth-order 2D Laplacian cross stencil.
60///
61/// \f[
62/// y_{ij} = \frac{1}{12}\bigl(
63/// -x_{i-2,j} + 16x_{i-1,j} - 30x_{i,j} + 16x_{i+1,j} - x_{i+2,j}
64/// -x_{i,j-2} + 16x_{i,j-1} + 16x_{i,j+1} - x_{i,j+2}
65/// \bigr)
66/// \f]
67template<typename T>
69 for (int i = 0; i < N; ++i) {
70 for (int j = 0; j < N; ++j) {
71 int k = (i * N) + j;
72 T val = T(-30) * x[k];
73 // i-axis: +/-1
74 if (i > 0) {
75 val += T(16) * x[((i - 1) * N) + j];
76 }
77 if (i < N - 1) {
78 val += T(16) * x[((i + 1) * N) + j];
79 }
80 // i-axis: +/-2
81 if (i > 1) {
82 val -= x[((i - 2) * N) + j];
83 }
84 if (i < N - 2) {
85 val -= x[((i + 2) * N) + j];
86 }
87 // j-axis: +/-1
88 if (j > 0) {
89 val += T(16) * x[k - 1];
90 }
91 if (j < N - 1) {
92 val += T(16) * x[k + 1];
93 }
94 // j-axis: +/-2
95 if (j > 1) {
96 val -= x[k - 2];
97 }
98 if (j < N - 2) {
99 val -= x[k + 2];
100 }
101 y[k] = val / T(12);
102 }
103 }
104}
105
106/// Bilinear interpolation on a periodic NxN grid with configurable stagger
107/// offset.
108///
109/// field[i,j] is defined at physical position ((i + ox/h)*h, (j + oy/h)*h).
110/// Returns the interpolated field value at physical point (px, py).
111///
112/// @param ox x-axis origin offset in physical units (0 for unstaggered, h/2
113/// for v-face)
114/// @param oy y-axis origin offset in physical units (0 for unstaggered, h/2
115/// for u-face)
116///
117/// MAC grid usage:
118/// \f[
119/// \text{interp\_u}(px,py) = \texttt{sample\_2d\_periodic}(u, N, h,\; px,
120/// py,\; 0,\; h/2)
121/// \f]
122/// \f[
123/// \text{interp\_v}(px,py) = \texttt{sample\_2d\_periodic}(v, N, h,\; px,
124/// py,\; h/2,\; 0)
125/// \f]
126inline real sample_2d_periodic(const Vector& field,
127 idx N,
128 real h,
129 real px,
130 real py,
131 real ox,
132 real oy) {
133 real fx = std::fmod((px - ox) / h, static_cast<real>(N));
134 real fy = std::fmod((py - oy) / h, static_cast<real>(N));
135 if (fx < 0.0)
136 fx += N;
137 if (fy < 0.0)
138 fy += N;
139 idx i0 = static_cast<idx>(fx) % N;
140 idx i1 = (i0 + 1) % N;
141 real fi = fx - std::floor(fx);
142 idx j0 = static_cast<idx>(fy) % N;
143 idx j1 = (j0 + 1) % N;
144 real fj = fy - std::floor(fy);
145 return (1 - fi) * (1 - fj) * field[i0 * N + j0] + fi * (1 - fj) * field[i1 * N + j0]
146 + (1 - fi) * fj * field[i0 * N + j1] + fi * fj * field[i1 * N + j1];
147}
148
149/// @brief Apply a mutable 1D operation to each column fiber.
150template<typename T, typename F>
151void col_fiber_sweep(BasicVector<T>& data, int N, F&& f) {
152 std::vector<T> fiber(N);
153 for (int j = 0; j < N; ++j) {
154 for (int i = 0; i < N; ++i)
155 fiber[i] = data[i * N + j];
156 f(fiber);
157 for (int i = 0; i < N; ++i)
158 data[i * N + j] = fiber[i];
159 }
160}
161
162/// @brief Apply a mutable 1D operation to each row fiber.
163template<typename T, typename F>
164void row_fiber_sweep(BasicVector<T>& data, int N, F&& f) {
165 std::vector<T> fiber(N);
166 for (int i = 0; i < N; ++i) {
167 for (int j = 0; j < N; ++j)
168 fiber[j] = data[i * N + j];
169 f(fiber);
170 for (int j = 0; j < N; ++j)
171 data[i * N + j] = fiber[j];
172 }
173}
174
175/// @brief Fill grid values at \f$x_i=(i+1)h,\ y_j=(j+1)h\f$.
176template<typename F>
177void fill_grid(Vector& u, int N, double h, F&& f) {
178 for (int i = 0; i < N; ++i) {
179 double xi = (i + 1) * h;
180 for (int j = 0; j < N; ++j) {
181 u[static_cast<std::size_t>(i) * N + j] = f(xi, (j + 1) * h);
182 }
183 }
184}
185
186template<typename F>
187void fill_grid(ScalarField2D& g, F&& f) {
188 fill_grid(g.vec(), g.N(), g.h(), std::forward<F>(f));
189}
190
194
196 laplacian_stencil_2d_4th(x.vec(), y.vec(), x.N());
197}
198
200 real px,
201 real py,
202 real ox = 0.0,
203 real oy = 0.0) {
204 return sample_2d_periodic(g.vec(), static_cast<idx>(g.N()), g.h(), px, py, ox, oy);
205}
206
207/// @brief Compute \f$-\Delta_h x\f$ on a 3D grid.
208inline void neg_laplacian_3d(const Vector& x,
209 Vector& y,
210 int nx,
211 int ny,
212 int nz,
213 double inv_dx2) {
214 auto flat = [&](int i, int j, int k) -> idx {
215 return static_cast<idx>(k * ny * nx + j * nx + i);
216 };
217 for (int k = 0; k < nz; ++k)
218 for (int j = 0; j < ny; ++j)
219 for (int i = 0; i < nx; ++i) {
220 idx id = flat(i, j, k);
221 if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1 || k == 0 || k == nz - 1) {
222 y[id] = x[id];
223 } else {
224 y[id] = inv_dx2
225 * (6.0 * x[id] - x[flat(i + 1, j, k)] - x[flat(i - 1, j, k)]
226 - x[flat(i, j + 1, k)] - x[flat(i, j - 1, k)] - x[flat(i, j, k + 1)]
227 - x[flat(i, j, k - 1)]);
228 }
229 }
230}
231
232/// @brief Compute \f$\nabla\phi\f$ with central differences.
233inline void gradient_3d(const ScalarField3D& phi,
234 ScalarField3D& gx,
235 ScalarField3D& gy,
236 ScalarField3D& gz) {
237 int nx = phi.nx(), ny = phi.ny(), nz = phi.nz();
238 double inv2dx = 1.0 / (2.0 * phi.dx());
239 for (int k = 0; k < nz; ++k)
240 for (int j = 0; j < ny; ++j)
241 for (int i = 0; i < nx; ++i) {
242 int ip = std::min(i + 1, nx - 1), im = std::max(i - 1, 0);
243 int jp = std::min(j + 1, ny - 1), jm = std::max(j - 1, 0);
244 int kp = std::min(k + 1, nz - 1), km = std::max(k - 1, 0);
245 gx(i, j, k) = (phi(ip, j, k) - phi(im, j, k)) * inv2dx;
246 gy(i, j, k) = (phi(i, jp, k) - phi(i, jm, k)) * inv2dx;
247 gz(i, j, k) = (phi(i, j, kp) - phi(i, j, km)) * inv2dx;
248 }
249}
250
251/// @brief Compute \f$\nabla\cdot f\f$ with central differences.
252inline void divergence_3d(const ScalarField3D& fx,
253 const ScalarField3D& fy,
254 const ScalarField3D& fz,
255 ScalarField3D& out) {
256 int nx = fx.nx(), ny = fx.ny(), nz = fx.nz();
257 double inv2dx = 1.0 / (2.0 * fx.dx());
258 for (int k = 0; k < nz; ++k)
259 for (int j = 0; j < ny; ++j)
260 for (int i = 0; i < nx; ++i) {
261 int ip = std::min(i + 1, nx - 1), im = std::max(i - 1, 0);
262 int jp = std::min(j + 1, ny - 1), jm = std::max(j - 1, 0);
263 int kp = std::min(k + 1, nz - 1), km = std::max(k - 1, 0);
264 out(i, j, k) = ((fx(ip, j, k) - fx(im, j, k)) + (fy(i, jp, k) - fy(i, jm, k))
265 + (fz(i, j, kp) - fz(i, j, km)))
266 * inv2dx;
267 }
268}
269
270/// @brief Compute \f$\nabla\times A\f$ with central differences.
271inline void curl_3d(const ScalarField3D& ax,
272 const ScalarField3D& ay,
273 const ScalarField3D& az,
274 ScalarField3D& bx,
275 ScalarField3D& by,
276 ScalarField3D& bz) {
277 int nx = ax.nx(), ny = ax.ny(), nz = ax.nz();
278 double inv2dx = 1.0 / (2.0 * ax.dx());
279 for (int k = 0; k < nz; ++k)
280 for (int j = 0; j < ny; ++j)
281 for (int i = 0; i < nx; ++i) {
282 int ip = std::min(i + 1, nx - 1), im = std::max(i - 1, 0);
283 int jp = std::min(j + 1, ny - 1), jm = std::max(j - 1, 0);
284 int kp = std::min(k + 1, nz - 1), km = std::max(k - 1, 0);
285 bx(i, j, k) =
286 (az(i, jp, k) - az(i, jm, k) - ay(i, j, kp) + ay(i, j, km)) * inv2dx;
287 by(i, j, k) =
288 (ax(i, j, kp) - ax(i, j, km) - az(ip, j, k) + az(im, j, k)) * inv2dx;
289 bz(i, j, k) =
290 (ay(ip, j, k) - ay(im, j, k) - ax(i, jp, k) + ax(i, jm, k)) * inv2dx;
291 }
292}
293
294} // namespace num
Dense owning vector.
Definition vector.hpp:16
int nx() const
Definition field3d.hpp:49
float dx() const
Definition field3d.hpp:52
int ny() const
Definition field3d.hpp:50
int nz() const
Definition field3d.hpp:51
3D scalar and vector fields on uniform Cartesian grids.
2D uniform interior grid: geometry only, no field data.
void laplacian_stencil_2d(const BasicVector< T > &x, BasicVector< T > &y, int N)
Definition stencil.hpp:23
void row_fiber_sweep(BasicVector< T > &data, int N, F &&f)
Apply a mutable 1D operation to each row fiber.
Definition stencil.hpp:164
double real
Definition types.hpp:10
constexpr real phi
Golden ratio.
Definition math.hpp:45
void neg_laplacian_3d(const Vector &x, Vector &y, int nx, int ny, int nz, double inv_dx2)
Compute on a 3D grid.
Definition stencil.hpp:208
real sample_2d_periodic(const Vector &field, idx N, real h, real px, real py, real ox, real oy)
Definition stencil.hpp:126
void curl_3d(const ScalarField3D &ax, const ScalarField3D &ay, const ScalarField3D &az, ScalarField3D &bx, ScalarField3D &by, ScalarField3D &bz)
Compute with central differences.
Definition stencil.hpp:271
void laplacian_stencil_2d_4th(const BasicVector< T > &x, BasicVector< T > &y, int N)
Fourth-order 2D Laplacian cross stencil.
Definition stencil.hpp:68
std::size_t idx
Definition types.hpp:11
void gradient_3d(const ScalarField3D &phi, ScalarField3D &gx, ScalarField3D &gy, ScalarField3D &gz)
Compute with central differences.
Definition stencil.hpp:233
void laplacian_stencil_2d_periodic(const BasicVector< T > &x, BasicVector< T > &y, int N)
Periodic second-order 2D Laplacian stencil.
Definition stencil.hpp:43
void col_fiber_sweep(BasicVector< T > &data, int N, F &&f)
Apply a mutable 1D operation to each column fiber.
Definition stencil.hpp:151
void divergence_3d(const ScalarField3D &fx, const ScalarField3D &fy, const ScalarField3D &fz, ScalarField3D &out)
Compute with central differences.
Definition stencil.hpp:252
void fill_grid(Vector &u, int N, double h, F &&f)
Fill grid values at .
Definition stencil.hpp:177
Scalar field on a 2D uniform interior grid.
Dense vector storage and operations.