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,103 @@
#include <cmath>
#include <catch2/catch_test_macros.hpp>
#include "mfem/fem/coefficient.hpp"
#include "mfem/mesh/mesh.hpp"
#include "serif/backend/mfem/projection/radial.hpp"
namespace {
namespace projection = serif::utils::projection::backend::mfem;
/** @brief Refine one of two adjacent cells so the shared edge has constrained child coefficients. */
[[nodiscard]] mfem::Mesh nonconforming_mesh() {
mfem::Mesh mesh = mfem::Mesh::MakeCartesian2D(2, 1, mfem::Element::QUADRILATERAL, true, 2.0, 1.0);
mesh.EnsureNCMesh();
mfem::Array<int> selected(1);
selected[0] = 0;
mesh.GeneralRefinement(selected);
return mesh;
}
/** @brief Independently measure the distance from the true finite-element subspace through its constraint matrices. */
[[nodiscard]] double constraint_defect(const mfem::GridFunction& field) {
const mfem::FiniteElementSpace& space = *field.FESpace();
const mfem::SparseMatrix* restriction = space.GetConformingRestriction();
const mfem::SparseMatrix* prolongation = space.GetConformingProlongation();
REQUIRE(restriction != nullptr);
REQUIRE(prolongation != nullptr);
REQUIRE(space.GetTrueVSize() < space.GetVSize());
mfem::Vector true_values(restriction->Height()), constrained(space.GetVSize());
restriction->Mult(field, true_values);
prolongation->Mult(true_values, constrained);
constrained -= field;
return constrained.Norml2();
}
}
TEST_CASE("Radial scalar projection satisfies hanging-node constraints after nonlinear sampling", "[projection][nonconforming]") {
mfem::Mesh mesh = nonconforming_mesh();
mfem::H1_FECollection collection(1, 2);
mfem::FiniteElementSpace space(&mesh, &collection);
mfem::FunctionCoefficient coefficient([](const mfem::Vector& position) {
return position * position;
});
mfem::GridFunction native(&space);
native.ProjectCoefficient(coefficient);
REQUIRE(constraint_defect(native) > 0.1);
const auto projected = projection::project_scalar(space, [](double radius) {
return radius * radius;
});
REQUIRE(projected);
CHECK(constraint_defect(*projected) < 1.0e-13);
}
TEST_CASE("Radial vector projection satisfies hanging-node and component constraints", "[projection][nonconforming]") {
mfem::Mesh mesh = nonconforming_mesh();
mfem::H1_FECollection vector_collection(1, 2);
mfem::VectorFunctionCoefficient coefficient(2, [](const mfem::Vector& position, mfem::Vector& value) {
value = position;
value *= position * position;
});
const auto check = [&](mfem::FiniteElementSpace& space) {
mfem::GridFunction native(&space);
native.ProjectCoefficient(coefficient);
REQUIRE(constraint_defect(native) > 1.0e-3);
const auto projected = projection::project_vector(space, [](double radius) {
return radius * radius * radius;
});
REQUIRE(projected);
CHECK(constraint_defect(*projected) < 1.0e-13);
};
for (const mfem::Ordering::Type ordering : {mfem::Ordering::byNODES, mfem::Ordering::byVDIM}) {
mfem::FiniteElementSpace vector(&mesh, &vector_collection, 2, ordering);
check(vector);
}
}
TEST_CASE("Constrained radial interpolation retains representable scalar fields and exact vector divergence", "[projection][nonconforming][math]") {
mfem::Mesh mesh = nonconforming_mesh();
mfem::H1_FECollection scalar_collection(2, 2);
mfem::H1_FECollection vector_collection(1, 2);
mfem::FiniteElementSpace scalar_space(&mesh, &scalar_collection), flux_space(&mesh, &vector_collection, 2);
const auto scalar = projection::project_scalar(scalar_space, [](double radius) {
return radius * radius;
});
const auto flux = projection::project_vector(flux_space, [](double radius) {
return radius;
});
REQUIRE(scalar);
REQUIRE(flux);
mfem::FunctionCoefficient exact_scalar([](const mfem::Vector& position) {
return position * position;
});
mfem::VectorFunctionCoefficient exact_vector(2, [](const mfem::Vector& position, mfem::Vector& value) {
value = position;
});
mfem::ConstantCoefficient divergence(2.0);
CHECK(scalar->ComputeL2Error(exact_scalar) < 2.0e-13);
CHECK(flux->ComputeL2Error(exact_vector) < 2.0e-13);
CHECK(flux->ComputeDivError(&divergence) < 2.0e-13);
CHECK(constraint_defect(*scalar) < 1.0e-13);
CHECK(constraint_defect(*flux) < 1.0e-13);
}