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:
118
tests/equations/h1_vector_gradient.cpp
Normal file
118
tests/equations/h1_vector_gradient.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#include <cmath>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "mfem/fem/fe_coll.hpp"
|
||||
#include "mfem/mesh/mesh.hpp"
|
||||
#include "serif/backend/mfem/operators/operand_evaluation.hpp"
|
||||
#include "serif/discretization/blocks/fields.hpp"
|
||||
|
||||
namespace {
|
||||
namespace forms = serif::discretization::forms;
|
||||
namespace backend = serif::operators::backend::mfem;
|
||||
using Gradient = forms::Operand<serif::discretization::blocks::displacement::vector, forms::Operation::Gradient>;
|
||||
using Evaluator = backend::OperandEvaluator<Gradient>;
|
||||
}
|
||||
|
||||
TEST_CASE("Vector H1 gradients retain native physical components and transpose actions", "[equations][vector_gradient][unit]") {
|
||||
for (const auto geometry : {::mfem::Element::HEXAHEDRON, ::mfem::Element::TETRAHEDRON}) {
|
||||
auto mesh = ::mfem::Mesh::MakeCartesian3D(1, 1, 1, geometry, 1.7, 0.9, 1.2);
|
||||
for (int vertex = 0; vertex < mesh.GetNV(); ++vertex) {
|
||||
double* position = mesh.GetVertex(vertex);
|
||||
position[0] += 0.23 * position[1];
|
||||
position[2] -= 0.17 * position[1];
|
||||
}
|
||||
::mfem::H1_FECollection collection(3, 3);
|
||||
backend::TensorBasisCache cache;
|
||||
backend::BasisWorkspace workspace;
|
||||
for (int index = 0; index < mesh.GetNE(); ++index) {
|
||||
auto& transformation = *mesh.GetElementTransformation(index);
|
||||
const auto& element = *collection.FiniteElementForGeometry(mesh.GetElementBaseGeometry(index));
|
||||
const auto& rule = ::mfem::IntRules.Get(element.GetGeomType(), 8);
|
||||
for (const auto mode : {backend::ContractionMode::automatic, backend::ContractionMode::dense}) {
|
||||
backend::BasisEvaluation basis;
|
||||
Evaluator::sample_basis(element, rule, basis, cache, mode, transformation, workspace);
|
||||
CHECK(basis.uses_tensor() == (geometry == ::mfem::Element::HEXAHEDRON && mode == backend::ContractionMode::automatic));
|
||||
::mfem::Vector coefficients(3 * element.GetDof()), values, dual(9 * rule.GetNPoints()), transpose;
|
||||
for (int repeat = 0; repeat < 2; ++repeat) {
|
||||
for (int dof = 0; dof < coefficients.Size(); ++dof) {
|
||||
coefficients[dof] = repeat == 0 ? std::sin(0.37 * (dof + 1)) : 0.0;
|
||||
}
|
||||
Evaluator::evaluate(basis, coefficients, values, workspace);
|
||||
::mfem::DenseMatrix physical_basis(element.GetDof(), 3);
|
||||
for (int point = 0; point < rule.GetNPoints(); ++point) {
|
||||
transformation.SetIntPoint(&rule.IntPoint(point));
|
||||
element.CalcPhysDShape(transformation, physical_basis);
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
for (int derivative = 0; derivative < 3; ++derivative) {
|
||||
double expected = 0.0;
|
||||
for (int dof = 0; dof < element.GetDof(); ++dof) {
|
||||
expected += physical_basis(dof, derivative) * coefficients[component * element.GetDof() + dof];
|
||||
}
|
||||
const int entry = 9 * point + 3 * component + derivative;
|
||||
CHECK(std::abs(values[entry] - expected) < 2.0e-11);
|
||||
dual[entry] = repeat == 0 ? std::cos(0.19 * (entry + 1)) : 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Evaluator::integrate(basis, dual, transpose, workspace);
|
||||
CHECK(std::abs(values * dual - coefficients * transpose) < 2.0e-10);
|
||||
if (repeat != 0) {
|
||||
CHECK(values.Norml2() == 0.0);
|
||||
CHECK(transpose.Norml2() == 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Vector gradient mapping differentiates right multiplication by the inverse map", "[equations][vector_gradient][unit]") {
|
||||
serif::mapping::MappingPointContext context;
|
||||
context.mapping_jacobian.SetSize(3);
|
||||
context.mapping_jacobian = 0.0;
|
||||
context.mapping_jacobian(0, 0) = 1.2;
|
||||
context.mapping_jacobian(1, 1) = 0.8;
|
||||
context.mapping_jacobian(2, 2) = 1.1;
|
||||
context.mapping_jacobian(0, 1) = 0.21;
|
||||
context.mapping_jacobian(2, 0) = -0.13;
|
||||
context.inverse_mapping_jacobian.SetSize(3);
|
||||
::mfem::CalcInverse(context.mapping_jacobian, context.inverse_mapping_jacobian);
|
||||
serif::mapping::MappingPointVariation variation;
|
||||
variation.mapping_jacobian_variation.SetSize(3);
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
variation.mapping_jacobian_variation(row, column) = std::sin(0.41 * (3 * row + column + 1));
|
||||
}
|
||||
}
|
||||
::mfem::DenseMatrix product(3);
|
||||
variation.inverse_mapping_jacobian_variation.SetSize(3);
|
||||
::mfem::Mult(context.inverse_mapping_jacobian, variation.mapping_jacobian_variation, product);
|
||||
::mfem::Mult(product, context.inverse_mapping_jacobian, variation.inverse_mapping_jacobian_variation);
|
||||
variation.inverse_mapping_jacobian_variation *= -1.0;
|
||||
const Evaluator::ReferenceValue reference{0.7, -0.2, 0.4, -0.6, 0.3, 0.1, 0.8, -0.9, 0.5};
|
||||
const auto physical = Evaluator::physical_value(reference, context);
|
||||
const auto derivative = Evaluator::geometry_variation(reference, context, variation);
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
double recovered = 0.0;
|
||||
for (int inner = 0; inner < 3; ++inner) {
|
||||
recovered += physical[component][inner] * context.mapping_jacobian(inner, axis);
|
||||
}
|
||||
CHECK(std::abs(recovered - reference[3 * component + axis]) < 2.0e-14);
|
||||
}
|
||||
}
|
||||
for (const double step : {1.0e-5, 1.0e-6}) {
|
||||
auto plus = context, minus = context;
|
||||
plus.mapping_jacobian.Add(step, variation.mapping_jacobian_variation);
|
||||
minus.mapping_jacobian.Add(-step, variation.mapping_jacobian_variation);
|
||||
::mfem::CalcInverse(plus.mapping_jacobian, plus.inverse_mapping_jacobian);
|
||||
::mfem::CalcInverse(minus.mapping_jacobian, minus.inverse_mapping_jacobian);
|
||||
const auto plus_gradient = Evaluator::physical_value(reference, plus);
|
||||
const auto minus_gradient = Evaluator::physical_value(reference, minus);
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
CHECK(std::abs(derivative[component][axis] - (plus_gradient[component][axis] - minus_gradient[component][axis]) / (2.0 * step)) < 3.0e-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user