201 lines
8.4 KiB
C++
201 lines
8.4 KiB
C++
#include <array>
|
|
#include <cmath>
|
|
#include <concepts>
|
|
#include <limits>
|
|
#include <type_traits>
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
|
|
|
import mean_field;
|
|
import test_helpers;
|
|
|
|
namespace {
|
|
namespace eos = mean_field::eos;
|
|
|
|
template <typename Candidate>
|
|
concept HasAnyUnaryEquationOfStateConversion =
|
|
requires(const Candidate &candidate, const double value) { candidate.pressure_from_density(value); } ||
|
|
requires(const Candidate &candidate, const double value) { candidate.pressure_from_enthalpy(value); } ||
|
|
requires(const Candidate &candidate, const double value) { candidate.enthalpy_from_density(value); } ||
|
|
requires(const Candidate &candidate, const double value) { candidate.enthalpy_from_pressure(value); } ||
|
|
requires(const Candidate &candidate, const double value) { candidate.density_from_enthalpy(value); } ||
|
|
requires(const Candidate &candidate, const double value) {
|
|
candidate.density_derivative_from_enthalpy(value);
|
|
} ||
|
|
requires(const Candidate &candidate, const double value) {
|
|
candidate.pressure_derivative_from_enthalpy(value);
|
|
} ||
|
|
requires(const Candidate &candidate, const double value) { candidate.pressure_derivative_from_density(value); };
|
|
} // namespace
|
|
|
|
TEST_CASE(
|
|
"Polytropic EOS Declares Its Thermodynamic Relation Contract",
|
|
tags::polytropic_eos_relation_contract
|
|
) {
|
|
using Polytrope = eos::Polytrope;
|
|
|
|
STATIC_CHECK(eos::EquationOfStateModel<Polytrope>);
|
|
STATIC_CHECK_FALSE(std::is_polymorphic_v<Polytrope>);
|
|
STATIC_CHECK_FALSE(HasAnyUnaryEquationOfStateConversion<Polytrope>);
|
|
STATIC_CHECK(Polytrope::Relations::size == 5);
|
|
|
|
STATIC_CHECK(eos::SupportsRelation<Polytrope, eos::PressureFromDensity>);
|
|
STATIC_CHECK(eos::SupportsRelation<Polytrope, eos::PressureFromSpecificEnthalpy>);
|
|
STATIC_CHECK(eos::SupportsRelation<Polytrope, eos::SpecificEnthalpyFromDensity>);
|
|
STATIC_CHECK(eos::SupportsRelation<Polytrope, eos::SpecificEnthalpyFromPressure>);
|
|
STATIC_CHECK(eos::SupportsRelation<Polytrope, eos::DensityFromSpecificEnthalpy>);
|
|
|
|
STATIC_CHECK(eos::SupportsPartialDerivative<Polytrope, eos::PressureFromDensity, eos::quantity::Density>);
|
|
STATIC_CHECK(
|
|
eos::SupportsPartialDerivative<Polytrope, eos::PressureFromSpecificEnthalpy, eos::quantity::SpecificEnthalpy>
|
|
);
|
|
STATIC_CHECK(
|
|
eos::SupportsPartialDerivative<Polytrope, eos::DensityFromSpecificEnthalpy, eos::quantity::SpecificEnthalpy>
|
|
);
|
|
|
|
STATIC_CHECK_FALSE(
|
|
eos::SupportsPartialDerivative<Polytrope, eos::SpecificEnthalpyFromPressure, eos::quantity::Pressure>
|
|
);
|
|
STATIC_CHECK_FALSE(
|
|
eos::SupportsPartialDerivative<Polytrope, eos::SpecificEnthalpyFromDensity, eos::quantity::Density>
|
|
);
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Polytropic EOS Typed Relations Preserve Analytic Values",
|
|
tags::polytropic_eos_characterization
|
|
) {
|
|
constexpr std::array<double, 3> polytropicIndices{1.0, 1.5, 3.0};
|
|
constexpr std::array<double, 2> polytropicConstants{0.25, 0.73};
|
|
constexpr std::array<double, 4> densities{0.0, 1.0e-6, 0.2, 2.0};
|
|
constexpr std::array<double, 4> specificEnthalpies{-0.3, 0.0, 0.2, 1.7};
|
|
constexpr std::array<double, 4> pressures{0.0, 1.0e-8, 0.3, 4.0};
|
|
|
|
for (const double polytropicIndex : polytropicIndices) {
|
|
for (const double polytropicConstant : polytropicConstants) {
|
|
const eos::Polytrope equationOfState(polytropicIndex, polytropicConstant);
|
|
|
|
for (const double density : densities) {
|
|
CAPTURE(polytropicIndex, polytropicConstant, density);
|
|
|
|
const double expectedPressure = polytropicConstant * std::pow(density, 1.0 + 1.0 / polytropicIndex);
|
|
const double expectedSpecificEnthalpy =
|
|
(polytropicIndex + 1.0) * polytropicConstant * std::pow(density, 1.0 / polytropicIndex);
|
|
|
|
CHECK(
|
|
eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::DensityValue{density}).value() ==
|
|
expectedPressure
|
|
);
|
|
|
|
CHECK(
|
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(equationOfState, eos::DensityValue{density})
|
|
.value() == expectedSpecificEnthalpy
|
|
);
|
|
|
|
CHECK_THAT(
|
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::Density>(
|
|
equationOfState, eos::DensityValue{density}
|
|
)
|
|
.value()),
|
|
Catch::Matchers::WithinRel(
|
|
density == 0.0 ? 0.0 : expectedSpecificEnthalpy / polytropicIndex, 2.0e-15
|
|
)
|
|
);
|
|
}
|
|
|
|
for (const double specificEnthalpy : specificEnthalpies) {
|
|
CAPTURE(polytropicIndex, polytropicConstant, specificEnthalpy);
|
|
|
|
const double expectedDensity =
|
|
specificEnthalpy <= 0.0
|
|
? 0.0
|
|
: std::pow(specificEnthalpy / ((polytropicIndex + 1.0) * polytropicConstant), polytropicIndex);
|
|
|
|
CHECK(
|
|
eos::evaluate<eos::quantity::Density>(equationOfState, eos::SpecificEnthalpyValue{specificEnthalpy})
|
|
.value() == expectedDensity
|
|
);
|
|
|
|
CHECK(
|
|
eos::evaluate<eos::quantity::Pressure>(
|
|
equationOfState, eos::SpecificEnthalpyValue{specificEnthalpy}
|
|
)
|
|
.value() ==
|
|
(specificEnthalpy <= 0.0 ? 0.0 : expectedDensity * specificEnthalpy / (polytropicIndex + 1.0))
|
|
);
|
|
|
|
CHECK(
|
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
|
equationOfState, eos::SpecificEnthalpyValue{specificEnthalpy}
|
|
)
|
|
.value() == expectedDensity)
|
|
);
|
|
}
|
|
|
|
for (const double pressure : pressures) {
|
|
CAPTURE(polytropicIndex, polytropicConstant, pressure);
|
|
|
|
const double indexPlusOne = polytropicIndex + 1.0;
|
|
const double expectedSpecificEnthalpy = indexPlusOne *
|
|
std::pow(polytropicConstant, polytropicIndex / indexPlusOne) *
|
|
std::pow(pressure, 1.0 / indexPlusOne);
|
|
|
|
CHECK(
|
|
eos::evaluate<eos::quantity::SpecificEnthalpy>(equationOfState, eos::PressureValue{pressure})
|
|
.value() == expectedSpecificEnthalpy
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE(
|
|
"Typed Polytropic EOS Preserves Domain And Exterior Semantics",
|
|
tags::polytropic_eos_relation_contract
|
|
) {
|
|
const eos::Polytrope equationOfState(3.0, 0.75);
|
|
|
|
try {
|
|
static_cast<void>(eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::DensityValue{-0.1}));
|
|
FAIL("A negative density must be rejected.");
|
|
} catch (const eos::EvaluationError &error) {
|
|
CHECK(error.code() == eos::EvaluationErrorCode::outside_domain);
|
|
}
|
|
|
|
try {
|
|
static_cast<void>(eos::evaluate<eos::quantity::SpecificEnthalpy>(
|
|
equationOfState, eos::PressureValue{std::numeric_limits<double>::quiet_NaN()}
|
|
));
|
|
FAIL("A nonfinite pressure must be rejected.");
|
|
} catch (const eos::EvaluationError &error) {
|
|
CHECK(error.code() == eos::EvaluationErrorCode::nonfinite_input);
|
|
}
|
|
|
|
constexpr double exteriorSpecificEnthalpy = -0.3;
|
|
|
|
CHECK(
|
|
eos::evaluate<eos::quantity::Density>(equationOfState, eos::SpecificEnthalpyValue{exteriorSpecificEnthalpy})
|
|
.value() == 0.0
|
|
);
|
|
|
|
CHECK(
|
|
eos::evaluate<eos::quantity::Pressure>(equationOfState, eos::SpecificEnthalpyValue{exteriorSpecificEnthalpy})
|
|
.value() == 0.0
|
|
);
|
|
|
|
CHECK(
|
|
(eos::partialDerivative<eos::quantity::Density, eos::quantity::SpecificEnthalpy>(
|
|
equationOfState, eos::SpecificEnthalpyValue{exteriorSpecificEnthalpy}
|
|
)
|
|
.value() == 0.0)
|
|
);
|
|
|
|
CHECK(
|
|
(eos::partialDerivative<eos::quantity::Pressure, eos::quantity::SpecificEnthalpy>(
|
|
equationOfState, eos::SpecificEnthalpyValue{exteriorSpecificEnthalpy}
|
|
)
|
|
.value() == 0.0)
|
|
);
|
|
}
|