Files
Emily Boudreaux cb20faeb3d 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.
2026-11-09 13:53:17 -05:00

59 lines
2.4 KiB
C++

#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));
}
}