1433 lines
49 KiB
C++
1433 lines
49 KiB
C++
module;
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
|
|
#include <mfem.hpp>
|
|
|
|
module mean_field;
|
|
|
|
import :operators.prepared_hydrostatic_equilibrium;
|
|
|
|
namespace {
|
|
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."
|
|
);
|
|
|
|
m_enthalpySize = f.enthalpyFes->GetTrueVSize();
|
|
m_gravityPotentialSize = f.gravityPotentialFes->GetTrueVSize();
|
|
m_displacementSize = f.displacementFes->GetTrueVSize();
|
|
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::DomainMapperStateless &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."
|
|
);
|
|
}
|
|
|
|
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_fem.enthalpyFes->GetTrueVSize(),
|
|
"The prepared hydrostatic residual has the wrong size."
|
|
);
|
|
|
|
m_isPrepared = true;
|
|
return report;
|
|
}
|
|
|
|
void PreparedHydrostaticEquilibriumOperator::PrepareStaticPlan() {
|
|
m_elements.clear();
|
|
m_elements.reserve(m_fem.mesh->GetNE());
|
|
|
|
const int vacuumAttribute = m_domainMapper.GetVacuumElementAttribute();
|
|
|
|
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 (transformation->Attribute == vacuumAttribute) {
|
|
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::DomainMapperStateless::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_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::Vector enthalpyVariationLocal;
|
|
|
|
true_to_local(
|
|
*m_fem.enthalpyFes, enthalpyVariation, 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, action);
|
|
|
|
++m_algebraicJacobianStatistics.enthalpyApplications;
|
|
}
|
|
|
|
void
|
|
PreparedHydrostaticEquilibriumOperator::ApplyGravityPotentialJacobianAction(
|
|
const mfem::Vector &gravityPotentialVariation,
|
|
mfem::Vector &action
|
|
) const {
|
|
VerifyPrepared();
|
|
|
|
mfem::Vector gravityPotentialVariationLocal;
|
|
|
|
true_to_local(
|
|
*m_fem.gravityPotentialFes, gravityPotentialVariation,
|
|
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, 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, 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::Vector enthalpyVariationLocal;
|
|
mfem::Vector gravityPotentialVariationLocal;
|
|
|
|
true_to_local(
|
|
*m_fem.enthalpyFes, enthalpyVariation, enthalpyVariationLocal
|
|
);
|
|
|
|
true_to_local(
|
|
*m_fem.gravityPotentialFes, gravityPotentialVariation,
|
|
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, action);
|
|
|
|
++m_algebraicJacobianStatistics.combinedApplications;
|
|
}
|
|
|
|
void
|
|
PreparedHydrostaticEquilibriumOperator::ApplyDisplacementJacobianAction(
|
|
const mfem::Vector &displacementVariation,
|
|
mfem::Vector &action
|
|
) const {
|
|
VerifyPrepared();
|
|
|
|
mfem::Vector displacementVariationLocal;
|
|
|
|
true_to_local(
|
|
*m_fem.displacementFes, displacementVariation,
|
|
displacementVariationLocal
|
|
);
|
|
|
|
mfem::Vector localAction(m_fem.enthalpyFes->GetVSize());
|
|
|
|
localAction = 0.0;
|
|
|
|
mapping::DomainMapperStateless::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, 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;
|
|
}
|
|
|
|
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(
|
|
f.enthalpyFes != nullptr ? f.enthalpyFes->GetTrueVSize() : 0,
|
|
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
|