1390 lines
72 KiB
C++
1390 lines
72 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>);
|
|
}
|