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 "core/vector.hpp"
4#include "spectral/fft.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)] =
34 -Y[static_cast<std::size_t>(k) + 1].imag() / 2.0;
35 }
36 return out;
37}
38
39static void dst_rows(std::vector<double>& A, int N) {
40 Vector row(static_cast<std::size_t>(N));
41 for (int i = 0; i < N; ++i) {
42 const std::size_t base =
43 static_cast<std::size_t>(i) * static_cast<std::size_t>(N);
44 for (int j = 0; j < N; ++j) {
45 row[static_cast<std::size_t>(j)] = A[base + j];
46 }
47 row = dst1(row);
48 for (int j = 0; j < N; ++j) {
49 A[base + j] = row[static_cast<std::size_t>(j)];
50 }
51 }
52}
53
54static void dst_cols(std::vector<double>& A, int N) {
55 Vector col(static_cast<std::size_t>(N));
56 for (int j = 0; j < N; ++j) {
57 const std::size_t sj = static_cast<std::size_t>(j);
58 for (int i = 0; i < N; ++i) {
59 col[static_cast<std::size_t>(i)] =
60 A[(static_cast<std::size_t>(i) * static_cast<std::size_t>(N)) + sj];
61 }
62 col = dst1(col);
63 for (int i = 0; i < N; ++i) {
64 A[(static_cast<std::size_t>(i) * static_cast<std::size_t>(N)) + sj] =
65 col[static_cast<std::size_t>(i)];
66 }
67 }
68}
69
70// 2-D DST-I: F^T * A * F (columns first, then rows).
71static void dst2d(std::vector<double>& A, int N) {
72 dst_cols(A, N);
73 dst_rows(A, N);
74}
75
76static void check_n(int N) {
77 if (N <= 0 || (N & (N + 1)) != 0) {
78 throw std::invalid_argument(
79 "pde::poisson2d: N must equal 2^p - 1 (e.g. 7, 15, 31, 63, ...)");
80 }
81}
82
83static std::vector<double> flatten(const Matrix& M, int N) {
84 std::vector<double> v(static_cast<std::size_t>(N) * static_cast<std::size_t>(N));
85 for (int i = 0; i < N; ++i) {
86 for (int j = 0; j < N; ++j) {
87 v[(static_cast<std::size_t>(i) * static_cast<std::size_t>(N))
88 + static_cast<std::size_t>(j)] =
89 M(static_cast<idx>(i), static_cast<idx>(j));
90 }
91 }
92 return v;
93}
94
95static Matrix unflatten(const std::vector<double>& v, int N) {
96 Matrix M(static_cast<idx>(N), static_cast<idx>(N));
97 for (int i = 0; i < N; ++i) {
98 for (int j = 0; j < N; ++j) {
99 M(static_cast<idx>(i), static_cast<idx>(j)) =
100 v[(static_cast<std::size_t>(i) * static_cast<std::size_t>(N))
101 + static_cast<std::size_t>(j)];
102 }
103 }
104 return M;
105}
106
107} // anonymous namespace
108
109Matrix poisson2d_fd(const Matrix& f, int N) {
110 check_n(N);
111 const double h = 1.0 / (N + 1);
112 const double pi = M_PI;
113
114 std::vector<double> buf = flatten(f, N);
115 const std::size_t NN = static_cast<std::size_t>(N) * static_cast<std::size_t>(N);
116 for (std::size_t k = 0; k < NN; ++k) {
117 buf[k] *= h * h;
118 }
119 dst2d(buf, N);
120
121 std::vector<double> lam(static_cast<std::size_t>(N));
122 for (int k = 0; k < N; ++k) {
123 lam[static_cast<std::size_t>(k)] = 2.0 * (1.0 - std::cos((k + 1) * pi / (N + 1)));
124 }
125 for (int i = 0; i < N; ++i) {
126 for (int j = 0; j < N; ++j) {
127 buf[(static_cast<std::size_t>(i) * static_cast<std::size_t>(N))
128 + static_cast<std::size_t>(j)] /=
129 lam[static_cast<std::size_t>(i)] + lam[static_cast<std::size_t>(j)];
130 }
131 }
132 dst2d(buf, N);
133 const double s = (2.0 / (N + 1)) * (2.0 / (N + 1));
134 for (double& v : buf) {
135 v *= s;
136 }
137
138 return unflatten(buf, N);
139}
140
141Matrix poisson2d(const Matrix& f, int N) {
142 check_n(N);
143 const double pi = M_PI;
144 const double N1sq = static_cast<double>(N + 1) * (N + 1);
145
146 std::vector<double> buf = flatten(f, N);
147 dst2d(buf, N);
148
149 for (int i = 0; i < N; ++i) {
150 const double ji = i + 1;
151 for (int j = 0; j < N; ++j) {
152 const double jj = j + 1;
153 buf[(static_cast<std::size_t>(i) * static_cast<std::size_t>(N))
154 + static_cast<std::size_t>(j)] *=
155 4.0 / (N1sq * pi * pi * ((ji * ji) + (jj * jj)));
156 }
157 }
158 dst2d(buf, N);
159
160 return unflatten(buf, N);
161}
162
163} // namespace pde
164} // namespace num
FFT interface with backend dispatch.
Matrix poisson2d(const Matrix &f, int N)
Solve using continuous eigenvalues .
Definition poisson.cpp:141
Matrix poisson2d_fd(const Matrix &f, int N)
Solve using finite-difference eigenvalues.
Definition poisson.cpp:109
void fft(const CVector &in, CVector &out, FFTBackend b=default_fft_backend)
Definition fft.cpp:15
std::size_t idx
Definition types.hpp:11
BasicMatrix< real > Matrix
Double-precision dense matrix with full backend dispatch (CPU + GPU).
Definition matrix.hpp:127
constexpr real pi
Definition math.hpp:43
BasicVector< real > Vector
Real-valued dense vector with full backend dispatch (CPU + GPU)
Definition vector.hpp:129
std::complex< real > cplx
Definition types.hpp:12
BasicVector< cplx > CVector
Complex-valued dense vector (sequential; no GPU)
Definition vector.hpp:132
2D Poisson equation solved via the Discrete Sine Transform.
Dense vector storage and operations.