blob: a6d36e2b028c9ee50e97108a3096d5a209b551bc [file]
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
// Peaceman-Rachford ADI for u_t = u_xx + u_yy on the unit square. With x the
// fast grid index, the two directions are I (x) D and D (x) I, and each half
// step is an explicit product in one direction and a tridiagonal solve in the
// other:
// (I + tau/2 I (x) D) u_half = (I - tau/2 D (x) I) u_old,
// (I + tau/2 D (x) I) u_new = (I - tau/2 I (x) D) u_half.
// The Identity() factors cost nothing: the products are sparse products with D
// and the solves one SparseLU of the tridiagonal I + tau/2 D.
#include <contrib/Eigen/StructuredMatrices>
#include <cmath>
#include <iostream>
using namespace Eigen;
SparseMatrix<double> secondDifference(int n) {
const double h = 1.0 / (n + 1);
SparseMatrix<double> D(n, n);
D.reserve(VectorXi::Constant(n, 3)); // at most 3 nonzeros per column
for (int i = 0; i < n; ++i) {
D.insert(i, i) = 2 / (h * h);
if (i > 0) D.insert(i, i - 1) = -1 / (h * h);
if (i + 1 < n) D.insert(i, i + 1) = -1 / (h * h);
}
D.makeCompressed();
return D;
}
int main() {
const double pi = std::acos(-1.0);
const int n = 200, steps = 50;
const double h = 1.0 / (n + 1), tau = 1e-3;
const SparseMatrix<double> D = secondDifference(n);
SparseMatrix<double> I(n, n);
I.setIdentity();
const auto Id = MatrixXd::Identity(n, n);
const auto xImplicit = makeKroneckerOperator(Id, SparseMatrix<double>(I + tau / 2 * D));
const auto yImplicit = makeKroneckerOperator(SparseMatrix<double>(I + tau / 2 * D), Id);
const auto xExplicit = makeKroneckerOperator(Id, SparseMatrix<double>(I - tau / 2 * D));
const auto yExplicit = makeKroneckerOperator(SparseMatrix<double>(I - tau / 2 * D), Id);
// u0 = sin(pi x) sin(2 pi y), an eigenvector of both directions.
VectorXd sx(n), sy(n);
for (int i = 0; i < n; ++i) {
sx[i] = std::sin(pi * (i + 1) * h);
sy[i] = std::sin(2 * pi * (i + 1) * h);
}
const VectorXd u0 = (sx * sy.transpose()).reshaped(); // entry j*n + i is (x_i, y_j)
VectorXd u = u0;
for (int k = 0; k < steps; ++k) {
const VectorXd half = xImplicit.solve(yExplicit * u);
u = yImplicit.solve(xExplicit * half);
}
const auto factor = [&](double lambda) { return (1 - tau / 2 * lambda) / (1 + tau / 2 * lambda); };
const double lx = 4 / (h * h) * std::pow(std::sin(pi * h / 2), 2), ly = 4 / (h * h) * std::pow(std::sin(pi * h), 2);
std::cout << n * n << " unknowns, " << steps << " ADI steps\n";
std::cout << "amplitude: " << u.dot(u0) / u0.squaredNorm() << "\n";
std::cout << "ADI, exact: " << std::pow(factor(lx) * factor(ly), steps) << "\n";
std::cout << "PDE, exact: " << std::exp(-5 * pi * pi * tau * steps) << "\n";
}