Skip to content

Commit fb30197

Browse files
Add HalfFourier basis functions
Represending functions on a half-circle could be useful to Cartoon evolutions; in paticular for outer boundaries in GH where an angular basis could be more suitable to constraint-preserving BC Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ff12f61 commit fb30197

17 files changed

Lines changed: 874 additions & 11 deletions

src/NumericalAlgorithms/Spectral/BasisFunctions/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ spectre_target_sources(
66
PRIVATE
77
Chebyshev.cpp
88
Fourier.cpp
9+
HalfFourier.cpp
910
Jacobi.cpp
1011
Legendre.cpp
1112
Zernike.cpp
@@ -17,6 +18,7 @@ spectre_target_headers(
1718
HEADERS
1819
Chebyshev.hpp
1920
Fourier.hpp
21+
HalfFourier.hpp
2022
Jacobi.hpp
2123
Zernike.hpp
2224
)
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// Distributed under the MIT License.
2+
// See LICENSE.txt for details.
3+
4+
#include "NumericalAlgorithms/Spectral/BasisFunctions/HalfFourier.hpp"
5+
6+
#include <cmath>
7+
#include <cstddef>
8+
#include <numbers>
9+
10+
#include "DataStructures/DataVector.hpp"
11+
#include "DataStructures/Matrix.hpp"
12+
#include "NumericalAlgorithms/Spectral/Basis.hpp"
13+
#include "NumericalAlgorithms/Spectral/BasisFunctionNormalizationSquare.hpp"
14+
#include "NumericalAlgorithms/Spectral/BasisFunctionValue.hpp"
15+
#include "NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp"
16+
#include "NumericalAlgorithms/Spectral/Parity.hpp"
17+
#include "NumericalAlgorithms/Spectral/Quadrature.hpp"
18+
#include "Utilities/ContainerHelpers.hpp"
19+
#include "Utilities/ErrorHandling/Assert.hpp"
20+
#include "Utilities/GenerateInstantiations.hpp"
21+
22+
namespace Spectral {
23+
24+
DataVector HalfFourier::collocation_points(const size_t num_points) {
25+
const double pi_over_n = std::numbers::pi / static_cast<double>(num_points);
26+
DataVector result(num_points);
27+
for (size_t j = 0; j < num_points; ++j) {
28+
result[j] = pi_over_n * (static_cast<double>(j) + 0.5);
29+
}
30+
return result;
31+
}
32+
33+
DataVector HalfFourier::quadrature_weights(const size_t num_points) {
34+
return DataVector{num_points,
35+
std::numbers::pi / static_cast<double>(num_points)};
36+
}
37+
38+
Matrix HalfFourier::even_differentiation_matrix(const size_t num_points) {
39+
const double pi_over_n = std::numbers::pi / static_cast<double>(num_points);
40+
const double two_over_n = 2.0 / static_cast<double>(num_points);
41+
Matrix result(num_points, num_points, 0.0);
42+
for (size_t i = 0; i < num_points; ++i) {
43+
const double phi_i = pi_over_n * (static_cast<double>(i) + 0.5);
44+
for (size_t j = 0; j < num_points; ++j) {
45+
const double phi_j = pi_over_n * (static_cast<double>(j) + 0.5);
46+
double val = 0.0;
47+
for (size_t n = 1; n < num_points; ++n) {
48+
val += static_cast<double>(n) * sin(static_cast<double>(n) * phi_i) *
49+
cos(static_cast<double>(n) * phi_j);
50+
}
51+
result(i, j) = -two_over_n * val;
52+
}
53+
}
54+
return result;
55+
}
56+
57+
Matrix HalfFourier::odd_differentiation_matrix(const size_t num_points) {
58+
const double pi_over_n = std::numbers::pi / static_cast<double>(num_points);
59+
const double two_over_n = 2.0 / static_cast<double>(num_points);
60+
Matrix result(num_points, num_points, 0.0);
61+
for (size_t i = 0; i < num_points; ++i) {
62+
const double phi_i = pi_over_n * (static_cast<double>(i) + 0.5);
63+
for (size_t j = 0; j < num_points; ++j) {
64+
const double phi_j = pi_over_n * (static_cast<double>(j) + 0.5);
65+
double val = 0.0;
66+
// Note: the n=num_points term vanishes because cos(num_points * phi_i) =
67+
// cos(pi*(i+0.5)) = 0, so only n=1,...,num_points-1 contribute.
68+
for (size_t n = 1; n < num_points; ++n) {
69+
val += static_cast<double>(n) * cos(static_cast<double>(n) * phi_i) *
70+
sin(static_cast<double>(n) * phi_j);
71+
}
72+
result(i, j) = two_over_n * val;
73+
}
74+
}
75+
return result;
76+
}
77+
78+
template <typename T>
79+
Matrix HalfFourier::even_interpolation_matrix(const size_t num_points,
80+
const T& target_points) {
81+
const double pi_over_n = std::numbers::pi / static_cast<double>(num_points);
82+
const double inv_n = 1.0 / static_cast<double>(num_points);
83+
const size_t num_target_points = get_size(target_points);
84+
Matrix result(num_target_points, num_points);
85+
for (size_t i = 0; i < num_target_points; ++i) {
86+
const double x = get_element(target_points, i);
87+
for (size_t j = 0; j < num_points; ++j) {
88+
const double phi_j = pi_over_n * (static_cast<double>(j) + 0.5);
89+
double val = 1.0;
90+
for (size_t n = 1; n < num_points; ++n) {
91+
val += 2.0 * cos(static_cast<double>(n) * x) *
92+
cos(static_cast<double>(n) * phi_j);
93+
}
94+
result(i, j) = inv_n * val;
95+
}
96+
}
97+
return result;
98+
}
99+
100+
template <typename T>
101+
Matrix HalfFourier::odd_interpolation_matrix(const size_t num_points,
102+
const T& target_points) {
103+
const double pi_over_n = std::numbers::pi / static_cast<double>(num_points);
104+
const double two_over_n = 2.0 / static_cast<double>(num_points);
105+
const double inv_n = 1.0 / static_cast<double>(num_points);
106+
const size_t num_target_points = get_size(target_points);
107+
Matrix result(num_target_points, num_points);
108+
for (size_t i = 0; i < num_target_points; ++i) {
109+
const double x = get_element(target_points, i);
110+
for (size_t j = 0; j < num_points; ++j) {
111+
const double phi_j = pi_over_n * (static_cast<double>(j) + 0.5);
112+
double val = 0.0;
113+
// Modes n=1,...,N-1 have discrete norm N/2 → coefficient 2/N.
114+
// Mode n=N (Nyquist) has discrete norm N → coefficient 1/N.
115+
for (size_t n = 1; n < num_points; ++n) {
116+
val += sin(static_cast<double>(n) * x) *
117+
sin(static_cast<double>(n) * phi_j);
118+
}
119+
result(i, j) =
120+
two_over_n * val + inv_n * sin(static_cast<double>(num_points) * x) *
121+
sin(static_cast<double>(num_points) * phi_j);
122+
}
123+
}
124+
return result;
125+
}
126+
127+
template <typename T>
128+
Matrix HalfFourier::interpolation_matrix(const size_t num_points,
129+
const T& target_points,
130+
const Parity parity) {
131+
ASSERT(parity != Parity::Uninitialized,
132+
"Parity must be set to either Even or Odd");
133+
if (parity == Parity::Even) {
134+
return even_interpolation_matrix(num_points, target_points);
135+
} else {
136+
return odd_interpolation_matrix(num_points, target_points);
137+
}
138+
}
139+
140+
// Specializations of function templates defined in the Spectral directory
141+
142+
template <>
143+
std::pair<DataVector, DataVector> compute_collocation_points_and_weights<
144+
Basis::HalfFourier, Quadrature::Equiangular>(const size_t num_points) {
145+
return std::make_pair(HalfFourier::collocation_points(num_points),
146+
HalfFourier::quadrature_weights(num_points));
147+
}
148+
149+
template <Basis BasisType>
150+
Matrix spectral_indefinite_integral_matrix(size_t num_points);
151+
152+
#if defined(__GNUC__) && !defined(__clang__)
153+
#pragma GCC diagnostic push
154+
#pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn"
155+
#endif
156+
157+
template <>
158+
Matrix spectral_indefinite_integral_matrix<Basis::HalfFourier>(
159+
size_t /*num_points*/) {
160+
ERROR("Indefinite integral matrix is not implemented for HalfFourier basis");
161+
}
162+
163+
template <>
164+
DataVector compute_basis_function_value<Basis::HalfFourier>(
165+
const size_t /*k*/, const DataVector& /*x*/) {
166+
ERROR("HalfFourier basis function value requires a parity argument");
167+
}
168+
169+
template <>
170+
double compute_basis_function_value<Basis::HalfFourier>(const size_t /*k*/,
171+
const double& /*x*/) {
172+
ERROR("HalfFourier basis function value requires a parity argument");
173+
}
174+
175+
template <>
176+
double compute_basis_function_normalization_square<Basis::HalfFourier>(
177+
const size_t /*k*/) {
178+
ERROR(
179+
"HalfFourier normalization square requires a parity argument; "
180+
"norms are pi (even k=0), pi/2 (even k>=1 or odd k=1,...,N).");
181+
}
182+
183+
#if defined(__GNUC__) && !defined(__clang__)
184+
#pragma GCC diagnostic pop
185+
#endif
186+
187+
#define GET_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)
188+
189+
#define INSTANTIATE_INTERPOLATION(r, data) \
190+
template Matrix HalfFourier::even_interpolation_matrix( \
191+
size_t num_points, const GET_TYPE(data)&); \
192+
template Matrix HalfFourier::odd_interpolation_matrix( \
193+
size_t num_points, const GET_TYPE(data)&); \
194+
template Matrix HalfFourier::interpolation_matrix( \
195+
size_t, const GET_TYPE(data)&, Parity);
196+
197+
GENERATE_INSTANTIATIONS(INSTANTIATE_INTERPOLATION,
198+
(double, DataVector, std::vector<double>))
199+
200+
#undef INSTANTIATE_INTERP
201+
#undef GET_TYPE
202+
} // namespace Spectral
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Distributed under the MIT License.
2+
// See LICENSE.txt for details.
3+
4+
#pragma once
5+
6+
#include <cstddef>
7+
8+
/// \cond
9+
class DataVector;
10+
class Matrix;
11+
namespace Spectral {
12+
enum class Parity : uint8_t;
13+
} // namespace Spectral
14+
/// \endcond
15+
16+
namespace Spectral {
17+
18+
/*!
19+
* \ingroup SpectralGroup
20+
*
21+
* \brief A collection of helper functions for the half-Fourier spectral basis
22+
*
23+
* \details The half-Fourier basis represents functions on the interval
24+
* \f$\phi \in [0, \pi)\f$ using \f$N\f$ equispaced interior collocation
25+
* points \f$\phi_j = (j + \tfrac{1}{2})\pi/N\f$ for \f$j = 0, \ldots, N-1\f$.
26+
*
27+
* Functions of even parity under \f$\phi \to -\phi\f$ (i.e. those satisfying
28+
* \f$f(-\phi)=f(\phi)\f$) are expanded in cosines:
29+
* \f[
30+
* f(\phi) = \sum_{n=0}^{N-1} a_n \cos(n\phi)
31+
* \f]
32+
*
33+
* Functions of odd parity under \f$\phi \to -\phi\f$ (i.e. those satisfying
34+
* \f$f(-\phi)=-f(\phi)\f$) are expanded in sines:
35+
* \f[
36+
* f(\phi) = \sum_{n=1}^{N} b_n \sin(n\phi)
37+
* \f]
38+
*
39+
* The derivative \f$\partial / \partial \phi\f$ maps even-parity functions to
40+
* odd-parity functions and vice versa.
41+
*
42+
* This basis is intended for use in the Cartoon method for axisymmetric
43+
* problems on a cylinder, where the azimuthal direction covers only half a
44+
* circle due to the reflection symmetry, and the parity boundary conditions
45+
* are internal to the spectral representation.
46+
*/
47+
class HalfFourier {
48+
public:
49+
/*!
50+
* \brief Collocation points \f$\{\phi_j\}\f$
51+
*
52+
* \details The collocation points on the interval \f$(0, \pi)\f$ are given
53+
* by
54+
* \f[
55+
* \phi_j = \frac{(j + \tfrac{1}{2})\pi}{N}
56+
* \f]
57+
*/
58+
static DataVector collocation_points(size_t num_points);
59+
60+
/*!
61+
* \brief Quadrature weights \f$\{w_j\}\f$
62+
*
63+
* \details The quadrature weights are uniform:
64+
* \f[
65+
* w_j = \frac{\pi}{N}
66+
* \f]
67+
*/
68+
static DataVector quadrature_weights(size_t num_points);
69+
70+
/*!
71+
* \brief Differentiation matrix \f$D^{\text{even}}_{ij}\f$ for even-parity
72+
* functions
73+
*
74+
* \details Maps an even-parity function (expanded in cosines) to its
75+
* derivative, which is an odd-parity function (expanded in sines).
76+
* Explicitly:
77+
* \f[
78+
* D^{\text{even}}_{ij} = \frac{2}{N} \sum_{n=1}^{N-1}
79+
* (-n) \sin(n\phi_i) \cos(n\phi_j)
80+
* \f]
81+
*/
82+
static Matrix even_differentiation_matrix(size_t num_points);
83+
84+
/*!
85+
* \brief Differentiation matrix \f$D^{\text{odd}}_{ij}\f$ for odd-parity
86+
* functions
87+
*
88+
* \details Maps an odd-parity function (expanded in sines) to its
89+
* derivative, which is an even-parity function (expanded in cosines).
90+
* Explicitly:
91+
* \f[
92+
* D^{\text{odd}}_{ij} = \frac{2}{N} \sum_{n=1}^{N-1}
93+
* n \cos(n\phi_i) \sin(n\phi_j)
94+
* \f]
95+
*
96+
* Note that \f$D^{\text{even}} = -(D^{\text{odd}})^T\f$.
97+
*/
98+
static Matrix odd_differentiation_matrix(size_t num_points);
99+
100+
/*!
101+
* \brief Interpolation matrix for even-parity functions to
102+
* \p target_points.
103+
*
104+
* \details Using the discrete cosine transform (DCT-II) representation, the
105+
* interpolation weights at a target point \f$x\f$ are:
106+
* \f[
107+
* I^{\text{even}}_j(x) = \frac{1}{N}\left[1 + 2\sum_{n=1}^{N-1}
108+
* \cos(nx)\cos(n\phi_j)\right]
109+
* \f]
110+
*/
111+
template <typename T>
112+
static Matrix even_interpolation_matrix(size_t num_points,
113+
const T& target_points);
114+
115+
/*!
116+
* \brief Interpolation matrix for odd-parity functions to
117+
* \p target_points.
118+
*
119+
* \details Using the discrete sine transform (DST-II) representation, the
120+
* interpolation weights at a target point \f$x\f$ are:
121+
* \f[
122+
* I^{\text{odd}}_j(x) = \frac{2}{N}\sum_{n=1}^{N-1}
123+
* \sin(nx)\sin(n\phi_j) + \frac{1}{N}\sin(Nx)\sin(N\phi_j)
124+
* \f]
125+
* where the Nyquist mode \f$n=N\f$ carries half the weight of the other
126+
* modes.
127+
*/
128+
template <typename T>
129+
static Matrix odd_interpolation_matrix(size_t num_points,
130+
const T& target_points);
131+
132+
/*!
133+
* \brief %Matrix used to interpolate to the \p target_points for a given
134+
* \p parity.
135+
*
136+
* \details Dispatches to `even_interpolation_matrix` when \p parity is
137+
* `Parity::Even` and to `odd_interpolation_matrix` when it is `Parity::Odd`.
138+
* This mirrors the interface of `Zernike<1>::interpolation_matrix`.
139+
*/
140+
template <typename T>
141+
static Matrix interpolation_matrix(size_t num_points, const T& target_points,
142+
Parity parity);
143+
};
144+
} // namespace Spectral

src/NumericalAlgorithms/Spectral/CollocationPoints.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ template const DataVector& collocation_points<Basis::FiniteDifference,
4747
Quadrature::FaceCentered>(size_t);
4848
template const DataVector&
4949
collocation_points<Basis::Fourier, Quadrature::Equiangular>(size_t);
50+
template const DataVector&
51+
collocation_points<Basis::HalfFourier, Quadrature::Equiangular>(size_t);
5052
template const DataVector&
5153
collocation_points<Basis::ZernikeB1, Quadrature::GaussRadauUpper>(size_t);
5254
template const DataVector&

src/NumericalAlgorithms/Spectral/CollocationPointsAndWeights.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ template struct CollocationPointsAndWeightsGenerator<Basis::FiniteDifference,
3737
Quadrature::FaceCentered>;
3838
template struct CollocationPointsAndWeightsGenerator<Basis::Fourier,
3939
Quadrature::Equiangular>;
40+
template struct CollocationPointsAndWeightsGenerator<Basis::HalfFourier,
41+
Quadrature::Equiangular>;
4042
template struct CollocationPointsAndWeightsGenerator<
4143
Basis::ZernikeB1, Quadrature::GaussRadauUpper>;
4244
template struct CollocationPointsAndWeightsGenerator<

0 commit comments

Comments
 (0)