Files
MeanField_SERiF/tests/equations/basis_lifetime_meanfield.cpp
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

92 lines
4.0 KiB
C++

#include <cmath>
#include <catch2/catch_test_macros.hpp>
#include "mfem/fem/fe_coll.hpp"
#include "serif/backend/mfem/discretization/basis_evaluation.hpp"
namespace
{
using namespace serif::discretization::backend::mfem;
/// Require repeatability after the original finite element, quadrature, and cache have been destroyed.
void check_retained_basis(const int basis)
{
BasisEvaluation retained;
BasisWorkspace workspace;
::mfem::Vector coefficients, dual, expected_values, expected_transpose;
{
const ::mfem::H1_FECollection collection(3, 3, basis);
const ::mfem::FiniteElement& element = *collection.FiniteElementForGeometry(::mfem::Geometry::CUBE);
const ::mfem::IntegrationRule rule(::mfem::IntRules.Get(::mfem::Geometry::CUBE, 7));
TensorBasisCache cache;
retained.sample(element, rule, ReferenceOperation::value_and_gradient, 1, cache, ContractionMode::automatic);
CHECK(retained.uses_tensor() == (basis != ::mfem::BasisType::Positive));
coefficients.SetSize(retained.width());
dual.SetSize(retained.height());
for (int i = 0; i < coefficients.Size(); ++i)
{
coefficients[i] = std::sin(0.37 * (i + 1));
}
for (int i = 0; i < dual.Size(); ++i)
{
dual[i] = std::cos(0.23 * (i + 1));
}
retained.evaluate(coefficients, expected_values, workspace);
retained.integrate(dual, expected_transpose, workspace);
}
::mfem::Vector values, transpose;
retained.evaluate(coefficients, values, workspace);
retained.integrate(dual, transpose, workspace);
values -= expected_values;
transpose -= expected_transpose;
CHECK(values.Normlinf() == 0.0);
CHECK(transpose.Normlinf() == 0.0);
}
}
TEST_CASE("MeanField scalar basis actions survive cache finite element and rule destruction", "[equations][tensor_basis][unit]")
{
check_retained_basis(::mfem::BasisType::GaussLobatto);
check_retained_basis(::mfem::BasisType::Positive);
}
TEST_CASE("MeanField basis cache follows rule contents without mutating published factors", "[equations][tensor_basis][unit]")
{
::mfem::H1_FECollection collection(3, 3);
const ::mfem::FiniteElement& element = *collection.FiniteElementForGeometry(::mfem::Geometry::CUBE);
const ::mfem::IntegrationRule& rule = ::mfem::IntRules.Get(::mfem::Geometry::CUBE, 7);
::mfem::IntegrationRule copy(rule), changed_weight(rule), moved_point(rule);
changed_weight.IntPoint(0).weight *= 1.25;
moved_point.IntPoint(0).x += 0.03125;
TensorBasisCache cache;
const auto original = cache.get(element, rule, ReferenceOperation::value_and_gradient, 1);
REQUIRE(original);
::mfem::Vector coefficients(original->dofs()), expected, values;
coefficients = 0.37;
TensorWorkspace workspace;
original->apply(coefficients, expected, workspace);
CHECK(cache.get(element, copy, ReferenceOperation::value_and_gradient, 1) == original);
const auto reweighted = cache.get(element, changed_weight, ReferenceOperation::value_and_gradient, 1);
REQUIRE(reweighted);
CHECK(reweighted != original);
CHECK_FALSE(cache.get(element, moved_point, ReferenceOperation::value_and_gradient, 1));
CHECK(cache.size() == 3);
copy.IntPoint(0).x = moved_point.IntPoint(0).x;
CHECK_FALSE(cache.get(element, copy, ReferenceOperation::value_and_gradient, 1));
CHECK(cache.size() == 3);
CHECK(cache.get(element, rule, ReferenceOperation::value_and_gradient, 1) == original);
// Weights identify cache entries, but basis evaluation introduces no integration weight.
reweighted->apply(coefficients, values, workspace);
values -= expected;
CHECK(values.Normlinf() == 0.0);
cache.clear();
CHECK(cache.size() == 0);
original->apply(coefficients, values, workspace);
values -= expected;
CHECK(values.Normlinf() == 0.0);
}