This commit uses global pre allocated work space to dramatically reduce memory usage and allocation time
333 lines
12 KiB
C++
333 lines
12 KiB
C++
module;
|
|
|
|
#include <compare>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <expected>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
#include <mfem.hpp>
|
|
|
|
export module mean_field:operators.prepared_hydrostatic_equilibrium;
|
|
|
|
export import :fem;
|
|
export import :fem.reference_tables;
|
|
export import :mapping.domain_mapper;
|
|
export import :mapping.prepared_cache;
|
|
export import :operators.context.hydrostatic_equilibrium;
|
|
export import :physics.rigid_rotation;
|
|
|
|
export namespace mean_field::operators {
|
|
struct PreparedHydrostaticEquilibriumReport {
|
|
context::hydrostatic::HydrostaticPreparationReport contextReport;
|
|
|
|
bool updatedRotation{false};
|
|
bool preparedAlgebraicJacobianBlocks{false};
|
|
bool preparedDisplacementJacobianData{false};
|
|
bool preparedResidual{false};
|
|
|
|
[[nodiscard]] bool DidAnyWork() const noexcept {
|
|
return contextReport.DidAnyWork() || updatedRotation || preparedAlgebraicJacobianBlocks ||
|
|
preparedDisplacementJacobianData || preparedResidual;
|
|
}
|
|
};
|
|
|
|
enum class HydrostaticEquilibriumPreparationRejectionReason : std::uint8_t {
|
|
inverted_geometry,
|
|
non_finite_geometry,
|
|
non_finite_residual
|
|
};
|
|
|
|
struct HydrostaticEquilibriumPreparationRejection final {
|
|
HydrostaticEquilibriumPreparationRejectionReason reason{
|
|
HydrostaticEquilibriumPreparationRejectionReason::inverted_geometry
|
|
};
|
|
mapping::MappingStatus mappingStatus{mapping::MappingStatus::valid};
|
|
};
|
|
|
|
using HydrostaticEquilibriumPreparationResult =
|
|
std::expected<PreparedHydrostaticEquilibriumReport, HydrostaticEquilibriumPreparationRejection>;
|
|
|
|
[[noreturn]] inline void
|
|
throwHydrostaticEquilibriumPreparationRejection(const HydrostaticEquilibriumPreparationRejection &rejection) {
|
|
switch (rejection.reason) {
|
|
case HydrostaticEquilibriumPreparationRejectionReason::non_finite_geometry:
|
|
throw std::domain_error("Prepared hydrostatic equilibrium encountered non-finite mapped geometry.");
|
|
case HydrostaticEquilibriumPreparationRejectionReason::non_finite_residual:
|
|
throw std::domain_error("Prepared hydrostatic equilibrium produced a non-finite residual.");
|
|
case HydrostaticEquilibriumPreparationRejectionReason::inverted_geometry:
|
|
default:
|
|
throw std::domain_error("Prepared hydrostatic equilibrium encountered inverted mapped geometry.");
|
|
}
|
|
}
|
|
|
|
struct PreparedHydrostaticAlgebraicJacobianStatistics {
|
|
std::uint64_t preparations{0};
|
|
std::uint64_t enthalpyApplications{0};
|
|
std::uint64_t gravityPotentialApplications{0};
|
|
std::uint64_t bernoulliConstantApplications{0};
|
|
std::uint64_t rotationAmplitudeApplications{0};
|
|
std::uint64_t combinedApplications{0};
|
|
|
|
constexpr auto operator<=>(const PreparedHydrostaticAlgebraicJacobianStatistics &) const = default;
|
|
};
|
|
|
|
struct PreparedHydrostaticDisplacementJacobianStatistics {
|
|
std::uint64_t preparations{0};
|
|
std::uint64_t applications{0};
|
|
|
|
constexpr auto operator<=>(const PreparedHydrostaticDisplacementJacobianStatistics &) const = default;
|
|
};
|
|
|
|
struct PreparedHydrostaticCompleteJacobianStatistics {
|
|
std::uint64_t applications{0};
|
|
|
|
constexpr auto operator<=>(const PreparedHydrostaticCompleteJacobianStatistics &) const = default;
|
|
};
|
|
|
|
enum class HydrostaticJacobianInputBlock : int {
|
|
enthalpy = 0,
|
|
gravityPotential = 1,
|
|
bernoulliConstant = 2,
|
|
displacement = 3
|
|
};
|
|
|
|
class HydrostaticJacobianBlockLayout final {
|
|
public:
|
|
explicit HydrostaticJacobianBlockLayout(const fem::FEM &f);
|
|
|
|
[[nodiscard]] int Offset(HydrostaticJacobianInputBlock block) const;
|
|
|
|
[[nodiscard]] int Size(HydrostaticJacobianInputBlock block) const;
|
|
|
|
[[nodiscard]] int GetTotalSize() const noexcept;
|
|
|
|
[[nodiscard]] int GetResidualSize() const noexcept;
|
|
|
|
private:
|
|
int m_enthalpySize{0};
|
|
int m_gravityPotentialSize{0};
|
|
int m_displacementSize{0};
|
|
int m_totalSize{0};
|
|
int m_residualSize{0};
|
|
};
|
|
|
|
class PreparedHydrostaticEquilibriumOperator final {
|
|
public:
|
|
PreparedHydrostaticEquilibriumOperator(
|
|
const fem::FEM &f,
|
|
const mapping::DomainMapper &domainMapper
|
|
);
|
|
|
|
PreparedHydrostaticEquilibriumOperator(const PreparedHydrostaticEquilibriumOperator &) = delete;
|
|
|
|
PreparedHydrostaticEquilibriumOperator &operator=(const PreparedHydrostaticEquilibriumOperator &) = delete;
|
|
|
|
PreparedHydrostaticEquilibriumOperator(PreparedHydrostaticEquilibriumOperator &&) = delete;
|
|
|
|
PreparedHydrostaticEquilibriumOperator &operator=(PreparedHydrostaticEquilibriumOperator &&) = delete;
|
|
|
|
PreparedHydrostaticEquilibriumReport Prepare(
|
|
const context::hydrostatic::HydrostaticEquilibriumStateView &state,
|
|
const context::hydrostatic::HydrostaticEquilibriumDependencies &dependencies,
|
|
const physics::RigidRotation &rotation
|
|
);
|
|
|
|
[[nodiscard]] HydrostaticEquilibriumPreparationResult TryPrepare(
|
|
const context::hydrostatic::HydrostaticEquilibriumStateView &state,
|
|
const context::hydrostatic::HydrostaticEquilibriumDependencies &dependencies,
|
|
const physics::RigidRotation &rotation
|
|
);
|
|
|
|
void BuildResidual(mfem::Vector &residual) const;
|
|
|
|
void ApplyEnthalpyJacobianAction(
|
|
const mfem::Vector &enthalpyVariation,
|
|
mfem::Vector &action
|
|
) const;
|
|
|
|
void ApplyGravityPotentialJacobianAction(
|
|
const mfem::Vector &gravityPotentialVariation,
|
|
mfem::Vector &action
|
|
) const;
|
|
|
|
void ApplyBernoulliConstantJacobianAction(
|
|
double bernoulliConstantVariation,
|
|
mfem::Vector &action
|
|
) const;
|
|
|
|
// Differentiates a multiplicative change Omega -> (1 + alpha) Omega
|
|
// at the frozen rigid rotation. Since Psi_rotation is quadratic in
|
|
// Omega, this contributes -2 alpha Psi_rotation to the hydrostatic row.
|
|
void ApplyRotationAmplitudeJacobianAction(
|
|
double fractionalAngularVelocityVariation,
|
|
mfem::Vector &action
|
|
) const;
|
|
|
|
void ApplyAlgebraicJacobianAction(
|
|
const mfem::Vector &enthalpyVariation,
|
|
const mfem::Vector &gravityPotentialVariation,
|
|
double bernoulliConstantVariation,
|
|
mfem::Vector &action
|
|
) const;
|
|
|
|
void ApplyDisplacementJacobianAction(
|
|
const mfem::Vector &displacementVariation,
|
|
mfem::Vector &action
|
|
) const;
|
|
|
|
void ApplyCompleteJacobianAction(
|
|
const mfem::Vector &enthalpyVariation,
|
|
const mfem::Vector &gravityPotentialVariation,
|
|
double bernoulliConstantVariation,
|
|
const mfem::Vector &displacementVariation,
|
|
mfem::Vector &action
|
|
) const;
|
|
|
|
// Exact diagonal of the volume enthalpy-to-hydrostatic block. Surface
|
|
// boundary-row replacement is deliberately applied by its owner.
|
|
void AssembleEnthalpyJacobianDiagonal(mfem::Vector &diagonal) const;
|
|
|
|
[[nodiscard]] bool IsPrepared() const noexcept;
|
|
|
|
[[nodiscard]] const context::hydrostatic::HydrostaticPreparationStatistics &
|
|
GetContextPreparationStatistics() const noexcept;
|
|
|
|
[[nodiscard]] std::uint64_t GetResidualPreparationCount() const noexcept;
|
|
|
|
[[nodiscard]] std::uint64_t GetResidualApplicationCount() const noexcept;
|
|
|
|
[[nodiscard]] const PreparedHydrostaticAlgebraicJacobianStatistics &
|
|
GetAlgebraicJacobianStatistics() const noexcept;
|
|
|
|
[[nodiscard]] const PreparedHydrostaticDisplacementJacobianStatistics &
|
|
GetDisplacementJacobianStatistics() const noexcept;
|
|
|
|
[[nodiscard]] const PreparedHydrostaticCompleteJacobianStatistics &
|
|
GetCompleteJacobianStatistics() const noexcept;
|
|
|
|
[[nodiscard]] std::size_t GetStellarElementCount() const noexcept;
|
|
|
|
[[nodiscard]] const fem::FEM &GetFEM() const noexcept;
|
|
|
|
[[nodiscard]] const field::FieldDofMap &GetEnthalpyMap() const noexcept;
|
|
|
|
[[nodiscard]] const field::FieldDofMap &GetGravityPotentialMap() const noexcept;
|
|
|
|
[[nodiscard]] const field::FieldDofMap &GetDisplacementMap() const noexcept;
|
|
|
|
template <typename Visitor> void VisitMappedGeometryRules(Visitor &&visitor) const {
|
|
for (const ElementPAData &data : m_elements) {
|
|
visitor(data.elementId, *data.integrationRule);
|
|
}
|
|
}
|
|
|
|
private:
|
|
struct ElementPAData {
|
|
int elementId{-1};
|
|
|
|
mfem::Array<int> enthalpyDofs;
|
|
mfem::Array<int> gravityPotentialDofs;
|
|
mfem::Array<int> displacementDofs;
|
|
|
|
mfem::DofTransformation *enthalpyDofTransformation{nullptr};
|
|
|
|
mfem::DofTransformation *gravityPotentialDofTransformation{nullptr};
|
|
|
|
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
|
|
|
const mfem::IntegrationRule *integrationRule{nullptr};
|
|
|
|
// Immutable reference values and gradients are shared by FE/rule.
|
|
std::shared_ptr<const fem::ScalarReferenceTable> enthalpyReferenceTable;
|
|
std::shared_ptr<const fem::ScalarReferenceTable> gravityPotentialReferenceTable;
|
|
|
|
[[nodiscard]] const mfem::DenseMatrix &GetEnthalpyBasis() const {
|
|
return enthalpyReferenceTable->GetValues();
|
|
}
|
|
|
|
[[nodiscard]] const mfem::DenseMatrix &GetGravityPotentialBasis() const {
|
|
return gravityPotentialReferenceTable->GetValues();
|
|
}
|
|
|
|
// Rows are quadrature points and columns are physical components.
|
|
mfem::DenseMatrix physicalPositions;
|
|
|
|
mfem::Vector quadratureWeights;
|
|
mapping::VolumeMappingCache baseMappingContexts;
|
|
|
|
std::optional<mapping::ElementDisplacementData> baseDisplacementData;
|
|
|
|
std::optional<mapping::ElementCompactificationData> compactificationData;
|
|
|
|
mfem::Vector rotationPotential;
|
|
mfem::DenseMatrix rotationGradient;
|
|
mfem::Vector hydrostaticImbalance;
|
|
mfem::Vector weightedResidual;
|
|
|
|
// Geometry-dependent algebraic Jacobian blocks.
|
|
mfem::DenseMatrix enthalpyJacobian;
|
|
mfem::DenseMatrix gravityPotentialJacobian;
|
|
mfem::Vector bernoulliConstantJacobian;
|
|
};
|
|
|
|
void PrepareStaticPlan();
|
|
[[nodiscard]] std::optional<mapping::MappingStatus> PrepareGeometry();
|
|
[[nodiscard]] bool PrepareAlgebraicJacobianBlocks();
|
|
[[nodiscard]] bool PrepareRotation();
|
|
[[nodiscard]] bool PrepareBaseState();
|
|
void FinalizeDisplacementJacobianPreparation();
|
|
[[nodiscard]] bool AssembleCachedResidual();
|
|
void VerifyPrepared() const;
|
|
|
|
const fem::FEM &m_fem;
|
|
const mapping::DomainMapper &m_domainMapper;
|
|
|
|
context::hydrostatic::HydrostaticEquilibriumContext m_context;
|
|
|
|
std::optional<physics::RigidRotation> m_rotation;
|
|
std::vector<ElementPAData> m_elements;
|
|
mfem::Vector m_cachedResidual;
|
|
|
|
mutable mfem::Vector m_enthalpyVariationTrue;
|
|
mutable mfem::Vector m_gravityPotentialVariationTrue;
|
|
mutable mfem::Vector m_displacementVariationTrue;
|
|
mutable mfem::Vector m_fullEnthalpyAction;
|
|
|
|
std::uint64_t m_residualPreparationCount{0};
|
|
|
|
mutable std::uint64_t m_residualApplicationCount{0};
|
|
|
|
mutable PreparedHydrostaticAlgebraicJacobianStatistics m_algebraicJacobianStatistics;
|
|
|
|
mutable PreparedHydrostaticDisplacementJacobianStatistics m_displacementJacobianStatistics;
|
|
|
|
mutable PreparedHydrostaticCompleteJacobianStatistics m_completeJacobianStatistics;
|
|
|
|
bool m_isPrepared{false};
|
|
};
|
|
|
|
class PreparedHydrostaticEquilibriumJacobianOperator final : public mfem::Operator {
|
|
public:
|
|
PreparedHydrostaticEquilibriumJacobianOperator(
|
|
const fem::FEM &f,
|
|
const PreparedHydrostaticEquilibriumOperator &preparedOperator
|
|
);
|
|
|
|
void Mult(
|
|
const mfem::Vector &direction,
|
|
mfem::Vector &action
|
|
) const override;
|
|
|
|
[[nodiscard]] const HydrostaticJacobianBlockLayout &GetLayout() const noexcept;
|
|
|
|
private:
|
|
HydrostaticJacobianBlockLayout m_layout;
|
|
|
|
const PreparedHydrostaticEquilibriumOperator &m_preparedOperator;
|
|
};
|
|
} // namespace mean_field::operators
|