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

@@ -9,6 +9,28 @@ using namespace mean_field;
namespace prepared_test = gravity_prepared_test_utils;
namespace gravity_context = operators::context::gravity_field;
namespace {
[[nodiscard]] mfem::Vector makeAffineDisplacement(
const fem::FEM &finiteElements,
const double scale
) {
mfem::ParGridFunction field(finiteElements.displacementFes.get());
mfem::VectorFunctionCoefficient coefficient(
finiteElements.mesh->Dimension(), [scale](const mfem::Vector &position, mfem::Vector &value) {
value.SetSize(position.Size());
for (int component = 0; component < position.Size(); ++component) {
value(component) = scale * position(component);
}
}
);
field.ProjectCoefficient(coefficient);
mfem::Vector result;
field.GetTrueDofs(result);
return result;
}
} // namespace
TEST_CASE(
"Gravity Field Linearization Context Applies Selective Invalidation",
tags::gravity_context
@@ -125,6 +147,70 @@ TEST_CASE(
CHECK(context.GetGeometryContext().GetSourceOperator().GetPreparationCount() == 1);
}
TEST_CASE(
"Gravity Field Contexts Invalidate And Recover After Candidate Rejection",
tags::gravity_context &tags::geometry &tags::unit
) {
auto args = test_utils::setup_args();
fem::FEM f = fem::setup_fem(args.mesh_file, args, 0);
REQUIRE(f.okay());
gravity_context::GravityFieldGeometryContext geometryContext(f, *f.domainMapperStateless);
const mfem::Vector zeroDisplacement = geometryContext.GetDisplacementMap().gather(makeAffineDisplacement(f, 0.0));
const mfem::Vector foldedDisplacement =
geometryContext.GetDisplacementMap().gather(makeAffineDisplacement(f, -2.0));
REQUIRE(geometryContext.TryPrepare(zeroDisplacement, {.value = 0}, {.value = 0}).has_value());
REQUIRE(geometryContext.IsPrepared());
const auto geometryRejection = geometryContext.TryPrepare(foldedDisplacement, {.value = 0}, {.value = 1});
REQUIRE_FALSE(geometryRejection.has_value());
CHECK(geometryRejection.error().reason == gravity_context::GravityFieldPreparationRejectionReason::invalid_mapping);
CHECK(geometryRejection.error().mappingStatus == mapping::MappingStatus::non_positive_determinant);
CHECK_FALSE(geometryContext.IsPrepared());
const auto geometryRecovery = geometryContext.TryPrepare(zeroDisplacement, {.value = 0}, {.value = 1});
REQUIRE(geometryRecovery.has_value());
CHECK(geometryRecovery->reconstructed_operators);
CHECK(geometryContext.IsPrepared());
gravity_context::GravityFieldLinearizationContext linearizationContext(f, *f.domainMapperStateless);
mfem::Vector density =
prepared_test::make_deterministic_vector(linearizationContext.GetDensityMap().reduced_size(), 0.17);
mfem::Vector gravityGradient =
prepared_test::make_deterministic_vector(linearizationContext.GetGravityGradientMap().reduced_size(), 0.41);
mfem::Vector gravityPotential =
prepared_test::make_deterministic_vector(linearizationContext.GetGravityPotentialMap().reduced_size(), 0.73);
gravity_context::GravityFieldRevisions revisions;
const auto makeState = [&](const mfem::Vector &displacement) {
return gravity_context::GravityFieldStateView{
.density = density,
.displacement = displacement,
.gravity_gradient = gravityGradient,
.gravity_potential = gravityPotential
};
};
REQUIRE(linearizationContext.TryPrepare(makeState(zeroDisplacement), revisions).has_value());
REQUIRE(linearizationContext.IsPrepared());
revisions.displacement.value = 1;
const auto linearizationRejection = linearizationContext.TryPrepare(makeState(foldedDisplacement), revisions);
REQUIRE_FALSE(linearizationRejection.has_value());
CHECK(
linearizationRejection.error().reason ==
gravity_context::GravityFieldPreparationRejectionReason::invalid_mapping
);
CHECK(linearizationRejection.error().mappingStatus == mapping::MappingStatus::non_positive_determinant);
CHECK_FALSE(linearizationContext.IsPrepared());
const auto linearizationRecovery = linearizationContext.TryPrepare(makeState(zeroDisplacement), revisions);
REQUIRE(linearizationRecovery.has_value());
CHECK(linearizationRecovery->geometry.reconstructed_operators);
CHECK(linearizationContext.IsPrepared());
}
TEST_CASE(
"Gravity Field Geometry Context Distinguishes Primal And Linearization Preparation",
tags::gravity_context