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

@@ -1,10 +1,16 @@
module;
#include "profile.h"
#include <array>
#include <cmath>
#include <cstdint>
#include <expected>
#include <memory>
#include <mfem.hpp>
#include <numbers>
#include <stdexcept>
#include <string>
#include <mpi.h>
module mean_field;
import :operators.prepared_gravity_source;
@@ -12,6 +18,54 @@ import :operators.prepared_gravity_source;
namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
[[nodiscard]] bool is_candidate_mapping_failure(const mean_field::mapping::MappingStatus status) {
using mean_field::mapping::MappingStatus;
return status == MappingStatus::non_finite_input || status == MappingStatus::non_finite_result ||
status == MappingStatus::non_positive_determinant;
}
[[nodiscard]] mean_field::operators::GravitySourcePreparationResult synchronize_preparation_failure(
const mean_field::mapping::MappingStatus localMappingStatus,
const bool localNonFiniteArithmetic,
const MPI_Comm communicator
) {
std::array<int, 3> localFailures{0, 0, localNonFiniteArithmetic ? 1 : 0};
if (localMappingStatus != mean_field::mapping::MappingStatus::valid) {
const int encodedStatus = static_cast<int>(localMappingStatus) + 1;
localFailures[is_candidate_mapping_failure(localMappingStatus) ? 0 : 1] = encodedStatus;
}
std::array<int, 3> globalFailures{};
if (MPI_Allreduce(
localFailures.data(), globalFailures.data(), static_cast<int>(localFailures.size()), MPI_INT, MPI_MAX,
communicator
) != MPI_SUCCESS) {
throw std::runtime_error("PreparedMappedGravitySourceOperator could not synchronize candidate validity.");
}
if (globalFailures[1] != 0) {
throw std::runtime_error(
"PreparedMappedGravitySourceOperator encountered a structural mapping failure with status " +
std::to_string(globalFailures[1] - 1) + "."
);
}
if (globalFailures[0] != 0) {
return std::unexpected(
mean_field::operators::GravitySourcePreparationRejection{
.reason = mean_field::operators::GravitySourcePreparationRejectionReason::invalid_mapping,
.mappingStatus = static_cast<mean_field::mapping::MappingStatus>(globalFailures[0] - 1)
}
);
}
if (globalFailures[2] != 0) {
return std::unexpected(
mean_field::operators::GravitySourcePreparationRejection{
.reason = mean_field::operators::GravitySourcePreparationRejectionReason::non_finite_arithmetic
}
);
}
return {};
}
int get_operator_height(const mean_field::fem::FEM &f) {
MFEM_VERIFY(
f.gravityPotentialFes != nullptr, "PreparedMappedGravitySourceOperator requires the "
@@ -135,55 +189,33 @@ namespace {
);
if (status != mean_field::mapping::MappingStatus::valid) {
const mfem::FiniteElement &displacement_element = *m_fem.displacementFes->GetFE(element_id);
const mfem::FiniteElement &compactification_element = *m_fem.compactificationFes->GetFE(element_id);
mfem::Vector displacement_shape(displacement_element.GetDof());
mfem::Vector compactification_shape(compactification_element.GetDof());
mfem::Vector reference_position(m_domain_mapper.GetDimension());
mfem::Vector displacement_value(m_domain_mapper.GetDimension());
displacement_element.CalcShape(integration_point, displacement_shape);
compactification_element.CalcShape(integration_point, compactification_shape);
transformation.Transform(integration_point, reference_position);
m_displacement_data->GetDofMatrix().MultTranspose(displacement_shape, displacement_value);
const double compactification_coordinate = m_compactification_data->GetDofs() * compactification_shape;
MFEM_ABORT(
"Stateless domain mapping failed while preparing the "
"gravity "
"source operator."
<< "\nMapping status = " << static_cast<int>(status) << "\nElement ID = " << element_id
<< "\nElement attribute = " << transformation.Attribute
<< "\nIntegration-point index = " << integration_point.index << "\nIntegration point = <"
<< integration_point.x << ", " << integration_point.y << ", " << integration_point.z << ">"
<< "\nReference position = <" << reference_position(0) << ", " << reference_position(1) << ", "
<< reference_position(2) << ">"
<< "\nReference radius = " << reference_position.Norml2() << "\nDisplacement value = <"
<< displacement_value(0) << ", " << displacement_value(1) << ", " << displacement_value(2) << ">"
<< "\nDisplacement magnitude = " << displacement_value.Norml2()
<< "\nCompactification coordinate = " << compactification_coordinate
<< "\nDisplacement ordering = " << static_cast<int>(m_fem.displacementFes->GetOrdering())
);
m_mappingFailure = status;
return 0.0;
}
const double mapping_determinant = mapping_context.mapping.mapping_determinant;
MFEM_VERIFY(
std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
"Prepared gravity source operator encountered a non-positive "
"or "
"non-finite mapping determinant."
);
m_inverse_element_jacobian = mapping_context.quadrature.J_inv;
m_inverse_element_jacobian = mapping_context.quadrature.J_inv;
return 4.0 * std::numbers::pi * mean_field::utils::G * mapping_determinant;
const double value = 4.0 * std::numbers::pi * mean_field::utils::G * mapping_determinant;
if (!std::isfinite(value)) {
m_nonFiniteArithmetic = true;
return 0.0;
}
return value;
}
[[nodiscard]] const mfem::DenseMatrix &GetInverseElementJacobian() const noexcept {
return m_inverse_element_jacobian;
}
[[nodiscard]] mean_field::mapping::MappingStatus GetMappingFailure() const noexcept {
return m_mappingFailure;
}
[[nodiscard]] bool HasNonFiniteArithmetic() const noexcept {
return m_nonFiniteArithmetic;
}
private:
void LoadElement(const int element_id) {
if (element_id == m_cached_element_id) {
@@ -239,6 +271,8 @@ namespace {
mean_field::mapping::DomainMapper::Workspace m_workspace;
mfem::DenseMatrix m_inverse_element_jacobian;
int m_cached_element_id{-1};
mean_field::mapping::MappingStatus m_mappingFailure{mean_field::mapping::MappingStatus::valid};
bool m_nonFiniteArithmetic{false};
};
} // namespace
@@ -306,15 +340,32 @@ namespace mean_field::operators {
void PreparedMappedGravitySourceOperator::Prepare(const mfem::Vector &displacement) {
MEAN_FIELD_PROFILE_SCOPE_WARMUP("PreparedMappedGravitySourceOperator::Prepare linearization", 0);
PrepareImpl(displacement, PreparationMode::linearization);
auto result = TryPrepareImpl(displacement, PreparationMode::linearization);
if (!result.has_value()) {
throwGravitySourcePreparationRejection(result.error());
}
}
void PreparedMappedGravitySourceOperator::PreparePrimal(const mfem::Vector &displacement) {
MEAN_FIELD_PROFILE_SCOPE_WARMUP("PreparedMappedGravitySourceOperator::Prepare primal", 0);
PrepareImpl(displacement, PreparationMode::primal);
auto result = TryPrepareImpl(displacement, PreparationMode::primal);
if (!result.has_value()) {
throwGravitySourcePreparationRejection(result.error());
}
}
void PreparedMappedGravitySourceOperator::PrepareImpl(
GravitySourcePreparationResult PreparedMappedGravitySourceOperator::TryPrepare(const mfem::Vector &displacement) {
MEAN_FIELD_PROFILE_SCOPE_WARMUP("PreparedMappedGravitySourceOperator::TryPrepare linearization", 0);
return TryPrepareImpl(displacement, PreparationMode::linearization);
}
GravitySourcePreparationResult
PreparedMappedGravitySourceOperator::TryPreparePrimal(const mfem::Vector &displacement) {
MEAN_FIELD_PROFILE_SCOPE_WARMUP("PreparedMappedGravitySourceOperator::TryPrepare primal", 0);
return TryPrepareImpl(displacement, PreparationMode::primal);
}
GravitySourcePreparationResult PreparedMappedGravitySourceOperator::TryPrepareImpl(
const mfem::Vector &displacement,
const PreparationMode mode
) {
@@ -325,11 +376,18 @@ namespace mean_field::operators {
"with the wrong size."
);
bool localNonFiniteInput = false;
for (int i = 0; i < displacement.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(displacement(i)), "PreparedMappedGravitySourceOperator received a non-finite "
"displacement value."
localNonFiniteInput = localNonFiniteInput || !std::isfinite(displacement(i));
}
if (auto inputResult = synchronize_preparation_failure(
localNonFiniteInput ? mapping::MappingStatus::non_finite_input : mapping::MappingStatus::valid, false,
m_fem.mesh->GetComm()
);
!inputResult.has_value()) {
m_is_prepared = false;
m_has_variation_data = false;
return inputResult;
}
m_is_prepared = false;
@@ -340,6 +398,7 @@ namespace mean_field::operators {
m_elements.reserve(m_fem.mesh->GetNE());
FrozenMappedGravitySourceCoefficient source_coefficient(m_fem, m_domain_mapper, m_displacement_true);
bool localNonFiniteQuadrature = false;
for (int element_id = 0; element_id < m_fem.mesh->GetNE(); ++element_id) {
const int attribute = m_fem.mesh->GetAttribute(element_id);
@@ -414,6 +473,11 @@ namespace mean_field::operators {
const double coefficient_value = source_coefficient.Eval(transformation, integration_point);
if (source_coefficient.GetMappingFailure() != mapping::MappingStatus::valid ||
source_coefficient.HasNonFiniteArithmetic()) {
break;
}
if (mode == PreparationMode::linearization) {
const mfem::DenseMatrix &inverse_element_jacobian = source_coefficient.GetInverseElementJacobian();
for (int row = 0; row < dimension; ++row) {
@@ -428,15 +492,26 @@ namespace mean_field::operators {
const double quadrature_value = integration_point.weight * transformation.Weight() * coefficient_value;
MFEM_VERIFY(
std::isfinite(quadrature_value) && quadrature_value > 0.0,
"Prepared gravity source operator encountered invalid "
"quadrature data on element "
<< element_id << ", quadrature point " << quadrature_point << "."
);
if (!std::isfinite(quadrature_value) || quadrature_value <= 0.0) {
localNonFiniteQuadrature = true;
break;
}
data.quadrature_data(quadrature_point) = quadrature_value;
}
if (source_coefficient.GetMappingFailure() != mapping::MappingStatus::valid ||
source_coefficient.HasNonFiniteArithmetic() || localNonFiniteQuadrature) {
break;
}
}
const bool localNonFiniteArithmetic = source_coefficient.HasNonFiniteArithmetic() || localNonFiniteQuadrature;
auto preparationResult = synchronize_preparation_failure(
source_coefficient.GetMappingFailure(), localNonFiniteArithmetic, m_fem.mesh->GetComm()
);
if (!preparationResult.has_value()) {
return preparationResult;
}
MFEM_VERIFY(!m_elements.empty(), "PreparedMappedGravitySourceOperator found no stellar elements.");
@@ -444,6 +519,7 @@ namespace mean_field::operators {
m_is_prepared = true;
m_has_variation_data = mode == PreparationMode::linearization;
++m_preparation_count;
return {};
}
void PreparedMappedGravitySourceOperator::Mult(
const mfem::Vector &density,