1426 lines
71 KiB
C++
1426 lines
71 KiB
C++
#include <concepts>
|
|
#include <cstddef>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
|
|
import mean_field;
|
|
import test_helpers;
|
|
|
|
namespace {
|
|
namespace eos = mean_field::eos;
|
|
namespace model = mean_field::model;
|
|
namespace models = mean_field::models;
|
|
namespace surface = mean_field::surface;
|
|
|
|
struct Entropy final : eos::ThermodynamicQuantity { };
|
|
struct Temperature final : eos::ThermodynamicQuantity { };
|
|
|
|
using SpecificEnthalpyFromPressureAndEntropy =
|
|
eos::Relation<eos::quantity::SpecificEnthalpy, eos::quantity::Pressure, Entropy>;
|
|
|
|
class MockBarotropicEquationOfState final {
|
|
public:
|
|
struct Parameters final {
|
|
double densityScale;
|
|
};
|
|
|
|
using ModelDefinition = eos::ConstitutiveLaw<MockBarotropicEquationOfState, "MockBarotropicEquationOfState">;
|
|
using Relations = eos::RelationCatalog<eos::DensityFromSpecificEnthalpy>;
|
|
|
|
explicit constexpr MockBarotropicEquationOfState(const Parameters parameters) noexcept
|
|
: m_densityScale(parameters.densityScale) {
|
|
}
|
|
|
|
[[nodiscard]] constexpr eos::DensityValue evaluate(
|
|
eos::DensityFromSpecificEnthalpy,
|
|
const eos::SpecificEnthalpyValue enthalpy
|
|
) const noexcept {
|
|
return eos::DensityValue{m_densityScale * enthalpy.value()};
|
|
}
|
|
|
|
[[nodiscard]] constexpr eos::PartialDerivative<
|
|
eos::quantity::Density,
|
|
eos::quantity::SpecificEnthalpy>
|
|
partialDerivative(
|
|
eos::DensityFromSpecificEnthalpy,
|
|
eos::WithRespectTo<eos::quantity::SpecificEnthalpy>,
|
|
eos::SpecificEnthalpyValue
|
|
) const noexcept {
|
|
return eos::PartialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>{m_densityScale};
|
|
}
|
|
|
|
[[nodiscard]] constexpr double densityScale() const noexcept {
|
|
return m_densityScale;
|
|
}
|
|
|
|
private:
|
|
double m_densityScale;
|
|
};
|
|
|
|
class MockNonBarotropicEquationOfState final {
|
|
public:
|
|
struct Parameters final {
|
|
double entropyCoupling;
|
|
};
|
|
|
|
using ModelDefinition =
|
|
eos::ConstitutiveLaw<MockNonBarotropicEquationOfState, "MockNonBarotropicEquationOfState">;
|
|
using Relations = eos::RelationCatalog<SpecificEnthalpyFromPressureAndEntropy>;
|
|
|
|
explicit constexpr MockNonBarotropicEquationOfState(const Parameters parameters) noexcept
|
|
: m_entropyCoupling(parameters.entropyCoupling) {
|
|
}
|
|
|
|
[[nodiscard]] constexpr eos::SpecificEnthalpyValue evaluate(
|
|
SpecificEnthalpyFromPressureAndEntropy,
|
|
const eos::PressureValue pressure,
|
|
const eos::QuantityValue<Entropy> entropy
|
|
) const noexcept {
|
|
return eos::SpecificEnthalpyValue{pressure.value() + m_entropyCoupling * entropy.value()};
|
|
}
|
|
|
|
[[nodiscard]] constexpr double entropyCoupling() const noexcept {
|
|
return m_entropyCoupling;
|
|
}
|
|
|
|
private:
|
|
double m_entropyCoupling;
|
|
};
|
|
|
|
class MockIsobaricSurface final {
|
|
public:
|
|
struct Parameters final {
|
|
eos::PressureValue pressure{0.0};
|
|
};
|
|
|
|
using ModelDefinition = surface::BoundaryCondition<MockIsobaricSurface, "MockIsobaricSurface">;
|
|
using PhysicalQuantity = eos::quantity::Pressure;
|
|
using TargetValue = eos::PressureValue;
|
|
|
|
explicit constexpr MockIsobaricSurface(const Parameters parameters) noexcept : m_pressure(parameters.pressure) {
|
|
}
|
|
|
|
[[nodiscard]] constexpr TargetValue targetPressure() const noexcept {
|
|
return m_pressure;
|
|
}
|
|
|
|
private:
|
|
TargetValue m_pressure;
|
|
};
|
|
|
|
class MockIsothermalSurface final {
|
|
public:
|
|
struct Parameters final {
|
|
eos::QuantityValue<Temperature> temperature{0.0};
|
|
};
|
|
|
|
using ModelDefinition = surface::BoundaryCondition<MockIsothermalSurface, "MockIsothermalSurface">;
|
|
using PhysicalQuantity = Temperature;
|
|
using TargetValue = eos::QuantityValue<Temperature>;
|
|
|
|
explicit constexpr MockIsothermalSurface(const Parameters parameters) noexcept
|
|
: m_temperature(parameters.temperature) {
|
|
}
|
|
|
|
[[nodiscard]] constexpr TargetValue targetTemperature() const noexcept {
|
|
return m_temperature;
|
|
}
|
|
|
|
private:
|
|
TargetValue m_temperature;
|
|
};
|
|
|
|
struct DensityValueBlock final : mean_field::utils::blocks::value_block_base { };
|
|
struct MagneticFieldValueBlock final : mean_field::utils::blocks::value_block_base { };
|
|
struct EnthalpyValueBlock final : mean_field::utils::blocks::value_block_base { };
|
|
struct HydrostaticResidualBlock final : mean_field::utils::blocks::residual_block_base { };
|
|
struct InductionResidualBlock final : mean_field::utils::blocks::residual_block_base { };
|
|
|
|
struct MockScalarTarget final {
|
|
double magnitude;
|
|
|
|
[[nodiscard]] constexpr double value() const noexcept {
|
|
return magnitude;
|
|
}
|
|
};
|
|
|
|
using ScalarMassNormalization =
|
|
models::CoordinateNormalization<models::RieszTopology::global_scalar, models::PhysicalScaleLaw::mass>;
|
|
|
|
class MockFixedBaryonMass final {
|
|
public:
|
|
struct Parameters final {
|
|
double targetMass;
|
|
};
|
|
|
|
using ScalarDescription = mean_field::stellar::ScalarConstraint<
|
|
mean_field::dimensions::quantity::Mass,
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
mean_field::dimensions::quantity::Mass,
|
|
"mock_baryon_mass.multiplier",
|
|
"C_b",
|
|
"mock_baryon_mass.residual",
|
|
"R_Mb">;
|
|
using TargetValue = typename ScalarDescription::TargetValue;
|
|
using ModelDefinition = mean_field::integral::FixedScalarWithMultiplier<
|
|
MockFixedBaryonMass,
|
|
"MockFixedBaryonMass",
|
|
mean_field::stellar::Reads<mean_field::stellar::state::Density>,
|
|
mean_field::stellar::Changes<mean_field::stellar::equation::HydrostaticBalance>,
|
|
ScalarDescription>;
|
|
|
|
explicit constexpr MockFixedBaryonMass(const Parameters parameters) noexcept
|
|
: m_targetMass(parameters.targetMass) {
|
|
}
|
|
|
|
[[nodiscard]] constexpr double targetMass() const noexcept {
|
|
return m_targetMass;
|
|
}
|
|
|
|
[[nodiscard]] constexpr TargetValue target() const noexcept {
|
|
return TargetValue{m_targetMass};
|
|
}
|
|
|
|
private:
|
|
double m_targetMass;
|
|
};
|
|
|
|
class MockFixedRestMass final {
|
|
public:
|
|
struct Parameters final {
|
|
double targetMass;
|
|
};
|
|
|
|
using ModelDefinition = mean_field::integral::FixedWithMultiplier<
|
|
MockFixedRestMass,
|
|
"MockFixedRestMass",
|
|
models::DependsOn<models::stellar::state::Density>,
|
|
models::Affects<models::stellar::equation::HydrostaticBalance>,
|
|
models::
|
|
GlobalScalarNormalization<models::PhysicalScaleLaw::specific_energy, models::PhysicalScaleLaw::mass>,
|
|
models::GeneratedManifest<
|
|
"mock_rest_mass.multiplier",
|
|
"C_0",
|
|
"mock_rest_mass.residual",
|
|
"R_M0",
|
|
"mass",
|
|
"mass">>;
|
|
|
|
explicit constexpr MockFixedRestMass(const Parameters parameters) noexcept
|
|
: m_targetMass(parameters.targetMass) {
|
|
}
|
|
|
|
[[nodiscard]] constexpr double targetMass() const noexcept {
|
|
return m_targetMass;
|
|
}
|
|
|
|
[[nodiscard]] constexpr MockScalarTarget target() const noexcept {
|
|
return MockScalarTarget{m_targetMass};
|
|
}
|
|
|
|
private:
|
|
double m_targetMass;
|
|
};
|
|
|
|
/*
|
|
* A coupled integral written entirely in physics terms. Its residual
|
|
* reads the baryon-mass multiplier, while its own multiplier changes both
|
|
* its own scalar equation and the baryon-mass scalar equation. No
|
|
* generated backend block types appear in this declaration.
|
|
*/
|
|
class MockCrossCoupledIntegral final {
|
|
public:
|
|
struct Parameters final {
|
|
double targetMass;
|
|
};
|
|
|
|
using ScalarDescription = mean_field::stellar::ScalarConstraint<
|
|
mean_field::dimensions::quantity::Mass,
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
mean_field::dimensions::quantity::Mass,
|
|
"mock_cross_coupled.multiplier",
|
|
"C_x",
|
|
"mock_cross_coupled.residual",
|
|
"R_x">;
|
|
using TargetValue = typename ScalarDescription::TargetValue;
|
|
using ModelDefinition = mean_field::integral::FixedScalarWithMultiplier<
|
|
MockCrossCoupledIntegral,
|
|
"MockCrossCoupledIntegral",
|
|
mean_field::stellar::Reads<mean_field::stellar::state::GeneratedCoordinateOf<MockFixedBaryonMass>>,
|
|
mean_field::stellar::Changes<
|
|
mean_field::stellar::equation::ConstraintOf<MockFixedBaryonMass>,
|
|
mean_field::stellar::equation::OwnConstraint>,
|
|
ScalarDescription>;
|
|
|
|
explicit constexpr MockCrossCoupledIntegral(const Parameters parameters) noexcept
|
|
: m_targetMass(parameters.targetMass) {
|
|
}
|
|
|
|
[[nodiscard]] constexpr TargetValue target() const noexcept {
|
|
return TargetValue{m_targetMass};
|
|
}
|
|
|
|
private:
|
|
double m_targetMass;
|
|
};
|
|
|
|
class MockConflictingBaryonMass final {
|
|
public:
|
|
struct Parameters final {
|
|
double targetMass;
|
|
};
|
|
|
|
// A different generated-coordinate mechanism does not create a new
|
|
// identity. Stable names remain unique within a physical role.
|
|
using ModelDefinition =
|
|
mean_field::integral::FixedWithPhysicalCoordinate<MockConflictingBaryonMass, "MockFixedBaryonMass">;
|
|
|
|
explicit constexpr MockConflictingBaryonMass(const Parameters parameters) noexcept
|
|
: m_targetMass(parameters.targetMass) {
|
|
}
|
|
|
|
private:
|
|
double m_targetMass;
|
|
};
|
|
|
|
class MockFixedMagneticSpecificEnergy final {
|
|
public:
|
|
struct Parameters final {
|
|
double targetSpecificEnergy;
|
|
};
|
|
|
|
using ScalarDescription = mean_field::stellar::ScalarConstraint<
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
mean_field::dimensions::quantity::Dimensionless,
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
"mock_magnetic_specific_energy.amplitude",
|
|
"a_B",
|
|
"mock_magnetic_specific_energy.residual",
|
|
"R_EB">;
|
|
using TargetValue = typename ScalarDescription::TargetValue;
|
|
using ModelDefinition = mean_field::integral::FixedScalarWithPhysicalCoordinate<
|
|
MockFixedMagneticSpecificEnergy,
|
|
"MockFixedMagneticSpecificEnergy",
|
|
models::DependsOn<DensityValueBlock, MagneticFieldValueBlock>,
|
|
models::Affects<HydrostaticResidualBlock, InductionResidualBlock>,
|
|
ScalarDescription>;
|
|
|
|
explicit constexpr MockFixedMagneticSpecificEnergy(const Parameters parameters) noexcept
|
|
: m_targetSpecificEnergy(parameters.targetSpecificEnergy) {
|
|
}
|
|
|
|
[[nodiscard]] constexpr TargetValue targetSpecificEnergy() const noexcept {
|
|
return m_targetSpecificEnergy;
|
|
}
|
|
|
|
[[nodiscard]] constexpr TargetValue target() const noexcept {
|
|
return m_targetSpecificEnergy;
|
|
}
|
|
|
|
private:
|
|
TargetValue m_targetSpecificEnergy;
|
|
};
|
|
|
|
/*
|
|
* This mock deliberately does not claim that a magnetic field block is
|
|
* already present in the stellar core. It represents a future magnetic
|
|
* specific-energy amplitude using only physics dependencies that the current
|
|
* translation layer can map. A real MHD extension can replace those
|
|
* dependencies when magnetic state and induction blocks exist.
|
|
*/
|
|
class MockFixedMagneticEnergyAmplitude final {
|
|
public:
|
|
struct Parameters final {
|
|
double targetSpecificEnergy;
|
|
};
|
|
|
|
using ScalarDescription = mean_field::stellar::ScalarConstraint<
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
mean_field::dimensions::quantity::Dimensionless,
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
"mock_magnetic_energy.amplitude",
|
|
"a_B",
|
|
"mock_magnetic_energy.residual",
|
|
"R_EB">;
|
|
using TargetValue = typename ScalarDescription::TargetValue;
|
|
using ModelDefinition = mean_field::integral::FixedScalarWithPhysicalCoordinate<
|
|
MockFixedMagneticEnergyAmplitude,
|
|
"MockFixedMagneticEnergyAmplitude",
|
|
models::DependsOn<
|
|
models::stellar::state::Density,
|
|
models::stellar::state::SurfaceShape,
|
|
models::stellar::state::OwnGeneratedCoordinate>,
|
|
models::
|
|
Affects<models::stellar::equation::SurfaceShapeBalance, models::stellar::equation::HydrostaticBalance>,
|
|
ScalarDescription>;
|
|
|
|
explicit constexpr MockFixedMagneticEnergyAmplitude(const Parameters parameters) noexcept
|
|
: m_targetSpecificEnergy(parameters.targetSpecificEnergy) {
|
|
}
|
|
|
|
[[nodiscard]] constexpr TargetValue target() const noexcept {
|
|
return m_targetSpecificEnergy;
|
|
}
|
|
|
|
private:
|
|
TargetValue m_targetSpecificEnergy;
|
|
};
|
|
|
|
/*
|
|
* A physics-vocabulary audit for the gravitational half of the current
|
|
* stellar core. The declaration names no backend blocks: it says only
|
|
* that a virial-like scalar reads the gravitational field and potential
|
|
* and changes the field-definition and Poisson equations.
|
|
*/
|
|
class MockFixedVirialSpecificEnergy final {
|
|
public:
|
|
struct Parameters final {
|
|
double targetSpecificEnergy;
|
|
};
|
|
|
|
using ScalarDescription = mean_field::stellar::ScalarConstraint<
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
mean_field::dimensions::quantity::Dimensionless,
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
"mock_virial_specific_energy.multiplier",
|
|
"lambda_W",
|
|
"mock_virial_specific_energy.residual",
|
|
"R_W">;
|
|
using TargetValue = typename ScalarDescription::TargetValue;
|
|
using ModelDefinition = mean_field::integral::FixedScalarWithMultiplier<
|
|
MockFixedVirialSpecificEnergy,
|
|
"MockFixedVirialSpecificEnergy",
|
|
mean_field::stellar::
|
|
Reads<mean_field::stellar::state::GravityGradient, mean_field::stellar::state::GravitationalPotential>,
|
|
mean_field::stellar::Changes<
|
|
mean_field::stellar::equation::GravityGradientDefinition,
|
|
mean_field::stellar::equation::PoissonEquation>,
|
|
ScalarDescription>;
|
|
|
|
explicit constexpr MockFixedVirialSpecificEnergy(const Parameters parameters) noexcept
|
|
: m_targetSpecificEnergy(parameters.targetSpecificEnergy) {
|
|
}
|
|
|
|
[[nodiscard]] constexpr TargetValue target() const noexcept {
|
|
return TargetValue{m_targetSpecificEnergy};
|
|
}
|
|
|
|
private:
|
|
double m_targetSpecificEnergy;
|
|
};
|
|
|
|
class MockDimensionallyMismatchedMagneticEnergy final {
|
|
public:
|
|
struct Parameters final {
|
|
double targetEnergy;
|
|
};
|
|
|
|
using ScalarDescription = mean_field::stellar::ScalarConstraint<
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
mean_field::dimensions::quantity::Dimensionless,
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
"mock_mismatched_magnetic_energy.amplitude",
|
|
"a_bad",
|
|
"mock_mismatched_magnetic_energy.residual",
|
|
"R_bad">;
|
|
using ModelDefinition = mean_field::integral::FixedScalarWithPhysicalCoordinate<
|
|
MockDimensionallyMismatchedMagneticEnergy,
|
|
"MockDimensionallyMismatchedMagneticEnergy",
|
|
mean_field::stellar::Reads<mean_field::stellar::state::Density>,
|
|
mean_field::stellar::Changes<mean_field::stellar::equation::HydrostaticBalance>,
|
|
ScalarDescription>;
|
|
|
|
explicit constexpr MockDimensionallyMismatchedMagneticEnergy(const Parameters parameters) noexcept
|
|
: m_targetEnergy(parameters.targetEnergy) {
|
|
}
|
|
|
|
/* Deliberately disagrees with the declared SpecificEnergy target. */
|
|
[[nodiscard]] constexpr mean_field::dimensions::EnergyValue target() const noexcept {
|
|
return mean_field::dimensions::EnergyValue{m_targetEnergy};
|
|
}
|
|
|
|
private:
|
|
double m_targetEnergy;
|
|
};
|
|
|
|
class MockCentralEnthalpyPhase final {
|
|
public:
|
|
struct Parameters final {
|
|
double targetEnthalpy;
|
|
};
|
|
|
|
using ModelDefinition = mean_field::constraint::PhaseCondition<
|
|
MockCentralEnthalpyPhase,
|
|
"MockCentralEnthalpyPhase",
|
|
models::DependsOn<models::stellar::state::SpecificEnthalpy>,
|
|
models::Affects<models::stellar::equation::HydrostaticBalance>,
|
|
models::GlobalScalarNormalization<
|
|
models::PhysicalScaleLaw::specific_energy,
|
|
models::PhysicalScaleLaw::specific_energy>,
|
|
models::GeneratedManifest<
|
|
"mock_central_enthalpy.border",
|
|
"lambda_hc",
|
|
"mock_central_enthalpy.residual",
|
|
"R_hc",
|
|
"specific_enthalpy",
|
|
"specific_enthalpy">>;
|
|
|
|
explicit constexpr MockCentralEnthalpyPhase(const Parameters parameters) noexcept
|
|
: m_targetEnthalpy(parameters.targetEnthalpy) {
|
|
}
|
|
|
|
[[nodiscard]] constexpr double targetEnthalpy() const noexcept {
|
|
return m_targetEnthalpy;
|
|
}
|
|
|
|
[[nodiscard]] constexpr MockScalarTarget target() const noexcept {
|
|
return MockScalarTarget{m_targetEnthalpy};
|
|
}
|
|
|
|
private:
|
|
double m_targetEnthalpy;
|
|
};
|
|
|
|
class MockMalformedEquilibriumPhysics final {
|
|
public:
|
|
struct Parameters final {
|
|
double target;
|
|
};
|
|
|
|
using ScalarDescription = mean_field::stellar::ScalarConstraint<
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
mean_field::dimensions::quantity::SpecificEnergy,
|
|
"mock_malformed_runtime.value",
|
|
"lambda_bad",
|
|
"mock_malformed_runtime.residual",
|
|
"R_bad">;
|
|
using TargetValue = typename ScalarDescription::TargetValue;
|
|
using ModelDefinition = mean_field::constraint::ScalarPhaseCondition<
|
|
MockMalformedEquilibriumPhysics,
|
|
"MockMalformedEquilibriumPhysics",
|
|
mean_field::stellar::Reads<mean_field::stellar::state::SpecificEnthalpy>,
|
|
mean_field::stellar::Changes<mean_field::stellar::equation::HydrostaticBalance>,
|
|
ScalarDescription>;
|
|
|
|
// Deliberately present but not a valid SpecificationEquilibriumPhysics
|
|
// package. Selection must remain detection-safe.
|
|
using EquilibriumPhysics = int;
|
|
|
|
explicit constexpr MockMalformedEquilibriumPhysics(const Parameters parameters) noexcept
|
|
: m_target(parameters.target) {
|
|
}
|
|
|
|
[[nodiscard]] constexpr TargetValue target() const noexcept {
|
|
return TargetValue{m_target};
|
|
}
|
|
|
|
private:
|
|
double m_target;
|
|
};
|
|
|
|
struct MalformedNormalization final { };
|
|
|
|
class MockIntegralWithMalformedNormalization final {
|
|
public:
|
|
struct Parameters final {
|
|
double target;
|
|
};
|
|
|
|
using ModelDefinition = mean_field::integral::FixedWithMultiplier<
|
|
MockIntegralWithMalformedNormalization,
|
|
"MockIntegralWithMalformedNormalization",
|
|
models::ModelTypeList<DensityValueBlock>,
|
|
models::ModelTypeList<HydrostaticResidualBlock>,
|
|
models::GeneratedNormalization<MalformedNormalization, ScalarMassNormalization>>;
|
|
|
|
explicit constexpr MockIntegralWithMalformedNormalization(const Parameters parameters) noexcept
|
|
: m_target(parameters.target) {
|
|
}
|
|
|
|
private:
|
|
double m_target;
|
|
};
|
|
|
|
class MisidentifiedSpecification final {
|
|
public:
|
|
struct Parameters final { };
|
|
using ModelDefinition = eos::ConstitutiveLaw<MockBarotropicEquationOfState, "MisidentifiedSpecification">;
|
|
|
|
explicit MisidentifiedSpecification(Parameters) noexcept {
|
|
}
|
|
};
|
|
|
|
template <models::SpecificationRole Role, models::GeneratedStateKind StateKind>
|
|
class MockRoleStateSpecification final {
|
|
public:
|
|
struct Parameters final { };
|
|
|
|
using ModelDefinition =
|
|
models::ModelDefinition<MockRoleStateSpecification, "MockRoleStateSpecification", Role, StateKind>;
|
|
|
|
explicit constexpr MockRoleStateSpecification(Parameters) noexcept {
|
|
}
|
|
};
|
|
|
|
template <typename Candidate>
|
|
concept HasModelDefinitionProjection = requires { typename models::ModelDefinitionForT<Candidate>; };
|
|
|
|
template <typename Candidate>
|
|
concept CanEnterSpecificationSet =
|
|
requires { typename models::SpecificationSet<mean_field::eos::Polytrope, Candidate>; };
|
|
|
|
template <typename Candidate>
|
|
concept HasSpecificationContribution = requires { typename models::SpecificationContribution<Candidate>; };
|
|
|
|
template <typename... Specifications>
|
|
concept CanDeduceStellarModel = requires { model::StellarModel(std::declval<Specifications>()...); };
|
|
|
|
template <typename Specification, typename Model>
|
|
concept HasContributionStateView =
|
|
requires { typename mean_field::operators::StellarEquilibriumContributionStateView<Specification, Model>; };
|
|
|
|
template <typename Specification, typename Model>
|
|
concept HasContributionResidualView =
|
|
requires { typename mean_field::operators::StellarEquilibriumContributionResidualView<Specification, Model>; };
|
|
|
|
template <typename Specification>
|
|
concept HasDensityVolumeIntegralContext =
|
|
requires { typename mean_field::stellar::DensityVolumeIntegralContext<Specification>; };
|
|
|
|
template <typename Candidate>
|
|
concept HasSurfaceConditionType = requires { typename model::SurfaceConditionType<Candidate>; };
|
|
|
|
template <typename Candidate>
|
|
concept HasSurfaceConditionAccessor = requires(const Candidate &candidate) { candidate.surfaceCondition(); };
|
|
|
|
template <typename Candidate>
|
|
concept HasInvariantRoleAccessor = requires(const Candidate &candidate) {
|
|
candidate.template specificationForRole<models::SpecificationRole::invariant>();
|
|
};
|
|
|
|
template <typename EquationOfState, typename SurfaceCondition>
|
|
using MockModel = decltype(model::StellarModel(
|
|
EquationOfState(typename EquationOfState::Parameters{}),
|
|
SurfaceCondition(typename SurfaceCondition::Parameters{}),
|
|
MockFixedBaryonMass({.targetMass = 1.0}),
|
|
MockFixedRestMass({.targetMass = 0.875}),
|
|
MockCentralEnthalpyPhase({.targetEnthalpy = 0.125})
|
|
));
|
|
|
|
using BarotropicIsobaricModel = MockModel<MockBarotropicEquationOfState, MockIsobaricSurface>;
|
|
using BarotropicIsothermalModel = MockModel<MockBarotropicEquationOfState, MockIsothermalSurface>;
|
|
using NonBarotropicIsobaricModel = MockModel<MockNonBarotropicEquationOfState, MockIsobaricSurface>;
|
|
using NonBarotropicIsothermalModel = MockModel<MockNonBarotropicEquationOfState, MockIsothermalSurface>;
|
|
using BasePhysicsModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
|
mean_field::eos::Polytrope,
|
|
mean_field::surface::Isobaric,
|
|
mean_field::models::FixedTotalMass>>;
|
|
using MalformedRuntimeModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
|
mean_field::eos::Polytrope,
|
|
mean_field::surface::Isobaric,
|
|
mean_field::models::FixedTotalMass,
|
|
MockMalformedEquilibriumPhysics>>;
|
|
using DimensionallyMismatchedModel = mean_field::model::StellarModel<mean_field::models::SpecificationSet<
|
|
mean_field::eos::Polytrope,
|
|
mean_field::surface::Isobaric,
|
|
mean_field::models::FixedTotalMass,
|
|
MockDimensionallyMismatchedMagneticEnergy>>;
|
|
using DimensionallyMismatchedForm =
|
|
mean_field::operators::CompiledStellarEquilibriumForm<DimensionallyMismatchedModel>;
|
|
using DanglingCrossConstraintModel = mean_field::model::StellarModel<
|
|
mean_field::models::
|
|
SpecificationSet<mean_field::eos::Polytrope, mean_field::surface::Isobaric, MockCrossCoupledIntegral>>;
|
|
|
|
template <typename... Types> struct TypeList final { };
|
|
|
|
template <
|
|
std::size_t ExpectedContributionCouplings,
|
|
std::size_t ExpectedBorderCouplings,
|
|
std::size_t ExpectedSystemCouplings,
|
|
typename... Specifications>
|
|
struct ConstraintPack final {
|
|
static constexpr std::size_t generatedArity = sizeof...(Specifications);
|
|
static constexpr std::size_t expectedContributionCouplings = ExpectedContributionCouplings;
|
|
static constexpr std::size_t expectedBorderCouplings = ExpectedBorderCouplings;
|
|
static constexpr std::size_t expectedSystemCouplings = ExpectedSystemCouplings;
|
|
};
|
|
|
|
using FixedMassPack = ConstraintPack<3, 2, 19, MockFixedBaryonMass>;
|
|
using TwoIntegralPack = ConstraintPack<5, 4, 21, MockFixedBaryonMass, MockFixedRestMass>;
|
|
using PhysicalCoordinatePack = ConstraintPack<11, 7, 24, MockFixedBaryonMass, MockFixedMagneticEnergyAmplitude>;
|
|
using GravityVocabularyPack = ConstraintPack<11, 6, 24, MockFixedBaryonMass, MockFixedVirialSpecificEnergy>;
|
|
using CrossConstraintPack = ConstraintPack<7, 6, 23, MockFixedBaryonMass, MockCrossCoupledIntegral>;
|
|
using PhasePack = ConstraintPack<6, 4, 21, MockFixedBaryonMass, MockCentralEnthalpyPhase>;
|
|
using AllConstraintKindsPack = ConstraintPack<
|
|
16,
|
|
11,
|
|
28,
|
|
MockFixedBaryonMass,
|
|
MockFixedRestMass,
|
|
MockFixedMagneticEnergyAmplitude,
|
|
MockCentralEnthalpyPhase>;
|
|
|
|
using MockEquationOfStates = TypeList<MockBarotropicEquationOfState, MockNonBarotropicEquationOfState>;
|
|
using MockSurfaceConditions = TypeList<MockIsobaricSurface, MockIsothermalSurface>;
|
|
using ConstraintPacks = TypeList<
|
|
FixedMassPack,
|
|
TwoIntegralPack,
|
|
PhysicalCoordinatePack,
|
|
GravityVocabularyPack,
|
|
CrossConstraintPack,
|
|
PhasePack,
|
|
AllConstraintKindsPack>;
|
|
|
|
template <typename EquationOfState, typename SurfaceCondition, typename Pack> struct MatrixModel;
|
|
|
|
template <
|
|
typename EquationOfState,
|
|
typename SurfaceCondition,
|
|
std::size_t ExpectedContributionCouplings,
|
|
std::size_t ExpectedBorderCouplings,
|
|
std::size_t ExpectedSystemCouplings,
|
|
typename... Specifications>
|
|
struct MatrixModel<
|
|
EquationOfState,
|
|
SurfaceCondition,
|
|
ConstraintPack<
|
|
ExpectedContributionCouplings,
|
|
ExpectedBorderCouplings,
|
|
ExpectedSystemCouplings,
|
|
Specifications...>> {
|
|
using Type =
|
|
model::StellarModel<models::SpecificationSet<EquationOfState, SurfaceCondition, Specifications...>>;
|
|
using FactoryType = decltype(model::StellarModel(
|
|
std::declval<EquationOfState>(),
|
|
std::declval<SurfaceCondition>(),
|
|
std::declval<Specifications>()...
|
|
));
|
|
|
|
static_assert(std::same_as<
|
|
Type,
|
|
FactoryType>);
|
|
};
|
|
|
|
template <typename EquationOfState, typename SurfaceCondition, typename Pack>
|
|
using MatrixModelT = typename MatrixModel<EquationOfState, SurfaceCondition, Pack>::Type;
|
|
|
|
template <typename Needles, typename Haystack> struct TypeListIsSubset : std::false_type { };
|
|
|
|
template <typename Haystack, typename... Needles>
|
|
struct TypeListIsSubset<mean_field::utils::blocks::type_list<Needles...>, Haystack>
|
|
: std::bool_constant<(mean_field::utils::blocks::contains_type_v<Needles, Haystack> && ...)> { };
|
|
|
|
template <typename List> struct SingleType;
|
|
|
|
template <typename Type> struct SingleType<mean_field::utils::blocks::type_list<Type>> {
|
|
using type = Type;
|
|
};
|
|
|
|
template <typename Specification>
|
|
using GeneratedValueBlockFor =
|
|
typename SingleType<typename mean_field::operators::StellarEquilibriumSpecificationCompilation<
|
|
Specification>::GeneratedValueBlocks>::type;
|
|
|
|
template <typename Specification>
|
|
using GeneratedResidualBlockFor =
|
|
typename SingleType<typename mean_field::operators::StellarEquilibriumSpecificationCompilation<
|
|
Specification>::GeneratedResidualBlocks>::type;
|
|
|
|
template <typename Candidate>
|
|
concept CanFormStellarEquilibriumProblem =
|
|
requires { typename mean_field::equilibrium::StellarEquilibriumProblem<Candidate>; };
|
|
|
|
template <typename Model, typename Pack> struct SymbolicMatrixCell;
|
|
|
|
template <
|
|
typename Model,
|
|
std::size_t ExpectedContributionCouplings,
|
|
std::size_t ExpectedBorderCouplings,
|
|
std::size_t ExpectedSystemCouplings,
|
|
typename... Specifications>
|
|
struct SymbolicMatrixCell<
|
|
Model,
|
|
ConstraintPack<
|
|
ExpectedContributionCouplings,
|
|
ExpectedBorderCouplings,
|
|
ExpectedSystemCouplings,
|
|
Specifications...>> {
|
|
using System = mean_field::operators::CompiledStellarEquilibriumSystem<Model>;
|
|
using Form = typename System::FormType;
|
|
using Jacobian = typename System::JacobianType;
|
|
using Border = mean_field::preconditioning::CompiledSpecificationBorderFor<Model>;
|
|
|
|
static constexpr bool value = [] {
|
|
static_assert(mean_field::model::StellarModelType<Model>);
|
|
static_assert(mean_field::operators::StellarEquilibriumSymbolicallyCompilable<Model>);
|
|
static_assert(mean_field::operators::StellarEquilibriumSystemCompilable<Model>);
|
|
static_assert(
|
|
mean_field::operators::stellarEquilibriumIsSymbolicallyCompilable<Model> ==
|
|
mean_field::operators::stellarEquilibriumSystemIsCompilable<Model>
|
|
);
|
|
static_assert(Model::OperatorSignature::generatedValueArity == sizeof...(Specifications));
|
|
static_assert(Model::OperatorSignature::generatedResidualArity == sizeof...(Specifications));
|
|
static_assert(Model::symbolicallySquare);
|
|
static_assert(System::GeneratedValueBlocks::size == sizeof...(Specifications));
|
|
static_assert(System::GeneratedResidualBlocks::size == sizeof...(Specifications));
|
|
static_assert(Form::value_block_count == 5 + sizeof...(Specifications));
|
|
static_assert(Form::residual_block_count == 5 + sizeof...(Specifications));
|
|
static_assert(mean_field::utils::blocks::block_form_is_valid_v<Form>);
|
|
static_assert(mean_field::utils::blocks::types_are_unique_v<typename Form::value_blocks>);
|
|
static_assert(mean_field::utils::blocks::types_are_unique_v<typename Form::residual_blocks>);
|
|
static_assert(mean_field::utils::blocks::valid_jacobian_form<Form, Jacobian>);
|
|
static_assert(System::ContributionJacobianCouplings::size == ExpectedContributionCouplings);
|
|
static_assert(System::JacobianCouplings::size == ExpectedSystemCouplings);
|
|
static_assert(mean_field::utils::blocks::types_are_unique_v<typename System::JacobianCouplings>);
|
|
static_assert(Border::valueArity == sizeof...(Specifications));
|
|
static_assert(Border::residualArity == sizeof...(Specifications));
|
|
static_assert(Border::specificationCount == sizeof...(Specifications));
|
|
static_assert(std::same_as<typename Border::CorrectionBlocks, typename System::GeneratedCorrectionBlocks>);
|
|
static_assert(std::same_as<typename Border::ResidualBlocks, typename System::GeneratedResidualBlocks>);
|
|
static_assert(Border::RequiredCouplings::size == ExpectedBorderCouplings);
|
|
static_assert(
|
|
(TypeListIsSubset<
|
|
typename mean_field::operators::StellarEquilibriumSpecificationCompilation<
|
|
Specifications>::JacobianCouplings,
|
|
typename System::JacobianCouplings>::value &&
|
|
...)
|
|
);
|
|
static_assert(mean_field::normalization::CompleteStellarNormalizationFor<Model, Form>);
|
|
|
|
// These boundary mocks intentionally describe physics but do not
|
|
// register a numerical surface or manifest adapter. Symbolic
|
|
// success therefore never overclaims an executable problem.
|
|
static_assert(!mean_field::operators::CompilableRootManifestFor<Model, Form>);
|
|
static_assert(!mean_field::operators::hasStellarEquilibriumCoreRuntime<Model>);
|
|
static_assert(!mean_field::operators::hasCompleteStellarEquilibriumRuntime<Model>);
|
|
static_assert(!mean_field::operators::hasCompatibleStellarEquilibriumPhysicalRoot<Model>);
|
|
static_assert(!mean_field::equilibrium::hasStellarEquilibriumSurfaceCompilation<Model>);
|
|
static_assert(!mean_field::equilibrium::StellarEquilibriumModel<Model>);
|
|
static_assert(!CanFormStellarEquilibriumProblem<Model>);
|
|
return true;
|
|
}();
|
|
};
|
|
|
|
template <typename EquationOfState, typename SurfaceCondition, typename Packs> struct AuditConstraintPacks;
|
|
|
|
template <typename EquationOfState, typename SurfaceCondition, typename... Packs>
|
|
struct AuditConstraintPacks<EquationOfState, SurfaceCondition, TypeList<Packs...>>
|
|
: std::bool_constant<
|
|
(SymbolicMatrixCell<MatrixModelT<EquationOfState, SurfaceCondition, Packs>, Packs>::value && ...)> { };
|
|
|
|
template <typename EquationOfState, typename Surfaces, typename Packs> struct AuditSurfaceConditions;
|
|
|
|
template <typename EquationOfState, typename Packs, typename... Surfaces>
|
|
struct AuditSurfaceConditions<EquationOfState, TypeList<Surfaces...>, Packs>
|
|
: std::bool_constant<(AuditConstraintPacks<EquationOfState, Surfaces, Packs>::value && ...)> { };
|
|
|
|
template <typename EquationOfStates, typename Surfaces, typename Packs> struct AuditCartesianProduct;
|
|
|
|
template <typename Surfaces, typename Packs, typename... EquationOfStates>
|
|
struct AuditCartesianProduct<TypeList<EquationOfStates...>, Surfaces, Packs>
|
|
: std::bool_constant<(AuditSurfaceConditions<EquationOfStates, Surfaces, Packs>::value && ...)> { };
|
|
|
|
template <typename TargetQuantity, typename CoordinateQuantity, typename ResidualQuantity>
|
|
concept CanDescribeDimensionalScalar = requires {
|
|
typename mean_field::stellar::ScalarConstraint<
|
|
TargetQuantity, CoordinateQuantity, ResidualQuantity, "dimensional_probe.value", "q_probe",
|
|
"dimensional_probe.residual", "R_probe">;
|
|
};
|
|
} // namespace
|
|
|
|
TEST_CASE(
|
|
"Physics Specifications Describe Their Compile-Time Contribution In "
|
|
"One Place",
|
|
tags::stellar_model_specification_api
|
|
) {
|
|
using Contribution = models::SpecificationContribution<MockFixedMagneticSpecificEnergy>;
|
|
using BaryonCompilation = mean_field::operators::StellarEquilibriumSpecificationCompilation<MockFixedBaryonMass>;
|
|
|
|
STATIC_CHECK(models::SelfDescribingModelSpecification<MockBarotropicEquationOfState>);
|
|
STATIC_CHECK(models::SelfDescribingModelSpecification<MockNonBarotropicEquationOfState>);
|
|
STATIC_CHECK(models::SelfDescribingModelSpecification<MockIsobaricSurface>);
|
|
STATIC_CHECK(models::SelfDescribingModelSpecification<MockIsothermalSurface>);
|
|
STATIC_CHECK(models::SelfDescribingModelSpecification<MockFixedBaryonMass>);
|
|
STATIC_CHECK_FALSE(mean_field::operators::DensityVolumeIntegralSpecification<MockFixedBaryonMass>);
|
|
STATIC_CHECK_FALSE(HasDensityVolumeIntegralContext<MockFixedBaryonMass>);
|
|
STATIC_CHECK(mean_field::operators::DensityVolumeIntegralSpecification<mean_field::models::FixedTotalMass>);
|
|
STATIC_CHECK(HasDensityVolumeIntegralContext<mean_field::models::FixedTotalMass>);
|
|
STATIC_CHECK(mean_field::stellar::ScalarConstraintDescription<MockFixedBaryonMass::ScalarDescription>);
|
|
STATIC_CHECK(models::SelfDescribingModelSpecification<MockFixedRestMass>);
|
|
STATIC_CHECK(models::SelfDescribingModelSpecification<MockCrossCoupledIntegral>);
|
|
STATIC_CHECK(models::SelfDescribingModelSpecification<MockFixedMagneticSpecificEnergy>);
|
|
STATIC_CHECK(models::SelfDescribingModelSpecification<MockFixedMagneticEnergyAmplitude>);
|
|
STATIC_CHECK(models::SelfDescribingModelSpecification<MockFixedVirialSpecificEnergy>);
|
|
STATIC_CHECK_FALSE(models::SelfDescribingModelSpecification<MisidentifiedSpecification>);
|
|
STATIC_CHECK_FALSE(models::ModelSpecification<MisidentifiedSpecification>);
|
|
|
|
STATIC_CHECK(eos::EquationOfStateModel<MockBarotropicEquationOfState>);
|
|
STATIC_CHECK(eos::BarotropicClosureEquationOfState<MockBarotropicEquationOfState>);
|
|
STATIC_CHECK(eos::EquationOfStateModel<MockNonBarotropicEquationOfState>);
|
|
STATIC_CHECK_FALSE(eos::BarotropicClosureEquationOfState<MockNonBarotropicEquationOfState>);
|
|
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename Contribution::GeneratedValues,
|
|
models::ModelTypeList<models::PhysicalCoordinateFor<MockFixedMagneticSpecificEnergy>>>
|
|
);
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename Contribution::GeneratedResiduals,
|
|
models::ModelTypeList<models::ResidualFor<MockFixedMagneticSpecificEnergy>>>
|
|
);
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename Contribution::DependsOn, models::ModelTypeList<DensityValueBlock, MagneticFieldValueBlock>>
|
|
);
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename Contribution::Affects, models::ModelTypeList<HydrostaticResidualBlock, InductionResidualBlock>>
|
|
);
|
|
STATIC_CHECK(Contribution::generatedValueArity == 1);
|
|
STATIC_CHECK(Contribution::generatedResidualArity == 1);
|
|
STATIC_CHECK(Contribution::generatedStateKind == models::GeneratedStateKind::physical_coordinate);
|
|
STATIC_CHECK(Contribution::Normalization::available);
|
|
STATIC_CHECK(Contribution::Manifest::available);
|
|
STATIC_CHECK(Contribution::Manifest::valueStableId == "mock_magnetic_specific_energy.amplitude");
|
|
STATIC_CHECK(Contribution::Manifest::residualSymbol == "R_EB");
|
|
STATIC_CHECK(Contribution::Manifest::targetUnits == "specific_energy");
|
|
STATIC_CHECK(Contribution::Manifest::residualUnits == "specific_energy");
|
|
STATIC_CHECK(
|
|
MockFixedMagneticSpecificEnergy::ScalarDescription::targetScale == models::PhysicalScaleLaw::specific_energy
|
|
);
|
|
STATIC_CHECK(Contribution::Normalization::Value::scale == models::PhysicalScaleLaw::dimensionless);
|
|
STATIC_CHECK(Contribution::Normalization::Residual::scale == models::PhysicalScaleLaw::specific_energy);
|
|
STATIC_CHECK(models::CompleteGeneratedScalarDimensionsFor<MockFixedMagneticSpecificEnergy>);
|
|
STATIC_CHECK(models::CompleteGeneratedNormalizationFor<MockFixedMagneticSpecificEnergy>);
|
|
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockFixedMagneticSpecificEnergy>);
|
|
STATIC_CHECK(models::CompleteGeneratedScalarDimensionsFor<MockFixedMagneticEnergyAmplitude>);
|
|
STATIC_CHECK(models::CompleteGeneratedNormalizationFor<MockFixedMagneticEnergyAmplitude>);
|
|
STATIC_CHECK(models::CompleteGeneratedManifestFor<MockFixedMagneticEnergyAmplitude>);
|
|
STATIC_CHECK(models::CompleteGeneratedScalarDimensionsFor<MockFixedVirialSpecificEnergy>);
|
|
|
|
STATIC_CHECK(
|
|
models::physicalScaleForQuantity<mean_field::dimensions::quantity::Pressure> ==
|
|
models::PhysicalScaleLaw::pressure
|
|
);
|
|
STATIC_CHECK(
|
|
CanDescribeDimensionalScalar<
|
|
mean_field::dimensions::quantity::Pressure, mean_field::dimensions::quantity::SpecificEnergy,
|
|
mean_field::dimensions::quantity::Pressure>
|
|
);
|
|
|
|
// Total energy has no numerical reference-scale law yet. It cannot be
|
|
// mislabeled as specific energy merely by choosing that enum or unit text.
|
|
STATIC_CHECK(
|
|
models::physicalScaleForQuantity<mean_field::dimensions::quantity::Energy> ==
|
|
models::PhysicalScaleLaw::unavailable
|
|
);
|
|
STATIC_CHECK_FALSE(
|
|
CanDescribeDimensionalScalar<
|
|
mean_field::dimensions::quantity::Energy, mean_field::dimensions::quantity::Dimensionless,
|
|
mean_field::dimensions::quantity::Energy>
|
|
);
|
|
|
|
STATIC_CHECK(models::ModelSpecification<MockDimensionallyMismatchedMagneticEnergy>);
|
|
STATIC_CHECK_FALSE(models::CompleteGeneratedScalarDimensionsFor<MockDimensionallyMismatchedMagneticEnergy>);
|
|
STATIC_CHECK_FALSE(models::CompleteGeneratedNormalizationFor<MockDimensionallyMismatchedMagneticEnergy>);
|
|
STATIC_CHECK_FALSE(models::CompleteGeneratedManifestFor<MockDimensionallyMismatchedMagneticEnergy>);
|
|
STATIC_CHECK(mean_field::operators::StellarEquilibriumSymbolicallyCompilable<DimensionallyMismatchedModel>);
|
|
STATIC_CHECK_FALSE(
|
|
mean_field::normalization::CompleteStellarNormalizationFor<
|
|
DimensionallyMismatchedModel, DimensionallyMismatchedForm>
|
|
);
|
|
STATIC_CHECK_FALSE(
|
|
mean_field::operators::CompilableRootManifestFor<DimensionallyMismatchedModel, DimensionallyMismatchedForm>
|
|
);
|
|
STATIC_CHECK_FALSE(mean_field::equilibrium::StellarEquilibriumModel<DimensionallyMismatchedModel>);
|
|
|
|
STATIC_CHECK(models::CompleteGeneratedScalarDimensionsFor<mean_field::models::FixedTotalMass>);
|
|
STATIC_CHECK(models::CompleteGeneratedScalarDimensionsFor<mean_field::models::FixedAngularMomentum>);
|
|
STATIC_CHECK(models::CompleteGeneratedScalarDimensionsFor<mean_field::models::FixedCentralDensity>);
|
|
STATIC_CHECK(
|
|
mean_field::models::FixedCentralDensity::ScalarDescription::targetScale == models::PhysicalScaleLaw::density
|
|
);
|
|
STATIC_CHECK_FALSE(
|
|
std::same_as<
|
|
typename mean_field::models::FixedCentralDensity::ScalarDescription::TargetQuantity,
|
|
typename mean_field::models::FixedCentralDensity::ScalarDescription::ConstraintResidualQuantity>
|
|
);
|
|
|
|
// A third-party integral uses the same semantic translation path as the
|
|
// built-ins; it does not register an operator-compiler specialization.
|
|
STATIC_CHECK(mean_field::operators::stellarEquilibriumSpecificationCompilationComplete<MockFixedBaryonMass>);
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename BaryonCompilation::DependsOnValueBlocks,
|
|
mean_field::utils::blocks::type_list<mean_field::utils::blocks::density::mass::value>>
|
|
);
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename BaryonCompilation::AffectedResidualBlocks,
|
|
mean_field::utils::blocks::type_list<mean_field::utils::blocks::enthalpy::specific::residual>>
|
|
);
|
|
|
|
using VirialCompilation =
|
|
mean_field::operators::StellarEquilibriumSpecificationCompilation<MockFixedVirialSpecificEnergy>;
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename VirialCompilation::DependsOnValueBlocks, mean_field::utils::blocks::type_list<
|
|
mean_field::utils::blocks::gravity::gradient::value,
|
|
mean_field::utils::blocks::gravity::poisson::value>>
|
|
);
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename VirialCompilation::AffectedResidualBlocks,
|
|
mean_field::utils::blocks::type_list<
|
|
mean_field::utils::blocks::gravity::gradient::residual,
|
|
mean_field::utils::blocks::gravity::poisson::residual>>
|
|
);
|
|
STATIC_CHECK(VirialCompilation::JacobianCouplings::size == 8);
|
|
STATIC_CHECK(VirialCompilation::IncidentJacobianCouplings::size == 4);
|
|
|
|
using CrossCompilation =
|
|
mean_field::operators::StellarEquilibriumSpecificationCompilation<MockCrossCoupledIntegral>;
|
|
using BaryonValue = GeneratedValueBlockFor<MockFixedBaryonMass>;
|
|
using BaryonResidual = GeneratedResidualBlockFor<MockFixedBaryonMass>;
|
|
using CrossValue = GeneratedValueBlockFor<MockCrossCoupledIntegral>;
|
|
using CrossResidual = GeneratedResidualBlockFor<MockCrossCoupledIntegral>;
|
|
|
|
STATIC_CHECK(
|
|
std::same_as<typename CrossCompilation::DependsOnValueBlocks, mean_field::utils::blocks::type_list<BaryonValue>>
|
|
);
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename CrossCompilation::AffectedResidualBlocks,
|
|
mean_field::utils::blocks::type_list<BaryonResidual, CrossResidual>>
|
|
);
|
|
STATIC_CHECK(CrossCompilation::JacobianCouplings::size == 4);
|
|
STATIC_CHECK(CrossCompilation::IncidentJacobianCouplings::size == 4);
|
|
|
|
using CrossDerivative = mean_field::stellar::Derivative<
|
|
mean_field::stellar::equation::ConstraintOf<MockFixedBaryonMass>,
|
|
mean_field::stellar::state::GeneratedCoordinateOf<MockCrossCoupledIntegral>>;
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename CrossDerivative::EquationType, mean_field::stellar::equation::ConstraintOf<MockFixedBaryonMass>>
|
|
);
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename CrossDerivative::StateType,
|
|
mean_field::stellar::state::GeneratedCoordinateOf<MockCrossCoupledIntegral>>
|
|
);
|
|
|
|
using CrossModel = MatrixModelT<MockBarotropicEquationOfState, MockIsobaricSurface, CrossConstraintPack>;
|
|
using CrossSystem = mean_field::operators::CompiledStellarEquilibriumSystem<CrossModel>;
|
|
using CrossJacobian = typename CrossSystem::JacobianType;
|
|
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<CrossResidual, BaryonValue, CrossJacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<BaryonResidual, BaryonValue, CrossJacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<BaryonResidual, CrossValue, CrossJacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<CrossResidual, CrossValue, CrossJacobian>);
|
|
|
|
// A named cross-constraint dependency is not silently admitted when its
|
|
// owner is absent from the model and therefore absent from the block form.
|
|
STATIC_CHECK_FALSE(mean_field::operators::StellarEquilibriumSymbolicallyCompilable<DanglingCrossConstraintModel>);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Malformed Optional Declarations Are Detectable Without Breaking "
|
|
"Type Queries",
|
|
tags::stellar_model_specification_api
|
|
) {
|
|
using Contribution = models::SpecificationContribution<MockIntegralWithMalformedNormalization>;
|
|
|
|
STATIC_CHECK(models::ModelSpecification<MockIntegralWithMalformedNormalization>);
|
|
STATIC_CHECK(Contribution::generatedValueArity == 1);
|
|
STATIC_CHECK(std::same_as<typename Contribution::Normalization, models::UnavailableGeneratedNormalization>);
|
|
STATIC_CHECK_FALSE(models::CompleteGeneratedNormalizationFor<MockIntegralWithMalformedNormalization>);
|
|
STATIC_CHECK_FALSE(models::CompleteGeneratedManifestFor<MockIntegralWithMalformedNormalization>);
|
|
STATIC_CHECK_FALSE(models::CompleteGeneratedNormalizationFor<int>);
|
|
STATIC_CHECK_FALSE(models::CompleteGeneratedManifestFor<int>);
|
|
STATIC_CHECK_FALSE(models::CompleteGeneratedScalarDimensionsFor<int>);
|
|
STATIC_CHECK_FALSE(mean_field::stellar::ScalarConstraintDescription<MalformedNormalization>);
|
|
STATIC_CHECK(models::ModelSpecification<MockMalformedEquilibriumPhysics>);
|
|
STATIC_CHECK(mean_field::operators::StellarEquilibriumSystemCompilable<MalformedRuntimeModel>);
|
|
STATIC_CHECK_FALSE(
|
|
mean_field::operators::StellarEquilibriumPhysicsAvailableFor<
|
|
MockMalformedEquilibriumPhysics, MalformedRuntimeModel>
|
|
);
|
|
STATIC_CHECK_FALSE(mean_field::operators::hasCompleteStellarEquilibriumRuntime<MalformedRuntimeModel>);
|
|
STATIC_CHECK_FALSE(mean_field::equilibrium::StellarEquilibriumModel<MalformedRuntimeModel>);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Physics Capability And Restricted Views Require Model Membership",
|
|
tags::stellar_model_specification_api
|
|
) {
|
|
using Contained = mean_field::models::FixedTotalMass;
|
|
using Foreign = mean_field::models::FixedCentralDensity;
|
|
|
|
STATIC_CHECK(mean_field::operators::StellarEquilibriumSpecificationBelongsToModel<Contained, BasePhysicsModel>);
|
|
STATIC_CHECK_FALSE(mean_field::operators::StellarEquilibriumSpecificationBelongsToModel<Foreign, BasePhysicsModel>);
|
|
STATIC_CHECK(HasContributionStateView<Contained, BasePhysicsModel>);
|
|
STATIC_CHECK(HasContributionResidualView<Contained, BasePhysicsModel>);
|
|
STATIC_CHECK_FALSE(HasContributionStateView<Foreign, BasePhysicsModel>);
|
|
STATIC_CHECK_FALSE(HasContributionResidualView<Foreign, BasePhysicsModel>);
|
|
STATIC_CHECK(mean_field::operators::StellarEquilibriumPhysicsAvailableFor<Contained, BasePhysicsModel>);
|
|
STATIC_CHECK_FALSE(mean_field::operators::StellarEquilibriumPhysicsAvailableFor<Foreign, BasePhysicsModel>);
|
|
STATIC_CHECK_FALSE(mean_field::operators::StellarEquilibriumSpecificationBelongsToModel<int, int>);
|
|
STATIC_CHECK_FALSE(mean_field::operators::StellarEquilibriumPhysicsAvailableFor<int, int>);
|
|
STATIC_CHECK_FALSE(HasContributionStateView<int, int>);
|
|
STATIC_CHECK_FALSE(HasContributionResidualView<int, int>);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Specification Roles Admit Only Their Implemented Generated State Kinds",
|
|
tags::stellar_model_specification_api
|
|
) {
|
|
using Role = models::SpecificationRole;
|
|
using Kind = models::GeneratedStateKind;
|
|
|
|
// Constitutive laws and boundary conditions describe existing fields.
|
|
STATIC_CHECK(models::CompatibleSpecificationRoleAndGeneratedState<Role::constitutive_law, Kind::none>);
|
|
STATIC_CHECK_FALSE(models::CompatibleSpecificationRoleAndGeneratedState<Role::constitutive_law, Kind::multiplier>);
|
|
STATIC_CHECK_FALSE(
|
|
models::CompatibleSpecificationRoleAndGeneratedState<Role::constitutive_law, Kind::physical_coordinate>
|
|
);
|
|
STATIC_CHECK_FALSE(
|
|
models::CompatibleSpecificationRoleAndGeneratedState<Role::constitutive_law, Kind::solver_border>
|
|
);
|
|
STATIC_CHECK(models::CompatibleSpecificationRoleAndGeneratedState<Role::boundary_condition, Kind::none>);
|
|
STATIC_CHECK_FALSE(
|
|
models::CompatibleSpecificationRoleAndGeneratedState<Role::boundary_condition, Kind::multiplier>
|
|
);
|
|
STATIC_CHECK_FALSE(
|
|
models::CompatibleSpecificationRoleAndGeneratedState<Role::boundary_condition, Kind::physical_coordinate>
|
|
);
|
|
STATIC_CHECK_FALSE(
|
|
models::CompatibleSpecificationRoleAndGeneratedState<Role::boundary_condition, Kind::solver_border>
|
|
);
|
|
|
|
// Invariants own either a Lagrange multiplier or a physical coordinate.
|
|
STATIC_CHECK(models::CompatibleSpecificationRoleAndGeneratedState<Role::invariant, Kind::multiplier>);
|
|
STATIC_CHECK(models::CompatibleSpecificationRoleAndGeneratedState<Role::invariant, Kind::physical_coordinate>);
|
|
STATIC_CHECK_FALSE(models::CompatibleSpecificationRoleAndGeneratedState<Role::invariant, Kind::none>);
|
|
STATIC_CHECK_FALSE(models::CompatibleSpecificationRoleAndGeneratedState<Role::invariant, Kind::solver_border>);
|
|
|
|
// Phase and gauge choices close a null direction with a solver border.
|
|
STATIC_CHECK(models::CompatibleSpecificationRoleAndGeneratedState<Role::phase_condition, Kind::solver_border>);
|
|
STATIC_CHECK_FALSE(models::CompatibleSpecificationRoleAndGeneratedState<Role::phase_condition, Kind::none>);
|
|
STATIC_CHECK_FALSE(models::CompatibleSpecificationRoleAndGeneratedState<Role::phase_condition, Kind::multiplier>);
|
|
STATIC_CHECK_FALSE(
|
|
models::CompatibleSpecificationRoleAndGeneratedState<Role::phase_condition, Kind::physical_coordinate>
|
|
);
|
|
STATIC_CHECK(models::CompatibleSpecificationRoleAndGeneratedState<Role::gauge_choice, Kind::solver_border>);
|
|
STATIC_CHECK_FALSE(models::CompatibleSpecificationRoleAndGeneratedState<Role::gauge_choice, Kind::none>);
|
|
STATIC_CHECK_FALSE(models::CompatibleSpecificationRoleAndGeneratedState<Role::gauge_choice, Kind::multiplier>);
|
|
STATIC_CHECK_FALSE(
|
|
models::CompatibleSpecificationRoleAndGeneratedState<Role::gauge_choice, Kind::physical_coordinate>
|
|
);
|
|
|
|
// Rotation laws are currently prescribed closures, not root-state owners.
|
|
STATIC_CHECK(models::CompatibleSpecificationRoleAndGeneratedState<Role::rotation_law, Kind::none>);
|
|
STATIC_CHECK_FALSE(models::CompatibleSpecificationRoleAndGeneratedState<Role::rotation_law, Kind::multiplier>);
|
|
STATIC_CHECK_FALSE(
|
|
models::CompatibleSpecificationRoleAndGeneratedState<Role::rotation_law, Kind::physical_coordinate>
|
|
);
|
|
STATIC_CHECK_FALSE(models::CompatibleSpecificationRoleAndGeneratedState<Role::rotation_law, Kind::solver_border>);
|
|
|
|
constexpr auto unknownRole = static_cast<Role>(1000);
|
|
constexpr auto unknownKind = static_cast<Kind>(1000);
|
|
STATIC_CHECK_FALSE(models::CompatibleSpecificationRoleAndGeneratedState<unknownRole, Kind::none>);
|
|
STATIC_CHECK_FALSE(models::CompatibleSpecificationRoleAndGeneratedState<Role::constitutive_law, unknownKind>);
|
|
|
|
using ValidConstitutive = MockRoleStateSpecification<Role::constitutive_law, Kind::none>;
|
|
using ValidBoundary = MockRoleStateSpecification<Role::boundary_condition, Kind::none>;
|
|
using ValidInvariant = MockRoleStateSpecification<Role::invariant, Kind::multiplier>;
|
|
using ValidPhysicalInvariant = MockRoleStateSpecification<Role::invariant, Kind::physical_coordinate>;
|
|
using ValidPhase = MockRoleStateSpecification<Role::phase_condition, Kind::solver_border>;
|
|
using ValidGauge = MockRoleStateSpecification<Role::gauge_choice, Kind::solver_border>;
|
|
using ValidRotation = MockRoleStateSpecification<Role::rotation_law, Kind::none>;
|
|
using InvalidInvariant = MockRoleStateSpecification<Role::invariant, Kind::none>;
|
|
using InvalidBoundary = MockRoleStateSpecification<Role::boundary_condition, Kind::solver_border>;
|
|
using InvalidRotation = MockRoleStateSpecification<Role::rotation_law, Kind::physical_coordinate>;
|
|
using UnknownRole = MockRoleStateSpecification<unknownRole, Kind::none>;
|
|
using UnknownKind = MockRoleStateSpecification<Role::constitutive_law, unknownKind>;
|
|
|
|
STATIC_CHECK(models::ModelSpecification<ValidConstitutive>);
|
|
STATIC_CHECK(models::ModelSpecification<ValidBoundary>);
|
|
STATIC_CHECK(models::SelfDescribingModelSpecification<ValidInvariant>);
|
|
STATIC_CHECK(models::ModelSpecification<ValidInvariant>);
|
|
STATIC_CHECK(models::ModelSpecification<ValidPhysicalInvariant>);
|
|
STATIC_CHECK(models::ModelSpecification<ValidPhase>);
|
|
STATIC_CHECK(models::ModelSpecification<ValidGauge>);
|
|
STATIC_CHECK(models::ModelSpecification<ValidRotation>);
|
|
STATIC_CHECK(HasModelDefinitionProjection<ValidInvariant>);
|
|
STATIC_CHECK(HasSpecificationContribution<ValidInvariant>);
|
|
STATIC_CHECK(CanEnterSpecificationSet<ValidInvariant>);
|
|
STATIC_CHECK(models::ValidModelSpecificationPack<mean_field::eos::Polytrope, ValidInvariant>);
|
|
STATIC_CHECK(CanDeduceStellarModel<mean_field::eos::Polytrope, ValidInvariant>);
|
|
|
|
STATIC_CHECK_FALSE(models::SelfDescribingModelSpecification<InvalidInvariant>);
|
|
STATIC_CHECK_FALSE(models::ModelSpecification<InvalidInvariant>);
|
|
STATIC_CHECK_FALSE(models::ResolvedModelSpecification<InvalidInvariant>);
|
|
STATIC_CHECK_FALSE(HasModelDefinitionProjection<InvalidInvariant>);
|
|
STATIC_CHECK_FALSE(HasSpecificationContribution<InvalidInvariant>);
|
|
STATIC_CHECK_FALSE(CanEnterSpecificationSet<InvalidInvariant>);
|
|
STATIC_CHECK_FALSE(models::ValidModelSpecificationPack<mean_field::eos::Polytrope, InvalidInvariant>);
|
|
STATIC_CHECK_FALSE(CanDeduceStellarModel<mean_field::eos::Polytrope, InvalidInvariant>);
|
|
STATIC_CHECK_FALSE(models::ModelSpecification<InvalidBoundary>);
|
|
STATIC_CHECK_FALSE(CanDeduceStellarModel<mean_field::eos::Polytrope, InvalidBoundary>);
|
|
STATIC_CHECK_FALSE(models::ModelSpecification<InvalidRotation>);
|
|
STATIC_CHECK_FALSE(models::ModelSpecification<UnknownRole>);
|
|
STATIC_CHECK_FALSE(models::ModelSpecification<UnknownKind>);
|
|
|
|
// The physics-facing aliases continue to produce accepted combinations.
|
|
STATIC_CHECK(models::ModelSpecification<MockBarotropicEquationOfState>);
|
|
STATIC_CHECK(models::ModelSpecification<MockIsobaricSurface>);
|
|
STATIC_CHECK(models::ModelSpecification<MockFixedBaryonMass>);
|
|
STATIC_CHECK(models::ModelSpecification<MockCrossCoupledIntegral>);
|
|
STATIC_CHECK(models::ModelSpecification<MockFixedMagneticEnergyAmplitude>);
|
|
STATIC_CHECK(models::ModelSpecification<MockFixedVirialSpecificEnergy>);
|
|
STATIC_CHECK(models::ModelSpecification<MockCentralEnthalpyPhase>);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Stable Names Distinguish Independent Same-Role Extensions",
|
|
tags::stellar_model_specification_api
|
|
) {
|
|
constexpr auto baryonKey = models::SpecificationTraits<MockFixedBaryonMass>::key;
|
|
constexpr auto restKey = models::SpecificationTraits<MockFixedRestMass>::key;
|
|
|
|
STATIC_CHECK(baryonKey.role == models::SpecificationRole::invariant);
|
|
STATIC_CHECK(restKey.role == models::SpecificationRole::invariant);
|
|
STATIC_CHECK(baryonKey.generatedStateKind == models::GeneratedStateKind::multiplier);
|
|
STATIC_CHECK(restKey.generatedStateKind == models::GeneratedStateKind::multiplier);
|
|
STATIC_CHECK(baryonKey.stableName == "MockFixedBaryonMass");
|
|
STATIC_CHECK(restKey.stableName == "MockFixedRestMass");
|
|
STATIC_CHECK(baryonKey != restKey);
|
|
STATIC_CHECK(models::specificationKeysAreUnique<MockFixedBaryonMass, MockFixedRestMass>);
|
|
STATIC_CHECK_FALSE(models::specificationKeysAreUnique<MockFixedBaryonMass, MockConflictingBaryonMass>);
|
|
|
|
using Canonical = models::SpecificationSet<MockFixedRestMass, MockBarotropicEquationOfState, MockFixedBaryonMass>;
|
|
using Reordered = models::SpecificationSet<MockFixedBaryonMass, MockFixedRestMass, MockBarotropicEquationOfState>;
|
|
STATIC_CHECK(std::same_as<Canonical, Reordered>);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"EOS Surface And Integral Choices Compose As A Compile-Time Product",
|
|
tags::stellar_model_specification_api
|
|
) {
|
|
// Twenty-eight independently inferred types: 2 EOS choices x 2 boundary
|
|
// choices x 7 materially different global-constraint packs.
|
|
STATIC_CHECK(AuditCartesianProduct<MockEquationOfStates, MockSurfaceConditions, ConstraintPacks>::value);
|
|
|
|
STATIC_CHECK(model::StellarModelType<BarotropicIsobaricModel>);
|
|
STATIC_CHECK(model::StellarModelType<BarotropicIsothermalModel>);
|
|
STATIC_CHECK(model::StellarModelType<NonBarotropicIsobaricModel>);
|
|
STATIC_CHECK(model::StellarModelType<NonBarotropicIsothermalModel>);
|
|
STATIC_CHECK_FALSE(std::same_as<BarotropicIsobaricModel, BarotropicIsothermalModel>);
|
|
STATIC_CHECK_FALSE(std::same_as<BarotropicIsobaricModel, NonBarotropicIsobaricModel>);
|
|
|
|
STATIC_CHECK(BarotropicIsobaricModel::OperatorSignature::generatedValueArity == 3);
|
|
STATIC_CHECK(BarotropicIsobaricModel::OperatorSignature::generatedResidualArity == 3);
|
|
STATIC_CHECK(BarotropicIsobaricModel::symbolicallySquare);
|
|
STATIC_CHECK(NonBarotropicIsothermalModel::symbolicallySquare);
|
|
STATIC_CHECK(mean_field::operators::StellarEquilibriumSymbolicallyCompilable<BarotropicIsobaricModel>);
|
|
STATIC_CHECK(mean_field::operators::StellarEquilibriumSymbolicallyCompilable<BarotropicIsothermalModel>);
|
|
STATIC_CHECK(mean_field::operators::StellarEquilibriumSymbolicallyCompilable<NonBarotropicIsobaricModel>);
|
|
STATIC_CHECK(mean_field::operators::StellarEquilibriumSymbolicallyCompilable<NonBarotropicIsothermalModel>);
|
|
STATIC_CHECK(
|
|
mean_field::normalization::CompleteStellarNormalizationFor<
|
|
BarotropicIsobaricModel, mean_field::operators::CompiledStellarEquilibriumForm<BarotropicIsobaricModel>>
|
|
);
|
|
STATIC_CHECK_FALSE(mean_field::equilibrium::StellarEquilibriumModel<BarotropicIsobaricModel>);
|
|
STATIC_CHECK_FALSE(mean_field::equilibrium::StellarEquilibriumModel<NonBarotropicIsothermalModel>);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Constraint Packs Produce Their Declared Jacobian Incidence Without Bespoke Combination Types",
|
|
tags::stellar_model_specification_api
|
|
) {
|
|
using Model = MatrixModelT<MockBarotropicEquationOfState, MockIsothermalSurface, AllConstraintKindsPack>;
|
|
using System = mean_field::operators::CompiledStellarEquilibriumSystem<Model>;
|
|
using Jacobian = typename System::JacobianType;
|
|
using Density = mean_field::utils::blocks::density::mass::value;
|
|
using SurfaceShape = mean_field::utils::blocks::surface_deformation::parameters::value;
|
|
using Enthalpy = mean_field::utils::blocks::enthalpy::specific::value;
|
|
using SurfaceBalance = mean_field::utils::blocks::surface_deformation::shape_equilibrium::residual;
|
|
using HydrostaticBalance = mean_field::utils::blocks::enthalpy::specific::residual;
|
|
|
|
using BaryonValue = GeneratedValueBlockFor<MockFixedBaryonMass>;
|
|
using BaryonResidual = GeneratedResidualBlockFor<MockFixedBaryonMass>;
|
|
using RestMassValue = GeneratedValueBlockFor<MockFixedRestMass>;
|
|
using RestMassResidual = GeneratedResidualBlockFor<MockFixedRestMass>;
|
|
using MagneticAmplitude = GeneratedValueBlockFor<MockFixedMagneticEnergyAmplitude>;
|
|
using MagneticEnergyResidual = GeneratedResidualBlockFor<MockFixedMagneticEnergyAmplitude>;
|
|
using PhaseBorder = GeneratedValueBlockFor<MockCentralEnthalpyPhase>;
|
|
using PhaseResidual = GeneratedResidualBlockFor<MockCentralEnthalpyPhase>;
|
|
|
|
using BaryonCompilation = mean_field::operators::StellarEquilibriumSpecificationCompilation<MockFixedBaryonMass>;
|
|
using RestMassCompilation = mean_field::operators::StellarEquilibriumSpecificationCompilation<MockFixedRestMass>;
|
|
using MagneticCompilation =
|
|
mean_field::operators::StellarEquilibriumSpecificationCompilation<MockFixedMagneticEnergyAmplitude>;
|
|
using PhaseCompilation =
|
|
mean_field::operators::StellarEquilibriumSpecificationCompilation<MockCentralEnthalpyPhase>;
|
|
|
|
STATIC_CHECK(BaryonCompilation::JacobianCouplings::size == 3);
|
|
STATIC_CHECK(BaryonCompilation::IncidentJacobianCouplings::size == 2);
|
|
STATIC_CHECK(RestMassCompilation::JacobianCouplings::size == 3);
|
|
STATIC_CHECK(RestMassCompilation::IncidentJacobianCouplings::size == 2);
|
|
STATIC_CHECK(MagneticCompilation::JacobianCouplings::size == 9);
|
|
STATIC_CHECK(MagneticCompilation::IncidentJacobianCouplings::size == 5);
|
|
STATIC_CHECK(PhaseCompilation::JacobianCouplings::size == 3);
|
|
STATIC_CHECK(PhaseCompilation::IncidentJacobianCouplings::size == 2);
|
|
STATIC_CHECK(System::ContributionJacobianCouplings::size == 16);
|
|
STATIC_CHECK(System::JacobianCouplings::size == 28);
|
|
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<HydrostaticBalance, BaryonValue, Jacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<BaryonResidual, Density, Jacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<HydrostaticBalance, Density, Jacobian>);
|
|
STATIC_CHECK_FALSE(mean_field::utils::blocks::has_jacobian_coupling_v<BaryonResidual, Enthalpy, Jacobian>);
|
|
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<HydrostaticBalance, RestMassValue, Jacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<RestMassResidual, Density, Jacobian>);
|
|
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<SurfaceBalance, MagneticAmplitude, Jacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<HydrostaticBalance, MagneticAmplitude, Jacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<SurfaceBalance, Density, Jacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<SurfaceBalance, SurfaceShape, Jacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<HydrostaticBalance, SurfaceShape, Jacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<MagneticEnergyResidual, Density, Jacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<MagneticEnergyResidual, SurfaceShape, Jacobian>);
|
|
STATIC_CHECK(
|
|
mean_field::utils::blocks::has_jacobian_coupling_v<MagneticEnergyResidual, MagneticAmplitude, Jacobian>
|
|
);
|
|
STATIC_CHECK_FALSE(mean_field::utils::blocks::has_jacobian_coupling_v<MagneticEnergyResidual, Enthalpy, Jacobian>);
|
|
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<HydrostaticBalance, PhaseBorder, Jacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<PhaseResidual, Enthalpy, Jacobian>);
|
|
STATIC_CHECK(mean_field::utils::blocks::has_jacobian_coupling_v<HydrostaticBalance, Enthalpy, Jacobian>);
|
|
STATIC_CHECK_FALSE(mean_field::utils::blocks::has_jacobian_coupling_v<PhaseResidual, Density, Jacobian>);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Canonical Ordering Makes A Constraint Product Independent Of User Spelling",
|
|
tags::stellar_model_specification_api
|
|
) {
|
|
using Canonical = MatrixModelT<MockBarotropicEquationOfState, MockIsothermalSurface, AllConstraintKindsPack>;
|
|
using Permuted = model::StellarModel<models::SpecificationSet<
|
|
MockCentralEnthalpyPhase, MockFixedMagneticEnergyAmplitude, MockIsothermalSurface, MockFixedRestMass,
|
|
MockBarotropicEquationOfState, MockFixedBaryonMass>>;
|
|
|
|
STATIC_CHECK(std::same_as<Canonical, Permuted>);
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
mean_field::operators::CompiledStellarEquilibriumForm<Canonical>,
|
|
mean_field::operators::CompiledStellarEquilibriumForm<Permuted>>
|
|
);
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
mean_field::operators::CompiledStellarEquilibriumJacobianForm<Canonical>,
|
|
mean_field::operators::CompiledStellarEquilibriumJacobianForm<Permuted>>
|
|
);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Manifest Capability Is Inferred Separately From Symbolic And Runtime Capability",
|
|
tags::stellar_model_specification_api
|
|
) {
|
|
using BarotropicManifestModel =
|
|
MatrixModelT<MockBarotropicEquationOfState, surface::Isobaric, AllConstraintKindsPack>;
|
|
using NonBarotropicManifestModel =
|
|
MatrixModelT<MockNonBarotropicEquationOfState, surface::Isobaric, AllConstraintKindsPack>;
|
|
using BarotropicForm = mean_field::operators::CompiledStellarEquilibriumForm<BarotropicManifestModel>;
|
|
using NonBarotropicForm = mean_field::operators::CompiledStellarEquilibriumForm<NonBarotropicManifestModel>;
|
|
|
|
STATIC_CHECK(mean_field::operators::StellarEquilibriumSymbolicallyCompilable<BarotropicManifestModel>);
|
|
STATIC_CHECK(mean_field::operators::StellarEquilibriumSymbolicallyCompilable<NonBarotropicManifestModel>);
|
|
STATIC_CHECK(mean_field::normalization::CompleteStellarNormalizationFor<BarotropicManifestModel, BarotropicForm>);
|
|
STATIC_CHECK(
|
|
mean_field::normalization::CompleteStellarNormalizationFor<NonBarotropicManifestModel, NonBarotropicForm>
|
|
);
|
|
STATIC_CHECK(mean_field::operators::CompilableRootManifestFor<BarotropicManifestModel, BarotropicForm>);
|
|
STATIC_CHECK(mean_field::operators::CompilableRootManifestFor<NonBarotropicManifestModel, NonBarotropicForm>);
|
|
|
|
// A complete outer manifest still does not manufacture a numerical EOS
|
|
// core. The unsupported mock EOS types remain rejected at the full
|
|
// problem boundary.
|
|
STATIC_CHECK_FALSE(mean_field::operators::hasStellarEquilibriumCoreRuntime<BarotropicManifestModel>);
|
|
STATIC_CHECK_FALSE(mean_field::operators::hasStellarEquilibriumCoreRuntime<NonBarotropicManifestModel>);
|
|
STATIC_CHECK_FALSE(mean_field::equilibrium::StellarEquilibriumModel<BarotropicManifestModel>);
|
|
STATIC_CHECK_FALSE(mean_field::equilibrium::StellarEquilibriumModel<NonBarotropicManifestModel>);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Role Accessors Present Physics Names While Preserving Exact Types",
|
|
tags::stellar_model_specification_api
|
|
) {
|
|
const auto stellarModel = model::StellarModel(
|
|
MockNonBarotropicEquationOfState({.entropyCoupling = 2.5}),
|
|
MockIsothermalSurface({.temperature = eos::QuantityValue<Temperature>{0.375}}),
|
|
MockFixedBaryonMass({.targetMass = 1.75}), MockFixedMagneticSpecificEnergy({.targetSpecificEnergy = 0.125}),
|
|
MockCentralEnthalpyPhase({.targetEnthalpy = 0.75})
|
|
);
|
|
using Model = std::remove_cvref_t<decltype(stellarModel)>;
|
|
|
|
STATIC_CHECK(Model::template specificationRoleCount<models::SpecificationRole::constitutive_law> == 1);
|
|
STATIC_CHECK(Model::template specificationRoleCount<models::SpecificationRole::boundary_condition> == 1);
|
|
STATIC_CHECK(Model::template specificationRoleCount<models::SpecificationRole::invariant> == 2);
|
|
STATIC_CHECK(Model::template specificationRoleCount<models::SpecificationRole::phase_condition> == 1);
|
|
STATIC_CHECK(std::same_as<typename Model::EquationOfStateType, MockNonBarotropicEquationOfState>);
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename Model::template SpecificationForRole<models::SpecificationRole::constitutive_law>,
|
|
MockNonBarotropicEquationOfState>
|
|
);
|
|
STATIC_CHECK(
|
|
std::same_as<
|
|
typename Model::template SpecificationsForRole<models::SpecificationRole::invariant>,
|
|
models::ModelTypeList<MockFixedBaryonMass, MockFixedMagneticSpecificEnergy>>
|
|
);
|
|
STATIC_CHECK(std::same_as<model::EquationOfStateType<Model>, MockNonBarotropicEquationOfState>);
|
|
STATIC_CHECK(std::same_as<model::SurfaceConditionType<Model>, MockIsothermalSurface>);
|
|
STATIC_CHECK(model::HasEquationOfState<Model>);
|
|
STATIC_CHECK(model::HasSurfaceCondition<Model>);
|
|
STATIC_CHECK(model::HasUniqueSurfaceCondition<Model>);
|
|
STATIC_CHECK_FALSE(HasInvariantRoleAccessor<Model>);
|
|
|
|
CHECK(stellarModel.equationOfState().entropyCoupling() == 2.5);
|
|
CHECK(stellarModel.surfaceCondition().targetTemperature() == eos::QuantityValue<Temperature>{0.375});
|
|
CHECK(stellarModel.specification<MockFixedBaryonMass>().targetMass() == 1.75);
|
|
CHECK(stellarModel.specificationForRole<models::SpecificationRole::phase_condition>().targetEnthalpy() == 0.75);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Optional And Ambiguous Roles Remain Safe To Inspect",
|
|
tags::stellar_model_specification_api
|
|
) {
|
|
using NoSurfaceModel = decltype(model::StellarModel(MockBarotropicEquationOfState({.densityScale = 1.0})));
|
|
using TwoSurfaceModel = decltype(model::StellarModel(
|
|
MockBarotropicEquationOfState({.densityScale = 1.0}),
|
|
MockIsobaricSurface({.pressure = eos::PressureValue{0.0}}),
|
|
MockIsothermalSurface({.temperature = eos::QuantityValue<Temperature>{0.0}})
|
|
));
|
|
|
|
STATIC_CHECK(model::StellarModelType<NoSurfaceModel>);
|
|
STATIC_CHECK(model::specificationRoleCount<models::SpecificationRole::boundary_condition, NoSurfaceModel> == 0);
|
|
STATIC_CHECK_FALSE(model::HasSurfaceCondition<NoSurfaceModel>);
|
|
STATIC_CHECK_FALSE(model::HasUniqueSurfaceCondition<NoSurfaceModel>);
|
|
STATIC_CHECK_FALSE(HasSurfaceConditionType<NoSurfaceModel>);
|
|
STATIC_CHECK_FALSE(HasSurfaceConditionAccessor<NoSurfaceModel>);
|
|
|
|
STATIC_CHECK(model::StellarModelType<TwoSurfaceModel>);
|
|
STATIC_CHECK(model::specificationRoleCount<models::SpecificationRole::boundary_condition, TwoSurfaceModel> == 2);
|
|
STATIC_CHECK(model::HasSurfaceCondition<TwoSurfaceModel>);
|
|
STATIC_CHECK_FALSE(model::HasUniqueSurfaceCondition<TwoSurfaceModel>);
|
|
STATIC_CHECK_FALSE(HasSurfaceConditionType<TwoSurfaceModel>);
|
|
STATIC_CHECK_FALSE(HasSurfaceConditionAccessor<TwoSurfaceModel>);
|
|
|
|
STATIC_CHECK(model::specificationRoleCount<models::SpecificationRole::invariant, int> == 0);
|
|
STATIC_CHECK_FALSE(model::HasSpecificationsForRole<models::SpecificationRole::invariant, int>);
|
|
}
|