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 <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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user