feat(newton): first newton solver implementation

This commit is contained in:
2026-09-08 06:36:39 -04:00
parent 76818f2f82
commit b3c04d507a
98 changed files with 20397 additions and 11040 deletions

View File

@@ -2,6 +2,7 @@
#include <array>
#include <cmath>
#include <limits>
#include <stdexcept>
#include <catch2/catch_test_macros.hpp>
@@ -127,6 +128,26 @@ namespace rotational_displacement_force_test_utils {
return direction;
}
[[nodiscard]] mfem::Vector make_affine_displacement(
const mean_field::fem::FEM &f,
const double scale
) {
mfem::ParGridFunction field(f.displacementFes.get());
mfem::VectorFunctionCoefficient coefficient(
f.mesh->Dimension(), [scale](const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(position.Size());
for (int dimension = 0; dimension < position.Size(); ++dimension) {
value(dimension) = scale * position(dimension);
}
}
);
field.ProjectCoefficient(coefficient);
mfem::Vector result;
field.GetTrueDofs(result);
return result;
}
[[nodiscard]] mean_field::physics::RigidRotation make_rotation(const double scale = 1.0) {
mfem::Vector angularVelocity(3);
angularVelocity(0) = scale * 0.17;
@@ -377,6 +398,85 @@ TEST_CASE(
CHECK(rotational_displacement_force_test_utils::global_norm(zeroRotationResidual, f.mesh->GetComm()) == 0.0);
}
TEST_CASE(
"Rotational Displacement Force Reports Candidate Mapping And Arithmetic Rejections",
tags::rotation_prepared_unit
) {
mean_field::utils::Args args = test_utils::setup_args();
mean_field::fem::FEM f = mean_field::fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay());
mfem::Vector density = rotational_displacement_force_test_utils::make_density(f, 0.29);
mfem::Vector displacement = rotational_displacement_force_test_utils::make_affine_displacement(f, -2.0);
const mean_field::physics::RigidRotation rotation = rotational_displacement_force_test_utils::make_rotation(0.83);
mfem::Vector residual;
const auto invalidMapping = mean_field::operators::kernels::try_apply_rotational_displacement_force_residual(
f, *f.domainMapperStateless, rotation, density, displacement, residual
);
REQUIRE_FALSE(invalidMapping.has_value());
CHECK(
invalidMapping.error().reason ==
mean_field::operators::kernels::RotationalDisplacementForceRejectionReason::invalid_mapping
);
CHECK(invalidMapping.error().mappingStatus == mean_field::mapping::MappingStatus::non_positive_determinant);
CHECK_THROWS_AS(
mean_field::operators::kernels::apply_rotational_displacement_force_residual(
f, *f.domainMapperStateless, rotation, density, displacement, residual
),
std::domain_error
);
displacement = 0.0;
density = 1.0e200;
const mean_field::physics::RigidRotation extremeRotation =
rotational_displacement_force_test_utils::make_rotation(1.0e100);
const auto nonFiniteArithmetic = mean_field::operators::kernels::try_apply_rotational_displacement_force_residual(
f, *f.domainMapperStateless, extremeRotation, density, displacement, residual
);
REQUIRE_FALSE(nonFiniteArithmetic.has_value());
CHECK(
nonFiniteArithmetic.error().reason ==
mean_field::operators::kernels::RotationalDisplacementForceRejectionReason::non_finite_arithmetic
);
mean_field::operators::PreparedRotationalDisplacementForceOperator preparedOperator(f, *f.domainMapperStateless);
const auto &context = preparedOperator.GetContext();
auto dependencies = rotational_displacement_force_test_utils::make_dependencies();
mfem::Vector reducedDensity = context.GetDensityMap().gather(density);
const mfem::Vector reducedDisplacement = context.GetDisplacementMap().gather(displacement);
const auto preparedRejection = preparedOperator.TryPrepare(
{.density = reducedDensity, .displacement = reducedDisplacement}, dependencies, extremeRotation
);
REQUIRE_FALSE(preparedRejection.has_value());
CHECK(
preparedRejection.error().reason ==
mean_field::operators::kernels::RotationalDisplacementForceRejectionReason::non_finite_arithmetic
);
CHECK_FALSE(preparedOperator.IsPrepared());
CHECK_THROWS_AS(
preparedOperator.Prepare(
{.density = reducedDensity, .displacement = reducedDisplacement}, dependencies, extremeRotation
),
std::domain_error
);
density = rotational_displacement_force_test_utils::make_density(f, 0.29);
reducedDensity = context.GetDensityMap().gather(density);
++dependencies.density.revision;
++dependencies.rotation.revision;
const auto preparedAccepted = preparedOperator.TryPrepare(
{.density = reducedDensity, .displacement = reducedDisplacement}, dependencies, rotation
);
REQUIRE(preparedAccepted.has_value());
CHECK(preparedOperator.IsPrepared());
const auto accepted = mean_field::operators::kernels::try_apply_rotational_displacement_force_residual(
f, *f.domainMapperStateless, rotation, density, displacement, residual
);
CHECK(accepted.has_value());
}
TEST_CASE(
"Prepared Rotational Displacement Force Reprepares Selectively",
tags::rotation_prepared