From 0db22935b4395f364b6320fa364c350ba30697ec Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Fri, 2 Oct 2026 16:18:37 -0400 Subject: [PATCH] docs(domain/relation): added doxygen comments throughout --- .../domain/relation/concepts.hpp | 11 ++ .../domain/relation/lists/concepts.hpp | 10 ++ .../domain/relation/lists/relation_list.hpp | 10 ++ .../domain/relation/lists/traits.hpp | 15 +++ .../domain/relation/relations.hpp | 67 +++++++++--- .../domain/relation/validation/concepts.hpp | 14 +++ .../domain/relation/validation/runtime.hpp | 101 ++++++++++++------ .../domain/relation/validation/validation.hpp | 67 +++++++++++- .../solver/normalization/normalization.hpp | 0 .../serif/solver/normalization/plan.hpp | 0 src/include/serif/solver/solver.hpp | 0 tests/sandbox/solver_sandbox.cpp | 0 12 files changed, 244 insertions(+), 51 deletions(-) create mode 100644 src/include/serif/solver/normalization/normalization.hpp create mode 100644 src/include/serif/solver/normalization/plan.hpp create mode 100644 src/include/serif/solver/solver.hpp create mode 100644 tests/sandbox/solver_sandbox.cpp diff --git a/src/include/serif/discretization/domain/relation/concepts.hpp b/src/include/serif/discretization/domain/relation/concepts.hpp index c960a1b8..a5c74a2b 100644 --- a/src/include/serif/discretization/domain/relation/concepts.hpp +++ b/src/include/serif/discretization/domain/relation/concepts.hpp @@ -1,11 +1,22 @@ #pragma once +/** + * @file concepts.hpp + * @author Emily M. Boudreaux + * @brief This file defines concepts for relations in the domain module + */ + #include "serif/discretization/domain/relation/relations.hpp" #include "serif/utils/misc/std_helper/cleaning.hpp" namespace serif::discretization::domain::relation { using utils::misc::std_helper::AIsBaseClassOfB; + /** + * @brief Concept to validate that a type is a relation. This concept checks if the type is derived from the DomainRelation base class. + * + * @tparam R The type to check. + */ template concept IsRelation = AIsBaseClassOfB; } diff --git a/src/include/serif/discretization/domain/relation/lists/concepts.hpp b/src/include/serif/discretization/domain/relation/lists/concepts.hpp index 1cd14316..43ca681a 100644 --- a/src/include/serif/discretization/domain/relation/lists/concepts.hpp +++ b/src/include/serif/discretization/domain/relation/lists/concepts.hpp @@ -1,8 +1,18 @@ #pragma once +/** + * @file concepts.hpp + * @author Emily M. Boudreaux + * @brief This file defines concepts for relation lists in the discretization framework. + */ + #include "serif/discretization/domain/relation/lists/traits.hpp" namespace serif::discretization::domain::relation::lists { + + /** + * @brief Concept for relation list types in the discretization framework. To be a relation list, a type must satisfy the is_relation_list_v trait. + */ template concept IsRelationList = is_relation_list_v; } diff --git a/src/include/serif/discretization/domain/relation/lists/relation_list.hpp b/src/include/serif/discretization/domain/relation/lists/relation_list.hpp index 334974c9..37af7e9f 100644 --- a/src/include/serif/discretization/domain/relation/lists/relation_list.hpp +++ b/src/include/serif/discretization/domain/relation/lists/relation_list.hpp @@ -1,5 +1,11 @@ #pragma once +/** + * @file relation_list.hpp + * @author Emily M. Boudreaux + * @brief Compile-time list of relations, used to group relations together for operations and forms. + */ + #include #include "serif/discretization/domain/relation/concepts.hpp" @@ -8,6 +14,10 @@ namespace serif::discretization::domain::relation::lists { using utils::misc::std_helper::CountVariadicArguments; + /** + * @brief Compile-time list of relations, used to group relations together for operations and forms. This struct is a variadic template that takes a list of relation types as template parameters. + * @tparam RelationTs The types of relations in the relation list. + */ template struct RelationList { static constexpr std::size_t count = CountVariadicArguments(); diff --git a/src/include/serif/discretization/domain/relation/lists/traits.hpp b/src/include/serif/discretization/domain/relation/lists/traits.hpp index b52e94dc..f5b584b5 100644 --- a/src/include/serif/discretization/domain/relation/lists/traits.hpp +++ b/src/include/serif/discretization/domain/relation/lists/traits.hpp @@ -1,11 +1,26 @@ #pragma once +/** + * @file traits.hpp + * @author Emily M. Boudreaux + * @brief Compile-time traits for relation lists, used to extract types and properties from template parameters. + */ + #include "serif/discretization/domain/relation/lists/relation_list.hpp" namespace serif::discretization::domain::relation::lists { + + /** + * @brief Compile-time trait to determine if a type is a relation list. By default, this trait is false for all types. + * @tparam T The type to check. + */ template constexpr bool is_relation_list_v = false; + /** + * @brief Specialization of the is_relation_list_v trait for relation list types. This specialization sets the trait to true for any type that is a specialization of RelationList. + * @tparam RelationTs The types of relations in the relation list. + */ template constexpr bool is_relation_list_v> = true; } diff --git a/src/include/serif/discretization/domain/relation/relations.hpp b/src/include/serif/discretization/domain/relation/relations.hpp index 74943b7c..2b66f7af 100644 --- a/src/include/serif/discretization/domain/relation/relations.hpp +++ b/src/include/serif/discretization/domain/relation/relations.hpp @@ -1,5 +1,17 @@ #pragma once +/** + * @file relations.hpp + * @author Emily M. Boudreaux + * @brief This file defines compile time tags for the relations that can be declared about a mesh in the domain module. These are ultimately used to define a constrained mesh schema. + * + * Each relation must hold for a mesh to be considered valid for a given schema. This is intended to resolve some particular class of bug I imagine where a + * lively young astronomer confidently rolls up to SERiF with a new mesh in hand and a dream in their heart and then spend the next year trying to sort out esoteric errors. + * The idea here is that once a mesh gets through this system it should be provably at least good enough to be able to run some math against. That is not to say this + * validates the quality of a mesh, this system is not meant for that; rather, it is to say it lets be confident that the invariants we expect of a mesh + * are actually true. + */ + #include #include "serif/discretization/domain/concepts.hpp" @@ -13,37 +25,62 @@ namespace serif::discretization::domain::relation { using utils::misc::concepts::SetOfOneOrTwo; using utils::misc::std_helper::CountVariadicArguments; + /** + * @brief Base class for all relations in the domain module. This class is used to define compile-time tags for relations that can be declared about a mesh. + * + * Each relation must hold for a mesh to be considered valid for a given schema. This base class serves as a common interface for all relation types, allowing + * for compile-time checks and validation of relations in the domain module. + */ struct DomainRelation { }; - // "Every face of A that is not interior to A borders B." + /** + * @brief Compile time type representing the inscribed relation between two domains or sets. This relation states that every face of the inner domain (A) that is not interior to A borders the outer domain (B). + * + * @tparam A The inner domain or set type. + * @tparam B The outer domain or set type. + * + * This relation is used to define constraints on the mesh, ensuring that the inner domain is fully contained within the outer domain and that their boundaries are properly aligned. + */ template struct Inscribed final : DomainRelation { - using inner_type = A; - using outer_type = B; + using inner_type = A; ///< Domain or Domain Set defining the inner domain (A) in the inscribed relation. + using outer_type = B; ///< Domain or Domain Set defining the outer domain (B) in the inscribed relation. - static constexpr std::string_view name = "inscribed"; + static constexpr std::string_view name = "inscribed"; ///< Compile time name of the relation @todo we should replace this with our meta library. }; - // "A is a single connected blob of elements, not two separate pieces." + /** + * @brief Compile time type representing the fully connected relation for a domain or set. This relation states that the domain is a single connected blob of elements, not two separate pieces. + * + * @tparam A The domain or set type. + * + * This relation is used to define constraints on the mesh, ensuring that the domain is fully connected and does not consist of disjoint parts. + */ template struct FullyConnected final : DomainRelation { - using domain_type = A; + using domain_type = A; ///< Domain or Domain Set defining the domain in the fully connected relation. - static constexpr std::string_view name = "fully_connected"; + static constexpr std::string_view name = "fully_connected"; ///< Compile time name of the relation @todo we should replace this with our meta library. }; - // "BoundaryT is exactly the surface between these one or two volumes." - // - // One domain -> the surface between that volume and the outside of the mesh. - // Two domains -> the interface between the two volumes. + /** + * @brief Compile time type representing the domain boundary relation between a boundary and one or two domains. This relation states that the boundary is exactly the surface between the specified domains. + * We enforce here that the size of the DomainTs pack is either one or two, as a boundary can only be associated with one or two domains. If it is one domain + * then the boundary is the surface of that domain, if it is two domains then the boundary is the interface between those two domains. + * + * @tparam BoundaryT The boundary type. + * @tparam DomainTs The domain or set types (one or two) that define the domains associated with the boundary. + * + * This relation is used to define constraints on the mesh, ensuring that the boundary is properly aligned with the specified domains and that it represents the interface between them. + */ template requires SetOfOneOrTwo struct DomainBoundary final : DomainRelation { - using boundary_type = BoundaryT; - using domain_types = DomainOrSetList; + using boundary_type = BoundaryT; ///< Boundary type defining the boundary in the domain boundary relation. + using domain_types = DomainOrSetList; ///< Domain or Domain Set types defining the domains in the domain boundary relation. - static constexpr std::size_t domain_count = CountVariadicArguments(); + static constexpr std::size_t domain_count = CountVariadicArguments(); ///< Compile time count of the number of domains associated with the boundary (either one or two). - static constexpr std::string_view name = "domain_boundary"; + static constexpr std::string_view name = "domain_boundary"; ///< Compile time name of the relation @todo we should replace this with our meta library. }; } diff --git a/src/include/serif/discretization/domain/relation/validation/concepts.hpp b/src/include/serif/discretization/domain/relation/validation/concepts.hpp index 6afd420c..11b98611 100644 --- a/src/include/serif/discretization/domain/relation/validation/concepts.hpp +++ b/src/include/serif/discretization/domain/relation/validation/concepts.hpp @@ -1,8 +1,22 @@ #pragma once +/** + * @file concepts.hpp + * @author Emily M. Boudreaux + * @brief This file defines concepts for relation validation in the discretization framework. + */ + #include "serif/discretization/domain/relation/validation/validation.hpp" namespace serif::discretization::domain::relation::validation { + + /** + * @brief Concept to validate that a set of domain IDs, boundary IDs, and relations are valid. This concept checks if the relations use registered entities based on the provided domain and boundary IDs. + * + * @tparam DomainIDsT The type representing the domain IDs. + * @tparam BoundaryIDsT The type representing the boundary IDs. + * @tparam RelationsT The type representing the relations. + */ template concept HaveValidRelationEntities = RelationsUseRegisteredEntities::value; } diff --git a/src/include/serif/discretization/domain/relation/validation/runtime.hpp b/src/include/serif/discretization/domain/relation/validation/runtime.hpp index 7d9a7371..8197f9cd 100644 --- a/src/include/serif/discretization/domain/relation/validation/runtime.hpp +++ b/src/include/serif/discretization/domain/relation/validation/runtime.hpp @@ -1,71 +1,108 @@ #pragma once +/** + * @file runtime.hpp + * @author Emily M. Boudreaux + * + * @brief This file contains the runtime validation results for relations in the domain module. + */ + #include #include namespace serif::discretization::domain::relation::validation { + /** + * @enum RelationValidationFailure + * @brief Enum representing the types of validation failures that can occur when validating relations in the domain module. + * + * @note these are split into different categories based on their relation type; however, this is weakly enforced by convention and not the compiler. + */ enum class RelationValidationFailure : std::uint8_t { - None, + None, ///< No validation failure occurred. // FullyConnected - DomainAbsent, - DomainDisconnected, + DomainAbsent, ///< The specified domain is absent in the provided domain ID list. + DomainDisconnected, ///< The specified domain is disconnected, meaning it does not form a single connected component. // Inscribed - InnerDomainAbsent, - OuterDomainAbsent, - InnerDomainHasNoBoundary, - InnerDomainTouchesMeshBoundary, - InnerDomainTouchesUnexpectedDomain, + InnerDomainAbsent, ///< The inner domain is absent in the provided domain ID list. + OuterDomainAbsent, ///< The outer domain is absent in the provided domain ID list. + InnerDomainHasNoBoundary, ///< The inner domain has no boundary, which is required for the inscribed relation (the boundary must be shared) + InnerDomainTouchesMeshBoundary, ///< The inner domain touches the mesh boundary, which is not allowed for the inscribed relation (the inner domain must be fully contained within the outer domain). + InnerDomainTouchesUnexpectedDomain, ///< The inner domain touches an unexpected domain, which is not allowed for the inscribed relation (the inner domain must be fully contained within the outer domain). // DomainBoundary - DomainBoundaryAbsent, - DomainBoundaryTaggedFaceHasWrongTopology, - DomainBoundaryTaggedFaceTouchesUnexpectedDomain, - DomainBoundaryExpectedFaceIsUntagged, - DomainBoundaryExpectedFaceHasWrongID + DomainBoundaryAbsent, ///< The specified domain boundary is absent in the provided boundary ID list. + DomainBoundaryTaggedFaceHasWrongTopology, ///< The tagged face of the domain boundary has the wrong topology, which is not allowed for the domain boundary relation (the face must have the correct topology). + DomainBoundaryTaggedFaceTouchesUnexpectedDomain, ///< The tagged face of the domain boundary touches an unexpected domain, which is not allowed for the domain boundary relation (the face must only touch the specified domains). + DomainBoundaryExpectedFaceIsUntagged, ///< The expected face of the domain boundary is untagged, which is not allowed for the domain boundary relation (the face must be tagged). + DomainBoundaryExpectedFaceHasWrongID ///< The expected face of the domain boundary has the wrong ID, which is not allowed for the domain boundary relation (the face must have the correct ID). }; + /** + * @struct RelationValidationResult + * @brief Struct representing the result of validating a relation in the domain module. It contains information about the validation failure, if any, and optional diagnostics for specific relation types. + */ struct RelationValidationResult { - RelationValidationFailure failure{RelationValidationFailure::None}; + RelationValidationFailure failure{RelationValidationFailure::None}; ///< The type of validation failure that occurred, if any. Default to None. + /** + * @struct InscribedDiagnostics + * @brief Optional diagnostics for the Inscribed relation type. It contains information about the face ID, inner element ID, adjacent element ID, and adjacent domain ID. + */ struct InscribedDiagnostics { - int faceID{-1}; - int innerElementID{-1}; - int adjacentElementID{-1}; - int adjacentDomainID{-1}; + int faceID{-1}; ///< The ID of the face associated with the inscribed relation that caused the failure (note this short-circuits so you only see the first face to cause a failure). + int innerElementID{-1}; ///< The ID of the inner element associated with the inscribed relation that caused the failure (note this short-circuits so you only see the first element to cause a failure). + int adjacentElementID{-1}; ///< The ID of the adjacent element associated with the inscribed relation that caused the failure (note this short-circuits so you only see the first element to cause a failure). + int adjacentDomainID{-1}; ///< The ID of the adjacent domain associated with the inscribed relation that caused the failure (note this short-circuits so you only see the first element to cause a failure). }; - std::optional inscribedDiagnostics = std::nullopt; + std::optional inscribedDiagnostics = std::nullopt; ///< Optional diagnostics for the Inscribed relation type, if applicable. If no inscribed failure occurred, this will be std::nullopt. + /** + * @struct ConnectedDiagnostics + * @brief Optional diagnostics for the FullyConnected relation type. It contains information about the element ID, domain element count, and visited element count. + */ struct ConnectedDiagnostics { - int elementID{-1}; - int domainElementCount{0}; - int visitedElementCount{0}; + int elementID{-1}; ///< The ID of the element associated with the fully connected relation that caused the failure (note this short-circuits so you only see the first element to cause a failure). + int domainElementCount{0}; ///< The total number of elements in the domain associated with the fully connected relation that caused the failure. + int visitedElementCount{0}; ///< The number of elements that were visited in the domain associated with the fully connected relation before the failure occurred. }; - std::optional connectedDiagnostics = std::nullopt; + std::optional connectedDiagnostics = std::nullopt; ///< Optional diagnostics for the FullyConnected relation type, if applicable. If no fully connected failure occurred, this will be std::nullopt. + /** + * @struct DomainBoundaryDiagnostics + * @brief Optional diagnostics for the DomainBoundary relation type. It contains information about the face ID, boundary element ID, expected boundary ID, actual boundary ID, first element ID, second element ID, first domain ID, and second domain ID. + */ struct DomainBoundaryDiagnostics { - int faceID{-1}; - int boundaryElementID{-1}; + int faceID{-1}; ///< The ID of the face associated with the domain boundary relation that caused the failure (note this short-circuits so you only see the first face to cause a failure). + int boundaryElementID{-1}; ///< The ID of the boundary element associated with the domain boundary relation that caused the failure (note this short-circuits so you only see the first element to cause a failure). - int expectedBoundaryID{0}; - std::optional actualBoundaryID = std::nullopt; + int expectedBoundaryID{0}; ///< The expected ID of the boundary associated with the domain boundary relation + std::optional actualBoundaryID = std::nullopt; ///< The actual ID of the boundary associated with the domain boundary relation, if applicable. If no actual boundary ID is available, this will be std::nullopt. - int firstElementID{-1}; - int secondElementID{-1}; + int firstElementID{-1}; ///< The ID of the first element associated with the domain boundary relation that caused the failure (note this short-circuits so you only see the first element to cause a failure). + int secondElementID{-1}; ///< The ID of the second element associated with the domain boundary relation that caused the failure (note this short-circuits so you only see the first element to cause a failure). - std::optional firstDomainID = std::nullopt; - std::optional secondDomainID = std::nullopt; + std::optional firstDomainID = std::nullopt; ///< The ID of the first domain associated with the domain boundary relation, if applicable. If no first domain ID is available, this will be std::nullopt. + std::optional secondDomainID = std::nullopt; ///< The ID of the second domain associated with the domain boundary relation, if applicable. If no second domain ID is available, this will be std::nullopt. }; - std::optional domainBoundaryDiagnostics = std::nullopt; + std::optional domainBoundaryDiagnostics = std::nullopt; ///< Optional diagnostics for the DomainBoundary relation type, if applicable. If no domain boundary failure occurred, this will be std::nullopt. + /** + * @brief Check if the validation result is valid (i.e., no validation failure occurred). + * @return true if the validation result is valid, false otherwise + */ [[nodiscard]] bool valid() const noexcept { return failure == RelationValidationFailure::None; } + /** + * @brief Explicit conversion operator to bool. Returns true if the validation result is valid (i.e., no validation failure occurred), false otherwise. + * @return true if the validation result is valid, false otherwise + */ [[nodiscard]] explicit operator bool() const noexcept { return valid(); } diff --git a/src/include/serif/discretization/domain/relation/validation/validation.hpp b/src/include/serif/discretization/domain/relation/validation/validation.hpp index 522ee847..38e754de 100644 --- a/src/include/serif/discretization/domain/relation/validation/validation.hpp +++ b/src/include/serif/discretization/domain/relation/validation/validation.hpp @@ -1,5 +1,13 @@ #pragma once +/** + * @file validation.hpp + * @author Emily M. Boudreaux + * @brief Compile-time validation of relations in the discretization framework. This file provides utilities to check if relations use registered entities based on provided domain and boundary IDs. + * + * @note This header is primarily split into a singular and a plural section. The plural section simply folds over a pack to invoke the singular section. + */ + #include #include "serif/discretization/domain/concepts.hpp" @@ -9,38 +17,89 @@ #include "serif/discretization/domain/relation/lists/relation_list.hpp" #include "serif/discretization/domain/resolver.hpp" -// Compile-time check that every volume and surface named by a relation is -// actually registered in the schema's ID tables. namespace serif::discretization::domain::relation::validation { using ids::lists::IsDomainIDList; using ids::lists::IsBoundaryIDList; using lists::IsRelationList; - // This header is primarily split into a singular and a plural section. The plural section simply folds over a pack to invoke the singular section. + /** + * @brief Compile-time validation to check if a relation uses registered entities based on provided domain and boundary IDs. This struct is specialized for different relation types to determine if they use registered entities. + * + * @note None of these structs do runtime checking, these are compile-time claims which we can encode about some mesh and then, if those claims are present, they will + * be checked at runtime. + * + * @tparam RelationT The relation type to validate. + * @tparam DomainIDsT The type representing the domain IDs. + * @tparam BoundaryIDsT The type representing the boundary IDs. + */ template struct RelationUsesRegisteredEntities; + /** + * @brief Compile-time validation to check if a relation uses registered entities based on provided domain and boundary IDs. This specialization is for the Relation type FullyConnected. Fully connected + * only requires a single domain so we only enforce that the domain is registered. + * + * @tparam DomainIDsT The type representing the domain IDs. + * @tparam BoundaryIDsT The type representing the boundary IDs. + * @tparam RelationListT The type representing the list of relations. + */ template struct RelationUsesRegisteredEntities, DomainIDsT, BoundaryIDsT> : std::bool_constant::registered> { }; + /** + * @brief Compile-time validation to check if a relation uses registered entities based on provided domain and boundary IDs. This specialization is for the Relation type Inscribed. Inscribed requires two + * domains, an inner and an outer domain, and we enforce that both domains are registered. + * + * @tparam InnerDomainT The type representing the inner domain. + * @tparam OuterDomainT The type representing the outer domain. + * @tparam DomainIDsT The type representing the domain IDs. + * @tparam BoundaryIDsT The type representing the boundary IDs. + */ template struct RelationUsesRegisteredEntities, DomainIDsT, BoundaryIDsT> : std::bool_constant< DomainIDResolver::registered && DomainIDResolver::registered> { }; - //Note that the fold below is over DomainTs (the volumes the relation names). + /** + * @brief Compile-time validation to check if a relation uses registered entities based on provided domain and boundary IDs. This specialization is for the Relation type DomainBoundary. + * DomainBoundary requires a boundary and one or more domains, and we enforce that the boundary and all domains are registered. + * + * @note Since DomainBoundary is a variadic template, we use a fold expression to check that all domains are registered. + * + * @tparam BoundaryT The type representing the boundary. + * @tparam DomainTs The types representing the domains. + * @tparam DomainIDsT The type representing the domain IDs. + * @tparam BoundaryIDsT The type representing the boundary IDs. + */ template struct RelationUsesRegisteredEntities, DomainIDsT, BoundaryIDsT> : std::bool_constant< BoundaryIDResolver::registered && (DomainIDResolver::registered && ...)> { }; + /** + * @brief Compile time type to check if a list of relations uses the registered entities + * + * @tparam DomainIDsT Domain ID list type + * @tparam BoundaryIDsT Boundary ID list type + * @tparam RelationListT Relation list type + */ template struct RelationsUseRegisteredEntities; + + /** + * @brief Compile time type to check if a list of relations uses the registered entities. This specialization is for the RelationList type, which is a variadic template that takes a list of relation types as template parameters. + * It checks if all relations in the list use registered entities based on the provided domain and boundary IDs. This will distribute to the + * various specializations of the singular RelationUsesRegisteredEntities struct while folding at compile time. + * + * @tparam DomainIDsT The type representing the domain IDs. + * @tparam BoundaryIDsT The type representing the boundary IDs. + * @tparam RelationTs The types of relations in the relation list. + */ template struct RelationsUseRegisteredEntities> : std::bool_constant<(RelationUsesRegisteredEntities::value && ...)> { }; diff --git a/src/include/serif/solver/normalization/normalization.hpp b/src/include/serif/solver/normalization/normalization.hpp new file mode 100644 index 00000000..e69de29b diff --git a/src/include/serif/solver/normalization/plan.hpp b/src/include/serif/solver/normalization/plan.hpp new file mode 100644 index 00000000..e69de29b diff --git a/src/include/serif/solver/solver.hpp b/src/include/serif/solver/solver.hpp new file mode 100644 index 00000000..e69de29b diff --git a/tests/sandbox/solver_sandbox.cpp b/tests/sandbox/solver_sandbox.cpp new file mode 100644 index 00000000..e69de29b