diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index c770f5a315ceecef0bfef5c5dc81bdb4b336c407..2cc86d14a4060b13cbc1d5d3bab305e13b06ab88 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -122,12 +122,16 @@ PolynomialReconstruction::_build( } else if constexpr (is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>) { return data_type::Dimension; } else { + // LCOV_EXCL_START throw UnexpectedError("unexpected data type " + demangle<data_type>()); + // LCOV_EXCL_STOP } } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { return discrete_function.size(); } else { + // LCOV_EXCL_START throw UnexpectedError("unexpected discrete function type"); + // LCOV_EXCL_STOP } }, discrete_function_variant_p->discreteFunction()); @@ -171,9 +175,11 @@ PolynomialReconstruction::_build( } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { using DataType = std::remove_const_t<std::decay_t<typename DiscreteFunctionT::data_type>>; mutable_discrete_function_dpk_variant_list.push_back( - DiscreteFunctionDPkVector<MeshType::Dimension, DataType>(p_mesh, discrete_function.size(), degree)); + DiscreteFunctionDPkVector<MeshType::Dimension, DataType>(p_mesh, degree, discrete_function.size())); } else { + // LCOV_EXCL_START throw UnexpectedError("unexpected discrete function type"); + // LCOV_EXCL_STOP } }, discrete_function_variant->discreteFunction()); @@ -245,7 +251,9 @@ PolynomialReconstruction::_build( } column_begin += pj_vector.size(); } else { + // LCOV_EXCL_START throw UnexpectedError("invalid discrete function type"); + // LCOV_EXCL_STOP } }, discrete_function_variant->discreteFunction()); @@ -282,59 +290,79 @@ PolynomialReconstruction::_build( if constexpr (std::is_same_v<DataType, P0DataType>) { if constexpr (is_discrete_function_P0_v<P0FunctionT>) { - auto dpk_j = dpk_function.coefficients(cell_j_id); - dpk_j[0] = p0_function[cell_j_id]; + if constexpr (is_discrete_function_dpk_scalar_v<DPkFunctionT>) { + auto dpk_j = dpk_function.coefficients(cell_j_id); + dpk_j[0] = p0_function[cell_j_id]; - if constexpr (std::is_arithmetic_v<DataType>) { - for (size_t i = 0; i < MeshType::Dimension; ++i) { - auto& dpk_j_ip1 = dpk_j[i + 1]; - dpk_j_ip1 = X(i, column_begin); - } - ++column_begin; - } else if constexpr (is_tiny_vector_v<DataType>) { - for (size_t i = 0; i < MeshType::Dimension; ++i) { - auto& dpk_j_ip1 = dpk_j[i + 1]; - for (size_t k = 0; k < DataType::Dimension; ++k) { - dpk_j_ip1[k] = X(i, column_begin + k); + if constexpr (std::is_arithmetic_v<DataType>) { + for (size_t i = 0; i < MeshType::Dimension; ++i) { + auto& dpk_j_ip1 = dpk_j[i + 1]; + dpk_j_ip1 = X(i, column_begin); } - } - column_begin += DataType::Dimension; - } else if constexpr (is_tiny_matrix_v<DataType>) { - for (size_t i = 0; i < MeshType::Dimension; ++i) { - auto& dpk_j_ip1 = dpk_j[i + 1]; - for (size_t k = 0; k < DataType::NumberOfRows; ++k) { - for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - dpk_j_ip1(k, l) = X(i, column_begin + k * DataType::NumberOfColumns + l); + ++column_begin; + } else if constexpr (is_tiny_vector_v<DataType>) { + for (size_t i = 0; i < MeshType::Dimension; ++i) { + auto& dpk_j_ip1 = dpk_j[i + 1]; + for (size_t k = 0; k < DataType::Dimension; ++k) { + dpk_j_ip1[k] = X(i, column_begin + k); + } + } + column_begin += DataType::Dimension; + } else if constexpr (is_tiny_matrix_v<DataType>) { + for (size_t i = 0; i < MeshType::Dimension; ++i) { + auto& dpk_j_ip1 = dpk_j[i + 1]; + for (size_t k = 0; k < DataType::NumberOfRows; ++k) { + for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { + dpk_j_ip1(k, l) = X(i, column_begin + k * DataType::NumberOfColumns + l); + } } } + column_begin += DataType::Dimension; + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected data type"); + // LCOV_EXCL_STOP } - column_begin += DataType::Dimension; } else { - throw UnexpectedError("unexpected data type"); + // LCOV_EXCL_START + throw UnexpectedError("unexpected discrete dpk function type"); + // LCOV_EXCL_STOP } } else if constexpr (is_discrete_function_P0_vector_v<P0FunctionT>) { - auto dpk_j = dpk_function.coefficients(cell_j_id); - auto cell_vector = p0_function[cell_j_id]; - const size_t size = MeshType::Dimension + 1; - - for (size_t l = 0; l < cell_vector.size(); ++l) { - const size_t component_begin = l * size; - dpk_j[component_begin] = cell_vector[l]; - if constexpr (std::is_arithmetic_v<DataType>) { - for (size_t i = 0; i < MeshType::Dimension; ++i) { - auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; - dpk_j_ip1 = X(i, column_begin); + if constexpr (is_discrete_function_dpk_vector_v<DPkFunctionT>) { + auto dpk_j = dpk_function.coefficients(cell_j_id); + auto cell_vector = p0_function[cell_j_id]; + const size_t size = MeshType::Dimension + 1; + + for (size_t l = 0; l < cell_vector.size(); ++l) { + const size_t component_begin = l * size; + dpk_j[component_begin] = cell_vector[l]; + if constexpr (std::is_arithmetic_v<DataType>) { + for (size_t i = 0; i < MeshType::Dimension; ++i) { + auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; + dpk_j_ip1 = X(i, column_begin); + } + ++column_begin; + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected data type"); + // LCOV_EXCL_STOP } - ++column_begin; - } else { - throw UnexpectedError("unexpected data type"); } + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected discrete dpk function type"); + // LCOV_EXCL_STOP } } else { + // LCOV_EXCL_START throw UnexpectedError("unexpected discrete function type"); + // LCOV_EXCL_STOP } } else { + // LCOV_EXCL_START throw UnexpectedError("incompatible data types"); + // LCOV_EXCL_STOP } }, dpk_variant.mutableDiscreteFunctionDPk(), discrete_function_variant->discreteFunction()); @@ -364,7 +392,7 @@ PolynomialReconstruction::build( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const { if (not hasSameMesh(discrete_function_variant_list)) { - throw NormalError("cannot reconstruct functions living of different mesh simultaneously"); + throw NormalError("cannot reconstruct functions living of different meshes simultaneously"); } auto mesh_v = getCommonMesh(discrete_function_variant_list); diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 12307226697c6a6aa0ce1a414b81e49241914b74..029fa30e8aa88279e90896a5918dd024fb2e4474 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -171,6 +171,64 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } } + SECTION("R vector 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 vector_affine = [](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0Vector<double> Vh{p_mesh, 3}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + Vh[cell_id][i] = vector[i]; + } + }); + + auto reconstructions = PolynomialReconstruction{}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); + } + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); + } + + { + double max_slope_error = 0; + const TinyVector<3> slope{+1.7, +2.1, -0.6}; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = + (1 / 0.2) * (dpk_Vh(cell_id, i)(R1{0.1} + xj[cell_id]) - dpk_Vh(cell_id, i)(xj[cell_id] - R1{0.1})); + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); + } + REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); + } + } + } + } + } + SECTION("list of various types") { using R3x3 = TinyMatrix<3>; @@ -198,6 +256,10 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") }; }; + auto vector_affine = [](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); DiscreteFunctionP0<double> fh{p_mesh}; @@ -212,8 +274,18 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); + DiscreteFunctionP0Vector<double> Vh{p_mesh, 3}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + Vh[cell_id][i] = vector[i]; + } + }); + auto reconstructions = PolynomialReconstruction{}.build(std::make_shared<DiscreteFunctionVariant>(fh), uh, - std::make_shared<DiscreteFunctionP0<R3x3>>(Ah)); + std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), + DiscreteFunctionVariant(Vh)); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); @@ -283,6 +355,34 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); } + + auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); + } + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); + } + + { + double max_slope_error = 0; + const TinyVector<3> slope{+1.7, +2.1, -0.6}; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = + (1 / 0.2) * (dpk_Vh(cell_id, i)(R1{0.1} + xj[cell_id]) - dpk_Vh(cell_id, i)(xj[cell_id] - R1{0.1})); + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); + } + REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); + } + } } } } @@ -464,6 +564,78 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } } } + + SECTION("vector data") + { + using R4 = TinyVector<4>; + + 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 vector_affine = [](const R2& x) -> R4 { + return R4{+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]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0Vector<double> Vh{p_mesh, 4}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + Vh[cell_id][i] = vector[i]; + } + }); + + auto reconstructions = PolynomialReconstruction{}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); + } + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); + } + + { + double max_x_slope_error = 0; + const R4 slope{+1.7, +2.1, -0.6, -2.3}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R2{0.1, 0} + xj[cell_id]) - + dpk_Vh(cell_id, i)(xj[cell_id] - R2{0.1, 0})); + + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12)); + } + + { + double max_y_slope_error = 0; + const R4 slope{+1.2, -2.2, -2.1, +1.3}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R2{0, 0.1} + xj[cell_id]) - + dpk_Vh(cell_id, i)(xj[cell_id] - R2{0, 0.1})); + + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12)); + } + } + } + } } SECTION("3D") @@ -675,5 +847,104 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } } } + + SECTION("vector 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 vector_affine = [](const R3& x) -> R4 { + return R4{+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], + // + +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]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0Vector<double> Vh{p_mesh, 4}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + Vh[cell_id][i] = vector[i]; + } + }); + + auto reconstructions = PolynomialReconstruction{}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto vector = vector_affine(xj[cell_id]); + for (size_t i = 0; i < vector.dimension(); ++i) { + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); + } + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); + } + + { + double max_x_slope_error = 0; + const R4 slope{+1.7, 2.1, -2.3, +3.1}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0.1, 0, 0} + xj[cell_id]) - + dpk_Vh(cell_id, i)(xj[cell_id] - R3{0.1, 0, 0})); + + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12)); + } + + { + double max_y_slope_error = 0; + const R4 slope{+1.2, -2.2, 1.3, +0.8}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0, 0.1, 0} + xj[cell_id]) - + dpk_Vh(cell_id, i)(xj[cell_id] - R3{0, 0.1, 0})); + + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12)); + } + + { + double max_z_slope_error = 0; + const R4 slope{-1.3, -2.4, +1.4, -1.8}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < slope.dimension(); ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0, 0, 0.1} + xj[cell_id]) - + dpk_Vh(cell_id, i)(xj[cell_id] - R3{0, 0, 0.1})); + + max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-12)); + } + } + } + } + } + + SECTION("errors") + { + auto p_mesh1 = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + DiscreteFunctionP0<double> f1{p_mesh1}; + + auto p_mesh2 = MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); + DiscreteFunctionP0<double> f2{p_mesh2}; + + REQUIRE_THROWS_WITH(PolynomialReconstruction{}.build(f1, f2), + "error: cannot reconstruct functions living of different meshes simultaneously"); } }