1280 lines
46 KiB
C++
1280 lines
46 KiB
C++
module;
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
|
|
#include <mfem.hpp>
|
|
|
|
module mean_field;
|
|
|
|
import :operators.prepared_hydrostatic_equilibrium;
|
|
|
|
namespace {
|
|
using DomainSchema = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
|
|
|
[[nodiscard]] bool is_vacuum_attribute(const int attribute) {
|
|
return DomainSchema::template attribute_belongs_to<
|
|
mean_field::utils::domain::Vacuum>(attribute);
|
|
}
|
|
|
|
void true_to_local(const mfem::ParFiniteElementSpace &finiteElementSpace,
|
|
const mfem::Vector &trueVector, mfem::Vector &localVector) {
|
|
MFEM_VERIFY(trueVector.Size() == finiteElementSpace.GetTrueVSize(),
|
|
"Hydrostatic true vector has the wrong size.");
|
|
|
|
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) {
|
|
MFEM_VERIFY(localVector.Size() == finiteElementSpace.GetVSize(),
|
|
"Hydrostatic local vector has the wrong size.");
|
|
|
|
trueVector.SetSize(finiteElementSpace.GetTrueVSize());
|
|
trueVector = 0.0;
|
|
|
|
const mfem::Operator *prolongation =
|
|
finiteElementSpace.GetProlongationMatrix();
|
|
|
|
if (prolongation != nullptr) {
|
|
prolongation->MultTranspose(localVector, trueVector);
|
|
} else {
|
|
trueVector = localVector;
|
|
}
|
|
}
|
|
|
|
void copy_vector_block(const mfem::Vector &source, const int offset,
|
|
const int size, mfem::Vector &block) {
|
|
MFEM_VERIFY(offset >= 0 && size >= 0 && offset + size <= source.Size(),
|
|
"Hydrostatic Jacobian block lies outside the "
|
|
"input vector.");
|
|
|
|
block.SetSize(size);
|
|
|
|
for (int entry = 0; entry < size; ++entry) {
|
|
block(entry) = source(offset + entry);
|
|
}
|
|
}
|
|
|
|
const mfem::IntegrationRule &
|
|
get_hydrostatic_rule(const mean_field::fem::FEM &f,
|
|
const mfem::FiniteElement &enthalpyElement,
|
|
const mfem::FiniteElement &gravityPotentialElement,
|
|
const mfem::ElementTransformation &transformation) {
|
|
using EnthalpyField = mean_field::field::Field<mean_field::field::Enthalpy>;
|
|
|
|
MFEM_VERIFY(enthalpyElement.GetOrder() ==
|
|
mean_field::field::Enthalpy::Scalar::familyOrder,
|
|
"The prepared hydrostatic enthalpy element does "
|
|
"not match the registered field.");
|
|
|
|
MFEM_VERIFY(gravityPotentialElement.GetOrder() ==
|
|
mean_field::field::Gravity::Potential::familyOrder,
|
|
"The prepared hydrostatic potential element does "
|
|
"not match the registered field.");
|
|
|
|
const auto enthalpyQuery = EnthalpyField::make_query<
|
|
mean_field::field::Enthalpy::Form::EquilibriumEnthalpy>(
|
|
mean_field::quadrature::QuadratureRole::discretization,
|
|
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
|
mean_field::quadrature::MappingKind::general);
|
|
|
|
const auto gravityQuery = EnthalpyField::make_query<
|
|
mean_field::field::Enthalpy::Form::EquilibriumGravity>(
|
|
mean_field::quadrature::QuadratureRole::discretization,
|
|
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
|
mean_field::quadrature::MappingKind::general);
|
|
|
|
// A rigid-rotation potential is quadratic in physical position.
|
|
const auto rotationQuery = EnthalpyField::make_query<
|
|
mean_field::field::Enthalpy::Form::EquilibriumRotation>(
|
|
mean_field::quadrature::QuadratureRole::discretization,
|
|
transformation.OrderW(), std::array<int, 1>{2},
|
|
mean_field::utils::DOMAINS::STELLAR,
|
|
mean_field::quadrature::MappingKind::general);
|
|
|
|
const auto constantQuery = EnthalpyField::make_query<
|
|
mean_field::field::Enthalpy::Form::EquilibriumConstant>(
|
|
mean_field::quadrature::QuadratureRole::discretization,
|
|
transformation.OrderW(), {}, mean_field::utils::DOMAINS::STELLAR,
|
|
mean_field::quadrature::MappingKind::general);
|
|
|
|
const std::array<mean_field::quadrature::MfemRule, 4> candidateRules{
|
|
f.quadratureFactory->get(enthalpyQuery, transformation.GetGeometryType()),
|
|
f.quadratureFactory->get(gravityQuery, transformation.GetGeometryType()),
|
|
f.quadratureFactory->get(rotationQuery, transformation.GetGeometryType()),
|
|
f.quadratureFactory->get(constantQuery,
|
|
transformation.GetGeometryType())};
|
|
|
|
const auto selectedRule =
|
|
std::max_element(candidateRules.begin(), candidateRules.end(),
|
|
[](const auto &left, const auto &right) {
|
|
return left.resolution.order < right.resolution.order;
|
|
});
|
|
|
|
MFEM_VERIFY(selectedRule != candidateRules.end() &&
|
|
selectedRule->integration_rule != nullptr,
|
|
"The quadrature policy did not return a valid "
|
|
"hydrostatic-equilibrium integration rule.");
|
|
|
|
return *selectedRule->integration_rule;
|
|
}
|
|
} // namespace
|
|
|
|
namespace mean_field::operators {
|
|
HydrostaticJacobianBlockLayout::HydrostaticJacobianBlockLayout(
|
|
const fem::FEM &f) {
|
|
MFEM_VERIFY(f.enthalpyFes != nullptr,
|
|
"HydrostaticJacobianBlockLayout requires the "
|
|
"enthalpy finite-element space.");
|
|
|
|
MFEM_VERIFY(f.gravityPotentialFes != nullptr,
|
|
"HydrostaticJacobianBlockLayout requires the "
|
|
"gravity-potential finite-element space.");
|
|
|
|
MFEM_VERIFY(f.displacementFes != nullptr,
|
|
"HydrostaticJacobianBlockLayout requires the "
|
|
"displacement finite-element space.");
|
|
|
|
const field::FieldDofMap enthalpyMap =
|
|
field::make_field_dof_map<field::Enthalpy, DomainSchema>(*f.enthalpyFes);
|
|
|
|
const field::FieldDofMap gravityPotentialMap =
|
|
field::make_field_dof_map<field::Gravity, DomainSchema>(
|
|
*f.gravityPotentialFes);
|
|
|
|
const field::FieldDofMap displacementMap =
|
|
field::make_field_dof_map<field::Displacement, DomainSchema>(
|
|
*f.displacementFes);
|
|
|
|
m_enthalpySize = enthalpyMap.reduced_size();
|
|
m_gravityPotentialSize = gravityPotentialMap.reduced_size();
|
|
m_displacementSize = displacementMap.reduced_size();
|
|
m_residualSize = m_enthalpySize;
|
|
|
|
m_totalSize =
|
|
m_enthalpySize + m_gravityPotentialSize + 1 + m_displacementSize;
|
|
|
|
MFEM_VERIFY(m_enthalpySize > 0 && m_gravityPotentialSize > 0 &&
|
|
m_displacementSize > 0,
|
|
"HydrostaticJacobianBlockLayout received an empty "
|
|
"finite-element space.");
|
|
}
|
|
|
|
int HydrostaticJacobianBlockLayout::Offset(
|
|
const HydrostaticJacobianInputBlock block) const {
|
|
switch (block) {
|
|
case HydrostaticJacobianInputBlock::enthalpy:
|
|
return 0;
|
|
|
|
case HydrostaticJacobianInputBlock::gravityPotential:
|
|
return m_enthalpySize;
|
|
|
|
case HydrostaticJacobianInputBlock::bernoulliConstant:
|
|
return m_enthalpySize + m_gravityPotentialSize;
|
|
|
|
case HydrostaticJacobianInputBlock::displacement:
|
|
return m_enthalpySize + m_gravityPotentialSize + 1;
|
|
}
|
|
|
|
MFEM_ABORT("HydrostaticJacobianBlockLayout received an "
|
|
"unknown input block.");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int HydrostaticJacobianBlockLayout::Size(
|
|
const HydrostaticJacobianInputBlock block) const {
|
|
switch (block) {
|
|
case HydrostaticJacobianInputBlock::enthalpy:
|
|
return m_enthalpySize;
|
|
|
|
case HydrostaticJacobianInputBlock::gravityPotential:
|
|
return m_gravityPotentialSize;
|
|
|
|
case HydrostaticJacobianInputBlock::bernoulliConstant:
|
|
return 1;
|
|
|
|
case HydrostaticJacobianInputBlock::displacement:
|
|
return m_displacementSize;
|
|
}
|
|
|
|
MFEM_ABORT("HydrostaticJacobianBlockLayout received an "
|
|
"unknown input block.");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int HydrostaticJacobianBlockLayout::GetTotalSize() const noexcept {
|
|
return m_totalSize;
|
|
}
|
|
|
|
int HydrostaticJacobianBlockLayout::GetResidualSize() const noexcept {
|
|
return m_residualSize;
|
|
}
|
|
|
|
PreparedHydrostaticEquilibriumOperator::PreparedHydrostaticEquilibriumOperator(
|
|
const fem::FEM &f, const mapping::DomainMapper &domainMapper)
|
|
: m_fem(f), m_domainMapper(domainMapper), m_context(f, domainMapper) {
|
|
MFEM_VERIFY(m_fem.mesh != nullptr,
|
|
"PreparedHydrostaticEquilibriumOperator requires a mesh.");
|
|
|
|
MFEM_VERIFY(m_fem.enthalpyFes != nullptr,
|
|
"PreparedHydrostaticEquilibriumOperator requires "
|
|
"the enthalpy finite-element space.");
|
|
|
|
MFEM_VERIFY(m_fem.gravityPotentialFes != nullptr,
|
|
"PreparedHydrostaticEquilibriumOperator requires "
|
|
"the gravity-potential finite-element space.");
|
|
|
|
MFEM_VERIFY(m_fem.displacementFes != nullptr,
|
|
"PreparedHydrostaticEquilibriumOperator requires "
|
|
"the displacement finite-element space.");
|
|
|
|
MFEM_VERIFY(m_fem.compactificationFes != nullptr,
|
|
"PreparedHydrostaticEquilibriumOperator requires "
|
|
"the compactification finite-element space.");
|
|
|
|
MFEM_VERIFY(m_fem.compactificationCoordinate != nullptr,
|
|
"PreparedHydrostaticEquilibriumOperator requires "
|
|
"the compactification coordinate.");
|
|
|
|
MFEM_VERIFY(m_fem.quadratureFactory != nullptr,
|
|
"PreparedHydrostaticEquilibriumOperator requires "
|
|
"the quadrature-rule factory.");
|
|
|
|
MFEM_VERIFY(m_domainMapper.GetDimension() == m_fem.mesh->Dimension(),
|
|
"The hydrostatic operator's stateless mapper "
|
|
"dimension does not match the mesh dimension.");
|
|
|
|
m_enthalpyVariationTrue.SetSize(m_context.GetEnthalpyMap().full_size());
|
|
m_gravityPotentialVariationTrue.SetSize(
|
|
m_context.GetGravityPotentialMap().full_size());
|
|
m_displacementVariationTrue.SetSize(
|
|
m_context.GetDisplacementMap().full_size());
|
|
m_fullEnthalpyAction.SetSize(m_context.GetEnthalpyMap().full_size());
|
|
}
|
|
|
|
PreparedHydrostaticEquilibriumReport
|
|
PreparedHydrostaticEquilibriumOperator::Prepare(
|
|
const context::hydrostatic::HydrostaticEquilibriumStateView &state,
|
|
const context::hydrostatic::HydrostaticEquilibriumDependencies
|
|
&dependencies,
|
|
const physics::RigidRotation &rotation) {
|
|
const bool rotationObjectChanged =
|
|
!m_context.IsPrepared() ||
|
|
dependencies.rotation != m_context.GetDependencies().rotation;
|
|
|
|
PreparedHydrostaticEquilibriumReport report;
|
|
|
|
report.contextReport = m_context.Prepare(state, dependencies);
|
|
|
|
if (rotationObjectChanged) {
|
|
m_rotation = rotation;
|
|
report.updatedRotation = true;
|
|
}
|
|
|
|
MFEM_VERIFY(m_rotation.has_value(),
|
|
"The prepared hydrostatic operator has no frozen "
|
|
"rotation state.");
|
|
|
|
m_isPrepared = false;
|
|
|
|
if (report.contextReport.preparedStaticDependencies) {
|
|
PrepareStaticPlan();
|
|
}
|
|
|
|
if (report.contextReport.preparedGeometryState) {
|
|
PrepareGeometry();
|
|
PrepareAlgebraicJacobianBlocks();
|
|
report.preparedAlgebraicJacobianBlocks = true;
|
|
}
|
|
|
|
if (report.contextReport.preparedRotationDependencies) {
|
|
PrepareRotation();
|
|
}
|
|
|
|
if (report.contextReport.preparedBaseState) {
|
|
PrepareBaseState();
|
|
FinalizeDisplacementJacobianPreparation();
|
|
AssembleCachedResidual();
|
|
++m_residualPreparationCount;
|
|
report.preparedDisplacementJacobianData = true;
|
|
report.preparedResidual = true;
|
|
}
|
|
|
|
MFEM_VERIFY(!m_elements.empty(),
|
|
"PreparedHydrostaticEquilibriumOperator found no "
|
|
"stellar elements.");
|
|
|
|
MFEM_VERIFY(
|
|
m_cachedResidual.Size() == m_context.GetEnthalpyMap().reduced_size(),
|
|
"The prepared hydrostatic residual has the wrong supported size.");
|
|
|
|
m_isPrepared = true;
|
|
return report;
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::PrepareStaticPlan() {
|
|
m_elements.clear();
|
|
m_elements.reserve(m_fem.mesh->GetNE());
|
|
|
|
mfem::Vector enthalpyShape;
|
|
mfem::Vector gravityPotentialShape;
|
|
|
|
for (int elementId = 0; elementId < m_fem.mesh->GetNE(); ++elementId) {
|
|
mfem::ElementTransformation *transformation =
|
|
m_fem.mesh->GetElementTransformation(elementId);
|
|
|
|
MFEM_VERIFY(transformation != nullptr,
|
|
"Prepared hydrostatic static planning received "
|
|
"a null element transformation.");
|
|
|
|
if (is_vacuum_attribute(transformation->Attribute)) {
|
|
continue;
|
|
}
|
|
|
|
const mfem::FiniteElement &enthalpyElement =
|
|
*m_fem.enthalpyFes->GetFE(elementId);
|
|
|
|
const mfem::FiniteElement &gravityPotentialElement =
|
|
*m_fem.gravityPotentialFes->GetFE(elementId);
|
|
|
|
MFEM_VERIFY(enthalpyElement.GetGeomType() ==
|
|
gravityPotentialElement.GetGeomType() &&
|
|
enthalpyElement.GetGeomType() ==
|
|
transformation->GetGeometryType(),
|
|
"Hydrostatic element geometries do not agree.");
|
|
|
|
m_elements.emplace_back();
|
|
ElementPAData &data = m_elements.back();
|
|
data.elementId = elementId;
|
|
|
|
data.enthalpyDofTransformation =
|
|
m_fem.enthalpyFes->GetElementDofs(elementId, data.enthalpyDofs);
|
|
|
|
data.gravityPotentialDofTransformation =
|
|
m_fem.gravityPotentialFes->GetElementDofs(elementId,
|
|
data.gravityPotentialDofs);
|
|
|
|
data.displacementDofTransformation = m_fem.displacementFes->GetElementVDofs(
|
|
elementId, data.displacementDofs);
|
|
|
|
data.integrationRule = &get_hydrostatic_rule(
|
|
m_fem, enthalpyElement, gravityPotentialElement, *transformation);
|
|
|
|
const int quadraturePointCount = data.integrationRule->GetNPoints();
|
|
|
|
const int enthalpyDofCount = enthalpyElement.GetDof();
|
|
|
|
const int gravityPotentialDofCount = gravityPotentialElement.GetDof();
|
|
|
|
data.enthalpyBasis.SetSize(quadraturePointCount, enthalpyDofCount);
|
|
|
|
data.gravityPotentialBasis.SetSize(quadraturePointCount,
|
|
gravityPotentialDofCount);
|
|
|
|
enthalpyShape.SetSize(enthalpyDofCount);
|
|
gravityPotentialShape.SetSize(gravityPotentialDofCount);
|
|
|
|
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount;
|
|
++quadraturePoint) {
|
|
const mfem::IntegrationPoint &integrationPoint =
|
|
data.integrationRule->IntPoint(quadraturePoint);
|
|
|
|
enthalpyElement.CalcShape(integrationPoint, enthalpyShape);
|
|
|
|
gravityPotentialElement.CalcShape(integrationPoint,
|
|
gravityPotentialShape);
|
|
|
|
for (int dof = 0; dof < enthalpyDofCount; ++dof) {
|
|
data.enthalpyBasis(quadraturePoint, dof) = enthalpyShape(dof);
|
|
}
|
|
|
|
for (int dof = 0; dof < gravityPotentialDofCount; ++dof) {
|
|
data.gravityPotentialBasis(quadraturePoint, dof) =
|
|
gravityPotentialShape(dof);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::PrepareGeometry() {
|
|
mfem::Vector displacementLocal;
|
|
|
|
true_to_local(*m_fem.displacementFes, m_context.GetDisplacementTrue(),
|
|
displacementLocal);
|
|
|
|
mapping::DomainMapper::Workspace workspace(m_fem.mesh->Dimension());
|
|
|
|
mfem::Array<int> compactificationDofs;
|
|
|
|
mfem::Vector elementDisplacement;
|
|
mfem::Vector elementCompactification;
|
|
|
|
for (ElementPAData &data : m_elements) {
|
|
mfem::ElementTransformation *transformation =
|
|
m_fem.mesh->GetElementTransformation(data.elementId);
|
|
|
|
MFEM_VERIFY(transformation != nullptr && data.integrationRule != nullptr,
|
|
"Prepared hydrostatic geometry has invalid "
|
|
"static element data.");
|
|
|
|
mfem::DofTransformation *compactificationDofTransformation =
|
|
m_fem.compactificationFes->GetElementDofs(data.elementId,
|
|
compactificationDofs);
|
|
|
|
displacementLocal.GetSubVector(data.displacementDofs, elementDisplacement);
|
|
|
|
m_fem.compactificationCoordinate->GetSubVector(compactificationDofs,
|
|
elementCompactification);
|
|
|
|
if (data.displacementDofTransformation != nullptr) {
|
|
data.displacementDofTransformation->InvTransformPrimal(
|
|
elementDisplacement);
|
|
}
|
|
|
|
if (compactificationDofTransformation != nullptr) {
|
|
compactificationDofTransformation->InvTransformPrimal(
|
|
elementCompactification);
|
|
}
|
|
|
|
const mfem::FiniteElement &displacementElement =
|
|
*m_fem.displacementFes->GetFE(data.elementId);
|
|
|
|
const mfem::FiniteElement &compactificationElement =
|
|
*m_fem.compactificationFes->GetFE(data.elementId);
|
|
|
|
data.baseDisplacementData.emplace(
|
|
mapping::ElementDisplacementDataFromElementVDofs(displacementElement,
|
|
elementDisplacement));
|
|
|
|
data.compactificationData.emplace(compactificationElement,
|
|
elementCompactification);
|
|
|
|
const mapping::ElementMappingData mappingData{
|
|
.displacement = *data.baseDisplacementData,
|
|
.compactification = *data.compactificationData};
|
|
|
|
const int quadraturePointCount = data.integrationRule->GetNPoints();
|
|
|
|
data.physicalPositions.SetSize(quadraturePointCount,
|
|
m_fem.mesh->Dimension());
|
|
|
|
data.quadratureWeights.SetSize(quadraturePointCount);
|
|
|
|
data.baseMappingContexts.resize(quadraturePointCount);
|
|
|
|
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount;
|
|
++quadraturePoint) {
|
|
const mfem::IntegrationPoint &integrationPoint =
|
|
data.integrationRule->IntPoint(quadraturePoint);
|
|
|
|
transformation->SetIntPoint(&integrationPoint);
|
|
|
|
mapping::VolumeMappingContext &mappingContext =
|
|
data.baseMappingContexts[quadraturePoint];
|
|
|
|
const mapping::MappingStatus mappingStatus =
|
|
m_domainMapper.EvaluateVolume(mappingData, *transformation,
|
|
integrationPoint, workspace,
|
|
mappingContext);
|
|
|
|
MFEM_VERIFY(mappingStatus == mapping::MappingStatus::valid,
|
|
"Stateless mapping failed while preparing "
|
|
"hydrostatic geometry. Element: "
|
|
<< data.elementId
|
|
<< ", attribute: " << transformation->Attribute
|
|
<< ", quadrature point: " << quadraturePoint
|
|
<< ", status: " << static_cast<int>(mappingStatus));
|
|
|
|
const double quadratureWeight = mappingContext.quadrature.weight;
|
|
|
|
MFEM_VERIFY(std::isfinite(quadratureWeight) && quadratureWeight > 0.0,
|
|
"Prepared hydrostatic geometry encountered "
|
|
"an invalid quadrature weight.");
|
|
|
|
data.quadratureWeights(quadraturePoint) = quadratureWeight;
|
|
|
|
for (int component = 0; component < m_fem.mesh->Dimension();
|
|
++component) {
|
|
const double position =
|
|
mappingContext.mapping.physical_position(component);
|
|
|
|
MFEM_VERIFY(std::isfinite(position),
|
|
"Prepared hydrostatic geometry encountered "
|
|
"a non-finite physical position.");
|
|
|
|
data.physicalPositions(quadraturePoint, component) = position;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::PrepareAlgebraicJacobianBlocks() {
|
|
for (ElementPAData &data : m_elements) {
|
|
const int quadraturePointCount = data.quadratureWeights.Size();
|
|
|
|
const int enthalpyDofCount = data.enthalpyBasis.Width();
|
|
|
|
const int gravityPotentialDofCount = data.gravityPotentialBasis.Width();
|
|
|
|
MFEM_VERIFY(data.enthalpyBasis.Height() == quadraturePointCount &&
|
|
data.gravityPotentialBasis.Height() == quadraturePointCount,
|
|
"Prepared hydrostatic algebraic Jacobian has "
|
|
"inconsistent quadrature data.");
|
|
|
|
data.enthalpyJacobian.SetSize(enthalpyDofCount, enthalpyDofCount);
|
|
|
|
data.gravityPotentialJacobian.SetSize(enthalpyDofCount,
|
|
gravityPotentialDofCount);
|
|
|
|
data.bernoulliConstantJacobian.SetSize(enthalpyDofCount);
|
|
|
|
data.enthalpyJacobian = 0.0;
|
|
data.gravityPotentialJacobian = 0.0;
|
|
data.bernoulliConstantJacobian = 0.0;
|
|
|
|
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount;
|
|
++quadraturePoint) {
|
|
const double quadratureWeight = data.quadratureWeights(quadraturePoint);
|
|
|
|
for (int testDof = 0; testDof < enthalpyDofCount; ++testDof) {
|
|
const double weightedTestBasis =
|
|
quadratureWeight * data.enthalpyBasis(quadraturePoint, testDof);
|
|
|
|
data.bernoulliConstantJacobian(testDof) -= weightedTestBasis;
|
|
|
|
for (int trialDof = 0; trialDof < enthalpyDofCount; ++trialDof) {
|
|
data.enthalpyJacobian(testDof, trialDof) +=
|
|
weightedTestBasis * data.enthalpyBasis(quadraturePoint, trialDof);
|
|
}
|
|
|
|
for (int trialDof = 0; trialDof < gravityPotentialDofCount;
|
|
++trialDof) {
|
|
data.gravityPotentialJacobian(testDof, trialDof) +=
|
|
weightedTestBasis *
|
|
data.gravityPotentialBasis(quadraturePoint, trialDof);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
++m_algebraicJacobianStatistics.preparations;
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::PrepareRotation() {
|
|
MFEM_VERIFY(m_rotation.has_value(),
|
|
"Prepared hydrostatic rotation has no frozen state.");
|
|
|
|
mfem::Vector physicalPosition(m_fem.mesh->Dimension());
|
|
|
|
mfem::Vector coordinateDirection(m_fem.mesh->Dimension());
|
|
|
|
for (ElementPAData &data : m_elements) {
|
|
const int quadraturePointCount = data.physicalPositions.Height();
|
|
|
|
MFEM_VERIFY(data.physicalPositions.Width() == m_fem.mesh->Dimension(),
|
|
"Prepared hydrostatic rotation has invalid "
|
|
"geometry data.");
|
|
|
|
data.rotationPotential.SetSize(quadraturePointCount);
|
|
|
|
data.rotationGradient.SetSize(quadraturePointCount,
|
|
m_fem.mesh->Dimension());
|
|
|
|
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount;
|
|
++quadraturePoint) {
|
|
for (int component = 0; component < physicalPosition.Size();
|
|
++component) {
|
|
physicalPosition(component) =
|
|
data.physicalPositions(quadraturePoint, component);
|
|
}
|
|
|
|
const double rotationPotential = m_rotation->potential(physicalPosition);
|
|
|
|
MFEM_VERIFY(std::isfinite(rotationPotential),
|
|
"Prepared hydrostatic rotation encountered "
|
|
"a non-finite potential.");
|
|
|
|
data.rotationPotential(quadraturePoint) = rotationPotential;
|
|
|
|
for (int component = 0; component < physicalPosition.Size();
|
|
++component) {
|
|
coordinateDirection = 0.0;
|
|
coordinateDirection(component) = 1.0;
|
|
|
|
const double gradientComponent =
|
|
m_rotation->potential_directional_derivative(physicalPosition,
|
|
coordinateDirection);
|
|
|
|
MFEM_VERIFY(std::isfinite(gradientComponent),
|
|
"Prepared hydrostatic rotation encountered "
|
|
"a non-finite potential gradient.");
|
|
|
|
data.rotationGradient(quadraturePoint, component) = gradientComponent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::PrepareBaseState() {
|
|
mfem::Vector enthalpyLocal;
|
|
mfem::Vector gravityPotentialLocal;
|
|
|
|
true_to_local(*m_fem.enthalpyFes, m_context.GetBaseEnthalpyTrue(),
|
|
enthalpyLocal);
|
|
|
|
true_to_local(*m_fem.gravityPotentialFes,
|
|
m_context.GetBaseGravityPotentialTrue(), gravityPotentialLocal);
|
|
|
|
mfem::Vector elementEnthalpy;
|
|
mfem::Vector elementGravityPotential;
|
|
mfem::Vector quadratureEnthalpy;
|
|
mfem::Vector quadratureGravityPotential;
|
|
|
|
for (ElementPAData &data : m_elements) {
|
|
enthalpyLocal.GetSubVector(data.enthalpyDofs, elementEnthalpy);
|
|
|
|
gravityPotentialLocal.GetSubVector(data.gravityPotentialDofs,
|
|
elementGravityPotential);
|
|
|
|
if (data.enthalpyDofTransformation != nullptr) {
|
|
data.enthalpyDofTransformation->InvTransformPrimal(elementEnthalpy);
|
|
}
|
|
|
|
if (data.gravityPotentialDofTransformation != nullptr) {
|
|
data.gravityPotentialDofTransformation->InvTransformPrimal(
|
|
elementGravityPotential);
|
|
}
|
|
|
|
const int quadraturePointCount = data.quadratureWeights.Size();
|
|
|
|
quadratureEnthalpy.SetSize(quadraturePointCount);
|
|
|
|
quadratureGravityPotential.SetSize(quadraturePointCount);
|
|
|
|
data.enthalpyBasis.Mult(elementEnthalpy, quadratureEnthalpy);
|
|
|
|
data.gravityPotentialBasis.Mult(elementGravityPotential,
|
|
quadratureGravityPotential);
|
|
|
|
MFEM_VERIFY(data.rotationPotential.Size() == quadraturePointCount,
|
|
"Prepared hydrostatic base state has stale "
|
|
"rotation data.");
|
|
|
|
data.weightedResidual.SetSize(quadraturePointCount);
|
|
|
|
data.hydrostaticImbalance.SetSize(quadraturePointCount);
|
|
|
|
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount;
|
|
++quadraturePoint) {
|
|
const double imbalance = quadratureEnthalpy(quadraturePoint) +
|
|
quadratureGravityPotential(quadraturePoint) -
|
|
data.rotationPotential(quadraturePoint) -
|
|
m_context.GetBernoulliConstant();
|
|
|
|
const double weightedResidual =
|
|
data.quadratureWeights(quadraturePoint) * imbalance;
|
|
|
|
MFEM_VERIFY(std::isfinite(weightedResidual),
|
|
"Prepared hydrostatic base state encountered "
|
|
"a non-finite residual value.");
|
|
|
|
data.weightedResidual(quadraturePoint) = weightedResidual;
|
|
|
|
data.hydrostaticImbalance(quadraturePoint) = imbalance;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::
|
|
FinalizeDisplacementJacobianPreparation() {
|
|
const int dimension = m_fem.mesh->Dimension();
|
|
|
|
for (const ElementPAData &data : m_elements) {
|
|
const int quadraturePointCount = data.quadratureWeights.Size();
|
|
|
|
MFEM_VERIFY(data.baseDisplacementData.has_value() &&
|
|
data.compactificationData.has_value() &&
|
|
static_cast<int>(data.baseMappingContexts.size()) ==
|
|
quadraturePointCount &&
|
|
data.rotationGradient.Height() == quadraturePointCount &&
|
|
data.rotationGradient.Width() == dimension &&
|
|
data.hydrostaticImbalance.Size() == quadraturePointCount,
|
|
"Prepared hydrostatic displacement Jacobian "
|
|
"has inconsistent frozen data.");
|
|
}
|
|
|
|
++m_displacementJacobianStatistics.preparations;
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::AssembleCachedResidual() {
|
|
mfem::Vector localResidual(m_fem.enthalpyFes->GetVSize());
|
|
|
|
localResidual = 0.0;
|
|
mfem::Vector elementResidual;
|
|
|
|
for (const ElementPAData &data : m_elements) {
|
|
elementResidual.SetSize(data.enthalpyDofs.Size());
|
|
|
|
data.enthalpyBasis.MultTranspose(data.weightedResidual, elementResidual);
|
|
|
|
if (data.enthalpyDofTransformation != nullptr) {
|
|
data.enthalpyDofTransformation->TransformDual(elementResidual);
|
|
}
|
|
|
|
localResidual.AddElementVector(data.enthalpyDofs, elementResidual);
|
|
}
|
|
|
|
local_to_true(*m_fem.enthalpyFes, localResidual, m_fullEnthalpyAction);
|
|
|
|
m_cachedResidual.SetSize(m_context.GetEnthalpyMap().reduced_size());
|
|
m_context.GetEnthalpyMap().gather(m_fullEnthalpyAction, m_cachedResidual);
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::BuildResidual(
|
|
mfem::Vector &residual) const {
|
|
VerifyPrepared();
|
|
residual = m_cachedResidual;
|
|
++m_residualApplicationCount;
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::ApplyEnthalpyJacobianAction(
|
|
const mfem::Vector &enthalpyVariation, mfem::Vector &action) const {
|
|
VerifyPrepared();
|
|
|
|
MFEM_VERIFY(
|
|
enthalpyVariation.Size() == m_context.GetEnthalpyMap().reduced_size(),
|
|
"Prepared hydrostatic enthalpy variation has the wrong supported size.");
|
|
|
|
m_context.GetEnthalpyMap().scatter(enthalpyVariation,
|
|
m_enthalpyVariationTrue);
|
|
|
|
mfem::Vector enthalpyVariationLocal;
|
|
|
|
true_to_local(*m_fem.enthalpyFes, m_enthalpyVariationTrue,
|
|
enthalpyVariationLocal);
|
|
|
|
mfem::Vector localAction(m_fem.enthalpyFes->GetVSize());
|
|
|
|
localAction = 0.0;
|
|
|
|
mfem::Vector elementVariation;
|
|
mfem::Vector elementAction;
|
|
|
|
for (const ElementPAData &data : m_elements) {
|
|
enthalpyVariationLocal.GetSubVector(data.enthalpyDofs, elementVariation);
|
|
|
|
if (data.enthalpyDofTransformation != nullptr) {
|
|
data.enthalpyDofTransformation->InvTransformPrimal(elementVariation);
|
|
}
|
|
|
|
elementAction.SetSize(data.enthalpyJacobian.Height());
|
|
|
|
data.enthalpyJacobian.Mult(elementVariation, elementAction);
|
|
|
|
if (data.enthalpyDofTransformation != nullptr) {
|
|
data.enthalpyDofTransformation->TransformDual(elementAction);
|
|
}
|
|
|
|
localAction.AddElementVector(data.enthalpyDofs, elementAction);
|
|
}
|
|
|
|
local_to_true(*m_fem.enthalpyFes, localAction, m_fullEnthalpyAction);
|
|
|
|
action.SetSize(m_context.GetEnthalpyMap().reduced_size());
|
|
m_context.GetEnthalpyMap().gather(m_fullEnthalpyAction, action);
|
|
|
|
++m_algebraicJacobianStatistics.enthalpyApplications;
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::
|
|
ApplyGravityPotentialJacobianAction(
|
|
const mfem::Vector &gravityPotentialVariation,
|
|
mfem::Vector &action) const {
|
|
VerifyPrepared();
|
|
|
|
MFEM_VERIFY(gravityPotentialVariation.Size() ==
|
|
m_context.GetGravityPotentialMap().reduced_size(),
|
|
"Prepared hydrostatic gravity-potential variation has the wrong "
|
|
"supported size.");
|
|
|
|
m_context.GetGravityPotentialMap().scatter(gravityPotentialVariation,
|
|
m_gravityPotentialVariationTrue);
|
|
|
|
mfem::Vector gravityPotentialVariationLocal;
|
|
|
|
true_to_local(*m_fem.gravityPotentialFes, m_gravityPotentialVariationTrue,
|
|
gravityPotentialVariationLocal);
|
|
|
|
mfem::Vector localAction(m_fem.enthalpyFes->GetVSize());
|
|
|
|
localAction = 0.0;
|
|
|
|
mfem::Vector elementVariation;
|
|
mfem::Vector elementAction;
|
|
|
|
for (const ElementPAData &data : m_elements) {
|
|
gravityPotentialVariationLocal.GetSubVector(data.gravityPotentialDofs,
|
|
elementVariation);
|
|
|
|
if (data.gravityPotentialDofTransformation != nullptr) {
|
|
data.gravityPotentialDofTransformation->InvTransformPrimal(
|
|
elementVariation);
|
|
}
|
|
|
|
elementAction.SetSize(data.gravityPotentialJacobian.Height());
|
|
|
|
data.gravityPotentialJacobian.Mult(elementVariation, elementAction);
|
|
|
|
if (data.enthalpyDofTransformation != nullptr) {
|
|
data.enthalpyDofTransformation->TransformDual(elementAction);
|
|
}
|
|
|
|
localAction.AddElementVector(data.enthalpyDofs, elementAction);
|
|
}
|
|
|
|
local_to_true(*m_fem.enthalpyFes, localAction, m_fullEnthalpyAction);
|
|
|
|
action.SetSize(m_context.GetEnthalpyMap().reduced_size());
|
|
m_context.GetEnthalpyMap().gather(m_fullEnthalpyAction, action);
|
|
|
|
++m_algebraicJacobianStatistics.gravityPotentialApplications;
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::
|
|
ApplyBernoulliConstantJacobianAction(
|
|
const double bernoulliConstantVariation, mfem::Vector &action) const {
|
|
VerifyPrepared();
|
|
|
|
MFEM_VERIFY(std::isfinite(bernoulliConstantVariation),
|
|
"Prepared hydrostatic Bernoulli-constant Jacobian "
|
|
"received a non-finite variation.");
|
|
|
|
mfem::Vector localAction(m_fem.enthalpyFes->GetVSize());
|
|
|
|
localAction = 0.0;
|
|
mfem::Vector elementAction;
|
|
|
|
for (const ElementPAData &data : m_elements) {
|
|
elementAction = data.bernoulliConstantJacobian;
|
|
elementAction *= bernoulliConstantVariation;
|
|
|
|
if (data.enthalpyDofTransformation != nullptr) {
|
|
data.enthalpyDofTransformation->TransformDual(elementAction);
|
|
}
|
|
|
|
localAction.AddElementVector(data.enthalpyDofs, elementAction);
|
|
}
|
|
|
|
local_to_true(*m_fem.enthalpyFes, localAction, m_fullEnthalpyAction);
|
|
|
|
action.SetSize(m_context.GetEnthalpyMap().reduced_size());
|
|
m_context.GetEnthalpyMap().gather(m_fullEnthalpyAction, action);
|
|
|
|
++m_algebraicJacobianStatistics.bernoulliConstantApplications;
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::ApplyAlgebraicJacobianAction(
|
|
const mfem::Vector &enthalpyVariation,
|
|
const mfem::Vector &gravityPotentialVariation,
|
|
const double bernoulliConstantVariation, mfem::Vector &action) const {
|
|
VerifyPrepared();
|
|
|
|
MFEM_VERIFY(std::isfinite(bernoulliConstantVariation),
|
|
"Prepared hydrostatic algebraic Jacobian received "
|
|
"a non-finite Bernoulli-constant variation.");
|
|
|
|
MFEM_VERIFY(enthalpyVariation.Size() ==
|
|
m_context.GetEnthalpyMap().reduced_size(),
|
|
"Prepared hydrostatic algebraic enthalpy variation has the wrong "
|
|
"supported size.");
|
|
|
|
MFEM_VERIFY(gravityPotentialVariation.Size() ==
|
|
m_context.GetGravityPotentialMap().reduced_size(),
|
|
"Prepared hydrostatic algebraic gravity-potential variation has "
|
|
"the wrong supported size.");
|
|
|
|
m_context.GetEnthalpyMap().scatter(enthalpyVariation,
|
|
m_enthalpyVariationTrue);
|
|
m_context.GetGravityPotentialMap().scatter(gravityPotentialVariation,
|
|
m_gravityPotentialVariationTrue);
|
|
|
|
mfem::Vector enthalpyVariationLocal;
|
|
mfem::Vector gravityPotentialVariationLocal;
|
|
|
|
true_to_local(*m_fem.enthalpyFes, m_enthalpyVariationTrue,
|
|
enthalpyVariationLocal);
|
|
|
|
true_to_local(*m_fem.gravityPotentialFes, m_gravityPotentialVariationTrue,
|
|
gravityPotentialVariationLocal);
|
|
|
|
mfem::Vector localAction(m_fem.enthalpyFes->GetVSize());
|
|
|
|
localAction = 0.0;
|
|
|
|
mfem::Vector elementEnthalpyVariation;
|
|
mfem::Vector elementGravityPotentialVariation;
|
|
mfem::Vector elementAction;
|
|
mfem::Vector elementWorkspace;
|
|
|
|
for (const ElementPAData &data : m_elements) {
|
|
enthalpyVariationLocal.GetSubVector(data.enthalpyDofs,
|
|
elementEnthalpyVariation);
|
|
|
|
gravityPotentialVariationLocal.GetSubVector(
|
|
data.gravityPotentialDofs, elementGravityPotentialVariation);
|
|
|
|
if (data.enthalpyDofTransformation != nullptr) {
|
|
data.enthalpyDofTransformation->InvTransformPrimal(
|
|
elementEnthalpyVariation);
|
|
}
|
|
|
|
if (data.gravityPotentialDofTransformation != nullptr) {
|
|
data.gravityPotentialDofTransformation->InvTransformPrimal(
|
|
elementGravityPotentialVariation);
|
|
}
|
|
|
|
MFEM_VERIFY(data.enthalpyJacobian.Height() ==
|
|
data.gravityPotentialJacobian.Height() &&
|
|
data.enthalpyJacobian.Width() ==
|
|
elementEnthalpyVariation.Size() &&
|
|
data.gravityPotentialJacobian.Width() ==
|
|
elementGravityPotentialVariation.Size() &&
|
|
data.bernoulliConstantJacobian.Size() ==
|
|
data.enthalpyJacobian.Height(),
|
|
"Prepared hydrostatic algebraic Jacobian has "
|
|
"incompatible element dimensions.");
|
|
|
|
elementAction.SetSize(data.enthalpyJacobian.Height());
|
|
|
|
elementWorkspace.SetSize(data.gravityPotentialJacobian.Height());
|
|
|
|
data.enthalpyJacobian.Mult(elementEnthalpyVariation, elementAction);
|
|
|
|
data.gravityPotentialJacobian.Mult(elementGravityPotentialVariation,
|
|
elementWorkspace);
|
|
|
|
elementAction.Add(1.0, elementWorkspace);
|
|
elementAction.Add(bernoulliConstantVariation,
|
|
data.bernoulliConstantJacobian);
|
|
|
|
if (data.enthalpyDofTransformation != nullptr) {
|
|
data.enthalpyDofTransformation->TransformDual(elementAction);
|
|
}
|
|
|
|
localAction.AddElementVector(data.enthalpyDofs, elementAction);
|
|
}
|
|
|
|
local_to_true(*m_fem.enthalpyFes, localAction, m_fullEnthalpyAction);
|
|
|
|
action.SetSize(m_context.GetEnthalpyMap().reduced_size());
|
|
m_context.GetEnthalpyMap().gather(m_fullEnthalpyAction, action);
|
|
|
|
++m_algebraicJacobianStatistics.combinedApplications;
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::ApplyDisplacementJacobianAction(
|
|
const mfem::Vector &displacementVariation, mfem::Vector &action) const {
|
|
VerifyPrepared();
|
|
|
|
MFEM_VERIFY(displacementVariation.Size() ==
|
|
m_context.GetDisplacementMap().reduced_size(),
|
|
"Prepared hydrostatic displacement variation has the wrong "
|
|
"supported size.");
|
|
|
|
m_context.GetDisplacementMap().scatter(displacementVariation,
|
|
m_displacementVariationTrue);
|
|
|
|
mfem::Vector displacementVariationLocal;
|
|
|
|
true_to_local(*m_fem.displacementFes, m_displacementVariationTrue,
|
|
displacementVariationLocal);
|
|
|
|
mfem::Vector localAction(m_fem.enthalpyFes->GetVSize());
|
|
|
|
localAction = 0.0;
|
|
|
|
mapping::DomainMapper::Workspace workspace(m_fem.mesh->Dimension());
|
|
|
|
mfem::Vector elementDisplacementVariation;
|
|
mfem::Vector weightedQuadratureVariation;
|
|
mfem::Vector elementAction;
|
|
|
|
for (const ElementPAData &data : m_elements) {
|
|
MFEM_VERIFY(data.baseDisplacementData.has_value() &&
|
|
data.compactificationData.has_value() &&
|
|
data.integrationRule != nullptr,
|
|
"Prepared hydrostatic displacement Jacobian "
|
|
"has invalid frozen element data.");
|
|
|
|
mfem::ElementTransformation *transformation =
|
|
m_fem.mesh->GetElementTransformation(data.elementId);
|
|
|
|
MFEM_VERIFY(transformation != nullptr,
|
|
"Prepared hydrostatic displacement Jacobian "
|
|
"received a null element transformation.");
|
|
|
|
displacementVariationLocal.GetSubVector(data.displacementDofs,
|
|
elementDisplacementVariation);
|
|
|
|
if (data.displacementDofTransformation != nullptr) {
|
|
data.displacementDofTransformation->InvTransformPrimal(
|
|
elementDisplacementVariation);
|
|
}
|
|
|
|
const mfem::FiniteElement &displacementElement =
|
|
*m_fem.displacementFes->GetFE(data.elementId);
|
|
|
|
const mapping::ElementDisplacementData directionData =
|
|
mapping::ElementDisplacementDataFromElementVDofs(
|
|
displacementElement, elementDisplacementVariation);
|
|
|
|
const mapping::ElementMappingData mappingData{
|
|
.displacement = *data.baseDisplacementData,
|
|
.compactification = *data.compactificationData};
|
|
|
|
const int quadraturePointCount = data.integrationRule->GetNPoints();
|
|
|
|
MFEM_VERIFY(static_cast<int>(data.baseMappingContexts.size()) ==
|
|
quadraturePointCount &&
|
|
data.quadratureWeights.Size() == quadraturePointCount &&
|
|
data.hydrostaticImbalance.Size() == quadraturePointCount &&
|
|
data.rotationGradient.Height() == quadraturePointCount &&
|
|
data.rotationGradient.Width() == m_fem.mesh->Dimension(),
|
|
"Prepared hydrostatic displacement Jacobian "
|
|
"has inconsistent quadrature data.");
|
|
|
|
weightedQuadratureVariation.SetSize(quadraturePointCount);
|
|
|
|
for (int quadraturePoint = 0; quadraturePoint < quadraturePointCount;
|
|
++quadraturePoint) {
|
|
const mfem::IntegrationPoint &integrationPoint =
|
|
data.integrationRule->IntPoint(quadraturePoint);
|
|
|
|
transformation->SetIntPoint(&integrationPoint);
|
|
|
|
mapping::VolumeMappingVariation variation;
|
|
|
|
const mapping::MappingStatus mappingStatus =
|
|
m_domainMapper.EvaluateVolumeVariation(
|
|
mappingData, directionData, *transformation, integrationPoint,
|
|
data.baseMappingContexts[quadraturePoint], workspace, variation);
|
|
|
|
MFEM_VERIFY(mappingStatus == mapping::MappingStatus::valid,
|
|
"Stateless mapping variation failed while "
|
|
"applying the prepared hydrostatic "
|
|
"displacement Jacobian. Element: "
|
|
<< data.elementId
|
|
<< ", attribute: " << transformation->Attribute
|
|
<< ", quadrature point: " << quadraturePoint
|
|
<< ", status: " << static_cast<int>(mappingStatus));
|
|
|
|
double rotationPotentialVariation = 0.0;
|
|
|
|
for (int component = 0; component < m_fem.mesh->Dimension();
|
|
++component) {
|
|
rotationPotentialVariation +=
|
|
data.rotationGradient(quadraturePoint, component) *
|
|
variation.mapping.physical_position_variation(component);
|
|
}
|
|
|
|
const double weightedVariation =
|
|
data.hydrostaticImbalance(quadraturePoint) *
|
|
variation.weight_variation -
|
|
data.quadratureWeights(quadraturePoint) * rotationPotentialVariation;
|
|
|
|
MFEM_VERIFY(std::isfinite(weightedVariation),
|
|
"Prepared hydrostatic displacement Jacobian "
|
|
"encountered a non-finite quadrature action.");
|
|
|
|
weightedQuadratureVariation(quadraturePoint) = weightedVariation;
|
|
}
|
|
|
|
elementAction.SetSize(data.enthalpyDofs.Size());
|
|
|
|
data.enthalpyBasis.MultTranspose(weightedQuadratureVariation,
|
|
elementAction);
|
|
|
|
if (data.enthalpyDofTransformation != nullptr) {
|
|
data.enthalpyDofTransformation->TransformDual(elementAction);
|
|
}
|
|
|
|
localAction.AddElementVector(data.enthalpyDofs, elementAction);
|
|
}
|
|
|
|
local_to_true(*m_fem.enthalpyFes, localAction, m_fullEnthalpyAction);
|
|
|
|
action.SetSize(m_context.GetEnthalpyMap().reduced_size());
|
|
m_context.GetEnthalpyMap().gather(m_fullEnthalpyAction, action);
|
|
|
|
++m_displacementJacobianStatistics.applications;
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::ApplyCompleteJacobianAction(
|
|
const mfem::Vector &enthalpyVariation,
|
|
const mfem::Vector &gravityPotentialVariation,
|
|
const double bernoulliConstantVariation,
|
|
const mfem::Vector &displacementVariation, mfem::Vector &action) const {
|
|
VerifyPrepared();
|
|
|
|
mfem::Vector displacementAction;
|
|
|
|
ApplyAlgebraicJacobianAction(enthalpyVariation, gravityPotentialVariation,
|
|
bernoulliConstantVariation, action);
|
|
|
|
ApplyDisplacementJacobianAction(displacementVariation, displacementAction);
|
|
|
|
MFEM_VERIFY(action.Size() == displacementAction.Size(),
|
|
"Prepared hydrostatic complete Jacobian produced "
|
|
"incompatible algebraic and displacement actions.");
|
|
|
|
action += displacementAction;
|
|
++m_completeJacobianStatistics.applications;
|
|
}
|
|
|
|
bool PreparedHydrostaticEquilibriumOperator::IsPrepared() const noexcept {
|
|
return m_isPrepared;
|
|
}
|
|
|
|
const context::hydrostatic::HydrostaticPreparationStatistics &
|
|
PreparedHydrostaticEquilibriumOperator::GetContextPreparationStatistics()
|
|
const noexcept {
|
|
return m_context.GetPreparationStatistics();
|
|
}
|
|
|
|
std::uint64_t
|
|
PreparedHydrostaticEquilibriumOperator::GetResidualPreparationCount()
|
|
const noexcept {
|
|
return m_residualPreparationCount;
|
|
}
|
|
|
|
std::uint64_t
|
|
PreparedHydrostaticEquilibriumOperator::GetResidualApplicationCount()
|
|
const noexcept {
|
|
return m_residualApplicationCount;
|
|
}
|
|
|
|
const PreparedHydrostaticAlgebraicJacobianStatistics &
|
|
PreparedHydrostaticEquilibriumOperator::GetAlgebraicJacobianStatistics()
|
|
const noexcept {
|
|
return m_algebraicJacobianStatistics;
|
|
}
|
|
|
|
const PreparedHydrostaticDisplacementJacobianStatistics &
|
|
PreparedHydrostaticEquilibriumOperator::GetDisplacementJacobianStatistics()
|
|
const noexcept {
|
|
return m_displacementJacobianStatistics;
|
|
}
|
|
|
|
const PreparedHydrostaticCompleteJacobianStatistics &
|
|
PreparedHydrostaticEquilibriumOperator::GetCompleteJacobianStatistics()
|
|
const noexcept {
|
|
return m_completeJacobianStatistics;
|
|
}
|
|
|
|
std::size_t PreparedHydrostaticEquilibriumOperator::GetStellarElementCount()
|
|
const noexcept {
|
|
return m_elements.size();
|
|
}
|
|
|
|
const fem::FEM &
|
|
PreparedHydrostaticEquilibriumOperator::GetFEM() const noexcept {
|
|
return m_fem;
|
|
}
|
|
|
|
const field::FieldDofMap &
|
|
PreparedHydrostaticEquilibriumOperator::GetEnthalpyMap() const noexcept {
|
|
return m_context.GetEnthalpyMap();
|
|
}
|
|
|
|
const field::FieldDofMap &
|
|
PreparedHydrostaticEquilibriumOperator::GetGravityPotentialMap()
|
|
const noexcept {
|
|
return m_context.GetGravityPotentialMap();
|
|
}
|
|
|
|
const field::FieldDofMap &
|
|
PreparedHydrostaticEquilibriumOperator::GetDisplacementMap() const noexcept {
|
|
return m_context.GetDisplacementMap();
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::VerifyPrepared() const {
|
|
MFEM_VERIFY(m_isPrepared,
|
|
"PreparedHydrostaticEquilibriumOperator must be "
|
|
"prepared before residual or Jacobian application.");
|
|
}
|
|
|
|
PreparedHydrostaticEquilibriumJacobianOperator::
|
|
PreparedHydrostaticEquilibriumJacobianOperator(
|
|
const fem::FEM &f,
|
|
const PreparedHydrostaticEquilibriumOperator &preparedOperator)
|
|
: mfem::Operator(HydrostaticJacobianBlockLayout(f).GetResidualSize(),
|
|
HydrostaticJacobianBlockLayout(f).GetTotalSize()),
|
|
m_layout(f), m_preparedOperator(preparedOperator) {
|
|
MFEM_VERIFY(&m_preparedOperator.GetFEM() == &f,
|
|
"Prepared hydrostatic MFEM adapter and prepared "
|
|
"operator must use the same FEM object.");
|
|
|
|
MFEM_VERIFY(Height() == m_layout.GetResidualSize() &&
|
|
Width() == m_layout.GetTotalSize(),
|
|
"Prepared hydrostatic MFEM adapter has "
|
|
"inconsistent operator dimensions.");
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumJacobianOperator::Mult(
|
|
const mfem::Vector &direction, mfem::Vector &action) const {
|
|
MFEM_VERIFY(direction.Size() == Width(),
|
|
"Prepared hydrostatic MFEM adapter received a "
|
|
"direction with the wrong size.");
|
|
|
|
mfem::Vector enthalpyVariation;
|
|
mfem::Vector gravityPotentialVariation;
|
|
mfem::Vector displacementVariation;
|
|
|
|
copy_vector_block(direction,
|
|
m_layout.Offset(HydrostaticJacobianInputBlock::enthalpy),
|
|
m_layout.Size(HydrostaticJacobianInputBlock::enthalpy),
|
|
enthalpyVariation);
|
|
|
|
copy_vector_block(
|
|
direction,
|
|
m_layout.Offset(HydrostaticJacobianInputBlock::gravityPotential),
|
|
m_layout.Size(HydrostaticJacobianInputBlock::gravityPotential),
|
|
gravityPotentialVariation);
|
|
|
|
copy_vector_block(
|
|
direction, m_layout.Offset(HydrostaticJacobianInputBlock::displacement),
|
|
m_layout.Size(HydrostaticJacobianInputBlock::displacement),
|
|
displacementVariation);
|
|
|
|
const double bernoulliConstantVariation = direction(
|
|
m_layout.Offset(HydrostaticJacobianInputBlock::bernoulliConstant));
|
|
|
|
m_preparedOperator.ApplyCompleteJacobianAction(
|
|
enthalpyVariation, gravityPotentialVariation, bernoulliConstantVariation,
|
|
displacementVariation, action);
|
|
|
|
MFEM_VERIFY(action.Size() == Height(),
|
|
"Prepared hydrostatic MFEM adapter produced an "
|
|
"action with the wrong size.");
|
|
}
|
|
|
|
const HydrostaticJacobianBlockLayout &
|
|
PreparedHydrostaticEquilibriumJacobianOperator::GetLayout() const noexcept {
|
|
return m_layout;
|
|
}
|
|
} // namespace mean_field::operators
|