blob: acd670850078b5612427e35584db96c0a6f5fdd4 [file]
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
// Fourier spectral differentiation on the periodic grid x_j = j h, h = 2 pi / N,
// N even. The differentiation matrix is the circulant with first column
// D_0 = 0, D_j = (-1)^j / (2 tan(j h / 2)), j = 1, ..., N-1,
// and the second-derivative matrix the circulant with
// D2_0 = -pi^2 / (3 h^2) - 1/6, D2_j = -(-1)^j / (2 sin^2(j h / 2)).
// Their eigenvalues are the wavenumbers: D has i k for |k| < N/2 and 0 for the
// constant and for the sawtooth mode k = N/2, so it is singular of rank N - 2,
// and Circulant::solve returns the minimum-norm antiderivative, the one with
// zero mean and no sawtooth component. The derivatives, the antiderivative and
// the periodic boundary value problem u'' - u = f all converge geometrically
// for u = exp(sin x).
#include <contrib/Eigen/StructuredMatrices>
#include <cmath>
#include <iomanip>
#include <iostream>
using namespace Eigen;
// First column of the N x N spectral differentiation matrix of the given order.
VectorXd spectralColumn(int N, int order) {
const double pi = std::acos(-1.0), h = 2 * pi / N;
VectorXd column(N);
column[0] = order == 1 ? 0 : -pi * pi / (3 * h * h) - 1.0 / 6;
for (int j = 1; j < N; ++j) {
const double sign = j % 2 == 0 ? 1 : -1;
column[j] = order == 1 ? sign / (2 * std::tan(j * h / 2)) : -sign / (2 * std::pow(std::sin(j * h / 2), 2));
}
return column;
}
int main() {
const double pi = std::acos(-1.0);
const double mean = 1.2660658777520084; // mean of exp(sin x) over a period, the Bessel function I_0(1)
std::cout << " N rank error of u' error of u'' antiderivative u'' - u = f\n";
for (int N : {8, 16, 24, 32}) {
const auto D = makeCirculant(spectralColumn(N, 1));
const auto D2 = makeCirculant(spectralColumn(N, 2));
VectorXd shifted = spectralColumn(N, 2);
shifted[0] -= 1;
const auto D2mI = makeCirculant(shifted); // D2 - I
const ArrayXd x = ArrayXd::LinSpaced(N, 0, 2 * pi * (N - 1) / N), s = x.sin(), c = x.cos();
const VectorXd u = s.exp(), du = c * s.exp(), d2u = (c * c - s) * s.exp();
std::cout << std::setw(2) << N << std::setw(5) << D.rank() << " " << std::setw(11)
<< (D * u - du).cwiseAbs().maxCoeff() << " " << std::setw(12) << (D2 * u - d2u).cwiseAbs().maxCoeff()
<< " " << std::setw(14) << (D.solve(du).array() + mean - s.exp()).abs().maxCoeff() << " "
<< (D2mI.solve(VectorXd(d2u - u)) - u).cwiseAbs().maxCoeff() << "\n";
}
const auto D = makeCirculant(spectralColumn(8, 1));
std::cout << "N = 8, eigenvalues / i: " << D.eigenvalues().imag().array().round().cast<int>().transpose() << "\n";
}