Snapshot optimized solver and audit diagnostics before numerical experiments
This commit is contained in:
@@ -9,6 +9,7 @@ module;
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <mpi.h>
|
||||
|
||||
@@ -16,6 +17,7 @@ module mean_field;
|
||||
|
||||
import :fem.reference_tables;
|
||||
import :operators.prepared_hdiv_mass;
|
||||
import :mapping.kelvin;
|
||||
|
||||
namespace {
|
||||
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
||||
@@ -304,6 +306,124 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
// Keep MFEM's PA assembly, restrictions, signed RT ordering, and diagonal.
|
||||
// Only the CPU element contraction changes. Scratch is sized to the actual
|
||||
// rule, and the two domain forms visit only their marked elements.
|
||||
class TensorHDivMassIntegrator final : public mfem::VectorFEMassIntegrator {
|
||||
public:
|
||||
TensorHDivMassIntegrator(mfem::MatrixCoefficient &coefficient, const mfem::Array<int> &marker)
|
||||
: mfem::VectorFEMassIntegrator(coefficient), m_marker(marker) {}
|
||||
|
||||
void AssemblePA(const mfem::FiniteElementSpace &space) override {
|
||||
mfem::VectorFEMassIntegrator::AssemblePA(space);
|
||||
m_activeElements.clear();
|
||||
m_supported = !mfem::Device::Allows(~mfem::Backend::CPU) &&
|
||||
dim == 3 && trial_fetype == mfem::FiniteElement::DIV &&
|
||||
test_fetype == trial_fetype && dofs1D == dofs1Dtest;
|
||||
if (!m_supported) return;
|
||||
for (int e = 0; e < space.GetNE(); ++e) {
|
||||
const int attribute = space.GetMesh()->GetAttribute(e);
|
||||
if (attribute > 0 && attribute <= m_marker.Size() && m_marker[attribute - 1])
|
||||
m_activeElements.push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] int FastElementCount() const noexcept {
|
||||
return m_supported ? static_cast<int>(m_activeElements.size()) : 0;
|
||||
}
|
||||
[[nodiscard]] bool UseFastApplication() const noexcept { return m_useFast; }
|
||||
void SetFastApplication(bool value) const noexcept { m_useFast = value; }
|
||||
|
||||
void AddMultPA(const mfem::Vector &x, mfem::Vector &y) const override {
|
||||
if (!m_useFast || !m_supported) {
|
||||
mfem::VectorFEMassIntegrator::AddMultPA(x, y);
|
||||
return;
|
||||
}
|
||||
// The current RT2 / nine-point-per-axis rule is specialized; other
|
||||
// supported CPU orders use exactly the same algorithm at runtime.
|
||||
if (dofs1D == 4 && quad1D == 9) Apply<4, 9>(x, y);
|
||||
else Apply<0, 0>(x, y);
|
||||
}
|
||||
|
||||
private:
|
||||
template <int D, int Q>
|
||||
void Apply(const mfem::Vector &xVector, mfem::Vector &yVector) const {
|
||||
const int d = D == 0 ? dofs1D : D, q = Q == 0 ? quad1D : Q;
|
||||
const int nq = q*q*q, nd = 3*d*(d-1)*(d-1), entries = symmetric ? 6 : 9;
|
||||
MFEM_VERIFY(xVector.Size() == ne*nd && yVector.Size() == ne*nd,
|
||||
"H(div) PA element vectors have incompatible sizes.");
|
||||
m_workspace.SetSize(3*nq + q*d*d + q*q*d);
|
||||
double *values = m_workspace.HostWrite(), *sx = values + 3*nq, *sxy = sx + q*d*d;
|
||||
const double *x = xVector.HostRead(), *op = pa_data.HostRead();
|
||||
double *y = yVector.HostReadWrite();
|
||||
const double *bc = mapsC->B.HostRead(), *bo = mapsO->B.HostRead();
|
||||
for (const int e : m_activeElements) {
|
||||
int offset = e*nd;
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
const int dx = c == 0 ? d : d-1, dy = c == 1 ? d : d-1, dz = c == 2 ? d : d-1;
|
||||
const double *bx = c == 0 ? bc : bo, *by = c == 1 ? bc : bo, *bz = c == 2 ? bc : bo;
|
||||
for (int k = 0; k < dz; ++k) for (int j = 0; j < dy; ++j) for (int a = 0; a < q; ++a) {
|
||||
double v = 0.0;
|
||||
for (int i = 0; i < dx; ++i) v += bx[a + q*i]*x[offset + i + dx*(j + dy*k)];
|
||||
sx[a + q*(j + dy*k)] = v;
|
||||
}
|
||||
for (int k = 0; k < dz; ++k) for (int b = 0; b < q; ++b) for (int a = 0; a < q; ++a) {
|
||||
double v = 0.0;
|
||||
for (int j = 0; j < dy; ++j) v += by[b + q*j]*sx[a + q*(j + dy*k)];
|
||||
sxy[a + q*(b + q*k)] = v;
|
||||
}
|
||||
for (int z = 0; z < q; ++z) for (int b = 0; b < q; ++b) for (int a = 0; a < q; ++a) {
|
||||
double v = 0.0;
|
||||
for (int k = 0; k < dz; ++k) v += bz[z + q*k]*sxy[a + q*(b + q*k)];
|
||||
values[a + q*(b + q*z) + nq*c] = v;
|
||||
}
|
||||
offset += dx*dy*dz;
|
||||
}
|
||||
const double *metric = op + e*nq*entries;
|
||||
for (int p = 0; p < nq; ++p) {
|
||||
const double u = values[p], v = values[p+nq], w = values[p+2*nq];
|
||||
const double a00 = metric[p], a01 = metric[p+nq], a02 = metric[p+2*nq];
|
||||
const double a10 = symmetric ? a01 : metric[p+3*nq];
|
||||
const double a11 = metric[p+(symmetric ? 3 : 4)*nq];
|
||||
const double a12 = metric[p+(symmetric ? 4 : 5)*nq];
|
||||
const double a20 = symmetric ? a02 : metric[p+6*nq];
|
||||
const double a21 = symmetric ? a12 : metric[p+7*nq];
|
||||
const double a22 = metric[p+(symmetric ? 5 : 8)*nq];
|
||||
values[p] = a00*u + a01*v + a02*w;
|
||||
values[p+nq] = a10*u + a11*v + a12*w;
|
||||
values[p+2*nq] = a20*u + a21*v + a22*w;
|
||||
}
|
||||
offset = e*nd;
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
const int dx = c == 0 ? d : d-1, dy = c == 1 ? d : d-1, dz = c == 2 ? d : d-1;
|
||||
const double *bx = c == 0 ? bc : bo, *by = c == 1 ? bc : bo, *bz = c == 2 ? bc : bo;
|
||||
for (int k = 0; k < dz; ++k) for (int b = 0; b < q; ++b) for (int a = 0; a < q; ++a) {
|
||||
double v = 0.0;
|
||||
for (int z = 0; z < q; ++z) v += bz[z + q*k]*values[a + q*(b + q*z) + nq*c];
|
||||
sxy[a + q*(b + q*k)] = v;
|
||||
}
|
||||
for (int k = 0; k < dz; ++k) for (int j = 0; j < dy; ++j) for (int a = 0; a < q; ++a) {
|
||||
double v = 0.0;
|
||||
for (int b = 0; b < q; ++b) v += by[b + q*j]*sxy[a + q*(b + q*k)];
|
||||
sx[a + q*(j + dy*k)] = v;
|
||||
}
|
||||
for (int k = 0; k < dz; ++k) for (int j = 0; j < dy; ++j) for (int i = 0; i < dx; ++i) {
|
||||
double v = 0.0;
|
||||
for (int a = 0; a < q; ++a) v += bx[a + q*i]*sx[a + q*(j + dy*k)];
|
||||
y[offset + i + dx*(j + dy*k)] += v;
|
||||
}
|
||||
offset += dx*dy*dz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mfem::Array<int> m_marker;
|
||||
std::vector<int> m_activeElements;
|
||||
mutable mfem::Vector m_workspace;
|
||||
bool m_supported{false};
|
||||
mutable bool m_useFast{true};
|
||||
};
|
||||
|
||||
class FrozenMappedHDivMassCoefficient final : public mfem::MatrixCoefficient {
|
||||
public:
|
||||
FrozenMappedHDivMassCoefficient(
|
||||
@@ -520,6 +640,11 @@ namespace mean_field::operators {
|
||||
mfem::Vector elementDisplacement;
|
||||
mfem::Vector elementCompactification;
|
||||
mapping::VolumeMappingContext mappingContext;
|
||||
mfem::DenseMatrix inverseMeshJacobian(3), compactificationGradients;
|
||||
mfem::Vector coordinateGradient(3), scaleGradient(3);
|
||||
const auto *kelvin = dynamic_cast<const mapping::compactification::KelvinCompactification *>(
|
||||
&m_domain_mapper.GetExteriorMap()
|
||||
);
|
||||
|
||||
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) {
|
||||
m_variationElements.emplace_back();
|
||||
@@ -566,6 +691,24 @@ namespace mean_field::operators {
|
||||
data.integrationRule->GetNPoints(), frozen_mapping_width(m_domain_mapper.GetDimension())
|
||||
);
|
||||
|
||||
const bool compactified = m_domain_mapper.IsCompactifiedElement(*transformation);
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> compactificationTable;
|
||||
if (dimension == 3 && data.gravityReferenceTable != nullptr &&
|
||||
displacementElement.GetMapType() == mfem::FiniteElement::VALUE &&
|
||||
displacementElement.GetDerivType() == mfem::FiniteElement::GRAD &&
|
||||
displacementElement.GetDim() == 3 &&
|
||||
(!compactified || (kelvin != nullptr &&
|
||||
compactificationElement.GetDerivType() == mfem::FiniteElement::GRAD))) {
|
||||
data.displacementReferenceTable =
|
||||
m_fem.GetReferenceTables().GetScalarTable(displacementElement, *data.integrationRule);
|
||||
data.tangentFactors.SetSize(12, data.integrationRule->GetNPoints());
|
||||
if (compactified) {
|
||||
compactificationTable = m_fem.GetReferenceTables().GetScalarTable(
|
||||
compactificationElement, *data.integrationRule
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapping::ElementDisplacementData displacementData =
|
||||
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, data.baseDisplacement);
|
||||
const mapping::ElementCompactificationData compactificationData(
|
||||
@@ -584,11 +727,42 @@ namespace mean_field::operators {
|
||||
return status;
|
||||
}
|
||||
freeze_mapping_context(mappingContext, quadraturePoint, data.frozenMappingData);
|
||||
if (data.displacementReferenceTable != nullptr) {
|
||||
// Use the same fixed-mesh inverse as EvaluateVolumeVariation.
|
||||
mfem::Mult(mappingContext.quadrature.J_inv, mappingContext.mapping.mapping_jacobian,
|
||||
inverseMeshJacobian);
|
||||
double scale = 1.0;
|
||||
scaleGradient = 0.0;
|
||||
if (compactified) {
|
||||
const auto &values = compactificationTable->GetValues();
|
||||
double coordinate = 0.0;
|
||||
for (int dof = 0; dof < data.compactification.Size(); ++dof)
|
||||
coordinate += values(quadraturePoint, dof) * data.compactification(dof);
|
||||
compactificationGradients.SetSize(compactificationElement.GetDof(), dimension);
|
||||
mfem::Mult(compactificationTable->GetGradients(quadraturePoint), inverseMeshJacobian,
|
||||
compactificationGradients);
|
||||
compactificationGradients.MultTranspose(data.compactification, coordinateGradient);
|
||||
const auto factorStatus = kelvin->EvaluateJacobianVariationFactors(
|
||||
coordinate, coordinateGradient, scale, scaleGradient
|
||||
);
|
||||
if (factorStatus != mapping::MappingStatus::valid) return factorStatus;
|
||||
}
|
||||
double *factors = data.tangentFactors.GetColumn(quadraturePoint);
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
const double value = scale * inverseMeshJacobian(row, column);
|
||||
if (!std::isfinite(value)) return mapping::MappingStatus::non_finite_result;
|
||||
factors[3 * row + column] = value;
|
||||
}
|
||||
factors[9 + row] = scaleGradient(row);
|
||||
}
|
||||
}
|
||||
if (data.gravityReferenceTable != nullptr) {
|
||||
// CalcVShape_RT = reference_shape * J_mesh^T / Weight.
|
||||
// Cache only this small factor, never the mapped basis.
|
||||
const double meshWeight = transformation->Weight();
|
||||
const mfem::DenseMatrix &meshJacobian = transformation->Jacobian();
|
||||
const auto *meshGeometry = m_domain_mapper.GetFixedMeshGeometry(*transformation, integrationPoint);
|
||||
const double meshWeight = meshGeometry ? meshGeometry->weight : transformation->Weight();
|
||||
const mfem::DenseMatrix &meshJacobian = meshGeometry ? meshGeometry->jacobian : transformation->Jacobian();
|
||||
const double inverseMeshWeight = 1.0 / meshWeight;
|
||||
data.referenceWeights(quadraturePoint) = integrationPoint.weight * meshWeight;
|
||||
for (int row = 0; row < dimension; ++row) {
|
||||
@@ -693,8 +867,11 @@ namespace mean_field::operators {
|
||||
m_stellar_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
m_vacuum_mass_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL);
|
||||
|
||||
auto stellar_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(*m_stellar_mass_coefficient);
|
||||
auto vacuum_integrator = std::make_unique<mfem::VectorFEMassIntegrator>(*m_vacuum_mass_coefficient);
|
||||
auto stellar_integrator = std::make_unique<TensorHDivMassIntegrator>(*m_stellar_mass_coefficient, m_stellar_marker);
|
||||
auto vacuum_integrator = std::make_unique<TensorHDivMassIntegrator>(*m_vacuum_mass_coefficient, m_vacuum_marker);
|
||||
auto *stellarIntegratorView = stellar_integrator.get();
|
||||
auto *vacuumIntegratorView = vacuum_integrator.get();
|
||||
m_fastMassElements = 0;
|
||||
|
||||
m_fem.quadratureFactory->configure_gravity_hdiv_mass(
|
||||
*stellar_integrator, quadrature::QuadratureRole::discretization, stellar_element, stellar_transformation,
|
||||
@@ -731,6 +908,7 @@ namespace mean_field::operators {
|
||||
return preparationResult;
|
||||
}
|
||||
|
||||
m_fastMassElements = stellarIntegratorView->FastElementCount() + vacuumIntegratorView->FastElementCount();
|
||||
m_has_variation_data = mode == PreparationMode::linearization;
|
||||
m_is_prepared = true;
|
||||
++m_preparation_count;
|
||||
@@ -791,10 +969,71 @@ namespace mean_field::operators {
|
||||
}
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::MultReference(
|
||||
const mfem::Vector &gravityGradient, mfem::Vector &action
|
||||
) const {
|
||||
MFEM_VERIFY(m_is_prepared && m_stellar_mass_form && m_vacuum_mass_form,
|
||||
"The H(div) mass reference requires a prepared operator.");
|
||||
const auto *stellar = dynamic_cast<const TensorHDivMassIntegrator *>((*m_stellar_mass_form->GetDBFI())[0]);
|
||||
const auto *vacuum = dynamic_cast<const TensorHDivMassIntegrator *>((*m_vacuum_mass_form->GetDBFI())[0]);
|
||||
MFEM_VERIFY(stellar && vacuum, "The H(div) mass forms have unexpected integrators.");
|
||||
struct RestoreMode {
|
||||
const TensorHDivMassIntegrator *integrator;
|
||||
bool previous;
|
||||
~RestoreMode() { integrator->SetFastApplication(previous); }
|
||||
} restoreStellar{stellar, stellar->UseFastApplication()}, restoreVacuum{vacuum, vacuum->UseFastApplication()};
|
||||
stellar->SetFastApplication(false);
|
||||
vacuum->SetFastApplication(false);
|
||||
Mult(gravityGradient, action);
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::MultDisplacementVariationTrue(
|
||||
const mfem::Vector &gravityGradientTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue
|
||||
) const {
|
||||
MEAN_FIELD_PROFILE_SCOPE("PreparedMappedHDivMassOperator::MultDisplacementVariationTrue");
|
||||
MultDisplacementVariationTrueImpl(gravityGradientTrue, displacementVariationTrue, actionVariationTrue, true, true);
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::MultDisplacementVariationTrueReference(
|
||||
const mfem::Vector &gravityGradientTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue
|
||||
) const {
|
||||
MEAN_FIELD_PROFILE_SCOPE("PreparedMappedHDivMassOperator::MultDisplacementVariationTrueReference");
|
||||
MultDisplacementVariationTrueImpl(gravityGradientTrue, displacementVariationTrue, actionVariationTrue, false, false);
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::MultDisplacementVariationTrueCachedReference(
|
||||
const mfem::Vector &gravityGradientTrue, const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue
|
||||
) const {
|
||||
MultDisplacementVariationTrueImpl(gravityGradientTrue, displacementVariationTrue, actionVariationTrue, true, false);
|
||||
}
|
||||
|
||||
int PreparedMappedHDivMassOperator::GetTensorVariationElementCount() const noexcept {
|
||||
if (!m_is_prepared || !m_has_variation_data) return 0;
|
||||
int count = 0;
|
||||
for (const auto &data : m_variationElements)
|
||||
if (data.displacementReferenceTable && data.displacementReferenceTable->HasTensorEvaluation3D() &&
|
||||
data.gravityReferenceTable && data.gravityReferenceTable->HasTensorEvaluation3D()) ++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
int PreparedMappedHDivMassOperator::GetFastVariationElementCount() const noexcept {
|
||||
if (!m_is_prepared || !m_has_variation_data) return 0;
|
||||
int count = 0;
|
||||
for (const auto &data : m_variationElements)
|
||||
if (data.displacementReferenceTable != nullptr) ++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::MultDisplacementVariationTrueImpl(
|
||||
const mfem::Vector &gravityGradientTrue,
|
||||
const mfem::Vector &displacementVariationTrue,
|
||||
mfem::Vector &actionVariationTrue,
|
||||
const bool useFastPath, const bool useTensorPath
|
||||
) const {
|
||||
MFEM_VERIFY(
|
||||
m_is_prepared, "PreparedMappedHDivMassOperator must be prepared before applying a displacement variation."
|
||||
@@ -837,6 +1076,11 @@ namespace mean_field::operators {
|
||||
data.displacementDofTransformation->InvTransformPrimal(m_elementDisplacementVariation);
|
||||
}
|
||||
|
||||
if (useFastPath && data.displacementReferenceTable != nullptr) {
|
||||
AddFastElementVariation(data, useTensorPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
const mfem::FiniteElement &gravityGradientElement = *m_fem.gravityFluxFes->GetFE(data.elementId);
|
||||
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
|
||||
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(data.elementId);
|
||||
@@ -924,6 +1168,111 @@ namespace mean_field::operators {
|
||||
local_to_true(*m_fem.gravityFluxFes, m_localVariationAction, actionVariationTrue);
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::AddFastElementVariation(
|
||||
const ElementVariationData &data, const bool useTensorPath
|
||||
) const {
|
||||
// GetElementVDofs always returns component blocks, independently of the
|
||||
// global FES ordering. Signed DOFs and primal transforms were applied by
|
||||
// the caller; preserve the dual transform and scatter below.
|
||||
const int dofs = data.displacementReferenceTable->GetDofCount();
|
||||
MFEM_VERIFY(m_elementDisplacementVariation.Size() == 3 * dofs,
|
||||
"Prepared H(div) tangent has an incompatible displacement layout.");
|
||||
const auto &values = data.displacementReferenceTable->GetValues();
|
||||
const bool compactified = data.frozenMappingData(0, 47) != 0.0;
|
||||
m_elementVariationAction.SetSize(m_elementGravityGradient.Size());
|
||||
m_elementVariationAction = 0.0;
|
||||
m_gravityReferenceCellValue.SetSize(3);
|
||||
m_referenceCellDual.SetSize(3);
|
||||
const bool tensorDisplacement = useTensorPath && data.displacementReferenceTable->HasTensorEvaluation3D();
|
||||
const bool tensorGravity = useTensorPath && data.gravityReferenceTable->HasTensorEvaluation3D();
|
||||
if (tensorDisplacement) {
|
||||
const mfem::DenseMatrix direction(m_elementDisplacementVariation.GetData(), dofs, 3);
|
||||
data.displacementReferenceTable->EvaluateTensor3D(direction, m_tensorDisplacement, m_tensorWorkspace);
|
||||
}
|
||||
if (tensorGravity) {
|
||||
data.gravityReferenceTable->EvaluateTensor3D(m_elementGravityGradient, m_tensorGravity, m_tensorWorkspace);
|
||||
m_tensorDual.SetSize(data.integrationRule->GetNPoints(), 3);
|
||||
}
|
||||
|
||||
for (int q = 0; q < data.integrationRule->GetNPoints(); ++q) {
|
||||
const auto &gradients = data.displacementReferenceTable->GetGradients(q);
|
||||
const double *factors = data.tangentFactors.GetColumn(q);
|
||||
double deltaJ[3][3]{};
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
const double *coefficients = m_elementDisplacementVariation.GetData() + component * dofs;
|
||||
double value = 0.0;
|
||||
double referenceGradient[3]{};
|
||||
if (tensorDisplacement) {
|
||||
value = compactified ? m_tensorDisplacement(q, 4*component) : 0.0;
|
||||
for (int axis = 0; axis < 3; ++axis)
|
||||
referenceGradient[axis] = m_tensorDisplacement(q, 4*component + 1 + axis);
|
||||
} else {
|
||||
for (int dof = 0; dof < dofs; ++dof) {
|
||||
const double coefficient = coefficients[dof];
|
||||
if (compactified) value += coefficient * values(q, dof);
|
||||
for (int axis = 0; axis < 3; ++axis)
|
||||
referenceGradient[axis] += coefficient * gradients(dof, axis);
|
||||
}
|
||||
}
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
deltaJ[component][column] = value * factors[9 + column];
|
||||
for (int axis = 0; axis < 3; ++axis)
|
||||
deltaJ[component][column] += referenceGradient[axis] * factors[3 * axis + column];
|
||||
MFEM_VERIFY(std::isfinite(deltaJ[component][column]),
|
||||
"Prepared H(div) tangent produced a non-finite mapping derivative.");
|
||||
}
|
||||
}
|
||||
|
||||
const auto &referenceShape = data.gravityReferenceTable->GetValues(q);
|
||||
if (tensorGravity) {
|
||||
for (int c = 0; c < 3; ++c) m_gravityReferenceCellValue(c) = m_tensorGravity(q, c);
|
||||
} else referenceShape.MultTranspose(m_elementGravityGradient, m_gravityReferenceCellValue);
|
||||
double piola[3][3], jacobian[3][3];
|
||||
double gravity[3]{}, mappedGravity[3]{}, mappedVariation[3]{};
|
||||
double trace = 0.0;
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
piola[row][column] = data.meshPiolaJacobians(q, 3 * row + column);
|
||||
jacobian[row][column] = data.frozenMappingData(q, 18 + 3 * row + column);
|
||||
gravity[row] += piola[row][column] * m_gravityReferenceCellValue(column);
|
||||
trace += data.frozenMappingData(q, 27 + 3 * row + column) * deltaJ[column][row];
|
||||
}
|
||||
}
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
mappedGravity[row] += jacobian[row][column] * gravity[column];
|
||||
mappedVariation[row] += deltaJ[row][column] * gravity[column];
|
||||
}
|
||||
}
|
||||
// M = J^T J / det(J). Apply delta M directly to the three-vector:
|
||||
// [delta J^T (Jg) + J^T(delta J g - tr(J^-1 delta J) Jg)] / det(J).
|
||||
const double inverseDeterminant = 1.0 / data.frozenMappingData(q, 45);
|
||||
double massAction[3]{};
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
massAction[column] += deltaJ[row][column] * mappedGravity[row] +
|
||||
jacobian[row][column] * (mappedVariation[row] - trace * mappedGravity[row]);
|
||||
}
|
||||
massAction[column] *= inverseDeterminant;
|
||||
MFEM_VERIFY(std::isfinite(massAction[column]),
|
||||
"Prepared H(div) tangent produced a non-finite mass action.");
|
||||
}
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
double dual = 0.0;
|
||||
for (int row = 0; row < 3; ++row) dual += piola[row][column] * massAction[row];
|
||||
m_referenceCellDual(column) = dual;
|
||||
}
|
||||
if (tensorGravity) {
|
||||
for (int c = 0; c < 3; ++c) m_tensorDual(q, c) = data.referenceWeights(q) * m_referenceCellDual(c);
|
||||
} else referenceShape.AddMult(m_referenceCellDual, m_elementVariationAction, data.referenceWeights(q));
|
||||
}
|
||||
if (tensorGravity)
|
||||
data.gravityReferenceTable->ApplyTransposeTensor3D(m_tensorDual, m_elementVariationAction, m_tensorWorkspace);
|
||||
if (data.gravityGradientDofTransformation != nullptr)
|
||||
data.gravityGradientDofTransformation->TransformDual(m_elementVariationAction);
|
||||
m_localVariationAction.AddElementVector(data.gravityGradientDofs, m_elementVariationAction);
|
||||
}
|
||||
|
||||
void PreparedMappedHDivMassOperator::AssembleDiagonal(mfem::Vector &diagonal) const {
|
||||
mfem::Vector true_diagonal;
|
||||
AssembleTrueDiagonal(true_diagonal);
|
||||
|
||||
Reference in New Issue
Block a user