Snapshot optimized solver and audit diagnostics before numerical experiments
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
module;
|
||||
|
||||
#include "interior_displacement_contractions.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
@@ -406,13 +409,42 @@ namespace mean_field::operators {
|
||||
PreparedPressureForceOperator::TryPrepare(
|
||||
const context::pressure_force::PressureForceStateView &state,
|
||||
const context::pressure_force::PressureForceDependencies &dependencies
|
||||
) {
|
||||
return TryPrepareImpl(state, dependencies, true);
|
||||
}
|
||||
|
||||
PreparedPressureForceReport PreparedPressureForceOperator::PrepareReference(
|
||||
const context::pressure_force::PressureForceStateView &state,
|
||||
const context::pressure_force::PressureForceDependencies &dependencies
|
||||
) {
|
||||
auto result = TryPrepareImpl(state, dependencies, false, false);
|
||||
if (!result.has_value()) throw_rejection(result.error());
|
||||
return std::move(result).value();
|
||||
}
|
||||
|
||||
PreparedPressureForceReport PreparedPressureForceOperator::PrepareCachedReference(
|
||||
const context::pressure_force::PressureForceStateView &state,
|
||||
const context::pressure_force::PressureForceDependencies &dependencies
|
||||
) {
|
||||
auto result = TryPrepareImpl(state, dependencies, true, false);
|
||||
if (!result.has_value()) throw_rejection(result.error());
|
||||
return std::move(result).value();
|
||||
}
|
||||
|
||||
std::expected<PreparedPressureForceReport, PressureForcePreparationRejection>
|
||||
PreparedPressureForceOperator::TryPrepareImpl(
|
||||
const context::pressure_force::PressureForceStateView &state,
|
||||
const context::pressure_force::PressureForceDependencies &dependencies,
|
||||
const bool useFastPreparation, const bool useTensorPreparation
|
||||
) {
|
||||
const bool wasPrepared = m_isPrepared;
|
||||
const bool implementationChanged = m_preparedWithFastMaterialAssembly != useFastPreparation ||
|
||||
m_preparedWithTensorPreparation != useTensorPreparation;
|
||||
PreparedPressureForceReport report;
|
||||
|
||||
report.contextReport = m_context.Prepare(state, dependencies);
|
||||
|
||||
if (!report.contextReport.DidAnyWork() && wasPrepared) {
|
||||
if (!report.contextReport.DidAnyWork() && wasPrepared && !implementationChanged) {
|
||||
return report;
|
||||
}
|
||||
|
||||
@@ -433,16 +465,16 @@ namespace mean_field::operators {
|
||||
PrepareStaticPlan();
|
||||
}
|
||||
|
||||
if (report.contextReport.preparedGeometryState || !wasPrepared) {
|
||||
const auto globalGeometryFailure = synchronize_rejection(PrepareGeometry(), m_fem.enthalpyFes->GetComm());
|
||||
if (report.contextReport.preparedGeometryState || !wasPrepared || implementationChanged) {
|
||||
const auto globalGeometryFailure = synchronize_rejection(PrepareGeometry(useTensorPreparation), m_fem.enthalpyFes->GetComm());
|
||||
if (globalGeometryFailure.has_value()) {
|
||||
return std::unexpected(*globalGeometryFailure);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<PressureForcePreparationRejection> localMaterialFailure;
|
||||
if (report.contextReport.preparedMaterialState || !wasPrepared) {
|
||||
localMaterialFailure = PrepareMaterialState();
|
||||
if (report.contextReport.preparedMaterialState || !wasPrepared || implementationChanged) {
|
||||
localMaterialFailure = PrepareMaterialState(useFastPreparation, useTensorPreparation);
|
||||
}
|
||||
|
||||
const auto globalMaterialFailure = synchronize_rejection(localMaterialFailure, m_fem.enthalpyFes->GetComm());
|
||||
@@ -450,7 +482,7 @@ namespace mean_field::operators {
|
||||
return std::unexpected(*globalMaterialFailure);
|
||||
}
|
||||
|
||||
if (report.contextReport.preparedMaterialState || !wasPrepared) {
|
||||
if (report.contextReport.preparedMaterialState || !wasPrepared || implementationChanged) {
|
||||
FinalizeDisplacementJacobianPreparation();
|
||||
|
||||
const auto globalAssemblyFailure =
|
||||
@@ -482,6 +514,8 @@ namespace mean_field::operators {
|
||||
"supported displacement size."
|
||||
);
|
||||
|
||||
m_preparedWithTensorPreparation = useTensorPreparation;
|
||||
m_preparedWithFastMaterialAssembly = useFastPreparation;
|
||||
m_isPrepared = true;
|
||||
|
||||
return report;
|
||||
@@ -564,7 +598,7 @@ namespace mean_field::operators {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<PressureForcePreparationRejection> PreparedPressureForceOperator::PrepareGeometry() {
|
||||
std::optional<PressureForcePreparationRejection> PreparedPressureForceOperator::PrepareGeometry(const bool useTensorPreparation) {
|
||||
mfem::Vector displacementLocal;
|
||||
|
||||
true_to_local(*m_fem.displacementFes, m_baseDisplacementTrue, displacementLocal);
|
||||
@@ -574,6 +608,8 @@ namespace mean_field::operators {
|
||||
|
||||
mfem::Vector elementDisplacement;
|
||||
mfem::Vector elementCompactification;
|
||||
mfem::Vector sampleValue(3), tensorWorkspace;
|
||||
mfem::DenseMatrix sampleGradient(3), tensorSamples;
|
||||
|
||||
for (ElementPAData &data : m_elements) {
|
||||
MFEM_VERIFY(data.integrationRule != nullptr, "Prepared pressure-force geometry has no integration rule.");
|
||||
@@ -626,13 +662,24 @@ namespace mean_field::operators {
|
||||
|
||||
data.physicalTestGradients.resize(quadraturePointCount);
|
||||
|
||||
const bool tensor = useTensorPreparation && data.displacementReferenceTable &&
|
||||
data.displacementReferenceTable->HasTensorEvaluation3D();
|
||||
if (tensor) data.displacementReferenceTable->EvaluateTensor3D(
|
||||
data.baseDisplacementData->GetDofMatrix(), tensorSamples, tensorWorkspace);
|
||||
|
||||
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
|
||||
const mfem::IntegrationPoint &integrationPoint = data.integrationRule->IntPoint(quadraturePoint);
|
||||
|
||||
transformation->SetIntPoint(&integrationPoint);
|
||||
|
||||
if (tensor) for (int c = 0; c < 3; ++c) {
|
||||
sampleValue(c) = tensorSamples(quadraturePoint, 4*c);
|
||||
for (int a = 0; a < 3; ++a)
|
||||
sampleGradient(c,a) = tensorSamples(quadraturePoint, 4*c+a+1);
|
||||
}
|
||||
const mapping::DomainMapper::ReferenceDisplacementSample sample{sampleValue, sampleGradient};
|
||||
const mapping::MappingStatus mappingStatus = m_domainMapper.EvaluateVolume(
|
||||
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
||||
mappingData, *transformation, integrationPoint, workspace, mappingContext, tensor ? &sample : nullptr
|
||||
);
|
||||
|
||||
if (mappingStatus != mapping::MappingStatus::valid) {
|
||||
@@ -675,7 +722,9 @@ namespace mean_field::operators {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<PressureForcePreparationRejection> PreparedPressureForceOperator::PrepareMaterialState() {
|
||||
std::optional<PressureForcePreparationRejection> PreparedPressureForceOperator::PrepareMaterialState(
|
||||
const bool useFastPreparation, const bool useTensorPreparation
|
||||
) {
|
||||
mfem::Vector enthalpyLocal;
|
||||
|
||||
true_to_local(*m_fem.enthalpyFes, m_baseEnthalpyTrue, enthalpyLocal);
|
||||
@@ -687,6 +736,8 @@ namespace mean_field::operators {
|
||||
|
||||
const mfem::Ordering::Type displacementOrdering = m_fem.displacementFes->GetOrdering();
|
||||
std::optional<PressureForcePreparationRejection> materialFailure;
|
||||
mfem::DenseMatrix weightedGradients;
|
||||
mfem::DenseMatrix tensorFactors, tensorInverse;
|
||||
|
||||
for (ElementPAData &data : m_elements) {
|
||||
enthalpyLocal.GetSubVector(data.enthalpyDofs, elementEnthalpy);
|
||||
@@ -717,7 +768,9 @@ namespace mean_field::operators {
|
||||
|
||||
quadratureEnthalpy.SetSize(quadraturePointCount);
|
||||
|
||||
enthalpyBasis.Mult(elementEnthalpy, quadratureEnthalpy);
|
||||
if (useTensorPreparation && data.enthalpyReferenceTable->HasTensorEvaluation3D())
|
||||
data.enthalpyReferenceTable->EvaluateValuesTensor3D(elementEnthalpy, quadratureEnthalpy, m_tensorWorkspace);
|
||||
else enthalpyBasis.Mult(elementEnthalpy, quadratureEnthalpy);
|
||||
|
||||
data.pressure.SetSize(quadraturePointCount);
|
||||
|
||||
@@ -796,14 +849,72 @@ namespace mean_field::operators {
|
||||
|
||||
data.elementResidual(vectorDof) -= residualContribution;
|
||||
|
||||
for (int enthalpyDof = 0; enthalpyDof < enthalpyDofCount; ++enthalpyDof) {
|
||||
const double jacobianContribution =
|
||||
pressureDerivative * weightedTestGradient * enthalpyBasis(quadraturePoint, enthalpyDof);
|
||||
if (!std::isfinite(jacobianContribution)) {
|
||||
materialFailure = non_finite_rejection();
|
||||
continue;
|
||||
if (!useFastPreparation) {
|
||||
for (int enthalpyDof = 0; enthalpyDof < enthalpyDofCount; ++enthalpyDof) {
|
||||
const double jacobianContribution = pressureDerivative * weightedTestGradient *
|
||||
enthalpyBasis(quadraturePoint, enthalpyDof);
|
||||
if (!std::isfinite(jacobianContribution)) {
|
||||
materialFailure = non_finite_rejection();
|
||||
continue;
|
||||
}
|
||||
data.enthalpyJacobian(vectorDof, enthalpyDof) -= jacobianContribution;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (useFastPreparation && useTensorPreparation &&
|
||||
data.displacementReferenceTable->HasTensorEvaluation3D() && !materialFailure.has_value()) {
|
||||
// Build each enthalpy column by a tensor gradient transpose.
|
||||
// These are the same -w P'(h) grad(test) B_h entries.
|
||||
tensorFactors.SetSize(quadraturePointCount, 9);
|
||||
m_tensorDuals.SetSize(quadraturePointCount, 9);
|
||||
for (int q = 0; q < quadraturePointCount; ++q) {
|
||||
data.baseMappingContexts.LoadInverseJacobian(q, tensorInverse);
|
||||
const double w = -data.pressureDerivative(q)*data.quadratureWeights(q);
|
||||
for (int c = 0; c < 3; ++c) for (int a = 0; a < 3; ++a)
|
||||
tensorFactors(q, 3*c+a) = w*tensorInverse(a,c);
|
||||
}
|
||||
for (int col = 0; col < enthalpyDofCount; ++col) {
|
||||
for (int a = 0; a < 9; ++a) for (int q = 0; q < quadraturePointCount; ++q)
|
||||
m_tensorDuals(q,a) = tensorFactors(q,a)*enthalpyBasis(q,col);
|
||||
data.displacementReferenceTable->ApplyGradientTransposeTensor3D(
|
||||
m_tensorDuals, m_tensorElement, m_tensorWorkspace);
|
||||
for (int c = 0; c < 3; ++c) for (int i = 0; i < scalarDisplacementDofCount; ++i)
|
||||
data.enthalpyJacobian(i+c*scalarDisplacementDofCount,col) = m_tensorElement(i,c);
|
||||
}
|
||||
} else if (useFastPreparation && !materialFailure.has_value()) {
|
||||
// J_h = -sum_q w_q P'(h_q) grad(test)_q B_h(q).
|
||||
// Pack once, then accumulate small contiguous output tiles.
|
||||
// The old loop is retained above as the independent reference.
|
||||
weightedGradients.SetSize(displacementDofCount, quadraturePointCount);
|
||||
for (int q = 0; q < quadraturePointCount; ++q) {
|
||||
for (int c = 0; c < dimension; ++c) {
|
||||
for (int i = 0; i < scalarDisplacementDofCount; ++i) {
|
||||
const int row = vector_dof_index(
|
||||
displacementOrdering, i, c, scalarDisplacementDofCount, dimension);
|
||||
weightedGradients(row, q) = -data.pressureDerivative(q) *
|
||||
(data.quadratureWeights(q) * data.physicalTestGradients[q](i, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
const double *g = weightedGradients.Data();
|
||||
const double *b = enthalpyBasis.Data();
|
||||
double *j = data.enthalpyJacobian.Data();
|
||||
constexpr int rowBlock = 32, columnBlock = 8;
|
||||
for (int colBegin = 0; colBegin < enthalpyDofCount; colBegin += columnBlock) {
|
||||
const int colEnd = std::min(colBegin + columnBlock, enthalpyDofCount);
|
||||
for (int rowBegin = 0; rowBegin < displacementDofCount; rowBegin += rowBlock) {
|
||||
const int rowEnd = std::min(rowBegin + rowBlock, displacementDofCount);
|
||||
for (int q = 0; q < quadraturePointCount; ++q) {
|
||||
const double *gradient = g + q * displacementDofCount;
|
||||
for (int col = colBegin; col < colEnd; ++col) {
|
||||
const double basis = b[q + col * quadraturePointCount];
|
||||
double *column = j + col * displacementDofCount;
|
||||
for (int row = rowBegin; row < rowEnd; ++row)
|
||||
column[row] += gradient[row] * basis;
|
||||
}
|
||||
data.enthalpyJacobian(vectorDof, enthalpyDof) -= jacobianContribution;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -938,8 +1049,32 @@ namespace mean_field::operators {
|
||||
}
|
||||
|
||||
void PreparedPressureForceOperator::ApplyDisplacementJacobianAction(
|
||||
const mfem::Vector &displacementVariation,
|
||||
mfem::Vector &action
|
||||
const mfem::Vector &displacementVariation, mfem::Vector &action
|
||||
) const {
|
||||
ApplyDisplacementJacobianActionImpl(displacementVariation, action, true);
|
||||
}
|
||||
|
||||
void PreparedPressureForceOperator::ApplyDisplacementJacobianActionReference(
|
||||
const mfem::Vector &displacementVariation, mfem::Vector &action
|
||||
) const {
|
||||
ApplyDisplacementJacobianActionImpl(displacementVariation, action, false, false);
|
||||
}
|
||||
|
||||
void PreparedPressureForceOperator::ApplyDisplacementJacobianActionCachedReference(
|
||||
const mfem::Vector &displacementVariation, mfem::Vector &action
|
||||
) const {
|
||||
ApplyDisplacementJacobianActionImpl(displacementVariation, action, true, false);
|
||||
}
|
||||
|
||||
int PreparedPressureForceOperator::GetTensorElementCount() const noexcept {
|
||||
int count = 0;
|
||||
for (const auto &data : m_elements)
|
||||
count += data.displacementReferenceTable->HasTensorEvaluation3D();
|
||||
return count;
|
||||
}
|
||||
|
||||
void PreparedPressureForceOperator::ApplyDisplacementJacobianActionImpl(
|
||||
const mfem::Vector &displacementVariation, mfem::Vector &action, const bool useFastPath, const bool useTensorPath
|
||||
) const {
|
||||
VerifyPrepared();
|
||||
|
||||
@@ -1017,7 +1152,39 @@ namespace mean_field::operators {
|
||||
matrixTemporary.SetSize(dimension, dimension);
|
||||
physicalTestGradientVariation.SetSize(scalarDisplacementDofCount, dimension);
|
||||
|
||||
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount; ++quadraturePoint) {
|
||||
const bool tensor = useTensorPath && data.displacementReferenceTable->HasTensorEvaluation3D();
|
||||
if (tensor) {
|
||||
data.displacementReferenceTable->EvaluateTensor3D(directionDofs, m_tensorSamples, m_tensorWorkspace);
|
||||
m_tensorDuals.SetSize(quadraturePointCount, 9);
|
||||
for (int q = 0; q < quadraturePointCount; ++q) {
|
||||
data.baseMappingContexts.LoadInverseJacobian(q, inverseElementJacobian);
|
||||
double gradient[3][3]{};
|
||||
for (int c = 0; c < 3; ++c) for (int j = 0; j < 3; ++j)
|
||||
for (int a = 0; a < 3; ++a)
|
||||
gradient[c][j] += m_tensorSamples(q, 4*c+a+1)*inverseElementJacobian(a,j);
|
||||
const double trace = gradient[0][0]+gradient[1][1]+gradient[2][2];
|
||||
const double weight = data.pressure(q)*data.quadratureWeights(q);
|
||||
// d(-P grad(test) dx): grad_ref(test) J^-1 (du/dx - tr(du/dx) I).
|
||||
for (int c = 0; c < 3; ++c) for (int a = 0; a < 3; ++a) {
|
||||
double value = 0.0;
|
||||
for (int j = 0; j < 3; ++j)
|
||||
value += inverseElementJacobian(a,j)*(gradient[j][c]-(j==c ? trace : 0.0));
|
||||
m_tensorDuals(q, 3*c+a) = weight*value;
|
||||
}
|
||||
}
|
||||
data.displacementReferenceTable->ApplyGradientTransposeTensor3D(
|
||||
m_tensorDuals, m_tensorElement, m_tensorWorkspace);
|
||||
for (int c = 0; c < 3; ++c) for (int i = 0; i < scalarDisplacementDofCount; ++i)
|
||||
elementAction(i+c*scalarDisplacementDofCount) = m_tensorElement(i,c);
|
||||
}
|
||||
|
||||
for (int quadraturePoint = 0; !tensor && quadraturePoint < quadraturePointCount; ++quadraturePoint) {
|
||||
if (useFastPath && dimension == 3) {
|
||||
pressure_displacement_action_3d(
|
||||
directionDofs, data.physicalTestGradients[quadraturePoint],
|
||||
data.pressure(quadraturePoint) * data.quadratureWeights(quadraturePoint), elementAction);
|
||||
continue;
|
||||
}
|
||||
const mfem::DenseMatrix &referenceTestGradient =
|
||||
data.displacementReferenceTable->GetGradients(quadraturePoint);
|
||||
|
||||
@@ -1076,7 +1243,7 @@ namespace mean_field::operators {
|
||||
|
||||
m_displacementMap.gather(m_fullDisplacementAction, action);
|
||||
|
||||
++m_displacementJacobianStatistics.applications;
|
||||
if (useFastPath) ++m_displacementJacobianStatistics.applications;
|
||||
}
|
||||
|
||||
void PreparedPressureForceOperator::ApplyCompleteJacobianAction(
|
||||
|
||||
Reference in New Issue
Block a user