Files
MeanField/libmeanfield/interface/operators/prepared_hydrostatic_equilibrium_operator.cppm
Emily Boudreaux 75cc638739 perf(allocations): reduced overall allocations by 95%, increaseed jacobian applicatin by 2x
This commit uses global pre allocated work space to dramatically reduce memory usage and allocation time
2026-09-10 06:50:56 -04:00

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