feat(field-support): added field support system, mid migration

currently the barotope and the pressure force operator are migrated to the new support system
This commit is contained in:
2026-08-23 10:13:53 -04:00
parent dc912fd15e
commit 0f3ca8050b
137 changed files with 29975 additions and 16389 deletions

414
tests/field/field_base.cpp Normal file
View File

@@ -0,0 +1,414 @@
#include <catch2/catch_test_macros.hpp>
#include <concepts>
#include <cstddef>
#include <string_view>
#include <type_traits>
import mean_field;
import test_helpers;
namespace field_base_test_utils {
namespace field = mean_field::field;
namespace domain = mean_field::utils::domain;
using IndependentL2Scalar = field::ScalarQ<field::FieldRelation::Independent, field::Disc<field::L2, 2>>;
using IndependentH1Scalar = field::ScalarQ<field::FieldRelation::Independent, field::Disc<field::H1, 3>>;
using IndependentH1Vector = field::VectorQ<field::FieldRelation::Independent, field::Disc<field::H1, 3>>;
using IndependentL2Vector = field::VectorQ<field::FieldRelation::Independent, field::Disc<field::L2, 2>>;
using Potential = field::ScalarQ<field::FieldRelation::Independent, field::Disc<field::L2, 2>>;
using Flux = field::VectorQ<field::FieldRelation::Gradient<Potential>, field::Disc<field::RT, 2>>;
using CurlSource = field::VectorQ<field::FieldRelation::Independent, field::Disc<field::ND, 2>>;
using CurlQuantity = field::VectorQ<field::FieldRelation::Curl<CurlSource>, field::Disc<field::ND, 2>>;
using SampleOperand = field::Operand<IndependentH1Scalar>;
using SampleGradientOperand = field::Operand<IndependentH1Scalar, field::FieldOperation::Gradient>;
using SampleForm = field::FormSpec<17, 2, SampleOperand, SampleGradientOperand>;
struct StellarSupportedObject {
using Support = field::DomainSupport<domain::Stellar>;
};
struct AllSupportedObject {
using Support = field::DomainSupport<domain::All>;
};
struct NonSpatialObject {
using Support = field::NonSpatialSupport;
};
} // namespace field_base_test_utils
TEST_CASE(
"Field Base Type Lists Track Compile Time Membership",
tags::unit &tags::field
) {
namespace field = mean_field::field;
using List = field::TypeList<int, double, char>;
STATIC_REQUIRE(field::typeListContains<int, List>);
STATIC_REQUIRE(field::typeListContains<double, List>);
STATIC_REQUIRE(field::typeListContains<char, List>);
STATIC_REQUIRE_FALSE(field::typeListContains<float, List>);
STATIC_REQUIRE_FALSE(field::typeListContains<int, field::TypeList<>>);
CHECK(true);
}
TEST_CASE(
"Field Base Function Space Tags Encode Supported Tensor Ranks",
tags::unit &tags::field
) {
namespace field = mean_field::field;
STATIC_REQUIRE(field::SpaceTag<field::L2>);
STATIC_REQUIRE(field::SpaceTag<field::H1>);
STATIC_REQUIRE(field::SpaceTag<field::RT>);
STATIC_REQUIRE(field::SpaceTag<field::ND>);
STATIC_REQUIRE_FALSE(field::SpaceTag<int>);
STATIC_REQUIRE(field::spaceSupportsRank<field::L2, 0>);
STATIC_REQUIRE(field::spaceSupportsRank<field::L2, 1>);
STATIC_REQUIRE(field::spaceSupportsRank<field::H1, 0>);
STATIC_REQUIRE(field::spaceSupportsRank<field::H1, 1>);
STATIC_REQUIRE_FALSE(field::spaceSupportsRank<field::RT, 0>);
STATIC_REQUIRE(field::spaceSupportsRank<field::RT, 1>);
STATIC_REQUIRE_FALSE(field::spaceSupportsRank<field::ND, 0>);
STATIC_REQUIRE(field::spaceSupportsRank<field::ND, 1>);
CHECK(field::L2::name == std::string_view{"L2"});
CHECK(field::H1::name == std::string_view{"H1"});
CHECK(field::RT::name == std::string_view{"RT"});
CHECK(field::ND::name == std::string_view{"ND"});
}
TEST_CASE(
"Field Base Discretization Descriptors Preserve Space And Family Order",
tags::unit &tags::field
) {
namespace field = mean_field::field;
using L2Disc = field::Disc<field::L2, 2>;
using H1Disc = field::Disc<field::H1, 4>;
using RTDisc = field::Disc<field::RT, 1>;
using NDDisc = field::Disc<field::ND, 3>;
STATIC_REQUIRE(field::DiscretizationTag<L2Disc>);
STATIC_REQUIRE(field::DiscretizationTag<H1Disc>);
STATIC_REQUIRE(field::DiscretizationTag<RTDisc>);
STATIC_REQUIRE(field::DiscretizationTag<NDDisc>);
STATIC_REQUIRE(std::same_as<typename L2Disc::Space, field::L2>);
STATIC_REQUIRE(std::same_as<typename H1Disc::Space, field::H1>);
STATIC_REQUIRE(L2Disc::familyOrder == 2);
STATIC_REQUIRE(H1Disc::familyOrder == 4);
STATIC_REQUIRE(RTDisc::familyOrder == 1);
STATIC_REQUIRE(NDDisc::familyOrder == 3);
CHECK(true);
}
TEST_CASE(
"Field Base Relations Preserve Their Source Quantities",
tags::unit &tags::field
) {
namespace field = mean_field::field;
using Source = field_base_test_utils::IndependentL2Scalar;
using Gradient = field::FieldRelation::Gradient<Source>;
using Divergence = field::FieldRelation::Divergence<Source>;
using Curl = field::FieldRelation::Curl<Source>;
STATIC_REQUIRE(field::ValidRelation<field::FieldRelation::Independent>);
STATIC_REQUIRE(field::ValidRelation<Gradient>);
STATIC_REQUIRE(field::ValidRelation<Divergence>);
STATIC_REQUIRE(field::ValidRelation<Curl>);
STATIC_REQUIRE_FALSE(field::ValidRelation<int>);
STATIC_REQUIRE(field::IsGradient<Gradient>::value);
STATIC_REQUIRE(field::IsDivergence<Divergence>::value);
STATIC_REQUIRE(field::IsCurl<Curl>::value);
STATIC_REQUIRE(std::same_as<typename field::RelationTarget<Gradient>::Type, Source>);
STATIC_REQUIRE(std::same_as<typename field::RelationTarget<Divergence>::Type, Source>);
STATIC_REQUIRE(std::same_as<typename field::RelationTarget<Curl>::Type, Source>);
STATIC_REQUIRE(std::same_as<typename field::RelationTarget<field::FieldRelation::Independent>::Type, void>);
CHECK(true);
}
TEST_CASE(
"Field Base Finite Element Quantities Preserve Rank Storage Space And Order",
tags::unit &tags::field
) {
namespace field = mean_field::field;
using Scalar = field_base_test_utils::IndependentL2Scalar;
using Vector = field_base_test_utils::IndependentH1Vector;
STATIC_REQUIRE(field::FieldQuantity<Scalar>);
STATIC_REQUIRE(field::FieldQuantity<Vector>);
STATIC_REQUIRE(field::RegisteredQuantity<Scalar>);
STATIC_REQUIRE(field::RegisteredQuantity<Vector>);
STATIC_REQUIRE(Scalar::rankValue == 0);
STATIC_REQUIRE(Vector::rankValue == 1);
STATIC_REQUIRE(Scalar::familyOrder == 2);
STATIC_REQUIRE(Vector::familyOrder == 3);
STATIC_REQUIRE(std::same_as<typename Scalar::Space, field::L2>);
STATIC_REQUIRE(std::same_as<typename Vector::Space, field::H1>);
STATIC_REQUIRE(Scalar::storageKind == field::StorageKind::finite_element);
STATIC_REQUIRE(Vector::storageKind == field::StorageKind::finite_element);
STATIC_REQUIRE(Scalar::staticBlockSize == field::dynamicBlockSize);
STATIC_REQUIRE(Vector::staticBlockSize == field::dynamicBlockSize);
CHECK(true);
}
TEST_CASE(
"Field Base Global Scalars Are Registered But Are Not Finite Element Quantities",
tags::unit &tags::field
) {
namespace field = mean_field::field;
STATIC_REQUIRE(field::GlobalScalarQuantity<field::GlobalScalarQ>);
STATIC_REQUIRE(field::RegisteredQuantity<field::GlobalScalarQ>);
STATIC_REQUIRE_FALSE(field::FieldQuantity<field::GlobalScalarQ>);
STATIC_REQUIRE(field::GlobalScalarQ::rankValue == 0);
STATIC_REQUIRE(field::GlobalScalarQ::storageKind == field::StorageKind::global_scalar);
STATIC_REQUIRE(field::GlobalScalarQ::staticBlockSize == 1);
STATIC_REQUIRE(std::same_as<typename field::GlobalScalarQ::Relation, field::FieldRelation::Independent>);
CHECK(true);
}
TEST_CASE(
"Field Base Derived Quantity Detection Follows Physical Relations",
tags::unit &tags::field
) {
namespace field = mean_field::field;
using Independent = field_base_test_utils::IndependentL2Scalar;
using Flux = field_base_test_utils::Flux;
using CurlQuantity = field_base_test_utils::CurlQuantity;
STATIC_REQUIRE_FALSE(field::DerivedQuantity<Independent>);
STATIC_REQUIRE(field::DerivedQuantity<Flux>);
STATIC_REQUIRE(field::DerivedQuantity<CurlQuantity>);
STATIC_REQUIRE(std::same_as<field::RelationTargetT<Flux>, field_base_test_utils::Potential>);
STATIC_REQUIRE(std::same_as<field::RelationTargetT<CurlQuantity>, field_base_test_utils::CurlSource>);
CHECK(true);
}
TEST_CASE(
"Field Base RT L2 Constraint Accepts The Registered Stable Pair Contract",
tags::unit &tags::field
) {
namespace field = mean_field::field;
using Potential = field_base_test_utils::Potential;
using Flux = field_base_test_utils::Flux;
using Constraint = field::RtL2StablePair<Flux, Potential>;
STATIC_REQUIRE(field::validate_constraints(field::TypeList<Constraint>{}));
STATIC_REQUIRE(field::validate_constraints(field::TypeList<>{}));
CHECK(true);
}
TEST_CASE(
"Field Base Operations And Operands Preserve Mathematical Intent",
tags::unit &tags::field
) {
namespace field = mean_field::field;
using Quantity = field_base_test_utils::IndependentH1Scalar;
using ValueOperand = field::Operand<Quantity>;
using GradientOperand = field::Operand<Quantity, field::FieldOperation::Gradient>;
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::Value>);
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::Gradient>);
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::Divergence>);
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::Curl>);
STATIC_REQUIRE(field::FieldOperationTag<field::FieldOperation::NormalTrace>);
STATIC_REQUIRE_FALSE(field::FieldOperationTag<int>);
STATIC_REQUIRE(field::FieldOperand<ValueOperand>);
STATIC_REQUIRE(field::FieldOperand<GradientOperand>);
STATIC_REQUIRE(std::same_as<typename ValueOperand::Quantity, Quantity>);
STATIC_REQUIRE(std::same_as<typename ValueOperand::Operation, field::FieldOperation::Value>);
STATIC_REQUIRE(std::same_as<typename GradientOperand::Operation, field::FieldOperation::Gradient>);
CHECK(true);
}
TEST_CASE(
"Field Base Form Specifications Preserve Policy Dynamic Orders And Operands",
tags::unit &tags::field
) {
namespace field = mean_field::field;
using Form = field_base_test_utils::SampleForm;
STATIC_REQUIRE(field::FieldForm<Form>);
STATIC_REQUIRE(Form::policyKey == 17);
STATIC_REQUIRE(Form::dynamicOrderCount == 2);
STATIC_REQUIRE(
std::same_as<
typename Form::Operands,
field::TypeList<field_base_test_utils::SampleOperand, field_base_test_utils::SampleGradientOperand>>
);
STATIC_REQUIRE(
field::isRegisteredQuantityList<field::TypeList<
field_base_test_utils::IndependentL2Scalar, field_base_test_utils::IndependentH1Vector,
field::GlobalScalarQ>>
);
STATIC_REQUIRE_FALSE(field::isRegisteredQuantityList<field::TypeList<int>>);
STATIC_REQUIRE(field::isFieldFormList<field::TypeList<Form>>);
STATIC_REQUIRE(field::isFieldFormList<field::TypeList<>>);
STATIC_REQUIRE_FALSE(field::isFieldFormList<field::TypeList<int>>);
CHECK(true);
}
TEST_CASE(
"Field Base Support Types Distinguish Domain And Non Spatial Fields",
tags::unit &tags::field
) {
namespace field = mean_field::field;
namespace domain = mean_field::utils::domain;
using StellarSupport = field::DomainSupport<domain::Stellar>;
using AllSupport = field::DomainSupport<domain::All>;
STATIC_REQUIRE(field::IsFieldSupport<StellarSupport>);
STATIC_REQUIRE(field::IsFieldSupport<AllSupport>);
STATIC_REQUIRE(field::IsFieldSupport<field::NonSpatialSupport>);
STATIC_REQUIRE(field::IsDomainSupport<StellarSupport>);
STATIC_REQUIRE(field::IsDomainSupport<AllSupport>);
STATIC_REQUIRE_FALSE(field::IsDomainSupport<field::NonSpatialSupport>);
STATIC_REQUIRE(std::same_as<typename StellarSupport::Domain, domain::Stellar>);
STATIC_REQUIRE(std::same_as<typename AllSupport::Domain, domain::All>);
STATIC_REQUIRE(field::DomainSupportedField<field_base_test_utils::StellarSupportedObject>);
STATIC_REQUIRE(field::DomainSupportedField<field_base_test_utils::AllSupportedObject>);
STATIC_REQUIRE_FALSE(field::DomainSupportedField<field_base_test_utils::NonSpatialObject>);
STATIC_REQUIRE(field::NonSpatialField<field_base_test_utils::NonSpatialObject>);
STATIC_REQUIRE_FALSE(field::NonSpatialField<field_base_test_utils::StellarSupportedObject>);
STATIC_REQUIRE(std::same_as<field::FieldSupportT<field_base_test_utils::StellarSupportedObject>, StellarSupport>);
STATIC_REQUIRE(std::same_as<field::FieldDomainT<field_base_test_utils::StellarSupportedObject>, domain::Stellar>);
CHECK(true);
}