Snapshot optimized solver and audit diagnostics before numerical experiments

This commit is contained in:
2026-09-16 14:34:06 -04:00
parent 75cc638739
commit 80cc3d83d7
64 changed files with 11969 additions and 267 deletions

View File

@@ -1,5 +1,7 @@
module;
#include "interior_displacement_contractions.hpp"
#include <array>
#include <cmath>
#include <expected>
@@ -14,6 +16,7 @@ module mean_field;
import :operators.kernels.rotational_displacement_force;
import :operators.prepared_rotational_displacement_force;
import :fem.reference_tables;
namespace {
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
@@ -194,7 +197,7 @@ namespace mean_field::operators {
std::expected<
void,
kernels::RotationalDisplacementForceRejection>
PreparedRotationalDisplacementForceOperator::TryPrepareElementData() {
PreparedRotationalDisplacementForceOperator::TryPrepareElementData(const bool assembleResidual) {
MFEM_VERIFY(m_rotation.has_value(), "Prepared rotational force has no frozen rotation state.");
m_elements.clear();
@@ -214,7 +217,20 @@ namespace mean_field::operators {
mfem::Vector densityShape;
mfem::Vector potentialGradient;
if (assembleResidual) {
m_localAction.SetSize(m_fem.displacementFes->GetVSize());
m_localAction = 0.0;
}
mfem::Vector residualComponent;
const int dimension = m_domainMapper.GetDimension();
// Rigid rotation has a position-independent acceleration derivative.
m_centrifugalJacobian.SetSize(dimension, dimension);
mfem::Vector unit(dimension), derivative(dimension);
for (int c = 0; c < dimension; ++c) {
unit = 0.0; unit(c) = 1.0;
m_rotation->potential_gradient_directional_derivative(unit, derivative);
for (int r = 0; r < dimension; ++r) m_centrifugalJacobian(r, c) = -derivative(r);
}
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) {
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(elementId);
@@ -249,6 +265,11 @@ namespace mean_field::operators {
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(elementId);
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(elementId);
data.integrationRule = &get_rotation_force_rule(m_fem, *transformation);
data.densityReferenceTable = m_fem.GetReferenceTables().GetScalarTable(densityElement, *data.integrationRule);
data.displacementReferenceTable = m_fem.GetReferenceTables().GetScalarTable(displacementElement, *data.integrationRule);
data.fastApplication = dimension == 3 && m_fem.displacementFes->GetOrdering() == mfem::Ordering::byNODES &&
displacementElement.GetMapType() == mfem::FiniteElement::VALUE &&
displacementElement.GetDerivType() == mfem::FiniteElement::GRAD;
const mapping::ElementDisplacementData displacementData =
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementBaseDisplacement);
@@ -266,6 +287,7 @@ namespace mean_field::operators {
data.quadratureWeights.SetSize(quadraturePointCount);
densityShape.SetSize(densityElement.GetDof());
potentialGradient.SetSize(dimension);
if (assembleResidual) m_quadratureForce.SetSize(quadraturePointCount, dimension);
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
const mfem::IntegrationPoint &integrationPoint = data.integrationRule->IntPoint(quadraturePoint);
@@ -304,8 +326,36 @@ namespace mean_field::operators {
}
}
}
if (assembleResidual) {
const double weightedDensity =
data.quadratureWeights(quadraturePoint) * data.baseDensityValues(quadraturePoint);
for (int component = 0; component < dimension; ++component)
m_quadratureForce(quadraturePoint, component) = -weightedDensity * potentialGradient(component);
}
}
if (assembleResidual) {
const int ndof = displacementElement.GetDof();
MFEM_VERIFY(data.displacementDofs.Size() == ndof * dimension,
"Prepared force residual has an invalid displacement element size.");
m_elementAction.SetSize(data.displacementDofs.Size());
residualComponent.SetSize(ndof);
for (int component = 0; component < dimension; ++component) {
data.displacementReferenceTable->GetValues().MultTranspose(
m_quadratureForce.Data() + component * quadraturePointCount, residualComponent.GetData());
for (int dof = 0; dof < ndof; ++dof)
m_elementAction(vector_dof_index(m_fem.displacementFes->GetOrdering(),
dof, component, ndof, dimension)) = residualComponent(dof);
}
if (!vector_is_finite(m_elementAction)) return std::unexpected(non_finite_rejection());
if (data.displacementDofTransformation != nullptr)
data.displacementDofTransformation->TransformDual(m_elementAction);
m_localAction.AddElementVector(data.displacementDofs, m_elementAction);
}
}
if (assembleResidual && !vector_is_finite(m_localAction))
return std::unexpected(non_finite_rejection());
return {};
}
@@ -328,15 +378,36 @@ namespace mean_field::operators {
const context::rotational_displacement_force::RotationalDisplacementForceStateView &state,
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
const physics::RigidRotation &rotation
) {
return TryPrepareImpl(state, dependencies, rotation, true);
}
PreparedRotationalDisplacementForceReport PreparedRotationalDisplacementForceOperator::PrepareReference(
const context::rotational_displacement_force::RotationalDisplacementForceStateView &state,
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
const physics::RigidRotation &rotation
) {
auto result = TryPrepareImpl(state, dependencies, rotation, false);
if (!result.has_value()) throw_rejection(result.error());
return std::move(result).value();
}
std::expected<PreparedRotationalDisplacementForceReport, kernels::RotationalDisplacementForceRejection>
PreparedRotationalDisplacementForceOperator::TryPrepareImpl(
const context::rotational_displacement_force::RotationalDisplacementForceStateView &state,
const context::rotational_displacement_force::RotationalDisplacementForceDependencies &dependencies,
const physics::RigidRotation &rotation,
const bool useFastPreparation
) {
const bool wasPrepared = m_isPrepared;
const bool implementationChanged = m_preparedWithFastResidual != useFastPreparation;
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() && wasPrepared) {
if (!report.contextReport.DidAnyWork() && wasPrepared && !implementationChanged) {
return report;
}
@@ -352,29 +423,38 @@ namespace mean_field::operators {
"rotation state."
);
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());
if (report.contextReport.preparedBaseState || !wasPrepared || implementationChanged) {
const bool inputsFinite = vector_is_finite(m_context.GetBaseDensityTrue()) &&
vector_is_finite(m_context.GetDisplacementTrue());
const auto inputStatus = synchronize_rejection(
inputsFinite ? std::optional<Rejection>{} : std::optional<Rejection>{non_finite_rejection()},
m_fem.mesh->GetComm());
if (!inputStatus.has_value()) return std::unexpected(inputStatus.error());
// Retain the original two-pass path for an independent residual
// comparison. The production path evaluates the geometry once.
if (!useFastPreparation) {
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());
}
const auto elementResult = TryPrepareElementData(useFastPreparation);
const std::optional<Rejection> localRejection = elementResult.has_value()
? std::optional<Rejection>{} : std::optional<Rejection>{elementResult.error()};
const auto elementStatus = synchronize_rejection(localRejection, m_fem.mesh->GetComm());
if (!elementStatus.has_value()) return std::unexpected(elementStatus.error());
if (useFastPreparation) local_to_true(*m_fem.displacementFes, m_localAction, m_actionTrue);
m_cachedResidual.SetSize(m_context.GetDisplacementMap().reduced_size());
m_context.GetDisplacementMap().gather(m_actionTrue, m_cachedResidual);
const auto residualStatus = synchronize_rejection(
vector_is_finite(m_cachedResidual) ? std::optional<Rejection>{}
: std::optional<Rejection>{non_finite_rejection()},
m_fem.mesh->GetComm());
if (!residualStatus.has_value()) return std::unexpected(residualStatus.error());
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_preparedWithFastResidual = useFastPreparation;
++m_residualPreparationCount;
report.preparedResidual = true;
}
@@ -437,7 +517,8 @@ namespace mean_field::operators {
void PreparedRotationalDisplacementForceOperator::ApplyPreparedCompleteJacobianActionTrue(
const mfem::Vector &densityVariationTrue,
const mfem::Vector &displacementVariationTrue,
mfem::Vector &actionTrue
mfem::Vector &actionTrue,
const bool useFastPath, const bool useTensorPath
) const {
true_to_local(*m_fem.densityFes, densityVariationTrue, m_densityVariationLocal);
true_to_local(*m_fem.displacementFes, displacementVariationTrue, m_displacementVariationLocal);
@@ -466,6 +547,61 @@ namespace mean_field::operators {
const mfem::DenseMatrix &directionDofs = directionData.GetDofMatrix();
const int scalarDisplacementDofCount = displacementElement.GetDof();
if (useFastPath && data.fastApplication) {
const int nq = data.integrationRule->GetNPoints();
m_quadratureDensityVariation.SetSize(nq);
if (useTensorPath && data.densityReferenceTable->HasTensorEvaluation3D())
data.densityReferenceTable->EvaluateValuesTensor3D(m_elementDensityVariation, m_quadratureDensityVariation, m_tensorWorkspace);
else data.densityReferenceTable->GetValues().Mult(m_elementDensityVariation, m_quadratureDensityVariation);
m_quadraturePositionVariation.SetSize(nq, 3);
m_quadratureForce.SetSize(nq, 3);
double *position = m_quadraturePositionVariation.Data();
double *force = m_quadratureForce.Data();
const double *u = directionDofs.Data();
const auto &values = data.displacementReferenceTable->GetValues();
const bool tensor = useTensorPath && data.displacementReferenceTable->HasTensorEvaluation3D();
if (tensor) {
data.displacementReferenceTable->EvaluateTensor3D(directionDofs, m_tensorDisplacement, m_tensorWorkspace);
for (int c = 0; c < 3; ++c) for (int q = 0; q < nq; ++q)
position[q + c * nq] = m_tensorDisplacement(q, 4 * c);
} else {
for (int c = 0; c < 3; ++c)
values.Mult(u + c * scalarDisplacementDofCount, position + c * nq);
}
for (int q = 0; q < nq; ++q) {
double trace = 0.0;
if (tensor) {
for (int row = 0; row < 3; ++row) for (int column = 0; column < 3; ++column)
trace += data.inverseElementJacobians(q, row * 3 + column) *
m_tensorDisplacement(q, 4 * column + row + 1);
} else trace = displacement_trace_3d(directionDofs,
data.displacementReferenceTable->GetGradients(q), data.inverseElementJacobians, q);
const double rho = data.baseDensityValues(q);
const double densityWeight = m_quadratureDensityVariation(q) + rho * trace;
for (int c = 0; c < 3; ++c) {
const double da = m_centrifugalJacobian(c, 0) * position[q] +
m_centrifugalJacobian(c, 1) * position[q + nq] +
m_centrifugalJacobian(c, 2) * position[q + 2 * nq];
force[q + c * nq] = data.quadratureWeights(q) *
(densityWeight * data.centrifugalAccelerations(q, c) + rho * da);
}
}
m_elementAction.SetSize(data.displacementDofs.Size());
double *element = m_elementAction.HostWrite();
if (tensor) {
data.displacementReferenceTable->ApplyTransposeTensor3D(m_quadratureForce, m_tensorElement, m_tensorWorkspace);
for (int c = 0; c < 3; ++c) for (int i = 0; i < scalarDisplacementDofCount; ++i)
element[i + c * scalarDisplacementDofCount] = m_tensorElement(i, c);
} else {
for (int c = 0; c < 3; ++c)
values.MultTranspose(force + c * nq, element + c * scalarDisplacementDofCount);
}
if (data.displacementDofTransformation)
data.displacementDofTransformation->TransformDual(m_elementAction);
m_localAction.AddElementVector(data.displacementDofs, m_elementAction);
continue;
}
m_densityShape.SetSize(densityElement.GetDof());
m_displacementShape.SetSize(scalarDisplacementDofCount);
m_referenceDisplacementDShape.SetSize(scalarDisplacementDofCount, dimension);
@@ -546,6 +682,50 @@ namespace mean_field::operators {
++m_completeJacobianStatistics.applications;
}
void PreparedRotationalDisplacementForceOperator::ApplyCompleteJacobianActionReference(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const {
VerifyPrepared();
m_densityVariationTrue.SetSize(m_context.GetDensityMap().full_size());
m_displacementVariationTrue.SetSize(m_context.GetDisplacementMap().full_size());
m_context.GetDensityMap().scatter(densityVariation, m_densityVariationTrue);
m_context.GetDisplacementMap().scatter(displacementVariation, m_displacementVariationTrue);
ApplyPreparedCompleteJacobianActionTrue(m_densityVariationTrue, m_displacementVariationTrue, m_actionTrue, false);
action.SetSize(m_context.GetDisplacementMap().reduced_size());
m_context.GetDisplacementMap().gather(m_actionTrue, action);
}
void PreparedRotationalDisplacementForceOperator::ApplyCompleteJacobianActionCachedReference(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const {
VerifyPrepared();
m_densityVariationTrue.SetSize(m_context.GetDensityMap().full_size());
m_displacementVariationTrue.SetSize(m_context.GetDisplacementMap().full_size());
m_context.GetDensityMap().scatter(densityVariation, m_densityVariationTrue);
m_context.GetDisplacementMap().scatter(displacementVariation, m_displacementVariationTrue);
ApplyPreparedCompleteJacobianActionTrue(m_densityVariationTrue, m_displacementVariationTrue, m_actionTrue, true, false);
action.SetSize(m_context.GetDisplacementMap().reduced_size());
m_context.GetDisplacementMap().gather(m_actionTrue, action);
}
int PreparedRotationalDisplacementForceOperator::GetTensorElementCount() const noexcept {
int count = 0;
for (const auto &data : m_elements)
count += data.fastApplication && data.densityReferenceTable->HasTensorEvaluation3D() &&
data.displacementReferenceTable->HasTensorEvaluation3D();
return count;
}
bool PreparedRotationalDisplacementForceOperator::IsPrepared() const noexcept {
return m_isPrepared && m_rotation.has_value() && m_context.MatchesDependencies(m_preparedDependencies);
}