#include #include #include #include #include #include #include #include #include #include #include #include import mean_field; import test_helpers; namespace { template concept HasRootManifestSpecificationDescriptor = requires(const ManifestType &manifest) { { manifest.template specification() } -> std::same_as; }; template concept CanMutateManifestBlock = requires(View &view, const Term &term) { view.block(term)(0) = 1.0; }; template concept HasLvalueRootViewFactories = requires(const ManifestType &manifest, mfem::Vector &mutableVector, const mfem::Vector &readOnlyVector) { { manifest.stateView(mutableVector) } -> std::same_as; { manifest.stateView(readOnlyVector) } -> std::same_as; { manifest.directionView(readOnlyVector) } -> std::same_as; { manifest.residualView(mutableVector) } -> std::same_as; }; template concept StateViewAcceptsTemporaryVector = requires(const ManifestType &manifest) { manifest.stateView(std::declval()); }; template concept StateViewAcceptsConstTemporaryVector = requires(const ManifestType &manifest) { manifest.stateView(std::declval()); }; template concept DirectionViewAcceptsTemporaryVector = requires(const ManifestType &manifest) { manifest.directionView(std::declval()); }; template concept DirectionViewAcceptsConstTemporaryVector = requires(const ManifestType &manifest) { manifest.directionView(std::declval()); }; template concept ResidualViewAcceptsTemporaryVector = requires(const ManifestType &manifest) { manifest.residualView(std::declval()); }; template concept ResidualViewAcceptsConstTemporaryVector = requires(const ManifestType &manifest) { manifest.residualView(std::declval()); }; template concept TemporaryManifestProvidesStateView = requires(mfem::Vector &vector) { std::declval().stateView(vector); }; template concept TemporaryManifestProvidesReadOnlyStateView = requires(const mfem::Vector &vector) { std::declval().stateView(vector); }; template concept TemporaryManifestProvidesDirectionView = requires(const mfem::Vector &vector) { std::declval().directionView(vector); }; template concept TemporaryManifestProvidesResidualView = requires(mfem::Vector &vector) { std::declval().residualView(vector); }; using CanonicalModel = mean_field::model::StellarModel>; using PermutedModel = mean_field::model::StellarModel>; using CentralDensityModel = mean_field::model::StellarModel>; using Form = mean_field::operators::CompiledStellarEquilibriumForm; using JacobianForm = mean_field::operators::CompiledStellarEquilibriumJacobianForm; using Manifest = mean_field::operators::CompiledRootManifest; using CentralForm = mean_field::utils::blocks::central_density_bordered_stellar_equilibrium_form; using CentralJacobianForm = mean_field::utils::blocks::central_density_bordered_stellar_equilibrium_jacobian_form; using CentralManifest = mean_field::operators::CompiledRootManifest; using AngularMomentumModel = mean_field::model::StellarModel>; using AngularMomentumForm = mean_field::operators::CompiledStellarEquilibriumForm; using AngularMomentumJacobian = mean_field::operators::CompiledStellarEquilibriumJacobianForm; using AngularMomentumManifest = mean_field::operators::CompiledRootManifest; class MockFixedMagneticSpecificEnergy final { public: struct Parameters final { mean_field::dimensions::SpecificEnergyValue target; }; using ScalarDimensionless = mean_field::models::CoordinateNormalization< mean_field::models::RieszTopology::global_scalar, mean_field::models::PhysicalScaleLaw::dimensionless>; using ScalarSpecificEnergy = mean_field::models::CoordinateNormalization< mean_field::models::RieszTopology::global_scalar, mean_field::models::PhysicalScaleLaw::specific_energy>; using ModelDefinition = mean_field::integral::FixedWithPhysicalCoordinate< MockFixedMagneticSpecificEnergy, "RootManifestMockFixedMagneticSpecificEnergy", mean_field::models::ModelTypeList< mean_field::utils::blocks::density::mass::value, mean_field::utils::blocks::surface_deformation::parameters::value>, mean_field::models::ModelTypeList, mean_field::models::GeneratedNormalization, mean_field::models::GeneratedManifest< "mock_magnetic_specific_energy.amplitude", "a_B", "mock_magnetic_specific_energy.residual", "R_EB", "specific_energy", "specific_energy">>; explicit MockFixedMagneticSpecificEnergy(const Parameters parameters) noexcept : m_target(parameters.target) { } [[nodiscard]] mean_field::dimensions::SpecificEnergyValue target() const noexcept { return m_target; } private: mean_field::dimensions::SpecificEnergyValue m_target; }; template class MockManifestIdentityConstraint final { public: struct Parameters final { mean_field::dimensions::SpecificEnergyValue target; }; using ModelDefinition = mean_field::integral::FixedWithPhysicalCoordinate< MockManifestIdentityConstraint, Name, mean_field::models::DependsOn, mean_field::models::Affects, mean_field::models::GlobalScalarNormalization< mean_field::models::PhysicalScaleLaw::dimensionless, mean_field::models::PhysicalScaleLaw::specific_energy>, ManifestDefinition>; explicit MockManifestIdentityConstraint(Parameters parameters) noexcept : m_target(parameters.target) { } [[nodiscard]] mean_field::dimensions::SpecificEnergyValue target() const noexcept { return m_target; } private: mean_field::dimensions::SpecificEnergyValue m_target; }; struct NonConstexprGeneratedManifest final { inline static std::string_view valueStableId = "nonconstexpr_manifest.coordinate"; static constexpr std::string_view valueSymbol = "q_nc"; static constexpr std::string_view residualStableId = "nonconstexpr_manifest.residual"; static constexpr std::string_view residualSymbol = "R_nc"; static constexpr std::string_view targetUnits = "specific_energy"; static constexpr std::string_view residualUnits = "specific_energy"; static constexpr bool available = true; }; struct ValidStructuralGeneratedManifest { static constexpr std::string_view valueStableId = "valid_structural_manifest.coordinate"; static constexpr std::string_view valueSymbol = "q_valid"; static constexpr std::string_view residualStableId = "valid_structural_manifest.residual"; static constexpr std::string_view residualSymbol = "R_valid"; static constexpr std::string_view targetUnits = "specific_energy"; static constexpr std::string_view residualUnits = "specific_energy"; static constexpr bool available = true; }; struct EmptyValueStableIdManifest final : ValidStructuralGeneratedManifest { static constexpr std::string_view valueStableId = ""; }; struct EmptyValueSymbolManifest final : ValidStructuralGeneratedManifest { static constexpr std::string_view valueSymbol = ""; }; struct EmptyResidualStableIdManifest final : ValidStructuralGeneratedManifest { static constexpr std::string_view residualStableId = ""; }; struct EmptyResidualSymbolManifest final : ValidStructuralGeneratedManifest { static constexpr std::string_view residualSymbol = ""; }; struct EmptyTargetUnitsManifest final : ValidStructuralGeneratedManifest { static constexpr std::string_view targetUnits = ""; }; struct EmptyResidualUnitsManifest final : ValidStructuralGeneratedManifest { static constexpr std::string_view residualUnits = ""; }; struct NonConstexprGeneratedUnitsManifest final : ValidStructuralGeneratedManifest { inline static std::string_view targetUnits = "specific_energy"; }; using MockGeneratedValueIdCollision = MockManifestIdentityConstraint< "RootManifestMockGeneratedValueIdCollision", mean_field::models::GeneratedManifest< "fixed_total_mass.multiplier", "C_value_collision", "mock_generated_value_collision.residual", "R_value_collision", "specific_energy", "specific_energy">>; using MockGeneratedResidualIdCollision = MockManifestIdentityConstraint< "RootManifestMockGeneratedResidualIdCollision", mean_field::models::GeneratedManifest< "mock_generated_residual_collision.coordinate", "q_residual_collision", "fixed_total_mass.residual", "R_residual_collision", "specific_energy", "specific_energy">>; using MockPhysicalValueIdCollision = MockManifestIdentityConstraint< "RootManifestMockPhysicalValueIdCollision", mean_field::models::GeneratedManifest< "density", "q_physical_value_collision", "mock_physical_value_collision.residual", "R_physical_value_collision", "specific_energy", "specific_energy">>; using MockPhysicalResidualIdCollision = MockManifestIdentityConstraint< "RootManifestMockPhysicalResidualIdCollision", mean_field::models::GeneratedManifest< "mock_physical_residual_collision.coordinate", "q_physical_residual_collision", "hydrostatic_balance", "R_physical_residual_collision", "specific_energy", "specific_energy">>; using MockReusedManifestSymbols = MockManifestIdentityConstraint< "RootManifestMockReusedSymbols", mean_field::models::GeneratedManifest< "mock_reused_symbols.coordinate", "C", "mock_reused_symbols.residual", "R_M", "specific_energy", "specific_energy">>; using MockNonConstexprManifest = MockManifestIdentityConstraint<"RootManifestMockNonConstexprManifest", NonConstexprGeneratedManifest>; using MockValidStructuralManifest = MockManifestIdentityConstraint<"RootManifestMockValidStructuralManifest", ValidStructuralGeneratedManifest>; using MockEmptyValueStableIdManifest = MockManifestIdentityConstraint<"RootManifestMockEmptyValueStableIdManifest", EmptyValueStableIdManifest>; using MockEmptyValueSymbolManifest = MockManifestIdentityConstraint<"RootManifestMockEmptyValueSymbolManifest", EmptyValueSymbolManifest>; using MockEmptyResidualStableIdManifest = MockManifestIdentityConstraint<"RootManifestMockEmptyResidualStableIdManifest", EmptyResidualStableIdManifest>; using MockEmptyResidualSymbolManifest = MockManifestIdentityConstraint<"RootManifestMockEmptyResidualSymbolManifest", EmptyResidualSymbolManifest>; using MockEmptyTargetUnitsManifest = MockManifestIdentityConstraint<"RootManifestMockEmptyTargetUnitsManifest", EmptyTargetUnitsManifest>; using MockEmptyResidualUnitsManifest = MockManifestIdentityConstraint<"RootManifestMockEmptyResidualUnitsManifest", EmptyResidualUnitsManifest>; using MockNonConstexprUnitsManifest = MockManifestIdentityConstraint<"RootManifestMockNonConstexprUnitsManifest", NonConstexprGeneratedUnitsManifest>; struct AlternatingManifestTarget final { double first; double subsequent; mutable int evaluations{0}; [[nodiscard]] double value() const noexcept { return evaluations++ == 0 ? first : subsequent; } }; class MockInconsistentManifestScale final { public: struct Parameters final { double first; double subsequent; }; using ModelDefinition = mean_field::integral::FixedWithPhysicalCoordinate< MockInconsistentManifestScale, "RootManifestMockInconsistentScale", mean_field::models::DependsOn, mean_field::models::Affects, mean_field::models::GlobalScalarNormalization< mean_field::models::PhysicalScaleLaw::dimensionless, mean_field::models::PhysicalScaleLaw::specific_energy>, ValidStructuralGeneratedManifest>; explicit MockInconsistentManifestScale(const Parameters parameters) noexcept : m_target{ parameters.first, parameters.subsequent } { } [[nodiscard]] const AlternatingManifestTarget &target() const noexcept { return m_target; } private: AlternatingManifestTarget m_target; }; struct NonConvertibleTarget final { struct Magnitude final { }; [[nodiscard]] constexpr Magnitude value() const noexcept { return {}; } }; class MockNonConvertibleManifestTarget final { public: struct Parameters final { }; using ScalarDescription = mean_field::stellar::ScalarConstraint< mean_field::dimensions::quantity::SpecificEnergy, mean_field::dimensions::quantity::Dimensionless, mean_field::dimensions::quantity::SpecificEnergy, "mock_nonconvertible_target.coordinate", "q_bad", "mock_nonconvertible_target.residual", "R_bad">; using ModelDefinition = mean_field::integral::FixedScalarWithPhysicalCoordinate< MockNonConvertibleManifestTarget, "RootManifestMockNonConvertibleTarget", mean_field::stellar::Reads, mean_field::stellar::Changes, ScalarDescription>; explicit MockNonConvertibleManifestTarget(Parameters) noexcept { } [[nodiscard]] constexpr NonConvertibleTarget target() const noexcept { return {}; } }; class MockMismatchedManifestWithoutResidualReference final { public: struct Parameters final { mean_field::dimensions::DensityValue target; }; using ScalarDescription = mean_field::stellar::ScalarConstraint< mean_field::dimensions::quantity::Density, mean_field::dimensions::quantity::Dimensionless, mean_field::dimensions::quantity::SpecificEnergy, "mock_mismatched_without_reference.coordinate", "q_missing_ref", "mock_mismatched_without_reference.residual", "R_missing_ref">; using ModelDefinition = mean_field::integral::FixedScalarWithPhysicalCoordinate< MockMismatchedManifestWithoutResidualReference, "RootManifestMockMismatchedWithoutResidualReference", mean_field::stellar::Reads, mean_field::stellar::Changes, ScalarDescription>; explicit MockMismatchedManifestWithoutResidualReference(const Parameters parameters) noexcept : m_target(parameters.target) { } [[nodiscard]] mean_field::dimensions::DensityValue target() const noexcept { return m_target; } private: mean_field::dimensions::DensityValue m_target; }; class MockMismatchedManifestWithResidualReference final { public: struct Parameters final { mean_field::dimensions::DensityValue target; mean_field::dimensions::SpecificEnergyValue residualReference; }; using ScalarDescription = mean_field::stellar::ScalarConstraint< mean_field::dimensions::quantity::Density, mean_field::dimensions::quantity::Dimensionless, mean_field::dimensions::quantity::SpecificEnergy, "mock_mismatched_with_reference.coordinate", "q_ref", "mock_mismatched_with_reference.residual", "R_ref">; using ModelDefinition = mean_field::integral::FixedScalarWithPhysicalCoordinate< MockMismatchedManifestWithResidualReference, "RootManifestMockMismatchedWithResidualReference", mean_field::stellar::Reads, mean_field::stellar::Changes, ScalarDescription>; explicit MockMismatchedManifestWithResidualReference(const Parameters parameters) noexcept : m_target(parameters.target), m_residualReference(parameters.residualReference) { } [[nodiscard]] mean_field::dimensions::DensityValue target() const noexcept { return m_target; } [[nodiscard]] mean_field::dimensions::SpecificEnergyValue residualReference() const noexcept { return m_residualReference; } private: mean_field::dimensions::DensityValue m_target; mean_field::dimensions::SpecificEnergyValue m_residualReference; }; class MockMismatchedManifestWithWrongResidualReference final { public: struct Parameters final { mean_field::dimensions::DensityValue target; }; using ScalarDescription = mean_field::stellar::ScalarConstraint< mean_field::dimensions::quantity::Density, mean_field::dimensions::quantity::Dimensionless, mean_field::dimensions::quantity::SpecificEnergy, "mock_mismatched_wrong_reference.coordinate", "q_wrong_ref", "mock_mismatched_wrong_reference.residual", "R_wrong_ref">; using ModelDefinition = mean_field::integral::FixedScalarWithPhysicalCoordinate< MockMismatchedManifestWithWrongResidualReference, "RootManifestMockMismatchedWithWrongResidualReference", mean_field::stellar::Reads, mean_field::stellar::Changes, ScalarDescription>; explicit MockMismatchedManifestWithWrongResidualReference(const Parameters parameters) noexcept : m_target(parameters.target) { } [[nodiscard]] mean_field::dimensions::DensityValue target() const noexcept { return m_target; } [[nodiscard]] mean_field::dimensions::DensityValue residualReference() const noexcept { return m_target; } private: mean_field::dimensions::DensityValue m_target; }; class MockMissingManifest final { public: struct Parameters final { }; using ModelDefinition = mean_field::integral::FixedWithPhysicalCoordinate< MockMissingManifest, "RootManifestMockMissingManifest", mean_field::models::ModelTypeList, mean_field::models::ModelTypeList>; explicit MockMissingManifest(Parameters) noexcept { } [[nodiscard]] mean_field::dimensions::SpecificEnergyValue target() const noexcept { return mean_field::dimensions::SpecificEnergyValue{1.0}; } }; class MockUncompiledSurface final { public: struct Parameters final { }; using ModelDefinition = mean_field::surface::BoundaryCondition; explicit MockUncompiledSurface(Parameters) noexcept { } }; class MockInvariantNamedLikeSurface final { public: struct Parameters final { mean_field::dimensions::SpecificEnergyValue target; }; using ModelDefinition = mean_field::integral::FixedWithPhysicalCoordinate< MockInvariantNamedLikeSurface, "IsobaricSurface", mean_field::models::DependsOn, mean_field::models::Affects, mean_field::models::GlobalScalarNormalization< mean_field::models::PhysicalScaleLaw::dimensionless, mean_field::models::PhysicalScaleLaw::specific_energy>, mean_field::models::GeneratedManifest< "same_name_invariant.coordinate", "q_same", "same_name_invariant.residual", "R_same", "specific_energy", "specific_energy">>; explicit MockInvariantNamedLikeSurface(Parameters parameters) noexcept : m_target(parameters.target) { } [[nodiscard]] mean_field::dimensions::SpecificEnergyValue target() const noexcept { return m_target; } private: mean_field::dimensions::SpecificEnergyValue m_target; }; using GenericManifestModel = mean_field::model::StellarModel>; using GenericManifestForm = mean_field::operators::CompiledStellarEquilibriumForm; using GenericManifestJacobian = mean_field::operators::CompiledStellarEquilibriumJacobianForm; using GenericManifest = mean_field::operators:: EquilibriumSystemManifest; using InconsistentScaleModel = mean_field::model::StellarModel>; using InconsistentScaleForm = mean_field::operators::CompiledStellarEquilibriumForm; using InconsistentScaleJacobian = mean_field::operators::CompiledStellarEquilibriumJacobianForm; using InconsistentScaleManifest = mean_field::operators:: EquilibriumSystemManifest; using NonConvertibleTargetModel = mean_field::model::StellarModel>; using NonConvertibleTargetForm = mean_field::operators::CompiledStellarEquilibriumForm; using MissingManifestModel = mean_field::model::StellarModel>; using MissingManifestForm = mean_field::operators::CompiledStellarEquilibriumForm; using UncompiledSurfaceModel = mean_field::model::StellarModel< mean_field::models:: SpecificationSet>; using UncompiledSurfaceForm = mean_field::operators::CompiledStellarEquilibriumForm; using UncompiledSurfaceJacobian = mean_field::operators::CompiledStellarEquilibriumJacobianForm; using UncompiledSurfaceManifest = mean_field::operators:: EquilibriumSystemManifest; using CrossRoleNameCollisionModel = mean_field::model::StellarModel>; using CrossRoleNameCollisionForm = mean_field::operators::CompiledStellarEquilibriumForm; using CrossRoleNameCollisionJacobian = mean_field::operators::CompiledStellarEquilibriumJacobianForm; using CrossRoleNameCollisionManifest = mean_field::operators::EquilibriumSystemManifest< CrossRoleNameCollisionModel, CrossRoleNameCollisionForm, CrossRoleNameCollisionJacobian>; template using ManifestExtensionModel = mean_field::model::StellarModel>; template using ManifestExtensionForm = mean_field::operators::CompiledStellarEquilibriumForm>; using MissingResidualReferenceModel = ManifestExtensionModel; using MissingResidualReferenceForm = ManifestExtensionForm; using WrongResidualReferenceModel = ManifestExtensionModel; using WrongResidualReferenceForm = ManifestExtensionForm; using DistinctResidualReferenceModel = ManifestExtensionModel; using DistinctResidualReferenceForm = ManifestExtensionForm; using DistinctResidualReferenceJacobian = mean_field::operators::CompiledStellarEquilibriumJacobianForm; using DistinctResidualReferenceManifest = mean_field::operators::EquilibriumSystemManifest< DistinctResidualReferenceModel, DistinctResidualReferenceForm, DistinctResidualReferenceJacobian>; [[nodiscard]] Manifest make_manifest() { const std::array valueSizes{2, 3, 4, 5, 6, 1}; const std::array residualSizes{4, 5, 2, 3, 6, 1}; const CanonicalModel model{ mean_field::eos::Polytrope{1.0, 0.25}, mean_field::surface::Isobaric{mean_field::dimensions::PressureValue{0.125}}, mean_field::models::FixedTotalMass{mean_field::dimensions::MassValue{2.5}} }; return {valueSizes, residualSizes, model, 3}; } } // namespace TEST_CASE( "Manifest Capability Rejects Malformed Targets And Foreign Generated Blocks", "[root-manifest][type-contract][sfinae]" ) { using namespace mean_field; STATIC_CHECK(models::ModelSpecification); STATIC_CHECK_FALSE(std::convertible_to); STATIC_CHECK_FALSE(models::CompleteGeneratedScalarDimensionsFor); STATIC_CHECK_FALSE(models::CompleteGeneratedNormalizationFor); STATIC_CHECK_FALSE(models::CompleteGeneratedManifestFor); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable); STATIC_CHECK_FALSE(operators::CompilableRootManifestFor); // A typed target may intentionally differ from the equation's residual // quantity, but then the manifest needs a residual-valued physical // reference. Absence and the wrong quantity are both rejected without a // hard template error. STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor ); STATIC_CHECK_FALSE(operators::CompilableRootManifestFor); STATIC_CHECK(operators::CompilableRootManifestFor); // This is a valid compiled form for GenericManifestModel, but its magnetic // generated pair is foreign to CanonicalModel. Described metadata alone // must not make that mismatched model/form pair manifest-compilable. STATIC_CHECK(operators::CompilableRootManifestFor); STATIC_CHECK_FALSE(operators::CompilableRootManifestFor); } TEST_CASE( "Dimensionally Distinct Constraint Residuals Use Their Own Physical Reference", "[root-manifest][type-contract][physics][scaling]" ) { using namespace mean_field; const auto model = model::StellarModel( eos::Polytrope({.n = 1.0, .K = 0.25}), surface::Isobaric({.Psurf = dimensions::PressureValue{0.125}}), integral::FixedTotalMass({.Mtotal = dimensions::MassValue{2.5}}), MockMismatchedManifestWithResidualReference( {.target = dimensions::DensityValue{12.0}, .residualReference = dimensions::SpecificEnergyValue{3.25}} ) ); const std::array valueSizes{2, 3, 4, 5, 6, 1, 1}; const std::array residualSizes{4, 5, 2, 3, 6, 1, 1}; const DistinctResidualReferenceManifest manifest(valueSizes, residualSizes, model, 3); const auto &descriptor = manifest.template specification(); CHECK(descriptor.target == 12.0); CHECK(descriptor.targetUnits == "density"); CHECK(descriptor.residualUnits == "specific_energy"); CHECK(descriptor.residualScale == 3.25); CHECK(manifest.residualBlocks().back().scale == 3.25); } TEST_CASE( "Root Manifest Stable IDs Are Unique Per Block Kind", "[root-manifest][identity][type-contract]" ) { using namespace mean_field; using GeneratedValueCollisionModel = ManifestExtensionModel; using GeneratedResidualCollisionModel = ManifestExtensionModel; using PhysicalValueCollisionModel = ManifestExtensionModel; using PhysicalResidualCollisionModel = ManifestExtensionModel; using ReusedSymbolsModel = ManifestExtensionModel; using NonConstexprManifestModel = ManifestExtensionModel; STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor< GeneratedValueCollisionModel, ManifestExtensionForm> ); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor< GeneratedResidualCollisionModel, ManifestExtensionForm> ); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor< PhysicalValueCollisionModel, ManifestExtensionForm> ); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor< PhysicalResidualCollisionModel, ManifestExtensionForm> ); // Symbols are mathematical presentation labels, not machine identities. STATIC_CHECK( operators::CompilableRootManifestFor> ); // A broad physics manifest may be structurally complete while its identity // strings are unsuitable for a constexpr compiled root descriptor. The // root capability must reject it through detection, not a hard error. STATIC_CHECK(models::GeneratedManifestDefinition); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor> ); } TEST_CASE( "Root Manifest Requires Complete Constant Generated Metadata", "[root-manifest][identity][type-contract][sfinae]" ) { using namespace mean_field; STATIC_CHECK(models::GeneratedManifestDefinition); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK( operators::CompilableRootManifestFor< ManifestExtensionModel, ManifestExtensionForm> ); // These structural manifests deliberately lie through available=true. // The root capability must inspect every identity and unit field itself. STATIC_CHECK(models::GeneratedManifestDefinition); STATIC_CHECK(models::GeneratedManifestDefinition); STATIC_CHECK(models::GeneratedManifestDefinition); STATIC_CHECK(models::GeneratedManifestDefinition); STATIC_CHECK(models::GeneratedManifestDefinition); STATIC_CHECK(models::GeneratedManifestDefinition); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable>); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable>); STATIC_CHECK( operators::StellarEquilibriumSystemCompilable> ); STATIC_CHECK( operators::StellarEquilibriumSystemCompilable> ); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable>); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable>); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor< ManifestExtensionModel, ManifestExtensionForm> ); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor< ManifestExtensionModel, ManifestExtensionForm> ); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor< ManifestExtensionModel, ManifestExtensionForm> ); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor< ManifestExtensionModel, ManifestExtensionForm> ); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor< ManifestExtensionModel, ManifestExtensionForm> ); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor< ManifestExtensionModel, ManifestExtensionForm> ); // Constexpr enforcement covers unit metadata as well as IDs and symbols. STATIC_CHECK(models::GeneratedManifestDefinition); STATIC_CHECK(models::CompleteGeneratedManifestFor); STATIC_CHECK_FALSE( operators::CompilableRootManifestFor< ManifestExtensionModel, ManifestExtensionForm> ); } TEST_CASE( "Self-Described Generated Specifications Enter The Manifest Without Combination Code", "[root-manifest][variadic][extension]" ) { using namespace mean_field; using MockValue = utils::blocks::generated_value_block>; using MockResidual = utils::blocks::generated_residual_block>; STATIC_CHECK(operators::StellarEquilibriumSystemCompilable); STATIC_CHECK(operators::CompilableRootManifestFor); STATIC_CHECK_FALSE(operators::CompilableRootManifestFor); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable); STATIC_CHECK_FALSE(operators::CompilableRootManifestFor); STATIC_CHECK_FALSE(UncompiledSurfaceManifest::hasCompleteEquilibriumCompiler); STATIC_CHECK( UncompiledSurfaceManifest::compilationClass == models::EquilibriumSystemCompilation::equation_contributions_only ); STATIC_CHECK(normalization::CompleteGeneratedPhysicalRieszNormalizationFor); STATIC_CHECK(preconditioning::SpecificationBorderContribution::registered); STATIC_CHECK( utils::blocks::contains_type_v< MockValue, typename preconditioning::SpecificationBorderContribution< MockFixedMagneticSpecificEnergy>::CorrectionBlocks> ); STATIC_CHECK( utils::blocks::contains_type_v< preconditioning::Coupling, typename preconditioning::SpecificationBorderContribution< MockFixedMagneticSpecificEnergy>::RequiredCouplings> ); STATIC_CHECK_FALSE(operators::hasCompleteStellarEquilibriumRuntime); const auto model = model::StellarModel( eos::Polytrope({.n = 1.0, .K = 0.25}), surface::Isobaric({.Psurf = dimensions::PressureValue{0.125}}), integral::FixedTotalMass({.Mtotal = dimensions::MassValue{2.5}}), MockFixedMagneticSpecificEnergy({.target = dimensions::SpecificEnergyValue{7.5}}) ); const std::array valueSizes{2, 3, 4, 5, 6, 1, 1}; const std::array residualSizes{4, 5, 2, 3, 6, 1, 1}; const GenericManifest manifest(valueSizes, residualSizes, model, 3); REQUIRE(manifest.valueBlocks().size() == 7); REQUIRE(manifest.residualBlocks().size() == 7); CHECK(manifest.valueBlocks().back().stableId == "mock_magnetic_specific_energy.amplitude"); CHECK(manifest.valueBlocks().back().symbol == "a_B"); CHECK(manifest.valueBlocks().back().columnPolicy == operators::RootColumnPolicy::generated_physical_coordinate); CHECK(manifest.residualBlocks().back().stableId == "mock_magnetic_specific_energy.residual"); CHECK(manifest.residualBlocks().back().symbol == "R_EB"); CHECK(manifest.residualBlocks().back().scale == 7.5); const auto descriptor = std::find_if( manifest.constraints().begin(), manifest.constraints().end(), [](const operators::RootConstraintDescriptor &candidate) { return candidate.stableId == "RootManifestMockFixedMagneticSpecificEnergy"; } ); REQUIRE(descriptor != manifest.constraints().end()); CHECK(descriptor->valueBlock == utils::blocks::type_index_v); CHECK( descriptor->residualBlock == utils::blocks::type_index_v ); CHECK(descriptor->target == 7.5); CHECK(descriptor->residualScale == 7.5); CHECK(descriptor->targetUnits == "specific_energy"); } TEST_CASE( "Root Manifest Rejects Non-Finite Targets And Scales", "[root-manifest][runtime-contract][scaling]" ) { using namespace mean_field; const std::array valueSizes{2, 3, 4, 5, 6, 1, 1}; const std::array residualSizes{4, 5, 2, 3, 6, 1, 1}; const auto nonFiniteTargetModel = model::StellarModel( eos::Polytrope({.n = 1.0, .K = 0.25}), surface::Isobaric({.Psurf = dimensions::PressureValue{0.125}}), integral::FixedTotalMass({.Mtotal = dimensions::MassValue{2.5}}), MockFixedMagneticSpecificEnergy( {.target = dimensions::SpecificEnergyValue{std::numeric_limits::quiet_NaN()}} ) ); CHECK_THROWS_WITH( GenericManifest(valueSizes, residualSizes, nonFiniteTargetModel, 3), "An equilibrium-system manifest constraint requires a finite target." ); const auto nonFiniteResidualReferenceModel = model::StellarModel( eos::Polytrope({.n = 1.0, .K = 0.25}), surface::Isobaric({.Psurf = dimensions::PressureValue{0.125}}), integral::FixedTotalMass({.Mtotal = dimensions::MassValue{2.5}}), MockMismatchedManifestWithResidualReference( {.target = dimensions::DensityValue{12.0}, .residualReference = dimensions::SpecificEnergyValue{std::numeric_limits::quiet_NaN()}} ) ); const std::array distinctValueSizes{2, 3, 4, 5, 6, 1, 1}; const std::array distinctResidualSizes{4, 5, 2, 3, 6, 1, 1}; CHECK_THROWS_WITH( DistinctResidualReferenceManifest( distinctValueSizes, distinctResidualSizes, nonFiniteResidualReferenceModel, 3 ), "An equilibrium-system manifest constraint requires a finite, positive residual scale." ); /* * This adversarial target is non-finite when the generated residual block * obtains its scale, then finite when the constraint descriptor is built. * It independently verifies the final block-scale guard rather than * reaching the target guard twice for the same NaN. */ const auto inconsistentScaleModel = model::StellarModel( eos::Polytrope({.n = 1.0, .K = 0.25}), surface::Isobaric({.Psurf = dimensions::PressureValue{0.125}}), integral::FixedTotalMass({.Mtotal = dimensions::MassValue{2.5}}), MockInconsistentManifestScale({.first = std::numeric_limits::quiet_NaN(), .subsequent = 2.0}) ); STATIC_CHECK(std::same_as, InconsistentScaleModel>); const std::array inconsistentValueSizes{2, 3, 4, 5, 6, 1, 1}; const std::array inconsistentResidualSizes{4, 5, 2, 3, 6, 1, 1}; CHECK_THROWS_WITH( InconsistentScaleManifest(inconsistentValueSizes, inconsistentResidualSizes, inconsistentScaleModel, 3), "An equilibrium-system manifest block requires a finite, positive scale." ); } TEST_CASE( "Manifest Lookup Uses Role And Stable Name Together", "[root-manifest][identity][type-contract]" ) { using namespace mean_field; constexpr auto surfaceKey = models::SpecificationTraits::key; constexpr auto invariantKey = models::SpecificationTraits::key; STATIC_CHECK(surfaceKey.stableName == invariantKey.stableName); STATIC_CHECK(surfaceKey.role != invariantKey.role); STATIC_CHECK(models::specificationKeysAreUnique); STATIC_CHECK(operators::StellarEquilibriumSystemCompilable); STATIC_CHECK(operators::CompilableRootManifestFor); const auto model = model::StellarModel( eos::Polytrope({.n = 1.0, .K = 0.25}), surface::Isobaric({.Psurf = dimensions::PressureValue{0.125}}), integral::FixedTotalMass({.Mtotal = dimensions::MassValue{2.5}}), MockInvariantNamedLikeSurface({.target = dimensions::SpecificEnergyValue{7.5}}) ); const std::array valueSizes{2, 3, 4, 5, 6, 1, 1}; const std::array residualSizes{4, 5, 2, 3, 6, 1, 1}; const CrossRoleNameCollisionManifest manifest(valueSizes, residualSizes, model, 3); const auto &surfaceDescriptor = manifest.specification(); const auto &invariantDescriptor = manifest.specification(); CHECK(surfaceDescriptor.stableId == "IsobaricSurface"); CHECK(invariantDescriptor.stableId == "IsobaricSurface"); CHECK(surfaceDescriptor.role == models::SpecificationRole::boundary_condition); CHECK(invariantDescriptor.role == models::SpecificationRole::invariant); CHECK(surfaceDescriptor.target == 0.125); CHECK(invariantDescriptor.target == 7.5); CHECK(invariantDescriptor.valueBlock == 6); } TEST_CASE( "Model Values Are Stored In Canonical Specification Order", tags::model_specification_type_contract ) { STATIC_CHECK(std::same_as); STATIC_CHECK(CanonicalModel::symbolicallySquare); STATIC_CHECK(CanonicalModel::hasCompleteEquilibriumDeclaration); STATIC_CHECK(mean_field::operators::StellarEquilibriumSystemCompilable); STATIC_CHECK(CentralDensityModel::symbolicallySquare); STATIC_CHECK(CentralDensityModel::hasCompleteEquilibriumDeclaration); STATIC_CHECK(mean_field::operators::StellarEquilibriumSystemCompilable); const mean_field::eos::Polytrope equationOfState{2.0, 0.75}; const mean_field::surface::Isobaric surface{mean_field::dimensions::PressureValue{0.125}}; const mean_field::models::FixedTotalMass mass{mean_field::dimensions::MassValue{1.75}}; const CanonicalModel model{mass, surface, equationOfState}; CHECK(model.specification().polytropic_index() == 2.0); CHECK(model.specification().targetPressure().value() == 0.125); CHECK( model.specification().targetMass() == mean_field::dimensions::MassValue{1.75} ); const auto descriptors = model.runtimeSpecificationDescriptors(); REQUIRE(descriptors.size() == 3); CHECK(descriptors[0].specification.name == "Polytrope"); CHECK(descriptors[1].specification.name == "IsobaricSurface"); CHECK(descriptors[2].specification.name == "FixedTotalMass"); CHECK(descriptors[0].canonicalIndex == 0); CHECK(descriptors[1].canonicalIndex == 1); CHECK(descriptors[2].canonicalIndex == 2); CHECK(descriptors[0].hasDeclarativeDefinition); CHECK(descriptors[1].hasDeclarativeDefinition); CHECK(descriptors[2].hasDeclarativeDefinition); } TEST_CASE( "Fixed Angular Momentum Manifest Appends A Generated Physical Coordinate And Invariant Row", tags::root_manifest_type_contract ) { using namespace mean_field; const std::array valueSizes{2, 3, 4, 5, 6, 1, 1}; const std::array residualSizes{4, 5, 2, 3, 6, 1, 1}; const AngularMomentumModel model( eos::Polytrope{1.0, 0.25}, surface::Isobaric{dimensions::PressureValue{0.125}}, models::FixedTotalMass{dimensions::MassValue{2.5}}, models::FixedAngularMomentum{dimensions::AngularMomentumValue{7.5}} ); const AngularMomentumManifest manifest(valueSizes, residualSizes, model, 3); STATIC_CHECK(AngularMomentumForm::value_block_count == 7); STATIC_CHECK(AngularMomentumForm::residual_block_count == 7); REQUIRE(manifest.valueBlocks().size() == 7); REQUIRE(manifest.residualBlocks().size() == 7); CHECK(manifest.layout().value_offsets().Last() == 22); CHECK(manifest.layout().residual_offsets().Last() == 22); CHECK(manifest.valueBlocks()[6].stableId == "fixed_angular_momentum.angular_velocity"); CHECK(manifest.valueBlocks()[6].symbol == "Omega"); CHECK(manifest.valueBlocks()[6].columnPolicy == operators::RootColumnPolicy::generated_physical_coordinate); CHECK(manifest.residualBlocks()[6].stableId == "fixed_angular_momentum.residual"); CHECK(manifest.residualBlocks()[6].symbol == "R_J"); CHECK(manifest.residualBlocks()[6].scale == 7.5); const auto constraints = manifest.constraints(); REQUIRE(constraints.size() == 3); CHECK(constraints[2].stableId == "FixedAngularMomentum"); CHECK(constraints[2].role == models::SpecificationRole::invariant); CHECK(constraints[2].valueBlock == 6); CHECK(constraints[2].residualBlock == 6); CHECK(constraints[2].rowArity == 1); CHECK(constraints[2].columnArity == 1); CHECK(constraints[2].target == 7.5); CHECK_FALSE(constraints[2].carrierTarget.has_value()); CHECK(constraints[2].targetUnits == "angular_momentum"); CHECK(constraints[2].residualScale == 7.5); } TEST_CASE( "Central Density Root Manifest Appends A Carrier Phase Row And Solver Border", tags::root_manifest_type_contract ) { STATIC_CHECK(mean_field::operators::CompilableRootManifestFor); const std::array valueSizes{2, 3, 4, 5, 6, 1, 1}; const std::array residualSizes{4, 5, 2, 3, 6, 1, 1}; const CentralDensityModel model{ mean_field::eos::Polytrope{1.0, 0.125}, mean_field::surface::Isobaric{mean_field::dimensions::PressureValue{0.125}}, mean_field::models::FixedTotalMass{mean_field::dimensions::MassValue{2.5}}, mean_field::models::FixedCentralDensity{mean_field::dimensions::DensityValue{8.0}} }; const CentralManifest manifest(valueSizes, residualSizes, model, 3); CHECK(manifest.layout().value_offsets().Last() == 22); CHECK(manifest.layout().residual_offsets().Last() == 22); REQUIRE(manifest.valueBlocks().size() == 7); REQUIRE(manifest.residualBlocks().size() == 7); CHECK(manifest.valueBlocks()[6].stableId == "fixed_central_density.border"); CHECK(manifest.valueBlocks()[6].symbol == "lambda_rho_c"); CHECK(manifest.valueBlocks()[6].columnPolicy == mean_field::operators::RootColumnPolicy::solver_border); CHECK(manifest.residualBlocks()[6].stableId == "fixed_central_density.residual"); CHECK(manifest.residualBlocks()[6].symbol == "R_rho_c"); CHECK(manifest.residualBlocks()[6].scale == 2.0); const auto constraints = manifest.constraints(); REQUIRE(constraints.size() == 3); CHECK(constraints[2].stableId == "FixedCentralDensity"); CHECK(constraints[2].role == mean_field::models::SpecificationRole::phase_condition); CHECK(constraints[2].valueBlock == 6); CHECK(constraints[2].residualBlock == 6); CHECK(constraints[2].target == 8.0); REQUIRE(constraints[2].carrierTarget.has_value()); CHECK(*constraints[2].carrierTarget == 2.0); CHECK(constraints[2].residualScale == 2.0); CHECK(manifest.specification().target == 0.125); } TEST_CASE( "Compiled Root Manifest Centralizes Canonical Blocks Provenance And Scaling", tags::root_manifest_type_contract ) { const Manifest manifest = make_manifest(); STATIC_CHECK(Manifest::symbolicallySquare); STATIC_CHECK(Manifest::compilationClass == mean_field::models::ModelCompilationClass::isolated_root); STATIC_CHECK( mean_field::operators::RootManifestSpecificationDescriptorFor< mean_field::models::FixedTotalMass, CanonicalModel> ); STATIC_CHECK( mean_field::operators::RootManifestSpecificationDescriptorFor ); STATIC_CHECK_FALSE( mean_field::operators::RootManifestSpecificationDescriptorFor ); STATIC_CHECK(HasRootManifestSpecificationDescriptor); STATIC_CHECK(HasRootManifestSpecificationDescriptor); STATIC_CHECK_FALSE(HasRootManifestSpecificationDescriptor); STATIC_CHECK_FALSE( CanMutateManifestBlock< typename Manifest::StateView, decltype(mean_field::utils::blocks::density_field.mass_term)> ); STATIC_CHECK_FALSE( CanMutateManifestBlock< typename Manifest::DirectionView, decltype(mean_field::utils::blocks::density_field.mass_term)> ); STATIC_CHECK( CanMutateManifestBlock< typename Manifest::MutableStateView, decltype(mean_field::utils::blocks::density_field.mass_term)> ); CHECK(manifest.layout().value_offsets().Last() == 21); CHECK(manifest.layout().residual_offsets().Last() == 21); const auto values = manifest.valueBlocks(); const auto residuals = manifest.residualBlocks(); REQUIRE(values.size() == 6); REQUIRE(residuals.size() == 6); CHECK(values[0].stableId == "density"); CHECK(values[0].symbol == "rho"); CHECK(values[1].stableId == "surface_deformation"); CHECK(values[5].stableId == "fixed_total_mass.multiplier"); CHECK(values[5].symbol == "C"); CHECK(values[5].provenance == mean_field::operators::RootBlockProvenance::model_specification); CHECK(values[5].source == "FixedTotalMass"); CHECK(values[5].columnPolicy == mean_field::operators::RootColumnPolicy::existing_physical_multiplier); CHECK(residuals[5].stableId == "fixed_total_mass.residual"); CHECK(residuals[5].symbol == "R_M"); CHECK(residuals[5].rowInjection == mean_field::operators::RootRowInjection::append_global); CHECK(residuals[5].scalePolicy == mean_field::operators::RootScalePolicy::target_relative); CHECK(residuals[5].scale == 2.5); const auto replacements = manifest.rowReplacements(); REQUIRE(replacements.size() == 1); CHECK(replacements[0].sourceSpecification == "IsobaricSurface"); CHECK(replacements[0].replacedRowCount == 3); CHECK(replacements[0].carrierResidualBlock == 4); const auto constraints = manifest.constraints(); REQUIRE(constraints.size() == 2); const auto &massConstraint = manifest.specification(); CHECK(massConstraint.stableId == "FixedTotalMass"); CHECK(massConstraint.valueBlock == 5); CHECK(massConstraint.residualBlock == 5); CHECK(massConstraint.target == 2.5); CHECK(massConstraint.residualScale == 2.5); const auto &surfaceConstraint = manifest.specification(); CHECK(surfaceConstraint.stableId == "IsobaricSurface"); CHECK(surfaceConstraint.rowInjection == mean_field::operators::RootRowInjection::replace_carrier_rows); CHECK(surfaceConstraint.target == 0.125); CHECK_FALSE(surfaceConstraint.carrierTarget.has_value()); const auto report = manifest.fixedMassReport(2.75); CHECK(report.achieved == 2.75); CHECK(report.dimensionalResidual == 0.25); CHECK(report.scaledResidual == 0.1); } TEST_CASE( "Typed Root Views Resolve Blocks Through The Compiled Manifest", tags::root_manifest_type_contract ) { const Manifest manifest = make_manifest(); STATIC_CHECK(HasLvalueRootViewFactories); STATIC_CHECK_FALSE(StateViewAcceptsTemporaryVector); STATIC_CHECK_FALSE(StateViewAcceptsConstTemporaryVector); STATIC_CHECK_FALSE(DirectionViewAcceptsTemporaryVector); STATIC_CHECK_FALSE(DirectionViewAcceptsConstTemporaryVector); STATIC_CHECK_FALSE(ResidualViewAcceptsTemporaryVector); STATIC_CHECK_FALSE(ResidualViewAcceptsConstTemporaryVector); STATIC_CHECK_FALSE(TemporaryManifestProvidesStateView); STATIC_CHECK_FALSE(TemporaryManifestProvidesReadOnlyStateView); STATIC_CHECK_FALSE(TemporaryManifestProvidesDirectionView); STATIC_CHECK_FALSE(TemporaryManifestProvidesResidualView); STATIC_CHECK( std::constructible_from ); STATIC_CHECK( std::constructible_from ); STATIC_CHECK( std::constructible_from ); STATIC_CHECK_FALSE( std::constructible_from ); STATIC_CHECK_FALSE( std::constructible_from ); STATIC_CHECK_FALSE( std::constructible_from ); STATIC_CHECK_FALSE( std::constructible_from ); STATIC_CHECK_FALSE( std::constructible_from ); STATIC_CHECK_FALSE( std::constructible_from ); STATIC_CHECK_FALSE( std::constructible_from ); mfem::Vector state(manifest.layout().value_offsets().Last()); for (int index = 0; index < state.Size(); ++index) { state(index) = static_cast(index + 1); } const auto stateView = manifest.stateView(state); const mfem::Vector density = stateView.block(mean_field::utils::blocks::density_field.mass_term); const mfem::Vector surface = stateView.block(mean_field::utils::blocks::surface_deformation_field.parameters_term); const mfem::Vector multiplier = stateView.block(mean_field::utils::blocks::fixed_total_mass_constraint.mass_normalization_term); REQUIRE(density.Size() == 2); REQUIRE(surface.Size() == 3); REQUIRE(multiplier.Size() == 1); CHECK(density(0) == 1.0); CHECK(surface(0) == 3.0); CHECK(multiplier(0) == 21.0); mfem::Vector residual(manifest.layout().residual_offsets().Last()); residual = 0.0; const auto residualView = manifest.residualView(residual); mfem::Vector massResidual(1); massResidual(0) = -0.375; residualView.assign(mean_field::utils::blocks::fixed_total_mass_constraint.mass_normalization_term, massResidual); CHECK(residual(20) == -0.375); mfem::Vector wrongState(state.Size() - 1); CHECK_THROWS_AS(manifest.stateView(wrongState), std::invalid_argument); }