Files
MeanField/tests/operators/root_manifest.cpp
2026-09-06 10:15:00 -04:00

1371 lines
64 KiB
C++

#include <algorithm>
#include <array>
#include <concepts>
#include <limits>
#include <optional>
#include <stdexcept>
#include <string_view>
#include <type_traits>
#include <utility>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/catch_test_macros.hpp>
#include <mfem.hpp>
import mean_field;
import test_helpers;
namespace {
template <typename ManifestType, typename Specification>
concept HasRootManifestSpecificationDescriptor = requires(const ManifestType &manifest) {
{
manifest.template specification<Specification>()
} -> std::same_as<const mean_field::operators::RootConstraintDescriptor &>;
};
template <typename View, typename Term>
concept CanMutateManifestBlock = requires(View &view, const Term &term) {
view.block(term)(0) = 1.0;
};
template <typename ManifestType>
concept HasLvalueRootViewFactories = requires(
const ManifestType &manifest,
mfem::Vector &mutableVector,
const mfem::Vector &readOnlyVector
) {
{ manifest.stateView(mutableVector) } -> std::same_as<typename ManifestType::MutableStateView>;
{ manifest.stateView(readOnlyVector) } -> std::same_as<typename ManifestType::StateView>;
{ manifest.directionView(readOnlyVector) } -> std::same_as<typename ManifestType::DirectionView>;
{ manifest.residualView(mutableVector) } -> std::same_as<typename ManifestType::RootResidualView>;
};
template <typename ManifestType>
concept StateViewAcceptsTemporaryVector = requires(const ManifestType &manifest) {
manifest.stateView(std::declval<mfem::Vector &&>());
};
template <typename ManifestType>
concept StateViewAcceptsConstTemporaryVector = requires(const ManifestType &manifest) {
manifest.stateView(std::declval<const mfem::Vector &&>());
};
template <typename ManifestType>
concept DirectionViewAcceptsTemporaryVector = requires(const ManifestType &manifest) {
manifest.directionView(std::declval<mfem::Vector &&>());
};
template <typename ManifestType>
concept DirectionViewAcceptsConstTemporaryVector = requires(const ManifestType &manifest) {
manifest.directionView(std::declval<const mfem::Vector &&>());
};
template <typename ManifestType>
concept ResidualViewAcceptsTemporaryVector = requires(const ManifestType &manifest) {
manifest.residualView(std::declval<mfem::Vector &&>());
};
template <typename ManifestType>
concept ResidualViewAcceptsConstTemporaryVector = requires(const ManifestType &manifest) {
manifest.residualView(std::declval<const mfem::Vector &&>());
};
template <typename ManifestType>
concept TemporaryManifestProvidesStateView = requires(mfem::Vector &vector) {
std::declval<ManifestType &&>().stateView(vector);
};
template <typename ManifestType>
concept TemporaryManifestProvidesReadOnlyStateView = requires(const mfem::Vector &vector) {
std::declval<ManifestType &&>().stateView(vector);
};
template <typename ManifestType>
concept TemporaryManifestProvidesDirectionView = requires(const mfem::Vector &vector) {
std::declval<ManifestType &&>().directionView(vector);
};
template <typename ManifestType>
concept TemporaryManifestProvidesResidualView = requires(mfem::Vector &vector) {
std::declval<ManifestType &&>().residualView(vector);
};
using CanonicalModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
mean_field::eos::Polytrope,
mean_field::models::FixedTotalMass,
mean_field::surface::Isobaric>>;
using PermutedModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
mean_field::models::FixedTotalMass,
mean_field::surface::Isobaric,
mean_field::eos::Polytrope>>;
using CentralDensityModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
mean_field::models::FixedCentralDensity,
mean_field::surface::Isobaric,
mean_field::eos::Polytrope,
mean_field::models::FixedTotalMass>>;
using Form = mean_field::operators::CompiledStellarEquilibriumForm<CanonicalModel>;
using JacobianForm =
mean_field::operators::CompiledStellarEquilibriumJacobianForm<CanonicalModel>;
using Manifest = mean_field::operators::CompiledRootManifest<CanonicalModel, Form, JacobianForm>;
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<CentralDensityModel, CentralForm, CentralJacobianForm>;
using AngularMomentumModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
mean_field::eos::Polytrope,
mean_field::surface::Isobaric,
mean_field::models::FixedTotalMass,
mean_field::models::FixedAngularMomentum>>;
using AngularMomentumForm = mean_field::operators::CompiledStellarEquilibriumForm<AngularMomentumModel>;
using AngularMomentumJacobian =
mean_field::operators::CompiledStellarEquilibriumJacobianForm<AngularMomentumModel>;
using AngularMomentumManifest = mean_field::operators::CompiledRootManifest<
AngularMomentumModel,
AngularMomentumForm,
AngularMomentumJacobian>;
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::utils::blocks::enthalpy::specific::residual>,
mean_field::models::GeneratedNormalization<ScalarDimensionless, ScalarSpecificEnergy>,
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 <mean_field::models::FixedString Name, typename ManifestDefinition>
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::stellar::state::Density>,
mean_field::models::Affects<mean_field::models::stellar::equation::HydrostaticBalance>,
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::stellar::state::Density>,
mean_field::models::Affects<mean_field::models::stellar::equation::HydrostaticBalance>,
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::state::Density>,
mean_field::stellar::Changes<mean_field::stellar::equation::HydrostaticBalance>,
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::state::Density>,
mean_field::stellar::Changes<mean_field::stellar::equation::HydrostaticBalance>,
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::state::Density>,
mean_field::stellar::Changes<mean_field::stellar::equation::HydrostaticBalance>,
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::state::Density>,
mean_field::stellar::Changes<mean_field::stellar::equation::HydrostaticBalance>,
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::utils::blocks::density::mass::value>,
mean_field::models::ModelTypeList<mean_field::utils::blocks::enthalpy::specific::residual>>;
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<
MockUncompiledSurface,
"RootManifestMockUncompiledSurface">;
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::stellar::state::Density>,
mean_field::models::Affects<mean_field::models::stellar::equation::HydrostaticBalance>,
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<mean_field::models::SpecificationSet<
mean_field::eos::Polytrope,
mean_field::surface::Isobaric,
mean_field::models::FixedTotalMass,
MockFixedMagneticSpecificEnergy>>;
using GenericManifestForm = mean_field::operators::CompiledStellarEquilibriumForm<GenericManifestModel>;
using GenericManifestJacobian =
mean_field::operators::CompiledStellarEquilibriumJacobianForm<GenericManifestModel>;
using GenericManifest = mean_field::operators::EquilibriumSystemManifest<
GenericManifestModel,
GenericManifestForm,
GenericManifestJacobian>;
using InconsistentScaleModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
mean_field::eos::Polytrope,
mean_field::surface::Isobaric,
mean_field::models::FixedTotalMass,
MockInconsistentManifestScale>>;
using InconsistentScaleForm = mean_field::operators::CompiledStellarEquilibriumForm<InconsistentScaleModel>;
using InconsistentScaleJacobian =
mean_field::operators::CompiledStellarEquilibriumJacobianForm<InconsistentScaleModel>;
using InconsistentScaleManifest = mean_field::operators::EquilibriumSystemManifest<
InconsistentScaleModel,
InconsistentScaleForm,
InconsistentScaleJacobian>;
using NonConvertibleTargetModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
mean_field::eos::Polytrope,
mean_field::surface::Isobaric,
mean_field::models::FixedTotalMass,
MockNonConvertibleManifestTarget>>;
using NonConvertibleTargetForm =
mean_field::operators::CompiledStellarEquilibriumForm<NonConvertibleTargetModel>;
using MissingManifestModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
mean_field::eos::Polytrope,
mean_field::surface::Isobaric,
mean_field::models::FixedTotalMass,
MockMissingManifest>>;
using MissingManifestForm = mean_field::operators::CompiledStellarEquilibriumForm<MissingManifestModel>;
using UncompiledSurfaceModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
mean_field::eos::Polytrope,
MockUncompiledSurface,
mean_field::models::FixedTotalMass>>;
using UncompiledSurfaceForm = mean_field::operators::CompiledStellarEquilibriumForm<UncompiledSurfaceModel>;
using UncompiledSurfaceJacobian =
mean_field::operators::CompiledStellarEquilibriumJacobianForm<UncompiledSurfaceModel>;
using UncompiledSurfaceManifest = mean_field::operators::EquilibriumSystemManifest<
UncompiledSurfaceModel,
UncompiledSurfaceForm,
UncompiledSurfaceJacobian>;
using CrossRoleNameCollisionModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
mean_field::eos::Polytrope,
mean_field::surface::Isobaric,
mean_field::models::FixedTotalMass,
MockInvariantNamedLikeSurface>>;
using CrossRoleNameCollisionForm =
mean_field::operators::CompiledStellarEquilibriumForm<CrossRoleNameCollisionModel>;
using CrossRoleNameCollisionJacobian =
mean_field::operators::CompiledStellarEquilibriumJacobianForm<CrossRoleNameCollisionModel>;
using CrossRoleNameCollisionManifest = mean_field::operators::EquilibriumSystemManifest<
CrossRoleNameCollisionModel,
CrossRoleNameCollisionForm,
CrossRoleNameCollisionJacobian>;
template <typename Specification>
using ManifestExtensionModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
mean_field::eos::Polytrope,
mean_field::surface::Isobaric,
mean_field::models::FixedTotalMass,
Specification>>;
template <typename Specification>
using ManifestExtensionForm =
mean_field::operators::CompiledStellarEquilibriumForm<ManifestExtensionModel<Specification>>;
using MissingResidualReferenceModel =
ManifestExtensionModel<MockMismatchedManifestWithoutResidualReference>;
using MissingResidualReferenceForm =
ManifestExtensionForm<MockMismatchedManifestWithoutResidualReference>;
using WrongResidualReferenceModel =
ManifestExtensionModel<MockMismatchedManifestWithWrongResidualReference>;
using WrongResidualReferenceForm =
ManifestExtensionForm<MockMismatchedManifestWithWrongResidualReference>;
using DistinctResidualReferenceModel =
ManifestExtensionModel<MockMismatchedManifestWithResidualReference>;
using DistinctResidualReferenceForm =
ManifestExtensionForm<MockMismatchedManifestWithResidualReference>;
using DistinctResidualReferenceJacobian =
mean_field::operators::CompiledStellarEquilibriumJacobianForm<
DistinctResidualReferenceModel>;
using DistinctResidualReferenceManifest =
mean_field::operators::EquilibriumSystemManifest<
DistinctResidualReferenceModel,
DistinctResidualReferenceForm,
DistinctResidualReferenceJacobian>;
[[nodiscard]] Manifest make_manifest() {
const std::array<int, Form::value_block_count> valueSizes{2, 3, 4, 5, 6, 1};
const std::array<int, Form::residual_block_count> 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<MockNonConvertibleManifestTarget>);
STATIC_CHECK_FALSE(std::convertible_to<NonConvertibleTarget::Magnitude, double>);
STATIC_CHECK_FALSE(models::CompleteGeneratedScalarDimensionsFor<MockNonConvertibleManifestTarget>);
STATIC_CHECK_FALSE(models::CompleteGeneratedNormalizationFor<MockNonConvertibleManifestTarget>);
STATIC_CHECK_FALSE(models::CompleteGeneratedManifestFor<MockNonConvertibleManifestTarget>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<NonConvertibleTargetModel>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
NonConvertibleTargetModel,
NonConvertibleTargetForm>);
// 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<
MockMismatchedManifestWithoutResidualReference>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<
MockMismatchedManifestWithWrongResidualReference>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<
MockMismatchedManifestWithResidualReference>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<
MissingResidualReferenceModel>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<
WrongResidualReferenceModel>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
MissingResidualReferenceModel,
MissingResidualReferenceForm>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
WrongResidualReferenceModel,
WrongResidualReferenceForm>);
STATIC_CHECK(operators::CompilableRootManifestFor<
DistinctResidualReferenceModel,
DistinctResidualReferenceForm>);
// 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<
GenericManifestModel,
GenericManifestForm>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
CanonicalModel,
GenericManifestForm>);
}
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<int, DistinctResidualReferenceForm::value_block_count>
valueSizes{2, 3, 4, 5, 6, 1, 1};
const std::array<int, DistinctResidualReferenceForm::residual_block_count>
residualSizes{4, 5, 2, 3, 6, 1, 1};
const DistinctResidualReferenceManifest manifest(
valueSizes,
residualSizes,
model,
3
);
const auto &descriptor =
manifest.template specification<MockMismatchedManifestWithResidualReference>();
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<MockGeneratedValueIdCollision>;
using GeneratedResidualCollisionModel = ManifestExtensionModel<MockGeneratedResidualIdCollision>;
using PhysicalValueCollisionModel = ManifestExtensionModel<MockPhysicalValueIdCollision>;
using PhysicalResidualCollisionModel = ManifestExtensionModel<MockPhysicalResidualIdCollision>;
using ReusedSymbolsModel = ManifestExtensionModel<MockReusedManifestSymbols>;
using NonConstexprManifestModel = ManifestExtensionModel<MockNonConstexprManifest>;
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockGeneratedValueIdCollision>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockGeneratedResidualIdCollision>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockPhysicalValueIdCollision>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockPhysicalResidualIdCollision>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockReusedManifestSymbols>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<GeneratedValueCollisionModel>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<GeneratedResidualCollisionModel>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<PhysicalValueCollisionModel>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<PhysicalResidualCollisionModel>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
GeneratedValueCollisionModel,
ManifestExtensionForm<MockGeneratedValueIdCollision>>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
GeneratedResidualCollisionModel,
ManifestExtensionForm<MockGeneratedResidualIdCollision>>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
PhysicalValueCollisionModel,
ManifestExtensionForm<MockPhysicalValueIdCollision>>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
PhysicalResidualCollisionModel,
ManifestExtensionForm<MockPhysicalResidualIdCollision>>);
// Symbols are mathematical presentation labels, not machine identities.
STATIC_CHECK(operators::CompilableRootManifestFor<
ReusedSymbolsModel,
ManifestExtensionForm<MockReusedManifestSymbols>>);
// 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<NonConstexprGeneratedManifest>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockNonConstexprManifest>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<NonConstexprManifestModel>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
NonConstexprManifestModel,
ManifestExtensionForm<MockNonConstexprManifest>>);
}
TEST_CASE(
"Root Manifest Requires Complete Constant Generated Metadata",
"[root-manifest][identity][type-contract][sfinae]"
) {
using namespace mean_field;
STATIC_CHECK(models::GeneratedManifestDefinition<ValidStructuralGeneratedManifest>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockValidStructuralManifest>);
STATIC_CHECK(operators::CompilableRootManifestFor<
ManifestExtensionModel<MockValidStructuralManifest>,
ManifestExtensionForm<MockValidStructuralManifest>>);
// These structural manifests deliberately lie through available=true.
// The root capability must inspect every identity and unit field itself.
STATIC_CHECK(models::GeneratedManifestDefinition<EmptyValueStableIdManifest>);
STATIC_CHECK(models::GeneratedManifestDefinition<EmptyValueSymbolManifest>);
STATIC_CHECK(models::GeneratedManifestDefinition<EmptyResidualStableIdManifest>);
STATIC_CHECK(models::GeneratedManifestDefinition<EmptyResidualSymbolManifest>);
STATIC_CHECK(models::GeneratedManifestDefinition<EmptyTargetUnitsManifest>);
STATIC_CHECK(models::GeneratedManifestDefinition<EmptyResidualUnitsManifest>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockEmptyValueStableIdManifest>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockEmptyValueSymbolManifest>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockEmptyResidualStableIdManifest>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockEmptyResidualSymbolManifest>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockEmptyTargetUnitsManifest>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockEmptyResidualUnitsManifest>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<
ManifestExtensionModel<MockEmptyValueStableIdManifest>>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<
ManifestExtensionModel<MockEmptyValueSymbolManifest>>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<
ManifestExtensionModel<MockEmptyResidualStableIdManifest>>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<
ManifestExtensionModel<MockEmptyResidualSymbolManifest>>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<
ManifestExtensionModel<MockEmptyTargetUnitsManifest>>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<
ManifestExtensionModel<MockEmptyResidualUnitsManifest>>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
ManifestExtensionModel<MockEmptyValueStableIdManifest>,
ManifestExtensionForm<MockEmptyValueStableIdManifest>>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
ManifestExtensionModel<MockEmptyValueSymbolManifest>,
ManifestExtensionForm<MockEmptyValueSymbolManifest>>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
ManifestExtensionModel<MockEmptyResidualStableIdManifest>,
ManifestExtensionForm<MockEmptyResidualStableIdManifest>>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
ManifestExtensionModel<MockEmptyResidualSymbolManifest>,
ManifestExtensionForm<MockEmptyResidualSymbolManifest>>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
ManifestExtensionModel<MockEmptyTargetUnitsManifest>,
ManifestExtensionForm<MockEmptyTargetUnitsManifest>>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
ManifestExtensionModel<MockEmptyResidualUnitsManifest>,
ManifestExtensionForm<MockEmptyResidualUnitsManifest>>);
// Constexpr enforcement covers unit metadata as well as IDs and symbols.
STATIC_CHECK(models::GeneratedManifestDefinition<NonConstexprGeneratedUnitsManifest>);
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockNonConstexprUnitsManifest>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<
ManifestExtensionModel<MockNonConstexprUnitsManifest>,
ManifestExtensionForm<MockNonConstexprUnitsManifest>>);
}
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<
models::PhysicalCoordinateFor<MockFixedMagneticSpecificEnergy>>;
using MockResidual = utils::blocks::generated_residual_block<
models::ResidualFor<MockFixedMagneticSpecificEnergy>>;
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<GenericManifestModel>);
STATIC_CHECK(operators::CompilableRootManifestFor<GenericManifestModel, GenericManifestForm>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<MissingManifestModel, MissingManifestForm>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<UncompiledSurfaceModel>);
STATIC_CHECK_FALSE(operators::CompilableRootManifestFor<UncompiledSurfaceModel, UncompiledSurfaceForm>);
STATIC_CHECK_FALSE(UncompiledSurfaceManifest::hasCompleteEquilibriumCompiler);
STATIC_CHECK(
UncompiledSurfaceManifest::compilationClass ==
models::EquilibriumSystemCompilation::equation_contributions_only);
STATIC_CHECK(normalization::CompleteGeneratedPhysicalRieszNormalizationFor<
MockFixedMagneticSpecificEnergy>);
STATIC_CHECK(preconditioning::SpecificationBorderContribution<
MockFixedMagneticSpecificEnergy>::registered);
STATIC_CHECK(utils::blocks::contains_type_v<
MockValue,
typename preconditioning::SpecificationBorderContribution<
MockFixedMagneticSpecificEnergy>::CorrectionBlocks>);
STATIC_CHECK(utils::blocks::contains_type_v<
preconditioning::Coupling<
utils::blocks::enthalpy::specific::residual,
MockValue>,
typename preconditioning::SpecificationBorderContribution<
MockFixedMagneticSpecificEnergy>::RequiredCouplings>);
STATIC_CHECK_FALSE(operators::hasCompleteStellarEquilibriumRuntime<GenericManifestModel>);
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<int, GenericManifestForm::value_block_count> valueSizes{2, 3, 4, 5, 6, 1, 1};
const std::array<int, GenericManifestForm::residual_block_count> 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<
MockValue,
typename GenericManifestForm::value_blocks>);
CHECK(descriptor->residualBlock == utils::blocks::type_index_v<
MockResidual,
typename GenericManifestForm::residual_blocks>);
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<int, GenericManifestForm::value_block_count> valueSizes{2, 3, 4, 5, 6, 1, 1};
const std::array<int, GenericManifestForm::residual_block_count> 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<double>::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<double>::quiet_NaN()}
})
);
const std::array<int, DistinctResidualReferenceForm::value_block_count>
distinctValueSizes{2, 3, 4, 5, 6, 1, 1};
const std::array<int, DistinctResidualReferenceForm::residual_block_count>
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<double>::quiet_NaN(),
.subsequent = 2.0
})
);
STATIC_CHECK(std::same_as<
std::remove_cvref_t<decltype(inconsistentScaleModel)>,
InconsistentScaleModel>);
const std::array<int, InconsistentScaleForm::value_block_count> inconsistentValueSizes{2, 3, 4, 5, 6, 1, 1};
const std::array<int, InconsistentScaleForm::residual_block_count> 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<surface::Isobaric>::key;
constexpr auto invariantKey = models::SpecificationTraits<MockInvariantNamedLikeSurface>::key;
STATIC_CHECK(surfaceKey.stableName == invariantKey.stableName);
STATIC_CHECK(surfaceKey.role != invariantKey.role);
STATIC_CHECK(models::specificationKeysAreUnique<surface::Isobaric, MockInvariantNamedLikeSurface>);
STATIC_CHECK(operators::StellarEquilibriumSystemCompilable<CrossRoleNameCollisionModel>);
STATIC_CHECK(operators::CompilableRootManifestFor<CrossRoleNameCollisionModel, CrossRoleNameCollisionForm>);
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<int, CrossRoleNameCollisionForm::value_block_count> valueSizes{2, 3, 4, 5, 6, 1, 1};
const std::array<int, CrossRoleNameCollisionForm::residual_block_count> residualSizes{4, 5, 2, 3, 6, 1, 1};
const CrossRoleNameCollisionManifest manifest(valueSizes, residualSizes, model, 3);
const auto &surfaceDescriptor = manifest.specification<surface::Isobaric>();
const auto &invariantDescriptor = manifest.specification<MockInvariantNamedLikeSurface>();
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<CanonicalModel, PermutedModel>);
STATIC_CHECK(CanonicalModel::symbolicallySquare);
STATIC_CHECK(CanonicalModel::hasCompleteEquilibriumDeclaration);
STATIC_CHECK(mean_field::operators::StellarEquilibriumSystemCompilable<CanonicalModel>);
STATIC_CHECK(CentralDensityModel::symbolicallySquare);
STATIC_CHECK(CentralDensityModel::hasCompleteEquilibriumDeclaration);
STATIC_CHECK(mean_field::operators::StellarEquilibriumSystemCompilable<CentralDensityModel>);
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<mean_field::eos::Polytrope>().polytropic_index() == 2.0);
CHECK(model.specification<mean_field::surface::Isobaric>().targetPressure().value() == 0.125);
CHECK(
model.specification<mean_field::models::FixedTotalMass>().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<int, AngularMomentumForm::value_block_count> valueSizes{2, 3, 4, 5, 6, 1, 1};
const std::array<int, AngularMomentumForm::residual_block_count> 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<CentralDensityModel, CentralForm>);
const std::array<int, CentralForm::value_block_count> valueSizes{2, 3, 4, 5, 6, 1, 1};
const std::array<int, CentralForm::residual_block_count> 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<mean_field::surface::Isobaric>().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<
mean_field::surface::Isobaric,
CanonicalModel>);
STATIC_CHECK_FALSE(mean_field::operators::RootManifestSpecificationDescriptorFor<
mean_field::eos::Polytrope,
CanonicalModel>);
STATIC_CHECK(HasRootManifestSpecificationDescriptor<
Manifest,
mean_field::models::FixedTotalMass>);
STATIC_CHECK(HasRootManifestSpecificationDescriptor<Manifest, mean_field::surface::Isobaric>);
STATIC_CHECK_FALSE(HasRootManifestSpecificationDescriptor<Manifest, mean_field::eos::Polytrope>);
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<mean_field::models::FixedTotalMass>();
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<mean_field::surface::Isobaric>();
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<Manifest>);
STATIC_CHECK_FALSE(StateViewAcceptsTemporaryVector<Manifest>);
STATIC_CHECK_FALSE(StateViewAcceptsConstTemporaryVector<Manifest>);
STATIC_CHECK_FALSE(DirectionViewAcceptsTemporaryVector<Manifest>);
STATIC_CHECK_FALSE(DirectionViewAcceptsConstTemporaryVector<Manifest>);
STATIC_CHECK_FALSE(ResidualViewAcceptsTemporaryVector<Manifest>);
STATIC_CHECK_FALSE(ResidualViewAcceptsConstTemporaryVector<Manifest>);
STATIC_CHECK_FALSE(TemporaryManifestProvidesStateView<Manifest>);
STATIC_CHECK_FALSE(TemporaryManifestProvidesReadOnlyStateView<Manifest>);
STATIC_CHECK_FALSE(TemporaryManifestProvidesDirectionView<Manifest>);
STATIC_CHECK_FALSE(TemporaryManifestProvidesResidualView<Manifest>);
STATIC_CHECK(std::constructible_from<
typename Manifest::StateView,
const mfem::Vector &,
const typename Manifest::Layout &>);
STATIC_CHECK(std::constructible_from<
typename Manifest::MutableStateView,
mfem::Vector &,
const typename Manifest::Layout &>);
STATIC_CHECK(std::constructible_from<
typename Manifest::RootResidualView,
mfem::Vector &,
const typename Manifest::Layout &>);
STATIC_CHECK_FALSE(std::constructible_from<
typename Manifest::StateView,
mfem::Vector &&,
const typename Manifest::Layout &>);
STATIC_CHECK_FALSE(std::constructible_from<
typename Manifest::StateView,
const mfem::Vector &&,
const typename Manifest::Layout &>);
STATIC_CHECK_FALSE(std::constructible_from<
typename Manifest::MutableStateView,
mfem::Vector &&,
const typename Manifest::Layout &>);
STATIC_CHECK_FALSE(std::constructible_from<
typename Manifest::RootResidualView,
mfem::Vector &&,
const typename Manifest::Layout &>);
STATIC_CHECK_FALSE(std::constructible_from<
typename Manifest::StateView,
const mfem::Vector &,
typename Manifest::Layout &&>);
STATIC_CHECK_FALSE(std::constructible_from<
typename Manifest::MutableStateView,
mfem::Vector &,
typename Manifest::Layout &&>);
STATIC_CHECK_FALSE(std::constructible_from<
typename Manifest::RootResidualView,
mfem::Vector &,
typename Manifest::Layout &&>);
mfem::Vector state(manifest.layout().value_offsets().Last());
for (int index = 0; index < state.Size(); ++index) {
state(index) = static_cast<double>(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);
}