module; #include #include #ifndef MEAN_FIELD_UNIFORM_POLYNOMIAL_ORDER_INCREMENT #define MEAN_FIELD_UNIFORM_POLYNOMIAL_ORDER_INCREMENT 0 #endif export module mean_field:field.registry; export import :dimensions.quantities; export import :field.base; export import :quadrature.policy; export import :utils.domain; export namespace mean_field::field { inline constexpr int uniformPolynomialOrderIncrement = MEAN_FIELD_UNIFORM_POLYNOMIAL_ORDER_INCREMENT; static_assert(uniformPolynomialOrderIncrement >= 0); // ========================================================================= // Density // ========================================================================= struct Density { static constexpr std::string_view name = "density"; static constexpr int scalarOrder = 2 + uniformPolynomialOrderIncrement; using PhysicalQuantity = dimensions::quantity::Density; using Support = DomainSupport; struct Scalar final : ScalarQ> { static constexpr std::string_view symbol = "ρ"; }; using Quantities = TypeList; using Constraints = TypeList<>; static constexpr bool constraintsAreValid = validate_constraints(Constraints{}); static_assert(constraintsAreValid); struct Form { // Density-space mass matrix: (rho, q). using ProjectionMass = FormSpec, Operand>; // Projection RHS with one runtime coefficient order. using ProjectionSource = FormSpec>; // Density-space contribution to the barotropic EOS closure: // (rho, q_rho). using EosClosureMass = FormSpec, Operand>; // Integral of density over the physical volume. using MassConservation = FormSpec>; // The same physical integral used as a nonlinear normalization // constraint. It has a distinct policy key so solver assembly and // diagnostics can be overintegrated independently. using MassNormalization = FormSpec>; // Integral of rho * x. The combined position-coefficient order is // supplied as one dynamic order. using CenterOfMass = FormSpec>; // Integral of rho times the quadratic position tensor. The // combined tensor-coefficient order is supplied dynamically. using Quadrupole = FormSpec>; using ErrorNorm = FormSpec, Operand>; }; using FormList = TypeList< Form::ProjectionMass, Form::ProjectionSource, Form::EosClosureMass, Form::MassConservation, Form::MassNormalization, Form::CenterOfMass, Form::Quadrupole, Form::ErrorNorm>; }; // ========================================================================= // Gravity // ========================================================================= struct Gravity { static constexpr std::string_view name = "gravity"; static constexpr int potentialOrder = 2 + uniformPolynomialOrderIncrement; static constexpr int fluxOrder = 2 + uniformPolynomialOrderIncrement; using Support = DomainSupport; struct Potential final : ScalarQ> { static constexpr std::string_view symbol = "φ"; }; struct Flux final : VectorQ, Disc> { static constexpr std::string_view symbol = "∇φ"; }; using Quantities = TypeList; using Constraints = TypeList>; static constexpr bool constraintsAreValid = validate_constraints(Constraints{}); static_assert(constraintsAreValid); struct Form { using HDivMass = FormSpec, Operand>; using DivergenceCoupling = FormSpec< quadrature::Term::gravity_divergence, 0, Operand, Operand>; using Boundary = FormSpec< quadrature::Term::gravity_boundary, 0, Operand, Operand>; // Density is a registered coefficient field and potential is the // test field, so the full polynomial order is compile-time data. using SourceLinear = FormSpec, Operand>; // Mixed density-to-potential projection. Both trial and test // orders are registered quantities. using SourceProjection = FormSpec, Operand>; using PotentialErrorNorm = FormSpec, Operand>; using FluxErrorNorm = FormSpec, Operand>; }; using FormList = TypeList< Form::HDivMass, Form::DivergenceCoupling, Form::Boundary, Form::SourceLinear, Form::SourceProjection, Form::PotentialErrorNorm, Form::FluxErrorNorm>; }; // ========================================================================= // Displacement // ========================================================================= struct Displacement { static constexpr std::string_view name = "displacement"; static constexpr int vectorOrder = 3 + uniformPolynomialOrderIncrement; using Support = DomainSupport; struct Vector final : VectorQ> { static constexpr std::string_view symbol = "d"; }; using Quantities = TypeList; using Constraints = TypeList<>; static constexpr bool constraintsAreValid = validate_constraints(Constraints{}); static_assert(constraintsAreValid); struct Form { // Harmonic or pseudoelastic interior mesh extension. For the // initial Laplacian model this is (grad d, grad w). using MeshExtension = FormSpec< quadrature::Term::mesh_extension, 0, Operand, Operand>; // Positive gravitational contribution to the displacement row: // // int rho grad(phi) . w dV. // // Both the base geometry Jacobian and the displacement test // function contribute to the polynomial order. The RT flux is // mapped to physical space by the contravariant Piola map. using GravityForce = FormSpec< quadrature::Term::gravity_force, 0, Operand, Operand, Operand, Operand>; // Rigid-rotation contribution to the displacement row: // // -int rho grad(Psi_rotation) . w dV. // // grad(Psi_rotation) is linear in physical position, so its // polynomial order is supplied as one runtime contribution. using CentrifugalForce = FormSpec, Operand>; using ErrorNorm = FormSpec, Operand>; }; using FormList = TypeList; }; // Current realization of MultiplierFor. This remains a // barotrope-specific field representation: the specification compiler, // rather than the universal state registry, decides when it is present. struct BarotropicConstant { static constexpr std::string_view name = "barotropic_constant"; using Support = NonSpatialSupport; struct Scalar final : GlobalScalarQ { static constexpr std::string_view symbol = "C"; }; using Quantities = TypeList; using Constraints = TypeList<>; using FormList = TypeList<>; static constexpr bool constraintsAreValid = validate_constraints(Constraints{}); static_assert(constraintsAreValid); }; // Solver border generated by FixedCentralDensity. This is deliberately a // non-spatial numerical coordinate rather than a physical stellar field. struct CentralDensityBorder { static constexpr std::string_view name = "central_density_border"; using Support = NonSpatialSupport; struct Scalar final : GlobalScalarQ { static constexpr std::string_view symbol = "lambda_rho_c"; }; using Quantities = TypeList; using Constraints = TypeList<>; using FormList = TypeList<>; static constexpr bool constraintsAreValid = validate_constraints(Constraints{}); static_assert(constraintsAreValid); }; // ========================================================================= // Specific enthalpy // // Pressure is deliberately not registered as an independent field. For a // barotrope it is derived from h through the EOS, while h supplies the // continuous H1 trace used to define the isobaric stellar surface. // ========================================================================= struct Enthalpy { static constexpr std::string_view name = "specific_enthalpy"; static constexpr int scalarOrder = 3 + uniformPolynomialOrderIncrement; using PhysicalQuantity = dimensions::quantity::SpecificEnthalpy; using Support = DomainSupport; struct Scalar final : ScalarQ> { static constexpr std::string_view symbol = "h"; }; using Quantities = TypeList; using Constraints = TypeList<>; static constexpr bool constraintsAreValid = validate_constraints(Constraints{}); static_assert(constraintsAreValid); struct Form { // EOS source contribution (rho(h), q_rho). The dynamic order is // the extra polynomial order introduced by the nonlinear EOS // beyond the registered order of h. For an n=3 polytrope this is // 2 * hOrder, making rho(h) cubic in h. using EosClosureSource = FormSpec, Operand>; // (h, q_h) contribution to // h + phi - Psi_rotation - C = 0. using EquilibriumEnthalpy = FormSpec, Operand>; // (phi, q_h) contribution to hydrostatic equilibrium. using EquilibriumGravity = FormSpec, Operand>; // (Psi_rotation, q_h). The rotation-potential order is supplied // dynamically because it belongs to runtime rotation data. using EquilibriumRotation = FormSpec>; // (C, q_h), where C is spatially constant. using EquilibriumConstant = FormSpec< quadrature::Term::hydrostatic_equilibrium, 0, Operand, Operand>; // Boundary trace form available for weak enforcement, testing, or // a future multiplier formulation of h|Gamma_star = 0. using IsobaricSurface = FormSpec, Operand>; // Integral of P(h). The dynamic order is the extra EOS order // beyond the registered order of h. using PressureIntegral = FormSpec>; // Weak pressure force in the displacement test space: // // -int P(h) I : grad(w) dV // // which is equivalent to -int P(h) div(w) dV. The dynamic order // is the extra EOS order beyond the registered order of h. For an // n=3 polytrope this is 3 * hOrder, making P(h) quartic in h. using PressureForce = FormSpec< quadrature::Term::pressure_force, 1, Operand, Operand>; using ErrorNorm = FormSpec, Operand>; }; using FormList = TypeList< Form::EosClosureSource, Form::EquilibriumEnthalpy, Form::EquilibriumGravity, Form::EquilibriumRotation, Form::EquilibriumConstant, Form::IsobaricSurface, Form::PressureIntegral, Form::PressureForce, Form::ErrorNorm>; }; // ========================================================================= // Field definition concept // ========================================================================= template concept FieldTag = requires { typename T::Quantities; typename T::Constraints; typename T::FormList; typename T::Support; { T::name } -> std::convertible_to; } && IsFieldSupport && isRegisteredQuantityList && isFieldFormList; static_assert(FieldTag); static_assert(FieldTag); static_assert(FieldTag); static_assert(FieldTag); static_assert(FieldTag); static_assert(DerivedQuantity); static_assert(std::same_as< RelationTargetT, Gravity::Potential>); static_assert(std::same_as< FieldDomainT, utils::domain::Stellar>); static_assert(std::same_as< FieldDomainT, utils::domain::Stellar>); static_assert(std::same_as< FieldDomainT, utils::domain::All>); static_assert(std::same_as< FieldDomainT, utils::domain::All>); static_assert(NonSpatialField); } // namespace mean_field::field