feat(newton): first newton solver implementation
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
module;
|
||||
|
||||
#include <cmath>
|
||||
#include <expected>
|
||||
#include <mfem.hpp>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include <mpi.h>
|
||||
|
||||
module mean_field;
|
||||
|
||||
@@ -8,6 +15,75 @@ import :operators.prepared_displacement_residual;
|
||||
|
||||
namespace {
|
||||
using Dependencies = mean_field::operators::DisplacementResidualDependencies;
|
||||
using Rejection = mean_field::operators::DisplacementResidualPreparationRejection;
|
||||
using Source = mean_field::operators::DisplacementResidualPreparationRejectionSource;
|
||||
using Reason = mean_field::operators::DisplacementResidualPreparationRejectionReason;
|
||||
|
||||
[[nodiscard]] Rejection
|
||||
pressure_rejection(const mean_field::operators::PressureForcePreparationRejection &rejection) noexcept {
|
||||
using PressureReason = mean_field::operators::PressureForcePreparationRejectionReason;
|
||||
switch (rejection.reason) {
|
||||
case PressureReason::equation_of_state:
|
||||
return {
|
||||
.source = Source::pressure,
|
||||
.reason = Reason::equation_of_state,
|
||||
.equationOfStateCode = rejection.equationOfStateCode
|
||||
};
|
||||
case PressureReason::invalid_mapping:
|
||||
return {
|
||||
.source = Source::pressure, .reason = Reason::invalid_mapping, .mappingStatus = rejection.mappingStatus
|
||||
};
|
||||
case PressureReason::non_finite_arithmetic:
|
||||
default:
|
||||
return {.source = Source::pressure, .reason = Reason::non_finite_arithmetic};
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] Rejection
|
||||
gravity_rejection(const mean_field::operators::kernels::GravityDisplacementForceRejection &rejection) noexcept {
|
||||
if (rejection.reason ==
|
||||
mean_field::operators::kernels::GravityDisplacementForceRejectionReason::invalid_mapping) {
|
||||
return {
|
||||
.source = Source::gravity, .reason = Reason::invalid_mapping, .mappingStatus = rejection.mappingStatus
|
||||
};
|
||||
}
|
||||
return {.source = Source::gravity, .reason = Reason::non_finite_arithmetic};
|
||||
}
|
||||
|
||||
[[nodiscard]] Rejection
|
||||
rotation_rejection(const mean_field::operators::kernels::RotationalDisplacementForceRejection &rejection) noexcept {
|
||||
if (rejection.reason ==
|
||||
mean_field::operators::kernels::RotationalDisplacementForceRejectionReason::invalid_mapping) {
|
||||
return {
|
||||
.source = Source::rotation, .reason = Reason::invalid_mapping, .mappingStatus = rejection.mappingStatus
|
||||
};
|
||||
}
|
||||
return {.source = Source::rotation, .reason = Reason::non_finite_arithmetic};
|
||||
}
|
||||
|
||||
[[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) {
|
||||
switch (rejection.reason) {
|
||||
case Reason::equation_of_state:
|
||||
throw mean_field::eos::EvaluationError(
|
||||
rejection.equationOfStateCode,
|
||||
"PreparedDisplacementResidualOperator encountered invalid thermodynamic data."
|
||||
);
|
||||
case Reason::invalid_mapping:
|
||||
throw std::domain_error("PreparedDisplacementResidualOperator encountered an invalid mapped domain.");
|
||||
case Reason::non_finite_arithmetic:
|
||||
default:
|
||||
throw std::domain_error("PreparedDisplacementResidualOperator produced non-finite arithmetic.");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] mean_field::operators::context::pressure_force::PressureForceDependencies
|
||||
make_pressure_dependencies(const Dependencies &dependencies) {
|
||||
@@ -118,6 +194,21 @@ namespace mean_field::operators {
|
||||
const DisplacementResidualStateView &state,
|
||||
const DisplacementResidualDependencies &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<
|
||||
PreparedDisplacementResidualReport,
|
||||
DisplacementResidualPreparationRejection>
|
||||
PreparedDisplacementResidualOperator::TryPrepare(
|
||||
const DisplacementResidualStateView &state,
|
||||
const DisplacementResidualDependencies &dependencies,
|
||||
const physics::RigidRotation &rotation
|
||||
) {
|
||||
validate_shared_gravity_revisions(m_gravityContext, dependencies);
|
||||
|
||||
@@ -161,20 +252,47 @@ namespace mean_field::operators {
|
||||
|
||||
PreparedDisplacementResidualReport report;
|
||||
|
||||
report.pressure = m_pressureOperator.Prepare(
|
||||
auto pressureResult = m_pressureOperator.TryPrepare(
|
||||
{.enthalpy = state.enthalpy, .displacement = displacement}, make_pressure_dependencies(dependencies)
|
||||
);
|
||||
if (!pressureResult.has_value()) {
|
||||
return std::unexpected(pressure_rejection(pressureResult.error()));
|
||||
}
|
||||
report.pressure = std::move(pressureResult).value();
|
||||
|
||||
report.gravity = m_gravityOperator.Prepare();
|
||||
auto gravityResult = m_gravityOperator.TryPrepare();
|
||||
if (!gravityResult.has_value()) {
|
||||
return std::unexpected(gravity_rejection(gravityResult.error()));
|
||||
}
|
||||
report.gravity = std::move(gravityResult).value();
|
||||
|
||||
report.rotation = m_rotationalOperator.Prepare(
|
||||
auto rotationResult = m_rotationalOperator.TryPrepare(
|
||||
{.density = density, .displacement = displacement}, make_rotational_dependencies(dependencies), rotation
|
||||
);
|
||||
if (!rotationResult.has_value()) {
|
||||
return std::unexpected(rotation_rejection(rotationResult.error()));
|
||||
}
|
||||
report.rotation = std::move(rotationResult).value();
|
||||
|
||||
if (report.DidAnyChildWork() ||
|
||||
m_cachedResidual.Size() != m_gravityContext.GetDisplacementMap().reduced_size()) {
|
||||
AssembleResidual();
|
||||
const auto localAssemblyRejection = AssembleResidual();
|
||||
const int localRejected = localAssemblyRejection.has_value() ? 1 : 0;
|
||||
int globallyRejected = 0;
|
||||
if (MPI_Allreduce(
|
||||
&localRejected, &globallyRejected, 1, MPI_INT, MPI_MAX, m_fem.displacementFes->GetComm()
|
||||
) != MPI_SUCCESS) {
|
||||
throw std::runtime_error(
|
||||
"PreparedDisplacementResidualOperator could not synchronize residual validity."
|
||||
);
|
||||
}
|
||||
if (globallyRejected != 0) {
|
||||
return std::unexpected(
|
||||
Rejection{.source = Source::composition, .reason = Reason::non_finite_arithmetic}
|
||||
);
|
||||
}
|
||||
report.assembledResidual = true;
|
||||
++m_residualPreparationCount;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(
|
||||
@@ -188,7 +306,7 @@ namespace mean_field::operators {
|
||||
return report;
|
||||
}
|
||||
|
||||
void PreparedDisplacementResidualOperator::AssembleResidual() {
|
||||
std::optional<DisplacementResidualPreparationRejection> PreparedDisplacementResidualOperator::AssembleResidual() {
|
||||
mfem::Vector pressureResidual;
|
||||
mfem::Vector gravityResidual;
|
||||
mfem::Vector rotationalResidual;
|
||||
@@ -211,7 +329,10 @@ namespace mean_field::operators {
|
||||
"different sizes."
|
||||
);
|
||||
|
||||
++m_residualPreparationCount;
|
||||
if (!vector_is_finite(m_cachedResidual)) {
|
||||
return Rejection{.source = Source::composition, .reason = Reason::non_finite_arithmetic};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void PreparedDisplacementResidualOperator::BuildResidual(mfem::Vector &residual) const {
|
||||
|
||||
Reference in New Issue
Block a user