#pragma once #include #include "serif/discretization/domain/concepts.hpp" #include "serif/discretization/domain/mesh/topology.hpp" #include "serif/discretization/domain/relation/relations.hpp" #include "serif/discretization/domain/relation/validation/runtime.hpp" #include "serif/discretization/domain/schema/concepts.hpp" #include "serif/discretization/domain/schema/validation/validator.hpp" // Inscribed: every face where the inner domain stops must have // the outer domain on the far side. namespace serif::discretization::domain::schema::validation { using relation::Inscribed; using relation::validation::RelationValidationResult; using relation::validation::RelationValidationFailure; template struct RelationValidator> { template [[nodiscard]] static RelationValidationResult validate(const mesh::MeshTopology &topology) { static_assert( SchemaT::template contains_domain(), "The inner domain of an Inscribed relation is not present in the supplied schema. Inscribed cannot be enforced" ); static_assert( SchemaT::template contains_domain(), "The outer domain of an Inscribed relation is not present in the supplied schema. Inscribed cannot be enforced" ); bool foundInnerElement = false; bool foundOuterElement = false; bool foundInnerBoundary = false; for (int elementID = 0; elementID < topology.element_count(); ++elementID) { const int domainID = topology.element_domain_id(elementID); foundInnerElement = foundInnerElement || SchemaT::template domain_id_belongs_to(domainID); foundOuterElement = foundOuterElement || SchemaT::template domain_id_belongs_to(domainID); } if (!foundInnerElement) { return {.failure = RelationValidationFailure::InnerDomainAbsent}; } if (!foundOuterElement) { return {.failure = RelationValidationFailure::OuterDomainAbsent}; } for (int faceID = 0; faceID < topology.face_count(); ++faceID) { const mesh::FaceElements faceElements = topology.face_elements(faceID); const int firstElementID = faceElements.firstElementID; const int secondElementID = faceElements.secondElementID; const bool firstIsInner = firstElementID >= 0 && SchemaT::template domain_id_belongs_to(topology.element_domain_id(firstElementID)); const bool secondIsInner = secondElementID >= 0 && SchemaT::template domain_id_belongs_to(topology.element_domain_id(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({ .faceID = faceID, .innerElementID = innerElementID, })}; } const int adjacentDomainID = topology.element_domain_id(adjacentElementID); if (!SchemaT::template domain_id_belongs_to(adjacentDomainID)) { return { .failure = RelationValidationFailure::InnerDomainTouchesUnexpectedDomain, .inscribedDiagnostics = std::make_optional({ .faceID = faceID, .innerElementID = innerElementID, .adjacentElementID = adjacentElementID, .adjacentDomainID = adjacentDomainID })}; } } if (!foundInnerBoundary) { return {.failure = RelationValidationFailure::InnerDomainHasNoBoundary}; } return {}; } }; }