numerics
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/// 2D (NxN grid, row-major storage: index = i*N + j, Dirichlet or periodic BC):
5/// laplacian_stencil_2d -- y = (Σ4 nbrs) - 4x, Dirichlet
6/// laplacian_stencil_2d_periodic -- same, periodic wrap (boundary-peeled for vectorization)
7/// col_fiber_sweep -- for each column j: extract fiber, call f, write back
8/// row_fiber_sweep -- for each row i: extract fiber, call f, write back
9///
10/// 3D (Grid3D, central-difference, one-sided at boundaries):
11/// neg_laplacian_3d -- y = -∆x (Dirichlet: boundary rows = identity)
12/// gradient_3d -- (gx,gy,gz) = ∇φ
13/// divergence_3d -- div f = ∂fx/∂x + ∂fy/∂y + ∂fz/∂z
14/// curl_3d -- ∇×A
15#pragma once
16
17#include "core/vector.hpp"
18#include "spatial/grid3d.hpp"
19#include <vector>
20#include <algorithm>
21
22namespace num {
23
24// ============================================================
25// 2-D stencils (NxN, row-major: data[i*N + j])
26// ============================================================
27
28/// Compute y[i,j] = x[i+1,j] + x[i-1,j] + x[i,j+1] + x[i,j-1] - 4*x[i,j]
29/// Dirichlet: out-of-bounds neighbors contribute 0.
30/// Works for T = real or cplx.
31template<typename T>
33 for (int i = 0; i < N; ++i) {
34 for (int j = 0; j < N; ++j) {
35 int k = i * N + j;
36 T val = T(-4) * x[k];
37 if (i > 0) val += x[k - N];
38 if (i < N-1) val += x[k + N];
39 if (j > 0) val += x[k - 1];
40 if (j < N-1) val += x[k + 1];
41 y[k] = val;
42 }
43 }
44}
45
46/// Same as laplacian_stencil_2d but with periodic (wrap-around) boundaries.
47///
48/// The j boundaries (j=0 and j=N-1) are peeled out of the inner loop so the
49/// interior range j=1..N-2 contains no modulo arithmetic and is auto-vectorizable.
50/// Row wrapping (i±1) uses precomputed indices.
51template<typename T>
53 for (int i = 0; i < N; ++i) {
54 int ip = (i + 1) % N, im = (i + N - 1) % N;
55 const T* row = x.data() + i * N;
56 const T* row_p = x.data() + ip * N;
57 const T* row_m = x.data() + im * N;
58 T* d = y.data() + i * N;
59
60 // j = 0: left neighbor wraps to j = N-1
61 d[0] = row_p[0] + row_m[0] + row[1] + row[N-1] - T(4) * row[0];
62 // j = 1..N-2: no wrap, compiler can vectorise
63 for (int j = 1; j < N - 1; ++j)
64 d[j] = row_p[j] + row_m[j] + row[j+1] + row[j-1] - T(4) * row[j];
65 // j = N-1: right neighbor wraps to j = 0
66 d[N-1] = row_p[N-1] + row_m[N-1] + row[0] + row[N-2] - T(4) * row[N-1];
67 }
68}
69
70/// For each column j in [0,N), extract the x-direction fiber
71/// data[0..N-1, j] into a std::vector<T>, call f(fiber), then write back.
72///
73/// f must have signature: void(std::vector<T>&) and modifies in-place.
74///
75/// Use for ADI / Crank-Nicolson sweeps along x.
76template<typename T, typename F>
77void col_fiber_sweep(BasicVector<T>& data, int N, F&& f) {
78 std::vector<T> fiber(N);
79 for (int j = 0; j < N; ++j) {
80 for (int i = 0; i < N; ++i) fiber[i] = data[i*N + j];
81 f(fiber);
82 for (int i = 0; i < N; ++i) data[i*N + j] = fiber[i];
83 }
84}
85
86/// For each row i in [0,N), extract the y-direction fiber
87/// data[i, 0..N-1] into a std::vector<T>, call f(fiber), then write back.
88///
89/// Use for ADI / Crank-Nicolson sweeps along y.
90template<typename T, typename F>
91void row_fiber_sweep(BasicVector<T>& data, int N, F&& f) {
92 std::vector<T> fiber(N);
93 for (int i = 0; i < N; ++i) {
94 for (int j = 0; j < N; ++j) fiber[j] = data[i*N + j];
95 f(fiber);
96 for (int j = 0; j < N; ++j) data[i*N + j] = fiber[j];
97 }
98}
99
100// ============================================================
101// 3-D stencils (Grid3D)
102// ============================================================
103
104/// Compute the negative Laplacian: y = -∆x = (6x[i,j,k] - Σ6 nbrs) / dx²
105/// Dirichlet BC: boundary nodes satisfy y[bdry] = x[bdry] (identity row,
106/// so that the system is SPD when used with a b=0 boundary RHS).
107///
108/// x and y are flat vectors in Grid3D layout: idx = k*ny*nx + j*nx + i.
109inline void neg_laplacian_3d(const Vector& x, Vector& y,
110 int nx, int ny, int nz, double inv_dx2) {
111 auto flat = [&](int i, int j, int k) -> idx {
112 return static_cast<idx>(k * ny * nx + j * nx + i);
113 };
114 for (int k = 0; k < nz; ++k)
115 for (int j = 0; j < ny; ++j)
116 for (int i = 0; i < nx; ++i) {
117 idx id = flat(i,j,k);
118 if (i==0||i==nx-1||j==0||j==ny-1||k==0||k==nz-1) {
119 y[id] = x[id];
120 } else {
121 y[id] = inv_dx2 * (6.0 * x[id]
122 - x[flat(i+1,j,k)] - x[flat(i-1,j,k)]
123 - x[flat(i,j+1,k)] - x[flat(i,j-1,k)]
124 - x[flat(i,j,k+1)] - x[flat(i,j,k-1)]);
125 }
126 }
127}
128
129/// Compute the central-difference gradient of a scalar field.
130/// One-sided differences at domain boundaries.
131/// gx, gy, gz must already be allocated with the same dimensions as phi.
132inline void gradient_3d(const Grid3D& phi,
133 Grid3D& gx, Grid3D& gy, Grid3D& gz) {
134 int nx = phi.nx(), ny = phi.ny(), nz = phi.nz();
135 double inv2dx = 1.0 / (2.0 * phi.dx());
136 for (int k = 0; k < nz; ++k)
137 for (int j = 0; j < ny; ++j)
138 for (int i = 0; i < nx; ++i) {
139 int ip = std::min(i+1,nx-1), im = std::max(i-1,0);
140 int jp = std::min(j+1,ny-1), jm = std::max(j-1,0);
141 int kp = std::min(k+1,nz-1), km = std::max(k-1,0);
142 gx(i,j,k) = (phi(ip,j,k) - phi(im,j,k)) * inv2dx;
143 gy(i,j,k) = (phi(i,jp,k) - phi(i,jm,k)) * inv2dx;
144 gz(i,j,k) = (phi(i,j,kp) - phi(i,j,km)) * inv2dx;
145 }
146}
147
148/// Compute the central-difference divergence of a vector field (fx, fy, fz).
149/// One-sided differences at domain boundaries.
150/// out must already be allocated with the same dimensions.
151inline void divergence_3d(const Grid3D& fx, const Grid3D& fy, const Grid3D& fz,
152 Grid3D& out) {
153 int nx = fx.nx(), ny = fx.ny(), nz = fx.nz();
154 double inv2dx = 1.0 / (2.0 * fx.dx());
155 for (int k = 0; k < nz; ++k)
156 for (int j = 0; j < ny; ++j)
157 for (int i = 0; i < nx; ++i) {
158 int ip = std::min(i+1,nx-1), im = std::max(i-1,0);
159 int jp = std::min(j+1,ny-1), jm = std::max(j-1,0);
160 int kp = std::min(k+1,nz-1), km = std::max(k-1,0);
161 out(i,j,k) = ((fx(ip,j,k) - fx(im,j,k))
162 + (fy(i,jp,k) - fy(i,jm,k))
163 + (fz(i,j,kp) - fz(i,j,km))) * inv2dx;
164 }
165}
166
167/// Compute the central-difference curl ∇×A of a vector field.
168/// One-sided differences at domain boundaries.
169/// bx, by, bz must already be allocated with the same dimensions as ax, ay, az.
170inline void curl_3d(const Grid3D& ax, const Grid3D& ay, const Grid3D& az,
171 Grid3D& bx, Grid3D& by, Grid3D& bz) {
172 int nx = ax.nx(), ny = ax.ny(), nz = ax.nz();
173 double inv2dx = 1.0 / (2.0 * ax.dx());
174 for (int k = 0; k < nz; ++k)
175 for (int j = 0; j < ny; ++j)
176 for (int i = 0; i < nx; ++i) {
177 int ip = std::min(i+1,nx-1), im = std::max(i-1,0);
178 int jp = std::min(j+1,ny-1), jm = std::max(j-1,0);
179 int kp = std::min(k+1,nz-1), km = std::max(k-1,0);
180 bx(i,j,k) = (az(i,jp,k) - az(i,jm,k) - ay(i,j,kp) + ay(i,j,km)) * inv2dx;
181 by(i,j,k) = (ax(i,j,kp) - ax(i,j,km) - az(ip,j,k) + az(im,j,k)) * inv2dx;
182 bz(i,j,k) = (ay(ip,j,k) - ay(im,j,k) - ax(i,jp,k) + ax(i,jm,k)) * inv2dx;
183 }
184}
185
186} // namespace num
Dense vector with optional GPU storage, templated over scalar type T.
Definition vector.hpp:23
double dx() const
Definition grid3d.hpp:23
int ny() const
Definition grid3d.hpp:21
int nz() const
Definition grid3d.hpp:22
int nx() const
Definition grid3d.hpp:20
3D Cartesian scalar grid backed by num::Vector storage.
void laplacian_stencil_2d(const BasicVector< T > &x, BasicVector< T > &y, int N)
Definition stencil.hpp:32
void row_fiber_sweep(BasicVector< T > &data, int N, F &&f)
Definition stencil.hpp:91
constexpr real phi
Golden ratio.
Definition math.hpp:42
void curl_3d(const Grid3D &ax, const Grid3D &ay, const Grid3D &az, Grid3D &bx, Grid3D &by, Grid3D &bz)
Definition stencil.hpp:170
void neg_laplacian_3d(const Vector &x, Vector &y, int nx, int ny, int nz, double inv_dx2)
Definition stencil.hpp:109
constexpr T ipow(T x) noexcept
Compute x^N at compile time via repeated squaring.
std::size_t idx
Definition types.hpp:11
void laplacian_stencil_2d_periodic(const BasicVector< T > &x, BasicVector< T > &y, int N)
Definition stencil.hpp:52
void divergence_3d(const Grid3D &fx, const Grid3D &fy, const Grid3D &fz, Grid3D &out)
Definition stencil.hpp:151
void gradient_3d(const Grid3D &phi, Grid3D &gx, Grid3D &gy, Grid3D &gz)
Definition stencil.hpp:132
void col_fiber_sweep(BasicVector< T > &data, int N, F &&f)
Definition stencil.hpp:77
Vector operations.