test(tests): added test suite

The test suite is extensive. Note that LLMs were used to aid in the development of the test suite. All LLM generated code was manually reviewed.
This commit is contained in:
2026-11-09 13:53:17 -05:00
parent 74e55a6859
commit cb20faeb3d
202 changed files with 35505 additions and 953 deletions

View File

@@ -0,0 +1,58 @@
#include <array>
#include <initializer_list>
#include <memory>
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include "mfem/linalg/densemat.hpp"
#include "serif/backend/mfem/solver/preconditioner/coupling.hpp"
#include "serif/backend/mfem/solver/preconditioner/inverse/diagonal.hpp"
namespace
{
namespace backend = serif::solver::preconditioner::backend::mfem;
using Order = serif::solver::preconditioner::strategies::coupling::CouplingOrder;
/** @brief Express the original small physical couplings in row-major order. */
std::shared_ptr<mfem::DenseMatrix> matrix(const int rows, const int columns, const std::initializer_list<double> entries)
{
REQUIRE(entries.size() == static_cast<std::size_t>(rows * columns));
auto result = std::make_shared<mfem::DenseMatrix>(rows, columns);
auto entry = entries.begin();
for (int row = 0; row < rows; ++row)
{
for (int column = 0; column < columns; ++column)
{
(*result)(row, column) = *entry++;
}
}
return result;
}
/** @brief Prepare the original signed scalar inverse without another test-only numerical implementation. */
backend::Inverse scalar_inverse(const double diagonal)
{
const auto inverse = backend::try_diagonal_inverse(std::array{diagonal});
REQUIRE(inverse);
return *inverse;
}
} // namespace
TEST_CASE("Generic coupling preserves the original material-gravity factorization tolerances", "[preconditioner][parity][unit]")
{
const auto material = scalar_inverse(2.0);
const auto gravity = scalar_inverse(5.0);
const backend::TwoBlockCouplings cross{.first_from_second = matrix(1, 1, {7.0}), .second_from_first = matrix(1, 1, {3.0})};
const std::array orders{Order::independent, Order::second_then_first, Order::lower_diagonal_upper};
const std::array<std::array<double, 2>, 3> expected{{{5.5, 2.6}, {-3.6, 2.6}, {7.95, -0.7}}};
mfem::Vector rhs(2), result(2);
rhs[0] = 11.0;
rhs[1] = 13.0;
for (std::size_t policy = 0; policy < orders.size(); ++policy)
{
const auto inverse = backend::try_coupled_inverse(material, gravity, cross, orders[policy]);
REQUIRE(inverse);
inverse->action->Mult(rhs, result);
CHECK(result[0] == Catch::Approx(expected[policy][0]).margin(2.0e-14));
CHECK(result[1] == Catch::Approx(expected[policy][1]).margin(2.0e-14));
}
}