module; #include #include #include #include #include #include #include export module mean_field:utils.domain; export namespace mean_field::utils::domain { struct Domain {}; struct Core final : public Domain { static constexpr std::string_view name = "core"; }; struct Envelope final : public Domain { static constexpr std::string_view name = "envelope"; }; struct Vacuum final : public Domain { static constexpr std::string_view name = "vacuum"; }; struct Boundary {}; struct StellarSurface final : public Boundary { static constexpr std::string_view name = "stellar_surface"; }; struct InfinitySurface final : public Boundary { static constexpr std::string_view name = "infinity_surface"; }; template concept IsDomain = std::is_base_of_v; template concept IsBoundary = std::is_base_of_v; template struct DomainSet {}; template constexpr bool is_domain_set_v = false; template constexpr bool is_domain_set_v> = true; template concept IsDomainSet = is_domain_set_v; template concept IsDomainOrSet = IsDomain || IsDomainSet; using Stellar = DomainSet; using All = DomainSet; struct DomainRelation {}; template struct Inscribed final : public DomainRelation { using inner_type = A; using outer_type = B; static constexpr std::string_view name = "inscribed"; }; template struct Connected final : public DomainRelation { using domain_type = A; static constexpr std::string_view name = "connected"; }; template concept IsRelation = std::is_base_of_v; template struct Material { using domain_type = D; static constexpr int id = Id; }; template struct BoundaryAttribute { using boundary_type = B; static constexpr int id = Id; }; template constexpr bool is_material_v = false; template constexpr bool is_material_v> = true; template concept IsMaterial = is_material_v; template constexpr bool is_boundary_attr_v = false; template constexpr bool is_boundary_attr_v> = true; template concept IsBoundaryAttr = is_boundary_attr_v; struct MaterialDescriptor { std::string_view name; int id; }; struct BoundaryDescriptor { std::string_view name; int id; }; template [[nodiscard]] consteval bool material_ids_are_unique() noexcept { constexpr std::array materialIds{ MaterialTs::id...}; for (std::size_t firstIndex = 0; firstIndex < materialIds.size(); ++firstIndex) { for (std::size_t secondIndex = firstIndex + 1; secondIndex < materialIds.size(); ++secondIndex) { if (materialIds[firstIndex] == materialIds[secondIndex]) { return false; } } } return true; } template [[nodiscard]] consteval bool boundary_ids_are_unique() noexcept { constexpr std::array boundaryIds{ BoundaryTs::id...}; for (std::size_t firstIndex = 0; firstIndex < boundaryIds.size(); ++firstIndex) { for (std::size_t secondIndex = firstIndex + 1; secondIndex < boundaryIds.size(); ++secondIndex) { if (boundaryIds[firstIndex] == boundaryIds[secondIndex]) { return false; } } } return true; } template struct MaterialDomainsAreUnique; template <> struct MaterialDomainsAreUnique<> : std::true_type {}; template struct MaterialDomainsAreUnique : std::true_type {}; template struct MaterialDomainsAreUnique : std::bool_constant< (!std::is_same_v && ...) && MaterialDomainsAreUnique::value> {}; template struct BoundaryTypesAreUnique; template <> struct BoundaryTypesAreUnique<> : std::true_type {}; template struct BoundaryTypesAreUnique : std::true_type {}; template struct BoundaryTypesAreUnique : std::bool_constant< (!std::is_same_v && ...) && BoundaryTypesAreUnique::value> {}; template concept HaveUniqueMaterialIds = material_ids_are_unique(); template concept HaveUniqueMaterialDomains = MaterialDomainsAreUnique::value; template concept HaveUniqueBoundaryIds = boundary_ids_are_unique(); template concept HaveUniqueBoundaryTypes = BoundaryTypesAreUnique::value; template requires(HaveUniqueMaterialIds && HaveUniqueMaterialDomains) struct MaterialList { static constexpr std::size_t count = sizeof...(MaterialTs); [[nodiscard]] static constexpr std::array descriptors() noexcept { return {MaterialDescriptor{.name = MaterialTs::domain_type::name, .id = MaterialTs::id}...}; } }; template requires(HaveUniqueBoundaryIds && HaveUniqueBoundaryTypes) struct BoundaryList { static constexpr std::size_t count = sizeof...(BoundaryTs); [[nodiscard]] static constexpr std::array descriptors() noexcept { return {BoundaryDescriptor{.name = BoundaryTs::boundary_type::name, .id = BoundaryTs::id}...}; } }; template struct DomainMaterialResolver; template struct DomainMaterialResolver> { static constexpr bool registered = (std::is_same_v || ...); [[nodiscard]] static constexpr bool contains_attribute(int materialId) noexcept { return ((std::is_same_v && MaterialTs::id == materialId) || ...); } [[nodiscard]] static consteval int attribute() { static_assert(registered, "Requested domain is not registered in this schema."); int result = 0; ((std::is_same_v ? result = MaterialTs::id : result), ...); return result; } }; template struct DomainMaterialResolver, MaterialList> { static constexpr bool registered = (DomainMaterialResolver>::registered && ...); [[nodiscard]] static constexpr bool contains_attribute(int materialId) noexcept { return (DomainMaterialResolver>:: contains_attribute(materialId) || ...); } }; template struct BoundaryAttributeResolver; template struct BoundaryAttributeResolver> { static constexpr bool registered = (std::is_same_v || ...); [[nodiscard]] static constexpr bool matches_attribute(int boundaryId) noexcept { return ((std::is_same_v && BoundaryTs::id == boundaryId) || ...); } [[nodiscard]] static consteval int attribute() { static_assert(registered, "Requested boundary is not registered " "in this schema."); int result = 0; ((std::is_same_v ? result = BoundaryTs::id : result), ...); return result; } }; template constexpr bool is_material_list_v = false; template constexpr bool is_material_list_v> = true; template concept IsMaterialList = is_material_list_v; template constexpr bool is_boundary_list_v = false; template constexpr bool is_boundary_list_v> = true; template concept IsBoundaryList = is_boundary_list_v; template struct DomainOperandList { static constexpr std::size_t count = sizeof...(DomainTs); }; template requires(sizeof...(DomainTs) == 1 || sizeof...(DomainTs) == 2) struct DomainBoundary final : public DomainRelation { using boundary_type = BoundaryT; using domains_type = DomainOperandList; static constexpr std::size_t domainCount = sizeof...(DomainTs); static constexpr std::string_view name = "domain_boundary"; }; template struct RelationList { static constexpr std::size_t count = sizeof...(RelationTs); }; template constexpr bool is_relation_list_v = false; template constexpr bool is_relation_list_v> = true; template concept IsRelationList = is_relation_list_v; /* * Compile-time validation that every semantic entity * referenced by a relation is registered by the schema. * * Connected and Inscribed only reference domains. * DomainBoundary references both a boundary and one or * two domains. */ template struct RelationUsesRegisteredEntities; template struct RelationUsesRegisteredEntities, MaterialsT, BoundariesT> : std::bool_constant< DomainMaterialResolver::registered> {}; template struct RelationUsesRegisteredEntities, MaterialsT, BoundariesT> : std::bool_constant< DomainMaterialResolver::registered && DomainMaterialResolver::registered> {}; template struct RelationUsesRegisteredEntities, MaterialsT, BoundariesT> : std::bool_constant< BoundaryAttributeResolver::registered && (DomainMaterialResolver::registered && ...)> {}; template struct RelationsUseRegisteredEntities; template struct RelationsUseRegisteredEntities> : std::bool_constant<(RelationUsesRegisteredEntities::value && ...)> {}; template concept HaveValidRelationEntities = RelationsUseRegisteredEntities::value; template requires HaveValidRelationEntities struct DomainSchema { using materials_type = Materials; using boundaries_type = Boundaries; using relations_type = Relations; static constexpr std::size_t materialCount = Materials::count; static constexpr std::size_t boundaryCount = Boundaries::count; static constexpr std::size_t relationCount = Relations::count; [[nodiscard]] static constexpr auto materials() noexcept { return Materials::descriptors(); } [[nodiscard]] static constexpr auto boundaries() noexcept { return Boundaries::descriptors(); } template [[nodiscard]] static consteval bool contains_domain() noexcept { return DomainMaterialResolver::registered; } template [[nodiscard]] static constexpr bool attribute_belongs_to(int materialId) noexcept { static_assert(contains_domain(), "Requested domain is not completely " "registered in this schema."); return DomainMaterialResolver::contains_attribute( materialId); } template [[nodiscard]] static consteval int material_attribute() noexcept { static_assert(contains_domain(), "Requested domain is not registered in this schema."); return DomainMaterialResolver::attribute(); } template [[nodiscard]] static consteval bool contains_boundary() noexcept { return BoundaryAttributeResolver::registered; } template [[nodiscard]] static consteval int boundary_attribute() noexcept { return BoundaryAttributeResolver::attribute(); } template [[nodiscard]] static constexpr bool boundary_attribute_matches(int boundaryId) noexcept { static_assert(contains_boundary(), "Requested boundary is not registered " "in this schema."); return BoundaryAttributeResolver::matches_attribute( boundaryId); } }; template constexpr bool is_schema_v = false; template constexpr bool is_schema_v> = true; template concept IsSchema = is_schema_v; enum class RelationValidationFailure { None, // Connected DomainAbsent, DomainDisconnected, // Inscribed InnerDomainAbsent, OuterDomainAbsent, InnerDomainHasNoBoundary, InnerDomainTouchesMeshBoundary, InnerDomainTouchesUnexpectedMaterial, // DomainBoundary DomainBoundaryAbsent, DomainBoundaryTaggedFaceHasWrongTopology, DomainBoundaryTaggedFaceTouchesUnexpectedMaterial, DomainBoundaryExpectedFaceIsUntagged, DomainBoundaryExpectedFaceHasWrongAttribute }; struct RelationValidationResult { RelationValidationFailure failure{RelationValidationFailure::None}; struct InscribedDiagnostics { int faceId{-1}; int innerElementId{-1}; int adjacentElementId{-1}; int adjacentMaterialId{-1}; }; std::optional inscribedDiagnostics = std::nullopt; struct ConnectedDiagnostics { int elementId{-1}; int domainElementCount{0}; int visitedElementCount{0}; }; std::optional connectedDiagnostics = std::nullopt; struct DomainBoundaryDiagnostics { int faceId{-1}; int boundaryElementId{-1}; int expectedBoundaryAttribute{0}; std::optional actualBoundaryAttribute = std::nullopt; int firstElementId{-1}; int secondElementId{-1}; std::optional firstMaterialId = std::nullopt; std::optional secondMaterialId = std::nullopt; }; std::optional domainBoundaryDiagnostics = std::nullopt; [[nodiscard]] bool valid() const noexcept { return failure == RelationValidationFailure::None; } [[nodiscard]] explicit operator bool() const noexcept { return valid(); } }; template struct RelationValidator; template struct RelationValidator> { template [[nodiscard]] static RelationValidationResult validate(const mfem::Mesh &mesh) { static_assert(SchemaT::template contains_domain(), "The inner domain of Inscribed is not " "registered in the supplied schema."); static_assert(SchemaT::template contains_domain(), "The outer domain of Inscribed is not " "registered in the supplied schema."); bool foundInnerElement = false; bool foundOuterElement = false; bool foundInnerBoundary = false; for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) { const int materialId = mesh.GetAttribute(elementId); foundInnerElement = foundInnerElement || SchemaT::template attribute_belongs_to(materialId); foundOuterElement = foundOuterElement || SchemaT::template attribute_belongs_to(materialId); } if (!foundInnerElement) { return {.failure = RelationValidationFailure::InnerDomainAbsent}; } if (!foundOuterElement) { return {.failure = RelationValidationFailure::OuterDomainAbsent}; } for (int faceId = 0; faceId < mesh.GetNumFaces(); ++faceId) { int firstElementId = -1; int secondElementId = -1; mesh.GetFaceElements(faceId, &firstElementId, &secondElementId); const bool firstIsInner = firstElementId >= 0 && SchemaT::template attribute_belongs_to( mesh.GetAttribute(firstElementId)); const bool secondIsInner = secondElementId >= 0 && SchemaT::template attribute_belongs_to( mesh.GetAttribute(secondElementId)); if (firstIsInner == secondIsInner) { continue; } foundInnerBoundary = true; const int innerElementId = firstIsInner ? firstElementId : secondElementId; const int adjacentElementId = firstIsInner ? secondElementId : firstElementId; if (adjacentElementId < 0) { return {.failure = RelationValidationFailure::InnerDomainTouchesMeshBoundary, .inscribedDiagnostics = std::make_optional< RelationValidationResult::InscribedDiagnostics>( {.faceId = faceId, .innerElementId = innerElementId})}; } const int adjacentMaterialId = mesh.GetAttribute(adjacentElementId); if (!SchemaT::template attribute_belongs_to(adjacentMaterialId)) { return { .failure = RelationValidationFailure::InnerDomainTouchesUnexpectedMaterial, .inscribedDiagnostics = std::make_optional< RelationValidationResult::InscribedDiagnostics>( {.faceId = faceId, .innerElementId = innerElementId, .adjacentElementId = adjacentElementId, .adjacentMaterialId = adjacentMaterialId})}; } } if (!foundInnerBoundary) { return {.failure = RelationValidationFailure::InnerDomainHasNoBoundary}; } return {}; } }; template struct RelationValidator> { template [[nodiscard]] static RelationValidationResult validate(const mfem::Mesh &mesh) { static_assert(SchemaT::template contains_domain(), "Connected refers to a domain which is " "not completely registered in the " "supplied DomainSchema."); std::vector belongsToDomain(static_cast(mesh.GetNE()), false); int domainElementCount = 0; int firstDomainElement = -1; for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) { const int materialId = mesh.GetAttribute(elementId); const bool belongs = SchemaT::template attribute_belongs_to(materialId); belongsToDomain[static_cast(elementId)] = belongs; if (!belongs) { continue; } ++domainElementCount; if (firstDomainElement < 0) { firstDomainElement = elementId; } } if (domainElementCount == 0) { return {.failure = RelationValidationFailure::DomainAbsent, .connectedDiagnostics = std::make_optional< RelationValidationResult::ConnectedDiagnostics>( {.domainElementCount = 0, .visitedElementCount = 0})}; } std::vector> adjacency( static_cast(mesh.GetNE())); for (int faceId = 0; faceId < mesh.GetNumFaces(); ++faceId) { int firstElementId = -1; int secondElementId = -1; mesh.GetFaceElements(faceId, &firstElementId, &secondElementId); if (firstElementId < 0 || secondElementId < 0) { continue; } const bool firstBelongs = belongsToDomain[static_cast(firstElementId)]; const bool secondBelongs = belongsToDomain[static_cast(secondElementId)]; if (!(firstBelongs && secondBelongs)) { continue; } adjacency[static_cast(firstElementId)].push_back( secondElementId); adjacency[static_cast(secondElementId)].push_back( firstElementId); } std::vector visited(static_cast(mesh.GetNE()), false); std::vector pending; pending.reserve(static_cast(domainElementCount)); pending.push_back(firstDomainElement); int visitedElementCount = 0; while (!pending.empty()) { const int elementId = pending.back(); pending.pop_back(); if (visited[static_cast(elementId)]) { continue; } visited[static_cast(elementId)] = true; ++visitedElementCount; for (const int neighborElementId : adjacency[static_cast(elementId)]) { if (!visited[static_cast(neighborElementId)]) { pending.push_back(neighborElementId); } } } if (visitedElementCount == domainElementCount) { return {.connectedDiagnostics = std::make_optional< RelationValidationResult::ConnectedDiagnostics>( {.domainElementCount = domainElementCount, .visitedElementCount = visitedElementCount})}; } int disconnectedElementId = -1; for (int elementId = 0; elementId < mesh.GetNE(); ++elementId) { const std::size_t index = static_cast(elementId); if (belongsToDomain[index] && !visited[index]) { disconnectedElementId = elementId; break; } } return { .failure = RelationValidationFailure::DomainDisconnected, .connectedDiagnostics = std::make_optional( {.elementId = disconnectedElementId, .domainElementCount = domainElementCount, .visitedElementCount = visitedElementCount})}; } }; template struct RelationValidator> { template [[nodiscard]] static RelationValidationResult validate(const mfem::Mesh &mesh) { static_assert(sizeof...(DomainTs) == 1 || sizeof...(DomainTs) == 2, "DomainBoundary requires exactly one or two domains."); static_assert(SchemaT::template contains_boundary(), "DomainBoundary refers to a boundary which is not " "registered in the supplied DomainSchema."); static_assert((SchemaT::template contains_domain() && ...), "DomainBoundary refers to a domain which is not " "completely registered in the supplied DomainSchema."); constexpr int expectedBoundaryAttribute = SchemaT::template boundary_attribute(); using DomainsTuple = std::tuple; /* * Record all MFEM boundary elements associated with each * mesh face. * * A face can in principle have more than one boundary * element associated with it. We do not require exactly * one here; instead, every boundary element on an expected * face must carry the expected semantic boundary attribute. */ std::vector> boundaryElementsByFace( static_cast(mesh.GetNumFaces())); for (int boundaryElementId = 0; boundaryElementId < mesh.GetNBE(); ++boundaryElementId) { const int faceId = mesh.GetBdrElementFaceIndex(boundaryElementId); if (faceId >= 0 && faceId < mesh.GetNumFaces()) { boundaryElementsByFace[static_cast(faceId)].push_back( boundaryElementId); } } /* * Build detailed diagnostics for one face. */ const auto make_diagnostics = [&mesh, expectedBoundaryAttribute]( int faceId, int boundaryElementId, std::optional actualBoundaryAttribute) { RelationValidationResult::DomainBoundaryDiagnostics diagnostics{ .faceId = faceId, .boundaryElementId = boundaryElementId, .expectedBoundaryAttribute = expectedBoundaryAttribute, .actualBoundaryAttribute = actualBoundaryAttribute}; if (faceId < 0 || faceId >= mesh.GetNumFaces()) { return diagnostics; } mesh.GetFaceElements(faceId, &diagnostics.firstElementId, &diagnostics.secondElementId); if (diagnostics.firstElementId >= 0) { diagnostics.firstMaterialId = mesh.GetAttribute(diagnostics.firstElementId); } if (diagnostics.secondElementId >= 0) { diagnostics.secondMaterialId = mesh.GetAttribute(diagnostics.secondElementId); } return diagnostics; }; /* * Check only the cardinality/topological shape required by * the relation. * * One-domain form: * * Domain | computational exterior * * Exactly one adjacent volume element must exist. * * Two-domain form: * * DomainA | DomainB * * Both adjacent volume elements must exist. */ const auto has_required_topology = [](int firstElementId, int secondElementId) { if constexpr (sizeof...(DomainTs) == 1) { const bool firstExists = firstElementId >= 0; const bool secondExists = secondElementId >= 0; return firstExists != secondExists; } else { return firstElementId >= 0 && secondElementId >= 0; } }; /* * Determine whether a face is exactly one of the faces * described by DomainBoundary. * * For two domains, ordering is intentionally irrelevant. */ const auto face_matches_domains = [&mesh](int firstElementId, int secondElementId) { if constexpr (sizeof...(DomainTs) == 1) { using DomainT = std::tuple_element_t<0, DomainsTuple>; const bool firstExists = firstElementId >= 0; const bool secondExists = secondElementId >= 0; if (firstExists == secondExists) { return false; } const int elementId = firstExists ? firstElementId : secondElementId; const int materialId = mesh.GetAttribute(elementId); return SchemaT::template attribute_belongs_to(materialId); } else { using FirstDomainT = std::tuple_element_t<0, DomainsTuple>; using SecondDomainT = std::tuple_element_t<1, DomainsTuple>; if (firstElementId < 0 || secondElementId < 0) { return false; } const int firstMaterialId = mesh.GetAttribute(firstElementId); const int secondMaterialId = mesh.GetAttribute(secondElementId); const bool forwardMatch = SchemaT::template attribute_belongs_to( firstMaterialId) && SchemaT::template attribute_belongs_to( secondMaterialId); const bool reverseMatch = SchemaT::template attribute_belongs_to( firstMaterialId) && SchemaT::template attribute_belongs_to( secondMaterialId); return forwardMatch || reverseMatch; } }; bool foundTaggedBoundary = false; /* * Forward validation: * * Every boundary element carrying BoundaryT must lie on * exactly the topology/material interface declared by * DomainBoundary. */ for (int boundaryElementId = 0; boundaryElementId < mesh.GetNBE(); ++boundaryElementId) { const int boundaryAttribute = mesh.GetBdrAttribute(boundaryElementId); if (boundaryAttribute != expectedBoundaryAttribute) { continue; } foundTaggedBoundary = true; const int faceId = mesh.GetBdrElementFaceIndex(boundaryElementId); int firstElementId = -1; int secondElementId = -1; mesh.GetFaceElements(faceId, &firstElementId, &secondElementId); if (!has_required_topology(firstElementId, secondElementId)) { return {.failure = RelationValidationFailure:: DomainBoundaryTaggedFaceHasWrongTopology, .domainBoundaryDiagnostics = std::make_optional< RelationValidationResult::DomainBoundaryDiagnostics>( make_diagnostics(faceId, boundaryElementId, boundaryAttribute))}; } if (!face_matches_domains(firstElementId, secondElementId)) { return {.failure = RelationValidationFailure:: DomainBoundaryTaggedFaceTouchesUnexpectedMaterial, .domainBoundaryDiagnostics = std::make_optional< RelationValidationResult::DomainBoundaryDiagnostics>( make_diagnostics(faceId, boundaryElementId, boundaryAttribute))}; } } bool foundExpectedFace = false; /* * Reverse validation: * * Every face having the declared domain adjacency must * carry BoundaryT. * * This is important for physical constraints: a partially * tagged Stellar/Vacuum interface must fail rather than * silently leaving part of the stellar surface unconstrained. */ for (int faceId = 0; faceId < mesh.GetNumFaces(); ++faceId) { int firstElementId = -1; int secondElementId = -1; mesh.GetFaceElements(faceId, &firstElementId, &secondElementId); if (!face_matches_domains(firstElementId, secondElementId)) { continue; } foundExpectedFace = true; const auto &boundaryElementIds = boundaryElementsByFace[static_cast(faceId)]; if (boundaryElementIds.empty()) { return { .failure = RelationValidationFailure::DomainBoundaryExpectedFaceIsUntagged, .domainBoundaryDiagnostics = std::make_optional< RelationValidationResult::DomainBoundaryDiagnostics>( make_diagnostics(faceId, -1, std::nullopt))}; } for (const int boundaryElementId : boundaryElementIds) { const int actualBoundaryAttribute = mesh.GetBdrAttribute(boundaryElementId); if (actualBoundaryAttribute == expectedBoundaryAttribute) { continue; } return {.failure = RelationValidationFailure:: DomainBoundaryExpectedFaceHasWrongAttribute, .domainBoundaryDiagnostics = std::make_optional< RelationValidationResult::DomainBoundaryDiagnostics>( make_diagnostics(faceId, boundaryElementId, actualBoundaryAttribute))}; } } /* * If neither a correctly tagged boundary nor a face having * the required semantic topology exists, the declared * DomainBoundary simply is not realized by this mesh. * * In the usual partial-failure cases above we will already * have returned a more specific diagnostic. */ if (!foundTaggedBoundary || !foundExpectedFace) { return {.failure = RelationValidationFailure::DomainBoundaryAbsent, .domainBoundaryDiagnostics = std::make_optional< RelationValidationResult::DomainBoundaryDiagnostics>( make_diagnostics(-1, -1, std::nullopt))}; } return {}; } }; struct SchemaRelationValidationResult { std::size_t relationIndex{0}; std::string_view relationName; RelationValidationResult result; [[nodiscard]] bool valid() const noexcept { return result.valid(); } [[nodiscard]] explicit operator bool() const noexcept { return valid(); } }; struct SchemaValidationResult { std::vector relationResults; [[nodiscard]] bool valid() const noexcept { for (const auto &relationResult : relationResults) { if (!relationResult.valid()) { return false; } } return true; } [[nodiscard]] explicit operator bool() const noexcept { return valid(); } [[nodiscard]] std::size_t relation_count() const noexcept { return relationResults.size(); } [[nodiscard]] std::size_t failed_relation_count() const noexcept { std::size_t failureCount = 0; for (const auto &relationResult : relationResults) { if (!relationResult.valid()) { ++failureCount; } } return failureCount; } [[nodiscard]] std::size_t passed_relation_count() const noexcept { return relationResults.size() - failed_relation_count(); } [[nodiscard]] std::optional first_failed_relation_index() const noexcept { for (std::size_t relationIndex = 0; relationIndex < relationResults.size(); ++relationIndex) { if (!relationResults[relationIndex].valid()) { return relationIndex; } } return std::nullopt; } }; template struct SchemaRelationValidator; template struct SchemaRelationValidator> { [[nodiscard]] static SchemaValidationResult validate(const mfem::Mesh &mesh) { SchemaValidationResult schemaResult; schemaResult.relationResults.reserve(sizeof...(RelationTs)); std::size_t relationIndex = 0; (schemaResult.relationResults.push_back(SchemaRelationValidationResult{ .relationIndex = relationIndex++, .relationName = RelationTs::name, .result = RelationValidator::template validate(mesh)}), ...); return schemaResult; } }; template [[nodiscard]] SchemaValidationResult validate_schema(const mfem::Mesh &mesh) { using RelationsT = typename SchemaT::relations_type; return SchemaRelationValidator::validate(mesh); } template [[nodiscard]] mfem::Array make_attribute_marker(const mfem::Mesh &mesh) { static_assert(SchemaT::template contains_domain(), "Requested marker domain is not completely registered in the " "supplied DomainSchema."); mfem::Array marker(mesh.attributes.Max()); for (int attribute = 1; attribute <= marker.Size(); ++attribute) { marker[attribute - 1] = SchemaT::template attribute_belongs_to(attribute) ? 1 : 0; } return marker; } using CoreEnvelopeVacuumDomainSchema = DomainSchema< MaterialList, Material, Material>, BoundaryList, BoundaryAttribute>, RelationList< // All Domains must be fully connected Connected, Connected, Connected, // Describe the topology of the mesh (core must be within envelope and // the stellar domain (core + envelope) must be inscribed within vacuum // region Inscribed, Inscribed, // The stellar surface sits between the stellar and vacuum domain and // the infinity surface sits at the outside of the vacuum domain DomainBoundary, DomainBoundary>>; } // namespace mean_field::utils::domain