feat(libmeanfield): variadic refactor

also added normaliztion operator
This commit is contained in:
2026-09-06 10:15:00 -04:00
parent 71423d543f
commit 76818f2f82
63 changed files with 28794 additions and 1119 deletions

View File

@@ -0,0 +1,196 @@
module;
#include <compare>
#include <cstdint>
#include <vector>
#include <mfem.hpp>
export module mean_field:operators.prepared_angular_momentum;
export import :fem;
export import :mapping.domain_mapper;
export import :model.compiled_fixed_angular_momentum;
export import :operators.context.gravity_field;
export namespace mean_field::operators {
struct AngularMomentumDependencyStamp final {
std::uint64_t identity{0};
std::uint64_t revision{0};
constexpr auto operator<=>(const AngularMomentumDependencyStamp &) const = default;
};
struct AngularMomentumDependencies final {
AngularMomentumDependencyStamp discretization;
AngularMomentumDependencyStamp density;
AngularMomentumDependencyStamp displacement;
AngularMomentumDependencyStamp rotation;
constexpr auto operator<=>(const AngularMomentumDependencies &) const = default;
};
struct PreparedAngularMomentumReport final {
bool rebuiltStaticPlan{false};
bool refreshedGeometry{false};
bool refreshedDensity{false};
bool updatedAngularVelocity{false};
bool assembledResidual{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return rebuiltStaticPlan || refreshedGeometry || refreshedDensity || updatedAngularVelocity ||
assembledResidual;
}
constexpr auto operator<=>(const PreparedAngularMomentumReport &) const = default;
};
struct AngularMomentumConstraintReport final {
double targetAngularMomentum;
double achievedAngularMomentum;
double momentOfInertia;
double angularVelocity;
double dimensionalResidual;
double scaledResidual;
};
struct PreparedAngularMomentumActionStatistics final {
std::uint64_t densityApplications{0};
std::uint64_t displacementApplications{0};
std::uint64_t angularVelocityApplications{0};
std::uint64_t completeApplications{0};
constexpr auto operator<=>(const PreparedAngularMomentumActionStatistics &) const = default;
};
/*
* Prepared scalar invariant
*
* R_J(rho, d, Omega) = Omega I_axis(rho, d) - J_target,
* I_axis = integral rho |(x-x_0)_perp|^2 dV.
*
* The axis is normalized by CompiledFixedAngularMomentum. Density and
* geometry are borrowed from the shared gravity context, so this row is
* linearized at exactly the same mapped state as every physical equation.
*/
class PreparedAngularMomentumOperator final {
public:
using SpecificationType = models::FixedAngularMomentum;
using CompiledConstraintType = models::CompiledFixedAngularMomentum;
using Dependencies = AngularMomentumDependencies;
using Report = PreparedAngularMomentumReport;
PreparedAngularMomentumOperator(
const fem::FEM &f,
const mapping::DomainMapper &domainMapper,
const context::gravity_field::GravityFieldLinearizationContext &gravityContext,
models::CompiledFixedAngularMomentum constraint
);
PreparedAngularMomentumOperator(const PreparedAngularMomentumOperator &) = delete;
PreparedAngularMomentumOperator &operator=(const PreparedAngularMomentumOperator &) = delete;
PreparedAngularMomentumOperator(PreparedAngularMomentumOperator &&) = delete;
PreparedAngularMomentumOperator &operator=(PreparedAngularMomentumOperator &&) = delete;
PreparedAngularMomentumReport Prepare(
double angularVelocity,
const AngularMomentumDependencies &dependencies
);
void BuildResidual(mfem::Vector &residual) const;
void ApplyDensityJacobianAction(
const mfem::Vector &densityVariation,
mfem::Vector &action
) const;
void ApplyDisplacementJacobianAction(
const mfem::Vector &displacementVariation,
mfem::Vector &action
) const;
void ApplyAngularVelocityJacobianAction(
double angularVelocityVariation,
mfem::Vector &action
) const;
void ApplyCompleteJacobianAction(
const mfem::Vector &densityVariation,
const mfem::Vector &displacementVariation,
double angularVelocityVariation,
mfem::Vector &action
) const;
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] double GetMomentOfInertia() const;
[[nodiscard]] double GetAngularVelocity() const;
[[nodiscard]] double GetCurrentAngularMomentum() const;
[[nodiscard]] double GetTargetAngularMomentum() const noexcept;
[[nodiscard]] physics::RigidRotation GetRotation() const;
[[nodiscard]] AngularMomentumConstraintReport GetConstraintReport() const;
[[nodiscard]] std::uint64_t GetPreparationCount() const noexcept;
[[nodiscard]] std::uint64_t GetResidualApplicationCount() const noexcept;
[[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};
};
struct ElementPAData final {
int elementId{-1};
mfem::Array<int> densityDofs;
mfem::Array<int> displacementDofs;
mfem::Array<int> compactificationDofs;
mfem::DofTransformation *densityDofTransformation{nullptr};
mfem::DofTransformation *displacementDofTransformation{nullptr};
mfem::DofTransformation *compactificationDofTransformation{nullptr};
mfem::Vector baseDisplacement;
mfem::Vector compactification;
std::vector<QuadraturePointData> quadraturePoints;
};
void BuildStaticPlan();
void RefreshGeometry(const mfem::Vector &displacement);
void RefreshDensity(const mfem::Vector &density);
void AssembleResidual();
void VerifyPrepared() const;
[[nodiscard]] double EvaluateDensityMomentActionLocal(const mfem::Vector &densityVariation) const;
[[nodiscard]] double EvaluateDisplacementMomentActionLocal(const mfem::Vector &displacementVariation) const;
[[nodiscard]] double CylindricalRadiusSquared(const mfem::Vector &physicalPosition) const noexcept;
[[nodiscard]] double CylindricalRadiusSquaredVariation(
const mfem::Vector &physicalPosition,
const mfem::Vector &physicalPositionVariation
) const noexcept;
[[nodiscard]] double GlobalSum(double localValue) const;
const fem::FEM &m_fem;
const mapping::DomainMapper &m_domainMapper;
const context::gravity_field::GravityFieldLinearizationContext &m_gravityContext;
models::CompiledFixedAngularMomentum m_constraint;
std::vector<ElementPAData> m_elements;
AngularMomentumDependencies m_preparedDependencies;
mfem::Vector m_cachedResidual;
mutable mfem::Vector m_densityVariationTrue;
mutable mfem::Vector m_displacementVariationTrue;
mutable mfem::Vector m_densityVariationLocal;
mutable mfem::Vector m_displacementVariationLocal;
mutable mfem::Vector m_elementDensityVariation;
mutable mfem::Vector m_elementDisplacementVariation;
double m_momentOfInertia{0.0};
double m_angularVelocity{0.0};
double m_currentAngularMomentum{0.0};
std::uint64_t m_preparationCount{0};
mutable std::uint64_t m_residualApplicationCount{0};
mutable PreparedAngularMomentumActionStatistics m_actionStatistics;
bool m_isPrepared{false};
};
} // namespace mean_field::operators

View File

@@ -1,117 +0,0 @@
module;
#include <concepts>
#include <memory>
#include <type_traits>
#include <utility>
#include <mfem.hpp>
export module mean_field:operators.prepared_central_density_stellar_equilibrium;
export import :model.compiled_fixed_central_density;
export import :operators.prepared_central_density;
export import :operators.prepared_stellar_equilibrium;
export namespace mean_field::operators {
using CentralDensityStellarEquilibriumSpecificationModel = model::StellarModel<
models::
SpecificationSet<eos::Polytrope, models::FixedTotalMass, surface::Isobaric, models::FixedCentralDensity>>;
using CentralDensityStellarEquilibriumForm = utils::blocks::central_density_bordered_stellar_equilibrium_form;
using CentralDensityStellarEquilibriumJacobianForm =
utils::blocks::central_density_bordered_stellar_equilibrium_jacobian_form;
using CentralDensityStellarEquilibriumLayout = utils::blocks::form_layout<CentralDensityStellarEquilibriumForm>;
using CentralDensityStellarEquilibriumSystemManifest = EquilibriumSystemManifest<
CentralDensityStellarEquilibriumSpecificationModel,
CentralDensityStellarEquilibriumForm,
CentralDensityStellarEquilibriumJacobianForm>;
using CentralDensityStellarEquilibriumRootManifest = CentralDensityStellarEquilibriumSystemManifest;
struct PreparedCentralDensityStellarEquilibriumReport final {
PreparedStellarEquilibriumReport physical;
PreparedCentralDensityReport phase;
bool assembledResidual{false};
[[nodiscard]] bool DidAnyWork() const noexcept {
return physical.DidAnyWork() || phase.DidAnyWork() || assembledResidual;
}
};
class PreparedCentralDensityStellarEquilibriumOperator final : public mfem::Operator {
public:
PreparedCentralDensityStellarEquilibriumOperator(
fem::FEM &f,
const mapping::DomainMapper &domainMapper,
const eos::Polytrope &equationOfState,
models::CompiledFixedMass fixedMassConstraint,
PressureSurfaceConstraintView surfaceConstraint,
deformation::PreparedDomainDeformationRuntime domainDeformation,
models::CompiledFixedCentralDensity centralDensity
)
: PreparedCentralDensityStellarEquilibriumOperator(
f,
std::make_unique<PreparedStellarEquilibriumOperator>(
f,
domainMapper,
equationOfState,
std::move(fixedMassConstraint),
surfaceConstraint,
std::move(domainDeformation)
),
std::move(centralDensity),
MakeCenterDofMap(f)
) {
}
PreparedCentralDensityStellarEquilibriumOperator(const PreparedCentralDensityStellarEquilibriumOperator &) =
delete;
PreparedCentralDensityStellarEquilibriumOperator &
operator=(const PreparedCentralDensityStellarEquilibriumOperator &) = delete;
PreparedCentralDensityStellarEquilibriumOperator(PreparedCentralDensityStellarEquilibriumOperator &&) = delete;
PreparedCentralDensityStellarEquilibriumOperator &
operator=(PreparedCentralDensityStellarEquilibriumOperator &&) = delete;
PreparedCentralDensityStellarEquilibriumReport Prepare(
const mfem::Vector &state,
const StellarEquilibriumDependencies &dependencies,
const physics::RigidRotation &rotation
);
void BuildResidual(mfem::Vector &residual) const;
void Mult(
const mfem::Vector &direction,
mfem::Vector &action
) const override;
[[nodiscard]] bool IsPrepared() const noexcept;
[[nodiscard]] const CentralDensityStellarEquilibriumLayout &GetLayout() const noexcept;
[[nodiscard]] const CentralDensityStellarEquilibriumRootManifest &GetRootManifest() const noexcept;
[[nodiscard]] const PreparedStellarEquilibriumOperator &GetPhysicalOperator() const noexcept;
[[nodiscard]] const PreparedCentralDensityConstraint &GetCentralDensityConstraint() const noexcept;
[[nodiscard]] RootConstraintReport GetFixedMassReport() const;
[[nodiscard]] CentralDensityConstraintReport GetCentralDensityReport() const;
private:
static field::FieldPointDofMap MakeCenterDofMap(const fem::FEM &f);
PreparedCentralDensityStellarEquilibriumOperator(
fem::FEM &f,
std::unique_ptr<PreparedStellarEquilibriumOperator> physicalOperator,
models::CompiledFixedCentralDensity centralDensity,
field::FieldPointDofMap centerDof
);
void AssembleResidual();
void VerifyPrepared() const;
std::unique_ptr<PreparedStellarEquilibriumOperator> m_physicalOperator;
models::CompiledFixedCentralDensity m_centralDensity;
PreparedCentralDensityConstraint m_phaseConstraint;
CentralDensityStellarEquilibriumRootManifest m_rootManifest;
mfem::Vector m_cachedResidual;
bool m_isPrepared{false};
};
} // namespace mean_field::operators

View File

@@ -35,6 +35,7 @@ export namespace mean_field::operators {
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;
@@ -118,6 +119,14 @@ export namespace mean_field::operators {
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,

View File

@@ -96,6 +96,20 @@ export namespace mean_field::operators {
class PreparedStellarEquilibriumOperator final : public mfem::Operator {
public:
/*
* Privileged aggregate runtimes can inspect this complete numerical
* core. Keep their allow-list on the concrete core itself: a custom
* EOS may reuse this class, but it cannot extend the class's backend
* privileges. Ordinary specifications use restricted nested physics
* and never interact with this list.
*/
using BackendSpecifications = models::ModelTypeList<
eos::Polytrope,
surface::Isobaric,
models::FixedTotalMass,
models::FixedAngularMomentum,
models::FixedCentralDensity>;
template <models::StellarModelType Model>
requires std::same_as<
typename std::remove_cvref_t<Model>::EquationOfStateType,
@@ -174,6 +188,12 @@ export namespace mean_field::operators {
[[nodiscard]] const PreparedHydrostaticEquilibriumOperator &GetHydrostaticOperator() const noexcept;
[[nodiscard]] const PreparedDisplacementResidualOperator &GetDisplacementOperator() const noexcept;
[[nodiscard]] const PreparedMassNormalizationOperator &GetMassNormalizationOperator() const noexcept;
[[nodiscard]] double ApplyDensityVolumeIntegralDensityAction(
const mfem::Vector &densityDirection
) const;
[[nodiscard]] double ApplyDensityVolumeIntegralSurfaceShapeAction(
const mfem::Vector &surfaceShapeDirection
) const;
[[nodiscard]] const PreparedPressureSurfaceConstraint &GetSurfaceConstraintOperator() const noexcept;
[[nodiscard]] const deformation::PreparedDomainDeformationRuntime &GetDomainDeformation() const noexcept;
[[nodiscard]] const mfem::Vector &GetSurfaceDeformationParameters() const;
@@ -234,5 +254,6 @@ export namespace mean_field::operators {
mutable mfem::Vector m_fullMechanicalAction;
mutable mfem::Vector m_surfaceShapeAction;
mutable mfem::Vector m_pullbackDerivativeAction;
mutable mfem::Vector m_densityVolumeIntegralAction;
};
} // namespace mean_field::operators

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,815 @@
module;
#include <concepts>
#include <type_traits>
export module mean_field:operators.stellar_equilibrium_compiler;
export import :model.compiled_fixed_angular_momentum;
export import :model.compiled_fixed_central_density;
export import :model.typed_stellar;
export import :utils.blocks;
export namespace mean_field::operators {
/*
* A coupling is the symbolic statement that one Jacobian block may be
* nonzero. Specifications contribute these statements independently of
* the final row and column layout.
*/
template <typename ResidualBlock, typename ValueBlock>
struct StellarEquilibriumJacobianCoupling final {
using Residual = ResidualBlock;
using Value = ValueBlock;
using ResidualBlockType = ResidualBlock;
using ValueBlockType = ValueBlock;
};
template <typename ResidualBlock, typename ValueBlock>
using EquilibriumJacobianCoupling =
StellarEquilibriumJacobianCoupling<ResidualBlock, ValueBlock>;
namespace detail {
template <typename... Lists> struct ConcatenateBlockLists;
template <> struct ConcatenateBlockLists<> {
using Type = utils::blocks::type_list<>;
};
template <typename... Types>
struct ConcatenateBlockLists<utils::blocks::type_list<Types...>> {
using Type = utils::blocks::type_list<Types...>;
};
template <typename... First, typename... Second, typename... Remaining>
struct ConcatenateBlockLists<utils::blocks::type_list<First...>,
utils::blocks::type_list<Second...>,
Remaining...> {
using Type = typename ConcatenateBlockLists<
utils::blocks::type_list<First..., Second...>, Remaining...>::Type;
};
template <typename... Lists>
using ConcatenateBlockListsT = typename ConcatenateBlockLists<Lists...>::Type;
template <typename List, typename Type> struct AppendUniqueBlockType;
template <typename... Types, typename Type>
struct AppendUniqueBlockType<utils::blocks::type_list<Types...>, Type> {
using TypeValue = std::conditional_t<
utils::blocks::contains_type_v<Type, utils::blocks::type_list<Types...>>,
utils::blocks::type_list<Types...>,
utils::blocks::type_list<Types..., Type>>;
};
template <typename Accumulated, typename Remaining> struct UniqueBlockListImpl;
template <typename Accumulated>
struct UniqueBlockListImpl<Accumulated, utils::blocks::type_list<>> {
using Type = Accumulated;
};
template <typename Accumulated, typename Head, typename... Tail>
struct UniqueBlockListImpl<Accumulated,
utils::blocks::type_list<Head, Tail...>> {
using Type = typename UniqueBlockListImpl<
typename AppendUniqueBlockType<Accumulated, Head>::TypeValue,
utils::blocks::type_list<Tail...>>::Type;
};
template <typename List>
using UniqueBlockListT =
typename UniqueBlockListImpl<utils::blocks::type_list<>, List>::Type;
template <typename... Lists>
using UniqueConcatenateBlockListsT =
UniqueBlockListT<ConcatenateBlockListsT<Lists...>>;
template <typename Candidate> struct IsValueBlockList : std::false_type {};
template <typename... Blocks>
struct IsValueBlockList<utils::blocks::type_list<Blocks...>>
: std::bool_constant<
(std::derived_from<Blocks, utils::blocks::value_block_base> && ...) &&
utils::blocks::types_are_unique_v<
utils::blocks::type_list<Blocks...>>> {};
template <typename Candidate> struct IsResidualBlockList : std::false_type {};
template <typename... Blocks>
struct IsResidualBlockList<utils::blocks::type_list<Blocks...>>
: std::bool_constant<
(std::derived_from<Blocks, utils::blocks::residual_block_base> &&
...) &&
utils::blocks::types_are_unique_v<
utils::blocks::type_list<Blocks...>>> {};
template <typename GeneratedValues> struct GeneratedValueBlocksFor;
template <typename... GeneratedValues>
struct GeneratedValueBlocksFor<models::ModelTypeList<GeneratedValues...>> {
using Type = utils::blocks::type_list<
utils::blocks::generated_value_block<GeneratedValues>...>;
};
template <typename GeneratedResiduals> struct GeneratedResidualBlocksFor;
template <typename... GeneratedResiduals>
struct GeneratedResidualBlocksFor<
models::ModelTypeList<GeneratedResiduals...>> {
using Type = utils::blocks::type_list<
utils::blocks::generated_residual_block<GeneratedResiduals>...>;
};
/*
* One translation boundary turns physics-facing stellar names into backend
* blocks. Existing backend block types pass through unchanged, which keeps
* the advanced extension API open without making built-in physics declarations
* depend on utils.blocks.
*/
template <typename DeclaredDependency>
struct UnmappedStellarDependency final {};
template <typename Blocks, typename DeclaredDependency>
struct SingleGeneratedBlock {
using Type = UnmappedStellarDependency<DeclaredDependency>;
static constexpr bool available = false;
};
template <typename Block, typename DeclaredDependency>
struct SingleGeneratedBlock<utils::blocks::type_list<Block>,
DeclaredDependency> {
using Type = Block;
static constexpr bool available = true;
};
template <models::ModelSpecification Specification, typename Dependency>
struct StellarDependencyBlock {
using Type = UnmappedStellarDependency<Dependency>;
static constexpr bool mapped = false;
};
template <models::ModelSpecification Specification, typename Block>
requires(std::derived_from<Block, utils::blocks::value_block_base> ||
std::derived_from<Block, utils::blocks::residual_block_base>)
struct StellarDependencyBlock<Specification, Block> {
using Type = Block;
static constexpr bool mapped = true;
};
template <models::ModelSpecification Specification>
struct StellarDependencyBlock<Specification, models::stellar::state::Density> {
using Type = utils::blocks::density::mass::value;
static constexpr bool mapped = true;
};
template <models::ModelSpecification Specification>
struct StellarDependencyBlock<Specification,
models::stellar::state::SurfaceShape> {
using Type = utils::blocks::surface_deformation::parameters::value;
static constexpr bool mapped = true;
};
template <models::ModelSpecification Specification>
struct StellarDependencyBlock<Specification,
models::stellar::state::GravityGradient> {
using Type = utils::blocks::gravity::gradient::value;
static constexpr bool mapped = true;
};
template <models::ModelSpecification Specification>
struct StellarDependencyBlock<
Specification, models::stellar::state::GravitationalPotential> {
using Type = utils::blocks::gravity::poisson::value;
static constexpr bool mapped = true;
};
template <models::ModelSpecification Specification>
struct StellarDependencyBlock<Specification,
models::stellar::state::SpecificEnthalpy> {
using Type = utils::blocks::enthalpy::specific::value;
static constexpr bool mapped = true;
};
template <models::ModelSpecification Specification>
struct StellarDependencyBlock<
Specification, models::stellar::state::OwnGeneratedCoordinate> {
private:
using GeneratedBlocks = typename GeneratedValueBlocksFor<
typename models::SpecificationContribution<
Specification>::GeneratedValues>::Type;
using Selection = SingleGeneratedBlock<
GeneratedBlocks, models::stellar::state::OwnGeneratedCoordinate>;
public:
using Type = typename Selection::Type;
static constexpr bool mapped = Selection::available;
};
template <models::ModelSpecification Specification,
models::ModelSpecification Owner>
struct StellarDependencyBlock<
Specification, models::stellar::state::GeneratedCoordinateOf<Owner>> {
private:
using GeneratedBlocks = typename GeneratedValueBlocksFor<
typename models::SpecificationContribution<Owner>::GeneratedValues>::Type;
using Dependency = models::stellar::state::GeneratedCoordinateOf<Owner>;
using Selection = SingleGeneratedBlock<GeneratedBlocks, Dependency>;
public:
using Type = typename Selection::Type;
static constexpr bool mapped = Selection::available;
};
template <models::ModelSpecification Specification>
struct StellarDependencyBlock<
Specification, models::stellar::equation::GravityGradientDefinition> {
using Type = utils::blocks::gravity::gradient::residual;
static constexpr bool mapped = true;
};
template <models::ModelSpecification Specification>
struct StellarDependencyBlock<Specification,
models::stellar::equation::PoissonEquation> {
using Type = utils::blocks::gravity::poisson::residual;
static constexpr bool mapped = true;
};
template <models::ModelSpecification Specification>
struct StellarDependencyBlock<Specification,
models::stellar::equation::DensityClosure> {
using Type = utils::blocks::density::mass::residual;
static constexpr bool mapped = true;
};
template <models::ModelSpecification Specification>
struct StellarDependencyBlock<
Specification, models::stellar::equation::SurfaceShapeBalance> {
using Type =
utils::blocks::surface_deformation::shape_equilibrium::residual;
static constexpr bool mapped = true;
};
template <models::ModelSpecification Specification>
struct StellarDependencyBlock<
Specification, models::stellar::equation::HydrostaticBalance> {
using Type = utils::blocks::enthalpy::specific::residual;
static constexpr bool mapped = true;
};
template <models::ModelSpecification Specification>
struct StellarDependencyBlock<Specification,
models::stellar::equation::OwnConstraint> {
private:
using GeneratedBlocks = typename GeneratedResidualBlocksFor<
typename models::SpecificationContribution<
Specification>::GeneratedResiduals>::Type;
using Selection = SingleGeneratedBlock<
GeneratedBlocks, models::stellar::equation::OwnConstraint>;
public:
using Type = typename Selection::Type;
static constexpr bool mapped = Selection::available;
};
template <models::ModelSpecification Specification,
models::ModelSpecification Owner>
struct StellarDependencyBlock<
Specification, models::stellar::equation::ConstraintOf<Owner>> {
private:
using GeneratedBlocks = typename GeneratedResidualBlocksFor<
typename models::SpecificationContribution<Owner>::GeneratedResiduals>::Type;
using Dependency = models::stellar::equation::ConstraintOf<Owner>;
using Selection = SingleGeneratedBlock<GeneratedBlocks, Dependency>;
public:
using Type = typename Selection::Type;
static constexpr bool mapped = Selection::available;
};
template <models::ModelSpecification Specification, typename Dependencies>
struct CompileStellarDependencies {
using Type = utils::blocks::type_list<
UnmappedStellarDependency<Dependencies>>;
static constexpr bool complete = false;
};
template <models::ModelSpecification Specification, typename... Dependencies>
struct CompileStellarDependencies<Specification,
models::ModelTypeList<Dependencies...>> {
using Type = utils::blocks::type_list<
typename StellarDependencyBlock<Specification, Dependencies>::Type...>;
static constexpr bool complete =
(StellarDependencyBlock<Specification, Dependencies>::mapped && ...);
};
template <typename Residual, typename Values> struct CoupleResidualToValues;
template <typename Residual, typename... Values>
struct CoupleResidualToValues<Residual, utils::blocks::type_list<Values...>> {
using Type = utils::blocks::type_list<
StellarEquilibriumJacobianCoupling<Residual, Values>...>;
};
template <typename Residuals, typename Values>
struct CartesianJacobianCouplings;
template <typename... Residuals, typename Values>
struct CartesianJacobianCouplings<utils::blocks::type_list<Residuals...>,
Values> {
using Type = ConcatenateBlockListsT<
typename CoupleResidualToValues<Residuals, Values>::Type...>;
};
template <typename Candidate> struct IsJacobianCoupling : std::false_type {};
template <typename Residual, typename Value>
struct IsJacobianCoupling<StellarEquilibriumJacobianCoupling<Residual, Value>>
: std::bool_constant<
std::derived_from<Residual, utils::blocks::residual_block_base> &&
std::derived_from<Value, utils::blocks::value_block_base>> {};
template <typename Candidate>
struct IsJacobianCouplingList : std::false_type {};
template <typename... Couplings>
struct IsJacobianCouplingList<utils::blocks::type_list<Couplings...>>
: std::bool_constant<(IsJacobianCoupling<Couplings>::value && ...) &&
utils::blocks::types_are_unique_v<
utils::blocks::type_list<Couplings...>>> {};
template <typename Candidate> struct IsGeneratedValueBlock : std::false_type {};
template <typename Owner>
struct IsGeneratedValueBlock<utils::blocks::generated_value_block<Owner>>
: std::true_type {};
template <typename Candidate>
struct IsGeneratedResidualBlock : std::false_type {};
template <typename Owner>
struct IsGeneratedResidualBlock<utils::blocks::generated_residual_block<Owner>>
: std::true_type {};
template <typename Coupling>
inline constexpr bool isGeneratedBorderIncidentCoupling =
IsGeneratedValueBlock<typename Coupling::Value>::value ||
IsGeneratedResidualBlock<typename Coupling::Residual>::value;
template <typename Couplings> struct GeneratedBorderIncidentCouplings;
template <>
struct GeneratedBorderIncidentCouplings<utils::blocks::type_list<>> {
using Type = utils::blocks::type_list<>;
};
template <typename Head, typename... Tail>
struct GeneratedBorderIncidentCouplings<
utils::blocks::type_list<Head, Tail...>> {
private:
using Remaining = typename GeneratedBorderIncidentCouplings<
utils::blocks::type_list<Tail...>>::Type;
public:
using Type = std::conditional_t<
isGeneratedBorderIncidentCoupling<Head>,
ConcatenateBlockListsT<utils::blocks::type_list<Head>, Remaining>,
Remaining>;
};
template <bool Registered, typename GeneratedValues,
typename GeneratedResiduals, typename DependsOn, typename Affects>
struct DeclarativeStellarEquilibriumSpecificationCompilation {
using GeneratedValueBlocks = GeneratedValues;
using GeneratedResidualBlocks = GeneratedResiduals;
using DependsOnValueBlocks = DependsOn;
using AffectedResidualBlocks = Affects;
/*
* Preserve the two physical meanings in the declaration instead of
* flattening their endpoints into independent unions:
*
* constraint equation <- everything named in Reads
* changed equations <- Reads plus the generated coordinate
*
* The second group deliberately includes Affects x Reads. Nonlinear
* constraints and multiplier forces generally contribute Hessian-like
* state derivatives there. Linear contributions simply assemble zero on
* those structurally permitted edges.
*/
using ConstraintInputValueBlocks = DependsOnValueBlocks;
using ConstraintOutputResidualBlocks = GeneratedResidualBlocks;
using ChangedEquationInputValueBlocks = UniqueConcatenateBlockListsT<
DependsOnValueBlocks, GeneratedValueBlocks>;
using ChangedEquationOutputResidualBlocks = AffectedResidualBlocks;
using ConstraintJacobianCouplings =
typename CartesianJacobianCouplings<GeneratedResidualBlocks,
DependsOnValueBlocks>::Type;
using ChangedEquationJacobianCouplings =
typename CartesianJacobianCouplings<AffectedResidualBlocks,
ChangedEquationInputValueBlocks>::Type;
// Compatibility names retained for backend code that distinguishes the
// generated row from the generated-coordinate column.
using GeneratedRowJacobianCouplings = ConstraintJacobianCouplings;
using AffectedRowJacobianCouplings =
typename CartesianJacobianCouplings<AffectedResidualBlocks,
GeneratedValueBlocks>::Type;
using AffectedStateJacobianCouplings =
typename CartesianJacobianCouplings<AffectedResidualBlocks,
DependsOnValueBlocks>::Type;
using JacobianCouplings = UniqueConcatenateBlockListsT<
ConstraintJacobianCouplings, ChangedEquationJacobianCouplings>;
using IncidentJacobianCouplings =
typename GeneratedBorderIncidentCouplings<JacobianCouplings>::Type;
// Correction is the Newton-facing name for a value coordinate.
using GeneratedCorrectionBlocks = GeneratedValueBlocks;
static constexpr bool registered = Registered;
static constexpr bool complete =
registered && IsValueBlockList<GeneratedValueBlocks>::value &&
IsResidualBlockList<GeneratedResidualBlocks>::value &&
IsValueBlockList<DependsOnValueBlocks>::value &&
IsResidualBlockList<AffectedResidualBlocks>::value &&
IsJacobianCouplingList<JacobianCouplings>::value &&
(GeneratedValueBlocks::size == GeneratedResidualBlocks::size) &&
((GeneratedValueBlocks::size == 0 && DependsOnValueBlocks::size == 0 &&
AffectedResidualBlocks::size == 0) ||
(GeneratedValueBlocks::size > 0 && DependsOnValueBlocks::size > 0 &&
AffectedResidualBlocks::size > 0));
};
using EmptySpecificationCompilation =
DeclarativeStellarEquilibriumSpecificationCompilation<
false, utils::blocks::type_list<>, utils::blocks::type_list<>,
utils::blocks::type_list<>, utils::blocks::type_list<>>;
template <models::ModelSpecification Specification>
struct SelfDescribingSpecificationCompilationInputs {
using Contribution = models::SpecificationContribution<Specification>;
using DependsOn =
CompileStellarDependencies<Specification, typename Contribution::DependsOn>;
using Affects =
CompileStellarDependencies<Specification, typename Contribution::Affects>;
using GeneratedValueBlocks = typename GeneratedValueBlocksFor<
typename Contribution::GeneratedValues>::Type;
using GeneratedResidualBlocks = typename GeneratedResidualBlocksFor<
typename Contribution::GeneratedResiduals>::Type;
using DependsOnValueBlocks = typename DependsOn::Type;
using AffectedResidualBlocks = typename Affects::Type;
static constexpr bool registered =
Contribution::hasDeclarativeDefinition && DependsOn::complete &&
Affects::complete;
};
template <models::ModelSpecification Specification>
struct SelfDescribingSpecificationCompilation
: DeclarativeStellarEquilibriumSpecificationCompilation<
SelfDescribingSpecificationCompilationInputs<Specification>::registered,
typename SelfDescribingSpecificationCompilationInputs<
Specification>::GeneratedValueBlocks,
typename SelfDescribingSpecificationCompilationInputs<
Specification>::GeneratedResidualBlocks,
typename SelfDescribingSpecificationCompilationInputs<
Specification>::DependsOnValueBlocks,
typename SelfDescribingSpecificationCompilationInputs<
Specification>::AffectedResidualBlocks> {};
} // namespace detail
/*
* Public, inspectable per-specification compilation metadata. The primary
* is deliberately well formed and incomplete, so testing an arbitrary type
* in a requires-expression never triggers a diagnostic.
*/
template <typename Specification>
struct StellarEquilibriumSpecificationCompilation
: detail::EmptySpecificationCompilation {};
template <models::ModelSpecification Specification>
struct StellarEquilibriumSpecificationCompilation<Specification>
: detail::SelfDescribingSpecificationCompilation<Specification> {};
namespace detail {
template <typename Specification, typename = void>
struct SpecificationCompilationIsComplete : std::false_type {};
template <typename Specification>
struct SpecificationCompilationIsComplete<
Specification,
std::void_t<typename StellarEquilibriumSpecificationCompilation<
Specification>::GeneratedValueBlocks,
typename StellarEquilibriumSpecificationCompilation<
Specification>::GeneratedResidualBlocks,
typename StellarEquilibriumSpecificationCompilation<
Specification>::DependsOnValueBlocks,
typename StellarEquilibriumSpecificationCompilation<
Specification>::AffectedResidualBlocks,
typename StellarEquilibriumSpecificationCompilation<
Specification>::JacobianCouplings,
std::bool_constant<StellarEquilibriumSpecificationCompilation<
Specification>::registered>,
std::bool_constant<StellarEquilibriumSpecificationCompilation<
Specification>::complete>>>
: std::bool_constant<
StellarEquilibriumSpecificationCompilation<
Specification>::registered &&
StellarEquilibriumSpecificationCompilation<Specification>::complete &&
IsValueBlockList<typename StellarEquilibriumSpecificationCompilation<
Specification>::GeneratedValueBlocks>::value &&
IsResidualBlockList<
typename StellarEquilibriumSpecificationCompilation<
Specification>::GeneratedResidualBlocks>::value &&
IsValueBlockList<typename StellarEquilibriumSpecificationCompilation<
Specification>::DependsOnValueBlocks>::value &&
IsResidualBlockList<
typename StellarEquilibriumSpecificationCompilation<
Specification>::AffectedResidualBlocks>::value &&
IsJacobianCouplingList<
typename StellarEquilibriumSpecificationCompilation<
Specification>::JacobianCouplings>::value> {};
} // namespace detail
template <typename Candidate>
inline constexpr bool stellarEquilibriumSpecificationCompilationComplete =
detail::SpecificationCompilationIsComplete<
std::remove_cvref_t<Candidate>>::value;
template <typename Candidate>
concept StellarEquilibriumSpecificationCompilable =
stellarEquilibriumSpecificationCompilationComplete<Candidate>;
namespace detail {
/*
* This five-by-five physical core is independent of global constraints.
* Even FixedTotalMass is compiled as a contribution, keeping C and R_M
* visible in that specification's metadata.
*/
using StellarPhysicsValueBlocks = utils::blocks::type_list<
utils::blocks::density::mass::value,
utils::blocks::surface_deformation::parameters::value,
utils::blocks::gravity::gradient::value,
utils::blocks::gravity::poisson::value,
utils::blocks::enthalpy::specific::value>;
using StellarPhysicsResidualBlocks = utils::blocks::type_list<
utils::blocks::gravity::gradient::residual,
utils::blocks::gravity::poisson::residual,
utils::blocks::density::mass::residual,
utils::blocks::surface_deformation::shape_equilibrium::residual,
utils::blocks::enthalpy::specific::residual>;
using StellarPhysicsJacobianRows = utils::blocks::type_list<
utils::blocks::block_row<
utils::blocks::gravity::gradient::residual,
utils::blocks::gravity::gradient::value,
utils::blocks::gravity::poisson::value,
utils::blocks::surface_deformation::parameters::value>,
utils::blocks::block_row<
utils::blocks::gravity::poisson::residual,
utils::blocks::gravity::gradient::value,
utils::blocks::density::mass::value,
utils::blocks::surface_deformation::parameters::value>,
utils::blocks::block_row<
utils::blocks::density::mass::residual,
utils::blocks::density::mass::value,
utils::blocks::enthalpy::specific::value,
utils::blocks::surface_deformation::parameters::value>,
utils::blocks::block_row<
utils::blocks::surface_deformation::shape_equilibrium::residual,
utils::blocks::density::mass::value,
utils::blocks::surface_deformation::parameters::value,
utils::blocks::gravity::gradient::value,
utils::blocks::enthalpy::specific::value>,
utils::blocks::block_row<
utils::blocks::enthalpy::specific::residual,
utils::blocks::enthalpy::specific::value,
utils::blocks::gravity::poisson::value,
utils::blocks::surface_deformation::parameters::value>>;
template <typename Row> struct JacobianRowCouplings;
template <typename Residual, typename... Values>
struct JacobianRowCouplings<utils::blocks::block_row<Residual, Values...>> {
using Type = utils::blocks::type_list<
StellarEquilibriumJacobianCoupling<Residual, Values>...>;
};
template <typename Rows> struct FlattenJacobianRows;
template <typename... Rows>
struct FlattenJacobianRows<utils::blocks::type_list<Rows...>> {
using Type =
ConcatenateBlockListsT<typename JacobianRowCouplings<Rows>::Type...>;
};
using StellarPhysicsJacobianCouplings =
typename FlattenJacobianRows<StellarPhysicsJacobianRows>::Type;
template <typename SpecificationSet>
struct SpecificationSetCompilationsAreComplete;
template <models::ModelSpecification... Specifications>
struct SpecificationSetCompilationsAreComplete<
models::detail::SpecificationSetStorage<Specifications...>>
: std::bool_constant<(
stellarEquilibriumSpecificationCompilationComplete<Specifications> &&
...)> {};
template <typename SpecificationSet, bool Complete>
struct CollectStellarEquilibriumContributionsImpl {
using GeneratedValueBlocks = utils::blocks::type_list<>;
using GeneratedResidualBlocks = utils::blocks::type_list<>;
using ContributionJacobianCouplings = utils::blocks::type_list<>;
using IncidentJacobianCouplings = ContributionJacobianCouplings;
static constexpr bool complete = false;
};
template <models::ModelSpecification... Specifications>
struct CollectStellarEquilibriumContributionsImpl<
models::detail::SpecificationSetStorage<Specifications...>, true> {
using GeneratedValueBlocks = ConcatenateBlockListsT<
typename StellarEquilibriumSpecificationCompilation<
Specifications>::GeneratedValueBlocks...>;
using GeneratedResidualBlocks = ConcatenateBlockListsT<
typename StellarEquilibriumSpecificationCompilation<
Specifications>::GeneratedResidualBlocks...>;
using ContributionJacobianCouplings = UniqueConcatenateBlockListsT<
typename StellarEquilibriumSpecificationCompilation<
Specifications>::JacobianCouplings...>;
using IncidentJacobianCouplings = UniqueConcatenateBlockListsT<
typename StellarEquilibriumSpecificationCompilation<
Specifications>::IncidentJacobianCouplings...>;
static constexpr bool complete = true;
};
template <typename SpecificationSet>
using CollectStellarEquilibriumContributions =
CollectStellarEquilibriumContributionsImpl<
SpecificationSet,
SpecificationSetCompilationsAreComplete<SpecificationSet>::value>;
template <typename Residual, typename Couplings> struct ValuesCoupledToResidual;
template <typename Residual>
struct ValuesCoupledToResidual<Residual, utils::blocks::type_list<>> {
using Type = utils::blocks::type_list<>;
};
template <typename Residual, typename HeadResidual, typename HeadValue,
typename... Tail>
struct ValuesCoupledToResidual<
Residual,
utils::blocks::type_list<
StellarEquilibriumJacobianCoupling<HeadResidual, HeadValue>, Tail...>> {
private:
using Remaining =
typename ValuesCoupledToResidual<Residual,
utils::blocks::type_list<Tail...>>::Type;
public:
using Type = std::conditional_t<
std::same_as<Residual, HeadResidual>,
ConcatenateBlockListsT<utils::blocks::type_list<HeadValue>, Remaining>,
Remaining>;
};
template <typename Residual, typename Values> struct MakeJacobianRow;
template <typename Residual, typename... Values>
struct MakeJacobianRow<Residual, utils::blocks::type_list<Values...>> {
using Type = utils::blocks::block_row<Residual, Values...>;
};
template <typename Residuals, typename Couplings> struct SynthesizeJacobianRows;
template <typename... Residuals, typename Couplings>
struct SynthesizeJacobianRows<utils::blocks::type_list<Residuals...>,
Couplings> {
using Type = utils::blocks::type_list<typename MakeJacobianRow<
Residuals,
typename ValuesCoupledToResidual<Residuals, Couplings>::Type>::Type...>;
};
template <typename Couplings, typename ValueBlocks, typename ResidualBlocks>
struct CouplingEndpointsBelongToForm : std::false_type {};
template <typename ValueBlocks, typename ResidualBlocks, typename... Couplings>
struct CouplingEndpointsBelongToForm<utils::blocks::type_list<Couplings...>,
ValueBlocks, ResidualBlocks>
: std::bool_constant<((utils::blocks::contains_type_v<
typename Couplings::Value, ValueBlocks> &&
utils::blocks::contains_type_v<
typename Couplings::Residual, ResidualBlocks>) &&
...)> {};
template <typename Candidate> struct CompileStellarEquilibriumSystem {
using GeneratedValueBlocks = utils::blocks::type_list<>;
using GeneratedCorrectionBlocks = GeneratedValueBlocks;
using GeneratedResidualBlocks = utils::blocks::type_list<>;
using BaseJacobianCouplings = utils::blocks::type_list<>;
using ContributionJacobianCouplings = utils::blocks::type_list<>;
using IncidentJacobianCouplings = ContributionJacobianCouplings;
using JacobianCouplings = utils::blocks::type_list<>;
static constexpr bool compilable = false;
};
template <model::StellarModelType Model>
struct CompileStellarEquilibriumSystem<Model> {
using ModelType = std::remove_cvref_t<Model>;
using Contributions = CollectStellarEquilibriumContributions<
typename ModelType::SpecificationTypes>;
using GeneratedValueBlocks = typename Contributions::GeneratedValueBlocks;
using GeneratedCorrectionBlocks = GeneratedValueBlocks;
using GeneratedResidualBlocks =
typename Contributions::GeneratedResidualBlocks;
using ValueBlocks =
ConcatenateBlockListsT<StellarPhysicsValueBlocks, GeneratedValueBlocks>;
using ResidualBlocks = ConcatenateBlockListsT<StellarPhysicsResidualBlocks,
GeneratedResidualBlocks>;
using FormType = utils::blocks::block_form<ValueBlocks, ResidualBlocks>;
using BaseJacobianCouplings = StellarPhysicsJacobianCouplings;
using ContributionJacobianCouplings =
typename Contributions::ContributionJacobianCouplings;
using IncidentJacobianCouplings = ContributionJacobianCouplings;
using JacobianCouplings =
UniqueConcatenateBlockListsT<BaseJacobianCouplings,
ContributionJacobianCouplings>;
// Pass two: materialize rows only after all contributed blocks are
// present in the final form.
using JacobianType =
typename SynthesizeJacobianRows<ResidualBlocks, JacobianCouplings>::Type;
static constexpr bool compilable =
Contributions::complete &&
utils::blocks::block_form_is_valid_v<FormType> &&
IsJacobianCouplingList<JacobianCouplings>::value &&
CouplingEndpointsBelongToForm<JacobianCouplings, ValueBlocks,
ResidualBlocks>::value &&
utils::blocks::jacobian_form_is_valid_v<FormType, JacobianType>;
};
} // namespace detail
template <typename Candidate>
inline constexpr bool stellarEquilibriumSystemIsCompilable =
detail::CompileStellarEquilibriumSystem<
std::remove_cvref_t<Candidate>>::compilable;
/*
* This compiler proves the symbolic block topology only. Keep the explicit
* name available to extension authors and tests so that success here is not
* mistaken for an assembled numerical runtime. The established spelling is
* retained below as a compatibility alias.
*/
template <typename Candidate>
inline constexpr bool stellarEquilibriumIsSymbolicallyCompilable =
stellarEquilibriumSystemIsCompilable<Candidate>;
template <typename Candidate>
concept StellarEquilibriumSymbolicallyCompilable =
stellarEquilibriumIsSymbolicallyCompilable<Candidate>;
template <typename Candidate>
concept StellarEquilibriumSystemCompilable =
StellarEquilibriumSymbolicallyCompilable<Candidate>;
template <model::StellarModelType Model>
requires StellarEquilibriumSystemCompilable<Model>
struct CompiledStellarEquilibriumSystem final
: detail::CompileStellarEquilibriumSystem<std::remove_cvref_t<Model>> {
using Base =
detail::CompileStellarEquilibriumSystem<std::remove_cvref_t<Model>>;
using FormType = typename Base::FormType;
using JacobianType = typename Base::JacobianType;
// This classification is exposed only after the complete compiler concept
// has succeeded; model declarations intentionally do not predict it.
static constexpr models::EquilibriumSystemCompilation compilationClass =
models::EquilibriumSystemCompilation::complete_equilibrium_system;
static_assert(utils::blocks::block_form_is_valid_v<FormType>);
static_assert(utils::blocks::valid_jacobian_form<FormType, JacobianType>);
};
template <model::StellarModelType Model>
requires StellarEquilibriumSystemCompilable<Model>
using CompiledStellarEquilibriumForm =
typename CompiledStellarEquilibriumSystem<Model>::FormType;
template <model::StellarModelType Model>
requires StellarEquilibriumSystemCompilable<Model>
using CompiledStellarEquilibriumJacobianForm =
typename CompiledStellarEquilibriumSystem<Model>::JacobianType;
} // namespace mean_field::operators

View File

@@ -2,6 +2,8 @@ module;
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <type_traits>
#include <utility>
@@ -13,46 +15,126 @@ export import :deformation.domain_deformation;
export import :equilibrium.stellar_discretization;
export import :material.thermodynamic_equations;
export import :model.typed_stellar;
export import :operators.prepared_central_density_stellar_equilibrium;
export import :normalization.operators;
export import :operators.prepared_variadic_stellar_equilibrium;
export import :surface.compiler;
export namespace mean_field::equilibrium {
namespace detail {
template <
model::StellarModelType Model,
bool SymbolicallyCompilable = operators::StellarEquilibriumSystemCompilable<Model>>
struct StellarSurfaceCompilationAudit {
static constexpr bool complete = false;
};
template <model::StellarModelType Model>
struct StellarSurfaceCompilationAudit<Model, true> {
private:
using ModelType = std::remove_cvref_t<Model>;
using EquationOfState = typename ModelType::EquationOfStateType;
using Form = operators::CompiledStellarEquilibriumForm<ModelType>;
using AvailableEquations = material::StellarEquilibriumThermodynamicEquations;
static constexpr bool thermodynamicsCompilable =
material::ThermodynamicEquationsCompilable<EquationOfState, Form, AvailableEquations>;
public:
static constexpr bool complete = [] {
if constexpr (!thermodynamicsCompilable) {
return false;
} else {
using ThermodynamicEquations =
material::CompiledThermodynamicEquationsT<EquationOfState, Form, AvailableEquations>;
using Formulation = typename ThermodynamicEquations::PressureSurfaceFormulation;
using CompiledSurface =
surface::CompiledPressureSurfaceConstraintT<Formulation, EquationOfState>;
return requires(const ModelType &model) {
{
surface::compilePressureSurfaceConstraint<Formulation>(
model.surfaceCondition(),
model.equationOfState()
)
} -> std::same_as<CompiledSurface>;
};
}
}();
};
} // namespace detail
template <model::StellarModelType Model>
inline constexpr bool hasStellarEquilibriumSurfaceCompilation =
detail::StellarSurfaceCompilationAudit<std::remove_cvref_t<Model>>::complete;
template <typename Candidate>
concept StellarEquilibriumModel = model::StellarModelType<Candidate> && requires {
requires std::remove_cvref_t<Candidate>::template containsSpecification<eos::Polytrope>;
requires std::remove_cvref_t<Candidate>::template containsSpecification<surface::Isobaric>;
typename std::remove_cvref_t<Candidate>::EquationOfStateType;
requires(
std::remove_cvref_t<Candidate>::template specificationRoleCount<
models::SpecificationRole::boundary_condition> == 1
);
requires std::remove_cvref_t<Candidate>::template containsSpecification<models::FixedTotalMass>;
requires std::remove_cvref_t<Candidate>::specificationCount ==
3 + static_cast<std::size_t>(
std::remove_cvref_t<Candidate>::template containsSpecification<models::FixedCentralDensity>
);
requires operators::StellarEquilibriumSystemCompilable<std::remove_cvref_t<Candidate>>;
requires hasStellarEquilibriumSurfaceCompilation<std::remove_cvref_t<Candidate>>;
requires operators::CompilableRootManifestFor<
std::remove_cvref_t<Candidate>,
operators::CompiledStellarEquilibriumForm<std::remove_cvref_t<Candidate>>>;
requires operators::hasStellarEquilibriumCoreRuntime<std::remove_cvref_t<Candidate>>;
requires operators::hasCompleteStellarEquilibriumRuntime<std::remove_cvref_t<Candidate>>;
requires operators::stellarEquilibriumRotationProviderCount<std::remove_cvref_t<Candidate>> <= 1;
};
template <StellarEquilibriumModel Model> class StellarEquilibriumProblem final {
namespace detail {
template <typename Model, typename Discretization, typename = void>
struct StellarEquilibriumModelDiscretizationStructureAudit : std::false_type { };
template <typename Model, typename Discretization>
requires StellarEquilibriumModel<std::remove_cvref_t<Model>> &&
StellarDiscretizationType<std::remove_cvref_t<Discretization>>
struct StellarEquilibriumModelDiscretizationStructureAudit<
Model,
Discretization,
std::void_t<
typename std::remove_cvref_t<Discretization>::NormalizationPrescriptionType,
typename std::remove_cvref_t<Model>::SpecificationTypes,
operators::CompiledStellarEquilibriumForm<std::remove_cvref_t<Model>>,
operators::StellarEquilibriumPhysicalCoreType<std::remove_cvref_t<Model>>>>
: std::bool_constant<normalization::StellarNormalizationRuntimeAvailableFor<
typename std::remove_cvref_t<Discretization>::NormalizationPrescriptionType,
operators::CompiledStellarEquilibriumForm<std::remove_cvref_t<Model>>,
operators::StellarEquilibriumPhysicalCoreType<std::remove_cvref_t<Model>>,
typename std::remove_cvref_t<Model>::SpecificationTypes>> { };
struct StellarEquilibriumProblemFactory;
} // namespace detail
template <
StellarEquilibriumModel Model,
StellarDiscretizationType Discretization = StellarDiscretization>
requires detail::StellarEquilibriumModelDiscretizationStructureAudit<
std::remove_cvref_t<Model>,
std::remove_cvref_t<Discretization>>::value
class StellarEquilibriumProblem final {
public:
using ModelType = std::remove_cvref_t<Model>;
using DiscretizationType = std::remove_cvref_t<Discretization>;
using NormalizationPrescriptionType = typename DiscretizationType::NormalizationPrescriptionType;
static constexpr bool hasFixedCentralDensity =
ModelType::template containsSpecification<models::FixedCentralDensity>;
static constexpr bool hasFixedAngularMomentum =
ModelType::template containsSpecification<models::FixedAngularMomentum>;
static constexpr std::size_t generatedRotationProviderCount =
operators::stellarEquilibriumRotationProviderCount<ModelType>;
static constexpr bool symbolicallySquare = ModelType::symbolicallySquare;
using PreparedOperatorType = std::conditional_t<
hasFixedCentralDensity,
operators::PreparedCentralDensityStellarEquilibriumOperator,
operators::PreparedStellarEquilibriumOperator>;
using FormType = std::conditional_t<
hasFixedCentralDensity,
operators::CentralDensityStellarEquilibriumForm,
utils::blocks::surface_deformed_stellar_equilibrium_form>;
using JacobianFormType = std::conditional_t<
hasFixedCentralDensity,
operators::CentralDensityStellarEquilibriumJacobianForm,
utils::blocks::surface_deformed_stellar_equilibrium_jacobian_form>;
using ManifestType = std::conditional_t<
hasFixedCentralDensity,
operators::CentralDensityStellarEquilibriumSystemManifest,
operators::StellarEquilibriumSystemManifest>;
using EquationOfStateType = eos::Polytrope;
using PreparedOperatorType = operators::PreparedVariadicStellarEquilibriumOperator<ModelType>;
using PhysicalCoreType = typename PreparedOperatorType::PhysicalCoreType;
using FormType = operators::CompiledStellarEquilibriumForm<ModelType>;
using JacobianFormType = operators::CompiledStellarEquilibriumJacobianForm<ModelType>;
using ManifestType = operators::EquilibriumSystemManifest<ModelType, FormType, JacobianFormType>;
using EquationOfStateType = model::EquationOfStateType<ModelType>;
using SurfaceConditionType = model::SurfaceConditionType<ModelType>;
using AvailableThermodynamicEquations = material::StellarEquilibriumThermodynamicEquations;
using ThermodynamicEquationsType =
material::CompiledThermodynamicEquationsT<EquationOfStateType, FormType, AvailableThermodynamicEquations>;
@@ -60,44 +142,22 @@ export namespace mean_field::equilibrium {
typename ThermodynamicEquationsType::PressureSurfaceFormulation,
EquationOfStateType>;
StellarEquilibriumProblem(
ModelType stellarModel,
const StellarDiscretization discretization
)
requires(!hasFixedCentralDensity)
: m_stellarModel(std::move(stellarModel)),
m_discretization(discretization),
m_compiledSurfaceConstraint(CompileSurfaceConstraint(m_stellarModel)),
m_preparedOperator(
m_discretization.finiteElementModel(),
m_discretization.domainMapper(),
m_stellarModel.template specification<eos::Polytrope>(),
models::compileConstraint(m_stellarModel.template specification<models::FixedTotalMass>()),
operators::PressureSurfaceConstraintView{m_compiledSurfaceConstraint},
CompileDefaultDomainDeformation(m_discretization.finiteElementModel())
) {
VerifyProblem();
}
private:
friend struct detail::StellarEquilibriumProblemFactory;
StellarEquilibriumProblem(
ModelType stellarModel,
const StellarDiscretization discretization
DiscretizationType discretization
)
requires hasFixedCentralDensity
: m_stellarModel(std::move(stellarModel)),
m_discretization(discretization),
m_compiledSurfaceConstraint(CompileSurfaceConstraint(m_stellarModel)),
: m_stellarModel(std::make_shared<ModelType>(std::move(stellarModel))),
m_discretization(std::move(discretization)),
m_compiledSurfaceConstraint(CompileSurfaceConstraint(*m_stellarModel)),
m_preparedOperator(
m_discretization.finiteElementModel(),
m_discretization.domainMapper(),
m_stellarModel.template specification<eos::Polytrope>(),
models::compileConstraint(m_stellarModel.template specification<models::FixedTotalMass>()),
m_stellarModel,
operators::PressureSurfaceConstraintView{m_compiledSurfaceConstraint},
CompileDefaultDomainDeformation(m_discretization.finiteElementModel()),
models::compileConstraint(
m_stellarModel.template specification<models::FixedCentralDensity>(),
m_stellarModel.template specification<eos::Polytrope>()
)
CompileDefaultDomainDeformation(m_discretization.finiteElementModel())
) {
VerifyProblem();
}
@@ -107,14 +167,19 @@ export namespace mean_field::equilibrium {
StellarEquilibriumProblem(StellarEquilibriumProblem &&) = delete;
StellarEquilibriumProblem &operator=(StellarEquilibriumProblem &&) = delete;
public:
[[nodiscard]] const ModelType &GetStellarModel() const noexcept {
return m_stellarModel;
return *m_stellarModel;
}
[[nodiscard]] const StellarDiscretization &GetDiscretization() const noexcept {
[[nodiscard]] const DiscretizationType &GetDiscretization() const noexcept {
return m_discretization;
}
[[nodiscard]] const NormalizationPrescriptionType &GetNormalizationPrescription() const noexcept {
return m_discretization.normalizationPrescription();
}
[[nodiscard]] const CompiledSurfaceConstraintType &GetCompiledSurfaceConstraint() const noexcept {
return m_compiledSurfaceConstraint;
}
@@ -127,6 +192,10 @@ export namespace mean_field::equilibrium {
return m_preparedOperator;
}
[[nodiscard]] const PhysicalCoreType &GetPhysicalOperator() const noexcept {
return m_preparedOperator.GetPhysicalOperator();
}
[[nodiscard]] const auto &GetManifest() const noexcept {
return m_preparedOperator.GetRootManifest();
}
@@ -135,28 +204,20 @@ export namespace mean_field::equilibrium {
return m_preparedOperator.IsPrepared();
}
[[nodiscard]] std::uint64_t GetPreparationGeneration() const noexcept {
return m_preparationGeneration;
}
[[nodiscard]] const operators::StellarEquilibriumDependencies &GetLinearizationDependencies() const {
if constexpr (hasFixedCentralDensity) {
return m_preparedOperator.GetPhysicalOperator().GetDependencies();
} else {
return m_preparedOperator.GetDependencies();
}
return GetPhysicalOperator().GetDependencies();
}
[[nodiscard]] const operators::StellarEquilibriumDependencyStamp &GetGeometryDependency() const {
if constexpr (hasFixedCentralDensity) {
return m_preparedOperator.GetPhysicalOperator().GetGeneratedDisplacementDependency();
} else {
return m_preparedOperator.GetGeneratedDisplacementDependency();
}
return GetPhysicalOperator().GetGeneratedDisplacementDependency();
}
[[nodiscard]] const field::FieldBoundaryDofMap &GetPressureSurfaceRows() const noexcept {
if constexpr (hasFixedCentralDensity) {
return m_preparedOperator.GetPhysicalOperator().GetSurfaceConstraintOperator().GetSurfaceRows();
} else {
return m_preparedOperator.GetSurfaceConstraintOperator().GetSurfaceRows();
}
return GetPhysicalOperator().GetSurfaceConstraintOperator().GetSurfaceRows();
}
[[nodiscard]] int StateSize() const noexcept {
@@ -175,8 +236,19 @@ export namespace mean_field::equilibrium {
const mfem::Vector &state,
const operators::StellarEquilibriumDependencies &dependencies,
const physics::RigidRotation &rotation
) {
return m_preparedOperator.Prepare(state, dependencies, rotation);
) requires(generatedRotationProviderCount == 0) {
auto report = m_preparedOperator.Prepare(state, dependencies, rotation);
++m_preparationGeneration;
return report;
}
[[nodiscard]] auto Prepare(
const mfem::Vector &state,
const operators::StellarEquilibriumDependencies &dependencies
) requires(generatedRotationProviderCount == 1) {
auto report = m_preparedOperator.Prepare(state, dependencies);
++m_preparationGeneration;
return report;
}
void BuildResidual(mfem::Vector &residual) const {
@@ -194,8 +266,8 @@ export namespace mean_field::equilibrium {
[[nodiscard]] static CompiledSurfaceConstraintType CompileSurfaceConstraint(const ModelType &stellarModel) {
return surface::compilePressureSurfaceConstraint<
typename ThermodynamicEquationsType::PressureSurfaceFormulation>(
stellarModel.template specification<surface::Isobaric>(),
stellarModel.template specification<EquationOfStateType>()
stellarModel.surfaceCondition(),
stellarModel.equationOfState()
);
}
@@ -224,34 +296,112 @@ export namespace mean_field::equilibrium {
MFEM_VERIFY(m_discretization.isCurrent(), "The stellar equilibrium problem has a stale discretization.");
}
ModelType m_stellarModel;
StellarDiscretization m_discretization;
std::shared_ptr<const ModelType> m_stellarModel;
DiscretizationType m_discretization;
CompiledSurfaceConstraintType m_compiledSurfaceConstraint;
PreparedOperatorType m_preparedOperator;
std::uint64_t m_preparationGeneration{0};
};
template <StellarEquilibriumModel Model>
template <typename Candidate> struct IsStellarEquilibriumProblem : std::false_type { };
template <StellarEquilibriumModel Model, StellarDiscretizationType Discretization>
requires detail::StellarEquilibriumModelDiscretizationStructureAudit<
std::remove_cvref_t<Model>,
std::remove_cvref_t<Discretization>>::value
struct IsStellarEquilibriumProblem<StellarEquilibriumProblem<Model, Discretization>> : std::true_type { };
template <typename Candidate>
concept DiscretizedStellarEquilibriumProblem = IsStellarEquilibriumProblem<std::remove_cvref_t<Candidate>>::value;
namespace detail {
template <
typename Model,
typename Discretization,
bool StructurallyCompatible =
StellarEquilibriumModelDiscretizationStructureAudit<
std::remove_cvref_t<Model>,
std::remove_cvref_t<Discretization>>::value>
struct StellarEquilibriumModelDiscretizationOperationAudit : std::false_type { };
template <typename Model, typename Discretization>
struct StellarEquilibriumModelDiscretizationOperationAudit<
Model,
Discretization,
true> {
private:
using ModelType = std::remove_cvref_t<Model>;
using DiscretizationType = std::remove_cvref_t<Discretization>;
using Problem = StellarEquilibriumProblem<ModelType, DiscretizationType>;
using Prescription = typename DiscretizationType::NormalizationPrescriptionType;
public:
static constexpr bool value = [] {
if constexpr (
std::same_as<Prescription, normalization::Unnormalized> ||
normalization::PhysicalRieszDiagonalPrescription<Prescription>) {
return true;
} else {
return normalization::RuntimePreparedNormalizationOperation<Problem>;
}
}();
};
} // namespace detail
/*
* A model and a discretization are separate compile-time choices. Their
* pairing is valid only when the normalization plan covers the inferred
* form, the selected physical runtime supports it, and a third-party
* runtime policy provides its exact preparation operation. Keeping this
* as a detection-safe public factory boundary rejects incomplete policies
* at discretize(), before a solver-facing problem can be constructed.
*/
template <typename Model, typename Discretization>
concept StellarEquilibriumModelDiscretizationCompatible =
detail::StellarEquilibriumModelDiscretizationOperationAudit<
std::remove_cvref_t<Model>,
std::remove_cvref_t<Discretization>>::value;
namespace detail {
/* The structurally formed problem type is needed to probe the ADL
* operation without a recursive concept. Its constructor remains
* private, and this factory is the single construction authority after
* the complete public compatibility contract has succeeded. */
struct StellarEquilibriumProblemFactory final {
template <StellarEquilibriumModel Model, StellarDiscretizationType Discretization>
requires StellarEquilibriumModelDiscretizationCompatible<Model, Discretization>
[[nodiscard]] static auto Create(
Model &&stellarModel,
Discretization discretization
) {
using ModelType = std::remove_cvref_t<Model>;
using DiscretizationType = std::remove_cvref_t<Discretization>;
return StellarEquilibriumProblem<ModelType, DiscretizationType>{
std::forward<Model>(stellarModel),
std::move(discretization)
};
}
};
} // namespace detail
template <StellarEquilibriumModel Model, StellarDiscretizationType Discretization>
requires StellarEquilibriumModelDiscretizationCompatible<Model, Discretization>
[[nodiscard]] auto discretize(
Model &&stellarModel,
const StellarDiscretization discretization
Discretization discretization
) {
using ModelType = std::remove_cvref_t<Model>;
return StellarEquilibriumProblem<ModelType>{std::forward<Model>(stellarModel), discretization};
return detail::StellarEquilibriumProblemFactory::Create(
std::forward<Model>(stellarModel),
std::move(discretization)
);
}
template <StellarEquilibriumModel Model>
requires StellarEquilibriumModelDiscretizationCompatible<Model, StellarDiscretization>
[[nodiscard]] auto discretize(
Model &&stellarModel,
fem::FEM &finiteElementModel
) {
return discretize(std::forward<Model>(stellarModel), StellarDiscretization{finiteElementModel});
}
template <typename Candidate> struct IsStellarEquilibriumProblem : std::false_type { };
template <StellarEquilibriumModel Model>
struct IsStellarEquilibriumProblem<StellarEquilibriumProblem<Model>> : std::true_type { };
template <typename Candidate>
concept DiscretizedStellarEquilibriumProblem = IsStellarEquilibriumProblem<std::remove_cvref_t<Candidate>>::value;
} // namespace mean_field::equilibrium