1174 lines
43 KiB
C++
1174 lines
43 KiB
C++
#include <array>
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <cstddef>
|
|
#include <mfem.hpp>
|
|
#include <optional>
|
|
#include <string_view>
|
|
#include <stroid/stroid.h>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
import mean_field;
|
|
import test_helpers;
|
|
|
|
namespace domain_test_utils {
|
|
struct UnregisteredDomain final : public mean_field::utils::domain::Domain {
|
|
static constexpr std::string_view name = "unregistered_domain";
|
|
};
|
|
|
|
struct UnregisteredBoundary final : public mean_field::utils::domain::Boundary {
|
|
static constexpr std::string_view name = "unregistered_boundary";
|
|
};
|
|
|
|
struct BoundaryEdge {
|
|
int firstVertexId{-1};
|
|
int secondVertexId{-1};
|
|
int attribute{0};
|
|
};
|
|
|
|
struct StroidCase {
|
|
std::string_view name;
|
|
int refinementLevels{0};
|
|
int order{1};
|
|
double flattening{0.0};
|
|
};
|
|
|
|
template <typename... MaterialTs>
|
|
concept CanFormMaterialList = requires { typename mean_field::utils::domain::MaterialList<MaterialTs...>; };
|
|
|
|
template <typename... BoundaryTs>
|
|
concept CanFormBoundaryList = requires { typename mean_field::utils::domain::BoundaryList<BoundaryTs...>; };
|
|
|
|
template <typename BoundaryT, typename... DomainTs>
|
|
concept CanFormDomainBoundary =
|
|
requires { typename mean_field::utils::domain::DomainBoundary<BoundaryT, DomainTs...>; };
|
|
|
|
template <typename MaterialsT, typename BoundariesT, typename RelationsT>
|
|
concept CanFormSchema =
|
|
requires { typename mean_field::utils::domain::DomainSchema<MaterialsT, BoundariesT, RelationsT>; };
|
|
|
|
[[nodiscard]]
|
|
int vertex_id(
|
|
const int xElementCount,
|
|
const int x,
|
|
const int y
|
|
) {
|
|
return y * (xElementCount + 1) + x;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
int cell_index(
|
|
const int xElementCount,
|
|
const int x,
|
|
const int y
|
|
) {
|
|
return y * xElementCount + x;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
int cell_attribute(
|
|
const std::vector<int> &attributes,
|
|
const int xElementCount,
|
|
const int x,
|
|
const int y
|
|
) {
|
|
return attributes.at(static_cast<std::size_t>(cell_index(xElementCount, x, y)));
|
|
}
|
|
|
|
template <
|
|
typename FirstPredicateT,
|
|
typename SecondPredicateT>
|
|
void append_interface_boundaries(
|
|
std::vector<BoundaryEdge> &boundaries,
|
|
const std::vector<int> &attributes,
|
|
const int xElementCount,
|
|
const int yElementCount,
|
|
FirstPredicateT firstPredicate,
|
|
SecondPredicateT secondPredicate,
|
|
const int boundaryAttribute
|
|
) {
|
|
/*
|
|
* Vertical internal faces.
|
|
*/
|
|
for (int y = 0; y < yElementCount; ++y) {
|
|
for (int x = 1; x < xElementCount; ++x) {
|
|
const int leftAttribute = cell_attribute(attributes, xElementCount, x - 1, y);
|
|
|
|
const int rightAttribute = cell_attribute(attributes, xElementCount, x, y);
|
|
|
|
const bool matches = (firstPredicate(leftAttribute) && secondPredicate(rightAttribute)) ||
|
|
(secondPredicate(leftAttribute) && firstPredicate(rightAttribute));
|
|
|
|
if (!matches) {
|
|
continue;
|
|
}
|
|
|
|
boundaries.push_back(
|
|
{.firstVertexId = vertex_id(xElementCount, x, y),
|
|
.secondVertexId = vertex_id(xElementCount, x, y + 1),
|
|
.attribute = boundaryAttribute}
|
|
);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Horizontal internal faces.
|
|
*/
|
|
for (int y = 1; y < yElementCount; ++y) {
|
|
for (int x = 0; x < xElementCount; ++x) {
|
|
const int lowerAttribute = cell_attribute(attributes, xElementCount, x, y - 1);
|
|
|
|
const int upperAttribute = cell_attribute(attributes, xElementCount, x, y);
|
|
|
|
const bool matches = (firstPredicate(lowerAttribute) && secondPredicate(upperAttribute)) ||
|
|
(secondPredicate(lowerAttribute) && firstPredicate(upperAttribute));
|
|
|
|
if (!matches) {
|
|
continue;
|
|
}
|
|
|
|
boundaries.push_back(
|
|
{.firstVertexId = vertex_id(xElementCount, x, y),
|
|
.secondVertexId = vertex_id(xElementCount, x + 1, y),
|
|
.attribute = boundaryAttribute}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename PredicateT>
|
|
void append_exterior_boundaries(
|
|
std::vector<BoundaryEdge> &boundaries,
|
|
const std::vector<int> &attributes,
|
|
const int xElementCount,
|
|
const int yElementCount,
|
|
PredicateT predicate,
|
|
const int boundaryAttribute
|
|
) {
|
|
/*
|
|
* Bottom.
|
|
*/
|
|
for (int x = 0; x < xElementCount; ++x) {
|
|
if (predicate(cell_attribute(attributes, xElementCount, x, 0))) {
|
|
boundaries.push_back(
|
|
{.firstVertexId = vertex_id(xElementCount, x, 0),
|
|
.secondVertexId = vertex_id(xElementCount, x + 1, 0),
|
|
.attribute = boundaryAttribute}
|
|
);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Top.
|
|
*/
|
|
for (int x = 0; x < xElementCount; ++x) {
|
|
if (predicate(cell_attribute(attributes, xElementCount, x, yElementCount - 1))) {
|
|
boundaries.push_back(
|
|
{.firstVertexId = vertex_id(xElementCount, x, yElementCount),
|
|
.secondVertexId = vertex_id(xElementCount, x + 1, yElementCount),
|
|
.attribute = boundaryAttribute}
|
|
);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Left.
|
|
*/
|
|
for (int y = 0; y < yElementCount; ++y) {
|
|
if (predicate(cell_attribute(attributes, xElementCount, 0, y))) {
|
|
boundaries.push_back(
|
|
{.firstVertexId = vertex_id(xElementCount, 0, y),
|
|
.secondVertexId = vertex_id(xElementCount, 0, y + 1),
|
|
.attribute = boundaryAttribute}
|
|
);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Right.
|
|
*/
|
|
for (int y = 0; y < yElementCount; ++y) {
|
|
if (predicate(cell_attribute(attributes, xElementCount, xElementCount - 1, y))) {
|
|
boundaries.push_back(
|
|
{.firstVertexId = vertex_id(xElementCount, xElementCount, y),
|
|
.secondVertexId = vertex_id(xElementCount, xElementCount, y + 1),
|
|
.attribute = boundaryAttribute}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
[[nodiscard]]
|
|
mfem::Mesh make_grid_mesh(
|
|
const int xElementCount,
|
|
const int yElementCount,
|
|
const std::vector<int> &attributes,
|
|
const std::vector<BoundaryEdge> &boundaryEdges
|
|
) {
|
|
REQUIRE(static_cast<int>(attributes.size()) == xElementCount * yElementCount);
|
|
|
|
mfem::Mesh mesh(
|
|
2, (xElementCount + 1) * (yElementCount + 1), xElementCount * yElementCount,
|
|
static_cast<int>(boundaryEdges.size()), 2
|
|
);
|
|
|
|
for (int y = 0; y <= yElementCount; ++y) {
|
|
for (int x = 0; x <= xElementCount; ++x) {
|
|
mesh.AddVertex(static_cast<double>(x), static_cast<double>(y));
|
|
}
|
|
}
|
|
|
|
for (int y = 0; y < yElementCount; ++y) {
|
|
for (int x = 0; x < xElementCount; ++x) {
|
|
const int lowerLeft = vertex_id(xElementCount, x, y);
|
|
|
|
const int lowerRight = vertex_id(xElementCount, x + 1, y);
|
|
|
|
const int upperRight = vertex_id(xElementCount, x + 1, y + 1);
|
|
|
|
const int upperLeft = vertex_id(xElementCount, x, y + 1);
|
|
|
|
mesh.AddQuad(
|
|
lowerLeft, lowerRight, upperRight, upperLeft, cell_attribute(attributes, xElementCount, x, y)
|
|
);
|
|
}
|
|
}
|
|
|
|
for (const BoundaryEdge &boundary : boundaryEdges) {
|
|
mesh.AddBdrSegment(boundary.firstVertexId, boundary.secondVertexId, boundary.attribute);
|
|
}
|
|
|
|
mesh.FinalizeTopology(false);
|
|
mesh.Finalize(false, false);
|
|
|
|
REQUIRE(mesh.GetNBE() == static_cast<int>(boundaryEdges.size()));
|
|
|
|
return mesh;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
std::vector<int> make_layered_attributes() {
|
|
constexpr int xElementCount = 5;
|
|
constexpr int yElementCount = 5;
|
|
|
|
std::vector<int> attributes(xElementCount * yElementCount, 3);
|
|
|
|
for (int y = 1; y <= 3; ++y) {
|
|
for (int x = 1; x <= 3; ++x) {
|
|
attributes[static_cast<std::size_t>(cell_index(xElementCount, x, y))] = 2;
|
|
}
|
|
}
|
|
|
|
attributes[static_cast<std::size_t>(cell_index(xElementCount, 2, 2))] = 1;
|
|
|
|
return attributes;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
mfem::Mesh make_layered_mesh(
|
|
const bool includeStellarSurface = true,
|
|
const bool includeInfinitySurface = true,
|
|
const int stellarSurfaceAttribute = 1,
|
|
const int infinitySurfaceAttribute = 2
|
|
) {
|
|
constexpr int xElementCount = 5;
|
|
constexpr int yElementCount = 5;
|
|
|
|
const std::vector<int> attributes = make_layered_attributes();
|
|
|
|
std::vector<BoundaryEdge> boundaries;
|
|
|
|
const auto isStellar = [](const int materialId) { return materialId == 1 || materialId == 2; };
|
|
|
|
const auto isVacuum = [](const int materialId) { return materialId == 3; };
|
|
|
|
if (includeStellarSurface) {
|
|
append_interface_boundaries(
|
|
boundaries, attributes, xElementCount, yElementCount, isStellar, isVacuum, stellarSurfaceAttribute
|
|
);
|
|
}
|
|
|
|
if (includeInfinitySurface) {
|
|
append_exterior_boundaries(
|
|
boundaries, attributes, xElementCount, yElementCount, isVacuum, infinitySurfaceAttribute
|
|
);
|
|
}
|
|
|
|
return make_grid_mesh(xElementCount, yElementCount, attributes, boundaries);
|
|
}
|
|
|
|
template <typename SchemaT> void check_schema_is_valid(const mfem::Mesh &mesh) {
|
|
const auto validation = mean_field::utils::domain::validate_schema<SchemaT>(mesh);
|
|
|
|
CHECK(validation.relationResults.size() == SchemaT::relationCount);
|
|
|
|
for (const auto &relationResult : validation.relationResults) {
|
|
INFO("Relation index = " << relationResult.relationIndex);
|
|
|
|
INFO("Relation name = " << relationResult.relationName);
|
|
|
|
INFO("Failure enum = " << static_cast<int>(relationResult.result.failure));
|
|
|
|
CHECK(relationResult.valid());
|
|
}
|
|
|
|
CHECK(validation.valid());
|
|
}
|
|
|
|
using AlternateIdSchema = mean_field::utils::domain::DomainSchema<
|
|
mean_field::utils::domain::MaterialList<
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Core, 11>,
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Envelope, 17>,
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Vacuum, 29>>,
|
|
mean_field::utils::domain::BoundaryList<
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::StellarSurface, 101>,
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::InfinitySurface, 203>>,
|
|
mean_field::utils::domain::RelationList<
|
|
mean_field::utils::domain::Connected<mean_field::utils::domain::Core>,
|
|
mean_field::utils::domain::Connected<mean_field::utils::domain::Envelope>,
|
|
mean_field::utils::domain::Connected<mean_field::utils::domain::Vacuum>,
|
|
mean_field::utils::domain::Inscribed<mean_field::utils::domain::Core, mean_field::utils::domain::Envelope>,
|
|
mean_field::utils::domain::Inscribed<mean_field::utils::domain::Stellar, mean_field::utils::domain::Vacuum>,
|
|
mean_field::utils::domain::DomainBoundary<
|
|
mean_field::utils::domain::StellarSurface,
|
|
mean_field::utils::domain::Stellar,
|
|
mean_field::utils::domain::Vacuum>,
|
|
mean_field::utils::domain::
|
|
DomainBoundary<mean_field::utils::domain::InfinitySurface, mean_field::utils::domain::Vacuum>>>;
|
|
|
|
[[nodiscard]]
|
|
stroid::config::MeshConfig make_stroid_config(
|
|
const int refinementLevels,
|
|
const int order,
|
|
const double flattening
|
|
) {
|
|
stroid::config::MeshConfig config;
|
|
|
|
config.refinement_levels = refinementLevels;
|
|
|
|
config.order = order;
|
|
|
|
config.include_external_domain = true;
|
|
|
|
config.r_core = 0.25;
|
|
|
|
config.r_star = 1.0;
|
|
|
|
config.r_infinity = 4.0;
|
|
|
|
config.flattening = flattening;
|
|
|
|
config.core_id = 1;
|
|
|
|
config.envelope_id = 2;
|
|
|
|
config.vacuum_id = 3;
|
|
|
|
config.surface_bdr_id = 1;
|
|
|
|
config.inf_bdr_id = 2;
|
|
|
|
config.optimization_methods = stroid::config::OptimizationMethods{.tmop = false, .smoothstep = true};
|
|
|
|
return config;
|
|
}
|
|
} // namespace domain_test_utils
|
|
|
|
TEST_CASE(
|
|
"Domain Types And Composite Domains Preserve Their Semantic Categories",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
STATIC_REQUIRE(mean_field::utils::domain::IsDomain<mean_field::utils::domain::Core>);
|
|
|
|
STATIC_REQUIRE(mean_field::utils::domain::IsDomain<mean_field::utils::domain::Envelope>);
|
|
|
|
STATIC_REQUIRE(mean_field::utils::domain::IsDomain<mean_field::utils::domain::Vacuum>);
|
|
|
|
STATIC_REQUIRE(mean_field::utils::domain::IsDomainSet<mean_field::utils::domain::Stellar>);
|
|
|
|
STATIC_REQUIRE(mean_field::utils::domain::IsDomainSet<mean_field::utils::domain::All>);
|
|
|
|
STATIC_REQUIRE_FALSE(mean_field::utils::domain::IsDomain<mean_field::utils::domain::Stellar>);
|
|
|
|
STATIC_REQUIRE(mean_field::utils::domain::IsDomainOrSet<mean_field::utils::domain::Stellar>);
|
|
|
|
STATIC_REQUIRE(mean_field::utils::domain::IsBoundary<mean_field::utils::domain::StellarSurface>);
|
|
|
|
STATIC_REQUIRE(mean_field::utils::domain::IsBoundary<mean_field::utils::domain::InfinitySurface>);
|
|
|
|
CHECK(true);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Material Lists Reject Duplicate Ids And Duplicate Semantic Domains",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
STATIC_REQUIRE(
|
|
domain_test_utils::CanFormMaterialList<
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Core, 1>,
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Envelope, 2>>
|
|
);
|
|
|
|
STATIC_REQUIRE_FALSE(
|
|
domain_test_utils::CanFormMaterialList<
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Core, 1>,
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Envelope, 1>>
|
|
);
|
|
|
|
STATIC_REQUIRE_FALSE(
|
|
domain_test_utils::CanFormMaterialList<
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Core, 1>,
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Core, 2>>
|
|
);
|
|
|
|
/*
|
|
* The schema intentionally imposes no convention on the
|
|
* numerical range or indexing scheme used by a mesh producer.
|
|
*/
|
|
STATIC_REQUIRE(
|
|
domain_test_utils::CanFormMaterialList<
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Core, 0>,
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Envelope, -7>,
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Vacuum, 42>>
|
|
);
|
|
|
|
CHECK(true);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Boundary Lists Reject Duplicate Ids And Duplicate Semantic Boundaries",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
STATIC_REQUIRE(
|
|
domain_test_utils::CanFormBoundaryList<
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::StellarSurface, 1>,
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::InfinitySurface, 2>>
|
|
);
|
|
|
|
STATIC_REQUIRE_FALSE(
|
|
domain_test_utils::CanFormBoundaryList<
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::StellarSurface, 1>,
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::InfinitySurface, 1>>
|
|
);
|
|
|
|
STATIC_REQUIRE_FALSE(
|
|
domain_test_utils::CanFormBoundaryList<
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::StellarSurface, 1>,
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::StellarSurface, 2>>
|
|
);
|
|
|
|
STATIC_REQUIRE(
|
|
domain_test_utils::CanFormBoundaryList<
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::StellarSurface, 0>,
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::InfinitySurface, -13>>
|
|
);
|
|
|
|
CHECK(true);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Domain Boundary Relations Accept Exactly One Or Two Domains",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
STATIC_REQUIRE(
|
|
domain_test_utils::CanFormDomainBoundary<
|
|
mean_field::utils::domain::InfinitySurface, mean_field::utils::domain::Vacuum>
|
|
);
|
|
|
|
STATIC_REQUIRE(
|
|
domain_test_utils::CanFormDomainBoundary<
|
|
mean_field::utils::domain::StellarSurface, mean_field::utils::domain::Stellar,
|
|
mean_field::utils::domain::Vacuum>
|
|
);
|
|
|
|
STATIC_REQUIRE_FALSE(domain_test_utils::CanFormDomainBoundary<mean_field::utils::domain::StellarSurface>);
|
|
|
|
STATIC_REQUIRE_FALSE(
|
|
domain_test_utils::CanFormDomainBoundary<
|
|
mean_field::utils::domain::StellarSurface, mean_field::utils::domain::Core,
|
|
mean_field::utils::domain::Envelope, mean_field::utils::domain::Vacuum>
|
|
);
|
|
|
|
CHECK(true);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Domain Schemas Reject Relations That Reference Unregistered Entities",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
using IncompleteMaterials = mean_field::utils::domain::MaterialList<
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Core, 1>,
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Vacuum, 3>>;
|
|
|
|
using CompleteMaterials = mean_field::utils::domain::MaterialList<
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Core, 1>,
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Envelope, 2>,
|
|
mean_field::utils::domain::Material<mean_field::utils::domain::Vacuum, 3>>;
|
|
|
|
using CompleteBoundaries = mean_field::utils::domain::BoundaryList<
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::StellarSurface, 1>,
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::InfinitySurface, 2>>;
|
|
|
|
using InfinityOnlyBoundary = mean_field::utils::domain::BoundaryList<
|
|
mean_field::utils::domain::BoundaryAttribute<mean_field::utils::domain::InfinitySurface, 2>>;
|
|
|
|
using MissingEnvelopeRelation = mean_field::utils::domain::RelationList<
|
|
mean_field::utils::domain::Connected<mean_field::utils::domain::Envelope>>;
|
|
|
|
using MissingBoundaryRelation = mean_field::utils::domain::RelationList<mean_field::utils::domain::DomainBoundary<
|
|
mean_field::utils::domain::StellarSurface, mean_field::utils::domain::Stellar,
|
|
mean_field::utils::domain::Vacuum>>;
|
|
|
|
STATIC_REQUIRE_FALSE(
|
|
domain_test_utils::CanFormSchema<IncompleteMaterials, CompleteBoundaries, MissingEnvelopeRelation>
|
|
);
|
|
|
|
STATIC_REQUIRE_FALSE(
|
|
domain_test_utils::CanFormSchema<CompleteMaterials, InfinityOnlyBoundary, MissingBoundaryRelation>
|
|
);
|
|
|
|
CHECK(true);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Core Envelope Vacuum Schema Exposes Exact Compile Time And Runtime "
|
|
"Metadata",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
using SchemaT = mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema;
|
|
|
|
STATIC_REQUIRE(mean_field::utils::domain::IsSchema<SchemaT>);
|
|
|
|
STATIC_REQUIRE(SchemaT::materialCount == 3);
|
|
|
|
STATIC_REQUIRE(SchemaT::boundaryCount == 2);
|
|
|
|
STATIC_REQUIRE(SchemaT::relationCount == 7);
|
|
|
|
constexpr auto materials = SchemaT::materials();
|
|
|
|
constexpr auto boundaries = SchemaT::boundaries();
|
|
|
|
STATIC_REQUIRE(materials[0].name == std::string_view{"core"});
|
|
|
|
STATIC_REQUIRE(materials[0].id == 1);
|
|
|
|
STATIC_REQUIRE(materials[1].name == std::string_view{"envelope"});
|
|
|
|
STATIC_REQUIRE(materials[1].id == 2);
|
|
|
|
STATIC_REQUIRE(materials[2].name == std::string_view{"vacuum"});
|
|
|
|
STATIC_REQUIRE(materials[2].id == 3);
|
|
|
|
STATIC_REQUIRE(boundaries[0].name == std::string_view{"stellar_surface"});
|
|
|
|
STATIC_REQUIRE(boundaries[0].id == 1);
|
|
|
|
STATIC_REQUIRE(boundaries[1].name == std::string_view{"infinity_surface"});
|
|
|
|
STATIC_REQUIRE(boundaries[1].id == 2);
|
|
|
|
STATIC_REQUIRE(SchemaT::template contains_domain<mean_field::utils::domain::Core>());
|
|
|
|
STATIC_REQUIRE(SchemaT::template contains_domain<mean_field::utils::domain::Stellar>());
|
|
|
|
STATIC_REQUIRE(SchemaT::template contains_domain<mean_field::utils::domain::All>());
|
|
|
|
STATIC_REQUIRE(SchemaT::template attribute_belongs_to<mean_field::utils::domain::Stellar>(1));
|
|
|
|
STATIC_REQUIRE(SchemaT::template attribute_belongs_to<mean_field::utils::domain::Stellar>(2));
|
|
|
|
STATIC_REQUIRE_FALSE(SchemaT::template attribute_belongs_to<mean_field::utils::domain::Stellar>(3));
|
|
|
|
STATIC_REQUIRE(SchemaT::template attribute_belongs_to<mean_field::utils::domain::All>(1));
|
|
|
|
STATIC_REQUIRE(SchemaT::template attribute_belongs_to<mean_field::utils::domain::All>(2));
|
|
|
|
STATIC_REQUIRE(SchemaT::template attribute_belongs_to<mean_field::utils::domain::All>(3));
|
|
|
|
STATIC_REQUIRE(SchemaT::template material_attribute<mean_field::utils::domain::Core>() == 1);
|
|
|
|
STATIC_REQUIRE(SchemaT::template material_attribute<mean_field::utils::domain::Envelope>() == 2);
|
|
|
|
STATIC_REQUIRE(SchemaT::template material_attribute<mean_field::utils::domain::Vacuum>() == 3);
|
|
|
|
STATIC_REQUIRE(SchemaT::template contains_boundary<mean_field::utils::domain::StellarSurface>());
|
|
|
|
STATIC_REQUIRE(SchemaT::template contains_boundary<mean_field::utils::domain::InfinitySurface>());
|
|
|
|
STATIC_REQUIRE(SchemaT::template boundary_attribute<mean_field::utils::domain::StellarSurface>() == 1);
|
|
|
|
STATIC_REQUIRE(SchemaT::template boundary_attribute<mean_field::utils::domain::InfinitySurface>() == 2);
|
|
|
|
CHECK(true);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Domain Schema Builds Exact MFEM Attribute Markers",
|
|
tags::domain &tags::utils &tags::unit
|
|
) {
|
|
using namespace mean_field::utils::domain;
|
|
using Schema = CoreEnvelopeVacuumDomainSchema;
|
|
|
|
const mfem::Mesh mesh = domain_test_utils::make_layered_mesh();
|
|
|
|
const mfem::Array<int> stellarMarker = make_attribute_marker<Stellar, Schema>(mesh);
|
|
const mfem::Array<int> vacuumMarker = make_attribute_marker<Vacuum, Schema>(mesh);
|
|
const mfem::Array<int> allMarker = make_attribute_marker<All, Schema>(mesh);
|
|
|
|
REQUIRE(stellarMarker.Size() == 3);
|
|
REQUIRE(vacuumMarker.Size() == 3);
|
|
REQUIRE(allMarker.Size() == 3);
|
|
|
|
CHECK(stellarMarker[0] == 1);
|
|
CHECK(stellarMarker[1] == 1);
|
|
CHECK(stellarMarker[2] == 0);
|
|
|
|
CHECK(vacuumMarker[0] == 0);
|
|
CHECK(vacuumMarker[1] == 0);
|
|
CHECK(vacuumMarker[2] == 1);
|
|
|
|
CHECK(allMarker[0] == 1);
|
|
CHECK(allMarker[1] == 1);
|
|
CHECK(allMarker[2] == 1);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Connected Accepts Face Connected Atomic And Composite Domains",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
mfem::Mesh mesh = domain_test_utils::make_layered_mesh();
|
|
|
|
const auto coreResult = mean_field::utils::domain::
|
|
RelationValidator<mean_field::utils::domain::Connected<mean_field::utils::domain::Core>>::template validate<
|
|
mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
REQUIRE(coreResult);
|
|
REQUIRE(coreResult.connectedDiagnostics.has_value());
|
|
|
|
CHECK(coreResult.connectedDiagnostics->domainElementCount == 1);
|
|
|
|
CHECK(coreResult.connectedDiagnostics->visitedElementCount == 1);
|
|
|
|
const auto stellarResult = mean_field::utils::domain::
|
|
RelationValidator<mean_field::utils::domain::Connected<mean_field::utils::domain::Stellar>>::template validate<
|
|
mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
REQUIRE(stellarResult);
|
|
REQUIRE(stellarResult.connectedDiagnostics.has_value());
|
|
|
|
CHECK(stellarResult.connectedDiagnostics->domainElementCount == 9);
|
|
|
|
CHECK(stellarResult.connectedDiagnostics->visitedElementCount == 9);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Connected Rejects An Absent Domain",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh = domain_test_utils::make_grid_mesh(2, 1, {2, 2}, {});
|
|
|
|
const auto result = mean_field::utils::domain::
|
|
RelationValidator<mean_field::utils::domain::Connected<mean_field::utils::domain::Core>>::template validate<
|
|
mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(result);
|
|
|
|
CHECK(result.failure == mean_field::utils::domain::RelationValidationFailure::DomainAbsent);
|
|
|
|
REQUIRE(result.connectedDiagnostics.has_value());
|
|
|
|
CHECK(result.connectedDiagnostics->domainElementCount == 0);
|
|
|
|
CHECK(result.connectedDiagnostics->visitedElementCount == 0);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Connected Rejects Multiple Face Disconnected Components",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh = domain_test_utils::make_grid_mesh(3, 1, {1, 2, 1}, {});
|
|
|
|
const auto result = mean_field::utils::domain::
|
|
RelationValidator<mean_field::utils::domain::Connected<mean_field::utils::domain::Core>>::template validate<
|
|
mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(result);
|
|
|
|
CHECK(result.failure == mean_field::utils::domain::RelationValidationFailure::DomainDisconnected);
|
|
|
|
REQUIRE(result.connectedDiagnostics.has_value());
|
|
|
|
CHECK(result.connectedDiagnostics->domainElementCount == 2);
|
|
|
|
CHECK(result.connectedDiagnostics->visitedElementCount == 1);
|
|
|
|
CHECK(result.connectedDiagnostics->elementId >= 0);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Inscribed Accepts Nested Atomic And Composite Domains",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh = domain_test_utils::make_layered_mesh();
|
|
|
|
const auto coreResult = mean_field::utils::domain::RelationValidator<
|
|
mean_field::utils::domain::Inscribed<mean_field::utils::domain::Core, mean_field::utils::domain::Envelope>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK(coreResult);
|
|
|
|
const auto stellarResult = mean_field::utils::domain::RelationValidator<
|
|
mean_field::utils::domain::Inscribed<mean_field::utils::domain::Stellar, mean_field::utils::domain::Vacuum>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK(stellarResult);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Inscribed Rejects An Absent Inner Domain",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh = domain_test_utils::make_grid_mesh(2, 2, {2, 2, 2, 2}, {});
|
|
|
|
const auto result = mean_field::utils::domain::RelationValidator<
|
|
mean_field::utils::domain::Inscribed<mean_field::utils::domain::Core, mean_field::utils::domain::Envelope>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(result);
|
|
|
|
CHECK(result.failure == mean_field::utils::domain::RelationValidationFailure::InnerDomainAbsent);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Inscribed Rejects An Absent Outer Domain",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh = domain_test_utils::make_grid_mesh(1, 1, {1}, {});
|
|
|
|
const auto result = mean_field::utils::domain::RelationValidator<
|
|
mean_field::utils::domain::Inscribed<mean_field::utils::domain::Core, mean_field::utils::domain::Envelope>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(result);
|
|
|
|
CHECK(result.failure == mean_field::utils::domain::RelationValidationFailure::OuterDomainAbsent);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Inscribed Rejects An Inner Domain Touching The Computational Boundary",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh = domain_test_utils::make_grid_mesh(2, 2, {1, 2, 2, 2}, {});
|
|
|
|
const auto result = mean_field::utils::domain::RelationValidator<
|
|
mean_field::utils::domain::Inscribed<mean_field::utils::domain::Core, mean_field::utils::domain::Envelope>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(result);
|
|
|
|
CHECK(result.failure == mean_field::utils::domain::RelationValidationFailure::InnerDomainTouchesMeshBoundary);
|
|
|
|
REQUIRE(result.inscribedDiagnostics.has_value());
|
|
|
|
CHECK(result.inscribedDiagnostics->faceId >= 0);
|
|
|
|
CHECK(result.inscribedDiagnostics->innerElementId >= 0);
|
|
|
|
CHECK(result.inscribedDiagnostics->adjacentElementId == -1);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Inscribed Rejects An Inner Domain Touching An Unexpected Material",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
std::vector<int> attributes{2, 2, 2, 2, 1, 3, 2, 2, 2};
|
|
|
|
const mfem::Mesh mesh = domain_test_utils::make_grid_mesh(3, 3, attributes, {});
|
|
|
|
const auto result = mean_field::utils::domain::RelationValidator<
|
|
mean_field::utils::domain::Inscribed<mean_field::utils::domain::Core, mean_field::utils::domain::Envelope>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(result);
|
|
|
|
CHECK(result.failure == mean_field::utils::domain::RelationValidationFailure::InnerDomainTouchesUnexpectedMaterial);
|
|
|
|
REQUIRE(result.inscribedDiagnostics.has_value());
|
|
|
|
CHECK(result.inscribedDiagnostics->adjacentMaterialId == 3);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Domain Boundary Accepts A Complete Internal Stellar Vacuum Interface",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
std::vector<domain_test_utils::BoundaryEdge> boundaries{{.firstVertexId = 1, .secondVertexId = 4, .attribute = 1}};
|
|
|
|
const mfem::Mesh mesh = domain_test_utils::make_grid_mesh(2, 1, {2, 3}, boundaries);
|
|
|
|
const auto result = mean_field::utils::domain::RelationValidator<mean_field::utils::domain::DomainBoundary<
|
|
mean_field::utils::domain::StellarSurface, mean_field::utils::domain::Stellar,
|
|
mean_field::utils::domain::Vacuum>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK(result);
|
|
|
|
/*
|
|
* Interface ordering is intentionally semantic rather
|
|
* than oriented.
|
|
*/
|
|
const auto reversedResult = mean_field::utils::domain::RelationValidator<mean_field::utils::domain::DomainBoundary<
|
|
mean_field::utils::domain::StellarSurface, mean_field::utils::domain::Vacuum,
|
|
mean_field::utils::domain::Stellar>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK(reversedResult);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Domain Boundary Accepts A Complete Exterior Vacuum Boundary",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const std::vector<int> attributes{3};
|
|
|
|
std::vector<domain_test_utils::BoundaryEdge> boundaries;
|
|
|
|
domain_test_utils::append_exterior_boundaries(
|
|
boundaries, attributes, 1, 1, [](const int materialId) { return materialId == 3; }, 2
|
|
);
|
|
|
|
const mfem::Mesh mesh = domain_test_utils::make_grid_mesh(1, 1, attributes, boundaries);
|
|
|
|
const auto result = mean_field::utils::domain::RelationValidator<mean_field::utils::domain::DomainBoundary<
|
|
mean_field::utils::domain::InfinitySurface, mean_field::utils::domain::Vacuum>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK(result);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Domain Boundary Rejects A Tagged Internal Face For An Exterior Boundary",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh =
|
|
domain_test_utils::make_grid_mesh(2, 1, {3, 3}, {{.firstVertexId = 1, .secondVertexId = 4, .attribute = 2}});
|
|
|
|
const auto result = mean_field::utils::domain::RelationValidator<mean_field::utils::domain::DomainBoundary<
|
|
mean_field::utils::domain::InfinitySurface, mean_field::utils::domain::Vacuum>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(result);
|
|
|
|
CHECK(
|
|
result.failure == mean_field::utils::domain::RelationValidationFailure::DomainBoundaryTaggedFaceHasWrongTopology
|
|
);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Domain Boundary Rejects A Tagged Exterior Face Of The Wrong Material",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh =
|
|
domain_test_utils::make_grid_mesh(1, 1, {2}, {{.firstVertexId = 0, .secondVertexId = 1, .attribute = 2}});
|
|
|
|
const auto result = mean_field::utils::domain::RelationValidator<mean_field::utils::domain::DomainBoundary<
|
|
mean_field::utils::domain::InfinitySurface, mean_field::utils::domain::Vacuum>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(result);
|
|
|
|
CHECK(
|
|
result.failure ==
|
|
mean_field::utils::domain::RelationValidationFailure::DomainBoundaryTaggedFaceTouchesUnexpectedMaterial
|
|
);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Domain Boundary Rejects A Tagged Internal Interface With Unexpected "
|
|
"Materials",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh =
|
|
domain_test_utils::make_grid_mesh(2, 1, {1, 2}, {{.firstVertexId = 1, .secondVertexId = 4, .attribute = 1}});
|
|
|
|
const auto result = mean_field::utils::domain::RelationValidator<mean_field::utils::domain::DomainBoundary<
|
|
mean_field::utils::domain::StellarSurface, mean_field::utils::domain::Stellar,
|
|
mean_field::utils::domain::Vacuum>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(result);
|
|
|
|
CHECK(
|
|
result.failure ==
|
|
mean_field::utils::domain::RelationValidationFailure::DomainBoundaryTaggedFaceTouchesUnexpectedMaterial
|
|
);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Domain Boundary Rejects An Untagged Expected Interface",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh = domain_test_utils::make_grid_mesh(2, 1, {2, 3}, {});
|
|
|
|
const auto result = mean_field::utils::domain::RelationValidator<mean_field::utils::domain::DomainBoundary<
|
|
mean_field::utils::domain::StellarSurface, mean_field::utils::domain::Stellar,
|
|
mean_field::utils::domain::Vacuum>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(result);
|
|
|
|
CHECK(result.failure == mean_field::utils::domain::RelationValidationFailure::DomainBoundaryExpectedFaceIsUntagged);
|
|
|
|
REQUIRE(result.domainBoundaryDiagnostics.has_value());
|
|
|
|
CHECK(result.domainBoundaryDiagnostics->faceId >= 0);
|
|
|
|
CHECK(result.domainBoundaryDiagnostics->boundaryElementId == -1);
|
|
|
|
CHECK_FALSE(result.domainBoundaryDiagnostics->actualBoundaryAttribute.has_value());
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Domain Boundary Rejects An Expected Interface With The Wrong Attribute",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh =
|
|
domain_test_utils::make_grid_mesh(2, 1, {2, 3}, {{.firstVertexId = 1, .secondVertexId = 4, .attribute = 9}});
|
|
|
|
const auto result = mean_field::utils::domain::RelationValidator<mean_field::utils::domain::DomainBoundary<
|
|
mean_field::utils::domain::StellarSurface, mean_field::utils::domain::Stellar,
|
|
mean_field::utils::domain::Vacuum>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(result);
|
|
|
|
CHECK(
|
|
result.failure ==
|
|
mean_field::utils::domain::RelationValidationFailure::DomainBoundaryExpectedFaceHasWrongAttribute
|
|
);
|
|
|
|
REQUIRE(result.domainBoundaryDiagnostics.has_value());
|
|
|
|
REQUIRE(result.domainBoundaryDiagnostics->actualBoundaryAttribute.has_value());
|
|
|
|
CHECK(*result.domainBoundaryDiagnostics->actualBoundaryAttribute == 9);
|
|
|
|
CHECK(result.domainBoundaryDiagnostics->expectedBoundaryAttribute == 1);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Domain Boundary Rejects A Relation That Is Not Realized Anywhere",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh = domain_test_utils::make_grid_mesh(2, 1, {2, 2}, {});
|
|
|
|
const auto result = mean_field::utils::domain::RelationValidator<mean_field::utils::domain::DomainBoundary<
|
|
mean_field::utils::domain::StellarSurface, mean_field::utils::domain::Stellar,
|
|
mean_field::utils::domain::Vacuum>>::
|
|
template validate<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(result);
|
|
|
|
CHECK(result.failure == mean_field::utils::domain::RelationValidationFailure::DomainBoundaryAbsent);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Complete Schema Validation Accepts A Synthetic Core Envelope Vacuum Mesh",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh = domain_test_utils::make_layered_mesh();
|
|
|
|
const auto validation =
|
|
mean_field::utils::domain::validate_schema<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
REQUIRE(validation.valid());
|
|
|
|
REQUIRE(validation.relationResults.size() == 7);
|
|
|
|
CHECK(validation.failed_relation_count() == 0);
|
|
|
|
CHECK(validation.passed_relation_count() == 7);
|
|
|
|
CHECK_FALSE(validation.first_failed_relation_index().has_value());
|
|
|
|
const std::array<std::string_view, 7> expectedRelationNames{"connected", "connected", "connected",
|
|
"inscribed", "inscribed", "domain_boundary",
|
|
"domain_boundary"};
|
|
|
|
for (std::size_t relationIndex = 0; relationIndex < expectedRelationNames.size(); ++relationIndex) {
|
|
CHECK(validation.relationResults[relationIndex].relationIndex == relationIndex);
|
|
|
|
CHECK(validation.relationResults[relationIndex].relationName == expectedRelationNames[relationIndex]);
|
|
|
|
CHECK(validation.relationResults[relationIndex].valid());
|
|
}
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Complete Schema Validation Evaluates Every Relation After A Failure",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
/*
|
|
* All material topology and the outer vacuum boundary are valid.
|
|
* Only the Stellar/Vacuum boundary tagging is intentionally absent.
|
|
*/
|
|
const mfem::Mesh mesh = domain_test_utils::make_layered_mesh(false, true);
|
|
|
|
const auto validation =
|
|
mean_field::utils::domain::validate_schema<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(validation.valid());
|
|
|
|
REQUIRE(validation.relationResults.size() == 7);
|
|
|
|
CHECK(validation.failed_relation_count() == 1);
|
|
|
|
CHECK(validation.passed_relation_count() == 6);
|
|
|
|
REQUIRE(validation.first_failed_relation_index().has_value());
|
|
|
|
CHECK(*validation.first_failed_relation_index() == 5);
|
|
|
|
for (std::size_t relationIndex = 0; relationIndex < 7; ++relationIndex) {
|
|
CAPTURE(relationIndex);
|
|
|
|
if (relationIndex == 5) {
|
|
CHECK_FALSE(validation.relationResults[relationIndex].valid());
|
|
|
|
CHECK(
|
|
validation.relationResults[relationIndex].result.failure ==
|
|
mean_field::utils::domain::RelationValidationFailure::DomainBoundaryExpectedFaceIsUntagged
|
|
);
|
|
|
|
continue;
|
|
}
|
|
|
|
CHECK(validation.relationResults[relationIndex].valid());
|
|
}
|
|
}
|
|
|
|
TEST_CASE(
|
|
"STROID Meshes Satisfy The Core Envelope Vacuum Domain Schema",
|
|
tags::integration &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const std::array<domain_test_utils::StroidCase, 3> testCases{
|
|
domain_test_utils::StroidCase{
|
|
.name = "spherical_low_order", .refinementLevels = 0, .order = 1, .flattening = 0.0
|
|
},
|
|
domain_test_utils::StroidCase{.name = "oblate", .refinementLevels = 0, .order = 2, .flattening = 0.15},
|
|
domain_test_utils::StroidCase{.name = "refined_oblate", .refinementLevels = 1, .order = 2, .flattening = 0.10}
|
|
};
|
|
|
|
for (const domain_test_utils::StroidCase &testCase : testCases) {
|
|
INFO("STROID case = " << testCase.name);
|
|
|
|
INFO("Refinement levels = " << testCase.refinementLevels);
|
|
|
|
INFO("Order = " << testCase.order);
|
|
|
|
INFO("Flattening = " << testCase.flattening);
|
|
|
|
const stroid::config::MeshConfig config =
|
|
domain_test_utils::make_stroid_config(testCase.refinementLevels, testCase.order, testCase.flattening);
|
|
|
|
stroid::StroidMesh stroidMesh = stroid::GenerateMesh(config);
|
|
|
|
REQUIRE(stroidMesh.reference_mesh != nullptr);
|
|
|
|
REQUIRE(stroidMesh.mesh != nullptr);
|
|
|
|
/*
|
|
* Validate both the reference topology and the projected
|
|
* physical mesh. The mapping/projection must not alter
|
|
* material or boundary semantics.
|
|
*/
|
|
domain_test_utils::check_schema_is_valid<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(
|
|
*stroidMesh.reference_mesh
|
|
);
|
|
|
|
domain_test_utils::check_schema_is_valid<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(
|
|
*stroidMesh.mesh
|
|
);
|
|
}
|
|
}
|
|
|
|
TEST_CASE(
|
|
"STROID Material And Boundary Id Conventions Are Fully Schema Driven",
|
|
tags::integration &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
stroid::config::MeshConfig config = domain_test_utils::make_stroid_config(0, 1, 0.0);
|
|
|
|
config.core_id = 11;
|
|
|
|
config.envelope_id = 17;
|
|
|
|
config.vacuum_id = 29;
|
|
|
|
config.surface_bdr_id = 101;
|
|
|
|
config.inf_bdr_id = 203;
|
|
|
|
stroid::StroidMesh stroidMesh = stroid::GenerateMesh(config);
|
|
|
|
REQUIRE(stroidMesh.reference_mesh != nullptr);
|
|
|
|
REQUIRE(stroidMesh.mesh != nullptr);
|
|
|
|
/*
|
|
* The same semantic topology must validate when a mesh generator
|
|
* uses an entirely different attribute numbering convention.
|
|
*/
|
|
domain_test_utils::check_schema_is_valid<domain_test_utils::AlternateIdSchema>(*stroidMesh.reference_mesh);
|
|
|
|
domain_test_utils::check_schema_is_valid<domain_test_utils::AlternateIdSchema>(*stroidMesh.mesh);
|
|
|
|
/*
|
|
* Conversely, the production 1/2/3 + 1/2 schema must not silently
|
|
* accept a mesh generated under another numbering convention.
|
|
*/
|
|
const auto productionValidation =
|
|
mean_field::utils::domain::validate_schema<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(
|
|
*stroidMesh.mesh
|
|
);
|
|
|
|
CHECK_FALSE(productionValidation.valid());
|
|
|
|
CHECK(productionValidation.failed_relation_count() > 0);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Complete Schema Validation Rejects A Mesh Without Vacuum",
|
|
tags::unit &tags::mesh &tags::utils &tags::domain
|
|
) {
|
|
const mfem::Mesh mesh = domain_test_utils::make_grid_mesh(3, 3, {2, 2, 2, 2, 1, 2, 2, 2, 2}, {});
|
|
|
|
const auto validation =
|
|
mean_field::utils::domain::validate_schema<mean_field::utils::domain::CoreEnvelopeVacuumDomainSchema>(mesh);
|
|
|
|
CHECK_FALSE(validation.valid());
|
|
|
|
REQUIRE(validation.relationResults.size() == 7);
|
|
|
|
/*
|
|
* Connected<Vacuum>
|
|
*/
|
|
CHECK_FALSE(validation.relationResults[2].valid());
|
|
|
|
CHECK(
|
|
validation.relationResults[2].result.failure ==
|
|
mean_field::utils::domain::RelationValidationFailure::DomainAbsent
|
|
);
|
|
|
|
/*
|
|
* Inscribed<Stellar, Vacuum>
|
|
*/
|
|
CHECK_FALSE(validation.relationResults[4].valid());
|
|
|
|
CHECK(
|
|
validation.relationResults[4].result.failure ==
|
|
mean_field::utils::domain::RelationValidationFailure::OuterDomainAbsent
|
|
);
|
|
}
|