module; #include #include export module mean_field:field.registry; export import :field.base; export import :quadrature.policy; export namespace mean_field::field { // ========================================================================= // Density // ========================================================================= struct Density { static constexpr std::string_view name = "density"; static constexpr int scalarOrder = 2; 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< quadrature::Term::density_projection, 0, Operand, Operand>; // Projection RHS with one runtime coefficient order. using ProjectionSource = FormSpec< quadrature::Term::density_projection, 1, Operand>; // Density-space contribution to the barotropic EOS closure: // (rho, q_rho). using EosClosureMass = FormSpec< quadrature::Term::eos_closure, 0, Operand, Operand>; // Integral of density over the physical volume. using MassConservation = FormSpec< quadrature::Term::mass_conservation, 0, Operand>; // 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< quadrature::Term::mass_normalization, 0, Operand>; // 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< quadrature::Term::error_norm, 0, Operand, 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; static constexpr int fluxOrder = 2; 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< quadrature::Term::gravity_hdiv_mass, 0, Operand, 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< quadrature::Term::gravity_source, 0, Operand, Operand>; // Mixed density-to-potential projection. Both trial and test // orders are registered quantities. using SourceProjection = FormSpec< quadrature::Term::gravity_source, 0, Operand, Operand>; using PotentialErrorNorm = FormSpec< quadrature::Term::error_norm, 0, Operand, Operand>; using FluxErrorNorm = FormSpec< quadrature::Term::error_norm, 0, Operand, 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; 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>; using ErrorNorm = FormSpec< quadrature::Term::error_norm, 0, Operand, Operand>; }; using FormList = TypeList; }; struct BarotropicConstant { static constexpr std::string_view name = "barotropic_constant"; 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); }; // ========================================================================= // 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; 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< quadrature::Term::eos_closure, 1, Operand, Operand>; // (h, q_h) contribution to // h + phi - Psi_rotation - C = 0. using EquilibriumEnthalpy = FormSpec< quadrature::Term::hydrostatic_equilibrium, 0, Operand, Operand>; // (phi, q_h) contribution to hydrostatic equilibrium. using EquilibriumGravity = FormSpec< quadrature::Term::hydrostatic_equilibrium, 0, Operand, Operand>; // (Psi_rotation, q_h). The rotation-potential order is supplied // dynamically because it belongs to runtime rotation data. using EquilibriumRotation = FormSpec< quadrature::Term::hydrostatic_equilibrium, 1, Operand>; // (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< quadrature::Term::isobaric_surface, 0, Operand, Operand>; // Integral of P(h). The dynamic order is the extra EOS order // beyond the registered order of h. using PressureIntegral = FormSpec< quadrature::Term::pressure_integral, 1, Operand>; // 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< quadrature::Term::error_norm, 0, Operand, 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; { T::name } -> std::convertible_to; } && 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>); } // namespace mean_field::field