Snapshot optimized solver and audit diagnostics before numerical experiments
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
module;
|
||||
|
||||
#include "interior_displacement_contractions.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
@@ -184,7 +186,7 @@ namespace mean_field::operators {
|
||||
std::expected<
|
||||
void,
|
||||
kernels::GravityDisplacementForceRejection>
|
||||
PreparedGravityDisplacementForceOperator::TryPrepareElementData() {
|
||||
PreparedGravityDisplacementForceOperator::TryPrepareElementData(const bool assembleResidual) {
|
||||
m_elements.clear();
|
||||
m_elements.reserve(m_fem.mesh->GetNE());
|
||||
|
||||
@@ -206,8 +208,14 @@ namespace mean_field::operators {
|
||||
mfem::Vector elementCompactification;
|
||||
mfem::Vector densityShape;
|
||||
mfem::Vector baseGravityReferenceValue;
|
||||
mfem::Vector baseGravityCellValue;
|
||||
mfem::DenseMatrix gravityGradientShape;
|
||||
|
||||
if (assembleResidual) {
|
||||
m_localAction.SetSize(m_fem.displacementFes->GetVSize());
|
||||
m_localAction = 0.0;
|
||||
}
|
||||
mfem::Vector residualComponent;
|
||||
const int dimension = m_domainMapper.GetDimension();
|
||||
|
||||
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) {
|
||||
@@ -262,6 +270,11 @@ namespace mean_field::operators {
|
||||
data.meshPiolaJacobians.SetSize(data.integrationRule->GetNPoints(), dimension * dimension);
|
||||
}
|
||||
|
||||
data.fastApplication = dimension == 3 && m_fem.displacementFes->GetOrdering() == mfem::Ordering::byNODES &&
|
||||
displacementElement.GetMapType() == mfem::FiniteElement::VALUE &&
|
||||
displacementElement.GetDerivType() == mfem::FiniteElement::GRAD &&
|
||||
data.gravityReferenceTable != nullptr;
|
||||
|
||||
const mapping::ElementDisplacementData displacementData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, elementBaseDisplacement);
|
||||
const mapping::ElementCompactificationData compactificationData(
|
||||
@@ -280,6 +293,7 @@ namespace mean_field::operators {
|
||||
densityShape.SetSize(densityElement.GetDof());
|
||||
gravityGradientShape.SetSize(gravityGradientElement.GetDof(), dimension);
|
||||
baseGravityReferenceValue.SetSize(dimension);
|
||||
if (assembleResidual) m_quadratureForce.SetSize(quadraturePointCount, dimension);
|
||||
|
||||
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
|
||||
const mfem::IntegrationPoint &integrationPoint = data.integrationRule->IntPoint(quadraturePoint);
|
||||
@@ -297,14 +311,26 @@ namespace mean_field::operators {
|
||||
for (int dof = 0; dof < densityElement.GetDof(); ++dof) {
|
||||
densityShape(dof) = data.densityReferenceTable->GetValues()(quadraturePoint, dof);
|
||||
}
|
||||
gravityGradientElement.CalcVShape(*transformation, gravityGradientShape);
|
||||
gravityGradientShape.MultTranspose(elementBaseGravityGradient, baseGravityReferenceValue);
|
||||
const auto *meshGeometry = m_domainMapper.GetFixedMeshGeometry(*transformation, integrationPoint);
|
||||
const double meshWeight = meshGeometry ? meshGeometry->weight : transformation->Weight();
|
||||
if (assembleResidual && meshGeometry && data.gravityReferenceTable) {
|
||||
baseGravityCellValue.SetSize(dimension);
|
||||
data.gravityReferenceTable->GetValues(quadraturePoint).MultTranspose(
|
||||
elementBaseGravityGradient, baseGravityCellValue);
|
||||
meshGeometry->jacobian.Mult(baseGravityCellValue, baseGravityReferenceValue);
|
||||
baseGravityReferenceValue /= meshWeight;
|
||||
} else {
|
||||
gravityGradientElement.CalcVShape(*transformation, gravityGradientShape);
|
||||
gravityGradientShape.MultTranspose(elementBaseGravityGradient, baseGravityReferenceValue);
|
||||
}
|
||||
data.baseDensityValues(quadraturePoint) = elementBaseDensity * densityShape;
|
||||
data.referenceWeights(quadraturePoint) = integrationPoint.weight * transformation->Weight();
|
||||
data.referenceWeights(quadraturePoint) = integrationPoint.weight * meshWeight;
|
||||
|
||||
const mfem::DenseMatrix &inverseMeshJacobian = transformation->InverseJacobian();
|
||||
const mfem::DenseMatrix &meshJacobian = transformation->Jacobian();
|
||||
const double inverseMeshWeight = 1.0 / transformation->Weight();
|
||||
const mfem::DenseMatrix &inverseMeshJacobian =
|
||||
meshGeometry ? meshGeometry->inverseJacobian : transformation->InverseJacobian();
|
||||
const mfem::DenseMatrix &meshJacobian =
|
||||
meshGeometry ? meshGeometry->jacobian : transformation->Jacobian();
|
||||
const double inverseMeshWeight = 1.0 / meshWeight;
|
||||
for (int row = 0; row < dimension; ++row) {
|
||||
data.baseGravityReferenceValues(quadraturePoint, row) = baseGravityReferenceValue(row);
|
||||
for (int column = 0; column < dimension; ++column) {
|
||||
@@ -335,8 +361,40 @@ namespace mean_field::operators {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (assembleResidual) {
|
||||
for (int row = 0; row < dimension; ++row) {
|
||||
double mapped = 0.0;
|
||||
for (int column = 0; column < dimension; ++column)
|
||||
mapped += mappingContext.mapping.mapping_jacobian(row, column) *
|
||||
baseGravityReferenceValue(column);
|
||||
m_quadratureForce(quadraturePoint, row) =
|
||||
data.referenceWeights(quadraturePoint) * data.baseDensityValues(quadraturePoint) * mapped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
|
||||
@@ -352,6 +410,17 @@ namespace mean_field::operators {
|
||||
PreparedGravityDisplacementForceReport,
|
||||
kernels::GravityDisplacementForceRejection>
|
||||
PreparedGravityDisplacementForceOperator::TryPrepare() {
|
||||
return TryPrepareImpl(true);
|
||||
}
|
||||
|
||||
PreparedGravityDisplacementForceReport PreparedGravityDisplacementForceOperator::PrepareReference() {
|
||||
auto result = TryPrepareImpl(false);
|
||||
if (!result.has_value()) throw_rejection(result.error());
|
||||
return std::move(result).value();
|
||||
}
|
||||
|
||||
std::expected<PreparedGravityDisplacementForceReport, kernels::GravityDisplacementForceRejection>
|
||||
PreparedGravityDisplacementForceOperator::TryPrepareImpl(const bool useFastPreparation) {
|
||||
MFEM_VERIFY(
|
||||
m_gravityContext.IsPrepared(), "PreparedGravityDisplacementForceOperator requires the shared "
|
||||
"gravity linearization context to be prepared first."
|
||||
@@ -359,20 +428,24 @@ namespace mean_field::operators {
|
||||
|
||||
const context::gravity_field::GravityFieldRevisions &requestedRevisions = m_gravityContext.GetRevisions();
|
||||
|
||||
if (m_isPrepared && relevant_revisions_match(requestedRevisions, m_preparedRevisions)) {
|
||||
if (m_isPrepared && m_preparedWithFastResidual == useFastPreparation &&
|
||||
relevant_revisions_match(requestedRevisions, m_preparedRevisions)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
m_isPrepared = false;
|
||||
|
||||
/*
|
||||
* Build the reusable element plan before assembling the residual.
|
||||
* This pass stops at the first invalid mapped quadrature point, so a
|
||||
* rejected line-search candidate need not traverse the full stateless
|
||||
* residual kernel. Synchronize before proceeding so every rank takes
|
||||
* the same branch.
|
||||
*/
|
||||
const auto elementResult = TryPrepareElementData();
|
||||
const bool inputsFinite = vector_is_finite(m_gravityContext.GetDensityTrue()) &&
|
||||
vector_is_finite(m_gravityContext.GetGravityGradientTrue()) &&
|
||||
vector_is_finite(m_gravityContext.GetGeometryContext().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());
|
||||
|
||||
// The production path assembles local residual contributions while
|
||||
// preparing the geometry. Synchronize validity before the true-DOF sum.
|
||||
const auto elementResult = TryPrepareElementData(useFastPreparation);
|
||||
const std::optional<Rejection> localElementRejection =
|
||||
elementResult.has_value() ? std::optional<Rejection>{} : std::optional<Rejection>{elementResult.error()};
|
||||
auto synchronizedElement = synchronize_rejection(localElementRejection, m_fem.mesh->GetComm());
|
||||
@@ -380,12 +453,14 @@ namespace mean_field::operators {
|
||||
return std::unexpected(synchronizedElement.error());
|
||||
}
|
||||
|
||||
auto residualResult = kernels::try_apply_gravity_displacement_force_residual(
|
||||
m_fem, m_domainMapper, m_gravityContext.GetDensityTrue(), m_gravityContext.GetGravityGradientTrue(),
|
||||
m_gravityContext.GetGeometryContext().GetDisplacementTrue(), m_actionTrue
|
||||
);
|
||||
if (!residualResult.has_value()) {
|
||||
return std::unexpected(residualResult.error());
|
||||
if (useFastPreparation) {
|
||||
local_to_true(*m_fem.displacementFes, m_localAction, m_actionTrue);
|
||||
} else {
|
||||
auto residualResult = kernels::try_apply_gravity_displacement_force_residual(
|
||||
m_fem, m_domainMapper, m_gravityContext.GetDensityTrue(), m_gravityContext.GetGravityGradientTrue(),
|
||||
m_gravityContext.GetGeometryContext().GetDisplacementTrue(), m_actionTrue
|
||||
);
|
||||
if (!residualResult.has_value()) return std::unexpected(residualResult.error());
|
||||
}
|
||||
m_cachedResidual.SetSize(m_gravityContext.GetDisplacementMap().reduced_size());
|
||||
m_gravityContext.GetDisplacementMap().gather(m_actionTrue, m_cachedResidual);
|
||||
@@ -397,6 +472,7 @@ namespace mean_field::operators {
|
||||
if (!synchronized.has_value()) {
|
||||
return std::unexpected(synchronized.error());
|
||||
}
|
||||
m_preparedWithFastResidual = useFastPreparation;
|
||||
m_preparedRevisions = requestedRevisions;
|
||||
++m_residualPreparationCount;
|
||||
m_isPrepared = true;
|
||||
@@ -471,7 +547,8 @@ namespace mean_field::operators {
|
||||
const mfem::Vector &densityVariationTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
const mfem::Vector &gravityGradientVariationTrue,
|
||||
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.gravityFluxFes, gravityGradientVariationTrue, m_gravityGradientVariationLocal);
|
||||
@@ -513,6 +590,60 @@ 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);
|
||||
data.densityReferenceTable->GetValues().Mult(m_elementDensityVariation, m_quadratureDensityVariation);
|
||||
m_quadratureForce.SetSize(nq, 3);
|
||||
double *force = m_quadratureForce.Data();
|
||||
const bool tensorDisplacement = useTensorPath && data.displacementReferenceTable->HasTensorEvaluation3D();
|
||||
const bool tensorGravity = useTensorPath && data.gravityReferenceTable->HasTensorEvaluation3D();
|
||||
if (tensorDisplacement)
|
||||
data.displacementReferenceTable->EvaluateTensor3D(directionDofs, m_tensorDisplacement, m_tensorWorkspace);
|
||||
if (tensorGravity)
|
||||
data.gravityReferenceTable->EvaluateTensor3D(m_elementGravityGradientVariation, m_tensorGravity, m_tensorWorkspace);
|
||||
for (int q = 0; q < nq; ++q) {
|
||||
double base[3], cellVariation[3], meshVariation[3], material[3], mapped[3];
|
||||
double referenceDirection[3], geometryVariation[3];
|
||||
for (int c = 0; c < 3; ++c) base[c] = data.baseGravityReferenceValues(q, c);
|
||||
if (tensorGravity) {
|
||||
for (int c = 0; c < 3; ++c) cellVariation[c] = m_tensorGravity(q, c);
|
||||
} else vector_basis_value_3d(data.gravityReferenceTable->GetValues(q),
|
||||
m_elementGravityGradientVariation, cellVariation);
|
||||
cached_matrix_vector_3d(data.meshPiolaJacobians, q, cellVariation, meshVariation);
|
||||
const double rho = data.baseDensityValues(q);
|
||||
const double drho = m_quadratureDensityVariation(q);
|
||||
for (int c = 0; c < 3; ++c) material[c] = drho * base[c] + rho * meshVariation[c];
|
||||
cached_matrix_vector_3d(data.mappingJacobians, q, material, mapped);
|
||||
cached_matrix_vector_3d(data.inverseMeshJacobians, q, base, referenceDirection);
|
||||
if (tensorDisplacement) {
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
geometryVariation[c] = 0.0;
|
||||
for (int axis = 0; axis < 3; ++axis)
|
||||
geometryVariation[c] += m_tensorDisplacement(q, 4*c + 1 + axis) * referenceDirection[axis];
|
||||
}
|
||||
} else displacement_directional_value_3d(directionDofs,
|
||||
data.displacementReferenceTable->GetGradients(q), referenceDirection, geometryVariation);
|
||||
for (int c = 0; c < 3; ++c)
|
||||
force[q + c * nq] = data.referenceWeights(q) * (mapped[c] + rho * geometryVariation[c]);
|
||||
}
|
||||
m_elementAction.SetSize(data.displacementDofs.Size());
|
||||
double *element = m_elementAction.HostWrite();
|
||||
if (tensorDisplacement) {
|
||||
data.displacementReferenceTable->ApplyTransposeTensor3D(
|
||||
m_quadratureForce, m_tensorElementDual, m_tensorWorkspace);
|
||||
for (int i = 0; i < m_elementAction.Size(); ++i) element[i] = m_tensorElementDual.Data()[i];
|
||||
} else {
|
||||
for (int c = 0; c < 3; ++c)
|
||||
data.displacementReferenceTable->GetValues().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_gravityGradientShape.SetSize(gravityGradientElement.GetDof(), dimension);
|
||||
@@ -628,6 +759,52 @@ namespace mean_field::operators {
|
||||
++m_completeJacobianStatistics.applications;
|
||||
}
|
||||
|
||||
void PreparedGravityDisplacementForceOperator::ApplyCompleteJacobianActionReference(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
const mfem::Vector &gravityGradientVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
m_densityVariationTrue.SetSize(m_gravityContext.GetDensityMap().full_size());
|
||||
m_gravityGradientVariationTrue.SetSize(m_gravityContext.GetGravityGradientMap().full_size());
|
||||
m_displacementVariationTrue.SetSize(m_gravityContext.GetDisplacementMap().full_size());
|
||||
m_gravityContext.GetDensityMap().scatter(densityVariation, m_densityVariationTrue);
|
||||
m_gravityContext.GetGravityGradientMap().scatter(gravityGradientVariation, m_gravityGradientVariationTrue);
|
||||
m_gravityContext.GetDisplacementMap().scatter(displacementVariation, m_displacementVariationTrue);
|
||||
|
||||
ApplyPreparedCompleteJacobianActionTrue(
|
||||
m_densityVariationTrue, m_displacementVariationTrue, m_gravityGradientVariationTrue, m_actionTrue, false
|
||||
);
|
||||
action.SetSize(m_gravityContext.GetDisplacementMap().reduced_size());
|
||||
m_gravityContext.GetDisplacementMap().gather(m_actionTrue, action);
|
||||
|
||||
}
|
||||
|
||||
void PreparedGravityDisplacementForceOperator::ApplyCompleteJacobianActionCachedReference(
|
||||
const mfem::Vector &densityVariation,
|
||||
const mfem::Vector &displacementVariation,
|
||||
const mfem::Vector &gravityGradientVariation,
|
||||
mfem::Vector &action
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
m_densityVariationTrue.SetSize(m_gravityContext.GetDensityMap().full_size());
|
||||
m_gravityGradientVariationTrue.SetSize(m_gravityContext.GetGravityGradientMap().full_size());
|
||||
m_displacementVariationTrue.SetSize(m_gravityContext.GetDisplacementMap().full_size());
|
||||
m_gravityContext.GetDensityMap().scatter(densityVariation, m_densityVariationTrue);
|
||||
m_gravityContext.GetGravityGradientMap().scatter(gravityGradientVariation, m_gravityGradientVariationTrue);
|
||||
m_gravityContext.GetDisplacementMap().scatter(displacementVariation, m_displacementVariationTrue);
|
||||
|
||||
ApplyPreparedCompleteJacobianActionTrue(
|
||||
m_densityVariationTrue, m_displacementVariationTrue, m_gravityGradientVariationTrue, m_actionTrue, true, false
|
||||
);
|
||||
action.SetSize(m_gravityContext.GetDisplacementMap().reduced_size());
|
||||
m_gravityContext.GetDisplacementMap().gather(m_actionTrue, action);
|
||||
|
||||
}
|
||||
|
||||
bool PreparedGravityDisplacementForceOperator::IsPrepared() const noexcept {
|
||||
if (!m_isPrepared || !m_gravityContext.IsPrepared()) {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user