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:
163
tests/surface/automatic.cpp
Normal file
163
tests/surface/automatic.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
|
||||
#include "serif/tests/equations/test_equations.hpp"
|
||||
#include "serif/eos/models/polytropic.hpp"
|
||||
#include "serif/geometry/surface/automatic.hpp"
|
||||
#include "serif/backend/mfem/geometry/surface/pressure_boundary.hpp"
|
||||
#include "serif/tests/surface/boundary_fixture.hpp"
|
||||
#include "serif/tests/surface/models.hpp"
|
||||
#include "serif/utils/dependencies/computation_cache.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace serif;
|
||||
using namespace serif::tests::surface;
|
||||
namespace backend = physics::backend::mfem;
|
||||
|
||||
using ContinuousDensity = ScalarTerm<quantities::MassDensity>;
|
||||
using Pressure = ScalarTerm<quantities::Pressure>;
|
||||
|
||||
struct ForwardOnly : GasEOS
|
||||
{
|
||||
using Relations = eos::relations::RelationCatalog<PressureFromDensityAndTemperature>;
|
||||
};
|
||||
|
||||
struct SymbolicEOS
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("Automatic isobaric compilation preserves the polytropic zero-pressure unit derivative", "[surface][automatic]")
|
||||
{
|
||||
using Fields = dependencies::KeyList<Enthalpy, Density, blocks::barotropic_constant::mass_normalization, blocks::rotation::angular_velocity>;
|
||||
using Selection = physics::AutomaticPressureSurface<eos::models::Polytrope, Fields, dependencies::KeyList<Enthalpy>>;
|
||||
using Formulation = Selection::Formulation;
|
||||
const eos::models::Polytrope equation_of_state{3.0, 0.25};
|
||||
physics::SurfacePoint<Formulation> point{};
|
||||
point.fields.get<Enthalpy>() = 0.0;
|
||||
|
||||
const auto result = physics::evaluate_pressure_surface<Formulation>(equation_of_state, physics::Isobaric{quantities::PressureValue{0.0}}, point);
|
||||
REQUIRE(result);
|
||||
CHECK(result->residual == 0.0);
|
||||
CHECK(result->field_derivatives.get<Enthalpy>() == 1.0);
|
||||
CHECK(Formulation::resolves_carrier);
|
||||
}
|
||||
|
||||
TEST_CASE("Automatic isobaric compilation uses inverse or direct EOS relations without enthalpy", "[surface][automatic]")
|
||||
{
|
||||
using Fields = dependencies::KeyList<ContinuousDensity, Temperature>;
|
||||
using Rows = dependencies::KeyList<ContinuousDensity>;
|
||||
const auto check = []<typename EOS>(const EOS& equation_of_state)
|
||||
{
|
||||
using Formulation = typename physics::AutomaticPressureSurface<EOS, Fields, Rows>::Formulation;
|
||||
physics::SurfacePoint<Formulation> point{};
|
||||
point.fields.template get<ContinuousDensity>() = 0.4;
|
||||
point.fields.template get<Temperature>() = 2.0;
|
||||
const auto result = physics::evaluate_pressure_surface<Formulation>(equation_of_state, physics::Isobaric{quantities::PressureValue{0.8}}, point);
|
||||
REQUIRE(result);
|
||||
CHECK(result->residual == 0.0);
|
||||
|
||||
constexpr double step = 1.0e-6;
|
||||
auto plus = point;
|
||||
auto minus = point;
|
||||
plus.fields.template get<ContinuousDensity>() += step * 0.3;
|
||||
minus.fields.template get<ContinuousDensity>() -= step * 0.3;
|
||||
plus.fields.template get<Temperature>() -= step * 0.2;
|
||||
minus.fields.template get<Temperature>() += step * 0.2;
|
||||
const auto forward = physics::evaluate_pressure_surface<Formulation>(equation_of_state, physics::Isobaric{quantities::PressureValue{0.8}}, plus);
|
||||
const auto backward = physics::evaluate_pressure_surface<Formulation>(equation_of_state, physics::Isobaric{quantities::PressureValue{0.8}}, minus);
|
||||
REQUIRE(forward);
|
||||
REQUIRE(backward);
|
||||
const double derivative = result->field_derivatives.template get<ContinuousDensity>() * 0.3 - result->field_derivatives.template get<Temperature>() * 0.2;
|
||||
CHECK_THAT((forward->residual - backward->residual) / (2.0 * step), Catch::Matchers::WithinAbs(derivative, 2.0e-9));
|
||||
};
|
||||
check(GasEOS{});
|
||||
check(ForwardOnly{});
|
||||
}
|
||||
|
||||
TEST_CASE("A pressure unknown needs no EOS relations or EOS graph input", "[surface][automatic]")
|
||||
{
|
||||
using Fields = dependencies::KeyList<Pressure>;
|
||||
using Formulation = physics::AutomaticPressureSurface<SymbolicEOS, Fields, Fields>::Formulation;
|
||||
physics::SurfacePoint<Formulation> point{};
|
||||
point.fields.get<Pressure>() = 0.2;
|
||||
const auto single_point = physics::evaluate_pressure_surface<Formulation>(SymbolicEOS{}, physics::Isobaric{quantities::PressureValue{0.2}}, point);
|
||||
REQUIRE(single_point);
|
||||
CHECK(single_point->residual == 0.0);
|
||||
CHECK(single_point->field_derivatives.get<Pressure>() == 1.0);
|
||||
|
||||
const auto inputs = [](const double target)
|
||||
{
|
||||
return dependencies::make_dependency_context(physics::pressure_condition(physics::Isobaric{quantities::PressureValue{target}}), physics::boundary_values<Pressure>({0.0, 0.2, 0.8}));
|
||||
};
|
||||
auto cache = dependencies::make_computation_cache<physics::SurfaceFailure>(physics::make_pressure_surface_graph<Formulation>(), inputs(0.2));
|
||||
REQUIRE(cache.try_prepare(inputs(0.2)));
|
||||
const auto& values = cache.get<physics::keys::Linearization>();
|
||||
REQUIRE(values.size() == 3);
|
||||
CHECK(values[0].residual == -0.2);
|
||||
CHECK(values[1].residual == 0.0);
|
||||
CHECK_THAT(values[2].residual, Catch::Matchers::WithinAbs(0.6, 1.0e-15));
|
||||
for (const auto& value : values)
|
||||
{
|
||||
CHECK(value.field_derivatives.get<Pressure>() == 1.0);
|
||||
}
|
||||
|
||||
REQUIRE(cache.try_prepare(inputs(0.8)));
|
||||
CHECK(cache.get<physics::keys::Linearization>()[2].residual == 0.0);
|
||||
const auto unchanged = cache.try_prepare(inputs(0.8));
|
||||
REQUIRE(unchanged);
|
||||
CHECK(unchanged->computed_count() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Automatic pressure-field boundaries preserve interior rows and exact transpose actions", "[surface][automatic][backend]")
|
||||
{
|
||||
namespace fixture = serif::tests::surface::boundary;
|
||||
namespace coefficients = operators::backend::mfem;
|
||||
using Fields = dependencies::KeyList<Pressure>;
|
||||
using Formulation = physics::AutomaticPressureSurface<SymbolicEOS, Fields, Fields>::Formulation;
|
||||
|
||||
fixture::Fixture mesh;
|
||||
discretization::backend::mfem::TermBinding<Pressure, fixture::Schema> binding{mesh.linear_space};
|
||||
const ::mfem::Vector pressure = fixture::sequence(binding.size(), 0.4, 0.2);
|
||||
const ::mfem::Vector direction = fixture::sequence(binding.size(), -0.1, 0.7);
|
||||
const ::mfem::Vector dual = fixture::sequence(binding.size(), 0.3, 0.4);
|
||||
const auto state = coefficients::make_term_coefficients(coefficients::bind(binding, pressure));
|
||||
const auto directions = coefficients::make_term_coefficients(coefficients::bind(binding, direction));
|
||||
const auto boundary = backend::make_pressure_boundary<Formulation>(state);
|
||||
const SymbolicEOS equation_of_state;
|
||||
const auto eos_input = dependencies::borrow<physics::keys::EquationOfState>(equation_of_state, dependencies::track_by([](const SymbolicEOS&)
|
||||
{
|
||||
return 0;
|
||||
}));
|
||||
const auto inputs = backend::make_pressure_boundary_inputs(boundary, eos_input, physics::Isobaric{quantities::PressureValue{0.2}}, state);
|
||||
auto cache = dependencies::make_computation_cache<physics::SurfaceFailure>(backend::make_pressure_boundary_graph<Formulation>(), inputs);
|
||||
REQUIRE(cache.try_prepare(inputs));
|
||||
|
||||
const auto& linearization = cache.get<physics::keys::Linearization>();
|
||||
::mfem::Vector residual(binding.size()), action(binding.size()), transpose(binding.size());
|
||||
residual = 4.0;
|
||||
action = 0.0;
|
||||
transpose = 0.0;
|
||||
backend::apply_residual_rows(*boundary, linearization, residual);
|
||||
backend::apply_jacobian_rows(*boundary, linearization, directions, action);
|
||||
backend::add_jacobian_transpose_rows<Pressure>(*boundary, linearization, dual, transpose);
|
||||
const ::mfem::Vector selected = fixture::boundary_mask(binding);
|
||||
|
||||
for (int row = 0; row < binding.size(); ++row)
|
||||
{
|
||||
if (selected[row] != 0.0)
|
||||
{
|
||||
CHECK_THAT(residual[row], Catch::Matchers::WithinAbs(pressure[row] - 0.2, 1.0e-14));
|
||||
CHECK_THAT(action[row], Catch::Matchers::WithinAbs(direction[row], 1.0e-14));
|
||||
CHECK_THAT(transpose[row], Catch::Matchers::WithinAbs(dual[row], 1.0e-14));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(residual[row] == 4.0);
|
||||
CHECK(action[row] == 0.0);
|
||||
CHECK(transpose[row] == 0.0);
|
||||
}
|
||||
}
|
||||
CHECK_THAT(action * dual, Catch::Matchers::WithinAbs(direction * transpose, 1.0e-14));
|
||||
}
|
||||
Reference in New Issue
Block a user