Files
MeanField/libmeanfield/interface/models/specifications.cppm

1554 lines
70 KiB
C++

module;
#include <array>
#include <cmath>
#include <compare>
#include <concepts>
#include <cstddef>
#include <format>
#include <span>
#include <stdexcept>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
export module mean_field:model.specifications;
export import :eos.polytrope;
export import :surface.constant;
export namespace mean_field::models {
enum class SpecificationRole {
constitutive_law,
boundary_condition,
invariant,
phase_condition,
gauge_choice,
rotation_law
};
/*
* The small declarations in this section are the physics-facing model
* extension API. A specification owns one nested ModelDefinition and the
* compiler projects the lower-level traits from it. Extension authors do
* not specialize a registry or choose a globally coordinated ordinal.
*/
template <std::size_t Extent> struct FixedString final {
char characters[Extent]{};
consteval FixedString(const char (&text)[Extent]) noexcept {
for (std::size_t index = 0; index < Extent; ++index) {
characters[index] = text[index];
}
}
[[nodiscard]] constexpr std::string_view view() const noexcept {
static_assert(Extent > 0);
return {characters, Extent - 1};
}
constexpr bool operator==(const FixedString &) const = default;
};
template <std::size_t Extent> FixedString(const char (&)[Extent]) -> FixedString<Extent>;
template <typename... Types> struct ModelTypeList final {
static constexpr std::size_t size = sizeof...(Types);
};
template <typename... ValueBlocks> using DependsOn = ModelTypeList<ValueBlocks...>;
template <typename... ResidualBlocks> using Affects = ModelTypeList<ResidualBlocks...>;
/*
* Physics vocabulary for declaring how a stellar specification couples to
* the equilibrium system. These names deliberately do not import the
* solver's block registry: the operator compiler translates them once at
* its backend boundary. Advanced extensions may still place an existing
* backend block type directly in DependsOn/Affects.
*/
namespace stellar {
namespace state {
struct Density final { };
struct SurfaceShape final { };
struct GravityGradient final { };
struct GravitationalPotential final { };
struct SpecificEnthalpy final { };
/*
* A coordinate generated by another named specification. This is
* the physics-facing spelling for coupled global constraints: an
* extension names the constraint it reads, never its solver block.
*/
template <typename Specification> struct GeneratedCoordinateOf final {
using SpecificationType = Specification;
};
/*
* The coordinate generated by the specification containing this
* marker. For example, FixedAngularMomentum uses it to state that
* its integral residual depends on angular velocity without naming
* a generated solver block.
*/
struct OwnGeneratedCoordinate final { };
} // namespace state
namespace equation {
struct GravityGradientDefinition final { };
struct PoissonEquation final { };
struct DensityClosure final { };
struct SurfaceShapeBalance final { };
struct HydrostaticBalance final { };
/* The scalar constraint equation owned by this specification. */
struct OwnConstraint final { };
/* The scalar constraint equation owned by another specification. */
template <typename Specification> struct ConstraintOf final {
using SpecificationType = Specification;
};
} // namespace equation
/*
* A readable, compile-time label for one declared Jacobian derivative.
* Runtime providers consume this vocabulary without learning backend
* row and column block types.
*/
template <typename Equation, typename State> struct Derivative final {
using EquationType = Equation;
using StateType = State;
};
} // namespace stellar
enum class GeneratedStateKind { none, multiplier, physical_coordinate, solver_border };
enum class RieszTopology {
unavailable,
identity,
scalar_volume_l2,
vector_volume_l2,
scalar_boundary_l2,
hybrid_scalar_volume_point_rows,
global_scalar
};
enum class PhysicalScaleLaw {
unavailable,
dimensionless,
density,
length,
acceleration,
inverse_time_squared,
specific_energy,
pressure,
mass,
force,
angular_velocity,
angular_momentum
};
/*
* PhysicalScaleLaw is the numerical scaling vocabulary, while the
* dimensions module carries the authoritative semantic quantity types.
* Keep their relationship in one extensible trait so a physics-facing
* scalar declaration cannot spell a quantity in one place and an
* unrelated normalization scale somewhere else.
*/
template <typename Quantity> struct PhysicalScaleForQuantity {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::unavailable;
};
template <> struct PhysicalScaleForQuantity<dimensions::quantity::Dimensionless> {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::dimensionless;
};
template <> struct PhysicalScaleForQuantity<dimensions::quantity::Mass> {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::mass;
};
template <> struct PhysicalScaleForQuantity<dimensions::quantity::Length> {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::length;
};
template <> struct PhysicalScaleForQuantity<dimensions::quantity::Density> {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::density;
};
template <> struct PhysicalScaleForQuantity<dimensions::quantity::Acceleration> {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::acceleration;
};
template <> struct PhysicalScaleForQuantity<dimensions::quantity::SpecificEnergy> {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::specific_energy;
};
template <> struct PhysicalScaleForQuantity<dimensions::quantity::SpecificEnthalpy> {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::specific_energy;
};
template <> struct PhysicalScaleForQuantity<dimensions::quantity::GravitationalPotential> {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::specific_energy;
};
template <> struct PhysicalScaleForQuantity<dimensions::quantity::Pressure> {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::pressure;
};
template <> struct PhysicalScaleForQuantity<dimensions::quantity::Force> {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::force;
};
template <> struct PhysicalScaleForQuantity<dimensions::quantity::AngularVelocity> {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::angular_velocity;
};
template <> struct PhysicalScaleForQuantity<dimensions::quantity::AngularMomentum> {
static constexpr PhysicalScaleLaw value = PhysicalScaleLaw::angular_momentum;
};
template <typename Quantity>
inline constexpr PhysicalScaleLaw physicalScaleForQuantity =
PhysicalScaleForQuantity<std::remove_cv_t<Quantity>>::value;
template <typename Quantity>
concept PhysicalScaleRepresentedQuantity =
dimensions::PhysicalQuantityType<Quantity> &&
physicalScaleForQuantity<Quantity> != PhysicalScaleLaw::unavailable &&
requires { typename std::bool_constant<!static_cast<std::string_view>(Quantity::identifier).empty()>; };
namespace detail {
template <typename Candidate> [[nodiscard]] consteval bool declaredCoordinateNormalizationIsAvailable() {
if constexpr (requires {
{ Candidate::available } -> std::convertible_to<bool>;
}) {
return static_cast<bool>(Candidate::available);
} else {
return false;
}
}
[[nodiscard]] consteval bool isKnownSpecificationRole(const SpecificationRole role) {
switch (role) {
case SpecificationRole::constitutive_law:
case SpecificationRole::boundary_condition:
case SpecificationRole::invariant:
case SpecificationRole::phase_condition:
case SpecificationRole::gauge_choice:
case SpecificationRole::rotation_law:
return true;
}
return false;
}
[[nodiscard]] consteval bool isKnownGeneratedStateKind(const GeneratedStateKind kind) {
switch (kind) {
case GeneratedStateKind::none:
case GeneratedStateKind::multiplier:
case GeneratedStateKind::physical_coordinate:
case GeneratedStateKind::solver_border:
return true;
}
return false;
}
[[nodiscard]] consteval bool specificationRoleAcceptsGeneratedStateKind(
const SpecificationRole role,
const GeneratedStateKind kind
) {
switch (role) {
case SpecificationRole::constitutive_law:
case SpecificationRole::boundary_condition:
return kind == GeneratedStateKind::none;
case SpecificationRole::invariant:
return kind == GeneratedStateKind::multiplier || kind == GeneratedStateKind::physical_coordinate;
case SpecificationRole::phase_condition:
case SpecificationRole::gauge_choice:
return kind == GeneratedStateKind::solver_border;
case SpecificationRole::rotation_law:
/*
* Rotation laws currently prescribe a physical profile; they
* do not own a root coordinate. Fixed angular momentum owns
* angular velocity as an invariant/physical-coordinate pair.
* Keep this closed until a state-generating rotation-law
* contract is designed and implemented end to end.
*/
return kind == GeneratedStateKind::none;
}
return false;
}
} // namespace detail
template <SpecificationRole Role, GeneratedStateKind StateKind>
concept CompatibleSpecificationRoleAndGeneratedState =
detail::isKnownSpecificationRole(Role) && detail::isKnownGeneratedStateKind(StateKind) &&
detail::specificationRoleAcceptsGeneratedStateKind(Role, StateKind);
template <RieszTopology Topology, PhysicalScaleLaw Scale> struct CoordinateNormalization final {
static constexpr RieszTopology topology = Topology;
static constexpr PhysicalScaleLaw scale = Scale;
static constexpr bool available =
topology != RieszTopology::unavailable && scale != PhysicalScaleLaw::unavailable;
};
using UnavailableCoordinateNormalization =
CoordinateNormalization<RieszTopology::unavailable, PhysicalScaleLaw::unavailable>;
template <
typename ValueNormalization = UnavailableCoordinateNormalization,
typename ResidualNormalization = UnavailableCoordinateNormalization>
struct GeneratedNormalization final {
using Value = ValueNormalization;
using Residual = ResidualNormalization;
static constexpr bool available = detail::declaredCoordinateNormalizationIsAvailable<Value>() &&
detail::declaredCoordinateNormalizationIsAvailable<Residual>();
};
using UnavailableGeneratedNormalization = GeneratedNormalization<>;
template <PhysicalScaleLaw ValueScale, PhysicalScaleLaw ResidualScale>
using GlobalScalarNormalization = GeneratedNormalization<
CoordinateNormalization<RieszTopology::global_scalar, ValueScale>,
CoordinateNormalization<RieszTopology::global_scalar, ResidualScale>>;
template <
FixedString ValueStableId = "",
FixedString ValueSymbol = "",
FixedString ResidualStableId = "",
FixedString ResidualSymbol = "",
FixedString TargetUnits = "",
FixedString ResidualUnits = "">
struct GeneratedManifest final {
private:
inline static constexpr auto valueStableIdStorage = ValueStableId;
inline static constexpr auto valueSymbolStorage = ValueSymbol;
inline static constexpr auto residualStableIdStorage = ResidualStableId;
inline static constexpr auto residualSymbolStorage = ResidualSymbol;
inline static constexpr auto targetUnitsStorage = TargetUnits;
inline static constexpr auto residualUnitsStorage = ResidualUnits;
public:
static constexpr std::string_view valueStableId = valueStableIdStorage.view();
static constexpr std::string_view valueSymbol = valueSymbolStorage.view();
static constexpr std::string_view residualStableId = residualStableIdStorage.view();
static constexpr std::string_view residualSymbol = residualSymbolStorage.view();
static constexpr std::string_view targetUnits = targetUnitsStorage.view();
static constexpr std::string_view residualUnits = residualUnitsStorage.view();
static constexpr bool available = !valueStableId.empty() && !valueSymbol.empty() && !residualStableId.empty() &&
!residualSymbol.empty() && !targetUnits.empty() && !residualUnits.empty();
};
using UnavailableGeneratedManifest = GeneratedManifest<>;
/*
* A scalar constraint has three independent dimensional statements:
*
* - the physical quantity supplied as its target;
* - the generated Newton coordinate; and
* - the appended scalar residual.
*
* They are deliberately not equated. FixedCentralDensity, for example,
* has a density target but a specific-enthalpy phase residual. The
* quantity types below generate both numerical scale laws and diagnostic
* unit labels, making the strings presentation rather than authority.
*/
template <
PhysicalScaleRepresentedQuantity TargetQuantityT,
PhysicalScaleRepresentedQuantity GeneratedCoordinateQuantityT,
PhysicalScaleRepresentedQuantity ConstraintResidualQuantityT,
FixedString ValueStableId,
FixedString ValueSymbol,
FixedString ResidualStableId,
FixedString ResidualSymbol>
struct DimensionalScalarConstraint final {
using TargetQuantity = TargetQuantityT;
using GeneratedCoordinateQuantity = GeneratedCoordinateQuantityT;
using ConstraintResidualQuantity = ConstraintResidualQuantityT;
using TargetValue = dimensions::QuantityValue<TargetQuantity>;
static constexpr PhysicalScaleLaw targetScale = physicalScaleForQuantity<TargetQuantity>;
struct Normalization final {
using TargetQuantity = TargetQuantityT;
using GeneratedCoordinateQuantity = GeneratedCoordinateQuantityT;
using ConstraintResidualQuantity = ConstraintResidualQuantityT;
using TargetValue = dimensions::QuantityValue<TargetQuantity>;
static constexpr PhysicalScaleLaw targetScale = physicalScaleForQuantity<TargetQuantity>;
using Value = CoordinateNormalization<
RieszTopology::global_scalar,
physicalScaleForQuantity<GeneratedCoordinateQuantity>>;
using Residual = CoordinateNormalization<
RieszTopology::global_scalar,
physicalScaleForQuantity<ConstraintResidualQuantity>>;
static constexpr bool available = Value::available && Residual::available;
};
struct Manifest final {
using TargetQuantity = TargetQuantityT;
using GeneratedCoordinateQuantity = GeneratedCoordinateQuantityT;
using ConstraintResidualQuantity = ConstraintResidualQuantityT;
private:
inline static constexpr auto valueStableIdStorage = ValueStableId;
inline static constexpr auto valueSymbolStorage = ValueSymbol;
inline static constexpr auto residualStableIdStorage = ResidualStableId;
inline static constexpr auto residualSymbolStorage = ResidualSymbol;
public:
static constexpr std::string_view valueStableId = valueStableIdStorage.view();
static constexpr std::string_view valueSymbol = valueSymbolStorage.view();
static constexpr std::string_view residualStableId = residualStableIdStorage.view();
static constexpr std::string_view residualSymbol = residualSymbolStorage.view();
static constexpr std::string_view targetUnits = TargetQuantity::identifier;
static constexpr std::string_view residualUnits = ConstraintResidualQuantity::identifier;
static constexpr bool available = !valueStableId.empty() && !valueSymbol.empty() &&
!residualStableId.empty() && !residualSymbol.empty() &&
!targetUnits.empty() && !residualUnits.empty();
};
static constexpr bool dimensionallyTyped = true;
};
template <
typename Specification,
FixedString StableName,
SpecificationRole Role,
GeneratedStateKind StateKind = GeneratedStateKind::none,
typename DependsOnBlocks = ModelTypeList<>,
typename AffectedResidualBlocks = ModelTypeList<>,
typename NormalizationDefinition = UnavailableGeneratedNormalization,
typename ManifestDefinition = UnavailableGeneratedManifest>
struct ModelDefinition final {
using SpecificationType = Specification;
using DependsOn = DependsOnBlocks;
using Affects = AffectedResidualBlocks;
using Normalization = NormalizationDefinition;
using Manifest = ManifestDefinition;
private:
inline static constexpr auto stableNameStorage = StableName;
public:
static constexpr std::string_view name = stableNameStorage.view();
static constexpr SpecificationRole role = Role;
static constexpr GeneratedStateKind generatedStateKind = StateKind;
static constexpr std::size_t generatedValueArity = StateKind == GeneratedStateKind::none ? 0U : 1U;
static constexpr std::size_t generatedResidualArity = StateKind == GeneratedStateKind::none ? 0U : 1U;
static constexpr bool structurallyAvailable = !name.empty();
};
template <typename Specification, FixedString Name>
using ConstitutiveLaw = ModelDefinition<Specification, Name, SpecificationRole::constitutive_law>;
template <typename Specification, FixedString Name>
using BoundaryCondition = ModelDefinition<Specification, Name, SpecificationRole::boundary_condition>;
template <
typename Specification,
FixedString Name,
typename DependsOn = ModelTypeList<>,
typename Affects = ModelTypeList<>,
typename Normalization = UnavailableGeneratedNormalization,
typename Manifest = UnavailableGeneratedManifest>
using FixedIntegralWithMultiplier = ModelDefinition<
Specification,
Name,
SpecificationRole::invariant,
GeneratedStateKind::multiplier,
DependsOn,
Affects,
Normalization,
Manifest>;
template <
typename Specification,
FixedString Name,
typename DependsOn = ModelTypeList<>,
typename Affects = ModelTypeList<>,
typename Normalization = UnavailableGeneratedNormalization,
typename Manifest = UnavailableGeneratedManifest>
using FixedIntegralWithPhysicalCoordinate = ModelDefinition<
Specification,
Name,
SpecificationRole::invariant,
GeneratedStateKind::physical_coordinate,
DependsOn,
Affects,
Normalization,
Manifest>;
template <
typename Specification,
FixedString Name,
typename DependsOn = ModelTypeList<>,
typename Affects = ModelTypeList<>,
typename Normalization = UnavailableGeneratedNormalization,
typename Manifest = UnavailableGeneratedManifest>
using PhaseCondition = ModelDefinition<
Specification,
Name,
SpecificationRole::phase_condition,
GeneratedStateKind::solver_border,
DependsOn,
Affects,
Normalization,
Manifest>;
struct SpecificationKey final {
SpecificationRole role;
GeneratedStateKind generatedStateKind;
std::string_view stableName;
constexpr auto operator<=>(const SpecificationKey &) const = default;
};
struct SpecificationDescriptor final {
std::string_view name;
SpecificationRole role;
SpecificationKey key;
std::size_t generatedValueArity;
std::size_t generatedResidualArity;
constexpr bool operator==(const SpecificationDescriptor &) const = default;
};
enum class EquilibriumSystemCompilation {
complete_equilibrium_system,
equation_contributions_only,
// Transitional spellings retained while internal solver code is
// migrated to physics-facing equilibrium-system terminology.
isolated_root = complete_equilibrium_system,
assembly_only = equation_contributions_only
};
using ModelCompilationClass = EquilibriumSystemCompilation;
struct RuntimeSpecificationDescriptor final {
SpecificationDescriptor specification;
std::size_t canonicalIndex;
// This reports only the self-owned physics declaration. Numerical
// support is queried from the operator compiler for the complete model.
bool hasDeclarativeDefinition;
constexpr bool operator==(const RuntimeSpecificationDescriptor &) const = default;
};
namespace detail {
template <typename Candidate> struct IsModelTypeList : std::false_type { };
template <typename... Types> struct IsModelTypeList<ModelTypeList<Types...>> : std::true_type { };
template <typename Candidate> struct IsModelDefinition : std::false_type { };
template <
typename Specification,
FixedString StableName,
SpecificationRole Role,
GeneratedStateKind StateKind,
typename DependsOn,
typename Affects,
typename Normalization,
typename Manifest>
struct IsModelDefinition<
ModelDefinition<Specification, StableName, Role, StateKind, DependsOn, Affects, Normalization, Manifest>>
: std::bool_constant<
(StableName.view().size() > 0) && CompatibleSpecificationRoleAndGeneratedState<Role, StateKind> &&
IsModelTypeList<DependsOn>::value && IsModelTypeList<Affects>::value> { };
template <typename Definition, typename Candidate, bool = IsModelDefinition<Definition>::value>
struct DefinitionDescribesCandidate : std::false_type { };
template <typename Definition, typename Candidate>
struct DefinitionDescribesCandidate<Definition, Candidate, true>
: std::bool_constant<std::same_as<typename Definition::SpecificationType, Candidate>> { };
template <typename Candidate, typename = void> struct SpecificationDefinitionFor {
static constexpr bool available = false;
};
template <typename Candidate>
struct SpecificationDefinitionFor<
Candidate,
std::void_t<typename std::remove_cvref_t<Candidate>::ModelDefinition>> {
using Type = typename std::remove_cvref_t<Candidate>::ModelDefinition;
static constexpr bool available = DefinitionDescribesCandidate<Type, std::remove_cvref_t<Candidate>>::value;
};
// Compatibility projections for the two physical types that predate
// the self-describing front end. New types use only ModelDefinition.
template <> struct SpecificationDefinitionFor<eos::Polytrope> {
using Type = ConstitutiveLaw<eos::Polytrope, "Polytrope">;
static constexpr bool available = true;
};
template <> struct SpecificationDefinitionFor<surface::ConstantPressureSurface> {
using Type = BoundaryCondition<surface::ConstantPressureSurface, "IsobaricSurface">;
static constexpr bool available = true;
};
} // namespace detail
template <typename Candidate>
concept SelfDescribingModelSpecification = requires {
typename std::remove_cvref_t<Candidate>::ModelDefinition;
} && detail::SpecificationDefinitionFor<std::remove_cvref_t<Candidate>>::available;
template <typename Candidate>
requires detail::SpecificationDefinitionFor<std::remove_cvref_t<Candidate>>::available
using ModelDefinitionForT = typename detail::SpecificationDefinitionFor<std::remove_cvref_t<Candidate>>::Type;
template <typename Candidate> struct SpecificationTraits;
template <typename Candidate>
requires detail::SpecificationDefinitionFor<std::remove_cvref_t<Candidate>>::available
struct SpecificationTraits<Candidate> {
using Definition = ModelDefinitionForT<Candidate>;
static constexpr std::string_view name = Definition::name;
static constexpr SpecificationRole role = Definition::role;
static constexpr SpecificationKey key{role, Definition::generatedStateKind, name};
};
template <typename Candidate>
concept ModelSpecification =
detail::SpecificationDefinitionFor<std::remove_cvref_t<Candidate>>::available &&
requires {
typename std::remove_cvref_t<Candidate>::Parameters;
{ SpecificationTraits<std::remove_cvref_t<Candidate>>::name } -> std::convertible_to<std::string_view>;
{ SpecificationTraits<std::remove_cvref_t<Candidate>>::role } -> std::convertible_to<SpecificationRole>;
{ SpecificationTraits<std::remove_cvref_t<Candidate>>::key } -> std::convertible_to<SpecificationKey>;
} &&
std::constructible_from<std::remove_cvref_t<Candidate>, typename std::remove_cvref_t<Candidate>::Parameters>;
class FixedTotalMass final {
public:
struct Parameters final {
dimensions::MassValue Mtotal;
};
using ScalarDescription = DimensionalScalarConstraint<
dimensions::quantity::Mass,
dimensions::quantity::SpecificEnergy,
dimensions::quantity::Mass,
"fixed_total_mass.multiplier",
"C",
"fixed_total_mass.residual",
"R_M">;
using TargetValue = typename ScalarDescription::TargetValue;
using ModelDefinition = FixedIntegralWithMultiplier<
FixedTotalMass,
"FixedTotalMass",
DependsOn<stellar::state::Density, stellar::state::SurfaceShape>,
Affects<stellar::equation::HydrostaticBalance>,
typename ScalarDescription::Normalization,
typename ScalarDescription::Manifest>;
explicit FixedTotalMass(const Parameters parameters) : FixedTotalMass(parameters.Mtotal) {
}
explicit FixedTotalMass(const TargetValue targetMass) : m_targetMass(targetMass) {
if (!std::isfinite(targetMass.value()) || targetMass.value() <= 0.0) {
throw std::invalid_argument(
std::format(
"The fixed total mass must be finite and positive. "
"Instead M = {} was provided.",
targetMass.value()
)
);
}
}
[[nodiscard]] TargetValue targetMass() const noexcept {
return m_targetMass;
}
[[nodiscard]] TargetValue target() const noexcept {
return m_targetMass;
}
private:
TargetValue m_targetMass;
};
class FixedAngularMomentum final {
public:
struct Parameters final {
dimensions::AngularMomentumValue Jtotal;
std::array<double, 3> axis{0.0, 0.0, 1.0};
std::array<double, 3> center{0.0, 0.0, 0.0};
};
using ScalarDescription = DimensionalScalarConstraint<
dimensions::quantity::AngularMomentum,
dimensions::quantity::AngularVelocity,
dimensions::quantity::AngularMomentum,
"fixed_angular_momentum.angular_velocity",
"Omega",
"fixed_angular_momentum.residual",
"R_J">;
using TargetValue = typename ScalarDescription::TargetValue;
using ModelDefinition = FixedIntegralWithPhysicalCoordinate<
FixedAngularMomentum,
"FixedAngularMomentum",
DependsOn<stellar::state::Density, stellar::state::SurfaceShape, stellar::state::OwnGeneratedCoordinate>,
Affects<stellar::equation::SurfaceShapeBalance, stellar::equation::HydrostaticBalance>,
typename ScalarDescription::Normalization,
typename ScalarDescription::Manifest>;
explicit FixedAngularMomentum(const Parameters parameters)
: m_targetAngularMomentum(parameters.Jtotal),
m_axis(parameters.axis),
m_center(parameters.center) {
if (!std::isfinite(m_targetAngularMomentum.value()) || m_targetAngularMomentum.value() < 0.0) {
throw std::invalid_argument(
std::format(
"The fixed total angular momentum must be finite and "
"nonnegative. Instead J = {} was "
"provided.",
m_targetAngularMomentum.value()
)
);
}
double axisNormSquared = 0.0;
for (std::size_t component = 0; component < m_axis.size(); ++component) {
if (!std::isfinite(m_axis[component]) || !std::isfinite(m_center[component])) {
throw std::invalid_argument(
"A fixed-angular-momentum rotation axis and center must contain "
"only finite values."
);
}
axisNormSquared += m_axis[component] * m_axis[component];
}
if (!std::isfinite(axisNormSquared) || axisNormSquared <= 0.0) {
throw std::invalid_argument("A fixed-angular-momentum rotation axis must be nonzero.");
}
const double inverseAxisNorm = 1.0 / std::sqrt(axisNormSquared);
for (double &component : m_axis) {
component *= inverseAxisNorm;
}
}
explicit FixedAngularMomentum(const TargetValue targetAngularMomentum)
: FixedAngularMomentum(Parameters{.Jtotal = targetAngularMomentum}) {
}
[[nodiscard]] TargetValue targetAngularMomentum() const noexcept {
return m_targetAngularMomentum;
}
[[nodiscard]] TargetValue target() const noexcept {
return m_targetAngularMomentum;
}
[[nodiscard]] const std::array<
double,
3> &
axis() const noexcept {
return m_axis;
}
[[nodiscard]] const std::array<
double,
3> &
center() const noexcept {
return m_center;
}
private:
TargetValue m_targetAngularMomentum;
std::array<double, 3> m_axis;
std::array<double, 3> m_center;
};
class FixedCentralDensity final {
public:
struct Parameters final {
dimensions::DensityValue RhoC;
};
using ScalarDescription = DimensionalScalarConstraint<
dimensions::quantity::Density,
dimensions::quantity::SpecificEnthalpy,
dimensions::quantity::SpecificEnthalpy,
"fixed_central_density.border",
"lambda_rho_c",
"fixed_central_density.residual",
"R_rho_c">;
using TargetValue = typename ScalarDescription::TargetValue;
using ModelDefinition = PhaseCondition<
FixedCentralDensity,
"FixedCentralDensity",
DependsOn<stellar::state::SpecificEnthalpy>,
Affects<stellar::equation::HydrostaticBalance>,
typename ScalarDescription::Normalization,
typename ScalarDescription::Manifest>;
explicit FixedCentralDensity(const Parameters parameters) : FixedCentralDensity(parameters.RhoC) {
}
explicit FixedCentralDensity(const TargetValue targetDensity) : m_targetDensity(targetDensity) {
if (!std::isfinite(targetDensity.value()) || targetDensity.value() <= 0.0) {
throw std::invalid_argument(
std::format(
"The fixed central density must be finite and positive. "
"Instead rho_c = {} was provided.",
targetDensity.value()
)
);
}
}
[[nodiscard]] TargetValue targetDensity() const noexcept {
return m_targetDensity;
}
[[nodiscard]] TargetValue target() const noexcept {
return m_targetDensity;
}
private:
TargetValue m_targetDensity;
};
template <typename Query, typename List> struct ModelTypeListContains;
template <typename Query, typename... Types>
struct ModelTypeListContains<Query, ModelTypeList<Types...>>
: std::bool_constant<(std::same_as<Query, Types> || ...)> { };
template <typename Query, typename List>
inline constexpr bool modelTypeListContains = ModelTypeListContains<Query, List>::value;
template <typename Specification> struct ResidualFor final {
using SpecificationType = Specification;
static constexpr std::size_t scalarArity = 1;
};
template <typename Specification> struct MultiplierFor final {
using SpecificationType = Specification;
static constexpr std::size_t scalarArity = 1;
};
// A generated state variable that participates directly in the physical
// equations, rather than serving only as a Lagrange multiplier or border.
template <typename Specification> struct PhysicalCoordinateFor final {
using SpecificationType = Specification;
static constexpr std::size_t scalarArity = 1;
};
template <typename Specification> struct BorderFor final {
using SpecificationType = Specification;
static constexpr std::size_t scalarArity = 1;
};
template <typename Candidate>
concept CoordinateNormalizationDefinition = requires {
{ Candidate::topology } -> std::convertible_to<RieszTopology>;
{ Candidate::scale } -> std::convertible_to<PhysicalScaleLaw>;
{ Candidate::available } -> std::convertible_to<bool>;
};
template <typename Candidate>
concept GeneratedNormalizationDefinition = requires {
typename Candidate::Value;
typename Candidate::Residual;
requires CoordinateNormalizationDefinition<typename Candidate::Value>;
requires CoordinateNormalizationDefinition<typename Candidate::Residual>;
{ Candidate::available } -> std::convertible_to<bool>;
};
template <typename Candidate>
concept GeneratedManifestDefinition = requires {
{ Candidate::valueStableId } -> std::convertible_to<std::string_view>;
{ Candidate::valueSymbol } -> std::convertible_to<std::string_view>;
{ Candidate::residualStableId } -> std::convertible_to<std::string_view>;
{ Candidate::residualSymbol } -> std::convertible_to<std::string_view>;
{ Candidate::targetUnits } -> std::convertible_to<std::string_view>;
{ Candidate::residualUnits } -> std::convertible_to<std::string_view>;
{ Candidate::available } -> std::convertible_to<bool>;
};
namespace detail {
template <typename Specification, GeneratedStateKind Kind> struct GeneratedSignatureFor;
template <typename Specification> struct GeneratedSignatureFor<Specification, GeneratedStateKind::none> {
using Values = ModelTypeList<>;
using Residuals = ModelTypeList<>;
};
template <typename Specification> struct GeneratedSignatureFor<Specification, GeneratedStateKind::multiplier> {
using Values = ModelTypeList<MultiplierFor<Specification>>;
using Residuals = ModelTypeList<ResidualFor<Specification>>;
};
template <typename Specification>
struct GeneratedSignatureFor<Specification, GeneratedStateKind::physical_coordinate> {
using Values = ModelTypeList<PhysicalCoordinateFor<Specification>>;
using Residuals = ModelTypeList<ResidualFor<Specification>>;
};
template <typename Specification>
struct GeneratedSignatureFor<Specification, GeneratedStateKind::solver_border> {
using Values = ModelTypeList<BorderFor<Specification>>;
using Residuals = ModelTypeList<ResidualFor<Specification>>;
};
template <typename Candidate> struct SafeGeneratedNormalization {
using Type = UnavailableGeneratedNormalization;
};
template <GeneratedNormalizationDefinition Candidate> struct SafeGeneratedNormalization<Candidate> {
using Type = Candidate;
};
template <typename Candidate> struct SafeGeneratedManifest {
using Type = UnavailableGeneratedManifest;
};
template <GeneratedManifestDefinition Candidate> struct SafeGeneratedManifest<Candidate> {
using Type = Candidate;
};
} // namespace detail
template <ModelSpecification Specification> struct SpecificationContribution {
private:
using CanonicalSpecification = std::remove_cvref_t<Specification>;
using Definition = ModelDefinitionForT<CanonicalSpecification>;
using Signature = detail::GeneratedSignatureFor<CanonicalSpecification, Definition::generatedStateKind>;
public:
using SpecificationType = CanonicalSpecification;
using ModelDefinition = Definition;
using GeneratedValues = typename Signature::Values;
using GeneratedResiduals = typename Signature::Residuals;
using DependsOn = typename Definition::DependsOn;
using Affects = typename Definition::Affects;
using Normalization = typename detail::SafeGeneratedNormalization<typename Definition::Normalization>::Type;
using Manifest = typename detail::SafeGeneratedManifest<typename Definition::Manifest>::Type;
static constexpr GeneratedStateKind generatedStateKind = Definition::generatedStateKind;
static constexpr std::size_t generatedValueArity = Definition::generatedValueArity;
static constexpr std::size_t generatedResidualArity = Definition::generatedResidualArity;
static constexpr bool isDefined = Definition::structurallyAvailable;
static constexpr bool hasDeclarativeDefinition = Definition::structurallyAvailable;
};
namespace detail {
template <ModelSpecification Specification>
[[nodiscard]] consteval bool generatedScalarDimensionsAreCoherent() {
using Contribution = SpecificationContribution<Specification>;
using Normalization = typename Contribution::Normalization;
using Manifest = typename Contribution::Manifest;
if constexpr (Contribution::generatedValueArity == 0) {
return true;
} else {
constexpr bool normalizationIsTyped = requires {
typename Normalization::TargetQuantity;
typename Normalization::GeneratedCoordinateQuantity;
typename Normalization::ConstraintResidualQuantity;
typename Normalization::TargetValue;
{ Normalization::targetScale } -> std::convertible_to<PhysicalScaleLaw>;
};
constexpr bool manifestIsTyped = requires {
typename Manifest::TargetQuantity;
typename Manifest::GeneratedCoordinateQuantity;
typename Manifest::ConstraintResidualQuantity;
};
/* The lower-level declaration API remains a deliberate
* compatibility escape hatch. Once either half opts into the
* dimensional protocol, however, the complete typed contract
* is mandatory and cannot be mixed with free-form metadata. */
if constexpr (!normalizationIsTyped && !manifestIsTyped) {
return true;
} else if constexpr (!normalizationIsTyped || !manifestIsTyped) {
return false;
} else {
using TargetQuantity = typename Normalization::TargetQuantity;
using GeneratedCoordinateQuantity = typename Normalization::GeneratedCoordinateQuantity;
using ConstraintResidualQuantity = typename Normalization::ConstraintResidualQuantity;
using ValueNormalization = typename Normalization::Value;
using ResidualNormalization = typename Normalization::Residual;
if constexpr (
!PhysicalScaleRepresentedQuantity<TargetQuantity> ||
!PhysicalScaleRepresentedQuantity<GeneratedCoordinateQuantity> ||
!PhysicalScaleRepresentedQuantity<ConstraintResidualQuantity>
) {
return false;
} else if constexpr (
!requires {
typename std::integral_constant<
PhysicalScaleLaw, static_cast<PhysicalScaleLaw>(Normalization::targetScale)>;
typename std::integral_constant<
PhysicalScaleLaw, static_cast<PhysicalScaleLaw>(ValueNormalization::scale)>;
typename std::integral_constant<
PhysicalScaleLaw, static_cast<PhysicalScaleLaw>(ResidualNormalization::scale)>;
typename std::bool_constant<
static_cast<std::string_view>(Manifest::targetUnits) == TargetQuantity::identifier>;
typename std::bool_constant<
static_cast<std::string_view>(Manifest::residualUnits) ==
ConstraintResidualQuantity::identifier>;
}
) {
return false;
} else if constexpr (!requires(const Specification &specification) { specification.target(); }) {
return false;
} else {
return std::same_as<
typename Normalization::TargetValue, dimensions::QuantityValue<TargetQuantity>> &&
Normalization::targetScale == physicalScaleForQuantity<TargetQuantity> &&
std::same_as<TargetQuantity, typename Manifest::TargetQuantity> &&
std::same_as<
GeneratedCoordinateQuantity, typename Manifest::GeneratedCoordinateQuantity> &&
std::same_as<
ConstraintResidualQuantity, typename Manifest::ConstraintResidualQuantity> &&
ValueNormalization::scale == physicalScaleForQuantity<GeneratedCoordinateQuantity> &&
ResidualNormalization::scale == physicalScaleForQuantity<ConstraintResidualQuantity> &&
static_cast<std::string_view>(Manifest::targetUnits) == TargetQuantity::identifier &&
static_cast<std::string_view>(Manifest::residualUnits) ==
ConstraintResidualQuantity::identifier &&
std::same_as<
std::remove_cvref_t<decltype(std::declval<const Specification &>().target())>,
typename Normalization::TargetValue>;
}
}
}
}
} // namespace detail
template <typename Specification>
concept CompleteGeneratedScalarDimensionsFor =
ModelSpecification<Specification> &&
detail::generatedScalarDimensionsAreCoherent<std::remove_cvref_t<Specification>>();
template <typename Specification>
concept CompleteGeneratedNormalizationFor =
ModelSpecification<Specification> && CompleteGeneratedScalarDimensionsFor<Specification> &&
(SpecificationContribution<std::remove_cvref_t<Specification>>::generatedValueArity == 0 ||
SpecificationContribution<std::remove_cvref_t<Specification>>::Normalization::available);
template <typename Specification>
concept CompleteGeneratedManifestFor =
ModelSpecification<Specification> && CompleteGeneratedScalarDimensionsFor<Specification> &&
(SpecificationContribution<std::remove_cvref_t<Specification>>::generatedValueArity == 0 ||
SpecificationContribution<std::remove_cvref_t<Specification>>::Manifest::available);
template <typename Candidate>
concept ResolvedModelSpecification =
ModelSpecification<Candidate> && SpecificationContribution<std::remove_cvref_t<Candidate>>::isDefined;
namespace detail {
template <typename... Specifications> struct SpecificationSetStorage final {
static constexpr std::size_t size = sizeof...(Specifications);
};
template <typename... Lists> struct ConcatenateModelTypeLists;
template <> struct ConcatenateModelTypeLists<> {
using Type = ModelTypeList<>;
};
template <typename... Types> struct ConcatenateModelTypeLists<ModelTypeList<Types...>> {
using Type = ModelTypeList<Types...>;
};
template <typename... First, typename... Second, typename... Remaining>
struct ConcatenateModelTypeLists<ModelTypeList<First...>, ModelTypeList<Second...>, Remaining...> {
using Type = typename ConcatenateModelTypeLists<ModelTypeList<First..., Second...>, Remaining...>::Type;
};
template <SpecificationRole Role, typename SpecificationSet> struct SpecificationsForRole {
using Type = ModelTypeList<>;
static constexpr bool available = false;
static constexpr std::size_t count = 0;
};
template <SpecificationRole Role, ModelSpecification... Specifications>
struct SpecificationsForRole<Role, SpecificationSetStorage<Specifications...>> {
using Type = typename ConcatenateModelTypeLists<std::conditional_t<
SpecificationTraits<Specifications>::role == Role,
ModelTypeList<Specifications>,
ModelTypeList<>>...>::Type;
static constexpr bool available = true;
static constexpr std::size_t count = Type::size;
};
template <typename Types> struct UniqueModelType;
template <typename Type> struct UniqueModelType<ModelTypeList<Type>> {
using TypeValue = Type;
};
template <ModelSpecification Specification, typename Set> struct InsertSpecification;
template <ModelSpecification Specification>
struct InsertSpecification<Specification, SpecificationSetStorage<>> {
using Type = SpecificationSetStorage<Specification>;
};
template <ModelSpecification Specification, ModelSpecification Head, ModelSpecification... Tail>
struct InsertSpecification<Specification, SpecificationSetStorage<Head, Tail...>> {
private:
using InsertedTail = typename InsertSpecification<Specification, SpecificationSetStorage<Tail...>>::Type;
template <typename First, typename Rest> struct PrependSpecification;
template <typename First, ModelSpecification... Rest>
struct PrependSpecification<First, SpecificationSetStorage<Rest...>> {
using Type = SpecificationSetStorage<First, Rest...>;
};
public:
using Type = std::conditional_t<
(SpecificationTraits<Specification>::key < SpecificationTraits<Head>::key),
SpecificationSetStorage<Specification, Head, Tail...>,
typename PrependSpecification<Head, InsertedTail>::Type>;
};
template <typename Set, ModelSpecification... Specifications> struct CanonicalizeSpecifications;
template <typename Set> struct CanonicalizeSpecifications<Set> {
using Type = Set;
};
template <typename Set, ModelSpecification Head, ModelSpecification... Tail>
struct CanonicalizeSpecifications<Set, Head, Tail...> {
using Inserted = typename InsertSpecification<Head, Set>::Type;
using Type = typename CanonicalizeSpecifications<Inserted, Tail...>::Type;
};
template <ModelSpecification... Specifications>
using CanonicalSpecificationSet =
typename CanonicalizeSpecifications<SpecificationSetStorage<>, Specifications...>::Type;
template <
ModelSpecification Head,
ModelSpecification... Tail>
consteval bool specificationKeyIsUnique() {
constexpr auto headKey = SpecificationTraits<Head>::key;
return (
(headKey.role != SpecificationTraits<Tail>::key.role ||
headKey.stableName != SpecificationTraits<Tail>::key.stableName) &&
...
);
}
template <ModelSpecification... Specifications> struct SpecificationKeysAreUnique;
template <> struct SpecificationKeysAreUnique<> : std::true_type { };
template <ModelSpecification Head, ModelSpecification... Tail>
struct SpecificationKeysAreUnique<Head, Tail...>
: std::bool_constant<
specificationKeyIsUnique<Head, Tail...>() && SpecificationKeysAreUnique<Tail...>::value> { };
template <SpecificationRole Role, ModelSpecification... Specifications>
inline constexpr std::size_t specificationRoleCount =
(std::size_t{0} + ... + (SpecificationTraits<Specifications>::role == Role ? 1 : 0));
template <typename List> struct ModelTypeListScalarArity;
template <typename... Types>
struct ModelTypeListScalarArity<ModelTypeList<Types...>>
: std::integral_constant<std::size_t, (std::size_t{0} + ... + Types::scalarArity)> { };
template <typename Query, typename... Types>
inline constexpr bool isOneOf = (std::same_as<Query, Types> || ...);
template <typename Query, typename... Types>
inline constexpr std::size_t typeCount =
(std::size_t{0} + ... +
(std::same_as<Query, std::remove_cvref_t<Types>> ? std::size_t{1} : std::size_t{0}));
template <typename CanonicalSet, typename... Arguments> struct ArgumentsMatchCanonicalSpecifications;
template <ModelSpecification... CanonicalSpecifications, typename... Arguments>
struct ArgumentsMatchCanonicalSpecifications<SpecificationSetStorage<CanonicalSpecifications...>, Arguments...>
: std::bool_constant<
sizeof...(CanonicalSpecifications) == sizeof...(Arguments) &&
(isOneOf<std::remove_cvref_t<Arguments>, CanonicalSpecifications...> && ...) &&
((typeCount<CanonicalSpecifications, Arguments...> == 1) && ...)> { };
} // namespace detail
template <typename... Specifications>
requires(ModelSpecification<std::remove_cvref_t<Specifications>> && ...)
inline constexpr bool specificationKeysAreUnique =
detail::SpecificationKeysAreUnique<std::remove_cvref_t<Specifications>...>::value;
template <typename... Specifications>
concept ValidModelSpecificationPack =
(ResolvedModelSpecification<std::remove_cvref_t<Specifications>> && ...) &&
specificationKeysAreUnique<std::remove_cvref_t<Specifications>...> &&
detail::specificationRoleCount<SpecificationRole::constitutive_law, std::remove_cvref_t<Specifications>...> ==
1;
template <typename... Specifications>
requires(ModelSpecification<std::remove_cvref_t<Specifications>> && ...) &&
specificationKeysAreUnique<std::remove_cvref_t<Specifications>...>
using SpecificationSet = detail::CanonicalSpecificationSet<std::remove_cvref_t<Specifications>...>;
template <SpecificationRole Role, typename SpecificationSet>
using SpecificationsForRoleT =
typename detail::SpecificationsForRole<Role, std::remove_cvref_t<SpecificationSet>>::Type;
template <SpecificationRole Role, typename SpecificationSet>
inline constexpr std::size_t specificationRoleCount =
detail::SpecificationsForRole<Role, std::remove_cvref_t<SpecificationSet>>::count;
template <SpecificationRole Role, typename SpecificationSet>
concept HasSpecificationsForRole =
detail::SpecificationsForRole<Role, std::remove_cvref_t<SpecificationSet>>::available &&
specificationRoleCount<Role, SpecificationSet> > 0;
template <SpecificationRole Role, typename SpecificationSet>
concept HasUniqueSpecificationForRole =
detail::SpecificationsForRole<Role, std::remove_cvref_t<SpecificationSet>>::available &&
specificationRoleCount<Role, SpecificationSet> == 1;
template <SpecificationRole Role, typename SpecificationSet>
requires HasUniqueSpecificationForRole<Role, SpecificationSet>
using SpecificationForRoleT =
typename detail::UniqueModelType<SpecificationsForRoleT<Role, SpecificationSet>>::TypeValue;
template <typename SpecificationSet> struct SpecificationOperatorSignature;
template <ModelSpecification... Specifications>
struct SpecificationOperatorSignature<detail::SpecificationSetStorage<Specifications...>> final {
using GeneratedValues = typename detail::ConcatenateModelTypeLists<
typename SpecificationContribution<Specifications>::GeneratedValues...>::Type;
using GeneratedResiduals = typename detail::ConcatenateModelTypeLists<
typename SpecificationContribution<Specifications>::GeneratedResiduals...>::Type;
static constexpr std::size_t generatedValueArity = detail::ModelTypeListScalarArity<GeneratedValues>::value;
static constexpr std::size_t generatedResidualArity =
detail::ModelTypeListScalarArity<GeneratedResiduals>::value;
static constexpr bool symbolicallySquare = generatedValueArity == generatedResidualArity;
};
template <ResolvedModelSpecification Specification>
[[nodiscard]] consteval SpecificationDescriptor specificationDescriptor() {
using Contribution = SpecificationContribution<Specification>;
return {
.name = SpecificationTraits<Specification>::name,
.role = SpecificationTraits<Specification>::role,
.key = SpecificationTraits<Specification>::key,
.generatedValueArity = detail::ModelTypeListScalarArity<typename Contribution::GeneratedValues>::value,
.generatedResidualArity = detail::ModelTypeListScalarArity<typename Contribution::GeneratedResiduals>::value
};
}
namespace detail {
template <typename Specifications> class SpecifiedModel;
template <ModelSpecification... Specifications>
class SpecifiedModel<SpecificationSetStorage<Specifications...>> final {
private:
struct CanonicalArgumentsTag final { };
/*
* Capture the user-spelled pack once, then move each exact type
* into its canonical slot. Reconstructing an argument tuple in
* the pack expansion would forward every argument once per
* specification and silently consume move-sensitive physics
* objects multiple times.
*/
template <typename... ArgumentTypes>
explicit SpecifiedModel(
std::tuple<ArgumentTypes...> &&arguments,
CanonicalArgumentsTag
)
: m_specifications(std::get<Specifications>(std::move(arguments))...) {
}
public:
using SpecificationTypes = SpecificationSetStorage<Specifications...>;
using OperatorSignature = SpecificationOperatorSignature<SpecificationTypes>;
static constexpr bool symbolicallySquare = OperatorSignature::symbolicallySquare;
// A declaration can be complete before every physics name has a
// backend block mapping. Keep this deliberately separate from
// operators::StellarEquilibriumSystemCompilable<Model>.
static constexpr bool hasCompleteEquilibriumDeclaration =
symbolicallySquare && (SpecificationContribution<Specifications>::hasDeclarativeDefinition && ...);
template <typename... Arguments>
requires ArgumentsMatchCanonicalSpecifications<
SpecificationTypes,
Arguments...>::value &&
std::constructible_from<
std::tuple<std::remove_cvref_t<Arguments>...>,
Arguments...> &&
(std::constructible_from<
Specifications,
Specifications &&> &&
...)
explicit SpecifiedModel(Arguments &&...arguments)
: SpecifiedModel(
std::tuple<std::remove_cvref_t<Arguments>...>{std::forward<Arguments>(arguments)...},
CanonicalArgumentsTag{}
) {
}
template <ModelSpecification Specification>
requires isOneOf<
Specification,
Specifications...>
[[nodiscard]] const Specification &specification() const noexcept {
return std::get<Specification>(m_specifications);
}
template <ModelSpecification Specification>
static constexpr bool containsSpecification = isOneOf<Specification, Specifications...>;
[[nodiscard]] static constexpr std::span<const RuntimeSpecificationDescriptor>
runtimeSpecificationDescriptors() noexcept {
return runtimeDescriptors;
}
private:
inline static constexpr std::array<RuntimeSpecificationDescriptor, sizeof...(Specifications)>
runtimeDescriptors = [] {
std::array<RuntimeSpecificationDescriptor, sizeof...(Specifications)> descriptors{};
std::size_t index = 0;
((descriptors[index] =
{.specification = specificationDescriptor<Specifications>(),
.canonicalIndex = index,
.hasDeclarativeDefinition =
SpecificationContribution<Specifications>::hasDeclarativeDefinition},
++index),
...);
return descriptors;
}();
std::tuple<Specifications...> m_specifications;
};
} // namespace detail
template <ModelSpecification... Specifications>
requires ValidModelSpecificationPack<Specifications...> &&
SpecificationOperatorSignature<SpecificationSet<Specifications...>>::symbolicallySquare
using Model = detail::SpecifiedModel<SpecificationSet<Specifications...>>;
template <typename Candidate>
concept SpecifiedModelType = requires {
typename std::remove_cvref_t<Candidate>::SpecificationTypes;
typename std::remove_cvref_t<Candidate>::OperatorSignature;
requires std::remove_cvref_t<Candidate>::symbolicallySquare;
{ std::remove_cvref_t<Candidate>::hasCompleteEquilibriumDeclaration } -> std::convertible_to<bool>;
{
std::remove_cvref_t<Candidate>::runtimeSpecificationDescriptors()
} -> std::same_as<std::span<const RuntimeSpecificationDescriptor>>;
};
static_assert(ModelSpecification<eos::Polytrope>);
static_assert(ModelSpecification<surface::ConstantPressureSurface>);
static_assert(ModelSpecification<FixedTotalMass>);
static_assert(ModelSpecification<FixedAngularMomentum>);
static_assert(ModelSpecification<FixedCentralDensity>);
static_assert(ResolvedModelSpecification<eos::Polytrope>);
static_assert(ResolvedModelSpecification<surface::ConstantPressureSurface>);
static_assert(ResolvedModelSpecification<FixedTotalMass>);
static_assert(ResolvedModelSpecification<FixedAngularMomentum>);
static_assert(ResolvedModelSpecification<FixedCentralDensity>);
static_assert(CompleteGeneratedScalarDimensionsFor<FixedTotalMass>);
static_assert(CompleteGeneratedScalarDimensionsFor<FixedAngularMomentum>);
static_assert(CompleteGeneratedScalarDimensionsFor<FixedCentralDensity>);
} // namespace mean_field::models
/*
* Astronomer-facing declaration vocabulary. These aliases deliberately
* package the backend model lists, normalization topology, and manifest pair
* without duplicating any compiler logic. Advanced code may still use the
* models:: spellings directly; both paths produce exactly the same types.
*/
export namespace mean_field::stellar {
namespace state {
using Density = models::stellar::state::Density;
using SurfaceShape = models::stellar::state::SurfaceShape;
using GravityGradient = models::stellar::state::GravityGradient;
using GravitationalPotential = models::stellar::state::GravitationalPotential;
using SpecificEnthalpy = models::stellar::state::SpecificEnthalpy;
using OwnGeneratedCoordinate = models::stellar::state::OwnGeneratedCoordinate;
template <typename Specification>
using GeneratedCoordinateOf = models::stellar::state::GeneratedCoordinateOf<Specification>;
} // namespace state
namespace equation {
using GravityGradientDefinition = models::stellar::equation::GravityGradientDefinition;
using PoissonEquation = models::stellar::equation::PoissonEquation;
using DensityClosure = models::stellar::equation::DensityClosure;
using SurfaceShapeBalance = models::stellar::equation::SurfaceShapeBalance;
using HydrostaticBalance = models::stellar::equation::HydrostaticBalance;
using OwnConstraint = models::stellar::equation::OwnConstraint;
template <typename Specification> using ConstraintOf = models::stellar::equation::ConstraintOf<Specification>;
} // namespace equation
template <typename Equation, typename State> using Derivative = models::stellar::Derivative<Equation, State>;
template <typename... Quantities> using Reads = models::DependsOn<Quantities...>;
template <typename... Equations> using Changes = models::Affects<Equations...>;
using PhysicalScale = models::PhysicalScaleLaw;
template <
models::PhysicalScaleRepresentedQuantity TargetQuantity,
models::PhysicalScaleRepresentedQuantity GeneratedCoordinateQuantity,
models::PhysicalScaleRepresentedQuantity ConstraintResidualQuantity,
models::FixedString ValueStableId,
models::FixedString ValueSymbol,
models::FixedString ResidualStableId,
models::FixedString ResidualSymbol>
using ScalarConstraint = models::DimensionalScalarConstraint<
TargetQuantity,
GeneratedCoordinateQuantity,
ConstraintResidualQuantity,
ValueStableId,
ValueSymbol,
ResidualStableId,
ResidualSymbol>;
template <typename Candidate>
concept ScalarConstraintDescription = requires {
typename std::remove_cvref_t<Candidate>::Normalization;
typename std::remove_cvref_t<Candidate>::Manifest;
typename std::remove_cvref_t<Candidate>::TargetQuantity;
typename std::remove_cvref_t<Candidate>::GeneratedCoordinateQuantity;
typename std::remove_cvref_t<Candidate>::ConstraintResidualQuantity;
typename std::remove_cvref_t<Candidate>::TargetValue;
requires models::GeneratedNormalizationDefinition<typename std::remove_cvref_t<Candidate>::Normalization>;
requires models::GeneratedManifestDefinition<typename std::remove_cvref_t<Candidate>::Manifest>;
requires std::remove_cvref_t<Candidate>::Normalization::available;
requires std::remove_cvref_t<Candidate>::Manifest::available;
requires std::remove_cvref_t<Candidate>::dimensionallyTyped;
};
} // namespace mean_field::stellar
export namespace mean_field::integral {
using FixedTotalMass = models::FixedTotalMass;
using FixedAngularMomentum = models::FixedAngularMomentum;
template <
typename Specification,
models::FixedString Name,
typename DependsOn = models::ModelTypeList<>,
typename Affects = models::ModelTypeList<>,
typename Normalization = models::UnavailableGeneratedNormalization,
typename Manifest = models::UnavailableGeneratedManifest>
using FixedIntegralWithMultiplier =
models::FixedIntegralWithMultiplier<Specification, Name, DependsOn, Affects, Normalization, Manifest>;
template <
typename Specification,
models::FixedString Name,
typename DependsOn = models::ModelTypeList<>,
typename Affects = models::ModelTypeList<>,
typename Normalization = models::UnavailableGeneratedNormalization,
typename Manifest = models::UnavailableGeneratedManifest>
using FixedWithMultiplier =
FixedIntegralWithMultiplier<Specification, Name, DependsOn, Affects, Normalization, Manifest>;
template <
typename Specification,
models::FixedString Name,
typename DependsOn = models::ModelTypeList<>,
typename Affects = models::ModelTypeList<>,
typename Normalization = models::UnavailableGeneratedNormalization,
typename Manifest = models::UnavailableGeneratedManifest>
using FixedIntegralWithPhysicalCoordinate =
models::FixedIntegralWithPhysicalCoordinate<Specification, Name, DependsOn, Affects, Normalization, Manifest>;
template <
typename Specification,
models::FixedString Name,
typename DependsOn = models::ModelTypeList<>,
typename Affects = models::ModelTypeList<>,
typename Normalization = models::UnavailableGeneratedNormalization,
typename Manifest = models::UnavailableGeneratedManifest>
using FixedWithPhysicalCoordinate =
FixedIntegralWithPhysicalCoordinate<Specification, Name, DependsOn, Affects, Normalization, Manifest>;
template <
typename Specification,
models::FixedString Name,
typename Reads,
typename Changes,
stellar::ScalarConstraintDescription Description>
using FixedScalarWithMultiplier = models::FixedIntegralWithMultiplier<
Specification,
Name,
Reads,
Changes,
typename Description::Normalization,
typename Description::Manifest>;
template <
typename Specification,
models::FixedString Name,
typename Reads,
typename Changes,
stellar::ScalarConstraintDescription Description>
using FixedScalarWithPhysicalCoordinate = models::FixedIntegralWithPhysicalCoordinate<
Specification,
Name,
Reads,
Changes,
typename Description::Normalization,
typename Description::Manifest>;
} // namespace mean_field::integral
export namespace mean_field::constraint {
using FixedCentralDensity = models::FixedCentralDensity;
template <
typename Specification,
models::FixedString Name,
typename DependsOn = models::ModelTypeList<>,
typename Affects = models::ModelTypeList<>,
typename Normalization = models::UnavailableGeneratedNormalization,
typename Manifest = models::UnavailableGeneratedManifest>
using PhaseCondition = models::PhaseCondition<Specification, Name, DependsOn, Affects, Normalization, Manifest>;
template <
typename Specification,
models::FixedString Name,
typename Reads,
typename Changes,
stellar::ScalarConstraintDescription Description>
using ScalarPhaseCondition = models::PhaseCondition<
Specification,
Name,
Reads,
Changes,
typename Description::Normalization,
typename Description::Manifest>;
} // namespace mean_field::constraint
export namespace mean_field::eos {
template <typename Specification, models::FixedString Name>
using ConstitutiveLaw = models::ConstitutiveLaw<Specification, Name>;
}
export namespace mean_field::surface {
template <typename Specification, models::FixedString Name>
using BoundaryCondition = models::BoundaryCondition<Specification, Name>;
}