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,9 +1,16 @@
module;
#include "profile.h"
#include <array>
#include <cmath>
#include <cstdint>
#include <expected>
#include <memory>
#include <mfem.hpp>
#include <optional>
#include <stdexcept>
#include <string>
#include <mpi.h>
module mean_field;
import :operators.prepared_hdiv_mass;
@@ -11,6 +18,83 @@ import :operators.prepared_hdiv_mass;
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::HDivMassPreparationResult 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("PreparedMappedHDivMassOperator could not synchronize candidate validity.");
}
if (globalFailures[1] != 0) {
throw std::runtime_error(
"PreparedMappedHDivMassOperator encountered a structural mapping failure with status " +
std::to_string(globalFailures[1] - 1) + "."
);
}
if (globalFailures[0] != 0) {
return std::unexpected(
mean_field::operators::HDivMassPreparationRejection{
.reason = mean_field::operators::HDivMassPreparationRejectionReason::invalid_mapping,
.mappingStatus = static_cast<mean_field::mapping::MappingStatus>(globalFailures[0] - 1)
}
);
}
if (globalFailures[2] != 0) {
return std::unexpected(
mean_field::operators::HDivMassPreparationRejection{
.reason = mean_field::operators::HDivMassPreparationRejectionReason::non_finite_arithmetic
}
);
}
return {};
}
[[nodiscard]] mean_field::mapping::MappingStatus higher_priority_mapping_status(
const mean_field::mapping::MappingStatus left,
const mean_field::mapping::MappingStatus right
) noexcept {
if (left == mean_field::mapping::MappingStatus::valid) {
return right;
}
if (right == mean_field::mapping::MappingStatus::valid) {
return left;
}
const bool leftIsCandidate = is_candidate_mapping_failure(left);
const bool rightIsCandidate = is_candidate_mapping_failure(right);
if (leftIsCandidate != rightIsCandidate) {
return leftIsCandidate ? right : left;
}
return static_cast<int>(right) > static_cast<int>(left) ? right : left;
}
[[nodiscard]] bool matrix_is_finite(const mfem::DenseMatrix &matrix) noexcept {
for (int row = 0; row < matrix.Height(); ++row) {
for (int column = 0; column < matrix.Width(); ++column) {
if (!std::isfinite(matrix(row, column))) {
return false;
}
}
}
return true;
}
int get_operator_size(const mean_field::fem::FEM &f) {
MFEM_VERIFY(
f.gravityFluxFes != nullptr, "PreparedMappedHDivMassOperator requires the "
@@ -270,27 +354,30 @@ namespace {
mapping_data, transformation, integration_point, m_workspace, mapping_context
);
MFEM_VERIFY(
status == mean_field::mapping::MappingStatus::valid,
"Stateless domain mapping failed while preparing the H(div) "
"mass "
"operator. Mapping status = "
<< static_cast<int>(status) << ", element ID = " << element_id
<< ", element attribute = " << transformation.Attribute
<< ", coefficient domain = " << (m_elevates_vacuum ? "vacuum" : "stellar")
);
if (status != mean_field::mapping::MappingStatus::valid) {
m_mappingFailure = higher_priority_mapping_status(m_mappingFailure, status);
mass_tensor.SetSize(m_domain_mapper.GetDimension());
mass_tensor = 0.0;
return;
}
const mfem::DenseMatrix &mapping_jacobian = mapping_context.mapping.mapping_jacobian;
const double mapping_determinant = mapping_context.mapping.mapping_determinant;
MFEM_VERIFY(
std::isfinite(mapping_determinant) && mapping_determinant > 0.0,
"Prepared H(div) mass operator encountered a non-positive or "
"non-finite mapping determinant."
);
mfem::MultAtB(mapping_jacobian, mapping_jacobian, mass_tensor);
mass_tensor *= 1.0 / mapping_determinant;
if (!matrix_is_finite(mass_tensor)) {
m_nonFiniteArithmetic = true;
mass_tensor = 0.0;
}
}
[[nodiscard]] mean_field::mapping::MappingStatus GetMappingFailure() const noexcept {
return m_mappingFailure;
}
[[nodiscard]] bool HasNonFiniteArithmetic() const noexcept {
return m_nonFiniteArithmetic;
}
private:
@@ -348,6 +435,8 @@ namespace {
mean_field::mapping::DomainMapper::Workspace m_workspace;
int m_cached_element_id{-1};
bool m_elevates_vacuum;
mean_field::mapping::MappingStatus m_mappingFailure{mean_field::mapping::MappingStatus::valid};
bool m_nonFiniteArithmetic{false};
};
} // namespace
@@ -417,7 +506,7 @@ namespace mean_field::operators {
validate_uniform_domain_discretization(f, m_vacuum_marker, vacuum_element_id);
}
void PreparedMappedHDivMassOperator::PrepareVariationData() {
mapping::MappingStatus PreparedMappedHDivMassOperator::PrepareVariationData() {
MEAN_FIELD_PROFILE_SCOPE_WARMUP("PreparedMappedHDivMassOperator::PrepareVariationData", 0);
m_variationElements.clear();
@@ -480,28 +569,42 @@ namespace mean_field::operators {
const mapping::MappingStatus status = m_domain_mapper.EvaluateVolume(
mappingData, *transformation, integrationPoint, m_variationWorkspace, mappingContext
);
MFEM_VERIFY(
status == mapping::MappingStatus::valid,
"Prepared H(div) variation data encountered an invalid mapping. Element: "
<< elementId << ", quadrature point: " << quadraturePoint
<< ", status: " << static_cast<int>(status)
);
if (status != mapping::MappingStatus::valid) {
return status;
}
freeze_mapping_context(mappingContext, quadraturePoint, data.frozenMappingData);
}
}
return mapping::MappingStatus::valid;
}
void PreparedMappedHDivMassOperator::Prepare(const mfem::Vector &displacement) {
MEAN_FIELD_PROFILE_SCOPE_WARMUP("PreparedMappedHDivMassOperator::Prepare linearization", 0);
PrepareImpl(displacement, PreparationMode::linearization);
auto result = TryPrepareImpl(displacement, PreparationMode::linearization);
if (!result.has_value()) {
throwHDivMassPreparationRejection(result.error());
}
}
void PreparedMappedHDivMassOperator::PreparePrimal(const mfem::Vector &displacement) {
MEAN_FIELD_PROFILE_SCOPE_WARMUP("PreparedMappedHDivMassOperator::Prepare primal", 0);
PrepareImpl(displacement, PreparationMode::primal);
auto result = TryPrepareImpl(displacement, PreparationMode::primal);
if (!result.has_value()) {
throwHDivMassPreparationRejection(result.error());
}
}
void PreparedMappedHDivMassOperator::PrepareImpl(
HDivMassPreparationResult PreparedMappedHDivMassOperator::TryPrepare(const mfem::Vector &displacement) {
MEAN_FIELD_PROFILE_SCOPE_WARMUP("PreparedMappedHDivMassOperator::TryPrepare linearization", 0);
return TryPrepareImpl(displacement, PreparationMode::linearization);
}
HDivMassPreparationResult PreparedMappedHDivMassOperator::TryPreparePrimal(const mfem::Vector &displacement) {
MEAN_FIELD_PROFILE_SCOPE_WARMUP("PreparedMappedHDivMassOperator::TryPrepare primal", 0);
return TryPrepareImpl(displacement, PreparationMode::primal);
}
HDivMassPreparationResult PreparedMappedHDivMassOperator::TryPrepareImpl(
const mfem::Vector &displacement,
const PreparationMode mode
) {
@@ -512,12 +615,18 @@ namespace mean_field::operators {
"the wrong size."
);
bool localNonFiniteInput = false;
for (int i = 0; i < displacement.Size(); ++i) {
MFEM_VERIFY(
std::isfinite(displacement(i)), "PreparedMappedHDivMassOperator 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;
@@ -540,13 +649,17 @@ namespace mean_field::operators {
m_stellar_mass_coefficient.reset();
m_vacuum_mass_coefficient.reset();
m_stellar_mass_coefficient =
auto stellarMassCoefficient =
std::make_unique<FrozenMappedHDivMassCoefficient>(m_fem, m_domain_mapper, m_displacement_true, false);
m_vacuum_mass_coefficient =
auto vacuumMassCoefficient =
std::make_unique<FrozenMappedHDivMassCoefficient>(m_fem, m_domain_mapper, m_displacement_true, true);
auto *stellarMassCoefficientView = stellarMassCoefficient.get();
auto *vacuumMassCoefficientView = vacuumMassCoefficient.get();
m_stellar_mass_coefficient = std::move(stellarMassCoefficient);
m_vacuum_mass_coefficient = std::move(vacuumMassCoefficient);
m_stellar_mass_form = std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
m_vacuum_mass_form = std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
m_stellar_mass_form = std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
m_vacuum_mass_form = std::make_unique<mfem::ParBilinearForm>(m_fem.gravityFluxFes.get());
m_stellar_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
m_vacuum_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
@@ -568,15 +681,30 @@ namespace mean_field::operators {
m_stellar_mass_form->Assemble();
m_vacuum_mass_form->Assemble();
mapping::MappingStatus localMappingFailure = higher_priority_mapping_status(
stellarMassCoefficientView->GetMappingFailure(), vacuumMassCoefficientView->GetMappingFailure()
);
bool localNonFiniteArithmetic =
stellarMassCoefficientView->HasNonFiniteArithmetic() || vacuumMassCoefficientView->HasNonFiniteArithmetic();
if (mode == PreparationMode::linearization) {
PrepareVariationData();
m_has_variation_data = true;
if (localMappingFailure == mapping::MappingStatus::valid && !localNonFiniteArithmetic) {
localMappingFailure = PrepareVariationData();
}
} else {
m_variationElements.clear();
}
m_is_prepared = true;
auto preparationResult =
synchronize_preparation_failure(localMappingFailure, localNonFiniteArithmetic, m_fem.mesh->GetComm());
if (!preparationResult.has_value()) {
return preparationResult;
}
m_has_variation_data = mode == PreparationMode::linearization;
m_is_prepared = true;
++m_preparation_count;
return {};
}
void PreparedMappedHDivMassOperator::Mult(