numerics 0.1.0
Loading...
Searching...
No Matches
poisson.cpp
Go to the documentation of this file.
1/// @file pde/poisson.cpp
2#include "pde/poisson.hpp"
3#include "spectral/fft.hpp"
4#include "core/vector.hpp"
5#include <cmath>
6#include <cstddef>
7#include <stdexcept>
8#include <vector>
9
10namespace num {
11namespace pde {
12
13namespace {
14
15// Unnormalised DST-I of an N-point vector via complex FFT.
16//
17// X[k] = sum_{j=1}^{N} x[j] * sin(j*k*pi/(N+1)), k = 1..N (stored 0-indexed).
18// Odd-extension y = [0, x, 0, -rev(x)] has length M = 2(N+1).
19// FFT(y)[k] = -2i * sum sin(...) => DST(x)[k-1] = -Im(FFT(y)[k]) / 2.
20static Vector dst1(const Vector& x) {
21 const int N = static_cast<int>(x.size());
22 const int M = 2 * (N + 1);
23 CVector y(static_cast<std::size_t>(M), cplx{0.0, 0.0});
24 for (int j = 0; j < N; ++j) {
25 const auto sj = static_cast<std::size_t>(j);
26 y[sj + 1] = cplx{ x[sj], 0.0};
27 y[static_cast<std::size_t>(M - 1 - j)] = cplx{-x[sj], 0.0};
28 }
29 CVector Y(static_cast<std::size_t>(M));
30 spectral::fft(y, Y);
31 Vector out(static_cast<std::size_t>(N));
32 for (int k = 0; k < N; ++k) {
33 out[static_cast<std::size_t>(k)] = -Y[static_cast<std::size_t>(k) + 1].imag() / 2.0;
34 }
35 return out;
36}
37
38static void dst_rows(std::vector<double>& A, int N) {
39 Vector row(static_cast<std::size_t>(N));
40 for (int i = 0; i < N; ++i) {
41 const std::size_t base = static_cast<std::size_t>(i) * static_cast<std::size_t>(N);
42 for (int j = 0; j < N; ++j) { row[static_cast<std::size_t>(j)] = A[base + j]; }
43 row = dst1(row);
44 for (int j = 0; j < N; ++j) { A[base + j] = row[static_cast<std::size_t>(j)]; }
45 }
46}
47
48static void dst_cols(std::vector<double>& A, int N) {
49 Vector col(static_cast<std::size_t>(N));
50 for (int j = 0; j < N; ++j) {
51 const std::size_t sj = static_cast<std::size_t>(j);
52 for (int i = 0; i < N; ++i) {
53 col[static_cast<std::size_t>(i)] = A[(static_cast<std::size_t>(i) * static_cast<std::size_t>(N)) + sj];
54 }
55 col = dst1(col);
56 for (int i = 0; i < N; ++i) {
57 A[(static_cast<std::size_t>(i) * static_cast<std::size_t>(N)) + sj] = col[static_cast<std::size_t>(i)];
58 }
59 }
60}
61
62// 2-D DST-I: F^T * A * F (columns first, then rows).
63static void dst2d(std::vector<double>& A, int N) {
64 dst_cols(A, N);
65 dst_rows(A, N);
66}
67
68static void check_n(int N) {
69 if (N <= 0 || (N & (N + 1)) != 0) {
70 throw std::invalid_argument(
71 "pde::poisson2d: N must equal 2^p - 1 (e.g. 7, 15, 31, 63, ...)");
72 }
73}
74
75static std::vector<double> flatten(const Matrix& M, int N) {
76 std::vector<double> v(static_cast<std::size_t>(N) * static_cast<std::size_t>(N));
77 for (int i = 0; i < N; ++i) {
78 for (int j = 0; j < N; ++j) {
79 v[(static_cast<std::size_t>(i) * static_cast<std::size_t>(N)) + static_cast<std::size_t>(j)] =
80 M(static_cast<idx>(i), static_cast<idx>(j));
81 }
82 }
83 return v;
84}
85
86static Matrix unflatten(const std::vector<double>& v, int N) {
87 Matrix M(static_cast<idx>(N), static_cast<idx>(N));
88 for (int i = 0; i < N; ++i) {
89 for (int j = 0; j < N; ++j) {
90 M(static_cast<idx>(i), static_cast<idx>(j)) =
91 v[(static_cast<std::size_t>(i) * static_cast<std::size_t>(N)) + static_cast<std::size_t>(j)];
92 }
93 }
94 return M;
95}
96
97} // anonymous namespace
98
99// ---------------------------------------------------------------------------
100// FD solver: eigenvalues D_k = 2(1 - cos(k*pi/(N+1))). Error O(h^2).
101//
102// 1. f_hat = DST2D(h^2 * f)
103// 2. u_hat_{jk} = f_hat_{jk} / (D_j + D_k)
104// 3. u = (2/(N+1))^2 * DST2D(u_hat) [IDST-I = (2/(N+1)) * DST-I]
105// ---------------------------------------------------------------------------
106Matrix poisson2d_fd(const Matrix& f, int N) {
107 check_n(N);
108 const double h = 1.0 / (N + 1);
109 const double pi = M_PI;
110
111 std::vector<double> buf = flatten(f, N);
112 const std::size_t NN = static_cast<std::size_t>(N) * static_cast<std::size_t>(N);
113 for (std::size_t k = 0; k < NN; ++k) { buf[k] *= h * h; }
114 dst2d(buf, N);
115
116 std::vector<double> lam(static_cast<std::size_t>(N));
117 for (int k = 0; k < N; ++k) {
118 lam[static_cast<std::size_t>(k)] = 2.0 * (1.0 - std::cos((k + 1) * pi / (N + 1)));
119 }
120 for (int i = 0; i < N; ++i) {
121 for (int j = 0; j < N; ++j) {
122 buf[(static_cast<std::size_t>(i) * static_cast<std::size_t>(N)) + static_cast<std::size_t>(j)] /=
123 lam[static_cast<std::size_t>(i)] + lam[static_cast<std::size_t>(j)];
124 }
125 }
126 dst2d(buf, N);
127 const double s = (2.0 / (N + 1)) * (2.0 / (N + 1));
128 for (double& v : buf) { v *= s; }
129
130 return unflatten(buf, N);
131}
132
133// ---------------------------------------------------------------------------
134// Spectral solver: exact eigenvalues (k*pi)^2.
135//
136// 1. f_hat = DST2D(f)
137// 2. u_hat_{jk} = [4 / ((N+1)^2 * pi^2 * (j^2+k^2))] * f_hat_{jk}
138// 3. u = DST2D(u_hat)
139//
140// DST satisfies F*F = ((N+1)/2)*I, so the continuous spectral coefficient is
141// f_tilde_{jk} = (4/(N+1)^2) * f_hat_{j-1,k-1}. Dividing by (j^2+k^2)*pi^2
142// and reconstructing via DST gives machine-precision error for any f whose
143// DST representation is exact on the N-point grid.
144// ---------------------------------------------------------------------------
145Matrix poisson2d(const Matrix& f, int N) {
146 check_n(N);
147 const double pi = M_PI;
148 const double N1sq = static_cast<double>(N + 1) * (N + 1);
149
150 std::vector<double> buf = flatten(f, N);
151 dst2d(buf, N);
152
153 for (int i = 0; i < N; ++i) {
154 const double ji = i + 1;
155 for (int j = 0; j < N; ++j) {
156 const double jj = j + 1;
157 buf[(static_cast<std::size_t>(i) * static_cast<std::size_t>(N)) + static_cast<std::size_t>(j)] *=
158 4.0 / (N1sq * pi * pi * ((ji * ji) + (jj * jj)));
159 }
160 }
161 dst2d(buf, N);
162
163 return unflatten(buf, N);
164}
165
166} // namespace pde
167} // namespace num
Dense row-major matrix with optional GPU storage.
Definition matrix.hpp:12
FFT interface with backend dispatch.
Matrix poisson2d(const Matrix &f, int N)
Solve -Delta u = f on (0,1)^2 (Dirichlet) via DST with exact eigenvalues.
Definition poisson.cpp:145
Matrix poisson2d_fd(const Matrix &f, int N)
Solve -Delta u = f on (0,1)^2 (Dirichlet) via DST with FD eigenvalues.
Definition poisson.cpp:106
void fft(const CVector &in, CVector &out, FFTBackend b=default_fft_backend)
Forward complex DFT. out must be pre-allocated to in.size().
Definition fft.cpp:15
std::size_t idx
Definition types.hpp:11
constexpr real pi
Definition math.hpp:42
BasicVector< real > Vector
Real-valued dense vector with full backend dispatch (CPU + GPU)
Definition vector.hpp:130
std::complex< real > cplx
Definition types.hpp:12
BasicVector< cplx > CVector
Complex-valued dense vector (sequential; no GPU)
Definition vector.hpp:133
2D Poisson equation solved via the Discrete Sine Transform.
Vector operations.