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:
381
tests/deformation/geometry_step.cpp
Normal file
381
tests/deformation/geometry_step.cpp
Normal file
@@ -0,0 +1,381 @@
|
||||
// Ported from MeanField tests/deformation/safe_newton_step.cpp, with serial input and sampling regressions.
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include "mfem/fem/coefficient.hpp"
|
||||
#include "mfem/fem/fe_coll.hpp"
|
||||
#include "mfem/fem/gridfunc.hpp"
|
||||
#include "mfem/mesh/mesh.hpp"
|
||||
#include "serif/backend/mfem/geometry/deformation/geometry_step.hpp"
|
||||
#include "serif/geometry/deformation/detail/determinant_polynomial.hpp"
|
||||
#include "serif/backend/mfem/mapping/domain_mapper.hpp"
|
||||
|
||||
namespace {
|
||||
namespace deformation = serif::geometry::deformation;
|
||||
namespace backend = deformation::backend::mfem;
|
||||
namespace mapping = serif::mapping;
|
||||
using Catch::Matchers::WithinAbs;
|
||||
|
||||
constexpr int dimension = 3;
|
||||
|
||||
/** @brief Keep unit element widths so the mapped determinant has an independent analytic value. */
|
||||
[[nodiscard]] mfem::Mesh make_mesh(const bool compactified, const bool nonconforming = false) {
|
||||
mfem::Mesh mesh = mfem::Mesh::MakeCartesian3D(2, 1, 1, mfem::Element::HEXAHEDRON, 2.0, 1.0, 1.0);
|
||||
if (nonconforming) {
|
||||
mesh.EnsureNCMesh();
|
||||
mfem::Array<int> selected{0};
|
||||
mesh.GeneralRefinement(selected);
|
||||
mesh.SetCurvature(6);
|
||||
mesh.Transform([](const mfem::Vector& x, mfem::Vector& y) {
|
||||
y = x;
|
||||
y[0] += 0.03 * x[0] * x[1];
|
||||
y[1] += 0.02 * x[1] * x[2];
|
||||
y[2] += 0.01 * x[0] * x[2];
|
||||
});
|
||||
}
|
||||
for (int element = 0; element < mesh.GetNE(); ++element) {
|
||||
mesh.GetElement(element)->SetAttribute(compactified ? 2 : 1);
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
|
||||
/** @brief Reuse the physical Kelvin implementation in both interior and vacuum tests. */
|
||||
[[nodiscard]] std::unique_ptr<const mapping::compactification::ExteriorDomainMap> make_exterior_map() {
|
||||
return std::make_unique<mapping::compactification::KelvinCompactification>(serif::utils::user_options::KelvinCompactificationOptions{.r_star_ref = 1.0, .r_inf_ref = 4.0});
|
||||
}
|
||||
|
||||
struct GeometryFixture {
|
||||
mfem::Mesh mesh;
|
||||
mfem::H1_FECollection displacement_collection;
|
||||
mfem::FiniteElementSpace displacement_space;
|
||||
mfem::H1_FECollection compactification_collection;
|
||||
mfem::FiniteElementSpace compactification_space;
|
||||
mfem::GridFunction compactification_coordinate;
|
||||
mapping::DomainMapper mapper;
|
||||
|
||||
/** @brief Vary global ordering and interpolation degree without changing the reference geometry. */
|
||||
explicit GeometryFixture(const bool compactified = false, const mfem::Ordering::Type ordering = mfem::Ordering::byNODES, const int degree = 1, const bool nonconforming = false) :
|
||||
mesh(make_mesh(compactified, nonconforming)),
|
||||
displacement_collection(degree, dimension),
|
||||
displacement_space(&mesh, &displacement_collection, dimension, ordering),
|
||||
compactification_collection(1, dimension),
|
||||
compactification_space(&mesh, &compactification_collection),
|
||||
compactification_coordinate(&compactification_space),
|
||||
mapper({.dimension = dimension, .vacuum_element_attribute_ID = 2}, make_exterior_map()) {
|
||||
compactification_coordinate = 0.0;
|
||||
}
|
||||
|
||||
/** @brief Provide an accepted identity map in true-DOF ordering. */
|
||||
[[nodiscard]] mfem::Vector zero_displacement() const {
|
||||
mfem::Vector result(displacement_space.GetTrueVSize());
|
||||
result = 0.0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @brief Construct directions independently of the geometry-step element extraction. */
|
||||
template <typename Function>
|
||||
[[nodiscard]] mfem::Vector project_direction(Function&& function) {
|
||||
mfem::VectorFunctionCoefficient coefficient(dimension, std::forward<Function>(function));
|
||||
mfem::GridFunction field(&displacement_space);
|
||||
field.ProjectCoefficient(coefficient);
|
||||
mfem::Vector result;
|
||||
field.GetTrueDofs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @brief Supply explicit sample rules for every element instead of choosing a hidden quadrature policy. */
|
||||
[[nodiscard]] std::vector<backend::GeometrySampleRule> sample_rules() {
|
||||
std::vector<backend::GeometrySampleRule> result;
|
||||
for (int element = 0; element < mesh.GetNE(); ++element) {
|
||||
result.push_back({.element = element, .integration_rule = &mfem::IntRules.Get(mesh.GetElementBaseGeometry(element), 2)});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief Give det(J(alpha)) = 1 - 2 alpha. */
|
||||
void compress_x(const mfem::Vector& position, mfem::Vector& value) {
|
||||
value.SetSize(dimension);
|
||||
value = 0.0;
|
||||
value(0) = -2.0 * position(0);
|
||||
}
|
||||
|
||||
/** @brief Give a tangent singularity at one half while both endpoints remain positive. */
|
||||
void compress_x_and_y(const mfem::Vector& position, mfem::Vector& value) {
|
||||
compress_x(position, value);
|
||||
value(1) = -2.0 * position(1);
|
||||
}
|
||||
|
||||
/** @brief Provide a path with no geometry obstruction at positive steps. */
|
||||
void expand_x(const mfem::Vector& position, mfem::Vector& value) {
|
||||
value.SetSize(dimension);
|
||||
value = 0.0;
|
||||
value(0) = position(0);
|
||||
}
|
||||
|
||||
class NonAffineExteriorMap final : public mapping::compactification::ExteriorDomainMap {
|
||||
public:
|
||||
/** @brief Declare that a cubic determinant model cannot certify this map. */
|
||||
[[nodiscard]] bool is_affine_in_displacement() const noexcept override {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @brief Fail loudly if validation mistakenly reaches exterior evaluation. */
|
||||
[[nodiscard]] mapping::MappingStatus evaluate(const mapping::compactification::ExteriorMapInput&, mapping::compactification::ExteriorMapResult&) const override {
|
||||
FAIL("A non-affine exterior map must be rejected before evaluation.");
|
||||
return mapping::MappingStatus::NON_FINITE_MAPPING_RESULT;
|
||||
}
|
||||
|
||||
/** @brief Fail loudly if validation mistakenly reaches exterior differentiation. */
|
||||
[[nodiscard]] mapping::MappingStatus evaluate_variation(const mapping::compactification::ExteriorMapInput&, const mapping::compactification::ExteriorMapResult&, const mapping::compactification::ExteriorMapDirection&, mapping::compactification::ExteriorMapVariation&) const override {
|
||||
FAIL("A non-affine exterior map must be rejected before differentiation.");
|
||||
return mapping::MappingStatus::NON_FINITE_MAPPING_RESULT;
|
||||
}
|
||||
|
||||
/** @brief Identify the deliberately unsupported model in diagnostics. */
|
||||
[[nodiscard]] std::string_view get_name() const noexcept override {
|
||||
return "NonAffineExteriorMap";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("Geometry step finds the first mapping boundary for both global DOF orderings", "[unit][deformation][geometry_step]") {
|
||||
for (const mfem::Ordering::Type ordering : {mfem::Ordering::byNODES, mfem::Ordering::byVDIM}) {
|
||||
GeometryFixture fixture(false, ordering);
|
||||
const mfem::Vector accepted = fixture.zero_displacement();
|
||||
const mfem::Vector direction = fixture.project_direction(compress_x);
|
||||
const auto rules = fixture.sample_rules();
|
||||
const auto result = backend::estimate_geometry_step_limit(fixture.mapper, fixture.displacement_space, fixture.compactification_coordinate, accepted, direction, rules, {.fraction_to_boundary = 0.8});
|
||||
|
||||
REQUIRE(result);
|
||||
CHECK(result->limited_by_geometry);
|
||||
CHECK_THAT(result->boundary_step, WithinAbs(0.5, 2.0e-13));
|
||||
CHECK_THAT(result->step, WithinAbs(0.4, 2.0e-13));
|
||||
CHECK_THAT(result->minimum_determinant_at_accepted, WithinAbs(1.0, 2.0e-13));
|
||||
CHECK_THAT(result->minimum_determinant_at_maximum, WithinAbs(-1.0, 2.0e-13));
|
||||
CHECK_THAT(result->limiting_point_determinant_at_step, WithinAbs(0.2, 2.0e-13));
|
||||
CHECK(result->sampled_point_count > 0);
|
||||
CHECK(result->limiting_element >= 0);
|
||||
CHECK(result->limiting_rule >= 0);
|
||||
CHECK(result->limiting_point >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Geometry step detects a tangent singularity before an admissible endpoint", "[unit][deformation][geometry_step]") {
|
||||
GeometryFixture fixture(true);
|
||||
const mfem::Vector accepted = fixture.zero_displacement();
|
||||
const mfem::Vector direction = fixture.project_direction(compress_x_and_y);
|
||||
const auto rules = fixture.sample_rules();
|
||||
const auto result = backend::estimate_geometry_step_limit(fixture.mapper, fixture.displacement_space, fixture.compactification_coordinate, accepted, direction, rules);
|
||||
|
||||
REQUIRE(result);
|
||||
CHECK(result->limited_by_geometry);
|
||||
CHECK_THAT(result->minimum_determinant_at_maximum, WithinAbs(1.0, 3.0e-13));
|
||||
CHECK_THAT(result->boundary_step, WithinAbs(0.5, 3.0e-13));
|
||||
CHECK_THAT(result->step, WithinAbs(0.45, 3.0e-13));
|
||||
CHECK_THAT(result->limiting_point_determinant_at_step, WithinAbs(0.01, 3.0e-13));
|
||||
}
|
||||
|
||||
TEST_CASE("Geometry step preserves constrained displacement and all samples on curved NC geometry", "[unit][deformation][geometry_step][geometry_reuse]") {
|
||||
for (const bool compactified : {false, true}) {
|
||||
for (const mfem::Ordering::Type ordering : {mfem::Ordering::byNODES, mfem::Ordering::byVDIM}) {
|
||||
GeometryFixture fixture(compactified, ordering, 3, true);
|
||||
REQUIRE(fixture.displacement_space.GetTrueVSize() < fixture.displacement_space.GetVSize());
|
||||
const auto accepted = fixture.zero_displacement();
|
||||
const auto direction = fixture.project_direction(compress_x);
|
||||
auto rules = fixture.sample_rules();
|
||||
std::size_t expected_samples = 0;
|
||||
for (int element = 0; element < fixture.mesh.GetNE(); ++element) {
|
||||
rules.push_back({.element = element, .integration_rule = &mfem::IntRules.Get(fixture.mesh.GetElementBaseGeometry(element), 4)});
|
||||
}
|
||||
for (const auto& rule : rules) { expected_samples += rule.integration_rule->GetNPoints(); }
|
||||
const auto result = backend::estimate_geometry_step_limit(fixture.mapper, fixture.displacement_space,
|
||||
fixture.compactification_coordinate, accepted, direction, rules, {.fraction_to_boundary = 0.8});
|
||||
REQUIRE(result);
|
||||
CHECK(result->sampled_point_count == expected_samples);
|
||||
CHECK(result->limited_by_geometry);
|
||||
CHECK_THAT(result->boundary_step, WithinAbs(0.5, 3.0e-12));
|
||||
CHECK_THAT(result->step, WithinAbs(0.4, 3.0e-12));
|
||||
CHECK_THAT(result->minimum_determinant_at_accepted, WithinAbs(1.0, 3.0e-12));
|
||||
CHECK_THAT(result->minimum_determinant_at_maximum, WithinAbs(-1.0, 3.0e-12));
|
||||
CHECK_THAT(result->limiting_point_determinant_at_step, WithinAbs(0.2, 3.0e-12));
|
||||
REQUIRE(result->limiting_rule >= 0);
|
||||
REQUIRE(static_cast<std::size_t>(result->limiting_rule) < rules.size());
|
||||
CHECK(result->limiting_element == rules[result->limiting_rule].element);
|
||||
CHECK(result->limiting_point >= 0);
|
||||
CHECK(result->limiting_point < rules[result->limiting_rule].integration_rule->GetNPoints());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Geometry step honors a positive determinant floor", "[unit][deformation][geometry_step]") {
|
||||
GeometryFixture fixture;
|
||||
const mfem::Vector accepted = fixture.zero_displacement();
|
||||
const mfem::Vector direction = fixture.project_direction(compress_x);
|
||||
const auto rules = fixture.sample_rules();
|
||||
const auto result = backend::estimate_geometry_step_limit(fixture.mapper, fixture.displacement_space, fixture.compactification_coordinate, accepted, direction, rules, {.determinant_floor = 0.25, .fraction_to_boundary = 0.8});
|
||||
|
||||
REQUIRE(result);
|
||||
CHECK(result->limited_by_geometry);
|
||||
CHECK_THAT(result->boundary_step, WithinAbs(0.375, 2.0e-13));
|
||||
CHECK_THAT(result->step, WithinAbs(0.3, 2.0e-13));
|
||||
CHECK(result->limiting_point_determinant_at_step > 0.25);
|
||||
}
|
||||
|
||||
TEST_CASE("Geometry step leaves an unconstrained step unchanged", "[unit][deformation][geometry_step]") {
|
||||
GeometryFixture fixture;
|
||||
const mfem::Vector accepted = fixture.zero_displacement();
|
||||
const mfem::Vector direction = fixture.project_direction(expand_x);
|
||||
const auto rules = fixture.sample_rules();
|
||||
const auto result = backend::estimate_geometry_step_limit(fixture.mapper, fixture.displacement_space, fixture.compactification_coordinate, accepted, direction, rules);
|
||||
|
||||
REQUIRE(result);
|
||||
CHECK_FALSE(result->limited_by_geometry);
|
||||
CHECK_THAT(result->boundary_step, WithinAbs(1.0, 2.0e-13));
|
||||
CHECK_THAT(result->step, WithinAbs(1.0, 2.0e-13));
|
||||
CHECK_THAT(result->minimum_determinant_at_maximum, WithinAbs(2.0, 2.0e-13));
|
||||
CHECK_THAT(result->limiting_point_determinant_at_step, WithinAbs(2.0, 2.0e-13));
|
||||
CHECK(result->limiting_element == -1);
|
||||
CHECK(result->limiting_rule == -1);
|
||||
CHECK(result->limiting_point == -1);
|
||||
}
|
||||
|
||||
TEST_CASE("Geometry step rescales the polynomial to the requested maximum step", "[unit][deformation][geometry_step]") {
|
||||
GeometryFixture fixture;
|
||||
const mfem::Vector zero = fixture.zero_displacement();
|
||||
const mfem::Vector direction = fixture.project_direction(compress_x);
|
||||
const auto rules = fixture.sample_rules();
|
||||
const auto result = backend::estimate_geometry_step_limit(fixture.mapper, fixture.displacement_space, fixture.compactification_coordinate, zero, direction, rules, {.maximum_step = 0.25});
|
||||
REQUIRE(result);
|
||||
CHECK_FALSE(result->limited_by_geometry);
|
||||
CHECK(result->step == 0.25);
|
||||
CHECK_THAT(result->minimum_determinant_at_maximum, WithinAbs(0.5, 2.0e-13));
|
||||
|
||||
const auto endpoint = backend::estimate_geometry_step_limit(fixture.mapper, fixture.displacement_space, fixture.compactification_coordinate, zero, direction, rules, {.maximum_step = 0.5});
|
||||
REQUIRE(endpoint);
|
||||
CHECK(endpoint->limited_by_geometry);
|
||||
CHECK_THAT(endpoint->step, WithinAbs(0.45, 2.0e-13));
|
||||
}
|
||||
|
||||
TEST_CASE("Geometry step rejects invalid inputs as values", "[unit][deformation][geometry_step]") {
|
||||
GeometryFixture fixture;
|
||||
mfem::Vector zero = fixture.zero_displacement();
|
||||
auto rules = fixture.sample_rules();
|
||||
const auto estimate = [&](const deformation::GeometryStepOptions options = {}) {
|
||||
return backend::estimate_geometry_step_limit(fixture.mapper, fixture.displacement_space, fixture.compactification_coordinate, zero, zero, rules, options);
|
||||
};
|
||||
|
||||
SECTION("Invalid numerical controls") {
|
||||
CHECK_FALSE(estimate({.maximum_step = 0.0}));
|
||||
CHECK_FALSE(estimate({.maximum_step = std::numeric_limits<double>::infinity()}));
|
||||
CHECK_FALSE(estimate({.determinant_floor = -1.0}));
|
||||
CHECK_FALSE(estimate({.fraction_to_boundary = 0.0}));
|
||||
CHECK_FALSE(estimate({.fraction_to_boundary = 1.0}));
|
||||
CHECK_FALSE(estimate({.fraction_to_boundary = std::numeric_limits<double>::quiet_NaN()}));
|
||||
}
|
||||
SECTION("Empty or invalid rules") {
|
||||
rules.clear();
|
||||
CHECK_FALSE(estimate());
|
||||
rules.push_back({.element = 0, .integration_rule = nullptr});
|
||||
CHECK_FALSE(estimate());
|
||||
rules = fixture.sample_rules();
|
||||
rules[0].element = fixture.mesh.GetNE();
|
||||
CHECK_FALSE(estimate());
|
||||
}
|
||||
SECTION("Invalid coefficients") {
|
||||
zero.SetSize(1);
|
||||
CHECK_FALSE(estimate());
|
||||
zero = fixture.zero_displacement();
|
||||
zero(0) = std::numeric_limits<double>::quiet_NaN();
|
||||
CHECK_FALSE(estimate());
|
||||
zero = fixture.zero_displacement();
|
||||
fixture.compactification_coordinate(0) = std::numeric_limits<double>::infinity();
|
||||
CHECK_FALSE(estimate());
|
||||
}
|
||||
SECTION("Folded accepted state") {
|
||||
const mfem::Vector invalid_accepted = fixture.project_direction(compress_x);
|
||||
const auto result = backend::estimate_geometry_step_limit(fixture.mapper, fixture.displacement_space, fixture.compactification_coordinate, invalid_accepted, zero, rules);
|
||||
REQUIRE_FALSE(result);
|
||||
CHECK(result.error().reason == deformation::DeformationFailureReason::invalid_geometry);
|
||||
CHECK(result.error().element >= 0);
|
||||
CHECK(result.error().point >= 0);
|
||||
}
|
||||
SECTION("Accepted state must lie strictly above the floor") {
|
||||
const auto result = estimate({.determinant_floor = 1.0});
|
||||
REQUIRE_FALSE(result);
|
||||
CHECK(result.error().reason == deformation::DeformationFailureReason::invalid_geometry);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Geometry step rejects a non-affine exterior model before evaluation", "[unit][deformation][geometry_step]") {
|
||||
GeometryFixture fixture(true);
|
||||
mapping::DomainMapper mapper({.dimension = dimension, .vacuum_element_attribute_ID = 2}, std::make_unique<NonAffineExteriorMap>());
|
||||
const mfem::Vector zero = fixture.zero_displacement();
|
||||
const auto rules = fixture.sample_rules();
|
||||
const auto result = backend::estimate_geometry_step_limit(mapper, fixture.displacement_space, fixture.compactification_coordinate, zero, zero, rules);
|
||||
REQUIRE_FALSE(result);
|
||||
CHECK(result.error().reason == deformation::DeformationFailureReason::invalid_configuration);
|
||||
}
|
||||
|
||||
TEST_CASE("Geometry step retains the union of nonnested sampling rules", "[unit][deformation][geometry_step]") {
|
||||
GeometryFixture fixture(false, mfem::Ordering::byVDIM, 3);
|
||||
const mfem::Vector zero = fixture.zero_displacement();
|
||||
const mfem::Vector direction = fixture.project_direction([](const mfem::Vector& position, mfem::Vector& value) {
|
||||
value.SetSize(dimension);
|
||||
value = 0.0;
|
||||
const double centered_x = position(0) - 0.5;
|
||||
value(0) = -2.0 * position(0) + 8.0 * centered_x * centered_x * centered_x;
|
||||
});
|
||||
const std::array rules{
|
||||
backend::GeometrySampleRule{.element = 0, .integration_rule = &mfem::IntRules.Get(mfem::Geometry::CUBE, 1)},
|
||||
backend::GeometrySampleRule{.element = 0, .integration_rule = &mfem::IntRules.Get(mfem::Geometry::CUBE, 3)}
|
||||
};
|
||||
|
||||
// The cubic direction compresses at the central point, while its x derivative vanishes at the two-point Gauss abscissae.
|
||||
const auto highest_only = backend::estimate_geometry_step_limit(fixture.mapper, fixture.displacement_space, fixture.compactification_coordinate, zero, direction, std::span(rules).subspan(1));
|
||||
const auto both = backend::estimate_geometry_step_limit(fixture.mapper, fixture.displacement_space, fixture.compactification_coordinate, zero, direction, rules);
|
||||
REQUIRE(highest_only);
|
||||
REQUIRE(both);
|
||||
CHECK_FALSE(highest_only->limited_by_geometry);
|
||||
CHECK(both->limited_by_geometry);
|
||||
CHECK_THAT(both->boundary_step, WithinAbs(0.5, 2.0e-13));
|
||||
CHECK(both->limiting_rule == 0);
|
||||
CHECK(both->sampled_point_count == static_cast<std::size_t>(rules[0].integration_rule->GetNPoints() + rules[1].integration_rule->GetNPoints()));
|
||||
}
|
||||
|
||||
TEST_CASE("Determinant polynomial preserves first roots and dimensional degree", "[unit][deformation][geometry_step]") {
|
||||
using deformation::detail::DeterminantPolynomial;
|
||||
using deformation::detail::JacobianEntries;
|
||||
using deformation::detail::determinant_polynomial;
|
||||
using deformation::detail::evaluate_polynomial;
|
||||
using deformation::detail::first_boundary_parameter;
|
||||
|
||||
JacobianEntries base{};
|
||||
JacobianEntries direction{};
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
base[component][component] = 1.0;
|
||||
direction[component][component] = -2.0;
|
||||
}
|
||||
for (int spatial_dimension = 1; spatial_dimension <= 3; ++spatial_dimension) {
|
||||
const DeterminantPolynomial coefficients = determinant_polynomial(base, direction, spatial_dimension, 0.75, 0.125);
|
||||
for (const double parameter : {0.0, 0.2, 0.6, 1.0}) {
|
||||
const double expected = std::pow(1.0 - 1.5 * parameter, spatial_dimension) - 0.125;
|
||||
CHECK_THAT(evaluate_polynomial(coefficients, parameter), WithinAbs(expected, 2.0e-15));
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(std::isinf(first_boundary_parameter({1.0, 0.0, 0.0, 0.0})));
|
||||
CHECK(std::isinf(first_boundary_parameter({1.0, 2.0, 3.0, 4.0})));
|
||||
CHECK_THAT(first_boundary_parameter({1.0, -2.0, 0.0, 0.0}), WithinAbs(0.5, 2.0e-13));
|
||||
CHECK_THAT(first_boundary_parameter({1.0, -4.0, 4.0, 0.0}), WithinAbs(0.5, 2.0e-13));
|
||||
CHECK_THAT(first_boundary_parameter({1.0, -5.5, 8.5, -3.0}), WithinAbs(1.0 / 3.0, 2.0e-13));
|
||||
CHECK_THAT(first_boundary_parameter({1.0e300, -4.0e300, 4.0e300, 0.0}), WithinAbs(0.5, 2.0e-13));
|
||||
CHECK_THAT(first_boundary_parameter({1.0e300, -5.5e300, 8.5e300, -3.0e300}), WithinAbs(1.0 / 3.0, 2.0e-13));
|
||||
}
|
||||
Reference in New Issue
Block a user