feat(newton): first newton solver implementation
This commit is contained in:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user