|
DNDSR 0.1.0.dev1+gcd065ad
Distributed Numeric Data Structure for CFV
|
Tests for the ODE integrators, iterative solvers, and direct factorization routines in src/Solver/. All C++ tests use doctest and run serially (no MPI).
| CMake target | CTest name | Source file | Timeout |
|---|---|---|---|
solver_test_ode | solver_ode | test_ODE.cpp | 60 s |
solver_test_linear | solver_linear | test_Linear.cpp | 60 s |
solver_test_direct | solver_direct | test_Direct.cpp | 60 s |
solver_test_scalar | solver_scalar | test_Scalar.cpp | 60 s |
13 test cases, 25 assertions. Tests use the harmonic oscillator
du/dt = A * u, A = [[0, -w], [w, 0]], u(0) = [1, 0]
with exact solution u(t) = [cos(wt), sin(wt)]. This oscillatory (non-dissipative) system reveals phase and amplitude errors that exponential-decay tests hide.
Each integrator is run at two step counts (N, 2N) over one full period T = 2*pi/w. The order is computed as log2(err_N / err_2N). A tolerance of 0.15 is used (expected_order - 0.15 < measured < expected_order + 0.85).
For implicit methods, Newton iterations use threshold-based stopping (resNorm < 1e-13 or iter >= 50). ESDIRK methods require at least 2 Newton iterations so that rhsbuf[iB] holds f(x_converged) for subsequent stages. Fresh ODE instances are constructed per convergence run to avoid FSAL state carryover.
| Test case | Integrator | Expected order |
|---|---|---|
SSPRK3: 3rd-order on oscillator | ExplicitSSPRK3 | 3 |
| Test case | Integrator | Expected order |
|---|---|---|
ImplicitEuler: 1st-order on oscillator | ImplicitEuler | 1 |
SDIRK4 (sc=0): 4th-order on oscillator | SDIRK4, subCode=0 | 4 |
ESDIRK4 (sc=1): 4th-order on oscillator | ESDIRK, subCode=1 | 4 |
ESDIRK3 (sc=2): 3rd-order on oscillator | ESDIRK, subCode=2 | 3 |
Trapezoid (sc=3): 2nd-order on oscillator | ESDIRK, subCode=3 (trapezoidal rule) | 2 |
ESDIRK2 (sc=4): 2nd-order on oscillator | ESDIRK, subCode=4 | 2 |
Note on ESDIRK4 step count: N is kept at 10-20 for clean 4th-order measurement. At large N the error approaches machine precision and roundoff dominates, showing only 2nd order.
| Test case | Integrator | Expected order |
|---|---|---|
VBDF k=1: 1st-order on oscillator | VBDF, maxOrder=1 | 1 |
VBDF k=2: 2nd-order on oscillator | VBDF, maxOrder=2 | 2 |
The DITR family uses ImplicitHermite3SimpleJacobianDualStep with mask and alpha parameters:
| Test case | Mask | Alpha | Expected order | Notes |
|---|---|---|---|---|
DITR U2R2 (mask=0, alpha=0.5) | 0 | 0.5 | 4 | 4th order at alpha=0.5 exactly |
DITR U2R2 (mask=0, alpha=0.55) | 0 | 0.55 | 3 | 3rd order for alpha != 0.5 |
DITR U2R1 (mask=1) | 1 | 0.5 | 3 | L-stable, 3rd order |
Theory reference: HM3Draft_Content.tex (U2R2, U2R1, U3R1 methods in the Hermite-Multistep family).
| Test case | Description |
|---|---|
SSPRK3: golden value on oscillator | Error at N=100 against captured golden value. |
4 test cases, 8 assertions. Tests use a thin DVec wrapper around Eigen::VectorXd satisfying the TDATA interface.
| Test case | Description |
|---|---|
GMRES: solve 3x3 SPD system exactly | A = [[4,1,0],[1,3,1],[0,1,2]], b = [1,2,3]. Residual < 1e-12 after at most 3 iterations. |
GMRES: solve 10x10 random SPD system | Random A'A + 10I system. Residual < 1e-10 within 10 iterations. |
GMRES: diagonal preconditioner improves convergence | Preconditioned GMRES converges in fewer iterations than unpreconditioned. |
| Test case | Description |
|---|---|
PCG: solve 5x5 SPD system | Tridiagonal (2,-1) matrix. Exact solution recovery to 1e-12. |
7 test cases, 26 assertions. Uses a 2D periodic 4x4 Laplacian with 2x2 blocks (16 cells, 5-point stencil with wrap-around).
Each cell (i,j) on a 4x4 periodic grid connects to its 4 neighbors (i+/-1, j+/-1) with periodic wrap-around. The diagonal block is 4*I + delta*diag(i, 2i) (asymmetric to exercise full LU, not LDLT). Off-diagonal blocks are -I. This gives a 32x32 system (16 cells x 2x2 blocks) with a diverse sparse pattern including fill-in during factorization.
| Test case | Description |
|---|---|
symbolic factorization has fill-in | SerialSymLUStructure with iluCode=-1 produces more non-zeros than the original adjacency. |
MatMul is correct | A*x == b verified against dense reference for random x. |
complete LU solve is exact | Full LU decompose + solve recovers x to 1e-12. |
ILU(0) is approximate but reduces residual | ILU(0) single-step solve has residual > 1e-10 (not exact) but < initial residual. |
ILU(1) converges faster than ILU(0) as preconditioner | Fixed-point iteration x += M^{-1}(b-Ax): ILU(1) reaches tolerance in fewer iterations. Only original-adjacency entries are filled with -I; fill-in positions stay zero. |
LDLT: MatMul matches LU MatMul for symmetric system | Symmetric variant produces identical A*x. |
LDLT: complete decompose + solve is exact | LDLT on symmetric system recovers x to 1e-12. |
The ILU(0) vs ILU(1) comparison uses 200 iterations of stationary iteration x_{k+1} = x_k + M^{-1}(b - A*x_k). The test verifies that ILU(1) reaches a residual threshold (1e-8) in strictly fewer iterations than ILU(0). Fill-in positions from higher ILU levels are identified via cell2cellFaceVLocal2FullRowPos and left at zero so only original adjacency entries carry -I blocks.
5 test cases, 6 assertions. Tests BisectSolveLower from Solver/Scalar.hpp, a bisection root-finder for monotonic functions.
| Test case | Description |
|---|---|
x^2 = 4 => x = 2 | Quadratic, tolerance 1e-10. |
sin(x) = 0.5 => x ~ 0.5236 | Trigonometric, tolerance 1e-10. |
linear f(x)=x, target=0.75 | Trivial case. |
exp(x) = e => x = 1 | Exponential. |
few iterations gives lower accuracy | 5 iterations: error < 0.1 but > 1e-6 (verifying iteration count affects precision). |