19#define DOCTEST_CONFIG_IMPLEMENT
34static constexpr int BS = 2;
35using Block = Eigen::Matrix<real, BS, BS>;
36using BVec = Eigen::Vector<real, BS>;
42using tVec = std::vector<BVec>;
48 std::vector<std::vector<Block>>
lower;
49 std::vector<std::vector<Block>>
upper;
57 lower[i].resize(s->lowerTriStructure[i].size());
58 upper[i].resize(s->upperTriStructure[i].size());
59 for (
auto &
b :
lower[i])
b.setZero();
60 for (
auto &
b :
upper[i])
b.setZero();
73static constexpr int G = 4;
74static const int NCells = G * G;
78 return ((
r % G) + G) % G * G + ((c % G) + G) % G;
81static std::vector<std::vector<DNDS::index>> build2DPeriodicAdj()
83 std::vector<std::vector<DNDS::index>>
adj(NCells);
84 for (
int r = 0;
r < G;
r++)
85 for (
int c = 0; c < G; c++)
88 adj[me].push_back(cellIdx(
r - 1, c));
89 adj[me].push_back(cellIdx(
r + 1, c));
90 adj[me].push_back(cellIdx(
r, c - 1));
91 adj[me].push_back(cellIdx(
r, c + 1));
102static void fill2DLaplacian(
TestBlockLU &lu,
double delta = 0.1)
104 auto adj = build2DPeriodicAdj();
105 Block I2 = Block::Identity();
106 auto &symLU = *lu.
symLU;
108 for (
int i = 0; i < NCells; i++)
111 D(0, 0) += delta * i;
112 D(1, 1) += delta * 2 * i;
116 auto &c2cPos = symLU.cell2cellFaceVLocal2FullRowPos[i];
117 for (
int ic2c = 0; ic2c < (int)
adj[i].size(); ic2c++)
119 int pos = c2cPos[ic2c];
122 int nLower = symLU.lowerTriStructure[i].size();
124 lu.
lower[i][pos - 1] = -I2;
126 lu.
upper[i][pos - nLower - 1] = -I2;
137TEST_CASE(
"Direct 2D periodic: symbolic factorization has fill-in")
139 auto symLU = std::make_shared<Direct::SerialSymLUStructure>(g_mpi, NCells);
140 auto adj = build2DPeriodicAdj();
141 symLU->ObtainSymmetricSymbolicFactorization(
adj, {0, NCells}, -1);
145 for (
int i = 0; i < NCells; i++)
146 maxLower = std::max(maxLower, (
int)symLU->lowerTriStructure[i].size());
148 std::cout <<
"[2D-sym] maxLower entries = " << maxLower
149 <<
" (> 4 means fill-in)" << std::endl;
155 auto symLU = std::make_shared<Direct::SerialSymLUStructure>(g_mpi, NCells);
156 auto adj = build2DPeriodicAdj();
157 symLU->ObtainSymmetricSymbolicFactorization(
adj, {0, NCells}, 0);
164 for (
int i = 0; i < NCells; i++)
167 x[i](0) = std::sin(0.5 * i);
168 x[i](1) = std::cos(0.3 * i);
169 Ax[i] = BVec::Zero();
178 DNDS::index neighbors[] = {cellIdx(r0 - 1, c0), cellIdx(r0 + 1, c0),
179 cellIdx(r0, c0 - 1), cellIdx(r0, c0 + 1)};
180 for (
auto nb : neighbors)
187 for (
int i = 0; i < NCells; i++)
188 CHECK_FALSE(
Ax[i].hasNaN());
191TEST_CASE(
"Direct 2D periodic: complete LU solve is exact")
193 auto symLU = std::make_shared<Direct::SerialSymLUStructure>(g_mpi, NCells);
194 auto adj = build2DPeriodicAdj();
195 symLU->ObtainSymmetricSymbolicFactorization(
adj, {0, NCells}, -1);
199 fill2DLaplacian(luMat);
202 for (
int i = 0; i < NCells; i++)
205 x_known[i](0) = std::sin(0.7 * i + 0.1);
206 x_known[i](1) = std::cos(0.4 * i + 0.2);
208 x_solved[i] = BVec::Zero();
214 fill2DLaplacian(luSolve);
216 luSolve.
Solve(
b, x_solved);
219 for (
int i = 0; i < NCells; i++)
220 maxErr = std::max(maxErr, (x_solved[i] -
x_known[i]).norm());
222 std::cout <<
"[2D-LU-full] maxErr = " << std::scientific << maxErr << std::endl;
223 CHECK(maxErr < 1e-10);
226TEST_CASE(
"Direct 2D periodic: ILU(0) is approximate but reduces residual")
228 auto symLU0 = std::make_shared<Direct::SerialSymLUStructure>(g_mpi, NCells);
229 auto adj = build2DPeriodicAdj();
230 symLU0->ObtainSymmetricSymbolicFactorization(
adj, {0, NCells}, 0);
233 auto symLUorig = std::make_shared<Direct::SerialSymLUStructure>(g_mpi, NCells);
234 symLUorig->ObtainSymmetricSymbolicFactorization(
adj, {0, NCells}, 0);
239 for (
int i = 0; i < NCells; i++)
242 x_known[i](0) = std::sin(0.7 * i + 0.1);
243 x_known[i](1) = std::cos(0.4 * i + 0.2);
245 x_ilu[i] = BVec::Zero();
259 auto symLUmul = std::make_shared<Direct::SerialSymLUStructure>(g_mpi, NCells);
260 symLUmul->ObtainSymmetricSymbolicFactorization(
adj, {0, NCells}, 0);
266 for (
int i = 0; i < NCells; i++)
269 bNorm +=
b[i].squaredNorm();
275 std::cout <<
"[2D-ILU0] relResidual = " << std::scientific <<
relResidual << std::endl;
286 auto adj = build2DPeriodicAdj();
289 auto symA = std::make_shared<Direct::SerialSymLUStructure>(g_mpi, NCells);
290 symA->ObtainSymmetricSymbolicFactorization(
adj, {0, NCells}, 0);
295 auto symM = std::make_shared<Direct::SerialSymLUStructure>(g_mpi, NCells);
296 symM->ObtainSymmetricSymbolicFactorization(
adj, {0, NCells}, iluCode);
302 for (
int i = 0; i < NCells; i++)
305 for (
int iter = 0; iter < nIter; iter++)
308 for (
int i = 0; i < NCells; i++)
311 for (
int i = 0; i < NCells; i++)
316 for (
int i = 0; i < NCells; i++)
318 return std::sqrt(
err);
321TEST_CASE(
"Direct 2D periodic: ILU(1) converges faster than ILU(0) as preconditioner")
323 auto adj = build2DPeriodicAdj();
326 auto symLUorig = std::make_shared<Direct::SerialSymLUStructure>(g_mpi, NCells);
327 symLUorig->ObtainSymmetricSymbolicFactorization(
adj, {0, NCells}, 0);
332 for (
int i = 0; i < NCells; i++)
335 x_known[i](0) = std::sin(0.7 * i + 0.1);
336 x_known[i](1) = std::cos(0.4 * i + 0.2);
346 std::cout <<
"[2D-ILU-prec-iter] ILU(0)=" << std::scientific << errILU0
347 <<
" ILU(1)=" << errILU1
348 <<
" Full=" << errFull
349 <<
" (after " << nIter <<
" iters)" << std::endl;
351 CHECK(errFull < 1e-10);
352 CHECK(errILU1 < errILU0);
353 CHECK(errILU0 < 1e-3);
365 std::vector<std::vector<Block>>
lower;
373 lower[i].resize(s->lowerTriStructure[i].size());
374 for (
auto &
b :
lower[i])
b.setZero();
385static void fill2DLaplacianSym(
TestBlockLDLT &lu,
double delta = 0.1)
387 auto adj = build2DPeriodicAdj();
388 Block I2 = Block::Identity();
389 auto &symLU = *lu.
symLU;
391 for (
int i = 0; i < NCells; i++)
393 lu.
diag[i] = (4.0 + delta * i) * I2;
394 auto &c2cPos = symLU.cell2cellFaceVLocal2FullRowPos[i];
395 int nLower = symLU.lowerTriStructure[i].size();
396 for (
int ic2c = 0; ic2c < (int)
adj[i].size(); ic2c++)
398 int pos = c2cPos[ic2c];
403 lu.
lower[i][pos - 1] = -I2;
410static void fill2DLaplacianSymLU(
TestBlockLU &lu,
double delta = 0.1)
412 auto adj = build2DPeriodicAdj();
413 Block I2 = Block::Identity();
414 auto &symLU = *lu.
symLU;
416 for (
int i = 0; i < NCells; i++)
418 lu.
diag[i] = (4.0 + delta * i) * I2;
419 auto &c2cPos = symLU.cell2cellFaceVLocal2FullRowPos[i];
420 for (
int ic2c = 0; ic2c < (int)
adj[i].size(); ic2c++)
422 int pos = c2cPos[ic2c];
425 int nLower = symLU.lowerTriStructure[i].size();
427 lu.
lower[i][pos - 1] = -I2;
429 lu.
upper[i][pos - nLower - 1] = -I2;
438TEST_CASE(
"Direct 2D periodic LDLT: MatMul matches LU MatMul for symmetric system")
440 auto adj = build2DPeriodicAdj();
442 auto symLU = std::make_shared<Direct::SerialSymLUStructure>(g_mpi, NCells);
443 symLU->ObtainSymmetricSymbolicFactorization(
adj, {0, NCells}, -1);
447 fill2DLaplacianSymLU(luRef);
451 fill2DLaplacianSym(ldlt);
453 tVec x(NCells), Ax_lu(NCells), Ax_ldlt(NCells);
454 for (
int i = 0; i < NCells; i++)
457 x[i](0) = std::sin(0.3 * i);
458 x[i](1) = std::cos(0.7 * i);
459 Ax_lu[i] = BVec::Zero();
460 Ax_ldlt[i] = BVec::Zero();
467 for (
int i = 0; i < NCells; i++)
468 maxDiff = std::max(maxDiff, (Ax_lu[i] - Ax_ldlt[i]).norm());
470 std::cout <<
"[2D-LDLT-MatMul] maxDiff = " << std::scientific << maxDiff << std::endl;
471 CHECK(maxDiff < 1e-14);
474TEST_CASE(
"Direct 2D periodic LDLT: complete decompose + solve is exact")
476 auto adj = build2DPeriodicAdj();
479 auto symLU = std::make_shared<Direct::SerialSymLUStructure>(g_mpi, NCells);
480 symLU->ObtainSymmetricSymbolicFactorization(
adj, {0, NCells}, -1);
482 fill2DLaplacianSymLU(luRef);
485 for (
int i = 0; i < NCells; i++)
488 x_known[i](0) = std::sin(0.7 * i + 0.1);
489 x_known[i](1) = std::cos(0.4 * i + 0.2);
491 x_solved[i] = BVec::Zero();
496 auto symLU2 = std::make_shared<Direct::SerialSymLUStructure>(g_mpi, NCells);
497 symLU2->ObtainSymmetricSymbolicFactorization(
adj, {0, NCells}, -1);
499 fill2DLaplacianSym(ldlt);
504 for (
int i = 0; i < NCells; i++)
505 maxErr = std::max(maxErr, (x_solved[i] -
x_known[i]).norm());
507 std::cout <<
"[2D-LDLT-Solve] maxErr = " << std::scientific << maxErr << std::endl;
508 CHECK(maxErr < 1e-10);
516 MPI_Init(&argc, &argv);
518 doctest::Context ctx;
519 ctx.applyCommandLine(argc, argv);
Core type aliases, constants, and metaprogramming utilities for the DNDS framework.
MPI wrappers: MPIInfo, collective operations, type mapping, CommStrategy.
the host side operators are provided as implemented
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
std::shared_ptr< T > ssp
Shortened alias for std::shared_ptr used pervasively in DNDSR.
double real
Canonical floating-point scalar used throughout DNDSR (double precision).
ssp< Direct::SerialSymLUStructure > symLU
void Solve(tVec &b, tVec &result)
void MatMul(tVec &x, tVec &result)
ssp< Direct::SerialSymLUStructure > symLU
void Solve(tVec &b, tVec &result)
void MatMul(tVec &x, tVec &result)
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
void setWorld()
Initialise the object to MPI_COMM_WORLD. Requires MPI_Init to have run.
Block & GetLower(DNDS::index i, int ij)
Block InvertDiag(const Block &v)
std::vector< std::vector< Block > > lower
TestBlockLDLT(ssp< Direct::SerialSymLUStructure > s, int N)
std::vector< Block > diag
Block & GetDiag(DNDS::index i)
Block & GetDiag(DNDS::index i)
TestBlockLU(ssp< Direct::SerialSymLUStructure > s, int N)
std::vector< std::vector< Block > > lower
std::vector< std::vector< Block > > upper
std::vector< Block > diag
Block InvertDiag(const Block &v)
Block & GetLower(DNDS::index i, int ij)
Block & GetUpper(DNDS::index i, int ij)
Eigen::Matrix< real, 5, 1 > v
TestBlockLU luILU(symLU0, NCells)
Eigen::Matrix< real, BS, BS > Block
Eigen::Vector< real, BS > BVec
TestBlockLU luMul(symLUmul, NCells)
TestBlockLU luM(symM, NCells)
TestBlockLU luA(symA, NCells)
TestBlockLU luOrig(symLUorig, NCells)
TEST_CASE("3D: VFV P2 HQM error < P1 on sinCos3D")