module; #include #include #include #include export module mean_field:field.base; export namespace mean_field::field { template struct TypeList { }; template struct TypeListContains; template struct TypeListContains> : std::bool_constant<(std::same_as || ...)> { }; template inline constexpr bool typeListContains = TypeListContains::value; enum class StorageKind { finite_element, global_scalar }; inline constexpr int dynamicBlockSize = -1; // ------------------------------------------------------------------------- // Function-space tags // ------------------------------------------------------------------------- struct L2 { static constexpr std::string_view name = "L2"; }; struct H1 { static constexpr std::string_view name = "H1"; }; struct RT { static constexpr std::string_view name = "RT"; }; struct ND { static constexpr std::string_view name = "ND"; }; template concept SpaceTag = std::same_as || std::same_as || std::same_as || std::same_as; template inline constexpr bool spaceSupportsRank = (std::same_as && (RankV == 0 || RankV == 1)) || (std::same_as && (RankV == 0 || RankV == 1)) || (std::same_as && RankV == 1) || (std::same_as && RankV == 1); // ------------------------------------------------------------------------- // Discretization descriptors // // familyOrder is the order passed to the backend's FE collection // constructor. It is deliberately not called polynomialOrder because those // values differ for some spaces, notably Raviart-Thomas elements in MFEM. // ------------------------------------------------------------------------- template struct Disc { using Space = SpaceT; static constexpr int familyOrder = FamilyOrderV; static_assert( FamilyOrderV >= 0, "Finite-element family order must be non-negative." ); }; template concept DiscretizationTag = requires { typename T::Space; { T::familyOrder } -> std::convertible_to; } && SpaceTag; // ------------------------------------------------------------------------- // Physical relations between quantities // ------------------------------------------------------------------------- struct FieldRelation { struct Independent { }; template struct Gradient { using Source = SourceT; }; template struct Divergence { using Source = SourceT; }; template struct Curl { using Source = SourceT; }; }; template struct IsGradient : std::false_type { }; template struct IsDivergence : std::false_type { }; template struct IsCurl : std::false_type { }; template struct IsGradient> : std::true_type { }; template struct IsDivergence> : std::true_type { }; template struct IsCurl> : std::true_type { }; template concept ValidRelation = std::same_as || IsGradient::value || IsDivergence::value || IsCurl::value; template struct RelationTarget { using Type = void; }; template struct RelationTarget> { using Type = SourceT; }; template struct RelationTarget> { using Type = SourceT; }; template struct RelationTarget> { using Type = SourceT; }; template using RelationTargetT = typename RelationTarget::Type; // ------------------------------------------------------------------------- // Field quantities // ------------------------------------------------------------------------- template struct Quantity { using Relation = RelationT; using Discretization = DiscT; using Space = typename DiscT::Space; static constexpr int rankValue = RankV; static constexpr int familyOrder = DiscT::familyOrder; static constexpr StorageKind storageKind = StorageKind::finite_element; static constexpr int staticBlockSize = dynamicBlockSize; static_assert( RankV >= 0, "A field quantity cannot have a negative tensor rank." ); static_assert( spaceSupportsRank< Space, RankV>, "This function space cannot represent a quantity of this rank." ); }; template using ScalarQ = Quantity<0, RelationT, DiscT>; template using VectorQ = Quantity<1, RelationT, DiscT>; struct GlobalScalarQ { using Relation = FieldRelation::Independent; static constexpr int rankValue = 0; static constexpr StorageKind storageKind = StorageKind::global_scalar; static constexpr int staticBlockSize = 1; }; template concept FieldQuantity = requires { typename T::Relation; typename T::Discretization; typename T::Space; { T::rankValue } -> std::convertible_to; { T::familyOrder } -> std::convertible_to; { T::storageKind } -> std::convertible_to; { T::staticBlockSize } -> std::convertible_to; } && SpaceTag && T::storageKind == StorageKind::finite_element; template concept GlobalScalarQuantity = requires { typename T::Relation; { T::rankValue } -> std::convertible_to; { T::storageKind } -> std::convertible_to; { T::staticBlockSize } -> std::convertible_to; } && T::rankValue == 0 && T::storageKind == StorageKind::global_scalar && T::staticBlockSize == 1; template concept RegisteredQuantity = FieldQuantity || GlobalScalarQuantity; template concept DerivedQuantity = FieldQuantity && (!std::same_as, void>); // ------------------------------------------------------------------------- // Compile-time discretization constraints // ------------------------------------------------------------------------- template struct RtL2StablePair { static consteval void validate() { static_assert( std::same_as, "The flux in an RT/L2 pair must use Raviart-Thomas elements." ); static_assert( std::same_as, "The potential in an RT/L2 pair must use L2 elements." ); static_assert( FluxT::rankValue == 1, "The flux in an RT/L2 pair must be vector-valued." ); static_assert( PotentialT::rankValue == 0, "The potential in an RT/L2 pair must be scalar-valued." ); static_assert( FluxT::familyOrder == PotentialT::familyOrder, "The MFEM RT and L2 family orders must match." ); } }; template consteval bool validate_constraints(TypeList) { (ConstraintTs::validate(), ...); return true; } // ------------------------------------------------------------------------- // Operations applied to quantities inside weak forms // // These describe the mathematics. Backend-specific polynomial-order rules // are provided by field.mfem. // ------------------------------------------------------------------------- struct FieldOperation { struct Value { }; struct Gradient { }; struct Divergence { }; struct Curl { }; struct NormalTrace { }; }; template concept FieldOperationTag = std::same_as || std::same_as || std::same_as || std::same_as || std::same_as; template < RegisteredQuantity QuantityT, FieldOperationTag OperationT = FieldOperation::Value> struct Operand { using Quantity = QuantityT; using Operation = OperationT; static_assert( FieldQuantity || std::same_as< OperationT, FieldOperation::Value>, "Global scalar quantities support only the value operation." ); }; template concept FieldOperand = requires { typename T::Quantity; typename T::Operation; } && RegisteredQuantity && FieldOperationTag; // ------------------------------------------------------------------------- // Weak-form descriptions // // PolicyKeyV associates the form with a runtime quadrature-policy key. // // DynamicOrderCountV is the number of polynomial-order contributions that // cannot yet be derived from registered quantities. For example, a source // coefficient supplied at runtime contributes one dynamic order. // ------------------------------------------------------------------------- template < auto PolicyKeyV, std::size_t DynamicOrderCountV, FieldOperand... OperandTs> struct FormSpec { static constexpr auto policyKey = PolicyKeyV; static constexpr std::size_t dynamicOrderCount = DynamicOrderCountV; using Operands = TypeList; }; template concept FieldForm = requires { typename T::Operands; T::policyKey; { T::dynamicOrderCount } -> std::convertible_to; }; template struct IsRegisteredQuantityList : std::false_type { }; template struct IsRegisteredQuantityList> : std::true_type { }; template inline constexpr bool isRegisteredQuantityList = IsRegisteredQuantityList::value; template struct IsFieldFormList : std::false_type { }; template struct IsFieldFormList> : std::true_type { }; template inline constexpr bool isFieldFormList = IsFieldFormList::value; } // namespace mean_field::field