feat(newton): first newton solver implementation
This commit is contained in:
@@ -1,7 +1,14 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <expected>
|
||||
#include <mfem.hpp>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include <mpi.h>
|
||||
|
||||
module mean_field;
|
||||
|
||||
@@ -10,11 +17,76 @@ import :operators.prepared_rotational_displacement_force;
|
||||
|
||||
namespace {
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
using Rejection = mean_field::operators::kernels::RotationalDisplacementForceRejection;
|
||||
using Reason = mean_field::operators::kernels::RotationalDisplacementForceRejectionReason;
|
||||
|
||||
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
|
||||
return DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(attribute);
|
||||
}
|
||||
|
||||
[[nodiscard]] Rejection mapping_rejection(const mean_field::mapping::MappingStatus status) {
|
||||
MFEM_VERIFY(
|
||||
status != mean_field::mapping::MappingStatus::invalid_dimension,
|
||||
"Prepared rotational force mapping reported an invariant dimension mismatch."
|
||||
);
|
||||
return {.reason = Reason::invalid_mapping, .mappingStatus = status};
|
||||
}
|
||||
|
||||
[[nodiscard]] Rejection non_finite_rejection() noexcept {
|
||||
return {.reason = Reason::non_finite_arithmetic};
|
||||
}
|
||||
|
||||
[[nodiscard]] int encode_rejection(const std::optional<Rejection> &rejection) noexcept {
|
||||
if (!rejection.has_value()) {
|
||||
return 0;
|
||||
}
|
||||
if (rejection->reason == Reason::non_finite_arithmetic) {
|
||||
return 256;
|
||||
}
|
||||
return static_cast<int>(rejection->mappingStatus) + 1;
|
||||
}
|
||||
|
||||
[[nodiscard]] Rejection decode_rejection(const int encoded) {
|
||||
if (encoded >= 256) {
|
||||
return non_finite_rejection();
|
||||
}
|
||||
return mapping_rejection(static_cast<mean_field::mapping::MappingStatus>(encoded - 1));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::expected<
|
||||
void,
|
||||
Rejection>
|
||||
synchronize_rejection(
|
||||
const std::optional<Rejection> &localRejection,
|
||||
const MPI_Comm communicator
|
||||
) {
|
||||
const int localEncoded = encode_rejection(localRejection);
|
||||
int globalEncoded = 0;
|
||||
if (MPI_Allreduce(&localEncoded, &globalEncoded, 1, MPI_INT, MPI_MAX, communicator) != MPI_SUCCESS) {
|
||||
throw std::runtime_error("Could not synchronize prepared rotational-force candidate validity.");
|
||||
}
|
||||
if (globalEncoded != 0) {
|
||||
return std::unexpected(decode_rejection(globalEncoded));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] bool vector_is_finite(const mfem::Vector &vector) noexcept {
|
||||
for (int index = 0; index < vector.Size(); ++index) {
|
||||
if (!std::isfinite(vector(index))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[noreturn]] void throw_rejection(const Rejection &rejection) {
|
||||
if (rejection.reason == Reason::non_finite_arithmetic) {
|
||||
throw std::domain_error("Prepared rotational force produced non-finite arithmetic.");
|
||||
}
|
||||
throw std::domain_error("Prepared rotational force encountered an invalid mapped domain.");
|
||||
}
|
||||
|
||||
void true_to_local(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const mfem::Vector &trueVector,
|
||||
@@ -119,7 +191,10 @@ namespace mean_field::operators {
|
||||
);
|
||||
}
|
||||
|
||||
void PreparedRotationalDisplacementForceOperator::PrepareElementData() {
|
||||
std::expected<
|
||||
void,
|
||||
kernels::RotationalDisplacementForceRejection>
|
||||
PreparedRotationalDisplacementForceOperator::TryPrepareElementData() {
|
||||
MFEM_VERIFY(m_rotation.has_value(), "Prepared rotational force has no frozen rotation state.");
|
||||
|
||||
m_elements.clear();
|
||||
@@ -197,9 +272,12 @@ namespace mean_field::operators {
|
||||
const mapping::MappingStatus status = m_domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||
);
|
||||
if (status != mapping::MappingStatus::valid) {
|
||||
return std::unexpected(mapping_rejection(status));
|
||||
}
|
||||
MFEM_VERIFY(
|
||||
status == mapping::MappingStatus::valid && !mappingContext.mapping.compactified,
|
||||
"Prepared rotational force encountered an invalid stellar mapping."
|
||||
!mappingContext.mapping.compactified,
|
||||
"Prepared rotational force encountered compactification on a stellar element."
|
||||
);
|
||||
|
||||
densityElement.CalcShape(integrationPoint, densityShape);
|
||||
@@ -214,8 +292,21 @@ namespace mean_field::operators {
|
||||
mappingContext.quadrature.J_inv(row, column);
|
||||
}
|
||||
}
|
||||
|
||||
if (!std::isfinite(data.baseDensityValues(quadraturePoint)) ||
|
||||
!std::isfinite(data.quadratureWeights(quadraturePoint)) || !vector_is_finite(potentialGradient)) {
|
||||
return std::unexpected(non_finite_rejection());
|
||||
}
|
||||
for (int row = 0; row < dimension; ++row) {
|
||||
for (int column = 0; column < dimension; ++column) {
|
||||
if (!std::isfinite(data.inverseElementJacobians(quadraturePoint, row * dimension + column))) {
|
||||
return std::unexpected(non_finite_rejection());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
PreparedRotationalDisplacementForceReport PreparedRotationalDisplacementForceOperator::Prepare(
|
||||
@@ -223,13 +314,29 @@ namespace mean_field::operators {
|
||||
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
) {
|
||||
auto result = TryPrepare(state, dependencies, rotation);
|
||||
if (!result.has_value()) {
|
||||
throw_rejection(result.error());
|
||||
}
|
||||
return std::move(result).value();
|
||||
}
|
||||
|
||||
std::expected<
|
||||
PreparedRotationalDisplacementForceReport,
|
||||
kernels::RotationalDisplacementForceRejection>
|
||||
PreparedRotationalDisplacementForceOperator::TryPrepare(
|
||||
const context::rotational_displacement_force::RotationalDisplacementForceStateView &state,
|
||||
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
) {
|
||||
const bool wasPrepared = m_isPrepared;
|
||||
const bool rotationChanged =
|
||||
!m_context.IsPrepared() || dependencies.rotation != m_context.GetDependencies().rotation;
|
||||
|
||||
PreparedRotationalDisplacementForceReport report;
|
||||
report.contextReport = m_context.Prepare(state, dependencies);
|
||||
|
||||
if (!report.contextReport.DidAnyWork()) {
|
||||
if (!report.contextReport.DidAnyWork() && wasPrepared) {
|
||||
return report;
|
||||
}
|
||||
|
||||
@@ -245,14 +352,28 @@ namespace mean_field::operators {
|
||||
"rotation state."
|
||||
);
|
||||
|
||||
if (report.contextReport.preparedBaseState) {
|
||||
kernels::apply_rotational_displacement_force_residual(
|
||||
if (report.contextReport.preparedBaseState || !wasPrepared) {
|
||||
auto residualResult = kernels::try_apply_rotational_displacement_force_residual(
|
||||
m_fem, m_domainMapper, *m_rotation, m_context.GetBaseDensityTrue(), m_context.GetDisplacementTrue(),
|
||||
m_actionTrue
|
||||
);
|
||||
if (!residualResult.has_value()) {
|
||||
return std::unexpected(residualResult.error());
|
||||
}
|
||||
m_cachedResidual.SetSize(m_context.GetDisplacementMap().reduced_size());
|
||||
m_context.GetDisplacementMap().gather(m_actionTrue, m_cachedResidual);
|
||||
PrepareElementData();
|
||||
|
||||
const auto elementResult = TryPrepareElementData();
|
||||
std::optional<Rejection> localRejection = elementResult.has_value()
|
||||
? std::optional<Rejection>{}
|
||||
: std::optional<Rejection>{elementResult.error()};
|
||||
if (!vector_is_finite(m_cachedResidual)) {
|
||||
localRejection = non_finite_rejection();
|
||||
}
|
||||
auto synchronized = synchronize_rejection(localRejection, m_fem.mesh->GetComm());
|
||||
if (!synchronized.has_value()) {
|
||||
return std::unexpected(synchronized.error());
|
||||
}
|
||||
|
||||
++m_residualPreparationCount;
|
||||
report.preparedResidual = true;
|
||||
|
||||
Reference in New Issue
Block a user