diff --git a/src/include/serif/discretization/functional_space/space.hpp b/src/include/serif/discretization/functional_space/space.hpp index 2bfb6856..7f8eb878 100644 --- a/src/include/serif/discretization/functional_space/space.hpp +++ b/src/include/serif/discretization/functional_space/space.hpp @@ -1,61 +1,104 @@ #pragma once + +/** + * @file space.hpp + * @author Emily M. Boudreaux + * @brief This file defines the functional space types used in the discretization framework, including compile-time descriptors and runtime descriptors. + */ + #include #include #include "serif/meta/enums.hpp" namespace serif::discretization::functional_space { + + /** + * @brief Compile-time descriptor for the rank of a functional space. + * @tparam R The rank of the functional space. + */ template struct Rank { - constexpr static std::size_t value = R; + constexpr static std::size_t value = R; ///< The rank of the functional space. }; + /** + * @brief Compile-time descriptor for the family, continuity, trace, and mapping of a functional space. + * + * @note These are almost all of the functional spaces that MFEM supports, if in the future MFEM adds some other space and + * you wish to extend SERiF to support it (or you introduce some non MFEM backend with another space) then one early + * place you will need to modify is this enum. + * + * @note We do not support mfem::ceed::ParAlgebraicCoarseSpace + */ enum class Family { - l2, - h1, - hdiv, - hcurl + l2, ///< L2 space, typically used for discontinuous functions. + h1, ///< H1 space, typically used for continuous functions. + hdiv, ///< H(div) space, typically used for vector fields with normal continuity. + hcurl ///< H(curl) space, typically used for vector fields with tangential continuity. }; - inline static auto Family2StringMap = meta::enum_to_enum_string_map(); - inline static auto String2FamilyMap = meta::enum_to_string_enum_map(); + inline static auto Family2StringMap = meta::enum_to_enum_string_map(); ///< Mapping from Family enum values to their string representations. + inline static auto String2FamilyMap = meta::enum_to_string_enum_map(); ///< Mapping from string representations to Family enum values. + /** + * @brief Compile-time descriptor for the continuity of a functional space. This enum is parallel to the @ref Family enum. + * + * @todo There may be a better way to store these relations that forces them to be parallel rather than just relying on the programmer to keep them in sync. + */ enum class Continuity { - discontinuous, - continuous, - normal_continuous, - tangential_continuous + discontinuous, ///< Discontinuous functions, typically used for L2 spaces. + continuous, ///< C0 continuous functions, typically used for H1 spaces. + normal_continuous, ///< Functions with normal continuity, typically used for H(div) spaces. + tangential_continuous ///< Functions with tangential continuity, typically used for H(curl) spaces. }; - inline static auto Continuity2StringMap = meta::enum_to_enum_string_map(); - inline static auto String2ContinuityMap = meta::enum_to_string_enum_map(); + inline static auto Continuity2StringMap = meta::enum_to_enum_string_map(); ///< Mapping from Continuity enum values to their string representations. + inline static auto String2ContinuityMap = meta::enum_to_string_enum_map(); ///< Mapping from string representations to Continuity enum values. + /** + * @brief Compile-time descriptor for the trace of a functional space. This enum is parallel to the @ref Family enum. + */ enum class Trace { - none, - value, - normal, - tangential + none, ///< No trace, typically used for L2 spaces. + value, ///< Value trace, typically used for H1 spaces. + normal, ///< Normal trace, typically used for H(div) spaces. + tangential ///< Tangential trace, typically used for H(curl) spaces. }; - inline static auto Trace2StringMap = meta::enum_to_enum_string_map(); - inline static auto String2TraceMap = meta::enum_to_string_enum_map(); + inline static auto Trace2StringMap = meta::enum_to_enum_string_map(); ///< Mapping from Trace enum values to their string representations. + inline static auto String2TraceMap = meta::enum_to_string_enum_map(); ///< Mapping from string representations to Trace enum values. + /** + * @brief Compile-time descriptor for the mapping of a functional space. This tells the DomainMapper which piola transformations to use when moving between reference and physical spaces. + */ enum class Mapping { - identity, - contravariant_piola, - covariant_piola + identity, ///< Identity mapping, typically used for L2 and H1 spaces. + contravariant_piola, ///< Contravariant Piola mapping, preserving the normal trace, typically used for H(div) spaces. (\f$ \frac{1}{\text{det(J)}}J\hat{\phi}(F^{-1}(x)) \f$ transforming some vector field \f$\phi(x)\f$ for some transformation \f$ F \f$ with Jacobian \f$ J \f$) + covariant_piola ///< Covariant Piola mapping, preserving the tangential trace, typically used for H(curl) spaces. (\f$ J^{-T}\hat{\phi}(F^{-1}(x)) \f$ transforming some vector field \f$\phi(x)\f$ for some transformation \f$ F \f$ with Jacobian \f$ J \f$) }; - inline static auto Mapping2StringMap = meta::enum_to_enum_string_map(); - inline static auto String2MappingMap = meta::enum_to_string_enum_map(); + inline static auto Mapping2StringMap = meta::enum_to_enum_string_map(); ///< Mapping from Mapping enum values to their string representations. + inline static auto String2MappingMap = meta::enum_to_string_enum_map(); ///< Mapping from string representations to Mapping enum values. + /** + * @brief Compile-time descriptor for a functional space. This struct is used to describe the family, continuity, trace, and mapping of a functional space at compile time. + */ struct SpaceDescriptor final { - const Family family; - const Continuity continuity; - const Trace trace; - const Mapping mapping; + const Family family; ///< The family of the functional space (e.g. L2, H1, H(div), H(curl)). + const Continuity continuity; ///< The continuity of the functional space (e.g. discontinuous, continuous, normal continuous, tangential continuous). + const Trace trace; ///< The trace of the functional space (e.g. none, value, normal, tangential). + const Mapping mapping; ///< The mapping of the functional space (e.g. identity, contravariant_piola, covariant_piola). }; + /** + * @brief Compile-time descriptor for an L2 functional space. This is a convenience descriptor that allows you to easily create an L2 functional space descriptor without having to specify all the parameters. + * The specifications are + * - family = Family::l2 + * - continuity = Continuity::discontinuous + * - trace = Trace::none + * - mapping = Mapping::identity + */ inline static constexpr SpaceDescriptor L2{ .family = Family::l2, .continuity = Continuity::discontinuous, @@ -63,6 +106,14 @@ namespace serif::discretization::functional_space { .mapping = Mapping::identity }; + /** + * @brief Compile-time descriptor for an H1 functional space. This is a convenience descriptor that allows you to easily create an H1 functional space descriptor without having to specify all the parameters. + * The specifications are + * - family = Family::h1 + * - continuity = Continuity::continuous + * - trace = Trace::value + * - mapping = Mapping::identity + */ inline static constexpr SpaceDescriptor H1{ .family = Family::h1, .continuity = Continuity::continuous, @@ -70,6 +121,14 @@ namespace serif::discretization::functional_space { .mapping = Mapping::identity }; + /** + * @brief Compile-time descriptor for an H(div) functional space. This is a convenience descriptor that allows you to easily create an H(div) functional space descriptor without having to specify all the parameters. + * The specifications are + * - family = Family::hdiv + * - continuity = Continuity::normal_continuous + * - trace = Trace::normal + * - mapping = Mapping::contravariant_piola + */ inline static constexpr SpaceDescriptor Hdiv{ .family = Family::hdiv, .continuity = Continuity::normal_continuous, @@ -77,6 +136,14 @@ namespace serif::discretization::functional_space { .mapping = Mapping::contravariant_piola }; + /** + * @brief Compile-time descriptor for an H(curl) functional space. This is a convenience descriptor that allows you to easily create an H(curl) functional space descriptor without having to specify all the parameters. + * The specifications are + * - family = Family::hcurl + * - continuity = Continuity::tangential_continuous + * - trace = Trace::tangential + * - mapping = Mapping::covariant_piola + */ inline static constexpr SpaceDescriptor Hcurl{ .family = Family::hcurl, .continuity = Continuity::tangential_continuous, @@ -84,17 +151,35 @@ namespace serif::discretization::functional_space { .mapping = Mapping::covariant_piola }; + /** + * @brief Concept to validate a valid space descriptor. A space descriptor is valid if it is of type SpaceDescriptor. This is a very simple concept that just checks that the type is exactly SpaceDescriptor. + */ template concept IsSpaceDescriptor = std::is_same_v; + /** + * @brief Compile-time descriptor for a discrete functional space. The difference between a FunctionalSpace and a discrete functional space is that + * a discrete functional space carries some additional information which is needed to actually construct a finite element space (e.g. polynomial order, and rank). + * Once you have a discrete functional space you have, for MFEM at least, enough information to fully define a finite element space. + * + * @tparam Space The functional space descriptor (e.g. L2, H1, H(div), H(curl)). + * @tparam Order The order of the finite element family (e.g. 0 for P0, 1 for P1, etc.). + * @tparam R The rank of the functional space (e.g. 0 for scalar, 1 for vector, 2 for tensor). + */ template struct DiscreteFunctionalSpace { + static_assert(Order >= 0, "Finite-element family order must be non-negative."); + static_assert((Space.family != Family::hdiv && Space.family != Family::hcurl) || R == 1, "H(div) and H(curl) spaces must be vector-valued."); + static constexpr SpaceDescriptor functional_space = Space; static constexpr int order = Order; using rank = Rank; }; } +/** + * @brief Specialization of the std::formatter for the functional space types. This allows us to use std::format to format the functional space types as strings. + */ template struct std::formatter> : std::formatter { template @@ -109,6 +194,9 @@ struct std::formatter> : std::f } }; +/** + * @brief Specialization of the std::formatter for the SpaceDescriptor types. This allows us to use std::format to format the SpaceDescriptor types as strings. + */ template <> struct std::formatter : std::formatter { template @@ -121,6 +209,9 @@ struct std::formatter } }; +/** + * @brief Specialization of the std::formatter for the DiscreteFunctionalSpace types. This allows us to use std::format to format the DiscreteFunctionalSpace types as strings. + */ // formatter for DiscreteFunctionalSpace template struct std::formatter> : std::formatter { @@ -129,11 +220,13 @@ struct std::formatter::format(std::format("DiscreteFunctionalSpace(functional_space={}, order={}, rank={})", space.functional_space, space.order, - space.rank), ctx); + serif::discretization::functional_space::Rank{}), ctx); } }; -// formatter for each enum +/** + * @brief Specialization of the std::formatter for the Family enum. This allows us to use std::format to format the Family enum as strings. + */ template <> struct std::formatter : std::formatter { template @@ -142,6 +235,9 @@ struct std::formatter : std::fo } }; +/** + * @brief Specialization of the std::formatter for the Continuity enum. This allows us to use std::format to format the Continuity enum as strings. + */ template <> struct std::formatter : std::formatter { template @@ -150,6 +246,9 @@ struct std::formatter : std } }; +/** + * @brief Specialization of the std::formatter for the Trace enum. This allows us to use std::format to format the Trace enum as strings. + */ template <> struct std::formatter : std::formatter { template @@ -158,6 +257,9 @@ struct std::formatter : std::for } }; +/** + * @brief Specialization of the std::formatter for the Mapping enum. This allows us to use std::format to format the Mapping enum as strings. + */ template <> struct std::formatter : std::formatter { template