feat(surface): major work on implementing surface constraints in a presciption agnostic manner

This commit is contained in:
2026-08-30 16:41:14 -04:00
parent 36adfa1174
commit 0a7f18c5c7
95 changed files with 30144 additions and 25766 deletions

View File

@@ -8,115 +8,202 @@
import mean_field;
import test_helpers;
TEST_CASE("Polytropic EOS Satisfies Its Analytic Identities",
tags::barotrope_eos_unit) {
constexpr double polytropic_index = 3.0;
constexpr double polytropic_constant = 1.5;
TEST_CASE(
"Polytropic EOS Satisfies Its Analytic Identities",
tags::barotrope_eos_unit
) {
using namespace mean_field::eos;
const mean_field::eos::Polytrope barotrope(polytropic_index,
polytropic_constant);
constexpr double polytropic_index = 3.0;
constexpr double polytropic_constant = 1.5;
const std::array<double, 5> densities{1.0e-6, 1.0e-3, 0.1, 0.7, 2.0};
const Polytrope barotrope(polytropic_index, polytropic_constant);
for (const double density : densities) {
const double pressure = barotrope.pressure_from_density(density);
using densityV = DensityValue;
using pressureV = PressureValue;
using enthalpyV = SpecificEnthalpyValue;
const double enthalpy = barotrope.enthalpy_from_density(density);
constexpr std::array<densityV, 5> densities{
densityV{1.0e-6}, densityV{1.0e-3}, densityV{0.1}, densityV{0.7}, densityV{2.0}
};
const double reconstructed_density =
barotrope.density_from_enthalpy(enthalpy);
for (const densityV density : densities) {
const pressureV pressure = evaluate<quantity::Pressure>(barotrope, density);
const double reconstructed_pressure =
barotrope.pressure_from_enthalpy(enthalpy);
const enthalpyV enthalpy = evaluate<quantity::SpecificEnthalpy>(barotrope, density);
const densityV reconstructed_density = evaluate<quantity::Density>(barotrope, enthalpy);
const double reconstructed_enthalpy =
barotrope.enthalpy_from_pressure(pressure);
const pressureV reconstructed_pressure = evaluate<quantity::Pressure>(barotrope, enthalpy);
CHECK_THAT(reconstructed_density,
Catch::Matchers::WithinRel(density, 2.0e-14));
const enthalpyV reconstructed_enthalpy = evaluate<quantity::SpecificEnthalpy>(barotrope, pressure);
CHECK_THAT(reconstructed_pressure,
Catch::Matchers::WithinRel(pressure, 2.0e-14));
CHECK_THAT(reconstructed_density.value(), Catch::Matchers::WithinRel(density.value(), 2.0e-14));
CHECK_THAT(reconstructed_enthalpy,
Catch::Matchers::WithinRel(enthalpy, 2.0e-14));
CHECK_THAT(reconstructed_pressure.value(), Catch::Matchers::WithinRel(pressure.value(), 2.0e-14));
CHECK_THAT(pressure,
Catch::Matchers::WithinRel(
density * enthalpy / (polytropic_index + 1.0), 2.0e-14));
CHECK_THAT(reconstructed_enthalpy.value(), Catch::Matchers::WithinRel(enthalpy.value(), 2.0e-14));
CHECK_THAT(barotrope.pressure_derivative_from_enthalpy(enthalpy),
Catch::Matchers::WithinRel(density, 2.0e-14));
CHECK_THAT(
pressure.value(),
Catch::Matchers::WithinRel(density.value() * enthalpy.value() / (polytropic_index + 1.0), 2.0e-14)
);
CHECK_THAT(
barotrope.pressure_derivative_from_density(density),
Catch::Matchers::WithinRel(enthalpy / polytropic_index, 2.0e-14));
}
CHECK_THAT(
(mean_field::eos::partialDerivative<
mean_field::eos::quantity::Pressure, mean_field::eos::quantity::SpecificEnthalpy>(
barotrope, mean_field::eos::SpecificEnthalpyValue{enthalpy}
)
.value()),
Catch::Matchers::WithinRel(density.value(), 2.0e-14)
);
CHECK_THAT(
(mean_field::eos::partialDerivative<
mean_field::eos::quantity::Pressure, mean_field::eos::quantity::Density>(
barotrope, mean_field::eos::DensityValue{density}
)
.value()),
Catch::Matchers::WithinRel(enthalpy.value() / polytropic_index, 2.0e-14)
);
}
}
TEST_CASE("Polytropic EOS Derivatives Match Centered Differences",
tags::barotrope_eos_jacobian) {
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
TEST_CASE(
"Polytropic EOS Derivatives Match Centered Differences",
tags::barotrope_eos_jacobian
) {
using namespace mean_field::eos;
const std::array<double, 4> enthalpies{0.05, 0.2, 0.7, 1.4};
const Polytrope barotrope(3.0, 1.5);
for (const double enthalpy : enthalpies) {
const double step = 1.0e-6 * std::max(1.0, enthalpy);
using densityV = DensityValue;
using pressureV = PressureValue;
using enthalpyV = SpecificEnthalpyValue;
const double density_difference =
(barotrope.density_from_enthalpy(enthalpy + step) -
barotrope.density_from_enthalpy(enthalpy - step)) /
(2.0 * step);
constexpr std::array<enthalpyV, 4> enthalpies{enthalpyV{0.05}, enthalpyV{0.2}, enthalpyV{0.7}, enthalpyV{1.4}};
const double pressure_difference =
(barotrope.pressure_from_enthalpy(enthalpy + step) -
barotrope.pressure_from_enthalpy(enthalpy - step)) /
(2.0 * step);
for (const enthalpyV enthalpy : enthalpies) {
const enthalpyV step = enthalpyV{1.0e-6} * std::max(1.0, enthalpy.value());
CHECK_THAT(
density_difference,
Catch::Matchers::WithinRel(
barotrope.density_derivative_from_enthalpy(enthalpy), 5.0e-10));
const densityV density_difference = (evaluate<quantity::Density>(barotrope, enthalpy + step) -
evaluate<quantity::Density>(barotrope, enthalpy - step)) /
(2.0 * step.value());
CHECK_THAT(
pressure_difference,
Catch::Matchers::WithinRel(
barotrope.pressure_derivative_from_enthalpy(enthalpy), 5.0e-10));
}
const pressureV pressure_difference = (evaluate<quantity::Pressure>(barotrope, enthalpy + step) -
evaluate<quantity::Pressure>(barotrope, enthalpy - step)) /
(2.0 * step.value());
CHECK_THAT(
density_difference.value(),
Catch::Matchers::WithinRel(
mean_field::eos::partialDerivative<
mean_field::eos::quantity::Density, mean_field::eos::quantity::SpecificEnthalpy>(
barotrope, enthalpy
)
.value(),
5.0e-10
)
);
CHECK_THAT(
pressure_difference.value(),
Catch::Matchers::WithinRel(
mean_field::eos::partialDerivative<
mean_field::eos::quantity::Pressure, mean_field::eos::quantity::SpecificEnthalpy>(
barotrope, enthalpy
)
.value(),
5.0e-10
)
);
}
}
TEST_CASE("Polytropic EOS Has An Exact Zero Density Surface",
tags::barotrope_eos_unit) {
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
TEST_CASE(
"Polytropic EOS Has An Exact Zero Density Surface",
tags::barotrope_eos_unit
) {
const mean_field::eos::Polytrope barotrope(3.0, 1.5);
CHECK(barotrope.density_from_enthalpy(-1.0) == 0.0);
CHECK(barotrope.density_from_enthalpy(0.0) == 0.0);
CHECK(
mean_field::eos::evaluate<mean_field::eos::quantity::Density>(
barotrope, mean_field::eos::SpecificEnthalpyValue{-1.0}
)
.value() == 0.0
);
CHECK(
mean_field::eos::evaluate<mean_field::eos::quantity::Density>(
barotrope, mean_field::eos::SpecificEnthalpyValue{0.0}
)
.value() == 0.0
);
CHECK(barotrope.pressure_from_enthalpy(-1.0) == 0.0);
CHECK(barotrope.pressure_from_enthalpy(0.0) == 0.0);
CHECK(
mean_field::eos::evaluate<mean_field::eos::quantity::Pressure>(
barotrope, mean_field::eos::SpecificEnthalpyValue{-1.0}
)
.value() == 0.0
);
CHECK(
mean_field::eos::evaluate<mean_field::eos::quantity::Pressure>(
barotrope, mean_field::eos::SpecificEnthalpyValue{0.0}
)
.value() == 0.0
);
CHECK(barotrope.density_derivative_from_enthalpy(-1.0) == 0.0);
CHECK(
(mean_field::eos::partialDerivative<
mean_field::eos::quantity::Density, mean_field::eos::quantity::SpecificEnthalpy>(
barotrope, mean_field::eos::SpecificEnthalpyValue{-1.0}
)
.value() == 0.0)
);
CHECK(barotrope.density_derivative_from_enthalpy(0.0) == 0.0);
CHECK(
(mean_field::eos::partialDerivative<
mean_field::eos::quantity::Density, mean_field::eos::quantity::SpecificEnthalpy>(
barotrope, mean_field::eos::SpecificEnthalpyValue{0.0}
)
.value() == 0.0)
);
CHECK(barotrope.pressure_derivative_from_enthalpy(0.0) == 0.0);
CHECK(
(mean_field::eos::partialDerivative<
mean_field::eos::quantity::Pressure, mean_field::eos::quantity::SpecificEnthalpy>(
barotrope, mean_field::eos::SpecificEnthalpyValue{0.0}
)
.value() == 0.0)
);
}
TEST_CASE("Polytropic EOS Rejects Invalid Material Parameters",
tags::barotrope_eos_unit) {
CHECK_THROWS_AS(mean_field::eos::Polytrope(0.5, 1.0), std::invalid_argument);
TEST_CASE(
"Polytropic EOS Rejects Invalid Material Parameters",
tags::barotrope_eos_unit
) {
CHECK_THROWS_AS(mean_field::eos::Polytrope(0.5, 1.0), std::invalid_argument);
CHECK_THROWS_AS(mean_field::eos::Polytrope(3.0, 0.0), std::invalid_argument);
CHECK_THROWS_AS(mean_field::eos::Polytrope(3.0, 0.0), std::invalid_argument);
CHECK_THROWS_AS(
mean_field::eos::Polytrope(std::numeric_limits<double>::infinity(), 1.0),
std::invalid_argument);
CHECK_THROWS_AS(mean_field::eos::Polytrope(std::numeric_limits<double>::infinity(), 1.0), std::invalid_argument);
const mean_field::eos::Polytrope barotrope(3.0, 1.0);
const mean_field::eos::Polytrope barotrope(3.0, 1.0);
CHECK_THROWS_AS(barotrope.pressure_from_density(-1.0), std::domain_error);
CHECK_THROWS_AS(
mean_field::eos::evaluate<mean_field::eos::quantity::Pressure>(barotrope, mean_field::eos::DensityValue{-1.0}),
std::domain_error
);
CHECK_THROWS_AS(barotrope.enthalpy_from_density(-1.0), std::domain_error);
CHECK_THROWS_AS(
mean_field::eos::evaluate<mean_field::eos::quantity::SpecificEnthalpy>(
barotrope, mean_field::eos::DensityValue{-1.0}
),
std::domain_error
);
CHECK_THROWS_AS(barotrope.enthalpy_from_pressure(-1.0), std::domain_error);
CHECK_THROWS_AS(
mean_field::eos::evaluate<mean_field::eos::quantity::SpecificEnthalpy>(
barotrope, mean_field::eos::PressureValue{-1.0}
),
std::domain_error
);
}