blob: 101d81e9ecb91c19099139e22070e64002e6b1f4 [file]
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
// The screened Poisson equation -(u_xx + u_yy) + kappa^2 u = f on the periodic
// unit square, five-point stencil on an n x n grid. Periodic boundaries make
// the matrix a block circulant with circulant blocks, solved by Bccb in
// O(N log N) through the 2-D FFT. The same matrix is the Kronecker sum
// (C + kappa^2 I) (+) C of the 1-D periodic second-difference circulant C,
// which BartelsStewart solves by fast diagonalization; the two agree.
#include <contrib/Eigen/StructuredMatrices>
#include <cmath>
#include <iostream>
using namespace Eigen;
int main() {
const double pi = std::acos(-1.0), kappa = 2.0;
const int n = 64;
const double h = 1.0 / n;
// Stencil generator: column k holds the first column of block k.
MatrixXd G = MatrixXd::Zero(n, n);
G(0, 0) = 4 / (h * h) + kappa * kappa;
G(1, 0) = G(n - 1, 0) = G(0, 1) = G(0, n - 1) = -1 / (h * h);
const auto A = makeBccb(G);
// u = cos(2 pi x) sin(4 pi y) is exact up to the O(h^2) stencil error.
VectorXd exact(n * n), f(n * n);
for (int j = 0; j < n; ++j)
for (int i = 0; i < n; ++i) {
exact[j * n + i] = std::cos(2 * pi * i * h) * std::sin(4 * pi * j * h);
f[j * n + i] = (20 * pi * pi + kappa * kappa) * exact[j * n + i];
}
const VectorXd u = A.solve(f);
std::cout << "Bccb: max error " << (u - exact).cwiseAbs().maxCoeff() << "\n";
VectorXd column = VectorXd::Zero(n);
column[0] = 2 / (h * h);
column[1] = column[n - 1] = -1 / (h * h);
MatrixXd C;
C = makeCirculant(column); // dense, for the Kronecker sum
const auto L = makeKroneckerSum(MatrixXd(C + kappa * kappa * MatrixXd::Identity(n, n)), C);
const VectorXd v = L.solve(f);
std::cout << "KroneckerSum: max error " << (v - exact).cwiseAbs().maxCoeff() << "\n";
std::cout << "max |u_Bccb - u_KroneckerSum|: " << (u - v).cwiseAbs().maxCoeff() << "\n";
}