feat(mean_field): added dimensions, discritization, and start of eos
The full rewrite of mean_field into something maintainable is progressing. dimensions is mostly done, discritization (domain, blocks, and fields) is done, and eos is progressing quickly
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#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"
|
||||
|
||||
namespace serif::discretization::domain::schema::validation {
|
||||
using relation::DomainBoundary;
|
||||
|
||||
using relation::validation::RelationValidationResult;
|
||||
using relation::validation::RelationValidationFailure;
|
||||
|
||||
template <IsBoundary BoundaryT, IsDomainOrSet... DomainTs>
|
||||
struct RelationValidator<DomainBoundary<BoundaryT, DomainTs...>> {
|
||||
template <IsSchema SchemaT>
|
||||
[[nodiscard]] static RelationValidationResult validate(const mesh::MeshTopology &topology) {
|
||||
static_assert(
|
||||
sizeof...(DomainTs) == 1 || sizeof...(DomainTs) == 2,
|
||||
"DomainBoundary requires exactly one or two domains."
|
||||
);
|
||||
|
||||
static_assert(
|
||||
SchemaT::template contains_boundary<BoundaryT>(),
|
||||
"DomainBoundary refers to a boundary which is not registered in the supplied DomainSchema."
|
||||
);
|
||||
|
||||
static_assert(
|
||||
(SchemaT::template contains_domain<DomainTs>() && ...),
|
||||
"DomainBoundary refers to a domain which is not completely registered in the supplied DomainSchema."
|
||||
);
|
||||
|
||||
constexpr int expectedBoundaryID = SchemaT::template boundary_id<BoundaryT>();
|
||||
|
||||
using DomainsTuple = std::tuple<DomainTs...>;
|
||||
|
||||
const auto make_diagnostics = [&](const int faceID, const int boundaryElementID, const std::optional<int> actualBoundaryID) {
|
||||
RelationValidationResult::DomainBoundaryDiagnostics diagnostics{
|
||||
.faceID = faceID,
|
||||
.boundaryElementID = boundaryElementID,
|
||||
.expectedBoundaryID = expectedBoundaryID,
|
||||
.actualBoundaryID = actualBoundaryID
|
||||
};
|
||||
|
||||
if (faceID < 0 || faceID >= topology.face_count()) {
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
const mesh::FaceElements faceElements = topology.face_elements(faceID);
|
||||
|
||||
diagnostics.firstElementID = faceElements.firstElementID;
|
||||
diagnostics.secondElementID = faceElements.secondElementID;
|
||||
|
||||
if (diagnostics.firstElementID >= 0) {
|
||||
diagnostics.firstDomainID = topology.element_domain_id(diagnostics.firstElementID);
|
||||
}
|
||||
if (diagnostics.secondElementID >= 0) {
|
||||
diagnostics.secondDomainID = topology.element_domain_id(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 = [](const int firstElementID, const int secondElementID) noexcept {
|
||||
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<BoundaryT, DomainTs...>. For two domains, ordering
|
||||
// is intentionally irrelevant.
|
||||
const auto face_matches_domains = [&topology](const int firstElementID, const int secondElementID) noexcept {
|
||||
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 domainID = topology.element_domain_id(elementID);
|
||||
|
||||
return SchemaT::template domain_id_belongs_to<DomainT>(domainID);
|
||||
} 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 firstDomainID = topology.element_domain_id(firstElementID);
|
||||
const int secondDomainID = topology.element_domain_id(secondElementID);
|
||||
|
||||
const bool forwardMatch = SchemaT::template domain_id_belongs_to<FirstDomainT>(firstDomainID) &&
|
||||
SchemaT::template domain_id_belongs_to<SecondDomainT>(secondDomainID);
|
||||
|
||||
const bool reverseMatch = SchemaT::template domain_id_belongs_to<SecondDomainT>(firstDomainID) &&
|
||||
SchemaT::template domain_id_belongs_to<FirstDomainT>(secondDomainID);
|
||||
|
||||
return forwardMatch || reverseMatch;
|
||||
}
|
||||
};
|
||||
|
||||
bool foundTaggedBoundary = false;
|
||||
|
||||
// Forward validation: every boundary element carrying BoundaryT must
|
||||
// lie on exactly the interface declared by DomainBoundary.
|
||||
for (int boundaryElementID = 0; boundaryElementID < topology.boundary_element_count(); ++boundaryElementID) {
|
||||
const int boundaryID = topology.boundary_element_boundary_id(boundaryElementID);
|
||||
|
||||
if (boundaryID != expectedBoundaryID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foundTaggedBoundary = true;
|
||||
|
||||
const int faceID = topology.boundary_element_face(boundaryElementID);
|
||||
|
||||
const auto [firstElementID, secondElementID] = topology.face_elements(faceID);
|
||||
|
||||
if (!has_required_topology(firstElementID, secondElementID)) {
|
||||
return {
|
||||
.failure = RelationValidationFailure::DomainBoundaryTaggedFaceHasWrongTopology,
|
||||
.domainBoundaryDiagnostics =
|
||||
std::make_optional<RelationValidationResult::DomainBoundaryDiagnostics>(
|
||||
make_diagnostics(faceID, boundaryElementID, boundaryID)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
if (!face_matches_domains(firstElementID, secondElementID)) {
|
||||
return {
|
||||
.failure = RelationValidationFailure::DomainBoundaryTaggedFaceTouchesUnexpectedDomain,
|
||||
.domainBoundaryDiagnostics =
|
||||
std::make_optional<RelationValidationResult::DomainBoundaryDiagnostics>(
|
||||
make_diagnostics(faceID, boundaryElementID, boundaryID)
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
bool foundExpectedFace = false;
|
||||
|
||||
// Reverse validation: every face having the declared domain
|
||||
// adjacency must carry BoundaryT.
|
||||
for (int faceID = 0; faceID < topology.face_count(); ++faceID) {
|
||||
const auto [firstElementID, secondElementID] = topology.face_elements(faceID);
|
||||
|
||||
if (!face_matches_domains(firstElementID, secondElementID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foundExpectedFace = true;
|
||||
|
||||
const std::vector<int> &boundaryElementIDs = topology.boundary_elements_on_face(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 actualBoundaryID = topology.boundary_element_boundary_id(boundaryElementID);
|
||||
|
||||
if (actualBoundaryID == expectedBoundaryID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return {
|
||||
.failure = RelationValidationFailure::DomainBoundaryExpectedFaceHasWrongID,
|
||||
.domainBoundaryDiagnostics =
|
||||
std::make_optional<RelationValidationResult::DomainBoundaryDiagnostics>(
|
||||
make_diagnostics(faceID, boundaryElementID, actualBoundaryID)
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (!foundTaggedBoundary || !foundExpectedFace) {
|
||||
return {
|
||||
.failure = RelationValidationFailure::DomainBoundaryAbsent,
|
||||
.domainBoundaryDiagnostics =
|
||||
std::make_optional<RelationValidationResult::DomainBoundaryDiagnostics>(
|
||||
make_diagnostics(-1, -1, std::nullopt)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user