#include #include #include #include #include #include #include "serif/discretization/domain/schema/schemas.hpp" #include "serif/discretization/domain/schema/validation/all.hpp" #include "serif/tests/test_tags.hpp" #include "serif/tests/discritization/domain/domain_test_utils.hpp" namespace domain = serif::discretization::domain; namespace schema = domain::schema; namespace schema_validation = schema::validation; 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 = schema_validation::validate_schema(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()); constexpr std::array expectedRelationNames{"fully_connected", "fully_connected", "fully_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 = schema_validation::validate_schema(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 == schema_validation::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 ) { constexpr std::array 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 auto &[name, refinementLevels, order, flattening] : testCases) { INFO("STROID case = " << name); INFO("Refinement levels = " << refinementLevels); INFO("Order = " << order); INFO("Flattening = " << flattening); const stroid::config::MeshConfig config = domain_test_utils::make_stroid_config(refinementLevels, order, 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( *stroidMesh.reference_mesh ); domain_test_utils::check_schema_is_valid( *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(*stroidMesh.reference_mesh); domain_test_utils::check_schema_is_valid(*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 = schema_validation::validate_schema(*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 = schema_validation::validate_schema(mesh); CHECK_FALSE(validation.valid()); REQUIRE(validation.relationResults.size() == 7); /* * FullyConnected */ CHECK_FALSE(validation.relationResults[2].valid()); CHECK( validation.relationResults[2].result.failure == schema_validation::RelationValidationFailure::DomainAbsent ); /* * Inscribed */ CHECK_FALSE(validation.relationResults[4].valid()); CHECK( validation.relationResults[4].result.failure == schema_validation::RelationValidationFailure::OuterDomainAbsent ); }