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
This commit is contained in:
82
libmeanfield/interface/deformation/safe_newton_step.cppm
Normal file
82
libmeanfield/interface/deformation/safe_newton_step.cppm
Normal file
@@ -0,0 +1,82 @@
|
||||
module;
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:deformation.safe_newton_step;
|
||||
|
||||
export import :mapping.domain_mapper;
|
||||
|
||||
export namespace mean_field::deformation {
|
||||
/*
|
||||
* One element-local quadrature rule at which a downstream operator will
|
||||
* evaluate the mapped geometry. A caller may provide several entries for
|
||||
* one element when several operators use different, non-nested rules.
|
||||
* The integration-rule object must outlive the call.
|
||||
*/
|
||||
struct NewtonStepGeometryRule final {
|
||||
int element{-1};
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
};
|
||||
|
||||
struct LargestSafeNewtonStepSizeOptions final {
|
||||
double maximumStepSize{1.0};
|
||||
double determinantFloor{0.0};
|
||||
double fractionToBoundarySafety{0.9};
|
||||
|
||||
void Validate() const;
|
||||
};
|
||||
|
||||
/*
|
||||
* The estimated boundary is the first alpha in [0, maximumStepSize] at
|
||||
* which any sampled mapping determinant reaches determinantFloor. When
|
||||
* no such point exists, boundaryStepSize equals maximumStepSize and
|
||||
* limitedByGeometry is false. stepSize is the boundary multiplied by the
|
||||
* safety fraction only when geometry is limiting.
|
||||
*
|
||||
* Element and rule indices are local to limitingRank. limitingRule is an
|
||||
* index into that rank's input span.
|
||||
*/
|
||||
struct LargestSafeNewtonStepSizeEstimate final {
|
||||
double stepSize{0.0};
|
||||
double boundaryStepSize{0.0};
|
||||
double minimumDeterminantAtAcceptedState{std::numeric_limits<double>::quiet_NaN()};
|
||||
double minimumDeterminantAtMaximumStepSize{std::numeric_limits<double>::quiet_NaN()};
|
||||
double limitingPointDeterminantAtStepSize{std::numeric_limits<double>::quiet_NaN()};
|
||||
std::uint64_t sampledQuadraturePointCount{0};
|
||||
bool limitedByGeometry{false};
|
||||
int limitingRank{-1};
|
||||
int limitingElement{-1};
|
||||
int limitingRule{-1};
|
||||
int limitingQuadraturePoint{-1};
|
||||
};
|
||||
|
||||
/*
|
||||
* Estimate the largest safe alpha for
|
||||
*
|
||||
* displacement(alpha) = acceptedVolumeDisplacement
|
||||
* + alpha * volumeNewtonDirection.
|
||||
*
|
||||
* Both vectors use the displacement space's true-DOF layout. The
|
||||
* compactification coordinate is held fixed. The result is collective on
|
||||
* displacementSpace.GetComm() and is identical on every rank.
|
||||
*
|
||||
* The calculation is exact for the current domain mapper: its mapping
|
||||
* Jacobian is affine along a displacement direction, so each sampled
|
||||
* determinant is a polynomial of degree at most the spatial dimension.
|
||||
* Compactified elements require an exterior map that explicitly advertises
|
||||
* the same affine contract.
|
||||
*/
|
||||
[[nodiscard]] LargestSafeNewtonStepSizeEstimate estimate_largest_safe_newton_step_size(
|
||||
const mapping::DomainMapper &domainMapper,
|
||||
const mfem::ParFiniteElementSpace &displacementSpace,
|
||||
const mfem::ParGridFunction &compactificationCoordinate,
|
||||
const mfem::Vector &acceptedVolumeDisplacement,
|
||||
const mfem::Vector &volumeNewtonDirection,
|
||||
std::span<const NewtonStepGeometryRule> geometryRules,
|
||||
const LargestSafeNewtonStepSizeOptions &options = {}
|
||||
);
|
||||
} // namespace mean_field::deformation
|
||||
@@ -14,6 +14,7 @@ export import :utils.misc;
|
||||
export import :utils.user;
|
||||
export import :quadrature.mfem;
|
||||
export import :field.mfem;
|
||||
export import :fem.reference_tables;
|
||||
|
||||
export namespace mean_field::fem {
|
||||
using GravityField = field::Field<field::Gravity>;
|
||||
@@ -145,6 +146,13 @@ export namespace mean_field::fem {
|
||||
[[nodiscard]] bool has_mapping() const {
|
||||
return domainMapperStateless != nullptr && displacement != nullptr && compactificationCoordinate != nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] const ReferenceTableCache &GetReferenceTables() const {
|
||||
return *m_reference_tables;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ReferenceTableCache> m_reference_tables{std::make_unique<ReferenceTableCache>()};
|
||||
};
|
||||
|
||||
FEM setup_fem(
|
||||
|
||||
78
libmeanfield/interface/fem/reference_tables.cppm
Normal file
78
libmeanfield/interface/fem/reference_tables.cppm
Normal file
@@ -0,0 +1,78 @@
|
||||
module;
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
export module mean_field:fem.reference_tables;
|
||||
|
||||
export namespace mean_field::fem {
|
||||
// These tables contain only reference-element data: no mesh coordinates,
|
||||
// orientation, element DOF transforms, or state-dependent mapping factors.
|
||||
class ScalarReferenceTable {
|
||||
public:
|
||||
[[nodiscard]] const mfem::DenseMatrix &GetValues() const;
|
||||
// Available for GRAD elements; otherwise throws std::out_of_range.
|
||||
[[nodiscard]] const mfem::DenseMatrix &GetGradients(int point) const;
|
||||
[[nodiscard]] int GetPointCount() const;
|
||||
[[nodiscard]] int GetDofCount() const;
|
||||
[[nodiscard]] int GetDimension() const;
|
||||
|
||||
private:
|
||||
friend class ReferenceTableCache;
|
||||
ScalarReferenceTable(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::IntegrationRule &rule
|
||||
);
|
||||
|
||||
mfem::DenseMatrix m_values;
|
||||
std::vector<mfem::DenseMatrix> m_gradients;
|
||||
int m_dimension;
|
||||
};
|
||||
|
||||
class VectorReferenceTable {
|
||||
public:
|
||||
[[nodiscard]] const mfem::DenseMatrix &GetValues(int point) const;
|
||||
[[nodiscard]] int GetPointCount() const;
|
||||
[[nodiscard]] int GetDofCount() const;
|
||||
[[nodiscard]] int GetDimension() const;
|
||||
|
||||
private:
|
||||
friend class ReferenceTableCache;
|
||||
VectorReferenceTable(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::IntegrationRule &rule
|
||||
);
|
||||
|
||||
std::vector<mfem::DenseMatrix> m_values;
|
||||
int m_dof_count;
|
||||
int m_dimension;
|
||||
};
|
||||
|
||||
// Owned by one discretization, never process-global. Finite elements must
|
||||
// remain alive and immutable while this cache is used; rebuild the cache if
|
||||
// their collections are replaced. Rules are keyed by their actual points
|
||||
// and weights, so temporary, copied, or modified rules are safe to use.
|
||||
// Returned immutable handles also keep tables alive after cache destruction.
|
||||
class ReferenceTableCache {
|
||||
public:
|
||||
ReferenceTableCache();
|
||||
~ReferenceTableCache();
|
||||
ReferenceTableCache(const ReferenceTableCache &) = delete;
|
||||
ReferenceTableCache &operator=(const ReferenceTableCache &) = delete;
|
||||
|
||||
[[nodiscard]] std::shared_ptr<const ScalarReferenceTable> GetScalarTable(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::IntegrationRule &rule
|
||||
) const;
|
||||
[[nodiscard]] std::shared_ptr<const VectorReferenceTable> GetVectorTable(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::IntegrationRule &rule
|
||||
) const;
|
||||
|
||||
private:
|
||||
struct Storage;
|
||||
std::unique_ptr<Storage> m_storage;
|
||||
};
|
||||
} // namespace mean_field::fem
|
||||
@@ -31,6 +31,16 @@ export namespace mean_field::mapping::compactification {
|
||||
public:
|
||||
virtual ~ExteriorDomainMap() = default;
|
||||
|
||||
/*
|
||||
* Return true when, with the reference and compactification data held
|
||||
* fixed, both outputs of Evaluate are affine functions of the
|
||||
* displaced position and displacement Jacobian. This is the contract
|
||||
* required by the exact determinant-polynomial geometry preflight.
|
||||
*/
|
||||
[[nodiscard]] virtual bool IsAffineInDisplacement() const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual MappingStatus Evaluate(
|
||||
const ExteriorMapInput &input,
|
||||
ExteriorMapResult &result
|
||||
|
||||
@@ -12,6 +12,10 @@ export namespace mean_field::mapping::compactification {
|
||||
public:
|
||||
explicit KelvinCompactification(options::KelvinCompactificationOptions options);
|
||||
|
||||
[[nodiscard]] bool IsAffineInDisplacement() const noexcept override {
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] MappingStatus Evaluate(
|
||||
const ExteriorMapInput &input,
|
||||
ExteriorMapResult &result
|
||||
@@ -45,4 +49,4 @@ export namespace mean_field::mapping::compactification {
|
||||
|
||||
options::KelvinCompactificationOptions m_options;
|
||||
};
|
||||
} // namespace mean_field::mapping::compactification
|
||||
} // namespace mean_field::mapping::compactification
|
||||
|
||||
@@ -80,6 +80,7 @@ export namespace mean_field::mapping {
|
||||
mfem::Vector m_shape;
|
||||
mfem::DenseMatrix m_reference_dshape;
|
||||
mfem::DenseMatrix m_mesh_dshape;
|
||||
mfem::DenseMatrix m_reference_field_jacobian;
|
||||
mfem::Vector m_field_value;
|
||||
mfem::DenseMatrix m_field_jacobian;
|
||||
|
||||
|
||||
42
libmeanfield/interface/mapping/prepared_cache.cppm
Normal file
42
libmeanfield/interface/mapping/prepared_cache.cppm
Normal file
@@ -0,0 +1,42 @@
|
||||
module;
|
||||
|
||||
#include <mfem.hpp>
|
||||
#include <vector>
|
||||
|
||||
export module mean_field:mapping.prepared_cache;
|
||||
import :mapping.types;
|
||||
|
||||
export namespace mean_field::mapping {
|
||||
// Flat, owning storage for prepared volume contexts. Load into reusable
|
||||
// workspaces: no MFEM buffers or pointer aliases are owned per quadrature
|
||||
// point. The layout retains every public context field without recomputing
|
||||
// inverses or changing the mapper's numerical contract.
|
||||
class VolumeMappingCache {
|
||||
public:
|
||||
void SetSize(
|
||||
int point_count,
|
||||
int dimension
|
||||
);
|
||||
void Store(
|
||||
int point,
|
||||
const VolumeMappingContext &context
|
||||
);
|
||||
void Load(
|
||||
int point,
|
||||
VolumeMappingContext &context
|
||||
) const;
|
||||
void LoadInverseJacobian(
|
||||
int point,
|
||||
mfem::DenseMatrix &inverse
|
||||
) const;
|
||||
[[nodiscard]] int GetPointCount() const;
|
||||
[[nodiscard]] int GetDimension() const;
|
||||
|
||||
private:
|
||||
[[nodiscard]] const double *GetPointData(int point) const;
|
||||
std::vector<double> m_data;
|
||||
int m_point_count{0};
|
||||
int m_dimension{0};
|
||||
int m_point_stride{0};
|
||||
};
|
||||
} // namespace mean_field::mapping
|
||||
@@ -14,6 +14,7 @@ export import :mapping.compactification;
|
||||
export import :mapping.kelvin;
|
||||
export import :mapping.transformations;
|
||||
export import :mapping.types;
|
||||
export import :mapping.prepared_cache;
|
||||
export import :mapping.compactification.options;
|
||||
export import :integrators.advection;
|
||||
export import :integrators.centrifugal;
|
||||
@@ -86,6 +87,7 @@ export import :deformation.interior_extension;
|
||||
export import :deformation.vacuum_extension;
|
||||
export import :deformation.radial_extensions;
|
||||
export import :deformation.domain_deformation;
|
||||
export import :deformation.safe_newton_step;
|
||||
export import :model.stellar;
|
||||
export import :operators.root_manifest;
|
||||
export import :operators.prepared_constraint;
|
||||
|
||||
@@ -4,6 +4,7 @@ module;
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
@@ -14,6 +15,7 @@ export module mean_field:operators.prepared_angular_momentum;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :mapping.prepared_cache;
|
||||
export import :model.compiled_fixed_angular_momentum;
|
||||
export import :operators.context.gravity_field;
|
||||
|
||||
@@ -188,15 +190,13 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const PreparedAngularMomentumActionStatistics &GetActionStatistics() const noexcept;
|
||||
[[nodiscard]] const models::CompiledFixedAngularMomentum &GetCompiledConstraint() const noexcept;
|
||||
|
||||
private:
|
||||
struct QuadraturePointData final {
|
||||
mfem::IntegrationPoint integrationPoint;
|
||||
mfem::Vector densityShape;
|
||||
mapping::VolumeMappingContext mappingContext;
|
||||
double density{0.0};
|
||||
double cylindricalRadiusSquared{0.0};
|
||||
};
|
||||
template <typename Visitor> void VisitMappedGeometryRules(Visitor &&visitor) const {
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
visitor(data.elementId, *data.integrationRule);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct ElementPAData final {
|
||||
int elementId{-1};
|
||||
mfem::Array<int> densityDofs;
|
||||
@@ -205,9 +205,14 @@ export namespace mean_field::operators {
|
||||
mfem::DofTransformation *densityDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
mfem::DofTransformation *compactificationDofTransformation{nullptr};
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
mfem::Vector baseDisplacement;
|
||||
mfem::Vector compactification;
|
||||
std::vector<QuadraturePointData> quadraturePoints;
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> densityBasis;
|
||||
mapping::VolumeMappingCache mappingContexts;
|
||||
mfem::Vector density;
|
||||
mfem::Vector quadratureWeights;
|
||||
mfem::Vector cylindricalRadiusSquared;
|
||||
};
|
||||
|
||||
void BuildStaticPlan();
|
||||
|
||||
@@ -2,6 +2,7 @@ module;
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <mfem.hpp>
|
||||
#include <vector>
|
||||
|
||||
@@ -97,6 +98,12 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const context::barotropic::BarotropicClosurePreparationStatistics &
|
||||
GetContextPreparationStatistics() const noexcept;
|
||||
|
||||
template <typename Visitor> void VisitMappedGeometryRules(Visitor &&visitor) const {
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
visitor(data.elementId, *data.integrationRule);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct ConstructionData;
|
||||
|
||||
@@ -132,8 +139,11 @@ export namespace mean_field::operators {
|
||||
mfem::DofTransformation *enthalpyDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
|
||||
mfem::DenseMatrix densityBasis;
|
||||
mfem::DenseMatrix enthalpyBasis;
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> densityBasis;
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> enthalpyBasis;
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> displacementBasis;
|
||||
mfem::DenseMatrix inverseElementJacobians;
|
||||
|
||||
mfem::Vector weightedResidual;
|
||||
@@ -169,7 +179,6 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_elementDisplacementVariation;
|
||||
mutable mfem::Vector m_quadratureDisplacementAction;
|
||||
mutable mfem::Vector m_elementDisplacementAction;
|
||||
mutable mfem::DenseMatrix m_referenceDShape;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementJacobian;
|
||||
|
||||
std::uint64_t m_preparationCount{0};
|
||||
|
||||
@@ -3,6 +3,7 @@ module;
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
@@ -14,6 +15,7 @@ export import :mapping.domain_mapper;
|
||||
export import :operators.context.gravity_field;
|
||||
export import :operators.kernels.gravity_displacement_force;
|
||||
export import :utils.blocks;
|
||||
import :fem.reference_tables;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
struct PreparedGravityDisplacementForceReport final {
|
||||
@@ -111,6 +113,12 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const context::gravity_field::GravityFieldLinearizationContext &
|
||||
GetGravityContext() const noexcept;
|
||||
|
||||
template <typename Visitor> void VisitMappedGeometryRules(Visitor &&visitor) const {
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
visitor(data.elementId, *data.integrationRule);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
[[nodiscard]] std::expected<
|
||||
@@ -133,6 +141,10 @@ export namespace mean_field::operators {
|
||||
mfem::DofTransformation *gravityGradientDofTransformation{nullptr};
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> densityReferenceTable;
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> displacementReferenceTable;
|
||||
std::shared_ptr<const fem::VectorReferenceTable> gravityReferenceTable;
|
||||
mfem::DenseMatrix meshPiolaJacobians;
|
||||
mfem::DenseMatrix mappingJacobians;
|
||||
mfem::DenseMatrix inverseMeshJacobians;
|
||||
mfem::DenseMatrix baseGravityReferenceValues;
|
||||
@@ -164,16 +176,17 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_displacementShape;
|
||||
mutable mfem::Vector m_baseGravityReferenceValue;
|
||||
mutable mfem::Vector m_gravityVariationReferenceValue;
|
||||
mutable mfem::Vector m_gravityVariationReferenceCellValue;
|
||||
mutable mfem::Vector m_mappedBaseGravity;
|
||||
mutable mfem::Vector m_mappedGravityVariation;
|
||||
mutable mfem::Vector m_mappedGeometryVariation;
|
||||
mutable mfem::Vector m_forceValue;
|
||||
mutable mfem::DenseMatrix m_gravityGradientShape;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementDShape;
|
||||
mutable mfem::DenseMatrix m_referenceDisplacementJacobian;
|
||||
mutable mfem::DenseMatrix m_displacementJacobianVariation;
|
||||
mutable mfem::DenseMatrix m_mappingJacobian;
|
||||
mutable mfem::DenseMatrix m_inverseMeshJacobian;
|
||||
mutable mfem::DenseMatrix m_meshPiolaJacobian;
|
||||
|
||||
std::uint64_t m_residualPreparationCount{0};
|
||||
mutable std::uint64_t m_residualApplicationCount{0};
|
||||
|
||||
@@ -58,6 +58,12 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const field::FieldDofMap &GetPotentialMap() 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.element_id, *data.integration_rule);
|
||||
}
|
||||
}
|
||||
|
||||
void MultTranspose(
|
||||
const mfem::Vector &potential,
|
||||
mfem::Vector &action
|
||||
@@ -80,6 +86,10 @@ export namespace mean_field::operators {
|
||||
const mfem::IntegrationRule *integration_rule{nullptr};
|
||||
|
||||
// Rows are quadrature points; columns are element DOFs.
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> density_reference;
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> potential_reference;
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> displacement_reference;
|
||||
// Non-VALUE map types retain their element-dependent physical basis.
|
||||
mfem::DenseMatrix density_basis;
|
||||
mfem::DenseMatrix potential_basis;
|
||||
mfem::DenseMatrix inverse_element_jacobians;
|
||||
@@ -87,6 +97,14 @@ export namespace mean_field::operators {
|
||||
// Contains quadrature weight, mesh Jacobian, mapped Jacobian,
|
||||
// and 4*pi*G.
|
||||
mfem::Vector quadrature_data;
|
||||
|
||||
[[nodiscard]] const mfem::DenseMatrix &GetDensityBasis() const {
|
||||
return density_reference ? density_reference->GetValues() : density_basis;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::DenseMatrix &GetPotentialBasis() const {
|
||||
return potential_reference ? potential_reference->GetValues() : potential_basis;
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] GravitySourcePreparationResult TryPrepareImpl(
|
||||
@@ -119,7 +137,6 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_element_displacement_variation;
|
||||
mutable mfem::Vector m_quadrature_variation_action;
|
||||
mutable mfem::Vector m_element_variation_action;
|
||||
mutable mfem::DenseMatrix m_reference_displacement_dshape;
|
||||
mutable mfem::DenseMatrix m_reference_displacement_jacobian;
|
||||
mfem::Vector m_displacement_true;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ export module mean_field:operators.prepared_hdiv_mass;
|
||||
export import :fem;
|
||||
export import :field.mfem;
|
||||
export import :mapping.domain_mapper;
|
||||
import :fem.reference_tables;
|
||||
|
||||
export namespace mean_field::operators {
|
||||
enum class HDivMassPreparationRejectionReason : std::uint8_t { invalid_mapping, non_finite_arithmetic };
|
||||
@@ -58,6 +59,12 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const field::FieldDofMap &GetFluxMap() const noexcept;
|
||||
[[nodiscard]] const field::FieldDofMap &GetDisplacementMap() const noexcept;
|
||||
|
||||
template <typename Visitor> void VisitMappedGeometryRules(Visitor &&visitor) const {
|
||||
for (const ElementVariationData &data : m_variationElements) {
|
||||
visitor(data.elementId, *data.integrationRule);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
enum class PreparationMode : std::uint8_t { primal, linearization };
|
||||
|
||||
@@ -71,6 +78,10 @@ export namespace mean_field::operators {
|
||||
mfem::Vector baseDisplacement;
|
||||
mfem::Vector compactification;
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
std::shared_ptr<const fem::VectorReferenceTable> gravityReferenceTable;
|
||||
// Fixed computational-mesh Piola factor, separate from J_map.
|
||||
mfem::DenseMatrix meshPiolaJacobians;
|
||||
mfem::Vector referenceWeights;
|
||||
mfem::DenseMatrix frozenMappingData;
|
||||
};
|
||||
|
||||
@@ -112,7 +123,10 @@ export namespace mean_field::operators {
|
||||
mutable mfem::Vector m_elementDisplacementVariation;
|
||||
mutable mfem::Vector m_elementVariationAction;
|
||||
mutable mfem::Vector m_gravityGradientValue;
|
||||
mutable mfem::Vector m_gravityReferenceCellValue;
|
||||
mutable mfem::Vector m_referenceCellDual;
|
||||
mutable mfem::Vector m_massTensorVariationAction;
|
||||
mutable mfem::DenseMatrix m_meshPiolaJacobian;
|
||||
mutable mfem::DenseMatrix m_gravityGradientShape;
|
||||
mutable mfem::DenseMatrix m_massTensorVariation;
|
||||
std::uint64_t m_preparation_count{0};
|
||||
|
||||
@@ -4,6 +4,7 @@ module;
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
@@ -13,7 +14,9 @@ module;
|
||||
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;
|
||||
|
||||
@@ -216,6 +219,12 @@ export namespace mean_field::operators {
|
||||
|
||||
[[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};
|
||||
@@ -232,15 +241,23 @@ export namespace mean_field::operators {
|
||||
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
|
||||
// Rows are quadrature points and columns are element DOFs.
|
||||
mfem::DenseMatrix enthalpyBasis;
|
||||
mfem::DenseMatrix gravityPotentialBasis;
|
||||
// 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;
|
||||
std::vector<mapping::VolumeMappingContext> baseMappingContexts;
|
||||
mapping::VolumeMappingCache baseMappingContexts;
|
||||
|
||||
std::optional<mapping::ElementDisplacementData> baseDisplacementData;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ module;
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <mfem.hpp>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
@@ -11,6 +12,7 @@ export module mean_field:operators.prepared_mass_normalization;
|
||||
|
||||
export import :fem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :mapping.prepared_cache;
|
||||
export import :model.compiled_fixed_mass;
|
||||
export import :operators.context.gravity_field;
|
||||
export import :operators.prepared_constraint;
|
||||
@@ -187,14 +189,13 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const context::gravity_field::GravityFieldLinearizationContext &
|
||||
GetGravityContext() const noexcept;
|
||||
|
||||
private:
|
||||
struct QuadraturePointData final {
|
||||
mfem::IntegrationPoint integrationPoint;
|
||||
mfem::Vector densityShape;
|
||||
mapping::VolumeMappingContext mappingContext;
|
||||
double density{0.0};
|
||||
};
|
||||
template <typename Visitor> void VisitMappedGeometryRules(Visitor &&visitor) const {
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
visitor(data.elementId, *data.integrationRule);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct ElementPAData final {
|
||||
int elementId{-1};
|
||||
|
||||
@@ -206,10 +207,15 @@ export namespace mean_field::operators {
|
||||
mfem::DofTransformation *displacementDofTransformation{nullptr};
|
||||
mfem::DofTransformation *compactificationDofTransformation{nullptr};
|
||||
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
|
||||
mfem::Vector baseDisplacement;
|
||||
mfem::Vector compactification;
|
||||
|
||||
std::vector<QuadraturePointData> quadraturePoints;
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> densityBasis;
|
||||
mapping::VolumeMappingCache mappingContexts;
|
||||
mfem::Vector density;
|
||||
mfem::Vector quadratureWeights;
|
||||
};
|
||||
|
||||
void BuildStaticPlan();
|
||||
|
||||
@@ -4,6 +4,7 @@ module;
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
@@ -13,8 +14,10 @@ export module mean_field:operators.prepared_pressure_force;
|
||||
|
||||
export import :eos.polytrope;
|
||||
export import :fem;
|
||||
export import :fem.reference_tables;
|
||||
export import :field.mfem;
|
||||
export import :mapping.domain_mapper;
|
||||
export import :mapping.prepared_cache;
|
||||
export import :operators.context.pressure_force;
|
||||
export import :utils.blocks;
|
||||
|
||||
@@ -168,6 +171,12 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]]
|
||||
const fem::FEM &GetFEM() const noexcept;
|
||||
|
||||
template <typename Visitor> void VisitMappedGeometryRules(Visitor &&visitor) const {
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
visitor(data.elementId, *data.integrationRule);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct ConstructionData;
|
||||
|
||||
@@ -196,10 +205,13 @@ export namespace mean_field::operators {
|
||||
|
||||
const mfem::IntegrationRule *integrationRule{nullptr};
|
||||
|
||||
/*
|
||||
* Rows are quadrature points and columns are enthalpy DOFs.
|
||||
*/
|
||||
mfem::DenseMatrix enthalpyBasis;
|
||||
// Immutable reference values and gradients are shared by FE/rule.
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> enthalpyReferenceTable;
|
||||
std::shared_ptr<const fem::ScalarReferenceTable> displacementReferenceTable;
|
||||
|
||||
[[nodiscard]] const mfem::DenseMatrix &GetEnthalpyBasis() const {
|
||||
return enthalpyReferenceTable->GetValues();
|
||||
}
|
||||
|
||||
/*
|
||||
* Each entry is:
|
||||
@@ -208,11 +220,9 @@ export namespace mean_field::operators {
|
||||
* x
|
||||
* physical dimension.
|
||||
*/
|
||||
std::vector<mfem::DenseMatrix> referenceTestGradients;
|
||||
|
||||
std::vector<mfem::DenseMatrix> physicalTestGradients;
|
||||
|
||||
std::vector<mapping::VolumeMappingContext> baseMappingContexts;
|
||||
mapping::VolumeMappingCache baseMappingContexts;
|
||||
|
||||
std::optional<mapping::ElementDisplacementData> baseDisplacementData;
|
||||
|
||||
|
||||
@@ -113,6 +113,12 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const context::rotational_displacement_force::RotationalDisplacementForceLinearizationContext &
|
||||
GetContext() const noexcept;
|
||||
|
||||
template <typename Visitor> void VisitMappedGeometryRules(Visitor &&visitor) const {
|
||||
for (const ElementPAData &data : m_elements) {
|
||||
visitor(data.elementId, *data.integrationRule);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void VerifyPrepared() const;
|
||||
[[nodiscard]] std::expected<
|
||||
|
||||
@@ -269,6 +269,10 @@ export namespace mean_field::operators {
|
||||
[[nodiscard]] const deformation::PreparedDomainDeformationRuntime &GetDomainDeformation() const noexcept;
|
||||
[[nodiscard]] const mfem::Vector &GetSurfaceDeformationParameters() const;
|
||||
[[nodiscard]] const mfem::Vector &GetGeneratedVolumeDisplacement() const;
|
||||
void BuildVolumeDisplacementDirection(
|
||||
const mfem::Vector &surfaceDeformationDirection,
|
||||
mfem::Vector &volumeDisplacementDirection
|
||||
) const;
|
||||
[[nodiscard]] const mfem::Vector &GetFullMechanicalResidual() const;
|
||||
[[nodiscard]] const StellarEquilibriumDependencyStamp &GetGeneratedDisplacementDependency() const;
|
||||
|
||||
|
||||
@@ -2785,6 +2785,22 @@ export namespace mean_field::operators {
|
||||
return *m_physical;
|
||||
}
|
||||
|
||||
void BuildVolumeDisplacementDirection(
|
||||
const mfem::Vector &stateDirection,
|
||||
mfem::Vector &volumeDisplacementDirection
|
||||
) const {
|
||||
if (stateDirection.Size() != Width()) {
|
||||
throw std::invalid_argument(
|
||||
"The prepared stellar-equilibrium root received a state direction with the wrong size."
|
||||
);
|
||||
}
|
||||
const auto rootDirection = m_manifest.directionView(stateDirection);
|
||||
m_physical->BuildVolumeDisplacementDirection(
|
||||
rootDirection.block(utils::blocks::surface_deformation_field.parameters_term),
|
||||
volumeDisplacementDirection
|
||||
);
|
||||
}
|
||||
|
||||
template <models::ModelSpecification Specification>
|
||||
requires ModelType::template
|
||||
containsSpecification<Specification> [[nodiscard]] const auto &GetPreparedContribution() const noexcept {
|
||||
|
||||
@@ -298,6 +298,13 @@ export namespace mean_field::equilibrium {
|
||||
m_preparedOperator.Mult(direction, action);
|
||||
}
|
||||
|
||||
void BuildVolumeDisplacementDirection(
|
||||
const mfem::Vector &stateDirection,
|
||||
mfem::Vector &volumeDisplacementDirection
|
||||
) const {
|
||||
m_preparedOperator.BuildVolumeDisplacementDirection(stateDirection, volumeDisplacementDirection);
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] static CompiledSurfaceConstraintType CompileSurfaceConstraint(const ModelType &stellarModel) {
|
||||
return surface::compilePressureSurfaceConstraint<
|
||||
|
||||
@@ -381,7 +381,7 @@ export namespace mean_field::solver {
|
||||
export namespace mean_field::solver::linear {
|
||||
struct FGMRESOptions final {
|
||||
int restartLength{50};
|
||||
int printLevel{-1};
|
||||
int printLevel{1};
|
||||
|
||||
void Validate() const {
|
||||
if (restartLength <= 0) {
|
||||
|
||||
@@ -17,6 +17,7 @@ module;
|
||||
|
||||
export module mean_field:solver.newton;
|
||||
|
||||
export import :deformation.safe_newton_step;
|
||||
export import :solver.linear_backend;
|
||||
|
||||
export namespace mean_field::solver::nonlinear {
|
||||
@@ -264,11 +265,13 @@ export namespace mean_field::solver::nonlinear {
|
||||
double relativeResidualNorm{0.0};
|
||||
double merit{0.0};
|
||||
double iterationSeconds{0.0};
|
||||
double geometryPreflightSeconds{0.0};
|
||||
double lineSearchSeconds{0.0};
|
||||
double trialPreparationSeconds{0.0};
|
||||
double metricEvaluationSeconds{0.0};
|
||||
double preconditionerRefreshSeconds{0.0};
|
||||
double rollbackSeconds{0.0};
|
||||
std::optional<deformation::LargestSafeNewtonStepSizeEstimate> geometryPreflight{};
|
||||
std::optional<LinearSolveReport> linearSolve{};
|
||||
MPI_Comm communicator{MPI_COMM_NULL};
|
||||
std::span<const mfem::real_t> physicalState{};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module;
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
@@ -8,6 +9,7 @@ module;
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -17,6 +19,7 @@ module;
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
#include <mpi.h>
|
||||
@@ -37,6 +40,11 @@ export namespace mean_field::solver {
|
||||
template <typename Context, typename NewtonConfiguration, typename Observer> class StellarEquilibriumSolver;
|
||||
} // namespace mean_field::solver
|
||||
|
||||
export namespace mean_field::solver::detail {
|
||||
// Internal, synchronous experiment access; not a stable solver API.
|
||||
struct StellarEquilibriumContextDiagnostics;
|
||||
}
|
||||
|
||||
namespace mean_field::solver::detail {
|
||||
template <typename Model, typename Discretization>
|
||||
using StellarContextProblem =
|
||||
@@ -256,6 +264,7 @@ export namespace mean_field::solver {
|
||||
private:
|
||||
template <typename, typename, typename> friend class StellarEquilibriumSolver;
|
||||
friend struct detail::StellarEquilibriumContextAssembly;
|
||||
friend struct detail::StellarEquilibriumContextDiagnostics;
|
||||
|
||||
using NormalizedOperatorType = detail::StellarContextNormalizedOperator<ProblemType>;
|
||||
using PhysicalInverseType = detail::StellarContextPhysicalInverse<PreconditionerPrescriptionType, ProblemType>;
|
||||
@@ -323,11 +332,16 @@ export namespace mean_field::solver {
|
||||
trialNormalizedResidual(RequireProblem(storage).EquationSize()),
|
||||
linearRightHandSide(RequireProblem(storage).EquationSize()),
|
||||
normalizedCorrection(RequireProblem(storage).StateSize()),
|
||||
physicalCorrection(RequireProblem(storage).StateSize()),
|
||||
volumeDisplacementDirection(
|
||||
RequireProblem(storage).GetPhysicalOperator().GetDomainDeformation().volumeDisplacementSize()
|
||||
),
|
||||
candidatePhysicalState(RequireProblem(storage).StateSize()),
|
||||
normalizedOperator(std::make_unique<NormalizedOperatorType>(RequireProblem(storage))) {
|
||||
ValidateInitialState();
|
||||
InitializeWorkspaces();
|
||||
PrepareInitialOperator();
|
||||
InitializeGeometryPreflightRules();
|
||||
|
||||
physicalInverse = std::unique_ptr<PhysicalInverseType>{new PhysicalInverseType(
|
||||
PreparePhysicalInverse(std::move(preconditionerPrescription), RequireProblem(storage))
|
||||
@@ -375,6 +389,9 @@ export namespace mean_field::solver {
|
||||
trialNormalizedResidual.Size() == problem->EquationSize() &&
|
||||
linearRightHandSide.Size() == problem->EquationSize() &&
|
||||
normalizedCorrection.Size() == problem->StateSize() &&
|
||||
physicalCorrection.Size() == problem->StateSize() &&
|
||||
volumeDisplacementDirection.Size() ==
|
||||
problem->GetPhysicalOperator().GetDomainDeformation().volumeDisplacementSize() &&
|
||||
candidatePhysicalState.Size() == problem->StateSize() &&
|
||||
storage->physicalState->Size() == problem->StateSize();
|
||||
}
|
||||
@@ -464,6 +481,26 @@ export namespace mean_field::solver {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] deformation::LargestSafeNewtonStepSizeEstimate EstimateLargestSafeStepSize(
|
||||
const double maximumStepSize,
|
||||
const double fractionToBoundarySafety
|
||||
) {
|
||||
normalizedOperator->DenormalizeState(normalizedCorrection, physicalCorrection);
|
||||
const auto &physicalOperator = Problem().GetPhysicalOperator();
|
||||
Problem().BuildVolumeDisplacementDirection(physicalCorrection, volumeDisplacementDirection);
|
||||
|
||||
const fem::FEM &finiteElements =
|
||||
equilibrium::detail::StellarEquilibriumProblemFactory::FiniteElementModel(Problem());
|
||||
return deformation::estimate_largest_safe_newton_step_size(
|
||||
Problem().GetDiscretization().domainMapper(), *finiteElements.displacementFes,
|
||||
*finiteElements.compactificationCoordinate, physicalOperator.GetGeneratedVolumeDisplacement(),
|
||||
volumeDisplacementDirection, geometryPreflightRules,
|
||||
{.maximumStepSize = maximumStepSize,
|
||||
.determinantFloor = 0.0,
|
||||
.fractionToBoundarySafety = fractionToBoundarySafety}
|
||||
);
|
||||
}
|
||||
|
||||
std::shared_ptr<Storage> storage;
|
||||
DependencyLedger dependencyLedger;
|
||||
mfem::Vector acceptedNormalizedState;
|
||||
@@ -472,7 +509,10 @@ export namespace mean_field::solver {
|
||||
mfem::Vector trialNormalizedResidual;
|
||||
mfem::Vector linearRightHandSide;
|
||||
mfem::Vector normalizedCorrection;
|
||||
mfem::Vector physicalCorrection;
|
||||
mfem::Vector volumeDisplacementDirection;
|
||||
mfem::Vector candidatePhysicalState;
|
||||
std::vector<deformation::NewtonStepGeometryRule> geometryPreflightRules;
|
||||
double acceptedMinimumJacobianDeterminant{std::numeric_limits<double>::quiet_NaN()};
|
||||
std::unique_ptr<NormalizedOperatorType> normalizedOperator;
|
||||
std::unique_ptr<PhysicalInverseType> physicalInverse;
|
||||
@@ -565,12 +605,14 @@ export namespace mean_field::solver {
|
||||
|
||||
void InitializeWorkspaces() {
|
||||
normalizedOperator->NormalizeState(AcceptedPhysicalState(), acceptedNormalizedState);
|
||||
trialNormalizedState = acceptedNormalizedState;
|
||||
acceptedNormalizedResidual = 0.0;
|
||||
trialNormalizedResidual = 0.0;
|
||||
linearRightHandSide = 0.0;
|
||||
normalizedCorrection = 0.0;
|
||||
candidatePhysicalState = AcceptedPhysicalState();
|
||||
trialNormalizedState = acceptedNormalizedState;
|
||||
acceptedNormalizedResidual = 0.0;
|
||||
trialNormalizedResidual = 0.0;
|
||||
linearRightHandSide = 0.0;
|
||||
normalizedCorrection = 0.0;
|
||||
physicalCorrection = 0.0;
|
||||
volumeDisplacementDirection = 0.0;
|
||||
candidatePhysicalState = AcceptedPhysicalState();
|
||||
}
|
||||
|
||||
void PrepareInitialOperator() {
|
||||
@@ -582,6 +624,97 @@ export namespace mean_field::solver {
|
||||
trialNormalizedResidual = acceptedNormalizedResidual;
|
||||
}
|
||||
|
||||
void AppendGeometryPreflightRule(
|
||||
const int element,
|
||||
const mfem::IntegrationRule &integrationRule
|
||||
) {
|
||||
geometryPreflightRules.push_back({.element = element, .integrationRule = &integrationRule});
|
||||
}
|
||||
|
||||
void InitializeGeometryPreflightRules() {
|
||||
const ProblemType &problem = Problem();
|
||||
const fem::FEM &finiteElements =
|
||||
equilibrium::detail::StellarEquilibriumProblemFactory::FiniteElementModel(problem);
|
||||
if (finiteElements.mesh == nullptr || finiteElements.displacementFes == nullptr ||
|
||||
finiteElements.compactificationCoordinate == nullptr) {
|
||||
throw std::logic_error(
|
||||
"The Newton geometry preflight requires complete displacement geometry data."
|
||||
);
|
||||
}
|
||||
if (!problem.GetPhysicalOperator().GetDomainDeformation().descriptor().linearOnReferenceGeometry) {
|
||||
throw std::invalid_argument(
|
||||
"The Newton geometry preflight requires a domain deformation that is linear on the "
|
||||
"reference geometry."
|
||||
);
|
||||
}
|
||||
|
||||
geometryPreflightRules.clear();
|
||||
geometryPreflightRules.reserve(
|
||||
static_cast<std::size_t>(finiteElements.mesh->GetNE()) * static_cast<std::size_t>(10)
|
||||
);
|
||||
|
||||
const int dimension = problem.GetDiscretization().domainMapper().GetDimension();
|
||||
for (int element = 0; element < finiteElements.mesh->GetNE(); ++element) {
|
||||
const mfem::FiniteElement *finiteElement = finiteElements.displacementFes->GetFE(element);
|
||||
mfem::ElementTransformation *transformation =
|
||||
finiteElements.mesh->GetElementTransformation(element);
|
||||
if (finiteElement == nullptr || transformation == nullptr) {
|
||||
throw std::logic_error(
|
||||
"The Newton geometry preflight encountered incomplete element geometry data."
|
||||
);
|
||||
}
|
||||
const int geometryInspectionOrder =
|
||||
std::max(finiteElement->GetOrder() + 2, 2 * dimension * finiteElement->GetOrder());
|
||||
AppendGeometryPreflightRule(
|
||||
element, mfem::IntRules.Get(transformation->GetGeometryType(), geometryInspectionOrder)
|
||||
);
|
||||
}
|
||||
|
||||
const auto appendPreparedRules = [this](const auto &preparedOperator) {
|
||||
preparedOperator.VisitMappedGeometryRules(
|
||||
[this](const int element, const mfem::IntegrationRule &integrationRule) {
|
||||
AppendGeometryPreflightRule(element, integrationRule);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const auto &physicalOperator = problem.GetPhysicalOperator();
|
||||
const auto &gravityGeometry = physicalOperator.GetGravityContext().GetGeometryContext();
|
||||
appendPreparedRules(gravityGeometry.GetMassOperator());
|
||||
appendPreparedRules(gravityGeometry.GetSourceOperator());
|
||||
appendPreparedRules(physicalOperator.GetBarotropicClosureOperator());
|
||||
appendPreparedRules(physicalOperator.GetHydrostaticOperator());
|
||||
|
||||
const auto &displacementOperator = physicalOperator.GetDisplacementOperator();
|
||||
appendPreparedRules(displacementOperator.GetPressureOperator());
|
||||
appendPreparedRules(displacementOperator.GetGravityOperator());
|
||||
appendPreparedRules(displacementOperator.GetRotationalOperator());
|
||||
appendPreparedRules(physicalOperator.GetMassNormalizationOperator());
|
||||
|
||||
if constexpr (ProblemType::hasFixedAngularMomentum) {
|
||||
appendPreparedRules(problem.GetPreparedOperator().GetAngularMomentumConstraint());
|
||||
}
|
||||
|
||||
const auto ruleLess = [](const deformation::NewtonStepGeometryRule &left,
|
||||
const deformation::NewtonStepGeometryRule &right) {
|
||||
if (left.element != right.element) {
|
||||
return left.element < right.element;
|
||||
}
|
||||
return std::less<const mfem::IntegrationRule *>{}(left.integrationRule, right.integrationRule);
|
||||
};
|
||||
std::sort(geometryPreflightRules.begin(), geometryPreflightRules.end(), ruleLess);
|
||||
geometryPreflightRules.erase(
|
||||
std::unique(
|
||||
geometryPreflightRules.begin(), geometryPreflightRules.end(),
|
||||
[](const deformation::NewtonStepGeometryRule &left,
|
||||
const deformation::NewtonStepGeometryRule &right) {
|
||||
return left.element == right.element && left.integrationRule == right.integrationRule;
|
||||
}
|
||||
),
|
||||
geometryPreflightRules.end()
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto PrepareOperator(const mfem::Vector &normalizedState) {
|
||||
if constexpr (ProblemType::generatedRotationProviderCount == 0) {
|
||||
return normalizedOperator->Prepare(
|
||||
@@ -695,6 +828,42 @@ export namespace mean_field::solver {
|
||||
concept StellarEquilibriumContextType = IsStellarEquilibriumContext<std::remove_cvref_t<Candidate>>::value;
|
||||
} // namespace mean_field::solver
|
||||
|
||||
export namespace mean_field::solver::detail {
|
||||
struct StellarEquilibriumContextDiagnostics final {
|
||||
// The callback must not retain references to runtime storage. It may
|
||||
// prepare trial states, but accepted vectors must remain unchanged.
|
||||
// Restore the production preparation and correction on every exit.
|
||||
template <typename Context, typename Callback>
|
||||
static void WithState(Context &context, Callback &&callback) {
|
||||
if (context.hasActiveSolver() || !context.isReady()) {
|
||||
throw std::logic_error("Diagnostics require a ready context with no active solver.");
|
||||
}
|
||||
auto &state = *context.m_state;
|
||||
mfem::Vector savedCorrection(state.normalizedCorrection);
|
||||
context.AcquireSolver();
|
||||
try {
|
||||
state.BeginEvaluation();
|
||||
std::invoke(std::forward<Callback>(callback), state,
|
||||
equilibrium::detail::StellarEquilibriumProblemFactory::FiniteElementModel(state.Problem()));
|
||||
state.RestoreAccepted();
|
||||
state.normalizedCorrection = savedCorrection;
|
||||
context.ReleaseSolver();
|
||||
} catch (...) {
|
||||
const auto original = std::current_exception();
|
||||
try {
|
||||
state.RestoreAccepted();
|
||||
state.normalizedCorrection = savedCorrection;
|
||||
} catch (...) {
|
||||
context.ReleaseSolver();
|
||||
throw;
|
||||
}
|
||||
context.ReleaseSolver();
|
||||
std::rethrow_exception(original);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace mean_field::solver::detail {
|
||||
[[nodiscard]] inline physics::RigidRotation ZeroRigidRotation() {
|
||||
mfem::Vector angularVelocity(3);
|
||||
@@ -973,12 +1142,14 @@ export namespace mean_field::solver {
|
||||
|
||||
struct IterationTimings final {
|
||||
Clock::time_point start{};
|
||||
double geometryPreflightSeconds{0.0};
|
||||
double lineSearchSeconds{0.0};
|
||||
double trialPreparationSeconds{0.0};
|
||||
double metricEvaluationSeconds{0.0};
|
||||
double preconditionerRefreshSeconds{0.0};
|
||||
double rollbackSeconds{0.0};
|
||||
double observerSeconds{0.0};
|
||||
std::optional<deformation::LargestSafeNewtonStepSizeEstimate> geometryPreflight;
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -1100,14 +1271,39 @@ export namespace mean_field::solver {
|
||||
}
|
||||
|
||||
const nonlinear::MetricEvaluation previousMetric = acceptedMetric;
|
||||
bool accepted = false;
|
||||
double acceptedStepLength = 0.0;
|
||||
int lineSearchTrials = 0;
|
||||
const Clock::time_point geometryPreflightStart = Clock::now();
|
||||
timings.geometryPreflight = state.EstimateLargestSafeStepSize(
|
||||
nextLineSearchStepLength, options.backtracking.fractionToBoundarySafety
|
||||
);
|
||||
timings.geometryPreflightSeconds =
|
||||
std::chrono::duration<double>(Clock::now() - geometryPreflightStart).count();
|
||||
diagnostics.totalGeometryPreflightSeconds += timings.geometryPreflightSeconds;
|
||||
diagnostics.lastGeometryPreflight = timings.geometryPreflight;
|
||||
if (timings.geometryPreflight->limitedByGeometry) {
|
||||
++diagnostics.geometryLimitedIterations;
|
||||
}
|
||||
|
||||
if (timings.geometryPreflight->stepSize < options.backtracking.minimumStepLength) {
|
||||
diagnostics.finalResidualNorm = acceptedMetric.residualNorm;
|
||||
NotifyAfter(
|
||||
iteration, nonlinear::IterationDisposition::globalization_failure, false, 0.0, 0,
|
||||
diagnostics.initialResidualNorm, previousMetric, acceptedMetric, linearReport, timings, state
|
||||
);
|
||||
return Failure(
|
||||
state, std::move(diagnostics), StellarEquilibriumFailureReason::globalization_failure,
|
||||
"The geometry preflight found no orientation-preserving Newton step at or above the "
|
||||
"configured minimum step length."
|
||||
);
|
||||
}
|
||||
|
||||
bool accepted = false;
|
||||
double acceptedStepLength = 0.0;
|
||||
int lineSearchTrials = 0;
|
||||
nonlinear::MetricEvaluation trialMetric{};
|
||||
StellarEquilibriumFailureReason rejectionReason =
|
||||
StellarEquilibriumFailureReason::globalization_failure;
|
||||
std::string rejectionMessage = "The backtracking line search found no acceptable Newton step.";
|
||||
double stepLength = nextLineSearchStepLength;
|
||||
double stepLength = timings.geometryPreflight->stepSize;
|
||||
|
||||
const Clock::time_point lineSearchStart = Clock::now();
|
||||
try {
|
||||
@@ -1553,11 +1749,13 @@ export namespace mean_field::solver {
|
||||
.relativeResidualNorm = RelativeResidual(metric.residualNorm, initialResidualNorm),
|
||||
.merit = metric.merit,
|
||||
.iterationSeconds = DurationExcludingObserver(timings.start, timings.observerSeconds),
|
||||
.geometryPreflightSeconds = timings.geometryPreflightSeconds,
|
||||
.lineSearchSeconds = timings.lineSearchSeconds,
|
||||
.trialPreparationSeconds = timings.trialPreparationSeconds,
|
||||
.metricEvaluationSeconds = timings.metricEvaluationSeconds,
|
||||
.preconditionerRefreshSeconds = timings.preconditionerRefreshSeconds,
|
||||
.rollbackSeconds = timings.rollbackSeconds,
|
||||
.geometryPreflight = timings.geometryPreflight,
|
||||
.linearSolve = linearReport,
|
||||
.communicator = state.Problem().GetCommunicator(),
|
||||
.physicalState = detail::ReadOnlySpan(state.AcceptedPhysicalState()),
|
||||
|
||||
@@ -6,6 +6,7 @@ module;
|
||||
|
||||
export module mean_field:solver.stellar_equilibrium_types;
|
||||
|
||||
export import :deformation.safe_newton_step;
|
||||
export import :solver.linear_backend;
|
||||
|
||||
export namespace mean_field::solver {
|
||||
@@ -46,15 +47,18 @@ export namespace mean_field::solver {
|
||||
int inadmissibleLineSearchTrials{0};
|
||||
int nonFiniteLineSearchTrials{0};
|
||||
int insufficientDecreaseTrials{0};
|
||||
int geometryLimitedIterations{0};
|
||||
double initialResidualNorm{0.0};
|
||||
double finalResidualNorm{0.0};
|
||||
double lastAcceptedStepLength{0.0};
|
||||
double totalLinearSolveSeconds{0.0};
|
||||
double totalGeometryPreflightSeconds{0.0};
|
||||
double totalLineSearchSeconds{0.0};
|
||||
double totalTrialPreparationSeconds{0.0};
|
||||
double totalMetricEvaluationSeconds{0.0};
|
||||
double totalPreconditionerRefreshSeconds{0.0};
|
||||
double totalRollbackSeconds{0.0};
|
||||
std::optional<deformation::LargestSafeNewtonStepSizeEstimate> lastGeometryPreflight;
|
||||
std::optional<LinearSolveReport> lastLinearSolve;
|
||||
};
|
||||
} // namespace mean_field::solver
|
||||
|
||||
Reference in New Issue
Block a user