Select Git revision
test_AffectationProcessor.cpp
test_PolynomialReconstruction.cpp 23.02 KiB
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_all.hpp>
#include <Kokkos_Core.hpp>
#include <utils/PugsAssert.hpp>
#include <utils/Types.hpp>
#include <algebra/SmallMatrix.hpp>
#include <algebra/SmallVector.hpp>
#include <mesh/Mesh.hpp>
#include <mesh/MeshDataManager.hpp>
#include <scheme/DiscreteFunctionP0.hpp>
#include <scheme/DiscreteFunctionVariant.hpp>
#include <scheme/PolynomialReconstruction.hpp>
#include <MeshDataBaseForTests.hpp>
template <typename... DiscreteFunctionT>
std::vector<std::shared_ptr<const DiscreteFunctionVariant>>
build_list(DiscreteFunctionT... input)
{
std::vector<std::shared_ptr<const DiscreteFunctionVariant>> variant_vector;
auto convert = [&variant_vector](auto&& df) {
using DF_T = std::decay_t<decltype(df)>;
if constexpr (is_discrete_function_v<DF_T> or std::is_same_v<DiscreteFunctionVariant, DF_T>) {
variant_vector.push_back(std::make_shared<DiscreteFunctionVariant>(df));
} else if constexpr (is_shared_ptr_v<DF_T>) {
using DF_Value_T = std::decay_t<typename DF_T::element_type>;
if constexpr (is_discrete_function_v<DF_Value_T> or std::is_same_v<DiscreteFunctionVariant, DF_Value_T>) {
variant_vector.push_back(std::make_shared<DiscreteFunctionVariant>(*df));
} else {
static_assert(is_false_v<DF_T>, "unexpected type");
}
} else {
static_assert(is_false_v<DF_T>, "unexpected type");
}
};
(convert(input), ...);
return variant_vector;
}
// clazy:excludeall=non-pod-global-static
TEST_CASE("PolynomialReconstruction", "[scheme]")
{
SECTION("1D")
{
using R1 = TinyVector<1>;
SECTION("R data")
{
for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) {
SECTION(named_mesh.name())
{
auto p_mesh = named_mesh.mesh()->get<Mesh<1>>();
auto& mesh = *p_mesh;
auto affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; };
auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
DiscreteFunctionP0<double> fh{p_mesh};
parallel_for(
mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
DiscreteFunctionVariant fh_v(fh);
std::shared_ptr<const DiscreteFunctionVariant> p_fh_v = std::make_shared<DiscreteFunctionVariant>(fh);
auto reconstructions = PolynomialReconstruction{}.build(build_list(p_fh_v, fh_v, fh));
auto dpk = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>();
{
double max_mean_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
max_mean_error = std::max(max_mean_error, std::abs(dpk[cell_id](xj[cell_id]) - affine(xj[cell_id])));
}
REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14));
}
{
double max_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const double reconstructed_slope =
(dpk[cell_id](R1{0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R1{0.1})) / 0.2;
max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7));
}
REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14));
}
}
}
}
SECTION("R^d data")
{
using R3 = TinyVector<3>;
for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) {
SECTION(named_mesh.name())
{
auto p_mesh = named_mesh.mesh()->get<Mesh<1>>();
auto& mesh = *p_mesh;
auto affine = [](const R1& x) -> R3 {
return R3{+2.3 + 1.7 * x[0], //
+1.4 - 0.6 * x[0], //
-0.2 + 3.1 * x[0]};
};
auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
DiscreteFunctionP0<R3> fh{p_mesh};
parallel_for(
mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
PolynomialReconstruction reconstruction;
auto dpk = reconstruction.build(mesh, fh);
{
double max_mean_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
max_mean_error = std::max(max_mean_error, l2Norm(dpk[cell_id](xj[cell_id]) - affine(xj[cell_id])));
}
REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14));
}
{
double max_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const R3 reconstructed_slope =
(1 / 0.2) * (dpk[cell_id](R1{0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R1{0.1}));
max_slope_error = std::max(max_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1}));
}
REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14));
}
}
}
}
SECTION("R^mn data")
{
using R32 = TinyMatrix<3, 2>;
for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) {
SECTION(named_mesh.name())
{
auto p_mesh = named_mesh.mesh()->get<Mesh<1>>();
auto& mesh = *p_mesh;
auto affine = [](const R1& x) -> R32 {
return R32{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], //
+1.4 - 0.6 * x[0], +2.4 - 2.3 * x[0], //
-0.2 + 3.1 * x[0], -3.2 - 3.6 * x[0]};
};
auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
DiscreteFunctionP0<R32> fh{p_mesh};
parallel_for(
mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
PolynomialReconstruction reconstruction;
auto dpk = reconstruction.build(mesh, fh);
{
double max_mean_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
max_mean_error = std::max(max_mean_error, frobeniusNorm(dpk[cell_id](xj[cell_id]) - affine(xj[cell_id])));
}
REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13));
}
{
double max_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const R32 reconstructed_slope =
(1 / 0.2) * (dpk[cell_id](R1{0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R1{0.1}));
max_slope_error = std::max(max_slope_error, frobeniusNorm(reconstructed_slope - R32{+1.7, +2.1, //
-0.6, -2.3, //
+3.1, -3.6}));
}
REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13));
}
}
}
}
}
SECTION("2D")
{
using R2 = TinyVector<2>;
SECTION("R data")
{
for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) {
SECTION(named_mesh.name())
{
auto p_mesh = named_mesh.mesh()->get<Mesh<2>>();
auto& mesh = *p_mesh;
auto affine = [](const R2& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1]; };
auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
DiscreteFunctionP0<double> fh{p_mesh};
parallel_for(
mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
PolynomialReconstruction reconstruction;
auto dpk = reconstruction.build(mesh, fh);
{
double max_mean_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
max_mean_error = std::max(max_mean_error, std::abs(dpk[cell_id](xj[cell_id]) - affine(xj[cell_id])));
}
REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14));
}
{
double max_x_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const double reconstructed_slope =
(dpk[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R2{0.1, 0})) / 0.2;
max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7));
}
REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-14));
}
{
double max_y_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const double reconstructed_slope =
(dpk[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R2{0, 0.1})) / 0.2;
max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3)));
}
REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-14));
}
}
}
}
SECTION("R^d data")
{
using R3 = TinyVector<3>;
for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) {
SECTION(named_mesh.name())
{
auto p_mesh = named_mesh.mesh()->get<Mesh<2>>();
auto& mesh = *p_mesh;
auto affine = [](const R2& x) -> R3 {
return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1], //
+1.4 - 0.6 * x[0] + 1.3 * x[1], //
-0.2 + 3.1 * x[0] - 1.1 * x[1]};
};
auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
DiscreteFunctionP0<R3> fh{p_mesh};
parallel_for(
mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
PolynomialReconstruction reconstruction;
auto dpk = reconstruction.build(mesh, fh);
{
double max_mean_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
max_mean_error = std::max(max_mean_error, l2Norm(dpk[cell_id](xj[cell_id]) - affine(xj[cell_id])));
}
REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14));
}
{
double max_x_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const R3 reconstructed_slope =
(1 / 0.2) * (dpk[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R2{0.1, 0}));
max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1}));
}
REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-14));
}
{
double max_y_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const R3 reconstructed_slope =
(1 / 0.2) * (dpk[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R2{0, 0.1}));
max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1}));
}
REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-14));
}
}
}
}
SECTION("R^mn data")
{
using R32 = TinyMatrix<3, 2>;
for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) {
SECTION(named_mesh.name())
{
auto p_mesh = named_mesh.mesh()->get<Mesh<2>>();
auto& mesh = *p_mesh;
auto affine = [](const R2& x) -> R32 {
return R32{+2.3 + 1.7 * x[0] + 1.2 * x[1], -1.7 + 2.1 * x[0] - 2.2 * x[1], //
+1.4 - 0.6 * x[0] - 2.1 * x[1], +2.4 - 2.3 * x[0] + 1.3 * x[1], //
-0.2 + 3.1 * x[0] + 0.8 * x[1], -3.2 - 3.6 * x[0] - 1.7 * x[1]};
};
auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
DiscreteFunctionP0<R32> fh{p_mesh};
parallel_for(
mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
PolynomialReconstruction reconstruction;
auto dpk = reconstruction.build(mesh, fh);
{
double max_mean_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
max_mean_error = std::max(max_mean_error, frobeniusNorm(dpk[cell_id](xj[cell_id]) - affine(xj[cell_id])));
}
REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13));
}
{
double max_x_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const R32 reconstructed_slope =
(1 / 0.2) * (dpk[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R2{0.1, 0}));
max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R32{+1.7, 2.1, //
-0.6, -2.3, //
+3.1, -3.6}));
}
REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12));
}
{
double max_y_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const R32 reconstructed_slope =
(1 / 0.2) * (dpk[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R2{0, 0.1}));
max_y_slope_error = std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R32{+1.2, -2.2, //
-2.1, 1.3, //
+0.8, -1.7}));
}
REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12));
}
}
}
}
}
SECTION("3D")
{
using R3 = TinyVector<3>;
SECTION("R data")
{
for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) {
SECTION(named_mesh.name())
{
auto p_mesh = named_mesh.mesh()->get<Mesh<3>>();
auto& mesh = *p_mesh;
auto affine = [](const R3& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1] + 2.1 * x[2]; };
auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
DiscreteFunctionP0<double> fh{p_mesh};
parallel_for(
mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
PolynomialReconstruction reconstruction;
auto dpk = reconstruction.build(mesh, fh);
{
double max_mean_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
max_mean_error = std::max(max_mean_error, std::abs(dpk[cell_id](xj[cell_id]) - affine(xj[cell_id])));
}
REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14));
}
{
double max_x_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const double reconstructed_slope =
(dpk[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0.1, 0, 0})) / 0.2;
max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7));
}
REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-14));
}
{
double max_y_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const double reconstructed_slope =
(dpk[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0, 0.1, 0})) / 0.2;
max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3)));
}
REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-14));
}
{
double max_z_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const double reconstructed_slope =
(dpk[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0, 0, 0.1})) / 0.2;
max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - 2.1));
}
REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-14));
}
}
}
}
SECTION("R^d data")
{
using R4 = TinyVector<4>;
for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) {
SECTION(named_mesh.name())
{
auto p_mesh = named_mesh.mesh()->get<Mesh<3>>();
auto& mesh = *p_mesh;
auto affine = [](const R3& x) -> R4 {
return R4{+2.3 + 1.7 * x[0] - 2.2 * x[1] + 1.8 * x[2], //
+1.4 - 0.6 * x[0] + 1.3 * x[1] - 3.7 * x[2], //
-0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2], //
-1.2 - 1.5 * x[0] - 0.1 * x[1] - 1.6 * x[2]};
};
auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
DiscreteFunctionP0<R4> fh{p_mesh};
parallel_for(
mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
PolynomialReconstruction reconstruction;
auto dpk = reconstruction.build(mesh, fh);
{
double max_mean_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
max_mean_error = std::max(max_mean_error, l2Norm(dpk[cell_id](xj[cell_id]) - affine(xj[cell_id])));
}
REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14));
}
{
double max_x_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const R4 reconstructed_slope =
(1 / 0.2) * (dpk[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0.1, 0, 0}));
max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R4{1.7, -0.6, 3.1, -1.5}));
}
REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-13));
}
{
double max_y_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const R4 reconstructed_slope =
(1 / 0.2) * (dpk[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0, 0.1, 0}));
max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R4{-2.2, 1.3, -1.1, -0.1}));
}
REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-13));
}
{
double max_z_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const R4 reconstructed_slope =
(1 / 0.2) * (dpk[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0, 0, 0.1}));
max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R4{1.8, -3.7, 1.9, -1.6}));
}
REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-13));
}
}
}
}
SECTION("R^mn data")
{
using R32 = TinyMatrix<3, 2>;
for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) {
SECTION(named_mesh.name())
{
auto p_mesh = named_mesh.mesh()->get<Mesh<3>>();
auto& mesh = *p_mesh;
auto affine = [](const R3& x) -> R32 {
return R32{+2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2], -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2], //
+1.4 - 0.6 * x[0] - 2.1 * x[1] + 2.9 * x[2], +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2], //
-0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 * x[2], -3.2 - 3.6 * x[0] - 1.7 * x[1] + 0.7 * x[2]};
};
auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
DiscreteFunctionP0<R32> fh{p_mesh};
parallel_for(
mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
PolynomialReconstruction reconstruction;
auto dpk = reconstruction.build(mesh, fh);
{
double max_mean_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
max_mean_error = std::max(max_mean_error, frobeniusNorm(dpk[cell_id](xj[cell_id]) - affine(xj[cell_id])));
}
REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13));
}
{
double max_x_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const R32 reconstructed_slope =
(1 / 0.2) * (dpk[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0.1, 0, 0}));
max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R32{+1.7, 2.1, //
-0.6, -2.3, //
+3.1, -3.6}));
}
REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12));
}
{
double max_y_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const R32 reconstructed_slope =
(1 / 0.2) * (dpk[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0, 0.1, 0}));
max_y_slope_error = std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R32{+1.2, -2.2, //
-2.1, 1.3, //
+0.8, -1.7}));
}
REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12));
}
{
double max_z_slope_error = 0;
for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
const R32 reconstructed_slope =
(1 / 0.2) * (dpk[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0, 0, 0.1}));
max_z_slope_error = std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R32{-1.3, -2.4, //
+2.9, +1.4, //
-1.8, +0.7}));
}
REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-12));
}
}
}
}
}
}