|
| 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 |
0 commit comments