964 lines
49 KiB
C++
964 lines
49 KiB
C++
module;
|
|
|
|
#include "interior_displacement_contractions.hpp"
|
|
|
|
#include <cmath>
|
|
#include <expected>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
|
|
#include <mfem.hpp>
|
|
#include <mpi.h>
|
|
|
|
module mean_field;
|
|
|
|
import :operators.kernels.gravity_displacement_force;
|
|
import :operators.prepared_gravity_displacement_force;
|
|
import :fem.reference_tables;
|
|
|
|
namespace {
|
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
|
using Rejection = mean_field::operators::kernels::GravityDisplacementForceRejection;
|
|
using Reason = mean_field::operators::kernels::GravityDisplacementForceRejectionReason;
|
|
|
|
[[nodiscard]] bool relevant_revisions_match(
|
|
const mean_field::operators::context::gravity_field::GravityFieldRevisions &left,
|
|
const mean_field::operators::context::gravity_field::GravityFieldRevisions &right
|
|
) noexcept {
|
|
return left.discretization == right.discretization && left.displacement == right.displacement &&
|
|
left.density == right.density && left.gravity_gradient == right.gravity_gradient;
|
|
}
|
|
|
|
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
|
|
return DomainSchema::template attribute_belongs_to<mean_field::utils::domain::Vacuum>(attribute);
|
|
}
|
|
|
|
[[nodiscard]] Rejection mapping_rejection(const mean_field::mapping::MappingStatus status) {
|
|
MFEM_VERIFY(
|
|
status != mean_field::mapping::MappingStatus::invalid_dimension,
|
|
"Prepared gravity force mapping reported an invariant dimension mismatch."
|
|
);
|
|
return {.reason = Reason::invalid_mapping, .mappingStatus = status};
|
|
}
|
|
|
|
[[nodiscard]] Rejection non_finite_rejection() noexcept {
|
|
return {.reason = Reason::non_finite_arithmetic};
|
|
}
|
|
|
|
[[nodiscard]] int encode_rejection(const std::optional<Rejection> &rejection) noexcept {
|
|
if (!rejection.has_value()) {
|
|
return 0;
|
|
}
|
|
if (rejection->reason == Reason::non_finite_arithmetic) {
|
|
return 256;
|
|
}
|
|
return static_cast<int>(rejection->mappingStatus) + 1;
|
|
}
|
|
|
|
[[nodiscard]] Rejection decode_rejection(const int encoded) {
|
|
if (encoded >= 256) {
|
|
return non_finite_rejection();
|
|
}
|
|
return mapping_rejection(static_cast<mean_field::mapping::MappingStatus>(encoded - 1));
|
|
}
|
|
|
|
[[nodiscard]] std::expected<
|
|
void,
|
|
Rejection>
|
|
synchronize_rejection(
|
|
const std::optional<Rejection> &localRejection,
|
|
const MPI_Comm communicator
|
|
) {
|
|
const int localEncoded = encode_rejection(localRejection);
|
|
int globalEncoded = 0;
|
|
if (MPI_Allreduce(&localEncoded, &globalEncoded, 1, MPI_INT, MPI_MAX, communicator) != MPI_SUCCESS) {
|
|
throw std::runtime_error("Could not synchronize prepared gravity-force candidate validity.");
|
|
}
|
|
if (globalEncoded != 0) {
|
|
return std::unexpected(decode_rejection(globalEncoded));
|
|
}
|
|
return {};
|
|
}
|
|
|
|
[[nodiscard]] bool vector_is_finite(const mfem::Vector &vector) noexcept {
|
|
for (int index = 0; index < vector.Size(); ++index) {
|
|
if (!std::isfinite(vector(index))) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
[[noreturn]] void throw_rejection(const Rejection &rejection) {
|
|
if (rejection.reason == Reason::non_finite_arithmetic) {
|
|
throw std::domain_error("Prepared gravity force produced non-finite arithmetic.");
|
|
}
|
|
throw std::domain_error("Prepared gravity force encountered an invalid mapped domain.");
|
|
}
|
|
|
|
void true_to_local(
|
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
|
const mfem::Vector &trueVector,
|
|
mfem::Vector &localVector
|
|
) {
|
|
localVector.SetSize(finiteElementSpace.GetVSize());
|
|
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
|
if (prolongation != nullptr) {
|
|
prolongation->Mult(trueVector, localVector);
|
|
} else {
|
|
localVector = trueVector;
|
|
}
|
|
}
|
|
|
|
void local_to_true(
|
|
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
|
const mfem::Vector &localVector,
|
|
mfem::Vector &trueVector
|
|
) {
|
|
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
|
trueVector = 0.0;
|
|
const mfem::Operator *prolongation = finiteElementSpace.GetProlongationMatrix();
|
|
if (prolongation != nullptr) {
|
|
prolongation->MultTranspose(localVector, trueVector);
|
|
} else {
|
|
trueVector = localVector;
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] int vector_dof_index(
|
|
const mfem::Ordering::Type ordering,
|
|
const int scalarDof,
|
|
const int component,
|
|
const int scalarDofCount,
|
|
const int dimension
|
|
) {
|
|
if (ordering == mfem::Ordering::byNODES) {
|
|
return scalarDof + component * scalarDofCount;
|
|
}
|
|
MFEM_VERIFY(ordering == mfem::Ordering::byVDIM, "Unsupported displacement ordering.");
|
|
return scalarDof * dimension + component;
|
|
}
|
|
|
|
[[nodiscard]] const mfem::IntegrationRule &get_gravity_force_rule(
|
|
const mean_field::fem::FEM &f,
|
|
const mfem::ElementTransformation &transformation
|
|
) {
|
|
using DisplacementField = mean_field::field::Field<mean_field::field::Displacement>;
|
|
const mean_field::quadrature::Query query =
|
|
DisplacementField::make_query<mean_field::field::Displacement::Form::GravityForce>(
|
|
mean_field::quadrature::QuadratureRole::discretization, transformation.OrderW(), {},
|
|
mean_field::utils::DOMAINS::STELLAR, mean_field::quadrature::MappingKind::general
|
|
);
|
|
const mean_field::quadrature::MfemRule rule = f.quadratureFactory->get(query, transformation.GetGeometryType());
|
|
MFEM_VERIFY(
|
|
rule.integration_rule != nullptr,
|
|
"The quadrature policy did not return a gravity-displacement-force integration rule."
|
|
);
|
|
return *rule.integration_rule;
|
|
}
|
|
} // namespace
|
|
|
|
namespace mean_field::operators {
|
|
PreparedGravityDisplacementForceOperator::PreparedGravityDisplacementForceOperator(
|
|
const fem::FEM &f,
|
|
const mapping::DomainMapper &domainMapper,
|
|
const context::gravity_field::GravityFieldLinearizationContext &gravityContext
|
|
)
|
|
: m_fem(f),
|
|
m_domainMapper(domainMapper),
|
|
m_gravityContext(gravityContext) {
|
|
MFEM_VERIFY(m_fem.mesh != nullptr, "PreparedGravityDisplacementForceOperator requires a mesh.");
|
|
|
|
MFEM_VERIFY(
|
|
m_fem.densityFes != nullptr && m_fem.gravityFluxFes != nullptr && m_fem.displacementFes != nullptr,
|
|
"PreparedGravityDisplacementForceOperator requires density, "
|
|
"gravity-gradient, and displacement finite-element spaces."
|
|
);
|
|
|
|
MFEM_VERIFY(
|
|
m_domainMapper.GetDimension() == m_fem.mesh->Dimension(),
|
|
"PreparedGravityDisplacementForceOperator received a mapper "
|
|
"with the wrong dimension."
|
|
);
|
|
}
|
|
|
|
std::expected<
|
|
void,
|
|
kernels::GravityDisplacementForceRejection>
|
|
PreparedGravityDisplacementForceOperator::TryPrepareElementData(const bool assembleResidual) {
|
|
m_elements.clear();
|
|
m_elements.reserve(m_fem.mesh->GetNE());
|
|
|
|
mfem::Vector baseDensityLocal;
|
|
mfem::Vector baseGravityGradientLocal;
|
|
mfem::Vector baseDisplacementLocal;
|
|
true_to_local(*m_fem.densityFes, m_gravityContext.GetDensityTrue(), baseDensityLocal);
|
|
true_to_local(*m_fem.gravityFluxFes, m_gravityContext.GetGravityGradientTrue(), baseGravityGradientLocal);
|
|
true_to_local(
|
|
*m_fem.displacementFes, m_gravityContext.GetGeometryContext().GetDisplacementTrue(), baseDisplacementLocal
|
|
);
|
|
|
|
mapping::DomainMapper::Workspace workspace(m_domainMapper.GetDimension());
|
|
mapping::VolumeMappingContext mappingContext;
|
|
mfem::Array<int> compactificationDofs;
|
|
mfem::Vector elementBaseDensity;
|
|
mfem::Vector elementBaseGravityGradient;
|
|
mfem::Vector elementBaseDisplacement;
|
|
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) {
|
|
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(elementId);
|
|
MFEM_VERIFY(transformation != nullptr, "Prepared gravity force received a null transformation.");
|
|
if (is_vacuum_attribute(transformation->Attribute)) {
|
|
continue;
|
|
}
|
|
|
|
m_elements.emplace_back();
|
|
ElementPAData &data = m_elements.back();
|
|
data.elementId = elementId;
|
|
data.densityDofTransformation = m_fem.densityFes->GetElementDofs(elementId, data.densityDofs);
|
|
data.gravityGradientDofTransformation =
|
|
m_fem.gravityFluxFes->GetElementVDofs(elementId, data.gravityGradientDofs);
|
|
data.displacementDofTransformation =
|
|
m_fem.displacementFes->GetElementVDofs(elementId, data.displacementDofs);
|
|
mfem::DofTransformation *compactificationDofTransformation =
|
|
m_fem.compactificationFes->GetElementDofs(elementId, compactificationDofs);
|
|
|
|
baseDensityLocal.GetSubVector(data.densityDofs, elementBaseDensity);
|
|
baseGravityGradientLocal.GetSubVector(data.gravityGradientDofs, elementBaseGravityGradient);
|
|
baseDisplacementLocal.GetSubVector(data.displacementDofs, elementBaseDisplacement);
|
|
m_fem.compactificationCoordinate->GetSubVector(compactificationDofs, elementCompactification);
|
|
if (data.densityDofTransformation != nullptr) {
|
|
data.densityDofTransformation->InvTransformPrimal(elementBaseDensity);
|
|
}
|
|
if (data.gravityGradientDofTransformation != nullptr) {
|
|
data.gravityGradientDofTransformation->InvTransformPrimal(elementBaseGravityGradient);
|
|
}
|
|
if (data.displacementDofTransformation != nullptr) {
|
|
data.displacementDofTransformation->InvTransformPrimal(elementBaseDisplacement);
|
|
}
|
|
if (compactificationDofTransformation != nullptr) {
|
|
compactificationDofTransformation->InvTransformPrimal(elementCompactification);
|
|
}
|
|
|
|
const mfem::FiniteElement &densityElement = *m_fem.densityFes->GetFE(elementId);
|
|
const mfem::FiniteElement &gravityGradientElement = *m_fem.gravityFluxFes->GetFE(elementId);
|
|
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(elementId);
|
|
const mfem::FiniteElement &compactificationElement = *m_fem.compactificationFes->GetFE(elementId);
|
|
data.integrationRule = &get_gravity_force_rule(m_fem, *transformation);
|
|
data.densityReferenceTable =
|
|
m_fem.GetReferenceTables().GetScalarTable(densityElement, *data.integrationRule);
|
|
data.displacementReferenceTable =
|
|
m_fem.GetReferenceTables().GetScalarTable(displacementElement, *data.integrationRule);
|
|
if (gravityGradientElement.GetMapType() == mfem::FiniteElement::H_DIV &&
|
|
gravityGradientElement.GetDim() == dimension && gravityGradientElement.GetRangeDim() == dimension &&
|
|
transformation->GetSpaceDim() == dimension) {
|
|
data.gravityReferenceTable =
|
|
m_fem.GetReferenceTables().GetVectorTable(gravityGradientElement, *data.integrationRule);
|
|
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(
|
|
compactificationElement, elementCompactification
|
|
);
|
|
const mapping::ElementMappingData mappingData{
|
|
.displacement = displacementData, .compactification = compactificationData
|
|
};
|
|
|
|
const int quadraturePointCount = data.integrationRule->GetNPoints();
|
|
data.mappingJacobians.SetSize(quadraturePointCount, dimension * dimension);
|
|
data.inverseMeshJacobians.SetSize(quadraturePointCount, dimension * dimension);
|
|
data.baseGravityReferenceValues.SetSize(quadraturePointCount, dimension);
|
|
data.baseDensityValues.SetSize(quadraturePointCount);
|
|
data.referenceWeights.SetSize(quadraturePointCount);
|
|
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);
|
|
const mapping::MappingStatus status = m_domainMapper.EvaluateVolume(
|
|
mappingData, *transformation, integrationPoint, workspace, mappingContext
|
|
);
|
|
if (status != mapping::MappingStatus::valid) {
|
|
return std::unexpected(mapping_rejection(status));
|
|
}
|
|
MFEM_VERIFY(
|
|
!mappingContext.mapping.compactified,
|
|
"Prepared gravity force encountered compactification on a stellar element."
|
|
);
|
|
|
|
for (int dof = 0; dof < densityElement.GetDof(); ++dof) {
|
|
densityShape(dof) = data.densityReferenceTable->GetValues()(quadraturePoint, dof);
|
|
}
|
|
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 * meshWeight;
|
|
|
|
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) {
|
|
const int entry = row * dimension + column;
|
|
data.mappingJacobians(quadraturePoint, entry) =
|
|
mappingContext.mapping.mapping_jacobian(row, column);
|
|
data.inverseMeshJacobians(quadraturePoint, entry) = inverseMeshJacobian(row, column);
|
|
if (data.gravityReferenceTable != nullptr) {
|
|
data.meshPiolaJacobians(quadraturePoint, entry) =
|
|
inverseMeshWeight * meshJacobian(row, column);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!std::isfinite(data.baseDensityValues(quadraturePoint)) ||
|
|
!std::isfinite(data.referenceWeights(quadraturePoint)) ||
|
|
!vector_is_finite(baseGravityReferenceValue)) {
|
|
return std::unexpected(non_finite_rejection());
|
|
}
|
|
for (int row = 0; row < dimension; ++row) {
|
|
for (int column = 0; column < dimension; ++column) {
|
|
const int entry = row * dimension + column;
|
|
if (!std::isfinite(data.mappingJacobians(quadraturePoint, entry)) ||
|
|
!std::isfinite(data.inverseMeshJacobians(quadraturePoint, entry)) ||
|
|
(data.gravityReferenceTable != nullptr &&
|
|
!std::isfinite(data.meshPiolaJacobians(quadraturePoint, entry)))) {
|
|
return std::unexpected(non_finite_rejection());
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {};
|
|
}
|
|
|
|
PreparedGravityDisplacementForceReport PreparedGravityDisplacementForceOperator::Prepare() {
|
|
auto result = TryPrepare();
|
|
if (!result.has_value()) {
|
|
throw_rejection(result.error());
|
|
}
|
|
return std::move(result).value();
|
|
}
|
|
|
|
std::expected<
|
|
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."
|
|
);
|
|
|
|
const context::gravity_field::GravityFieldRevisions &requestedRevisions = m_gravityContext.GetRevisions();
|
|
|
|
if (m_isPrepared && m_preparedWithFastResidual == useFastPreparation &&
|
|
relevant_revisions_match(requestedRevisions, m_preparedRevisions)) {
|
|
return {};
|
|
}
|
|
|
|
m_isPrepared = false;
|
|
|
|
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());
|
|
if (!synchronizedElement.has_value()) {
|
|
return std::unexpected(synchronizedElement.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);
|
|
std::optional<Rejection> localRejection;
|
|
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_preparedRevisions = requestedRevisions;
|
|
++m_residualPreparationCount;
|
|
m_isPrepared = true;
|
|
|
|
return PreparedGravityDisplacementForceReport{.preparedResidual = true};
|
|
}
|
|
|
|
void PreparedGravityDisplacementForceOperator::BuildResidual(mfem::Vector &residual) const {
|
|
VerifyPrepared();
|
|
residual = m_cachedResidual;
|
|
++m_residualApplicationCount;
|
|
}
|
|
|
|
void PreparedGravityDisplacementForceOperator::ApplyDensityJacobianAction(
|
|
const mfem::Vector &densityVariation,
|
|
mfem::Vector &action
|
|
) const {
|
|
VerifyPrepared();
|
|
|
|
m_densityVariationTrue.SetSize(m_gravityContext.GetDensityMap().full_size());
|
|
m_gravityContext.GetDensityMap().scatter(densityVariation, m_densityVariationTrue);
|
|
|
|
kernels::apply_gravity_displacement_force_density_action(
|
|
m_fem, m_domainMapper, m_densityVariationTrue, m_gravityContext.GetGravityGradientTrue(),
|
|
m_gravityContext.GetGeometryContext().GetDisplacementTrue(), m_actionTrue
|
|
);
|
|
action.SetSize(m_gravityContext.GetDisplacementMap().reduced_size());
|
|
m_gravityContext.GetDisplacementMap().gather(m_actionTrue, action);
|
|
|
|
++m_densityJacobianStatistics.applications;
|
|
}
|
|
|
|
void PreparedGravityDisplacementForceOperator::ApplyGravityGradientJacobianAction(
|
|
const mfem::Vector &gravityGradientVariation,
|
|
mfem::Vector &action
|
|
) const {
|
|
VerifyPrepared();
|
|
|
|
m_gravityGradientVariationTrue.SetSize(m_gravityContext.GetGravityGradientMap().full_size());
|
|
m_gravityContext.GetGravityGradientMap().scatter(gravityGradientVariation, m_gravityGradientVariationTrue);
|
|
|
|
kernels::apply_gravity_displacement_force_gradient_action(
|
|
m_fem, m_domainMapper, m_gravityContext.GetDensityTrue(), m_gravityGradientVariationTrue,
|
|
m_gravityContext.GetGeometryContext().GetDisplacementTrue(), m_actionTrue
|
|
);
|
|
action.SetSize(m_gravityContext.GetDisplacementMap().reduced_size());
|
|
m_gravityContext.GetDisplacementMap().gather(m_actionTrue, action);
|
|
|
|
++m_gravityGradientJacobianStatistics.applications;
|
|
}
|
|
|
|
void PreparedGravityDisplacementForceOperator::ApplyDisplacementJacobianAction(
|
|
const mfem::Vector &displacementVariation,
|
|
mfem::Vector &action
|
|
) const {
|
|
VerifyPrepared();
|
|
|
|
m_displacementVariationTrue.SetSize(m_gravityContext.GetDisplacementMap().full_size());
|
|
m_gravityContext.GetDisplacementMap().scatter(displacementVariation, m_displacementVariationTrue);
|
|
|
|
kernels::apply_gravity_displacement_force_displacement_action(
|
|
m_fem, m_domainMapper, m_gravityContext.GetDensityTrue(), m_gravityContext.GetGravityGradientTrue(),
|
|
m_displacementVariationTrue, m_gravityContext.GetGeometryContext().GetDisplacementTrue(), m_actionTrue
|
|
);
|
|
action.SetSize(m_gravityContext.GetDisplacementMap().reduced_size());
|
|
m_gravityContext.GetDisplacementMap().gather(m_actionTrue, action);
|
|
|
|
++m_displacementJacobianStatistics.applications;
|
|
}
|
|
|
|
void PreparedGravityDisplacementForceOperator::ApplyPreparedCompleteJacobianActionTrue(
|
|
const mfem::Vector &densityVariationTrue,
|
|
const mfem::Vector &displacementVariationTrue,
|
|
const mfem::Vector &gravityGradientVariationTrue,
|
|
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);
|
|
true_to_local(*m_fem.displacementFes, displacementVariationTrue, m_displacementVariationLocal);
|
|
m_localAction.SetSize(m_fem.displacementFes->GetVSize());
|
|
m_localAction = 0.0;
|
|
|
|
const int dimension = m_domainMapper.GetDimension();
|
|
const mfem::Ordering::Type ordering = m_fem.displacementFes->GetOrdering();
|
|
|
|
for (const ElementPAData &data : m_elements) {
|
|
MFEM_VERIFY(data.integrationRule != nullptr, "Prepared gravity force has no integration rule.");
|
|
MFEM_VERIFY(
|
|
data.densityReferenceTable != nullptr && data.displacementReferenceTable != nullptr,
|
|
"Prepared gravity force has no reference basis tables."
|
|
);
|
|
|
|
m_densityVariationLocal.GetSubVector(data.densityDofs, m_elementDensityVariation);
|
|
m_gravityGradientVariationLocal.GetSubVector(data.gravityGradientDofs, m_elementGravityGradientVariation);
|
|
m_displacementVariationLocal.GetSubVector(data.displacementDofs, m_elementDisplacementVariation);
|
|
if (data.densityDofTransformation != nullptr) {
|
|
data.densityDofTransformation->InvTransformPrimal(m_elementDensityVariation);
|
|
}
|
|
if (data.gravityGradientDofTransformation != nullptr) {
|
|
data.gravityGradientDofTransformation->InvTransformPrimal(m_elementGravityGradientVariation);
|
|
}
|
|
if (data.displacementDofTransformation != nullptr) {
|
|
data.displacementDofTransformation->InvTransformPrimal(m_elementDisplacementVariation);
|
|
}
|
|
|
|
const mfem::FiniteElement &densityElement = *m_fem.densityFes->GetFE(data.elementId);
|
|
const mfem::FiniteElement &gravityGradientElement = *m_fem.gravityFluxFes->GetFE(data.elementId);
|
|
const mfem::FiniteElement &displacementElement = *m_fem.displacementFes->GetFE(data.elementId);
|
|
mfem::ElementTransformation *transformation = m_fem.mesh->GetElementTransformation(data.elementId);
|
|
MFEM_VERIFY(transformation != nullptr, "Prepared gravity force received a null transformation.");
|
|
|
|
const mapping::ElementDisplacementData directionData =
|
|
mapping::ElementDisplacementDataFromElementVDofs(displacementElement, m_elementDisplacementVariation);
|
|
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);
|
|
m_referenceDisplacementJacobian.SetSize(dimension, dimension);
|
|
m_displacementJacobianVariation.SetSize(dimension, dimension);
|
|
m_mappingJacobian.SetSize(dimension, dimension);
|
|
m_inverseMeshJacobian.SetSize(dimension, dimension);
|
|
m_meshPiolaJacobian.SetSize(dimension, dimension);
|
|
m_baseGravityReferenceValue.SetSize(dimension);
|
|
m_gravityVariationReferenceValue.SetSize(dimension);
|
|
m_gravityVariationReferenceCellValue.SetSize(dimension);
|
|
m_mappedBaseGravity.SetSize(dimension);
|
|
m_mappedGravityVariation.SetSize(dimension);
|
|
m_mappedGeometryVariation.SetSize(dimension);
|
|
m_forceValue.SetSize(dimension);
|
|
m_elementAction.SetSize(data.displacementDofs.Size());
|
|
m_elementAction = 0.0;
|
|
|
|
for (int quadraturePoint = 0; quadraturePoint < data.integrationRule->GetNPoints(); ++quadraturePoint) {
|
|
const mfem::IntegrationPoint &integrationPoint = data.integrationRule->IntPoint(quadraturePoint);
|
|
const mfem::DenseMatrix &referenceDisplacementDShape =
|
|
data.displacementReferenceTable->GetGradients(quadraturePoint);
|
|
mfem::MultAtB(directionDofs, referenceDisplacementDShape, m_referenceDisplacementJacobian);
|
|
const mfem::DenseMatrix &densityValues = data.densityReferenceTable->GetValues();
|
|
const mfem::DenseMatrix &displacementValues = data.displacementReferenceTable->GetValues();
|
|
for (int dof = 0; dof < densityElement.GetDof(); ++dof) {
|
|
m_densityShape(dof) = densityValues(quadraturePoint, dof);
|
|
}
|
|
for (int dof = 0; dof < scalarDisplacementDofCount; ++dof) {
|
|
m_displacementShape(dof) = displacementValues(quadraturePoint, dof);
|
|
}
|
|
|
|
if (data.gravityReferenceTable != nullptr) {
|
|
data.gravityReferenceTable->GetValues(quadraturePoint)
|
|
.MultTranspose(m_elementGravityGradientVariation, m_gravityVariationReferenceCellValue);
|
|
} else {
|
|
transformation->SetIntPoint(&integrationPoint);
|
|
gravityGradientElement.CalcVShape(*transformation, m_gravityGradientShape);
|
|
m_gravityGradientShape.MultTranspose(
|
|
m_elementGravityGradientVariation, m_gravityVariationReferenceValue
|
|
);
|
|
}
|
|
|
|
for (int row = 0; row < dimension; ++row) {
|
|
m_baseGravityReferenceValue(row) = data.baseGravityReferenceValues(quadraturePoint, row);
|
|
for (int column = 0; column < dimension; ++column) {
|
|
const int entry = row * dimension + column;
|
|
m_mappingJacobian(row, column) = data.mappingJacobians(quadraturePoint, entry);
|
|
m_inverseMeshJacobian(row, column) = data.inverseMeshJacobians(quadraturePoint, entry);
|
|
if (data.gravityReferenceTable != nullptr) {
|
|
m_meshPiolaJacobian(row, column) = data.meshPiolaJacobians(quadraturePoint, entry);
|
|
}
|
|
}
|
|
}
|
|
if (data.gravityReferenceTable != nullptr) {
|
|
m_meshPiolaJacobian.Mult(m_gravityVariationReferenceCellValue, m_gravityVariationReferenceValue);
|
|
}
|
|
mfem::Mult(m_referenceDisplacementJacobian, m_inverseMeshJacobian, m_displacementJacobianVariation);
|
|
m_mappingJacobian.Mult(m_baseGravityReferenceValue, m_mappedBaseGravity);
|
|
m_mappingJacobian.Mult(m_gravityVariationReferenceValue, m_mappedGravityVariation);
|
|
m_displacementJacobianVariation.Mult(m_baseGravityReferenceValue, m_mappedGeometryVariation);
|
|
|
|
const double densityVariationValue = m_elementDensityVariation * m_densityShape;
|
|
const double baseDensityValue = data.baseDensityValues(quadraturePoint);
|
|
m_forceValue = 0.0;
|
|
m_forceValue.Add(densityVariationValue, m_mappedBaseGravity);
|
|
m_forceValue.Add(baseDensityValue, m_mappedGravityVariation);
|
|
m_forceValue.Add(baseDensityValue, m_mappedGeometryVariation);
|
|
m_forceValue *= data.referenceWeights(quadraturePoint);
|
|
|
|
for (int scalarDof = 0; scalarDof < scalarDisplacementDofCount; ++scalarDof) {
|
|
for (int component = 0; component < dimension; ++component) {
|
|
const int vectorDof =
|
|
vector_dof_index(ordering, scalarDof, component, scalarDisplacementDofCount, dimension);
|
|
m_elementAction(vectorDof) += m_displacementShape(scalarDof) * m_forceValue(component);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (data.displacementDofTransformation != nullptr) {
|
|
data.displacementDofTransformation->TransformDual(m_elementAction);
|
|
}
|
|
m_localAction.AddElementVector(data.displacementDofs, m_elementAction);
|
|
}
|
|
|
|
local_to_true(*m_fem.displacementFes, m_localAction, actionTrue);
|
|
}
|
|
|
|
void PreparedGravityDisplacementForceOperator::ApplyCompleteJacobianAction(
|
|
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
|
|
);
|
|
action.SetSize(m_gravityContext.GetDisplacementMap().reduced_size());
|
|
m_gravityContext.GetDisplacementMap().gather(m_actionTrue, action);
|
|
|
|
++m_densityJacobianStatistics.applications;
|
|
++m_gravityGradientJacobianStatistics.applications;
|
|
++m_displacementJacobianStatistics.applications;
|
|
++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;
|
|
}
|
|
|
|
return relevant_revisions_match(m_gravityContext.GetRevisions(), m_preparedRevisions);
|
|
}
|
|
|
|
std::uint64_t PreparedGravityDisplacementForceOperator::GetResidualPreparationCount() const noexcept {
|
|
return m_residualPreparationCount;
|
|
}
|
|
|
|
std::uint64_t PreparedGravityDisplacementForceOperator::GetResidualApplicationCount() const noexcept {
|
|
return m_residualApplicationCount;
|
|
}
|
|
|
|
const PreparedGravityDisplacementForceColumnStatistics &
|
|
PreparedGravityDisplacementForceOperator::GetDensityJacobianStatistics() const noexcept {
|
|
return m_densityJacobianStatistics;
|
|
}
|
|
|
|
const PreparedGravityDisplacementForceColumnStatistics &
|
|
PreparedGravityDisplacementForceOperator::GetGravityGradientJacobianStatistics() const noexcept {
|
|
return m_gravityGradientJacobianStatistics;
|
|
}
|
|
|
|
const PreparedGravityDisplacementForceColumnStatistics &
|
|
PreparedGravityDisplacementForceOperator::GetDisplacementJacobianStatistics() const noexcept {
|
|
return m_displacementJacobianStatistics;
|
|
}
|
|
|
|
const PreparedGravityDisplacementForceCompleteStatistics &
|
|
PreparedGravityDisplacementForceOperator::GetCompleteJacobianStatistics() const noexcept {
|
|
return m_completeJacobianStatistics;
|
|
}
|
|
|
|
const fem::FEM &PreparedGravityDisplacementForceOperator::GetFEM() const noexcept {
|
|
return m_fem;
|
|
}
|
|
|
|
const context::gravity_field::GravityFieldLinearizationContext &
|
|
PreparedGravityDisplacementForceOperator::GetGravityContext() const noexcept {
|
|
return m_gravityContext;
|
|
}
|
|
|
|
void PreparedGravityDisplacementForceOperator::VerifyPrepared() const {
|
|
MFEM_VERIFY(
|
|
IsPrepared(), "PreparedGravityDisplacementForceOperator must be prepared for "
|
|
"the current shared gravity-context revisions before residual or "
|
|
"Jacobian application."
|
|
);
|
|
}
|
|
|
|
PreparedGravityDisplacementForceJacobianOperator::PreparedGravityDisplacementForceJacobianOperator(
|
|
const GravityDisplacementForceLayout &layout,
|
|
const PreparedGravityDisplacementForceOperator &preparedOperator
|
|
)
|
|
: mfem::Operator(
|
|
layout.residual_offsets().Last(),
|
|
layout.value_offsets().Last()
|
|
),
|
|
m_layout(layout),
|
|
m_preparedOperator(preparedOperator) {
|
|
using Form = utils::blocks::barotropic_equilibrium_form;
|
|
|
|
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
|
|
|
constexpr auto displacementValue =
|
|
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
|
|
|
constexpr auto gravityGradientValue =
|
|
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
|
|
|
|
constexpr auto displacementResidual =
|
|
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
|
|
|
MFEM_VERIFY(
|
|
m_layout.size(densityValue) == m_preparedOperator.GetGravityContext().GetDensityMap().reduced_size() &&
|
|
m_layout.size(displacementValue) ==
|
|
m_preparedOperator.GetGravityContext().GetDisplacementMap().reduced_size() &&
|
|
m_layout.size(gravityGradientValue) ==
|
|
m_preparedOperator.GetGravityContext().GetGravityGradientMap().reduced_size() &&
|
|
m_layout.size(displacementResidual) ==
|
|
m_preparedOperator.GetGravityContext().GetDisplacementMap().reduced_size(),
|
|
"Prepared gravity-displacement-force MFEM adapter received "
|
|
"incompatible coupled block sizes."
|
|
);
|
|
}
|
|
|
|
void PreparedGravityDisplacementForceJacobianOperator::Mult(
|
|
const mfem::Vector &direction,
|
|
mfem::Vector &action
|
|
) const {
|
|
MFEM_VERIFY(
|
|
m_preparedOperator.IsPrepared(), "Prepared gravity-displacement-force MFEM adapter requires a "
|
|
"prepared operator."
|
|
);
|
|
|
|
MFEM_VERIFY(
|
|
direction.Size() == Width(), "Prepared gravity-displacement-force MFEM adapter received a "
|
|
"direction with the wrong size."
|
|
);
|
|
|
|
using Form = utils::blocks::barotropic_equilibrium_form;
|
|
|
|
constexpr auto densityValue = utils::blocks::get_value_block<Form>(utils::blocks::density_field.mass_term);
|
|
|
|
constexpr auto displacementValue =
|
|
utils::blocks::get_value_block<Form>(utils::blocks::displacement_field.geometry_term);
|
|
|
|
constexpr auto gravityGradientValue =
|
|
utils::blocks::get_value_block<Form>(utils::blocks::gravity_field.gradient_term);
|
|
|
|
constexpr auto displacementResidual =
|
|
utils::blocks::get_residual_block<Form>(utils::blocks::displacement_field.geometry_term);
|
|
|
|
const mfem::Vector densityVariation(
|
|
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(densityValue), m_layout.size(densityValue)
|
|
);
|
|
|
|
const mfem::Vector displacementVariation(
|
|
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(displacementValue),
|
|
m_layout.size(displacementValue)
|
|
);
|
|
|
|
const mfem::Vector gravityGradientVariation(
|
|
const_cast<mfem::real_t *>(direction.GetData()) + m_layout.offset(gravityGradientValue),
|
|
m_layout.size(gravityGradientValue)
|
|
);
|
|
|
|
mfem::Vector displacementAction;
|
|
|
|
m_preparedOperator.ApplyCompleteJacobianAction(
|
|
densityVariation, displacementVariation, gravityGradientVariation, displacementAction
|
|
);
|
|
|
|
MFEM_VERIFY(
|
|
displacementAction.Size() == m_layout.size(displacementResidual),
|
|
"Prepared gravity-displacement-force MFEM adapter produced a "
|
|
"displacement action with the wrong size."
|
|
);
|
|
|
|
action.SetSize(Height());
|
|
action = 0.0;
|
|
|
|
const int residualOffset = m_layout.offset(displacementResidual);
|
|
|
|
for (int entry = 0; entry < displacementAction.Size(); ++entry) {
|
|
action(residualOffset + entry) = displacementAction(entry);
|
|
}
|
|
}
|
|
|
|
const GravityDisplacementForceLayout &PreparedGravityDisplacementForceJacobianOperator::GetLayout() const noexcept {
|
|
return m_layout;
|
|
}
|
|
} // namespace mean_field::operators
|