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

@@ -3,9 +3,14 @@ module;
#include <algorithm>
#include <array>
#include <cmath>
#include <expected>
#include <optional>
#include <stdexcept>
#include <string>
#include <utility>
#include <mfem.hpp>
#include <mpi.h>
module mean_field;
@@ -18,10 +23,71 @@ namespace {
return DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(attribute);
}
void validate_finite_vector(const mfem::Vector &vector, const char *message) {
[[nodiscard]] bool is_finite_vector(const mfem::Vector &vector) {
for (int index = 0; index < vector.Size(); ++index) {
MFEM_VERIFY(std::isfinite(vector(index)), message);
if (!std::isfinite(vector(index))) {
return false;
}
}
return true;
}
void verify_finite_vector(
const mfem::Vector &vector,
const char *message
) {
MFEM_VERIFY(is_finite_vector(vector), message);
}
[[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]] std::optional<mean_field::mapping::MappingStatus> synchronize_mapping_failure(
const std::optional<mean_field::mapping::MappingStatus> localFailure,
const MPI_Comm communicator
) {
int localFailures[2]{0, 0};
if (localFailure.has_value()) {
const int encodedStatus = static_cast<int>(*localFailure) + 1;
if (is_candidate_mapping_failure(*localFailure)) {
localFailures[0] = encodedStatus;
} else {
localFailures[1] = encodedStatus;
}
}
int globalFailures[2]{0, 0};
if (MPI_Allreduce(localFailures, globalFailures, 2, MPI_INT, MPI_MAX, communicator) != MPI_SUCCESS) {
throw std::runtime_error("PreparedAngularMomentumOperator could not synchronize mapped-geometry validity.");
}
if (globalFailures[1] != 0) {
throw std::runtime_error(
"PreparedAngularMomentumOperator encountered a structural mapping failure with status " +
std::to_string(globalFailures[1] - 1) + "."
);
}
if (globalFailures[0] == 0) {
return std::nullopt;
}
return static_cast<mean_field::mapping::MappingStatus>(globalFailures[0] - 1);
}
[[nodiscard]] bool synchronize_non_finite_failure(
const bool localFailure,
const MPI_Comm communicator,
const char *operation
) {
const int localStatus = localFailure ? 1 : 0;
int globalStatus = 0;
if (MPI_Allreduce(&localStatus, &globalStatus, 1, MPI_INT, MPI_MAX, communicator) != MPI_SUCCESS) {
throw std::runtime_error(
std::string("PreparedAngularMomentumOperator could not synchronize ") + operation + "."
);
}
return globalStatus != 0;
}
void true_to_local(
@@ -51,11 +117,8 @@ namespace {
);
const mean_field::quadrature::Query query =
DensityField::make_query<mean_field::field::Density::Form::Quadrupole>(
mean_field::quadrature::QuadratureRole::discretization,
transformation.OrderW(),
std::array<int, 1>{2},
mean_field::utils::DOMAINS::STELLAR,
mean_field::quadrature::MappingKind::general
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), std::array<int, 1>{2},
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
);
const auto resolution = f.quadratureFactory->get(query, transformation.GetGeometryType());
MFEM_VERIFY(
@@ -108,9 +171,8 @@ namespace mean_field::operators {
"PreparedAngularMomentumOperator currently requires a three-dimensional mapped domain."
);
MFEM_VERIFY(
m_fem.densityFes != nullptr && m_fem.displacementFes != nullptr &&
m_fem.compactificationFes != nullptr && m_fem.compactificationCoordinate != nullptr &&
m_fem.quadratureFactory != nullptr,
m_fem.densityFes != nullptr && m_fem.displacementFes != nullptr && m_fem.compactificationFes != nullptr &&
m_fem.compactificationCoordinate != nullptr && m_fem.quadratureFactory != nullptr,
"PreparedAngularMomentumOperator requires density, displacement, compactification, and quadrature data."
);
MFEM_VERIFY(
@@ -126,31 +188,34 @@ namespace mean_field::operators {
const double angularVelocity,
const AngularMomentumDependencies &dependencies
) {
MFEM_VERIFY(
std::isfinite(angularVelocity),
"PreparedAngularMomentumOperator requires a finite angular-velocity coordinate."
);
auto result = TryPrepare(angularVelocity, dependencies);
if (!result.has_value()) {
throwAngularMomentumPreparationRejection(result.error());
}
return std::move(result).value();
}
AngularMomentumPreparationResult PreparedAngularMomentumOperator::TryPrepare(
const double angularVelocity,
const AngularMomentumDependencies &dependencies
) {
validate_shared_gravity_revisions(m_gravityContext, dependencies);
if (m_isPrepared) {
validate_identity_transition(
m_preparedDependencies.discretization,
dependencies.discretization,
m_preparedDependencies.discretization, dependencies.discretization,
"A new angular-momentum discretization identity must change its revision."
);
validate_identity_transition(
m_preparedDependencies.density,
dependencies.density,
m_preparedDependencies.density, dependencies.density,
"A new angular-momentum density identity must change its revision."
);
validate_identity_transition(
m_preparedDependencies.displacement,
dependencies.displacement,
m_preparedDependencies.displacement, dependencies.displacement,
"A new angular-momentum displacement identity must change its revision."
);
validate_identity_transition(
m_preparedDependencies.rotation,
dependencies.rotation,
m_preparedDependencies.rotation, dependencies.rotation,
"A new angular-momentum rotation identity must change its revision."
);
}
@@ -159,36 +224,67 @@ namespace mean_field::operators {
!m_isPrepared || dependencies.discretization != m_preparedDependencies.discretization;
const bool refreshGeometry =
rebuildStaticPlan || dependencies.displacement != m_preparedDependencies.displacement;
const bool refreshDensity = rebuildStaticPlan || dependencies.density != m_preparedDependencies.density;
const bool updateAngularVelocity =
!m_isPrepared || dependencies.rotation != m_preparedDependencies.rotation ||
angularVelocity != m_angularVelocity;
const bool refreshDensity = rebuildStaticPlan || dependencies.density != m_preparedDependencies.density;
const bool updateAngularVelocity = !m_isPrepared || dependencies.rotation != m_preparedDependencies.rotation ||
angularVelocity != m_angularVelocity;
m_isPrepared = false;
if (synchronize_non_finite_failure(
!std::isfinite(angularVelocity), m_fem.mesh->GetComm(), "angular-velocity validity"
)) {
return std::unexpected(
AngularMomentumPreparationRejection{
.reason = AngularMomentumPreparationRejectionReason::non_finite_angular_velocity
}
);
}
m_isPrepared = false;
PreparedAngularMomentumReport report;
if (rebuildStaticPlan) {
BuildStaticPlan();
report.rebuiltStaticPlan = true;
}
if (refreshGeometry) {
RefreshGeometry(m_gravityContext.GetGeometryContext().GetDisplacementTrue());
const auto mappingFailure = synchronize_mapping_failure(
RefreshGeometry(m_gravityContext.GetGeometryContext().GetDisplacementTrue()), m_fem.mesh->GetComm()
);
if (mappingFailure.has_value()) {
const auto reason = *mappingFailure == mapping::MappingStatus::non_positive_determinant
? AngularMomentumPreparationRejectionReason::inverted_geometry
: AngularMomentumPreparationRejectionReason::non_finite_geometry;
return std::unexpected(
AngularMomentumPreparationRejection{.reason = reason, .mappingStatus = *mappingFailure}
);
}
report.refreshedGeometry = true;
}
if (refreshDensity) {
RefreshDensity(m_gravityContext.GetDensityTrue());
if (synchronize_non_finite_failure(
!RefreshDensity(m_gravityContext.GetDensityTrue()), m_fem.mesh->GetComm(),
"interpolated-density validity"
)) {
return std::unexpected(
AngularMomentumPreparationRejection{
.reason = AngularMomentumPreparationRejectionReason::non_finite_density
}
);
}
report.refreshedDensity = true;
}
if (updateAngularVelocity) {
m_angularVelocity = angularVelocity;
m_angularVelocity = angularVelocity;
report.updatedAngularVelocity = true;
}
if (refreshGeometry || refreshDensity || updateAngularVelocity) {
AssembleResidual();
auto rejection = TryAssembleResidual();
if (rejection.has_value()) {
return std::unexpected(*rejection);
}
report.assembledResidual = true;
}
m_preparedDependencies = dependencies;
m_isPrepared = true;
m_isPrepared = true;
return report;
}
@@ -204,8 +300,8 @@ namespace mean_field::operators {
}
++localStellarElementCount;
m_elements.emplace_back();
ElementPAData &data = m_elements.back();
data.elementId = elementId;
ElementPAData &data = m_elements.back();
data.elementId = elementId;
data.densityDofTransformation = m_fem.densityFes->GetElementDofs(elementId, data.densityDofs);
data.displacementDofTransformation =
m_fem.displacementFes->GetElementVDofs(elementId, data.displacementDofs);
@@ -218,31 +314,35 @@ namespace mean_field::operators {
data.quadraturePoints.resize(integrationRule.GetNPoints());
for (int quadraturePoint = 0; quadraturePoint < integrationRule.GetNPoints(); ++quadraturePoint) {
QuadraturePointData &point = data.quadraturePoints[quadraturePoint];
point.integrationPoint = integrationRule.IntPoint(quadraturePoint);
point.integrationPoint = integrationRule.IntPoint(quadraturePoint);
point.densityShape.SetSize(densityElement.GetDof());
densityElement.CalcShape(point.integrationPoint, point.densityShape);
}
}
int globalStellarElementCount = 0;
MPI_Allreduce(
&localStellarElementCount,
&globalStellarElementCount,
1,
MPI_INT,
MPI_SUM,
m_fem.mesh->GetComm()
MFEM_VERIFY(
MPI_Allreduce(
&localStellarElementCount, &globalStellarElementCount, 1, MPI_INT, MPI_SUM, m_fem.mesh->GetComm()
) == MPI_SUCCESS,
"PreparedAngularMomentumOperator could not count stellar elements."
);
MFEM_VERIFY(globalStellarElementCount > 0, "PreparedAngularMomentumOperator found no stellar elements.");
}
void PreparedAngularMomentumOperator::RefreshGeometry(const mfem::Vector &displacement) {
std::optional<mapping::MappingStatus>
PreparedAngularMomentumOperator::RefreshGeometry(const mfem::Vector &displacement) {
MFEM_VERIFY(
displacement.Size() == m_fem.displacementFes->GetTrueVSize(),
"Angular-momentum geometry has the wrong displacement size."
);
validate_finite_vector(displacement, "Angular-momentum geometry contains a non-finite displacement.");
if (!is_finite_vector(displacement)) {
return mapping::MappingStatus::non_finite_input;
}
mfem::Vector displacementLocal;
true_to_local(*m_fem.displacementFes, displacement, displacementLocal);
if (!is_finite_vector(displacementLocal)) {
return mapping::MappingStatus::non_finite_result;
}
mapping::DomainMapper::Workspace workspace(m_fem.mesh->Dimension());
for (ElementPAData &data : m_elements) {
@@ -254,75 +354,109 @@ namespace mean_field::operators {
if (data.compactificationDofTransformation != nullptr) {
data.compactificationDofTransformation->InvTransformPrimal(data.compactification);
}
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
if (!is_finite_vector(data.baseDisplacement)) {
return mapping::MappingStatus::non_finite_result;
}
MFEM_VERIFY(
is_finite_vector(data.compactification),
"Angular-momentum preparation encountered invalid static compactification data."
);
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(data.elementId);
const mapping::ElementDisplacementData displacementData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, data.baseDisplacement);
const mapping::ElementCompactificationData compactificationData(
compactificationElement,
data.compactification
compactificationElement, data.compactification
);
const mapping::ElementMappingData mappingData{
.displacement = displacementData,
.compactification = compactificationData
.displacement = displacementData, .compactification = compactificationData
};
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId);
for (QuadraturePointData &point : data.quadraturePoints) {
const mapping::MappingStatus status = m_domainMapper.EvaluateVolume(
mappingData,
*transformation,
point.integrationPoint,
workspace,
point.mappingContext
);
MFEM_VERIFY(
status == mapping::MappingStatus::valid && !point.mappingContext.mapping.compactified,
"Mapped angular-momentum geometry is invalid. Element: " << data.elementId
mappingData, *transformation, point.integrationPoint, workspace, point.mappingContext
);
if (status != mapping::MappingStatus::valid) {
return status;
}
if (point.mappingContext.mapping.compactified) {
return mapping::MappingStatus::at_compactified_infinity;
}
point.cylindricalRadiusSquared =
CylindricalRadiusSquared(point.mappingContext.mapping.physical_position);
if (!std::isfinite(point.cylindricalRadiusSquared)) {
return mapping::MappingStatus::non_finite_result;
}
}
}
return std::nullopt;
}
void PreparedAngularMomentumOperator::RefreshDensity(const mfem::Vector &density) {
MFEM_VERIFY(
density.Size() == m_fem.densityFes->GetTrueVSize(),
"Angular-momentum density has the wrong size."
);
validate_finite_vector(density, "Angular-momentum density contains a non-finite value.");
bool PreparedAngularMomentumOperator::RefreshDensity(const mfem::Vector &density) {
MFEM_VERIFY(density.Size() == m_fem.densityFes->GetTrueVSize(), "Angular-momentum density has the wrong size.");
if (!is_finite_vector(density)) {
return false;
}
mfem::Vector densityLocal;
true_to_local(*m_fem.densityFes, density, densityLocal);
if (!is_finite_vector(densityLocal)) {
return false;
}
mfem::Vector elementDensity;
for (ElementPAData &data : m_elements) {
densityLocal.GetSubVector(data.densityDofs, elementDensity);
if (data.densityDofTransformation != nullptr) {
data.densityDofTransformation->InvTransformPrimal(elementDensity);
}
if (!is_finite_vector(elementDensity)) {
return false;
}
for (QuadraturePointData &point : data.quadraturePoints) {
point.density = elementDensity * point.densityShape;
MFEM_VERIFY(std::isfinite(point.density), "Angular-momentum quadrature density is non-finite.");
if (!std::isfinite(point.density)) {
return false;
}
}
}
return true;
}
void PreparedAngularMomentumOperator::AssembleResidual() {
std::optional<AngularMomentumPreparationRejection> PreparedAngularMomentumOperator::TryAssembleResidual() {
double localMomentOfInertia = 0.0;
for (const ElementPAData &data : m_elements) {
for (const QuadraturePointData &point : data.quadraturePoints) {
localMomentOfInertia += point.density * point.cylindricalRadiusSquared *
point.mappingContext.quadrature.weight;
localMomentOfInertia +=
point.density * point.cylindricalRadiusSquared * point.mappingContext.quadrature.weight;
}
}
m_momentOfInertia = GlobalSum(localMomentOfInertia);
if (!std::isfinite(m_momentOfInertia)) {
return AngularMomentumPreparationRejection{
.reason = AngularMomentumPreparationRejectionReason::non_finite_moment_of_inertia,
.momentOfInertia = m_momentOfInertia
};
}
if (m_momentOfInertia < 0.0) {
return AngularMomentumPreparationRejection{
.reason = AngularMomentumPreparationRejectionReason::negative_moment_of_inertia,
.momentOfInertia = m_momentOfInertia
};
}
MFEM_VERIFY(
std::isfinite(m_momentOfInertia) && m_momentOfInertia >= 0.0,
"PreparedAngularMomentumOperator assembled an invalid moment of inertia."
std::isfinite(m_constraint.targetAngularMomentum().value()),
"PreparedAngularMomentumOperator has a non-finite configured target angular momentum."
);
m_currentAngularMomentum = m_angularVelocity * m_momentOfInertia;
m_cachedResidual.SetSize(1);
m_cachedResidual(0) = m_currentAngularMomentum - m_constraint.targetAngularMomentum().value();
if (!std::isfinite(m_currentAngularMomentum) || !std::isfinite(m_cachedResidual(0))) {
return AngularMomentumPreparationRejection{
.reason = AngularMomentumPreparationRejectionReason::non_finite_residual,
.momentOfInertia = m_momentOfInertia
};
}
++m_preparationCount;
return std::nullopt;
}
void PreparedAngularMomentumOperator::BuildResidual(mfem::Vector &residual) const {
@@ -331,9 +465,8 @@ namespace mean_field::operators {
++m_residualApplicationCount;
}
double PreparedAngularMomentumOperator::EvaluateDensityMomentActionLocal(
const mfem::Vector &densityVariation
) const {
double
PreparedAngularMomentumOperator::EvaluateDensityMomentActionLocal(const mfem::Vector &densityVariation) const {
MFEM_VERIFY(
densityVariation.Size() == m_fem.densityFes->GetTrueVSize(),
"Angular-momentum density action has the wrong true-vector size."
@@ -346,8 +479,8 @@ namespace mean_field::operators {
data.densityDofTransformation->InvTransformPrimal(m_elementDensityVariation);
}
for (const QuadraturePointData &point : data.quadraturePoints) {
localAction += (m_elementDensityVariation * point.densityShape) *
point.cylindricalRadiusSquared * point.mappingContext.quadrature.weight;
localAction += (m_elementDensityVariation * point.densityShape) * point.cylindricalRadiusSquared *
point.mappingContext.quadrature.weight;
}
}
return localAction;
@@ -369,42 +502,33 @@ namespace mean_field::operators {
if (data.displacementDofTransformation != nullptr) {
data.displacementDofTransformation->InvTransformPrimal(m_elementDisplacementVariation);
}
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(data.elementId);
const mapping::ElementDisplacementData baseDisplacementData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, data.baseDisplacement);
const mapping::ElementDisplacementData directionData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, m_elementDisplacementVariation);
const mapping::ElementCompactificationData compactificationData(
compactificationElement,
data.compactification
compactificationElement, data.compactification
);
const mapping::ElementMappingData mappingData{
.displacement = baseDisplacementData,
.compactification = compactificationData
.displacement = baseDisplacementData, .compactification = compactificationData
};
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId);
for (const QuadraturePointData &point : data.quadraturePoints) {
const mapping::MappingStatus status = m_domainMapper.EvaluateVolumeVariation(
mappingData,
directionData,
*transformation,
point.integrationPoint,
point.mappingContext,
workspace,
variation
mappingData, directionData, *transformation, point.integrationPoint, point.mappingContext,
workspace, variation
);
MFEM_VERIFY(
status == mapping::MappingStatus::valid,
"Mapped angular-momentum variation is invalid. Element: " << data.elementId
);
const double radiusSquaredVariation = CylindricalRadiusSquaredVariation(
point.mappingContext.mapping.physical_position,
variation.mapping.physical_position_variation
point.mappingContext.mapping.physical_position, variation.mapping.physical_position_variation
);
localAction += point.density *
(radiusSquaredVariation * point.mappingContext.quadrature.weight +
point.cylindricalRadiusSquared * variation.weight_variation);
localAction += point.density * (radiusSquaredVariation * point.mappingContext.quadrature.weight +
point.cylindricalRadiusSquared * variation.weight_variation);
}
}
return localAction;
@@ -419,7 +543,7 @@ namespace mean_field::operators {
densityVariation.Size() == m_gravityContext.GetDensityMap().reduced_size(),
"Angular-momentum density action has the wrong reduced size."
);
validate_finite_vector(densityVariation, "Angular-momentum density direction is non-finite.");
verify_finite_vector(densityVariation, "Angular-momentum density direction is non-finite.");
m_gravityContext.GetDensityMap().scatter(densityVariation, m_densityVariationTrue);
action.SetSize(1);
action(0) = m_angularVelocity * GlobalSum(EvaluateDensityMomentActionLocal(m_densityVariationTrue));
@@ -435,11 +559,10 @@ namespace mean_field::operators {
displacementVariation.Size() == m_gravityContext.GetDisplacementMap().reduced_size(),
"Angular-momentum displacement action has the wrong reduced size."
);
validate_finite_vector(displacementVariation, "Angular-momentum displacement direction is non-finite.");
verify_finite_vector(displacementVariation, "Angular-momentum displacement direction is non-finite.");
m_gravityContext.GetDisplacementMap().scatter(displacementVariation, m_displacementVariationTrue);
action.SetSize(1);
action(0) = m_angularVelocity *
GlobalSum(EvaluateDisplacementMomentActionLocal(m_displacementVariationTrue));
action(0) = m_angularVelocity * GlobalSum(EvaluateDisplacementMomentActionLocal(m_displacementVariationTrue));
++m_actionStatistics.displacementApplications;
}
@@ -466,24 +589,22 @@ namespace mean_field::operators {
displacementVariation.Size() == m_gravityContext.GetDisplacementMap().reduced_size(),
"Angular-momentum complete action has incompatible reduced coordinates."
);
validate_finite_vector(densityVariation, "Angular-momentum density direction is non-finite.");
validate_finite_vector(displacementVariation, "Angular-momentum displacement direction is non-finite.");
verify_finite_vector(densityVariation, "Angular-momentum density direction is non-finite.");
verify_finite_vector(displacementVariation, "Angular-momentum displacement direction is non-finite.");
MFEM_VERIFY(std::isfinite(angularVelocityVariation), "Angular-velocity direction is non-finite.");
m_gravityContext.GetDensityMap().scatter(densityVariation, m_densityVariationTrue);
m_gravityContext.GetDisplacementMap().scatter(displacementVariation, m_displacementVariationTrue);
const double localMomentAction = EvaluateDensityMomentActionLocal(m_densityVariationTrue) +
EvaluateDisplacementMomentActionLocal(m_displacementVariationTrue);
action.SetSize(1);
action(0) = m_angularVelocity * GlobalSum(localMomentAction) +
m_momentOfInertia * angularVelocityVariation;
action(0) = m_angularVelocity * GlobalSum(localMomentAction) + m_momentOfInertia * angularVelocityVariation;
++m_actionStatistics.completeApplications;
}
double PreparedAngularMomentumOperator::CylindricalRadiusSquared(
const mfem::Vector &physicalPosition
) const noexcept {
const auto &axis = m_constraint.specification().axis();
const auto &center = m_constraint.specification().center();
double
PreparedAngularMomentumOperator::CylindricalRadiusSquared(const mfem::Vector &physicalPosition) const noexcept {
const auto &axis = m_constraint.specification().axis();
const auto &center = m_constraint.specification().center();
double radiusSquared = 0.0;
double axialPosition = 0.0;
for (int component = 0; component < 3; ++component) {
@@ -491,18 +612,22 @@ namespace mean_field::operators {
radiusSquared += relative * relative;
axialPosition += axis[static_cast<std::size_t>(component)] * relative;
}
return std::max(0.0, radiusSquared - axialPosition * axialPosition);
const double perpendicularRadiusSquared = radiusSquared - axialPosition * axialPosition;
if (!std::isfinite(perpendicularRadiusSquared)) {
return perpendicularRadiusSquared;
}
return std::max(0.0, perpendicularRadiusSquared);
}
double PreparedAngularMomentumOperator::CylindricalRadiusSquaredVariation(
const mfem::Vector &physicalPosition,
const mfem::Vector &physicalPositionVariation
) const noexcept {
const auto &axis = m_constraint.specification().axis();
const auto &center = m_constraint.specification().center();
const auto &axis = m_constraint.specification().axis();
const auto &center = m_constraint.specification().center();
double relativeDotVariation = 0.0;
double axialPosition = 0.0;
double axialVariation = 0.0;
double axialPosition = 0.0;
double axialVariation = 0.0;
for (int component = 0; component < 3; ++component) {
const double relative = physicalPosition(component) - center[static_cast<std::size_t>(component)];
relativeDotVariation += relative * physicalPositionVariation(component);
@@ -514,7 +639,9 @@ namespace mean_field::operators {
double PreparedAngularMomentumOperator::GlobalSum(const double localValue) const {
double globalValue = 0.0;
MPI_Allreduce(&localValue, &globalValue, 1, MPI_DOUBLE, MPI_SUM, m_fem.mesh->GetComm());
if (MPI_Allreduce(&localValue, &globalValue, 1, MPI_DOUBLE, MPI_SUM, m_fem.mesh->GetComm()) != MPI_SUCCESS) {
throw std::runtime_error("PreparedAngularMomentumOperator could not reduce the moment of inertia.");
}
return globalValue;
}
@@ -554,15 +681,15 @@ namespace mean_field::operators {
AngularMomentumConstraintReport PreparedAngularMomentumOperator::GetConstraintReport() const {
VerifyPrepared();
const double target = GetTargetAngularMomentum();
const double target = GetTargetAngularMomentum();
const double residual = m_currentAngularMomentum - target;
return {
.targetAngularMomentum = target,
.targetAngularMomentum = target,
.achievedAngularMomentum = m_currentAngularMomentum,
.momentOfInertia = m_momentOfInertia,
.angularVelocity = m_angularVelocity,
.dimensionalResidual = residual,
.scaledResidual = residual / std::max(std::abs(target), 1.0e-300)
.momentOfInertia = m_momentOfInertia,
.angularVelocity = m_angularVelocity,
.dimensionalResidual = residual,
.scaledResidual = residual / std::max(std::abs(target), 1.0e-300)
};
}