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.
136 lines
8.2 KiB
C++
136 lines
8.2 KiB
C++
// Preserve the independent identities, finite differences, and surface conventions from MeanField's barotrope tests.
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cmath>
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
|
|
|
#include "serif/eos/evaluation.hpp"
|
|
#include "serif/eos/models/polytropic.hpp"
|
|
|
|
namespace
|
|
{
|
|
namespace eos = serif::eos;
|
|
namespace quantity = serif::dimensions;
|
|
using Catch::Matchers::WithinRel;
|
|
|
|
/** @brief Evaluate a centered difference independently of the EOS derivative implementation. */
|
|
template <typename Function>
|
|
[[nodiscard]] double centered_derivative(Function&& function, const double value, const double step)
|
|
{
|
|
return (function(value + step) - function(value - step)) / (2.0 * step);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("MeanField polytropic round trips preserve the original strict identities", "[unit][eos][meanfield]")
|
|
{
|
|
const eos::models::Polytrope model{3.0, 1.5};
|
|
for (const double density : {1.0e-6, 1.0e-3, 0.1, 0.7, 2.0})
|
|
{
|
|
CAPTURE(density);
|
|
const double pressure = eos::evaluate<quantity::Pressure>(model, quantity::DensityValue{density}).value();
|
|
const double enthalpy = eos::evaluate<quantity::SpecificEnthalpy>(model, quantity::DensityValue{density}).value();
|
|
CHECK_THAT(eos::evaluate<quantity::MassDensity>(model, quantity::SpecificEnthalpyValue{enthalpy}).value(), WithinRel(density, 2.0e-14));
|
|
CHECK_THAT(eos::evaluate<quantity::Pressure>(model, quantity::SpecificEnthalpyValue{enthalpy}).value(), WithinRel(pressure, 2.0e-14));
|
|
CHECK_THAT(eos::evaluate<quantity::SpecificEnthalpy>(model, quantity::PressureValue{pressure}).value(), WithinRel(enthalpy, 2.0e-14));
|
|
CHECK_THAT(pressure, WithinRel(density * enthalpy / 4.0, 2.0e-14));
|
|
CHECK_THAT((eos::partial_derivative<quantity::Pressure, quantity::SpecificEnthalpy>(model, quantity::SpecificEnthalpyValue{enthalpy}).value()), WithinRel(density, 2.0e-14));
|
|
CHECK_THAT((eos::partial_derivative<quantity::Pressure, quantity::MassDensity>(model, quantity::DensityValue{density}).value()), WithinRel(enthalpy / 3.0, 2.0e-14));
|
|
}
|
|
}
|
|
|
|
TEST_CASE("MeanField polytropic thermodynamic identities span integer and fractional indices", "[unit][eos][meanfield]")
|
|
{
|
|
for (const double index : {1.0, 1.5, 3.0})
|
|
{
|
|
const eos::models::Polytrope model{index, 0.73};
|
|
CHECK(model.polytropic_index() == index);
|
|
CHECK(model.polytropic_constant() == 0.73);
|
|
CHECK(model.enthalpy_scale() == (index + 1.0) * 0.73);
|
|
for (const double density : {1.0e-4, 0.02, 0.37, 2.4})
|
|
{
|
|
CAPTURE(index, density);
|
|
const double pressure = eos::evaluate<quantity::Pressure>(model, quantity::DensityValue{density}).value();
|
|
const double enthalpy = eos::evaluate<quantity::SpecificEnthalpy>(model, quantity::DensityValue{density}).value();
|
|
const double recovered_density = eos::evaluate<quantity::MassDensity>(model, quantity::SpecificEnthalpyValue{enthalpy}).value();
|
|
const double recovered_pressure = eos::evaluate<quantity::Pressure>(model, quantity::SpecificEnthalpyValue{enthalpy}).value();
|
|
CHECK_THAT(pressure, WithinRel(0.73 * std::pow(density, 1.0 + 1.0 / index), 2.0e-13));
|
|
CHECK_THAT(enthalpy, WithinRel((index + 1.0) * 0.73 * std::pow(density, 1.0 / index), 2.0e-13));
|
|
CHECK_THAT(recovered_density, WithinRel(density, 5.0e-13));
|
|
CHECK_THAT(recovered_pressure, WithinRel(pressure, 5.0e-13));
|
|
CHECK_THAT(recovered_pressure, WithinRel(density * enthalpy / (index + 1.0), 5.0e-13));
|
|
CHECK((eos::partial_derivative<quantity::Pressure, quantity::SpecificEnthalpy>(model, quantity::SpecificEnthalpyValue{enthalpy}).value()) == recovered_density);
|
|
CHECK_THAT((eos::partial_derivative<quantity::Pressure, quantity::MassDensity>(model, quantity::DensityValue{density}).value()), WithinRel(enthalpy / index, 5.0e-13));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("MeanField cubic polytrope derivatives retain the strict centered difference tolerance", "[unit][eos][meanfield]")
|
|
{
|
|
const eos::models::Polytrope model{3.0, 1.5};
|
|
for (const double enthalpy : {0.05, 0.2, 0.7, 1.4})
|
|
{
|
|
CAPTURE(enthalpy);
|
|
const double step = 1.0e-6 * std::max(1.0, enthalpy);
|
|
const double density_derivative = centered_derivative([&](const double value)
|
|
{
|
|
return eos::evaluate<quantity::MassDensity>(model, quantity::SpecificEnthalpyValue{value}).value();
|
|
}, enthalpy, step);
|
|
const double pressure_derivative = centered_derivative([&](const double value)
|
|
{
|
|
return eos::evaluate<quantity::Pressure>(model, quantity::SpecificEnthalpyValue{value}).value();
|
|
}, enthalpy, step);
|
|
CHECK_THAT(density_derivative, WithinRel((eos::partial_derivative<quantity::MassDensity, quantity::SpecificEnthalpy>(model, quantity::SpecificEnthalpyValue{enthalpy}).value()), 5.0e-10));
|
|
CHECK_THAT(pressure_derivative, WithinRel((eos::partial_derivative<quantity::Pressure, quantity::SpecificEnthalpy>(model, quantity::SpecificEnthalpyValue{enthalpy}).value()), 5.0e-10));
|
|
}
|
|
}
|
|
|
|
TEST_CASE("MeanField pressure and density derivatives match centered differences for every original index", "[unit][eos][meanfield]")
|
|
{
|
|
for (const double index : {1.0, 1.5, 3.0})
|
|
{
|
|
const eos::models::Polytrope model{index, 0.61};
|
|
for (const double value : {0.2, 0.73, 1.8})
|
|
{
|
|
CAPTURE(index, value);
|
|
const double step = 2.0e-6 * std::max(1.0, std::abs(value));
|
|
const double pressure_enthalpy = centered_derivative([&](const double enthalpy)
|
|
{
|
|
return eos::evaluate<quantity::Pressure>(model, quantity::SpecificEnthalpyValue{enthalpy}).value();
|
|
}, value, step);
|
|
const double pressure_density = centered_derivative([&](const double density)
|
|
{
|
|
return eos::evaluate<quantity::Pressure>(model, quantity::DensityValue{density}).value();
|
|
}, value, step);
|
|
const double density_enthalpy = centered_derivative([&](const double enthalpy)
|
|
{
|
|
return eos::evaluate<quantity::MassDensity>(model, quantity::SpecificEnthalpyValue{enthalpy}).value();
|
|
}, value, step);
|
|
CHECK_THAT(pressure_enthalpy, WithinRel((eos::partial_derivative<quantity::Pressure, quantity::SpecificEnthalpy>(model, quantity::SpecificEnthalpyValue{value}).value()), 5.0e-8));
|
|
CHECK_THAT(pressure_density, WithinRel((eos::partial_derivative<quantity::Pressure, quantity::MassDensity>(model, quantity::DensityValue{value}).value()), 5.0e-8));
|
|
CHECK_THAT(density_enthalpy, WithinRel((eos::partial_derivative<quantity::MassDensity, quantity::SpecificEnthalpy>(model, quantity::SpecificEnthalpyValue{value}).value()), 5.0e-8));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("MeanField polytropic surface keeps the n one right derivative and zero exterior", "[unit][eos][meanfield]")
|
|
{
|
|
for (const double index : {1.0, 1.5, 3.0})
|
|
{
|
|
const eos::models::Polytrope model{index, 0.47};
|
|
for (const double enthalpy : {0.0, -0.3, -1.0})
|
|
{
|
|
CAPTURE(index, enthalpy);
|
|
CHECK(eos::evaluate<quantity::MassDensity>(model, quantity::SpecificEnthalpyValue{enthalpy}).value() == 0.0);
|
|
CHECK(eos::evaluate<quantity::Pressure>(model, quantity::SpecificEnthalpyValue{enthalpy}).value() == 0.0);
|
|
CHECK((eos::partial_derivative<quantity::Pressure, quantity::SpecificEnthalpy>(model, quantity::SpecificEnthalpyValue{enthalpy}).value()) == 0.0);
|
|
const double density_derivative = index == 1.0 && enthalpy == 0.0 ? 1.0 / model.enthalpy_scale() : 0.0;
|
|
CHECK((eos::partial_derivative<quantity::MassDensity, quantity::SpecificEnthalpy>(model, quantity::SpecificEnthalpyValue{enthalpy}).value()) == density_derivative);
|
|
}
|
|
CHECK(eos::evaluate<quantity::Pressure>(model, quantity::DensityValue{0.0}).value() == 0.0);
|
|
CHECK(eos::evaluate<quantity::SpecificEnthalpy>(model, quantity::DensityValue{0.0}).value() == 0.0);
|
|
CHECK((eos::partial_derivative<quantity::Pressure, quantity::MassDensity>(model, quantity::DensityValue{0.0}).value()) == 0.0);
|
|
}
|
|
}
|