test(tests): added test suite

The test suite is extensive. Note that LLMs were used to aid in the development of the test suite. All LLM generated code was manually reviewed.
This commit is contained in:
2026-11-09 13:53:17 -05:00
parent 74e55a6859
commit cb20faeb3d
202 changed files with 35505 additions and 953 deletions

View File

@@ -0,0 +1,101 @@
#include <cmath>
#include <limits>
#include <expected>
#include <string>
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include "serif/solver/normalization/scales.hpp"
namespace
{
namespace dimensions = serif::dimensions;
namespace normalization = serif::solver::normalization;
/** @brief Supply typed reference values while retaining concise physical test cases. */
std::expected<normalization::StellarReferenceScales, std::string> scales(double mass, double radius, double gravity = 1.0)
{
return normalization::prepare_stellar_reference_scales(dimensions::QuantityValue<dimensions::Mass>{mass}, dimensions::QuantityValue<dimensions::Length>{radius}, dimensions::QuantityValue<dimensions::GravitationalConstant>{gravity});
}
}
// Ported from MeanField tests/normalization/physical_riesz.cpp.
TEST_CASE("Characteristic stellar scales satisfy gravity virial and rotation identities", "[normalization][unit][physics]")
{
constexpr double mass = 7.0;
constexpr double radius = 3.0;
constexpr double gravity = 5.0;
const auto prepared = scales(mass, radius, gravity);
REQUIRE(prepared);
const normalization::StellarReferenceScales &reference = *prepared;
CHECK(reference.density.value() == Catch::Approx(mass / std::pow(radius, 3)));
CHECK(reference.acceleration.value() == Catch::Approx(gravity * mass / std::pow(radius, 2)));
CHECK(reference.specific_energy.value() == Catch::Approx(gravity * mass / radius));
CHECK(reference.pressure.value() == Catch::Approx(gravity * mass * mass / std::pow(radius, 4)));
CHECK(reference.angular_velocity.value() == Catch::Approx(std::sqrt(gravity * mass / std::pow(radius, 3))));
CHECK(reference.angular_momentum.value() == Catch::Approx(mass * std::sqrt(gravity * mass * radius)));
const double virial = reference.pressure.value() * std::pow(radius, 3);
CHECK(virial == Catch::Approx(mass * reference.specific_energy.value()).epsilon(2.0e-15));
CHECK(virial == Catch::Approx(reference.force.value() * radius).epsilon(2.0e-15));
CHECK(reference.angular_velocity.value() * reference.angular_velocity.value() * radius == Catch::Approx(reference.acceleration.value()).epsilon(2.0e-15));
CHECK(reference.angular_momentum.value() == Catch::Approx(mass * radius * radius * reference.angular_velocity.value()).epsilon(2.0e-15));
}
TEST_CASE("Characteristic scales obey stellar homology exponents", "[normalization][unit][physics]")
{
const auto reference = scales(2.5, 4.0, 3.0);
constexpr double mass_factor = 11.0;
constexpr double radius_factor = 0.2;
constexpr double gravity_factor = 7.0;
const auto transformed = scales(2.5 * mass_factor, 4.0 * radius_factor, 3.0 * gravity_factor);
REQUIRE(reference);
REQUIRE(transformed);
CHECK(transformed->density.value() / reference->density.value() == Catch::Approx(mass_factor / std::pow(radius_factor, 3)).epsilon(4.0e-15));
CHECK(transformed->acceleration.value() / reference->acceleration.value() == Catch::Approx(gravity_factor * mass_factor / std::pow(radius_factor, 2)).epsilon(4.0e-15));
CHECK(transformed->inverse_time_squared.value() / reference->inverse_time_squared.value() == Catch::Approx(gravity_factor * mass_factor / std::pow(radius_factor, 3)).epsilon(4.0e-15));
CHECK(transformed->specific_energy.value() / reference->specific_energy.value() == Catch::Approx(gravity_factor * mass_factor / radius_factor).epsilon(4.0e-15));
CHECK(transformed->pressure.value() / reference->pressure.value() == Catch::Approx(gravity_factor * mass_factor * mass_factor / std::pow(radius_factor, 4)).epsilon(4.0e-15));
CHECK(transformed->angular_velocity.value() / reference->angular_velocity.value() == Catch::Approx(std::sqrt(gravity_factor * mass_factor / std::pow(radius_factor, 3))).epsilon(4.0e-15));
CHECK(transformed->angular_momentum.value() / reference->angular_momentum.value() == Catch::Approx(mass_factor * std::sqrt(gravity_factor * mass_factor * radius_factor)).epsilon(4.0e-15));
CHECK(transformed->force.value() / reference->force.value() == Catch::Approx(gravity_factor * mass_factor * mass_factor / std::pow(radius_factor, 2)).epsilon(4.0e-15));
}
TEST_CASE("Typed scales distinguish residual quantities and share the gravitational energy scale", "[normalization][unit][physics]")
{
const auto reference = scales(9.0, 2.0, 4.0);
REQUIRE(reference);
CHECK(normalization::characteristic_scale(*reference, dimensions::Dimensionless{}).value() == 1.0);
CHECK(normalization::characteristic_scale(*reference, dimensions::Mass{}).value() == 9.0);
CHECK(normalization::characteristic_scale(*reference, dimensions::Length{}).value() == 2.0);
CHECK(normalization::characteristic_scale(*reference, dimensions::MassDensity{}) == reference->density);
CHECK(normalization::characteristic_scale(*reference, dimensions::Acceleration{}) == reference->acceleration);
CHECK(normalization::characteristic_scale(*reference, dimensions::InverseTimeSquared{}) == reference->inverse_time_squared);
CHECK(normalization::characteristic_scale(*reference, dimensions::Pressure{}) == reference->pressure);
CHECK(normalization::characteristic_scale(*reference, dimensions::AngularVelocity{}) == reference->angular_velocity);
CHECK(normalization::characteristic_scale(*reference, dimensions::AngularMomentum{}) == reference->angular_momentum);
CHECK(normalization::characteristic_scale(*reference, dimensions::Force{}) == reference->force);
CHECK(normalization::characteristic_scale(*reference, dimensions::SpecificEnergy{}) == reference->specific_energy);
CHECK(normalization::characteristic_scale(*reference, dimensions::SpecificEnthalpy{}).value() == reference->specific_energy.value());
CHECK(normalization::characteristic_scale(*reference, dimensions::GravitationalPotential{}).value() == reference->specific_energy.value());
CHECK(normalization::characteristic_scale(*reference, dimensions::SpecificEnthalpy{}).value() != reference->density.value());
CHECK(*reference == *scales(9.0, 2.0, 4.0));
CHECK_FALSE(*reference == *scales(9.0, 2.1, 4.0));
}
TEST_CASE("Characteristic scale preparation rejects invalid and unrepresentable references", "[normalization][unit][validation]")
{
for (const double invalid : {0.0, -1.0, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::infinity()})
{
CHECK_FALSE(scales(invalid, 1.0));
CHECK_FALSE(scales(1.0, invalid));
CHECK_FALSE(scales(1.0, 1.0, invalid));
}
CHECK_FALSE(scales(1.0e300, 1.0e-200, 1.0e100));
CHECK_FALSE(scales(1.0, 1.0e300));
CHECK_FALSE(scales(1.0e-300, 1.0));
REQUIRE(scales(1.0, 1.0));
}