From 1f79d573999ff3c80e2384407f7eea2ac93f481c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Wed, 17 Apr 2024 12:15:31 +0200 Subject: [PATCH 001/122] Begin piecewise polynomial reconstruction (in 1D) --- src/scheme/DiscreteFunctionDPk.hpp | 197 +++++++++++++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/test_DiscreteFunctionDPk.cpp | 58 +++++++++ 3 files changed, 256 insertions(+) create mode 100644 src/scheme/DiscreteFunctionDPk.hpp create mode 100644 tests/test_DiscreteFunctionDPk.cpp diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp new file mode 100644 index 000000000..799f89e7f --- /dev/null +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -0,0 +1,197 @@ +#ifndef DISCRETE_FUNCTION_D_PK_HPP +#define DISCRETE_FUNCTION_D_PK_HPP + +#include <language/utils/ASTNodeDataTypeTraits.hpp> + +#include <mesh/ItemArray.hpp> +#include <mesh/ItemArrayUtils.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/MeshVariant.hpp> +#include <scheme/DiscreteFunctionDescriptorP0.hpp> + +template <size_t Dimension, typename DataType> +class TaylorView +{ + private: + using CoefficientList = typename Table<DataType>::UnsafeRowView; + + const size_t m_degree; + const CoefficientList& m_coefficient_list; + const TinyVector<Dimension>& m_xj; + + public: + static size_t + dimensionFromDegree(size_t degree) + { + if constexpr (Dimension == 1) { + return degree + 1; + } else { + throw NotImplementedError("Dimension > 1"); + } + } + static size_t + degreeFromDimension(size_t polynomial_dimension) + { + if constexpr (Dimension == 1) { + Assert(polynomial_dimension > 1); + return polynomial_dimension - 1; + } else { + throw NotImplementedError("Dimension > 1"); + } + } + + DataType + operator()(const TinyVector<Dimension> x) const + { + if constexpr (Dimension == 1) { + const double x_xj = (x - m_xj)[0]; + DataType result = [] { + if constexpr (std::is_arithmetic_v<DataType>) { + return 0; + } else { + return zero; + } + }(); + for (size_t i_coeffiencient = m_coefficient_list.size() - 1; i_coeffiencient > 0; --i_coeffiencient) { + result = (m_coefficient_list[i_coeffiencient] + result) * x_xj; + } + result += m_coefficient_list[0]; + return result; + } else { + throw NotImplementedError("Dimension>2"); + } + } + + TaylorView(const size_t degree, const CoefficientList& coefficient_list, const TinyVector<Dimension>& xj) + : m_degree{degree}, m_coefficient_list{coefficient_list}, m_xj{xj} + {} + + TaylorView(const TaylorView&) = default; + TaylorView(TaylorView&&) = default; + TaylorView() = delete; + ~TaylorView() = default; +}; + +template <size_t Dimension, typename DataType> +class DiscreteFunctionDPk +{ + public: + using data_type = DataType; + + friend class DiscreteFunctionDPk<Dimension, std::add_const_t<DataType>>; + friend class DiscreteFunctionDPk<Dimension, std::remove_const_t<DataType>>; + + private: + std::shared_ptr<const MeshVariant> m_mesh_v; + const size_t m_degree; + CellArray<DataType> m_cell_array; + CellValue<const TinyVector<Dimension>> m_xj; + + public: + PUGS_INLINE + ASTNodeDataType + dataType() const + { + return ast_node_data_type_from<std::remove_const_t<DataType>>; + } + + PUGS_INLINE + const CellArray<DataType>& + cellArrays() const + { + return m_cell_array; + } + + PUGS_INLINE std::shared_ptr<const MeshVariant> + meshVariant() const + { + return m_mesh_v; + } + + PUGS_FORCEINLINE + operator DiscreteFunctionDPk<Dimension, const DataType>() const + { + return DiscreteFunctionDPk<Dimension, const DataType>(m_mesh_v, m_cell_array); + } + + PUGS_INLINE + void + fill(const DataType& data) const noexcept + { + static_assert(not std::is_const_v<DataType>, "Cannot modify ItemValue of const"); + m_cell_array.fill(data); + } + + friend PUGS_INLINE DiscreteFunctionDPk<Dimension, std::remove_const_t<DataType>> + copy(const DiscreteFunctionDPk& source) + { + return DiscreteFunctionDPk<Dimension, std::remove_const_t<DataType>>{source.m_mesh_v, copy(source.cellValues())}; + } + + friend PUGS_INLINE void + copy_to(const DiscreteFunctionDPk<Dimension, DataType>& source, + DiscreteFunctionDPk<Dimension, std::remove_const_t<DataType>>& destination) + { + Assert(source.m_mesh_v == destination.m_mesh_v); + copy_to(source.m_cell_values, destination.m_cell_values); + } + + PUGS_INLINE + auto + coefficients(const CellId cell_id) const + { + return m_cell_array[cell_id]; + } + + PUGS_FORCEINLINE TaylorView<Dimension, DataType> + operator[](const CellId cell_id) const noexcept(NO_ASSERT) + { + return TaylorView<Dimension, DataType>{m_degree, m_cell_array[cell_id], m_xj[cell_id]}; + } + + PUGS_INLINE DiscreteFunctionDPk + operator=(const DiscreteFunctionDPk& f) + { + Assert(m_mesh_v->id() == f.m_mesh_v->id()); + m_cell_array = f.m_cell_array; + return *this; + } + + DiscreteFunctionDPk(const std::shared_ptr<const MeshVariant>& mesh_v, size_t degree) + : m_mesh_v{mesh_v}, + m_degree{degree}, + m_cell_array{mesh_v->connectivity(), TaylorView<Dimension, DataType>::dimensionFromDegree(degree)}, + m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} + {} + + DiscreteFunctionDPk(const std::shared_ptr<const MeshVariant>& mesh_v, const CellValue<DataType>& cell_array) + : m_mesh_v{mesh_v}, + m_degree{TaylorView<Dimension, DataType>::degreeFromDimension(cell_array.sizeOfArrays())}, + m_cell_array{cell_array}, + m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} + { + Assert(mesh_v->connectivity().id() == cell_array.connectivity_ptr()->id()); + } + + template <MeshConcept MeshType> + DiscreteFunctionDPk(const std::shared_ptr<const MeshType>& p_mesh, size_t degree) + : DiscreteFunctionDPk{p_mesh->meshVariant(), degree} + {} + + template <MeshConcept MeshType> + DiscreteFunctionDPk(const std::shared_ptr<const MeshType>& p_mesh, const CellValue<DataType>& cell_array) + : DiscreteFunctionDPk{p_mesh->meshVariant(), cell_array} + { + Assert(m_mesh_v->connectivity().id() == cell_array.connectivity_ptr()->id()); + } + + DiscreteFunctionDPk() noexcept = delete; + + DiscreteFunctionDPk(const DiscreteFunctionDPk&) noexcept = default; + DiscreteFunctionDPk(DiscreteFunctionDPk&&) noexcept = default; + + ~DiscreteFunctionDPk() = default; +}; + +#endif // DISCRETE_FUNCTION_D_PK_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e7665f894..e7c75245c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -159,6 +159,7 @@ add_executable (unit_tests add_executable (mpi_unit_tests mpi_test_main.cpp test_Connectivity.cpp + test_DiscreteFunctionDPk.cpp test_DiscreteFunctionIntegrator.cpp test_DiscreteFunctionIntegratorByZone.cpp test_DiscreteFunctionInterpoler.cpp diff --git a/tests/test_DiscreteFunctionDPk.cpp b/tests/test_DiscreteFunctionDPk.cpp new file mode 100644 index 000000000..abdd6e866 --- /dev/null +++ b/tests/test_DiscreteFunctionDPk.cpp @@ -0,0 +1,58 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <mesh/Mesh.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> +#include <scheme/DiscreteFunctionP0.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("DiscreteFunctionDPk", "[scheme]") +{ + SECTION("constructors") + { + SECTION("1D") + { + constexpr size_t Dimension = 1; + + using R1 = TinyVector<1>; + + const std::shared_ptr mesh_v = MeshDataBaseForTests::get().cartesian1DMesh(); + const std::shared_ptr mesh = mesh_v->get<Mesh<1>>(); + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + const size_t degree = 4; + + DiscreteFunctionDPk<Dimension, double> pk(mesh_v, degree); + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + pk.coefficients(cell_id)[0] = 1; + pk.coefficients(cell_id)[1] = 1.4; + pk.coefficients(cell_id)[2] = -6.2; + pk.coefficients(cell_id)[3] = 2.7; + pk.coefficients(cell_id)[4] = 3.1; + }; + + DiscreteFunctionP0<double> p_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { p_xj[cell_id] = pk[cell_id](xj[cell_id]); }); + + REQUIRE(max(p_xj) == 1); + REQUIRE(min(p_xj) == 1); + + DiscreteFunctionP0<double> delta(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const double x0 = 0.3 * Vj[cell_id]; + delta[cell_id] = pk[cell_id](xj[cell_id] + R1{x0}) - + (1 + 1.4 * x0 - 6.2 * x0 * x0 + 2.7 * x0 * x0 * x0 + 3.1 * x0 * x0 * x0 * x0); + }); + + REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); + REQUIRE(min(delta) == Catch::Approx(0.).margin(1E-14)); + } + } +} -- GitLab From c5392ac962c4b2f787df06b7cbaf1aaff35a6a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 25 Apr 2024 19:26:41 +0200 Subject: [PATCH 002/122] Allow empty lines for ConnectivityMatrix --- src/mesh/ConnectivityMatrix.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/ConnectivityMatrix.hpp b/src/mesh/ConnectivityMatrix.hpp index eadc85347..aabd8de94 100644 --- a/src/mesh/ConnectivityMatrix.hpp +++ b/src/mesh/ConnectivityMatrix.hpp @@ -65,7 +65,7 @@ class ConnectivityMatrix Assert(m_row_map[0] == 0, "row map should start with 0"); #ifndef NDEBUG for (size_t i = 1; i < m_row_map.size(); ++i) { - Assert(m_row_map[i] > m_row_map[i - 1], "row map values must be strictly increasing"); + Assert(m_row_map[i] >= m_row_map[i - 1], "row map values must be increasing"); } #endif // NDEBUG } -- GitLab From 35d5c18de862dfda1207ec94b920f6485477b2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 25 Apr 2024 19:27:14 +0200 Subject: [PATCH 003/122] Add a first stencil construction mechanism - in parallel, ghost cells have no stencil - the stencil is composed of cells that share a node with the considered cell - a storage mechanism should be used to build the stencil once for all (soon?) --- src/mesh/CMakeLists.txt | 1 + src/mesh/Stencil.hpp | 26 ++++++++ src/mesh/StencilBuilder.cpp | 117 ++++++++++++++++++++++++++++++++++ src/mesh/StencilBuilder.hpp | 26 ++++++++ tests/CMakeLists.txt | 1 + tests/test_StencilBuilder.cpp | 113 ++++++++++++++++++++++++++++++++ 6 files changed, 284 insertions(+) create mode 100644 src/mesh/Stencil.hpp create mode 100644 src/mesh/StencilBuilder.cpp create mode 100644 src/mesh/StencilBuilder.hpp create mode 100644 tests/test_StencilBuilder.cpp diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt index 78b72bcaf..d03051cae 100644 --- a/src/mesh/CMakeLists.txt +++ b/src/mesh/CMakeLists.txt @@ -43,6 +43,7 @@ add_library( MeshTransformer.cpp MeshUtils.cpp MeshVariant.cpp + StencilBuilder.cpp SynchronizerManager.cpp ) diff --git a/src/mesh/Stencil.hpp b/src/mesh/Stencil.hpp new file mode 100644 index 000000000..d67aa09ed --- /dev/null +++ b/src/mesh/Stencil.hpp @@ -0,0 +1,26 @@ +#ifndef STENCIL_HPP +#define STENCIL_HPP + +#include <mesh/ConnectivityMatrix.hpp> +#include <mesh/ItemId.hpp> + +class Stencil +{ + private: + ConnectivityMatrix m_stencil; + + public: + PUGS_INLINE + auto + operator[](CellId cell_id) const + { + return m_stencil[cell_id]; + } + + Stencil(const ConnectivityMatrix& stencil) : m_stencil(stencil) {} + Stencil(const Stencil&) = default; + Stencil(Stencil&&) = default; + ~Stencil() = default; +}; + +#endif // STENCIL_HPP diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp new file mode 100644 index 000000000..a43dbef60 --- /dev/null +++ b/src/mesh/StencilBuilder.cpp @@ -0,0 +1,117 @@ +#include <mesh/Connectivity.hpp> +#include <mesh/StencilBuilder.hpp> + +template <typename ConnectivityType> +Array<const uint32_t> +StencilBuilder::_getRowMap(const ConnectivityType& connectivity) const +{ + auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); + auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + + auto cell_is_owned = connectivity.cellIsOwned(); + + Array<uint32_t> row_map{connectivity.numberOfCells() + 1}; + row_map[0] = 0; + std::vector<CellId> neighbors; + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + neighbors.resize(0); + // The stencil is not built for ghost cells + if (cell_is_owned[cell_id]) { + auto cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + const NodeId node_id = cell_nodes[i_node]; + auto node_cells = node_to_cell_matrix[node_id]; + for (size_t i_node_cell = 0; i_node_cell < node_cells.size(); ++i_node_cell) { + const CellId node_cell_id = node_cells[i_node_cell]; + if (node_cell_id != cell_id) { + neighbors.push_back(node_cells[i_node_cell]); + } + } + } + std::sort(neighbors.begin(), neighbors.end()); + neighbors.erase(std::unique(neighbors.begin(), neighbors.end()), neighbors.end()); + } + // The cell itself is not counted + row_map[cell_id + 1] = row_map[cell_id] + neighbors.size(); + } + + return row_map; +} + +template <typename ConnectivityType> +Array<const uint32_t> +StencilBuilder::_getColumnIndices(const ConnectivityType& connectivity, const Array<const uint32_t>& row_map) const +{ + auto cell_number = connectivity.cellNumber(); + + Array<uint32_t> max_index(row_map.size() - 1); + parallel_for( + max_index.size(), PUGS_LAMBDA(size_t i) { max_index[i] = row_map[i]; }); + + auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); + auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + + auto cell_is_owned = connectivity.cellIsOwned(); + + Array<uint32_t> column_indices(row_map[row_map.size() - 1]); + column_indices.fill(std::numeric_limits<uint32_t>::max()); + + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + // The stencil is not built for ghost cells + if (cell_is_owned[cell_id]) { + auto cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + const NodeId node_id = cell_nodes[i_node]; + auto node_cells = node_to_cell_matrix[node_id]; + for (size_t i_node_cell = 0; i_node_cell < node_cells.size(); ++i_node_cell) { + const CellId node_cell_id = node_cells[i_node_cell]; + if (node_cell_id != cell_id) { + bool found = false; + for (size_t i_index = row_map[cell_id]; i_index < max_index[cell_id]; ++i_index) { + if (column_indices[i_index] == node_cell_id) { + found = true; + break; + } + } + if (not found) { + int node_cell_number = cell_number[node_cell_id]; + size_t i_index = row_map[cell_id]; + // search for position for index + while ((i_index < max_index[cell_id])) { + if (node_cell_number > cell_number[CellId(column_indices[i_index])]) { + ++i_index; + } else { + break; + } + } + + for (size_t i_destination = max_index[cell_id]; i_destination > i_index; --i_destination) { + size_t i_source = i_destination - 1; + + column_indices[i_destination] = column_indices[i_source]; + } + ++max_index[cell_id]; + column_indices[i_index] = node_cell_id; + } + } + } + } + } + } + + return column_indices; +} + +template <typename ConnectivityType> +Stencil +StencilBuilder::build(const ConnectivityType& connectivity) const +{ + Array<const uint32_t> row_map = this->_getRowMap(connectivity); + Array<const uint32_t> column_indices = this->_getColumnIndices(connectivity, row_map); + + return ConnectivityMatrix{row_map, column_indices}; +} + +template Stencil StencilBuilder::build(const Connectivity<1>& connectivity) const; +template Stencil StencilBuilder::build(const Connectivity<2>& connectivity) const; +template Stencil StencilBuilder::build(const Connectivity<3>& connectivity) const; diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp new file mode 100644 index 000000000..3a4235079 --- /dev/null +++ b/src/mesh/StencilBuilder.hpp @@ -0,0 +1,26 @@ +#ifndef STENCIL_BUILDER_HPP +#define STENCIL_BUILDER_HPP + +#include <mesh/Stencil.hpp> + +class StencilBuilder +{ + private: + template <typename ConnectivityType> + Array<const uint32_t> _getRowMap(const ConnectivityType& connectivity) const; + + template <typename ConnectivityType> + Array<const uint32_t> _getColumnIndices(const ConnectivityType& connectivity, + const Array<const uint32_t>& row_map) const; + + public: + template <typename ConnectivityType> + Stencil build(const ConnectivityType& connectivity) const; + + StencilBuilder() = default; + StencilBuilder(const StencilBuilder&) = default; + StencilBuilder(StencilBuilder&&) = default; + ~StencilBuilder() = default; +}; + +#endif // STENCIL_BUILDER_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e7c75245c..68da0dc88 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -204,6 +204,7 @@ add_executable (mpi_unit_tests test_ParallelChecker_read.cpp test_Partitioner.cpp test_RandomEngine.cpp + test_StencilBuilder.cpp test_SubItemArrayPerItemVariant.cpp test_SubItemValuePerItem.cpp test_SubItemValuePerItemVariant.cpp diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder.cpp new file mode 100644 index 000000000..af6031ee4 --- /dev/null +++ b/tests/test_StencilBuilder.cpp @@ -0,0 +1,113 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/ConnectivityUtils.hpp> +#include <mesh/ItemValue.hpp> +#include <mesh/ItemValueUtils.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshVariant.hpp> +#include <mesh/StencilBuilder.hpp> +#include <utils/Messenger.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("StencilBuilder", "[mesh]") +{ + auto is_valid = [](const auto& connectivity, const Stencil& stencil) { + auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); + auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + + auto cell_is_owned = connectivity.cellIsOwned(); + auto cell_number = connectivity.cellNumber(); + + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + if (cell_is_owned[cell_id]) { + std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + auto cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + const NodeId node_id = cell_nodes[i_node]; + auto node_cells = node_to_cell_matrix[node_id]; + for (size_t i_node_cell = 0; i_node_cell < node_cells.size(); ++i_node_cell) { + const CellId node_cell_id = node_cells[i_node_cell]; + if (node_cell_id != cell_id) { + cell_set.insert(node_cell_id); + } + } + } + + auto cell_stencil = stencil[cell_id]; + + auto i_set_cell = cell_set.begin(); + for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { + if (*i_set_cell != cell_stencil[index]) { + return false; + } + } + } + } + return true; + }; + + SECTION("1D") + { + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + Stencil stencil = StencilBuilder{}.build(connectivity); + + REQUIRE(is_valid(connectivity, stencil)); + } + + SECTION("unordered") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + REQUIRE(is_valid(connectivity, StencilBuilder{}.build(connectivity))); + } + } + + SECTION("2D") + { + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + REQUIRE(is_valid(connectivity, StencilBuilder{}.build(connectivity))); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + REQUIRE(is_valid(connectivity, StencilBuilder{}.build(connectivity))); + } + } + + SECTION("3D") + { + SECTION("carteian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + REQUIRE(is_valid(connectivity, StencilBuilder{}.build(connectivity))); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + REQUIRE(is_valid(connectivity, StencilBuilder{}.build(connectivity))); + } + } +} -- GitLab From 05994004fa0bab83b4ef853f5bba349b9b12abb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 26 Apr 2024 12:27:23 +0200 Subject: [PATCH 004/122] Simplify coefficients handling in TaylorView This may be reworked a lot... --- src/scheme/DiscreteFunctionDPk.hpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index 799f89e7f..d7809a09f 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -17,7 +17,8 @@ class TaylorView using CoefficientList = typename Table<DataType>::UnsafeRowView; const size_t m_degree; - const CoefficientList& m_coefficient_list; + DataType* const m_coefficients; + const size_t m_nb_coefficients; const TinyVector<Dimension>& m_xj; public: @@ -53,10 +54,11 @@ class TaylorView return zero; } }(); - for (size_t i_coeffiencient = m_coefficient_list.size() - 1; i_coeffiencient > 0; --i_coeffiencient) { - result = (m_coefficient_list[i_coeffiencient] + result) * x_xj; + + for (size_t i_coeffiencient = m_nb_coefficients - 1; i_coeffiencient > 0; --i_coeffiencient) { + result = (m_coefficients[i_coeffiencient] + result) * x_xj; } - result += m_coefficient_list[0]; + result += m_coefficients[0]; return result; } else { throw NotImplementedError("Dimension>2"); @@ -64,10 +66,10 @@ class TaylorView } TaylorView(const size_t degree, const CoefficientList& coefficient_list, const TinyVector<Dimension>& xj) - : m_degree{degree}, m_coefficient_list{coefficient_list}, m_xj{xj} + : m_degree{degree}, m_coefficients(&(coefficient_list[0])), m_nb_coefficients(coefficient_list.size()), m_xj{xj} {} - TaylorView(const TaylorView&) = default; + TaylorView(const TaylorView&) = delete; TaylorView(TaylorView&&) = default; TaylorView() = delete; ~TaylorView() = default; -- GitLab From 46417516de62a8d1eb28c4c1112c43fd506a33d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 26 Apr 2024 14:08:27 +0200 Subject: [PATCH 005/122] Use std::span to store view --- src/scheme/DiscreteFunctionDPk.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index d7809a09f..6b6d696e4 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -10,6 +10,8 @@ #include <mesh/MeshVariant.hpp> #include <scheme/DiscreteFunctionDescriptorP0.hpp> +#include <span> + template <size_t Dimension, typename DataType> class TaylorView { @@ -17,8 +19,8 @@ class TaylorView using CoefficientList = typename Table<DataType>::UnsafeRowView; const size_t m_degree; - DataType* const m_coefficients; - const size_t m_nb_coefficients; + + std::span<DataType> m_coefficients; const TinyVector<Dimension>& m_xj; public: @@ -55,7 +57,7 @@ class TaylorView } }(); - for (size_t i_coeffiencient = m_nb_coefficients - 1; i_coeffiencient > 0; --i_coeffiencient) { + for (size_t i_coeffiencient = m_coefficients.size() - 1; i_coeffiencient > 0; --i_coeffiencient) { result = (m_coefficients[i_coeffiencient] + result) * x_xj; } result += m_coefficients[0]; @@ -66,7 +68,7 @@ class TaylorView } TaylorView(const size_t degree, const CoefficientList& coefficient_list, const TinyVector<Dimension>& xj) - : m_degree{degree}, m_coefficients(&(coefficient_list[0])), m_nb_coefficients(coefficient_list.size()), m_xj{xj} + : m_degree{degree}, m_coefficients{&(coefficient_list[0]), coefficient_list.size()}, m_xj{xj} {} TaylorView(const TaylorView&) = delete; -- GitLab From 83022e213c6272c712639b54809dda75225e3488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 26 Apr 2024 14:49:58 +0200 Subject: [PATCH 006/122] Add few tests for polynomial vector or matrix discrete functions --- src/scheme/DiscreteFunctionDPk.hpp | 2 +- tests/test_DiscreteFunctionDPk.cpp | 107 ++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index 6b6d696e4..b42bb2897 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -58,7 +58,7 @@ class TaylorView }(); for (size_t i_coeffiencient = m_coefficients.size() - 1; i_coeffiencient > 0; --i_coeffiencient) { - result = (m_coefficients[i_coeffiencient] + result) * x_xj; + result = x_xj * (m_coefficients[i_coeffiencient] + result); } result += m_coefficients[0]; return result; diff --git a/tests/test_DiscreteFunctionDPk.cpp b/tests/test_DiscreteFunctionDPk.cpp index abdd6e866..02ff3527f 100644 --- a/tests/test_DiscreteFunctionDPk.cpp +++ b/tests/test_DiscreteFunctionDPk.cpp @@ -10,7 +10,7 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") { - SECTION("constructors") + SECTION("R data") { SECTION("1D") { @@ -55,4 +55,109 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") REQUIRE(min(delta) == Catch::Approx(0.).margin(1E-14)); } } + + SECTION("R^d data") + { + SECTION("1D") + { + constexpr size_t Dimension = 1; + + using R1 = TinyVector<1>; + using R2 = TinyVector<2>; + + const std::shared_ptr mesh_v = MeshDataBaseForTests::get().cartesian1DMesh(); + const std::shared_ptr mesh = mesh_v->get<Mesh<1>>(); + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + const size_t degree = 4; + + DiscreteFunctionDPk<Dimension, R2> pk(mesh_v, degree); + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + pk.coefficients(cell_id)[0] = R2{-1.0, +3.0}; + pk.coefficients(cell_id)[1] = R2{+1.4, +1.9}; + pk.coefficients(cell_id)[2] = R2{-6.2, -1.0}; + pk.coefficients(cell_id)[3] = R2{+2.7, +1.6}; + pk.coefficients(cell_id)[4] = R2{+3.1, -1.3}; + }; + + DiscreteFunctionP0<double> delta_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + delta_xj[cell_id] = l2Norm(pk[cell_id](xj[cell_id]) - R2{-1.0, +3.0}); + }); + + REQUIRE(max(delta_xj) == Catch::Approx(0).margin(1E-14)); + + DiscreteFunctionP0<double> delta(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const double x0 = 0.3 * Vj[cell_id]; + delta[cell_id] = l2Norm(pk[cell_id](xj[cell_id] + R1{x0}) - + R2{-1 + 1.4 * x0 - 6.2 * x0 * x0 + 2.7 * x0 * x0 * x0 + 3.1 * x0 * x0 * x0 * x0, + 3 + 1.9 * x0 - 1.0 * x0 * x0 + 1.6 * x0 * x0 * x0 - 1.3 * x0 * x0 * x0 * x0}); + }); + + REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); + } + } + + SECTION("R^d1xd2 data") + { + SECTION("1D") + { + constexpr size_t Dimension = 1; + + using R1 = TinyVector<1>; + using R2x3 = TinyMatrix<2, 3>; + + const std::shared_ptr mesh_v = MeshDataBaseForTests::get().cartesian1DMesh(); + const std::shared_ptr mesh = mesh_v->get<Mesh<1>>(); + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + const size_t degree = 4; + + DiscreteFunctionDPk<Dimension, R2x3> pk(mesh_v, degree); + + const R2x3 A{}; + const R2x3 B{}; + const R2x3 C{}; + const R2x3 D{}; + const R2x3 E{}; + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + pk.coefficients(cell_id)[0] = A; + pk.coefficients(cell_id)[1] = B; + pk.coefficients(cell_id)[2] = C; + pk.coefficients(cell_id)[3] = D; + pk.coefficients(cell_id)[4] = E; + }; + + DiscreteFunctionP0<double> delta_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const R2x3 Diff = pk[cell_id](xj[cell_id]) - A; + + delta_xj[cell_id] = l2Norm((transpose(Diff) * Diff) * TinyVector<3>(1, 1, 1)); + }); + + REQUIRE(max(delta_xj) == Catch::Approx(0).margin(1E-14)); + + DiscreteFunctionP0<double> delta(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const double x0 = 0.3 * Vj[cell_id]; + const R2x3 Diff = + pk[cell_id](xj[cell_id] + R1{x0}) - (A + x0 * B + x0 * x0 * C + x0 * x0 * x0 * D + x0 * x0 * x0 * x0 * E); + + delta[cell_id] = l2Norm((transpose(Diff) * Diff) * TinyVector<3>(1, 1, 1)); + }); + + REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); + } + } } -- GitLab From d9245809b744c3eb022ee81365ab06bfd7e42536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 2 May 2024 11:55:09 +0200 Subject: [PATCH 007/122] Add polynomial reconstruction Works in parallel for degree 1 polynomials in 1d --- src/algebra/Givens.hpp | 99 +++++++++++++++++++++++++ src/scheme/PolynomialReconstruction.hpp | 81 ++++++++++++++++++++ tests/CMakeLists.txt | 2 + tests/test_Givens.cpp | 96 ++++++++++++++++++++++++ tests/test_PolynomialReconstruction.cpp | 65 ++++++++++++++++ 5 files changed, 343 insertions(+) create mode 100644 src/algebra/Givens.hpp create mode 100644 src/scheme/PolynomialReconstruction.hpp create mode 100644 tests/test_Givens.cpp create mode 100644 tests/test_PolynomialReconstruction.cpp diff --git a/src/algebra/Givens.hpp b/src/algebra/Givens.hpp new file mode 100644 index 000000000..02a71d0aa --- /dev/null +++ b/src/algebra/Givens.hpp @@ -0,0 +1,99 @@ +#ifndef GIVENS_HPP +#define GIVENS_HPP +#include <cmath> +#include <iomanip> +#include <iostream> +#include <rang.hpp> +#include <utils/Exceptions.hpp> +#include <utils/PugsAssert.hpp> + +class Givens +{ + private: + static void + _givens(const double a, const double b, double& c, double& s) + { + if (b == 0) { + c = 1; + s = 0; + } else { + if (std::abs(b) > std::abs(a)) { + const double tau = -a / b; + s = 1. / sqrt(1 + tau * tau); + c = s * tau; + } else { + Assert(a != 0); + const double tau = -b / a; + c = 1 / sqrt(1 + tau * tau); + s = c * tau; + } + } + } + + static void + _rotate(const double Ai_1j, const double Aij, const double c, const double s, double& ui_1, double& ui) + { + ui_1 = c * Ai_1j - s * Aij; + ui = s * Ai_1j + c * Aij; + } + + template <typename MatrixType, typename VectorType, typename RHSVectorType> + static void + _lift(const MatrixType& A, const RHSVectorType& b, VectorType& x) + { + x.fill(0); + for (size_t i = x.size(); i >= 1; --i) { + double sum = 0; + for (size_t j = i; j < x.size(); ++j) { + sum += A(i - 1, j) * x[j]; + } + x[i - 1] = (b[i - 1] - sum) / A(i - 1, i - 1); + } + } + + public: + template <typename MatrixType, typename VectorType, typename RHSVectorType> + static void + solveInPlace(MatrixType& A, VectorType& x, RHSVectorType& b) + { + Assert(x.size() == A.numberOfColumns(), "The number of columns of A must be the size of x"); + Assert(b.size() == A.numberOfRows(), "The number of rows of A must be the size of b"); + + for (size_t j = 0; j < A.numberOfColumns(); ++j) { + for (size_t i = A.numberOfRows() - 1; i > j; --i) { + double c(0), s(0); + _givens(A(i - 1, j), A(i, j), c, s); + for (size_t k = j; k < A.numberOfColumns(); ++k) { + double xi_1(0), xi(0); + _rotate(A(i - 1, k), A(i, k), c, s, xi_1, xi); + A(i - 1, k) = xi_1; + A(i, k) = xi; + } + double xi_1(0), xi(0); + _rotate(b[i - 1], b[i], c, s, xi_1, xi); + b[i - 1] = xi_1; + b[i] = xi; + } + } + + _lift(A, b, x); + } + + template <typename MatrixType, typename VectorType, typename RHSVectorType> + static void + solve(const MatrixType& A, VectorType& x, const RHSVectorType& b) + { + Assert(x.size() == A.numberOfColumns(), "The number of columns of A must be the size of x"); + Assert(b.size() == A.numberOfRows(), "The number of rows of A must be the size of b"); + + MatrixType rotateA = copy(A); + RHSVectorType rotateb = copy(b); + + solveInPlace(rotateA, x, rotateb); + } + + Givens() = delete; + ~Givens() = delete; +}; + +#endif // GIVENS_HPP diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp new file mode 100644 index 000000000..7010d3d19 --- /dev/null +++ b/src/scheme/PolynomialReconstruction.hpp @@ -0,0 +1,81 @@ +#ifndef POLYNOMIAL_RECONSTRUCTION_HPP +#define POLYNOMIAL_RECONSTRUCTION_HPP + +#include <mesh/MeshTraits.hpp> +#include <mesh/StencilBuilder.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> + +#include <algebra/SmallMatrix.hpp> +#include <algebra/SmallVector.hpp> + +#include <algebra/Givens.hpp> +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/LineTransformation.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> + +class PolynomialReconstruction +{ + public: + template <MeshConcept MeshType, typename DataType> + PUGS_INLINE DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> + build(const MeshType& mesh, const DiscreteFunctionP0<DataType> p0_function) + { + const size_t degree = 1; + const Stencil stencil = StencilBuilder{}.build(mesh.connectivity()); + + auto xr = mesh.xr(); + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + auto cell_is_owned = mesh.connectivity().cellIsOwned(); + + DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> dpk{mesh.meshVariant(), degree}; + + if constexpr ((is_polygonal_mesh_v<MeshType>)and(MeshType::Dimension == 1)) { + auto qf = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(1)); + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { + if (cell_is_owned[cell_j_id]) { + auto stencil_cell_list = stencil[cell_j_id]; + SmallVector<double> b(stencil_cell_list.size()); + + const double p_j = p0_function[cell_j_id]; + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + + b[i] = p0_function[cell_i_id] - p_j; + } + + SmallMatrix<double> A(stencil_cell_list.size(), degree); + + using R1 = TinyVector<1>; + + const R1& Xj = xj[cell_j_id]; + auto X_Xj = [&](const R1& X) { return X - Xj; }; + + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + + A(i, 0) = X_Xj(xj[cell_i_id])[0]; + } + + SmallVector<double> x(1); + Givens::solveInPlace(A, x, b); + + dpk.coefficients(cell_j_id)[0] = p_j; + dpk.coefficients(cell_j_id)[1] = x[0]; + } + }); + } + + synchronize(dpk.cellArrays()); + return dpk; + } + + PolynomialReconstruction() {} +}; + +#endif // POLYNOMIAL_RECONSTRUCTION_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 68da0dc88..fccafd015 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -96,6 +96,7 @@ add_executable (unit_tests test_GaussLegendreQuadratureDescriptor.cpp test_GaussLobattoQuadratureDescriptor.cpp test_GaussQuadratureDescriptor.cpp + test_Givens.cpp test_IfProcessor.cpp test_IncDecExpressionProcessor.cpp test_IntegrateCellArray.cpp @@ -203,6 +204,7 @@ add_executable (mpi_unit_tests test_OFStream.cpp test_ParallelChecker_read.cpp test_Partitioner.cpp + test_PolynomialReconstruction.cpp test_RandomEngine.cpp test_StencilBuilder.cpp test_SubItemArrayPerItemVariant.cpp diff --git a/tests/test_Givens.cpp b/tests/test_Givens.cpp new file mode 100644 index 000000000..818d82222 --- /dev/null +++ b/tests/test_Givens.cpp @@ -0,0 +1,96 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <algebra/Givens.hpp> +#include <algebra/SmallMatrix.hpp> +#include <algebra/SmallVector.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("Givens", "[algebra]") +{ + SECTION("square matrix") + { + SmallMatrix<double> A{5, 5}; + A.fill(0); + A(0, 0) = 2; + A(0, 1) = -1; + + A(1, 0) = -0.2; + A(1, 1) = 2; + A(1, 2) = -1; + + A(2, 1) = -1; + A(2, 2) = 4; + A(2, 3) = -2; + + A(3, 2) = -1; + A(3, 3) = 2; + A(3, 4) = -0.1; + + A(4, 3) = 1; + A(4, 4) = 3; + + // SmallMatrix<double> A{S.getMatrix()}; + + SmallVector<const double> x_exact = [] { + SmallVector<double> y{5}; + y[0] = 1; + y[1] = 3; + y[2] = 2; + y[3] = 4; + y[4] = 5; + return y; + }(); + + SmallVector<double> b = A * x_exact; + + SmallVector<double> x{5}; + x = zero; + + Givens::solve(A, x, b); + SmallVector<double> error = x - x_exact; + + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x, x))); + } + + SECTION("rectangular matrix") + { + SmallMatrix<double> A{5, 3}; + A.fill(0); + A(0, 0) = 2; + A(0, 1) = -1; + + A(1, 0) = -0.2; + A(1, 1) = 2; + A(1, 2) = -1; + + A(2, 1) = -1; + A(2, 2) = 4; + + A(3, 2) = -1; + A(4, 0) = 0.5; + A(4, 1) = 1; + A(4, 2) = 1; + + // SmallMatrix<double> A{S.getMatrix()}; + + SmallVector<const double> x_exact = [] { + SmallVector<double> y{3}; + y[0] = 1; + y[1] = 3; + y[2] = 2; + return y; + }(); + + SmallVector<double> b = A * x_exact; + + SmallVector<double> x{3}; + x = zero; + + Givens::solve(A, x, b); + SmallVector<double> error = x - x_exact; + + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x, x))); + } +} diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp new file mode 100644 index 000000000..9a1df3257 --- /dev/null +++ b/tests/test_PolynomialReconstruction.cpp @@ -0,0 +1,65 @@ +#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/PolynomialReconstruction.hpp> + +#include <MeshDataBaseForTests.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("PolynomialReconstruction", "[scheme]") +{ + SECTION("1D") + { + using R1 = TinyVector<1>; + + 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]); }); + + 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_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)); + } + } + } + } +} -- GitLab From aa527b8c3c2fbcb815f0679b9e8ad1c4498c1b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 2 May 2024 11:58:15 +0200 Subject: [PATCH 008/122] Rename TaylorView to the more appropriate CenteredCanonicalBasisView --- src/scheme/DiscreteFunctionDPk.hpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index b42bb2897..53d5ab52e 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -13,7 +13,7 @@ #include <span> template <size_t Dimension, typename DataType> -class TaylorView +class CenteredCanonicalBasisView { private: using CoefficientList = typename Table<DataType>::UnsafeRowView; @@ -67,14 +67,16 @@ class TaylorView } } - TaylorView(const size_t degree, const CoefficientList& coefficient_list, const TinyVector<Dimension>& xj) + CenteredCanonicalBasisView(const size_t degree, + const CoefficientList& coefficient_list, + const TinyVector<Dimension>& xj) : m_degree{degree}, m_coefficients{&(coefficient_list[0]), coefficient_list.size()}, m_xj{xj} {} - TaylorView(const TaylorView&) = delete; - TaylorView(TaylorView&&) = default; - TaylorView() = delete; - ~TaylorView() = default; + CenteredCanonicalBasisView(const CenteredCanonicalBasisView&) = delete; + CenteredCanonicalBasisView(CenteredCanonicalBasisView&&) = default; + CenteredCanonicalBasisView() = delete; + ~CenteredCanonicalBasisView() = default; }; template <size_t Dimension, typename DataType> @@ -148,10 +150,10 @@ class DiscreteFunctionDPk return m_cell_array[cell_id]; } - PUGS_FORCEINLINE TaylorView<Dimension, DataType> + PUGS_FORCEINLINE CenteredCanonicalBasisView<Dimension, DataType> operator[](const CellId cell_id) const noexcept(NO_ASSERT) { - return TaylorView<Dimension, DataType>{m_degree, m_cell_array[cell_id], m_xj[cell_id]}; + return CenteredCanonicalBasisView<Dimension, DataType>{m_degree, m_cell_array[cell_id], m_xj[cell_id]}; } PUGS_INLINE DiscreteFunctionDPk @@ -165,13 +167,14 @@ class DiscreteFunctionDPk DiscreteFunctionDPk(const std::shared_ptr<const MeshVariant>& mesh_v, size_t degree) : m_mesh_v{mesh_v}, m_degree{degree}, - m_cell_array{mesh_v->connectivity(), TaylorView<Dimension, DataType>::dimensionFromDegree(degree)}, + m_cell_array{mesh_v->connectivity(), + CenteredCanonicalBasisView<Dimension, DataType>::dimensionFromDegree(degree)}, m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} {} DiscreteFunctionDPk(const std::shared_ptr<const MeshVariant>& mesh_v, const CellValue<DataType>& cell_array) : m_mesh_v{mesh_v}, - m_degree{TaylorView<Dimension, DataType>::degreeFromDimension(cell_array.sizeOfArrays())}, + m_degree{CenteredCanonicalBasisView<Dimension, DataType>::degreeFromDimension(cell_array.sizeOfArrays())}, m_cell_array{cell_array}, m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} { -- GitLab From ddde0248c7a1161601f3958db3e971fc701b1590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 3 May 2024 12:44:50 +0200 Subject: [PATCH 009/122] Add evaluation operator for polynomials of 2 variables The evaluation is performed using a recursive Horner method Coefficients are stored in the following order given the shifting value is omitted here for simplicity (ie: xj=0). For a polynomial of degree n depending on x and y, one has 1 y y^2 y^3 ... y^n x x*y x*y^2 x*y^3 ... y^{n-1} . . . x^{n-1} x^{n-1}*y x^n --- src/scheme/DiscreteFunctionDPk.hpp | 60 ++++--- tests/test_DiscreteFunctionDPk.cpp | 241 +++++++++++++++++++++++++---- 2 files changed, 246 insertions(+), 55 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index 53d5ab52e..e3342d0dd 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -24,13 +24,15 @@ class CenteredCanonicalBasisView const TinyVector<Dimension>& m_xj; public: - static size_t + static constexpr size_t dimensionFromDegree(size_t degree) { if constexpr (Dimension == 1) { return degree + 1; + } else if constexpr (Dimension == 2) { + return ((degree + 2) * (degree + 1)) / 2; } else { - throw NotImplementedError("Dimension > 1"); + throw NotImplementedError("Dimension 3"); } } static size_t @@ -49,18 +51,32 @@ class CenteredCanonicalBasisView { if constexpr (Dimension == 1) { const double x_xj = (x - m_xj)[0]; - DataType result = [] { - if constexpr (std::is_arithmetic_v<DataType>) { - return 0; - } else { - return zero; - } - }(); - for (size_t i_coeffiencient = m_coefficients.size() - 1; i_coeffiencient > 0; --i_coeffiencient) { - result = x_xj * (m_coefficients[i_coeffiencient] + result); + DataType result = m_coefficients[m_degree]; + for (ssize_t i_coeffiencient = m_degree - 1; i_coeffiencient >= 0; --i_coeffiencient) { + result = x_xj * result + m_coefficients[i_coeffiencient]; + } + + return result; + } else if constexpr (Dimension == 2) { + const TinyVector X_Xj = x - m_xj; + const double& x_xj = X_Xj[0]; + const double& y_yj = X_Xj[1]; + + size_t i = m_coefficients.size() - 1; + + DataType result = m_coefficients[i]; + --i; + for (ssize_t i_x = m_degree - 1; i_x >= 0; --i_x) { + DataType y_result = m_coefficients[i]; + --i; + for (ssize_t i_y = m_degree - i_x - 1; i_y >= 0; --i_y) { + y_result = y_yj * y_result + m_coefficients[i]; + --i; + } + result = x_xj * result + y_result; } - result += m_coefficients[0]; + return result; } else { throw NotImplementedError("Dimension>2"); @@ -91,7 +107,7 @@ class DiscreteFunctionDPk private: std::shared_ptr<const MeshVariant> m_mesh_v; const size_t m_degree; - CellArray<DataType> m_cell_array; + CellArray<DataType> m_by_cell_coefficients; CellValue<const TinyVector<Dimension>> m_xj; public: @@ -106,7 +122,7 @@ class DiscreteFunctionDPk const CellArray<DataType>& cellArrays() const { - return m_cell_array; + return m_by_cell_coefficients; } PUGS_INLINE std::shared_ptr<const MeshVariant> @@ -118,7 +134,7 @@ class DiscreteFunctionDPk PUGS_FORCEINLINE operator DiscreteFunctionDPk<Dimension, const DataType>() const { - return DiscreteFunctionDPk<Dimension, const DataType>(m_mesh_v, m_cell_array); + return DiscreteFunctionDPk<Dimension, const DataType>(m_mesh_v, m_by_cell_coefficients); } PUGS_INLINE @@ -126,7 +142,7 @@ class DiscreteFunctionDPk fill(const DataType& data) const noexcept { static_assert(not std::is_const_v<DataType>, "Cannot modify ItemValue of const"); - m_cell_array.fill(data); + m_by_cell_coefficients.fill(data); } friend PUGS_INLINE DiscreteFunctionDPk<Dimension, std::remove_const_t<DataType>> @@ -147,35 +163,35 @@ class DiscreteFunctionDPk auto coefficients(const CellId cell_id) const { - return m_cell_array[cell_id]; + return m_by_cell_coefficients[cell_id]; } PUGS_FORCEINLINE CenteredCanonicalBasisView<Dimension, DataType> operator[](const CellId cell_id) const noexcept(NO_ASSERT) { - return CenteredCanonicalBasisView<Dimension, DataType>{m_degree, m_cell_array[cell_id], m_xj[cell_id]}; + return CenteredCanonicalBasisView<Dimension, DataType>{m_degree, m_by_cell_coefficients[cell_id], m_xj[cell_id]}; } PUGS_INLINE DiscreteFunctionDPk operator=(const DiscreteFunctionDPk& f) { Assert(m_mesh_v->id() == f.m_mesh_v->id()); - m_cell_array = f.m_cell_array; + m_by_cell_coefficients = f.m_by_cell_coefficients; return *this; } DiscreteFunctionDPk(const std::shared_ptr<const MeshVariant>& mesh_v, size_t degree) : m_mesh_v{mesh_v}, m_degree{degree}, - m_cell_array{mesh_v->connectivity(), - CenteredCanonicalBasisView<Dimension, DataType>::dimensionFromDegree(degree)}, + m_by_cell_coefficients{mesh_v->connectivity(), + CenteredCanonicalBasisView<Dimension, DataType>::dimensionFromDegree(degree)}, m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} {} DiscreteFunctionDPk(const std::shared_ptr<const MeshVariant>& mesh_v, const CellValue<DataType>& cell_array) : m_mesh_v{mesh_v}, m_degree{CenteredCanonicalBasisView<Dimension, DataType>::degreeFromDimension(cell_array.sizeOfArrays())}, - m_cell_array{cell_array}, + m_by_cell_coefficients{cell_array}, m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} { Assert(mesh_v->connectivity().id() == cell_array.connectivity_ptr()->id()); diff --git a/tests/test_DiscreteFunctionDPk.cpp b/tests/test_DiscreteFunctionDPk.cpp index 02ff3527f..bac479f3d 100644 --- a/tests/test_DiscreteFunctionDPk.cpp +++ b/tests/test_DiscreteFunctionDPk.cpp @@ -19,7 +19,7 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") using R1 = TinyVector<1>; const std::shared_ptr mesh_v = MeshDataBaseForTests::get().cartesian1DMesh(); - const std::shared_ptr mesh = mesh_v->get<Mesh<1>>(); + const std::shared_ptr mesh = mesh_v->get<Mesh<Dimension>>(); auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); @@ -28,12 +28,57 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") DiscreteFunctionDPk<Dimension, double> pk(mesh_v, degree); + std::vector<double> a = {1, 1.4, -6.2, 2.7, 3.1}; + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + auto coefficients = pk.coefficients(cell_id); + for (size_t i = 0; i < a.size(); ++i) { + coefficients[i] = a[i]; + } + }; + + DiscreteFunctionP0<double> p_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { p_xj[cell_id] = pk[cell_id](xj[cell_id]); }); + + REQUIRE(max(p_xj) == 1); + REQUIRE(min(p_xj) == 1); + + DiscreteFunctionP0<double> delta(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const double x0 = 0.3 * Vj[cell_id]; + delta[cell_id] = pk[cell_id](xj[cell_id] + R1{x0}) // + - (a[0] + x0 * a[1] + x0 * x0 * a[2] + x0 * x0 * x0 * a[3] + x0 * x0 * x0 * x0 * a[4]); + }); + + REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); + REQUIRE(min(delta) == Catch::Approx(0.).margin(1E-14)); + } + + SECTION("2D") + { + constexpr size_t Dimension = 2; + + using R2 = TinyVector<2>; + + const std::shared_ptr mesh_v = MeshDataBaseForTests::get().cartesian2DMesh(); + const std::shared_ptr mesh = mesh_v->get<Mesh<Dimension>>(); + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + const size_t degree = 3; + + DiscreteFunctionDPk<Dimension, double> pk(mesh_v, degree); + + const std::vector<double> a = {1, 1.4, -6.2, 3.5, -2.3, 5.2, 6.1, 2.3, 0.5, -1.3}; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { - pk.coefficients(cell_id)[0] = 1; - pk.coefficients(cell_id)[1] = 1.4; - pk.coefficients(cell_id)[2] = -6.2; - pk.coefficients(cell_id)[3] = 2.7; - pk.coefficients(cell_id)[4] = 3.1; + auto coefficients = pk.coefficients(cell_id); + for (size_t i = 0; i < coefficients.size(); ++i) { + coefficients[i] = a[i]; + } }; DiscreteFunctionP0<double> p_xj(mesh_v); @@ -47,8 +92,18 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") parallel_for( mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { const double x0 = 0.3 * Vj[cell_id]; - delta[cell_id] = pk[cell_id](xj[cell_id] + R1{x0}) - - (1 + 1.4 * x0 - 6.2 * x0 * x0 + 2.7 * x0 * x0 * x0 + 3.1 * x0 * x0 * x0 * x0); + const double y0 = 0.2 * Vj[cell_id]; + delta[cell_id] = pk[cell_id](xj[cell_id] + R2{x0, y0}) // + - (a[0] // + + y0 * a[1] // + + y0 * y0 * a[2] // + + y0 * y0 * y0 * a[3] // + + x0 * a[4] // + + x0 * y0 * a[5] // + + x0 * y0 * y0 * a[6] // + + x0 * x0 * a[7] // + + x0 * x0 * y0 * a[8] // + + x0 * x0 * x0 * a[9]); }); REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); @@ -66,7 +121,7 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") using R2 = TinyVector<2>; const std::shared_ptr mesh_v = MeshDataBaseForTests::get().cartesian1DMesh(); - const std::shared_ptr mesh = mesh_v->get<Mesh<1>>(); + const std::shared_ptr mesh = mesh_v->get<Mesh<Dimension>>(); auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); @@ -75,29 +130,83 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") DiscreteFunctionDPk<Dimension, R2> pk(mesh_v, degree); + const std::vector<R2> a = {R2{-1.0, +3.0}, R2{+1.4, +1.9}, R2{-6.2, -1.0}, R2{+2.7, +1.6}, R2{+3.1, -1.3}}; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { - pk.coefficients(cell_id)[0] = R2{-1.0, +3.0}; - pk.coefficients(cell_id)[1] = R2{+1.4, +1.9}; - pk.coefficients(cell_id)[2] = R2{-6.2, -1.0}; - pk.coefficients(cell_id)[3] = R2{+2.7, +1.6}; - pk.coefficients(cell_id)[4] = R2{+3.1, -1.3}; - }; + auto coefficients = pk.coefficients(cell_id); + for (size_t i = 0; i < a.size(); ++i) { + coefficients[i] = a[i]; + } + } DiscreteFunctionP0<double> delta_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(CellId cell_id) { delta_xj[cell_id] = l2Norm(pk[cell_id](xj[cell_id]) - a[0]); }); + + REQUIRE(max(delta_xj) == Catch::Approx(0).margin(1E-14)); + + DiscreteFunctionP0<double> delta(mesh_v); parallel_for( mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { - delta_xj[cell_id] = l2Norm(pk[cell_id](xj[cell_id]) - R2{-1.0, +3.0}); + const double x0 = 0.3 * Vj[cell_id]; + delta[cell_id] = + l2Norm(pk[cell_id](xj[cell_id] + R1{x0}) // + - (a[0] + x0 * a[1] + x0 * x0 * a[2] + x0 * x0 * x0 * a[3] + x0 * x0 * x0 * x0 * a[4])); }); + REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); + } + + SECTION("2D") + { + constexpr size_t Dimension = 2; + + using R2 = TinyVector<2>; + + const std::shared_ptr mesh_v = MeshDataBaseForTests::get().hybrid2DMesh(); + const std::shared_ptr mesh = mesh_v->get<Mesh<Dimension>>(); + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + const size_t degree = 3; + + DiscreteFunctionDPk<Dimension, R2> pk(mesh_v, degree); + + const std::vector<R2> a = {R2{-1.0, +3.0}, R2{+1.4, +1.9}, R2{-6.2, -1.0}, R2{+2.7, +1.6}, R2{+3.1, -1.3}, + R2{+2.5, -4.2}, R2{+2.1, -1.7}, R2{-3.2, +1.0}, R2{-2.3, +1.3}, R2{-2.9, -3.2}}; + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + auto coefficients = pk.coefficients(cell_id); + for (size_t i = 0; i < a.size(); ++i) { + coefficients[i] = a[i]; + } + } + + DiscreteFunctionP0<double> delta_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(CellId cell_id) { delta_xj[cell_id] = l2Norm(pk[cell_id](xj[cell_id]) - a[0]); }); + REQUIRE(max(delta_xj) == Catch::Approx(0).margin(1E-14)); DiscreteFunctionP0<double> delta(mesh_v); parallel_for( mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { const double x0 = 0.3 * Vj[cell_id]; - delta[cell_id] = l2Norm(pk[cell_id](xj[cell_id] + R1{x0}) - - R2{-1 + 1.4 * x0 - 6.2 * x0 * x0 + 2.7 * x0 * x0 * x0 + 3.1 * x0 * x0 * x0 * x0, - 3 + 1.9 * x0 - 1.0 * x0 * x0 + 1.6 * x0 * x0 * x0 - 1.3 * x0 * x0 * x0 * x0}); + const double y0 = 0.2 * Vj[cell_id]; + delta[cell_id] = l2Norm(pk[cell_id](xj[cell_id] + R2{x0, y0}) // + - (a[0] // + + y0 * a[1] // + + y0 * y0 * a[2] // + + y0 * y0 * y0 * a[3] // + + x0 * a[4] // + + x0 * y0 * a[5] // + + x0 * y0 * y0 * a[6] // + + x0 * x0 * a[7] // + + x0 * x0 * y0 * a[8] // + + x0 * x0 * x0 * a[9])); }); REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); @@ -114,7 +223,7 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") using R2x3 = TinyMatrix<2, 3>; const std::shared_ptr mesh_v = MeshDataBaseForTests::get().cartesian1DMesh(); - const std::shared_ptr mesh = mesh_v->get<Mesh<1>>(); + const std::shared_ptr mesh = mesh_v->get<Mesh<Dimension>>(); auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); @@ -123,25 +232,81 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") DiscreteFunctionDPk<Dimension, R2x3> pk(mesh_v, degree); - const R2x3 A{}; - const R2x3 B{}; - const R2x3 C{}; - const R2x3 D{}; - const R2x3 E{}; + std::vector<R2x3> A = {R2x3{+1.0, +2.0, +3.0, +4.0, +5.0, +6.0}, // + R2x3{-1.2, -2.3, +7.2, +8.4, -5.0, +0.7}, // + R2x3{-2.1, -3.3, -2.7, -3.4, -0.5, -2.7}, // + R2x3{+6.2, -2.9, +3.1, -2.6, +1.5, +2.1}, // + R2x3{-2.6, -2.2, +4.2, -1.7, +8.5, -1.4}}; for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { - pk.coefficients(cell_id)[0] = A; - pk.coefficients(cell_id)[1] = B; - pk.coefficients(cell_id)[2] = C; - pk.coefficients(cell_id)[3] = D; - pk.coefficients(cell_id)[4] = E; + auto coefficients = pk.coefficients(cell_id); + for (size_t i = 0; i < coefficients.size(); ++i) { + coefficients[i] = A[i]; + } }; DiscreteFunctionP0<double> delta_xj(mesh_v); parallel_for( mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { - const R2x3 Diff = pk[cell_id](xj[cell_id]) - A; + const R2x3 Diff = pk[cell_id](xj[cell_id]) - A[0]; + + delta_xj[cell_id] = l2Norm((transpose(Diff) * Diff) * TinyVector<3>(1, 1, 1)); + }); + + REQUIRE(max(delta_xj) == Catch::Approx(0).margin(1E-14)); + + DiscreteFunctionP0<double> delta(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const double x0 = 0.3 * Vj[cell_id]; + const R2x3 Diff = pk[cell_id](xj[cell_id] + R1{x0}) - + (A[0] + x0 * A[1] + x0 * x0 * A[2] + x0 * x0 * x0 * A[3] + x0 * x0 * x0 * x0 * A[4]); + delta[cell_id] = l2Norm((transpose(Diff) * Diff) * TinyVector<3>(1, 1, 1)); + }); + + REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); + } + + SECTION("2D") + { + constexpr size_t Dimension = 2; + + using R2 = TinyVector<2>; + using R2x3 = TinyMatrix<2, 3>; + + const std::shared_ptr mesh_v = MeshDataBaseForTests::get().cartesian2DMesh(); + const std::shared_ptr mesh = mesh_v->get<Mesh<Dimension>>(); + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + const size_t degree = 3; + + DiscreteFunctionDPk<Dimension, R2x3> pk(mesh_v, degree); + + std::vector<R2x3> A = {R2x3{+1.0, +2.0, +3.0, +4.0, +5.0, +6.0}, // + R2x3{-1.2, -2.3, +7.2, +8.4, -5.0, +0.7}, // + R2x3{-2.1, -3.3, -2.7, -3.4, -0.5, -2.7}, // + R2x3{+6.2, -2.9, +3.1, -2.6, +1.5, +2.1}, // + R2x3{-2.6, -2.2, +4.2, -1.7, +8.5, -1.4}, // + R2x3{+1.7, +3.1, +3.0, +0.4, +3.4, +4.3}, // + R2x3{+2.5, +2.3, +4.7, -8.7, -5.0, +2.4}, // + R2x3{-3.6, -1.3, -1.3, -4.1, -0.5, -6.2}, // + R2x3{+6.4, -2.9, +2.3, -6.1, +1.5, -1.9}, // + R2x3{-7.3, -3.2, +4.1, +2.7, +8.5, -6.9}}; + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + auto coefficients = pk.coefficients(cell_id); + for (size_t i = 0; i < coefficients.size(); ++i) { + coefficients[i] = A[i]; + } + }; + + DiscreteFunctionP0<double> delta_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const R2x3 Diff = pk[cell_id](xj[cell_id]) - A[0]; delta_xj[cell_id] = l2Norm((transpose(Diff) * Diff) * TinyVector<3>(1, 1, 1)); }); @@ -151,8 +316,18 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") parallel_for( mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { const double x0 = 0.3 * Vj[cell_id]; - const R2x3 Diff = - pk[cell_id](xj[cell_id] + R1{x0}) - (A + x0 * B + x0 * x0 * C + x0 * x0 * x0 * D + x0 * x0 * x0 * x0 * E); + const double y0 = 0.2 * Vj[cell_id]; + const R2x3 Diff = pk[cell_id](xj[cell_id] + R2{x0, y0}) // + - (A[0] // + + y0 * A[1] // + + y0 * y0 * A[2] // + + y0 * y0 * y0 * A[3] // + + x0 * A[4] // + + x0 * y0 * A[5] // + + x0 * y0 * y0 * A[6] // + + x0 * x0 * A[7] // + + x0 * x0 * y0 * A[8] // + + x0 * x0 * x0 * A[9]); delta[cell_id] = l2Norm((transpose(Diff) * Diff) * TinyVector<3>(1, 1, 1)); }); -- GitLab From 4e280da954b43b8b8af4a2993506b97a45af3d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 3 May 2024 12:52:59 +0200 Subject: [PATCH 010/122] Add a simple comment describing the ordering of coefficients --- src/scheme/DiscreteFunctionDPk.hpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index e3342d0dd..f24ca6f23 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -20,6 +20,23 @@ class CenteredCanonicalBasisView const size_t m_degree; + // Coefficients are stored in the following order given the shifting + // value is omitted here for simplicity (ie: xj=0). + // + // ---------------------------------------------------- + // For a polynomial of degree n depending on x, one has + // 1 x x^2 x^3 ... x^n + // + // ---------------------------------------------------- + // For a polynomial of degree n depending on x and y, one has + // 1 y y^2 y^3 ... y^n + // x x*y x*y^2 x*y^3 ... y^{n-1} + // . + // . + // . + // x^{n-1} x^{n-1}*y + // x^n + std::span<DataType> m_coefficients; const TinyVector<Dimension>& m_xj; -- GitLab From 18b809a5fff90e83ee60b01df5d4962b63fc4e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 3 May 2024 15:23:40 +0200 Subject: [PATCH 011/122] Add degreeFromDimension for polynomials of 2 variables --- src/scheme/DiscreteFunctionDPk.hpp | 17 ++++++++++++----- tests/test_DiscreteFunctionDPk.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index f24ca6f23..47616a96d 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -41,7 +41,7 @@ class CenteredCanonicalBasisView const TinyVector<Dimension>& m_xj; public: - static constexpr size_t + static size_t dimensionFromDegree(size_t degree) { if constexpr (Dimension == 1) { @@ -53,18 +53,25 @@ class CenteredCanonicalBasisView } } static size_t - degreeFromDimension(size_t polynomial_dimension) + degreeFromDimension(size_t polynomial_basis_dimension) { + Assert(polynomial_basis_dimension > 0); if constexpr (Dimension == 1) { - Assert(polynomial_dimension > 1); - return polynomial_dimension - 1; + return polynomial_basis_dimension - 1; + } else if constexpr (Dimension == 2) { + const size_t degree = std::round((std::sqrt(8 * polynomial_basis_dimension + 1) - 1) / 2) - 1; + + if (dimensionFromDegree(degree) != polynomial_basis_dimension) { + throw NormalError("incorrect polynomial basis dimension"); + } + return degree; } else { throw NotImplementedError("Dimension > 1"); } } DataType - operator()(const TinyVector<Dimension> x) const + operator()(const TinyVector<Dimension>& x) const { if constexpr (Dimension == 1) { const double x_xj = (x - m_xj)[0]; diff --git a/tests/test_DiscreteFunctionDPk.cpp b/tests/test_DiscreteFunctionDPk.cpp index bac479f3d..42abf3d87 100644 --- a/tests/test_DiscreteFunctionDPk.cpp +++ b/tests/test_DiscreteFunctionDPk.cpp @@ -335,4 +335,34 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); } } + + SECTION("degree and polynomial basis size") + { + SECTION("1D") + { + for (size_t degree = 0; degree < 10; ++degree) { + size_t polynomial_basis_dimension = CenteredCanonicalBasisView<1, double>::dimensionFromDegree(degree); + REQUIRE(polynomial_basis_dimension == degree + 1); + REQUIRE(CenteredCanonicalBasisView<1, double>::degreeFromDimension(polynomial_basis_dimension) == degree); + } + } + + SECTION("2D") + { + for (size_t degree = 0; degree < 10; ++degree) { + size_t polynomial_basis_dimension = CenteredCanonicalBasisView<2, double>::dimensionFromDegree(degree); + REQUIRE(2 * polynomial_basis_dimension == (degree + 1) * (degree + 2)); + REQUIRE(CenteredCanonicalBasisView<2, double>::degreeFromDimension(polynomial_basis_dimension) == degree); + } + + REQUIRE_THROWS_WITH((CenteredCanonicalBasisView<2, double>::degreeFromDimension(2)), + "error: incorrect polynomial basis dimension"); + REQUIRE_THROWS_WITH((CenteredCanonicalBasisView<2, double>::degreeFromDimension(4)), + "error: incorrect polynomial basis dimension"); + REQUIRE_THROWS_WITH((CenteredCanonicalBasisView<2, double>::degreeFromDimension(5)), + "error: incorrect polynomial basis dimension"); + REQUIRE_THROWS_WITH((CenteredCanonicalBasisView<2, double>::degreeFromDimension(7)), + "error: incorrect polynomial basis dimension"); + } + } } -- GitLab From 5b02e0b1159e36eeb9713aa8cd032df5a700fa25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 3 May 2024 15:49:43 +0200 Subject: [PATCH 012/122] Add BasisView as a template parameter. This template parameter is defaulted to CenteredCanonicalBasisView<Dimension, DataType> --- src/scheme/DiscreteFunctionDPk.hpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index 47616a96d..2fb7078e4 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -119,7 +119,7 @@ class CenteredCanonicalBasisView ~CenteredCanonicalBasisView() = default; }; -template <size_t Dimension, typename DataType> +template <size_t Dimension, typename DataType, typename BasisView = CenteredCanonicalBasisView<Dimension, DataType>> class DiscreteFunctionDPk { public: @@ -190,10 +190,10 @@ class DiscreteFunctionDPk return m_by_cell_coefficients[cell_id]; } - PUGS_FORCEINLINE CenteredCanonicalBasisView<Dimension, DataType> + PUGS_FORCEINLINE BasisView operator[](const CellId cell_id) const noexcept(NO_ASSERT) { - return CenteredCanonicalBasisView<Dimension, DataType>{m_degree, m_by_cell_coefficients[cell_id], m_xj[cell_id]}; + return BasisView{m_degree, m_by_cell_coefficients[cell_id], m_xj[cell_id]}; } PUGS_INLINE DiscreteFunctionDPk @@ -207,14 +207,13 @@ class DiscreteFunctionDPk DiscreteFunctionDPk(const std::shared_ptr<const MeshVariant>& mesh_v, size_t degree) : m_mesh_v{mesh_v}, m_degree{degree}, - m_by_cell_coefficients{mesh_v->connectivity(), - CenteredCanonicalBasisView<Dimension, DataType>::dimensionFromDegree(degree)}, + m_by_cell_coefficients{mesh_v->connectivity(), BasisView::dimensionFromDegree(degree)}, m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} {} DiscreteFunctionDPk(const std::shared_ptr<const MeshVariant>& mesh_v, const CellValue<DataType>& cell_array) : m_mesh_v{mesh_v}, - m_degree{CenteredCanonicalBasisView<Dimension, DataType>::degreeFromDimension(cell_array.sizeOfArrays())}, + m_degree{BasisView::degreeFromDimension(cell_array.sizeOfArrays())}, m_by_cell_coefficients{cell_array}, m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} { -- GitLab From 1ca13469e65d046450e27c45e5285d53969d1c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 3 May 2024 19:30:58 +0200 Subject: [PATCH 013/122] Polynomials of 3 variables with Horner-like evaluation --- src/scheme/DiscreteFunctionDPk.hpp | 38 +++-- tests/test_DiscreteFunctionDPk.cpp | 219 +++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+), 10 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index 2fb7078e4..fb526e6d9 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -16,6 +16,7 @@ template <size_t Dimension, typename DataType> class CenteredCanonicalBasisView { private: + static_assert((Dimension > 0) and (Dimension <= 3), "invalid dimension"); using CoefficientList = typename Table<DataType>::UnsafeRowView; const size_t m_degree; @@ -48,8 +49,8 @@ class CenteredCanonicalBasisView return degree + 1; } else if constexpr (Dimension == 2) { return ((degree + 2) * (degree + 1)) / 2; - } else { - throw NotImplementedError("Dimension 3"); + } else { // Dimension == 3 + return ((degree + 3) * (degree + 2) * (degree + 1)) / 6; } } static size_t @@ -89,21 +90,38 @@ class CenteredCanonicalBasisView size_t i = m_coefficients.size() - 1; - DataType result = m_coefficients[i]; - --i; + DataType result = m_coefficients[i--]; for (ssize_t i_x = m_degree - 1; i_x >= 0; --i_x) { - DataType y_result = m_coefficients[i]; - --i; + DataType y_result = m_coefficients[i--]; for (ssize_t i_y = m_degree - i_x - 1; i_y >= 0; --i_y) { - y_result = y_yj * y_result + m_coefficients[i]; - --i; + y_result = y_yj * y_result + m_coefficients[i--]; + } + result = x_xj * result + y_result; + } + + return result; + } else { // Dimension == 3 + const TinyVector X_Xj = x - m_xj; + const double& x_xj = X_Xj[0]; + const double& y_yj = X_Xj[1]; + const double& z_zj = X_Xj[2]; + + size_t i = m_coefficients.size() - 1; + + DataType result = m_coefficients[i--]; + for (ssize_t i_x = m_degree - 1; i_x >= 0; --i_x) { + DataType y_result = m_coefficients[i--]; + for (ssize_t i_y = m_degree - i_x - 1; i_y >= 0; --i_y) { + DataType z_result = m_coefficients[i--]; + for (ssize_t i_z = m_degree - i_x - i_y - 1; i_z >= 0; --i_z) { + z_result = z_zj * z_result + m_coefficients[i--]; + } + y_result = y_yj * y_result + z_result; } result = x_xj * result + y_result; } return result; - } else { - throw NotImplementedError("Dimension>2"); } } diff --git a/tests/test_DiscreteFunctionDPk.cpp b/tests/test_DiscreteFunctionDPk.cpp index 42abf3d87..4db093ab1 100644 --- a/tests/test_DiscreteFunctionDPk.cpp +++ b/tests/test_DiscreteFunctionDPk.cpp @@ -109,6 +109,75 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); REQUIRE(min(delta) == Catch::Approx(0.).margin(1E-14)); } + + SECTION("3D") + { + constexpr size_t Dimension = 3; + + using R3 = TinyVector<3>; + + const std::shared_ptr mesh_v = MeshDataBaseForTests::get().cartesian3DMesh(); + const std::shared_ptr mesh = mesh_v->get<Mesh<Dimension>>(); + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + const size_t degree = 3; + + DiscreteFunctionDPk<Dimension, double> pk(mesh_v, degree); + + const std::vector<double> a = {+1.0, +1.4, -6.2, +3.5, -2.3, +5.2, +6.1, +2.3, +0.5, -1.3, // + +2.8, -8.4, +9.5, +4.0, +4.3, +7.2, -9.1, +6.8, +6.7, +9.2}; + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + auto coefficients = pk.coefficients(cell_id); + for (size_t i = 0; i < coefficients.size(); ++i) { + coefficients[i] = a[i]; + } + }); + + DiscreteFunctionP0<double> delta_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { delta_xj[cell_id] = pk[cell_id](xj[cell_id]) - a[0]; }); + + REQUIRE(max(delta_xj) == Catch::Approx(0.).margin(1E-14)); + REQUIRE(min(delta_xj) == Catch::Approx(0.).margin(1E-14)); + + DiscreteFunctionP0<double> delta(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const double x0 = +0.5 * Vj[cell_id]; + const double y0 = +0.3 * Vj[cell_id]; + const double z0 = -0.4 * Vj[cell_id]; + + delta[cell_id] = pk[cell_id](xj[cell_id] + R3{x0, y0, z0}) // + - (a[0] // + + z0 * a[1] // + + z0 * z0 * a[2] // + + z0 * z0 * z0 * a[3] // + + y0 * a[4] // + + y0 * z0 * a[5] // + + y0 * z0 * z0 * a[6] // + + y0 * y0 * a[7] // + + y0 * y0 * z0 * a[8] // + + y0 * y0 * y0 * a[9] // + + x0 * a[10] // + + x0 * z0 * a[11] // + + x0 * z0 * z0 * a[12] // + + x0 * y0 * a[13] // + + x0 * y0 * z0 * a[14] // + + x0 * y0 * y0 * a[15] // + + x0 * x0 * a[16] // + + x0 * x0 * z0 * a[17] // + + x0 * x0 * y0 * a[18] // + + x0 * x0 * x0 * a[19] // + ); + }); + + REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); + REQUIRE(min(delta) == Catch::Approx(0.).margin(1E-14)); + } } SECTION("R^d data") @@ -211,6 +280,76 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); } + + SECTION("3D") + { + constexpr size_t Dimension = 3; + + using R2 = TinyVector<2>; + using R3 = TinyVector<3>; + + const std::shared_ptr mesh_v = MeshDataBaseForTests::get().hybrid3DMesh(); + const std::shared_ptr mesh = mesh_v->get<Mesh<Dimension>>(); + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + const size_t degree = 3; + + const std::vector<R2> a = {R2{+9.2, +7.2}, R2{+0.0, -2.7}, R2{+4.8, -5.8}, R2{+6.0, +3.6}, R2{-7.0, -9.7}, + R2{+5.3, -1.2}, R2{-1.2, +3.7}, R2{-0.4, +6.1}, R2{+8.4, +9.5}, R2{-9.7, +3.3}, + R2{+0.5, +4.2}, R2{+3.8, +3.3}, R2{+8.0, -10.0}, R2{-5.1, -4.1}, R2{+2.6, -2.5}, + R2{-3.4, -2.7}, R2{+0.7, +4.9}, R2{+6.0, +6.4}, R2{+3.5, +5.0}, R2{+1.7, +4.8}}; + + DiscreteFunctionDPk<Dimension, R2> pk(mesh_v, degree); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + auto coefficients = pk.coefficients(cell_id); + for (size_t i = 0; i < coefficients.size(); ++i) { + coefficients[i] = a[i]; + } + }); + + DiscreteFunctionP0<double> delta_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(CellId cell_id) { delta_xj[cell_id] = l2Norm(pk[cell_id](xj[cell_id]) - a[0]); }); + + REQUIRE(max(delta_xj) == Catch::Approx(0.).margin(1E-14)); + + DiscreteFunctionP0<double> delta(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const double x0 = +0.5 * Vj[cell_id]; + const double y0 = +0.3 * Vj[cell_id]; + const double z0 = -0.4 * Vj[cell_id]; + + delta[cell_id] = l2Norm(pk[cell_id](xj[cell_id] + R3{x0, y0, z0}) // + - (a[0] // + + z0 * a[1] // + + z0 * z0 * a[2] // + + z0 * z0 * z0 * a[3] // + + y0 * a[4] // + + y0 * z0 * a[5] // + + y0 * z0 * z0 * a[6] // + + y0 * y0 * a[7] // + + y0 * y0 * z0 * a[8] // + + y0 * y0 * y0 * a[9] // + + x0 * a[10] // + + x0 * z0 * a[11] // + + x0 * z0 * z0 * a[12] // + + x0 * y0 * a[13] // + + x0 * y0 * z0 * a[14] // + + x0 * y0 * y0 * a[15] // + + x0 * x0 * a[16] // + + x0 * x0 * z0 * a[17] // + + x0 * x0 * y0 * a[18] // + + x0 * x0 * x0 * a[19] // + )); + }); + + REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); + } } SECTION("R^d1xd2 data") @@ -334,6 +473,86 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); } + + SECTION("3D") + { + constexpr size_t Dimension = 3; + + using R2x3 = TinyMatrix<2, 3>; + using R3 = TinyVector<3>; + + const std::shared_ptr mesh_v = MeshDataBaseForTests::get().hybrid3DMesh(); + const std::shared_ptr mesh = mesh_v->get<Mesh<Dimension>>(); + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + const size_t degree = 3; + + const std::vector<R2x3> A = {R2x3{-3.1, +0.7, -4.2, -6.6, -2.3, +7.2}, R2x3{-5.1, -4.5, +9.6, +0.8, -3.2, +3.7}, + R2x3{-2.4, +3.8, -2.5, +0.7, -6.5, -7.7}, R2x3{+3.2, +7.6, +7.8, -4.1, +1.6, +9.9}, + R2x3{-0.8, +6.1, -2.4, -1.1, -4.1, +0.6}, R2x3{+6.3, +1.0, +0.0, -9.4, +4.4, +1.2}, + R2x3{-1.1, +9.8, +10., -4.9, -2.4, -4.3}, R2x3{+6.9, +4.2, +8.8, +2.0, +3.4, +6.4}, + R2x3{+6.5, -6.3, +7.1, +8.7, -1.9, -9.7}, R2x3{+3.3, +1.5, -8.2, +8.1, +2.9, +3.3}, + R2x3{+6.9, -9.8, -2.5, -6.8, +0.9, +9.4}, R2x3{+4.3, +3.0, +2.2, -8.8, +3.0, -3.2}, + R2x3{-4.7, +9.0, +2.7, -0.3, -8.1, -8.6}, R2x3{+1.0, +1.7, -3.9, +7.8, -1.2, +5.6}, + R2x3{-2.1, +6.1, -8.7, +4.3, -1.9, -9.3}, R2x3{-0.3, -8.3, -9.7, +9.4, -9.7, +3.8}, + R2x3{+9.2, +7.1, +9.1, -9.1, +6.8, -5.2}, R2x3{-9.1, +4.8, +5.3, +9.4, -1.2, -9.2}, + R2x3{+1.3, -8.7, -1.2, +2.7, -1.8, -1.6}, R2x3{+1.0, -9.7, +1.0, +9.2, -0.1, -4.9}}; + + DiscreteFunctionDPk<Dimension, R2x3> pk(mesh_v, degree); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + auto coefficients = pk.coefficients(cell_id); + for (size_t i = 0; i < coefficients.size(); ++i) { + coefficients[i] = A[i]; + } + }); + + DiscreteFunctionP0<double> delta_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const R2x3 Diff = pk[cell_id](xj[cell_id]) - A[0]; + delta_xj[cell_id] = l2Norm(transpose(Diff) * Diff * TinyVector<3>{1, 1, 1}); + }); + + REQUIRE(max(delta_xj) == Catch::Approx(0.).margin(1E-14)); + + DiscreteFunctionP0<double> delta(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const double x0 = +0.5 * Vj[cell_id]; + const double y0 = +0.3 * Vj[cell_id]; + const double z0 = -0.4 * Vj[cell_id]; + + const R2x3 Diff = pk[cell_id](xj[cell_id] + R3{x0, y0, z0}) // + - (A[0] // + + z0 * A[1] // + + z0 * z0 * A[2] // + + z0 * z0 * z0 * A[3] // + + y0 * A[4] // + + y0 * z0 * A[5] // + + y0 * z0 * z0 * A[6] // + + y0 * y0 * A[7] // + + y0 * y0 * z0 * A[8] // + + y0 * y0 * y0 * A[9] // + + x0 * A[10] // + + x0 * z0 * A[11] // + + x0 * z0 * z0 * A[12] // + + x0 * y0 * A[13] // + + x0 * y0 * z0 * A[14] // + + x0 * y0 * y0 * A[15] // + + x0 * x0 * A[16] // + + x0 * x0 * z0 * A[17] // + + x0 * x0 * y0 * A[18] // + + x0 * x0 * x0 * A[19] // + ); + + delta[cell_id] = l2Norm(transpose(Diff) * Diff * TinyVector<3>{1, 1, 1}); + }); + + REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); + } } SECTION("degree and polynomial basis size") -- GitLab From 79dd4b5e33b2fe82c4890ed3fd856df1298677cd Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Sat, 4 May 2024 13:16:20 +0200 Subject: [PATCH 014/122] Change the ordering of coefficients to a more natural way 1 variable: 1, x, x^2, ..., x^n 2 variables: 1, x, x^2, ..., x^{n-1}, x^n y, x y, x^2 y, ..., x^{n-1} y ... y^{n-1}, x y^{n-1} y^n 3 variables: 1, x, x^2, ..., x^{n-1}, x^n y, x y, x^2 y, ..., x^{n-1} y ... y^{n-1}, x y^{n-1} y^n ... z, x z, x^2 z, ..., x^{n-2} z, x^{n-1} z y z, x y z, x^2 y z, ..., x^{n-2} y z ... y^{n-2} z, x y^{n-2} z y^{n -1} z ... ... z^{n-2}, x z^{n-2}, x^2 z^{n-2} y z^{n-2}, x y z^{n-2} y^2 z^{n-2} z^{n-1}, x z^{n-1} y z^{n-1} z^n --- src/scheme/DiscreteFunctionDPk.hpp | 90 ++++++++----- tests/test_DiscreteFunctionDPk.cpp | 195 +++++++++++++++-------------- 2 files changed, 160 insertions(+), 125 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index fb526e6d9..641faa796 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -13,7 +13,7 @@ #include <span> template <size_t Dimension, typename DataType> -class CenteredCanonicalBasisView +class PolynomialCenteredCanonicalBasisView { private: static_assert((Dimension > 0) and (Dimension <= 3), "invalid dimension"); @@ -26,17 +26,39 @@ class CenteredCanonicalBasisView // // ---------------------------------------------------- // For a polynomial of degree n depending on x, one has - // 1 x x^2 x^3 ... x^n + // 1, x, x^2, ..., x^n // // ---------------------------------------------------- // For a polynomial of degree n depending on x and y, one has - // 1 y y^2 y^3 ... y^n - // x x*y x*y^2 x*y^3 ... y^{n-1} - // . - // . - // . - // x^{n-1} x^{n-1}*y - // x^n + // 1, x, x^2, ..., x^{n-1}, x^n + // y, x y, x^2 y, ..., x^{n-1} y + // ... + // y^{n-1}, x y^{n-1} + // y^n + // + // ---------------------------------------------------- + // For a polynomial of degree n depending on x, y and z, one has + // 1, x, x^2, ..., x^{n-1}, x^n + // y, x y, x^2 y, ..., x^{n-1} y + // ... + // y^{n-1}, x y^{n-1} + // y^n + // + // z, x z, x^2 z, ..., x^{n-2} z, x^{n-1} z + // y z, x y z, x^2 y z, ..., x^{n-2} y z + // ... + // y^{n-2} z, x y^{n-2} z + // y^{n -1} z + // ... + // ... + // z^{n-2}, x z^{n-2}, x^2 z^{n-2} + // y z^{n-2}, x y z^{n-2} + // y^2 z^{n-2} + // + // z^{n-1}, x z^{n-1} + // y z^{n-1} + // + // z^n std::span<DataType> m_coefficients; const TinyVector<Dimension>& m_xj; @@ -91,12 +113,12 @@ class CenteredCanonicalBasisView size_t i = m_coefficients.size() - 1; DataType result = m_coefficients[i--]; - for (ssize_t i_x = m_degree - 1; i_x >= 0; --i_x) { - DataType y_result = m_coefficients[i--]; - for (ssize_t i_y = m_degree - i_x - 1; i_y >= 0; --i_y) { - y_result = y_yj * y_result + m_coefficients[i--]; + for (ssize_t i_y = m_degree - 1; i_y >= 0; --i_y) { + DataType x_result = m_coefficients[i--]; + for (ssize_t i_x = m_degree - i_y - 1; i_x >= 0; --i_x) { + x_result = x_xj * x_result + m_coefficients[i--]; } - result = x_xj * result + y_result; + result = y_yj * result + x_result; } return result; @@ -109,35 +131,45 @@ class CenteredCanonicalBasisView size_t i = m_coefficients.size() - 1; DataType result = m_coefficients[i--]; - for (ssize_t i_x = m_degree - 1; i_x >= 0; --i_x) { + for (ssize_t i_z = m_degree - 1; i_z >= 0; --i_z) { DataType y_result = m_coefficients[i--]; - for (ssize_t i_y = m_degree - i_x - 1; i_y >= 0; --i_y) { - DataType z_result = m_coefficients[i--]; - for (ssize_t i_z = m_degree - i_x - i_y - 1; i_z >= 0; --i_z) { - z_result = z_zj * z_result + m_coefficients[i--]; + for (ssize_t i_y = m_degree - i_z - 1; i_y >= 0; --i_y) { + DataType x_result = m_coefficients[i--]; + for (ssize_t i_x = m_degree - i_z - i_y - 1; i_x >= 0; --i_x) { + x_result = x_xj * x_result + m_coefficients[i--]; } - y_result = y_yj * y_result + z_result; + y_result = y_yj * y_result + x_result; } - result = x_xj * result + y_result; + result = z_zj * result + y_result; } return result; } } - CenteredCanonicalBasisView(const size_t degree, - const CoefficientList& coefficient_list, - const TinyVector<Dimension>& xj) + template <typename ArrayType> + PolynomialCenteredCanonicalBasisView(const size_t degree, + ArrayType& coefficient_list, + const TinyVector<Dimension>& xj) + : m_degree{degree}, m_coefficients{&(coefficient_list[0]), coefficient_list.size()}, m_xj{xj} + {} + + template <typename ArrayType> + PolynomialCenteredCanonicalBasisView(const size_t degree, + const ArrayType& coefficient_list, + const TinyVector<Dimension>& xj) : m_degree{degree}, m_coefficients{&(coefficient_list[0]), coefficient_list.size()}, m_xj{xj} {} - CenteredCanonicalBasisView(const CenteredCanonicalBasisView&) = delete; - CenteredCanonicalBasisView(CenteredCanonicalBasisView&&) = default; - CenteredCanonicalBasisView() = delete; - ~CenteredCanonicalBasisView() = default; + PolynomialCenteredCanonicalBasisView(const PolynomialCenteredCanonicalBasisView&) = delete; + PolynomialCenteredCanonicalBasisView(PolynomialCenteredCanonicalBasisView&&) = default; + PolynomialCenteredCanonicalBasisView() = delete; + ~PolynomialCenteredCanonicalBasisView() = default; }; -template <size_t Dimension, typename DataType, typename BasisView = CenteredCanonicalBasisView<Dimension, DataType>> +template <size_t Dimension, + typename DataType, + typename BasisView = PolynomialCenteredCanonicalBasisView<Dimension, DataType>> class DiscreteFunctionDPk { public: diff --git a/tests/test_DiscreteFunctionDPk.cpp b/tests/test_DiscreteFunctionDPk.cpp index 4db093ab1..e303e5757 100644 --- a/tests/test_DiscreteFunctionDPk.cpp +++ b/tests/test_DiscreteFunctionDPk.cpp @@ -88,26 +88,25 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") REQUIRE(max(p_xj) == 1); REQUIRE(min(p_xj) == 1); - DiscreteFunctionP0<double> delta(mesh_v); + DiscreteFunctionP0<double> error(mesh_v); parallel_for( mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { - const double x0 = 0.3 * Vj[cell_id]; - const double y0 = 0.2 * Vj[cell_id]; - delta[cell_id] = pk[cell_id](xj[cell_id] + R2{x0, y0}) // - - (a[0] // - + y0 * a[1] // - + y0 * y0 * a[2] // - + y0 * y0 * y0 * a[3] // - + x0 * a[4] // - + x0 * y0 * a[5] // - + x0 * y0 * y0 * a[6] // - + x0 * x0 * a[7] // - + x0 * x0 * y0 * a[8] // - + x0 * x0 * x0 * a[9]); + const double x0 = 0.2 * Vj[cell_id]; + const double y0 = 0.3 * Vj[cell_id]; + error[cell_id] = std::abs(pk[cell_id](xj[cell_id] + R2{x0, y0}) // + - (a[0] // + + x0 * a[1] // + + x0 * x0 * a[2] // + + x0 * x0 * x0 * a[3] // + + y0 * a[4] // + + y0 * x0 * a[5] // + + y0 * x0 * x0 * a[6] // + + y0 * y0 * a[7] // + + y0 * y0 * x0 * a[8] // + + y0 * y0 * y0 * a[9])); }); - REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); - REQUIRE(min(delta) == Catch::Approx(0.).margin(1E-14)); + REQUIRE(max(error) == Catch::Approx(0.).margin(1E-14)); } SECTION("3D") @@ -139,10 +138,10 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") DiscreteFunctionP0<double> delta_xj(mesh_v); parallel_for( - mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { delta_xj[cell_id] = pk[cell_id](xj[cell_id]) - a[0]; }); + mesh->numberOfCells(), + PUGS_LAMBDA(CellId cell_id) { delta_xj[cell_id] = std::abs(pk[cell_id](xj[cell_id]) - a[0]); }); REQUIRE(max(delta_xj) == Catch::Approx(0.).margin(1E-14)); - REQUIRE(min(delta_xj) == Catch::Approx(0.).margin(1E-14)); DiscreteFunctionP0<double> delta(mesh_v); parallel_for( @@ -153,25 +152,25 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") delta[cell_id] = pk[cell_id](xj[cell_id] + R3{x0, y0, z0}) // - (a[0] // - + z0 * a[1] // - + z0 * z0 * a[2] // - + z0 * z0 * z0 * a[3] // + + x0 * a[1] // + + x0 * x0 * a[2] // + + x0 * x0 * x0 * a[3] // + y0 * a[4] // - + y0 * z0 * a[5] // - + y0 * z0 * z0 * a[6] // + + y0 * x0 * a[5] // + + y0 * x0 * x0 * a[6] // + y0 * y0 * a[7] // - + y0 * y0 * z0 * a[8] // + + y0 * y0 * x0 * a[8] // + y0 * y0 * y0 * a[9] // - + x0 * a[10] // - + x0 * z0 * a[11] // - + x0 * z0 * z0 * a[12] // - + x0 * y0 * a[13] // - + x0 * y0 * z0 * a[14] // - + x0 * y0 * y0 * a[15] // - + x0 * x0 * a[16] // - + x0 * x0 * z0 * a[17] // - + x0 * x0 * y0 * a[18] // - + x0 * x0 * x0 * a[19] // + + z0 * a[10] // + + z0 * x0 * a[11] // + + z0 * x0 * x0 * a[12] // + + z0 * y0 * a[13] // + + z0 * y0 * x0 * a[14] // + + z0 * y0 * y0 * a[15] // + + z0 * z0 * a[16] // + + z0 * z0 * x0 * a[17] // + + z0 * z0 * y0 * a[18] // + + z0 * z0 * z0 * a[19] // ); }); @@ -263,19 +262,19 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") DiscreteFunctionP0<double> delta(mesh_v); parallel_for( mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { - const double x0 = 0.3 * Vj[cell_id]; - const double y0 = 0.2 * Vj[cell_id]; + const double x0 = 0.2 * Vj[cell_id]; + const double y0 = 0.3 * Vj[cell_id]; delta[cell_id] = l2Norm(pk[cell_id](xj[cell_id] + R2{x0, y0}) // - (a[0] // - + y0 * a[1] // - + y0 * y0 * a[2] // - + y0 * y0 * y0 * a[3] // - + x0 * a[4] // - + x0 * y0 * a[5] // - + x0 * y0 * y0 * a[6] // - + x0 * x0 * a[7] // - + x0 * x0 * y0 * a[8] // - + x0 * x0 * x0 * a[9])); + + x0 * a[1] // + + x0 * x0 * a[2] // + + x0 * x0 * x0 * a[3] // + + y0 * a[4] // + + y0 * x0 * a[5] // + + y0 * x0 * x0 * a[6] // + + y0 * y0 * a[7] // + + y0 * y0 * x0 * a[8] // + + y0 * y0 * y0 * a[9])); }); REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); @@ -326,25 +325,25 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") delta[cell_id] = l2Norm(pk[cell_id](xj[cell_id] + R3{x0, y0, z0}) // - (a[0] // - + z0 * a[1] // - + z0 * z0 * a[2] // - + z0 * z0 * z0 * a[3] // + + x0 * a[1] // + + x0 * x0 * a[2] // + + x0 * x0 * x0 * a[3] // + y0 * a[4] // - + y0 * z0 * a[5] // - + y0 * z0 * z0 * a[6] // + + y0 * x0 * a[5] // + + y0 * x0 * x0 * a[6] // + y0 * y0 * a[7] // - + y0 * y0 * z0 * a[8] // + + y0 * y0 * x0 * a[8] // + y0 * y0 * y0 * a[9] // - + x0 * a[10] // - + x0 * z0 * a[11] // - + x0 * z0 * z0 * a[12] // - + x0 * y0 * a[13] // - + x0 * y0 * z0 * a[14] // - + x0 * y0 * y0 * a[15] // - + x0 * x0 * a[16] // - + x0 * x0 * z0 * a[17] // - + x0 * x0 * y0 * a[18] // - + x0 * x0 * x0 * a[19] // + + z0 * a[10] // + + z0 * x0 * a[11] // + + z0 * x0 * x0 * a[12] // + + z0 * y0 * a[13] // + + z0 * y0 * x0 * a[14] // + + z0 * y0 * y0 * a[15] // + + z0 * z0 * a[16] // + + z0 * z0 * x0 * a[17] // + + z0 * z0 * y0 * a[18] // + + z0 * z0 * z0 * a[19] // )); }); @@ -454,19 +453,19 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") DiscreteFunctionP0<double> delta(mesh_v); parallel_for( mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { - const double x0 = 0.3 * Vj[cell_id]; - const double y0 = 0.2 * Vj[cell_id]; + const double x0 = 0.2 * Vj[cell_id]; + const double y0 = 0.3 * Vj[cell_id]; const R2x3 Diff = pk[cell_id](xj[cell_id] + R2{x0, y0}) // - (A[0] // - + y0 * A[1] // - + y0 * y0 * A[2] // - + y0 * y0 * y0 * A[3] // - + x0 * A[4] // - + x0 * y0 * A[5] // - + x0 * y0 * y0 * A[6] // - + x0 * x0 * A[7] // - + x0 * x0 * y0 * A[8] // - + x0 * x0 * x0 * A[9]); + + x0 * A[1] // + + x0 * x0 * A[2] // + + x0 * x0 * x0 * A[3] // + + y0 * A[4] // + + y0 * x0 * A[5] // + + y0 * x0 * x0 * A[6] // + + y0 * y0 * A[7] // + + y0 * y0 * x0 * A[8] // + + y0 * y0 * y0 * A[9]); delta[cell_id] = l2Norm((transpose(Diff) * Diff) * TinyVector<3>(1, 1, 1)); }); @@ -527,25 +526,25 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") const R2x3 Diff = pk[cell_id](xj[cell_id] + R3{x0, y0, z0}) // - (A[0] // - + z0 * A[1] // - + z0 * z0 * A[2] // - + z0 * z0 * z0 * A[3] // + + x0 * A[1] // + + x0 * x0 * A[2] // + + x0 * x0 * x0 * A[3] // + y0 * A[4] // - + y0 * z0 * A[5] // - + y0 * z0 * z0 * A[6] // + + y0 * x0 * A[5] // + + y0 * x0 * x0 * A[6] // + y0 * y0 * A[7] // - + y0 * y0 * z0 * A[8] // + + y0 * y0 * x0 * A[8] // + y0 * y0 * y0 * A[9] // - + x0 * A[10] // - + x0 * z0 * A[11] // - + x0 * z0 * z0 * A[12] // - + x0 * y0 * A[13] // - + x0 * y0 * z0 * A[14] // - + x0 * y0 * y0 * A[15] // - + x0 * x0 * A[16] // - + x0 * x0 * z0 * A[17] // - + x0 * x0 * y0 * A[18] // - + x0 * x0 * x0 * A[19] // + + z0 * A[10] // + + z0 * x0 * A[11] // + + z0 * x0 * x0 * A[12] // + + z0 * y0 * A[13] // + + z0 * y0 * x0 * A[14] // + + z0 * y0 * y0 * A[15] // + + z0 * z0 * A[16] // + + z0 * z0 * x0 * A[17] // + + z0 * z0 * y0 * A[18] // + + z0 * z0 * z0 * A[19] // ); delta[cell_id] = l2Norm(transpose(Diff) * Diff * TinyVector<3>{1, 1, 1}); @@ -560,27 +559,31 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") SECTION("1D") { for (size_t degree = 0; degree < 10; ++degree) { - size_t polynomial_basis_dimension = CenteredCanonicalBasisView<1, double>::dimensionFromDegree(degree); + size_t polynomial_basis_dimension = + PolynomialCenteredCanonicalBasisView<1, double>::dimensionFromDegree(degree); REQUIRE(polynomial_basis_dimension == degree + 1); - REQUIRE(CenteredCanonicalBasisView<1, double>::degreeFromDimension(polynomial_basis_dimension) == degree); + REQUIRE(PolynomialCenteredCanonicalBasisView<1, double>::degreeFromDimension(polynomial_basis_dimension) == + degree); } } SECTION("2D") { for (size_t degree = 0; degree < 10; ++degree) { - size_t polynomial_basis_dimension = CenteredCanonicalBasisView<2, double>::dimensionFromDegree(degree); + size_t polynomial_basis_dimension = + PolynomialCenteredCanonicalBasisView<2, double>::dimensionFromDegree(degree); REQUIRE(2 * polynomial_basis_dimension == (degree + 1) * (degree + 2)); - REQUIRE(CenteredCanonicalBasisView<2, double>::degreeFromDimension(polynomial_basis_dimension) == degree); + REQUIRE(PolynomialCenteredCanonicalBasisView<2, double>::degreeFromDimension(polynomial_basis_dimension) == + degree); } - REQUIRE_THROWS_WITH((CenteredCanonicalBasisView<2, double>::degreeFromDimension(2)), + REQUIRE_THROWS_WITH((PolynomialCenteredCanonicalBasisView<2, double>::degreeFromDimension(2)), "error: incorrect polynomial basis dimension"); - REQUIRE_THROWS_WITH((CenteredCanonicalBasisView<2, double>::degreeFromDimension(4)), + REQUIRE_THROWS_WITH((PolynomialCenteredCanonicalBasisView<2, double>::degreeFromDimension(4)), "error: incorrect polynomial basis dimension"); - REQUIRE_THROWS_WITH((CenteredCanonicalBasisView<2, double>::degreeFromDimension(5)), + REQUIRE_THROWS_WITH((PolynomialCenteredCanonicalBasisView<2, double>::degreeFromDimension(5)), "error: incorrect polynomial basis dimension"); - REQUIRE_THROWS_WITH((CenteredCanonicalBasisView<2, double>::degreeFromDimension(7)), + REQUIRE_THROWS_WITH((PolynomialCenteredCanonicalBasisView<2, double>::degreeFromDimension(7)), "error: incorrect polynomial basis dimension"); } } -- GitLab From 6e2c993c0c262f6f3245a57a236fa56715c50f4f Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Sun, 5 May 2024 12:00:30 +0200 Subject: [PATCH 015/122] Replace use of std::span by CoefficientView - it is not supported by clang-10 - CoefficientView is somehow safer --- src/scheme/DiscreteFunctionDPk.hpp | 44 +++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index 641faa796..64be52ef2 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -10,14 +10,49 @@ #include <mesh/MeshVariant.hpp> #include <scheme/DiscreteFunctionDescriptorP0.hpp> -#include <span> - template <size_t Dimension, typename DataType> class PolynomialCenteredCanonicalBasisView { + public: + class CoefficientsView + { + private: + DataType* const m_values; + const size_t m_size; + + public: + [[nodiscard]] PUGS_INLINE size_t + size() const + { + return m_size; + } + + [[nodiscard]] PUGS_INLINE DataType& + operator[](size_t i) const + { + Assert(i < m_size, "invalid index"); + return m_values[i]; + } + + CoefficientsView& operator=(const CoefficientsView&) = delete; + CoefficientsView& operator=(CoefficientsView&&) = delete; + + CoefficientsView(DataType* values, size_t size) : m_values{values}, m_size{size} + { + ; + } + + // To try to keep these views close to the initial array one + // forbids copy constructor and take benefits of C++-17 copy + // elisions. + CoefficientsView(const CoefficientsView&) = delete; + + CoefficientsView() = delete; + ~CoefficientsView() = default; + }; + private: static_assert((Dimension > 0) and (Dimension <= 3), "invalid dimension"); - using CoefficientList = typename Table<DataType>::UnsafeRowView; const size_t m_degree; @@ -60,7 +95,7 @@ class PolynomialCenteredCanonicalBasisView // // z^n - std::span<DataType> m_coefficients; + CoefficientsView m_coefficients; const TinyVector<Dimension>& m_xj; public: @@ -75,6 +110,7 @@ class PolynomialCenteredCanonicalBasisView return ((degree + 3) * (degree + 2) * (degree + 1)) / 6; } } + static size_t degreeFromDimension(size_t polynomial_basis_dimension) { -- GitLab From 041ef9a77b73eb61e7690f47c0733c7d05b975e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 6 May 2024 14:03:44 +0200 Subject: [PATCH 016/122] Non const SmallMatrix are now friends to const ones --- src/algebra/SmallMatrix.hpp | 64 +++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/src/algebra/SmallMatrix.hpp b/src/algebra/SmallMatrix.hpp index b8d3b81b5..22ccbf881 100644 --- a/src/algebra/SmallMatrix.hpp +++ b/src/algebra/SmallMatrix.hpp @@ -28,20 +28,25 @@ class [[nodiscard]] SmallMatrix // LCOV_EXCL_LINE // Allows const version to access our data friend SmallMatrix<std::add_const_t<DataType>>; + // Allows non-const version to access our data + friend SmallMatrix<std::remove_const_t<DataType>>; public: PUGS_INLINE - bool isSquare() const noexcept + bool + isSquare() const noexcept { return m_nb_rows == m_nb_columns; } - friend PUGS_INLINE SmallMatrix<std::remove_const_t<DataType>> copy(const SmallMatrix& A) noexcept + friend PUGS_INLINE SmallMatrix<std::remove_const_t<DataType>> + copy(const SmallMatrix& A) noexcept { return SmallMatrix<std::remove_const_t<DataType>>{A.m_nb_rows, A.m_nb_columns, copy(A.m_values)}; } - friend PUGS_INLINE SmallMatrix<std::remove_const_t<DataType>> transpose(const SmallMatrix& A) + friend PUGS_INLINE SmallMatrix<std::remove_const_t<DataType>> + transpose(const SmallMatrix& A) { SmallMatrix<std::remove_const_t<DataType>> A_transpose{A.m_nb_columns, A.m_nb_rows}; for (size_t i = 0; i < A.m_nb_rows; ++i) { @@ -52,14 +57,16 @@ class [[nodiscard]] SmallMatrix // LCOV_EXCL_LINE return A_transpose; } - friend PUGS_INLINE SmallMatrix operator*(const DataType& a, const SmallMatrix& A) + friend PUGS_INLINE SmallMatrix + operator*(const DataType& a, const SmallMatrix& A) { SmallMatrix<std::remove_const_t<DataType>> aA = copy(A); return aA *= a; } template <typename DataType2> - PUGS_INLINE SmallVector<std::remove_const_t<DataType>> operator*(const SmallVector<DataType2>& x) const + PUGS_INLINE SmallVector<std::remove_const_t<DataType>> + operator*(const SmallVector<DataType2>& x) const { static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>, "incompatible data types"); @@ -77,7 +84,8 @@ class [[nodiscard]] SmallMatrix // LCOV_EXCL_LINE } template <typename DataType2> - PUGS_INLINE SmallMatrix<std::remove_const_t<DataType>> operator*(const SmallMatrix<DataType2>& B) const + PUGS_INLINE SmallMatrix<std::remove_const_t<DataType>> + operator*(const SmallMatrix<DataType2>& B) const { static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>, "incompatible data types"); @@ -98,14 +106,16 @@ class [[nodiscard]] SmallMatrix // LCOV_EXCL_LINE } template <typename DataType2> - PUGS_INLINE SmallMatrix& operator/=(const DataType2& a) + PUGS_INLINE SmallMatrix& + operator/=(const DataType2& a) { const auto inv_a = 1. / a; return (*this) *= inv_a; } template <typename DataType2> - PUGS_INLINE SmallMatrix& operator*=(const DataType2& a) + PUGS_INLINE SmallMatrix& + operator*=(const DataType2& a) { parallel_for( m_values.size(), PUGS_LAMBDA(index_type i) { m_values[i] *= a; }); @@ -113,7 +123,8 @@ class [[nodiscard]] SmallMatrix // LCOV_EXCL_LINE } template <typename DataType2> - PUGS_INLINE SmallMatrix& operator-=(const SmallMatrix<DataType2>& B) + PUGS_INLINE SmallMatrix& + operator-=(const SmallMatrix<DataType2>& B) { static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>, "incompatible data types"); @@ -126,7 +137,8 @@ class [[nodiscard]] SmallMatrix // LCOV_EXCL_LINE } template <typename DataType2> - PUGS_INLINE SmallMatrix& operator+=(const SmallMatrix<DataType2>& B) + PUGS_INLINE SmallMatrix& + operator+=(const SmallMatrix<DataType2>& B) { static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>, "incompatible data types"); @@ -139,7 +151,8 @@ class [[nodiscard]] SmallMatrix // LCOV_EXCL_LINE } template <typename DataType2> - PUGS_INLINE SmallMatrix<std::remove_const_t<DataType>> operator+(const SmallMatrix<DataType2>& B) const + PUGS_INLINE SmallMatrix<std::remove_const_t<DataType>> + operator+(const SmallMatrix<DataType2>& B) const { static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>, "incompatible data types"); @@ -155,7 +168,8 @@ class [[nodiscard]] SmallMatrix // LCOV_EXCL_LINE } template <typename DataType2> - PUGS_INLINE SmallMatrix<std::remove_const_t<DataType>> operator-(const SmallMatrix<DataType2>& B) const + PUGS_INLINE SmallMatrix<std::remove_const_t<DataType>> + operator-(const SmallMatrix<DataType2>& B) const { static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>, "incompatible data types"); @@ -171,36 +185,42 @@ class [[nodiscard]] SmallMatrix // LCOV_EXCL_LINE } PUGS_INLINE - DataType& operator()(index_type i, index_type j) const noexcept(NO_ASSERT) + DataType& + operator()(index_type i, index_type j) const noexcept(NO_ASSERT) { Assert(i < m_nb_rows and j < m_nb_columns, "cannot access element: invalid indices"); return m_values[i * m_nb_columns + j]; } PUGS_INLINE - size_t numberOfRows() const noexcept + size_t + numberOfRows() const noexcept { return m_nb_rows; } PUGS_INLINE - size_t numberOfColumns() const noexcept + size_t + numberOfColumns() const noexcept { return m_nb_columns; } - PUGS_INLINE void fill(const DataType& value) noexcept + PUGS_INLINE void + fill(const DataType& value) noexcept { m_values.fill(value); } - PUGS_INLINE SmallMatrix& operator=(ZeroType) noexcept + PUGS_INLINE SmallMatrix& + operator=(ZeroType) noexcept { m_values.fill(0); return *this; } - PUGS_INLINE SmallMatrix& operator=(IdentityType) noexcept(NO_ASSERT) + PUGS_INLINE SmallMatrix& + operator=(IdentityType) noexcept(NO_ASSERT) { Assert(m_nb_rows == m_nb_columns, "identity must be a square matrix"); @@ -211,7 +231,8 @@ class [[nodiscard]] SmallMatrix // LCOV_EXCL_LINE } template <typename DataType2> - PUGS_INLINE SmallMatrix& operator=(const SmallMatrix<DataType2>& A) noexcept + PUGS_INLINE SmallMatrix& + operator=(const SmallMatrix<DataType2>& A) noexcept { // ensures that DataType is the same as source DataType2 static_assert(std::is_same<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>(), @@ -232,7 +253,8 @@ class [[nodiscard]] SmallMatrix // LCOV_EXCL_LINE PUGS_INLINE SmallMatrix& operator=(SmallMatrix&&) = default; - friend std::ostream& operator<<(std::ostream& os, const SmallMatrix& A) + friend std::ostream& + operator<<(std::ostream& os, const SmallMatrix& A) { for (size_t i = 0; i < A.numberOfRows(); ++i) { os << i << '|'; @@ -259,7 +281,7 @@ class [[nodiscard]] SmallMatrix // LCOV_EXCL_LINE SmallMatrix(const SmallMatrix&) = default; - SmallMatrix(SmallMatrix &&) = default; + SmallMatrix(SmallMatrix&&) = default; explicit SmallMatrix(size_t nb_rows, size_t nb_columns, const SmallArray<DataType>& values) : m_nb_rows{nb_rows}, m_nb_columns{nb_columns}, m_values{values} -- GitLab From 52ad860db6e13cbc0b303589a463eabd8a603f3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 6 May 2024 14:04:19 +0200 Subject: [PATCH 017/122] Add generalized versions of Givens: RHS and unknowns being matrices --- src/algebra/Givens.hpp | 78 ++++++++++-- tests/test_Givens.cpp | 278 +++++++++++++++++++++++++++++------------ 2 files changed, 268 insertions(+), 88 deletions(-) diff --git a/src/algebra/Givens.hpp b/src/algebra/Givens.hpp index 02a71d0aa..575eaed51 100644 --- a/src/algebra/Givens.hpp +++ b/src/algebra/Givens.hpp @@ -1,12 +1,11 @@ #ifndef GIVENS_HPP #define GIVENS_HPP -#include <cmath> -#include <iomanip> -#include <iostream> -#include <rang.hpp> #include <utils/Exceptions.hpp> #include <utils/PugsAssert.hpp> +#include <cmath> +#include <iomanip> + class Givens { private: @@ -41,13 +40,29 @@ class Givens static void _lift(const MatrixType& A, const RHSVectorType& b, VectorType& x) { - x.fill(0); - for (size_t i = x.size(); i >= 1; --i) { - double sum = 0; + for (ssize_t i = x.size() - 1; i >= 0; --i) { + const double inv_Aii = 1 / A(i, i); + double sum = 0; for (size_t j = i; j < x.size(); ++j) { - sum += A(i - 1, j) * x[j]; + sum += A(i, j) * x[j]; + } + x[i] = (b[i] - sum) * inv_Aii; + } + } + + template <typename MatrixType, typename UnknownMatrixType, typename RHSMatrixType> + static void + _liftCollection(const MatrixType& A, const RHSMatrixType& B, UnknownMatrixType& X) + { + for (ssize_t i = X.numberOfRows() - 1; i >= 0; --i) { + const double inv_Aii = 1 / A(i, i); + for (size_t k = 0; k < X.numberOfColumns(); ++k) { + double sum = 0; + for (size_t j = i; j < X.numberOfRows(); ++j) { + sum += A(i, j) * X(j, k); + } + X(i, k) = (B(i, k) - sum) * inv_Aii; } - x[i - 1] = (b[i - 1] - sum) / A(i - 1, i - 1); } } @@ -92,6 +107,51 @@ class Givens solveInPlace(rotateA, x, rotateb); } + template <typename MatrixType, typename UnknownMatrixType, typename RHSMatrixType> + static void + solveCollectionInPlace(const MatrixType& A, UnknownMatrixType& X, const RHSMatrixType& B) + { + Assert(X.numberOfRows() == A.numberOfColumns(), "The number of columns of A must be the number of rows of X"); + Assert(B.numberOfRows() == A.numberOfRows(), "The number of rows of A must be the rows of B"); + Assert(X.numberOfColumns() == B.numberOfColumns(), "The number of columns of X and B must be the same"); + + for (size_t j = 0; j < A.numberOfColumns(); ++j) { + for (size_t i = A.numberOfRows() - 1; i > j; --i) { + double c(0), s(0); + _givens(A(i - 1, j), A(i, j), c, s); + for (size_t k = j; k < A.numberOfColumns(); ++k) { + double xi_1(0), xi(0); + _rotate(A(i - 1, k), A(i, k), c, s, xi_1, xi); + A(i - 1, k) = xi_1; + A(i, k) = xi; + } + + for (size_t l = 0; l < B.numberOfColumns(); ++l) { + double xi_1(0), xi(0); + _rotate(B(i - 1, l), B(i, l), c, s, xi_1, xi); + B(i - 1, l) = xi_1; + B(i, l) = xi; + } + } + } + + _liftCollection(A, B, X); + } + + template <typename MatrixType, typename UnknownMatrixType, typename RHSMatrixType> + static void + solveCollection(const MatrixType& A, UnknownMatrixType& X, const RHSMatrixType& B) + { + Assert(X.numberOfRows() == A.numberOfColumns(), "The number of columns of A must be the number of rows of X"); + Assert(B.numberOfRows() == A.numberOfRows(), "The number of rows of A must be the rows of B"); + Assert(X.numberOfColumns() == B.numberOfColumns(), "The number of columns of X and B must be the same"); + + MatrixType rotateA = copy(A); + RHSMatrixType rotateB = copy(B); + + solveCollectionInPlace(rotateA, X, rotateB); + } + Givens() = delete; ~Givens() = delete; }; diff --git a/tests/test_Givens.cpp b/tests/test_Givens.cpp index 818d82222..aec60a82b 100644 --- a/tests/test_Givens.cpp +++ b/tests/test_Givens.cpp @@ -9,88 +9,208 @@ TEST_CASE("Givens", "[algebra]") { - SECTION("square matrix") + SECTION("classic (single vector)") { - SmallMatrix<double> A{5, 5}; - A.fill(0); - A(0, 0) = 2; - A(0, 1) = -1; - - A(1, 0) = -0.2; - A(1, 1) = 2; - A(1, 2) = -1; - - A(2, 1) = -1; - A(2, 2) = 4; - A(2, 3) = -2; - - A(3, 2) = -1; - A(3, 3) = 2; - A(3, 4) = -0.1; - - A(4, 3) = 1; - A(4, 4) = 3; - - // SmallMatrix<double> A{S.getMatrix()}; - - SmallVector<const double> x_exact = [] { - SmallVector<double> y{5}; - y[0] = 1; - y[1] = 3; - y[2] = 2; - y[3] = 4; - y[4] = 5; - return y; - }(); - - SmallVector<double> b = A * x_exact; - - SmallVector<double> x{5}; - x = zero; - - Givens::solve(A, x, b); - SmallVector<double> error = x - x_exact; - - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x, x))); + SECTION("square matrix") + { + SmallMatrix<double> A{5, 5}; + A.fill(0); + A(0, 0) = 2; + A(0, 1) = -1; + + A(1, 0) = -0.2; + A(1, 1) = 2; + A(1, 2) = -1; + + A(2, 1) = -1; + A(2, 2) = 4; + A(2, 3) = -2; + + A(3, 2) = -1; + A(3, 3) = 2; + A(3, 4) = -0.1; + + A(4, 3) = 1; + A(4, 4) = 3; + + SmallVector<const double> x_exact = [] { + SmallVector<double> y{5}; + y[0] = 1; + y[1] = 3; + y[2] = 2; + y[3] = 4; + y[4] = 5; + return y; + }(); + + SmallVector<double> b = A * x_exact; + + SmallVector<double> x{5}; + x = zero; + + Givens::solve(A, x, b); + SmallVector<double> error = x - x_exact; + + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x, x))); + } + + SECTION("rectangular matrix") + { + SmallMatrix<double> A{5, 3}; + A.fill(0); + A(0, 0) = 2; + A(0, 1) = -1; + + A(1, 0) = -0.2; + A(1, 1) = 2; + A(1, 2) = -1; + + A(2, 1) = -1; + A(2, 2) = 4; + + A(3, 2) = -1; + A(4, 0) = 0.5; + A(4, 1) = 1; + A(4, 2) = 1; + + SmallVector<const double> x_exact = [] { + SmallVector<double> y{3}; + y[0] = 1; + y[1] = 3; + y[2] = 2; + return y; + }(); + + SmallVector<double> b = A * x_exact; + + SmallVector<double> x{3}; + x = zero; + + Givens::solve(A, x, b); + SmallVector<double> error = x - x_exact; + + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x, x))); + } } - SECTION("rectangular matrix") + SECTION("generalized (vector collection)") { - SmallMatrix<double> A{5, 3}; - A.fill(0); - A(0, 0) = 2; - A(0, 1) = -1; - - A(1, 0) = -0.2; - A(1, 1) = 2; - A(1, 2) = -1; - - A(2, 1) = -1; - A(2, 2) = 4; - - A(3, 2) = -1; - A(4, 0) = 0.5; - A(4, 1) = 1; - A(4, 2) = 1; - - // SmallMatrix<double> A{S.getMatrix()}; - - SmallVector<const double> x_exact = [] { - SmallVector<double> y{3}; - y[0] = 1; - y[1] = 3; - y[2] = 2; - return y; - }(); - - SmallVector<double> b = A * x_exact; - - SmallVector<double> x{3}; - x = zero; - - Givens::solve(A, x, b); - SmallVector<double> error = x - x_exact; - - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x, x))); + SECTION("square matrix") + { + SmallMatrix<double> A{5, 5}; + A.fill(0); + A(0, 0) = 2; + A(0, 1) = -1; + + A(1, 0) = -0.2; + A(1, 1) = 2; + A(1, 2) = -1; + + A(2, 1) = -1; + A(2, 2) = 4; + A(2, 3) = -2; + + A(3, 2) = -1; + A(3, 3) = 2; + A(3, 4) = -0.1; + + A(4, 3) = 1; + A(4, 4) = 3; + + SmallMatrix<const double> X_exact = [] { + SmallMatrix<double> Y{5, 3}; + Y(0, 0) = 1; + Y(1, 0) = 3; + Y(2, 0) = 2; + Y(3, 0) = 4; + Y(4, 0) = 5; + + Y(0, 1) = -3; + Y(1, 1) = 6; + Y(2, 1) = 1; + Y(3, 1) = -2; + Y(4, 1) = 4; + + Y(0, 2) = -2; + Y(1, 2) = -4; + Y(2, 2) = 2; + Y(3, 2) = 6; + Y(4, 2) = 7; + + return Y; + }(); + + SmallMatrix<double> b = A * X_exact; + + SmallMatrix<double> X{5, 3}; + + Givens::solveCollection(A, X, b); + SmallMatrix<double> error = X - X_exact; + + double max_error = 0; + for (size_t i = 0; i < error.numberOfRows(); ++i) { + for (size_t j = 0; j < error.numberOfColumns(); ++j) { + max_error = std::max(max_error, std::abs(error(i, j))); + } + } + REQUIRE(max_error < 1E-10); + } + + SECTION("rectangular matrix") + { + SmallMatrix<double> A{5, 3}; + A.fill(0); + A(0, 0) = 2; + A(0, 1) = -1; + + A(1, 0) = -0.2; + A(1, 1) = 2; + A(1, 2) = -1; + + A(2, 1) = -1; + A(2, 2) = 4; + + A(3, 2) = -1; + A(4, 0) = 0.5; + A(4, 1) = 1; + A(4, 2) = 1; + + SmallMatrix<const double> X_exact = [] { + SmallMatrix<double> Y{3, 4}; + Y(0, 0) = 1; + Y(1, 0) = 3; + Y(2, 0) = 2; + + Y(0, 1) = -1; + Y(1, 1) = 1.5; + Y(2, 1) = 5; + + Y(0, 2) = -3; + Y(1, 2) = 1; + Y(2, 2) = 4; + + Y(0, 3) = 0.7; + Y(1, 3) = -3.2; + Y(2, 3) = 2.5; + + return Y; + }(); + + SmallMatrix<double> B = A * X_exact; + + SmallMatrix<double> X{3, 4}; + X = zero; + + Givens::solveCollection(A, X, B); + SmallMatrix<double> error = X - X_exact; + + double max_error = 0; + for (size_t i = 0; i < error.numberOfRows(); ++i) { + for (size_t j = 0; j < error.numberOfColumns(); ++j) { + max_error = std::max(max_error, std::abs(error(i, j))); + } + } + REQUIRE(max_error < 1E-10); + } } } -- GitLab From 515f1d03b9055163d1a07e1e25853949ee1dddb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 14 May 2024 18:35:14 +0200 Subject: [PATCH 018/122] Add dot product and Frobenius norm for TinyMatrix Also add a few decorators (nodiscard and noexcept) for TinyMatrix and TinyVector --- src/algebra/TinyMatrix.hpp | 151 +++++++++++++++++++------------------ src/algebra/TinyVector.hpp | 68 +++++++---------- tests/test_TinyMatrix.cpp | 33 ++++++++ 3 files changed, 140 insertions(+), 112 deletions(-) diff --git a/src/algebra/TinyMatrix.hpp b/src/algebra/TinyMatrix.hpp index 94590ff65..e2a87058c 100644 --- a/src/algebra/TinyMatrix.hpp +++ b/src/algebra/TinyMatrix.hpp @@ -56,16 +56,14 @@ class [[nodiscard]] TinyMatrix } public: - PUGS_INLINE - constexpr bool + [[nodiscard]] PUGS_INLINE constexpr bool isSquare() const noexcept { return M == N; } - PUGS_INLINE - constexpr friend TinyMatrix<N, M, T> - transpose(const TinyMatrix& A) + [[nodiscard]] PUGS_INLINE constexpr friend TinyMatrix<N, M, T> + transpose(const TinyMatrix& A) noexcept { TinyMatrix<N, M, T> tA; for (size_t i = 0; i < M; ++i) { @@ -76,37 +74,32 @@ class [[nodiscard]] TinyMatrix return tA; } - PUGS_INLINE - constexpr size_t - dimension() const + [[nodiscard]] PUGS_INLINE constexpr size_t + dimension() const noexcept { return M * N; } - PUGS_INLINE - constexpr size_t - numberOfValues() const + [[nodiscard]] PUGS_INLINE constexpr size_t + numberOfValues() const noexcept { return this->dimension(); } - PUGS_INLINE - constexpr size_t - numberOfRows() const + [[nodiscard]] PUGS_INLINE constexpr size_t + numberOfRows() const noexcept { return M; } - PUGS_INLINE - constexpr size_t - numberOfColumns() const + [[nodiscard]] PUGS_INLINE constexpr size_t + numberOfColumns() const noexcept { return N; } - PUGS_INLINE - constexpr TinyMatrix - operator-() const + [[nodiscard]] PUGS_INLINE constexpr TinyMatrix + operator-() const noexcept { TinyMatrix opposite; for (size_t i = 0; i < M * N; ++i) { @@ -115,9 +108,8 @@ class [[nodiscard]] TinyMatrix return opposite; } - PUGS_INLINE - constexpr friend TinyMatrix - operator*(const T& t, const TinyMatrix& A) + [[nodiscard]] PUGS_INLINE constexpr friend TinyMatrix + operator*(const T& t, const TinyMatrix& A) noexcept { TinyMatrix B = A; return B *= t; @@ -125,14 +117,14 @@ class [[nodiscard]] TinyMatrix PUGS_INLINE constexpr friend TinyMatrix - operator*(const T& t, TinyMatrix&& A) + operator*(const T& t, TinyMatrix&& A) noexcept { return std::move(A *= t); } PUGS_INLINE constexpr TinyMatrix& - operator*=(const T& t) + operator*=(const T& t) noexcept { for (size_t i = 0; i < M * N; ++i) { m_values[i] *= t; @@ -141,8 +133,8 @@ class [[nodiscard]] TinyMatrix } template <size_t P> - PUGS_INLINE constexpr TinyMatrix<M, P, T> - operator*(const TinyMatrix<N, P, T>& B) const + [[nodiscard]] PUGS_INLINE constexpr TinyMatrix<M, P, T> + operator*(const TinyMatrix<N, P, T>& B) const noexcept { const TinyMatrix& A = *this; TinyMatrix<M, P, T> AB; @@ -158,9 +150,8 @@ class [[nodiscard]] TinyMatrix return AB; } - PUGS_INLINE - constexpr TinyVector<M, T> - operator*(const TinyVector<N, T>& x) const + [[nodiscard]] PUGS_INLINE constexpr TinyVector<M, T> + operator*(const TinyVector<N, T>& x) const noexcept { const TinyMatrix& A = *this; TinyVector<M, T> Ax; @@ -194,9 +185,8 @@ class [[nodiscard]] TinyMatrix return os; } - PUGS_INLINE - constexpr bool - operator==(const TinyMatrix& A) const + [[nodiscard]] PUGS_INLINE constexpr bool + operator==(const TinyMatrix& A) const noexcept { for (size_t i = 0; i < M * N; ++i) { if (m_values[i] != A.m_values[i]) @@ -205,16 +195,26 @@ class [[nodiscard]] TinyMatrix return true; } - PUGS_INLINE - constexpr bool - operator!=(const TinyMatrix& A) const + [[nodiscard]] PUGS_INLINE constexpr bool + operator!=(const TinyMatrix& A) const noexcept { return not this->operator==(A); } - PUGS_INLINE - constexpr TinyMatrix - operator+(const TinyMatrix& A) const + [[nodiscard]] PUGS_INLINE constexpr friend T + dot(const TinyMatrix& A, const TinyMatrix& B) noexcept + { + T sum = A.m_values[0] * B.m_values[0]; + + for (size_t i = 1; i < M * N; ++i) { + sum += A.m_values[i] * B.m_values[i]; + } + + return sum; + } + + [[nodiscard]] PUGS_INLINE constexpr TinyMatrix + operator+(const TinyMatrix& A) const noexcept { TinyMatrix sum; for (size_t i = 0; i < M * N; ++i) { @@ -223,17 +223,15 @@ class [[nodiscard]] TinyMatrix return sum; } - PUGS_INLINE - constexpr TinyMatrix - operator+(TinyMatrix&& A) const + [[nodiscard]] PUGS_INLINE constexpr TinyMatrix + operator+(TinyMatrix&& A) const noexcept { A += *this; return std::move(A); } - PUGS_INLINE - constexpr TinyMatrix - operator-(const TinyMatrix& A) const + [[nodiscard]] PUGS_INLINE constexpr TinyMatrix + operator-(const TinyMatrix& A) const noexcept { TinyMatrix difference; for (size_t i = 0; i < M * N; ++i) { @@ -242,9 +240,8 @@ class [[nodiscard]] TinyMatrix return difference; } - PUGS_INLINE - constexpr TinyMatrix - operator-(TinyMatrix&& A) const + [[nodiscard]] PUGS_INLINE constexpr TinyMatrix + operator-(TinyMatrix&& A) const noexcept { for (size_t i = 0; i < M * N; ++i) { A.m_values[i] = m_values[i] - A.m_values[i]; @@ -254,7 +251,7 @@ class [[nodiscard]] TinyMatrix PUGS_INLINE constexpr TinyMatrix& - operator+=(const TinyMatrix& A) + operator+=(const TinyMatrix& A) noexcept { for (size_t i = 0; i < M * N; ++i) { m_values[i] += A.m_values[i]; @@ -264,7 +261,7 @@ class [[nodiscard]] TinyMatrix PUGS_INLINE constexpr TinyMatrix& - operator-=(const TinyMatrix& A) + operator-=(const TinyMatrix& A) noexcept { for (size_t i = 0; i < M * N; ++i) { m_values[i] -= A.m_values[i]; @@ -377,8 +374,8 @@ class [[nodiscard]] TinyMatrix }; template <size_t M, size_t N, typename T> -PUGS_INLINE constexpr TinyMatrix<M, N, T> -tensorProduct(const TinyVector<M, T>& x, const TinyVector<N, T>& y) +[[nodiscard]] PUGS_INLINE constexpr TinyMatrix<M, N, T> +tensorProduct(const TinyVector<M, T>& x, const TinyVector<N, T>& y) noexcept { TinyMatrix<M, N, T> A; for (size_t i = 0; i < M; ++i) { @@ -389,9 +386,17 @@ tensorProduct(const TinyVector<M, T>& x, const TinyVector<N, T>& y) return A; } +template <size_t M, size_t N, typename T> +[[nodiscard]] PUGS_INLINE constexpr auto +frobeniusNorm(const TinyMatrix<M, N, T>& A) noexcept +{ + static_assert(std::is_arithmetic<T>(), "Cannot compute frobeniusNorm value for non-arithmetic types"); + return std::sqrt(dot(A, A)); +} + template <size_t N, typename T> -PUGS_INLINE constexpr T -det(const TinyMatrix<N, N, T>& A) +[[nodiscard]] PUGS_INLINE constexpr T +det(const TinyMatrix<N, N, T>& A) noexcept { static_assert(std::is_arithmetic<T>::value, "determinant is not defined for non-arithmetic types"); static_assert(std::is_floating_point<T>::value, "determinant for arbitrary dimension N is defined for floating " @@ -441,24 +446,24 @@ det(const TinyMatrix<N, N, T>& A) } template <typename T> -PUGS_INLINE constexpr T -det(const TinyMatrix<1, 1, T>& A) +[[nodiscard]] PUGS_INLINE constexpr T +det(const TinyMatrix<1, 1, T>& A) noexcept { static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non-arithmetic types"); return A(0, 0); } template <typename T> -PUGS_INLINE constexpr T -det(const TinyMatrix<2, 2, T>& A) +[[nodiscard]] PUGS_INLINE constexpr T +det(const TinyMatrix<2, 2, T>& A) noexcept { static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non-arithmetic types"); return A(0, 0) * A(1, 1) - A(1, 0) * A(0, 1); } template <typename T> -PUGS_INLINE constexpr T -det(const TinyMatrix<3, 3, T>& A) +[[nodiscard]] PUGS_INLINE constexpr T +det(const TinyMatrix<3, 3, T>& A) noexcept { static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non-arithmetic types"); return A(0, 0) * (A(1, 1) * A(2, 2) - A(2, 1) * A(1, 2)) - A(1, 0) * (A(0, 1) * A(2, 2) - A(2, 1) * A(0, 2)) + @@ -466,8 +471,8 @@ det(const TinyMatrix<3, 3, T>& A) } template <size_t M, size_t N, typename T> -PUGS_INLINE constexpr TinyMatrix<M - 1, N - 1, T> -getMinor(const TinyMatrix<M, N, T>& A, size_t I, size_t J) +[[nodiscard]] PUGS_INLINE constexpr TinyMatrix<M - 1, N - 1, T> +getMinor(const TinyMatrix<M, N, T>& A, size_t I, size_t J) noexcept(NO_ASSERT) { static_assert(M >= 2 and N >= 2, "minor calculation requires at least 2x2 matrices"); Assert((I < M) and (J < N)); @@ -492,8 +497,8 @@ getMinor(const TinyMatrix<M, N, T>& A, size_t I, size_t J) } template <size_t N, typename T> -PUGS_INLINE T -trace(const TinyMatrix<N, N, T>& A) +[[nodiscard]] PUGS_INLINE T +trace(const TinyMatrix<N, N, T>& A) noexcept { static_assert(std::is_arithmetic<T>::value, "trace is not defined for non-arithmetic types"); @@ -505,11 +510,11 @@ trace(const TinyMatrix<N, N, T>& A) } template <size_t N, typename T> -PUGS_INLINE constexpr TinyMatrix<N, N, T> inverse(const TinyMatrix<N, N, T>& A); +[[nodiscard]] PUGS_INLINE constexpr TinyMatrix<N, N, T> inverse(const TinyMatrix<N, N, T>& A); template <typename T> -PUGS_INLINE constexpr TinyMatrix<1, 1, T> -inverse(const TinyMatrix<1, 1, T>& A) +[[nodiscard]] PUGS_INLINE constexpr TinyMatrix<1, 1, T> +inverse(const TinyMatrix<1, 1, T>& A) noexcept { static_assert(std::is_arithmetic<T>::value, "inverse is not defined for non-arithmetic types"); static_assert(std::is_floating_point<T>::value, "inverse is defined for floating point types only"); @@ -519,8 +524,8 @@ inverse(const TinyMatrix<1, 1, T>& A) } template <size_t N, typename T> -PUGS_INLINE constexpr T -cofactor(const TinyMatrix<N, N, T>& A, size_t i, size_t j) +[[nodiscard]] PUGS_INLINE constexpr T +cofactor(const TinyMatrix<N, N, T>& A, size_t i, size_t j) noexcept(NO_ASSERT) { static_assert(std::is_arithmetic<T>::value, "cofactor is not defined for non-arithmetic types"); const T sign = ((i + j) % 2) ? -1 : 1; @@ -529,8 +534,8 @@ cofactor(const TinyMatrix<N, N, T>& A, size_t i, size_t j) } template <typename T> -PUGS_INLINE constexpr TinyMatrix<2, 2, T> -inverse(const TinyMatrix<2, 2, T>& A) +[[nodiscard]] PUGS_INLINE constexpr TinyMatrix<2, 2, T> +inverse(const TinyMatrix<2, 2, T>& A) noexcept { static_assert(std::is_arithmetic<T>::value, "inverse is not defined for non-arithmetic types"); static_assert(std::is_floating_point<T>::value, "inverse is defined for floating point types only"); @@ -543,8 +548,8 @@ inverse(const TinyMatrix<2, 2, T>& A) } template <typename T> -PUGS_INLINE constexpr TinyMatrix<3, 3, T> -inverse(const TinyMatrix<3, 3, T>& A) +[[nodiscard]] PUGS_INLINE constexpr TinyMatrix<3, 3, T> +inverse(const TinyMatrix<3, 3, T>& A) noexcept(NO_ASSERT) { static_assert(std::is_arithmetic<T>::value, "inverse is not defined for non-arithmetic types"); static_assert(std::is_floating_point<T>::value, "inverse is defined for floating point types only"); diff --git a/src/algebra/TinyVector.hpp b/src/algebra/TinyVector.hpp index 2bc9c79c3..e6d33fe26 100644 --- a/src/algebra/TinyVector.hpp +++ b/src/algebra/TinyVector.hpp @@ -33,9 +33,8 @@ class [[nodiscard]] TinyVector } public: - PUGS_INLINE - constexpr TinyVector - operator-() const + [[nodiscard]] PUGS_INLINE constexpr TinyVector + operator-() const noexcept { TinyVector opposite; for (size_t i = 0; i < N; ++i) { @@ -45,13 +44,13 @@ class [[nodiscard]] TinyVector } [[nodiscard]] PUGS_INLINE constexpr size_t - dimension() const + dimension() const noexcept { return N; } [[nodiscard]] PUGS_INLINE constexpr bool - operator==(const TinyVector& v) const + operator==(const TinyVector& v) const noexcept { for (size_t i = 0; i < N; ++i) { if (m_values[i] != v.m_values[i]) @@ -61,13 +60,13 @@ class [[nodiscard]] TinyVector } [[nodiscard]] PUGS_INLINE constexpr bool - operator!=(const TinyVector& v) const + operator!=(const TinyVector& v) const noexcept { return not this->operator==(v); } [[nodiscard]] PUGS_INLINE constexpr friend T - dot(const TinyVector& u, const TinyVector& v) + dot(const TinyVector& u, const TinyVector& v) noexcept { T t = u.m_values[0] * v.m_values[0]; for (size_t i = 1; i < N; ++i) { @@ -78,7 +77,7 @@ class [[nodiscard]] TinyVector PUGS_INLINE constexpr TinyVector& - operator*=(const T& t) + operator*=(const T& t) noexcept { for (size_t i = 0; i < N; ++i) { m_values[i] *= t; @@ -86,17 +85,15 @@ class [[nodiscard]] TinyVector return *this; } - PUGS_INLINE - constexpr friend TinyVector - operator*(const T& t, const TinyVector& v) + [[nodiscard]] PUGS_INLINE constexpr friend TinyVector + operator*(const T& t, const TinyVector& v) noexcept { TinyVector w = v; return w *= t; } - PUGS_INLINE - constexpr friend TinyVector - operator*(const T& t, TinyVector&& v) + [[nodiscard]] PUGS_INLINE constexpr friend TinyVector + operator*(const T& t, TinyVector&& v) noexcept { v *= t; return std::move(v); @@ -114,9 +111,8 @@ class [[nodiscard]] TinyVector return os; } - PUGS_INLINE - constexpr TinyVector - operator+(const TinyVector& v) const + [[nodiscard]] PUGS_INLINE constexpr TinyVector + operator+(const TinyVector& v) const noexcept { TinyVector sum; for (size_t i = 0; i < N; ++i) { @@ -125,9 +121,8 @@ class [[nodiscard]] TinyVector return sum; } - PUGS_INLINE - constexpr TinyVector - operator+(TinyVector&& v) const + [[nodiscard]] PUGS_INLINE constexpr TinyVector + operator+(TinyVector&& v) const noexcept { for (size_t i = 0; i < N; ++i) { v.m_values[i] += m_values[i]; @@ -135,9 +130,8 @@ class [[nodiscard]] TinyVector return std::move(v); } - PUGS_INLINE - constexpr TinyVector - operator-(const TinyVector& v) const + [[nodiscard]] PUGS_INLINE constexpr TinyVector + operator-(const TinyVector& v) const noexcept { TinyVector difference; for (size_t i = 0; i < N; ++i) { @@ -146,9 +140,8 @@ class [[nodiscard]] TinyVector return difference; } - PUGS_INLINE - constexpr TinyVector - operator-(TinyVector&& v) const + [[nodiscard]] PUGS_INLINE constexpr TinyVector + operator-(TinyVector&& v) const noexcept { for (size_t i = 0; i < N; ++i) { v.m_values[i] = m_values[i] - v.m_values[i]; @@ -156,9 +149,8 @@ class [[nodiscard]] TinyVector return std::move(v); } - PUGS_INLINE - constexpr TinyVector& - operator+=(const TinyVector& v) + PUGS_INLINE constexpr TinyVector& + operator+=(const TinyVector& v) noexcept { for (size_t i = 0; i < N; ++i) { m_values[i] += v.m_values[i]; @@ -168,7 +160,7 @@ class [[nodiscard]] TinyVector PUGS_INLINE constexpr TinyVector& - operator-=(const TinyVector& v) + operator-=(const TinyVector& v) noexcept { for (size_t i = 0; i < N; ++i) { m_values[i] -= v.m_values[i]; @@ -176,16 +168,14 @@ class [[nodiscard]] TinyVector return *this; } - PUGS_INLINE - constexpr T& + [[nodiscard]] PUGS_INLINE constexpr T& operator[](size_t i) noexcept(NO_ASSERT) { Assert(i < N); return m_values[i]; } - PUGS_INLINE - constexpr const T& + [[nodiscard]] PUGS_INLINE constexpr const T& operator[](size_t i) const noexcept(NO_ASSERT) { Assert(i < N); @@ -249,8 +239,8 @@ class [[nodiscard]] TinyVector }; template <size_t N, typename T> -[[nodiscard]] PUGS_INLINE constexpr T -l2Norm(const TinyVector<N, T>& x) +[[nodiscard]] PUGS_INLINE constexpr auto +l2Norm(const TinyVector<N, T>& x) noexcept { static_assert(std::is_arithmetic<T>(), "Cannot compute L2 norm for non-arithmetic types"); static_assert(std::is_floating_point<T>::value, "L2 norm is defined for floating point types only"); @@ -259,7 +249,7 @@ l2Norm(const TinyVector<N, T>& x) template <size_t N, typename T> [[nodiscard]] PUGS_INLINE constexpr T -min(const TinyVector<N, T>& x) +min(const TinyVector<N, T>& x) noexcept { T m = x[0]; for (size_t i = 1; i < N; ++i) { @@ -272,7 +262,7 @@ min(const TinyVector<N, T>& x) template <size_t N, typename T> [[nodiscard]] PUGS_INLINE constexpr T -max(const TinyVector<N, T>& x) +max(const TinyVector<N, T>& x) noexcept { T m = x[0]; for (size_t i = 1; i < N; ++i) { @@ -286,7 +276,7 @@ max(const TinyVector<N, T>& x) // Cross product is only defined for dimension 3 vectors template <typename T> [[nodiscard]] PUGS_INLINE constexpr TinyVector<3, T> -crossProduct(const TinyVector<3, T>& u, const TinyVector<3, T>& v) +crossProduct(const TinyVector<3, T>& u, const TinyVector<3, T>& v) noexcept { TinyVector<3, T> cross_product(u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0]); return cross_product; diff --git a/tests/test_TinyMatrix.cpp b/tests/test_TinyMatrix.cpp index 80159a021..7bade539c 100644 --- a/tests/test_TinyMatrix.cpp +++ b/tests/test_TinyMatrix.cpp @@ -9,6 +9,7 @@ #include <algebra/TinyMatrix.hpp> +#include <cmath> #include <sstream> // Instantiate to ensure full coverage is performed @@ -209,6 +210,38 @@ TEST_CASE("TinyMatrix", "[algebra]") REQUIRE(trace(TinyMatrix<4>(1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 2, 0, 0, 2, 2)) == 1 + 0 + 1 + 2); } + SECTION("checking for dot product calculation") + { + { + TinyMatrix<1, 1, int> M(6); + TinyMatrix<1, 1, int> N(7); + REQUIRE(dot(M, N) == trace(M * transpose(N))); + } + + { + TinyMatrix<2, 3, int> M(6, 3, -2, // + 5, -1, 4); + TinyMatrix<2, 3, int> N(7, 8, -6, // + -3, 4, 2); + REQUIRE(dot(M, N) == trace(M * transpose(N))); + REQUIRE(dot(M, N) == trace(N * transpose(M))); + } + } + + SECTION("checking for Frobenius norm calculation") + { + { + TinyMatrix<1, 1, int> M(-6); + REQUIRE(frobeniusNorm(M) == 6); + } + + { + TinyMatrix<2, 3, int> M(6, 3, -2, // + 5, -1, 4); + REQUIRE(frobeniusNorm(M) == std::sqrt(dot(M, M))); + } + } + SECTION("checking for inverse calculations") { { -- GitLab From 017a617e303172f58639fa9d425e01f0bfd5facb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 14 May 2024 18:43:09 +0200 Subject: [PATCH 019/122] Fix indices in Givens lifting. This allows to use a non-initialized unknown vector (as expected) and reduces (slightly) the computational complexity of the method. --- src/algebra/Givens.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algebra/Givens.hpp b/src/algebra/Givens.hpp index 575eaed51..648086e1e 100644 --- a/src/algebra/Givens.hpp +++ b/src/algebra/Givens.hpp @@ -43,7 +43,7 @@ class Givens for (ssize_t i = x.size() - 1; i >= 0; --i) { const double inv_Aii = 1 / A(i, i); double sum = 0; - for (size_t j = i; j < x.size(); ++j) { + for (size_t j = i + 1; j < x.size(); ++j) { sum += A(i, j) * x[j]; } x[i] = (b[i] - sum) * inv_Aii; @@ -58,7 +58,7 @@ class Givens const double inv_Aii = 1 / A(i, i); for (size_t k = 0; k < X.numberOfColumns(); ++k) { double sum = 0; - for (size_t j = i; j < X.numberOfRows(); ++j) { + for (size_t j = i + 1; j < X.numberOfRows(); ++j) { sum += A(i, j) * X(j, k); } X(i, k) = (B(i, k) - sum) * inv_Aii; -- GitLab From fb83f35555fc8c70bcf86f372c04aa7e9273c28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 17 May 2024 11:45:12 +0200 Subject: [PATCH 020/122] Add affine reconstruction for DiscreteFunctionP0 in 1, 2 and 3d The possible data types are double, TinyVector and TinyMatrix --- src/algebra/Givens.hpp | 20 +- src/algebra/SmallVector.hpp | 7 + src/scheme/CMakeLists.txt | 2 + src/scheme/PolynomialReconstruction.hpp | 152 +++++-- src/utils/PugsTraits.hpp | 12 + tests/test_PolynomialReconstruction.cpp | 533 +++++++++++++++++++++++- 6 files changed, 666 insertions(+), 60 deletions(-) diff --git a/src/algebra/Givens.hpp b/src/algebra/Givens.hpp index 648086e1e..dc69a4537 100644 --- a/src/algebra/Givens.hpp +++ b/src/algebra/Givens.hpp @@ -40,10 +40,10 @@ class Givens static void _lift(const MatrixType& A, const RHSVectorType& b, VectorType& x) { - for (ssize_t i = x.size() - 1; i >= 0; --i) { + for (ssize_t i = x.dimension() - 1; i >= 0; --i) { const double inv_Aii = 1 / A(i, i); double sum = 0; - for (size_t j = i + 1; j < x.size(); ++j) { + for (size_t j = i + 1; j < x.dimension(); ++j) { sum += A(i, j) * x[j]; } x[i] = (b[i] - sum) * inv_Aii; @@ -71,12 +71,13 @@ class Givens static void solveInPlace(MatrixType& A, VectorType& x, RHSVectorType& b) { - Assert(x.size() == A.numberOfColumns(), "The number of columns of A must be the size of x"); - Assert(b.size() == A.numberOfRows(), "The number of rows of A must be the size of b"); + Assert(x.dimension() == A.numberOfColumns(), "The number of columns of A must be the size of x"); + Assert(b.dimension() == A.numberOfRows(), "The number of rows of A must be the size of b"); for (size_t j = 0; j < A.numberOfColumns(); ++j) { for (size_t i = A.numberOfRows() - 1; i > j; --i) { - double c(0), s(0); + double c; + double s; _givens(A(i - 1, j), A(i, j), c, s); for (size_t k = j; k < A.numberOfColumns(); ++k) { double xi_1(0), xi(0); @@ -98,8 +99,8 @@ class Givens static void solve(const MatrixType& A, VectorType& x, const RHSVectorType& b) { - Assert(x.size() == A.numberOfColumns(), "The number of columns of A must be the size of x"); - Assert(b.size() == A.numberOfRows(), "The number of rows of A must be the size of b"); + Assert(x.dimension() == A.numberOfColumns(), "The number of columns of A must be the size of x"); + Assert(b.dimension() == A.numberOfRows(), "The number of rows of A must be the size of b"); MatrixType rotateA = copy(A); RHSVectorType rotateb = copy(b); @@ -109,7 +110,7 @@ class Givens template <typename MatrixType, typename UnknownMatrixType, typename RHSMatrixType> static void - solveCollectionInPlace(const MatrixType& A, UnknownMatrixType& X, const RHSMatrixType& B) + solveCollectionInPlace(MatrixType& A, UnknownMatrixType& X, RHSMatrixType& B) { Assert(X.numberOfRows() == A.numberOfColumns(), "The number of columns of A must be the number of rows of X"); Assert(B.numberOfRows() == A.numberOfRows(), "The number of rows of A must be the rows of B"); @@ -117,7 +118,8 @@ class Givens for (size_t j = 0; j < A.numberOfColumns(); ++j) { for (size_t i = A.numberOfRows() - 1; i > j; --i) { - double c(0), s(0); + double c; + double s; _givens(A(i - 1, j), A(i, j), c, s); for (size_t k = j; k < A.numberOfColumns(); ++k) { double xi_1(0), xi(0); diff --git a/src/algebra/SmallVector.hpp b/src/algebra/SmallVector.hpp index d4a0bf842..bc3686c75 100644 --- a/src/algebra/SmallVector.hpp +++ b/src/algebra/SmallVector.hpp @@ -166,6 +166,13 @@ class SmallVector // LCOV_EXCL_LINE return m_values.size(); } + PUGS_INLINE + size_t + dimension() const noexcept + { + return m_values.size(); + } + PUGS_INLINE SmallVector& fill(const DataType& value) noexcept { diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt index dadbe73bb..c093c34f1 100644 --- a/src/scheme/CMakeLists.txt +++ b/src/scheme/CMakeLists.txt @@ -10,6 +10,8 @@ add_library( DiscreteFunctionVectorIntegrator.cpp DiscreteFunctionVectorInterpoler.cpp FluxingAdvectionSolver.cpp + + test_reconstruction.cpp ) target_link_libraries( diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index 7010d3d19..90b95c1db 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -20,55 +20,149 @@ class PolynomialReconstruction { public: template <MeshConcept MeshType, typename DataType> - PUGS_INLINE DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> + [[nodiscard]] PUGS_INLINE DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> build(const MeshType& mesh, const DiscreteFunctionP0<DataType> p0_function) { + using Rd = TinyVector<MeshType::Dimension>; + const size_t degree = 1; const Stencil stencil = StencilBuilder{}.build(mesh.connectivity()); - auto xr = mesh.xr(); auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); auto cell_is_owned = mesh.connectivity().cellIsOwned(); DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> dpk{mesh.meshVariant(), degree}; - if constexpr ((is_polygonal_mesh_v<MeshType>)and(MeshType::Dimension == 1)) { - auto qf = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(1)); - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { - if (cell_is_owned[cell_j_id]) { - auto stencil_cell_list = stencil[cell_j_id]; - SmallVector<double> b(stencil_cell_list.size()); + if constexpr (is_polygonal_mesh_v<MeshType>) { + if constexpr (std::is_arithmetic_v<DataType>) { + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { + if (cell_is_owned[cell_j_id]) { + auto stencil_cell_list = stencil[cell_j_id]; + SmallVector<double> b(stencil_cell_list.size()); - const double p_j = p0_function[cell_j_id]; - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; + const double pj = p0_function[cell_j_id]; + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; - b[i] = p0_function[cell_i_id] - p_j; - } + b[i] = p0_function[cell_i_id] - pj; + } - SmallMatrix<double> A(stencil_cell_list.size(), degree); + SmallMatrix<double> A(stencil_cell_list.size(), MeshType::Dimension); - using R1 = TinyVector<1>; + const Rd& Xj = xj[cell_j_id]; - const R1& Xj = xj[cell_j_id]; - auto X_Xj = [&](const R1& X) { return X - Xj; }; + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const Rd Xi_Xj = xj[cell_i_id] - Xj; + for (size_t l = 0; l < MeshType::Dimension; ++l) { + A(i, l) = Xi_Xj[l]; + } + } - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; + SmallVector<double> x(MeshType::Dimension); + Givens::solveInPlace(A, x, b); - A(i, 0) = X_Xj(xj[cell_i_id])[0]; - } + auto dpk_j = dpk.coefficients(cell_j_id); - SmallVector<double> x(1); - Givens::solveInPlace(A, x, b); + dpk_j[0] = pj; - dpk.coefficients(cell_j_id)[0] = p_j; - dpk.coefficients(cell_j_id)[1] = x[0]; - } - }); + for (size_t l = 0; l < MeshType::Dimension; ++l) { + dpk_j[1 + l] = x[l]; + } + } + }); + } else if constexpr (is_tiny_vector_v<DataType>) { + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { + if (cell_is_owned[cell_j_id]) { + auto stencil_cell_list = stencil[cell_j_id]; + SmallMatrix<double> B(stencil_cell_list.size(), DataType::Dimension); + + const DataType& pj = p0_function[cell_j_id]; + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const DataType& pi_pj = p0_function[cell_i_id] - pj; + for (size_t k = 0; k < DataType::Dimension; ++k) { + B(i, k) = pi_pj[k]; + } + } + + SmallMatrix<double> A(stencil_cell_list.size(), MeshType::Dimension); + + const Rd& Xj = xj[cell_j_id]; + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const Rd Xi_Xj = xj[cell_i_id] - Xj; + for (size_t l = 0; l < MeshType::Dimension; ++l) { + A(i, l) = Xi_Xj[l]; + } + } + + SmallMatrix<double> X(MeshType::Dimension, DataType::Dimension); + Givens::solveCollectionInPlace(A, X, B); + + auto dpk_j = dpk.coefficients(cell_j_id); + + dpk_j[0] = pj; + 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, k); + } + } + } + }); + } else if constexpr (is_tiny_matrix_v<DataType>) { + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { + if (cell_is_owned[cell_j_id]) { + auto stencil_cell_list = stencil[cell_j_id]; + SmallMatrix<double> B(stencil_cell_list.size(), DataType::Dimension); + + const DataType& pj = p0_function[cell_j_id]; + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const DataType& pi_pj = p0_function[cell_i_id] - pj; + for (size_t k = 0; k < DataType::NumberOfRows; ++k) { + for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { + B(i, k * DataType::NumberOfColumns + l) = pi_pj(k, l); + } + } + } + + SmallMatrix<double> A(stencil_cell_list.size(), MeshType::Dimension); + + const Rd& Xj = xj[cell_j_id]; + + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const Rd Xi_Xj = xj[cell_i_id] - Xj; + for (size_t l = 0; l < MeshType::Dimension; ++l) { + A(i, l) = Xi_Xj[l]; + } + } + + SmallMatrix<double> X(MeshType::Dimension, DataType::Dimension); + Givens::solveCollectionInPlace(A, X, B); + + auto dpk_j = dpk.coefficients(cell_j_id); + dpk_j[0] = pj; + + 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, k * DataType::NumberOfColumns + l); + } + } + } + } + }); + } else { + throw NotImplementedError("dealing with " + demangle<DataType>()); + } } synchronize(dpk.cellArrays()); diff --git a/src/utils/PugsTraits.hpp b/src/utils/PugsTraits.hpp index 2e1d40957..ab2244b69 100644 --- a/src/utils/PugsTraits.hpp +++ b/src/utils/PugsTraits.hpp @@ -106,6 +106,12 @@ inline constexpr bool is_tiny_vector_v = false; template <size_t N, typename T> inline constexpr bool is_tiny_vector_v<TinyVector<N, T>> = true; +template <typename T> +inline constexpr bool is_tiny_vector_v<const T> = is_tiny_vector_v<std::remove_cvref_t<T>>; + +template <typename T> +inline constexpr bool is_tiny_vector_v<T&> = is_tiny_vector_v<std::remove_cvref_t<T>>; + // Traits is_tiny_matrix template <typename T> @@ -114,6 +120,12 @@ inline constexpr bool is_tiny_matrix_v = false; template <size_t M, size_t N, typename T> inline constexpr bool is_tiny_matrix_v<TinyMatrix<M, N, T>> = true; +template <typename T> +inline constexpr bool is_tiny_matrix_v<const T> = is_tiny_matrix_v<std::remove_cvref_t<T>>; + +template <typename T> +inline constexpr bool is_tiny_matrix_v<T&> = is_tiny_matrix_v<std::remove_cvref_t<T>>; + // Trais is ItemValue template <typename T> diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 9a1df3257..72036d09f 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -24,40 +24,529 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { using R1 = TinyVector<1>; - for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); - auto& mesh = *p_mesh; + 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]); }); + + 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_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]); }); - auto affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + PolynomialReconstruction reconstruction; + auto dpk = reconstruction.build(mesh, fh); - DiscreteFunctionP0<double> fh{p_mesh}; + { + 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})); - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); }); + 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)); + } + } + } + } - PolynomialReconstruction reconstruction; - auto dpk = reconstruction.build(mesh, fh); + SECTION("R^mn data") + { + using R32 = TinyMatrix<3, 2>; + for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { + SECTION(named_mesh.name()) { - 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]))); + 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)); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } + } + } + } + + SECTION("2D") + { + using R2 = TinyVector<2>; + SECTION("R data") + { + for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + SECTION(named_mesh.name()) { - 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; + 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_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); + 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)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14)); } } } -- GitLab From 97598acd5c9d5962e4b49bc0832674e5985f0565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 14 Jun 2024 19:28:36 +0200 Subject: [PATCH 021/122] Add a StencilManager mechanism + performances tests and improvements Stencils are kept as long as the associated connectivity is alive --- src/algebra/ShrinkMatrixView.hpp | 52 +++ src/algebra/ShrinkVectorView.hpp | 46 ++ src/language/modules/SchemeModule.cpp | 23 + src/main.cpp | 3 + src/mesh/CMakeLists.txt | 1 + src/mesh/Connectivity.cpp | 11 + src/mesh/Connectivity.hpp | 2 +- src/mesh/StencilBuilder.cpp | 23 +- src/mesh/StencilBuilder.hpp | 8 +- src/mesh/StencilManager.cpp | 59 +++ src/mesh/StencilManager.hpp | 40 ++ src/scheme/CMakeLists.txt | 1 + src/scheme/PolynomialReconstruction.cpp | 558 ++++++++++++++++++++++++ src/scheme/PolynomialReconstruction.hpp | 188 +++++++- src/scheme/test_reconstruction.cpp | 64 +++ src/scheme/test_reconstruction.hpp | 16 + src/utils/PugsTraits.hpp | 29 ++ tests/mpi_test_main.cpp | 4 + tests/test_StencilBuilder.cpp | 16 +- tests/test_main.cpp | 4 + 20 files changed, 1121 insertions(+), 27 deletions(-) create mode 100644 src/algebra/ShrinkMatrixView.hpp create mode 100644 src/algebra/ShrinkVectorView.hpp create mode 100644 src/mesh/StencilManager.cpp create mode 100644 src/mesh/StencilManager.hpp create mode 100644 src/scheme/PolynomialReconstruction.cpp create mode 100644 src/scheme/test_reconstruction.cpp create mode 100644 src/scheme/test_reconstruction.hpp diff --git a/src/algebra/ShrinkMatrixView.hpp b/src/algebra/ShrinkMatrixView.hpp new file mode 100644 index 000000000..a93efc07a --- /dev/null +++ b/src/algebra/ShrinkMatrixView.hpp @@ -0,0 +1,52 @@ +#ifndef SHRINK_MATRIX_VIEW_HPP +#define SHRINK_MATRIX_VIEW_HPP + +#include <utils/PugsAssert.hpp> +#include <utils/PugsMacros.hpp> + +#include <cstddef> + +template <typename MatrixType> +class ShrinkMatrixView +{ + public: + using index_type = typename MatrixType::index_type; + using data_type = typename MatrixType::data_type; + + private: + MatrixType& m_matrix; + const size_t m_nb_rows; + + public: + PUGS_INLINE size_t + numberOfRows() const noexcept + { + return m_nb_rows; + } + + PUGS_INLINE size_t + numberOfColumns() const noexcept + { + return m_matrix.numberOfColumns(); + } + + PUGS_INLINE + data_type& + operator()(index_type i, index_type j) const noexcept(NO_ASSERT) + { + Assert(i < m_nb_rows and j < m_matrix.numberOfColumns(), "cannot access element: invalid indices"); + return m_matrix(i, j); + } + + ShrinkMatrixView(MatrixType& matrix, size_t nb_rows) noexcept(NO_ASSERT) : m_matrix{matrix}, m_nb_rows{nb_rows} + { + Assert(m_nb_rows <= matrix.numberOfRows(), "shrink number of rows must be smaller than original matrix's"); + } + + ShrinkMatrixView(const ShrinkMatrixView&) = delete; + ShrinkMatrixView(ShrinkMatrixView&&) = delete; + + ~ShrinkMatrixView() noexcept = default; +}; + +#endif // SHRINK_MATRIX_VIEW_HPP diff --git a/src/algebra/ShrinkVectorView.hpp b/src/algebra/ShrinkVectorView.hpp new file mode 100644 index 000000000..dc5faa800 --- /dev/null +++ b/src/algebra/ShrinkVectorView.hpp @@ -0,0 +1,46 @@ +#ifndef SHRINK_VECTOR_VIEW_HPP +#define SHRINK_VECTOR_VIEW_HPP + +#include <utils/PugsAssert.hpp> +#include <utils/PugsMacros.hpp> + +#include <cstddef> + +template <typename VectorType> +class ShrinkVectorView +{ + public: + using index_type = typename VectorType::index_type; + using data_type = typename VectorType::data_type; + + private: + VectorType& m_vector; + const size_t m_dimension; + + public: + PUGS_INLINE size_t + dimension() const noexcept + { + return m_dimension; + } + + PUGS_INLINE + data_type& + operator[](index_type i) const noexcept(NO_ASSERT) + { + Assert(i < m_dimension, "cannot access element: invalid indices"); + return m_vector[i]; + } + + ShrinkVectorView(VectorType& vector, size_t dimension) noexcept(NO_ASSERT) : m_vector{vector}, m_dimension{dimension} + { + Assert(m_dimension <= vector.dimension(), "shrink number of rows must be smaller than original vector's"); + } + + ShrinkVectorView(const ShrinkVectorView&) = delete; + ShrinkVectorView(ShrinkVectorView&&) = delete; + + ~ShrinkVectorView() noexcept = default; +}; + +#endif // SHRINK_VECTOR_VIEW_HPP diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index 7b404fcf6..f96e96f13 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -43,6 +43,7 @@ #include <scheme/OutflowBoundaryConditionDescriptor.hpp> #include <scheme/SymmetryBoundaryConditionDescriptor.hpp> #include <scheme/VariableBCDescriptor.hpp> +#include <scheme/test_reconstruction.hpp> #include <utils/Socket.hpp> #include <memory> @@ -670,6 +671,28 @@ SchemeModule::SchemeModule() )); +#warning REMOVE AFTER TESTS FINISHED + this->_addBuiltinFunction("test_reconstruction", + std::function( + + [](const std::shared_ptr<const MeshVariant> mesh_v, + const std::shared_ptr<const DiscreteFunctionVariant>& discrete_function_v) -> void { + test_reconstruction(mesh_v, discrete_function_v); + } + + )); + +#warning REMOVE AFTER TESTS FINISHED + this->_addBuiltinFunction("test_reconstruction", + std::function( + + [](const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& + discrete_function_variant_list) -> void { + test_reconstruction(discrete_function_variant_list); + } + + )); + MathFunctionRegisterForVh{*this}; } diff --git a/src/main.cpp b/src/main.cpp index 7d0112c80..a9bec44f0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ #include <mesh/DualConnectivityManager.hpp> #include <mesh/DualMeshManager.hpp> #include <mesh/MeshDataManager.hpp> +#include <mesh/StencilManager.hpp> #include <mesh/SynchronizerManager.hpp> #include <utils/ExecutionStatManager.hpp> #include <utils/GlobalVariableManager.hpp> @@ -24,6 +25,7 @@ main(int argc, char* argv[]) MeshDataManager::create(); DualConnectivityManager::create(); DualMeshManager::create(); + StencilManager::create(); GlobalVariableManager::create(); @@ -32,6 +34,7 @@ main(int argc, char* argv[]) GlobalVariableManager::destroy(); + StencilManager::destroy(); DualMeshManager::destroy(); DualConnectivityManager::destroy(); MeshDataManager::destroy(); diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt index d03051cae..b4d70359a 100644 --- a/src/mesh/CMakeLists.txt +++ b/src/mesh/CMakeLists.txt @@ -44,6 +44,7 @@ add_library( MeshUtils.cpp MeshVariant.cpp StencilBuilder.cpp + StencilManager.cpp SynchronizerManager.cpp ) diff --git a/src/mesh/Connectivity.cpp b/src/mesh/Connectivity.cpp index bd1e0d85b..b28237ded 100644 --- a/src/mesh/Connectivity.cpp +++ b/src/mesh/Connectivity.cpp @@ -2,6 +2,7 @@ #include <mesh/ConnectivityDescriptor.hpp> #include <mesh/ItemValueUtils.hpp> +#include <mesh/StencilManager.hpp> #include <utils/GlobalVariableManager.hpp> #include <utils/Messenger.hpp> @@ -310,6 +311,12 @@ Connectivity<Dimension>::_write(std::ostream& os) const return os; } +template <size_t Dim> +Connectivity<Dim>::~Connectivity() +{ + StencilManager::instance().deleteConnectivity(this->m_id); +} + template std::ostream& Connectivity<1>::_write(std::ostream&) const; template std::ostream& Connectivity<2>::_write(std::ostream&) const; template std::ostream& Connectivity<3>::_write(std::ostream&) const; @@ -330,6 +337,10 @@ template Connectivity<1>::Connectivity(); template Connectivity<2>::Connectivity(); template Connectivity<3>::Connectivity(); +template Connectivity<1>::~Connectivity(); +template Connectivity<2>::~Connectivity(); +template Connectivity<3>::~Connectivity(); + template void Connectivity<1>::_buildFrom(const ConnectivityDescriptor&); template void Connectivity<2>::_buildFrom(const ConnectivityDescriptor&); template void Connectivity<3>::_buildFrom(const ConnectivityDescriptor&); diff --git a/src/mesh/Connectivity.hpp b/src/mesh/Connectivity.hpp index 24d9ef656..c04bdbdf9 100644 --- a/src/mesh/Connectivity.hpp +++ b/src/mesh/Connectivity.hpp @@ -744,7 +744,7 @@ class Connectivity final : public IConnectivity void _buildFrom(const ConnectivityDescriptor& descriptor); public: - ~Connectivity() = default; + ~Connectivity(); }; template <size_t Dimension> diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index a43dbef60..443130400 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -104,7 +104,7 @@ StencilBuilder::_getColumnIndices(const ConnectivityType& connectivity, const Ar template <typename ConnectivityType> Stencil -StencilBuilder::build(const ConnectivityType& connectivity) const +StencilBuilder::_build(const ConnectivityType& connectivity) const { Array<const uint32_t> row_map = this->_getRowMap(connectivity); Array<const uint32_t> column_indices = this->_getColumnIndices(connectivity, row_map); @@ -112,6 +112,21 @@ StencilBuilder::build(const ConnectivityType& connectivity) const return ConnectivityMatrix{row_map, column_indices}; } -template Stencil StencilBuilder::build(const Connectivity<1>& connectivity) const; -template Stencil StencilBuilder::build(const Connectivity<2>& connectivity) const; -template Stencil StencilBuilder::build(const Connectivity<3>& connectivity) const; +Stencil +StencilBuilder::build(const IConnectivity& connectivity) const +{ + switch (connectivity.dimension()) { + case 1: { + return StencilBuilder::_build(dynamic_cast<const Connectivity<1>&>(connectivity)); + } + case 2: { + return StencilBuilder::_build(dynamic_cast<const Connectivity<2>&>(connectivity)); + } + case 3: { + return StencilBuilder::_build(dynamic_cast<const Connectivity<3>&>(connectivity)); + } + default: { + throw UnexpectedError("invalid connectivity dimension"); + } + } +} diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index 3a4235079..1743a2d71 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -3,6 +3,7 @@ #include <mesh/Stencil.hpp> +class IConnectivity; class StencilBuilder { private: @@ -13,10 +14,13 @@ class StencilBuilder Array<const uint32_t> _getColumnIndices(const ConnectivityType& connectivity, const Array<const uint32_t>& row_map) const; - public: template <typename ConnectivityType> - Stencil build(const ConnectivityType& connectivity) const; + Stencil _build(const ConnectivityType& connectivity) const; + + friend class StencilManager; + Stencil build(const IConnectivity& connectivity) const; + public: StencilBuilder() = default; StencilBuilder(const StencilBuilder&) = default; StencilBuilder(StencilBuilder&&) = default; diff --git a/src/mesh/StencilManager.cpp b/src/mesh/StencilManager.cpp new file mode 100644 index 000000000..58190f002 --- /dev/null +++ b/src/mesh/StencilManager.cpp @@ -0,0 +1,59 @@ +#include <mesh/StencilManager.hpp> + +#include <mesh/StencilBuilder.hpp> +#include <utils/Exceptions.hpp> + +StencilManager* StencilManager::m_instance = nullptr; + +void +StencilManager::create() +{ + Assert(m_instance == nullptr, "StencilManager is already created"); + m_instance = new StencilManager; +} + +void +StencilManager::destroy() +{ + Assert(m_instance != nullptr, "StencilManager was not created"); + + // LCOV_EXCL_START + if (m_instance->m_connectivity_id_to_stencil_map.size() > 0) { + std::stringstream error; + error << ": some connectivities are still registered\n"; + for (const auto& [id, stencil_p] : m_instance->m_connectivity_id_to_stencil_map) { + error << " - connectivity of id " << rang::fgB::magenta << id << rang::style::reset << '\n'; + } + throw UnexpectedError(error.str()); + // LCOV_EXCL_STOP + } + delete m_instance; + m_instance = nullptr; +} + +const Stencil& +StencilManager::getStencil(const IConnectivity& connectivity) +{ + if (not m_connectivity_id_to_stencil_map.contains(connectivity.id())) { + m_connectivity_id_to_stencil_map[connectivity.id()] = + std::make_shared<Stencil>(StencilBuilder{}.build(connectivity)); + } + + return *m_connectivity_id_to_stencil_map.at(connectivity.id()); +} + +void +StencilManager::deleteConnectivity(const size_t connectivity_id) +{ + bool has_removed = false; + do { + has_removed = false; + for (const auto& [id, p_stencil] : m_connectivity_id_to_stencil_map) { + if (connectivity_id == id) { + m_connectivity_id_to_stencil_map.erase(connectivity_id); + has_removed = true; + break; + } + } + } while (has_removed); +} diff --git a/src/mesh/StencilManager.hpp b/src/mesh/StencilManager.hpp new file mode 100644 index 000000000..d1503e60f --- /dev/null +++ b/src/mesh/StencilManager.hpp @@ -0,0 +1,40 @@ +#ifndef STENCIL_MANAGER_HPP +#define STENCIL_MANAGER_HPP + +#include <mesh/IConnectivity.hpp> +#include <mesh/Stencil.hpp> + +#include <memory> +#include <unordered_map> + +class StencilManager +{ + private: + StencilManager() = default; + ~StencilManager() = default; + + static StencilManager* m_instance; + + std::unordered_map<size_t, std::shared_ptr<const Stencil>> m_connectivity_id_to_stencil_map; + + public: + static void create(); + static void destroy(); + + PUGS_INLINE + static StencilManager& + instance() + { + Assert(m_instance != nullptr, "StencilManager was not created!"); + return *m_instance; + } + + void deleteConnectivity(const size_t connectivity_id); + + const Stencil& getStencil(const IConnectivity& i_connectivity); + + StencilManager(const StencilManager&) = delete; + StencilManager(StencilManager&&) = delete; +}; + +#endif // STENCIL_MANAGER_HPP diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt index c093c34f1..a759fca6a 100644 --- a/src/scheme/CMakeLists.txt +++ b/src/scheme/CMakeLists.txt @@ -10,6 +10,7 @@ add_library( DiscreteFunctionVectorIntegrator.cpp DiscreteFunctionVectorInterpoler.cpp FluxingAdvectionSolver.cpp + PolynomialReconstruction.cpp test_reconstruction.cpp ) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp new file mode 100644 index 000000000..fc9b88d0e --- /dev/null +++ b/src/scheme/PolynomialReconstruction.cpp @@ -0,0 +1,558 @@ +#include <scheme/PolynomialReconstruction.hpp> + +#include <scheme/DiscreteFunctionUtils.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> + +class MutableDiscreteFunctionDPkVariant +{ + public: + using Variant = std::variant<DiscreteFunctionDPk<1, double>, + DiscreteFunctionDPk<1, TinyVector<1>>, + DiscreteFunctionDPk<1, TinyVector<2>>, + DiscreteFunctionDPk<1, TinyVector<3>>, + DiscreteFunctionDPk<1, TinyMatrix<1>>, + DiscreteFunctionDPk<1, TinyMatrix<2>>, + DiscreteFunctionDPk<1, TinyMatrix<3>>, + + DiscreteFunctionDPk<2, double>, + DiscreteFunctionDPk<2, TinyVector<1>>, + DiscreteFunctionDPk<2, TinyVector<2>>, + DiscreteFunctionDPk<2, TinyVector<3>>, + DiscreteFunctionDPk<2, TinyMatrix<1>>, + DiscreteFunctionDPk<2, TinyMatrix<2>>, + DiscreteFunctionDPk<2, TinyMatrix<3>>, + + DiscreteFunctionDPk<3, double>, + DiscreteFunctionDPk<3, TinyVector<1>>, + DiscreteFunctionDPk<3, TinyVector<2>>, + DiscreteFunctionDPk<3, TinyVector<3>>, + DiscreteFunctionDPk<3, TinyMatrix<1>>, + DiscreteFunctionDPk<3, TinyMatrix<2>>, + DiscreteFunctionDPk<3, TinyMatrix<3>>>; + +#warning add DiscreteFunctionDPkVector to the variant + + private: + Variant m_mutable_discrete_function_dpk; + + public: + PUGS_INLINE + const Variant& + mutableDiscreteFunctionDPk() const + { + return m_mutable_discrete_function_dpk; + } + + template <typename DiscreteFunctionDPkT> + PUGS_INLINE auto&& + get() const + { + static_assert(is_discrete_function_dpk_v<DiscreteFunctionDPkT>, "invalid template argument"); + +#ifndef NDEBUG + if (not std::holds_alternative<DiscreteFunctionDPkT>(this->m_mutable_discrete_function_dpk)) { + std::ostringstream error_msg; + error_msg << "invalid discrete function type\n"; + error_msg << "- required " << rang::fgB::red << demangle<DiscreteFunctionDPkT>() << rang::fg::reset << '\n'; + error_msg << "- contains " << rang::fgB::yellow + << std::visit([](auto&& f) -> std::string { return demangle<decltype(f)>(); }, + this->m_mutable_discrete_function_dpk) + << rang::fg::reset; + throw NormalError(error_msg.str()); + } +#endif // NDEBUG + + return std::get<DiscreteFunctionDPkT>(this->mutableDiscreteFunctionDPk()); + } + + template <size_t Dimension, typename DataType> + MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPk<Dimension, DataType>& discrete_function_dpk) + : m_mutable_discrete_function_dpk{DiscreteFunctionDPk<Dimension, DataType>{discrete_function_dpk}} + { + static_assert(std::is_same_v<std::remove_const_t<DataType>, double> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<3, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<1, 1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<2, 2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<3, 3, double>>, + "DiscreteFunctionDPk with this DataType is not allowed in variant"); + } + + MutableDiscreteFunctionDPkVariant& operator=(MutableDiscreteFunctionDPkVariant&&) = default; + MutableDiscreteFunctionDPkVariant& operator=(const MutableDiscreteFunctionDPkVariant&) = default; + + MutableDiscreteFunctionDPkVariant(const MutableDiscreteFunctionDPkVariant&) = default; + MutableDiscreteFunctionDPkVariant(MutableDiscreteFunctionDPkVariant&&) = default; + + MutableDiscreteFunctionDPkVariant() = delete; + ~MutableDiscreteFunctionDPkVariant() = default; +}; + +template <MeshConcept MeshType> +[[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> +PolynomialReconstruction::_build( + const std::shared_ptr<const MeshType>& p_mesh, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const +{ + const MeshType& mesh = *p_mesh; + + using Rd = TinyVector<MeshType::Dimension>; + + const size_t number_of_columns = [&]() { + size_t n = 0; + for (auto discrete_function_variant_p : discrete_function_variant_list) { + n += std::visit( + [](auto&& discrete_function) -> size_t { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + using data_type = std::decay_t<typename DiscreteFunctionT::data_type>; + if constexpr (std::is_same_v<data_type, double>) { + return 1; + } else if constexpr (is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>) { + return data_type::Dimension; + } else { + throw UnexpectedError("unexpected data type " + demangle<data_type>()); + } + } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { + return discrete_function.size(); + } else { + throw UnexpectedError("unexpected discrete function type"); + } + }, + discrete_function_variant_p->discreteFunction()); + } + return n; + }(); + + const size_t degree = 1; + const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity()); + + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + auto cell_is_owned = mesh.connectivity().cellIsOwned(); + + const size_t max_stencil_size = [&]() { + size_t max_size = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto stencil_cell_list = stencil[cell_id]; + if (cell_is_owned[cell_id] and stencil_cell_list.size() > max_size) { + max_size = stencil_cell_list.size(); + } + } + return max_size; + }(); + + Kokkos::Experimental::UniqueToken<Kokkos::DefaultExecutionSpace::execution_space, + Kokkos::Experimental::UniqueTokenScope::Global> + tokens; + + std::vector<MutableDiscreteFunctionDPkVariant> mutable_discrete_function_dpk_variant_list; + for (size_t i_discrete_function_variant = 0; i_discrete_function_variant < discrete_function_variant_list.size(); + ++i_discrete_function_variant) { + auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; + + std::visit( + [&](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + using DataType = std::remove_const_t<std::decay_t<typename DiscreteFunctionT::data_type>>; + mutable_discrete_function_dpk_variant_list.push_back( + DiscreteFunctionDPk<MeshType::Dimension, DataType>(p_mesh, degree)); + } else { + throw NotImplementedError("discrete function type"); + } + }, + discrete_function_variant->discreteFunction()); + } + + SmallArray<SmallMatrix<double>> A_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallMatrix<double>> B_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallMatrix<double>> X_pool(Kokkos::DefaultExecutionSpace::concurrency()); + + for (size_t i = 0; i < A_pool.size(); ++i) { + A_pool[i] = SmallMatrix<double>(max_stencil_size, MeshType::Dimension); + B_pool[i] = SmallMatrix<double>(max_stencil_size, number_of_columns); + X_pool[i] = SmallMatrix<double>(MeshType::Dimension, number_of_columns); + } + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { + if (cell_is_owned[cell_j_id]) { + const int32_t t = tokens.acquire(); + + auto stencil_cell_list = stencil[cell_j_id]; + ShrinkMatrixView B(B_pool[t], stencil_cell_list.size()); + + size_t column_begin = 0; + for (size_t i_discrete_function_variant = 0; + i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { + auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; + + std::visit( + [&](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + + const DataType& pj = discrete_function[cell_j_id]; + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const DataType& pi_pj = discrete_function[cell_i_id] - pj; + if constexpr (std::is_arithmetic_v<DataType>) { + B(i, column_begin) = pi_pj; + } else if constexpr (is_tiny_vector_v<DataType>) { + for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { + B(i, kB) = pi_pj[k]; + } + } else if constexpr (is_tiny_matrix_v<DataType>) { + for (size_t k = 0; k < DataType::NumberOfRows; ++k) { + for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { + B(i, column_begin + k * DataType::NumberOfColumns + l) = pi_pj(k, l); + } + } + } + } + + if constexpr (std::is_arithmetic_v<DataType>) { + ++column_begin; + } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) { + column_begin += DataType::Dimension; + } + } + }, + discrete_function_variant->discreteFunction()); + } + + ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); + + const Rd& Xj = xj[cell_j_id]; + + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const Rd Xi_Xj = xj[cell_i_id] - Xj; + for (size_t l = 0; l < MeshType::Dimension; ++l) { + A(i, l) = Xi_Xj[l]; + } + } + + SmallMatrix<double> X = X_pool[t]; + Givens::solveCollectionInPlace(A, X, B); + + column_begin = 0; + for (size_t i_discrete_function_variant = 0; + i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { + auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; + + std::visit( + [&](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + + const DataType& pj = discrete_function[cell_j_id]; + auto discrete_function_dpk = mutable_discrete_function_dpk_variant_list[i_discrete_function_variant] + .get<DiscreteFunctionDPk<MeshType::Dimension, DataType>>(); + auto dpk_j = discrete_function_dpk.coefficients(cell_j_id); + dpk_j[0] = pj; + + 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); + } + } + 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 { + throw NotImplementedError("discrete function type"); + } + }, + discrete_function_variant->discreteFunction()); + } + + tokens.release(t); + } + }); + + std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> discrete_function_dpk_variant_list; + + // for (auto discrete_function_dpk_variant_p : mutable_discrete_function_dpk_variant_list) { + // const_discrete_function_dpk_variant_list.push_back(discrete_function_dpk_variant_p); + // } + + return discrete_function_dpk_variant_list; +} + +std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> +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"); + } + + auto mesh_v = getCommonMesh(discrete_function_variant_list); + + return std::visit([&](auto&& p_mesh) { return this->_build(p_mesh, discrete_function_variant_list); }, + mesh_v->variant()); +} + +/**************************************/ + +template <MeshConcept MeshType> +[[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> +PolynomialReconstruction::_build2( + const std::shared_ptr<const MeshType>& p_mesh, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const +{ + const MeshType& mesh = *p_mesh; + + using Rd = TinyVector<MeshType::Dimension>; + + const size_t number_of_columns = [&]() { + size_t n = 0; + for (auto discrete_function_variant_p : discrete_function_variant_list) { + n += std::visit( + [](auto&& discrete_function) -> size_t { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + using data_type = std::decay_t<typename DiscreteFunctionT::data_type>; + if constexpr (std::is_same_v<data_type, double>) { + return 1; + } else if constexpr (is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>) { + return data_type::Dimension; + } else { + throw UnexpectedError("unexpected data type " + demangle<data_type>()); + } + } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { + return discrete_function.size(); + } else { + throw UnexpectedError("unexpected discrete function type"); + } + }, + discrete_function_variant_p->discreteFunction()); + } + return n; + }(); + + const size_t degree = 1; + const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity()); + + Array<const double*> value_begining(discrete_function_variant_list.size()); + Array<size_t> value_size(discrete_function_variant_list.size()); + + for (size_t i = 0; i < discrete_function_variant_list.size(); ++i) { + DiscreteFunctionVariant discrete_function_v = *(discrete_function_variant_list[i]); + std::visit( + [=](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + using data_type = std::decay_t<typename DiscreteFunctionT::data_type>; + if constexpr (std::is_same_v<data_type, double>) { + value_begining[i] = &(discrete_function[CellId{0}]); + value_size[i] = 1; + } else if constexpr (is_tiny_vector_v<data_type>) { + value_begining[i] = &(discrete_function[CellId{0}][0]); + value_size[i] = data_type::Dimension; + } else if constexpr (is_tiny_matrix_v<data_type>) { + value_begining[i] = &(discrete_function[CellId{0}](0, 0)); + value_size[i] = data_type::Dimension; + } else { + throw UnexpectedError("unexpected data type " + demangle<data_type>()); + } + } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { + throw NotImplementedError("Discrete Function P0 Vector"); + } else { + throw UnexpectedError("unexpected discrete function type"); + } + }, + discrete_function_v.discreteFunction()); + } + + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + auto cell_is_owned = mesh.connectivity().cellIsOwned(); + + const size_t max_stencil_size = [&]() { + size_t max_size = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto stencil_cell_list = stencil[cell_id]; + if (cell_is_owned[cell_id] and stencil_cell_list.size() > max_size) { + max_size = stencil_cell_list.size(); + } + } + return max_size; + }(); + + Kokkos::Experimental::UniqueToken<Kokkos::DefaultExecutionSpace::execution_space, + Kokkos::Experimental::UniqueTokenScope::Global> + tokens; + + std::vector<MutableDiscreteFunctionDPkVariant> mutable_discrete_function_dpk_variant_list; + for (size_t i_discrete_function_variant = 0; i_discrete_function_variant < discrete_function_variant_list.size(); + ++i_discrete_function_variant) { + auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; + + std::visit( + [&](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + using DataType = std::remove_const_t<std::decay_t<typename DiscreteFunctionT::data_type>>; + mutable_discrete_function_dpk_variant_list.push_back( + DiscreteFunctionDPk<MeshType::Dimension, DataType>(p_mesh, degree)); + } else { + throw NotImplementedError("discrete function type"); + } + }, + discrete_function_variant->discreteFunction()); + } + + SmallArray<SmallMatrix<double>> A_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallMatrix<double>> B_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallMatrix<double>> X_pool(Kokkos::DefaultExecutionSpace::concurrency()); + + for (size_t i = 0; i < A_pool.size(); ++i) { + A_pool[i] = SmallMatrix<double>(max_stencil_size, MeshType::Dimension); + B_pool[i] = SmallMatrix<double>(max_stencil_size, number_of_columns); + X_pool[i] = SmallMatrix<double>(MeshType::Dimension, number_of_columns); + } + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { + if (cell_is_owned[cell_j_id]) { + const int32_t t = tokens.acquire(); + + auto stencil_cell_list = stencil[cell_j_id]; + ShrinkMatrixView B(B_pool[t], stencil_cell_list.size()); + + size_t i_column = 0; + + for (size_t i_value = 0; i_value < value_begining.size(); ++i_value) { + const size_t i_value_size = value_size[i_value]; + + const size_t cell_j_value_index = cell_j_id * i_value_size; + + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const size_t cell_i_value_index = cell_i_id * i_value_size; + size_t j_index = cell_j_value_index; + size_t i_index = cell_i_value_index; + + for (size_t shift = 0; shift < i_value_size; ++shift, ++i_index, ++j_index) { + B(i, i_column++) = value_begining[i_value][i_index] - value_begining[i_value][j_index]; + } + } + } + + ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); + + const Rd& Xj = xj[cell_j_id]; + + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const Rd Xi_Xj = xj[cell_i_id] - Xj; + for (size_t l = 0; l < MeshType::Dimension; ++l) { + A(i, l) = Xi_Xj[l]; + } + } + + // SmallMatrix<double> X = X_pool[t]; + + TinyMatrix<MeshType::Dimension, 1> X; + Givens::solveCollectionInPlace(A, X, B); + + size_t column_begin = 0; + for (size_t i_discrete_function_variant = 0; + i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { + auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; + + std::visit( + [&](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + + const DataType& pj = discrete_function[cell_j_id]; + auto discrete_function_dpk = mutable_discrete_function_dpk_variant_list[i_discrete_function_variant] + .get<DiscreteFunctionDPk<MeshType::Dimension, DataType>>(); + auto dpk_j = discrete_function_dpk.coefficients(cell_j_id); + dpk_j[0] = pj; + + 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); + } + } + 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 { + throw NotImplementedError("discrete function type"); + } + }, + discrete_function_variant->discreteFunction()); + } + + tokens.release(t); + } + }); + + std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> discrete_function_dpk_variant_list; + + // for (auto discrete_function_dpk_variant_p : mutable_discrete_function_dpk_variant_list) { + // const_discrete_function_dpk_variant_list.push_back(discrete_function_dpk_variant_p); + // } + + return discrete_function_dpk_variant_list; +} + +std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> +PolynomialReconstruction::build2( + 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"); + } + + auto mesh_v = getCommonMesh(discrete_function_variant_list); + + return std::visit([&](auto&& p_mesh) { return this->_build(p_mesh, discrete_function_variant_list); }, + mesh_v->variant()); +} diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index 90b95c1db..0612e605b 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -2,9 +2,11 @@ #define POLYNOMIAL_RECONSTRUCTION_HPP #include <mesh/MeshTraits.hpp> -#include <mesh/StencilBuilder.hpp> +#include <mesh/StencilManager.hpp> #include <scheme/DiscreteFunctionDPk.hpp> +#include <algebra/ShrinkMatrixView.hpp> +#include <algebra/ShrinkVectorView.hpp> #include <algebra/SmallMatrix.hpp> #include <algebra/SmallVector.hpp> @@ -16,31 +18,161 @@ #include <mesh/MeshData.hpp> #include <mesh/MeshDataManager.hpp> +class DiscreteFunctionDPkVariant; +class DiscreteFunctionVariant; + +#include <memory> +#include <variant> + +class DiscreteFunctionDPkVariant +{ + public: + using Variant = std::variant<DiscreteFunctionDPk<1, const double>, + DiscreteFunctionDPk<1, const TinyVector<1>>, + DiscreteFunctionDPk<1, const TinyVector<2>>, + DiscreteFunctionDPk<1, const TinyVector<3>>, + DiscreteFunctionDPk<1, const TinyMatrix<1>>, + DiscreteFunctionDPk<1, const TinyMatrix<2>>, + DiscreteFunctionDPk<1, const TinyMatrix<3>>, + + DiscreteFunctionDPk<2, const double>, + DiscreteFunctionDPk<2, const TinyVector<1>>, + DiscreteFunctionDPk<2, const TinyVector<2>>, + DiscreteFunctionDPk<2, const TinyVector<3>>, + DiscreteFunctionDPk<2, const TinyMatrix<1>>, + DiscreteFunctionDPk<2, const TinyMatrix<2>>, + DiscreteFunctionDPk<2, const TinyMatrix<3>>, + + DiscreteFunctionDPk<3, const double>, + DiscreteFunctionDPk<3, const TinyVector<1>>, + DiscreteFunctionDPk<3, const TinyVector<2>>, + DiscreteFunctionDPk<3, const TinyVector<3>>, + DiscreteFunctionDPk<3, const TinyMatrix<1>>, + DiscreteFunctionDPk<3, const TinyMatrix<2>>, + DiscreteFunctionDPk<3, const TinyMatrix<3>>>; + +#warning add DiscreteFunctionDPkVector to the variant + + private: + Variant m_discrete_function_dpk; + + public: + PUGS_INLINE + const Variant& + discreteFunctionDPk() const + { + return m_discrete_function_dpk; + } + + template <typename DiscreteFunctionDPkT> + PUGS_INLINE auto + get() const + { + static_assert(is_discrete_function_dpk_v<DiscreteFunctionDPkT>, "invalid template argument"); + using DataType = typename DiscreteFunctionDPkT::data_type; + static_assert(std::is_const_v<DataType>, "data type of extracted discrete function must be const"); + +#ifndef NDEBUG + if (not std::holds_alternative<DiscreteFunctionDPkT>(this->m_discrete_function_dpk)) { + std::ostringstream error_msg; + error_msg << "invalid discrete function type\n"; + error_msg << "- required " << rang::fgB::red << demangle<DiscreteFunctionDPkT>() << rang::fg::reset << '\n'; + error_msg << "- contains " << rang::fgB::yellow + << std::visit([](auto&& f) -> std::string { return demangle<decltype(f)>(); }, + this->m_discrete_function_dpk) + << rang::fg::reset; + throw NormalError(error_msg.str()); + } +#endif // NDEBUG + + return std::get<DiscreteFunctionDPkT>(this->discreteFunctionDPk()); + } + + template <size_t Dimension, typename DataType> + DiscreteFunctionDPkVariant(const DiscreteFunctionDPk<Dimension, DataType>& discrete_function_dpk) + : m_discrete_function_dpk{DiscreteFunctionDPk<Dimension, const DataType>{discrete_function_dpk}} + { + static_assert(std::is_same_v<std::remove_const_t<DataType>, double> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<3, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<1, 1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<2, 2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<3, 3, double>>, + "DiscreteFunctionDPk with this DataType is not allowed in variant"); + } + + DiscreteFunctionDPkVariant& operator=(DiscreteFunctionDPkVariant&&) = default; + DiscreteFunctionDPkVariant& operator=(const DiscreteFunctionDPkVariant&) = default; + + DiscreteFunctionDPkVariant(const DiscreteFunctionDPkVariant&) = default; + DiscreteFunctionDPkVariant(DiscreteFunctionDPkVariant&&) = default; + + DiscreteFunctionDPkVariant() = delete; + ~DiscreteFunctionDPkVariant() = default; +}; + class PolynomialReconstruction { + private: + template <MeshConcept MeshType> + [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> _build( + const std::shared_ptr<const MeshType>& p_mesh, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + + template <MeshConcept MeshType> + [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> _build2( + const std::shared_ptr<const MeshType>& p_mesh, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + public: template <MeshConcept MeshType, typename DataType> [[nodiscard]] PUGS_INLINE DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> - build(const MeshType& mesh, const DiscreteFunctionP0<DataType> p0_function) + build(const MeshType& mesh, const DiscreteFunctionP0<DataType> p0_function) const { using Rd = TinyVector<MeshType::Dimension>; - const size_t degree = 1; - const Stencil stencil = StencilBuilder{}.build(mesh.connectivity()); + const size_t degree = 1; + const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity()); auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); auto cell_is_owned = mesh.connectivity().cellIsOwned(); + const size_t max_stencil_size = [&]() { + size_t max_size = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto stencil_cell_list = stencil[cell_id]; + if (cell_is_owned[cell_id] and stencil_cell_list.size() > max_size) { + max_size = stencil_cell_list.size(); + } + } + return max_size; + }(); + + Kokkos::Experimental::UniqueToken<Kokkos::DefaultExecutionSpace::execution_space, + Kokkos::Experimental::UniqueTokenScope::Global> + tokens; + DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> dpk{mesh.meshVariant(), degree}; if constexpr (is_polygonal_mesh_v<MeshType>) { if constexpr (std::is_arithmetic_v<DataType>) { + SmallArray<SmallMatrix<double>> A_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallVector<double>> b_pool(Kokkos::DefaultExecutionSpace::concurrency()); + for (size_t i = 0; i < A_pool.size(); ++i) { + A_pool[i] = SmallMatrix<double>(max_stencil_size, MeshType::Dimension); + b_pool[i] = SmallVector<double>(max_stencil_size); + } + parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { if (cell_is_owned[cell_j_id]) { + const int32_t t = tokens.acquire(); + auto stencil_cell_list = stencil[cell_j_id]; - SmallVector<double> b(stencil_cell_list.size()); + + ShrinkVectorView b(b_pool[t], stencil_cell_list.size()); const double pj = p0_function[cell_j_id]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { @@ -49,7 +181,7 @@ class PolynomialReconstruction b[i] = p0_function[cell_i_id] - pj; } - SmallMatrix<double> A(stencil_cell_list.size(), MeshType::Dimension); + ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); const Rd& Xj = xj[cell_j_id]; @@ -61,7 +193,7 @@ class PolynomialReconstruction } } - SmallVector<double> x(MeshType::Dimension); + TinyVector<MeshType::Dimension> x; Givens::solveInPlace(A, x, b); auto dpk_j = dpk.coefficients(cell_j_id); @@ -71,14 +203,25 @@ class PolynomialReconstruction for (size_t l = 0; l < MeshType::Dimension; ++l) { dpk_j[1 + l] = x[l]; } + + tokens.release(t); } }); } else if constexpr (is_tiny_vector_v<DataType>) { + SmallArray<SmallMatrix<double>> A_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallMatrix<double>> B_pool(Kokkos::DefaultExecutionSpace::concurrency()); + for (size_t i = 0; i < A_pool.size(); ++i) { + A_pool[i] = SmallMatrix<double>(max_stencil_size, MeshType::Dimension); + B_pool[i] = SmallMatrix<double>(max_stencil_size, DataType::Dimension); + } + parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { if (cell_is_owned[cell_j_id]) { + const int32_t t = tokens.acquire(); + auto stencil_cell_list = stencil[cell_j_id]; - SmallMatrix<double> B(stencil_cell_list.size(), DataType::Dimension); + ShrinkMatrixView B(B_pool[t], stencil_cell_list.size()); const DataType& pj = p0_function[cell_j_id]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { @@ -89,7 +232,7 @@ class PolynomialReconstruction } } - SmallMatrix<double> A(stencil_cell_list.size(), MeshType::Dimension); + ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); const Rd& Xj = xj[cell_j_id]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { @@ -100,7 +243,7 @@ class PolynomialReconstruction } } - SmallMatrix<double> X(MeshType::Dimension, DataType::Dimension); + TinyMatrix<MeshType::Dimension, DataType::Dimension> X; Givens::solveCollectionInPlace(A, X, B); auto dpk_j = dpk.coefficients(cell_j_id); @@ -112,14 +255,25 @@ class PolynomialReconstruction dpk_j_ip1[k] = X(i, k); } } + + tokens.release(t); } }); } else if constexpr (is_tiny_matrix_v<DataType>) { + SmallArray<SmallMatrix<double>> A_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallMatrix<double>> B_pool(Kokkos::DefaultExecutionSpace::concurrency()); + for (size_t i = 0; i < A_pool.size(); ++i) { + A_pool[i] = SmallMatrix<double>(max_stencil_size, MeshType::Dimension); + B_pool[i] = SmallMatrix<double>(max_stencil_size, DataType::Dimension); + } + parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { if (cell_is_owned[cell_j_id]) { + const int32_t t = tokens.acquire(); + auto stencil_cell_list = stencil[cell_j_id]; - SmallMatrix<double> B(stencil_cell_list.size(), DataType::Dimension); + ShrinkMatrixView B(B_pool[t], stencil_cell_list.size()); const DataType& pj = p0_function[cell_j_id]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { @@ -132,7 +286,7 @@ class PolynomialReconstruction } } - SmallMatrix<double> A(stencil_cell_list.size(), MeshType::Dimension); + ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); const Rd& Xj = xj[cell_j_id]; @@ -144,7 +298,7 @@ class PolynomialReconstruction } } - SmallMatrix<double> X(MeshType::Dimension, DataType::Dimension); + TinyMatrix<MeshType::Dimension, DataType::Dimension> X; Givens::solveCollectionInPlace(A, X, B); auto dpk_j = dpk.coefficients(cell_j_id); @@ -158,6 +312,8 @@ class PolynomialReconstruction } } } + + tokens.release(t); } }); } else { @@ -169,6 +325,12 @@ class PolynomialReconstruction return dpk; } + [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> build( + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + + [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> build2( + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + PolynomialReconstruction() {} }; diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp new file mode 100644 index 000000000..9ec19396e --- /dev/null +++ b/src/scheme/test_reconstruction.cpp @@ -0,0 +1,64 @@ +#include <scheme/test_reconstruction.hpp> + +#include <scheme/PolynomialReconstruction.hpp> +#include <utils/Timer.hpp> + +void +test_reconstruction(const std::shared_ptr<const MeshVariant>& mesh_v, + const std::shared_ptr<const DiscreteFunctionVariant>& discrete_function_v) +{ + std::cout << "** one variable at a time (30 times)\n"; + + std::visit( + [&](auto&& p_mesh) { + std::visit( + [&](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + PolynomialReconstruction reconstruction; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + Timer t; + for (size_t i = 0; i < 30; ++i) { + auto res = reconstruction.build(*p_mesh, discrete_function); + } + t.pause(); + std::cout << "t = " << t << '\n'; + } + }, + discrete_function_v->discreteFunction()); + }, + mesh_v->variant()); + + std::cout << "finished!\n"; +} + +void +test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) +{ + std::cout << "** variable list at once (30 times)\n"; + + { + PolynomialReconstruction reconstruction; + Timer t; + for (size_t i = 0; i < 30; ++i) { + auto res = reconstruction.build(discrete_function_variant_list); + } + t.pause(); + std::cout << "t = " << t << '\n'; + } + + std::cout << "finished!\n"; + + std::cout << "** variable list at once (30 times)\n"; + + { + PolynomialReconstruction reconstruction; + Timer t; + for (size_t i = 0; i < 30; ++i) { + auto res = reconstruction.build2(discrete_function_variant_list); + } + t.pause(); + std::cout << "t = " << t << '\n'; + } + + std::cout << "finished!\n"; +} diff --git a/src/scheme/test_reconstruction.hpp b/src/scheme/test_reconstruction.hpp new file mode 100644 index 000000000..f74bf0781 --- /dev/null +++ b/src/scheme/test_reconstruction.hpp @@ -0,0 +1,16 @@ +#ifndef TEST_RECONSTRUCTION_HPP +#define TEST_RECONSTRUCTION_HPP + +#warning REMOVE AFTER TESTS FINISHED +#include <mesh/MeshVariant.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> + +#include <vector> + +void test_reconstruction(const std::shared_ptr<const MeshVariant>& mesh_v, + const std::shared_ptr<const DiscreteFunctionVariant>& discrete_function_variant_list); + +void test_reconstruction( + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list); + +#endif // TEST_RECONSTRUCTION_HPP diff --git a/src/utils/PugsTraits.hpp b/src/utils/PugsTraits.hpp index ab2244b69..cb8f154e3 100644 --- a/src/utils/PugsTraits.hpp +++ b/src/utils/PugsTraits.hpp @@ -26,6 +26,12 @@ class DiscreteFunctionP0; template <typename DataType> class DiscreteFunctionP0Vector; +template <size_t Dimension, typename DataType, typename BasisView> +class DiscreteFunctionDPk; + +template <size_t Dimension, typename DataType, typename BasisView> +class DiscreteFunctionDPkVector; + // Traits is_trivially_castable template <typename T> @@ -163,6 +169,29 @@ constexpr inline bool is_discrete_function_P0_vector_v<DiscreteFunctionP0Vector< template <typename T> constexpr inline bool is_discrete_function_v = is_discrete_function_P0_v<T> or is_discrete_function_P0_vector_v<T>; +// Trais is DiscreteFunctionDPk + +template <typename T> +constexpr inline bool is_discrete_function_dpk_scalar_v = false; + +template <size_t Dimension, typename DataType, typename BasisView> +constexpr inline bool is_discrete_function_dpk_scalar_v<DiscreteFunctionDPk<Dimension, DataType, BasisView>> = true; + +// Trais is DiscreteFunctionDPkVector + +template <typename T> +constexpr inline bool is_discrete_function_dpk_vector_v = false; + +template <size_t Dimension, typename DataType, typename BasisView> +constexpr inline bool is_discrete_function_dpk_vector_v<DiscreteFunctionDPkVector<Dimension, DataType, BasisView>> = + true; + +// Trais is DiscreteFunction + +template <typename T> +constexpr inline bool is_discrete_function_dpk_v = + is_discrete_function_dpk_scalar_v<T> or is_discrete_function_dpk_vector_v<T>; + // helper to check if a type is part of a variant template <typename T, typename V> diff --git a/tests/mpi_test_main.cpp b/tests/mpi_test_main.cpp index ef3fdfcc0..657cccc8c 100644 --- a/tests/mpi_test_main.cpp +++ b/tests/mpi_test_main.cpp @@ -7,6 +7,7 @@ #include <mesh/DualConnectivityManager.hpp> #include <mesh/DualMeshManager.hpp> #include <mesh/MeshDataManager.hpp> +#include <mesh/StencilManager.hpp> #include <mesh/SynchronizerManager.hpp> #include <utils/GlobalVariableManager.hpp> #include <utils/Messenger.hpp> @@ -99,6 +100,8 @@ main(int argc, char* argv[]) MeshDataManager::create(); DualConnectivityManager::create(); DualMeshManager::create(); + StencilManager::create(); + GlobalVariableManager::create(); MeshDataBaseForTests::create(); @@ -111,6 +114,7 @@ main(int argc, char* argv[]) MeshDataBaseForTests::destroy(); + StencilManager::destroy(); GlobalVariableManager::destroy(); DualMeshManager::destroy(); DualConnectivityManager::destroy(); diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder.cpp index af6031ee4..0cb54f262 100644 --- a/tests/test_StencilBuilder.cpp +++ b/tests/test_StencilBuilder.cpp @@ -8,7 +8,7 @@ #include <mesh/ItemValueUtils.hpp> #include <mesh/Mesh.hpp> #include <mesh/MeshVariant.hpp> -#include <mesh/StencilBuilder.hpp> +#include <mesh/StencilManager.hpp> #include <utils/Messenger.hpp> // clazy:excludeall=non-pod-global-static @@ -59,7 +59,7 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<1>& connectivity = mesh.connectivity(); - Stencil stencil = StencilBuilder{}.build(connectivity); + Stencil stencil = StencilManager::instance().getStencil(connectivity); REQUIRE(is_valid(connectivity, stencil)); } @@ -69,7 +69,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); const Connectivity<1>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilBuilder{}.build(connectivity))); + Stencil stencil = StencilManager::instance().getStencil(connectivity); + + REQUIRE(is_valid(connectivity, stencil)); } } @@ -80,7 +82,7 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilBuilder{}.build(connectivity))); + REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); } SECTION("hybrid") @@ -88,7 +90,7 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilBuilder{}.build(connectivity))); + REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); } } @@ -99,7 +101,7 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilBuilder{}.build(connectivity))); + REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); } SECTION("hybrid") @@ -107,7 +109,7 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilBuilder{}.build(connectivity))); + REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); } } } diff --git a/tests/test_main.cpp b/tests/test_main.cpp index d8641d318..3f81068df 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -7,6 +7,7 @@ #include <mesh/DualConnectivityManager.hpp> #include <mesh/DualMeshManager.hpp> #include <mesh/MeshDataManager.hpp> +#include <mesh/StencilManager.hpp> #include <mesh/SynchronizerManager.hpp> #include <utils/GlobalVariableManager.hpp> #include <utils/Messenger.hpp> @@ -58,6 +59,8 @@ main(int argc, char* argv[]) MeshDataManager::create(); DualConnectivityManager::create(); DualMeshManager::create(); + StencilManager::create(); + GlobalVariableManager::create(); MeshDataBaseForTests::create(); @@ -71,6 +74,7 @@ main(int argc, char* argv[]) MeshDataBaseForTests::destroy(); GlobalVariableManager::destroy(); + StencilManager::destroy(); DualMeshManager::destroy(); DualConnectivityManager::destroy(); MeshDataManager::destroy(); -- GitLab From c3a6bdf04800ececff3dffd9de4dcd609b51ec31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 14 Jun 2024 19:31:26 +0200 Subject: [PATCH 022/122] Remove legacy useless declarations --- src/scheme/CellIntegrator.hpp | 3 --- src/scheme/EdgeIntegrator.hpp | 3 --- src/scheme/FaceIntegrator.hpp | 3 --- 3 files changed, 9 deletions(-) diff --git a/src/scheme/CellIntegrator.hpp b/src/scheme/CellIntegrator.hpp index 33241f9b8..068e9cebe 100644 --- a/src/scheme/CellIntegrator.hpp +++ b/src/scheme/CellIntegrator.hpp @@ -89,9 +89,6 @@ class CellIntegrator static_assert(std::is_same_v<std::remove_const_t<typename OutputArrayT::data_type>, OutputType>, "invalid output data type"); - using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space; - Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens; - if constexpr (std::is_arithmetic_v<OutputType>) { value.fill(0); } else if constexpr (is_tiny_vector_v<OutputType> or is_tiny_matrix_v<OutputType>) { diff --git a/src/scheme/EdgeIntegrator.hpp b/src/scheme/EdgeIntegrator.hpp index 7557a7ed8..d1a8bfec1 100644 --- a/src/scheme/EdgeIntegrator.hpp +++ b/src/scheme/EdgeIntegrator.hpp @@ -82,9 +82,6 @@ class EdgeIntegrator static_assert(std::is_same_v<std::remove_const_t<typename OutputArrayT::data_type>, OutputType>, "invalid output data type"); - using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space; - Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens; - if constexpr (std::is_arithmetic_v<OutputType>) { value.fill(0); } else if constexpr (is_tiny_vector_v<OutputType> or is_tiny_matrix_v<OutputType>) { diff --git a/src/scheme/FaceIntegrator.hpp b/src/scheme/FaceIntegrator.hpp index 3fc6b25f7..4b73a50d7 100644 --- a/src/scheme/FaceIntegrator.hpp +++ b/src/scheme/FaceIntegrator.hpp @@ -84,9 +84,6 @@ class FaceIntegrator static_assert(std::is_same_v<std::remove_const_t<typename OutputArrayT::data_type>, OutputType>, "invalid output data type"); - using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space; - Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens; - if constexpr (std::is_arithmetic_v<OutputType>) { value.fill(0); } else if constexpr (is_tiny_vector_v<OutputType> or is_tiny_matrix_v<OutputType>) { -- GitLab From 179ec413e48baa20d5c91d047cee31951221c711 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Tue, 18 Jun 2024 16:06:51 +0200 Subject: [PATCH 023/122] Fix construction of empty sub view located at the end of the array This situation occurs for instance when dealing with CRS matrices whose last lines are empty... Accessing to these lines requires this change --- src/utils/Array.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/Array.hpp b/src/utils/Array.hpp index a08453ba7..94c6bcce4 100644 --- a/src/utils/Array.hpp +++ b/src/utils/Array.hpp @@ -63,9 +63,9 @@ class [[nodiscard]] Array UnsafeArrayView& operator=(UnsafeArrayView&&) = delete; UnsafeArrayView(const Array<DataType>& array, index_type begin, index_type size) - : m_values{&array[begin]}, m_size{size} + : m_values{(size == 0) ? nullptr : &array[begin]}, m_size{size} { - Assert((begin < array.size()) and (begin + size <= array.size()), "required view is not contained in the Array"); + Assert((size == 0) or (begin + size <= array.size()), "required view is not contained in the Array"); } // To try to keep these views close to the initial array one -- GitLab From a03881be56271383756d3490ed51c3628fc3ddb0 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Sun, 23 Jun 2024 20:59:46 +0200 Subject: [PATCH 024/122] Fix performance issue for grouped reconstructions --- src/scheme/PolynomialReconstruction.cpp | 471 ++++++++++-------------- src/scheme/PolynomialReconstruction.hpp | 10 +- src/scheme/test_reconstruction.cpp | 47 ++- 3 files changed, 227 insertions(+), 301 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index fc9b88d0e..74fe08b7d 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -128,8 +128,7 @@ PolynomialReconstruction::_build( const size_t degree = 1; const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity()); - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); auto cell_is_owned = mesh.connectivity().cellIsOwned(); const size_t max_stencil_size = [&]() { @@ -182,20 +181,20 @@ PolynomialReconstruction::_build( const int32_t t = tokens.acquire(); auto stencil_cell_list = stencil[cell_j_id]; + ShrinkMatrixView B(B_pool[t], stencil_cell_list.size()); - size_t column_begin = 0; for (size_t i_discrete_function_variant = 0; i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { - auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; + const auto& discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; std::visit( [&](auto&& discrete_function) { using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - - const DataType& pj = discrete_function[cell_j_id]; + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + size_t column_begin = 0; + const DataType& pj = discrete_function[cell_j_id]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { const CellId cell_i_id = stencil_cell_list[i]; const DataType& pi_pj = discrete_function[cell_i_id] - pj; @@ -224,244 +223,12 @@ PolynomialReconstruction::_build( discrete_function_variant->discreteFunction()); } - ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); - - const Rd& Xj = xj[cell_j_id]; - - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const Rd Xi_Xj = xj[cell_i_id] - Xj; - for (size_t l = 0; l < MeshType::Dimension; ++l) { - A(i, l) = Xi_Xj[l]; - } - } - - SmallMatrix<double> X = X_pool[t]; - Givens::solveCollectionInPlace(A, X, B); - - column_begin = 0; - for (size_t i_discrete_function_variant = 0; - i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { - auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; - - std::visit( - [&](auto&& discrete_function) { - using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - - const DataType& pj = discrete_function[cell_j_id]; - auto discrete_function_dpk = mutable_discrete_function_dpk_variant_list[i_discrete_function_variant] - .get<DiscreteFunctionDPk<MeshType::Dimension, DataType>>(); - auto dpk_j = discrete_function_dpk.coefficients(cell_j_id); - dpk_j[0] = pj; - - 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); - } - } - 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 { - throw NotImplementedError("discrete function type"); - } - }, - discrete_function_variant->discreteFunction()); - } - - tokens.release(t); - } - }); - - std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> discrete_function_dpk_variant_list; - - // for (auto discrete_function_dpk_variant_p : mutable_discrete_function_dpk_variant_list) { - // const_discrete_function_dpk_variant_list.push_back(discrete_function_dpk_variant_p); - // } - - return discrete_function_dpk_variant_list; -} - -std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> -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"); - } - - auto mesh_v = getCommonMesh(discrete_function_variant_list); - - return std::visit([&](auto&& p_mesh) { return this->_build(p_mesh, discrete_function_variant_list); }, - mesh_v->variant()); -} - -/**************************************/ - -template <MeshConcept MeshType> -[[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> -PolynomialReconstruction::_build2( - const std::shared_ptr<const MeshType>& p_mesh, - const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const -{ - const MeshType& mesh = *p_mesh; - - using Rd = TinyVector<MeshType::Dimension>; - - const size_t number_of_columns = [&]() { - size_t n = 0; - for (auto discrete_function_variant_p : discrete_function_variant_list) { - n += std::visit( - [](auto&& discrete_function) -> size_t { - using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - using data_type = std::decay_t<typename DiscreteFunctionT::data_type>; - if constexpr (std::is_same_v<data_type, double>) { - return 1; - } else if constexpr (is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>) { - return data_type::Dimension; - } else { - throw UnexpectedError("unexpected data type " + demangle<data_type>()); - } - } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - return discrete_function.size(); - } else { - throw UnexpectedError("unexpected discrete function type"); - } - }, - discrete_function_variant_p->discreteFunction()); - } - return n; - }(); - - const size_t degree = 1; - const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity()); - - Array<const double*> value_begining(discrete_function_variant_list.size()); - Array<size_t> value_size(discrete_function_variant_list.size()); - - for (size_t i = 0; i < discrete_function_variant_list.size(); ++i) { - DiscreteFunctionVariant discrete_function_v = *(discrete_function_variant_list[i]); - std::visit( - [=](auto&& discrete_function) { - using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - using data_type = std::decay_t<typename DiscreteFunctionT::data_type>; - if constexpr (std::is_same_v<data_type, double>) { - value_begining[i] = &(discrete_function[CellId{0}]); - value_size[i] = 1; - } else if constexpr (is_tiny_vector_v<data_type>) { - value_begining[i] = &(discrete_function[CellId{0}][0]); - value_size[i] = data_type::Dimension; - } else if constexpr (is_tiny_matrix_v<data_type>) { - value_begining[i] = &(discrete_function[CellId{0}](0, 0)); - value_size[i] = data_type::Dimension; - } else { - throw UnexpectedError("unexpected data type " + demangle<data_type>()); - } - } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - throw NotImplementedError("Discrete Function P0 Vector"); - } else { - throw UnexpectedError("unexpected discrete function type"); - } - }, - discrete_function_v.discreteFunction()); - } - - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - auto cell_is_owned = mesh.connectivity().cellIsOwned(); - - const size_t max_stencil_size = [&]() { - size_t max_size = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto stencil_cell_list = stencil[cell_id]; - if (cell_is_owned[cell_id] and stencil_cell_list.size() > max_size) { - max_size = stencil_cell_list.size(); - } - } - return max_size; - }(); - - Kokkos::Experimental::UniqueToken<Kokkos::DefaultExecutionSpace::execution_space, - Kokkos::Experimental::UniqueTokenScope::Global> - tokens; - - std::vector<MutableDiscreteFunctionDPkVariant> mutable_discrete_function_dpk_variant_list; - for (size_t i_discrete_function_variant = 0; i_discrete_function_variant < discrete_function_variant_list.size(); - ++i_discrete_function_variant) { - auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; - - std::visit( - [&](auto&& discrete_function) { - using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - using DataType = std::remove_const_t<std::decay_t<typename DiscreteFunctionT::data_type>>; - mutable_discrete_function_dpk_variant_list.push_back( - DiscreteFunctionDPk<MeshType::Dimension, DataType>(p_mesh, degree)); - } else { - throw NotImplementedError("discrete function type"); - } - }, - discrete_function_variant->discreteFunction()); - } - - SmallArray<SmallMatrix<double>> A_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallMatrix<double>> B_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallMatrix<double>> X_pool(Kokkos::DefaultExecutionSpace::concurrency()); - - for (size_t i = 0; i < A_pool.size(); ++i) { - A_pool[i] = SmallMatrix<double>(max_stencil_size, MeshType::Dimension); - B_pool[i] = SmallMatrix<double>(max_stencil_size, number_of_columns); - X_pool[i] = SmallMatrix<double>(MeshType::Dimension, number_of_columns); - } - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { - if (cell_is_owned[cell_j_id]) { - const int32_t t = tokens.acquire(); - - auto stencil_cell_list = stencil[cell_j_id]; - ShrinkMatrixView B(B_pool[t], stencil_cell_list.size()); - - size_t i_column = 0; - - for (size_t i_value = 0; i_value < value_begining.size(); ++i_value) { - const size_t i_value_size = value_size[i_value]; - - const size_t cell_j_value_index = cell_j_id * i_value_size; - - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const size_t cell_i_value_index = cell_i_id * i_value_size; - size_t j_index = cell_j_value_index; - size_t i_index = cell_i_value_index; - - for (size_t shift = 0; shift < i_value_size; ++shift, ++i_index, ++j_index) { - B(i, i_column++) = value_begining[i_value][i_index] - value_begining[i_value][j_index]; - } - } - } + // for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + // const CellId cell_i_id = stencil_cell_list[i]; + // for (size_t j = 0; j < number_of_columns; ++j) { + // B(i, j) = values[cell_i_id][j] - values[cell_j_id][j]; + // } + // } ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); @@ -475,76 +242,210 @@ PolynomialReconstruction::_build2( } } - // SmallMatrix<double> X = X_pool[t]; - - TinyMatrix<MeshType::Dimension, 1> X; + const SmallMatrix<double>& X = X_pool[t]; Givens::solveCollectionInPlace(A, X, B); size_t column_begin = 0; - for (size_t i_discrete_function_variant = 0; - i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { - auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; + for (size_t i_dpk_variant = 0; i_dpk_variant < mutable_discrete_function_dpk_variant_list.size(); + ++i_dpk_variant) { + const auto& dpk_variant = mutable_discrete_function_dpk_variant_list[i_dpk_variant]; + + const auto& discrete_function_variant = discrete_function_variant_list[i_dpk_variant]; std::visit( - [&](auto&& discrete_function) { - using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + [&](auto&& dpk_function, auto&& p0_function) { + using DPkFunctionT = std::decay_t<decltype(dpk_function)>; + using P0FunctionT = std::decay_t<decltype(p0_function)>; + using DataType = std::remove_const_t<std::decay_t<typename DPkFunctionT::data_type>>; + using P0DataType = std::remove_const_t<std::decay_t<typename P0FunctionT::data_type>>; - const DataType& pj = discrete_function[cell_j_id]; - auto discrete_function_dpk = mutable_discrete_function_dpk_variant_list[i_discrete_function_variant] - .get<DiscreteFunctionDPk<MeshType::Dimension, DataType>>(); - auto dpk_j = discrete_function_dpk.coefficients(cell_j_id); - dpk_j[0] = pj; + 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 (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 { + throw NotImplementedError("unexpected data type"); } - column_begin += DataType::Dimension; + } else { + throw NotImplementedError("non scalar P0 functions"); } - } else { - throw NotImplementedError("discrete function type"); + throw UnexpectedError("incompatible data types"); } }, - discrete_function_variant->discreteFunction()); + dpk_variant.mutableDiscreteFunctionDPk(), discrete_function_variant->discreteFunction()); } tokens.release(t); } }); + return {}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { + // if (cell_is_owned[cell_j_id]) { + // const int32_t t = tokens.acquire(); + + // auto stencil_cell_list = stencil[cell_j_id]; + // ShrinkMatrixView B(B_pool[t], stencil_cell_list.size()); + + // size_t column_begin = 0; + // for (size_t i_discrete_function_variant = 0; + // i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { + // // const auto& discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; + + // // std::visit( + // // [&](auto&& discrete_function) { + // // using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + // // if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + // // using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + + // // const DataType& pj = discrete_function[cell_j_id]; + // // for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + // // const CellId cell_i_id = stencil_cell_list[i]; + // // const DataType& pi_pj = discrete_function[cell_i_id] - pj; + // // if constexpr (std::is_arithmetic_v<DataType>) { + // // B(i, column_begin) = pi_pj; + // // } else if constexpr (is_tiny_vector_v<DataType>) { + // // for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { + // // B(i, kB) = pi_pj[k]; + // // } + // // } else if constexpr (is_tiny_matrix_v<DataType>) { + // // for (size_t k = 0; k < DataType::NumberOfRows; ++k) { + // // for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { + // // B(i, column_begin + k * DataType::NumberOfColumns + l) = pi_pj(k, l); + // // } + // // } + // // } + // // } + + // // if constexpr (std::is_arithmetic_v<DataType>) { + // // ++column_begin; + // // } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) { + // // column_begin += DataType::Dimension; + // // } + // // } + // // }, + // // discrete_function_variant->discreteFunction()); + + // using DataType = double; + + // const DataType& pj = my_discrete_function[cell_j_id]; + // for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + // const CellId cell_i_id = stencil_cell_list[i]; + // const DataType& pi_pj = my_discrete_function[cell_i_id] - pj; + // B(i, column_begin) = pi_pj; + // } + + // ++column_begin; + // } + // ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); + + // const Rd& Xj = xj[cell_j_id]; + + // for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + // const CellId cell_i_id = stencil_cell_list[i]; + // const Rd Xi_Xj = xj[cell_i_id] - Xj; + // for (size_t l = 0; l < MeshType::Dimension; ++l) { + // A(i, l) = Xi_Xj[l]; + // } + // } + + // const SmallMatrix<double>& X = X_pool[t]; + // Givens::solveCollectionInPlace(A, X, B); + + // column_begin = 0; + // for (size_t i_discrete_function_variant = 0; + // i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { + // auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; + + // std::visit( + // [&](auto&& discrete_function) { + // using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + // if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + // using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + + // const DataType& pj = discrete_function[cell_j_id]; + // auto discrete_function_dpk = mutable_discrete_function_dpk_variant_list[i_discrete_function_variant] + // .get<DiscreteFunctionDPk<MeshType::Dimension, DataType>>(); + // auto dpk_j = discrete_function_dpk.coefficients(cell_j_id); + // dpk_j[0] = pj; + + // 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); + // } + // } + // 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 { + // throw NotImplementedError("discrete function type"); + // } + // }, + // discrete_function_variant->discreteFunction()); + // } + + // tokens.release(t); + // } + // }); + std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> discrete_function_dpk_variant_list; // for (auto discrete_function_dpk_variant_p : mutable_discrete_function_dpk_variant_list) { - // const_discrete_function_dpk_variant_list.push_back(discrete_function_dpk_variant_p); + // discrete_function_dpk_variant_list.push_back( + // std::make_shared<const DiscreteFunctionDPkVariant>(discrete_function_dpk_variant_p)); // } return discrete_function_dpk_variant_list; } std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> -PolynomialReconstruction::build2( +PolynomialReconstruction::build( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const { if (not hasSameMesh(discrete_function_variant_list)) { diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index 0612e605b..b55e84fee 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -120,11 +120,6 @@ class PolynomialReconstruction const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; - template <MeshConcept MeshType> - [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> _build2( - const std::shared_ptr<const MeshType>& p_mesh, - const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; - public: template <MeshConcept MeshType, typename DataType> [[nodiscard]] PUGS_INLINE DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> @@ -193,7 +188,7 @@ class PolynomialReconstruction } } - TinyVector<MeshType::Dimension> x; + SmallVector<double> x(MeshType::Dimension); Givens::solveInPlace(A, x, b); auto dpk_j = dpk.coefficients(cell_j_id); @@ -328,9 +323,6 @@ class PolynomialReconstruction [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> build( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; - [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> build2( - const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; - PolynomialReconstruction() {} }; diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index 9ec19396e..08527f96d 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -31,30 +31,63 @@ test_reconstruction(const std::shared_ptr<const MeshVariant>& mesh_v, std::cout << "finished!\n"; } +void +test_reconstruction_one(const std::shared_ptr<const MeshVariant>& mesh_v, + const std::shared_ptr<const DiscreteFunctionVariant>& discrete_function_v) +{ + std::visit( + [&](auto&& p_mesh) { + std::visit( + [&](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + PolynomialReconstruction reconstruction; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + for (size_t i = 0; i < 30; ++i) { + auto res = reconstruction.build(*p_mesh, discrete_function); + } + } + }, + discrete_function_v->discreteFunction()); + }, + mesh_v->variant()); +} + void test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) { - std::cout << "** variable list at once (30 times)\n"; + std::cout << "** variable list one by one (30 times)\n"; { - PolynomialReconstruction reconstruction; Timer t; - for (size_t i = 0; i < 30; ++i) { - auto res = reconstruction.build(discrete_function_variant_list); + for (auto discrete_function_v : discrete_function_variant_list) { + std::visit( + [&](auto&& discrete_function) { + auto mesh_v = discrete_function.meshVariant(); + std::visit( + [&](auto&& p_mesh) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + PolynomialReconstruction reconstruction; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + for (size_t i = 0; i < 30; ++i) { + auto res = reconstruction.build(*p_mesh, discrete_function); + } + } + }, + mesh_v->variant()); + }, + discrete_function_v->discreteFunction()); } t.pause(); std::cout << "t = " << t << '\n'; } - std::cout << "finished!\n"; - std::cout << "** variable list at once (30 times)\n"; { PolynomialReconstruction reconstruction; Timer t; for (size_t i = 0; i < 30; ++i) { - auto res = reconstruction.build2(discrete_function_variant_list); + auto res = reconstruction.build(discrete_function_variant_list); } t.pause(); std::cout << "t = " << t << '\n'; -- GitLab From b4c6ee7071c07907b48f5ed9e7fe98d6c4658002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Wed, 26 Jun 2024 19:23:51 +0200 Subject: [PATCH 025/122] Begin code cleaning and interface improvements --- src/scheme/DiscreteFunctionDPk.hpp | 16 +-- src/scheme/PolynomialReconstruction.cpp | 142 ++---------------------- tests/test_PolynomialReconstruction.cpp | 16 ++- 3 files changed, 31 insertions(+), 143 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index 64be52ef2..2690646b7 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -135,7 +135,7 @@ class PolynomialCenteredCanonicalBasisView if constexpr (Dimension == 1) { const double x_xj = (x - m_xj)[0]; - DataType result = m_coefficients[m_degree]; + std::remove_const_t<DataType> result = m_coefficients[m_degree]; for (ssize_t i_coeffiencient = m_degree - 1; i_coeffiencient >= 0; --i_coeffiencient) { result = x_xj * result + m_coefficients[i_coeffiencient]; } @@ -148,9 +148,9 @@ class PolynomialCenteredCanonicalBasisView size_t i = m_coefficients.size() - 1; - DataType result = m_coefficients[i--]; + std::remove_const_t<DataType> result = m_coefficients[i--]; for (ssize_t i_y = m_degree - 1; i_y >= 0; --i_y) { - DataType x_result = m_coefficients[i--]; + std::remove_const_t<DataType> x_result = m_coefficients[i--]; for (ssize_t i_x = m_degree - i_y - 1; i_x >= 0; --i_x) { x_result = x_xj * x_result + m_coefficients[i--]; } @@ -166,11 +166,11 @@ class PolynomialCenteredCanonicalBasisView size_t i = m_coefficients.size() - 1; - DataType result = m_coefficients[i--]; + std::remove_const_t<DataType> result = m_coefficients[i--]; for (ssize_t i_z = m_degree - 1; i_z >= 0; --i_z) { - DataType y_result = m_coefficients[i--]; + std::remove_const_t<DataType> y_result = m_coefficients[i--]; for (ssize_t i_y = m_degree - i_z - 1; i_y >= 0; --i_y) { - DataType x_result = m_coefficients[i--]; + std::remove_const_t<DataType> x_result = m_coefficients[i--]; for (ssize_t i_x = m_degree - i_z - i_y - 1; i_x >= 0; --i_x) { x_result = x_xj * x_result + m_coefficients[i--]; } @@ -297,7 +297,7 @@ class DiscreteFunctionDPk m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} {} - DiscreteFunctionDPk(const std::shared_ptr<const MeshVariant>& mesh_v, const CellValue<DataType>& cell_array) + DiscreteFunctionDPk(const std::shared_ptr<const MeshVariant>& mesh_v, const CellArray<DataType>& cell_array) : m_mesh_v{mesh_v}, m_degree{BasisView::degreeFromDimension(cell_array.sizeOfArrays())}, m_by_cell_coefficients{cell_array}, @@ -312,7 +312,7 @@ class DiscreteFunctionDPk {} template <MeshConcept MeshType> - DiscreteFunctionDPk(const std::shared_ptr<const MeshType>& p_mesh, const CellValue<DataType>& cell_array) + DiscreteFunctionDPk(const std::shared_ptr<const MeshType>& p_mesh, const CellArray<DataType>& cell_array) : DiscreteFunctionDPk{p_mesh->meshVariant(), cell_array} { Assert(m_mesh_v->connectivity().id() == cell_array.connectivity_ptr()->id()); diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 74fe08b7d..ed6a9cfc6 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -305,141 +305,17 @@ PolynomialReconstruction::_build( } }); - return {}; - - // parallel_for( - // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { - // if (cell_is_owned[cell_j_id]) { - // const int32_t t = tokens.acquire(); - - // auto stencil_cell_list = stencil[cell_j_id]; - // ShrinkMatrixView B(B_pool[t], stencil_cell_list.size()); - - // size_t column_begin = 0; - // for (size_t i_discrete_function_variant = 0; - // i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { - // // const auto& discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; - - // // std::visit( - // // [&](auto&& discrete_function) { - // // using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - // // if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - // // using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - - // // const DataType& pj = discrete_function[cell_j_id]; - // // for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - // // const CellId cell_i_id = stencil_cell_list[i]; - // // const DataType& pi_pj = discrete_function[cell_i_id] - pj; - // // if constexpr (std::is_arithmetic_v<DataType>) { - // // B(i, column_begin) = pi_pj; - // // } else if constexpr (is_tiny_vector_v<DataType>) { - // // for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { - // // B(i, kB) = pi_pj[k]; - // // } - // // } else if constexpr (is_tiny_matrix_v<DataType>) { - // // for (size_t k = 0; k < DataType::NumberOfRows; ++k) { - // // for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - // // B(i, column_begin + k * DataType::NumberOfColumns + l) = pi_pj(k, l); - // // } - // // } - // // } - // // } - - // // if constexpr (std::is_arithmetic_v<DataType>) { - // // ++column_begin; - // // } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) { - // // column_begin += DataType::Dimension; - // // } - // // } - // // }, - // // discrete_function_variant->discreteFunction()); - - // using DataType = double; - - // const DataType& pj = my_discrete_function[cell_j_id]; - // for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - // const CellId cell_i_id = stencil_cell_list[i]; - // const DataType& pi_pj = my_discrete_function[cell_i_id] - pj; - // B(i, column_begin) = pi_pj; - // } - - // ++column_begin; - // } - // ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); - - // const Rd& Xj = xj[cell_j_id]; - - // for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - // const CellId cell_i_id = stencil_cell_list[i]; - // const Rd Xi_Xj = xj[cell_i_id] - Xj; - // for (size_t l = 0; l < MeshType::Dimension; ++l) { - // A(i, l) = Xi_Xj[l]; - // } - // } - - // const SmallMatrix<double>& X = X_pool[t]; - // Givens::solveCollectionInPlace(A, X, B); - - // column_begin = 0; - // for (size_t i_discrete_function_variant = 0; - // i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { - // auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; - - // std::visit( - // [&](auto&& discrete_function) { - // using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - // if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - // using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - - // const DataType& pj = discrete_function[cell_j_id]; - // auto discrete_function_dpk = mutable_discrete_function_dpk_variant_list[i_discrete_function_variant] - // .get<DiscreteFunctionDPk<MeshType::Dimension, DataType>>(); - // auto dpk_j = discrete_function_dpk.coefficients(cell_j_id); - // dpk_j[0] = pj; - - // 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); - // } - // } - // 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 { - // throw NotImplementedError("discrete function type"); - // } - // }, - // discrete_function_variant->discreteFunction()); - // } - - // tokens.release(t); - // } - // }); - std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> discrete_function_dpk_variant_list; - // for (auto discrete_function_dpk_variant_p : mutable_discrete_function_dpk_variant_list) { - // discrete_function_dpk_variant_list.push_back( - // std::make_shared<const DiscreteFunctionDPkVariant>(discrete_function_dpk_variant_p)); - // } + for (auto discrete_function_dpk_variant_p : mutable_discrete_function_dpk_variant_list) { + std::visit( + [&](auto&& mutable_function_dpk) { + synchronize(mutable_function_dpk.cellArrays()); + discrete_function_dpk_variant_list.push_back( + std::make_shared<DiscreteFunctionDPkVariant>(mutable_function_dpk)); + }, + discrete_function_dpk_variant_p.mutableDiscreteFunctionDPk()); + } return discrete_function_dpk_variant_list; } diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 72036d09f..d90d0e84a 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -12,10 +12,21 @@ #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... DataType> +std::vector<std::shared_ptr<const DiscreteFunctionVariant>> +build_list(DiscreteFunctionP0<DataType>... input) +{ + std::vector<std::shared_ptr<const DiscreteFunctionVariant>> variant_vector; + variant_vector.push_back(std::make_shared<DiscreteFunctionVariant>(input...)); + + return variant_vector; +} + // clazy:excludeall=non-pod-global-static TEST_CASE("PolynomialReconstruction", "[scheme]") @@ -40,8 +51,9 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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); + auto reconstructions = PolynomialReconstruction{}.build(build_list(fh)); + + auto dpk = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); { double max_mean_error = 0; -- GitLab From d1d616e148fd2ea049bab82aadda6727a1b5db9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 27 Jun 2024 19:27:23 +0200 Subject: [PATCH 026/122] Continue interface improvement for grouped reconstructions --- tests/test_PolynomialReconstruction.cpp | 26 +++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index d90d0e84a..5242b6b04 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -17,13 +17,28 @@ #include <MeshDataBaseForTests.hpp> -template <typename... DataType> +template <typename... DiscreteFunctionT> std::vector<std::shared_ptr<const DiscreteFunctionVariant>> -build_list(DiscreteFunctionP0<DataType>... input) +build_list(DiscreteFunctionT... input) { std::vector<std::shared_ptr<const DiscreteFunctionVariant>> variant_vector; - variant_vector.push_back(std::make_shared<DiscreteFunctionVariant>(input...)); + 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; } @@ -51,7 +66,10 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(build_list(fh)); + 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>>(); -- GitLab From 6c8bf3a2ee2e072f029d8e4339a42cc39210b2e2 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Mon, 1 Jul 2024 08:09:43 +0200 Subject: [PATCH 027/122] Improve interface for polynomial reconstruction Now one can pass list of made of discrete functions, discrete function variants or even shared_ptr of these. --- src/scheme/PolynomialReconstruction.hpp | 25 ++++++ tests/test_PolynomialReconstruction.cpp | 113 ++++++++++-------------- 2 files changed, 70 insertions(+), 68 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index b55e84fee..4c88d7476 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -323,6 +323,31 @@ class PolynomialReconstruction [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> build( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + template <typename... DiscreteFunctionT> + [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> + build(DiscreteFunctionT... input) const + { + 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(std::forward<DiscreteFunctionT>(input)), ...); + return this->build(variant_vector); + } + PolynomialReconstruction() {} }; diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 5242b6b04..4099960e3 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -17,31 +17,6 @@ #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]") @@ -69,7 +44,10 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + std::shared_ptr<DiscreteFunctionP0<const double>> p_fh = + std::make_shared<DiscreteFunctionP0<const double>>(fh); + + auto reconstructions = PolynomialReconstruction{}.build(p_fh, p_fh_v, fh_v, fh); auto dpk = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); @@ -144,7 +122,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") SECTION("R^mn data") { - using R32 = TinyMatrix<3, 2>; + using R33 = TinyMatrix<3, 3>; for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { SECTION(named_mesh.name()) @@ -152,14 +130,16 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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 affine = [](const R1& x) -> R33 { + return R33{ + +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], + -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], + }; }; auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - DiscreteFunctionP0<R32> fh{p_mesh}; + DiscreteFunctionP0<R33> fh{p_mesh}; parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); }); @@ -178,12 +158,15 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { double max_slope_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R32 reconstructed_slope = + const R33 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})); + R33 slops = R33{+1.7, +2.1, -0.6, // + -2.3, +3.1, -3.6, // + +3.1, +2.9, +2.3}; + + max_slope_error = std::max(max_slope_error, // + frobeniusNorm(reconstructed_slope - slops)); } REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); } @@ -308,7 +291,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") SECTION("R^mn data") { - using R32 = TinyMatrix<3, 2>; + using R22 = TinyMatrix<2, 2>; for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { SECTION(named_mesh.name()) @@ -316,14 +299,13 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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 affine = [](const R2& x) -> R22 { + return R22{+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(); - DiscreteFunctionP0<R32> fh{p_mesh}; + DiscreteFunctionP0<R22> fh{p_mesh}; parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); }); @@ -342,12 +324,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { double max_x_slope_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R32 reconstructed_slope = + const R22 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})); + max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R22{+1.7, +2.1, // + -0.6, -2.3})); } REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12)); } @@ -355,12 +336,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { double max_y_slope_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R32 reconstructed_slope = + const R22 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})); + max_y_slope_error = std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R22{+1.2, -2.2, // + -2.1, +1.3})); } REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12)); } @@ -508,7 +488,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") SECTION("R^mn data") { - using R32 = TinyMatrix<3, 2>; + using R22 = TinyMatrix<2, 2>; for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { SECTION(named_mesh.name()) @@ -516,14 +496,14 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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 affine = [](const R3& x) -> R22 { + return R22{+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(); - DiscreteFunctionP0<R32> fh{p_mesh}; + DiscreteFunctionP0<R22> fh{p_mesh}; parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); }); @@ -542,12 +522,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { double max_x_slope_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R32 reconstructed_slope = + const R22 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})); + max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R22{+1.7, 2.1, // + -2.3, +3.1})); } REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12)); } @@ -555,12 +534,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { double max_y_slope_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R32 reconstructed_slope = + const R22 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})); + max_y_slope_error = std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R22{+1.2, -2.2, // + 1.3, +0.8})); } REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12)); } @@ -568,12 +546,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { double max_z_slope_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R32 reconstructed_slope = + const R22 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})); + max_z_slope_error = std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R22{-1.3, -2.4, // + +1.4, -1.8})); } REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-12)); } -- GitLab From 6c2422709a91d5bbefc238ac583dbc80a502d0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 1 Jul 2024 17:32:39 +0200 Subject: [PATCH 028/122] Fix RHS filling --- src/scheme/PolynomialReconstruction.cpp | 15 ++++----------- tests/test_PolynomialReconstruction.cpp | 15 ++++++++++----- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index ed6a9cfc6..af1723a47 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -184,6 +184,7 @@ PolynomialReconstruction::_build( ShrinkMatrixView B(B_pool[t], stencil_cell_list.size()); + size_t column_begin = 0; for (size_t i_discrete_function_variant = 0; i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { const auto& discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; @@ -192,9 +193,8 @@ PolynomialReconstruction::_build( [&](auto&& discrete_function) { using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - size_t column_begin = 0; - const DataType& pj = discrete_function[cell_j_id]; + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + const DataType& pj = discrete_function[cell_j_id]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { const CellId cell_i_id = stencil_cell_list[i]; const DataType& pi_pj = discrete_function[cell_i_id] - pj; @@ -223,13 +223,6 @@ PolynomialReconstruction::_build( discrete_function_variant->discreteFunction()); } - // for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - // const CellId cell_i_id = stencil_cell_list[i]; - // for (size_t j = 0; j < number_of_columns; ++j) { - // B(i, j) = values[cell_i_id][j] - values[cell_j_id][j]; - // } - // } - ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); const Rd& Xj = xj[cell_j_id]; @@ -245,7 +238,7 @@ PolynomialReconstruction::_build( const SmallMatrix<double>& X = X_pool[t]; Givens::solveCollectionInPlace(A, X, B); - size_t column_begin = 0; + column_begin = 0; for (size_t i_dpk_variant = 0; i_dpk_variant < mutable_discrete_function_dpk_variant_list.size(); ++i_dpk_variant) { const auto& dpk_variant = mutable_discrete_function_dpk_variant_list[i_dpk_variant]; diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 4099960e3..3c3d3e0dc 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -41,15 +41,20 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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); + DiscreteFunctionP0<double> gh{p_mesh}; - std::shared_ptr<DiscreteFunctionP0<const double>> p_fh = + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { gh[cell_id] = 0; }); + + DiscreteFunctionVariant gh_v(gh); + std::shared_ptr<const DiscreteFunctionVariant> p_gh_v = std::make_shared<DiscreteFunctionVariant>(fh); + + std::shared_ptr<DiscreteFunctionP0<const double>> g_fh = std::make_shared<DiscreteFunctionP0<const double>>(fh); - auto reconstructions = PolynomialReconstruction{}.build(p_fh, p_fh_v, fh_v, fh); + auto reconstructions = PolynomialReconstruction{}.build(g_fh, p_gh_v, gh_v, fh); - auto dpk = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + auto dpk = reconstructions[3]->get<DiscreteFunctionDPk<1, const double>>(); { double max_mean_error = 0; -- GitLab From 471485dd334a7353d014bafbd6c8cb2b49c88356 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Tue, 2 Jul 2024 07:46:44 +0200 Subject: [PATCH 029/122] Compute polynomial degree from dimension for polynomials in 3D --- src/scheme/DiscreteFunctionDPk.hpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index 2690646b7..db8ebdc11 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -117,15 +117,18 @@ class PolynomialCenteredCanonicalBasisView Assert(polynomial_basis_dimension > 0); if constexpr (Dimension == 1) { return polynomial_basis_dimension - 1; - } else if constexpr (Dimension == 2) { - const size_t degree = std::round((std::sqrt(8 * polynomial_basis_dimension + 1) - 1) / 2) - 1; - - if (dimensionFromDegree(degree) != polynomial_basis_dimension) { - throw NormalError("incorrect polynomial basis dimension"); - } - return degree; } else { - throw NotImplementedError("Dimension > 1"); + // No need fir an explicit formula + // - degree is small and integer + // - do not need the use of sqrt + for (size_t degree = 0; degree < polynomial_basis_dimension; ++degree) { + size_t dimension_from_degree = dimensionFromDegree(degree); + if (dimension_from_degree == polynomial_basis_dimension) { + return degree; + } else if (dimension_from_degree > polynomial_basis_dimension) { + throw NormalError("incorrect polynomial basis dimension"); + } + } } } -- GitLab From 0db0579a0692eac1f48fc5ed94571c38ee90a4ec Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Tue, 2 Jul 2024 07:47:38 +0200 Subject: [PATCH 030/122] Use new polynomial reconstruction interface in tests --- tests/test_PolynomialReconstruction.cpp | 369 ++++++++++++++++-------- 1 file changed, 241 insertions(+), 128 deletions(-) diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 3c3d3e0dc..123072266 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -33,33 +33,22 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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(); + auto R_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]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - DiscreteFunctionP0<double> gh{p_mesh}; + auto reconstructions = PolynomialReconstruction{}.build(fh); - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { gh[cell_id] = 0; }); - - DiscreteFunctionVariant gh_v(gh); - std::shared_ptr<const DiscreteFunctionVariant> p_gh_v = std::make_shared<DiscreteFunctionVariant>(fh); - - std::shared_ptr<DiscreteFunctionP0<const double>> g_fh = - std::make_shared<DiscreteFunctionP0<const double>>(fh); - - auto reconstructions = PolynomialReconstruction{}.build(g_fh, p_gh_v, gh_v, fh); - - auto dpk = reconstructions[3]->get<DiscreteFunctionDPk<1, const double>>(); + auto dpk_fh = 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]))); + max_mean_error = std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); } REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } @@ -68,7 +57,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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; + (dpk_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2; max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); } @@ -78,7 +67,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } } - SECTION("R^d data") + SECTION("R^3 data") { using R3 = TinyVector<3>; @@ -88,25 +77,26 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto affine = [](const R1& x) -> R3 { + auto R3_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}; + DiscreteFunctionP0<R3> uh{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{}.build(uh); - PolynomialReconstruction reconstruction; - auto dpk = reconstruction.build(mesh, fh); + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); { 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]))); + max_mean_error = std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); } REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } @@ -115,7 +105,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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})); + (1 / 0.2) * (dpk_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[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})); } @@ -125,9 +115,66 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } } - SECTION("R^mn data") + SECTION("R^3x3 data") + { + using R3x3 = TinyMatrix<3, 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 R3x3_affine = [](const R1& x) -> R3x3 { + return R3x3{ + +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], + -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], + }; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<R3x3> Ah{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); + + { + 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_Ah[cell_id](xj[cell_id]) - R3x3_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 R3x3 reconstructed_slope = + (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); + + R3x3 slops = R3x3{+1.7, +2.1, -0.6, // + -2.3, +3.1, -3.6, // + +3.1, +2.9, +2.3}; + + max_slope_error = std::max(max_slope_error, // + frobeniusNorm(reconstructed_slope - slops)); + } + REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("list of various types") { - using R33 = TinyMatrix<3, 3>; + using R3x3 = TinyMatrix<3>; + using R3 = TinyVector<3>; for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { SECTION(named_mesh.name()) @@ -135,27 +182,88 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto affine = [](const R1& x) -> R33 { - return R33{ + auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; + + auto R3_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 R3x3_affine = [](const R1& x) -> R3x3 { + return R3x3{ +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], -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], }; }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - DiscreteFunctionP0<R33> fh{p_mesh}; + DiscreteFunctionP0<double> fh{p_mesh}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + DiscreteFunctionP0<R3> uh{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + DiscreteFunctionP0<R3x3> Ah{p_mesh}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{}.build(std::make_shared<DiscreteFunctionVariant>(fh), uh, + std::make_shared<DiscreteFunctionP0<R3x3>>(Ah)); + + auto dpk_fh = 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_fh[cell_id](xj[cell_id]) - R_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_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[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)); + } + + auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); - 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_uh[cell_id](xj[cell_id]) - R3_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_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[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)); + } + + auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); { 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]))); + max_mean_error = + std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); } REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); } @@ -163,12 +271,12 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { double max_slope_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R33 reconstructed_slope = - (1 / 0.2) * (dpk[cell_id](R1{0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R1{0.1})); + const R3x3 reconstructed_slope = + (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); - R33 slops = R33{+1.7, +2.1, -0.6, // - -2.3, +3.1, -3.6, // - +3.1, +2.9, +2.3}; + R3x3 slops = R3x3{+1.7, +2.1, -0.6, // + -2.3, +3.1, -3.6, // + +3.1, +2.9, +2.3}; max_slope_error = std::max(max_slope_error, // frobeniusNorm(reconstructed_slope - slops)); @@ -192,21 +300,22 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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(); + auto R_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]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - PolynomialReconstruction reconstruction; - auto dpk = reconstruction.build(mesh, fh); + auto reconstructions = PolynomialReconstruction{}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, 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]))); + max_mean_error = std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); } REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } @@ -215,7 +324,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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; + (dpk_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[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)); } @@ -226,7 +335,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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; + (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[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))); } @@ -236,7 +345,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } } - SECTION("R^d data") + SECTION("R^3 data") { using R3 = TinyVector<3>; @@ -246,25 +355,26 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - auto affine = [](const R2& x) -> R3 { + auto R3_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}; + DiscreteFunctionP0<R3> uh{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{}.build(uh); - PolynomialReconstruction reconstruction; - auto dpk = reconstruction.build(mesh, fh); + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); { 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]))); + max_mean_error = std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); } REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } @@ -273,7 +383,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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})); + (1 / 0.2) * (dpk_uh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_uh[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})); } @@ -284,7 +394,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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})); + (1 / 0.2) * (dpk_uh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_uh[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})); } @@ -294,9 +404,9 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } } - SECTION("R^mn data") + SECTION("R^2x2 data") { - using R22 = TinyMatrix<2, 2>; + using R2x2 = TinyMatrix<2, 2>; for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { SECTION(named_mesh.name()) @@ -304,24 +414,26 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - auto affine = [](const R2& x) -> R22 { - return R22{+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 R2x2_affine = [](const R2& x) -> R2x2 { + return R2x2{+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(); - DiscreteFunctionP0<R22> fh{p_mesh}; + DiscreteFunctionP0<R2x2> Ah{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{}.build(Ah); - PolynomialReconstruction reconstruction; - auto dpk = reconstruction.build(mesh, fh); + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); { 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]))); + max_mean_error = + std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); } REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); } @@ -329,11 +441,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { double max_x_slope_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R22 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})); + const R2x2 reconstructed_slope = + (1 / 0.2) * (dpk_Ah[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R2{0.1, 0})); - max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R22{+1.7, +2.1, // - -0.6, -2.3})); + max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // + -0.6, -2.3})); } REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12)); } @@ -341,11 +453,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { double max_y_slope_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R22 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})); + const R2x2 reconstructed_slope = + (1 / 0.2) * (dpk_Ah[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R2{0, 0.1})); - max_y_slope_error = std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R22{+1.2, -2.2, // - -2.1, +1.3})); + max_y_slope_error = std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + -2.1, +1.3})); } REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12)); } @@ -366,21 +478,22 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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(); + auto R_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]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - PolynomialReconstruction reconstruction; - auto dpk = reconstruction.build(mesh, fh); + auto reconstructions = PolynomialReconstruction{}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, 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]))); + max_mean_error = std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); } REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } @@ -389,7 +502,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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; + (dpk_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[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)); } @@ -400,7 +513,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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; + (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[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))); } @@ -411,7 +524,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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; + (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[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)); } @@ -421,36 +534,34 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } } - SECTION("R^d data") + SECTION("R^3 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], // + auto R3_affine = [](const R3& x) -> R3 { + return R3{+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]}; + -0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2]}; }; auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - DiscreteFunctionP0<R4> fh{p_mesh}; + DiscreteFunctionP0<R3> uh{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{}.build(uh); - PolynomialReconstruction reconstruction; - auto dpk = reconstruction.build(mesh, fh); + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); { 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]))); + max_mean_error = std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); } REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } @@ -458,10 +569,10 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { 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})); + const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + dpk_uh[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})); + 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-13)); } @@ -469,10 +580,10 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { 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})); + const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + dpk_uh[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})); + 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-13)); } @@ -480,10 +591,10 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { 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})); + const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + dpk_uh[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})); + max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9})); } REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-13)); } @@ -491,9 +602,9 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } } - SECTION("R^mn data") + SECTION("R^2x2 data") { - using R22 = TinyMatrix<2, 2>; + using R2x2 = TinyMatrix<2, 2>; for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { SECTION(named_mesh.name()) @@ -501,25 +612,27 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto affine = [](const R3& x) -> R22 { - return R22{+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 R2x2_affine = [](const R3& x) -> R2x2 { + return R2x2{+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(); - DiscreteFunctionP0<R22> fh{p_mesh}; + DiscreteFunctionP0<R2x2> Ah{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{}.build(Ah); - PolynomialReconstruction reconstruction; - auto dpk = reconstruction.build(mesh, fh); + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); { 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]))); + max_mean_error = + std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); } REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); } @@ -527,11 +640,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { double max_x_slope_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R22 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})); + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R22{+1.7, 2.1, // - -2.3, +3.1})); + max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // + -2.3, +3.1})); } REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12)); } @@ -539,11 +652,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { double max_y_slope_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R22 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})); + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - max_y_slope_error = std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R22{+1.2, -2.2, // - 1.3, +0.8})); + max_y_slope_error = std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + 1.3, +0.8})); } REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12)); } @@ -551,11 +664,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { double max_z_slope_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R22 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})); + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - max_z_slope_error = std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R22{-1.3, -2.4, // - +1.4, -1.8})); + max_z_slope_error = std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // + +1.4, -1.8})); } REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-12)); } -- GitLab From f7b05460428503205bc3dda939f2dae8aae9a8a1 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Tue, 2 Jul 2024 08:16:38 +0200 Subject: [PATCH 031/122] Mark old polynomial reconstruction interface as deprecated It will be removed when code will be cleaned-up. The idea is to keep track of performances improvements until the new API is stabilized --- src/scheme/PolynomialReconstruction.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index 4c88d7476..b42ea9e3e 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -122,7 +122,7 @@ class PolynomialReconstruction public: template <MeshConcept MeshType, typename DataType> - [[nodiscard]] PUGS_INLINE DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> + [[deprecated]] PUGS_INLINE DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> build(const MeshType& mesh, const DiscreteFunctionP0<DataType> p0_function) const { using Rd = TinyVector<MeshType::Dimension>; -- GitLab From fe0acd4d9552cdd0a02dac022e02a7c7b11d641d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 2 Jul 2024 18:31:33 +0200 Subject: [PATCH 032/122] Add reconstruction for discrete function p0 vectors --- src/scheme/DiscreteFunctionDPk.hpp | 208 +----------------- src/scheme/DiscreteFunctionDPkVector.hpp | 141 ++++++++++++ .../PolynomialCenteredCanonicalBasisView.hpp | 205 +++++++++++++++++ src/scheme/PolynomialReconstruction.cpp | 72 ++++-- src/scheme/PolynomialReconstruction.hpp | 17 +- 5 files changed, 426 insertions(+), 217 deletions(-) create mode 100644 src/scheme/DiscreteFunctionDPkVector.hpp create mode 100644 src/scheme/PolynomialCenteredCanonicalBasisView.hpp diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index db8ebdc11..ea18f3e34 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -1,5 +1,5 @@ -#ifndef DISCRETE_FUNCTION_D_PK_HPP -#define DISCRETE_FUNCTION_D_PK_HPP +#ifndef DISCRETE_FUNCTION_DPK_HPP +#define DISCRETE_FUNCTION_DPK_HPP #include <language/utils/ASTNodeDataTypeTraits.hpp> @@ -9,202 +9,7 @@ #include <mesh/MeshDataManager.hpp> #include <mesh/MeshVariant.hpp> #include <scheme/DiscreteFunctionDescriptorP0.hpp> - -template <size_t Dimension, typename DataType> -class PolynomialCenteredCanonicalBasisView -{ - public: - class CoefficientsView - { - private: - DataType* const m_values; - const size_t m_size; - - public: - [[nodiscard]] PUGS_INLINE size_t - size() const - { - return m_size; - } - - [[nodiscard]] PUGS_INLINE DataType& - operator[](size_t i) const - { - Assert(i < m_size, "invalid index"); - return m_values[i]; - } - - CoefficientsView& operator=(const CoefficientsView&) = delete; - CoefficientsView& operator=(CoefficientsView&&) = delete; - - CoefficientsView(DataType* values, size_t size) : m_values{values}, m_size{size} - { - ; - } - - // To try to keep these views close to the initial array one - // forbids copy constructor and take benefits of C++-17 copy - // elisions. - CoefficientsView(const CoefficientsView&) = delete; - - CoefficientsView() = delete; - ~CoefficientsView() = default; - }; - - private: - static_assert((Dimension > 0) and (Dimension <= 3), "invalid dimension"); - - const size_t m_degree; - - // Coefficients are stored in the following order given the shifting - // value is omitted here for simplicity (ie: xj=0). - // - // ---------------------------------------------------- - // For a polynomial of degree n depending on x, one has - // 1, x, x^2, ..., x^n - // - // ---------------------------------------------------- - // For a polynomial of degree n depending on x and y, one has - // 1, x, x^2, ..., x^{n-1}, x^n - // y, x y, x^2 y, ..., x^{n-1} y - // ... - // y^{n-1}, x y^{n-1} - // y^n - // - // ---------------------------------------------------- - // For a polynomial of degree n depending on x, y and z, one has - // 1, x, x^2, ..., x^{n-1}, x^n - // y, x y, x^2 y, ..., x^{n-1} y - // ... - // y^{n-1}, x y^{n-1} - // y^n - // - // z, x z, x^2 z, ..., x^{n-2} z, x^{n-1} z - // y z, x y z, x^2 y z, ..., x^{n-2} y z - // ... - // y^{n-2} z, x y^{n-2} z - // y^{n -1} z - // ... - // ... - // z^{n-2}, x z^{n-2}, x^2 z^{n-2} - // y z^{n-2}, x y z^{n-2} - // y^2 z^{n-2} - // - // z^{n-1}, x z^{n-1} - // y z^{n-1} - // - // z^n - - CoefficientsView m_coefficients; - const TinyVector<Dimension>& m_xj; - - public: - static size_t - dimensionFromDegree(size_t degree) - { - if constexpr (Dimension == 1) { - return degree + 1; - } else if constexpr (Dimension == 2) { - return ((degree + 2) * (degree + 1)) / 2; - } else { // Dimension == 3 - return ((degree + 3) * (degree + 2) * (degree + 1)) / 6; - } - } - - static size_t - degreeFromDimension(size_t polynomial_basis_dimension) - { - Assert(polynomial_basis_dimension > 0); - if constexpr (Dimension == 1) { - return polynomial_basis_dimension - 1; - } else { - // No need fir an explicit formula - // - degree is small and integer - // - do not need the use of sqrt - for (size_t degree = 0; degree < polynomial_basis_dimension; ++degree) { - size_t dimension_from_degree = dimensionFromDegree(degree); - if (dimension_from_degree == polynomial_basis_dimension) { - return degree; - } else if (dimension_from_degree > polynomial_basis_dimension) { - throw NormalError("incorrect polynomial basis dimension"); - } - } - } - } - - DataType - operator()(const TinyVector<Dimension>& x) const - { - if constexpr (Dimension == 1) { - const double x_xj = (x - m_xj)[0]; - - std::remove_const_t<DataType> result = m_coefficients[m_degree]; - for (ssize_t i_coeffiencient = m_degree - 1; i_coeffiencient >= 0; --i_coeffiencient) { - result = x_xj * result + m_coefficients[i_coeffiencient]; - } - - return result; - } else if constexpr (Dimension == 2) { - const TinyVector X_Xj = x - m_xj; - const double& x_xj = X_Xj[0]; - const double& y_yj = X_Xj[1]; - - size_t i = m_coefficients.size() - 1; - - std::remove_const_t<DataType> result = m_coefficients[i--]; - for (ssize_t i_y = m_degree - 1; i_y >= 0; --i_y) { - std::remove_const_t<DataType> x_result = m_coefficients[i--]; - for (ssize_t i_x = m_degree - i_y - 1; i_x >= 0; --i_x) { - x_result = x_xj * x_result + m_coefficients[i--]; - } - result = y_yj * result + x_result; - } - - return result; - } else { // Dimension == 3 - const TinyVector X_Xj = x - m_xj; - const double& x_xj = X_Xj[0]; - const double& y_yj = X_Xj[1]; - const double& z_zj = X_Xj[2]; - - size_t i = m_coefficients.size() - 1; - - std::remove_const_t<DataType> result = m_coefficients[i--]; - for (ssize_t i_z = m_degree - 1; i_z >= 0; --i_z) { - std::remove_const_t<DataType> y_result = m_coefficients[i--]; - for (ssize_t i_y = m_degree - i_z - 1; i_y >= 0; --i_y) { - std::remove_const_t<DataType> x_result = m_coefficients[i--]; - for (ssize_t i_x = m_degree - i_z - i_y - 1; i_x >= 0; --i_x) { - x_result = x_xj * x_result + m_coefficients[i--]; - } - y_result = y_yj * y_result + x_result; - } - result = z_zj * result + y_result; - } - - return result; - } - } - - template <typename ArrayType> - PolynomialCenteredCanonicalBasisView(const size_t degree, - ArrayType& coefficient_list, - const TinyVector<Dimension>& xj) - : m_degree{degree}, m_coefficients{&(coefficient_list[0]), coefficient_list.size()}, m_xj{xj} - {} - - template <typename ArrayType> - PolynomialCenteredCanonicalBasisView(const size_t degree, - const ArrayType& coefficient_list, - const TinyVector<Dimension>& xj) - : m_degree{degree}, m_coefficients{&(coefficient_list[0]), coefficient_list.size()}, m_xj{xj} - {} - - PolynomialCenteredCanonicalBasisView(const PolynomialCenteredCanonicalBasisView&) = delete; - PolynomialCenteredCanonicalBasisView(PolynomialCenteredCanonicalBasisView&&) = default; - PolynomialCenteredCanonicalBasisView() = delete; - ~PolynomialCenteredCanonicalBasisView() = default; -}; +#include <scheme/PolynomialCenteredCanonicalBasisView.hpp> template <size_t Dimension, typename DataType, @@ -261,7 +66,7 @@ class DiscreteFunctionDPk friend PUGS_INLINE DiscreteFunctionDPk<Dimension, std::remove_const_t<DataType>> copy(const DiscreteFunctionDPk& source) { - return DiscreteFunctionDPk<Dimension, std::remove_const_t<DataType>>{source.m_mesh_v, copy(source.cellValues())}; + return DiscreteFunctionDPk<Dimension, std::remove_const_t<DataType>>{source.m_mesh_v, copy(source.cellArrays())}; } friend PUGS_INLINE void @@ -269,7 +74,8 @@ class DiscreteFunctionDPk DiscreteFunctionDPk<Dimension, std::remove_const_t<DataType>>& destination) { Assert(source.m_mesh_v == destination.m_mesh_v); - copy_to(source.m_cell_values, destination.m_cell_values); + Assert(source.m_degree == destination.m_degree); + copy_to(source.m_by_cell_coefficients, destination.m_by_cell_coefficients); } PUGS_INLINE @@ -329,4 +135,4 @@ class DiscreteFunctionDPk ~DiscreteFunctionDPk() = default; }; -#endif // DISCRETE_FUNCTION_D_PK_HPP +#endif // DISCRETE_FUNCTION_DPK_HPP diff --git a/src/scheme/DiscreteFunctionDPkVector.hpp b/src/scheme/DiscreteFunctionDPkVector.hpp new file mode 100644 index 000000000..912dac653 --- /dev/null +++ b/src/scheme/DiscreteFunctionDPkVector.hpp @@ -0,0 +1,141 @@ +#ifndef DISCRETE_FUNCTION_DPK_VECTOR_HPP +#define DISCRETE_FUNCTION_DPK_VECTOR_HPP + +#include <language/utils/ASTNodeDataTypeTraits.hpp> + +#include <mesh/ItemArray.hpp> +#include <mesh/ItemArrayUtils.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/MeshVariant.hpp> +#include <scheme/DiscreteFunctionDescriptorP0.hpp> +#include <scheme/PolynomialCenteredCanonicalBasisView.hpp> + +template <size_t Dimension, + typename DataType, + typename BasisView = PolynomialCenteredCanonicalBasisView<Dimension, DataType>> +class DiscreteFunctionDPkVector +{ + public: + using data_type = DataType; + + friend class DiscreteFunctionDPkVector<Dimension, std::add_const_t<DataType>>; + friend class DiscreteFunctionDPkVector<Dimension, std::remove_const_t<DataType>>; + + private: + std::shared_ptr<const MeshVariant> m_mesh_v; + const size_t m_degree; + const size_t m_number_of_components; + CellArray<DataType> m_by_cell_coefficients; + CellValue<const TinyVector<Dimension>> m_xj; + + public: + PUGS_INLINE + ASTNodeDataType + dataType() const + { + return ast_node_data_type_from<std::remove_const_t<DataType>>; + } + + PUGS_INLINE + const CellArray<DataType>& + cellArrays() const + { + return m_by_cell_coefficients; + } + + PUGS_INLINE std::shared_ptr<const MeshVariant> + meshVariant() const + { + return m_mesh_v; + } + + PUGS_FORCEINLINE + operator DiscreteFunctionDPkVector<Dimension, const DataType>() const + { + return DiscreteFunctionDPkVector<Dimension, const DataType>(m_mesh_v, m_degree, m_number_of_components, + m_by_cell_coefficients); + } + + PUGS_INLINE + void + fill(const DataType& data) const noexcept + { + static_assert(not std::is_const_v<DataType>, "Cannot modify ItemValue of const"); + m_by_cell_coefficients.fill(data); + } + + friend PUGS_INLINE DiscreteFunctionDPkVector<Dimension, std::remove_const_t<DataType>> + copy(const DiscreteFunctionDPkVector& source) + { + return DiscreteFunctionDPkVector<Dimension, std::remove_const_t<DataType>>{source.m_mesh_v, source.m_degree, + source.m_number_of_components, + copy(source.cellArrays())}; + } + + friend PUGS_INLINE void + copy_to(const DiscreteFunctionDPkVector<Dimension, DataType>& source, + DiscreteFunctionDPkVector<Dimension, std::remove_const_t<DataType>>& destination) + { + Assert(source.m_mesh_v == destination.m_mesh_v); + Assert(source.m_degree == destination.m_degree); + Assert(source.m_number_of_components == destination.m_number_of_components); + copy_to(source.m_by_cell_coefficients, destination.m_by_cell_coefficients); + } + + PUGS_INLINE + auto + coefficients(const CellId cell_id) const + { + return m_by_cell_coefficients[cell_id]; + } + + PUGS_FORCEINLINE BasisView + operator[](const CellId cell_id) const noexcept(NO_ASSERT) + { + return BasisView{m_degree, m_by_cell_coefficients[cell_id], m_xj[cell_id]}; + } + + PUGS_INLINE DiscreteFunctionDPkVector + operator=(const DiscreteFunctionDPkVector& f) + { + Assert(m_mesh_v->id() == f.m_mesh_v->id()); + m_by_cell_coefficients = f.m_by_cell_coefficients; + return *this; + } + + DiscreteFunctionDPkVector(const std::shared_ptr<const MeshVariant>& mesh_v, + size_t degree, + size_t number_of_components) + : m_mesh_v{mesh_v}, + m_degree{degree}, + m_number_of_components{number_of_components}, + m_by_cell_coefficients{mesh_v->connectivity(), BasisView::dimensionFromDegree(degree)}, + m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} + {} + + DiscreteFunctionDPkVector(const std::shared_ptr<const MeshVariant>& mesh_v, + size_t degree, + size_t number_of_components, + CellArray<DataType> by_cell_coefficients) + : m_mesh_v{mesh_v}, + m_degree{degree}, + m_number_of_components{number_of_components}, + m_by_cell_coefficients{by_cell_coefficients}, + m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} + {} + + template <MeshConcept MeshType> + DiscreteFunctionDPkVector(const std::shared_ptr<const MeshType>& p_mesh, size_t degree, size_t number_of_components) + : DiscreteFunctionDPkVector{p_mesh->meshVariant(), degree, number_of_components} + {} + + DiscreteFunctionDPkVector() noexcept = delete; + + DiscreteFunctionDPkVector(const DiscreteFunctionDPkVector&) noexcept = default; + DiscreteFunctionDPkVector(DiscreteFunctionDPkVector&&) noexcept = default; + + ~DiscreteFunctionDPkVector() = default; +}; + +#endif // DISCRETE_FUNCTION_DPK_VECTOR_HPP diff --git a/src/scheme/PolynomialCenteredCanonicalBasisView.hpp b/src/scheme/PolynomialCenteredCanonicalBasisView.hpp new file mode 100644 index 000000000..dbe9a5d20 --- /dev/null +++ b/src/scheme/PolynomialCenteredCanonicalBasisView.hpp @@ -0,0 +1,205 @@ +#ifndef POLYNOMIAL_CENTERED_CANONICAL_BASIS_VIEW_HPP +#define POLYNOMIAL_CENTERED_CANONICAL_BASIS_VIEW_HPP + +#include <algebra/TinyVector.hpp> +#include <utils/Array.hpp> +#include <utils/Exceptions.hpp> + +template <size_t Dimension, typename DataType> +class PolynomialCenteredCanonicalBasisView +{ + public: + class CoefficientsView + { + private: + DataType* const m_values; + const size_t m_size; + + public: + [[nodiscard]] PUGS_INLINE size_t + size() const + { + return m_size; + } + + [[nodiscard]] PUGS_INLINE DataType& + operator[](size_t i) const + { + Assert(i < m_size, "invalid index"); + return m_values[i]; + } + + CoefficientsView& operator=(const CoefficientsView&) = delete; + CoefficientsView& operator=(CoefficientsView&&) = delete; + + CoefficientsView(DataType* values, size_t size) : m_values{values}, m_size{size} + { + ; + } + + // To try to keep these views close to the initial array one + // forbids copy constructor and take benefits of C++-17 copy + // elisions. + CoefficientsView(const CoefficientsView&) = delete; + + CoefficientsView() = delete; + ~CoefficientsView() = default; + }; + + private: + static_assert((Dimension > 0) and (Dimension <= 3), "invalid dimension"); + + const size_t m_degree; + + // Coefficients are stored in the following order given the shifting + // value is omitted here for simplicity (ie: xj=0). + // + // ---------------------------------------------------- + // For a polynomial of degree n depending on x, one has + // 1, x, x^2, ..., x^n + // + // ---------------------------------------------------- + // For a polynomial of degree n depending on x and y, one has + // 1, x, x^2, ..., x^{n-1}, x^n + // y, x y, x^2 y, ..., x^{n-1} y + // ... + // y^{n-1}, x y^{n-1} + // y^n + // + // ---------------------------------------------------- + // For a polynomial of degree n depending on x, y and z, one has + // 1, x, x^2, ..., x^{n-1}, x^n + // y, x y, x^2 y, ..., x^{n-1} y + // ... + // y^{n-1}, x y^{n-1} + // y^n + // + // z, x z, x^2 z, ..., x^{n-2} z, x^{n-1} z + // y z, x y z, x^2 y z, ..., x^{n-2} y z + // ... + // y^{n-2} z, x y^{n-2} z + // y^{n -1} z + // ... + // ... + // z^{n-2}, x z^{n-2}, x^2 z^{n-2} + // y z^{n-2}, x y z^{n-2} + // y^2 z^{n-2} + // + // z^{n-1}, x z^{n-1} + // y z^{n-1} + // + // z^n + + CoefficientsView m_coefficients; + const TinyVector<Dimension>& m_xj; + + public: + static size_t + dimensionFromDegree(size_t degree) + { + if constexpr (Dimension == 1) { + return degree + 1; + } else if constexpr (Dimension == 2) { + return ((degree + 2) * (degree + 1)) / 2; + } else { // Dimension == 3 + return ((degree + 3) * (degree + 2) * (degree + 1)) / 6; + } + } + + static size_t + degreeFromDimension(size_t polynomial_basis_dimension) + { + Assert(polynomial_basis_dimension > 0); + if constexpr (Dimension == 1) { + return polynomial_basis_dimension - 1; + } else { + // No need fir an explicit formula + // - degree is small and integer + // - do not need the use of sqrt + for (size_t degree = 0; degree < polynomial_basis_dimension; ++degree) { + size_t dimension_from_degree = dimensionFromDegree(degree); + if (dimension_from_degree == polynomial_basis_dimension) { + return degree; + } else if (dimension_from_degree > polynomial_basis_dimension) { + break; + } + } + throw NormalError("incorrect polynomial basis dimension"); + } + } + + DataType + operator()(const TinyVector<Dimension>& x) const + { + if constexpr (Dimension == 1) { + const double x_xj = (x - m_xj)[0]; + + std::remove_const_t<DataType> result = m_coefficients[m_degree]; + for (ssize_t i_coeffiencient = m_degree - 1; i_coeffiencient >= 0; --i_coeffiencient) { + result = x_xj * result + m_coefficients[i_coeffiencient]; + } + + return result; + } else if constexpr (Dimension == 2) { + const TinyVector X_Xj = x - m_xj; + const double& x_xj = X_Xj[0]; + const double& y_yj = X_Xj[1]; + + size_t i = m_coefficients.size() - 1; + + std::remove_const_t<DataType> result = m_coefficients[i--]; + for (ssize_t i_y = m_degree - 1; i_y >= 0; --i_y) { + std::remove_const_t<DataType> x_result = m_coefficients[i--]; + for (ssize_t i_x = m_degree - i_y - 1; i_x >= 0; --i_x) { + x_result = x_xj * x_result + m_coefficients[i--]; + } + result = y_yj * result + x_result; + } + + return result; + } else { // Dimension == 3 + const TinyVector X_Xj = x - m_xj; + const double& x_xj = X_Xj[0]; + const double& y_yj = X_Xj[1]; + const double& z_zj = X_Xj[2]; + + size_t i = m_coefficients.size() - 1; + + std::remove_const_t<DataType> result = m_coefficients[i--]; + for (ssize_t i_z = m_degree - 1; i_z >= 0; --i_z) { + std::remove_const_t<DataType> y_result = m_coefficients[i--]; + for (ssize_t i_y = m_degree - i_z - 1; i_y >= 0; --i_y) { + std::remove_const_t<DataType> x_result = m_coefficients[i--]; + for (ssize_t i_x = m_degree - i_z - i_y - 1; i_x >= 0; --i_x) { + x_result = x_xj * x_result + m_coefficients[i--]; + } + y_result = y_yj * y_result + x_result; + } + result = z_zj * result + y_result; + } + + return result; + } + } + + template <typename ArrayType> + PolynomialCenteredCanonicalBasisView(const size_t degree, + ArrayType& coefficient_list, + const TinyVector<Dimension>& xj) + : m_degree{degree}, m_coefficients{&(coefficient_list[0]), coefficient_list.size()}, m_xj{xj} + {} + + template <typename ArrayType> + PolynomialCenteredCanonicalBasisView(const size_t degree, + const ArrayType& coefficient_list, + const TinyVector<Dimension>& xj) + : m_degree{degree}, m_coefficients{&(coefficient_list[0]), coefficient_list.size()}, m_xj{xj} + {} + + PolynomialCenteredCanonicalBasisView(const PolynomialCenteredCanonicalBasisView&) = delete; + PolynomialCenteredCanonicalBasisView(PolynomialCenteredCanonicalBasisView&&) = default; + PolynomialCenteredCanonicalBasisView() = delete; + ~PolynomialCenteredCanonicalBasisView() = default; +}; + +#endif // POLYNOMIAL_CENTERED_CANONICAL_BASIS_VIEW_HPP diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index af1723a47..c770f5a31 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -28,9 +28,11 @@ class MutableDiscreteFunctionDPkVariant DiscreteFunctionDPk<3, TinyVector<3>>, DiscreteFunctionDPk<3, TinyMatrix<1>>, DiscreteFunctionDPk<3, TinyMatrix<2>>, - DiscreteFunctionDPk<3, TinyMatrix<3>>>; + DiscreteFunctionDPk<3, TinyMatrix<3>>, -#warning add DiscreteFunctionDPkVector to the variant + DiscreteFunctionDPkVector<1, double>, + DiscreteFunctionDPkVector<2, double>, + DiscreteFunctionDPkVector<3, double>>; private: Variant m_mutable_discrete_function_dpk; @@ -67,18 +69,26 @@ class MutableDiscreteFunctionDPkVariant template <size_t Dimension, typename DataType> MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPk<Dimension, DataType>& discrete_function_dpk) - : m_mutable_discrete_function_dpk{DiscreteFunctionDPk<Dimension, DataType>{discrete_function_dpk}} + : m_mutable_discrete_function_dpk{discrete_function_dpk} { - static_assert(std::is_same_v<std::remove_const_t<DataType>, double> or // - std::is_same_v<std::remove_const_t<DataType>, TinyVector<1, double>> or // - std::is_same_v<std::remove_const_t<DataType>, TinyVector<2, double>> or // - std::is_same_v<std::remove_const_t<DataType>, TinyVector<3, double>> or // - std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<1, 1, double>> or // - std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<2, 2, double>> or // - std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<3, 3, double>>, + static_assert(std::is_same_v<DataType, double> or // + std::is_same_v<DataType, TinyVector<1, double>> or // + std::is_same_v<DataType, TinyVector<2, double>> or // + std::is_same_v<DataType, TinyVector<3, double>> or // + std::is_same_v<DataType, TinyMatrix<1, 1, double>> or // + std::is_same_v<DataType, TinyMatrix<2, 2, double>> or // + std::is_same_v<DataType, TinyMatrix<3, 3, double>>, "DiscreteFunctionDPk with this DataType is not allowed in variant"); } + template <size_t Dimension, typename DataType> + MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) + : m_mutable_discrete_function_dpk{discrete_function_dpk} + { + static_assert(std::is_same_v<DataType, double>, + "DiscreteFunctionDPkVector with this DataType is not allowed in variant"); + } + MutableDiscreteFunctionDPkVariant& operator=(MutableDiscreteFunctionDPkVariant&&) = default; MutableDiscreteFunctionDPkVariant& operator=(const MutableDiscreteFunctionDPkVariant&) = default; @@ -158,8 +168,12 @@ PolynomialReconstruction::_build( using DataType = std::remove_const_t<std::decay_t<typename DiscreteFunctionT::data_type>>; mutable_discrete_function_dpk_variant_list.push_back( DiscreteFunctionDPk<MeshType::Dimension, DataType>(p_mesh, degree)); + } 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)); } else { - throw NotImplementedError("discrete function type"); + throw UnexpectedError("unexpected discrete function type"); } }, discrete_function_variant->discreteFunction()); @@ -218,6 +232,20 @@ PolynomialReconstruction::_build( } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) { column_begin += DataType::Dimension; } + } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + const auto pj_vector = discrete_function[cell_j_id]; + for (size_t l = 0; l < pj_vector.size(); ++l) { + const DataType& pj = pj_vector[l]; + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const DataType& pi_pj = discrete_function[cell_i_id][l] - pj; + B(i, column_begin + l) = pi_pj; + } + } + column_begin += pj_vector.size(); + } else { + throw UnexpectedError("invalid discrete function type"); } }, discrete_function_variant->discreteFunction()); @@ -282,10 +310,28 @@ PolynomialReconstruction::_build( } column_begin += DataType::Dimension; } else { - throw NotImplementedError("unexpected data type"); + throw UnexpectedError("unexpected data type"); + } + } 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); + } + ++column_begin; + } else { + throw UnexpectedError("unexpected data type"); + } } } else { - throw NotImplementedError("non scalar P0 functions"); + throw UnexpectedError("unexpected discrete function type"); } } else { throw UnexpectedError("incompatible data types"); diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index b42ea9e3e..430ce0234 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -4,6 +4,7 @@ #include <mesh/MeshTraits.hpp> #include <mesh/StencilManager.hpp> #include <scheme/DiscreteFunctionDPk.hpp> +#include <scheme/DiscreteFunctionDPkVector.hpp> #include <algebra/ShrinkMatrixView.hpp> #include <algebra/ShrinkVectorView.hpp> @@ -49,9 +50,11 @@ class DiscreteFunctionDPkVariant DiscreteFunctionDPk<3, const TinyVector<3>>, DiscreteFunctionDPk<3, const TinyMatrix<1>>, DiscreteFunctionDPk<3, const TinyMatrix<2>>, - DiscreteFunctionDPk<3, const TinyMatrix<3>>>; + DiscreteFunctionDPk<3, const TinyMatrix<3>>, -#warning add DiscreteFunctionDPkVector to the variant + DiscreteFunctionDPkVector<1, const double>, + DiscreteFunctionDPkVector<2, const double>, + DiscreteFunctionDPkVector<3, const double>>; private: Variant m_discrete_function_dpk; @@ -102,6 +105,14 @@ class DiscreteFunctionDPkVariant "DiscreteFunctionDPk with this DataType is not allowed in variant"); } + template <size_t Dimension, typename DataType> + DiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) + : m_discrete_function_dpk{DiscreteFunctionDPkVector<Dimension, const DataType>{discrete_function_dpk}} + { + static_assert(std::is_same_v<std::remove_const_t<DataType>, double>, + "DiscreteFunctionDPkVector with this DataType is not allowed in variant"); + } + DiscreteFunctionDPkVariant& operator=(DiscreteFunctionDPkVariant&&) = default; DiscreteFunctionDPkVariant& operator=(const DiscreteFunctionDPkVariant&) = default; @@ -122,7 +133,7 @@ class PolynomialReconstruction public: template <MeshConcept MeshType, typename DataType> - [[deprecated]] PUGS_INLINE DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> + PUGS_INLINE DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> build(const MeshType& mesh, const DiscreteFunctionP0<DataType> p0_function) const { using Rd = TinyVector<MeshType::Dimension>; -- GitLab From 24fbbdfc7d910e1eebd7dac67d7ea54fec99d3a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 2 Jul 2024 19:37:07 +0200 Subject: [PATCH 033/122] Add component view to access polynomial reconstructions of P0 vector --- src/scheme/DiscreteFunctionDPk.hpp | 2 +- src/scheme/DiscreteFunctionDPkVector.hpp | 44 +++++++++++++++++++++--- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index ea18f3e34..d0edbfab1 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -73,7 +73,7 @@ class DiscreteFunctionDPk copy_to(const DiscreteFunctionDPk<Dimension, DataType>& source, DiscreteFunctionDPk<Dimension, std::remove_const_t<DataType>>& destination) { - Assert(source.m_mesh_v == destination.m_mesh_v); + Assert(source.m_mesh_v->id() == destination.m_mesh_v->id()); Assert(source.m_degree == destination.m_degree); copy_to(source.m_by_cell_coefficients, destination.m_by_cell_coefficients); } diff --git a/src/scheme/DiscreteFunctionDPkVector.hpp b/src/scheme/DiscreteFunctionDPkVector.hpp index 912dac653..dabf657e6 100644 --- a/src/scheme/DiscreteFunctionDPkVector.hpp +++ b/src/scheme/DiscreteFunctionDPkVector.hpp @@ -22,10 +22,39 @@ class DiscreteFunctionDPkVector friend class DiscreteFunctionDPkVector<Dimension, std::add_const_t<DataType>>; friend class DiscreteFunctionDPkVector<Dimension, std::remove_const_t<DataType>>; + class ComponentCoefficientView + { + private: + DataType* const m_data; + const size_t m_size; + + public: + size_t + size() const + { + return m_size; + } + + DataType& + operator[](size_t i) const + { + Assert(i < m_size); + return m_data[i]; + } + + ComponentCoefficientView(DataType* data, size_t size) : m_data{data}, m_size{size} {} + + ComponentCoefficientView(const ComponentCoefficientView&) = delete; + ComponentCoefficientView(ComponentCoefficientView&&) = delete; + + ~ComponentCoefficientView() = default; + }; + private: std::shared_ptr<const MeshVariant> m_mesh_v; const size_t m_degree; const size_t m_number_of_components; + const size_t m_nb_coefficients_per_component; CellArray<DataType> m_by_cell_coefficients; CellValue<const TinyVector<Dimension>> m_xj; @@ -70,6 +99,7 @@ class DiscreteFunctionDPkVector { return DiscreteFunctionDPkVector<Dimension, std::remove_const_t<DataType>>{source.m_mesh_v, source.m_degree, source.m_number_of_components, + source.m_nb_coefficients_per_component, copy(source.cellArrays())}; } @@ -77,8 +107,9 @@ class DiscreteFunctionDPkVector copy_to(const DiscreteFunctionDPkVector<Dimension, DataType>& source, DiscreteFunctionDPkVector<Dimension, std::remove_const_t<DataType>>& destination) { - Assert(source.m_mesh_v == destination.m_mesh_v); + Assert(source.m_mesh_v->id() == destination.m_mesh_v->id()); Assert(source.m_degree == destination.m_degree); + Assert(source.m_nb_coefficients_per_component == destination.m_nb_coefficients_per_component); Assert(source.m_number_of_components == destination.m_number_of_components); copy_to(source.m_by_cell_coefficients, destination.m_by_cell_coefficients); } @@ -91,9 +122,12 @@ class DiscreteFunctionDPkVector } PUGS_FORCEINLINE BasisView - operator[](const CellId cell_id) const noexcept(NO_ASSERT) + operator()(const CellId cell_id, size_t i_component) const noexcept(NO_ASSERT) { - return BasisView{m_degree, m_by_cell_coefficients[cell_id], m_xj[cell_id]}; + ComponentCoefficientView + component_coefficient_view{&m_by_cell_coefficients[cell_id][i_component * m_nb_coefficients_per_component], + m_nb_coefficients_per_component}; + return BasisView{m_degree, component_coefficient_view, m_xj[cell_id]}; } PUGS_INLINE DiscreteFunctionDPkVector @@ -110,7 +144,8 @@ class DiscreteFunctionDPkVector : m_mesh_v{mesh_v}, m_degree{degree}, m_number_of_components{number_of_components}, - m_by_cell_coefficients{mesh_v->connectivity(), BasisView::dimensionFromDegree(degree)}, + m_nb_coefficients_per_component(BasisView::dimensionFromDegree(degree)), + m_by_cell_coefficients{mesh_v->connectivity(), m_number_of_components * BasisView::dimensionFromDegree(degree)}, m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} {} @@ -121,6 +156,7 @@ class DiscreteFunctionDPkVector : m_mesh_v{mesh_v}, m_degree{degree}, m_number_of_components{number_of_components}, + m_nb_coefficients_per_component(BasisView::dimensionFromDegree(degree)), m_by_cell_coefficients{by_cell_coefficients}, m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} {} -- GitLab From 34661146acce17480cb85093aa4ba4487f12f14b Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Wed, 3 Jul 2024 08:31:57 +0200 Subject: [PATCH 034/122] Change DiscreteFunctionDPk function API and add missing tests --- src/scheme/DiscreteFunctionDPk.hpp | 36 +++++---- tests/test_DiscreteFunctionDPk.cpp | 120 +++++++++++++++++++++++------ 2 files changed, 120 insertions(+), 36 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index d0edbfab1..cdb36a617 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -24,7 +24,7 @@ class DiscreteFunctionDPk private: std::shared_ptr<const MeshVariant> m_mesh_v; - const size_t m_degree; + size_t m_degree; CellArray<DataType> m_by_cell_coefficients; CellValue<const TinyVector<Dimension>> m_xj; @@ -36,11 +36,10 @@ class DiscreteFunctionDPk return ast_node_data_type_from<std::remove_const_t<DataType>>; } - PUGS_INLINE - const CellArray<DataType>& - cellArrays() const + PUGS_INLINE size_t + degree() const { - return m_by_cell_coefficients; + return m_degree; } PUGS_INLINE std::shared_ptr<const MeshVariant> @@ -49,6 +48,13 @@ class DiscreteFunctionDPk return m_mesh_v; } + PUGS_INLINE + const CellArray<DataType>& + cellArrays() const + { + return m_by_cell_coefficients; + } + PUGS_FORCEINLINE operator DiscreteFunctionDPk<Dimension, const DataType>() const { @@ -59,7 +65,7 @@ class DiscreteFunctionDPk void fill(const DataType& data) const noexcept { - static_assert(not std::is_const_v<DataType>, "Cannot modify ItemValue of const"); + static_assert(not std::is_const_v<DataType>, "cannot modify DiscreteFunctionDPk of const"); m_by_cell_coefficients.fill(data); } @@ -73,8 +79,8 @@ class DiscreteFunctionDPk copy_to(const DiscreteFunctionDPk<Dimension, DataType>& source, DiscreteFunctionDPk<Dimension, std::remove_const_t<DataType>>& destination) { - Assert(source.m_mesh_v->id() == destination.m_mesh_v->id()); - Assert(source.m_degree == destination.m_degree); + Assert(source.m_mesh_v->id() == destination.m_mesh_v->id(), "copy_to target must use the same mesh"); + Assert(source.m_degree == destination.m_degree, "copy_to target must have the same degree"); copy_to(source.m_by_cell_coefficients, destination.m_by_cell_coefficients); } @@ -94,8 +100,11 @@ class DiscreteFunctionDPk PUGS_INLINE DiscreteFunctionDPk operator=(const DiscreteFunctionDPk& f) { - Assert(m_mesh_v->id() == f.m_mesh_v->id()); + m_mesh_v = f.m_mesh_v; + m_degree = f.m_degree; m_by_cell_coefficients = f.m_by_cell_coefficients; + m_xj = f.m_xj; + return *this; } @@ -112,7 +121,8 @@ class DiscreteFunctionDPk m_by_cell_coefficients{cell_array}, m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} { - Assert(mesh_v->connectivity().id() == cell_array.connectivity_ptr()->id()); + Assert(mesh_v->connectivity().id() == cell_array.connectivity_ptr()->id(), + "cell_array is built on different connectivity"); } template <MeshConcept MeshType> @@ -123,11 +133,9 @@ class DiscreteFunctionDPk template <MeshConcept MeshType> DiscreteFunctionDPk(const std::shared_ptr<const MeshType>& p_mesh, const CellArray<DataType>& cell_array) : DiscreteFunctionDPk{p_mesh->meshVariant(), cell_array} - { - Assert(m_mesh_v->connectivity().id() == cell_array.connectivity_ptr()->id()); - } + {} - DiscreteFunctionDPk() noexcept = delete; + DiscreteFunctionDPk() noexcept = default; DiscreteFunctionDPk(const DiscreteFunctionDPk&) noexcept = default; DiscreteFunctionDPk(DiscreteFunctionDPk&&) noexcept = default; diff --git a/tests/test_DiscreteFunctionDPk.cpp b/tests/test_DiscreteFunctionDPk.cpp index e303e5757..d4f8598bd 100644 --- a/tests/test_DiscreteFunctionDPk.cpp +++ b/tests/test_DiscreteFunctionDPk.cpp @@ -10,6 +10,80 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") { + SECTION("Basic interface") + { + const std::shared_ptr mesh_2d_v = MeshDataBaseForTests::get().cartesian2DMesh(); + const std::shared_ptr mesh_2d = mesh_2d_v->get<Mesh<2>>(); + + const std::shared_ptr mesh_2d_2_v = MeshDataBaseForTests::get().hybrid2DMesh(); + const std::shared_ptr mesh_2d_2 = mesh_2d_2_v->get<Mesh<2>>(); + + DiscreteFunctionDPk<2, const double> R_dkp; + + { + DiscreteFunctionDPk<2, double> tmp_R_dkp(mesh_2d_v, 2); + tmp_R_dkp.fill(2); + R_dkp = tmp_R_dkp; + } + + REQUIRE(R_dkp.degree() == 2); + + REQUIRE(min(R_dkp.cellArrays()) == 2); + REQUIRE(max(R_dkp.cellArrays()) == 2); + + REQUIRE(R_dkp.dataType() == ast_node_data_type_from<double>); + REQUIRE(R_dkp.meshVariant()->id() == mesh_2d->id()); + + DiscreteFunctionDPk<2, double> R_dkp2; + R_dkp2 = copy(R_dkp); + + REQUIRE(R_dkp2.degree() == 2); + + REQUIRE(min(R_dkp2.cellArrays()) == 2); + REQUIRE(max(R_dkp2.cellArrays()) == 2); + + REQUIRE(R_dkp2.dataType() == ast_node_data_type_from<double>); + REQUIRE(R_dkp2.meshVariant()->id() == mesh_2d_v->id()); + + DiscreteFunctionDPk<2, double> R_dkp3(mesh_2d_2_v, 1); + R_dkp3.fill(3); + + REQUIRE(R_dkp3.degree() == 1); + + REQUIRE(min(R_dkp3.cellArrays()) == 3); + REQUIRE(max(R_dkp3.cellArrays()) == 3); + + REQUIRE(R_dkp3.dataType() == ast_node_data_type_from<double>); + REQUIRE(R_dkp3.meshVariant()->id() == mesh_2d_2_v->id()); + + DiscreteFunctionDPk<2, double> R_dkp4(mesh_2d, R_dkp2.cellArrays()); + + REQUIRE(min(R_dkp4.cellArrays()) == 2); + REQUIRE(max(R_dkp4.cellArrays()) == 2); + + R_dkp4.fill(5); + REQUIRE(min(R_dkp4.cellArrays()) == 5); + REQUIRE(max(R_dkp4.cellArrays()) == 5); + REQUIRE(min(R_dkp2.cellArrays()) == 5); + REQUIRE(max(R_dkp2.cellArrays()) == 5); + + copy_to(R_dkp, R_dkp4); + REQUIRE(min(R_dkp4.cellArrays()) == 2); + REQUIRE(max(R_dkp4.cellArrays()) == 2); + REQUIRE(min(R_dkp2.cellArrays()) == 2); + REQUIRE(max(R_dkp2.cellArrays()) == 2); + +#ifndef NDEBUG + REQUIRE_THROWS_WITH(copy_to(R_dkp, R_dkp3), "copy_to target must use the same mesh"); + + DiscreteFunctionDPk<2, double> R_dkp5(mesh_2d, 3); + REQUIRE_THROWS_WITH(copy_to(R_dkp, R_dkp5), "copy_to target must have the same degree"); + + REQUIRE_THROWS_WITH((DiscreteFunctionDPk<2, double>{mesh_2d_2, R_dkp2.cellArrays()}), + "cell_array is built on different connectivity"); +#endif // NDEBUG + } + SECTION("R data") { SECTION("1D") @@ -93,17 +167,18 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { const double x0 = 0.2 * Vj[cell_id]; const double y0 = 0.3 * Vj[cell_id]; - error[cell_id] = std::abs(pk[cell_id](xj[cell_id] + R2{x0, y0}) // - - (a[0] // - + x0 * a[1] // - + x0 * x0 * a[2] // - + x0 * x0 * x0 * a[3] // - + y0 * a[4] // - + y0 * x0 * a[5] // - + y0 * x0 * x0 * a[6] // - + y0 * y0 * a[7] // - + y0 * y0 * x0 * a[8] // - + y0 * y0 * y0 * a[9])); + error[cell_id] // + = std::abs(pk[cell_id](xj[cell_id] + R2{x0, y0}) // + - (a[0] // + + x0 * a[1] // + + x0 * x0 * a[2] // + + x0 * x0 * x0 * a[3] // + + y0 * a[4] // + + y0 * x0 * a[5] // + + y0 * x0 * x0 * a[6] // + + y0 * y0 * a[7] // + + y0 * y0 * x0 * a[8] // + + y0 * y0 * y0 * a[9])); }); REQUIRE(max(error) == Catch::Approx(0.).margin(1E-14)); @@ -264,17 +339,18 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { const double x0 = 0.2 * Vj[cell_id]; const double y0 = 0.3 * Vj[cell_id]; - delta[cell_id] = l2Norm(pk[cell_id](xj[cell_id] + R2{x0, y0}) // - - (a[0] // - + x0 * a[1] // - + x0 * x0 * a[2] // - + x0 * x0 * x0 * a[3] // - + y0 * a[4] // - + y0 * x0 * a[5] // - + y0 * x0 * x0 * a[6] // - + y0 * y0 * a[7] // - + y0 * y0 * x0 * a[8] // - + y0 * y0 * y0 * a[9])); + delta[cell_id] // + = l2Norm(pk[cell_id](xj[cell_id] + R2{x0, y0}) // + - (a[0] // + + x0 * a[1] // + + x0 * x0 * a[2] // + + x0 * x0 * x0 * a[3] // + + y0 * a[4] // + + y0 * x0 * a[5] // + + y0 * x0 * x0 * a[6] // + + y0 * y0 * a[7] // + + y0 * y0 * x0 * a[8] // + + y0 * y0 * y0 * a[9])); }); REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); -- GitLab From 3bfda82a966b09f8da7b21a5a4541310009e49e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Wed, 3 Jul 2024 19:25:38 +0200 Subject: [PATCH 035/122] Adjust API for simplicity --- src/scheme/DiscreteFunctionDPk.hpp | 13 +++++---- src/scheme/DiscreteFunctionDPkVector.hpp | 36 ++++++++++++++++++++---- tests/test_DiscreteFunctionDPk.cpp | 4 +++ 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index cdb36a617..0aa0e38e5 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -42,12 +42,6 @@ class DiscreteFunctionDPk return m_degree; } - PUGS_INLINE std::shared_ptr<const MeshVariant> - meshVariant() const - { - return m_mesh_v; - } - PUGS_INLINE const CellArray<DataType>& cellArrays() const @@ -55,6 +49,12 @@ class DiscreteFunctionDPk return m_by_cell_coefficients; } + PUGS_INLINE std::shared_ptr<const MeshVariant> + meshVariant() const + { + return m_mesh_v; + } + PUGS_FORCEINLINE operator DiscreteFunctionDPk<Dimension, const DataType>() const { @@ -94,6 +94,7 @@ class DiscreteFunctionDPk PUGS_FORCEINLINE BasisView operator[](const CellId cell_id) const noexcept(NO_ASSERT) { + Assert(m_mesh_v.use_count() > 0, "DiscreteFunctionDPk is not built"); return BasisView{m_degree, m_by_cell_coefficients[cell_id], m_xj[cell_id]}; } diff --git a/src/scheme/DiscreteFunctionDPkVector.hpp b/src/scheme/DiscreteFunctionDPkVector.hpp index dabf657e6..2dc6a7889 100644 --- a/src/scheme/DiscreteFunctionDPkVector.hpp +++ b/src/scheme/DiscreteFunctionDPkVector.hpp @@ -52,9 +52,9 @@ class DiscreteFunctionDPkVector private: std::shared_ptr<const MeshVariant> m_mesh_v; - const size_t m_degree; - const size_t m_number_of_components; - const size_t m_nb_coefficients_per_component; + size_t m_degree; + size_t m_number_of_components; + size_t m_nb_coefficients_per_component; CellArray<DataType> m_by_cell_coefficients; CellValue<const TinyVector<Dimension>> m_xj; @@ -66,6 +66,25 @@ class DiscreteFunctionDPkVector return ast_node_data_type_from<std::remove_const_t<DataType>>; } + PUGS_INLINE size_t + degree() const + { + return m_degree; + } + + PUGS_INLINE size_t + numberOfComponents() const + { + return m_number_of_components; + } + + PUGS_INLINE + size_t + numberOfCoefficientsPerComponent() const + { + return m_nb_coefficients_per_component; + } + PUGS_INLINE const CellArray<DataType>& cellArrays() const @@ -124,6 +143,8 @@ class DiscreteFunctionDPkVector PUGS_FORCEINLINE BasisView operator()(const CellId cell_id, size_t i_component) const noexcept(NO_ASSERT) { + Assert(m_mesh_v.use_count() > 0, "DiscreteFunctionDPkVector is not built"); + Assert(i_component < m_number_of_components, "DiscreteFunctionDPkVector is not built"); ComponentCoefficientView component_coefficient_view{&m_by_cell_coefficients[cell_id][i_component * m_nb_coefficients_per_component], m_nb_coefficients_per_component}; @@ -133,8 +154,11 @@ class DiscreteFunctionDPkVector PUGS_INLINE DiscreteFunctionDPkVector operator=(const DiscreteFunctionDPkVector& f) { - Assert(m_mesh_v->id() == f.m_mesh_v->id()); - m_by_cell_coefficients = f.m_by_cell_coefficients; + m_mesh_v = f.m_mesh_v; + m_degree = f.m_degree; + m_number_of_components = f.m_number_of_components; + m_nb_coefficients_per_component = f.m_nb_coefficients_per_component; + m_by_cell_coefficients = f.m_by_cell_coefficients; return *this; } @@ -166,7 +190,7 @@ class DiscreteFunctionDPkVector : DiscreteFunctionDPkVector{p_mesh->meshVariant(), degree, number_of_components} {} - DiscreteFunctionDPkVector() noexcept = delete; + DiscreteFunctionDPkVector() noexcept = default; DiscreteFunctionDPkVector(const DiscreteFunctionDPkVector&) noexcept = default; DiscreteFunctionDPkVector(DiscreteFunctionDPkVector&&) noexcept = default; diff --git a/tests/test_DiscreteFunctionDPk.cpp b/tests/test_DiscreteFunctionDPk.cpp index d4f8598bd..60120f894 100644 --- a/tests/test_DiscreteFunctionDPk.cpp +++ b/tests/test_DiscreteFunctionDPk.cpp @@ -20,6 +20,10 @@ TEST_CASE("DiscreteFunctionDPk", "[scheme]") DiscreteFunctionDPk<2, const double> R_dkp; +#ifndef NDEBUG + REQUIRE_THROWS_WITH(R_dkp[CellId(0)], "DiscreteFunctionDPk is not built"); +#endif // NDEBUG + { DiscreteFunctionDPk<2, double> tmp_R_dkp(mesh_2d_v, 2); tmp_R_dkp.fill(2); -- GitLab From d23e21282f6c6a4f41e6131715b31ff6fafc932a Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Thu, 4 Jul 2024 08:08:28 +0200 Subject: [PATCH 036/122] Add tests for DiscreteFunctionDPkVector --- src/scheme/DiscreteFunctionDPkVector.hpp | 24 +- tests/CMakeLists.txt | 1 + tests/test_DiscreteFunctionDPkVector.cpp | 291 +++++++++++++++++++++++ 3 files changed, 309 insertions(+), 7 deletions(-) create mode 100644 tests/test_DiscreteFunctionDPkVector.cpp diff --git a/src/scheme/DiscreteFunctionDPkVector.hpp b/src/scheme/DiscreteFunctionDPkVector.hpp index 2dc6a7889..2fefc078b 100644 --- a/src/scheme/DiscreteFunctionDPkVector.hpp +++ b/src/scheme/DiscreteFunctionDPkVector.hpp @@ -118,7 +118,6 @@ class DiscreteFunctionDPkVector { return DiscreteFunctionDPkVector<Dimension, std::remove_const_t<DataType>>{source.m_mesh_v, source.m_degree, source.m_number_of_components, - source.m_nb_coefficients_per_component, copy(source.cellArrays())}; } @@ -126,10 +125,10 @@ class DiscreteFunctionDPkVector copy_to(const DiscreteFunctionDPkVector<Dimension, DataType>& source, DiscreteFunctionDPkVector<Dimension, std::remove_const_t<DataType>>& destination) { - Assert(source.m_mesh_v->id() == destination.m_mesh_v->id()); - Assert(source.m_degree == destination.m_degree); - Assert(source.m_nb_coefficients_per_component == destination.m_nb_coefficients_per_component); - Assert(source.m_number_of_components == destination.m_number_of_components); + Assert(source.m_mesh_v->id() == destination.m_mesh_v->id(), "copy_to target must use the same mesh"); + Assert(source.m_degree == destination.m_degree, "copy_to target must have the same degree"); + Assert(source.m_number_of_components == destination.m_number_of_components, + "copy_to target must have the same number of components"); copy_to(source.m_by_cell_coefficients, destination.m_by_cell_coefficients); } @@ -144,7 +143,7 @@ class DiscreteFunctionDPkVector operator()(const CellId cell_id, size_t i_component) const noexcept(NO_ASSERT) { Assert(m_mesh_v.use_count() > 0, "DiscreteFunctionDPkVector is not built"); - Assert(i_component < m_number_of_components, "DiscreteFunctionDPkVector is not built"); + Assert(i_component < m_number_of_components, "incorrect component number"); ComponentCoefficientView component_coefficient_view{&m_by_cell_coefficients[cell_id][i_component * m_nb_coefficients_per_component], m_nb_coefficients_per_component}; @@ -183,13 +182,24 @@ class DiscreteFunctionDPkVector m_nb_coefficients_per_component(BasisView::dimensionFromDegree(degree)), m_by_cell_coefficients{by_cell_coefficients}, m_xj{MeshDataManager::instance().getMeshData(*m_mesh_v->get<Mesh<Dimension>>()).xj()} - {} + { + Assert(mesh_v->connectivity().id() == by_cell_coefficients.connectivity_ptr()->id(), + "cell_array is built on different connectivity"); + } template <MeshConcept MeshType> DiscreteFunctionDPkVector(const std::shared_ptr<const MeshType>& p_mesh, size_t degree, size_t number_of_components) : DiscreteFunctionDPkVector{p_mesh->meshVariant(), degree, number_of_components} {} + template <MeshConcept MeshType> + DiscreteFunctionDPkVector(const std::shared_ptr<const MeshType>& p_mesh, + size_t degree, + size_t number_of_components, + CellArray<DataType> by_cell_coefficients) + : DiscreteFunctionDPkVector{p_mesh->meshVariant(), degree, number_of_components, by_cell_coefficients} + {} + DiscreteFunctionDPkVector() noexcept = default; DiscreteFunctionDPkVector(const DiscreteFunctionDPkVector&) noexcept = default; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fccafd015..4c94f8a39 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -161,6 +161,7 @@ add_executable (mpi_unit_tests mpi_test_main.cpp test_Connectivity.cpp test_DiscreteFunctionDPk.cpp + test_DiscreteFunctionDPkVector.cpp test_DiscreteFunctionIntegrator.cpp test_DiscreteFunctionIntegratorByZone.cpp test_DiscreteFunctionInterpoler.cpp diff --git a/tests/test_DiscreteFunctionDPkVector.cpp b/tests/test_DiscreteFunctionDPkVector.cpp new file mode 100644 index 000000000..6ea028072 --- /dev/null +++ b/tests/test_DiscreteFunctionDPkVector.cpp @@ -0,0 +1,291 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <mesh/Mesh.hpp> +#include <scheme/DiscreteFunctionDPkVector.hpp> +#include <scheme/DiscreteFunctionP0Vector.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("DiscreteFunctionDPkVector", "[scheme]") +{ + SECTION("Basic interface") + { + const std::shared_ptr mesh_2d_v = MeshDataBaseForTests::get().cartesian2DMesh(); + const std::shared_ptr mesh_2d = mesh_2d_v->get<Mesh<2>>(); + + const std::shared_ptr mesh_2d_2_v = MeshDataBaseForTests::get().hybrid2DMesh(); + const std::shared_ptr mesh_2d_2 = mesh_2d_2_v->get<Mesh<2>>(); + + DiscreteFunctionDPkVector<2, const double> R_dkp; + +#ifndef NDEBUG + REQUIRE_THROWS_WITH(R_dkp(CellId(0), 0), "DiscreteFunctionDPkVector is not built"); +#endif // NDEBUG + + { + DiscreteFunctionDPkVector<2, double> tmp0{mesh_2d_v, 2, 3}; + DiscreteFunctionDPkVector<2, double> tmp_R_dkp(std::move(tmp0)); + tmp_R_dkp.fill(2); + R_dkp = tmp_R_dkp; + } + +#ifndef NDEBUG + REQUIRE_THROWS_WITH(R_dkp(CellId(0), 10), "incorrect component number"); +#endif // NDEBUG + + REQUIRE(R_dkp.degree() == 2); + REQUIRE(R_dkp.numberOfComponents() == 3); + REQUIRE(R_dkp.numberOfCoefficientsPerComponent() == + PolynomialCenteredCanonicalBasisView<2, double>::dimensionFromDegree(2)); + + REQUIRE(min(R_dkp.cellArrays()) == 2); + REQUIRE(max(R_dkp.cellArrays()) == 2); + + REQUIRE(R_dkp.dataType() == ast_node_data_type_from<double>); + REQUIRE(R_dkp.meshVariant()->id() == mesh_2d->id()); + + DiscreteFunctionDPkVector<2, double> R_dkp2; + R_dkp2 = copy(R_dkp); + + REQUIRE(R_dkp2.degree() == 2); + REQUIRE(R_dkp2.numberOfComponents() == 3); + REQUIRE(R_dkp2.numberOfCoefficientsPerComponent() == + PolynomialCenteredCanonicalBasisView<2, double>::dimensionFromDegree(2)); + + REQUIRE(min(R_dkp2.cellArrays()) == 2); + REQUIRE(max(R_dkp2.cellArrays()) == 2); + + REQUIRE(R_dkp2.dataType() == ast_node_data_type_from<double>); + REQUIRE(R_dkp2.meshVariant()->id() == mesh_2d_v->id()); + + DiscreteFunctionDPkVector<2, double> R_dkp3(mesh_2d_2_v, 1, 2); + R_dkp3.fill(3); + + REQUIRE(R_dkp3.degree() == 1); + REQUIRE(R_dkp3.numberOfComponents() == 2); + + REQUIRE(min(R_dkp3.cellArrays()) == 3); + REQUIRE(max(R_dkp3.cellArrays()) == 3); + + REQUIRE(R_dkp3.dataType() == ast_node_data_type_from<double>); + REQUIRE(R_dkp3.meshVariant()->id() == mesh_2d_2_v->id()); + + DiscreteFunctionDPkVector<2, double> R_dkp4(mesh_2d_v, R_dkp2.degree(), R_dkp2.numberOfComponents(), + R_dkp2.cellArrays()); + + REQUIRE(min(R_dkp4.cellArrays()) == 2); + REQUIRE(max(R_dkp4.cellArrays()) == 2); + + R_dkp4.fill(5); + REQUIRE(min(R_dkp4.cellArrays()) == 5); + REQUIRE(max(R_dkp4.cellArrays()) == 5); + REQUIRE(min(R_dkp2.cellArrays()) == 5); + REQUIRE(max(R_dkp2.cellArrays()) == 5); + + copy_to(R_dkp, R_dkp4); + REQUIRE(min(R_dkp4.cellArrays()) == 2); + REQUIRE(max(R_dkp4.cellArrays()) == 2); + REQUIRE(min(R_dkp2.cellArrays()) == 2); + REQUIRE(max(R_dkp2.cellArrays()) == 2); + +#ifndef NDEBUG + REQUIRE_THROWS_WITH(copy_to(R_dkp, R_dkp3), "copy_to target must use the same mesh"); + + DiscreteFunctionDPkVector<2, double> R_dkp5(mesh_2d, 3, 3); + REQUIRE_THROWS_WITH(copy_to(R_dkp, R_dkp5), "copy_to target must have the same degree"); + R_dkp5 = DiscreteFunctionDPkVector<2, double>{mesh_2d, 2, 1}; + REQUIRE_THROWS_WITH(copy_to(R_dkp, R_dkp5), "copy_to target must have the same number of components"); + + REQUIRE_THROWS_WITH((DiscreteFunctionDPkVector<2, double>{mesh_2d_2, 2, 3, R_dkp2.cellArrays()}), + "cell_array is built on different connectivity"); +#endif // NDEBUG + } + + SECTION("1D") + { + constexpr size_t Dimension = 1; + + using R1 = TinyVector<1>; + + const std::shared_ptr mesh_v = MeshDataBaseForTests::get().cartesian1DMesh(); + const std::shared_ptr mesh = mesh_v->get<Mesh<Dimension>>(); + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + const size_t degree = 4; + + DiscreteFunctionDPkVector<Dimension, double> pk(mesh_v, degree, 3); + + std::vector<double> a = {+1.0, +1.4, -6.2, +2.7, +3.1, // + +2.0, -2.3, +5.4, -2.7, -1.3, // + -1.2, +2.3, +3.1, +1.6, +2.3}; + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + auto coefficients = pk.coefficients(cell_id); + for (size_t i = 0; i < a.size(); ++i) { + coefficients[i] = a[i]; + } + }; + + const size_t number_of_coefs = pk.numberOfCoefficientsPerComponent(); + + for (size_t i = 0; i < pk.numberOfComponents(); ++i) { + DiscreteFunctionP0<double> p_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { p_xj[cell_id] = pk(cell_id, i)(xj[cell_id]); }); + + REQUIRE(max(p_xj) == a[i * number_of_coefs]); + REQUIRE(min(p_xj) == a[i * number_of_coefs]); + } + + for (size_t i = 0; i < pk.numberOfComponents(); ++i) { + DiscreteFunctionP0<double> delta(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const double x0 = 0.3 * Vj[cell_id]; + delta[cell_id] = pk(cell_id, i)(xj[cell_id] + R1{x0}) // + - (a[i * number_of_coefs] + // + x0 * a[i * number_of_coefs + 1] + // + x0 * x0 * a[i * number_of_coefs + 2] + // + x0 * x0 * x0 * a[i * number_of_coefs + 3] + // + x0 * x0 * x0 * x0 * a[i * number_of_coefs + 4]); + }); + + REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); + REQUIRE(min(delta) == Catch::Approx(0.).margin(1E-14)); + } + } + + SECTION("2D") + { + constexpr size_t Dimension = 2; + + using R2 = TinyVector<2>; + + const std::shared_ptr mesh_v = MeshDataBaseForTests::get().cartesian2DMesh(); + const std::shared_ptr mesh = mesh_v->get<Mesh<Dimension>>(); + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + const size_t degree = 3; + + DiscreteFunctionDPkVector<Dimension, double> pk(mesh_v, degree, 2); + + const std::vector<double> a = {+1.0, +1.4, -6.2, +3.5, -2.3, +5.2, +6.1, +2.3, +0.5, -1.3, // + -1.3, +2.3, +2.7, +1.7, +2.1, -2.7, -5.3, +1.2, +1.3, +3.2}; + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + auto coefficients = pk.coefficients(cell_id); + for (size_t i = 0; i < coefficients.size(); ++i) { + coefficients[i] = a[i]; + } + } + + const size_t number_of_coefs = pk.numberOfCoefficientsPerComponent(); + + for (size_t i = 0; i < pk.numberOfComponents(); ++i) { + DiscreteFunctionP0<double> p_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { p_xj[cell_id] = pk(cell_id, i)(xj[cell_id]); }); + + REQUIRE(max(p_xj) == a[i * number_of_coefs]); + REQUIRE(min(p_xj) == a[i * number_of_coefs]); + } + + for (size_t i = 0; i < pk.numberOfComponents(); ++i) { + DiscreteFunctionP0<double> error(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const double x0 = 0.2 * Vj[cell_id]; + const double y0 = 0.3 * Vj[cell_id]; + error[cell_id] // + = std::abs(pk(cell_id, i)(xj[cell_id] + R2{x0, y0}) // + - (a[i * number_of_coefs] // + + x0 * a[i * number_of_coefs + 1] // + + x0 * x0 * a[i * number_of_coefs + 2] // + + x0 * x0 * x0 * a[i * number_of_coefs + 3] // + + y0 * a[i * number_of_coefs + 4] // + + y0 * x0 * a[i * number_of_coefs + 5] // + + y0 * x0 * x0 * a[i * number_of_coefs + 6] // + + y0 * y0 * a[i * number_of_coefs + 7] // + + y0 * y0 * x0 * a[i * number_of_coefs + 8] // + + y0 * y0 * y0 * a[i * number_of_coefs + 9])); + }); + + REQUIRE(max(error) == Catch::Approx(0.).margin(1E-14)); + } + } + + SECTION("3D") + { + constexpr size_t Dimension = 3; + + using R3 = TinyVector<3>; + + const std::shared_ptr mesh_v = MeshDataBaseForTests::get().cartesian3DMesh(); + const std::shared_ptr mesh = mesh_v->get<Mesh<Dimension>>(); + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + const size_t degree = 3; + + DiscreteFunctionDPkVector<Dimension, double> pk(mesh_v, degree, 1); + + const std::vector<double> a = {+1.0, +1.4, -6.2, +3.5, -2.3, +5.2, +6.1, +2.3, +0.5, -1.3, // + +2.8, -8.4, +9.5, +4.0, +4.3, +7.2, -9.1, +6.8, +6.7, +9.2}; + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + auto coefficients = pk.coefficients(cell_id); + for (size_t i = 0; i < coefficients.size(); ++i) { + coefficients[i] = a[i]; + } + }); + + DiscreteFunctionP0<double> delta_xj(mesh_v); + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(CellId cell_id) { delta_xj[cell_id] = std::abs(pk(cell_id, 0)(xj[cell_id]) - a[0]); }); + + REQUIRE(max(delta_xj) == Catch::Approx(0.).margin(1E-14)); + + DiscreteFunctionP0<double> delta(mesh_v); + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + const double x0 = +0.5 * Vj[cell_id]; + const double y0 = +0.3 * Vj[cell_id]; + const double z0 = -0.4 * Vj[cell_id]; + + delta[cell_id] = pk(cell_id, 0)(xj[cell_id] + R3{x0, y0, z0}) // + - (a[0] // + + x0 * a[1] // + + x0 * x0 * a[2] // + + x0 * x0 * x0 * a[3] // + + y0 * a[4] // + + y0 * x0 * a[5] // + + y0 * x0 * x0 * a[6] // + + y0 * y0 * a[7] // + + y0 * y0 * x0 * a[8] // + + y0 * y0 * y0 * a[9] // + + z0 * a[10] // + + z0 * x0 * a[11] // + + z0 * x0 * x0 * a[12] // + + z0 * y0 * a[13] // + + z0 * y0 * x0 * a[14] // + + z0 * y0 * y0 * a[15] // + + z0 * z0 * a[16] // + + z0 * z0 * x0 * a[17] // + + z0 * z0 * y0 * a[18] // + + z0 * z0 * z0 * a[19] // + ); + }); + + REQUIRE(max(delta) == Catch::Approx(0.).margin(1E-14)); + REQUIRE(min(delta) == Catch::Approx(0.).margin(1E-14)); + } +} -- GitLab From 70558d9576558ce0c303a4807da878f263e0f587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 5 Jul 2024 19:09:07 +0200 Subject: [PATCH 037/122] Fix degree 1 reconstruction for discrete function p0 vectors Also add tests --- src/scheme/PolynomialReconstruction.cpp | 106 +++++---- tests/test_PolynomialReconstruction.cpp | 273 +++++++++++++++++++++++- 2 files changed, 339 insertions(+), 40 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index c770f5a31..2cc86d14a 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 123072266..029fa30e8 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"); } } -- GitLab From c7b40d4fa0f39f2d084bd0540d414e3a47fe74a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 8 Jul 2024 12:02:33 +0200 Subject: [PATCH 038/122] Use polynomial dimension from degree This is a first step toward polynomials of degree higher than 1 --- src/scheme/DiscreteFunctionDPk.hpp | 3 ++- src/scheme/PolynomialReconstruction.cpp | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPk.hpp b/src/scheme/DiscreteFunctionDPk.hpp index 0aa0e38e5..aacac5380 100644 --- a/src/scheme/DiscreteFunctionDPk.hpp +++ b/src/scheme/DiscreteFunctionDPk.hpp @@ -17,7 +17,8 @@ template <size_t Dimension, class DiscreteFunctionDPk { public: - using data_type = DataType; + using BasisViewType = BasisView; + using data_type = DataType; friend class DiscreteFunctionDPk<Dimension, std::add_const_t<DataType>>; friend class DiscreteFunctionDPk<Dimension, std::remove_const_t<DataType>>; diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 2cc86d14a..2e400c4e0 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -139,7 +139,10 @@ PolynomialReconstruction::_build( return n; }(); - const size_t degree = 1; + const size_t degree = 1; + const size_t basis_dimension = + DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(degree); + const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity()); auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); @@ -190,9 +193,9 @@ PolynomialReconstruction::_build( SmallArray<SmallMatrix<double>> X_pool(Kokkos::DefaultExecutionSpace::concurrency()); for (size_t i = 0; i < A_pool.size(); ++i) { - A_pool[i] = SmallMatrix<double>(max_stencil_size, MeshType::Dimension); + A_pool[i] = SmallMatrix<double>(max_stencil_size, basis_dimension - 1); B_pool[i] = SmallMatrix<double>(max_stencil_size, number_of_columns); - X_pool[i] = SmallMatrix<double>(MeshType::Dimension, number_of_columns); + X_pool[i] = SmallMatrix<double>(basis_dimension - 1, number_of_columns); } parallel_for( @@ -266,7 +269,7 @@ PolynomialReconstruction::_build( for (size_t i = 0; i < stencil_cell_list.size(); ++i) { const CellId cell_i_id = stencil_cell_list[i]; const Rd Xi_Xj = xj[cell_i_id] - Xj; - for (size_t l = 0; l < MeshType::Dimension; ++l) { + for (size_t l = 0; l < basis_dimension - 1; ++l) { A(i, l) = Xi_Xj[l]; } } @@ -295,13 +298,13 @@ PolynomialReconstruction::_build( dpk_j[0] = p0_function[cell_j_id]; if constexpr (std::is_arithmetic_v<DataType>) { - for (size_t i = 0; i < MeshType::Dimension; ++i) { + for (size_t i = 0; i < basis_dimension - 1; ++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) { + for (size_t i = 0; i < basis_dimension - 1; ++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); @@ -309,7 +312,7 @@ PolynomialReconstruction::_build( } column_begin += DataType::Dimension; } else if constexpr (is_tiny_matrix_v<DataType>) { - for (size_t i = 0; i < MeshType::Dimension; ++i) { + for (size_t i = 0; i < basis_dimension - 1; ++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) { @@ -332,13 +335,13 @@ PolynomialReconstruction::_build( 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; + const size_t size = basis_dimension; 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) { + for (size_t i = 0; i < basis_dimension - 1; ++i) { auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; dpk_j_ip1 = X(i, column_begin); } -- GitLab From f2e4b332e3152f609289b19f222af5e267868ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 8 Jul 2024 12:48:03 +0200 Subject: [PATCH 039/122] Begin code clean-up and set degree of reconstruction as a parameter Other degrees than 1 are still not implemented --- src/scheme/DiscreteFunctionDPkVariant.hpp | 108 ++ src/scheme/PolynomialReconstruction.cpp | 11 +- src/scheme/PolynomialReconstruction.hpp | 118 +- src/scheme/test_reconstruction.cpp | 14 +- tests/test_PolynomialReconstruction.cpp | 1323 +++++++++++---------- 5 files changed, 802 insertions(+), 772 deletions(-) create mode 100644 src/scheme/DiscreteFunctionDPkVariant.hpp diff --git a/src/scheme/DiscreteFunctionDPkVariant.hpp b/src/scheme/DiscreteFunctionDPkVariant.hpp new file mode 100644 index 000000000..0d683c178 --- /dev/null +++ b/src/scheme/DiscreteFunctionDPkVariant.hpp @@ -0,0 +1,108 @@ +#ifndef DISCRETE_FUNCTION_DPK_VARIANT_HPP +#define DISCRETE_FUNCTION_DPK_VARIANT_HPP + +#include <scheme/DiscreteFunctionDPk.hpp> +#include <scheme/DiscreteFunctionDPkVector.hpp> + +#include <memory> +#include <variant> + +class DiscreteFunctionDPkVariant +{ + public: + using Variant = std::variant<DiscreteFunctionDPk<1, const double>, + DiscreteFunctionDPk<1, const TinyVector<1>>, + DiscreteFunctionDPk<1, const TinyVector<2>>, + DiscreteFunctionDPk<1, const TinyVector<3>>, + DiscreteFunctionDPk<1, const TinyMatrix<1>>, + DiscreteFunctionDPk<1, const TinyMatrix<2>>, + DiscreteFunctionDPk<1, const TinyMatrix<3>>, + + DiscreteFunctionDPk<2, const double>, + DiscreteFunctionDPk<2, const TinyVector<1>>, + DiscreteFunctionDPk<2, const TinyVector<2>>, + DiscreteFunctionDPk<2, const TinyVector<3>>, + DiscreteFunctionDPk<2, const TinyMatrix<1>>, + DiscreteFunctionDPk<2, const TinyMatrix<2>>, + DiscreteFunctionDPk<2, const TinyMatrix<3>>, + + DiscreteFunctionDPk<3, const double>, + DiscreteFunctionDPk<3, const TinyVector<1>>, + DiscreteFunctionDPk<3, const TinyVector<2>>, + DiscreteFunctionDPk<3, const TinyVector<3>>, + DiscreteFunctionDPk<3, const TinyMatrix<1>>, + DiscreteFunctionDPk<3, const TinyMatrix<2>>, + DiscreteFunctionDPk<3, const TinyMatrix<3>>, + + DiscreteFunctionDPkVector<1, const double>, + DiscreteFunctionDPkVector<2, const double>, + DiscreteFunctionDPkVector<3, const double>>; + + private: + Variant m_discrete_function_dpk; + + public: + PUGS_INLINE + const Variant& + discreteFunctionDPk() const + { + return m_discrete_function_dpk; + } + + template <typename DiscreteFunctionDPkT> + PUGS_INLINE auto + get() const + { + static_assert(is_discrete_function_dpk_v<DiscreteFunctionDPkT>, "invalid template argument"); + using DataType = typename DiscreteFunctionDPkT::data_type; + static_assert(std::is_const_v<DataType>, "data type of extracted discrete function must be const"); + +#ifndef NDEBUG + if (not std::holds_alternative<DiscreteFunctionDPkT>(this->m_discrete_function_dpk)) { + std::ostringstream error_msg; + error_msg << "invalid discrete function type\n"; + error_msg << "- required " << rang::fgB::red << demangle<DiscreteFunctionDPkT>() << rang::fg::reset << '\n'; + error_msg << "- contains " << rang::fgB::yellow + << std::visit([](auto&& f) -> std::string { return demangle<decltype(f)>(); }, + this->m_discrete_function_dpk) + << rang::fg::reset; + throw NormalError(error_msg.str()); + } +#endif // NDEBUG + + return std::get<DiscreteFunctionDPkT>(this->discreteFunctionDPk()); + } + + template <size_t Dimension, typename DataType> + DiscreteFunctionDPkVariant(const DiscreteFunctionDPk<Dimension, DataType>& discrete_function_dpk) + : m_discrete_function_dpk{DiscreteFunctionDPk<Dimension, const DataType>{discrete_function_dpk}} + { + static_assert(std::is_same_v<std::remove_const_t<DataType>, double> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<3, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<1, 1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<2, 2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<3, 3, double>>, + "DiscreteFunctionDPk with this DataType is not allowed in variant"); + } + + template <size_t Dimension, typename DataType> + DiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) + : m_discrete_function_dpk{DiscreteFunctionDPkVector<Dimension, const DataType>{discrete_function_dpk}} + { + static_assert(std::is_same_v<std::remove_const_t<DataType>, double>, + "DiscreteFunctionDPkVector with this DataType is not allowed in variant"); + } + + DiscreteFunctionDPkVariant& operator=(DiscreteFunctionDPkVariant&&) = default; + DiscreteFunctionDPkVariant& operator=(const DiscreteFunctionDPkVariant&) = default; + + DiscreteFunctionDPkVariant(const DiscreteFunctionDPkVariant&) = default; + DiscreteFunctionDPkVariant(DiscreteFunctionDPkVariant&&) = default; + + DiscreteFunctionDPkVariant() = delete; + ~DiscreteFunctionDPkVariant() = default; +}; + +#endif // DISCRETE_FUNCTION_DPK_VARIANT_HPP diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 2e400c4e0..0e10c1bf8 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -3,7 +3,7 @@ #include <scheme/DiscreteFunctionUtils.hpp> #include <scheme/DiscreteFunctionVariant.hpp> -class MutableDiscreteFunctionDPkVariant +class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant { public: using Variant = std::variant<DiscreteFunctionDPk<1, double>, @@ -102,9 +102,14 @@ class MutableDiscreteFunctionDPkVariant template <MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::_build( + const size_t degree, const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const { + if (degree != 1) { + throw NotImplementedError("only degree 1 is available"); + } + const MeshType& mesh = *p_mesh; using Rd = TinyVector<MeshType::Dimension>; @@ -139,7 +144,6 @@ PolynomialReconstruction::_build( return n; }(); - const size_t degree = 1; const size_t basis_dimension = DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(degree); @@ -392,6 +396,7 @@ PolynomialReconstruction::_build( std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::build( + const size_t degree, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const { if (not hasSameMesh(discrete_function_variant_list)) { @@ -400,6 +405,6 @@ PolynomialReconstruction::build( auto mesh_v = getCommonMesh(discrete_function_variant_list); - return std::visit([&](auto&& p_mesh) { return this->_build(p_mesh, discrete_function_variant_list); }, + return std::visit([&](auto&& p_mesh) { return this->_build(degree, p_mesh, discrete_function_variant_list); }, mesh_v->variant()); } diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index 430ce0234..454e77c9d 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -1,16 +1,14 @@ #ifndef POLYNOMIAL_RECONSTRUCTION_HPP #define POLYNOMIAL_RECONSTRUCTION_HPP -#include <mesh/MeshTraits.hpp> -#include <mesh/StencilManager.hpp> -#include <scheme/DiscreteFunctionDPk.hpp> -#include <scheme/DiscreteFunctionDPkVector.hpp> - #include <algebra/ShrinkMatrixView.hpp> #include <algebra/ShrinkVectorView.hpp> #include <algebra/SmallMatrix.hpp> #include <algebra/SmallVector.hpp> +#include <mesh/MeshTraits.hpp> +#include <scheme/DiscreteFunctionDPkVariant.hpp> +#warning MOVE TO .cpp WHEN FINISHED #include <algebra/Givens.hpp> #include <analysis/GaussLegendreQuadratureDescriptor.hpp> #include <analysis/QuadratureFormula.hpp> @@ -18,116 +16,19 @@ #include <geometry/LineTransformation.hpp> #include <mesh/MeshData.hpp> #include <mesh/MeshDataManager.hpp> +#include <mesh/StencilManager.hpp> class DiscreteFunctionDPkVariant; class DiscreteFunctionVariant; -#include <memory> -#include <variant> - -class DiscreteFunctionDPkVariant -{ - public: - using Variant = std::variant<DiscreteFunctionDPk<1, const double>, - DiscreteFunctionDPk<1, const TinyVector<1>>, - DiscreteFunctionDPk<1, const TinyVector<2>>, - DiscreteFunctionDPk<1, const TinyVector<3>>, - DiscreteFunctionDPk<1, const TinyMatrix<1>>, - DiscreteFunctionDPk<1, const TinyMatrix<2>>, - DiscreteFunctionDPk<1, const TinyMatrix<3>>, - - DiscreteFunctionDPk<2, const double>, - DiscreteFunctionDPk<2, const TinyVector<1>>, - DiscreteFunctionDPk<2, const TinyVector<2>>, - DiscreteFunctionDPk<2, const TinyVector<3>>, - DiscreteFunctionDPk<2, const TinyMatrix<1>>, - DiscreteFunctionDPk<2, const TinyMatrix<2>>, - DiscreteFunctionDPk<2, const TinyMatrix<3>>, - - DiscreteFunctionDPk<3, const double>, - DiscreteFunctionDPk<3, const TinyVector<1>>, - DiscreteFunctionDPk<3, const TinyVector<2>>, - DiscreteFunctionDPk<3, const TinyVector<3>>, - DiscreteFunctionDPk<3, const TinyMatrix<1>>, - DiscreteFunctionDPk<3, const TinyMatrix<2>>, - DiscreteFunctionDPk<3, const TinyMatrix<3>>, - - DiscreteFunctionDPkVector<1, const double>, - DiscreteFunctionDPkVector<2, const double>, - DiscreteFunctionDPkVector<3, const double>>; - - private: - Variant m_discrete_function_dpk; - - public: - PUGS_INLINE - const Variant& - discreteFunctionDPk() const - { - return m_discrete_function_dpk; - } - - template <typename DiscreteFunctionDPkT> - PUGS_INLINE auto - get() const - { - static_assert(is_discrete_function_dpk_v<DiscreteFunctionDPkT>, "invalid template argument"); - using DataType = typename DiscreteFunctionDPkT::data_type; - static_assert(std::is_const_v<DataType>, "data type of extracted discrete function must be const"); - -#ifndef NDEBUG - if (not std::holds_alternative<DiscreteFunctionDPkT>(this->m_discrete_function_dpk)) { - std::ostringstream error_msg; - error_msg << "invalid discrete function type\n"; - error_msg << "- required " << rang::fgB::red << demangle<DiscreteFunctionDPkT>() << rang::fg::reset << '\n'; - error_msg << "- contains " << rang::fgB::yellow - << std::visit([](auto&& f) -> std::string { return demangle<decltype(f)>(); }, - this->m_discrete_function_dpk) - << rang::fg::reset; - throw NormalError(error_msg.str()); - } -#endif // NDEBUG - - return std::get<DiscreteFunctionDPkT>(this->discreteFunctionDPk()); - } - - template <size_t Dimension, typename DataType> - DiscreteFunctionDPkVariant(const DiscreteFunctionDPk<Dimension, DataType>& discrete_function_dpk) - : m_discrete_function_dpk{DiscreteFunctionDPk<Dimension, const DataType>{discrete_function_dpk}} - { - static_assert(std::is_same_v<std::remove_const_t<DataType>, double> or // - std::is_same_v<std::remove_const_t<DataType>, TinyVector<1, double>> or // - std::is_same_v<std::remove_const_t<DataType>, TinyVector<2, double>> or // - std::is_same_v<std::remove_const_t<DataType>, TinyVector<3, double>> or // - std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<1, 1, double>> or // - std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<2, 2, double>> or // - std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<3, 3, double>>, - "DiscreteFunctionDPk with this DataType is not allowed in variant"); - } - - template <size_t Dimension, typename DataType> - DiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) - : m_discrete_function_dpk{DiscreteFunctionDPkVector<Dimension, const DataType>{discrete_function_dpk}} - { - static_assert(std::is_same_v<std::remove_const_t<DataType>, double>, - "DiscreteFunctionDPkVector with this DataType is not allowed in variant"); - } - - DiscreteFunctionDPkVariant& operator=(DiscreteFunctionDPkVariant&&) = default; - DiscreteFunctionDPkVariant& operator=(const DiscreteFunctionDPkVariant&) = default; - - DiscreteFunctionDPkVariant(const DiscreteFunctionDPkVariant&) = default; - DiscreteFunctionDPkVariant(DiscreteFunctionDPkVariant&&) = default; - - DiscreteFunctionDPkVariant() = delete; - ~DiscreteFunctionDPkVariant() = default; -}; - class PolynomialReconstruction { private: + class MutableDiscreteFunctionDPkVariant; + template <MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> _build( + const size_t degree, const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; @@ -332,11 +233,12 @@ class PolynomialReconstruction } [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> build( + const size_t degree, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; template <typename... DiscreteFunctionT> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> - build(DiscreteFunctionT... input) const + build(const size_t degree, DiscreteFunctionT... input) const { std::vector<std::shared_ptr<const DiscreteFunctionVariant>> variant_vector; auto convert = [&variant_vector](auto&& df) { @@ -356,7 +258,7 @@ class PolynomialReconstruction }; (convert(std::forward<DiscreteFunctionT>(input)), ...); - return this->build(variant_vector); + return this->build(degree, variant_vector); } PolynomialReconstruction() {} diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index 08527f96d..87b9bcc16 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -55,9 +55,8 @@ test_reconstruction_one(const std::shared_ptr<const MeshVariant>& mesh_v, void test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) { - std::cout << "** variable list one by one (30 times)\n"; - { + std::cout << "** variable list one by one (50 times)\n"; Timer t; for (auto discrete_function_v : discrete_function_variant_list) { std::visit( @@ -68,9 +67,11 @@ test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVari using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; PolynomialReconstruction reconstruction; if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - for (size_t i = 0; i < 30; ++i) { + for (size_t i = 0; i < 50; ++i) { auto res = reconstruction.build(*p_mesh, discrete_function); } + } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { + std::cout << "Omitting discrete function p0 vector!\n"; } }, mesh_v->variant()); @@ -81,13 +82,12 @@ test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVari std::cout << "t = " << t << '\n'; } - std::cout << "** variable list at once (30 times)\n"; - { + std::cout << "** variable list at once (50 times)\n"; PolynomialReconstruction reconstruction; Timer t; - for (size_t i = 0; i < 30; ++i) { - auto res = reconstruction.build(discrete_function_variant_list); + for (size_t i = 0; i < 50; ++i) { + auto res = reconstruction.build(1, discrete_function_variant_list); } t.pause(); std::cout << "t = " << t << '\n'; diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 029fa30e8..7ce59a910 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -21,930 +21,945 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { - SECTION("1D") + SECTION("degree 1") { - using R1 = TinyVector<1>; - - SECTION("R data") + SECTION("1D") { - for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); - auto& mesh = *p_mesh; + 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 R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - DiscreteFunctionP0<double> fh{p_mesh}; + DiscreteFunctionP0<double> fh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(fh); + auto reconstructions = PolynomialReconstruction{}.build(1, fh); - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + auto dpk_fh = 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + { + 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } - 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_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2; + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2; - max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); + } + REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14)); } } } - } - SECTION("R^3 data") - { - using R3 = TinyVector<3>; + SECTION("R^3 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; + 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 R3_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(); + auto R3_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> uh{p_mesh}; + DiscreteFunctionP0<R3> uh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(uh); + auto reconstructions = PolynomialReconstruction{}.build(1, uh); - auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); - { - 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + { + 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } - 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_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R1{0.1})); + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = + (1 / 0.2) * (dpk_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[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})); + 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)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14)); } } } - } - SECTION("R^3x3 data") - { - using R3x3 = TinyMatrix<3, 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 R3x3_affine = [](const R1& x) -> R3x3 { - return R3x3{ - +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], - -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], + SECTION("R^3x3 data") + { + using R3x3 = TinyMatrix<3, 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 R3x3_affine = [](const R1& x) -> R3x3 { + return R3x3{ + +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], + -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], + }; }; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - DiscreteFunctionP0<R3x3> Ah{p_mesh}; + DiscreteFunctionP0<R3x3> Ah{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(Ah); + auto reconstructions = PolynomialReconstruction{}.build(1, Ah); - auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); - { - 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_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); + { + 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_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); } - 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 R3x3 reconstructed_slope = - (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3x3 reconstructed_slope = + (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); - R3x3 slops = R3x3{+1.7, +2.1, -0.6, // - -2.3, +3.1, -3.6, // - +3.1, +2.9, +2.3}; + R3x3 slops = R3x3{+1.7, +2.1, -0.6, // + -2.3, +3.1, -3.6, // + +3.1, +2.9, +2.3}; - max_slope_error = std::max(max_slope_error, // - frobeniusNorm(reconstructed_slope - slops)); + max_slope_error = std::max(max_slope_error, // + frobeniusNorm(reconstructed_slope - slops)); + } + REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); } } } - } - SECTION("R vector data") - { - using R3 = TinyVector<3>; + 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; + 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(); + 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}; + 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]; - } - }); + 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 reconstructions = PolynomialReconstruction{}.build(1, Vh); - auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); + 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])); + { + 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)); } - 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}; + { + 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})); + 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])); + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); + } + REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); } } } } - } - SECTION("list of various types") - { - using R3x3 = TinyMatrix<3>; - 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 R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; - - auto R3_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 R3x3_affine = [](const R1& x) -> R3x3 { - return R3x3{ - +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], - -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], + SECTION("list of various types") + { + using R3x3 = TinyMatrix<3>; + 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 R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; + + auto R3_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 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 R3x3_affine = [](const R1& x) -> R3x3 { + return R3x3{ + +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], + -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], + }; + }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + 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]}; + }; - DiscreteFunctionP0<double> fh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - DiscreteFunctionP0<R3> uh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + DiscreteFunctionP0<double> fh{p_mesh}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - DiscreteFunctionP0<R3x3> Ah{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); + DiscreteFunctionP0<R3> uh{p_mesh}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_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]; - } - }); + DiscreteFunctionP0<R3x3> Ah{p_mesh}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(std::make_shared<DiscreteFunctionVariant>(fh), uh, - std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), - DiscreteFunctionVariant(Vh)); + 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 dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + auto reconstructions = PolynomialReconstruction{}.build(1, std::make_shared<DiscreteFunctionVariant>(fh), + uh, std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), + DiscreteFunctionVariant(Vh)); - { - 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + auto dpk_fh = 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } - 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_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2; + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2; - max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); + } + REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14)); - } - auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); + auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); - { - 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + { + 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } - 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_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R1{0.1})); + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = + (1 / 0.2) * (dpk_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[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})); + 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)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14)); - } - auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); + auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); - { - 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_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); + { + 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_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); } - 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 R3x3 reconstructed_slope = - (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3x3 reconstructed_slope = + (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); - R3x3 slops = R3x3{+1.7, +2.1, -0.6, // - -2.3, +3.1, -3.6, // - +3.1, +2.9, +2.3}; + R3x3 slops = R3x3{+1.7, +2.1, -0.6, // + -2.3, +3.1, -3.6, // + +3.1, +2.9, +2.3}; - max_slope_error = std::max(max_slope_error, // - frobeniusNorm(reconstructed_slope - slops)); + max_slope_error = std::max(max_slope_error, // + frobeniusNorm(reconstructed_slope - slops)); + } + REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); - } - auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); + 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])); + { + 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)); } - 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}; + { + 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})); + 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])); + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); + } + REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); } } } } } - } - - SECTION("2D") - { - using R2 = TinyVector<2>; - SECTION("R data") + SECTION("2D") { - for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); - auto& mesh = *p_mesh; + using R2 = TinyVector<2>; - auto R_affine = [](const R2& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1]; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + 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; - DiscreteFunctionP0<double> fh{p_mesh}; + auto R_affine = [](const R2& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1]; }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + DiscreteFunctionP0<double> fh{p_mesh}; - auto reconstructions = PolynomialReconstruction{}.build(fh); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + auto reconstructions = PolynomialReconstruction{}.build(1, 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } - 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_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0.1, 0})) / 0.2; + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[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)); + 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)); } - 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_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0, 0.1})) / 0.2; + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[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))); + 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)); } - REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-14)); } } } - } - SECTION("R^3 data") - { - using R3 = TinyVector<3>; + SECTION("R^3 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; + 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 R3_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(); + auto R3_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> uh{p_mesh}; + DiscreteFunctionP0<R3> uh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(uh); + auto reconstructions = PolynomialReconstruction{}.build(1, uh); - auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); - { - 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + { + 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } - 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_uh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R2{0.1, 0})); + { + 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_uh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_uh[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})); + 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)); } - 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_uh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R2{0, 0.1})); + { + 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_uh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_uh[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})); + 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)); } - REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-14)); } } } - } - SECTION("R^2x2 data") - { - using R2x2 = TinyMatrix<2, 2>; + SECTION("R^2x2 data") + { + using R2x2 = TinyMatrix<2, 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; + 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 R2x2_affine = [](const R2& x) -> R2x2 { - return R2x2{+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(); + auto R2x2_affine = [](const R2& x) -> R2x2 { + return R2x2{+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(); - DiscreteFunctionP0<R2x2> Ah{p_mesh}; + DiscreteFunctionP0<R2x2> Ah{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(Ah); + auto reconstructions = PolynomialReconstruction{}.build(1, Ah); - auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); - { - 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_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); + { + 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_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); } - 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 R2x2 reconstructed_slope = - (1 / 0.2) * (dpk_Ah[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R2{0.1, 0})); + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = + (1 / 0.2) * (dpk_Ah[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R2{0.1, 0})); - max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // - -0.6, -2.3})); + max_x_slope_error = + std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // + -0.6, -2.3})); + } + REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12)); } - 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 R2x2 reconstructed_slope = - (1 / 0.2) * (dpk_Ah[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R2{0, 0.1})); + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = + (1 / 0.2) * (dpk_Ah[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R2{0, 0.1})); - max_y_slope_error = std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // - -2.1, +1.3})); + max_y_slope_error = + std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + -2.1, +1.3})); + } + REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12)); } - REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12)); } } } - } - SECTION("vector data") - { - using R4 = TinyVector<4>; + 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; + 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(); + 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}; + 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]; - } - }); + 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 reconstructions = PolynomialReconstruction{}.build(1, Vh); - auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + 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])); + { + 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)); } - 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})); + { + 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])); + 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)); } - 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})); + { + 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])); + 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)); } - REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12)); } } } } - } - - SECTION("3D") - { - using R3 = TinyVector<3>; - SECTION("R data") + SECTION("3D") { - for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); - auto& mesh = *p_mesh; + using R3 = TinyVector<3>; - auto R_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(); + 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; - DiscreteFunctionP0<double> fh{p_mesh}; + auto R_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(); - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + DiscreteFunctionP0<double> fh{p_mesh}; - auto reconstructions = PolynomialReconstruction{}.build(fh); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + auto reconstructions = PolynomialReconstruction{}.build(1, 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } - 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_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0.1, 0, 0})) / 0.2; + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[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)); + 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)); } - 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_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0.1, 0})) / 0.2; + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[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))); + 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)); } - 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_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0, 0.1})) / 0.2; + { + double max_z_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[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)); + 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)); } - REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-14)); } } } - } - SECTION("R^3 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; + SECTION("R^3 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 R3_affine = [](const R3& x) -> R3 { - return R3{+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]}; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + auto R3_affine = [](const R3& x) -> R3 { + return R3{+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]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - DiscreteFunctionP0<R3> uh{p_mesh}; + DiscreteFunctionP0<R3> uh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(uh); + auto reconstructions = PolynomialReconstruction{}.build(1, uh); - auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); - { - 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + { + 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); } - 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_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + { + 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_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); + 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-13)); } - 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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + { + 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_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.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-13)); } - 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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + { + double max_z_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9})); + max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9})); + } + REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-13)); } - REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-13)); } } } - } - SECTION("R^2x2 data") - { - using R2x2 = TinyMatrix<2, 2>; + SECTION("R^2x2 data") + { + using R2x2 = TinyMatrix<2, 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; + 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 R2x2_affine = [](const R3& x) -> R2x2 { - return R2x2{+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(); + auto R2x2_affine = [](const R3& x) -> R2x2 { + return R2x2{+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(); - DiscreteFunctionP0<R2x2> Ah{p_mesh}; + DiscreteFunctionP0<R2x2> Ah{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(Ah); + auto reconstructions = PolynomialReconstruction{}.build(1, Ah); - auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); - { - 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_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); + { + 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_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); + } + REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); } - 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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // - -2.3, +3.1})); + max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // + -2.3, +3.1})); + } + REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12)); } - 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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - max_y_slope_error = std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // - 1.3, +0.8})); + max_y_slope_error = + std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + 1.3, +0.8})); + } + REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12)); } - 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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + { + double max_z_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - max_z_slope_error = std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // - +1.4, -1.8})); + max_z_slope_error = + std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // + +1.4, -1.8})); + } + REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-12)); } - REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-12)); } } } - } - SECTION("vector data") - { - using R4 = TinyVector<4>; + 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; + 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(); + 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}; + 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]; - } - }); + 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 reconstructions = PolynomialReconstruction{}.build(1, Vh); - auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); + 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])); + { + 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)); } - 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})); + { + 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])); + 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)); } - 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})); + { + 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])); + 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)); } - 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})); + { + 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])); + 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)); } - 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}; + 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}; + 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"); + REQUIRE_THROWS_WITH(PolynomialReconstruction{}.build(1, f1, f2), + "error: cannot reconstruct functions living of different meshes simultaneously"); + } } } -- GitLab From 32220435e1b20f3ffba1044d72b199eae63d1aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 8 Jul 2024 14:23:52 +0200 Subject: [PATCH 040/122] Add row weighting for least-square resolution The idea is to add weight for the values that are provided by cells that are close to the reconstruction --- src/scheme/PolynomialReconstruction.cpp | 11 +++++++++ src/scheme/PolynomialReconstruction.hpp | 31 +++++++++++++++++++++++++ tests/test_PolynomialReconstruction.cpp | 2 +- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 0e10c1bf8..788c6df64 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -278,6 +278,17 @@ PolynomialReconstruction::_build( } } + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(i, l) *= weight; + } + for (size_t l = 0; l < number_of_columns; ++l) { + B(i, l) *= weight; + } + } + const SmallMatrix<double>& X = X_pool[t]; Givens::solveCollectionInPlace(A, X, B); diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index 454e77c9d..78f7bc5ba 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -100,6 +100,15 @@ class PolynomialReconstruction } } + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); + for (size_t l = 0; l < MeshType::Dimension; ++l) { + A(i, l) *= weight; + } + b[i] *= weight; + } + SmallVector<double> x(MeshType::Dimension); Givens::solveInPlace(A, x, b); @@ -150,6 +159,17 @@ class PolynomialReconstruction } } + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); + for (size_t l = 0; l < MeshType::Dimension - 1; ++l) { + A(i, l) *= weight; + } + for (size_t l = 0; l < DataType::Dimension; ++l) { + B(i, l) *= weight; + } + } + TinyMatrix<MeshType::Dimension, DataType::Dimension> X; Givens::solveCollectionInPlace(A, X, B); @@ -205,6 +225,17 @@ class PolynomialReconstruction } } + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); + for (size_t l = 0; l < MeshType::Dimension - 1; ++l) { + A(i, l) *= weight; + } + for (size_t l = 0; l < DataType::Dimension; ++l) { + B(i, l) *= weight; + } + } + TinyMatrix<MeshType::Dimension, DataType::Dimension> X; Givens::solveCollectionInPlace(A, X, B); diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 7ce59a910..e019cc016 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -506,7 +506,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-13)); } } } -- GitLab From 0adccd02c00ae4407ee09e65c4e3d5855a94941d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 8 Jul 2024 16:26:26 +0200 Subject: [PATCH 041/122] Add column weighting as a preconditioner for LS problems Also add pseudo-options for preconditioning and row weighting --- src/scheme/PolynomialReconstruction.cpp | 67 ++++++++++--- src/scheme/PolynomialReconstruction.hpp | 9 +- src/scheme/test_reconstruction.cpp | 2 +- tests/test_PolynomialReconstruction.cpp | 121 ++++++++++++------------ 4 files changed, 125 insertions(+), 74 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 788c6df64..e3fe01930 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -103,6 +103,8 @@ template <MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::_build( const size_t degree, + const bool preconditioning, + const bool row_weighting, const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const { @@ -194,11 +196,13 @@ PolynomialReconstruction::_build( SmallArray<SmallMatrix<double>> A_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallMatrix<double>> B_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallArray<double>> G_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallMatrix<double>> X_pool(Kokkos::DefaultExecutionSpace::concurrency()); for (size_t i = 0; i < A_pool.size(); ++i) { A_pool[i] = SmallMatrix<double>(max_stencil_size, basis_dimension - 1); B_pool[i] = SmallMatrix<double>(max_stencil_size, number_of_columns); + G_pool[i] = SmallArray<double>(basis_dimension - 1); X_pool[i] = SmallMatrix<double>(basis_dimension - 1, number_of_columns); } @@ -278,19 +282,55 @@ PolynomialReconstruction::_build( } } - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(i, l) *= weight; - } - for (size_t l = 0; l < number_of_columns; ++l) { - B(i, l) *= weight; + if (row_weighting) { + // Add row weighting (give more importance to cells that are + // closer to j) + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(i, l) *= weight; + } + for (size_t l = 0; l < number_of_columns; ++l) { + B(i, l) *= weight; + } } } const SmallMatrix<double>& X = X_pool[t]; - Givens::solveCollectionInPlace(A, X, B); + + if (preconditioning) { + // Add column weighting preconditioning (increase the presition) + SmallArray<double>& G = G_pool[t]; + + for (size_t l = 0; l < basis_dimension - 1; ++l) { + double g = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const double Ail = A(i, l); + + g += Ail * Ail; + } + G[l] = std::sqrt(g); + } + + for (size_t l = 0; l < basis_dimension - 1; ++l) { + const double Gl = G[l]; + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + A(i, l) *= Gl; + } + } + + Givens::solveCollectionInPlace(A, X, B); + + for (size_t l = 0; l < basis_dimension - 1; ++l) { + const double Gl = G[l]; + for (size_t i = 0; i < number_of_columns; ++i) { + X(l, i) *= Gl; + } + } + } else { + Givens::solveCollectionInPlace(A, X, B); + } column_begin = 0; for (size_t i_dpk_variant = 0; i_dpk_variant < mutable_discrete_function_dpk_variant_list.size(); @@ -408,6 +448,8 @@ PolynomialReconstruction::_build( std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::build( const size_t degree, + const bool preconditioning, + const bool row_weighting, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const { if (not hasSameMesh(discrete_function_variant_list)) { @@ -416,6 +458,9 @@ PolynomialReconstruction::build( auto mesh_v = getCommonMesh(discrete_function_variant_list); - return std::visit([&](auto&& p_mesh) { return this->_build(degree, p_mesh, discrete_function_variant_list); }, - mesh_v->variant()); + return std::visit( + [&](auto&& p_mesh) { + return this->_build(degree, preconditioning, row_weighting, p_mesh, discrete_function_variant_list); + }, + mesh_v->variant()); } diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index 78f7bc5ba..74bb82785 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -29,6 +29,8 @@ class PolynomialReconstruction template <MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> _build( const size_t degree, + const bool preconditioning, + const bool row_weighting, const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; @@ -265,11 +267,14 @@ class PolynomialReconstruction [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> build( const size_t degree, + const bool preconditioning, + const bool row_weighting, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; +#warning add reconstruction descriptor/option to handle the options to be used for reconstruction template <typename... DiscreteFunctionT> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> - build(const size_t degree, DiscreteFunctionT... input) const + build(const size_t degree, const bool preconditioning, const bool row_weighting, DiscreteFunctionT... input) const { std::vector<std::shared_ptr<const DiscreteFunctionVariant>> variant_vector; auto convert = [&variant_vector](auto&& df) { @@ -289,7 +294,7 @@ class PolynomialReconstruction }; (convert(std::forward<DiscreteFunctionT>(input)), ...); - return this->build(degree, variant_vector); + return this->build(degree, preconditioning, row_weighting, variant_vector); } PolynomialReconstruction() {} diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index 87b9bcc16..1267aabec 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -87,7 +87,7 @@ test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVari PolynomialReconstruction reconstruction; Timer t; for (size_t i = 0; i < 50; ++i) { - auto res = reconstruction.build(1, discrete_function_variant_list); + auto res = reconstruction.build(1, true, true, discrete_function_variant_list); } t.pause(); std::cout << "t = " << t << '\n'; diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index e019cc016..ada21453a 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -43,7 +43,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, fh); + auto reconstructions = PolynomialReconstruction{}.build(1, true, true, fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); @@ -53,7 +53,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -64,7 +64,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -92,7 +92,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, uh); + auto reconstructions = PolynomialReconstruction{}.build(1, true, true, uh); auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); @@ -102,7 +102,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -113,7 +113,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -143,7 +143,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, Ah); + auto reconstructions = PolynomialReconstruction{}.build(1, true, true, Ah); auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); @@ -153,7 +153,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); } { @@ -169,7 +169,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_slope_error = std::max(max_slope_error, // frobeniusNorm(reconstructed_slope - slops)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); } } } @@ -200,7 +200,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } }); - auto reconstructions = PolynomialReconstruction{}.build(1, Vh); + auto reconstructions = PolynomialReconstruction{}.build(1, true, true, Vh); auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); @@ -212,7 +212,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); } { @@ -226,8 +226,8 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); } } } @@ -287,9 +287,10 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } }); - auto reconstructions = PolynomialReconstruction{}.build(1, std::make_shared<DiscreteFunctionVariant>(fh), - uh, std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), - DiscreteFunctionVariant(Vh)); + auto reconstructions = + PolynomialReconstruction{}.build(1, true, true, std::make_shared<DiscreteFunctionVariant>(fh), uh, + std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), + DiscreteFunctionVariant(Vh)); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); @@ -299,7 +300,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -310,7 +311,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); } auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); @@ -321,7 +322,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -332,7 +333,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); } auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); @@ -343,7 +344,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); } { @@ -359,7 +360,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_slope_error = std::max(max_slope_error, // frobeniusNorm(reconstructed_slope - slops)); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); } auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); @@ -372,7 +373,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); } { @@ -386,8 +387,8 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); } - REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13)); } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); } } } @@ -414,7 +415,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, fh); + auto reconstructions = PolynomialReconstruction{}.build(1, true, true, fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); @@ -424,7 +425,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -435,7 +436,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-14)); } { @@ -446,7 +447,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -474,7 +475,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, uh); + auto reconstructions = PolynomialReconstruction{}.build(1, true, true, uh); auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); @@ -484,7 +485,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -495,7 +496,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-14)); } { @@ -506,7 +507,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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-13)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -533,7 +534,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, Ah); + auto reconstructions = PolynomialReconstruction{}.build(1, true, true, Ah); auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); @@ -543,7 +544,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); } { @@ -556,7 +557,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // -0.6, -2.3})); } - REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); } { @@ -569,7 +570,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // -2.1, +1.3})); } - REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -601,7 +602,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } }); - auto reconstructions = PolynomialReconstruction{}.build(1, Vh); + auto reconstructions = PolynomialReconstruction{}.build(1, true, true, Vh); auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); @@ -613,7 +614,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); } { @@ -627,7 +628,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); } { @@ -641,7 +642,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -668,7 +669,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, fh); + auto reconstructions = PolynomialReconstruction{}.build(1, true, true, fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); @@ -678,7 +679,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -689,7 +690,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-14)); } { @@ -700,7 +701,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); } { @@ -711,7 +712,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -737,7 +738,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, uh); + auto reconstructions = PolynomialReconstruction{}.build(1, true, true, uh); auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); @@ -747,7 +748,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -758,7 +759,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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-13)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); } { @@ -769,7 +770,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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-13)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); } { @@ -780,7 +781,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9})); } - REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); } } } @@ -808,7 +809,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, Ah); + auto reconstructions = PolynomialReconstruction{}.build(1, true, true, Ah); auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); @@ -818,7 +819,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); } - REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); } { @@ -830,7 +831,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // -2.3, +3.1})); } - REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); } { @@ -843,7 +844,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // 1.3, +0.8})); } - REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); } { @@ -856,7 +857,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // +1.4, -1.8})); } - REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -889,7 +890,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } }); - auto reconstructions = PolynomialReconstruction{}.build(1, Vh); + auto reconstructions = PolynomialReconstruction{}.build(1, true, true, Vh); auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); @@ -901,7 +902,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); } { @@ -915,7 +916,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); } { @@ -929,7 +930,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); } { @@ -943,7 +944,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") 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)); + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -958,7 +959,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") auto p_mesh2 = MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); DiscreteFunctionP0<double> f2{p_mesh2}; - REQUIRE_THROWS_WITH(PolynomialReconstruction{}.build(1, f1, f2), + REQUIRE_THROWS_WITH(PolynomialReconstruction{}.build(1, true, true, f1, f2), "error: cannot reconstruct functions living of different meshes simultaneously"); } } -- GitLab From 96a5b5faa0e80eff2deaf6fd70af856c69284c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 8 Jul 2024 17:09:12 +0200 Subject: [PATCH 042/122] Reduce few tests tolerances benefiting of preconditioning --- tests/test_PolynomialReconstruction.cpp | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index ada21453a..2f25f0168 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -153,7 +153,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -212,7 +212,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); } } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -227,7 +227,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); } } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -344,7 +344,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -373,7 +373,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); } } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -388,7 +388,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); } } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -544,7 +544,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -557,7 +557,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // -0.6, -2.3})); } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); } { @@ -570,7 +570,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // -2.1, +1.3})); } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); } } } @@ -614,7 +614,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); } } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -628,7 +628,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i])); } } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-14)); } { @@ -642,7 +642,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i])); } } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -819,7 +819,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -831,7 +831,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // -2.3, +3.1})); } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); } { @@ -844,7 +844,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // 1.3, +0.8})); } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); } { @@ -857,7 +857,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // +1.4, -1.8})); } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); } } } @@ -902,7 +902,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) - vector[i])); } } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } { @@ -916,7 +916,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i])); } } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); } { @@ -930,7 +930,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i])); } } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); } { @@ -944,7 +944,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - slope[i])); } } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); } } } -- GitLab From 5a216bba1c1015ce67a7f66506f7e26754c0df5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 8 Jul 2024 18:51:58 +0200 Subject: [PATCH 043/122] Improve interface for PolynomialReconstruction Add PolynomialReconstructionDescriptor and associated tests --- src/scheme/PolynomialReconstruction.cpp | 19 ++--- src/scheme/PolynomialReconstruction.hpp | 17 ++-- .../PolynomialReconstructionDescriptor.hpp | 68 +++++++++++++++ src/scheme/test_reconstruction.cpp | 14 ++-- tests/CMakeLists.txt | 1 + tests/test_PolynomialReconstruction.cpp | 36 ++++---- ...est_PolynomialReconstructionDescriptor.cpp | 82 +++++++++++++++++++ 7 files changed, 193 insertions(+), 44 deletions(-) create mode 100644 src/scheme/PolynomialReconstructionDescriptor.hpp create mode 100644 tests/test_PolynomialReconstructionDescriptor.cpp diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index e3fe01930..7557c284e 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -102,12 +102,11 @@ class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant template <MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::_build( - const size_t degree, - const bool preconditioning, - const bool row_weighting, const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const { + const size_t degree = m_descriptor.degree(); + if (degree != 1) { throw NotImplementedError("only degree 1 is available"); } @@ -282,7 +281,7 @@ PolynomialReconstruction::_build( } } - if (row_weighting) { + if (m_descriptor.rowWeighting()) { // Add row weighting (give more importance to cells that are // closer to j) for (size_t i = 0; i < stencil_cell_list.size(); ++i) { @@ -299,7 +298,7 @@ PolynomialReconstruction::_build( const SmallMatrix<double>& X = X_pool[t]; - if (preconditioning) { + if (m_descriptor.preconditioning()) { // Add column weighting preconditioning (increase the presition) SmallArray<double>& G = G_pool[t]; @@ -447,9 +446,6 @@ PolynomialReconstruction::_build( std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::build( - const size_t degree, - const bool preconditioning, - const bool row_weighting, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const { if (not hasSameMesh(discrete_function_variant_list)) { @@ -458,9 +454,6 @@ PolynomialReconstruction::build( auto mesh_v = getCommonMesh(discrete_function_variant_list); - return std::visit( - [&](auto&& p_mesh) { - return this->_build(degree, preconditioning, row_weighting, p_mesh, discrete_function_variant_list); - }, - mesh_v->variant()); + return std::visit([&](auto&& p_mesh) { return this->_build(p_mesh, discrete_function_variant_list); }, + mesh_v->variant()); } diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index 74bb82785..c0da0f260 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -7,6 +7,7 @@ #include <algebra/SmallVector.hpp> #include <mesh/MeshTraits.hpp> #include <scheme/DiscreteFunctionDPkVariant.hpp> +#include <scheme/PolynomialReconstructionDescriptor.hpp> #warning MOVE TO .cpp WHEN FINISHED #include <algebra/Givens.hpp> @@ -26,11 +27,10 @@ class PolynomialReconstruction private: class MutableDiscreteFunctionDPkVariant; + const PolynomialReconstructionDescriptor m_descriptor; + template <MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> _build( - const size_t degree, - const bool preconditioning, - const bool row_weighting, const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; @@ -266,15 +266,11 @@ class PolynomialReconstruction } [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> build( - const size_t degree, - const bool preconditioning, - const bool row_weighting, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; -#warning add reconstruction descriptor/option to handle the options to be used for reconstruction template <typename... DiscreteFunctionT> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> - build(const size_t degree, const bool preconditioning, const bool row_weighting, DiscreteFunctionT... input) const + build(DiscreteFunctionT... input) const { std::vector<std::shared_ptr<const DiscreteFunctionVariant>> variant_vector; auto convert = [&variant_vector](auto&& df) { @@ -294,10 +290,11 @@ class PolynomialReconstruction }; (convert(std::forward<DiscreteFunctionT>(input)), ...); - return this->build(degree, preconditioning, row_weighting, variant_vector); + return this->build(variant_vector); } - PolynomialReconstruction() {} + PolynomialReconstruction(const PolynomialReconstructionDescriptor& descriptor) : m_descriptor{descriptor} {} + ~PolynomialReconstruction() = default; }; #endif // POLYNOMIAL_RECONSTRUCTION_HPP diff --git a/src/scheme/PolynomialReconstructionDescriptor.hpp b/src/scheme/PolynomialReconstructionDescriptor.hpp new file mode 100644 index 000000000..a73945001 --- /dev/null +++ b/src/scheme/PolynomialReconstructionDescriptor.hpp @@ -0,0 +1,68 @@ +#ifndef POLYNOMIAL_RECONSTRUCTION_DESCRIPTOR_HPP +#define POLYNOMIAL_RECONSTRUCTION_DESCRIPTOR_HPP + +#include <utils/PugsMacros.hpp> + +#include <cstddef> + +class PolynomialReconstructionDescriptor +{ + private: + size_t m_degree; + bool m_preconditioning = true; + bool m_row_weighting = true; + + public: + PUGS_INLINE + size_t + degree() const + { + return m_degree; + } + + PUGS_INLINE + bool + preconditioning() const + { + return m_preconditioning; + } + + PUGS_INLINE + bool + rowWeighting() const + { + return m_row_weighting; + } + + PUGS_INLINE + void + setDegree(const size_t degree) + { + m_degree = degree; + } + + PUGS_INLINE + void + setPreconditioning(const bool preconditioning) + { + m_preconditioning = preconditioning; + } + + PUGS_INLINE + void + setRowWeighting(const bool row_weighting) + { + m_row_weighting = row_weighting; + } + + PolynomialReconstructionDescriptor(const size_t degree) : m_degree{degree} {} + + PolynomialReconstructionDescriptor() = delete; + + PolynomialReconstructionDescriptor(const PolynomialReconstructionDescriptor&) = default; + PolynomialReconstructionDescriptor(PolynomialReconstructionDescriptor&&) = default; + + ~PolynomialReconstructionDescriptor() = default; +}; + +#endif // POLYNOMIAL_RECONSTRUCTION_DESCRIPTOR_HPP diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index 1267aabec..6639b8493 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -8,13 +8,14 @@ test_reconstruction(const std::shared_ptr<const MeshVariant>& mesh_v, const std::shared_ptr<const DiscreteFunctionVariant>& discrete_function_v) { std::cout << "** one variable at a time (30 times)\n"; + PolynomialReconstructionDescriptor descriptor{1}; std::visit( [&](auto&& p_mesh) { std::visit( [&](auto&& discrete_function) { using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - PolynomialReconstruction reconstruction; + PolynomialReconstruction reconstruction{descriptor}; if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { Timer t; for (size_t i = 0; i < 30; ++i) { @@ -35,12 +36,13 @@ void test_reconstruction_one(const std::shared_ptr<const MeshVariant>& mesh_v, const std::shared_ptr<const DiscreteFunctionVariant>& discrete_function_v) { + PolynomialReconstructionDescriptor descriptor{1}; std::visit( [&](auto&& p_mesh) { std::visit( [&](auto&& discrete_function) { using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - PolynomialReconstruction reconstruction; + PolynomialReconstruction reconstruction{descriptor}; if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { for (size_t i = 0; i < 30; ++i) { auto res = reconstruction.build(*p_mesh, discrete_function); @@ -55,6 +57,7 @@ test_reconstruction_one(const std::shared_ptr<const MeshVariant>& mesh_v, void test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) { + PolynomialReconstructionDescriptor descriptor{1}; { std::cout << "** variable list one by one (50 times)\n"; Timer t; @@ -65,7 +68,7 @@ test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVari std::visit( [&](auto&& p_mesh) { using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - PolynomialReconstruction reconstruction; + PolynomialReconstruction reconstruction{descriptor}; if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { for (size_t i = 0; i < 50; ++i) { auto res = reconstruction.build(*p_mesh, discrete_function); @@ -84,10 +87,11 @@ test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVari { std::cout << "** variable list at once (50 times)\n"; - PolynomialReconstruction reconstruction; + + PolynomialReconstruction reconstruction{descriptor}; Timer t; for (size_t i = 0; i < 50; ++i) { - auto res = reconstruction.build(1, true, true, discrete_function_variant_list); + auto res = reconstruction.build(discrete_function_variant_list); } t.pause(); std::cout << "t = " << t << '\n'; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4c94f8a39..b242c06dc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -206,6 +206,7 @@ add_executable (mpi_unit_tests test_ParallelChecker_read.cpp test_Partitioner.cpp test_PolynomialReconstruction.cpp + test_PolynomialReconstructionDescriptor.cpp test_RandomEngine.cpp test_StencilBuilder.cpp test_SubItemArrayPerItemVariant.cpp diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 2f25f0168..d9b33b911 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -23,6 +23,8 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { SECTION("degree 1") { + PolynomialReconstructionDescriptor descriptor{1}; + SECTION("1D") { using R1 = TinyVector<1>; @@ -43,7 +45,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, true, true, fh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); @@ -92,7 +94,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, true, true, uh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); @@ -143,7 +145,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, true, true, Ah); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); @@ -200,7 +202,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } }); - auto reconstructions = PolynomialReconstruction{}.build(1, true, true, Vh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); @@ -288,9 +290,9 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") }); auto reconstructions = - PolynomialReconstruction{}.build(1, true, true, std::make_shared<DiscreteFunctionVariant>(fh), uh, - std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), - DiscreteFunctionVariant(Vh)); + PolynomialReconstruction{descriptor}.build(std::make_shared<DiscreteFunctionVariant>(fh), uh, + std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), + DiscreteFunctionVariant(Vh)); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); @@ -415,7 +417,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, true, true, fh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); @@ -475,7 +477,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, true, true, uh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); @@ -534,7 +536,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, true, true, Ah); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); @@ -602,7 +604,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } }); - auto reconstructions = PolynomialReconstruction{}.build(1, true, true, Vh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); @@ -669,7 +671,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, true, true, fh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); @@ -738,7 +740,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, true, true, uh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); @@ -809,7 +811,8 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{}.build(1, true, true, Ah); + descriptor.setRowWeighting(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); @@ -890,7 +893,8 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } }); - auto reconstructions = PolynomialReconstruction{}.build(1, true, true, Vh); + descriptor.setPreconditioning(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); @@ -959,7 +963,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") auto p_mesh2 = MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); DiscreteFunctionP0<double> f2{p_mesh2}; - REQUIRE_THROWS_WITH(PolynomialReconstruction{}.build(1, true, true, f1, f2), + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(f1, f2), "error: cannot reconstruct functions living of different meshes simultaneously"); } } diff --git a/tests/test_PolynomialReconstructionDescriptor.cpp b/tests/test_PolynomialReconstructionDescriptor.cpp new file mode 100644 index 000000000..6b7cd2853 --- /dev/null +++ b/tests/test_PolynomialReconstructionDescriptor.cpp @@ -0,0 +1,82 @@ +#include <catch2/catch_approx.hpp> +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <scheme/PolynomialReconstructionDescriptor.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") +{ + SECTION("degree 1") + { + PolynomialReconstructionDescriptor descriptor{1}; + + REQUIRE(descriptor.degree() == 1); + REQUIRE(descriptor.preconditioning() == true); + REQUIRE(descriptor.rowWeighting() == true); + } + + SECTION("degree 4") + { + PolynomialReconstructionDescriptor descriptor{4}; + + REQUIRE(descriptor.degree() == 4); + REQUIRE(descriptor.preconditioning() == true); + REQUIRE(descriptor.rowWeighting() == true); + } + + SECTION("utlities") + { + PolynomialReconstructionDescriptor descriptor{3}; + + REQUIRE(descriptor.degree() == 3); + REQUIRE(descriptor.preconditioning() == true); + REQUIRE(descriptor.rowWeighting() == true); + + SECTION("set degree") + { + descriptor.setDegree(2); + + REQUIRE(descriptor.degree() == 2); + REQUIRE(descriptor.preconditioning() == true); + REQUIRE(descriptor.rowWeighting() == true); + + descriptor.setDegree(4); + + REQUIRE(descriptor.degree() == 4); + REQUIRE(descriptor.preconditioning() == true); + REQUIRE(descriptor.rowWeighting() == true); + } + + SECTION("set preconditioning") + { + descriptor.setPreconditioning(false); + + REQUIRE(descriptor.degree() == 3); + REQUIRE(descriptor.preconditioning() == false); + REQUIRE(descriptor.rowWeighting() == true); + + descriptor.setPreconditioning(true); + + REQUIRE(descriptor.degree() == 3); + REQUIRE(descriptor.preconditioning() == true); + REQUIRE(descriptor.rowWeighting() == true); + } + + SECTION("set row weighting") + { + descriptor.setRowWeighting(false); + + REQUIRE(descriptor.degree() == 3); + REQUIRE(descriptor.preconditioning() == true); + REQUIRE(descriptor.rowWeighting() == false); + + descriptor.setRowWeighting(true); + + REQUIRE(descriptor.degree() == 3); + REQUIRE(descriptor.preconditioning() == true); + REQUIRE(descriptor.rowWeighting() == true); + } + } +} -- GitLab From 5c29e90388446e0076de557f47d390a024a55308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 12 Jul 2024 18:48:05 +0200 Subject: [PATCH 044/122] Improve performances for reconstruction using quadrature A quadrature approach is (a priori) needed for polynomial reconstructions of degrees higher than 1 --- .../PolynomialCenteredCanonicalBasisView.hpp | 4 +- src/scheme/PolynomialReconstruction.cpp | 199 +++++++++++++++--- src/scheme/test_reconstruction.cpp | 52 ++--- tests/test_PolynomialReconstruction.cpp | 10 +- 4 files changed, 204 insertions(+), 61 deletions(-) diff --git a/src/scheme/PolynomialCenteredCanonicalBasisView.hpp b/src/scheme/PolynomialCenteredCanonicalBasisView.hpp index dbe9a5d20..2417ec159 100644 --- a/src/scheme/PolynomialCenteredCanonicalBasisView.hpp +++ b/src/scheme/PolynomialCenteredCanonicalBasisView.hpp @@ -135,8 +135,8 @@ class PolynomialCenteredCanonicalBasisView const double x_xj = (x - m_xj)[0]; std::remove_const_t<DataType> result = m_coefficients[m_degree]; - for (ssize_t i_coeffiencient = m_degree - 1; i_coeffiencient >= 0; --i_coeffiencient) { - result = x_xj * result + m_coefficients[i_coeffiencient]; + for (ssize_t i = m_degree - 1; i >= 0; --i) { + result = x_xj * result + m_coefficients[i]; } return result; diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 7557c284e..a105a1207 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -3,6 +3,13 @@ #include <scheme/DiscreteFunctionUtils.hpp> #include <scheme/DiscreteFunctionVariant.hpp> +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/SquareTransformation.hpp> +#include <geometry/TriangleTransformation.hpp> + class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant { public: @@ -105,12 +112,6 @@ PolynomialReconstruction::_build( const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const { - const size_t degree = m_descriptor.degree(); - - if (degree != 1) { - throw NotImplementedError("only degree 1 is available"); - } - const MeshType& mesh = *p_mesh; using Rd = TinyVector<MeshType::Dimension>; @@ -146,12 +147,17 @@ PolynomialReconstruction::_build( }(); const size_t basis_dimension = - DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(degree); + DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(m_descriptor.degree()); const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity()); - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - auto cell_is_owned = mesh.connectivity().cellIsOwned(); + auto xr = mesh.xr(); + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_is_owned = mesh.connectivity().cellIsOwned(); + auto cell_type = mesh.connectivity().cellType(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); const size_t max_stencil_size = [&]() { size_t max_size = 0; @@ -179,11 +185,12 @@ PolynomialReconstruction::_build( if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { using DataType = std::remove_const_t<std::decay_t<typename DiscreteFunctionT::data_type>>; mutable_discrete_function_dpk_variant_list.push_back( - DiscreteFunctionDPk<MeshType::Dimension, DataType>(p_mesh, degree)); + DiscreteFunctionDPk<MeshType::Dimension, DataType>(p_mesh, m_descriptor.degree())); } 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, degree, discrete_function.size())); + DiscreteFunctionDPkVector<MeshType::Dimension, DataType>(p_mesh, m_descriptor.degree(), + discrete_function.size())); } else { // LCOV_EXCL_START throw UnexpectedError("unexpected discrete function type"); @@ -198,11 +205,19 @@ PolynomialReconstruction::_build( SmallArray<SmallArray<double>> G_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallMatrix<double>> X_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallArray<double>> inv_Vj_wq_detJ_ek_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallArray<double>> mean_j_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallArray<double>> mean_i_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); + for (size_t i = 0; i < A_pool.size(); ++i) { A_pool[i] = SmallMatrix<double>(max_stencil_size, basis_dimension - 1); B_pool[i] = SmallMatrix<double>(max_stencil_size, number_of_columns); G_pool[i] = SmallArray<double>(basis_dimension - 1); X_pool[i] = SmallMatrix<double>(basis_dimension - 1, number_of_columns); + + inv_Vj_wq_detJ_ek_pool[i] = SmallArray<double>(basis_dimension); + mean_j_of_ejk_pool[i] = SmallArray<double>(basis_dimension - 1); + mean_i_of_ejk_pool[i] = SmallArray<double>(basis_dimension - 1); } parallel_for( @@ -224,20 +239,20 @@ PolynomialReconstruction::_build( using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - const DataType& pj = discrete_function[cell_j_id]; + const DataType& qj = discrete_function[cell_j_id]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { const CellId cell_i_id = stencil_cell_list[i]; - const DataType& pi_pj = discrete_function[cell_i_id] - pj; + const DataType& qi_qj = discrete_function[cell_i_id] - qj; if constexpr (std::is_arithmetic_v<DataType>) { - B(i, column_begin) = pi_pj; + B(i, column_begin) = qi_qj; } else if constexpr (is_tiny_vector_v<DataType>) { for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { - B(i, kB) = pi_pj[k]; + B(i, kB) = qi_qj[k]; } } else if constexpr (is_tiny_matrix_v<DataType>) { for (size_t k = 0; k < DataType::NumberOfRows; ++k) { for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - B(i, column_begin + k * DataType::NumberOfColumns + l) = pi_pj(k, l); + B(i, column_begin + k * DataType::NumberOfColumns + l) = qi_qj(k, l); } } } @@ -250,16 +265,16 @@ PolynomialReconstruction::_build( } } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - const auto pj_vector = discrete_function[cell_j_id]; - for (size_t l = 0; l < pj_vector.size(); ++l) { - const DataType& pj = pj_vector[l]; + const auto qj_vector = discrete_function[cell_j_id]; + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { const CellId cell_i_id = stencil_cell_list[i]; - const DataType& pi_pj = discrete_function[cell_i_id][l] - pj; - B(i, column_begin + l) = pi_pj; + const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; + B(i, column_begin + l) = qi_qj; } } - column_begin += pj_vector.size(); + column_begin += qj_vector.size(); } else { // LCOV_EXCL_START throw UnexpectedError("invalid discrete function type"); @@ -271,19 +286,141 @@ PolynomialReconstruction::_build( ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); - const Rd& Xj = xj[cell_j_id]; +#warning remove after rework + std::string ENV_SWITCH = []() { + auto value = std::getenv("INTEGRATE"); + if (value == nullptr) { + return std::string{""}; + } else { + return std::string{value}; + } + }(); - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const Rd Xi_Xj = xj[cell_i_id] - Xj; - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(i, l) = Xi_Xj[l]; + if ((m_descriptor.degree() == 1) and ((MeshType::Dimension != 2) or (ENV_SWITCH == ""))) { + const Rd& Xj = xj[cell_j_id]; + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + const Rd Xi_Xj = xj[cell_i_id] - Xj; + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(i, l) = Xi_Xj[l]; + } + } + } else { + if constexpr (MeshType::Dimension == 2) { + SmallArray<double> inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t]; + SmallArray<double> mean_j_of_ejk = mean_j_of_ejk_pool[t]; + SmallArray<double> mean_i_of_ejk = mean_i_of_ejk_pool[t]; + + auto compute_mean_ejk = [&inv_Vj_wq_detJ_ek](const auto& quadrature, const auto& T, const Rd& Xj, + const double Vi, const size_t degree, const size_t dimension, + SmallArray<double>& mean_of_ejk) { + mean_of_ejk.fill(0); + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const Rd& xi_q = quadrature.point(i_q); + const double detT = [&] { + if constexpr (std::is_same_v<TriangleTransformation<2>, std::decay_t<decltype(T)>>) { + return T.jacobianDeterminant(); + } else { + return T.jacobianDeterminant(xi_q); + } + }(); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + + { + size_t k = 0; + inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; + for (; k <= degree; ++k) { + inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; + } + + for (size_t i_y = 1; i_y <= degree; ++i_y) { + const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; + for (size_t l = 0; l <= degree - i_y; ++l) { + inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; + } + } + } + + for (size_t k = 1; k < dimension; ++k) { + mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; + } + } + }; + + const Rd& Xj = xj[cell_j_id]; + + if (cell_type[cell_j_id] == CellType::Triangle) { + auto cell_node = cell_to_node_matrix[cell_j_id]; + + const TriangleTransformation<2> T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]]}; + + const auto& quadrature = + QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + + } else if (cell_type[cell_j_id] == CellType::Quadrangle) { + auto cell_node = cell_to_node_matrix[cell_j_id]; + + const SquareTransformation<2> T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]]}; + + const auto& quadrature = QuadratureManager::instance().getSquareFormula( + GaussLegendreQuadratureDescriptor{m_descriptor.degree() + 1}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type"); + } + + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + + auto cell_node = cell_to_node_matrix[cell_i_id]; + + if (cell_type[cell_i_id] == CellType::Triangle) { + const TriangleTransformation<2> T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]]}; + + const auto& quadrature = + QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + mean_i_of_ejk); + + } else if (cell_type[cell_i_id] == CellType::Quadrangle) { + const SquareTransformation<2> T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]]}; + + const auto& quadrature = QuadratureManager::instance().getSquareFormula( + GaussLegendreQuadratureDescriptor{m_descriptor.degree() + 1}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + mean_i_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type"); + } + + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; + } + } + + } else { + throw NotImplementedError("unexpected dimension"); } } if (m_descriptor.rowWeighting()) { // Add row weighting (give more importance to cells that are // closer to j) + const Rd& Xj = xj[cell_j_id]; + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { const CellId cell_i_id = stencil_cell_list[i]; const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); @@ -331,6 +468,12 @@ PolynomialReconstruction::_build( Givens::solveCollectionInPlace(A, X, B); } + // Results are now written from the {ejk} basis to the {ek} basis + if (m_descriptor.degree() > 1) { + throw NotImplementedError( + "write the result from the {ejk} basis to the {ek} basis to get correct polynomial values"); + } + column_begin = 0; for (size_t i_dpk_variant = 0; i_dpk_variant < mutable_discrete_function_dpk_variant_list.size(); ++i_dpk_variant) { diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index 6639b8493..42ec98cc9 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -58,32 +58,32 @@ void test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) { PolynomialReconstructionDescriptor descriptor{1}; - { - std::cout << "** variable list one by one (50 times)\n"; - Timer t; - for (auto discrete_function_v : discrete_function_variant_list) { - std::visit( - [&](auto&& discrete_function) { - auto mesh_v = discrete_function.meshVariant(); - std::visit( - [&](auto&& p_mesh) { - using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - PolynomialReconstruction reconstruction{descriptor}; - if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - for (size_t i = 0; i < 50; ++i) { - auto res = reconstruction.build(*p_mesh, discrete_function); - } - } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - std::cout << "Omitting discrete function p0 vector!\n"; - } - }, - mesh_v->variant()); - }, - discrete_function_v->discreteFunction()); - } - t.pause(); - std::cout << "t = " << t << '\n'; - } + // { + // std::cout << "** variable list one by one (50 times)\n"; + // Timer t; + // for (auto discrete_function_v : discrete_function_variant_list) { + // std::visit( + // [&](auto&& discrete_function) { + // auto mesh_v = discrete_function.meshVariant(); + // std::visit( + // [&](auto&& p_mesh) { + // using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + // PolynomialReconstruction reconstruction{descriptor}; + // if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + // for (size_t i = 0; i < 50; ++i) { + // auto res = reconstruction.build(*p_mesh, discrete_function); + // } + // } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { + // std::cout << "Omitting discrete function p0 vector!\n"; + // } + // }, + // mesh_v->variant()); + // }, + // discrete_function_v->discreteFunction()); + // } + // t.pause(); + // std::cout << "t = " << t << '\n'; + // } { std::cout << "** variable list at once (50 times)\n"; diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index d9b33b911..0e9398fab 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -438,7 +438,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7)); } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); } { @@ -498,7 +498,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); } { @@ -509,7 +509,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1})); } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); } } } @@ -630,7 +630,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i])); } } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); } { @@ -644,7 +644,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i])); } } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); } } } -- GitLab From 73952c5496f8e777c6d364ad2e76e14bcd5a5341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 12 Jul 2024 19:04:45 +0200 Subject: [PATCH 045/122] Add quadrature-based polynomial reconstruction in 1D --- src/scheme/PolynomialReconstruction.cpp | 78 +++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 6 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index a105a1207..4fa7cc1d3 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -296,7 +296,7 @@ PolynomialReconstruction::_build( } }(); - if ((m_descriptor.degree() == 1) and ((MeshType::Dimension != 2) or (ENV_SWITCH == ""))) { + if ((m_descriptor.degree() == 1) and ((MeshType::Dimension == 3) or (ENV_SWITCH == ""))) { const Rd& Xj = xj[cell_j_id]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { const CellId cell_i_id = stencil_cell_list[i]; @@ -306,11 +306,78 @@ PolynomialReconstruction::_build( } } } else { - if constexpr (MeshType::Dimension == 2) { - SmallArray<double> inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t]; - SmallArray<double> mean_j_of_ejk = mean_j_of_ejk_pool[t]; - SmallArray<double> mean_i_of_ejk = mean_i_of_ejk_pool[t]; + SmallArray<double> inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t]; + SmallArray<double> mean_j_of_ejk = mean_j_of_ejk_pool[t]; + SmallArray<double> mean_i_of_ejk = mean_i_of_ejk_pool[t]; + if constexpr (MeshType::Dimension == 1) { + auto compute_mean_ejk = [&inv_Vj_wq_detJ_ek](const auto& quadrature, const auto& T, const Rd& Xj, + const double Vi, const size_t degree, const size_t dimension, + SmallArray<double>& mean_of_ejk) { + mean_of_ejk.fill(0); + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const Rd& xi_q = quadrature.point(i_q); + const double detT = T.jacobianDeterminant(); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + + { + size_t k = 0; + inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; + for (; k <= degree; ++k) { + inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; + } + } + + for (size_t k = 1; k < dimension; ++k) { + mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; + } + } + }; + + const Rd& Xj = xj[cell_j_id]; + + if (cell_type[cell_j_id] == CellType::Line) { + auto cell_node = cell_to_node_matrix[cell_j_id]; + + const LineTransformation<1> T{xr[cell_node[0]], xr[cell_node[1]]}; + + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{m_descriptor.degree()}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type"); + } + + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + + auto cell_node = cell_to_node_matrix[cell_i_id]; + + if (cell_type[cell_i_id] == CellType::Line) { + const LineTransformation<1> T{xr[cell_node[0]], xr[cell_node[1]]}; + + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + mean_i_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type"); + } + + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; + } + } + } else if constexpr (MeshType::Dimension == 2) { auto compute_mean_ejk = [&inv_Vj_wq_detJ_ek](const auto& quadrature, const auto& T, const Rd& Xj, const double Vi, const size_t degree, const size_t dimension, SmallArray<double>& mean_of_ejk) { @@ -410,7 +477,6 @@ PolynomialReconstruction::_build( A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; } } - } else { throw NotImplementedError("unexpected dimension"); } -- GitLab From 5c038f8c2363f1fb85036510445e05e337a57140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 15 Jul 2024 16:35:58 +0200 Subject: [PATCH 046/122] Add quadrature based reconstruction formula in 3D --- src/scheme/PolynomialReconstruction.cpp | 192 ++++++++++++++++++++++-- tests/test_PolynomialReconstruction.cpp | 18 +-- 2 files changed, 186 insertions(+), 24 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 4fa7cc1d3..69d24b9e5 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -7,7 +7,11 @@ #include <analysis/GaussQuadratureDescriptor.hpp> #include <analysis/QuadratureFormula.hpp> #include <analysis/QuadratureManager.hpp> +#include <geometry/CubeTransformation.hpp> +#include <geometry/PrismTransformation.hpp> +#include <geometry/PyramidTransformation.hpp> #include <geometry/SquareTransformation.hpp> +#include <geometry/TetrahedronTransformation.hpp> #include <geometry/TriangleTransformation.hpp> class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant @@ -220,6 +224,16 @@ PolynomialReconstruction::_build( mean_i_of_ejk_pool[i] = SmallArray<double>(basis_dimension - 1); } +#warning remove after rework + const bool FORCE_INTEGRATE = []() { + auto value = std::getenv("INTEGRATE"); + if (value == nullptr) { + return false; + } else { + return true; + } + }(); + parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { if (cell_is_owned[cell_j_id]) { @@ -286,17 +300,7 @@ PolynomialReconstruction::_build( ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); -#warning remove after rework - std::string ENV_SWITCH = []() { - auto value = std::getenv("INTEGRATE"); - if (value == nullptr) { - return std::string{""}; - } else { - return std::string{value}; - } - }(); - - if ((m_descriptor.degree() == 1) and ((MeshType::Dimension == 3) or (ENV_SWITCH == ""))) { + if ((m_descriptor.degree() == 1) and (not FORCE_INTEGRATE)) { const Rd& Xj = xj[cell_j_id]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { const CellId cell_i_id = stencil_cell_list[i]; @@ -352,7 +356,7 @@ PolynomialReconstruction::_build( compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); } else { - throw NotImplementedError("unexpected cell type"); + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_j_id])}); } for (size_t i = 0; i < stencil_cell_list.size(); ++i) { @@ -370,7 +374,7 @@ PolynomialReconstruction::_build( mean_i_of_ejk); } else { - throw NotImplementedError("unexpected cell type"); + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_i_id])}); } for (size_t l = 0; l < basis_dimension - 1; ++l) { @@ -443,7 +447,7 @@ PolynomialReconstruction::_build( compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); } else { - throw NotImplementedError("unexpected cell type"); + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_j_id])}); } for (size_t i = 0; i < stencil_cell_list.size(); ++i) { @@ -470,13 +474,171 @@ PolynomialReconstruction::_build( mean_i_of_ejk); } else { - throw NotImplementedError("unexpected cell type"); + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_i_id])}); + } + + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; + } + } + + } else if constexpr (MeshType::Dimension == 3) { + auto compute_mean_ejk = [&inv_Vj_wq_detJ_ek](const auto& quadrature, const auto& T, const Rd& Xj, + const double Vi, const size_t degree, const size_t dimension, + SmallArray<double>& mean_of_ejk) { + mean_of_ejk.fill(0); + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const Rd& xi_q = quadrature.point(i_q); + const double detT = [&] { + if constexpr (std::is_same_v<TetrahedronTransformation, std::decay_t<decltype(T)>>) { + return T.jacobianDeterminant(); + } else { + return T.jacobianDeterminant(xi_q); + } + }(); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + const double z_zj = X_Xj[2]; + + { + size_t k = 0; + inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; + for (; k <= degree; ++k) { + inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; + } + + for (size_t i_y = 1; i_y <= degree; ++i_y) { + const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; + for (size_t l = 0; l <= degree - i_y; ++l) { + inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; + } + } + + for (size_t i_z = 1; i_z <= degree; ++i_z) { + const size_t begin_i_z_1 = i_z * (i_z + 1) * (i_z + 2) / 6 - 1; + for (size_t i_y = 0; i_y <= degree - i_z; ++i_y) { + const size_t begin_i_y_1 = (i_y * (2 * degree - i_y + 3)) / 2 + begin_i_z_1; + for (size_t l = 0; l <= degree - i_y - i_z; ++l) { + inv_Vj_wq_detJ_ek[k++] = z_zj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; + } + } + } + } + + for (size_t k = 1; k < dimension; ++k) { + mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; + } + } + }; + + const Rd& Xj = xj[cell_j_id]; + + if (cell_type[cell_j_id] == CellType::Tetrahedron) { + auto cell_node = cell_to_node_matrix[cell_j_id]; + + const TetrahedronTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]]}; + + const auto& quadrature = + QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + + } else if (cell_type[cell_j_id] == CellType::Prism) { + auto cell_node = cell_to_node_matrix[cell_j_id]; + + const PrismTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], // + xr[cell_node[3]], xr[cell_node[4]], xr[cell_node[5]]}; + + const auto& quadrature = + QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_descriptor.degree() + 1}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + + } else if (cell_type[cell_j_id] == CellType::Pyramid) { + auto cell_node = cell_to_node_matrix[cell_j_id]; + + const PyramidTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]], + xr[cell_node[4]]}; + + const auto& quadrature = + QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_descriptor.degree() + 1}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + + } else if (cell_type[cell_j_id] == CellType::Hexahedron) { + auto cell_node = cell_to_node_matrix[cell_j_id]; + + const CubeTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]], + xr[cell_node[4]], xr[cell_node[5]], xr[cell_node[6]], xr[cell_node[7]]}; + + const auto& quadrature = QuadratureManager::instance().getCubeFormula( + GaussLegendreQuadratureDescriptor{m_descriptor.degree() + 1}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_j_id])}); + } + + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + + auto cell_node = cell_to_node_matrix[cell_i_id]; + + if (cell_type[cell_i_id] == CellType::Tetrahedron) { + const TetrahedronTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], + xr[cell_node[3]]}; + + const auto& quadrature = + QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + mean_i_of_ejk); + + } else if (cell_type[cell_i_id] == CellType::Prism) { + const PrismTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], // + xr[cell_node[3]], xr[cell_node[4]], xr[cell_node[5]]}; + + const auto& quadrature = + QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_descriptor.degree() + 1}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + mean_i_of_ejk); + + } else if (cell_type[cell_i_id] == CellType::Pyramid) { + const PyramidTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]], + xr[cell_node[4]]}; + + const auto& quadrature = + QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_descriptor.degree() + 1}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + mean_i_of_ejk); + + } else if (cell_type[cell_i_id] == CellType::Hexahedron) { + const CubeTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]], + xr[cell_node[4]], xr[cell_node[5]], xr[cell_node[6]], xr[cell_node[7]]}; + + const auto& quadrature = QuadratureManager::instance().getCubeFormula( + GaussLegendreQuadratureDescriptor{m_descriptor.degree() + 1}); + + compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + mean_i_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_i_id])}); } for (size_t l = 0; l < basis_dimension - 1; ++l) { A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; } } + } else { throw NotImplementedError("unexpected dimension"); } diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 0e9398fab..47a538f0d 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -692,7 +692,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7)); } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); } { @@ -703,7 +703,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3))); } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); } { @@ -714,7 +714,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - 2.1)); } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -761,7 +761,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); } { @@ -772,7 +772,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1})); } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); } { @@ -783,7 +783,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9})); } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -847,7 +847,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // 1.3, +0.8})); } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); } { @@ -860,7 +860,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // +1.4, -1.8})); } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -934,7 +934,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i])); } } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); } { -- GitLab From ae1cbc1060ac59874c06a058ccdc30ae28575a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 16 Jul 2024 19:23:20 +0200 Subject: [PATCH 047/122] Begin code clean-up --- src/language/modules/SchemeModule.cpp | 11 - src/scheme/PolynomialReconstruction.cpp | 454 +++++++++++++----------- src/scheme/PolynomialReconstruction.hpp | 238 +------------ src/scheme/test_reconstruction.cpp | 51 --- src/scheme/test_reconstruction.hpp | 3 - 5 files changed, 261 insertions(+), 496 deletions(-) diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index f96e96f13..0f3dadaad 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -671,17 +671,6 @@ SchemeModule::SchemeModule() )); -#warning REMOVE AFTER TESTS FINISHED - this->_addBuiltinFunction("test_reconstruction", - std::function( - - [](const std::shared_ptr<const MeshVariant> mesh_v, - const std::shared_ptr<const DiscreteFunctionVariant>& discrete_function_v) -> void { - test_reconstruction(mesh_v, discrete_function_v); - } - - )); - #warning REMOVE AFTER TESTS FINISHED this->_addBuiltinFunction("test_reconstruction", std::function( diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 69d24b9e5..f9e55f19c 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -110,6 +110,225 @@ class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant ~MutableDiscreteFunctionDPkVariant() = default; }; +template <typename ConformTransformationT> +void +_computeEjkMean(const QuadratureFormula<1>& quadrature, + const ConformTransformationT& T, + const TinyVector<1>& Xj, + const double Vi, + const size_t degree, + const size_t dimension, + SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + using Rd = TinyVector<1>; + mean_of_ejk.fill(0); + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const Rd& xi_q = quadrature.point(i_q); + const double detT = T.jacobianDeterminant(); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + + { + size_t k = 0; + inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; + for (; k <= degree; ++k) { + inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; + } + } + + for (size_t k = 1; k < dimension; ++k) { + mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; + } + } +} + +template <typename ConformTransformationT> +void +_computeEjkMean(const QuadratureFormula<2>& quadrature, + const ConformTransformationT& T, + const TinyVector<2>& Xj, + const double Vi, + const size_t degree, + const size_t dimension, + SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + using Rd = TinyVector<2>; + + mean_of_ejk.fill(0); + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const Rd& xi_q = quadrature.point(i_q); + const double detT = [&] { + if constexpr (std::is_same_v<TriangleTransformation<2>, std::decay_t<decltype(T)>>) { + return T.jacobianDeterminant(); + } else { + return T.jacobianDeterminant(xi_q); + } + }(); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + + { + size_t k = 0; + inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; + for (; k <= degree; ++k) { + inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; + } + + for (size_t i_y = 1; i_y <= degree; ++i_y) { + const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; + for (size_t l = 0; l <= degree - i_y; ++l) { + inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; + } + } + } + + for (size_t k = 1; k < dimension; ++k) { + mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; + } + } +} + +template <typename ConformTransformationT> +void +_computeEjkMean(const QuadratureFormula<3>& quadrature, + const ConformTransformationT& T, + const TinyVector<3>& Xj, + const double Vi, + const size_t degree, + const size_t dimension, + SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + using Rd = TinyVector<3>; + mean_of_ejk.fill(0); + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const Rd& xi_q = quadrature.point(i_q); + const double detT = [&] { + if constexpr (std::is_same_v<TetrahedronTransformation, std::decay_t<decltype(T)>>) { + return T.jacobianDeterminant(); + } else { + return T.jacobianDeterminant(xi_q); + } + }(); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + const double z_zj = X_Xj[2]; + + { + size_t k = 0; + inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; + for (; k <= degree; ++k) { + inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; + } + + for (size_t i_y = 1; i_y <= degree; ++i_y) { + const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; + for (size_t l = 0; l <= degree - i_y; ++l) { + inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; + } + } + + for (size_t i_z = 1; i_z <= degree; ++i_z) { + const size_t begin_i_z_1 = i_z * (i_z + 1) * (i_z + 2) / 6 - 1; + for (size_t i_y = 0; i_y <= degree - i_z; ++i_y) { + const size_t begin_i_y_1 = (i_y * (2 * degree - i_y + 3)) / 2 + begin_i_z_1; + for (size_t l = 0; l <= degree - i_y - i_z; ++l) { + inv_Vj_wq_detJ_ek[k++] = z_zj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; + } + } + } + } + + for (size_t k = 1; k < dimension; ++k) { + mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; + } + } +} + +size_t +PolynomialReconstruction::_getNumberOfColumns( + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const +{ + size_t number_of_columns = 0; + for (auto discrete_function_variant_p : discrete_function_variant_list) { + number_of_columns += std::visit( + [](auto&& discrete_function) -> size_t { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + using data_type = std::decay_t<typename DiscreteFunctionT::data_type>; + if constexpr (std::is_same_v<data_type, double>) { + return 1; + } 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()); + } + return number_of_columns; +} + +template <MeshConcept MeshType> +std::vector<PolynomialReconstruction::MutableDiscreteFunctionDPkVariant> +PolynomialReconstruction::_createMutableDiscreteFunctionDPKVariantList( + const std::shared_ptr<const MeshType>& p_mesh, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const +{ + std::vector<MutableDiscreteFunctionDPkVariant> mutable_discrete_function_dpk_variant_list; + for (size_t i_discrete_function_variant = 0; i_discrete_function_variant < discrete_function_variant_list.size(); + ++i_discrete_function_variant) { + auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; + + std::visit( + [&](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + using DataType = std::remove_const_t<std::decay_t<typename DiscreteFunctionT::data_type>>; + mutable_discrete_function_dpk_variant_list.push_back( + DiscreteFunctionDPk<MeshType::Dimension, DataType>(p_mesh, m_descriptor.degree())); + } 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, m_descriptor.degree(), + discrete_function.size())); + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected discrete function type"); + // LCOV_EXCL_STOP + } + }, + discrete_function_variant->discreteFunction()); + } + + return mutable_discrete_function_dpk_variant_list; +} + template <MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::_build( @@ -120,35 +339,7 @@ PolynomialReconstruction::_build( using Rd = TinyVector<MeshType::Dimension>; - const size_t number_of_columns = [&]() { - size_t n = 0; - for (auto discrete_function_variant_p : discrete_function_variant_list) { - n += std::visit( - [](auto&& discrete_function) -> size_t { - using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - using data_type = std::decay_t<typename DiscreteFunctionT::data_type>; - if constexpr (std::is_same_v<data_type, double>) { - return 1; - } 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()); - } - return n; - }(); + const size_t number_of_columns = this->_getNumberOfColumns(discrete_function_variant_list); const size_t basis_dimension = DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(m_descriptor.degree()); @@ -178,31 +369,8 @@ PolynomialReconstruction::_build( Kokkos::Experimental::UniqueTokenScope::Global> tokens; - std::vector<MutableDiscreteFunctionDPkVariant> mutable_discrete_function_dpk_variant_list; - for (size_t i_discrete_function_variant = 0; i_discrete_function_variant < discrete_function_variant_list.size(); - ++i_discrete_function_variant) { - auto discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; - - std::visit( - [&](auto&& discrete_function) { - using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - using DataType = std::remove_const_t<std::decay_t<typename DiscreteFunctionT::data_type>>; - mutable_discrete_function_dpk_variant_list.push_back( - DiscreteFunctionDPk<MeshType::Dimension, DataType>(p_mesh, m_descriptor.degree())); - } 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, m_descriptor.degree(), - discrete_function.size())); - } else { - // LCOV_EXCL_START - throw UnexpectedError("unexpected discrete function type"); - // LCOV_EXCL_STOP - } - }, - discrete_function_variant->discreteFunction()); - } + auto mutable_discrete_function_dpk_variant_list = + this->_createMutableDiscreteFunctionDPKVariantList(p_mesh, discrete_function_variant_list); SmallArray<SmallMatrix<double>> A_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallMatrix<double>> B_pool(Kokkos::DefaultExecutionSpace::concurrency()); @@ -310,39 +478,11 @@ PolynomialReconstruction::_build( } } } else { - SmallArray<double> inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t]; - SmallArray<double> mean_j_of_ejk = mean_j_of_ejk_pool[t]; - SmallArray<double> mean_i_of_ejk = mean_i_of_ejk_pool[t]; + SmallArray<double>& inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t]; + SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; if constexpr (MeshType::Dimension == 1) { - auto compute_mean_ejk = [&inv_Vj_wq_detJ_ek](const auto& quadrature, const auto& T, const Rd& Xj, - const double Vi, const size_t degree, const size_t dimension, - SmallArray<double>& mean_of_ejk) { - mean_of_ejk.fill(0); - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const Rd& xi_q = quadrature.point(i_q); - const double detT = T.jacobianDeterminant(); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - - { - size_t k = 0; - inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; - for (; k <= degree; ++k) { - inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; - } - } - - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; - } - } - }; - const Rd& Xj = xj[cell_j_id]; if (cell_type[cell_j_id] == CellType::Line) { @@ -353,7 +493,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{m_descriptor.degree()}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_j_of_ejk); } else { throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_j_id])}); @@ -370,8 +511,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - mean_i_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_i_of_ejk); } else { throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_i_id])}); @@ -382,48 +523,6 @@ PolynomialReconstruction::_build( } } } else if constexpr (MeshType::Dimension == 2) { - auto compute_mean_ejk = [&inv_Vj_wq_detJ_ek](const auto& quadrature, const auto& T, const Rd& Xj, - const double Vi, const size_t degree, const size_t dimension, - SmallArray<double>& mean_of_ejk) { - mean_of_ejk.fill(0); - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const Rd& xi_q = quadrature.point(i_q); - const double detT = [&] { - if constexpr (std::is_same_v<TriangleTransformation<2>, std::decay_t<decltype(T)>>) { - return T.jacobianDeterminant(); - } else { - return T.jacobianDeterminant(xi_q); - } - }(); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - const double y_yj = X_Xj[1]; - - { - size_t k = 0; - inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; - for (; k <= degree; ++k) { - inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; - } - - for (size_t i_y = 1; i_y <= degree; ++i_y) { - const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; - for (size_t l = 0; l <= degree - i_y; ++l) { - inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; - } - } - } - - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; - } - } - }; - const Rd& Xj = xj[cell_j_id]; if (cell_type[cell_j_id] == CellType::Triangle) { @@ -434,7 +533,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_j_of_ejk); } else if (cell_type[cell_j_id] == CellType::Quadrangle) { auto cell_node = cell_to_node_matrix[cell_j_id]; @@ -444,7 +544,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getSquareFormula( GaussLegendreQuadratureDescriptor{m_descriptor.degree() + 1}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_j_of_ejk); } else { throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_j_id])}); @@ -461,8 +562,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - mean_i_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_i_of_ejk); } else if (cell_type[cell_i_id] == CellType::Quadrangle) { const SquareTransformation<2> T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]]}; @@ -470,8 +571,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getSquareFormula( GaussLegendreQuadratureDescriptor{m_descriptor.degree() + 1}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - mean_i_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_i_of_ejk); } else { throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_i_id])}); @@ -483,59 +584,6 @@ PolynomialReconstruction::_build( } } else if constexpr (MeshType::Dimension == 3) { - auto compute_mean_ejk = [&inv_Vj_wq_detJ_ek](const auto& quadrature, const auto& T, const Rd& Xj, - const double Vi, const size_t degree, const size_t dimension, - SmallArray<double>& mean_of_ejk) { - mean_of_ejk.fill(0); - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const Rd& xi_q = quadrature.point(i_q); - const double detT = [&] { - if constexpr (std::is_same_v<TetrahedronTransformation, std::decay_t<decltype(T)>>) { - return T.jacobianDeterminant(); - } else { - return T.jacobianDeterminant(xi_q); - } - }(); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - const double y_yj = X_Xj[1]; - const double z_zj = X_Xj[2]; - - { - size_t k = 0; - inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; - for (; k <= degree; ++k) { - inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; - } - - for (size_t i_y = 1; i_y <= degree; ++i_y) { - const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; - for (size_t l = 0; l <= degree - i_y; ++l) { - inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; - } - } - - for (size_t i_z = 1; i_z <= degree; ++i_z) { - const size_t begin_i_z_1 = i_z * (i_z + 1) * (i_z + 2) / 6 - 1; - for (size_t i_y = 0; i_y <= degree - i_z; ++i_y) { - const size_t begin_i_y_1 = (i_y * (2 * degree - i_y + 3)) / 2 + begin_i_z_1; - for (size_t l = 0; l <= degree - i_y - i_z; ++l) { - inv_Vj_wq_detJ_ek[k++] = z_zj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; - } - } - } - } - - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; - } - } - }; - const Rd& Xj = xj[cell_j_id]; if (cell_type[cell_j_id] == CellType::Tetrahedron) { @@ -546,7 +594,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_j_of_ejk); } else if (cell_type[cell_j_id] == CellType::Prism) { auto cell_node = cell_to_node_matrix[cell_j_id]; @@ -557,7 +606,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_descriptor.degree() + 1}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_j_of_ejk); } else if (cell_type[cell_j_id] == CellType::Pyramid) { auto cell_node = cell_to_node_matrix[cell_j_id]; @@ -568,7 +618,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_descriptor.degree() + 1}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_j_of_ejk); } else if (cell_type[cell_j_id] == CellType::Hexahedron) { auto cell_node = cell_to_node_matrix[cell_j_id]; @@ -579,7 +630,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getCubeFormula( GaussLegendreQuadratureDescriptor{m_descriptor.degree() + 1}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, mean_j_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_j_of_ejk); } else { throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_j_id])}); @@ -597,8 +649,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - mean_i_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_i_of_ejk); } else if (cell_type[cell_i_id] == CellType::Prism) { const PrismTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], // @@ -607,8 +659,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_descriptor.degree() + 1}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - mean_i_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_i_of_ejk); } else if (cell_type[cell_i_id] == CellType::Pyramid) { const PyramidTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]], @@ -617,8 +669,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_descriptor.degree() + 1}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - mean_i_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_i_of_ejk); } else if (cell_type[cell_i_id] == CellType::Hexahedron) { const CubeTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]], @@ -627,8 +679,8 @@ PolynomialReconstruction::_build( const auto& quadrature = QuadratureManager::instance().getCubeFormula( GaussLegendreQuadratureDescriptor{m_descriptor.degree() + 1}); - compute_mean_ejk(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - mean_i_of_ejk); + _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, + inv_Vj_wq_detJ_ek, mean_i_of_ejk); } else { throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_i_id])}); diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index c0da0f260..f52314872 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -29,242 +29,20 @@ class PolynomialReconstruction const PolynomialReconstructionDescriptor m_descriptor; + size_t _getNumberOfColumns( + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + + template <MeshConcept MeshType> + std::vector<MutableDiscreteFunctionDPkVariant> _createMutableDiscreteFunctionDPKVariantList( + const std::shared_ptr<const MeshType>& p_mesh, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + template <MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> _build( const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; public: - template <MeshConcept MeshType, typename DataType> - PUGS_INLINE DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> - build(const MeshType& mesh, const DiscreteFunctionP0<DataType> p0_function) const - { - using Rd = TinyVector<MeshType::Dimension>; - - const size_t degree = 1; - const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity()); - - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - auto cell_is_owned = mesh.connectivity().cellIsOwned(); - - const size_t max_stencil_size = [&]() { - size_t max_size = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto stencil_cell_list = stencil[cell_id]; - if (cell_is_owned[cell_id] and stencil_cell_list.size() > max_size) { - max_size = stencil_cell_list.size(); - } - } - return max_size; - }(); - - Kokkos::Experimental::UniqueToken<Kokkos::DefaultExecutionSpace::execution_space, - Kokkos::Experimental::UniqueTokenScope::Global> - tokens; - - DiscreteFunctionDPk<MeshType::Dimension, std::remove_const_t<DataType>> dpk{mesh.meshVariant(), degree}; - - if constexpr (is_polygonal_mesh_v<MeshType>) { - if constexpr (std::is_arithmetic_v<DataType>) { - SmallArray<SmallMatrix<double>> A_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallVector<double>> b_pool(Kokkos::DefaultExecutionSpace::concurrency()); - for (size_t i = 0; i < A_pool.size(); ++i) { - A_pool[i] = SmallMatrix<double>(max_stencil_size, MeshType::Dimension); - b_pool[i] = SmallVector<double>(max_stencil_size); - } - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { - if (cell_is_owned[cell_j_id]) { - const int32_t t = tokens.acquire(); - - auto stencil_cell_list = stencil[cell_j_id]; - - ShrinkVectorView b(b_pool[t], stencil_cell_list.size()); - - const double pj = p0_function[cell_j_id]; - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - - b[i] = p0_function[cell_i_id] - pj; - } - - ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); - - const Rd& Xj = xj[cell_j_id]; - - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const Rd Xi_Xj = xj[cell_i_id] - Xj; - for (size_t l = 0; l < MeshType::Dimension; ++l) { - A(i, l) = Xi_Xj[l]; - } - } - - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); - for (size_t l = 0; l < MeshType::Dimension; ++l) { - A(i, l) *= weight; - } - b[i] *= weight; - } - - SmallVector<double> x(MeshType::Dimension); - Givens::solveInPlace(A, x, b); - - auto dpk_j = dpk.coefficients(cell_j_id); - - dpk_j[0] = pj; - - for (size_t l = 0; l < MeshType::Dimension; ++l) { - dpk_j[1 + l] = x[l]; - } - - tokens.release(t); - } - }); - } else if constexpr (is_tiny_vector_v<DataType>) { - SmallArray<SmallMatrix<double>> A_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallMatrix<double>> B_pool(Kokkos::DefaultExecutionSpace::concurrency()); - for (size_t i = 0; i < A_pool.size(); ++i) { - A_pool[i] = SmallMatrix<double>(max_stencil_size, MeshType::Dimension); - B_pool[i] = SmallMatrix<double>(max_stencil_size, DataType::Dimension); - } - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { - if (cell_is_owned[cell_j_id]) { - const int32_t t = tokens.acquire(); - - auto stencil_cell_list = stencil[cell_j_id]; - ShrinkMatrixView B(B_pool[t], stencil_cell_list.size()); - - const DataType& pj = p0_function[cell_j_id]; - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const DataType& pi_pj = p0_function[cell_i_id] - pj; - for (size_t k = 0; k < DataType::Dimension; ++k) { - B(i, k) = pi_pj[k]; - } - } - - ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); - - const Rd& Xj = xj[cell_j_id]; - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const Rd Xi_Xj = xj[cell_i_id] - Xj; - for (size_t l = 0; l < MeshType::Dimension; ++l) { - A(i, l) = Xi_Xj[l]; - } - } - - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); - for (size_t l = 0; l < MeshType::Dimension - 1; ++l) { - A(i, l) *= weight; - } - for (size_t l = 0; l < DataType::Dimension; ++l) { - B(i, l) *= weight; - } - } - - TinyMatrix<MeshType::Dimension, DataType::Dimension> X; - Givens::solveCollectionInPlace(A, X, B); - - auto dpk_j = dpk.coefficients(cell_j_id); - - dpk_j[0] = pj; - 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, k); - } - } - - tokens.release(t); - } - }); - } else if constexpr (is_tiny_matrix_v<DataType>) { - SmallArray<SmallMatrix<double>> A_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallMatrix<double>> B_pool(Kokkos::DefaultExecutionSpace::concurrency()); - for (size_t i = 0; i < A_pool.size(); ++i) { - A_pool[i] = SmallMatrix<double>(max_stencil_size, MeshType::Dimension); - B_pool[i] = SmallMatrix<double>(max_stencil_size, DataType::Dimension); - } - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { - if (cell_is_owned[cell_j_id]) { - const int32_t t = tokens.acquire(); - - auto stencil_cell_list = stencil[cell_j_id]; - ShrinkMatrixView B(B_pool[t], stencil_cell_list.size()); - - const DataType& pj = p0_function[cell_j_id]; - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const DataType& pi_pj = p0_function[cell_i_id] - pj; - for (size_t k = 0; k < DataType::NumberOfRows; ++k) { - for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - B(i, k * DataType::NumberOfColumns + l) = pi_pj(k, l); - } - } - } - - ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); - - const Rd& Xj = xj[cell_j_id]; - - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const Rd Xi_Xj = xj[cell_i_id] - Xj; - for (size_t l = 0; l < MeshType::Dimension; ++l) { - A(i, l) = Xi_Xj[l]; - } - } - - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); - for (size_t l = 0; l < MeshType::Dimension - 1; ++l) { - A(i, l) *= weight; - } - for (size_t l = 0; l < DataType::Dimension; ++l) { - B(i, l) *= weight; - } - } - - TinyMatrix<MeshType::Dimension, DataType::Dimension> X; - Givens::solveCollectionInPlace(A, X, B); - - auto dpk_j = dpk.coefficients(cell_j_id); - dpk_j[0] = pj; - - 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, k * DataType::NumberOfColumns + l); - } - } - } - - tokens.release(t); - } - }); - } else { - throw NotImplementedError("dealing with " + demangle<DataType>()); - } - } - - synchronize(dpk.cellArrays()); - return dpk; - } - [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> build( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index 42ec98cc9..3d255a547 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -3,57 +3,6 @@ #include <scheme/PolynomialReconstruction.hpp> #include <utils/Timer.hpp> -void -test_reconstruction(const std::shared_ptr<const MeshVariant>& mesh_v, - const std::shared_ptr<const DiscreteFunctionVariant>& discrete_function_v) -{ - std::cout << "** one variable at a time (30 times)\n"; - PolynomialReconstructionDescriptor descriptor{1}; - - std::visit( - [&](auto&& p_mesh) { - std::visit( - [&](auto&& discrete_function) { - using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - PolynomialReconstruction reconstruction{descriptor}; - if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - Timer t; - for (size_t i = 0; i < 30; ++i) { - auto res = reconstruction.build(*p_mesh, discrete_function); - } - t.pause(); - std::cout << "t = " << t << '\n'; - } - }, - discrete_function_v->discreteFunction()); - }, - mesh_v->variant()); - - std::cout << "finished!\n"; -} - -void -test_reconstruction_one(const std::shared_ptr<const MeshVariant>& mesh_v, - const std::shared_ptr<const DiscreteFunctionVariant>& discrete_function_v) -{ - PolynomialReconstructionDescriptor descriptor{1}; - std::visit( - [&](auto&& p_mesh) { - std::visit( - [&](auto&& discrete_function) { - using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - PolynomialReconstruction reconstruction{descriptor}; - if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - for (size_t i = 0; i < 30; ++i) { - auto res = reconstruction.build(*p_mesh, discrete_function); - } - } - }, - discrete_function_v->discreteFunction()); - }, - mesh_v->variant()); -} - void test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) { diff --git a/src/scheme/test_reconstruction.hpp b/src/scheme/test_reconstruction.hpp index f74bf0781..6620acd44 100644 --- a/src/scheme/test_reconstruction.hpp +++ b/src/scheme/test_reconstruction.hpp @@ -7,9 +7,6 @@ #include <vector> -void test_reconstruction(const std::shared_ptr<const MeshVariant>& mesh_v, - const std::shared_ptr<const DiscreteFunctionVariant>& discrete_function_variant_list); - void test_reconstruction( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list); -- GitLab From e00336ad14880b98fbf6615ce4afd653bc44157b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 18 Jul 2024 18:59:19 +0200 Subject: [PATCH 048/122] Continue code clean-up and improve genericity --- src/scheme/PolynomialReconstruction.cpp | 355 ++++++++++-------------- src/scheme/PolynomialReconstruction.hpp | 15 - tests/test_PolynomialReconstruction.cpp | 1 + 3 files changed, 144 insertions(+), 227 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index f9e55f19c..c20fbc897 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -1,18 +1,26 @@ #include <scheme/PolynomialReconstruction.hpp> -#include <scheme/DiscreteFunctionUtils.hpp> -#include <scheme/DiscreteFunctionVariant.hpp> - +#include <algebra/Givens.hpp> +#include <algebra/ShrinkMatrixView.hpp> +#include <algebra/ShrinkVectorView.hpp> +#include <algebra/SmallMatrix.hpp> #include <analysis/GaussLegendreQuadratureDescriptor.hpp> #include <analysis/GaussQuadratureDescriptor.hpp> #include <analysis/QuadratureFormula.hpp> #include <analysis/QuadratureManager.hpp> #include <geometry/CubeTransformation.hpp> +#include <geometry/LineTransformation.hpp> #include <geometry/PrismTransformation.hpp> #include <geometry/PyramidTransformation.hpp> #include <geometry/SquareTransformation.hpp> #include <geometry/TetrahedronTransformation.hpp> #include <geometry/TriangleTransformation.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/StencilManager.hpp> +#include <scheme/DiscreteFunctionDPkVariant.hpp> +#include <scheme/DiscreteFunctionUtils.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant { @@ -261,6 +269,126 @@ _computeEjkMean(const QuadratureFormula<3>& quadrature, } } +template <typename NodeListT, size_t Dimension> +void +_computeEjkMean(const TinyVector<Dimension>& Xj, + const NodeValue<const TinyVector<Dimension>>& xr, + const NodeListT& node_list, + const CellType& cell_type, + const double Vi, + const size_t degree, + const size_t basis_dimension, + SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{} + +template <typename NodeListT> +void +_computeEjkMean(const TinyVector<1>& Xj, + const NodeValue<const TinyVector<1>>& xr, + const NodeListT& node_list, + const CellType& cell_type, + const double Vi, + const size_t degree, + const size_t basis_dimension, + SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + if (cell_type == CellType::Line) { + const LineTransformation<1> T{xr[node_list[0]], xr[node_list[1]]}; + + const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } +} + +template <typename NodeListT> +void +_computeEjkMean(const TinyVector<2>& Xj, + const NodeValue<const TinyVector<2>>& xr, + const NodeListT& node_list, + const CellType& cell_type, + const double Vi, + const size_t degree, + const size_t basis_dimension, + SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + switch (cell_type) { + case CellType::Triangle: { + const TriangleTransformation<2> T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]]}; + const auto& quadrature = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + break; + } + case CellType::Quadrangle: { + const SquareTransformation<2> T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]]}; + const auto& quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } +} + +template <typename NodeListT> +void +_computeEjkMean(const TinyVector<3>& Xj, + const NodeValue<const TinyVector<3>>& xr, + const NodeListT& node_list, + const CellType& cell_type, + const double Vi, + const size_t degree, + const size_t basis_dimension, + SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + if (cell_type == CellType::Tetrahedron) { + const TetrahedronTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]]}; + + const auto& quadrature = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else if (cell_type == CellType::Prism) { + const PrismTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], // + xr[node_list[3]], xr[node_list[4]], xr[node_list[5]]}; + + const auto& quadrature = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else if (cell_type == CellType::Pyramid) { + const PyramidTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]], + xr[node_list[4]]}; + + const auto& quadrature = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else if (cell_type == CellType::Hexahedron) { + const CubeTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]], + xr[node_list[4]], xr[node_list[5]], xr[node_list[6]], xr[node_list[7]]}; + + const auto& quadrature = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } +} + size_t PolynomialReconstruction::_getNumberOfColumns( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const @@ -374,7 +502,7 @@ PolynomialReconstruction::_build( SmallArray<SmallMatrix<double>> A_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallMatrix<double>> B_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallArray<double>> G_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallVector<double>> G_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallMatrix<double>> X_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallArray<double>> inv_Vj_wq_detJ_ek_pool(Kokkos::DefaultExecutionSpace::concurrency()); @@ -384,7 +512,7 @@ PolynomialReconstruction::_build( for (size_t i = 0; i < A_pool.size(); ++i) { A_pool[i] = SmallMatrix<double>(max_stencil_size, basis_dimension - 1); B_pool[i] = SmallMatrix<double>(max_stencil_size, number_of_columns); - G_pool[i] = SmallArray<double>(basis_dimension - 1); + G_pool[i] = SmallVector<double>(basis_dimension - 1); X_pool[i] = SmallMatrix<double>(basis_dimension - 1, number_of_columns); inv_Vj_wq_detJ_ek_pool[i] = SmallArray<double>(basis_dimension); @@ -482,217 +610,20 @@ PolynomialReconstruction::_build( SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; - if constexpr (MeshType::Dimension == 1) { - const Rd& Xj = xj[cell_j_id]; - - if (cell_type[cell_j_id] == CellType::Line) { - auto cell_node = cell_to_node_matrix[cell_j_id]; - - const LineTransformation<1> T{xr[cell_node[0]], xr[cell_node[1]]}; - - const auto& quadrature = - QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{m_descriptor.degree()}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_j_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_j_id])}); - } - - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - - auto cell_node = cell_to_node_matrix[cell_i_id]; - - if (cell_type[cell_i_id] == CellType::Line) { - const LineTransformation<1> T{xr[cell_node[0]], xr[cell_node[1]]}; - - const auto& quadrature = - QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_i_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_i_id])}); - } - - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; - } - } - } else if constexpr (MeshType::Dimension == 2) { - const Rd& Xj = xj[cell_j_id]; - - if (cell_type[cell_j_id] == CellType::Triangle) { - auto cell_node = cell_to_node_matrix[cell_j_id]; - - const TriangleTransformation<2> T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]]}; - - const auto& quadrature = - QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_j_of_ejk); - - } else if (cell_type[cell_j_id] == CellType::Quadrangle) { - auto cell_node = cell_to_node_matrix[cell_j_id]; - - const SquareTransformation<2> T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]]}; - - const auto& quadrature = QuadratureManager::instance().getSquareFormula( - GaussLegendreQuadratureDescriptor{m_descriptor.degree() + 1}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_j_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_j_id])}); - } - - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - - auto cell_node = cell_to_node_matrix[cell_i_id]; - - if (cell_type[cell_i_id] == CellType::Triangle) { - const TriangleTransformation<2> T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]]}; - - const auto& quadrature = - QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_i_of_ejk); - - } else if (cell_type[cell_i_id] == CellType::Quadrangle) { - const SquareTransformation<2> T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]]}; - - const auto& quadrature = QuadratureManager::instance().getSquareFormula( - GaussLegendreQuadratureDescriptor{m_descriptor.degree() + 1}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_i_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_i_id])}); - } - - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; - } - } - - } else if constexpr (MeshType::Dimension == 3) { - const Rd& Xj = xj[cell_j_id]; - - if (cell_type[cell_j_id] == CellType::Tetrahedron) { - auto cell_node = cell_to_node_matrix[cell_j_id]; - - const TetrahedronTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]]}; - - const auto& quadrature = - QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_j_of_ejk); - - } else if (cell_type[cell_j_id] == CellType::Prism) { - auto cell_node = cell_to_node_matrix[cell_j_id]; - - const PrismTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], // - xr[cell_node[3]], xr[cell_node[4]], xr[cell_node[5]]}; - - const auto& quadrature = - QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_descriptor.degree() + 1}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_j_of_ejk); - - } else if (cell_type[cell_j_id] == CellType::Pyramid) { - auto cell_node = cell_to_node_matrix[cell_j_id]; - - const PyramidTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]], - xr[cell_node[4]]}; - - const auto& quadrature = - QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_descriptor.degree() + 1}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_j_of_ejk); - - } else if (cell_type[cell_j_id] == CellType::Hexahedron) { - auto cell_node = cell_to_node_matrix[cell_j_id]; - - const CubeTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]], - xr[cell_node[4]], xr[cell_node[5]], xr[cell_node[6]], xr[cell_node[7]]}; - - const auto& quadrature = QuadratureManager::instance().getCubeFormula( - GaussLegendreQuadratureDescriptor{m_descriptor.degree() + 1}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_j_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_j_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_j_id])}); - } - - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - - auto cell_node = cell_to_node_matrix[cell_i_id]; - - if (cell_type[cell_i_id] == CellType::Tetrahedron) { - const TetrahedronTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], - xr[cell_node[3]]}; - - const auto& quadrature = - QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_descriptor.degree()}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_i_of_ejk); - - } else if (cell_type[cell_i_id] == CellType::Prism) { - const PrismTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], // - xr[cell_node[3]], xr[cell_node[4]], xr[cell_node[5]]}; - - const auto& quadrature = - QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_descriptor.degree() + 1}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_i_of_ejk); - - } else if (cell_type[cell_i_id] == CellType::Pyramid) { - const PyramidTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]], - xr[cell_node[4]]}; - - const auto& quadrature = - QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_descriptor.degree() + 1}); - - _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_i_of_ejk); - - } else if (cell_type[cell_i_id] == CellType::Hexahedron) { - const CubeTransformation T{xr[cell_node[0]], xr[cell_node[1]], xr[cell_node[2]], xr[cell_node[3]], - xr[cell_node[4]], xr[cell_node[5]], xr[cell_node[6]], xr[cell_node[7]]}; + const Rd& Xj = xj[cell_j_id]; - const auto& quadrature = QuadratureManager::instance().getCubeFormula( - GaussLegendreQuadratureDescriptor{m_descriptor.degree() + 1}); + _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_j_id], cell_type[cell_j_id], Vj[cell_j_id], + m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_j_of_ejk); - _computeEjkMean(quadrature, T, Xj, Vj[cell_i_id], m_descriptor.degree(), basis_dimension, - inv_Vj_wq_detJ_ek, mean_i_of_ejk); + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type[cell_i_id])}); - } + _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], Vj[cell_i_id], + m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_i_of_ejk); - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; - } + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; } - - } else { - throw NotImplementedError("unexpected dimension"); } } @@ -717,7 +648,7 @@ PolynomialReconstruction::_build( if (m_descriptor.preconditioning()) { // Add column weighting preconditioning (increase the presition) - SmallArray<double>& G = G_pool[t]; + SmallVector<double>& G = G_pool[t]; for (size_t l = 0; l < basis_dimension - 1; ++l) { double g = 0; diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index f52314872..b2b855271 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -1,24 +1,9 @@ #ifndef POLYNOMIAL_RECONSTRUCTION_HPP #define POLYNOMIAL_RECONSTRUCTION_HPP -#include <algebra/ShrinkMatrixView.hpp> -#include <algebra/ShrinkVectorView.hpp> -#include <algebra/SmallMatrix.hpp> -#include <algebra/SmallVector.hpp> #include <mesh/MeshTraits.hpp> -#include <scheme/DiscreteFunctionDPkVariant.hpp> #include <scheme/PolynomialReconstructionDescriptor.hpp> -#warning MOVE TO .cpp WHEN FINISHED -#include <algebra/Givens.hpp> -#include <analysis/GaussLegendreQuadratureDescriptor.hpp> -#include <analysis/QuadratureFormula.hpp> -#include <analysis/QuadratureManager.hpp> -#include <geometry/LineTransformation.hpp> -#include <mesh/MeshData.hpp> -#include <mesh/MeshDataManager.hpp> -#include <mesh/StencilManager.hpp> - class DiscreteFunctionDPkVariant; class DiscreteFunctionVariant; diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 47a538f0d..b7c7019f5 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -11,6 +11,7 @@ #include <algebra/SmallVector.hpp> #include <mesh/Mesh.hpp> #include <mesh/MeshDataManager.hpp> +#include <scheme/DiscreteFunctionDPkVariant.hpp> #include <scheme/DiscreteFunctionP0.hpp> #include <scheme/DiscreteFunctionVariant.hpp> #include <scheme/PolynomialReconstruction.hpp> -- GitLab From 41ddf6589e326625ff45a9d1b6a43a12c313d19a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 23 Jul 2024 16:14:02 +0200 Subject: [PATCH 049/122] Allow output of ShrinkVectorView and ShrinkMatrixView --- src/algebra/ShrinkMatrixView.hpp | 15 +++++++++++++++ src/algebra/ShrinkVectorView.hpp | 14 ++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/algebra/ShrinkMatrixView.hpp b/src/algebra/ShrinkMatrixView.hpp index a93efc07a..a41089a0a 100644 --- a/src/algebra/ShrinkMatrixView.hpp +++ b/src/algebra/ShrinkMatrixView.hpp @@ -5,6 +5,8 @@ #include <utils/PugsMacros.hpp> #include <cstddef> +#include <iostream> +#include <utils/NaNHelper.hpp> template <typename MatrixType> class ShrinkMatrixView @@ -18,6 +20,19 @@ class ShrinkMatrixView const size_t m_nb_rows; public: + friend std::ostream& + operator<<(std::ostream& os, const ShrinkMatrixView& A) + { + for (size_t i = 0; i < A.numberOfRows(); ++i) { + os << i << '|'; + for (size_t j = 0; j < A.numberOfColumns(); ++j) { + os << ' ' << j << ':' << NaNHelper(A(i, j)); + } + os << '\n'; + } + return os; + } + PUGS_INLINE size_t numberOfRows() const noexcept { diff --git a/src/algebra/ShrinkVectorView.hpp b/src/algebra/ShrinkVectorView.hpp index dc5faa800..cdf786c5a 100644 --- a/src/algebra/ShrinkVectorView.hpp +++ b/src/algebra/ShrinkVectorView.hpp @@ -5,6 +5,8 @@ #include <utils/PugsMacros.hpp> #include <cstddef> +#include <iostream> +#include <utils/NaNHelper.hpp> template <typename VectorType> class ShrinkVectorView @@ -18,6 +20,18 @@ class ShrinkVectorView const size_t m_dimension; public: + friend std::ostream& + operator<<(std::ostream& os, const ShrinkVectorView& x) + { + if (x.size() > 0) { + os << 0 << ':' << NaNHelper(x[0]); + } + for (size_t i = 1; i < x.size(); ++i) { + os << ' ' << i << ':' << NaNHelper(x[i]); + } + return os; + } + PUGS_INLINE size_t dimension() const noexcept { -- GitLab From 18c5269423a1a902236e0fcfe504cb8bf1b717e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 23 Jul 2024 16:14:33 +0200 Subject: [PATCH 050/122] Add access to velocity for LineTransformation --- src/geometry/LineTransformation.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/geometry/LineTransformation.hpp b/src/geometry/LineTransformation.hpp index ae0b00a8a..c9f7def71 100644 --- a/src/geometry/LineTransformation.hpp +++ b/src/geometry/LineTransformation.hpp @@ -59,6 +59,13 @@ class LineTransformation return x[0] * m_velocity + m_shift; } + PUGS_INLINE + const TinyVector<Dimension>& + velocity() const + { + return m_velocity; + } + double velocityNorm() const { -- GitLab From 906a26f46d831b41d20506a7d319c158132f657d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 23 Jul 2024 16:16:21 +0200 Subject: [PATCH 051/122] Begin polynomial cell integration in 2D using divergence theorem This allows to compute integral of centered canonical basis using integrals over lines and thus valid for any polygonal cell types --- src/language/modules/SchemeModule.cpp | 12 ++ src/scheme/PolynomialReconstruction.cpp | 179 +++++++++++++++++++----- src/scheme/test_reconstruction.cpp | 37 +---- src/scheme/test_reconstruction.hpp | 3 +- 4 files changed, 165 insertions(+), 66 deletions(-) diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index 0f3dadaad..212afc552 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -682,6 +682,18 @@ SchemeModule::SchemeModule() )); +#warning REMOVE AFTER TESTS FINISHED + this->_addBuiltinFunction("test_reconstruction", + std::function( + + [](const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& + discrete_function_variant_list, + uint64_t degree) -> void { + test_reconstruction(discrete_function_variant_list, degree); + } + + )); + MathFunctionRegisterForVh{*this}; } diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index c20fbc897..0d0c829c7 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -270,17 +270,15 @@ _computeEjkMean(const QuadratureFormula<3>& quadrature, } template <typename NodeListT, size_t Dimension> -void -_computeEjkMean(const TinyVector<Dimension>& Xj, - const NodeValue<const TinyVector<Dimension>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{} +void _computeEjkMean(const TinyVector<Dimension>& Xj, + const NodeValue<const TinyVector<Dimension>>& xr, + const NodeListT& node_list, + const CellType& cell_type, + const double Vi, + const size_t degree, + const size_t basis_dimension, + SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk); template <typename NodeListT> void @@ -306,6 +304,86 @@ _computeEjkMean(const TinyVector<1>& Xj, } } +template <typename ConformTransformationT> +void +_computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, + const ConformTransformationT& T, + const TinyVector<2>& Xj, + const double Vi, + const size_t degree, + const size_t dimension, + SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + SmallArray<double>& mean_of_ejk) +{ + using Rd = TinyVector<2>; + + const double velocity_perp_e1 = T.velocity()[1]; + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const TinyVector<1> xi_q = quadrature.point(i_q); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + + { + size_t k = 0; + inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1 / Vi; + for (; k <= degree; ++k) { + inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k] = + x_xj * inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * ((1. * k) / (k + 1)); + } + + for (size_t i_y = 1; i_y <= degree; ++i_y) { + const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; + for (size_t l = 0; l <= degree - i_y; ++l) { + inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = y_yj * inv_Vj_alpha_p_1_wq_X_prime_orth_ek[begin_i_y_1 + l]; + } + } + } + + for (size_t k = 1; k < dimension; ++k) { + mean_of_ejk[k - 1] += inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k]; + } + } +} + +void +_computeEjkMeanByBoundary(const TinyVector<2>& Xj, + const CellId& cell_id, + const NodeValue<const TinyVector<2>>& xr, + const auto& cell_to_face_matrix, + const auto& face_to_node_matrix, + const auto& cell_face_is_reversed, + const CellValue<const double>& Vi, + const size_t degree, + const size_t basis_dimension, + SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + SmallArray<double>& mean_of_ejk) +{ + const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(degree + 1)); + + mean_of_ejk.fill(0); + auto cell_face_list = cell_to_face_matrix[cell_id]; + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + bool is_reversed = cell_face_is_reversed[cell_id][i_face]; + + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = face_to_node_matrix[face_id]; + if (is_reversed) { + const LineTransformation<2> T{xr[face_node_list[1]], xr[face_node_list[0]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, + inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); + } else { + const LineTransformation<2> T{xr[face_node_list[0]], xr[face_node_list[1]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, + inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); + } + } +} + template <typename NodeListT> void _computeEjkMean(const TinyVector<2>& Xj, @@ -481,6 +559,7 @@ PolynomialReconstruction::_build( auto cell_is_owned = mesh.connectivity().cellIsOwned(); auto cell_type = mesh.connectivity().cellType(); auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + auto cell_to_face_matrix = mesh.connectivity().cellToFaceMatrix(); const size_t max_stencil_size = [&]() { size_t max_size = 0; @@ -509,6 +588,9 @@ PolynomialReconstruction::_build( SmallArray<SmallArray<double>> mean_j_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallArray<double>> mean_i_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallArray<double>> inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool(Kokkos::DefaultExecutionSpace::concurrency()); + SmallArray<SmallArray<double>> mean_l_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); + for (size_t i = 0; i < A_pool.size(); ++i) { A_pool[i] = SmallMatrix<double>(max_stencil_size, basis_dimension - 1); B_pool[i] = SmallMatrix<double>(max_stencil_size, number_of_columns); @@ -518,15 +600,17 @@ PolynomialReconstruction::_build( inv_Vj_wq_detJ_ek_pool[i] = SmallArray<double>(basis_dimension); mean_j_of_ejk_pool[i] = SmallArray<double>(basis_dimension - 1); mean_i_of_ejk_pool[i] = SmallArray<double>(basis_dimension - 1); + + inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[i] = SmallArray<double>(basis_dimension); } #warning remove after rework - const bool FORCE_INTEGRATE = []() { + [[maybe_unused]] const int INTEGRATE = []() { auto value = std::getenv("INTEGRATE"); if (value == nullptr) { - return false; + return 0; } else { - return true; + return std::atoi(value); } }(); @@ -596,7 +680,7 @@ PolynomialReconstruction::_build( ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); - if ((m_descriptor.degree() == 1) and (not FORCE_INTEGRATE)) { + if ((m_descriptor.degree() == 1) and (INTEGRATE == 0)) { const Rd& Xj = xj[cell_j_id]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { const CellId cell_i_id = stencil_cell_list[i]; @@ -605,26 +689,59 @@ PolynomialReconstruction::_build( A(i, l) = Xi_Xj[l]; } } - } else { - SmallArray<double>& inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t]; - SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; - const Rd& Xj = xj[cell_j_id]; + } else if ((INTEGRATE == 2) or (INTEGRATE == 1)) { + if ((INTEGRATE == 2) and (MeshType::Dimension == 2)) { + if constexpr (MeshType::Dimension == 2) { + SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek = inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[t]; + SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; - _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_j_id], cell_type[cell_j_id], Vj[cell_j_id], - m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_j_of_ejk); + auto face_to_node_matrix = p_mesh->connectivity().faceToNodeMatrix(); + auto cell_face_is_reversed = p_mesh->connectivity().cellFaceIsReversed(); + const Rd& Xj = xj[cell_j_id]; - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; + _computeEjkMeanByBoundary(Xj, cell_j_id, xr, cell_to_face_matrix, face_to_node_matrix, + cell_face_is_reversed, Vj, m_descriptor.degree(), basis_dimension, + inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_j_of_ejk); - _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], Vj[cell_i_id], - m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_i_of_ejk); + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; + _computeEjkMeanByBoundary(Xj, cell_i_id, xr, cell_to_face_matrix, face_to_node_matrix, + cell_face_is_reversed, Vj, m_descriptor.degree(), basis_dimension, + inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_i_of_ejk); + + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; + } + } + } else { + throw UnexpectedError("invalid mesh dimension"); + } + } else { + SmallArray<double>& inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t]; + SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; + + const Rd& Xj = xj[cell_j_id]; + + _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_j_id], cell_type[cell_j_id], Vj[cell_j_id], + m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_j_of_ejk); + + for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + const CellId cell_i_id = stencil_cell_list[i]; + + _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], Vj[cell_i_id], + m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_i_of_ejk); + + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; + } } } + } else { + throw UnexpectedError("invalid integration strategy"); } if (m_descriptor.rowWeighting()) { @@ -679,12 +796,6 @@ PolynomialReconstruction::_build( Givens::solveCollectionInPlace(A, X, B); } - // Results are now written from the {ejk} basis to the {ek} basis - if (m_descriptor.degree() > 1) { - throw NotImplementedError( - "write the result from the {ejk} basis to the {ek} basis to get correct polynomial values"); - } - column_begin = 0; for (size_t i_dpk_variant = 0; i_dpk_variant < mutable_discrete_function_dpk_variant_list.size(); ++i_dpk_variant) { diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index 3d255a547..4ba54bda2 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -4,42 +4,17 @@ #include <utils/Timer.hpp> void -test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) +test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, + uint64_t degree) { - PolynomialReconstructionDescriptor descriptor{1}; - // { - // std::cout << "** variable list one by one (50 times)\n"; - // Timer t; - // for (auto discrete_function_v : discrete_function_variant_list) { - // std::visit( - // [&](auto&& discrete_function) { - // auto mesh_v = discrete_function.meshVariant(); - // std::visit( - // [&](auto&& p_mesh) { - // using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - // PolynomialReconstruction reconstruction{descriptor}; - // if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - // for (size_t i = 0; i < 50; ++i) { - // auto res = reconstruction.build(*p_mesh, discrete_function); - // } - // } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - // std::cout << "Omitting discrete function p0 vector!\n"; - // } - // }, - // mesh_v->variant()); - // }, - // discrete_function_v->discreteFunction()); - // } - // t.pause(); - // std::cout << "t = " << t << '\n'; - // } - + PolynomialReconstructionDescriptor descriptor{degree}; { - std::cout << "** variable list at once (50 times)\n"; + const size_t nb_loops = 1; + std::cout << "** variable list at once (" << nb_loops << " times)\n"; PolynomialReconstruction reconstruction{descriptor}; Timer t; - for (size_t i = 0; i < 50; ++i) { + for (size_t i = 0; i < nb_loops; ++i) { auto res = reconstruction.build(discrete_function_variant_list); } t.pause(); diff --git a/src/scheme/test_reconstruction.hpp b/src/scheme/test_reconstruction.hpp index 6620acd44..d2fc5bd03 100644 --- a/src/scheme/test_reconstruction.hpp +++ b/src/scheme/test_reconstruction.hpp @@ -8,6 +8,7 @@ #include <vector> void test_reconstruction( - const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list); + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, + uint64_t degree = 1); #endif // TEST_RECONSTRUCTION_HPP -- GitLab From 3c849f511b28b8c955da18215ce5bee802b439c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 25 Jul 2024 19:16:36 +0200 Subject: [PATCH 052/122] Improve reconstruction for polynomials of degree greater than 1 - The code is really crappy - Missing basis change have been implemented - Add few tests (seems to work at least for polynomials of degree 2) Still need a lot of clean-up and (performances) tests --- src/mesh/StencilBuilder.cpp | 86 +- src/mesh/StencilBuilder.hpp | 4 +- src/mesh/StencilManager.cpp | 22 +- src/mesh/StencilManager.hpp | 25 +- src/scheme/PolynomialReconstruction.cpp | 62 +- .../PolynomialReconstructionDescriptor.hpp | 23 +- src/scheme/test_reconstruction.cpp | 2 +- tests/CMakeLists.txt | 1 + tests/test_PolynomialReconstruction.cpp | 4 +- ...est_PolynomialReconstructionDescriptor.cpp | 14 +- ...test_QuadraticPolynomialReconstruction.cpp | 1198 +++++++++++++++++ 11 files changed, 1397 insertions(+), 44 deletions(-) create mode 100644 tests/test_QuadraticPolynomialReconstruction.cpp diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 443130400..34015395a 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -1,6 +1,11 @@ #include <mesh/Connectivity.hpp> #include <mesh/StencilBuilder.hpp> +#include <utils/Messenger.hpp> + +#warning remove after rework +#include <set> + template <typename ConnectivityType> Array<const uint32_t> StencilBuilder::_getRowMap(const ConnectivityType& connectivity) const @@ -104,26 +109,91 @@ StencilBuilder::_getColumnIndices(const ConnectivityType& connectivity, const Ar template <typename ConnectivityType> Stencil -StencilBuilder::_build(const ConnectivityType& connectivity) const +StencilBuilder::_build(const ConnectivityType& connectivity, size_t number_of_layers) const { - Array<const uint32_t> row_map = this->_getRowMap(connectivity); - Array<const uint32_t> column_indices = this->_getColumnIndices(connectivity, row_map); + if (number_of_layers == 1) { + Array<const uint32_t> row_map = this->_getRowMap(connectivity); + Array<const uint32_t> column_indices = this->_getColumnIndices(connectivity, row_map); + + return ConnectivityMatrix{row_map, column_indices}; + } else { + if (number_of_layers > 2) { + throw NotImplementedError("number of layers too large"); + } + if (parallel::size() > 1) { + throw NotImplementedError("stencils with more than 1 layers are not defined in parallel"); + } + + auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); + auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + + auto cell_is_owned = connectivity.cellIsOwned(); + auto cell_number = connectivity.cellNumber(); + + Array<uint32_t> row_map{connectivity.numberOfCells() + 1}; + row_map[0] = 0; + + std::vector<CellId> column_indices_vector; - return ConnectivityMatrix{row_map, column_indices}; + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + if (cell_is_owned[cell_id]) { + std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + + for (size_t i_node_1 = 0; i_node_1 < cell_to_node_matrix[cell_id].size(); ++i_node_1) { + const NodeId layer_1_node_id = cell_to_node_matrix[cell_id][i_node_1]; + + for (size_t i_cell_1 = 0; i_cell_1 < node_to_cell_matrix[layer_1_node_id].size(); ++i_cell_1) { + CellId cell_1_id = node_to_cell_matrix[layer_1_node_id][i_cell_1]; + + for (size_t i_node_2 = 0; i_node_2 < cell_to_node_matrix[cell_1_id].size(); ++i_node_2) { + const NodeId layer_2_node_id = cell_to_node_matrix[cell_1_id][i_node_2]; + + for (size_t i_cell_2 = 0; i_cell_2 < node_to_cell_matrix[layer_2_node_id].size(); ++i_cell_2) { + CellId cell_2_id = node_to_cell_matrix[layer_2_node_id][i_cell_2]; + + if (cell_2_id != cell_id) { + cell_set.insert(cell_2_id); + } + } + } + } + } + + for (auto stencil_cell_id : cell_set) { + column_indices_vector.push_back(stencil_cell_id); + } + row_map[cell_id + 1] = row_map[cell_id] + cell_set.size(); + } + } + + if (row_map[row_map.size() - 1] != column_indices_vector.size()) { + throw UnexpectedError("incorrect stencil size"); + } + + Array<uint32_t> column_indices(row_map[row_map.size() - 1]); + column_indices.fill(std::numeric_limits<uint32_t>::max()); + + for (size_t i = 0; i < column_indices.size(); ++i) { + column_indices[i] = column_indices_vector[i]; + } + + return ConnectivityMatrix{row_map, column_indices}; + } } Stencil -StencilBuilder::build(const IConnectivity& connectivity) const +StencilBuilder::build(const IConnectivity& connectivity, size_t number_of_layers) const { switch (connectivity.dimension()) { case 1: { - return StencilBuilder::_build(dynamic_cast<const Connectivity<1>&>(connectivity)); + return StencilBuilder::_build(dynamic_cast<const Connectivity<1>&>(connectivity), number_of_layers); } case 2: { - return StencilBuilder::_build(dynamic_cast<const Connectivity<2>&>(connectivity)); + return StencilBuilder::_build(dynamic_cast<const Connectivity<2>&>(connectivity), number_of_layers); } case 3: { - return StencilBuilder::_build(dynamic_cast<const Connectivity<3>&>(connectivity)); + return StencilBuilder::_build(dynamic_cast<const Connectivity<3>&>(connectivity), number_of_layers); } default: { throw UnexpectedError("invalid connectivity dimension"); diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index 1743a2d71..53918e64a 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -15,10 +15,10 @@ class StencilBuilder const Array<const uint32_t>& row_map) const; template <typename ConnectivityType> - Stencil _build(const ConnectivityType& connectivity) const; + Stencil _build(const ConnectivityType& connectivity, size_t number_of_layers) const; friend class StencilManager; - Stencil build(const IConnectivity& connectivity) const; + Stencil build(const IConnectivity& connectivity, size_t number_of_layers) const; public: StencilBuilder() = default; diff --git a/src/mesh/StencilManager.cpp b/src/mesh/StencilManager.cpp index 58190f002..120a95e0d 100644 --- a/src/mesh/StencilManager.cpp +++ b/src/mesh/StencilManager.cpp @@ -18,11 +18,11 @@ StencilManager::destroy() Assert(m_instance != nullptr, "StencilManager was not created"); // LCOV_EXCL_START - if (m_instance->m_connectivity_id_to_stencil_map.size() > 0) { + if (m_instance->m_stored_stencil_map.size() > 0) { std::stringstream error; error << ": some connectivities are still registered\n"; - for (const auto& [id, stencil_p] : m_instance->m_connectivity_id_to_stencil_map) { - error << " - connectivity of id " << rang::fgB::magenta << id << rang::style::reset << '\n'; + for (const auto& [key, stencil_p] : m_instance->m_stored_stencil_map) { + error << " - connectivity of id " << rang::fgB::magenta << key.connectivity_id << rang::style::reset << '\n'; } throw UnexpectedError(error.str()); // LCOV_EXCL_STOP @@ -32,14 +32,14 @@ StencilManager::destroy() } const Stencil& -StencilManager::getStencil(const IConnectivity& connectivity) +StencilManager::getStencil(const IConnectivity& connectivity, size_t degree) { - if (not m_connectivity_id_to_stencil_map.contains(connectivity.id())) { - m_connectivity_id_to_stencil_map[connectivity.id()] = - std::make_shared<Stencil>(StencilBuilder{}.build(connectivity)); + if (not m_stored_stencil_map.contains(Key{connectivity.id(), degree})) { + m_stored_stencil_map[Key{connectivity.id(), degree}] = + std::make_shared<Stencil>(StencilBuilder{}.build(connectivity, degree)); } - return *m_connectivity_id_to_stencil_map.at(connectivity.id()); + return *m_stored_stencil_map.at(Key{connectivity.id(), degree}); } void @@ -48,9 +48,9 @@ StencilManager::deleteConnectivity(const size_t connectivity_id) bool has_removed = false; do { has_removed = false; - for (const auto& [id, p_stencil] : m_connectivity_id_to_stencil_map) { - if (connectivity_id == id) { - m_connectivity_id_to_stencil_map.erase(connectivity_id); + for (const auto& [key, p_stencil] : m_stored_stencil_map) { + if (connectivity_id == key.connectivity_id) { + m_stored_stencil_map.erase(key); has_removed = true; break; } diff --git a/src/mesh/StencilManager.hpp b/src/mesh/StencilManager.hpp index d1503e60f..f2912be1f 100644 --- a/src/mesh/StencilManager.hpp +++ b/src/mesh/StencilManager.hpp @@ -15,7 +15,28 @@ class StencilManager static StencilManager* m_instance; - std::unordered_map<size_t, std::shared_ptr<const Stencil>> m_connectivity_id_to_stencil_map; + struct Key + { + size_t connectivity_id; + size_t degree; + + PUGS_INLINE bool + operator==(const Key& k) const + { + return (connectivity_id == k.connectivity_id) and (degree == k.degree); + } + }; + struct HashKey + { + size_t + operator()(const Key& key) const + { + return (std::hash<decltype(Key::connectivity_id)>()(key.connectivity_id)) ^ + (std::hash<decltype(Key::degree)>()(key.degree) << 1); + } + }; + + std::unordered_map<Key, std::shared_ptr<const Stencil>, HashKey> m_stored_stencil_map; public: static void create(); @@ -31,7 +52,7 @@ class StencilManager void deleteConnectivity(const size_t connectivity_id); - const Stencil& getStencil(const IConnectivity& i_connectivity); + const Stencil& getStencil(const IConnectivity& i_connectivity, size_t degree = 1); StencilManager(const StencilManager&) = delete; StencilManager(StencilManager&&) = delete; diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 0d0c829c7..270d663c3 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -253,7 +253,7 @@ _computeEjkMean(const QuadratureFormula<3>& quadrature, } for (size_t i_z = 1; i_z <= degree; ++i_z) { - const size_t begin_i_z_1 = i_z * (i_z + 1) * (i_z + 2) / 6 - 1; + const size_t begin_i_z_1 = ((i_z - 1) * (3 * (degree + 2) * (degree + 3) + (i_z - (3 * degree + 8)) * i_z)) / 6; for (size_t i_y = 0; i_y <= degree - i_z; ++i_y) { const size_t begin_i_y_1 = (i_y * (2 * degree - i_y + 3)) / 2 + begin_i_z_1; for (size_t l = 0; l <= degree - i_y - i_z; ++l) { @@ -550,7 +550,7 @@ PolynomialReconstruction::_build( const size_t basis_dimension = DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(m_descriptor.degree()); - const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity()); + const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity(), m_descriptor.degree()); auto xr = mesh.xr(); auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); @@ -604,16 +604,6 @@ PolynomialReconstruction::_build( inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[i] = SmallArray<double>(basis_dimension); } -#warning remove after rework - [[maybe_unused]] const int INTEGRATE = []() { - auto value = std::getenv("INTEGRATE"); - if (value == nullptr) { - return 0; - } else { - return std::atoi(value); - } - }(); - parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { if (cell_is_owned[cell_j_id]) { @@ -680,7 +670,8 @@ PolynomialReconstruction::_build( ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); - if ((m_descriptor.degree() == 1) and (INTEGRATE == 0)) { + if ((m_descriptor.degree() == 1) and + (m_descriptor.integrationMethod() == PolynomialReconstructionDescriptor::IntegrationMethod::cell_center)) { const Rd& Xj = xj[cell_j_id]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { const CellId cell_i_id = stencil_cell_list[i]; @@ -690,8 +681,12 @@ PolynomialReconstruction::_build( } } - } else if ((INTEGRATE == 2) or (INTEGRATE == 1)) { - if ((INTEGRATE == 2) and (MeshType::Dimension == 2)) { + } else if ((m_descriptor.integrationMethod() == + PolynomialReconstructionDescriptor::IntegrationMethod::element) or + (m_descriptor.integrationMethod() == + PolynomialReconstructionDescriptor::IntegrationMethod::boundary)) { + if ((m_descriptor.integrationMethod() == PolynomialReconstructionDescriptor::IntegrationMethod::boundary) and + (MeshType::Dimension == 2)) { if constexpr (MeshType::Dimension == 2) { SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek = inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[t]; SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; @@ -817,12 +812,29 @@ PolynomialReconstruction::_build( dpk_j[0] = p0_function[cell_j_id]; if constexpr (std::is_arithmetic_v<DataType>) { + if (m_descriptor.degree() > 1) { + auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + for (size_t i = 0; i < basis_dimension - 1; ++i) { + dpk_j[0] -= X(i, column_begin) * mean_j_of_ejk[i]; + } + } + for (size_t i = 0; i < basis_dimension - 1; ++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>) { + if (m_descriptor.degree() > 1) { + auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + for (size_t i = 0; i < basis_dimension - 1; ++i) { + auto& dpk_j_0 = dpk_j[0]; + for (size_t k = 0; k < DataType::Dimension; ++k) { + dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; + } + } + } + for (size_t i = 0; i < basis_dimension - 1; ++i) { auto& dpk_j_ip1 = dpk_j[i + 1]; for (size_t k = 0; k < DataType::Dimension; ++k) { @@ -831,6 +843,19 @@ PolynomialReconstruction::_build( } column_begin += DataType::Dimension; } else if constexpr (is_tiny_matrix_v<DataType>) { + if (m_descriptor.degree() > 1) { + auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + for (size_t i = 0; i < basis_dimension - 1; ++i) { + auto& dpk_j_0 = dpk_j[0]; + for (size_t k = 0; k < DataType::NumberOfRows; ++k) { + for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { + dpk_j_0(k, l) -= + X(i, column_begin + k * DataType::NumberOfColumns + l) * mean_j_of_ejk[i]; + } + } + } + } + for (size_t i = 0; i < basis_dimension - 1; ++i) { auto& dpk_j_ip1 = dpk_j[i + 1]; for (size_t k = 0; k < DataType::NumberOfRows; ++k) { @@ -860,6 +885,13 @@ PolynomialReconstruction::_build( const size_t component_begin = l * size; dpk_j[component_begin] = cell_vector[l]; if constexpr (std::is_arithmetic_v<DataType>) { + if (m_descriptor.degree() > 1) { + auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + for (size_t i = 0; i < basis_dimension - 1; ++i) { + dpk_j[component_begin] -= X(i, column_begin) * mean_j_of_ejk[i]; + } + } + for (size_t i = 0; i < basis_dimension - 1; ++i) { auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; dpk_j_ip1 = X(i, column_begin); diff --git a/src/scheme/PolynomialReconstructionDescriptor.hpp b/src/scheme/PolynomialReconstructionDescriptor.hpp index a73945001..b561f7080 100644 --- a/src/scheme/PolynomialReconstructionDescriptor.hpp +++ b/src/scheme/PolynomialReconstructionDescriptor.hpp @@ -7,12 +7,31 @@ class PolynomialReconstructionDescriptor { + public: + enum class IntegrationMethod + { + cell_center, // use exact integrals for degree 1 polynomials + // using evaluation at mass center + element, // use element based quadrature to compute + // polynomial integrals + boundary // use divergence theorem to compute polynomial + // integrals + }; + private: + IntegrationMethod m_integration_method; size_t m_degree; bool m_preconditioning = true; bool m_row_weighting = true; public: + PUGS_INLINE + IntegrationMethod + integrationMethod() const + { + return m_integration_method; + } + PUGS_INLINE size_t degree() const @@ -55,7 +74,9 @@ class PolynomialReconstructionDescriptor m_row_weighting = row_weighting; } - PolynomialReconstructionDescriptor(const size_t degree) : m_degree{degree} {} + PolynomialReconstructionDescriptor(const IntegrationMethod integration_method, const size_t degree) + : m_integration_method{integration_method}, m_degree{degree} + {} PolynomialReconstructionDescriptor() = delete; diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index 4ba54bda2..bf1c51121 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -7,7 +7,7 @@ void test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, uint64_t degree) { - PolynomialReconstructionDescriptor descriptor{degree}; + PolynomialReconstructionDescriptor descriptor{PolynomialReconstructionDescriptor::IntegrationMethod::element, degree}; { const size_t nb_loops = 1; std::cout << "** variable list at once (" << nb_loops << " times)\n"; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b242c06dc..e2dff14cc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -128,6 +128,7 @@ add_executable (unit_tests test_PugsUtils.cpp test_PyramidGaussQuadrature.cpp test_PyramidTransformation.cpp + test_QuadraticPolynomialReconstruction.cpp test_QuadratureType.cpp test_RefId.cpp test_RefItemList.cpp diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index b7c7019f5..dfa092bb1 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -24,7 +24,9 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { SECTION("degree 1") { - PolynomialReconstructionDescriptor descriptor{1}; + PolynomialReconstructionDescriptor::IntegrationMethod method = + PolynomialReconstructionDescriptor::IntegrationMethod::cell_center; + PolynomialReconstructionDescriptor descriptor{method, 1}; SECTION("1D") { diff --git a/tests/test_PolynomialReconstructionDescriptor.cpp b/tests/test_PolynomialReconstructionDescriptor.cpp index 6b7cd2853..eda18e3d6 100644 --- a/tests/test_PolynomialReconstructionDescriptor.cpp +++ b/tests/test_PolynomialReconstructionDescriptor.cpp @@ -8,9 +8,12 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") { +#warning tests are not completed SECTION("degree 1") { - PolynomialReconstructionDescriptor descriptor{1}; + PolynomialReconstructionDescriptor::IntegrationMethod method = + PolynomialReconstructionDescriptor::IntegrationMethod::cell_center; + PolynomialReconstructionDescriptor descriptor{method, 1}; REQUIRE(descriptor.degree() == 1); REQUIRE(descriptor.preconditioning() == true); @@ -19,7 +22,10 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") SECTION("degree 4") { - PolynomialReconstructionDescriptor descriptor{4}; + PolynomialReconstructionDescriptor::IntegrationMethod method = + PolynomialReconstructionDescriptor::IntegrationMethod::cell_center; + + PolynomialReconstructionDescriptor descriptor{method, 4}; REQUIRE(descriptor.degree() == 4); REQUIRE(descriptor.preconditioning() == true); @@ -28,7 +34,9 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") SECTION("utlities") { - PolynomialReconstructionDescriptor descriptor{3}; + PolynomialReconstructionDescriptor::IntegrationMethod method = + PolynomialReconstructionDescriptor::IntegrationMethod::cell_center; + PolynomialReconstructionDescriptor descriptor{method, 3}; REQUIRE(descriptor.degree() == 3); REQUIRE(descriptor.preconditioning() == true); diff --git a/tests/test_QuadraticPolynomialReconstruction.cpp b/tests/test_QuadraticPolynomialReconstruction.cpp new file mode 100644 index 000000000..69c685f54 --- /dev/null +++ b/tests/test_QuadraticPolynomialReconstruction.cpp @@ -0,0 +1,1198 @@ +#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 <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/CubeTransformation.hpp> +#include <geometry/LineTransformation.hpp> +#include <geometry/PrismTransformation.hpp> +#include <geometry/PyramidTransformation.hpp> +#include <geometry/SquareTransformation.hpp> +#include <geometry/TetrahedronTransformation.hpp> +#include <geometry/TriangleTransformation.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshDataManager.hpp> +#include <scheme/DiscreteFunctionDPkVariant.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/PolynomialReconstruction.hpp> + +#include <MeshDataBaseForTests.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") +{ + SECTION("degree 2") + { + PolynomialReconstructionDescriptor::IntegrationMethod method = + PolynomialReconstructionDescriptor::IntegrationMethod::element; + PolynomialReconstructionDescriptor descriptor{method, 2}; + + SECTION("1D") + { + using R1 = TinyVector<1>; + + QuadratureFormula<1> qf = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{2}); + + 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 f_exact = [](const R1& x) { return 2.3 + 1.7 * x[0] - 3.2 * x[0] * x[0]; }; + + auto xr = mesh.xr(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + DiscreteFunctionP0<double> fh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + double value = 0; + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + value += qf.weight(i_q) * f_exact(T(qf.point(i_q))) * T.jacobianDeterminant(); + } + + fh[cell_id] = value / Vj[cell_id]; + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + + { + double L1_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const auto cell_node_list = cell_to_node_matrix[cell_id]; + const auto dpkj = dpk_fh[cell_id]; + + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + double error = 0; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + error += std::abs(qf.weight(i_q) * (f_exact(T(qf.point(i_q))) - dpkj(T(qf.point(i_q)))) * + T.jacobianDeterminant()); + } + + L1_error += error; + } + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("R^3 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 u_exact = [](const R1& X) -> R3 { + const double x = X[0]; + + return R3{2.3 + 1.7 * x - 3.2 * x * x, // + 7 + 5 * x + 3 * x * x, // + -4 + 3.5 * x + 2.7 * x * x}; + }; + + auto xr = mesh.xr(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + DiscreteFunctionP0<R3> uh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + R3 value = zero; + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + value += qf.weight(i_q) * T.jacobianDeterminant() * u_exact(T(qf.point(i_q))); + } + + uh[cell_id] = 1. / Vj[cell_id] * value; + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); + + { + double L1_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const auto cell_node_list = cell_to_node_matrix[cell_id]; + const auto dpkj = dpk_fh[cell_id]; + + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + double error = 0; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + R3 diff = + qf.weight(i_q) * T.jacobianDeterminant() * (u_exact(T(qf.point(i_q))) - dpkj(T(qf.point(i_q)))); + error += std::abs(diff[0]) + std::abs(diff[1]) + std::abs(diff[2]); + } + + L1_error += error; + } + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("R^3x3 data") + { + using R3x3 = TinyMatrix<3, 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 A_exact = [](const R1& X) -> R3x3 { + const double x = X[0]; + const double x2 = x * x; + return R3x3{+2.3 + 1.7 * x + 2.9 * x2, // + -1.7 + 2.1 * x + 1.7 * x2, // + +1.4 - 0.6 * x - 0.5 * x2, // + // + +2.4 - 2.3 * x + 2.3 * x2, // + -0.2 + 3.1 * x - 1.9 * x2, // + -3.2 - 3.6 * x - 0.3 * x2, // + // + -4.1 + 3.1 * x - 1.3 * x2, // + +0.8 + 2.9 * x + 2.1 * x2, // + -1.6 + 2.3 * x + 0.8 * x2}; + }; + + auto xr = mesh.xr(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + DiscreteFunctionP0<R3x3> Ah{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + R3x3 value = zero; + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + value += qf.weight(i_q) * T.jacobianDeterminant() * A_exact(T(qf.point(i_q))); + } + + Ah[cell_id] = 1. / Vj[cell_id] * value; + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); + + { + double L1_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const auto cell_node_list = cell_to_node_matrix[cell_id]; + const auto dpkj = dpk_Ah[cell_id]; + + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + double error = 0; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + R3x3 diff = + qf.weight(i_q) * T.jacobianDeterminant() * (A_exact(T(qf.point(i_q))) - dpkj(T(qf.point(i_q)))); + for (size_t i = 0; i < diff.numberOfRows(); ++i) { + for (size_t j = 0; j < diff.numberOfColumns(); ++j) { + error += std::abs(diff(i, j)); + } + } + } + + L1_error += error; + } + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + 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 u_exact = [](const R1& X) -> R3 { + const double x = X[0]; + + return R3{2.3 + 1.7 * x - 3.2 * x * x, // + 7 + 5 * x + 3 * x * x, // + -4 + 3.5 * x + 2.7 * x * x}; + }; + + auto xr = mesh.xr(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + DiscreteFunctionP0Vector<double> uh{p_mesh, 3}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + R3 value = zero; + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + value += qf.weight(i_q) * T.jacobianDeterminant() * u_exact(T(qf.point(i_q))); + } + + value *= 1. / Vj[cell_id]; + + for (size_t i = 0; i < value.dimension(); ++i) { + uh[cell_id][i] = value[i]; + } + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); + + { + double L1_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const auto cell_node_list = cell_to_node_matrix[cell_id]; + + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + double error = 0; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + for (size_t l = 0; l < dpk_uh.numberOfComponents(); ++l) { + error += std::abs(qf.weight(i_q) * T.jacobianDeterminant() * + (u_exact(T(qf.point(i_q)))[l] - dpk_uh(cell_id, l)(T(qf.point(i_q))))); + } + } + + L1_error += error; + } + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + SECTION("list of various types") + { + using R3x3 = TinyMatrix<3>; + 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 f_exact = [](const R1& x) { return 2.3 + 1.7 * x[0] - 3.2 * x[0] * x[0]; }; + + auto u_exact = [](const R1& X) -> R3 { + const double x = X[0]; + + return R3{2.3 + 1.7 * x - 3.2 * x * x, // + 7 + 5 * x + 3 * x * x, // + -4 + 3.5 * x + 2.7 * x * x}; + }; + + auto A_exact = [](const R1& X) -> R3x3 { + const double x = X[0]; + const double x2 = x * x; + return R3x3{+2.3 + 1.7 * x + 2.9 * x2, // + -1.7 + 2.1 * x + 1.7 * x2, // + +1.4 - 0.6 * x - 0.5 * x2, // + // + +2.4 - 2.3 * x + 2.3 * x2, // + -0.2 + 3.1 * x - 1.9 * x2, // + -3.2 - 3.6 * x - 0.3 * x2, // + // + -4.1 + 3.1 * x - 1.3 * x2, // + +0.8 + 2.9 * x + 2.1 * x2, // + -1.6 + 2.3 * x + 0.8 * x2}; + }; + + auto xr = mesh.xr(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + DiscreteFunctionP0Vector<double> vh{p_mesh, 3}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + R3 value = zero; + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + value += qf.weight(i_q) * T.jacobianDeterminant() * u_exact(T(qf.point(i_q))); + } + + value *= 1. / Vj[cell_id]; + + for (size_t i = 0; i < value.dimension(); ++i) { + vh[cell_id][i] = value[i]; + } + }); + + DiscreteFunctionP0<double> fh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + double value = 0; + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + value += qf.weight(i_q) * f_exact(T(qf.point(i_q))) * T.jacobianDeterminant(); + } + + fh[cell_id] = value / Vj[cell_id]; + }); + + DiscreteFunctionP0<R3x3> Ah{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + R3x3 value = zero; + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + value += qf.weight(i_q) * T.jacobianDeterminant() * A_exact(T(qf.point(i_q))); + } + + Ah[cell_id] = 1. / Vj[cell_id] * value; + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah, vh, fh); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); + auto dpk_vh = reconstructions[1]->get<DiscreteFunctionDPkVector<1, const double>>(); + auto dpk_fh = reconstructions[2]->get<DiscreteFunctionDPk<1, const double>>(); + + { + double L1_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const auto cell_node_list = cell_to_node_matrix[cell_id]; + + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + double error = 0; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + for (size_t l = 0; l < dpk_vh.numberOfComponents(); ++l) { + error += std::abs(qf.weight(i_q) * T.jacobianDeterminant() * + (u_exact(T(qf.point(i_q)))[l] - dpk_vh(cell_id, l)(T(qf.point(i_q))))); + } + } + + L1_error += error; + } + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double L1_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const auto cell_node_list = cell_to_node_matrix[cell_id]; + const auto dpkj = dpk_fh[cell_id]; + + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + double error = 0; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + error += std::abs(qf.weight(i_q) * (f_exact(T(qf.point(i_q))) - dpkj(T(qf.point(i_q)))) * + T.jacobianDeterminant()); + } + + L1_error += error; + } + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double L1_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const auto cell_node_list = cell_to_node_matrix[cell_id]; + const auto dpkj = dpk_Ah[cell_id]; + + LineTransformation<1> T{xr[cell_node_list[0]], xr[cell_node_list[1]]}; + + double error = 0; + + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + R3x3 diff = + qf.weight(i_q) * T.jacobianDeterminant() * (A_exact(T(qf.point(i_q))) - dpkj(T(qf.point(i_q)))); + for (size_t i = 0; i < diff.numberOfRows(); ++i) { + for (size_t j = 0; j < diff.numberOfColumns(); ++j) { + error += std::abs(diff(i, j)); + } + } + } + + L1_error += error; + } + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + } + + SECTION("2D") + { + using R2 = TinyVector<2>; + + auto integrate_in_cell = [](const CellType& cell_type, const auto& cell_node_list, const auto& xr, + const auto& exact, auto& value) { + switch (cell_type) { + case CellType::Triangle: { + TriangleTransformation<2> T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]]}; + QuadratureFormula<2> qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{2}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + value += qf.weight(i_q) * T.jacobianDeterminant() * exact(T(qf.point(i_q))); + } + break; + } + case CellType::Quadrangle: { + SquareTransformation<2> T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]]}; + QuadratureFormula<2> qf = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R2 x_hat = qf.point(i_q); + const R2 x = T(x_hat); + value += qf.weight(i_q) * T.jacobianDeterminant(x_hat) * exact(x); + } + break; + } + default: { + throw UnexpectedError("invalid cell type"); + } + } + }; + + auto compute_L1_error = [](const auto& cell_type, const auto& mesh, const auto& exact, const auto& dpk) { + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + const auto& xr = mesh.xr(); + + using DataType = typename std::decay_t<decltype(dpk)>::data_type; + + auto sum_abs = [](const DataType& diff) -> double { + if constexpr (std::is_arithmetic_v<DataType>) { + return std::abs(diff); + } else if constexpr (is_tiny_vector_v<DataType>) { + double sum = 0; + for (size_t i = 0; i < diff.dimension(); ++i) { + sum += std::abs(diff[i]); + } + return sum; + } else if constexpr (is_tiny_matrix_v<DataType>) { + double sum = 0; + for (size_t i = 0; i < diff.numberOfRows(); ++i) { + for (size_t j = 0; j < diff.numberOfRows(); ++j) { + sum += std::abs(diff(i, j)); + } + } + return sum; + } else { + throw UnexpectedError("unexpected value type"); + } + }; + + double L1_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const auto cell_node_list = cell_to_node_matrix[cell_id]; + const auto dpkj = dpk[cell_id]; + + double error = 0; + + switch (cell_type[cell_id]) { + case CellType::Triangle: { + TriangleTransformation<2> T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]]}; + QuadratureFormula<2> qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{2}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R2 x = T(qf.point(i_q)); + DataType diff = qf.weight(i_q) * T.jacobianDeterminant() * (exact(x) - dpkj(x)); + error += sum_abs(diff); + } + break; + } + case CellType::Quadrangle: { + SquareTransformation<2> T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]]}; + QuadratureFormula<2> qf = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R2 x_hat = qf.point(i_q); + const R2 x = T(x_hat); + DataType diff = qf.weight(i_q) * T.jacobianDeterminant(x_hat) * (exact(x) - dpkj(x)); + error += sum_abs(diff); + } + break; + } + default: { + throw UnexpectedError("invalid cell type"); + } + } + + L1_error += error; + } + return L1_error; + }; + + 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 f_exact = [](const R2& x) { + return 2.3 + 1.7 * x[0] + 0.2 * x[1] - 3.2 * x[0] * x[0] + 1.3 * x[0] * x[1] - 1.4 * x[1] * x[1]; + }; + + auto xr = mesh.xr(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_type = mesh.connectivity().cellType(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + DiscreteFunctionP0<double> fh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + double value = 0; + integrate_in_cell(cell_type[cell_id], cell_node_list, xr, f_exact, value); + fh[cell_id] = value / Vj[cell_id]; + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + + double L1_error = compute_L1_error(cell_type, mesh, f_exact, dpk_fh); + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("R^3 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 u_exact = [](const R2& X) -> R3 { + const double x = X[0]; + const double y = X[1]; + const double x2 = x * x; + const double xy = x * y; + const double y2 = y * y; + + return R3{+2.3 + 1.7 * x + 1.3 * y - 3.2 * x2 + 1.6 * xy + 0.9 * y2, // + +7.0 + 5.0 * x - 2.4 * y + 3.0 * x2 - 0.8 * xy + 1.1 * y2, // + -4.0 + 3.5 * x - 0.7 * y + 2.7 * x2 - 2.1 * xy - 1.8 * y2}; + }; + + auto xr = mesh.xr(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_type = mesh.connectivity().cellType(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + DiscreteFunctionP0<R3> uh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + R3 value = zero; + integrate_in_cell(cell_type[cell_id], cell_node_list, xr, u_exact, value); + uh[cell_id] = 1. / Vj[cell_id] * value; + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); + + double L1_error = compute_L1_error(cell_type, mesh, u_exact, dpk_uh); + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("R^2x2 data") + { + using R2x2 = TinyMatrix<2, 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 A_exact = [](const R2& X) -> R2x2 { + const double x = X[0]; + const double y = X[1]; + const double x2 = x * x; + const double xy = x * y; + const double y2 = y * y; + + return R2x2{+2.3 + 1.7 * x + 1.2 * y + 1.1 * x2 + 0.7 * xy - 0.8 * y2, // + -1.7 + 2.1 * x - 2.2 * y - 1.9 * x2 + 0.2 * xy + 1.2 * y2, // + +1.4 - 0.6 * x - 2.1 * y + 0.3 * x2 - 3.1 * xy + 1.3 * y2, // + +2.4 - 2.3 * x + 1.3 * y - 0.8 * x2 + 1.2 * xy - 2.0 * y2}; + }; + + auto xr = mesh.xr(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_type = mesh.connectivity().cellType(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + DiscreteFunctionP0<R2x2> Ah{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + R2x2 value = zero; + integrate_in_cell(cell_type[cell_id], cell_node_list, xr, A_exact, value); + Ah[cell_id] = 1. / Vj[cell_id] * value; + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + + double L1_error = compute_L1_error(cell_type, mesh, A_exact, dpk_Ah); + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + + // 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{descriptor}.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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + // } + + // { + // 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + + // { + // 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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + // } + // } + // } + } + + 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 f_exact = [](const R3& X) { + const double& x = X[0]; + const double& y = X[1]; + const double& z = X[2]; + + return 2.3 + 1.7 * x + 0.2 * y + 1.4 * z - 3.2 * x * x + 1.3 * x * y - 1.6 * x * z - 1.4 * y * y + + 2 * y * z - 1.8 * z * z; + }; + + auto xr = mesh.xr(); + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_type = mesh.connectivity().cellType(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + DiscreteFunctionP0<double> fh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + double value = 0; + + switch (cell_type[cell_id]) { + case CellType::Tetrahedron: { + TetrahedronTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]]}; + QuadratureFormula<3> qf = + QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{2}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + value += qf.weight(i_q) * f_exact(x) * T.jacobianDeterminant(); + } + break; + } + case CellType::Pyramid: { + PyramidTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]], xr[cell_node_list[4]]}; + QuadratureFormula<3> qf = + QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + value += qf.weight(i_q) * f_exact(x) * T.jacobianDeterminant(x_hat); + } + break; + } + case CellType::Prism: { + PrismTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]]}; + QuadratureFormula<3> qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + value += qf.weight(i_q) * f_exact(x) * T.jacobianDeterminant(x_hat); + } + break; + } + case CellType::Hexahedron: { + CubeTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]], + xr[cell_node_list[6]], xr[cell_node_list[7]]}; + QuadratureFormula<3> qf = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + value += qf.weight(i_q) * f_exact(x) * T.jacobianDeterminant(x_hat); + } + break; + } + default: { + throw UnexpectedError("invalid cell type"); + } + } + + fh[cell_id] = value / Vj[cell_id]; + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + + { + double L1_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const auto cell_node_list = cell_to_node_matrix[cell_id]; + const auto dpkj = dpk_fh[cell_id]; + + double error = 0; + + switch (cell_type[cell_id]) { + case CellType::Tetrahedron: { + TetrahedronTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]]}; + QuadratureFormula<3> qf = + QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{2}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + error += std::abs(qf.weight(i_q) * (f_exact(x) - dpkj(x)) * T.jacobianDeterminant()); + } + break; + } + case CellType::Pyramid: { + PyramidTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]], xr[cell_node_list[4]]}; + QuadratureFormula<3> qf = + QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + error += std::abs(qf.weight(i_q) * (f_exact(x) - dpkj(x)) * T.jacobianDeterminant(x_hat)); + } + break; + } + case CellType::Prism: { + PrismTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]]}; + QuadratureFormula<3> qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + error += std::abs(qf.weight(i_q) * (f_exact(x) - dpkj(x)) * T.jacobianDeterminant(x_hat)); + } + break; + } + case CellType::Hexahedron: { + CubeTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]], + xr[cell_node_list[6]], xr[cell_node_list[7]]}; + QuadratureFormula<3> qf = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + error += std::abs(qf.weight(i_q) * (f_exact(x) - dpkj(x)) * T.jacobianDeterminant(x_hat)); + } + break; + } + default: { + throw UnexpectedError("invalid cell type"); + } + } + + L1_error += error; + } + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + + // SECTION("R^3 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 R3_affine = [](const R3& x) -> R3 { + // return R3{+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]}; + // }; + // auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + // DiscreteFunctionP0<R3> uh{p_mesh}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + // { + // 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(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_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + // dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + + // max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); + // } + // REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + // dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + + // max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1})); + // } + // REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + // dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + + // max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9})); + // } + // REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + + // SECTION("R^2x2 data") + // { + // using R2x2 = TinyMatrix<2, 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 R2x2_affine = [](const R3& x) -> R2x2 { + // return R2x2{+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(); + + // DiscreteFunctionP0<R2x2> Ah{p_mesh}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); + + // descriptor.setRowWeighting(false); + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + // auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + + // { + // 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_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + + // max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, + // // + // -2.3, + // +3.1})); + // } + // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + + // max_y_slope_error = + // std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + // 1.3, +0.8})); + // } + // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + + // max_z_slope_error = + // std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // + // +1.4, -1.8})); + // } + // REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + + // 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]; + // } + // }); + + // descriptor.setPreconditioning(false); + // auto reconstructions = PolynomialReconstruction{descriptor}.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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + // } + + // { + // 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + + // { + // 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(parallel::allReduceMax(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(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + // } + // } + // } + } + } +} -- GitLab From 7fb6450ce7e6ec9b3879c175d8fee3c51e856947 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Fri, 26 Jul 2024 01:09:35 +0200 Subject: [PATCH 053/122] Add a bunch of tests for quadratic PolynomialReconstruction --- src/scheme/test_reconstruction.cpp | 34 +- ...test_QuadraticPolynomialReconstruction.cpp | 854 +++++++----------- 2 files changed, 380 insertions(+), 508 deletions(-) diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index bf1c51121..3753143d3 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -7,10 +7,36 @@ void test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, uint64_t degree) { - PolynomialReconstructionDescriptor descriptor{PolynomialReconstructionDescriptor::IntegrationMethod::element, degree}; - { - const size_t nb_loops = 1; - std::cout << "** variable list at once (" << nb_loops << " times)\n"; + std::vector descriptor_list = + {PolynomialReconstructionDescriptor{PolynomialReconstructionDescriptor::IntegrationMethod::cell_center, degree}, + PolynomialReconstructionDescriptor{PolynomialReconstructionDescriptor::IntegrationMethod::element, degree}, + PolynomialReconstructionDescriptor{PolynomialReconstructionDescriptor::IntegrationMethod::boundary, degree}}; + + [[maybe_unused]] auto x = + PolynomialReconstruction{ + PolynomialReconstructionDescriptor{PolynomialReconstructionDescriptor::IntegrationMethod::cell_center, degree}} + .build(discrete_function_variant_list); + + for (auto&& descriptor : descriptor_list) { + std::string method_name; + switch (descriptor.integrationMethod()) { + case PolynomialReconstructionDescriptor::IntegrationMethod::element: { + method_name = "element"; + break; + } + case PolynomialReconstructionDescriptor::IntegrationMethod::boundary: { + method_name = "boundary"; + break; + } + case PolynomialReconstructionDescriptor::IntegrationMethod::cell_center: { + method_name = "cell_center"; + break; + } + } + + const size_t nb_loops = 50; + std::cout << "** variable list at once (" << nb_loops << " times) using " << rang::fgB::yellow << method_name + << rang::fg::reset << "\n"; PolynomialReconstruction reconstruction{descriptor}; Timer t; diff --git a/tests/test_QuadraticPolynomialReconstruction.cpp b/tests/test_QuadraticPolynomialReconstruction.cpp index 69c685f54..6b992e960 100644 --- a/tests/test_QuadraticPolynomialReconstruction.cpp +++ b/tests/test_QuadraticPolynomialReconstruction.cpp @@ -584,16 +584,326 @@ TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") return L1_error; }; + std::map<std::string, PolynomialReconstructionDescriptor> name_method_map = + {{"element", + PolynomialReconstructionDescriptor{PolynomialReconstructionDescriptor::IntegrationMethod::element, 2}}, + {"boundary", + PolynomialReconstructionDescriptor{PolynomialReconstructionDescriptor::IntegrationMethod::boundary, 2}}}; + + for (auto [method_name, method_descriptor] : name_method_map) { + SECTION(method_name) + { + 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 f_exact = [](const R2& x) { + return 2.3 + 1.7 * x[0] + 0.2 * x[1] - 3.2 * x[0] * x[0] + 1.3 * x[0] * x[1] - 1.4 * x[1] * x[1]; + }; + + auto xr = mesh.xr(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_type = mesh.connectivity().cellType(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + DiscreteFunctionP0<double> fh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + double value = 0; + integrate_in_cell(cell_type[cell_id], cell_node_list, xr, f_exact, value); + fh[cell_id] = value / Vj[cell_id]; + }); + + auto reconstructions = PolynomialReconstruction{method_descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + + double L1_error = compute_L1_error(cell_type, mesh, f_exact, dpk_fh); + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("R^3 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 u_exact = [](const R2& X) -> R3 { + const double x = X[0]; + const double y = X[1]; + const double x2 = x * x; + const double xy = x * y; + const double y2 = y * y; + + return R3{+2.3 + 1.7 * x + 1.3 * y - 3.2 * x2 + 1.6 * xy + 0.9 * y2, // + +7.0 + 5.0 * x - 2.4 * y + 3.0 * x2 - 0.8 * xy + 1.1 * y2, // + -4.0 + 3.5 * x - 0.7 * y + 2.7 * x2 - 2.1 * xy - 1.8 * y2}; + }; + + auto xr = mesh.xr(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_type = mesh.connectivity().cellType(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + DiscreteFunctionP0<R3> uh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + R3 value = zero; + integrate_in_cell(cell_type[cell_id], cell_node_list, xr, u_exact, value); + uh[cell_id] = 1. / Vj[cell_id] * value; + }); + + auto reconstructions = PolynomialReconstruction{method_descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); + + double L1_error = compute_L1_error(cell_type, mesh, u_exact, dpk_uh); + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + + SECTION("R^2x2 data") + { + using R2x2 = TinyMatrix<2, 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 A_exact = [](const R2& X) -> R2x2 { + const double x = X[0]; + const double y = X[1]; + const double x2 = x * x; + const double xy = x * y; + const double y2 = y * y; + + return R2x2{+2.3 + 1.7 * x + 1.2 * y + 1.1 * x2 + 0.7 * xy - 0.8 * y2, // + -1.7 + 2.1 * x - 2.2 * y - 1.9 * x2 + 0.2 * xy + 1.2 * y2, // + +1.4 - 0.6 * x - 2.1 * y + 0.3 * x2 - 3.1 * xy + 1.3 * y2, // + +2.4 - 2.3 * x + 1.3 * y - 0.8 * x2 + 1.2 * xy - 2.0 * y2}; + }; + + auto xr = mesh.xr(); + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto cell_type = mesh.connectivity().cellType(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + + DiscreteFunctionP0<R2x2> Ah{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + R2x2 value = zero; + integrate_in_cell(cell_type[cell_id], cell_node_list, xr, A_exact, value); + Ah[cell_id] = 1. / Vj[cell_id] * value; + }); + + auto reconstructions = PolynomialReconstruction{method_descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + + double L1_error = compute_L1_error(cell_type, mesh, A_exact, dpk_Ah); + REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); + } + } + } + } + } + } + + SECTION("3D") + { + using R3 = TinyVector<3>; + + auto integrate_in_cell = [](const CellType& cell_type, const auto& cell_node_list, const auto& xr, + const auto& exact, auto& value) { + switch (cell_type) { + case CellType::Tetrahedron: { + TetrahedronTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]]}; + QuadratureFormula<3> qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{2}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + value += qf.weight(i_q) * T.jacobianDeterminant() * exact(x); + } + break; + } + case CellType::Pyramid: { + PyramidTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]], xr[cell_node_list[4]]}; + QuadratureFormula<3> qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + value += qf.weight(i_q) * T.jacobianDeterminant(x_hat) * exact(x); + } + break; + } + case CellType::Prism: { + PrismTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]]}; + QuadratureFormula<3> qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + value += qf.weight(i_q) * T.jacobianDeterminant(x_hat) * exact(x); + } + break; + } + case CellType::Hexahedron: { + CubeTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]], + xr[cell_node_list[6]], xr[cell_node_list[7]]}; + QuadratureFormula<3> qf = QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + value += qf.weight(i_q) * T.jacobianDeterminant(x_hat) * exact(x); + } + break; + } + default: { + throw UnexpectedError("invalid cell type"); + } + } + }; + + auto compute_L1_error = [](const auto& cell_type, const auto& mesh, const auto& exact, const auto& dpk) { + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + const auto& xr = mesh.xr(); + + using DataType = typename std::decay_t<decltype(dpk)>::data_type; + + auto sum_abs = [](const DataType& diff) -> double { + if constexpr (std::is_arithmetic_v<DataType>) { + return std::abs(diff); + } else if constexpr (is_tiny_vector_v<DataType>) { + double sum = 0; + for (size_t i = 0; i < diff.dimension(); ++i) { + sum += std::abs(diff[i]); + } + return sum; + } else if constexpr (is_tiny_matrix_v<DataType>) { + double sum = 0; + for (size_t i = 0; i < diff.numberOfRows(); ++i) { + for (size_t j = 0; j < diff.numberOfRows(); ++j) { + sum += std::abs(diff(i, j)); + } + } + return sum; + } else { + throw UnexpectedError("unexpected value type"); + } + }; + + double L1_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const auto cell_node_list = cell_to_node_matrix[cell_id]; + const auto dpkj = dpk[cell_id]; + + double error = 0; + + switch (cell_type[cell_id]) { + case CellType::Tetrahedron: { + TetrahedronTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]]}; + QuadratureFormula<3> qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{2}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + DataType diff = qf.weight(i_q) * T.jacobianDeterminant() * (exact(x) - dpkj(x)); + error += sum_abs(diff); + } + break; + } + case CellType::Pyramid: { + PyramidTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]], xr[cell_node_list[4]]}; + QuadratureFormula<3> qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + DataType diff = qf.weight(i_q) * T.jacobianDeterminant(x_hat) * (exact(x) - dpkj(x)); + error += sum_abs(diff); + } + break; + } + case CellType::Prism: { + PrismTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]]}; + QuadratureFormula<3> qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + DataType diff = qf.weight(i_q) * T.jacobianDeterminant(x_hat) * (exact(x) - dpkj(x)); + error += sum_abs(diff); + } + break; + } + case CellType::Hexahedron: { + CubeTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], + xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]], + xr[cell_node_list[6]], xr[cell_node_list[7]]}; + QuadratureFormula<3> qf = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{3}); + for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { + const R3 x_hat = qf.point(i_q); + const R3 x = T(x_hat); + DataType diff = qf.weight(i_q) * T.jacobianDeterminant(x_hat) * (exact(x) - dpkj(x)); + error += sum_abs(diff); + } + break; + } + default: { + throw UnexpectedError("invalid cell type"); + } + } + + L1_error += error; + } + return L1_error; + }; + SECTION("R data") { - for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { SECTION(named_mesh.name()) { - auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); - auto& mesh = *p_mesh; + auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); + auto& mesh = *p_mesh; + auto f_exact = [](const R3& X) { + const double x = X[0]; + const double y = X[1]; + const double z = X[2]; + const double x2 = x * x; + const double xy = x * y; + const double xz = x * z; + const double y2 = y * y; + const double yz = y * z; + const double z2 = z * z; - auto f_exact = [](const R2& x) { - return 2.3 + 1.7 * x[0] + 0.2 * x[1] - 3.2 * x[0] * x[0] + 1.3 * x[0] * x[1] - 1.4 * x[1] * x[1]; + return 2.3 + 1.7 * x + 0.2 * y + 1.4 * z - 3.2 * x2 + 1.3 * xy - 1.6 * xz - 1.4 * y2 + 2 * yz - 1.8 * z2; }; auto xr = mesh.xr(); @@ -614,7 +924,7 @@ TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); double L1_error = compute_L1_error(cell_type, mesh, f_exact, dpk_fh); REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); @@ -624,24 +934,29 @@ TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") SECTION("R^3 data") { - using R3 = TinyVector<3>; - - for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { SECTION(named_mesh.name()) { - auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); + auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto u_exact = [](const R2& X) -> R3 { + auto u_exact = [](const R3& X) -> R3 { const double x = X[0]; const double y = X[1]; + const double z = X[2]; const double x2 = x * x; const double xy = x * y; + const double xz = x * z; const double y2 = y * y; - - return R3{+2.3 + 1.7 * x + 1.3 * y - 3.2 * x2 + 1.6 * xy + 0.9 * y2, // - +7.0 + 5.0 * x - 2.4 * y + 3.0 * x2 - 0.8 * xy + 1.1 * y2, // - -4.0 + 3.5 * x - 0.7 * y + 2.7 * x2 - 2.1 * xy - 1.8 * y2}; + const double yz = y * z; + const double z2 = z * z; + + return R3{+2.3 + 1.7 * x - 2.2 * y + 1.8 * z // + + 0.3 * x2 - 1.3 * xy + 2.7 * xz + 0.7 * y2 + 2.0 * yz - 1.2 * z2, // + +1.4 - 0.6 * x + 1.3 * y - 3.7 * z // + + 3.1 * x2 - 2.4 * xy - 1.5 * xz - 1.9 * y2 + 0.2 * yz + 0.9 * z2, // + -0.2 + 3.1 * x - 1.1 * y + 1.9 * z // + - 1.3 * x2 + 2.8 * xy - 2.1 * xz + 3.2 * y2 - 2.1 * yz + 1.6 * z2}; }; auto xr = mesh.xr(); @@ -662,8 +977,7 @@ TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); - + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); double L1_error = compute_L1_error(cell_type, mesh, u_exact, dpk_uh); REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); } @@ -674,23 +988,32 @@ TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") { using R2x2 = TinyMatrix<2, 2>; - for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { SECTION(named_mesh.name()) { - auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); + auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto A_exact = [](const R2& X) -> R2x2 { + auto A_exact = [](const R3& X) -> R2x2 { const double x = X[0]; const double y = X[1]; + const double z = X[2]; const double x2 = x * x; const double xy = x * y; + const double xz = x * z; const double y2 = y * y; - - return R2x2{+2.3 + 1.7 * x + 1.2 * y + 1.1 * x2 + 0.7 * xy - 0.8 * y2, // - -1.7 + 2.1 * x - 2.2 * y - 1.9 * x2 + 0.2 * xy + 1.2 * y2, // - +1.4 - 0.6 * x - 2.1 * y + 0.3 * x2 - 3.1 * xy + 1.3 * y2, // - +2.4 - 2.3 * x + 1.3 * y - 0.8 * x2 + 1.2 * xy - 2.0 * y2}; + const double yz = y * z; + const double z2 = z * z; + + return R2x2{+2.3 + 1.7 * x - 2.2 * y + 1.8 * z // + + 0.3 * x2 - 1.3 * xy + 2.7 * xz + 0.7 * y2 + 2.0 * yz - 1.2 * z2, // + +1.4 - 0.6 * x + 1.3 * y - 3.7 * z // + + 3.1 * x2 - 2.4 * xy - 1.5 * xz - 1.9 * y2 + 0.2 * yz + 0.9 * z2, // + // + -0.2 + 3.1 * x - 1.1 * y + 1.9 * z // + - 1.3 * x2 + 2.8 * xy - 2.1 * xz + 3.2 * y2 - 2.1 * yz + 1.6 * z2, // + +0.9 - 1.3 * x + 2.1 * y + 3.1 * z // + + 1.1 * x2 + 1.8 * xy + 1.2 * xz - 2.3 * y2 + 3.3 * yz - 1.2 * z2}; }; auto xr = mesh.xr(); @@ -709,490 +1032,13 @@ TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") Ah[cell_id] = 1. / Vj[cell_id] * value; }); + descriptor.setRowWeighting(false); auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); - - double L1_error = compute_L1_error(cell_type, mesh, A_exact, dpk_Ah); - REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); - } - } - } - - // 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{descriptor}.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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - // } - - // { - // 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - // } - - // { - // 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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - // } - // } - // } - // } - } - - 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 f_exact = [](const R3& X) { - const double& x = X[0]; - const double& y = X[1]; - const double& z = X[2]; - - return 2.3 + 1.7 * x + 0.2 * y + 1.4 * z - 3.2 * x * x + 1.3 * x * y - 1.6 * x * z - 1.4 * y * y + - 2 * y * z - 1.8 * z * z; - }; - - auto xr = mesh.xr(); - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); - - auto cell_type = mesh.connectivity().cellType(); - auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); - - DiscreteFunctionP0<double> fh{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - auto cell_node_list = cell_to_node_matrix[cell_id]; - double value = 0; - - switch (cell_type[cell_id]) { - case CellType::Tetrahedron: { - TetrahedronTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], - xr[cell_node_list[3]]}; - QuadratureFormula<3> qf = - QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{2}); - for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { - const R3 x_hat = qf.point(i_q); - const R3 x = T(x_hat); - value += qf.weight(i_q) * f_exact(x) * T.jacobianDeterminant(); - } - break; - } - case CellType::Pyramid: { - PyramidTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], - xr[cell_node_list[3]], xr[cell_node_list[4]]}; - QuadratureFormula<3> qf = - QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{3}); - for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { - const R3 x_hat = qf.point(i_q); - const R3 x = T(x_hat); - value += qf.weight(i_q) * f_exact(x) * T.jacobianDeterminant(x_hat); - } - break; - } - case CellType::Prism: { - PrismTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], - xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]]}; - QuadratureFormula<3> qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{3}); - for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { - const R3 x_hat = qf.point(i_q); - const R3 x = T(x_hat); - value += qf.weight(i_q) * f_exact(x) * T.jacobianDeterminant(x_hat); - } - break; - } - case CellType::Hexahedron: { - CubeTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], - xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]], - xr[cell_node_list[6]], xr[cell_node_list[7]]}; - QuadratureFormula<3> qf = - QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{3}); - for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { - const R3 x_hat = qf.point(i_q); - const R3 x = T(x_hat); - value += qf.weight(i_q) * f_exact(x) * T.jacobianDeterminant(x_hat); - } - break; - } - default: { - throw UnexpectedError("invalid cell type"); - } - } - - fh[cell_id] = value / Vj[cell_id]; - }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); - - { - double L1_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const auto cell_node_list = cell_to_node_matrix[cell_id]; - const auto dpkj = dpk_fh[cell_id]; - - double error = 0; - - switch (cell_type[cell_id]) { - case CellType::Tetrahedron: { - TetrahedronTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], - xr[cell_node_list[3]]}; - QuadratureFormula<3> qf = - QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{2}); - for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { - const R3 x_hat = qf.point(i_q); - const R3 x = T(x_hat); - error += std::abs(qf.weight(i_q) * (f_exact(x) - dpkj(x)) * T.jacobianDeterminant()); - } - break; - } - case CellType::Pyramid: { - PyramidTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], - xr[cell_node_list[3]], xr[cell_node_list[4]]}; - QuadratureFormula<3> qf = - QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{3}); - for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { - const R3 x_hat = qf.point(i_q); - const R3 x = T(x_hat); - error += std::abs(qf.weight(i_q) * (f_exact(x) - dpkj(x)) * T.jacobianDeterminant(x_hat)); - } - break; - } - case CellType::Prism: { - PrismTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], - xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]]}; - QuadratureFormula<3> qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{3}); - for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { - const R3 x_hat = qf.point(i_q); - const R3 x = T(x_hat); - error += std::abs(qf.weight(i_q) * (f_exact(x) - dpkj(x)) * T.jacobianDeterminant(x_hat)); - } - break; - } - case CellType::Hexahedron: { - CubeTransformation T{xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]], - xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]], - xr[cell_node_list[6]], xr[cell_node_list[7]]}; - QuadratureFormula<3> qf = - QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{3}); - for (size_t i_q = 0; i_q < qf.numberOfPoints(); ++i_q) { - const R3 x_hat = qf.point(i_q); - const R3 x = T(x_hat); - error += std::abs(qf.weight(i_q) * (f_exact(x) - dpkj(x)) * T.jacobianDeterminant(x_hat)); - } - break; - } - default: { - throw UnexpectedError("invalid cell type"); - } - } - - L1_error += error; - } - REQUIRE(parallel::allReduceMax(L1_error) == Catch::Approx(0).margin(1E-13)); - } + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); } } } - - // SECTION("R^3 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 R3_affine = [](const R3& x) -> R3 { - // return R3{+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]}; - // }; - // auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - // DiscreteFunctionP0<R3> uh{p_mesh}; - - // parallel_for( - // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - - // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); - - // { - // 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - // } - // REQUIRE(parallel::allReduceMax(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_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - - // dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - - // max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); - // } - // REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - - // dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - - // max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1})); - // } - // REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - - // dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - - // max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9})); - // } - // REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } - // } - - // SECTION("R^2x2 data") - // { - // using R2x2 = TinyMatrix<2, 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 R2x2_affine = [](const R3& x) -> R2x2 { - // return R2x2{+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(); - - // DiscreteFunctionP0<R2x2> Ah{p_mesh}; - - // parallel_for( - // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); - - // descriptor.setRowWeighting(false); - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - - // auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); - - // { - // 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_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); - // } - // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - - // dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - - // max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, - // // - // -2.3, - // +3.1})); - // } - // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - - // dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - - // max_y_slope_error = - // std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // - // 1.3, +0.8})); - // } - // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - - // dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - - // max_z_slope_error = - // std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // - // +1.4, -1.8})); - // } - // REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } - // } - - // 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]; - // } - // }); - - // descriptor.setPreconditioning(false); - // auto reconstructions = PolynomialReconstruction{descriptor}.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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - // } - - // { - // 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - // } - - // { - // 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(parallel::allReduceMax(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(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); - // } - // } - // } - // } } } } -- GitLab From e6bb5bed0c240b3cd4efc11432bfa0bf0f18c5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 26 Jul 2024 14:53:54 +0200 Subject: [PATCH 054/122] Reorganize slightly integration descriptors --- src/scheme/IntegrationMethodType.hpp | 38 +++++++++++++++++++ src/scheme/PolynomialReconstruction.cpp | 10 ++--- .../PolynomialReconstructionDescriptor.hpp | 20 +++------- src/scheme/test_reconstruction.cpp | 33 ++++------------ tests/test_PolynomialReconstruction.cpp | 4 +- ...est_PolynomialReconstructionDescriptor.cpp | 13 ++----- ...test_QuadraticPolynomialReconstruction.cpp | 25 ++++++------ 7 files changed, 70 insertions(+), 73 deletions(-) create mode 100644 src/scheme/IntegrationMethodType.hpp diff --git a/src/scheme/IntegrationMethodType.hpp b/src/scheme/IntegrationMethodType.hpp new file mode 100644 index 000000000..33264f1dc --- /dev/null +++ b/src/scheme/IntegrationMethodType.hpp @@ -0,0 +1,38 @@ +#ifndef INTEGRATION_METHOD_TYPE_HPP +#define INTEGRATION_METHOD_TYPE_HPP + +#include <utils/PugsMacros.hpp> + +#include <string> + +enum class IntegrationMethodType +{ + boundary, // use divergence theorem to compute polynomial + // integrals + cell_center, // use exact integrals for degree 1 polynomials + // using evaluation at mass center + element // use element based quadrature to compute + // polynomial integrals +}; + +std::string PUGS_INLINE +name(const IntegrationMethodType& method_type) +{ + std::string method_name; + switch (method_type) { + case IntegrationMethodType::boundary: { + method_name = "boundary"; + break; + } + case IntegrationMethodType::cell_center: { + method_name = "cell center"; + break; + } + case IntegrationMethodType::element: { + method_name = "element"; + } + } + return method_name; +} + +#endif // INTEGRATION_METHOD_TYPE_HPP diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 270d663c3..7673255bb 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -671,7 +671,7 @@ PolynomialReconstruction::_build( ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); if ((m_descriptor.degree() == 1) and - (m_descriptor.integrationMethod() == PolynomialReconstructionDescriptor::IntegrationMethod::cell_center)) { + (m_descriptor.integrationMethodType() == IntegrationMethodType::cell_center)) { const Rd& Xj = xj[cell_j_id]; for (size_t i = 0; i < stencil_cell_list.size(); ++i) { const CellId cell_i_id = stencil_cell_list[i]; @@ -681,11 +681,9 @@ PolynomialReconstruction::_build( } } - } else if ((m_descriptor.integrationMethod() == - PolynomialReconstructionDescriptor::IntegrationMethod::element) or - (m_descriptor.integrationMethod() == - PolynomialReconstructionDescriptor::IntegrationMethod::boundary)) { - if ((m_descriptor.integrationMethod() == PolynomialReconstructionDescriptor::IntegrationMethod::boundary) and + } else if ((m_descriptor.integrationMethodType() == IntegrationMethodType::element) or + (m_descriptor.integrationMethodType() == IntegrationMethodType::boundary)) { + if ((m_descriptor.integrationMethodType() == IntegrationMethodType::boundary) and (MeshType::Dimension == 2)) { if constexpr (MeshType::Dimension == 2) { SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek = inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[t]; diff --git a/src/scheme/PolynomialReconstructionDescriptor.hpp b/src/scheme/PolynomialReconstructionDescriptor.hpp index b561f7080..1160724c4 100644 --- a/src/scheme/PolynomialReconstructionDescriptor.hpp +++ b/src/scheme/PolynomialReconstructionDescriptor.hpp @@ -1,6 +1,7 @@ #ifndef POLYNOMIAL_RECONSTRUCTION_DESCRIPTOR_HPP #define POLYNOMIAL_RECONSTRUCTION_DESCRIPTOR_HPP +#include <scheme/IntegrationMethodType.hpp> #include <utils/PugsMacros.hpp> #include <cstddef> @@ -8,26 +9,15 @@ class PolynomialReconstructionDescriptor { public: - enum class IntegrationMethod - { - cell_center, // use exact integrals for degree 1 polynomials - // using evaluation at mass center - element, // use element based quadrature to compute - // polynomial integrals - boundary // use divergence theorem to compute polynomial - // integrals - }; - private: - IntegrationMethod m_integration_method; + IntegrationMethodType m_integration_method; size_t m_degree; bool m_preconditioning = true; bool m_row_weighting = true; public: - PUGS_INLINE - IntegrationMethod - integrationMethod() const + PUGS_INLINE IntegrationMethodType + integrationMethodType() const { return m_integration_method; } @@ -74,7 +64,7 @@ class PolynomialReconstructionDescriptor m_row_weighting = row_weighting; } - PolynomialReconstructionDescriptor(const IntegrationMethod integration_method, const size_t degree) + PolynomialReconstructionDescriptor(const IntegrationMethodType integration_method, const size_t degree) : m_integration_method{integration_method}, m_degree{degree} {} diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index 3753143d3..01007d156 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -1,5 +1,6 @@ #include <scheme/test_reconstruction.hpp> +#include <scheme/IntegrationMethodType.hpp> #include <scheme/PolynomialReconstruction.hpp> #include <utils/Timer.hpp> @@ -7,36 +8,18 @@ void test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, uint64_t degree) { - std::vector descriptor_list = - {PolynomialReconstructionDescriptor{PolynomialReconstructionDescriptor::IntegrationMethod::cell_center, degree}, - PolynomialReconstructionDescriptor{PolynomialReconstructionDescriptor::IntegrationMethod::element, degree}, - PolynomialReconstructionDescriptor{PolynomialReconstructionDescriptor::IntegrationMethod::boundary, degree}}; + std::vector descriptor_list = {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, degree}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree}}; [[maybe_unused]] auto x = - PolynomialReconstruction{ - PolynomialReconstructionDescriptor{PolynomialReconstructionDescriptor::IntegrationMethod::cell_center, degree}} - .build(discrete_function_variant_list); + PolynomialReconstruction{PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, degree}}.build( + discrete_function_variant_list); for (auto&& descriptor : descriptor_list) { - std::string method_name; - switch (descriptor.integrationMethod()) { - case PolynomialReconstructionDescriptor::IntegrationMethod::element: { - method_name = "element"; - break; - } - case PolynomialReconstructionDescriptor::IntegrationMethod::boundary: { - method_name = "boundary"; - break; - } - case PolynomialReconstructionDescriptor::IntegrationMethod::cell_center: { - method_name = "cell_center"; - break; - } - } - const size_t nb_loops = 50; - std::cout << "** variable list at once (" << nb_loops << " times) using " << rang::fgB::yellow << method_name - << rang::fg::reset << "\n"; + std::cout << "** variable list at once (" << nb_loops << " times) using " << rang::fgB::yellow + << name(descriptor.integrationMethodType()) << rang::fg::reset << "\n"; PolynomialReconstruction reconstruction{descriptor}; Timer t; diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index dfa092bb1..3efabce26 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -24,9 +24,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { SECTION("degree 1") { - PolynomialReconstructionDescriptor::IntegrationMethod method = - PolynomialReconstructionDescriptor::IntegrationMethod::cell_center; - PolynomialReconstructionDescriptor descriptor{method, 1}; + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 1}; SECTION("1D") { diff --git a/tests/test_PolynomialReconstructionDescriptor.cpp b/tests/test_PolynomialReconstructionDescriptor.cpp index eda18e3d6..cadc6aaf4 100644 --- a/tests/test_PolynomialReconstructionDescriptor.cpp +++ b/tests/test_PolynomialReconstructionDescriptor.cpp @@ -11,9 +11,7 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") #warning tests are not completed SECTION("degree 1") { - PolynomialReconstructionDescriptor::IntegrationMethod method = - PolynomialReconstructionDescriptor::IntegrationMethod::cell_center; - PolynomialReconstructionDescriptor descriptor{method, 1}; + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 1}; REQUIRE(descriptor.degree() == 1); REQUIRE(descriptor.preconditioning() == true); @@ -22,10 +20,7 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") SECTION("degree 4") { - PolynomialReconstructionDescriptor::IntegrationMethod method = - PolynomialReconstructionDescriptor::IntegrationMethod::cell_center; - - PolynomialReconstructionDescriptor descriptor{method, 4}; + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 4}; REQUIRE(descriptor.degree() == 4); REQUIRE(descriptor.preconditioning() == true); @@ -34,9 +29,7 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") SECTION("utlities") { - PolynomialReconstructionDescriptor::IntegrationMethod method = - PolynomialReconstructionDescriptor::IntegrationMethod::cell_center; - PolynomialReconstructionDescriptor descriptor{method, 3}; + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 3}; REQUIRE(descriptor.degree() == 3); REQUIRE(descriptor.preconditioning() == true); diff --git a/tests/test_QuadraticPolynomialReconstruction.cpp b/tests/test_QuadraticPolynomialReconstruction.cpp index 6b992e960..c9b1e7222 100644 --- a/tests/test_QuadraticPolynomialReconstruction.cpp +++ b/tests/test_QuadraticPolynomialReconstruction.cpp @@ -35,12 +35,9 @@ TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") { SECTION("degree 2") { - PolynomialReconstructionDescriptor::IntegrationMethod method = - PolynomialReconstructionDescriptor::IntegrationMethod::element; - PolynomialReconstructionDescriptor descriptor{method, 2}; - SECTION("1D") { + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 2}; using R1 = TinyVector<1>; QuadratureFormula<1> qf = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{2}); @@ -584,14 +581,12 @@ TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") return L1_error; }; - std::map<std::string, PolynomialReconstructionDescriptor> name_method_map = - {{"element", - PolynomialReconstructionDescriptor{PolynomialReconstructionDescriptor::IntegrationMethod::element, 2}}, - {"boundary", - PolynomialReconstructionDescriptor{PolynomialReconstructionDescriptor::IntegrationMethod::boundary, 2}}}; + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, 2}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 2}}; - for (auto [method_name, method_descriptor] : name_method_map) { - SECTION(method_name) + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) { SECTION("R data") { @@ -621,7 +616,7 @@ TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") fh[cell_id] = value / Vj[cell_id]; }); - auto reconstructions = PolynomialReconstruction{method_descriptor}.build(fh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); @@ -669,7 +664,7 @@ TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") uh[cell_id] = 1. / Vj[cell_id] * value; }); - auto reconstructions = PolynomialReconstruction{method_descriptor}.build(uh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); @@ -718,7 +713,7 @@ TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") Ah[cell_id] = 1. / Vj[cell_id] * value; }); - auto reconstructions = PolynomialReconstruction{method_descriptor}.build(Ah); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); @@ -733,6 +728,8 @@ TEST_CASE("QuadraticPolynomialReconstruction", "[scheme]") SECTION("3D") { + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 2}; + using R3 = TinyVector<3>; auto integrate_in_cell = [](const CellType& cell_type, const auto& cell_node_list, const auto& xr, -- GitLab From f69b27d8dfc78639e60e4f05c0ad72f33efadf84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 26 Jul 2024 15:15:06 +0200 Subject: [PATCH 055/122] Add tests for PolynomialReconstruction using element quadrature --- tests/test_PolynomialReconstruction.cpp | 1542 ++++++++++++----------- 1 file changed, 777 insertions(+), 765 deletions(-) diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 3efabce26..cdfb532f2 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -24,948 +24,960 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") { SECTION("degree 1") { - PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 1}; + std::vector<PolynomialReconstructionDescriptor> descriptor_list = { + PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1}, + }; - SECTION("1D") - { - using R1 = TinyVector<1>; - - SECTION("R data") + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) { - 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 R_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] = R_affine(xj[cell_id]); }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + SECTION("1D") + { + using R1 = TinyVector<1>; - { - 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } - } - } - } - - SECTION("R^3 data") - { - using R3 = TinyVector<3>; - - for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { - SECTION(named_mesh.name()) + SECTION("R data") { - auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); - auto& mesh = *p_mesh; + 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 R3_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(); + auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - DiscreteFunctionP0<R3> uh{p_mesh}; + DiscreteFunctionP0<double> fh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); + auto dpk_fh = 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, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } + { + 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(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_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R1{0.1})); + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2; - max_slope_error = std::max(max_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); } } - } - } - SECTION("R^3x3 data") - { - using R3x3 = TinyMatrix<3, 3>; - - for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { - SECTION(named_mesh.name()) + SECTION("R^3 data") { - auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); - auto& mesh = *p_mesh; + using R3 = TinyVector<3>; - auto R3x3_affine = [](const R1& x) -> R3x3 { - return R3x3{ - +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], - -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], - }; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; - DiscreteFunctionP0<R3x3> Ah{p_mesh}; + auto R3_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(); - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); + DiscreteFunctionP0<R3> uh{p_mesh}; - auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - { - 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_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); - { - double max_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3x3 reconstructed_slope = - (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); + { + 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } - R3x3 slops = R3x3{+1.7, +2.1, -0.6, // - -2.3, +3.1, -3.6, // - +3.1, +2.9, +2.3}; + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = + (1 / 0.2) * (dpk_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R1{0.1})); - max_slope_error = std::max(max_slope_error, // - frobeniusNorm(reconstructed_slope - slops)); + max_slope_error = std::max(max_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); } } - } - } - - SECTION("R vector data") - { - using R3 = TinyVector<3>; - for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { - SECTION(named_mesh.name()) + SECTION("R^3x3 data") { - 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]; + using R3x3 = TinyMatrix<3, 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 R3x3_affine = [](const R1& x) -> R3x3 { + return R3x3{ + +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], + -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], + }; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<R3x3> Ah{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); + + { + 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_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } - }); - auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3x3 reconstructed_slope = + (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); - auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); + R3x3 slops = R3x3{+1.7, +2.1, -0.6, // + -2.3, +3.1, -3.6, // + +3.1, +2.9, +2.3}; - { - 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])); + max_slope_error = std::max(max_slope_error, // + frobeniusNorm(reconstructed_slope - slops)); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); } } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } + } + + 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{descriptor}.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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } - { - double max_slope_error = 0; - const TinyVector<3> slope{+1.7, +2.1, -0.6}; + { + 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})); + 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])); + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); } } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); } } - } - } - SECTION("list of various types") - { - using R3x3 = TinyMatrix<3>; - using R3 = TinyVector<3>; - - for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { - SECTION(named_mesh.name()) + SECTION("list of various types") { - auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); - auto& mesh = *p_mesh; - - auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; - - auto R3_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 R3x3_affine = [](const R1& x) -> R3x3 { - return R3x3{ - +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], - -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], - }; - }; - - 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}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - - DiscreteFunctionP0<R3> uh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - - DiscreteFunctionP0<R3x3> Ah{p_mesh}; - 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{descriptor}.build(std::make_shared<DiscreteFunctionVariant>(fh), uh, - std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), - DiscreteFunctionVariant(Vh)); - - auto dpk_fh = 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } + using R3x3 = TinyMatrix<3>; + 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 R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; + + auto R3_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 R3x3_affine = [](const R1& x) -> R3x3 { + return R3x3{ + +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], + -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], + }; + }; + + 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}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + + DiscreteFunctionP0<R3> uh{p_mesh}; + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + DiscreteFunctionP0<R3x3> Ah{p_mesh}; + 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{descriptor}.build(std::make_shared<DiscreteFunctionVariant>(fh), uh, + std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), + DiscreteFunctionVariant(Vh)); + + auto dpk_fh = 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(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_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2; + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } - auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); + auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); - { - 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } + { + 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(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_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R1{0.1})); + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = + (1 / 0.2) * (dpk_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[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(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } + max_slope_error = std::max(max_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } - auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); + auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); - { - 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_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } + { + 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_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(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 R3x3 reconstructed_slope = - (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); + { + double max_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3x3 reconstructed_slope = + (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); - R3x3 slops = R3x3{+1.7, +2.1, -0.6, // - -2.3, +3.1, -3.6, // - +3.1, +2.9, +2.3}; + R3x3 slops = R3x3{+1.7, +2.1, -0.6, // + -2.3, +3.1, -3.6, // + +3.1, +2.9, +2.3}; - max_slope_error = std::max(max_slope_error, // - frobeniusNorm(reconstructed_slope - slops)); - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); + max_slope_error = std::max(max_slope_error, // + frobeniusNorm(reconstructed_slope - slops)); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); + } - { - 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])); + 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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - { - double max_slope_error = 0; - const TinyVector<3> slope{+1.7, +2.1, -0.6}; + { + 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})); + 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])); + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope[i])); + } + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); } } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); } } } - } - } - SECTION("2D") - { - using R2 = TinyVector<2>; + SECTION("2D") + { + using R2 = TinyVector<2>; - SECTION("R data") - { - for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { - SECTION(named_mesh.name()) + SECTION("R data") { - auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); - auto& mesh = *p_mesh; + 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 R_affine = [](const R2& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1]; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + auto R_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}; + DiscreteFunctionP0<double> fh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } + { + 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(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_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0.1, 0})) / 0.2; + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7)); + } + REQUIRE(parallel::allReduceMax(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 double reconstructed_slope = - (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0, 0.1})) / 0.2; + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[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))); + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3))); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); + } } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); } } - } - } - - SECTION("R^3 data") - { - using R3 = TinyVector<3>; - for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { - SECTION(named_mesh.name()) + SECTION("R^3 data") { - auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); - auto& mesh = *p_mesh; + using R3 = TinyVector<3>; - auto R3_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(); + for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); + auto& mesh = *p_mesh; - DiscreteFunctionP0<R3> uh{p_mesh}; + auto R3_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(); - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + DiscreteFunctionP0<R3> uh{p_mesh}; - auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - { - 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); + + { + 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(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_uh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R2{0.1, 0})); + { + 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_uh[cell_id](R2{0.1, 0} + xj[cell_id]) - + dpk_uh[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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } + max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); + } + REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = - (1 / 0.2) * (dpk_uh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R2{0, 0.1})); + { + 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_uh[cell_id](R2{0, 0.1} + xj[cell_id]) - + dpk_uh[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})); + max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1})); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + } } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); } } - } - } - - SECTION("R^2x2 data") - { - using R2x2 = TinyMatrix<2, 2>; - for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { - SECTION(named_mesh.name()) + SECTION("R^2x2 data") { - auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); - auto& mesh = *p_mesh; + using R2x2 = TinyMatrix<2, 2>; - auto R2x2_affine = [](const R2& x) -> R2x2 { - return R2x2{+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(); + for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); + auto& mesh = *p_mesh; - DiscreteFunctionP0<R2x2> Ah{p_mesh}; + auto R2x2_affine = [](const R2& x) -> R2x2 { + return R2x2{+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(); - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); + DiscreteFunctionP0<R2x2> Ah{p_mesh}; - auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); - auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - { - 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_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = - (1 / 0.2) * (dpk_Ah[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R2{0.1, 0})); + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); - max_x_slope_error = - std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // - -0.6, -2.3})); - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } + { + 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_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_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 R2x2 reconstructed_slope = - (1 / 0.2) * (dpk_Ah[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R2{0, 0.1})); + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0.1, 0} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R2{0.1, 0})); + + max_x_slope_error = + std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // + -0.6, -2.3})); + } + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + } - max_y_slope_error = - std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // - -2.1, +1.3})); + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0, 0.1} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R2{0, 0.1})); + + max_y_slope_error = + std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + -2.1, +1.3})); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + } } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); } } - } - } - SECTION("vector data") - { - using R4 = TinyVector<4>; - - for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { - SECTION(named_mesh.name()) + SECTION("vector data") { - 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]; + 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{descriptor}.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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } - }); - - auto reconstructions = PolynomialReconstruction{descriptor}.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])); + { + 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); } - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - { - 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - 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])); + { + 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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); } } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); } } } - } - } - SECTION("3D") - { - using R3 = TinyVector<3>; + SECTION("3D") + { + using R3 = TinyVector<3>; - SECTION("R data") - { - for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { - SECTION(named_mesh.name()) + SECTION("R data") { - auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); - auto& mesh = *p_mesh; + 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 R_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(); + auto R_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}; + DiscreteFunctionP0<double> fh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); }); - auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } + { + 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(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_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0.1, 0, 0})) / 0.2; + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7)); + } + REQUIRE(parallel::allReduceMax(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 double reconstructed_slope = - (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0.1, 0})) / 0.2; + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - } + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3))); + } + REQUIRE(parallel::allReduceMax(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 double reconstructed_slope = - (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0, 0.1})) / 0.2; + { + double max_z_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const double reconstructed_slope = + (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[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)); + max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - 2.1)); + } + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + } } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); } } - } - } - SECTION("R^3 data") - { - for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { - SECTION(named_mesh.name()) + SECTION("R^3 data") { - auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); - auto& mesh = *p_mesh; - - auto R3_affine = [](const R3& x) -> R3 { - return R3{+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]}; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<R3> uh{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - - auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - - auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); - - { - 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } + 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 R3_affine = [](const R3& x) -> R3 { + return R3{+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]}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<R3> uh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + { + 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(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_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + { + 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_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12)); - } + max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); + } + REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + { + 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_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1})); - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); - } + max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1})); + } + REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + { + double max_z_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9})); + max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9})); + } + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + } } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); } } - } - } - SECTION("R^2x2 data") - { - using R2x2 = TinyMatrix<2, 2>; - - for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { - SECTION(named_mesh.name()) + SECTION("R^2x2 data") { - auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); - auto& mesh = *p_mesh; - - auto R2x2_affine = [](const R3& x) -> R2x2 { - return R2x2{+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(); - - DiscreteFunctionP0<R2x2> Ah{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); - - descriptor.setRowWeighting(false); - auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - - auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); - - { - 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_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - - max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // - -2.3, +3.1})); - } - REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + using R2x2 = TinyMatrix<2, 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 R2x2_affine = [](const R3& x) -> R2x2 { + return R2x2{+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(); + + DiscreteFunctionP0<R2x2> Ah{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); + + descriptor.setRowWeighting(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + + { + 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_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } - max_y_slope_error = - std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // - 1.3, +0.8})); - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); - } + { + double max_x_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + + max_x_slope_error = + std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // + -2.3, +3.1})); + } + REQUIRE(parallel::allReduceMax(max_x_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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + { + double max_y_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + + max_y_slope_error = + std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + 1.3, +0.8})); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); + } - max_z_slope_error = - std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // - +1.4, -1.8})); + { + double max_z_slope_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + + max_z_slope_error = + std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // + +1.4, -1.8})); + } + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + } } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); } } - } - } - - SECTION("vector data") - { - using R4 = TinyVector<4>; - for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { - SECTION(named_mesh.name()) + SECTION("vector data") { - 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]; - } - }); - - descriptor.setPreconditioning(false); - auto reconstructions = PolynomialReconstruction{descriptor}.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])); + 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]; + } + }); + + descriptor.setPreconditioning(false); + auto reconstructions = PolynomialReconstruction{descriptor}.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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); } - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - 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])); + { + 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); } - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - 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])); + { + 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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); } - } - REQUIRE(parallel::allReduceMax(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])); + { + 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(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); } } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); } } } - } - } - SECTION("errors") - { - auto p_mesh1 = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - DiscreteFunctionP0<double> f1{p_mesh1}; + 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}; + auto p_mesh2 = MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); + DiscreteFunctionP0<double> f2{p_mesh2}; - REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(f1, f2), - "error: cannot reconstruct functions living of different meshes simultaneously"); + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(f1, f2), + "error: cannot reconstruct functions living of different meshes simultaneously"); + } + } } } } -- GitLab From 893678544ea5884ad73241661308fad2fad8c153 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Thu, 8 Aug 2024 14:30:29 +0200 Subject: [PATCH 056/122] Add origin calculation and storage for flat boundaries --- src/mesh/MeshFlatEdgeBoundary.cpp | 2 +- src/mesh/MeshFlatEdgeBoundary.hpp | 14 ++++- src/mesh/MeshFlatFaceBoundary.cpp | 2 +- src/mesh/MeshFlatFaceBoundary.hpp | 14 ++++- src/mesh/MeshFlatNodeBoundary.cpp | 85 +++++++++++++++++++++++++++++-- src/mesh/MeshFlatNodeBoundary.hpp | 13 ++++- 6 files changed, 117 insertions(+), 13 deletions(-) diff --git a/src/mesh/MeshFlatEdgeBoundary.cpp b/src/mesh/MeshFlatEdgeBoundary.cpp index 7a2d4918d..9c93f2bc7 100644 --- a/src/mesh/MeshFlatEdgeBoundary.cpp +++ b/src/mesh/MeshFlatEdgeBoundary.cpp @@ -11,7 +11,7 @@ getMeshFlatEdgeBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundar MeshEdgeBoundary mesh_edge_boundary = getMeshEdgeBoundary(mesh, boundary_descriptor); MeshFlatNodeBoundary mesh_flat_node_boundary = getMeshFlatNodeBoundary(mesh, boundary_descriptor); - return MeshFlatEdgeBoundary<MeshType>{mesh, mesh_edge_boundary.refEdgeList(), + return MeshFlatEdgeBoundary<MeshType>{mesh, mesh_edge_boundary.refEdgeList(), mesh_flat_node_boundary.origin(), mesh_flat_node_boundary.outgoingNormal()}; } diff --git a/src/mesh/MeshFlatEdgeBoundary.hpp b/src/mesh/MeshFlatEdgeBoundary.hpp index 70b51bc98..ce3d1ded1 100644 --- a/src/mesh/MeshFlatEdgeBoundary.hpp +++ b/src/mesh/MeshFlatEdgeBoundary.hpp @@ -12,9 +12,16 @@ class MeshFlatEdgeBoundary final : public MeshEdgeBoundary // clazy:exclude=co using Rd = TinyVector<MeshType::Dimension, double>; private: + const Rd m_origin; const Rd m_outgoing_normal; public: + const Rd& + origin() const + { + return m_origin; + } + const Rd& outgoingNormal() const { @@ -29,8 +36,11 @@ class MeshFlatEdgeBoundary final : public MeshEdgeBoundary // clazy:exclude=co const IBoundaryDescriptor& boundary_descriptor); private: - MeshFlatEdgeBoundary(const MeshType& mesh, const RefEdgeList& ref_edge_list, const Rd& outgoing_normal) - : MeshEdgeBoundary(mesh, ref_edge_list), m_outgoing_normal(outgoing_normal) + MeshFlatEdgeBoundary(const MeshType& mesh, + const RefEdgeList& ref_edge_list, + const Rd& origin, + const Rd& outgoing_normal) + : MeshEdgeBoundary(mesh, ref_edge_list), m_origin(origin), m_outgoing_normal(outgoing_normal) {} public: diff --git a/src/mesh/MeshFlatFaceBoundary.cpp b/src/mesh/MeshFlatFaceBoundary.cpp index 2ecdf6768..46aa7063f 100644 --- a/src/mesh/MeshFlatFaceBoundary.cpp +++ b/src/mesh/MeshFlatFaceBoundary.cpp @@ -11,7 +11,7 @@ getMeshFlatFaceBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundar MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(mesh, boundary_descriptor); MeshFlatNodeBoundary mesh_flat_node_boundary = getMeshFlatNodeBoundary(mesh, boundary_descriptor); - return MeshFlatFaceBoundary<MeshType>{mesh, mesh_face_boundary.refFaceList(), + return MeshFlatFaceBoundary<MeshType>{mesh, mesh_face_boundary.refFaceList(), mesh_flat_node_boundary.origin(), mesh_flat_node_boundary.outgoingNormal()}; } diff --git a/src/mesh/MeshFlatFaceBoundary.hpp b/src/mesh/MeshFlatFaceBoundary.hpp index fc4d9d0f8..214220268 100644 --- a/src/mesh/MeshFlatFaceBoundary.hpp +++ b/src/mesh/MeshFlatFaceBoundary.hpp @@ -12,9 +12,16 @@ class MeshFlatFaceBoundary final : public MeshFaceBoundary // clazy:exclude=co using Rd = TinyVector<MeshType::Dimension, double>; private: + const Rd m_origin; const Rd m_outgoing_normal; public: + const Rd& + origin() const + { + return m_origin; + } + const Rd& outgoingNormal() const { @@ -29,8 +36,11 @@ class MeshFlatFaceBoundary final : public MeshFaceBoundary // clazy:exclude=co const IBoundaryDescriptor& boundary_descriptor); private: - MeshFlatFaceBoundary(const MeshType& mesh, const RefFaceList& ref_face_list, const Rd& outgoing_normal) - : MeshFaceBoundary(mesh, ref_face_list), m_outgoing_normal(outgoing_normal) + MeshFlatFaceBoundary(const MeshType& mesh, + const RefFaceList& ref_face_list, + const Rd& origin, + const Rd& outgoing_normal) + : MeshFaceBoundary(mesh, ref_face_list), m_origin(origin), m_outgoing_normal(outgoing_normal) {} public: diff --git a/src/mesh/MeshFlatNodeBoundary.cpp b/src/mesh/MeshFlatNodeBoundary.cpp index c3ca1dbd4..647298e97 100644 --- a/src/mesh/MeshFlatNodeBoundary.cpp +++ b/src/mesh/MeshFlatNodeBoundary.cpp @@ -34,14 +34,13 @@ MeshFlatNodeBoundary<MeshType>::_checkBoundaryIsFlat(const TinyVector<MeshType:: template <> TinyVector<1, double> -MeshFlatNodeBoundary<Mesh<1>>::_getNormal(const Mesh<1>& mesh) +MeshFlatNodeBoundary<Mesh<1>>::_getOrigin(const Mesh<1>& mesh) { - using R = TinyVector<1, double>; + auto node_is_owned = mesh.connectivity().nodeIsOwned(); + auto node_list = m_ref_node_list.list(); const size_t number_of_bc_nodes = [&]() { size_t nb_bc_nodes = 0; - auto node_is_owned = mesh.connectivity().nodeIsOwned(); - auto node_list = m_ref_node_list.list(); for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { nb_bc_nodes += (node_is_owned[node_list[i_node]]); } @@ -55,7 +54,43 @@ MeshFlatNodeBoundary<Mesh<1>>::_getNormal(const Mesh<1>& mesh) throw NormalError(ost.str()); } - return R{1}; + auto xr = mesh.xr(); + + Array<Rd> origin; + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + if (node_is_owned[node_list[i_node]]) { + origin = Array<Rd>(1); + origin[0] = xr[node_id]; + } + } + origin = parallel::allGatherVariable(origin); + Assert(origin.size() == 1); + return origin[0]; +} + +template <> +TinyVector<1, double> +MeshFlatNodeBoundary<Mesh<1>>::_getNormal(const Mesh<1>&) +{ + using R1 = TinyVector<1, double>; + + // The verification of the unicity of the boundary node is performed by _getOrigin() + return R1{1}; +} + +template <> +TinyVector<2, double> +MeshFlatNodeBoundary<Mesh<2>>::_getOrigin(const Mesh<2>& mesh) +{ + using R2 = TinyVector<2, double>; + + std::array<R2, 2> bounds = getBounds(mesh, m_ref_node_list); + + const R2& xmin = bounds[0]; + const R2& xmax = bounds[1]; + + return 0.5 * (xmin + xmax); } template <> @@ -137,6 +172,46 @@ MeshFlatNodeBoundary<Mesh<3>>::_getFarestNode(const Mesh<3>& mesh, const Rd& x0, return farest_x; } +template <> +TinyVector<3, double> +MeshFlatNodeBoundary<Mesh<3>>::_getOrigin(const Mesh<3>& mesh) +{ + using R3 = TinyVector<3, double>; + + std::array<R3, 2> diagonal = [](const std::array<R3, 6>& bounds) { + size_t max_i = 0; + size_t max_j = 0; + double max_length = 0; + + for (size_t i = 0; i < bounds.size(); ++i) { + for (size_t j = i + 1; j < bounds.size(); ++j) { + double length = l2Norm(bounds[i] - bounds[j]); + if (length > max_length) { + max_i = i; + max_j = j; + max_length = length; + } + } + } + + return std::array<R3, 2>{bounds[max_i], bounds[max_j]}; + }(getBounds(mesh, m_ref_node_list)); + + const R3& x0 = diagonal[0]; + const R3& x1 = diagonal[1]; + + if (x0 == x1) { + std::ostringstream ost; + ost << "invalid boundary \"" << rang::fgB::yellow << m_ref_node_list.refId() << rang::style::reset + << "\": unable to compute normal"; + throw NormalError(ost.str()); + } + + const R3 x2 = this->_getFarestNode(mesh, x0, x1); + + return 1. / 3. * (x0 + x1 + x2); +} + template <> TinyVector<3, double> MeshFlatNodeBoundary<Mesh<3>>::_getNormal(const Mesh<3>& mesh) diff --git a/src/mesh/MeshFlatNodeBoundary.hpp b/src/mesh/MeshFlatNodeBoundary.hpp index 66c610ea7..2ef2a22da 100644 --- a/src/mesh/MeshFlatNodeBoundary.hpp +++ b/src/mesh/MeshFlatNodeBoundary.hpp @@ -13,6 +13,7 @@ class [[nodiscard]] MeshFlatNodeBoundary final : public MeshNodeBoundary // cl using Rd = TinyVector<MeshType::Dimension, double>; private: + const Rd m_origin; const Rd m_outgoing_normal; Rd _getFarestNode(const MeshType& mesh, const Rd& x0, const Rd& x1); @@ -24,9 +25,17 @@ class [[nodiscard]] MeshFlatNodeBoundary final : public MeshNodeBoundary // cl const double length, const MeshType& mesh) const; + Rd _getOrigin(const MeshType& mesh); + Rd _getOutgoingNormal(const MeshType& mesh); public: + const Rd& + origin() const + { + return m_origin; + } + const Rd& outgoingNormal() const { @@ -42,11 +51,11 @@ class [[nodiscard]] MeshFlatNodeBoundary final : public MeshNodeBoundary // cl private: MeshFlatNodeBoundary(const MeshType& mesh, const RefFaceList& ref_face_list) - : MeshNodeBoundary(mesh, ref_face_list), m_outgoing_normal(_getOutgoingNormal(mesh)) + : MeshNodeBoundary(mesh, ref_face_list), m_origin{_getOrigin(mesh)}, m_outgoing_normal(_getOutgoingNormal(mesh)) {} MeshFlatNodeBoundary(const MeshType& mesh, const RefNodeList& ref_node_list) - : MeshNodeBoundary(mesh, ref_node_list), m_outgoing_normal(_getOutgoingNormal(mesh)) + : MeshNodeBoundary(mesh, ref_node_list), m_origin{_getOrigin(mesh)}, m_outgoing_normal(_getOutgoingNormal(mesh)) {} public: -- GitLab From b4395bfdc621f22adb2e7e0c4350087a395fbec1 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Thu, 8 Aug 2024 14:31:27 +0200 Subject: [PATCH 057/122] Add dirty implementation of stencil dealing with symmetric BC This is a really quick and dirty implementation. Should not be used by now. --- src/mesh/Stencil.hpp | 14 +- src/mesh/StencilBuilder.cpp | 250 ++++++++--- src/mesh/StencilBuilder.hpp | 11 +- src/mesh/StencilManager.cpp | 12 +- src/mesh/StencilManager.hpp | 23 +- src/scheme/PolynomialReconstruction.cpp | 405 +++++++++++++++++- .../PolynomialReconstructionDescriptor.hpp | 18 +- src/scheme/test_reconstruction.cpp | 11 +- tests/test_StencilBuilder.cpp | 152 ++++--- 9 files changed, 741 insertions(+), 155 deletions(-) diff --git a/src/mesh/Stencil.hpp b/src/mesh/Stencil.hpp index d67aa09ed..5dc20dc32 100644 --- a/src/mesh/Stencil.hpp +++ b/src/mesh/Stencil.hpp @@ -8,8 +8,17 @@ class Stencil { private: ConnectivityMatrix m_stencil; +#warning TEMPORARY IMPLEMENTATION + std::vector<std::pair<std::string, ConnectivityMatrix>> m_symmetry_name_stencil; public: + PUGS_INLINE + const auto& + symmetryNameStencil() const + { + return m_symmetry_name_stencil; + } + PUGS_INLINE auto operator[](CellId cell_id) const @@ -17,7 +26,10 @@ class Stencil return m_stencil[cell_id]; } - Stencil(const ConnectivityMatrix& stencil) : m_stencil(stencil) {} + Stencil(const ConnectivityMatrix& stencil, + const std::vector<std::pair<std::string, ConnectivityMatrix>>& symmetry_name_stencil) + : m_stencil{stencil}, m_symmetry_name_stencil{symmetry_name_stencil} + {} Stencil(const Stencil&) = default; Stencil(Stencil&&) = default; ~Stencil() = default; diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 34015395a..92738a643 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -1,6 +1,7 @@ #include <mesh/Connectivity.hpp> #include <mesh/StencilBuilder.hpp> +#include <mesh/ItemArray.hpp> #include <utils/Messenger.hpp> #warning remove after rework @@ -109,91 +110,238 @@ StencilBuilder::_getColumnIndices(const ConnectivityType& connectivity, const Ar template <typename ConnectivityType> Stencil -StencilBuilder::_build(const ConnectivityType& connectivity, size_t number_of_layers) const +StencilBuilder::_build(const ConnectivityType& connectivity, + size_t number_of_layers, + const std::set<std::string>& symmetry_boundary_names) const { - if (number_of_layers == 1) { - Array<const uint32_t> row_map = this->_getRowMap(connectivity); - Array<const uint32_t> column_indices = this->_getColumnIndices(connectivity, row_map); - - return ConnectivityMatrix{row_map, column_indices}; - } else { - if (number_of_layers > 2) { - throw NotImplementedError("number of layers too large"); - } - if (parallel::size() > 1) { - throw NotImplementedError("stencils with more than 1 layers are not defined in parallel"); - } +#warning TEMPORARY + if (symmetry_boundary_names.size() == 0) { + if (number_of_layers == 1) { + Array<const uint32_t> row_map = this->_getRowMap(connectivity); + Array<const uint32_t> column_indices = this->_getColumnIndices(connectivity, row_map); + + return {ConnectivityMatrix{row_map, column_indices}, {}}; + } else { + if (number_of_layers > 2) { + throw NotImplementedError("number of layers too large"); + } + if (parallel::size() > 1) { + throw NotImplementedError("stencils with more than 1 layers are not defined in parallel"); + } - auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); - auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); + auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); - auto cell_is_owned = connectivity.cellIsOwned(); - auto cell_number = connectivity.cellNumber(); + auto cell_is_owned = connectivity.cellIsOwned(); + auto cell_number = connectivity.cellNumber(); - Array<uint32_t> row_map{connectivity.numberOfCells() + 1}; - row_map[0] = 0; + Array<uint32_t> row_map{connectivity.numberOfCells() + 1}; + row_map[0] = 0; - std::vector<CellId> column_indices_vector; + std::vector<CellId> column_indices_vector; - for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { - if (cell_is_owned[cell_id]) { - std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( - [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + if (cell_is_owned[cell_id]) { + std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); - for (size_t i_node_1 = 0; i_node_1 < cell_to_node_matrix[cell_id].size(); ++i_node_1) { - const NodeId layer_1_node_id = cell_to_node_matrix[cell_id][i_node_1]; + for (size_t i_node_1 = 0; i_node_1 < cell_to_node_matrix[cell_id].size(); ++i_node_1) { + const NodeId layer_1_node_id = cell_to_node_matrix[cell_id][i_node_1]; - for (size_t i_cell_1 = 0; i_cell_1 < node_to_cell_matrix[layer_1_node_id].size(); ++i_cell_1) { - CellId cell_1_id = node_to_cell_matrix[layer_1_node_id][i_cell_1]; + for (size_t i_cell_1 = 0; i_cell_1 < node_to_cell_matrix[layer_1_node_id].size(); ++i_cell_1) { + CellId cell_1_id = node_to_cell_matrix[layer_1_node_id][i_cell_1]; - for (size_t i_node_2 = 0; i_node_2 < cell_to_node_matrix[cell_1_id].size(); ++i_node_2) { - const NodeId layer_2_node_id = cell_to_node_matrix[cell_1_id][i_node_2]; + for (size_t i_node_2 = 0; i_node_2 < cell_to_node_matrix[cell_1_id].size(); ++i_node_2) { + const NodeId layer_2_node_id = cell_to_node_matrix[cell_1_id][i_node_2]; - for (size_t i_cell_2 = 0; i_cell_2 < node_to_cell_matrix[layer_2_node_id].size(); ++i_cell_2) { - CellId cell_2_id = node_to_cell_matrix[layer_2_node_id][i_cell_2]; + for (size_t i_cell_2 = 0; i_cell_2 < node_to_cell_matrix[layer_2_node_id].size(); ++i_cell_2) { + CellId cell_2_id = node_to_cell_matrix[layer_2_node_id][i_cell_2]; - if (cell_2_id != cell_id) { - cell_set.insert(cell_2_id); + if (cell_2_id != cell_id) { + cell_set.insert(cell_2_id); + } } } } } - } - for (auto stencil_cell_id : cell_set) { - column_indices_vector.push_back(stencil_cell_id); + for (auto stencil_cell_id : cell_set) { + column_indices_vector.push_back(stencil_cell_id); + } + row_map[cell_id + 1] = row_map[cell_id] + cell_set.size(); } - row_map[cell_id + 1] = row_map[cell_id] + cell_set.size(); } - } - if (row_map[row_map.size() - 1] != column_indices_vector.size()) { - throw UnexpectedError("incorrect stencil size"); - } + if (row_map[row_map.size() - 1] != column_indices_vector.size()) { + throw UnexpectedError("incorrect stencil size"); + } + + Array<uint32_t> column_indices(row_map[row_map.size() - 1]); + column_indices.fill(std::numeric_limits<uint32_t>::max()); - Array<uint32_t> column_indices(row_map[row_map.size() - 1]); - column_indices.fill(std::numeric_limits<uint32_t>::max()); + for (size_t i = 0; i < column_indices.size(); ++i) { + column_indices[i] = column_indices_vector[i]; + } + ConnectivityMatrix primal_stencil{row_map, column_indices}; - for (size_t i = 0; i < column_indices.size(); ++i) { - column_indices[i] = column_indices_vector[i]; + return {primal_stencil, {}}; } + } else { + if constexpr (ConnectivityType::Dimension > 1) { + std::vector<Array<const FaceId>> boundary_node_list; + + NodeArray<bool> symmetry_node_list(connectivity, symmetry_boundary_names.size()); + symmetry_node_list.fill(0); + + auto face_to_node_matrix = connectivity.faceToNodeMatrix(); + auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); + auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + + { + size_t i_boundary_name = 0; + for (auto boundary_name : symmetry_boundary_names) { + bool found = false; + for (size_t i_ref_node_list = 0; + i_ref_node_list < connectivity.template numberOfRefItemList<ItemType::face>(); ++i_ref_node_list) { + const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i_ref_node_list); + if (ref_face_list.refId().tagName() == boundary_name) { + found = true; + boundary_node_list.push_back(ref_face_list.list()); + for (size_t i_face = 0; i_face < ref_face_list.list().size(); ++i_face) { + const FaceId face_id = ref_face_list.list()[i_face]; + auto node_list = face_to_node_matrix[face_id]; + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + + symmetry_node_list[node_id][i_boundary_name] = true; + } + } + break; + } + } + ++i_boundary_name; + if (not found) { +#warning IMPROVE ERROR MESSAGE + throw NormalError("cannot find boundary " + boundary_name); + }; + } + } + + auto cell_is_owned = connectivity.cellIsOwned(); + auto cell_number = connectivity.cellNumber(); - return ConnectivityMatrix{row_map, column_indices}; + Array<uint32_t> row_map{connectivity.numberOfCells() + 1}; + row_map[0] = 0; + std::vector<Array<uint32_t>> symmetry_row_map_list(symmetry_boundary_names.size()); + for (auto&& symmetry_row_map : symmetry_row_map_list) { + symmetry_row_map = Array<uint32_t>{connectivity.numberOfCells() + 1}; + symmetry_row_map[0] = 0; + } + + std::vector<uint32_t> column_indices_vector; + std::vector<std::vector<uint32_t>> symmetry_column_indices_vector(symmetry_boundary_names.size()); + + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + std::set<CellId> cell_set; + + if (cell_is_owned[cell_id]) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + for (size_t i_cell_node = 0; i_cell_node < cell_node_list.size(); ++i_cell_node) { + const NodeId cell_node_id = cell_node_list[i_cell_node]; + auto node_cell_list = node_to_cell_matrix[cell_node_id]; + for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { + const CellId node_cell_id = node_cell_list[i_node_cell]; + if (cell_id != node_cell_id) { + cell_set.insert(node_cell_id); + } + } + } + row_map[cell_id + 1] = row_map[cell_id] + cell_set.size(); + + { + std::vector<CellId> cell_vector; + for (auto&& set_cell_id : cell_set) { + cell_vector.push_back(set_cell_id); + } + std::sort(cell_vector.begin(), cell_vector.end(), + [&cell_number](const CellId& cell0_id, const CellId& cell1_id) { + return cell_number[cell0_id] < cell_number[cell1_id]; + }); + + for (auto&& vector_cell_id : cell_vector) { + column_indices_vector.push_back(vector_cell_id); + } + } + + std::vector<std::set<CellId>> by_boundary_symmetry_cell; + for (size_t i = 0; i < symmetry_boundary_names.size(); ++i) { + std::set<CellId> symmetry_cell_set; + for (size_t i_cell_node = 0; i_cell_node < cell_node_list.size(); ++i_cell_node) { + const NodeId cell_node_id = cell_node_list[i_cell_node]; + if (symmetry_node_list[cell_node_id][i]) { + auto node_cell_list = node_to_cell_matrix[cell_node_id]; + for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { + const CellId node_cell_id = node_cell_list[i_node_cell]; + symmetry_cell_set.insert(node_cell_id); + } + } + } + symmetry_row_map_list[i][cell_id + 1] = symmetry_row_map_list[i][cell_id] + symmetry_cell_set.size(); + by_boundary_symmetry_cell.emplace_back(symmetry_cell_set); + + std::vector<CellId> cell_vector; + for (auto&& set_cell_id : symmetry_cell_set) { + cell_vector.push_back(set_cell_id); + } + std::sort(cell_vector.begin(), cell_vector.end(), + [&cell_number](const CellId& cell0_id, const CellId& cell1_id) { + return cell_number[cell0_id] < cell_number[cell1_id]; + }); + + for (auto&& vector_cell_id : cell_vector) { + symmetry_column_indices_vector[i].push_back(vector_cell_id); + } + } + } + } + ConnectivityMatrix primal_stencil{row_map, convert_to_array(column_indices_vector)}; + + std::vector<std::pair<std::string, ConnectivityMatrix>> symmetry_name_stencil; + { + size_t i = 0; + for (auto&& boundary_name : symmetry_boundary_names) { + symmetry_name_stencil.emplace_back( + std::make_pair(boundary_name, ConnectivityMatrix{symmetry_row_map_list[i], + convert_to_array(symmetry_column_indices_vector[i])})); + ++i; + } + } + + return {{primal_stencil}, {symmetry_name_stencil}}; + + } else { + throw NotImplementedError("Only implemented in 2D"); + } } } Stencil -StencilBuilder::build(const IConnectivity& connectivity, size_t number_of_layers) const +StencilBuilder::build(const IConnectivity& connectivity, + size_t number_of_layers, + const std::set<std::string>& symmetry_boundary_names) const { switch (connectivity.dimension()) { case 1: { - return StencilBuilder::_build(dynamic_cast<const Connectivity<1>&>(connectivity), number_of_layers); + return StencilBuilder::_build(dynamic_cast<const Connectivity<1>&>(connectivity), number_of_layers, + symmetry_boundary_names); } case 2: { - return StencilBuilder::_build(dynamic_cast<const Connectivity<2>&>(connectivity), number_of_layers); + return StencilBuilder::_build(dynamic_cast<const Connectivity<2>&>(connectivity), number_of_layers, + symmetry_boundary_names); } case 3: { - return StencilBuilder::_build(dynamic_cast<const Connectivity<3>&>(connectivity), number_of_layers); + return StencilBuilder::_build(dynamic_cast<const Connectivity<3>&>(connectivity), number_of_layers, + symmetry_boundary_names); } default: { throw UnexpectedError("invalid connectivity dimension"); diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index 53918e64a..c52933da2 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -3,6 +3,9 @@ #include <mesh/Stencil.hpp> +#include <set> +#include <string> + class IConnectivity; class StencilBuilder { @@ -15,10 +18,14 @@ class StencilBuilder const Array<const uint32_t>& row_map) const; template <typename ConnectivityType> - Stencil _build(const ConnectivityType& connectivity, size_t number_of_layers) const; + Stencil _build(const ConnectivityType& connectivity, + size_t number_of_layers, + const std::set<std::string>& symmetry_boundary_names) const; friend class StencilManager; - Stencil build(const IConnectivity& connectivity, size_t number_of_layers) const; + Stencil build(const IConnectivity& connectivity, + size_t number_of_layers, + const std::set<std::string>& symmetry_boundary_names) const; public: StencilBuilder() = default; diff --git a/src/mesh/StencilManager.cpp b/src/mesh/StencilManager.cpp index 120a95e0d..618431ee7 100644 --- a/src/mesh/StencilManager.cpp +++ b/src/mesh/StencilManager.cpp @@ -32,14 +32,16 @@ StencilManager::destroy() } const Stencil& -StencilManager::getStencil(const IConnectivity& connectivity, size_t degree) +StencilManager::getStencil(const IConnectivity& connectivity, + size_t degree, + const std::set<std::string>& symmetry_boundary_names) { - if (not m_stored_stencil_map.contains(Key{connectivity.id(), degree})) { - m_stored_stencil_map[Key{connectivity.id(), degree}] = - std::make_shared<Stencil>(StencilBuilder{}.build(connectivity, degree)); + if (not m_stored_stencil_map.contains(Key{connectivity.id(), degree, symmetry_boundary_names})) { + m_stored_stencil_map[Key{connectivity.id(), degree, symmetry_boundary_names}] = + std::make_shared<Stencil>(StencilBuilder{}.build(connectivity, degree, symmetry_boundary_names)); } - return *m_stored_stencil_map.at(Key{connectivity.id(), degree}); + return *m_stored_stencil_map.at(Key{connectivity.id(), degree, symmetry_boundary_names}); } void diff --git a/src/mesh/StencilManager.hpp b/src/mesh/StencilManager.hpp index f2912be1f..8199ce5a3 100644 --- a/src/mesh/StencilManager.hpp +++ b/src/mesh/StencilManager.hpp @@ -5,6 +5,7 @@ #include <mesh/Stencil.hpp> #include <memory> +#include <set> #include <unordered_map> class StencilManager @@ -19,11 +20,26 @@ class StencilManager { size_t connectivity_id; size_t degree; + std::set<std::string> symmetry_boundary_names; PUGS_INLINE bool operator==(const Key& k) const { - return (connectivity_id == k.connectivity_id) and (degree == k.degree); + if ((connectivity_id != k.connectivity_id) or (degree != k.degree) or + (symmetry_boundary_names.size() != k.symmetry_boundary_names.size())) { + return false; + } + + auto i_name = symmetry_boundary_names.begin(); + auto i_k_name = k.symmetry_boundary_names.begin(); + + for (; i_name != symmetry_boundary_names.end(); ++i_name, ++i_k_name) { + if (*i_name != *i_k_name) { + return false; + } + } + + return true; } }; struct HashKey @@ -31,6 +47,7 @@ class StencilManager size_t operator()(const Key& key) const { + // We do not use the symmetry boundary set by now return (std::hash<decltype(Key::connectivity_id)>()(key.connectivity_id)) ^ (std::hash<decltype(Key::degree)>()(key.degree) << 1); } @@ -52,7 +69,9 @@ class StencilManager void deleteConnectivity(const size_t connectivity_id); - const Stencil& getStencil(const IConnectivity& i_connectivity, size_t degree = 1); + const Stencil& getStencil(const IConnectivity& i_connectivity, + size_t degree = 1, + const std::set<std::string>& symmetry_boundary_names = {}); StencilManager(const StencilManager&) = delete; StencilManager(StencilManager&&) = delete; diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 7673255bb..086583c9f 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -17,11 +17,29 @@ #include <geometry/TriangleTransformation.hpp> #include <mesh/MeshData.hpp> #include <mesh/MeshDataManager.hpp> +#include <mesh/MeshFlatFaceBoundary.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> #include <mesh/StencilManager.hpp> #include <scheme/DiscreteFunctionDPkVariant.hpp> #include <scheme/DiscreteFunctionUtils.hpp> #include <scheme/DiscreteFunctionVariant.hpp> +template <size_t Dimension> +PUGS_INLINE auto +symmetrize_vector(const TinyVector<Dimension>& normal, const TinyVector<Dimension>& u) +{ + return u - 2 * dot(u, normal) * normal; +} + +template <size_t Dimension> +PUGS_INLINE auto +symmetrize_coordinates(const TinyVector<Dimension>& origin, + const TinyVector<Dimension>& normal, + const TinyVector<Dimension>& u) +{ + return u - 2 * dot(u - origin, normal) * normal; +} + class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant { public: @@ -304,6 +322,35 @@ _computeEjkMean(const TinyVector<1>& Xj, } } +template <typename NodeListT> +void +_computeEjkMeanInSymmetricCell(const TinyVector<1>& origin, + const TinyVector<1>& normal, + const TinyVector<1>& Xj, + const NodeValue<const TinyVector<1>>& xr, + const NodeListT& node_list, + const CellType& cell_type, + const double Vi, + const size_t degree, + const size_t basis_dimension, + SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + if (cell_type == CellType::Line) { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + + const LineTransformation<1> T{x0, x1}; + + const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } +} + template <typename ConformTransformationT> void _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, @@ -384,6 +431,46 @@ _computeEjkMeanByBoundary(const TinyVector<2>& Xj, } } +void +_computeEjkMeanByBoundaryInSymmetricCell(const TinyVector<2>& origin, + const TinyVector<2>& normal, + const TinyVector<2>& Xj, + const CellId& cell_id, + const NodeValue<const TinyVector<2>>& xr, + const auto& cell_to_face_matrix, + const auto& face_to_node_matrix, + const auto& cell_face_is_reversed, + const CellValue<const double>& Vi, + const size_t degree, + const size_t basis_dimension, + SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + SmallArray<double>& mean_of_ejk) +{ + const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(degree + 1)); + + mean_of_ejk.fill(0); + auto cell_face_list = cell_to_face_matrix[cell_id]; + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + bool is_reversed = cell_face_is_reversed[cell_id][i_face]; + + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = face_to_node_matrix[face_id]; + + const auto x0 = symmetrize_coordinates(origin, normal, xr[face_node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[face_node_list[0]]); + + if (is_reversed) { + const LineTransformation<2> T{x1, x0}; + _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, + inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); + } else { + const LineTransformation<2> T{x0, x1}; + _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, + inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); + } + } +} + template <typename NodeListT> void _computeEjkMean(const TinyVector<2>& Xj, @@ -418,6 +505,51 @@ _computeEjkMean(const TinyVector<2>& Xj, } } +template <typename NodeListT> +void +_computeEjkMeanInSymmetricCell(const TinyVector<2>& origin, + const TinyVector<2>& normal, + const TinyVector<2>& Xj, + const NodeValue<const TinyVector<2>>& xr, + const NodeListT& node_list, + const CellType& cell_type, + const double Vi, + const size_t degree, + const size_t basis_dimension, + SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + switch (cell_type) { + case CellType::Triangle: { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + + const TriangleTransformation<2> T{x0, x1, x2}; + const auto& quadrature = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + break; + } + case CellType::Quadrangle: { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + + const SquareTransformation<2> T{x0, x1, x2, x3}; + const auto& quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } +} + template <typename NodeListT> void _computeEjkMean(const TinyVector<3>& Xj, @@ -467,6 +599,81 @@ _computeEjkMean(const TinyVector<3>& Xj, } } +template <typename NodeListT> +void +_computeEjkMeanInSymmetricCell(const TinyVector<3>& origin, + const TinyVector<3>& normal, + const TinyVector<3>& Xj, + const NodeValue<const TinyVector<3>>& xr, + const NodeListT& node_list, + const CellType& cell_type, + const double Vi, + const size_t degree, + const size_t basis_dimension, + SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + if (cell_type == CellType::Tetrahedron) { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); + const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); + + const TetrahedronTransformation T{x0, x1, x2, x3}; + + const auto& quadrature = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else if (cell_type == CellType::Prism) { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); + const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); + const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); + const auto x5 = symmetrize_coordinates(origin, normal, xr[node_list[5]]); + + const PrismTransformation T{x0, x1, x2, // + x3, x4, x5}; + + const auto& quadrature = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else if (cell_type == CellType::Pyramid) { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); + const PyramidTransformation T{x0, x1, x2, x3, x4}; + + const auto& quadrature = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else if (cell_type == CellType::Hexahedron) { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[7]]); + const auto x5 = symmetrize_coordinates(origin, normal, xr[node_list[6]]); + const auto x6 = symmetrize_coordinates(origin, normal, xr[node_list[5]]); + const auto x7 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); + + const CubeTransformation T{x0, x1, x2, x3, x4, x5, x6, x7}; + + const auto& quadrature = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } +} + size_t PolynomialReconstruction::_getNumberOfColumns( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const @@ -550,7 +757,8 @@ PolynomialReconstruction::_build( const size_t basis_dimension = DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(m_descriptor.degree()); - const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity(), m_descriptor.degree()); + const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity(), m_descriptor.degree(), + m_descriptor.symmetryBoundaryNames()); auto xr = mesh.xr(); auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); @@ -561,17 +769,58 @@ PolynomialReconstruction::_build( auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); auto cell_to_face_matrix = mesh.connectivity().cellToFaceMatrix(); + auto full_stencil_size = [&](const CellId cell_id) { + auto stencil_cell_list = stencil[cell_id]; + size_t stencil_size = stencil_cell_list.size(); + for (size_t i = 0; i < m_descriptor.symmetryBoundaryNames().size(); ++i) { + auto& ghost_stencil = stencil.symmetryNameStencil()[i].second; + stencil_size += ghost_stencil[cell_id].size(); + } + + return stencil_size; + }; + const size_t max_stencil_size = [&]() { size_t max_size = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto stencil_cell_list = stencil[cell_id]; - if (cell_is_owned[cell_id] and stencil_cell_list.size() > max_size) { - max_size = stencil_cell_list.size(); + auto stencil_cell_list = stencil[cell_id]; + const size_t stencil_size = full_stencil_size(cell_id); + if (cell_is_owned[cell_id] and stencil_size > max_size) { + max_size = stencil_size; } } return max_size; }(); + SmallArray<const Rd> symmetry_normal_list = [&] { + SmallArray<Rd> normal_list(m_descriptor.symmetryBoundaryNames().size()); + size_t i_symmetry_boundary = 0; + for (auto name : m_descriptor.symmetryBoundaryNames()) { + auto symmetry_boundary = getMeshFlatFaceBoundary(mesh, NamedBoundaryDescriptor(name)); + normal_list[i_symmetry_boundary++] = symmetry_boundary.outgoingNormal(); + } + return normal_list; + }(); + SmallArray<const Rd> symmetry_origin_list = [&] { + SmallArray<Rd> origin_list(m_descriptor.symmetryBoundaryNames().size()); + size_t i_symmetry_boundary = 0; + for (auto name : m_descriptor.symmetryBoundaryNames()) { + auto symmetry_boundary = getMeshFlatFaceBoundary(mesh, NamedBoundaryDescriptor(name)); + origin_list[i_symmetry_boundary++] = symmetry_boundary.origin(); + } + return origin_list; + }(); + { + size_t i_symmetry_boundary = 0; + for (auto name : m_descriptor.symmetryBoundaryNames()) { + auto symmetry_boundary = getMeshFlatFaceBoundary(mesh, NamedBoundaryDescriptor(name)); + std::cout << name << " n=" << symmetry_boundary.outgoingNormal() << " o=" << symmetry_boundary.origin() << '\n'; + ++i_symmetry_boundary; + } + } + std::cout << "symmetry_origin_list = " << symmetry_origin_list << '\n'; + std::cout << "symmetry_normal_list = " << symmetry_normal_list << '\n'; + Kokkos::Experimental::UniqueToken<Kokkos::DefaultExecutionSpace::execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens; @@ -611,7 +860,7 @@ PolynomialReconstruction::_build( auto stencil_cell_list = stencil[cell_j_id]; - ShrinkMatrixView B(B_pool[t], stencil_cell_list.size()); + ShrinkMatrixView B(B_pool[t], full_stencil_size(cell_j_id)); size_t column_begin = 0; for (size_t i_discrete_function_variant = 0; @@ -624,19 +873,58 @@ PolynomialReconstruction::_build( if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; const DataType& qj = discrete_function[cell_j_id]; - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { const CellId cell_i_id = stencil_cell_list[i]; const DataType& qi_qj = discrete_function[cell_i_id] - qj; if constexpr (std::is_arithmetic_v<DataType>) { - B(i, column_begin) = qi_qj; + B(index, column_begin) = qi_qj; } else if constexpr (is_tiny_vector_v<DataType>) { for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { - B(i, kB) = qi_qj[k]; + B(index, kB) = qi_qj[k]; } } else if constexpr (is_tiny_matrix_v<DataType>) { for (size_t k = 0; k < DataType::NumberOfRows; ++k) { for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - B(i, column_begin + k * DataType::NumberOfColumns + l) = qi_qj(k, l); + B(index, column_begin + k * DataType::NumberOfColumns + l) = qi_qj(k, l); + } + } + } + } + + for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryNameStencil().size(); ++i_symmetry) { + auto& ghost_stencil = stencil.symmetryNameStencil()[i_symmetry].second; + auto ghost_cell_list = ghost_stencil[cell_j_id]; + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + if constexpr (std::is_arithmetic_v<DataType>) { + const DataType& qi_qj = discrete_function[cell_i_id] - qj; + B(index, column_begin) = qi_qj; + } else if constexpr (is_tiny_vector_v<DataType>) { + if constexpr (DataType::Dimension == MeshType::Dimension) { + const Rd& normal = symmetry_normal_list[i_symmetry]; + + const DataType& qi = discrete_function[cell_i_id]; + const DataType& qi_qj = symmetrize_vector(normal, qi) - qj; + for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { + B(index, kB) = qi_qj[k]; + } + } else { + const DataType& qi_qj = discrete_function[cell_i_id] - qj; + for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { + B(index, kB) = qi_qj[k]; + } + } + } else if constexpr (is_tiny_matrix_v<DataType>) { + if constexpr ((DataType::NumberOfColumns == DataType::NumberOfRows) and + (DataType::NumberOfColumns == MeshType::Dimension)) { + throw NotImplementedError("TinyMatrix symmetries for reconstruction"); + } + const DataType& qi_qj = discrete_function[cell_i_id] - qj; + for (size_t k = 0; k < DataType::NumberOfRows; ++k) { + for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { + B(index, column_begin + k * DataType::NumberOfColumns + l) = qi_qj(k, l); + } } } } @@ -650,12 +938,26 @@ PolynomialReconstruction::_build( } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; const auto qj_vector = discrete_function[cell_j_id]; - for (size_t l = 0; l < qj_vector.size(); ++l) { - const DataType& qj = qj_vector[l]; - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { - const CellId cell_i_id = stencil_cell_list[i]; - const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; - B(i, column_begin + l) = qi_qj; + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; + B(index, column_begin + l) = qi_qj; + } + } + + for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryNameStencil().size(); ++i_symmetry) { + auto& ghost_stencil = stencil.symmetryNameStencil()[i_symmetry].second; + auto ghost_cell_list = ghost_stencil[cell_j_id]; + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; + B(index, column_begin + l) = qi_qj; + } } } column_begin += qj_vector.size(); @@ -668,16 +970,32 @@ PolynomialReconstruction::_build( discrete_function_variant->discreteFunction()); } - ShrinkMatrixView A(A_pool[t], stencil_cell_list.size()); + ShrinkMatrixView A(A_pool[t], full_stencil_size(cell_j_id)); if ((m_descriptor.degree() == 1) and (m_descriptor.integrationMethodType() == IntegrationMethodType::cell_center)) { const Rd& Xj = xj[cell_j_id]; - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { const CellId cell_i_id = stencil_cell_list[i]; const Rd Xi_Xj = xj[cell_i_id] - Xj; for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(i, l) = Xi_Xj[l]; + A(index, l) = Xi_Xj[l]; + } + } + for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryNameStencil().size(); ++i_symmetry) { + auto& ghost_stencil = stencil.symmetryNameStencil()[i_symmetry].second; + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = symmetry_origin_list[i_symmetry]; + const Rd& normal = symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + const Rd Xi_Xj = symmetrize_coordinates(origin, normal, xj[cell_i_id]) - Xj; + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(index, l) = Xi_Xj[l]; + } } } @@ -698,7 +1016,8 @@ PolynomialReconstruction::_build( cell_face_is_reversed, Vj, m_descriptor.degree(), basis_dimension, inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_j_of_ejk); - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { const CellId cell_i_id = stencil_cell_list[i]; _computeEjkMeanByBoundary(Xj, cell_i_id, xr, cell_to_face_matrix, face_to_node_matrix, @@ -706,7 +1025,28 @@ PolynomialReconstruction::_build( inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_i_of_ejk); for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; + A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; + } + } + for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryNameStencil().size(); ++i_symmetry) { + auto& ghost_stencil = stencil.symmetryNameStencil()[i_symmetry].second; + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = symmetry_origin_list[i_symmetry]; + const Rd& normal = symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + + _computeEjkMeanByBoundaryInSymmetricCell(origin, normal, // + Xj, cell_i_id, xr, cell_to_face_matrix, face_to_node_matrix, + cell_face_is_reversed, Vj, m_descriptor.degree(), + basis_dimension, inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + mean_i_of_ejk); + + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; + } } } } else { @@ -722,14 +1062,35 @@ PolynomialReconstruction::_build( _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_j_id], cell_type[cell_j_id], Vj[cell_j_id], m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_j_of_ejk); - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { const CellId cell_i_id = stencil_cell_list[i]; _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], Vj[cell_i_id], m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_i_of_ejk); for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; + A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; + } + } + for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryNameStencil().size(); ++i_symmetry) { + auto& ghost_stencil = stencil.symmetryNameStencil()[i_symmetry].second; + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = symmetry_origin_list[i_symmetry]; + const Rd& normal = symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + + _computeEjkMeanInSymmetricCell(origin, normal, // + Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], + Vj[cell_i_id], m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, + mean_i_of_ejk); + + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; + } } } } diff --git a/src/scheme/PolynomialReconstructionDescriptor.hpp b/src/scheme/PolynomialReconstructionDescriptor.hpp index 1160724c4..9274a7282 100644 --- a/src/scheme/PolynomialReconstructionDescriptor.hpp +++ b/src/scheme/PolynomialReconstructionDescriptor.hpp @@ -5,6 +5,7 @@ #include <utils/PugsMacros.hpp> #include <cstddef> +#include <set> class PolynomialReconstructionDescriptor { @@ -12,6 +13,9 @@ class PolynomialReconstructionDescriptor private: IntegrationMethodType m_integration_method; size_t m_degree; + + std::set<std::string> m_symmetry_boundary_names; + bool m_preconditioning = true; bool m_row_weighting = true; @@ -29,6 +33,13 @@ class PolynomialReconstructionDescriptor return m_degree; } + PUGS_INLINE + const std::set<std::string>& + symmetryBoundaryNames() const + { + return m_symmetry_boundary_names; + } + PUGS_INLINE bool preconditioning() const @@ -64,8 +75,11 @@ class PolynomialReconstructionDescriptor m_row_weighting = row_weighting; } - PolynomialReconstructionDescriptor(const IntegrationMethodType integration_method, const size_t degree) - : m_integration_method{integration_method}, m_degree{degree} +#warning TEMPORARY + PolynomialReconstructionDescriptor(const IntegrationMethodType integration_method, + const size_t degree, + const std::set<std::string>& symmetry_boundary_names = {}) + : m_integration_method{integration_method}, m_degree{degree}, m_symmetry_boundary_names(symmetry_boundary_names) {} PolynomialReconstructionDescriptor() = delete; diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index 01007d156..a17965ac5 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -8,16 +8,11 @@ void test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, uint64_t degree) { - std::vector descriptor_list = {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, degree}, - PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree}, - PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree}}; - - [[maybe_unused]] auto x = - PolynomialReconstruction{PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, degree}}.build( - discrete_function_variant_list); + std::vector descriptor_list = { + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, {"XMIN", "XMAX", "YMIN", "YMAX"}}}; for (auto&& descriptor : descriptor_list) { - const size_t nb_loops = 50; + const size_t nb_loops = 1; std::cout << "** variable list at once (" << nb_loops << " times) using " << rang::fgB::yellow << name(descriptor.integrationMethodType()) << rang::fg::reset << "\n"; diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder.cpp index 0cb54f262..54dd97556 100644 --- a/tests/test_StencilBuilder.cpp +++ b/tests/test_StencilBuilder.cpp @@ -15,101 +15,129 @@ TEST_CASE("StencilBuilder", "[mesh]") { - auto is_valid = [](const auto& connectivity, const Stencil& stencil) { - auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); - auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); - - auto cell_is_owned = connectivity.cellIsOwned(); - auto cell_number = connectivity.cellNumber(); - - for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { - if (cell_is_owned[cell_id]) { - std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( - [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); - auto cell_nodes = cell_to_node_matrix[cell_id]; - for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { - const NodeId node_id = cell_nodes[i_node]; - auto node_cells = node_to_cell_matrix[node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cells.size(); ++i_node_cell) { - const CellId node_cell_id = node_cells[i_node_cell]; - if (node_cell_id != cell_id) { - cell_set.insert(node_cell_id); + SECTION("inner stencil") + { + auto is_valid = [](const auto& connectivity, const Stencil& stencil) { + auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); + auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + + auto cell_is_owned = connectivity.cellIsOwned(); + auto cell_number = connectivity.cellNumber(); + + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + if (cell_is_owned[cell_id]) { + std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + auto cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + const NodeId node_id = cell_nodes[i_node]; + auto node_cells = node_to_cell_matrix[node_id]; + for (size_t i_node_cell = 0; i_node_cell < node_cells.size(); ++i_node_cell) { + const CellId node_cell_id = node_cells[i_node_cell]; + if (node_cell_id != cell_id) { + cell_set.insert(node_cell_id); + } } } - } - auto cell_stencil = stencil[cell_id]; + auto cell_stencil = stencil[cell_id]; - auto i_set_cell = cell_set.begin(); - for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { - if (*i_set_cell != cell_stencil[index]) { - return false; + auto i_set_cell = cell_set.begin(); + for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { + if (*i_set_cell != cell_stencil[index]) { + return false; + } } } } - } - return true; - }; + return true; + }; - SECTION("1D") - { - SECTION("cartesian") + SECTION("1D") { - const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); - const Connectivity<1>& connectivity = mesh.connectivity(); + const Connectivity<1>& connectivity = mesh.connectivity(); - Stencil stencil = StencilManager::instance().getStencil(connectivity); + Stencil stencil = StencilManager::instance().getStencil(connectivity); - REQUIRE(is_valid(connectivity, stencil)); - } + REQUIRE(is_valid(connectivity, stencil)); + } - SECTION("unordered") - { - const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + SECTION("unordered") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - const Connectivity<1>& connectivity = mesh.connectivity(); - Stencil stencil = StencilManager::instance().getStencil(connectivity); + const Connectivity<1>& connectivity = mesh.connectivity(); + Stencil stencil = StencilManager::instance().getStencil(connectivity); - REQUIRE(is_valid(connectivity, stencil)); + REQUIRE(is_valid(connectivity, stencil)); + } } - } - SECTION("2D") - { - SECTION("cartesian") + SECTION("2D") { - const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); - const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); + const Connectivity<2>& connectivity = mesh.connectivity(); + REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); + } } - SECTION("hybrid") + SECTION("3D") { - const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + SECTION("carteian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); - const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); + const Connectivity<3>& connectivity = mesh.connectivity(); + REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); + } } } - SECTION("3D") + SECTION("Stencil using symmetries") { - SECTION("carteian") + SECTION("2D") { - const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); - const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); + const Connectivity<2>& connectivity = mesh.connectivity(); + StencilManager::instance().getStencil(connectivity, 1, {"XMIN", "XMAX", "YMIN", "YMAX"}); + } } - SECTION("hybrid") + SECTION("3D") { - const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); - const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); + const Connectivity<3>& connectivity = mesh.connectivity(); + StencilManager::instance().getStencil(connectivity, 1, {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}); + } } } } -- GitLab From 827233cf2d4123de07bf5151af314a3b07ade9d2 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Mon, 12 Aug 2024 18:37:07 +0200 Subject: [PATCH 058/122] Fix stencil builder with symmetry and add better tests --- src/mesh/StencilBuilder.cpp | 24 ++++--- tests/test_StencilBuilder.cpp | 123 +++++++++++++++++++++++++++++++++- 2 files changed, 135 insertions(+), 12 deletions(-) diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 92738a643..209007a2d 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -1,10 +1,9 @@ -#include <mesh/Connectivity.hpp> #include <mesh/StencilBuilder.hpp> +#include <mesh/Connectivity.hpp> #include <mesh/ItemArray.hpp> #include <utils/Messenger.hpp> -#warning remove after rework #include <set> template <typename ConnectivityType> @@ -221,9 +220,10 @@ StencilBuilder::_build(const ConnectivityType& connectivity, } ++i_boundary_name; if (not found) { -#warning IMPROVE ERROR MESSAGE - throw NormalError("cannot find boundary " + boundary_name); - }; + std::ostringstream error_msg; + error_msg << "cannot find boundary '" << rang::fgB::yellow << boundary_name << rang::fg::reset << '\''; + throw NormalError(error_msg.str()); + } } } @@ -243,6 +243,7 @@ StencilBuilder::_build(const ConnectivityType& connectivity, for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { std::set<CellId> cell_set; + std::vector<std::set<CellId>> by_boundary_symmetry_cell(symmetry_boundary_names.size()); if (cell_is_owned[cell_id]) { auto cell_node_list = cell_to_node_matrix[cell_id]; @@ -256,7 +257,6 @@ StencilBuilder::_build(const ConnectivityType& connectivity, } } } - row_map[cell_id + 1] = row_map[cell_id] + cell_set.size(); { std::vector<CellId> cell_vector; @@ -273,7 +273,6 @@ StencilBuilder::_build(const ConnectivityType& connectivity, } } - std::vector<std::set<CellId>> by_boundary_symmetry_cell; for (size_t i = 0; i < symmetry_boundary_names.size(); ++i) { std::set<CellId> symmetry_cell_set; for (size_t i_cell_node = 0; i_cell_node < cell_node_list.size(); ++i_cell_node) { @@ -286,8 +285,7 @@ StencilBuilder::_build(const ConnectivityType& connectivity, } } } - symmetry_row_map_list[i][cell_id + 1] = symmetry_row_map_list[i][cell_id] + symmetry_cell_set.size(); - by_boundary_symmetry_cell.emplace_back(symmetry_cell_set); + by_boundary_symmetry_cell[i] = symmetry_cell_set; std::vector<CellId> cell_vector; for (auto&& set_cell_id : symmetry_cell_set) { @@ -303,6 +301,12 @@ StencilBuilder::_build(const ConnectivityType& connectivity, } } } + row_map[cell_id + 1] = row_map[cell_id] + cell_set.size(); + + for (size_t i = 0; i < symmetry_row_map_list.size(); ++i) { + symmetry_row_map_list[i][cell_id + 1] = + symmetry_row_map_list[i][cell_id] + by_boundary_symmetry_cell[i].size(); + } } ConnectivityMatrix primal_stencil{row_map, convert_to_array(column_indices_vector)}; @@ -320,7 +324,7 @@ StencilBuilder::_build(const ConnectivityType& connectivity, return {{primal_stencil}, {symmetry_name_stencil}}; } else { - throw NotImplementedError("Only implemented in 2D"); + throw NotImplementedError("Only implemented in 2D/3D"); } } } diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder.cpp index 54dd97556..32ec498e6 100644 --- a/tests/test_StencilBuilder.cpp +++ b/tests/test_StencilBuilder.cpp @@ -7,7 +7,9 @@ #include <mesh/ItemValue.hpp> #include <mesh/ItemValueUtils.hpp> #include <mesh/Mesh.hpp> +#include <mesh/MeshFlatNodeBoundary.hpp> #include <mesh/MeshVariant.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> #include <mesh/StencilManager.hpp> #include <utils/Messenger.hpp> @@ -118,6 +120,89 @@ TEST_CASE("StencilBuilder", "[mesh]") SECTION("Stencil using symmetries") { + auto check_ghost_cells_have_empty_stencils = [](const auto& stencil, const auto& connecticity) { + bool is_empty = true; + + auto cell_is_owned = connecticity.cellIsOwned(); + + for (CellId cell_id = 0; cell_id < connecticity.numberOfCells(); ++cell_id) { + if (not cell_is_owned[cell_id]) { + is_empty &= (stencil[cell_id].size() == 0); + for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil.symmetryNameStencil().size(); + ++i_symmetry_stencil) { + is_empty &= (stencil.symmetryNameStencil()[i_symmetry_stencil].second[cell_id].size() == 0); + } + } + } + + return is_empty; + }; + + auto symmetry_stencils_are_valid = [](const auto& stencil, const auto& mesh) { + bool is_valid = true; + + auto node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + auto cell_is_owned = mesh.connectivity().cellIsOwned(); + auto cell_number = mesh.connectivity().cellNumber(); + + for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil.symmetryNameStencil().size(); + ++i_symmetry_stencil) { + auto boundary_name = stencil.symmetryNameStencil()[i_symmetry_stencil].first; + auto boundary_stencil = stencil.symmetryNameStencil()[i_symmetry_stencil].second; + auto boundary_node_list = getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor(boundary_name)); + + CellValue<bool> boundary_cell{mesh.connectivity()}; + boundary_cell.fill(false); + auto node_list = boundary_node_list.nodeList(); + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + auto node_cell_list = node_to_cell_matrix[node_id]; + for (size_t i_cell = 0; i_cell < node_cell_list.size(); ++i_cell) { + const CellId cell_id = node_cell_list[i_cell]; + boundary_cell[cell_id] = true; + } + } + + std::set<NodeId> symmetry_node; + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + symmetry_node.insert(node_id); + } + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + if ((not boundary_cell[cell_id]) or (not cell_is_owned[cell_id])) { + is_valid &= (boundary_stencil[cell_id].size() == 0); + } else { + auto cell_node_list = cell_to_node_matrix[cell_id]; + std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( + [&](CellId cell0_id, CellId cell1_id) { return cell_number[cell0_id] < cell_number[cell1_id]; }); + for (size_t i_node = 0; i_node < cell_node_list.size(); ++i_node) { + const NodeId node_id = cell_node_list[i_node]; + if (symmetry_node.contains(node_id)) { + auto node_cell_list = node_to_cell_matrix[node_id]; + for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { + const CellId node_cell_id = node_cell_list[i_node_cell]; + cell_set.insert(node_cell_id); + } + } + } + + if (cell_set.size() == boundary_stencil[cell_id].size()) { + size_t i = 0; + for (auto&& id : cell_set) { + is_valid &= (id == boundary_stencil[cell_id][i++]); + } + } else { + is_valid = false; + } + } + } + } + + return is_valid; + }; + SECTION("2D") { SECTION("cartesian") @@ -125,7 +210,23 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - StencilManager::instance().getStencil(connectivity, 1, {"XMIN", "XMAX", "YMIN", "YMAX"}); + auto stencil = StencilManager::instance().getStencil(connectivity, 1, {"XMIN", "XMAX", "YMIN", "YMAX"}); + + REQUIRE(stencil.symmetryNameStencil().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); + REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + auto stencil = StencilManager::instance().getStencil(connectivity, 1, {"XMIN", "XMAX", "YMIN", "YMAX"}); + + REQUIRE(stencil.symmetryNameStencil().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); + REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); } } @@ -136,7 +237,25 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - StencilManager::instance().getStencil(connectivity, 1, {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}); + auto stencil = + StencilManager::instance().getStencil(connectivity, 1, {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}); + + REQUIRE(stencil.symmetryNameStencil().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); + REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + auto stencil = + StencilManager::instance().getStencil(connectivity, 1, {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}); + + REQUIRE(stencil.symmetryNameStencil().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); + REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); } } } -- GitLab From 5afc8517baefeabb444a869e6d7007bb39a8f486 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Sun, 18 Aug 2024 09:32:28 +0200 Subject: [PATCH 059/122] Use symmetry BC to build symmetric stencils Also improve slightly the stencil builder interface --- src/mesh/Stencil.hpp | 22 +++++--- src/mesh/StencilBuilder.cpp | 50 ++++++++++-------- src/mesh/StencilBuilder.hpp | 10 ++-- src/mesh/StencilManager.cpp | 10 ++-- src/mesh/StencilManager.hpp | 30 +++++++---- src/scheme/PolynomialReconstruction.cpp | 52 ++++++++----------- .../PolynomialReconstructionDescriptor.hpp | 21 +++++--- src/scheme/test_reconstruction.cpp | 8 ++- tests/test_StencilBuilder.cpp | 47 +++++++++++------ 9 files changed, 146 insertions(+), 104 deletions(-) diff --git a/src/mesh/Stencil.hpp b/src/mesh/Stencil.hpp index 5dc20dc32..900c1b4d2 100644 --- a/src/mesh/Stencil.hpp +++ b/src/mesh/Stencil.hpp @@ -2,21 +2,26 @@ #define STENCIL_HPP #include <mesh/ConnectivityMatrix.hpp> +#include <mesh/IBoundaryDescriptor.hpp> #include <mesh/ItemId.hpp> class Stencil { + public: + using BoundaryDescriptorStencilList = + std::vector<std::pair<std::shared_ptr<const IBoundaryDescriptor>, ConnectivityMatrix>>; + private: ConnectivityMatrix m_stencil; -#warning TEMPORARY IMPLEMENTATION - std::vector<std::pair<std::string, ConnectivityMatrix>> m_symmetry_name_stencil; + BoundaryDescriptorStencilList m_symmetry_boundary_stencil_list; public: +#warning REWORK INTERFACE PUGS_INLINE const auto& - symmetryNameStencil() const + symmetryBoundaryStencilList() const { - return m_symmetry_name_stencil; + return m_symmetry_boundary_stencil_list; } PUGS_INLINE @@ -26,13 +31,14 @@ class Stencil return m_stencil[cell_id]; } - Stencil(const ConnectivityMatrix& stencil, - const std::vector<std::pair<std::string, ConnectivityMatrix>>& symmetry_name_stencil) - : m_stencil{stencil}, m_symmetry_name_stencil{symmetry_name_stencil} + Stencil(const ConnectivityMatrix& stencil, const BoundaryDescriptorStencilList& symmetry_name_stencil) + : m_stencil{stencil}, m_symmetry_boundary_stencil_list{symmetry_name_stencil} {} + Stencil(const Stencil&) = default; Stencil(Stencil&&) = default; - ~Stencil() = default; + + ~Stencil() = default; }; #endif // STENCIL_HPP diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 209007a2d..ddff37e0c 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -111,10 +111,10 @@ template <typename ConnectivityType> Stencil StencilBuilder::_build(const ConnectivityType& connectivity, size_t number_of_layers, - const std::set<std::string>& symmetry_boundary_names) const + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { #warning TEMPORARY - if (symmetry_boundary_names.size() == 0) { + if (symmetry_boundary_descriptor_list.size() == 0) { if (number_of_layers == 1) { Array<const uint32_t> row_map = this->_getRowMap(connectivity); Array<const uint32_t> column_indices = this->_getColumnIndices(connectivity, row_map); @@ -189,7 +189,7 @@ StencilBuilder::_build(const ConnectivityType& connectivity, if constexpr (ConnectivityType::Dimension > 1) { std::vector<Array<const FaceId>> boundary_node_list; - NodeArray<bool> symmetry_node_list(connectivity, symmetry_boundary_names.size()); + NodeArray<bool> symmetry_node_list(connectivity, symmetry_boundary_descriptor_list.size()); symmetry_node_list.fill(0); auto face_to_node_matrix = connectivity.faceToNodeMatrix(); @@ -197,13 +197,15 @@ StencilBuilder::_build(const ConnectivityType& connectivity, auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); { - size_t i_boundary_name = 0; - for (auto boundary_name : symmetry_boundary_names) { + size_t i_symmetry_boundary = 0; + for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { + const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; + bool found = false; for (size_t i_ref_node_list = 0; i_ref_node_list < connectivity.template numberOfRefItemList<ItemType::face>(); ++i_ref_node_list) { const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i_ref_node_list); - if (ref_face_list.refId().tagName() == boundary_name) { + if (ref_face_list.refId() == boundary_descriptor) { found = true; boundary_node_list.push_back(ref_face_list.list()); for (size_t i_face = 0; i_face < ref_face_list.list().size(); ++i_face) { @@ -212,16 +214,17 @@ StencilBuilder::_build(const ConnectivityType& connectivity, for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { const NodeId node_id = node_list[i_node]; - symmetry_node_list[node_id][i_boundary_name] = true; + symmetry_node_list[node_id][i_symmetry_boundary] = true; } } break; } } - ++i_boundary_name; + ++i_symmetry_boundary; if (not found) { std::ostringstream error_msg; - error_msg << "cannot find boundary '" << rang::fgB::yellow << boundary_name << rang::fg::reset << '\''; + error_msg << "cannot find boundary '" << rang::fgB::yellow << boundary_descriptor << rang::fg::reset + << '\''; throw NormalError(error_msg.str()); } } @@ -232,18 +235,18 @@ StencilBuilder::_build(const ConnectivityType& connectivity, Array<uint32_t> row_map{connectivity.numberOfCells() + 1}; row_map[0] = 0; - std::vector<Array<uint32_t>> symmetry_row_map_list(symmetry_boundary_names.size()); + std::vector<Array<uint32_t>> symmetry_row_map_list(symmetry_boundary_descriptor_list.size()); for (auto&& symmetry_row_map : symmetry_row_map_list) { symmetry_row_map = Array<uint32_t>{connectivity.numberOfCells() + 1}; symmetry_row_map[0] = 0; } std::vector<uint32_t> column_indices_vector; - std::vector<std::vector<uint32_t>> symmetry_column_indices_vector(symmetry_boundary_names.size()); + std::vector<std::vector<uint32_t>> symmetry_column_indices_vector(symmetry_boundary_descriptor_list.size()); for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { std::set<CellId> cell_set; - std::vector<std::set<CellId>> by_boundary_symmetry_cell(symmetry_boundary_names.size()); + std::vector<std::set<CellId>> by_boundary_symmetry_cell(symmetry_boundary_descriptor_list.size()); if (cell_is_owned[cell_id]) { auto cell_node_list = cell_to_node_matrix[cell_id]; @@ -273,7 +276,7 @@ StencilBuilder::_build(const ConnectivityType& connectivity, } } - for (size_t i = 0; i < symmetry_boundary_names.size(); ++i) { + for (size_t i = 0; i < symmetry_boundary_descriptor_list.size(); ++i) { std::set<CellId> symmetry_cell_set; for (size_t i_cell_node = 0; i_cell_node < cell_node_list.size(); ++i_cell_node) { const NodeId cell_node_id = cell_node_list[i_cell_node]; @@ -310,18 +313,19 @@ StencilBuilder::_build(const ConnectivityType& connectivity, } ConnectivityMatrix primal_stencil{row_map, convert_to_array(column_indices_vector)}; - std::vector<std::pair<std::string, ConnectivityMatrix>> symmetry_name_stencil; + Stencil::BoundaryDescriptorStencilList symmetry_boundary_stencil_list; { size_t i = 0; - for (auto&& boundary_name : symmetry_boundary_names) { - symmetry_name_stencil.emplace_back( - std::make_pair(boundary_name, ConnectivityMatrix{symmetry_row_map_list[i], - convert_to_array(symmetry_column_indices_vector[i])})); + for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { + symmetry_boundary_stencil_list.emplace_back( + std::make_pair(p_boundary_descriptor, + ConnectivityMatrix{symmetry_row_map_list[i], + convert_to_array(symmetry_column_indices_vector[i])})); ++i; } } - return {{primal_stencil}, {symmetry_name_stencil}}; + return {{primal_stencil}, {symmetry_boundary_stencil_list}}; } else { throw NotImplementedError("Only implemented in 2D/3D"); @@ -332,20 +336,20 @@ StencilBuilder::_build(const ConnectivityType& connectivity, Stencil StencilBuilder::build(const IConnectivity& connectivity, size_t number_of_layers, - const std::set<std::string>& symmetry_boundary_names) const + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { switch (connectivity.dimension()) { case 1: { return StencilBuilder::_build(dynamic_cast<const Connectivity<1>&>(connectivity), number_of_layers, - symmetry_boundary_names); + symmetry_boundary_descriptor_list); } case 2: { return StencilBuilder::_build(dynamic_cast<const Connectivity<2>&>(connectivity), number_of_layers, - symmetry_boundary_names); + symmetry_boundary_descriptor_list); } case 3: { return StencilBuilder::_build(dynamic_cast<const Connectivity<3>&>(connectivity), number_of_layers, - symmetry_boundary_names); + symmetry_boundary_descriptor_list); } default: { throw UnexpectedError("invalid connectivity dimension"); diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index c52933da2..7f11c0686 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -1,14 +1,18 @@ #ifndef STENCIL_BUILDER_HPP #define STENCIL_BUILDER_HPP +#include <mesh/IBoundaryDescriptor.hpp> #include <mesh/Stencil.hpp> -#include <set> #include <string> +#include <vector> class IConnectivity; class StencilBuilder { + public: + using BoundaryDescriptorList = std::vector<std::shared_ptr<const IBoundaryDescriptor>>; + private: template <typename ConnectivityType> Array<const uint32_t> _getRowMap(const ConnectivityType& connectivity) const; @@ -20,12 +24,12 @@ class StencilBuilder template <typename ConnectivityType> Stencil _build(const ConnectivityType& connectivity, size_t number_of_layers, - const std::set<std::string>& symmetry_boundary_names) const; + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; friend class StencilManager; Stencil build(const IConnectivity& connectivity, size_t number_of_layers, - const std::set<std::string>& symmetry_boundary_names) const; + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; public: StencilBuilder() = default; diff --git a/src/mesh/StencilManager.cpp b/src/mesh/StencilManager.cpp index 618431ee7..cc99577f2 100644 --- a/src/mesh/StencilManager.cpp +++ b/src/mesh/StencilManager.cpp @@ -34,14 +34,14 @@ StencilManager::destroy() const Stencil& StencilManager::getStencil(const IConnectivity& connectivity, size_t degree, - const std::set<std::string>& symmetry_boundary_names) + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) { - if (not m_stored_stencil_map.contains(Key{connectivity.id(), degree, symmetry_boundary_names})) { - m_stored_stencil_map[Key{connectivity.id(), degree, symmetry_boundary_names}] = - std::make_shared<Stencil>(StencilBuilder{}.build(connectivity, degree, symmetry_boundary_names)); + if (not m_stored_stencil_map.contains(Key{connectivity.id(), degree, symmetry_boundary_descriptor_list})) { + m_stored_stencil_map[Key{connectivity.id(), degree, symmetry_boundary_descriptor_list}] = + std::make_shared<Stencil>(StencilBuilder{}.build(connectivity, degree, symmetry_boundary_descriptor_list)); } - return *m_stored_stencil_map.at(Key{connectivity.id(), degree, symmetry_boundary_names}); + return *m_stored_stencil_map.at(Key{connectivity.id(), degree, symmetry_boundary_descriptor_list}); } void diff --git a/src/mesh/StencilManager.hpp b/src/mesh/StencilManager.hpp index 8199ce5a3..814d8e476 100644 --- a/src/mesh/StencilManager.hpp +++ b/src/mesh/StencilManager.hpp @@ -1,15 +1,20 @@ #ifndef STENCIL_MANAGER_HPP #define STENCIL_MANAGER_HPP +#include <mesh/IBoundaryDescriptor.hpp> #include <mesh/IConnectivity.hpp> #include <mesh/Stencil.hpp> #include <memory> #include <set> #include <unordered_map> +#include <vector> class StencilManager { + public: + using BoundaryDescriptorList = std::vector<std::shared_ptr<const IBoundaryDescriptor>>; + private: StencilManager() = default; ~StencilManager() = default; @@ -20,26 +25,29 @@ class StencilManager { size_t connectivity_id; size_t degree; - std::set<std::string> symmetry_boundary_names; + BoundaryDescriptorList symmetry_boundary_descriptor_list; PUGS_INLINE bool operator==(const Key& k) const { if ((connectivity_id != k.connectivity_id) or (degree != k.degree) or - (symmetry_boundary_names.size() != k.symmetry_boundary_names.size())) { + (symmetry_boundary_descriptor_list.size() != k.symmetry_boundary_descriptor_list.size())) { return false; } - auto i_name = symmetry_boundary_names.begin(); - auto i_k_name = k.symmetry_boundary_names.begin(); + std::set<std::string> boundary_descriptor_set; + for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { + const std::string name = stringify(*p_boundary_descriptor); + boundary_descriptor_set.insert(name); + } - for (; i_name != symmetry_boundary_names.end(); ++i_name, ++i_k_name) { - if (*i_name != *i_k_name) { - return false; - } + std::set<std::string> k_boundary_descriptor_set; + for (auto&& p_boundary_descriptor : k.symmetry_boundary_descriptor_list) { + const std::string name = stringify(*p_boundary_descriptor); + k_boundary_descriptor_set.insert(name); } - return true; + return boundary_descriptor_set == k_boundary_descriptor_set; } }; struct HashKey @@ -70,8 +78,8 @@ class StencilManager void deleteConnectivity(const size_t connectivity_id); const Stencil& getStencil(const IConnectivity& i_connectivity, - size_t degree = 1, - const std::set<std::string>& symmetry_boundary_names = {}); + size_t degree = 1, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list = {}); StencilManager(const StencilManager&) = delete; StencilManager(StencilManager&&) = delete; diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 086583c9f..65720b744 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -758,7 +758,7 @@ PolynomialReconstruction::_build( DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(m_descriptor.degree()); const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity(), m_descriptor.degree(), - m_descriptor.symmetryBoundaryNames()); + m_descriptor.symmetryBoundaryDescriptorList()); auto xr = mesh.xr(); auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); @@ -772,8 +772,8 @@ PolynomialReconstruction::_build( auto full_stencil_size = [&](const CellId cell_id) { auto stencil_cell_list = stencil[cell_id]; size_t stencil_size = stencil_cell_list.size(); - for (size_t i = 0; i < m_descriptor.symmetryBoundaryNames().size(); ++i) { - auto& ghost_stencil = stencil.symmetryNameStencil()[i].second; + for (size_t i = 0; i < m_descriptor.symmetryBoundaryDescriptorList().size(); ++i) { + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i].second; stencil_size += ghost_stencil[cell_id].size(); } @@ -793,33 +793,27 @@ PolynomialReconstruction::_build( }(); SmallArray<const Rd> symmetry_normal_list = [&] { - SmallArray<Rd> normal_list(m_descriptor.symmetryBoundaryNames().size()); + SmallArray<Rd> normal_list(m_descriptor.symmetryBoundaryDescriptorList().size()); size_t i_symmetry_boundary = 0; - for (auto name : m_descriptor.symmetryBoundaryNames()) { - auto symmetry_boundary = getMeshFlatFaceBoundary(mesh, NamedBoundaryDescriptor(name)); + for (auto p_boundary_descriptor : m_descriptor.symmetryBoundaryDescriptorList()) { + const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; + + auto symmetry_boundary = getMeshFlatFaceBoundary(mesh, boundary_descriptor); normal_list[i_symmetry_boundary++] = symmetry_boundary.outgoingNormal(); } return normal_list; }(); SmallArray<const Rd> symmetry_origin_list = [&] { - SmallArray<Rd> origin_list(m_descriptor.symmetryBoundaryNames().size()); + SmallArray<Rd> origin_list(m_descriptor.symmetryBoundaryDescriptorList().size()); size_t i_symmetry_boundary = 0; - for (auto name : m_descriptor.symmetryBoundaryNames()) { - auto symmetry_boundary = getMeshFlatFaceBoundary(mesh, NamedBoundaryDescriptor(name)); + for (auto p_boundary_descriptor : m_descriptor.symmetryBoundaryDescriptorList()) { + const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; + + auto symmetry_boundary = getMeshFlatFaceBoundary(mesh, boundary_descriptor); origin_list[i_symmetry_boundary++] = symmetry_boundary.origin(); } return origin_list; }(); - { - size_t i_symmetry_boundary = 0; - for (auto name : m_descriptor.symmetryBoundaryNames()) { - auto symmetry_boundary = getMeshFlatFaceBoundary(mesh, NamedBoundaryDescriptor(name)); - std::cout << name << " n=" << symmetry_boundary.outgoingNormal() << " o=" << symmetry_boundary.origin() << '\n'; - ++i_symmetry_boundary; - } - } - std::cout << "symmetry_origin_list = " << symmetry_origin_list << '\n'; - std::cout << "symmetry_normal_list = " << symmetry_normal_list << '\n'; Kokkos::Experimental::UniqueToken<Kokkos::DefaultExecutionSpace::execution_space, Kokkos::Experimental::UniqueTokenScope::Global> @@ -892,8 +886,8 @@ PolynomialReconstruction::_build( } } - for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryNameStencil().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryNameStencil()[i_symmetry].second; + for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].second; auto ghost_cell_list = ghost_stencil[cell_j_id]; for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { const CellId cell_i_id = ghost_cell_list[i]; @@ -948,8 +942,8 @@ PolynomialReconstruction::_build( } } - for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryNameStencil().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryNameStencil()[i_symmetry].second; + for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].second; auto ghost_cell_list = ghost_stencil[cell_j_id]; for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { const CellId cell_i_id = ghost_cell_list[i]; @@ -983,8 +977,8 @@ PolynomialReconstruction::_build( A(index, l) = Xi_Xj[l]; } } - for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryNameStencil().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryNameStencil()[i_symmetry].second; + for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].second; auto ghost_cell_list = ghost_stencil[cell_j_id]; const Rd& origin = symmetry_origin_list[i_symmetry]; @@ -1028,8 +1022,8 @@ PolynomialReconstruction::_build( A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; } } - for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryNameStencil().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryNameStencil()[i_symmetry].second; + for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].second; auto ghost_cell_list = ghost_stencil[cell_j_id]; const Rd& origin = symmetry_origin_list[i_symmetry]; @@ -1073,8 +1067,8 @@ PolynomialReconstruction::_build( A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; } } - for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryNameStencil().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryNameStencil()[i_symmetry].second; + for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].second; auto ghost_cell_list = ghost_stencil[cell_j_id]; const Rd& origin = symmetry_origin_list[i_symmetry]; diff --git a/src/scheme/PolynomialReconstructionDescriptor.hpp b/src/scheme/PolynomialReconstructionDescriptor.hpp index 9274a7282..8a51a8571 100644 --- a/src/scheme/PolynomialReconstructionDescriptor.hpp +++ b/src/scheme/PolynomialReconstructionDescriptor.hpp @@ -1,20 +1,24 @@ #ifndef POLYNOMIAL_RECONSTRUCTION_DESCRIPTOR_HPP #define POLYNOMIAL_RECONSTRUCTION_DESCRIPTOR_HPP +#include <mesh/IBoundaryDescriptor.hpp> #include <scheme/IntegrationMethodType.hpp> #include <utils/PugsMacros.hpp> #include <cstddef> -#include <set> +#include <memory> +#include <vector> class PolynomialReconstructionDescriptor { public: + using BoundaryDescriptorList = std::vector<std::shared_ptr<const IBoundaryDescriptor>>; + private: IntegrationMethodType m_integration_method; size_t m_degree; - std::set<std::string> m_symmetry_boundary_names; + BoundaryDescriptorList m_symmetry_boundary_descriptor_list; bool m_preconditioning = true; bool m_row_weighting = true; @@ -34,10 +38,10 @@ class PolynomialReconstructionDescriptor } PUGS_INLINE - const std::set<std::string>& - symmetryBoundaryNames() const + const BoundaryDescriptorList& + symmetryBoundaryDescriptorList() const { - return m_symmetry_boundary_names; + return m_symmetry_boundary_descriptor_list; } PUGS_INLINE @@ -75,11 +79,12 @@ class PolynomialReconstructionDescriptor m_row_weighting = row_weighting; } -#warning TEMPORARY PolynomialReconstructionDescriptor(const IntegrationMethodType integration_method, const size_t degree, - const std::set<std::string>& symmetry_boundary_names = {}) - : m_integration_method{integration_method}, m_degree{degree}, m_symmetry_boundary_names(symmetry_boundary_names) + const BoundaryDescriptorList& symmetry_boundary_descriptor_list = {}) + : m_integration_method{integration_method}, + m_degree{degree}, + m_symmetry_boundary_descriptor_list(symmetry_boundary_descriptor_list) {} PolynomialReconstructionDescriptor() = delete; diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index a17965ac5..629d1337e 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -1,5 +1,6 @@ #include <scheme/test_reconstruction.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> #include <scheme/IntegrationMethodType.hpp> #include <scheme/PolynomialReconstruction.hpp> #include <utils/Timer.hpp> @@ -9,7 +10,12 @@ test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVari uint64_t degree) { std::vector descriptor_list = { - PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, {"XMIN", "XMAX", "YMIN", "YMAX"}}}; + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, + degree, + {std::make_shared<NamedBoundaryDescriptor>("XMIN"), + std::make_shared<NamedBoundaryDescriptor>("XMAX"), + std::make_shared<NamedBoundaryDescriptor>("YMIN"), + std::make_shared<NamedBoundaryDescriptor>("YMAX")}}}; for (auto&& descriptor : descriptor_list) { const size_t nb_loops = 1; diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder.cpp index 32ec498e6..0ef8d97ba 100644 --- a/tests/test_StencilBuilder.cpp +++ b/tests/test_StencilBuilder.cpp @@ -128,9 +128,9 @@ TEST_CASE("StencilBuilder", "[mesh]") for (CellId cell_id = 0; cell_id < connecticity.numberOfCells(); ++cell_id) { if (not cell_is_owned[cell_id]) { is_empty &= (stencil[cell_id].size() == 0); - for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil.symmetryNameStencil().size(); + for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry_stencil) { - is_empty &= (stencil.symmetryNameStencil()[i_symmetry_stencil].second[cell_id].size() == 0); + is_empty &= (stencil.symmetryBoundaryStencilList()[i_symmetry_stencil].second[cell_id].size() == 0); } } } @@ -146,11 +146,13 @@ TEST_CASE("StencilBuilder", "[mesh]") auto cell_is_owned = mesh.connectivity().cellIsOwned(); auto cell_number = mesh.connectivity().cellNumber(); - for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil.symmetryNameStencil().size(); + for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry_stencil) { - auto boundary_name = stencil.symmetryNameStencil()[i_symmetry_stencil].first; - auto boundary_stencil = stencil.symmetryNameStencil()[i_symmetry_stencil].second; - auto boundary_node_list = getMeshFlatNodeBoundary(mesh, NamedBoundaryDescriptor(boundary_name)); + auto p_boundary_descriptor = stencil.symmetryBoundaryStencilList()[i_symmetry_stencil].first; + const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; + + auto boundary_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry_stencil].second; + auto boundary_node_list = getMeshFlatNodeBoundary(mesh, boundary_descriptor); CellValue<bool> boundary_cell{mesh.connectivity()}; boundary_cell.fill(false); @@ -205,14 +207,21 @@ TEST_CASE("StencilBuilder", "[mesh]") SECTION("2D") { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + SECTION("cartesian") { const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - auto stencil = StencilManager::instance().getStencil(connectivity, 1, {"XMIN", "XMAX", "YMIN", "YMAX"}); - REQUIRE(stencil.symmetryNameStencil().size() == 4); + auto stencil = StencilManager::instance().getStencil(connectivity, 1, list); + + REQUIRE(stencil.symmetryBoundaryStencilList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); } @@ -222,9 +231,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - auto stencil = StencilManager::instance().getStencil(connectivity, 1, {"XMIN", "XMAX", "YMIN", "YMAX"}); + auto stencil = StencilManager::instance().getStencil(connectivity, 1, list); - REQUIRE(stencil.symmetryNameStencil().size() == 4); + REQUIRE(stencil.symmetryBoundaryStencilList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); } @@ -232,15 +241,22 @@ TEST_CASE("StencilBuilder", "[mesh]") SECTION("3D") { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMAX")); + SECTION("cartesian") { const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - auto stencil = - StencilManager::instance().getStencil(connectivity, 1, {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}); + auto stencil = StencilManager::instance().getStencil(connectivity, 1, list); - REQUIRE(stencil.symmetryNameStencil().size() == 6); + REQUIRE(stencil.symmetryBoundaryStencilList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); } @@ -250,10 +266,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - auto stencil = - StencilManager::instance().getStencil(connectivity, 1, {"XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX"}); + auto stencil = StencilManager::instance().getStencil(connectivity, 1, list); - REQUIRE(stencil.symmetryNameStencil().size() == 6); + REQUIRE(stencil.symmetryBoundaryStencilList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); } -- GitLab From 2769d34ecbadf52f97539f1c71f07f0db30070da Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Sun, 18 Aug 2024 10:24:21 +0200 Subject: [PATCH 060/122] Fix row weighting and preconditioning when using symmetry stencil --- src/scheme/PolynomialReconstruction.cpp | 60 ++++++++++++++++++------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 65720b744..b0cacf9d9 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -352,9 +352,19 @@ _computeEjkMeanInSymmetricCell(const TinyVector<1>& origin, } template <typename ConformTransformationT> +void _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, + const ConformTransformationT& T, + const TinyVector<2>& Xj, + const double Vi, + const size_t degree, + const size_t dimension, + SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + SmallArray<double>& mean_of_ejk); + +template <> void _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, - const ConformTransformationT& T, + const LineTransformation<2>& T, const TinyVector<2>& Xj, const double Vi, const size_t degree, @@ -397,10 +407,11 @@ _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, } } +template <MeshConcept MeshType> void -_computeEjkMeanByBoundary(const TinyVector<2>& Xj, +_computeEjkMeanByBoundary(const MeshType& mesh, + const TinyVector<2>& Xj, const CellId& cell_id, - const NodeValue<const TinyVector<2>>& xr, const auto& cell_to_face_matrix, const auto& face_to_node_matrix, const auto& cell_face_is_reversed, @@ -412,6 +423,7 @@ _computeEjkMeanByBoundary(const TinyVector<2>& Xj, { const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(degree + 1)); + auto xr = mesh.xr(); mean_of_ejk.fill(0); auto cell_face_list = cell_to_face_matrix[cell_id]; for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { @@ -783,7 +795,6 @@ PolynomialReconstruction::_build( const size_t max_stencil_size = [&]() { size_t max_size = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto stencil_cell_list = stencil[cell_id]; const size_t stencil_size = full_stencil_size(cell_id); if (cell_is_owned[cell_id] and stencil_size > max_size) { max_size = stencil_size; @@ -1006,7 +1017,7 @@ PolynomialReconstruction::_build( auto cell_face_is_reversed = p_mesh->connectivity().cellFaceIsReversed(); const Rd& Xj = xj[cell_j_id]; - _computeEjkMeanByBoundary(Xj, cell_j_id, xr, cell_to_face_matrix, face_to_node_matrix, + _computeEjkMeanByBoundary(*p_mesh, Xj, cell_j_id, cell_to_face_matrix, face_to_node_matrix, cell_face_is_reversed, Vj, m_descriptor.degree(), basis_dimension, inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_j_of_ejk); @@ -1014,7 +1025,7 @@ PolynomialReconstruction::_build( for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { const CellId cell_i_id = stencil_cell_list[i]; - _computeEjkMeanByBoundary(Xj, cell_i_id, xr, cell_to_face_matrix, face_to_node_matrix, + _computeEjkMeanByBoundary(*p_mesh, Xj, cell_i_id, cell_to_face_matrix, face_to_node_matrix, cell_face_is_reversed, Vj, m_descriptor.degree(), basis_dimension, inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_i_of_ejk); @@ -1097,14 +1108,33 @@ PolynomialReconstruction::_build( // closer to j) const Rd& Xj = xj[cell_j_id]; - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { const CellId cell_i_id = stencil_cell_list[i]; const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(i, l) *= weight; + A(index, l) *= weight; } for (size_t l = 0; l < number_of_columns; ++l) { - B(i, l) *= weight; + B(index, l) *= weight; + } + } + for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].second; + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = symmetry_origin_list[i_symmetry]; + const Rd& normal = symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + const double weight = 1. / l2Norm(symmetrize_coordinates(origin, normal, xj[cell_i_id]) - Xj); + for (size_t l = 0; l < basis_dimension - 1; ++l) { + A(index, l) *= weight; + } + for (size_t l = 0; l < number_of_columns; ++l) { + B(index, l) *= weight; + } } } } @@ -1115,9 +1145,9 @@ PolynomialReconstruction::_build( // Add column weighting preconditioning (increase the presition) SmallVector<double>& G = G_pool[t]; - for (size_t l = 0; l < basis_dimension - 1; ++l) { + for (size_t l = 0; l < A.numberOfColumns(); ++l) { double g = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + for (size_t i = 0; i < A.numberOfRows(); ++i) { const double Ail = A(i, l); g += Ail * Ail; @@ -1125,18 +1155,18 @@ PolynomialReconstruction::_build( G[l] = std::sqrt(g); } - for (size_t l = 0; l < basis_dimension - 1; ++l) { + for (size_t l = 0; l < A.numberOfColumns(); ++l) { const double Gl = G[l]; - for (size_t i = 0; i < stencil_cell_list.size(); ++i) { + for (size_t i = 0; i < A.numberOfRows(); ++i) { A(i, l) *= Gl; } } Givens::solveCollectionInPlace(A, X, B); - for (size_t l = 0; l < basis_dimension - 1; ++l) { + for (size_t l = 0; l < X.numberOfRows(); ++l) { const double Gl = G[l]; - for (size_t i = 0; i < number_of_columns; ++i) { + for (size_t i = 0; i < X.numberOfColumns(); ++i) { X(l, i) *= Gl; } } -- GitLab From 2f65b85e09c97305320198e84720fafdf387f801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Wed, 9 Oct 2024 17:33:42 +0200 Subject: [PATCH 061/122] Few code clean-up --- src/mesh/ConnectivityDispatcher.cpp | 136 +++++++++++++++------------- src/utils/Partitioner.cpp | 3 +- 2 files changed, 73 insertions(+), 66 deletions(-) diff --git a/src/mesh/ConnectivityDispatcher.cpp b/src/mesh/ConnectivityDispatcher.cpp index 65326b56b..c78682184 100644 --- a/src/mesh/ConnectivityDispatcher.cpp +++ b/src/mesh/ConnectivityDispatcher.cpp @@ -28,17 +28,17 @@ ConnectivityDispatcher<Dimension>::_buildNewOwner() using ItemId = ItemIdT<item_type>; ItemValue<int, item_type> item_new_owner(m_connectivity); parallel_for( - item_new_owner.numberOfItems(), PUGS_LAMBDA(const ItemId& l) { - const auto& item_to_cell = item_to_cell_matrix[l]; - CellId Jmin = item_to_cell[0]; - - for (size_t j = 1; j < item_to_cell.size(); ++j) { - const CellId J = item_to_cell[j]; - if (cell_number[J] < cell_number[Jmin]) { - Jmin = J; + item_new_owner.numberOfItems(), PUGS_LAMBDA(const ItemId& item_id) { + const auto& item_to_cell = item_to_cell_matrix[item_id]; + CellId min_number_cell_id = item_to_cell[0]; + + for (size_t i_cell = 1; i_cell < item_to_cell.size(); ++i_cell) { + const CellId cell_id = item_to_cell[i_cell]; + if (cell_number[cell_id] < cell_number[min_number_cell_id]) { + min_number_cell_id = cell_id; } } - item_new_owner[l] = cell_new_owner[Jmin]; + item_new_owner[item_id] = cell_new_owner[min_number_cell_id]; }); synchronize(item_new_owner); @@ -72,22 +72,22 @@ ConnectivityDispatcher<Dimension>::_buildItemListToSend() std::vector<std::vector<CellId>> cell_vector_to_send_by_proc(parallel::size()); Array<bool> send_to_rank(parallel::size()); - for (CellId j = 0; j < m_connectivity.numberOfCells(); ++j) { + for (CellId cell_id = 0; cell_id < m_connectivity.numberOfCells(); ++cell_id) { send_to_rank.fill(false); - const auto& cell_to_node = cell_to_node_matrix[j]; - - for (size_t R = 0; R < cell_to_node.size(); ++R) { - const NodeId& r = cell_to_node[R]; - const auto& node_to_cell = node_to_cell_matrix[r]; - for (size_t K = 0; K < node_to_cell.size(); ++K) { - const CellId& k = node_to_cell[K]; - send_to_rank[cell_new_owner[k]] = true; + const auto& cell_to_node = cell_to_node_matrix[cell_id]; + + for (size_t i_node = 0; i_node < cell_to_node.size(); ++i_node) { + const NodeId& node_id = cell_to_node[i_node]; + const auto& node_to_cell = node_to_cell_matrix[node_id]; + for (size_t i_node_cell = 0; i_node_cell < node_to_cell.size(); ++i_node_cell) { + const CellId& node_cell_id = node_to_cell[i_node_cell]; + send_to_rank[cell_new_owner[node_cell_id]] = true; } } - for (size_t k = 0; k < send_to_rank.size(); ++k) { - if (send_to_rank[k]) { - cell_vector_to_send_by_proc[k].push_back(j); + for (size_t i_rank = 0; i_rank < send_to_rank.size(); ++i_rank) { + if (send_to_rank[i_rank]) { + cell_vector_to_send_by_proc[i_rank].push_back(cell_id); } } } @@ -110,11 +110,11 @@ ConnectivityDispatcher<Dimension>::_buildItemListToSend() Array<bool> tag(m_connectivity.template numberOf<item_type>()); tag.fill(false); std::vector<ItemId> item_id_vector; - for (size_t j = 0; j < cell_list_to_send_by_proc[i_rank].size(); ++j) { - const CellId& cell_id = cell_list_to_send_by_proc[i_rank][j]; + for (size_t i_cell = 0; i_cell < cell_list_to_send_by_proc[i_rank].size(); ++i_cell) { + const CellId& cell_id = cell_list_to_send_by_proc[i_rank][i_cell]; const auto& cell_sub_item_list = cell_to_sub_item_matrix[cell_id]; - for (size_t r = 0; r < cell_sub_item_list.size(); ++r) { - const ItemId& item_id = cell_sub_item_list[r]; + for (size_t i_item = 0; i_item < cell_sub_item_list.size(); ++i_item) { + const ItemId& item_id = cell_sub_item_list[i_item]; if (not tag[item_id]) { item_id_vector.push_back(item_id); tag[item_id] = true; @@ -155,9 +155,9 @@ ConnectivityDispatcher<Dimension>::_gatherFrom(const ItemValue<DataType, item_ty gathered_array = Array<std::remove_const_t<DataType>>(this->_dispatchedInfo<item_type>().m_number_to_id_map.size()); for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { Assert(recv_id_correspondance_by_proc[i_rank].size() == recv_item_data_by_proc[i_rank].size()); - for (size_t r = 0; r < recv_id_correspondance_by_proc[i_rank].size(); ++r) { - const auto& item_id = recv_id_correspondance_by_proc[i_rank][r]; - gathered_array[item_id] = recv_item_data_by_proc[i_rank][r]; + for (size_t i_item = 0; i_item < recv_id_correspondance_by_proc[i_rank].size(); ++i_item) { + const auto& item_id = recv_id_correspondance_by_proc[i_rank][i_item]; + gathered_array[item_id] = recv_item_data_by_proc[i_rank][i_item]; } } } @@ -180,11 +180,11 @@ ConnectivityDispatcher<Dimension>::_gatherFrom( for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { std::vector<MutableDataType> data_by_item_vector; - for (size_t j = 0; j < item_list_to_send_by_proc[i_rank].size(); ++j) { - const ItemId& item_id = item_list_to_send_by_proc[i_rank][j]; + for (size_t i_item = 0; i_item < item_list_to_send_by_proc[i_rank].size(); ++i_item) { + const ItemId& item_id = item_list_to_send_by_proc[i_rank][i_item]; const auto& item_data = data_to_gather.itemArray(item_id); - for (size_t l = 0; l < item_data.size(); ++l) { - data_by_item_vector.push_back(item_data[l]); + for (size_t i_data = 0; i_data < item_data.size(); ++i_data) { + data_by_item_vector.push_back(item_data[i_data]); } } data_to_send_by_proc[i_rank] = convert_to_array(data_by_item_vector); @@ -211,13 +211,13 @@ ConnectivityDispatcher<Dimension>::_gatherFrom( gathered_array = Array<std::remove_const_t<DataType>>(recv_array_size); { - size_t l = 0; + size_t i_value = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { - for (size_t j = 0; j < recv_data_to_gather_by_proc[i_rank].size(); ++j) { - gathered_array[l++] = recv_data_to_gather_by_proc[i_rank][j]; + for (size_t i_rank_value = 0; i_rank_value < recv_data_to_gather_by_proc[i_rank].size(); ++i_rank_value) { + gathered_array[i_value++] = recv_data_to_gather_by_proc[i_rank][i_rank_value]; } } - Assert(gathered_array.size() == l); + Assert(gathered_array.size() == i_value); } } @@ -229,8 +229,8 @@ ConnectivityDispatcher<Dimension>::_buildCellNumberIdMap() auto& cell_number_id_map = this->_dispatchedInfo<ItemType::cell>().m_number_to_id_map; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { CellId cell_id = 0; - for (size_t i = 0; i < recv_cell_number_by_proc[i_rank].size(); ++i) { - const int cell_number = recv_cell_number_by_proc[i_rank][i]; + for (size_t i_rank_cell = 0; i_rank_cell < recv_cell_number_by_proc[i_rank].size(); ++i_rank_cell) { + const int cell_number = recv_cell_number_by_proc[i_rank][i_rank_cell]; auto [iterator, inserted] = cell_number_id_map.insert(std::make_pair(cell_number, cell_id)); if (inserted) ++cell_id; @@ -252,8 +252,9 @@ ConnectivityDispatcher<Dimension>::_buildSubItemNumberToIdMap() auto& sub_item_number_id_map = this->_dispatchedInfo<ItemOfItemT::sub_item_type>().m_number_to_id_map; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { int sub_item_id = 0; - for (size_t i = 0; i < cell_sub_item_number_to_recv_by_proc[i_rank].size(); ++i) { - int sub_item_number = cell_sub_item_number_to_recv_by_proc[i_rank][i]; + for (size_t i_rank_sub_item = 0; i_rank_sub_item < cell_sub_item_number_to_recv_by_proc[i_rank].size(); + ++i_rank_sub_item) { + int sub_item_number = cell_sub_item_number_to_recv_by_proc[i_rank][i_rank_sub_item]; auto [iterator, inserted] = sub_item_number_id_map.insert(std::make_pair(sub_item_number, sub_item_id)); if (inserted) sub_item_id++; @@ -273,8 +274,9 @@ ConnectivityDispatcher<Dimension>::_buildNumberOfSubItemPerItemToRecvByProc() using ItemId = ItemIdT<SubItemOfItemT::item_type>; parallel_for( - number_of_sub_item_per_item.numberOfItems(), - PUGS_LAMBDA(const ItemId& j) { number_of_sub_item_per_item[j] = item_to_sub_item_matrix[j].size(); }); + number_of_sub_item_per_item.numberOfItems(), PUGS_LAMBDA(const ItemId& item_id) { + number_of_sub_item_per_item[item_id] = item_to_sub_item_matrix[item_id].size(); + }); this->_dispatchedInfo<SubItemOfItemT>().m_number_of_sub_item_per_item_to_recv_by_proc = this->exchange(number_of_sub_item_per_item); @@ -299,11 +301,11 @@ ConnectivityDispatcher<Dimension>::_buildSubItemNumbersToRecvByProc() const auto& item_list_to_send_by_proc = this->_dispatchedInfo<SubItemOfItemT::item_type>().m_list_to_send_by_proc; std::vector<int> sub_item_numbers_by_item_vector; - for (size_t j = 0; j < item_list_to_send_by_proc[i_rank].size(); ++j) { - const ItemId& item_id = item_list_to_send_by_proc[i_rank][j]; + for (size_t i_rank_item = 0; i_rank_item < item_list_to_send_by_proc[i_rank].size(); ++i_rank_item) { + const ItemId& item_id = item_list_to_send_by_proc[i_rank][i_rank_item]; const auto& sub_item_list = item_to_sub_item_matrix[item_id]; - for (size_t r = 0; r < sub_item_list.size(); ++r) { - const SubItemId& sub_item_id = sub_item_list[r]; + for (size_t i_sub_item = 0; i_sub_item < sub_item_list.size(); ++i_sub_item) { + const SubItemId& sub_item_id = sub_item_list[i_sub_item]; sub_item_numbers_by_item_vector.push_back(sub_item_number[sub_item_id]); } } @@ -351,11 +353,14 @@ ConnectivityDispatcher<Dimension>::_buildItemToSubItemDescriptor() std::vector<std::vector<unsigned int>> item_to_subitem_legacy; size_t number_of_node_by_cell = 0; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { - int l = 0; - for (size_t i = 0; i < item_list_to_recv_size_by_proc[i_rank]; ++i) { + int i_sub_item = 0; + for (size_t i_rank_item = 0; i_rank_item < item_list_to_recv_size_by_proc[i_rank]; ++i_rank_item) { std::vector<unsigned int> sub_item_vector; - for (int k = 0; k < number_of_sub_item_per_item_to_recv_by_proc[i_rank][i]; ++k) { - const auto& searched_sub_item_id = sub_item_number_id_map.find(recv_item_of_item_numbers_by_proc[i_rank][l++]); + for (int i_rank_sub_item_per_item = 0; + i_rank_sub_item_per_item < number_of_sub_item_per_item_to_recv_by_proc[i_rank][i_rank_item]; + ++i_rank_sub_item_per_item) { + const auto& searched_sub_item_id = + sub_item_number_id_map.find(recv_item_of_item_numbers_by_proc[i_rank][i_sub_item++]); Assert(searched_sub_item_id != sub_item_number_id_map.end()); sub_item_vector.push_back(searched_sub_item_id->second); } @@ -371,14 +376,16 @@ ConnectivityDispatcher<Dimension>::_buildItemToSubItemDescriptor() item_to_subitem_list.fill(10000000); item_to_subitem_row_map[0] = 0; - for (size_t i = 0; i < item_to_subitem_legacy.size(); ++i) { - item_to_subitem_row_map[i + 1] = item_to_subitem_row_map[i] + item_to_subitem_legacy[i].size(); + for (size_t i_rank = 0; i_rank < item_to_subitem_legacy.size(); ++i_rank) { + item_to_subitem_row_map[i_rank + 1] = item_to_subitem_row_map[i_rank] + item_to_subitem_legacy[i_rank].size(); } - size_t l = 0; - for (size_t i = 0; i < item_to_subitem_legacy.size(); ++i) { - const auto& subitem_list = item_to_subitem_legacy[i]; - for (size_t j = 0; j < subitem_list.size(); ++j, ++l) { - item_to_subitem_list[l] = subitem_list[j]; + { + size_t i_sub_item = 0; + for (size_t i_rank = 0; i_rank < item_to_subitem_legacy.size(); ++i_rank) { + const auto& subitem_list = item_to_subitem_legacy[i_rank]; + for (size_t i_rank_sub_item = 0; i_rank_sub_item < subitem_list.size(); ++i_rank_sub_item, ++i_sub_item) { + item_to_subitem_list[i_sub_item] = subitem_list[i_rank_sub_item]; + } } } @@ -401,7 +408,8 @@ ConnectivityDispatcher<Dimension>::_buildRecvItemIdCorrespondanceByProc() Array<int> send_item_number(item_list_to_send_by_proc[i_rank].size()); const Array<const ItemId> send_item_id = item_list_to_send_by_proc[i_rank]; parallel_for( - send_item_number.size(), PUGS_LAMBDA(size_t j) { send_item_number[j] = item_number[send_item_id[j]]; }); + send_item_number.size(), + PUGS_LAMBDA(size_t i_item) { send_item_number[i_item] = item_number[send_item_id[i_item]]; }); send_item_number_by_proc[i_rank] = send_item_number; } @@ -415,11 +423,11 @@ ConnectivityDispatcher<Dimension>::_buildRecvItemIdCorrespondanceByProc() const auto& item_number_to_id_map = this->_dispatchedInfo<item_type>().m_number_to_id_map; for (size_t i_rank = 0; i_rank < item_list_to_recv_size_by_proc.size(); ++i_rank) { Array<ItemId> item_id_correspondance(item_list_to_recv_size_by_proc[i_rank]); - for (size_t l = 0; l < item_list_to_recv_size_by_proc[i_rank]; ++l) { - const int& recv_item_number = recv_item_number_by_proc[i_rank][l]; + for (size_t i_rank_item = 0; i_rank_item < item_list_to_recv_size_by_proc[i_rank]; ++i_rank_item) { + const int& recv_item_number = recv_item_number_by_proc[i_rank][i_rank_item]; const auto& searched_item_id = item_number_to_id_map.find(recv_item_number); Assert(searched_item_id != item_number_to_id_map.end()); - item_id_correspondance[l] = searched_item_id->second; + item_id_correspondance[i_rank_item] = searched_item_id->second; } recv_item_id_correspondance_by_proc[i_rank] = item_id_correspondance; } @@ -572,9 +580,9 @@ ConnectivityDispatcher<Dimension>::_buildItemReferenceList() this->_dispatchedInfo<item_type>().m_recv_id_correspondance_by_proc; std::vector<block_type> item_refs(m_new_descriptor.template itemNumberVector<item_type>().size()); for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { - for (size_t r = 0; r < recv_item_refs_by_proc[i_rank].size(); ++r) { - const ItemId& item_id = recv_item_id_correspondance_by_proc[i_rank][r]; - item_refs[item_id] = recv_item_refs_by_proc[i_rank][r]; + for (size_t i_item = 0; i_item < recv_item_refs_by_proc[i_rank].size(); ++i_item) { + const ItemId& item_id = recv_item_id_correspondance_by_proc[i_rank][i_item]; + item_refs[item_id] = recv_item_refs_by_proc[i_rank][i_item]; } } diff --git a/src/utils/Partitioner.cpp b/src/utils/Partitioner.cpp index 85e13c22d..d86f798a3 100644 --- a/src/utils/Partitioner.cpp +++ b/src/utils/Partitioner.cpp @@ -72,10 +72,9 @@ Partitioner::partition(const CRSGraph& graph) throw UnexpectedError("Metis Error"); } // LCOV_EXCL_STOP - - MPI_Comm_free(&parmetis_comm); } + MPI_Comm_free(&parmetis_comm); MPI_Group_free(&mesh_group); return part; -- GitLab From 9e507a5091bb75690aa21edf1e876a92443aa0e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 10 Oct 2024 12:15:44 +0200 Subject: [PATCH 062/122] Add the option '--number-of-ghost-layers' This allows to define the number of cell layers for parallel communications (default is 1) --- src/main.cpp | 8 ++-- src/mesh/ConnectivityDispatcher.cpp | 56 ++++++++++++++++++++++++--- src/scheme/AcousticSolver.cpp | 5 +++ src/scheme/FluxingAdvectionSolver.cpp | 5 +++ src/utils/GlobalVariableManager.hpp | 23 +++++++++++ src/utils/PugsUtils.cpp | 5 +++ tests/mpi_test_main.cpp | 1 + tests/test_main.cpp | 1 + 8 files changed, 94 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a9bec44f0..917addbe5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,8 @@ main(int argc, char* argv[]) ExecutionStatManager::create(); ParallelChecker::create(); + GlobalVariableManager::create(); + std::string filename = initialize(argc, argv); SynchronizerManager::create(); @@ -27,13 +29,9 @@ main(int argc, char* argv[]) DualMeshManager::create(); StencilManager::create(); - GlobalVariableManager::create(); - parser(filename); ExecutionStatManager::printInfo(); - GlobalVariableManager::destroy(); - StencilManager::destroy(); DualMeshManager::destroy(); DualConnectivityManager::destroy(); @@ -44,6 +42,8 @@ main(int argc, char* argv[]) finalize(); + GlobalVariableManager::destroy(); + ParallelChecker::destroy(); int return_code = ExecutionStatManager::getInstance().exitCode(); diff --git a/src/mesh/ConnectivityDispatcher.cpp b/src/mesh/ConnectivityDispatcher.cpp index c78682184..e9491ad34 100644 --- a/src/mesh/ConnectivityDispatcher.cpp +++ b/src/mesh/ConnectivityDispatcher.cpp @@ -2,6 +2,7 @@ #include <mesh/ItemOfItemType.hpp> #include <utils/CRSGraph.hpp> +#include <utils/GlobalVariableManager.hpp> #include <utils/Partitioner.hpp> #include <iostream> @@ -72,18 +73,61 @@ ConnectivityDispatcher<Dimension>::_buildItemListToSend() std::vector<std::vector<CellId>> cell_vector_to_send_by_proc(parallel::size()); Array<bool> send_to_rank(parallel::size()); + + NodeValue<bool> node_tag{m_connectivity}; + node_tag.fill(false); + std::vector<NodeId> layer_node_id_list; + std::vector<NodeId> tagged_node_id_list; + + const size_t nb_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + for (CellId cell_id = 0; cell_id < m_connectivity.numberOfCells(); ++cell_id) { + layer_node_id_list.clear(); send_to_rank.fill(false); const auto& cell_to_node = cell_to_node_matrix[cell_id]; - for (size_t i_node = 0; i_node < cell_to_node.size(); ++i_node) { - const NodeId& node_id = cell_to_node[i_node]; - const auto& node_to_cell = node_to_cell_matrix[node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_to_cell.size(); ++i_node_cell) { - const CellId& node_cell_id = node_to_cell[i_node_cell]; - send_to_rank[cell_new_owner[node_cell_id]] = true; + const NodeId node_id = cell_to_node[i_node]; + layer_node_id_list.push_back(node_id); + node_tag[node_id] = true; + } + + for (size_t i_layer = 0; i_layer < nb_layers; ++i_layer) { + for (const auto node_id : layer_node_id_list) { + tagged_node_id_list.push_back(node_id); + const auto& node_to_cell = node_to_cell_matrix[node_id]; + for (size_t i_node_cell = 0; i_node_cell < node_to_cell.size(); ++i_node_cell) { + const CellId& node_cell_id = node_to_cell[i_node_cell]; + send_to_rank[cell_new_owner[node_cell_id]] = true; + } } + + if (i_layer + 1 < nb_layers) { + std::vector<NodeId> old_layer_node_id_list; + std::swap(layer_node_id_list, old_layer_node_id_list); + + for (const auto node_id : old_layer_node_id_list) { + const auto& node_to_cell = node_to_cell_matrix[node_id]; + for (size_t i_node_cell = 0; i_node_cell < node_to_cell.size(); ++i_node_cell) { + const CellId& node_cell_id = node_to_cell[i_node_cell]; + const auto& node_cell_to_node = cell_to_node_matrix[node_cell_id]; + for (size_t i_node = 0; i_node < node_cell_to_node.size(); ++i_node) { + const NodeId cell_node_id = node_cell_to_node[i_node]; + if (not node_tag[cell_node_id]) { + layer_node_id_list.push_back(cell_node_id); + node_tag[cell_node_id] = true; + } + } + } + } + } else { + layer_node_id_list.clear(); + } + } + + for (auto node_id : tagged_node_id_list) { + node_tag[node_id] = false; } + tagged_node_id_list.clear(); for (size_t i_rank = 0; i_rank < send_to_rank.size(); ++i_rank) { if (send_to_rank[i_rank]) { diff --git a/src/scheme/AcousticSolver.cpp b/src/scheme/AcousticSolver.cpp index e4db63bd7..5c8e894c0 100644 --- a/src/scheme/AcousticSolver.cpp +++ b/src/scheme/AcousticSolver.cpp @@ -17,6 +17,7 @@ #include <scheme/IBoundaryConditionDescriptor.hpp> #include <scheme/IDiscreteFunctionDescriptor.hpp> #include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <utils/GlobalVariableManager.hpp> #include <utils/Socket.hpp> #include <variant> @@ -371,6 +372,10 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler const std::shared_ptr<const DiscreteFunctionVariant>& p_v, const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const { + if ((parallel::size() > 1) and (GlobalVariableManager::instance().getNumberOfGhostLayers() == 0)) { + throw NormalError("Acoustic solver requires 1 layer of ghost cells in parallel"); + } + std::shared_ptr mesh_v = getCommonMesh({rho_v, c_v, u_v, p_v}); if (not mesh_v) { throw NormalError("discrete functions are not defined on the same mesh"); diff --git a/src/scheme/FluxingAdvectionSolver.cpp b/src/scheme/FluxingAdvectionSolver.cpp index a2ebb2b25..d57cb9add 100644 --- a/src/scheme/FluxingAdvectionSolver.cpp +++ b/src/scheme/FluxingAdvectionSolver.cpp @@ -23,6 +23,7 @@ #include <scheme/InflowBoundaryConditionDescriptor.hpp> #include <scheme/OutflowBoundaryConditionDescriptor.hpp> #include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <utils/GlobalVariableManager.hpp> #include <variant> #include <vector> @@ -201,6 +202,10 @@ class FluxingAdvectionSolver throw NormalError("old and new meshes must share the same connectivity"); } + if ((parallel::size() > 1) and (GlobalVariableManager::instance().getNumberOfGhostLayers() == 0)) { + throw NormalError("Fluxing advection requires 1 layer of ghost cells in parallel"); + } + this->_computeGeometricalData(); } diff --git a/src/utils/GlobalVariableManager.hpp b/src/utils/GlobalVariableManager.hpp index f720252fd..762c7a0fa 100644 --- a/src/utils/GlobalVariableManager.hpp +++ b/src/utils/GlobalVariableManager.hpp @@ -1,15 +1,20 @@ #ifndef GLOBAL_VARIABLE_MANAGER_HPP #define GLOBAL_VARIABLE_MANAGER_HPP +#include <utils/Exceptions.hpp> #include <utils/PugsAssert.hpp> #include <utils/PugsMacros.hpp> +#include <optional> + class GlobalVariableManager { private: size_t m_connectivity_id = 0; size_t m_mesh_id = 0; + std::optional<size_t> m_number_of_ghost_layers; + static GlobalVariableManager* m_instance; explicit GlobalVariableManager() = default; @@ -32,6 +37,24 @@ class GlobalVariableManager return m_mesh_id++; } + PUGS_INLINE + void + setNumberOfGhostLayers(const size_t number) + { + if (m_number_of_ghost_layers.has_value()) { + throw UnexpectedError("changing number of ghost layers is forbidden"); + } + m_number_of_ghost_layers = number; + } + + PUGS_INLINE + size_t + getNumberOfGhostLayers() + { + Assert(m_number_of_ghost_layers.has_value()); + return m_number_of_ghost_layers.value(); + } + PUGS_INLINE static GlobalVariableManager& instance() diff --git a/src/utils/PugsUtils.cpp b/src/utils/PugsUtils.cpp index f89f543f9..e19e6a29a 100644 --- a/src/utils/PugsUtils.cpp +++ b/src/utils/PugsUtils.cpp @@ -129,6 +129,10 @@ initialize(int& argc, char* argv[]) bool pause_on_error = false; app.add_flag("-p,--pause-on-error", pause_on_error, "Pause for debugging on unexpected error [default: false]"); + int nb_ghost_layers = 1; + app.add_option("--number-of-ghost-layers", nb_ghost_layers, "Number of ghost layers of cells [default: 1]") + ->check(CLI::Range(0, std::numeric_limits<decltype(nb_ghost_layers)>::max())); + bool reproducible_sums = true; app.add_flag("--reproducible-sums,!--no-reproducible-sums", reproducible_sums, "Special treatment of array sums to ensure reproducibility [default: true]"); @@ -165,6 +169,7 @@ initialize(int& argc, char* argv[]) CommunicatorManager::setSplitColor(mpi_split_color); } + GlobalVariableManager::instance().setNumberOfGhostLayers(nb_ghost_layers); ExecutionStatManager::getInstance().setPrint(print_exec_stat); BacktraceManager::setShow(show_backtrace); ConsoleManager::setShowPreamble(show_preamble); diff --git a/tests/mpi_test_main.cpp b/tests/mpi_test_main.cpp index 657cccc8c..794736494 100644 --- a/tests/mpi_test_main.cpp +++ b/tests/mpi_test_main.cpp @@ -103,6 +103,7 @@ main(int argc, char* argv[]) StencilManager::create(); GlobalVariableManager::create(); + GlobalVariableManager::instance().setNumberOfGhostLayers(1); MeshDataBaseForTests::create(); diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 3f81068df..504541ee0 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -62,6 +62,7 @@ main(int argc, char* argv[]) StencilManager::create(); GlobalVariableManager::create(); + GlobalVariableManager::instance().setNumberOfGhostLayers(1); MeshDataBaseForTests::create(); -- GitLab From c779ff9f236829422c6b32b327af0a3640dc444c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 10 Oct 2024 17:52:35 +0200 Subject: [PATCH 063/122] Add tests for arbitrary number of ghost layers construction --- src/utils/GlobalVariableManager.hpp | 3 + tests/CMakeLists.txt | 1 + tests/test_ConnectivityDispatcher.cpp | 214 ++++++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 tests/test_ConnectivityDispatcher.cpp diff --git a/src/utils/GlobalVariableManager.hpp b/src/utils/GlobalVariableManager.hpp index 762c7a0fa..8b7e0a339 100644 --- a/src/utils/GlobalVariableManager.hpp +++ b/src/utils/GlobalVariableManager.hpp @@ -10,6 +10,9 @@ class GlobalVariableManager { private: + // Give some special access for testing + friend class NbGhostLayersTester; + size_t m_connectivity_id = 0; size_t m_mesh_id = 0; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e2dff14cc..feac4de4a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -161,6 +161,7 @@ add_executable (unit_tests add_executable (mpi_unit_tests mpi_test_main.cpp test_Connectivity.cpp + test_ConnectivityDispatcher.cpp test_DiscreteFunctionDPk.cpp test_DiscreteFunctionDPkVector.cpp test_DiscreteFunctionIntegrator.cpp diff --git a/tests/test_ConnectivityDispatcher.cpp b/tests/test_ConnectivityDispatcher.cpp new file mode 100644 index 000000000..abe07cc21 --- /dev/null +++ b/tests/test_ConnectivityDispatcher.cpp @@ -0,0 +1,214 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <mesh/CartesianMeshBuilder.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/GmshReader.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshVariant.hpp> +#include <utils/Messenger.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <filesystem> + +// clazy:excludeall=non-pod-global-static + +class NbGhostLayersTester +{ + private: + const size_t m_original_number_of_ghost_layers; + + public: + NbGhostLayersTester(const size_t number_of_ghost_layers) + : m_original_number_of_ghost_layers{GlobalVariableManager::instance().getNumberOfGhostLayers()} + { + GlobalVariableManager::instance().m_number_of_ghost_layers = number_of_ghost_layers; + } + + ~NbGhostLayersTester() + { + GlobalVariableManager::instance().m_number_of_ghost_layers = m_original_number_of_ghost_layers; + } +}; + +TEST_CASE("ConnectivityDispatcher", "[mesh]") +{ + auto check_number_of_ghost_layers = [](const auto& connectivity, const size_t number_of_layers) { + // We assume that the specified number of layers can be built + // (there are enough non owned layer of cells in the connectivity) + const auto cell_is_owned = connectivity.cellIsOwned(); + + CellValue<size_t> cell_layer{connectivity}; + cell_layer.fill(number_of_layers + 1); + + NodeValue<size_t> node_layer{connectivity}; + node_layer.fill(number_of_layers + 1); + + auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + if (cell_is_owned[cell_id]) { + cell_layer[cell_id] = 0; + } + }); + + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + auto node_cell_list = node_to_cell_matrix[node_id]; + size_t min_layer = cell_layer[node_cell_list[0]]; + for (size_t i_cell = 1; i_cell < node_cell_list.size(); ++i_cell) { + min_layer = std::min(min_layer, cell_layer[node_cell_list[i_cell]]); + } + if (min_layer < number_of_layers + 1) { + node_layer[node_id] = min_layer; + } + }); + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + size_t min_layer = node_layer[cell_node_list[0]]; + size_t max_layer = min_layer; + for (size_t i_node = 1; i_node < cell_node_list.size(); ++i_node) { + min_layer = std::min(min_layer, node_layer[cell_node_list[i_node]]); + max_layer = std::max(max_layer, node_layer[cell_node_list[i_node]]); + } + if ((min_layer != max_layer) or + ((min_layer < number_of_layers + 1) and (cell_layer[cell_id] == number_of_layers + 1))) { + cell_layer[cell_id] = min_layer + 1; + } + }); + } + + auto is_boundary_face = connectivity.isBoundaryFace(); + auto face_to_cell_matrix = connectivity.faceToCellMatrix(); + + bool has_required_number_of_ghost_layers = true; + for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) { + auto face_cell_list = face_to_cell_matrix[face_id]; + if ((face_cell_list.size() == 1) and (not is_boundary_face[face_id])) { + const CellId face_cell_id = face_cell_list[0]; + has_required_number_of_ghost_layers &= (cell_layer[face_cell_id] == number_of_layers); + } + } + + REQUIRE(parallel::allReduceAnd(has_required_number_of_ghost_layers)); + bool first_ghost_layer_is_1 = true; + for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) { + auto face_cell_list = face_to_cell_matrix[face_id]; + if (face_cell_list.size() == 2) { + const CellId face_cell0_id = face_cell_list[0]; + const CellId face_cell1_id = face_cell_list[1]; + if (cell_is_owned[face_cell0_id] xor cell_is_owned[face_cell1_id]) { + for (size_t i_cell = 0; i_cell < face_cell_list.size(); ++i_cell) { + const CellId face_cell_id = face_cell_list[i_cell]; + if (not cell_is_owned[face_cell_id]) { + first_ghost_layer_is_1 &= (cell_layer[face_cell_id] == 1); + } + } + } + } + } + + REQUIRE(parallel::allReduceAnd(first_ghost_layer_is_1)); + }; + + SECTION("1 layer meshes") + { + for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { + SECTION(named_mesh.name()) + { + const std::shared_ptr p_mesh = named_mesh.mesh()->get<const Mesh<1>>(); + check_number_of_ghost_layers(p_mesh->connectivity(), 1); + } + } + for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) { + SECTION(named_mesh.name()) + { + const std::shared_ptr p_mesh = named_mesh.mesh()->get<const Mesh<2>>(); + check_number_of_ghost_layers(p_mesh->connectivity(), 1); + } + } + for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { + SECTION(named_mesh.name()) + { + const std::shared_ptr p_mesh = named_mesh.mesh()->get<const Mesh<3>>(); + check_number_of_ghost_layers(p_mesh->connectivity(), 1); + } + } + } + + for (size_t nb_ghost_layers = 2; nb_ghost_layers < 5; ++nb_ghost_layers) { + std::stringstream os; + os << nb_ghost_layers << " layer meshes"; + + SECTION(os.str()) + { + REQUIRE(GlobalVariableManager::instance().getNumberOfGhostLayers() == 1); + + NbGhostLayersTester nb_ghost_layers_tester(nb_ghost_layers); + + REQUIRE(GlobalVariableManager::instance().getNumberOfGhostLayers() == nb_ghost_layers); + + SECTION("Cartesian 1D mesh") + { + auto cartesian_1d_mesh = + CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{23}}.mesh(); + const std::shared_ptr p_mesh = cartesian_1d_mesh->get<const Mesh<1>>(); + check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers); + } + + SECTION("Cartesian 2D mesh") + { + auto cartesian_2d_mesh = + CartesianMeshBuilder{TinyVector<2>{0, -1}, TinyVector<2>{3, 2}, TinyVector<2, size_t>{6, 7}}.mesh(); + const std::shared_ptr p_mesh = cartesian_2d_mesh->get<const Mesh<2>>(); + check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers); + } + + SECTION("Cartesian 3D mesh") + { + auto cartesian_3d_mesh = + CartesianMeshBuilder{TinyVector<3>{0, 1, 0}, TinyVector<3>{2, -1, 3}, TinyVector<3, size_t>{6, 7, 4}}.mesh(); + const std::shared_ptr p_mesh = cartesian_3d_mesh->get<const Mesh<3>>(); + check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers); + } + + SECTION("unordered 1d mesh") + { + const std::string filename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("unordered-1d.msh"); + + auto mesh_v = GmshReader{filename}.mesh(); + + const std::shared_ptr p_mesh = mesh_v->get<const Mesh<1>>(); + check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers); + } + + SECTION("hybrid 2d mesh") + { + const std::string filename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("hybrid-2d.msh"); + + auto mesh_v = GmshReader{filename}.mesh(); + + const std::shared_ptr p_mesh = mesh_v->get<const Mesh<2>>(); + check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers); + } + + SECTION("hybrid 3d mesh") + { + const std::string filename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("hybrid-3d.msh"); + + auto mesh_v = GmshReader{filename}.mesh(); + + const std::shared_ptr p_mesh = mesh_v->get<const Mesh<3>>(); + check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers); + } + } + + REQUIRE(GlobalVariableManager::instance().getNumberOfGhostLayers() == 1); + } +} -- GitLab From d15bc96784c25f7a9d31ef72410b487585af95a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 10 Oct 2024 18:31:26 +0200 Subject: [PATCH 064/122] Few code clean-up --- src/mesh/Stencil.hpp | 34 +++++++++++++++++++++++-- src/mesh/StencilBuilder.cpp | 7 ++--- src/scheme/PolynomialReconstruction.cpp | 14 +++++----- tests/test_StencilBuilder.cpp | 8 +++--- 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/mesh/Stencil.hpp b/src/mesh/Stencil.hpp index 900c1b4d2..eb4864c7d 100644 --- a/src/mesh/Stencil.hpp +++ b/src/mesh/Stencil.hpp @@ -8,8 +8,38 @@ class Stencil { public: - using BoundaryDescriptorStencilList = - std::vector<std::pair<std::shared_ptr<const IBoundaryDescriptor>, ConnectivityMatrix>>; + class BoundaryDescriptorStencil + { + private: + std::shared_ptr<const IBoundaryDescriptor> m_iboundary_descriptor; + ConnectivityMatrix m_stencil; + + public: + const IBoundaryDescriptor& + boundaryDescriptor() const + { + return *m_iboundary_descriptor; + } + + const ConnectivityMatrix& + stencil() const + { + return m_stencil; + } + + BoundaryDescriptorStencil(const std::shared_ptr<const IBoundaryDescriptor>& iboundary_descriptor, + const ConnectivityMatrix& stencil) + : m_iboundary_descriptor{iboundary_descriptor}, m_stencil{stencil} + {} + + BoundaryDescriptorStencil(const BoundaryDescriptorStencil&) = default; + BoundaryDescriptorStencil(BoundaryDescriptorStencil&&) = default; + + BoundaryDescriptorStencil() = default; + ~BoundaryDescriptorStencil() = default; + }; + + using BoundaryDescriptorStencilList = std::vector<BoundaryDescriptorStencil>; private: ConnectivityMatrix m_stencil; diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index ddff37e0c..30e922ec2 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -318,9 +318,10 @@ StencilBuilder::_build(const ConnectivityType& connectivity, size_t i = 0; for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { symmetry_boundary_stencil_list.emplace_back( - std::make_pair(p_boundary_descriptor, - ConnectivityMatrix{symmetry_row_map_list[i], - convert_to_array(symmetry_column_indices_vector[i])})); + Stencil::BoundaryDescriptorStencil{p_boundary_descriptor, + ConnectivityMatrix{symmetry_row_map_list[i], + convert_to_array( + symmetry_column_indices_vector[i])}}); ++i; } } diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index b0cacf9d9..7bcf3a5c6 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -785,7 +785,7 @@ PolynomialReconstruction::_build( auto stencil_cell_list = stencil[cell_id]; size_t stencil_size = stencil_cell_list.size(); for (size_t i = 0; i < m_descriptor.symmetryBoundaryDescriptorList().size(); ++i) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i].second; + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i].stencil(); stencil_size += ghost_stencil[cell_id].size(); } @@ -898,7 +898,7 @@ PolynomialReconstruction::_build( } for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].second; + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].stencil(); auto ghost_cell_list = ghost_stencil[cell_j_id]; for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { const CellId cell_i_id = ghost_cell_list[i]; @@ -954,7 +954,7 @@ PolynomialReconstruction::_build( } for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].second; + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].stencil(); auto ghost_cell_list = ghost_stencil[cell_j_id]; for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { const CellId cell_i_id = ghost_cell_list[i]; @@ -989,7 +989,7 @@ PolynomialReconstruction::_build( } } for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].second; + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].stencil(); auto ghost_cell_list = ghost_stencil[cell_j_id]; const Rd& origin = symmetry_origin_list[i_symmetry]; @@ -1034,7 +1034,7 @@ PolynomialReconstruction::_build( } } for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].second; + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].stencil(); auto ghost_cell_list = ghost_stencil[cell_j_id]; const Rd& origin = symmetry_origin_list[i_symmetry]; @@ -1079,7 +1079,7 @@ PolynomialReconstruction::_build( } } for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].second; + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].stencil(); auto ghost_cell_list = ghost_stencil[cell_j_id]; const Rd& origin = symmetry_origin_list[i_symmetry]; @@ -1120,7 +1120,7 @@ PolynomialReconstruction::_build( } } for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].second; + auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].stencil(); auto ghost_cell_list = ghost_stencil[cell_j_id]; const Rd& origin = symmetry_origin_list[i_symmetry]; diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder.cpp index 0ef8d97ba..754a443a7 100644 --- a/tests/test_StencilBuilder.cpp +++ b/tests/test_StencilBuilder.cpp @@ -130,7 +130,7 @@ TEST_CASE("StencilBuilder", "[mesh]") is_empty &= (stencil[cell_id].size() == 0); for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry_stencil) { - is_empty &= (stencil.symmetryBoundaryStencilList()[i_symmetry_stencil].second[cell_id].size() == 0); + is_empty &= (stencil.symmetryBoundaryStencilList()[i_symmetry_stencil].stencil()[cell_id].size() == 0); } } } @@ -148,10 +148,10 @@ TEST_CASE("StencilBuilder", "[mesh]") for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry_stencil) { - auto p_boundary_descriptor = stencil.symmetryBoundaryStencilList()[i_symmetry_stencil].first; - const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; + const IBoundaryDescriptor& boundary_descriptor = + stencil.symmetryBoundaryStencilList()[i_symmetry_stencil].boundaryDescriptor(); - auto boundary_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry_stencil].second; + auto boundary_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry_stencil].stencil(); auto boundary_node_list = getMeshFlatNodeBoundary(mesh, boundary_descriptor); CellValue<bool> boundary_cell{mesh.connectivity()}; -- GitLab From db11c008a584aa4fe9b122b63a7a91abb77939bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 11 Oct 2024 16:25:01 +0200 Subject: [PATCH 065/122] Continue clean-up --- src/mesh/ItemToItemMatrix.hpp | 7 +- src/mesh/Stencil.hpp | 74 ----------------- src/mesh/StencilArray.hpp | 103 ++++++++++++++++++++++++ src/mesh/StencilBuilder.cpp | 15 ++-- src/mesh/StencilBuilder.hpp | 14 ++-- src/mesh/StencilManager.cpp | 26 +++--- src/mesh/StencilManager.hpp | 12 +-- src/scheme/PolynomialReconstruction.cpp | 42 ++++++---- tests/test_StencilBuilder.cpp | 60 +++++++------- 9 files changed, 198 insertions(+), 155 deletions(-) delete mode 100644 src/mesh/Stencil.hpp create mode 100644 src/mesh/StencilArray.hpp diff --git a/src/mesh/ItemToItemMatrix.hpp b/src/mesh/ItemToItemMatrix.hpp index 6cd2d798b..5d9c23d6f 100644 --- a/src/mesh/ItemToItemMatrix.hpp +++ b/src/mesh/ItemToItemMatrix.hpp @@ -20,8 +20,8 @@ class ItemToItemMatrix private: using IndexType = typename ConnectivityMatrix::IndexType; - const IndexType* const m_values; const size_t m_size; + const IndexType* const m_values; public: PUGS_INLINE @@ -40,8 +40,9 @@ class ItemToItemMatrix PUGS_INLINE UnsafeSubItemArray(const ConnectivityMatrix& connectivity_matrix, SourceItemId source_item_id) - : m_values{&(connectivity_matrix.values()[connectivity_matrix.rowsMap()[source_item_id]])}, - m_size(connectivity_matrix.rowsMap()[source_item_id + 1] - connectivity_matrix.rowsMap()[source_item_id]) + : m_size(connectivity_matrix.rowsMap()[source_item_id + 1] - connectivity_matrix.rowsMap()[source_item_id]), + m_values{(m_size == 0) ? nullptr + : (&(connectivity_matrix.values()[connectivity_matrix.rowsMap()[source_item_id]]))} {} PUGS_INLINE diff --git a/src/mesh/Stencil.hpp b/src/mesh/Stencil.hpp deleted file mode 100644 index eb4864c7d..000000000 --- a/src/mesh/Stencil.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef STENCIL_HPP -#define STENCIL_HPP - -#include <mesh/ConnectivityMatrix.hpp> -#include <mesh/IBoundaryDescriptor.hpp> -#include <mesh/ItemId.hpp> - -class Stencil -{ - public: - class BoundaryDescriptorStencil - { - private: - std::shared_ptr<const IBoundaryDescriptor> m_iboundary_descriptor; - ConnectivityMatrix m_stencil; - - public: - const IBoundaryDescriptor& - boundaryDescriptor() const - { - return *m_iboundary_descriptor; - } - - const ConnectivityMatrix& - stencil() const - { - return m_stencil; - } - - BoundaryDescriptorStencil(const std::shared_ptr<const IBoundaryDescriptor>& iboundary_descriptor, - const ConnectivityMatrix& stencil) - : m_iboundary_descriptor{iboundary_descriptor}, m_stencil{stencil} - {} - - BoundaryDescriptorStencil(const BoundaryDescriptorStencil&) = default; - BoundaryDescriptorStencil(BoundaryDescriptorStencil&&) = default; - - BoundaryDescriptorStencil() = default; - ~BoundaryDescriptorStencil() = default; - }; - - using BoundaryDescriptorStencilList = std::vector<BoundaryDescriptorStencil>; - - private: - ConnectivityMatrix m_stencil; - BoundaryDescriptorStencilList m_symmetry_boundary_stencil_list; - - public: -#warning REWORK INTERFACE - PUGS_INLINE - const auto& - symmetryBoundaryStencilList() const - { - return m_symmetry_boundary_stencil_list; - } - - PUGS_INLINE - auto - operator[](CellId cell_id) const - { - return m_stencil[cell_id]; - } - - Stencil(const ConnectivityMatrix& stencil, const BoundaryDescriptorStencilList& symmetry_name_stencil) - : m_stencil{stencil}, m_symmetry_boundary_stencil_list{symmetry_name_stencil} - {} - - Stencil(const Stencil&) = default; - Stencil(Stencil&&) = default; - - ~Stencil() = default; -}; - -#endif // STENCIL_HPP diff --git a/src/mesh/StencilArray.hpp b/src/mesh/StencilArray.hpp new file mode 100644 index 000000000..d1b35def0 --- /dev/null +++ b/src/mesh/StencilArray.hpp @@ -0,0 +1,103 @@ +#ifndef STENCIL_ARRAY_HPP +#define STENCIL_ARRAY_HPP + +#include <mesh/ConnectivityMatrix.hpp> +#include <mesh/IBoundaryDescriptor.hpp> +#include <mesh/ItemId.hpp> +#include <mesh/ItemToItemMatrix.hpp> + +template <ItemType source_item_type, ItemType target_item_type> +class StencilArray +{ + public: + using ItemToItemMatrixT = ItemToItemMatrix<source_item_type, target_item_type>; + + class BoundaryDescriptorStencilArray + { + private: + std::shared_ptr<const IBoundaryDescriptor> m_iboundary_descriptor; + const ConnectivityMatrix m_connectivity_matrix; + const ItemToItemMatrixT m_stencil_array; + + public: + const IBoundaryDescriptor& + boundaryDescriptor() const + { + return *m_iboundary_descriptor; + } + + const auto& + stencilArray() const + { + return m_stencil_array; + } + + BoundaryDescriptorStencilArray(const std::shared_ptr<const IBoundaryDescriptor>& iboundary_descriptor, + const ConnectivityMatrix& connectivity_matrix) + : m_iboundary_descriptor{iboundary_descriptor}, + m_connectivity_matrix{connectivity_matrix}, + m_stencil_array{m_connectivity_matrix} + {} + + BoundaryDescriptorStencilArray(const BoundaryDescriptorStencilArray& bdsa) + : m_iboundary_descriptor{bdsa.m_iboundary_descriptor}, + m_connectivity_matrix{bdsa.m_connectivity_matrix}, + m_stencil_array{m_connectivity_matrix} + {} + + BoundaryDescriptorStencilArray(BoundaryDescriptorStencilArray&& bdsa) + : m_iboundary_descriptor{std::move(bdsa.m_iboundary_descriptor)}, + m_connectivity_matrix{std::move(bdsa.m_connectivity_matrix)}, + m_stencil_array{m_connectivity_matrix} + {} + + ~BoundaryDescriptorStencilArray() = default; + }; + + using BoundaryDescriptorStencilArrayList = std::vector<BoundaryDescriptorStencilArray>; + + private: + const ConnectivityMatrix m_connectivity_matrix; + const ItemToItemMatrixT m_stencil_array; + BoundaryDescriptorStencilArrayList m_symmetry_boundary_stencil_array_list; + + public: + PUGS_INLINE + const auto& + symmetryBoundaryStencilArrayList() const + { + return m_symmetry_boundary_stencil_array_list; + } + + PUGS_INLINE + auto + operator[](CellId cell_id) const + { + return m_stencil_array[cell_id]; + } + + StencilArray(const ConnectivityMatrix& connectivity_matrix, + const BoundaryDescriptorStencilArrayList& symmetry_boundary_descriptor_stencil_array_list) + : m_connectivity_matrix{connectivity_matrix}, + m_stencil_array{m_connectivity_matrix}, + m_symmetry_boundary_stencil_array_list{symmetry_boundary_descriptor_stencil_array_list} + {} + + StencilArray(const StencilArray& stencil_array) + : m_connectivity_matrix{stencil_array.m_connectivity_matrix}, + m_stencil_array{m_connectivity_matrix}, + m_symmetry_boundary_stencil_array_list{stencil_array.m_symmetry_boundary_stencil_array_list} + {} + + StencilArray(StencilArray&& stencil_array) + : m_connectivity_matrix{std::move(stencil_array.m_connectivity_matrix)}, + m_stencil_array{m_connectivity_matrix}, + m_symmetry_boundary_stencil_array_list{std::move(stencil_array.m_symmetry_boundary_stencil_array_list)} + {} + + ~StencilArray() = default; +}; + +using CellToCellStencilArray = StencilArray<ItemType::cell, ItemType::cell>; + +#endif // STENCIL_ARRAY_HPP diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 30e922ec2..920dad9e2 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -108,12 +108,11 @@ StencilBuilder::_getColumnIndices(const ConnectivityType& connectivity, const Ar } template <typename ConnectivityType> -Stencil +CellToCellStencilArray StencilBuilder::_build(const ConnectivityType& connectivity, size_t number_of_layers, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { -#warning TEMPORARY if (symmetry_boundary_descriptor_list.size() == 0) { if (number_of_layers == 1) { Array<const uint32_t> row_map = this->_getRowMap(connectivity); @@ -313,15 +312,15 @@ StencilBuilder::_build(const ConnectivityType& connectivity, } ConnectivityMatrix primal_stencil{row_map, convert_to_array(column_indices_vector)}; - Stencil::BoundaryDescriptorStencilList symmetry_boundary_stencil_list; + CellToCellStencilArray::BoundaryDescriptorStencilArrayList symmetry_boundary_stencil_list; { size_t i = 0; for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { symmetry_boundary_stencil_list.emplace_back( - Stencil::BoundaryDescriptorStencil{p_boundary_descriptor, - ConnectivityMatrix{symmetry_row_map_list[i], - convert_to_array( - symmetry_column_indices_vector[i])}}); + CellToCellStencilArray:: + BoundaryDescriptorStencilArray{p_boundary_descriptor, + ConnectivityMatrix{symmetry_row_map_list[i], + convert_to_array(symmetry_column_indices_vector[i])}}); ++i; } } @@ -334,7 +333,7 @@ StencilBuilder::_build(const ConnectivityType& connectivity, } } -Stencil +CellToCellStencilArray StencilBuilder::build(const IConnectivity& connectivity, size_t number_of_layers, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index 7f11c0686..aaf5076f1 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -2,7 +2,7 @@ #define STENCIL_BUILDER_HPP #include <mesh/IBoundaryDescriptor.hpp> -#include <mesh/Stencil.hpp> +#include <mesh/StencilArray.hpp> #include <string> #include <vector> @@ -22,14 +22,14 @@ class StencilBuilder const Array<const uint32_t>& row_map) const; template <typename ConnectivityType> - Stencil _build(const ConnectivityType& connectivity, - size_t number_of_layers, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + CellToCellStencilArray _build(const ConnectivityType& connectivity, + size_t number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; friend class StencilManager; - Stencil build(const IConnectivity& connectivity, - size_t number_of_layers, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + CellToCellStencilArray build(const IConnectivity& connectivity, + size_t number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; public: StencilBuilder() = default; diff --git a/src/mesh/StencilManager.cpp b/src/mesh/StencilManager.cpp index cc99577f2..ed1f9ef76 100644 --- a/src/mesh/StencilManager.cpp +++ b/src/mesh/StencilManager.cpp @@ -18,10 +18,10 @@ StencilManager::destroy() Assert(m_instance != nullptr, "StencilManager was not created"); // LCOV_EXCL_START - if (m_instance->m_stored_stencil_map.size() > 0) { + if (m_instance->m_stored_cell_to_cell_stencil_map.size() > 0) { std::stringstream error; error << ": some connectivities are still registered\n"; - for (const auto& [key, stencil_p] : m_instance->m_stored_stencil_map) { + for (const auto& [key, stencil_p] : m_instance->m_stored_cell_to_cell_stencil_map) { error << " - connectivity of id " << rang::fgB::magenta << key.connectivity_id << rang::style::reset << '\n'; } throw UnexpectedError(error.str()); @@ -31,17 +31,19 @@ StencilManager::destroy() m_instance = nullptr; } -const Stencil& -StencilManager::getStencil(const IConnectivity& connectivity, - size_t degree, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) +const CellToCellStencilArray& +StencilManager::getCellToCellStencilArray(const IConnectivity& connectivity, + size_t degree, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) { - if (not m_stored_stencil_map.contains(Key{connectivity.id(), degree, symmetry_boundary_descriptor_list})) { - m_stored_stencil_map[Key{connectivity.id(), degree, symmetry_boundary_descriptor_list}] = - std::make_shared<Stencil>(StencilBuilder{}.build(connectivity, degree, symmetry_boundary_descriptor_list)); + if (not m_stored_cell_to_cell_stencil_map.contains( + Key{connectivity.id(), degree, symmetry_boundary_descriptor_list})) { + m_stored_cell_to_cell_stencil_map[Key{connectivity.id(), degree, symmetry_boundary_descriptor_list}] = + std::make_shared<CellToCellStencilArray>( + StencilBuilder{}.build(connectivity, degree, symmetry_boundary_descriptor_list)); } - return *m_stored_stencil_map.at(Key{connectivity.id(), degree, symmetry_boundary_descriptor_list}); + return *m_stored_cell_to_cell_stencil_map.at(Key{connectivity.id(), degree, symmetry_boundary_descriptor_list}); } void @@ -50,9 +52,9 @@ StencilManager::deleteConnectivity(const size_t connectivity_id) bool has_removed = false; do { has_removed = false; - for (const auto& [key, p_stencil] : m_stored_stencil_map) { + for (const auto& [key, p_stencil] : m_stored_cell_to_cell_stencil_map) { if (connectivity_id == key.connectivity_id) { - m_stored_stencil_map.erase(key); + m_stored_cell_to_cell_stencil_map.erase(key); has_removed = true; break; } diff --git a/src/mesh/StencilManager.hpp b/src/mesh/StencilManager.hpp index 814d8e476..de0007631 100644 --- a/src/mesh/StencilManager.hpp +++ b/src/mesh/StencilManager.hpp @@ -3,7 +3,7 @@ #include <mesh/IBoundaryDescriptor.hpp> #include <mesh/IConnectivity.hpp> -#include <mesh/Stencil.hpp> +#include <mesh/StencilArray.hpp> #include <memory> #include <set> @@ -50,6 +50,7 @@ class StencilManager return boundary_descriptor_set == k_boundary_descriptor_set; } }; + struct HashKey { size_t @@ -61,7 +62,7 @@ class StencilManager } }; - std::unordered_map<Key, std::shared_ptr<const Stencil>, HashKey> m_stored_stencil_map; + std::unordered_map<Key, std::shared_ptr<const CellToCellStencilArray>, HashKey> m_stored_cell_to_cell_stencil_map; public: static void create(); @@ -77,9 +78,10 @@ class StencilManager void deleteConnectivity(const size_t connectivity_id); - const Stencil& getStencil(const IConnectivity& i_connectivity, - size_t degree = 1, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list = {}); + const CellToCellStencilArray& getCellToCellStencilArray( + const IConnectivity& i_connectivity, + size_t degree = 1, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list = {}); StencilManager(const StencilManager&) = delete; StencilManager(StencilManager&&) = delete; diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 7bcf3a5c6..82f5081a7 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -769,8 +769,9 @@ PolynomialReconstruction::_build( const size_t basis_dimension = DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(m_descriptor.degree()); - const Stencil& stencil = StencilManager::instance().getStencil(mesh.connectivity(), m_descriptor.degree(), - m_descriptor.symmetryBoundaryDescriptorList()); + const auto& stencil_array = + StencilManager::instance().getCellToCellStencilArray(mesh.connectivity(), m_descriptor.degree(), + m_descriptor.symmetryBoundaryDescriptorList()); auto xr = mesh.xr(); auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); @@ -782,10 +783,10 @@ PolynomialReconstruction::_build( auto cell_to_face_matrix = mesh.connectivity().cellToFaceMatrix(); auto full_stencil_size = [&](const CellId cell_id) { - auto stencil_cell_list = stencil[cell_id]; + auto stencil_cell_list = stencil_array[cell_id]; size_t stencil_size = stencil_cell_list.size(); for (size_t i = 0; i < m_descriptor.symmetryBoundaryDescriptorList().size(); ++i) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i].stencil(); + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i].stencilArray(); stencil_size += ghost_stencil[cell_id].size(); } @@ -814,6 +815,7 @@ PolynomialReconstruction::_build( } return normal_list; }(); + SmallArray<const Rd> symmetry_origin_list = [&] { SmallArray<Rd> origin_list(m_descriptor.symmetryBoundaryDescriptorList().size()); size_t i_symmetry_boundary = 0; @@ -863,7 +865,7 @@ PolynomialReconstruction::_build( if (cell_is_owned[cell_j_id]) { const int32_t t = tokens.acquire(); - auto stencil_cell_list = stencil[cell_j_id]; + auto stencil_cell_list = stencil_array[cell_j_id]; ShrinkMatrixView B(B_pool[t], full_stencil_size(cell_j_id)); @@ -897,8 +899,9 @@ PolynomialReconstruction::_build( } } - for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].stencil(); + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); auto ghost_cell_list = ghost_stencil[cell_j_id]; for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { const CellId cell_i_id = ghost_cell_list[i]; @@ -953,8 +956,9 @@ PolynomialReconstruction::_build( } } - for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].stencil(); + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); auto ghost_cell_list = ghost_stencil[cell_j_id]; for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { const CellId cell_i_id = ghost_cell_list[i]; @@ -988,8 +992,9 @@ PolynomialReconstruction::_build( A(index, l) = Xi_Xj[l]; } } - for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].stencil(); + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); auto ghost_cell_list = ghost_stencil[cell_j_id]; const Rd& origin = symmetry_origin_list[i_symmetry]; @@ -1033,8 +1038,9 @@ PolynomialReconstruction::_build( A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; } } - for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].stencil(); + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); auto ghost_cell_list = ghost_stencil[cell_j_id]; const Rd& origin = symmetry_origin_list[i_symmetry]; @@ -1078,8 +1084,9 @@ PolynomialReconstruction::_build( A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; } } - for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].stencil(); + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); auto ghost_cell_list = ghost_stencil[cell_j_id]; const Rd& origin = symmetry_origin_list[i_symmetry]; @@ -1119,8 +1126,9 @@ PolynomialReconstruction::_build( B(index, l) *= weight; } } - for (size_t i_symmetry = 0; i_symmetry < stencil.symmetryBoundaryStencilList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry].stencil(); + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); auto ghost_cell_list = ghost_stencil[cell_j_id]; const Rd& origin = symmetry_origin_list[i_symmetry]; diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder.cpp index 754a443a7..b68f0883d 100644 --- a/tests/test_StencilBuilder.cpp +++ b/tests/test_StencilBuilder.cpp @@ -19,7 +19,7 @@ TEST_CASE("StencilBuilder", "[mesh]") { SECTION("inner stencil") { - auto is_valid = [](const auto& connectivity, const Stencil& stencil) { + auto is_valid = [](const auto& connectivity, const auto& stencil_array) { auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); @@ -42,7 +42,7 @@ TEST_CASE("StencilBuilder", "[mesh]") } } - auto cell_stencil = stencil[cell_id]; + auto cell_stencil = stencil_array[cell_id]; auto i_set_cell = cell_set.begin(); for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { @@ -63,9 +63,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<1>& connectivity = mesh.connectivity(); - Stencil stencil = StencilManager::instance().getStencil(connectivity); + auto stencil_array = StencilManager::instance().getCellToCellStencilArray(connectivity); - REQUIRE(is_valid(connectivity, stencil)); + REQUIRE(is_valid(connectivity, stencil_array)); } SECTION("unordered") @@ -73,9 +73,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); const Connectivity<1>& connectivity = mesh.connectivity(); - Stencil stencil = StencilManager::instance().getStencil(connectivity); + auto stencil_array = StencilManager::instance().getCellToCellStencilArray(connectivity); - REQUIRE(is_valid(connectivity, stencil)); + REQUIRE(is_valid(connectivity, stencil_array)); } } @@ -86,7 +86,7 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); + REQUIRE(is_valid(connectivity, StencilManager::instance().getCellToCellStencilArray(connectivity))); } SECTION("hybrid") @@ -94,7 +94,7 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); + REQUIRE(is_valid(connectivity, StencilManager::instance().getCellToCellStencilArray(connectivity))); } } @@ -105,7 +105,7 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); + REQUIRE(is_valid(connectivity, StencilManager::instance().getCellToCellStencilArray(connectivity))); } SECTION("hybrid") @@ -113,24 +113,26 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilManager::instance().getStencil(connectivity))); + REQUIRE(is_valid(connectivity, StencilManager::instance().getCellToCellStencilArray(connectivity))); } } } SECTION("Stencil using symmetries") { - auto check_ghost_cells_have_empty_stencils = [](const auto& stencil, const auto& connecticity) { + auto check_ghost_cells_have_empty_stencils = [](const auto& stencil_array, const auto& connecticity) { bool is_empty = true; auto cell_is_owned = connecticity.cellIsOwned(); for (CellId cell_id = 0; cell_id < connecticity.numberOfCells(); ++cell_id) { if (not cell_is_owned[cell_id]) { - is_empty &= (stencil[cell_id].size() == 0); - for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil.symmetryBoundaryStencilList().size(); - ++i_symmetry_stencil) { - is_empty &= (stencil.symmetryBoundaryStencilList()[i_symmetry_stencil].stencil()[cell_id].size() == 0); + is_empty &= (stencil_array[cell_id].size() == 0); + for (size_t i_symmetry_stencil = 0; + i_symmetry_stencil < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry_stencil) { + is_empty &= + (stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].stencilArray()[cell_id].size() == + 0); } } } @@ -138,7 +140,7 @@ TEST_CASE("StencilBuilder", "[mesh]") return is_empty; }; - auto symmetry_stencils_are_valid = [](const auto& stencil, const auto& mesh) { + auto symmetry_stencils_are_valid = [](const auto& stencil_array, const auto& mesh) { bool is_valid = true; auto node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); @@ -146,12 +148,12 @@ TEST_CASE("StencilBuilder", "[mesh]") auto cell_is_owned = mesh.connectivity().cellIsOwned(); auto cell_number = mesh.connectivity().cellNumber(); - for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil.symmetryBoundaryStencilList().size(); + for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry_stencil) { const IBoundaryDescriptor& boundary_descriptor = - stencil.symmetryBoundaryStencilList()[i_symmetry_stencil].boundaryDescriptor(); + stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].boundaryDescriptor(); - auto boundary_stencil = stencil.symmetryBoundaryStencilList()[i_symmetry_stencil].stencil(); + auto boundary_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].stencilArray(); auto boundary_node_list = getMeshFlatNodeBoundary(mesh, boundary_descriptor); CellValue<bool> boundary_cell{mesh.connectivity()}; @@ -219,11 +221,11 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<2>& connectivity = mesh.connectivity(); - auto stencil = StencilManager::instance().getStencil(connectivity, 1, list); + auto stencil_array = StencilManager::instance().getCellToCellStencilArray(connectivity, 1, list); - REQUIRE(stencil.symmetryBoundaryStencilList().size() == 4); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(symmetry_stencils_are_valid(stencil_array, mesh)); } SECTION("hybrid") @@ -231,9 +233,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - auto stencil = StencilManager::instance().getStencil(connectivity, 1, list); + auto stencil = StencilManager::instance().getCellToCellStencilArray(connectivity, 1, list); - REQUIRE(stencil.symmetryBoundaryStencilList().size() == 4); + REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); } @@ -254,9 +256,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - auto stencil = StencilManager::instance().getStencil(connectivity, 1, list); + auto stencil = StencilManager::instance().getCellToCellStencilArray(connectivity, 1, list); - REQUIRE(stencil.symmetryBoundaryStencilList().size() == 6); + REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); } @@ -266,9 +268,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - auto stencil = StencilManager::instance().getStencil(connectivity, 1, list); + auto stencil = StencilManager::instance().getCellToCellStencilArray(connectivity, 1, list); - REQUIRE(stencil.symmetryBoundaryStencilList().size() == 6); + REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); } -- GitLab From b417f2c678a5cf8476fef4c12877fee9631ee409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 11 Oct 2024 19:38:09 +0200 Subject: [PATCH 066/122] Add a (first?) stencil descriptor and prepare other kinds of stencils --- src/mesh/StencilArray.hpp | 2 + src/mesh/StencilBuilder.cpp | 14 +++--- src/mesh/StencilBuilder.hpp | 3 +- src/mesh/StencilDescriptor.hpp | 62 +++++++++++++++++++++++++ src/mesh/StencilManager.cpp | 60 +++++++++++++++--------- src/mesh/StencilManager.hpp | 31 ++++++++++--- src/scheme/PolynomialReconstruction.cpp | 5 +- tests/test_StencilBuilder.cpp | 48 +++++++++++++++---- 8 files changed, 177 insertions(+), 48 deletions(-) create mode 100644 src/mesh/StencilDescriptor.hpp diff --git a/src/mesh/StencilArray.hpp b/src/mesh/StencilArray.hpp index d1b35def0..273e38445 100644 --- a/src/mesh/StencilArray.hpp +++ b/src/mesh/StencilArray.hpp @@ -99,5 +99,7 @@ class StencilArray }; using CellToCellStencilArray = StencilArray<ItemType::cell, ItemType::cell>; +using CellToFaceStencilArray = StencilArray<ItemType::cell, ItemType::face>; +using NodeToCellStencilArray = StencilArray<ItemType::cell, ItemType::node>; #endif // STENCIL_ARRAY_HPP diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 920dad9e2..350492419 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -335,21 +335,21 @@ StencilBuilder::_build(const ConnectivityType& connectivity, CellToCellStencilArray StencilBuilder::build(const IConnectivity& connectivity, - size_t number_of_layers, + const StencilDescriptor& stencil_descriptor, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { switch (connectivity.dimension()) { case 1: { - return StencilBuilder::_build(dynamic_cast<const Connectivity<1>&>(connectivity), number_of_layers, - symmetry_boundary_descriptor_list); + return StencilBuilder::_build(dynamic_cast<const Connectivity<1>&>(connectivity), + stencil_descriptor.numberOfLayers(), symmetry_boundary_descriptor_list); } case 2: { - return StencilBuilder::_build(dynamic_cast<const Connectivity<2>&>(connectivity), number_of_layers, - symmetry_boundary_descriptor_list); + return StencilBuilder::_build(dynamic_cast<const Connectivity<2>&>(connectivity), + stencil_descriptor.numberOfLayers(), symmetry_boundary_descriptor_list); } case 3: { - return StencilBuilder::_build(dynamic_cast<const Connectivity<3>&>(connectivity), number_of_layers, - symmetry_boundary_descriptor_list); + return StencilBuilder::_build(dynamic_cast<const Connectivity<3>&>(connectivity), + stencil_descriptor.numberOfLayers(), symmetry_boundary_descriptor_list); } default: { throw UnexpectedError("invalid connectivity dimension"); diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index aaf5076f1..8b0cc09d1 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -3,6 +3,7 @@ #include <mesh/IBoundaryDescriptor.hpp> #include <mesh/StencilArray.hpp> +#include <mesh/StencilDescriptor.hpp> #include <string> #include <vector> @@ -28,7 +29,7 @@ class StencilBuilder friend class StencilManager; CellToCellStencilArray build(const IConnectivity& connectivity, - size_t number_of_layers, + const StencilDescriptor& stencil_descriptor, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; public: diff --git a/src/mesh/StencilDescriptor.hpp b/src/mesh/StencilDescriptor.hpp new file mode 100644 index 000000000..8dfffd3e5 --- /dev/null +++ b/src/mesh/StencilDescriptor.hpp @@ -0,0 +1,62 @@ +#ifndef STENCIL_DESCRIPTOR_HPP +#define STENCIL_DESCRIPTOR_HPP + +#include <utils/PugsMacros.hpp> + +#include <cstddef> + +class StencilDescriptor +{ + public: + enum class Type : int + { + by_nodes = 1, + by_edges = 2, + by_faces = 3 + }; + + private: + size_t m_number_of_layers; + Type m_type; + + public: + PUGS_INLINE + const size_t& + numberOfLayers() const + { + return m_number_of_layers; + }; + + PUGS_INLINE + const Type& + type() const + { + return m_type; + }; + + PUGS_INLINE + friend bool + operator==(const StencilDescriptor& sd0, const StencilDescriptor& sd1) + { + return (sd0.m_number_of_layers == sd1.m_number_of_layers) and (sd0.m_type == sd1.m_type); + } + + PUGS_INLINE + friend bool + operator!=(const StencilDescriptor& sd0, const StencilDescriptor& sd1) + { + return not(sd0 == sd1); + } + + StencilDescriptor(const size_t number_of_layers, const Type type) : m_number_of_layers{number_of_layers}, m_type{type} + {} + + // StencilDescriptor() : m_number_of_layers(1), m_type{Type::by_nodes} {}; + + StencilDescriptor(const StencilDescriptor&) = default; + StencilDescriptor(StencilDescriptor&&) = default; + + ~StencilDescriptor() = default; +}; + +#endif // STENCIL_DESCRIPTOR_HPP diff --git a/src/mesh/StencilManager.cpp b/src/mesh/StencilManager.cpp index ed1f9ef76..6de72c08a 100644 --- a/src/mesh/StencilManager.cpp +++ b/src/mesh/StencilManager.cpp @@ -18,46 +18,60 @@ StencilManager::destroy() Assert(m_instance != nullptr, "StencilManager was not created"); // LCOV_EXCL_START - if (m_instance->m_stored_cell_to_cell_stencil_map.size() > 0) { - std::stringstream error; - error << ": some connectivities are still registered\n"; - for (const auto& [key, stencil_p] : m_instance->m_stored_cell_to_cell_stencil_map) { - error << " - connectivity of id " << rang::fgB::magenta << key.connectivity_id << rang::style::reset << '\n'; + auto check_still_registered = [](auto& stored_stencil_map) { + if (stored_stencil_map.size() > 0) { + std::stringstream error; + error << ": some connectivities are still registered\n"; + for (const auto& [key, stencil_p] : stored_stencil_map) { + error << " - connectivity of id " << rang::fgB::magenta << key.connectivity_id << rang::style::reset << '\n'; + } + throw UnexpectedError(error.str()); } - throw UnexpectedError(error.str()); - // LCOV_EXCL_STOP - } + }; + + check_still_registered(m_instance->m_stored_cell_to_cell_stencil_map); + check_still_registered(m_instance->m_stored_cell_to_face_stencil_map); + check_still_registered(m_instance->m_stored_node_to_cell_stencil_map); + // LCOV_EXCL_STOP + delete m_instance; m_instance = nullptr; } const CellToCellStencilArray& StencilManager::getCellToCellStencilArray(const IConnectivity& connectivity, - size_t degree, + const StencilDescriptor& stencil_descriptor, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) { if (not m_stored_cell_to_cell_stencil_map.contains( - Key{connectivity.id(), degree, symmetry_boundary_descriptor_list})) { - m_stored_cell_to_cell_stencil_map[Key{connectivity.id(), degree, symmetry_boundary_descriptor_list}] = + Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list})) { + m_stored_cell_to_cell_stencil_map[Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list}] = std::make_shared<CellToCellStencilArray>( - StencilBuilder{}.build(connectivity, degree, symmetry_boundary_descriptor_list)); + StencilBuilder{}.build(connectivity, stencil_descriptor, symmetry_boundary_descriptor_list)); } - return *m_stored_cell_to_cell_stencil_map.at(Key{connectivity.id(), degree, symmetry_boundary_descriptor_list}); + return *m_stored_cell_to_cell_stencil_map.at( + Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list}); } void StencilManager::deleteConnectivity(const size_t connectivity_id) { - bool has_removed = false; - do { - has_removed = false; - for (const auto& [key, p_stencil] : m_stored_cell_to_cell_stencil_map) { - if (connectivity_id == key.connectivity_id) { - m_stored_cell_to_cell_stencil_map.erase(key); - has_removed = true; - break; + auto delete_connectivity_stencil = [&connectivity_id](auto& stored_stencil_map) { + bool has_removed = false; + do { + has_removed = false; + for (const auto& [key, p_stencil] : stored_stencil_map) { + if (connectivity_id == key.connectivity_id) { + stored_stencil_map.erase(key); + has_removed = true; + break; + } } - } - } while (has_removed); + } while (has_removed); + }; + + delete_connectivity_stencil(m_stored_cell_to_cell_stencil_map); + delete_connectivity_stencil(m_stored_cell_to_face_stencil_map); + delete_connectivity_stencil(m_stored_node_to_cell_stencil_map); } diff --git a/src/mesh/StencilManager.hpp b/src/mesh/StencilManager.hpp index de0007631..6deb86536 100644 --- a/src/mesh/StencilManager.hpp +++ b/src/mesh/StencilManager.hpp @@ -4,6 +4,7 @@ #include <mesh/IBoundaryDescriptor.hpp> #include <mesh/IConnectivity.hpp> #include <mesh/StencilArray.hpp> +#include <mesh/StencilDescriptor.hpp> #include <memory> #include <set> @@ -24,13 +25,13 @@ class StencilManager struct Key { size_t connectivity_id; - size_t degree; + StencilDescriptor stencil_descriptor; BoundaryDescriptorList symmetry_boundary_descriptor_list; PUGS_INLINE bool operator==(const Key& k) const { - if ((connectivity_id != k.connectivity_id) or (degree != k.degree) or + if ((connectivity_id != k.connectivity_id) or (stencil_descriptor != k.stencil_descriptor) or (symmetry_boundary_descriptor_list.size() != k.symmetry_boundary_descriptor_list.size())) { return false; } @@ -57,12 +58,20 @@ class StencilManager operator()(const Key& key) const { // We do not use the symmetry boundary set by now - return (std::hash<decltype(Key::connectivity_id)>()(key.connectivity_id)) ^ - (std::hash<decltype(Key::degree)>()(key.degree) << 1); + return ((std::hash<decltype(Key::connectivity_id)>()(key.connectivity_id)) ^ + (std::hash<std::decay_t<decltype(Key::stencil_descriptor.numberOfLayers())>>()( + key.stencil_descriptor.numberOfLayers()) + << 1)); } }; - std::unordered_map<Key, std::shared_ptr<const CellToCellStencilArray>, HashKey> m_stored_cell_to_cell_stencil_map; + template <ItemType source_item_type, ItemType target_item_type> + using StoredStencilTMap = + std::unordered_map<Key, std::shared_ptr<const StencilArray<source_item_type, target_item_type>>, HashKey>; + + StoredStencilTMap<ItemType::cell, ItemType::cell> m_stored_cell_to_cell_stencil_map; + StoredStencilTMap<ItemType::cell, ItemType::face> m_stored_cell_to_face_stencil_map; + StoredStencilTMap<ItemType::node, ItemType::cell> m_stored_node_to_cell_stencil_map; public: static void create(); @@ -80,7 +89,17 @@ class StencilManager const CellToCellStencilArray& getCellToCellStencilArray( const IConnectivity& i_connectivity, - size_t degree = 1, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list = {}); + + const CellToFaceStencilArray& getCellToFaceStencilArray( + const IConnectivity& i_connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list = {}); + + const NodeToCellStencilArray& getNodeToCellStencilArray( + const IConnectivity& i_connectivity, + const StencilDescriptor& stencil_descriptor, const BoundaryDescriptorList& symmetry_boundary_descriptor_list = {}); StencilManager(const StencilManager&) = delete; diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 82f5081a7..76d27b500 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -19,6 +19,7 @@ #include <mesh/MeshDataManager.hpp> #include <mesh/MeshFlatFaceBoundary.hpp> #include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/StencilDescriptor.hpp> #include <mesh/StencilManager.hpp> #include <scheme/DiscreteFunctionDPkVariant.hpp> #include <scheme/DiscreteFunctionUtils.hpp> @@ -770,7 +771,9 @@ PolynomialReconstruction::_build( DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(m_descriptor.degree()); const auto& stencil_array = - StencilManager::instance().getCellToCellStencilArray(mesh.connectivity(), m_descriptor.degree(), + StencilManager::instance().getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{m_descriptor.degree(), + StencilDescriptor::Type::by_nodes}, m_descriptor.symmetryBoundaryDescriptorList()); auto xr = mesh.xr(); diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder.cpp index b68f0883d..b8c6387e1 100644 --- a/tests/test_StencilBuilder.cpp +++ b/tests/test_StencilBuilder.cpp @@ -63,7 +63,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<1>& connectivity = mesh.connectivity(); - auto stencil_array = StencilManager::instance().getCellToCellStencilArray(connectivity); + auto stencil_array = + StencilManager::instance().getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::Type::by_nodes}); REQUIRE(is_valid(connectivity, stencil_array)); } @@ -73,7 +75,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); const Connectivity<1>& connectivity = mesh.connectivity(); - auto stencil_array = StencilManager::instance().getCellToCellStencilArray(connectivity); + auto stencil_array = + StencilManager::instance().getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::Type::by_nodes}); REQUIRE(is_valid(connectivity, stencil_array)); } @@ -86,7 +90,10 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilManager::instance().getCellToCellStencilArray(connectivity))); + REQUIRE(is_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::Type::by_nodes}))); } SECTION("hybrid") @@ -94,7 +101,10 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilManager::instance().getCellToCellStencilArray(connectivity))); + REQUIRE(is_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::Type::by_nodes}))); } } @@ -105,7 +115,10 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilManager::instance().getCellToCellStencilArray(connectivity))); + REQUIRE(is_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::Type::by_nodes}))); } SECTION("hybrid") @@ -113,7 +126,10 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, StencilManager::instance().getCellToCellStencilArray(connectivity))); + REQUIRE(is_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::Type::by_nodes}))); } } } @@ -221,7 +237,10 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<2>& connectivity = mesh.connectivity(); - auto stencil_array = StencilManager::instance().getCellToCellStencilArray(connectivity, 1, list); + auto stencil_array = + StencilManager::instance().getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::Type::by_nodes}, + list); REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); @@ -233,7 +252,10 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - auto stencil = StencilManager::instance().getCellToCellStencilArray(connectivity, 1, list); + auto stencil = + StencilManager::instance().getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::Type::by_nodes}, + list); REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); @@ -256,7 +278,10 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - auto stencil = StencilManager::instance().getCellToCellStencilArray(connectivity, 1, list); + auto stencil = + StencilManager::instance().getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::Type::by_nodes}, + list); REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); @@ -268,7 +293,10 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - auto stencil = StencilManager::instance().getCellToCellStencilArray(connectivity, 1, list); + auto stencil = + StencilManager::instance().getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::Type::by_nodes}, + list); REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); -- GitLab From 94c629d0e6f643359e16369c8a302eb7e2bdf030 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Sat, 12 Oct 2024 01:28:09 +0200 Subject: [PATCH 067/122] Revert MPI_Comm_free placement --- src/utils/Partitioner.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/Partitioner.cpp b/src/utils/Partitioner.cpp index d86f798a3..85e13c22d 100644 --- a/src/utils/Partitioner.cpp +++ b/src/utils/Partitioner.cpp @@ -72,9 +72,10 @@ Partitioner::partition(const CRSGraph& graph) throw UnexpectedError("Metis Error"); } // LCOV_EXCL_STOP + + MPI_Comm_free(&parmetis_comm); } - MPI_Comm_free(&parmetis_comm); MPI_Group_free(&mesh_group); return part; -- GitLab From fd575f169968d1c0bbd8e70f2137a4bf7a42fec9 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Sun, 13 Oct 2024 21:16:55 +0200 Subject: [PATCH 068/122] Remove temporary performances tests --- src/analysis/PyramidGaussQuadrature.cpp | 7 +++++ src/language/modules/SchemeModule.cpp | 24 ----------------- src/scheme/CMakeLists.txt | 4 +-- src/scheme/test_reconstruction.cpp | 35 ------------------------- src/scheme/test_reconstruction.hpp | 14 ---------- 5 files changed, 8 insertions(+), 76 deletions(-) delete mode 100644 src/scheme/test_reconstruction.cpp delete mode 100644 src/scheme/test_reconstruction.hpp diff --git a/src/analysis/PyramidGaussQuadrature.cpp b/src/analysis/PyramidGaussQuadrature.cpp index 679f3ded5..e85a74ac1 100644 --- a/src/analysis/PyramidGaussQuadrature.cpp +++ b/src/analysis/PyramidGaussQuadrature.cpp @@ -24,6 +24,11 @@ PyramidGaussQuadrature::_buildPointAndWeightLists(const size_t degree) const double w = (4. / 3) * unit_weight; + // gcc's bound checking are messed up due to the use of + // std::array and the following dynamic/general switch +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" + switch (id) { case 1: { Assert(value_list.size() == 1); @@ -96,6 +101,8 @@ PyramidGaussQuadrature::_buildPointAndWeightLists(const size_t degree) } // LCOV_EXCL_STOP } + +#pragma GCC diagnostic pop } }; diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index 010247fbe..5ad47feff 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -43,7 +43,6 @@ #include <scheme/OutflowBoundaryConditionDescriptor.hpp> #include <scheme/SymmetryBoundaryConditionDescriptor.hpp> #include <scheme/VariableBCDescriptor.hpp> -#include <scheme/test_reconstruction.hpp> #include <utils/Socket.hpp> #include <memory> @@ -692,29 +691,6 @@ SchemeModule::SchemeModule() )); -#warning REMOVE AFTER TESTS FINISHED - this->_addBuiltinFunction("test_reconstruction", - std::function( - - [](const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& - discrete_function_variant_list) -> void { - test_reconstruction(discrete_function_variant_list); - } - - )); - -#warning REMOVE AFTER TESTS FINISHED - this->_addBuiltinFunction("test_reconstruction", - std::function( - - [](const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& - discrete_function_variant_list, - uint64_t degree) -> void { - test_reconstruction(discrete_function_variant_list, degree); - } - - )); - MathFunctionRegisterForVh{*this}; } diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt index a759fca6a..a52ac4e6c 100644 --- a/src/scheme/CMakeLists.txt +++ b/src/scheme/CMakeLists.txt @@ -3,16 +3,14 @@ add_library( PugsScheme AcousticSolver.cpp - HyperelasticSolver.cpp DiscreteFunctionIntegrator.cpp DiscreteFunctionInterpoler.cpp DiscreteFunctionUtils.cpp DiscreteFunctionVectorIntegrator.cpp DiscreteFunctionVectorInterpoler.cpp FluxingAdvectionSolver.cpp + HyperelasticSolver.cpp PolynomialReconstruction.cpp - - test_reconstruction.cpp ) target_link_libraries( diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp deleted file mode 100644 index 629d1337e..000000000 --- a/src/scheme/test_reconstruction.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include <scheme/test_reconstruction.hpp> - -#include <mesh/NamedBoundaryDescriptor.hpp> -#include <scheme/IntegrationMethodType.hpp> -#include <scheme/PolynomialReconstruction.hpp> -#include <utils/Timer.hpp> - -void -test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, - uint64_t degree) -{ - std::vector descriptor_list = { - PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, - degree, - {std::make_shared<NamedBoundaryDescriptor>("XMIN"), - std::make_shared<NamedBoundaryDescriptor>("XMAX"), - std::make_shared<NamedBoundaryDescriptor>("YMIN"), - std::make_shared<NamedBoundaryDescriptor>("YMAX")}}}; - - for (auto&& descriptor : descriptor_list) { - const size_t nb_loops = 1; - std::cout << "** variable list at once (" << nb_loops << " times) using " << rang::fgB::yellow - << name(descriptor.integrationMethodType()) << rang::fg::reset << "\n"; - - PolynomialReconstruction reconstruction{descriptor}; - Timer t; - for (size_t i = 0; i < nb_loops; ++i) { - auto res = reconstruction.build(discrete_function_variant_list); - } - t.pause(); - std::cout << "t = " << t << '\n'; - } - - std::cout << "finished!\n"; -} diff --git a/src/scheme/test_reconstruction.hpp b/src/scheme/test_reconstruction.hpp deleted file mode 100644 index d2fc5bd03..000000000 --- a/src/scheme/test_reconstruction.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef TEST_RECONSTRUCTION_HPP -#define TEST_RECONSTRUCTION_HPP - -#warning REMOVE AFTER TESTS FINISHED -#include <mesh/MeshVariant.hpp> -#include <scheme/DiscreteFunctionVariant.hpp> - -#include <vector> - -void test_reconstruction( - const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, - uint64_t degree = 1); - -#endif // TEST_RECONSTRUCTION_HPP -- GitLab From b56ab9f8aba8c91f591ab50d446d653d4e13ad18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 14 Oct 2024 12:18:12 +0200 Subject: [PATCH 069/122] Check parallel ghost layers and stencil layers compatibility If the number of required stencil layers is greater than the number of ghost layers in parallel, then the code emits a normal error indicating to increase the number of ghost layers. --- src/mesh/StencilBuilder.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 350492419..d20ebbb5a 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -2,6 +2,7 @@ #include <mesh/Connectivity.hpp> #include <mesh/ItemArray.hpp> +#include <utils/GlobalVariableManager.hpp> #include <utils/Messenger.hpp> #include <set> @@ -113,6 +114,18 @@ StencilBuilder::_build(const ConnectivityType& connectivity, size_t number_of_layers, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { + if ((parallel::size() > 1) and (number_of_layers > GlobalVariableManager::instance().getNumberOfGhostLayers())) { + std::ostringstream error_msg; + error_msg << "Stencil builder requires" << rang::fgB::yellow << number_of_layers << rang::fg::reset + << " layers while parallel number of ghost layer is " + << GlobalVariableManager::instance().getNumberOfGhostLayers() << ".\n"; + error_msg << "Increase the number of ghost layers (using '--number-of-ghost-layers' option)."; + throw NormalError(error_msg.str()); + } + if (number_of_layers > 2) { + throw NotImplementedError("number of layers too large"); + } + if (symmetry_boundary_descriptor_list.size() == 0) { if (number_of_layers == 1) { Array<const uint32_t> row_map = this->_getRowMap(connectivity); @@ -120,13 +133,6 @@ StencilBuilder::_build(const ConnectivityType& connectivity, return {ConnectivityMatrix{row_map, column_indices}, {}}; } else { - if (number_of_layers > 2) { - throw NotImplementedError("number of layers too large"); - } - if (parallel::size() > 1) { - throw NotImplementedError("stencils with more than 1 layers are not defined in parallel"); - } - auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); -- GitLab From ecd69d464ccafcba5a5ca0348692afc81b0fb83f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 14 Oct 2024 12:48:35 +0200 Subject: [PATCH 070/122] Add stencil descriptor to polynomial reconstruction descriptor This is optional by now. It may - become mandatory - evolve to take into account better default value it is deduced from reconstruction degree --- .../PolynomialReconstructionDescriptor.hpp | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/scheme/PolynomialReconstructionDescriptor.hpp b/src/scheme/PolynomialReconstructionDescriptor.hpp index 8a51a8571..ef6c97653 100644 --- a/src/scheme/PolynomialReconstructionDescriptor.hpp +++ b/src/scheme/PolynomialReconstructionDescriptor.hpp @@ -2,6 +2,7 @@ #define POLYNOMIAL_RECONSTRUCTION_DESCRIPTOR_HPP #include <mesh/IBoundaryDescriptor.hpp> +#include <mesh/StencilDescriptor.hpp> #include <scheme/IntegrationMethodType.hpp> #include <utils/PugsMacros.hpp> @@ -17,6 +18,7 @@ class PolynomialReconstructionDescriptor private: IntegrationMethodType m_integration_method; size_t m_degree; + StencilDescriptor m_stencil_descriptor; BoundaryDescriptorList m_symmetry_boundary_descriptor_list; @@ -79,11 +81,34 @@ class PolynomialReconstructionDescriptor m_row_weighting = row_weighting; } + PolynomialReconstructionDescriptor(const IntegrationMethodType integration_method, const size_t degree) + : m_integration_method{integration_method}, + m_degree{degree}, + m_stencil_descriptor(degree, StencilDescriptor::Type::by_nodes) + {} + + PolynomialReconstructionDescriptor(const IntegrationMethodType integration_method, + const size_t degree, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) + : m_integration_method{integration_method}, + m_degree{degree}, + m_stencil_descriptor(degree, StencilDescriptor::Type::by_nodes), + m_symmetry_boundary_descriptor_list(symmetry_boundary_descriptor_list) + {} + + PolynomialReconstructionDescriptor(const IntegrationMethodType integration_method, + const size_t degree, + const StencilDescriptor& stencil_descriptor) + : m_integration_method{integration_method}, m_degree{degree}, m_stencil_descriptor{stencil_descriptor} + {} + PolynomialReconstructionDescriptor(const IntegrationMethodType integration_method, const size_t degree, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list = {}) + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) : m_integration_method{integration_method}, m_degree{degree}, + m_stencil_descriptor{stencil_descriptor}, m_symmetry_boundary_descriptor_list(symmetry_boundary_descriptor_list) {} -- GitLab From 365ed9267ad4b4bec3a4275c95f6cf8cc0ff6b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 14 Oct 2024 17:54:02 +0200 Subject: [PATCH 071/122] Allow specification of stencil type for polynomial reconstruction --- src/mesh/StencilBuilder.cpp | 2 +- src/scheme/PolynomialReconstruction.cpp | 4 +- .../PolynomialReconstructionDescriptor.hpp | 14 +-- ...est_PolynomialReconstructionDescriptor.cpp | 85 +++++++++++++++---- 4 files changed, 79 insertions(+), 26 deletions(-) diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index d20ebbb5a..8af6a76ae 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -119,7 +119,7 @@ StencilBuilder::_build(const ConnectivityType& connectivity, error_msg << "Stencil builder requires" << rang::fgB::yellow << number_of_layers << rang::fg::reset << " layers while parallel number of ghost layer is " << GlobalVariableManager::instance().getNumberOfGhostLayers() << ".\n"; - error_msg << "Increase the number of ghost layers (using '--number-of-ghost-layers' option)."; + error_msg << "Increase the number of ghost layers (using the '--number-of-ghost-layers' option)."; throw NormalError(error_msg.str()); } if (number_of_layers > 2) { diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 76d27b500..2af32e157 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -771,9 +771,7 @@ PolynomialReconstruction::_build( DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(m_descriptor.degree()); const auto& stencil_array = - StencilManager::instance().getCellToCellStencilArray(mesh.connectivity(), - StencilDescriptor{m_descriptor.degree(), - StencilDescriptor::Type::by_nodes}, + StencilManager::instance().getCellToCellStencilArray(mesh.connectivity(), m_descriptor.stencilDescriptor(), m_descriptor.symmetryBoundaryDescriptorList()); auto xr = mesh.xr(); diff --git a/src/scheme/PolynomialReconstructionDescriptor.hpp b/src/scheme/PolynomialReconstructionDescriptor.hpp index ef6c97653..87188f85f 100644 --- a/src/scheme/PolynomialReconstructionDescriptor.hpp +++ b/src/scheme/PolynomialReconstructionDescriptor.hpp @@ -39,6 +39,13 @@ class PolynomialReconstructionDescriptor return m_degree; } + PUGS_INLINE + const StencilDescriptor& + stencilDescriptor() const + { + return m_stencil_descriptor; + } + PUGS_INLINE const BoundaryDescriptorList& symmetryBoundaryDescriptorList() const @@ -60,13 +67,6 @@ class PolynomialReconstructionDescriptor return m_row_weighting; } - PUGS_INLINE - void - setDegree(const size_t degree) - { - m_degree = degree; - } - PUGS_INLINE void setPreconditioning(const bool preconditioning) diff --git a/tests/test_PolynomialReconstructionDescriptor.cpp b/tests/test_PolynomialReconstructionDescriptor.cpp index cadc6aaf4..9fc51a5bf 100644 --- a/tests/test_PolynomialReconstructionDescriptor.cpp +++ b/tests/test_PolynomialReconstructionDescriptor.cpp @@ -2,18 +2,23 @@ #include <catch2/catch_test_macros.hpp> #include <catch2/matchers/catch_matchers_all.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/NumberedBoundaryDescriptor.hpp> #include <scheme/PolynomialReconstructionDescriptor.hpp> // clazy:excludeall=non-pod-global-static TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") { -#warning tests are not completed SECTION("degree 1") { PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 1}; REQUIRE(descriptor.degree() == 1); + REQUIRE(descriptor.stencilDescriptor().numberOfLayers() == 1); + REQUIRE(descriptor.stencilDescriptor().type() == StencilDescriptor::Type::by_nodes); + REQUIRE(descriptor.symmetryBoundaryDescriptorList().size() == 0); + REQUIRE(descriptor.preconditioning() == true); REQUIRE(descriptor.rowWeighting() == true); } @@ -23,32 +28,82 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 4}; REQUIRE(descriptor.degree() == 4); + REQUIRE(descriptor.stencilDescriptor().numberOfLayers() == 4); + REQUIRE(descriptor.stencilDescriptor().type() == StencilDescriptor::Type::by_nodes); + REQUIRE(descriptor.symmetryBoundaryDescriptorList().size() == 0); + REQUIRE(descriptor.preconditioning() == true); REQUIRE(descriptor.rowWeighting() == true); } - SECTION("utlities") + SECTION("degree and stencil") { - PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 3}; + StencilDescriptor sd{2, StencilDescriptor::Type::by_faces}; + + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 1, sd}; + + REQUIRE(descriptor.degree() == 1); + REQUIRE(descriptor.stencilDescriptor().numberOfLayers() == 2); + REQUIRE(descriptor.stencilDescriptor().type() == StencilDescriptor::Type::by_faces); + REQUIRE(descriptor.symmetryBoundaryDescriptorList().size() == 0); - REQUIRE(descriptor.degree() == 3); REQUIRE(descriptor.preconditioning() == true); REQUIRE(descriptor.rowWeighting() == true); + } - SECTION("set degree") - { - descriptor.setDegree(2); + SECTION("degree and symmetries") + { + std::vector<std::shared_ptr<const IBoundaryDescriptor>> bc_list; + bc_list.push_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + bc_list.push_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + bc_list.push_back(std::make_shared<NumberedBoundaryDescriptor>(2)); - REQUIRE(descriptor.degree() == 2); - REQUIRE(descriptor.preconditioning() == true); - REQUIRE(descriptor.rowWeighting() == true); + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 2, bc_list}; - descriptor.setDegree(4); + REQUIRE(descriptor.degree() == 2); + REQUIRE(descriptor.stencilDescriptor().numberOfLayers() == 2); + REQUIRE(descriptor.stencilDescriptor().type() == StencilDescriptor::Type::by_nodes); + REQUIRE(descriptor.symmetryBoundaryDescriptorList().size() == 3); - REQUIRE(descriptor.degree() == 4); - REQUIRE(descriptor.preconditioning() == true); - REQUIRE(descriptor.rowWeighting() == true); - } + REQUIRE(descriptor.symmetryBoundaryDescriptorList()[0]->type() == IBoundaryDescriptor::Type::named); + REQUIRE(descriptor.symmetryBoundaryDescriptorList()[1]->type() == IBoundaryDescriptor::Type::named); + REQUIRE(descriptor.symmetryBoundaryDescriptorList()[2]->type() == IBoundaryDescriptor::Type::numbered); + + REQUIRE(descriptor.preconditioning() == true); + REQUIRE(descriptor.rowWeighting() == true); + } + + SECTION("degree, stencil and symmetries") + { + StencilDescriptor sd{3, StencilDescriptor::Type::by_edges}; + + std::vector<std::shared_ptr<const IBoundaryDescriptor>> bc_list; + bc_list.push_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + bc_list.push_back(std::make_shared<NumberedBoundaryDescriptor>(2)); + bc_list.push_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 1, sd, bc_list}; + + REQUIRE(descriptor.degree() == 1); + REQUIRE(descriptor.stencilDescriptor().numberOfLayers() == 3); + REQUIRE(descriptor.stencilDescriptor().type() == StencilDescriptor::Type::by_edges); + REQUIRE(descriptor.symmetryBoundaryDescriptorList().size() == 3); + + REQUIRE(descriptor.symmetryBoundaryDescriptorList()[0]->type() == IBoundaryDescriptor::Type::named); + REQUIRE(descriptor.symmetryBoundaryDescriptorList()[1]->type() == IBoundaryDescriptor::Type::numbered); + REQUIRE(descriptor.symmetryBoundaryDescriptorList()[2]->type() == IBoundaryDescriptor::Type::named); + + REQUIRE(descriptor.preconditioning() == true); + REQUIRE(descriptor.rowWeighting() == true); + } + + SECTION("utlities") + { + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 3}; + + REQUIRE(descriptor.degree() == 3); + REQUIRE(descriptor.preconditioning() == true); + REQUIRE(descriptor.rowWeighting() == true); SECTION("set preconditioning") { -- GitLab From a7a13955348d7d466ec8bae08f51510521119e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 22 Oct 2024 11:13:59 +0200 Subject: [PATCH 072/122] Fix NodeToCellStencilArray type definition --- src/mesh/StencilArray.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/StencilArray.hpp b/src/mesh/StencilArray.hpp index 273e38445..9571c346b 100644 --- a/src/mesh/StencilArray.hpp +++ b/src/mesh/StencilArray.hpp @@ -100,6 +100,6 @@ class StencilArray using CellToCellStencilArray = StencilArray<ItemType::cell, ItemType::cell>; using CellToFaceStencilArray = StencilArray<ItemType::cell, ItemType::face>; -using NodeToCellStencilArray = StencilArray<ItemType::cell, ItemType::node>; +using NodeToCellStencilArray = StencilArray<ItemType::node, ItemType::cell>; #endif // STENCIL_ARRAY_HPP -- GitLab From a61e7780802bbb2e66769ce2028490c09e417e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 22 Oct 2024 11:16:38 +0200 Subject: [PATCH 073/122] Generalize managed stencil types and begin new stencil building --- src/mesh/StencilBuilder.cpp | 37 ++++++++++++++++++--------- src/mesh/StencilBuilder.hpp | 47 +++++++++++++++++++++++++++++----- src/mesh/StencilManager.cpp | 50 +++++++++++++++++++++++++++++++------ src/mesh/StencilManager.hpp | 7 ++++++ 4 files changed, 115 insertions(+), 26 deletions(-) diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 8af6a76ae..d70ea9534 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -110,9 +110,9 @@ StencilBuilder::_getColumnIndices(const ConnectivityType& connectivity, const Ar template <typename ConnectivityType> CellToCellStencilArray -StencilBuilder::_build(const ConnectivityType& connectivity, - size_t number_of_layers, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +StencilBuilder::_buildC2C(const ConnectivityType& connectivity, + size_t number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { if ((parallel::size() > 1) and (number_of_layers > GlobalVariableManager::instance().getNumberOfGhostLayers())) { std::ostringstream error_msg; @@ -122,6 +122,7 @@ StencilBuilder::_build(const ConnectivityType& connectivity, error_msg << "Increase the number of ghost layers (using the '--number-of-ghost-layers' option)."; throw NormalError(error_msg.str()); } + if (number_of_layers > 2) { throw NotImplementedError("number of layers too large"); } @@ -340,25 +341,37 @@ StencilBuilder::_build(const ConnectivityType& connectivity, } CellToCellStencilArray -StencilBuilder::build(const IConnectivity& connectivity, - const StencilDescriptor& stencil_descriptor, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +StencilBuilder::buildC2C(const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { switch (connectivity.dimension()) { case 1: { - return StencilBuilder::_build(dynamic_cast<const Connectivity<1>&>(connectivity), - stencil_descriptor.numberOfLayers(), symmetry_boundary_descriptor_list); + return StencilBuilder::_buildC2C(dynamic_cast<const Connectivity<1>&>(connectivity), + stencil_descriptor.numberOfLayers(), symmetry_boundary_descriptor_list); } case 2: { - return StencilBuilder::_build(dynamic_cast<const Connectivity<2>&>(connectivity), - stencil_descriptor.numberOfLayers(), symmetry_boundary_descriptor_list); + return StencilBuilder::_buildC2C(dynamic_cast<const Connectivity<2>&>(connectivity), + stencil_descriptor.numberOfLayers(), symmetry_boundary_descriptor_list); } case 3: { - return StencilBuilder::_build(dynamic_cast<const Connectivity<3>&>(connectivity), - stencil_descriptor.numberOfLayers(), symmetry_boundary_descriptor_list); + return StencilBuilder::_buildC2C(dynamic_cast<const Connectivity<3>&>(connectivity), + stencil_descriptor.numberOfLayers(), symmetry_boundary_descriptor_list); } default: { throw UnexpectedError("invalid connectivity dimension"); } } } + +CellToFaceStencilArray +StencilBuilder::buildC2F(const IConnectivity&, const StencilDescriptor&, const BoundaryDescriptorList&) const +{ + throw NotImplementedError("cell to face stencil"); +} + +NodeToCellStencilArray +StencilBuilder::buildN2C(const IConnectivity&, const StencilDescriptor&, const BoundaryDescriptorList&) const +{ + throw NotImplementedError("node to cell stencil"); +} diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index 8b0cc09d1..b930ca4cc 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -23,14 +23,49 @@ class StencilBuilder const Array<const uint32_t>& row_map) const; template <typename ConnectivityType> - CellToCellStencilArray _build(const ConnectivityType& connectivity, - size_t number_of_layers, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + CellToCellStencilArray _buildC2C(const ConnectivityType& connectivity, + size_t number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + + template <typename ConnectivityType> + CellToFaceStencilArray _buildC2F(const ConnectivityType& connectivity, + size_t number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + + template <typename ConnectivityType> + NodeToCellStencilArray _buildN2C(const ConnectivityType& connectivity, + size_t number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + + CellToCellStencilArray buildC2C(const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + + CellToFaceStencilArray buildC2F(const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + + NodeToCellStencilArray buildN2C(const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; friend class StencilManager; - CellToCellStencilArray build(const IConnectivity& connectivity, - const StencilDescriptor& stencil_descriptor, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + template <ItemType source_item_type, ItemType target_item_type> + StencilArray<source_item_type, target_item_type> + build(const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const + { + if constexpr ((source_item_type == ItemType::cell) and (target_item_type == ItemType::cell)) { + return buildC2C(connectivity, stencil_descriptor, symmetry_boundary_descriptor_list); + } else if constexpr ((source_item_type == ItemType::cell) and (target_item_type == ItemType::face)) { + return buildC2F(connectivity, stencil_descriptor, symmetry_boundary_descriptor_list); + } else if constexpr ((source_item_type == ItemType::node) and (target_item_type == ItemType::cell)) { + return buildN2C(connectivity, stencil_descriptor, symmetry_boundary_descriptor_list); + } else { + static_assert(is_false_item_type_v<source_item_type>, "invalid stencil type"); + } + } public: StencilBuilder() = default; diff --git a/src/mesh/StencilManager.cpp b/src/mesh/StencilManager.cpp index 6de72c08a..ef1d9ef1f 100644 --- a/src/mesh/StencilManager.cpp +++ b/src/mesh/StencilManager.cpp @@ -38,20 +38,54 @@ StencilManager::destroy() m_instance = nullptr; } +template <ItemType source_item_type, ItemType target_item_type> +const StencilArray<source_item_type, target_item_type>& +StencilManager::_getStencilArray( + const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list, + StoredStencilTMap<source_item_type, target_item_type>& stored_source_to_target_stencil_map) +{ + if (not stored_source_to_target_stencil_map.contains( + Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list})) { + stored_source_to_target_stencil_map[Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list}] = + std::make_shared<StencilArray<source_item_type, target_item_type>>( + StencilBuilder{}.template build<source_item_type, target_item_type>(connectivity, stencil_descriptor, + symmetry_boundary_descriptor_list)); + } + + return *stored_source_to_target_stencil_map.at( + Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list}); +} + const CellToCellStencilArray& StencilManager::getCellToCellStencilArray(const IConnectivity& connectivity, const StencilDescriptor& stencil_descriptor, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) { - if (not m_stored_cell_to_cell_stencil_map.contains( - Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list})) { - m_stored_cell_to_cell_stencil_map[Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list}] = - std::make_shared<CellToCellStencilArray>( - StencilBuilder{}.build(connectivity, stencil_descriptor, symmetry_boundary_descriptor_list)); - } + return this->_getStencilArray<ItemType::cell, ItemType::cell>(connectivity, stencil_descriptor, + symmetry_boundary_descriptor_list, + m_stored_cell_to_cell_stencil_map); +} - return *m_stored_cell_to_cell_stencil_map.at( - Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list}); +const CellToFaceStencilArray& +StencilManager::getCellToFaceStencilArray(const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) +{ + return this->_getStencilArray<ItemType::cell, ItemType::face>(connectivity, stencil_descriptor, + symmetry_boundary_descriptor_list, + m_stored_cell_to_face_stencil_map); +} + +const NodeToCellStencilArray& +StencilManager::getNodeToCellStencilArray(const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) +{ + return this->_getStencilArray<ItemType::node, ItemType::cell>(connectivity, stencil_descriptor, + symmetry_boundary_descriptor_list, + m_stored_node_to_cell_stencil_map); } void diff --git a/src/mesh/StencilManager.hpp b/src/mesh/StencilManager.hpp index 6deb86536..c68e7fe06 100644 --- a/src/mesh/StencilManager.hpp +++ b/src/mesh/StencilManager.hpp @@ -73,6 +73,13 @@ class StencilManager StoredStencilTMap<ItemType::cell, ItemType::face> m_stored_cell_to_face_stencil_map; StoredStencilTMap<ItemType::node, ItemType::cell> m_stored_node_to_cell_stencil_map; + template <ItemType source_item_type, ItemType target_item_type> + const StencilArray<source_item_type, target_item_type>& _getStencilArray( + const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list, + StoredStencilTMap<source_item_type, target_item_type>& stored_source_to_target_stencil_map); + public: static void create(); static void destroy(); -- GitLab From b240d9b4e88593bc313d6d07087bd35de27448bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 22 Oct 2024 19:15:17 +0200 Subject: [PATCH 074/122] Add symmetry stencil for 1D and continue generalization and cleanup --- src/mesh/StencilBuilder.cpp | 357 +++++++++++------- src/mesh/StencilBuilder.hpp | 19 + src/mesh/StencilDescriptor.hpp | 20 +- .../PolynomialReconstructionDescriptor.hpp | 4 +- ...est_PolynomialReconstructionDescriptor.cpp | 14 +- tests/test_StencilBuilder.cpp | 106 ++++-- 6 files changed, 342 insertions(+), 178 deletions(-) diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index d70ea9534..1880c0493 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -7,6 +7,87 @@ #include <set> +template <typename ConnectivityType> +auto +StencilBuilder::_buildSymmetryNodeList(const ConnectivityType& connectivity, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +{ + static_assert(ConnectivityType::Dimension > 1); + NodeArray<bool> symmetry_node_list{connectivity, symmetry_boundary_descriptor_list.size()}; + symmetry_node_list.fill(false); + + auto face_to_node_matrix = connectivity.faceToNodeMatrix(); + size_t i_symmetry_boundary = 0; + for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { + const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; + + bool found = false; + for (size_t i_ref_node_list = 0; i_ref_node_list < connectivity.template numberOfRefItemList<ItemType::face>(); + ++i_ref_node_list) { + const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i_ref_node_list); + if (ref_face_list.refId() == boundary_descriptor) { + found = true; + for (size_t i_face = 0; i_face < ref_face_list.list().size(); ++i_face) { + const FaceId face_id = ref_face_list.list()[i_face]; + auto node_list = face_to_node_matrix[face_id]; + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + + symmetry_node_list[node_id][i_symmetry_boundary] = true; + } + } + break; + } + } + ++i_symmetry_boundary; + if (not found) { + std::ostringstream error_msg; + error_msg << "cannot find boundary '" << rang::fgB::yellow << boundary_descriptor << rang::fg::reset << '\''; + throw NormalError(error_msg.str()); + } + } + + return symmetry_node_list; +} + +template <> +auto +StencilBuilder::_buildSymmetryNodeList(const Connectivity<1>& connectivity, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +{ + NodeArray<bool> symmetry_node_list{connectivity, symmetry_boundary_descriptor_list.size()}; + symmetry_node_list.fill(false); + + size_t i_symmetry_boundary = 0; + for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { + const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; + + bool found = false; + for (size_t i_ref_node_list = 0; i_ref_node_list < connectivity.template numberOfRefItemList<ItemType::node>(); + ++i_ref_node_list) { + const auto& ref_node_list = connectivity.template refItemList<ItemType::node>(i_ref_node_list); + if (ref_node_list.refId() == boundary_descriptor) { + found = true; + auto node_list = ref_node_list.list(); + for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { + const NodeId node_id = node_list[i_node]; + + symmetry_node_list[node_id][i_symmetry_boundary] = true; + } + break; + } + } + ++i_symmetry_boundary; + if (not found) { + std::ostringstream error_msg; + error_msg << "cannot find boundary '" << rang::fgB::yellow << boundary_descriptor << rang::fg::reset << '\''; + throw NormalError(error_msg.str()); + } + } + + return symmetry_node_list; +} + template <typename ConnectivityType> Array<const uint32_t> StencilBuilder::_getRowMap(const ConnectivityType& connectivity) const @@ -192,151 +273,173 @@ StencilBuilder::_buildC2C(const ConnectivityType& connectivity, return {primal_stencil, {}}; } } else { - if constexpr (ConnectivityType::Dimension > 1) { - std::vector<Array<const FaceId>> boundary_node_list; + NodeArray<bool> symmetry_node_list = this->_buildSymmetryNodeList(connectivity, symmetry_boundary_descriptor_list); - NodeArray<bool> symmetry_node_list(connectivity, symmetry_boundary_descriptor_list.size()); - symmetry_node_list.fill(0); + auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); + auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); - auto face_to_node_matrix = connectivity.faceToNodeMatrix(); - auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); - auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + auto cell_is_owned = connectivity.cellIsOwned(); + auto cell_number = connectivity.cellNumber(); - { - size_t i_symmetry_boundary = 0; - for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { - const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; - - bool found = false; - for (size_t i_ref_node_list = 0; - i_ref_node_list < connectivity.template numberOfRefItemList<ItemType::face>(); ++i_ref_node_list) { - const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i_ref_node_list); - if (ref_face_list.refId() == boundary_descriptor) { - found = true; - boundary_node_list.push_back(ref_face_list.list()); - for (size_t i_face = 0; i_face < ref_face_list.list().size(); ++i_face) { - const FaceId face_id = ref_face_list.list()[i_face]; - auto node_list = face_to_node_matrix[face_id]; - for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { - const NodeId node_id = node_list[i_node]; - - symmetry_node_list[node_id][i_symmetry_boundary] = true; - } - } - break; + Array<uint32_t> row_map{connectivity.numberOfCells() + 1}; + row_map[0] = 0; + std::vector<Array<uint32_t>> symmetry_row_map_list(symmetry_boundary_descriptor_list.size()); + for (auto&& symmetry_row_map : symmetry_row_map_list) { + symmetry_row_map = Array<uint32_t>{connectivity.numberOfCells() + 1}; + symmetry_row_map[0] = 0; + } + + std::vector<uint32_t> column_indices_vector; + std::vector<std::vector<uint32_t>> symmetry_column_indices_vector(symmetry_boundary_descriptor_list.size()); + + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + std::set<CellId> cell_set; + std::vector<std::set<CellId>> by_boundary_symmetry_cell(symmetry_boundary_descriptor_list.size()); + + if (cell_is_owned[cell_id]) { + auto cell_node_list = cell_to_node_matrix[cell_id]; + for (size_t i_cell_node = 0; i_cell_node < cell_node_list.size(); ++i_cell_node) { + const NodeId cell_node_id = cell_node_list[i_cell_node]; + auto node_cell_list = node_to_cell_matrix[cell_node_id]; + for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { + const CellId node_cell_id = node_cell_list[i_node_cell]; + if (cell_id != node_cell_id) { + cell_set.insert(node_cell_id); } } - ++i_symmetry_boundary; - if (not found) { - std::ostringstream error_msg; - error_msg << "cannot find boundary '" << rang::fgB::yellow << boundary_descriptor << rang::fg::reset - << '\''; - throw NormalError(error_msg.str()); - } } - } - auto cell_is_owned = connectivity.cellIsOwned(); - auto cell_number = connectivity.cellNumber(); - - Array<uint32_t> row_map{connectivity.numberOfCells() + 1}; - row_map[0] = 0; - std::vector<Array<uint32_t>> symmetry_row_map_list(symmetry_boundary_descriptor_list.size()); - for (auto&& symmetry_row_map : symmetry_row_map_list) { - symmetry_row_map = Array<uint32_t>{connectivity.numberOfCells() + 1}; - symmetry_row_map[0] = 0; - } - - std::vector<uint32_t> column_indices_vector; - std::vector<std::vector<uint32_t>> symmetry_column_indices_vector(symmetry_boundary_descriptor_list.size()); + { + std::vector<CellId> cell_vector; + for (auto&& set_cell_id : cell_set) { + cell_vector.push_back(set_cell_id); + } + std::sort(cell_vector.begin(), cell_vector.end(), + [&cell_number](const CellId& cell0_id, const CellId& cell1_id) { + return cell_number[cell0_id] < cell_number[cell1_id]; + }); - for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { - std::set<CellId> cell_set; - std::vector<std::set<CellId>> by_boundary_symmetry_cell(symmetry_boundary_descriptor_list.size()); + for (auto&& vector_cell_id : cell_vector) { + column_indices_vector.push_back(vector_cell_id); + } + } - if (cell_is_owned[cell_id]) { - auto cell_node_list = cell_to_node_matrix[cell_id]; + for (size_t i = 0; i < symmetry_boundary_descriptor_list.size(); ++i) { + std::set<CellId> symmetry_cell_set; for (size_t i_cell_node = 0; i_cell_node < cell_node_list.size(); ++i_cell_node) { const NodeId cell_node_id = cell_node_list[i_cell_node]; - auto node_cell_list = node_to_cell_matrix[cell_node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { - const CellId node_cell_id = node_cell_list[i_node_cell]; - if (cell_id != node_cell_id) { - cell_set.insert(node_cell_id); + if (symmetry_node_list[cell_node_id][i]) { + auto node_cell_list = node_to_cell_matrix[cell_node_id]; + for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { + const CellId node_cell_id = node_cell_list[i_node_cell]; + symmetry_cell_set.insert(node_cell_id); } } } + by_boundary_symmetry_cell[i] = symmetry_cell_set; - { - std::vector<CellId> cell_vector; - for (auto&& set_cell_id : cell_set) { - cell_vector.push_back(set_cell_id); - } - std::sort(cell_vector.begin(), cell_vector.end(), - [&cell_number](const CellId& cell0_id, const CellId& cell1_id) { - return cell_number[cell0_id] < cell_number[cell1_id]; - }); - - for (auto&& vector_cell_id : cell_vector) { - column_indices_vector.push_back(vector_cell_id); - } + std::vector<CellId> cell_vector; + for (auto&& set_cell_id : symmetry_cell_set) { + cell_vector.push_back(set_cell_id); } + std::sort(cell_vector.begin(), cell_vector.end(), + [&cell_number](const CellId& cell0_id, const CellId& cell1_id) { + return cell_number[cell0_id] < cell_number[cell1_id]; + }); - for (size_t i = 0; i < symmetry_boundary_descriptor_list.size(); ++i) { - std::set<CellId> symmetry_cell_set; - for (size_t i_cell_node = 0; i_cell_node < cell_node_list.size(); ++i_cell_node) { - const NodeId cell_node_id = cell_node_list[i_cell_node]; - if (symmetry_node_list[cell_node_id][i]) { - auto node_cell_list = node_to_cell_matrix[cell_node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { - const CellId node_cell_id = node_cell_list[i_node_cell]; - symmetry_cell_set.insert(node_cell_id); - } - } - } - by_boundary_symmetry_cell[i] = symmetry_cell_set; - - std::vector<CellId> cell_vector; - for (auto&& set_cell_id : symmetry_cell_set) { - cell_vector.push_back(set_cell_id); - } - std::sort(cell_vector.begin(), cell_vector.end(), - [&cell_number](const CellId& cell0_id, const CellId& cell1_id) { - return cell_number[cell0_id] < cell_number[cell1_id]; - }); - - for (auto&& vector_cell_id : cell_vector) { - symmetry_column_indices_vector[i].push_back(vector_cell_id); - } + for (auto&& vector_cell_id : cell_vector) { + symmetry_column_indices_vector[i].push_back(vector_cell_id); } } - row_map[cell_id + 1] = row_map[cell_id] + cell_set.size(); + } + row_map[cell_id + 1] = row_map[cell_id] + cell_set.size(); - for (size_t i = 0; i < symmetry_row_map_list.size(); ++i) { - symmetry_row_map_list[i][cell_id + 1] = - symmetry_row_map_list[i][cell_id] + by_boundary_symmetry_cell[i].size(); - } + for (size_t i = 0; i < symmetry_row_map_list.size(); ++i) { + symmetry_row_map_list[i][cell_id + 1] = symmetry_row_map_list[i][cell_id] + by_boundary_symmetry_cell[i].size(); } - ConnectivityMatrix primal_stencil{row_map, convert_to_array(column_indices_vector)}; - - CellToCellStencilArray::BoundaryDescriptorStencilArrayList symmetry_boundary_stencil_list; - { - size_t i = 0; - for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { - symmetry_boundary_stencil_list.emplace_back( - CellToCellStencilArray:: - BoundaryDescriptorStencilArray{p_boundary_descriptor, - ConnectivityMatrix{symmetry_row_map_list[i], - convert_to_array(symmetry_column_indices_vector[i])}}); - ++i; - } + } + ConnectivityMatrix primal_stencil{row_map, convert_to_array(column_indices_vector)}; + + CellToCellStencilArray::BoundaryDescriptorStencilArrayList symmetry_boundary_stencil_list; + { + size_t i = 0; + for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { + symmetry_boundary_stencil_list.emplace_back( + CellToCellStencilArray:: + BoundaryDescriptorStencilArray{p_boundary_descriptor, + ConnectivityMatrix{symmetry_row_map_list[i], + convert_to_array(symmetry_column_indices_vector[i])}}); + ++i; } + } - return {{primal_stencil}, {symmetry_boundary_stencil_list}}; + return {{primal_stencil}, {symmetry_boundary_stencil_list}}; + } +} - } else { - throw NotImplementedError("Only implemented in 2D/3D"); - } +template <> +CellToCellStencilArray +StencilBuilder::_buildC2C(const Connectivity<1>& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +{ + if (stencil_descriptor.connectionType() != StencilDescriptor::ConnectionType::by_cells) { + return StencilBuilder::_buildC2C(connectivity, stencil_descriptor.numberOfLayers(), + symmetry_boundary_descriptor_list); + } else { + throw UnexpectedError("Cannot build cell to cell stencil using cell connectivity"); + } +} + +template <> +CellToCellStencilArray +StencilBuilder::_buildC2C(const Connectivity<2>& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +{ + switch (stencil_descriptor.connectionType()) { + case StencilDescriptor::ConnectionType::by_nodes: { + return StencilBuilder::_buildC2C(connectivity, stencil_descriptor.numberOfLayers(), + symmetry_boundary_descriptor_list); + } + case StencilDescriptor::ConnectionType::by_edges: + case StencilDescriptor::ConnectionType::by_faces: { + throw NotImplementedError("non by_node stencil"); + } + case StencilDescriptor::ConnectionType::by_cells: { + throw UnexpectedError("Cannot build cell to cell stencil using cell connectivity"); + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("invalid connection type"); + } + // LCOV_EXCL_STOP + } +} + +template <> +CellToCellStencilArray +StencilBuilder::_buildC2C(const Connectivity<3>& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +{ + switch (stencil_descriptor.connectionType()) { + case StencilDescriptor::ConnectionType::by_nodes: { + return StencilBuilder::_buildC2C(connectivity, stencil_descriptor.numberOfLayers(), + symmetry_boundary_descriptor_list); + } + case StencilDescriptor::ConnectionType::by_edges: + case StencilDescriptor::ConnectionType::by_faces: { + throw NotImplementedError("non by_node stencil"); + } + case StencilDescriptor::ConnectionType::by_cells: { + throw UnexpectedError("Cannot build cell to cell stencil using cell connectivity"); + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("invalid connection type"); + } + // LCOV_EXCL_STOP } } @@ -347,16 +450,16 @@ StencilBuilder::buildC2C(const IConnectivity& connectivity, { switch (connectivity.dimension()) { case 1: { - return StencilBuilder::_buildC2C(dynamic_cast<const Connectivity<1>&>(connectivity), - stencil_descriptor.numberOfLayers(), symmetry_boundary_descriptor_list); + return StencilBuilder::_buildC2C(dynamic_cast<const Connectivity<1>&>(connectivity), stencil_descriptor, + symmetry_boundary_descriptor_list); } case 2: { - return StencilBuilder::_buildC2C(dynamic_cast<const Connectivity<2>&>(connectivity), - stencil_descriptor.numberOfLayers(), symmetry_boundary_descriptor_list); + return StencilBuilder::_buildC2C(dynamic_cast<const Connectivity<2>&>(connectivity), stencil_descriptor, + symmetry_boundary_descriptor_list); } case 3: { - return StencilBuilder::_buildC2C(dynamic_cast<const Connectivity<3>&>(connectivity), - stencil_descriptor.numberOfLayers(), symmetry_boundary_descriptor_list); + return StencilBuilder::_buildC2C(dynamic_cast<const Connectivity<3>&>(connectivity), stencil_descriptor, + symmetry_boundary_descriptor_list); } default: { throw UnexpectedError("invalid connectivity dimension"); diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index b930ca4cc..464a7fc27 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -9,6 +9,7 @@ #include <vector> class IConnectivity; + class StencilBuilder { public: @@ -22,6 +23,24 @@ class StencilBuilder Array<const uint32_t> _getColumnIndices(const ConnectivityType& connectivity, const Array<const uint32_t>& row_map) const; + template <typename ConnectivityType> + auto _buildSymmetryNodeList(const ConnectivityType& connectivity, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + + template <typename ConnectivityType, + ItemType source_item_type, + ItemType connecting_item_type, + ItemType target_item_type> + StencilArray<source_item_type, target_item_type> _build( + const ConnectivityType& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + + template <typename ConnectivityType> + CellToCellStencilArray _buildC2C(const ConnectivityType& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + template <typename ConnectivityType> CellToCellStencilArray _buildC2C(const ConnectivityType& connectivity, size_t number_of_layers, diff --git a/src/mesh/StencilDescriptor.hpp b/src/mesh/StencilDescriptor.hpp index 8dfffd3e5..d099f0086 100644 --- a/src/mesh/StencilDescriptor.hpp +++ b/src/mesh/StencilDescriptor.hpp @@ -8,16 +8,17 @@ class StencilDescriptor { public: - enum class Type : int + enum class ConnectionType : int { by_nodes = 1, by_edges = 2, - by_faces = 3 + by_faces = 3, + by_cells = 4 }; private: size_t m_number_of_layers; - Type m_type; + ConnectionType m_connection_type; public: PUGS_INLINE @@ -28,17 +29,17 @@ class StencilDescriptor }; PUGS_INLINE - const Type& - type() const + const ConnectionType& + connectionType() const { - return m_type; + return m_connection_type; }; PUGS_INLINE friend bool operator==(const StencilDescriptor& sd0, const StencilDescriptor& sd1) { - return (sd0.m_number_of_layers == sd1.m_number_of_layers) and (sd0.m_type == sd1.m_type); + return (sd0.m_number_of_layers == sd1.m_number_of_layers) and (sd0.m_connection_type == sd1.m_connection_type); } PUGS_INLINE @@ -48,11 +49,10 @@ class StencilDescriptor return not(sd0 == sd1); } - StencilDescriptor(const size_t number_of_layers, const Type type) : m_number_of_layers{number_of_layers}, m_type{type} + StencilDescriptor(const size_t number_of_layers, const ConnectionType type) + : m_number_of_layers{number_of_layers}, m_connection_type{type} {} - // StencilDescriptor() : m_number_of_layers(1), m_type{Type::by_nodes} {}; - StencilDescriptor(const StencilDescriptor&) = default; StencilDescriptor(StencilDescriptor&&) = default; diff --git a/src/scheme/PolynomialReconstructionDescriptor.hpp b/src/scheme/PolynomialReconstructionDescriptor.hpp index 87188f85f..44fdd2082 100644 --- a/src/scheme/PolynomialReconstructionDescriptor.hpp +++ b/src/scheme/PolynomialReconstructionDescriptor.hpp @@ -84,7 +84,7 @@ class PolynomialReconstructionDescriptor PolynomialReconstructionDescriptor(const IntegrationMethodType integration_method, const size_t degree) : m_integration_method{integration_method}, m_degree{degree}, - m_stencil_descriptor(degree, StencilDescriptor::Type::by_nodes) + m_stencil_descriptor(degree, StencilDescriptor::ConnectionType::by_nodes) {} PolynomialReconstructionDescriptor(const IntegrationMethodType integration_method, @@ -92,7 +92,7 @@ class PolynomialReconstructionDescriptor const BoundaryDescriptorList& symmetry_boundary_descriptor_list) : m_integration_method{integration_method}, m_degree{degree}, - m_stencil_descriptor(degree, StencilDescriptor::Type::by_nodes), + m_stencil_descriptor(degree, StencilDescriptor::ConnectionType::by_nodes), m_symmetry_boundary_descriptor_list(symmetry_boundary_descriptor_list) {} diff --git a/tests/test_PolynomialReconstructionDescriptor.cpp b/tests/test_PolynomialReconstructionDescriptor.cpp index 9fc51a5bf..32d3c4af1 100644 --- a/tests/test_PolynomialReconstructionDescriptor.cpp +++ b/tests/test_PolynomialReconstructionDescriptor.cpp @@ -16,7 +16,7 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") REQUIRE(descriptor.degree() == 1); REQUIRE(descriptor.stencilDescriptor().numberOfLayers() == 1); - REQUIRE(descriptor.stencilDescriptor().type() == StencilDescriptor::Type::by_nodes); + REQUIRE(descriptor.stencilDescriptor().connectionType() == StencilDescriptor::ConnectionType::by_nodes); REQUIRE(descriptor.symmetryBoundaryDescriptorList().size() == 0); REQUIRE(descriptor.preconditioning() == true); @@ -29,7 +29,7 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") REQUIRE(descriptor.degree() == 4); REQUIRE(descriptor.stencilDescriptor().numberOfLayers() == 4); - REQUIRE(descriptor.stencilDescriptor().type() == StencilDescriptor::Type::by_nodes); + REQUIRE(descriptor.stencilDescriptor().connectionType() == StencilDescriptor::ConnectionType::by_nodes); REQUIRE(descriptor.symmetryBoundaryDescriptorList().size() == 0); REQUIRE(descriptor.preconditioning() == true); @@ -38,13 +38,13 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") SECTION("degree and stencil") { - StencilDescriptor sd{2, StencilDescriptor::Type::by_faces}; + StencilDescriptor sd{2, StencilDescriptor::ConnectionType::by_faces}; PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, 1, sd}; REQUIRE(descriptor.degree() == 1); REQUIRE(descriptor.stencilDescriptor().numberOfLayers() == 2); - REQUIRE(descriptor.stencilDescriptor().type() == StencilDescriptor::Type::by_faces); + REQUIRE(descriptor.stencilDescriptor().connectionType() == StencilDescriptor::ConnectionType::by_faces); REQUIRE(descriptor.symmetryBoundaryDescriptorList().size() == 0); REQUIRE(descriptor.preconditioning() == true); @@ -62,7 +62,7 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") REQUIRE(descriptor.degree() == 2); REQUIRE(descriptor.stencilDescriptor().numberOfLayers() == 2); - REQUIRE(descriptor.stencilDescriptor().type() == StencilDescriptor::Type::by_nodes); + REQUIRE(descriptor.stencilDescriptor().connectionType() == StencilDescriptor::ConnectionType::by_nodes); REQUIRE(descriptor.symmetryBoundaryDescriptorList().size() == 3); REQUIRE(descriptor.symmetryBoundaryDescriptorList()[0]->type() == IBoundaryDescriptor::Type::named); @@ -75,7 +75,7 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") SECTION("degree, stencil and symmetries") { - StencilDescriptor sd{3, StencilDescriptor::Type::by_edges}; + StencilDescriptor sd{3, StencilDescriptor::ConnectionType::by_edges}; std::vector<std::shared_ptr<const IBoundaryDescriptor>> bc_list; bc_list.push_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); @@ -86,7 +86,7 @@ TEST_CASE("PolynomialReconstructionDescriptor", "[scheme]") REQUIRE(descriptor.degree() == 1); REQUIRE(descriptor.stencilDescriptor().numberOfLayers() == 3); - REQUIRE(descriptor.stencilDescriptor().type() == StencilDescriptor::Type::by_edges); + REQUIRE(descriptor.stencilDescriptor().connectionType() == StencilDescriptor::ConnectionType::by_edges); REQUIRE(descriptor.symmetryBoundaryDescriptorList().size() == 3); REQUIRE(descriptor.symmetryBoundaryDescriptorList()[0]->type() == IBoundaryDescriptor::Type::named); diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder.cpp index b8c6387e1..b99ba218a 100644 --- a/tests/test_StencilBuilder.cpp +++ b/tests/test_StencilBuilder.cpp @@ -64,8 +64,8 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<1>& connectivity = mesh.connectivity(); auto stencil_array = - StencilManager::instance().getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::Type::by_nodes}); + StencilManager::instance() + .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}); REQUIRE(is_valid(connectivity, stencil_array)); } @@ -76,8 +76,8 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<1>& connectivity = mesh.connectivity(); auto stencil_array = - StencilManager::instance().getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::Type::by_nodes}); + StencilManager::instance() + .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}); REQUIRE(is_valid(connectivity, stencil_array)); } @@ -90,10 +90,11 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, - StencilManager::instance() - .getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::Type::by_nodes}))); + REQUIRE( + is_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}))); } SECTION("hybrid") @@ -101,10 +102,11 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, - StencilManager::instance() - .getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::Type::by_nodes}))); + REQUIRE( + is_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}))); } } @@ -115,10 +117,11 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, - StencilManager::instance() - .getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::Type::by_nodes}))); + REQUIRE( + is_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}))); } SECTION("hybrid") @@ -126,10 +129,11 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE(is_valid(connectivity, - StencilManager::instance() - .getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::Type::by_nodes}))); + REQUIRE( + is_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}))); } } } @@ -223,6 +227,44 @@ TEST_CASE("StencilBuilder", "[mesh]") return is_valid; }; + SECTION("1D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, + list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(symmetry_stencils_are_valid(stencil_array, mesh)); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + auto stencil = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, + list); + + REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); + REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); + } + } + SECTION("2D") { StencilManager::BoundaryDescriptorList list; @@ -238,9 +280,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<2>& connectivity = mesh.connectivity(); auto stencil_array = - StencilManager::instance().getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::Type::by_nodes}, - list); + StencilManager::instance() + .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, + list); REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); @@ -253,9 +295,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<2>& connectivity = mesh.connectivity(); auto stencil = - StencilManager::instance().getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::Type::by_nodes}, - list); + StencilManager::instance() + .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, + list); REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); @@ -279,9 +321,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<3>& connectivity = mesh.connectivity(); auto stencil = - StencilManager::instance().getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::Type::by_nodes}, - list); + StencilManager::instance() + .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, + list); REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); @@ -294,9 +336,9 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<3>& connectivity = mesh.connectivity(); auto stencil = - StencilManager::instance().getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::Type::by_nodes}, - list); + StencilManager::instance() + .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, + list); REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); -- GitLab From adcfda101cfa84054f9e816c4a90f707ab5b1bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 24 Oct 2024 16:47:41 +0200 Subject: [PATCH 075/122] Remove cell to face stencil handling (too early) --- src/mesh/StencilManager.cpp | 12 ------------ src/mesh/StencilManager.hpp | 6 ------ 2 files changed, 18 deletions(-) diff --git a/src/mesh/StencilManager.cpp b/src/mesh/StencilManager.cpp index ef1d9ef1f..842f8858f 100644 --- a/src/mesh/StencilManager.cpp +++ b/src/mesh/StencilManager.cpp @@ -30,7 +30,6 @@ StencilManager::destroy() }; check_still_registered(m_instance->m_stored_cell_to_cell_stencil_map); - check_still_registered(m_instance->m_stored_cell_to_face_stencil_map); check_still_registered(m_instance->m_stored_node_to_cell_stencil_map); // LCOV_EXCL_STOP @@ -68,16 +67,6 @@ StencilManager::getCellToCellStencilArray(const IConnectivity& connectivity, m_stored_cell_to_cell_stencil_map); } -const CellToFaceStencilArray& -StencilManager::getCellToFaceStencilArray(const IConnectivity& connectivity, - const StencilDescriptor& stencil_descriptor, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) -{ - return this->_getStencilArray<ItemType::cell, ItemType::face>(connectivity, stencil_descriptor, - symmetry_boundary_descriptor_list, - m_stored_cell_to_face_stencil_map); -} - const NodeToCellStencilArray& StencilManager::getNodeToCellStencilArray(const IConnectivity& connectivity, const StencilDescriptor& stencil_descriptor, @@ -106,6 +95,5 @@ StencilManager::deleteConnectivity(const size_t connectivity_id) }; delete_connectivity_stencil(m_stored_cell_to_cell_stencil_map); - delete_connectivity_stencil(m_stored_cell_to_face_stencil_map); delete_connectivity_stencil(m_stored_node_to_cell_stencil_map); } diff --git a/src/mesh/StencilManager.hpp b/src/mesh/StencilManager.hpp index c68e7fe06..8f07e2828 100644 --- a/src/mesh/StencilManager.hpp +++ b/src/mesh/StencilManager.hpp @@ -70,7 +70,6 @@ class StencilManager std::unordered_map<Key, std::shared_ptr<const StencilArray<source_item_type, target_item_type>>, HashKey>; StoredStencilTMap<ItemType::cell, ItemType::cell> m_stored_cell_to_cell_stencil_map; - StoredStencilTMap<ItemType::cell, ItemType::face> m_stored_cell_to_face_stencil_map; StoredStencilTMap<ItemType::node, ItemType::cell> m_stored_node_to_cell_stencil_map; template <ItemType source_item_type, ItemType target_item_type> @@ -99,11 +98,6 @@ class StencilManager const StencilDescriptor& stencil_descriptor, const BoundaryDescriptorList& symmetry_boundary_descriptor_list = {}); - const CellToFaceStencilArray& getCellToFaceStencilArray( - const IConnectivity& i_connectivity, - const StencilDescriptor& stencil_descriptor, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list = {}); - const NodeToCellStencilArray& getNodeToCellStencilArray( const IConnectivity& i_connectivity, const StencilDescriptor& stencil_descriptor, -- GitLab From 1ad30bf459d0034bf04e2517699d086ed98b8463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 24 Oct 2024 16:48:57 +0200 Subject: [PATCH 076/122] Additional step to general stencil building --- src/mesh/StencilBuilder.cpp | 445 +++++++++++++++++++----------------- src/mesh/StencilBuilder.hpp | 54 ++--- 2 files changed, 257 insertions(+), 242 deletions(-) diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 1880c0493..1fa54e702 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -7,85 +7,84 @@ #include <set> -template <typename ConnectivityType> +template <ItemType connecting_item_type, typename ConnectivityType> auto -StencilBuilder::_buildSymmetryNodeList(const ConnectivityType& connectivity, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +StencilBuilder::_buildSymmetryConnectingItemList(const ConnectivityType& connectivity, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { - static_assert(ConnectivityType::Dimension > 1); - NodeArray<bool> symmetry_node_list{connectivity, symmetry_boundary_descriptor_list.size()}; - symmetry_node_list.fill(false); - - auto face_to_node_matrix = connectivity.faceToNodeMatrix(); - size_t i_symmetry_boundary = 0; - for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { - const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; - - bool found = false; - for (size_t i_ref_node_list = 0; i_ref_node_list < connectivity.template numberOfRefItemList<ItemType::face>(); - ++i_ref_node_list) { - const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i_ref_node_list); - if (ref_face_list.refId() == boundary_descriptor) { - found = true; - for (size_t i_face = 0; i_face < ref_face_list.list().size(); ++i_face) { - const FaceId face_id = ref_face_list.list()[i_face]; - auto node_list = face_to_node_matrix[face_id]; - for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { - const NodeId node_id = node_list[i_node]; - - symmetry_node_list[node_id][i_symmetry_boundary] = true; + static_assert(connecting_item_type != ItemType::cell, "cells cannot be used to define symmetry boundaries"); + + using ConnectingItemId = ItemIdT<connecting_item_type>; + ItemArray<bool, connecting_item_type> symmetry_connecting_item_list{connectivity, + symmetry_boundary_descriptor_list.size()}; + symmetry_connecting_item_list.fill(false); + + if constexpr (ConnectivityType::Dimension > 1) { + auto face_to_connecting_item_matrix = + connectivity.template getItemToItemMatrix<ItemType::face, connecting_item_type>(); + size_t i_symmetry_boundary = 0; + for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { + const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; + + bool found = false; + for (size_t i_ref_face_list = 0; i_ref_face_list < connectivity.template numberOfRefItemList<ItemType::face>(); + ++i_ref_face_list) { + const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i_ref_face_list); + if (ref_face_list.refId() == boundary_descriptor) { + found = true; + for (size_t i_face = 0; i_face < ref_face_list.list().size(); ++i_face) { + const FaceId face_id = ref_face_list.list()[i_face]; + auto connecting_item_list = face_to_connecting_item_matrix[face_id]; + for (size_t i_connecting_item = 0; i_connecting_item < connecting_item_list.size(); ++i_connecting_item) { + const ConnectingItemId connecting_item_id = connecting_item_list[i_connecting_item]; + + symmetry_connecting_item_list[connecting_item_id][i_symmetry_boundary] = true; + } } + break; } - break; + } + ++i_symmetry_boundary; + if (not found) { + std::ostringstream error_msg; + error_msg << "cannot find boundary '" << rang::fgB::yellow << boundary_descriptor << rang::fg::reset << '\''; + throw NormalError(error_msg.str()); } } - ++i_symmetry_boundary; - if (not found) { - std::ostringstream error_msg; - error_msg << "cannot find boundary '" << rang::fgB::yellow << boundary_descriptor << rang::fg::reset << '\''; - throw NormalError(error_msg.str()); - } - } - - return symmetry_node_list; -} -template <> -auto -StencilBuilder::_buildSymmetryNodeList(const Connectivity<1>& connectivity, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const -{ - NodeArray<bool> symmetry_node_list{connectivity, symmetry_boundary_descriptor_list.size()}; - symmetry_node_list.fill(false); - - size_t i_symmetry_boundary = 0; - for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { - const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; - - bool found = false; - for (size_t i_ref_node_list = 0; i_ref_node_list < connectivity.template numberOfRefItemList<ItemType::node>(); - ++i_ref_node_list) { - const auto& ref_node_list = connectivity.template refItemList<ItemType::node>(i_ref_node_list); - if (ref_node_list.refId() == boundary_descriptor) { - found = true; - auto node_list = ref_node_list.list(); - for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { - const NodeId node_id = node_list[i_node]; - - symmetry_node_list[node_id][i_symmetry_boundary] = true; + return symmetry_connecting_item_list; + } else { + size_t i_symmetry_boundary = 0; + for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { + const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; + + bool found = false; + for (size_t i_ref_connecting_item_list = 0; + i_ref_connecting_item_list < connectivity.template numberOfRefItemList<connecting_item_type>(); + ++i_ref_connecting_item_list) { + const auto& ref_connecting_item_list = + connectivity.template refItemList<connecting_item_type>(i_ref_connecting_item_list); + if (ref_connecting_item_list.refId() == boundary_descriptor) { + found = true; + auto connecting_item_list = ref_connecting_item_list.list(); + for (size_t i_connecting_item = 0; i_connecting_item < connecting_item_list.size(); ++i_connecting_item) { + const ConnectingItemId connecting_item_id = connecting_item_list[i_connecting_item]; + + symmetry_connecting_item_list[connecting_item_id][i_symmetry_boundary] = true; + } + break; } - break; + } + ++i_symmetry_boundary; + if (not found) { + std::ostringstream error_msg; + error_msg << "cannot find boundary '" << rang::fgB::yellow << boundary_descriptor << rang::fg::reset << '\''; + throw NormalError(error_msg.str()); } } - ++i_symmetry_boundary; - if (not found) { - std::ostringstream error_msg; - error_msg << "cannot find boundary '" << rang::fgB::yellow << boundary_descriptor << rang::fg::reset << '\''; - throw NormalError(error_msg.str()); - } - } - return symmetry_node_list; + return symmetry_connecting_item_list; + } } template <typename ConnectivityType> @@ -161,8 +160,8 @@ StencilBuilder::_getColumnIndices(const ConnectivityType& connectivity, const Ar } } if (not found) { - int node_cell_number = cell_number[node_cell_id]; - size_t i_index = row_map[cell_id]; + const auto node_cell_number = cell_number[node_cell_id]; + size_t i_index = row_map[cell_id]; // search for position for index while ((i_index < max_index[cell_id])) { if (node_cell_number > cell_number[CellId(column_indices[i_index])]) { @@ -173,7 +172,7 @@ StencilBuilder::_getColumnIndices(const ConnectivityType& connectivity, const Ar } for (size_t i_destination = max_index[cell_id]; i_destination > i_index; --i_destination) { - size_t i_source = i_destination - 1; + const size_t i_source = i_destination - 1; column_indices[i_destination] = column_indices[i_source]; } @@ -189,24 +188,26 @@ StencilBuilder::_getColumnIndices(const ConnectivityType& connectivity, const Ar return column_indices; } -template <typename ConnectivityType> -CellToCellStencilArray -StencilBuilder::_buildC2C(const ConnectivityType& connectivity, - size_t number_of_layers, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +template <ItemType item_type, ItemType connecting_item_type, typename ConnectivityType> +StencilArray<item_type, item_type> +StencilBuilder::_build_for_same_source_and_target(const ConnectivityType& connectivity, + const size_t& number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { - if ((parallel::size() > 1) and (number_of_layers > GlobalVariableManager::instance().getNumberOfGhostLayers())) { - std::ostringstream error_msg; - error_msg << "Stencil builder requires" << rang::fgB::yellow << number_of_layers << rang::fg::reset - << " layers while parallel number of ghost layer is " - << GlobalVariableManager::instance().getNumberOfGhostLayers() << ".\n"; - error_msg << "Increase the number of ghost layers (using the '--number-of-ghost-layers' option)."; - throw NormalError(error_msg.str()); + if (number_of_layers == 0) { + throw NormalError("number of layers must be greater than 0 to build stencils"); } - if (number_of_layers > 2) { throw NotImplementedError("number of layers too large"); } + auto item_to_connecting_item_matrix = connectivity.template getItemToItemMatrix<item_type, connecting_item_type>(); + auto connecting_item_to_item_matrix = connectivity.template getItemToItemMatrix<connecting_item_type, item_type>(); + + auto item_is_owned = connectivity.template isOwned<item_type>(); + auto item_number = connectivity.template number<item_type>(); + + using ItemId = ItemIdT<item_type>; + using ConnectingItemId = ItemIdT<connecting_item_type>; if (symmetry_boundary_descriptor_list.size() == 0) { if (number_of_layers == 1) { @@ -215,46 +216,46 @@ StencilBuilder::_buildC2C(const ConnectivityType& connectivity, return {ConnectivityMatrix{row_map, column_indices}, {}}; } else { - auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); - auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); - - auto cell_is_owned = connectivity.cellIsOwned(); - auto cell_number = connectivity.cellNumber(); - - Array<uint32_t> row_map{connectivity.numberOfCells() + 1}; + Array<uint32_t> row_map{connectivity.template numberOf<item_type>() + 1}; row_map[0] = 0; - std::vector<CellId> column_indices_vector; + std::vector<ItemId> column_indices_vector; - for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { - if (cell_is_owned[cell_id]) { - std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( - [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + for (ItemId item_id = 0; item_id < connectivity.template numberOf<item_type>(); ++item_id) { + if (item_is_owned[item_id]) { + std::set<ItemId, std::function<bool(ItemId, ItemId)>> item_set( + [=](ItemId item_0, ItemId item_1) { return item_number[item_0] < item_number[item_1]; }); - for (size_t i_node_1 = 0; i_node_1 < cell_to_node_matrix[cell_id].size(); ++i_node_1) { - const NodeId layer_1_node_id = cell_to_node_matrix[cell_id][i_node_1]; + for (size_t i_connecting_item_1 = 0; i_connecting_item_1 < item_to_connecting_item_matrix[item_id].size(); + ++i_connecting_item_1) { + const ConnectingItemId layer_1_connecting_item_id = + item_to_connecting_item_matrix[item_id][i_connecting_item_1]; - for (size_t i_cell_1 = 0; i_cell_1 < node_to_cell_matrix[layer_1_node_id].size(); ++i_cell_1) { - CellId cell_1_id = node_to_cell_matrix[layer_1_node_id][i_cell_1]; + for (size_t i_item_1 = 0; i_item_1 < connecting_item_to_item_matrix[layer_1_connecting_item_id].size(); + ++i_item_1) { + ItemId item_1_id = connecting_item_to_item_matrix[layer_1_connecting_item_id][i_item_1]; - for (size_t i_node_2 = 0; i_node_2 < cell_to_node_matrix[cell_1_id].size(); ++i_node_2) { - const NodeId layer_2_node_id = cell_to_node_matrix[cell_1_id][i_node_2]; + for (size_t i_connecting_item_2 = 0; + i_connecting_item_2 < item_to_connecting_item_matrix[item_1_id].size(); ++i_connecting_item_2) { + const ConnectingItemId layer_2_connecting_item_id = + item_to_connecting_item_matrix[item_1_id][i_connecting_item_2]; - for (size_t i_cell_2 = 0; i_cell_2 < node_to_cell_matrix[layer_2_node_id].size(); ++i_cell_2) { - CellId cell_2_id = node_to_cell_matrix[layer_2_node_id][i_cell_2]; + for (size_t i_item_2 = 0; i_item_2 < connecting_item_to_item_matrix[layer_2_connecting_item_id].size(); + ++i_item_2) { + ItemId item_2_id = connecting_item_to_item_matrix[layer_2_connecting_item_id][i_item_2]; - if (cell_2_id != cell_id) { - cell_set.insert(cell_2_id); + if (item_2_id != item_id) { + item_set.insert(item_2_id); } } } } } - for (auto stencil_cell_id : cell_set) { - column_indices_vector.push_back(stencil_cell_id); + for (auto stencil_item_id : item_set) { + column_indices_vector.push_back(stencil_item_id); } - row_map[cell_id + 1] = row_map[cell_id] + cell_set.size(); + row_map[item_id + 1] = row_map[item_id] + item_set.size(); } } @@ -273,99 +274,98 @@ StencilBuilder::_buildC2C(const ConnectivityType& connectivity, return {primal_stencil, {}}; } } else { - NodeArray<bool> symmetry_node_list = this->_buildSymmetryNodeList(connectivity, symmetry_boundary_descriptor_list); - - auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); - auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + ItemArray<bool, connecting_item_type> symmetry_item_list = + this->_buildSymmetryConnectingItemList<connecting_item_type>(connectivity, symmetry_boundary_descriptor_list); - auto cell_is_owned = connectivity.cellIsOwned(); - auto cell_number = connectivity.cellNumber(); - - Array<uint32_t> row_map{connectivity.numberOfCells() + 1}; + Array<uint32_t> row_map{connectivity.template numberOf<item_type>() + 1}; row_map[0] = 0; std::vector<Array<uint32_t>> symmetry_row_map_list(symmetry_boundary_descriptor_list.size()); for (auto&& symmetry_row_map : symmetry_row_map_list) { - symmetry_row_map = Array<uint32_t>{connectivity.numberOfCells() + 1}; + symmetry_row_map = Array<uint32_t>{connectivity.template numberOf<item_type>() + 1}; symmetry_row_map[0] = 0; } std::vector<uint32_t> column_indices_vector; std::vector<std::vector<uint32_t>> symmetry_column_indices_vector(symmetry_boundary_descriptor_list.size()); - for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { - std::set<CellId> cell_set; - std::vector<std::set<CellId>> by_boundary_symmetry_cell(symmetry_boundary_descriptor_list.size()); - - if (cell_is_owned[cell_id]) { - auto cell_node_list = cell_to_node_matrix[cell_id]; - for (size_t i_cell_node = 0; i_cell_node < cell_node_list.size(); ++i_cell_node) { - const NodeId cell_node_id = cell_node_list[i_cell_node]; - auto node_cell_list = node_to_cell_matrix[cell_node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { - const CellId node_cell_id = node_cell_list[i_node_cell]; - if (cell_id != node_cell_id) { - cell_set.insert(node_cell_id); + for (ItemId item_id = 0; item_id < connectivity.template numberOf<item_type>(); ++item_id) { + std::set<ItemId> item_set; + std::vector<std::set<ItemId>> by_boundary_symmetry_item(symmetry_boundary_descriptor_list.size()); + + if (item_is_owned[item_id]) { + auto item_to_connecting_item_list = item_to_connecting_item_matrix[item_id]; + for (size_t i_connecting_item_of_item = 0; i_connecting_item_of_item < item_to_connecting_item_list.size(); + ++i_connecting_item_of_item) { + const ConnectingItemId connecting_item_id_of_item = item_to_connecting_item_list[i_connecting_item_of_item]; + auto connecting_item_to_item_list = connecting_item_to_item_matrix[connecting_item_id_of_item]; + for (size_t i_item_of_connecting_item = 0; i_item_of_connecting_item < connecting_item_to_item_list.size(); + ++i_item_of_connecting_item) { + const ItemId item_id_of_connecting_item = connecting_item_to_item_list[i_item_of_connecting_item]; + if (item_id != item_id_of_connecting_item) { + item_set.insert(item_id_of_connecting_item); } } } { - std::vector<CellId> cell_vector; - for (auto&& set_cell_id : cell_set) { - cell_vector.push_back(set_cell_id); + std::vector<ItemId> item_vector; + for (auto&& set_item_id : item_set) { + item_vector.push_back(set_item_id); } - std::sort(cell_vector.begin(), cell_vector.end(), - [&cell_number](const CellId& cell0_id, const CellId& cell1_id) { - return cell_number[cell0_id] < cell_number[cell1_id]; + std::sort(item_vector.begin(), item_vector.end(), + [&item_number](const ItemId& item0_id, const ItemId& item1_id) { + return item_number[item0_id] < item_number[item1_id]; }); - for (auto&& vector_cell_id : cell_vector) { - column_indices_vector.push_back(vector_cell_id); + for (auto&& vector_item_id : item_vector) { + column_indices_vector.push_back(vector_item_id); } } for (size_t i = 0; i < symmetry_boundary_descriptor_list.size(); ++i) { - std::set<CellId> symmetry_cell_set; - for (size_t i_cell_node = 0; i_cell_node < cell_node_list.size(); ++i_cell_node) { - const NodeId cell_node_id = cell_node_list[i_cell_node]; - if (symmetry_node_list[cell_node_id][i]) { - auto node_cell_list = node_to_cell_matrix[cell_node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { - const CellId node_cell_id = node_cell_list[i_node_cell]; - symmetry_cell_set.insert(node_cell_id); + std::set<ItemId> symmetry_item_set; + for (size_t i_connecting_item_of_item = 0; i_connecting_item_of_item < item_to_connecting_item_list.size(); + ++i_connecting_item_of_item) { + const ConnectingItemId connecting_item_id_of_item = item_to_connecting_item_list[i_connecting_item_of_item]; + if (symmetry_item_list[connecting_item_id_of_item][i]) { + auto item_of_connecting_item_list = connecting_item_to_item_matrix[connecting_item_id_of_item]; + for (size_t i_item_of_connecting_item = 0; + i_item_of_connecting_item < item_of_connecting_item_list.size(); ++i_item_of_connecting_item) { + const ItemId item_id_of_connecting_item = item_of_connecting_item_list[i_item_of_connecting_item]; + symmetry_item_set.insert(item_id_of_connecting_item); } } } - by_boundary_symmetry_cell[i] = symmetry_cell_set; + by_boundary_symmetry_item[i] = symmetry_item_set; - std::vector<CellId> cell_vector; - for (auto&& set_cell_id : symmetry_cell_set) { - cell_vector.push_back(set_cell_id); + std::vector<ItemId> item_vector; + for (auto&& set_item_id : symmetry_item_set) { + item_vector.push_back(set_item_id); } - std::sort(cell_vector.begin(), cell_vector.end(), - [&cell_number](const CellId& cell0_id, const CellId& cell1_id) { - return cell_number[cell0_id] < cell_number[cell1_id]; + std::sort(item_vector.begin(), item_vector.end(), + [&item_number](const ItemId& item0_id, const ItemId& item1_id) { + return item_number[item0_id] < item_number[item1_id]; }); - for (auto&& vector_cell_id : cell_vector) { - symmetry_column_indices_vector[i].push_back(vector_cell_id); + for (auto&& vector_item_id : item_vector) { + symmetry_column_indices_vector[i].push_back(vector_item_id); } } } - row_map[cell_id + 1] = row_map[cell_id] + cell_set.size(); + row_map[item_id + 1] = row_map[item_id] + item_set.size(); for (size_t i = 0; i < symmetry_row_map_list.size(); ++i) { - symmetry_row_map_list[i][cell_id + 1] = symmetry_row_map_list[i][cell_id] + by_boundary_symmetry_cell[i].size(); + symmetry_row_map_list[i][item_id + 1] = symmetry_row_map_list[i][item_id] + by_boundary_symmetry_item[i].size(); } } ConnectivityMatrix primal_stencil{row_map, convert_to_array(column_indices_vector)}; - CellToCellStencilArray::BoundaryDescriptorStencilArrayList symmetry_boundary_stencil_list; + typename StencilArray<item_type, item_type>::BoundaryDescriptorStencilArrayList symmetry_boundary_stencil_list; { size_t i = 0; for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { symmetry_boundary_stencil_list.emplace_back( - CellToCellStencilArray:: + typename StencilArray<item_type, item_type>:: BoundaryDescriptorStencilArray{p_boundary_descriptor, ConnectivityMatrix{symmetry_row_map_list[i], convert_to_array(symmetry_column_indices_vector[i])}}); @@ -377,63 +377,74 @@ StencilBuilder::_buildC2C(const ConnectivityType& connectivity, } } -template <> -CellToCellStencilArray -StencilBuilder::_buildC2C(const Connectivity<1>& connectivity, - const StencilDescriptor& stencil_descriptor, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +template <ItemType source_item_type, + ItemType connecting_item_type, + ItemType target_item_type, + typename ConnectivityType> +StencilArray<source_item_type, target_item_type> +StencilBuilder::_build_for_different_source_and_target( + const ConnectivityType& connectivity, + const size_t& number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { - if (stencil_descriptor.connectionType() != StencilDescriptor::ConnectionType::by_cells) { - return StencilBuilder::_buildC2C(connectivity, stencil_descriptor.numberOfLayers(), - symmetry_boundary_descriptor_list); - } else { - throw UnexpectedError("Cannot build cell to cell stencil using cell connectivity"); - } + static_assert(source_item_type != target_item_type); + throw NotImplementedError("different source target"); } -template <> -CellToCellStencilArray -StencilBuilder::_buildC2C(const Connectivity<2>& connectivity, - const StencilDescriptor& stencil_descriptor, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +template <ItemType source_item_type, + ItemType connecting_item_type, + ItemType target_item_type, + typename ConnectivityType> +StencilArray<source_item_type, target_item_type> +StencilBuilder::_build(const ConnectivityType& connectivity, + const size_t& number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { - switch (stencil_descriptor.connectionType()) { - case StencilDescriptor::ConnectionType::by_nodes: { - return StencilBuilder::_buildC2C(connectivity, stencil_descriptor.numberOfLayers(), - symmetry_boundary_descriptor_list); - } - case StencilDescriptor::ConnectionType::by_edges: - case StencilDescriptor::ConnectionType::by_faces: { - throw NotImplementedError("non by_node stencil"); - } - case StencilDescriptor::ConnectionType::by_cells: { - throw UnexpectedError("Cannot build cell to cell stencil using cell connectivity"); - } - // LCOV_EXCL_START - default: { - throw UnexpectedError("invalid connection type"); - } - // LCOV_EXCL_STOP + if constexpr (connecting_item_type != target_item_type) { + if constexpr (source_item_type == target_item_type) { + return this + ->_build_for_same_source_and_target<source_item_type, connecting_item_type>(connectivity, number_of_layers, + symmetry_boundary_descriptor_list); + } else { + return this->_build_for_different_source_and_target<source_item_type, connecting_item_type, + target_item_type>(connectivity, number_of_layers, + symmetry_boundary_descriptor_list); + } + } else { + std::ostringstream error_msg; + error_msg << "cannot build stencil of " << rang::fgB::yellow << itemName(target_item_type) << rang::fg::reset + << " using " << rang::fgB::yellow << itemName(connecting_item_type) << rang::fg::reset + << " for connectivity"; + throw UnexpectedError(error_msg.str()); } } -template <> -CellToCellStencilArray -StencilBuilder::_buildC2C(const Connectivity<3>& connectivity, - const StencilDescriptor& stencil_descriptor, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const +template <ItemType source_item_type, ItemType target_item_type, typename ConnectivityType> +StencilArray<source_item_type, target_item_type> +StencilBuilder::_build(const ConnectivityType& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { switch (stencil_descriptor.connectionType()) { case StencilDescriptor::ConnectionType::by_nodes: { - return StencilBuilder::_buildC2C(connectivity, stencil_descriptor.numberOfLayers(), - symmetry_boundary_descriptor_list); + return this->_build<source_item_type, ItemType::node, target_item_type>(connectivity, + stencil_descriptor.numberOfLayers(), + symmetry_boundary_descriptor_list); + } + case StencilDescriptor::ConnectionType::by_edges: { + return this->_build<source_item_type, ItemType::edge, target_item_type>(connectivity, + stencil_descriptor.numberOfLayers(), + symmetry_boundary_descriptor_list); } - case StencilDescriptor::ConnectionType::by_edges: case StencilDescriptor::ConnectionType::by_faces: { - throw NotImplementedError("non by_node stencil"); + return this->_build<source_item_type, ItemType::face, target_item_type>(connectivity, + stencil_descriptor.numberOfLayers(), + symmetry_boundary_descriptor_list); } case StencilDescriptor::ConnectionType::by_cells: { - throw UnexpectedError("Cannot build cell to cell stencil using cell connectivity"); + return this->_build<source_item_type, ItemType::cell, target_item_type>(connectivity, + stencil_descriptor.numberOfLayers(), + symmetry_boundary_descriptor_list); } // LCOV_EXCL_START default: { @@ -448,18 +459,28 @@ StencilBuilder::buildC2C(const IConnectivity& connectivity, const StencilDescriptor& stencil_descriptor, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { + if ((parallel::size() > 1) and + (stencil_descriptor.numberOfLayers() > GlobalVariableManager::instance().getNumberOfGhostLayers())) { + std::ostringstream error_msg; + error_msg << "Stencil builder requires" << rang::fgB::yellow << stencil_descriptor.numberOfLayers() + << rang::fg::reset << " layers while parallel number of ghost layer is " + << GlobalVariableManager::instance().getNumberOfGhostLayers() << ".\n"; + error_msg << "Increase the number of ghost layers (using the '--number-of-ghost-layers' option)."; + throw NormalError(error_msg.str()); + } + switch (connectivity.dimension()) { case 1: { - return StencilBuilder::_buildC2C(dynamic_cast<const Connectivity<1>&>(connectivity), stencil_descriptor, - symmetry_boundary_descriptor_list); + return this->_build<ItemType::cell, ItemType::cell>(dynamic_cast<const Connectivity<1>&>(connectivity), + stencil_descriptor, symmetry_boundary_descriptor_list); } case 2: { - return StencilBuilder::_buildC2C(dynamic_cast<const Connectivity<2>&>(connectivity), stencil_descriptor, - symmetry_boundary_descriptor_list); + return this->_build<ItemType::cell, ItemType::cell>(dynamic_cast<const Connectivity<2>&>(connectivity), + stencil_descriptor, symmetry_boundary_descriptor_list); } case 3: { - return StencilBuilder::_buildC2C(dynamic_cast<const Connectivity<3>&>(connectivity), stencil_descriptor, - symmetry_boundary_descriptor_list); + return this->_build<ItemType::cell, ItemType::cell>(dynamic_cast<const Connectivity<3>&>(connectivity), + stencil_descriptor, symmetry_boundary_descriptor_list); } default: { throw UnexpectedError("invalid connectivity dimension"); @@ -467,12 +488,6 @@ StencilBuilder::buildC2C(const IConnectivity& connectivity, } } -CellToFaceStencilArray -StencilBuilder::buildC2F(const IConnectivity&, const StencilDescriptor&, const BoundaryDescriptorList&) const -{ - throw NotImplementedError("cell to face stencil"); -} - NodeToCellStencilArray StencilBuilder::buildN2C(const IConnectivity&, const StencilDescriptor&, const BoundaryDescriptorList&) const { diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index 464a7fc27..5bfd821ca 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -23,33 +23,39 @@ class StencilBuilder Array<const uint32_t> _getColumnIndices(const ConnectivityType& connectivity, const Array<const uint32_t>& row_map) const; - template <typename ConnectivityType> - auto _buildSymmetryNodeList(const ConnectivityType& connectivity, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + template <ItemType connecting_item, typename ConnectivityType> + auto _buildSymmetryConnectingItemList(const ConnectivityType& connectivity, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; - template <typename ConnectivityType, - ItemType source_item_type, - ItemType connecting_item_type, - ItemType target_item_type> - StencilArray<source_item_type, target_item_type> _build( + template <ItemType item_type, ItemType connecting_item_type, typename ConnectivityType> + StencilArray<item_type, item_type> _build_for_same_source_and_target( const ConnectivityType& connectivity, - const StencilDescriptor& stencil_descriptor, + const size_t& number_of_layers, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; - template <typename ConnectivityType> - CellToCellStencilArray _buildC2C(const ConnectivityType& connectivity, - const StencilDescriptor& stencil_descriptor, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + template <ItemType source_item_type, + ItemType connecting_item_type, + ItemType target_item_type, + typename ConnectivityType> + StencilArray<source_item_type, target_item_type> _build_for_different_source_and_target( + const ConnectivityType& connectivity, + const size_t& number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; - template <typename ConnectivityType> - CellToCellStencilArray _buildC2C(const ConnectivityType& connectivity, - size_t number_of_layers, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + template <ItemType source_item_type, + ItemType connecting_item_type, + ItemType target_item_type, + typename ConnectivityType> + StencilArray<source_item_type, target_item_type> _build( + const ConnectivityType& connectivity, + const size_t& number_of_layers, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; - template <typename ConnectivityType> - CellToFaceStencilArray _buildC2F(const ConnectivityType& connectivity, - size_t number_of_layers, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; + template <ItemType source_item_type, ItemType target_item_type, typename ConnectivityType> + StencilArray<source_item_type, target_item_type> _build( + const ConnectivityType& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; template <typename ConnectivityType> NodeToCellStencilArray _buildN2C(const ConnectivityType& connectivity, @@ -60,10 +66,6 @@ class StencilBuilder const StencilDescriptor& stencil_descriptor, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; - CellToFaceStencilArray buildC2F(const IConnectivity& connectivity, - const StencilDescriptor& stencil_descriptor, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; - NodeToCellStencilArray buildN2C(const IConnectivity& connectivity, const StencilDescriptor& stencil_descriptor, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; @@ -77,8 +79,6 @@ class StencilBuilder { if constexpr ((source_item_type == ItemType::cell) and (target_item_type == ItemType::cell)) { return buildC2C(connectivity, stencil_descriptor, symmetry_boundary_descriptor_list); - } else if constexpr ((source_item_type == ItemType::cell) and (target_item_type == ItemType::face)) { - return buildC2F(connectivity, stencil_descriptor, symmetry_boundary_descriptor_list); } else if constexpr ((source_item_type == ItemType::node) and (target_item_type == ItemType::cell)) { return buildN2C(connectivity, stencil_descriptor, symmetry_boundary_descriptor_list); } else { -- GitLab From 316d875b3c58cb4fe03e15924645cd6a82461e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 25 Oct 2024 19:20:09 +0200 Subject: [PATCH 077/122] [ci-skip] WIP: Rework stencil construction --- src/mesh/StencilBuilder.cpp | 150 +++++++++++++++++++++++++++++++++++- src/mesh/StencilBuilder.hpp | 3 + 2 files changed, 149 insertions(+), 4 deletions(-) diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 1fa54e702..31db762a1 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -7,6 +7,104 @@ #include <set> +template <ItemType item_type> +class StencilBuilder::Layer +{ + using ItemId = ItemIdT<item_type>; + std::vector<ItemId> m_item_id_vector; + std::vector<int> m_item_number_vector; + + public: + size_t + size() const + { + Assert(m_item_id_vector.size() == m_item_number_vector.size()); + return m_item_id_vector.size(); + } + + bool + hasItemNumber(const int item_number) const + { + ssize_t begin = 0; + ssize_t end = m_item_number_vector.size(); + + while (begin < end) { + const ssize_t mid = (begin + end) / 2; + const auto& mid_number = m_item_number_vector[mid]; + if (mid_number == item_number) { + return true; // We found the value + } else if (mid_number < item_number) { + if (begin == mid) { + break; + } + begin = mid - 1; + } else { + if (end == mid) { + break; + } + end = mid + 1; + } + } + return false; + } + + const std::vector<ItemId>& + itemIdList() const + { + return m_item_id_vector; + } + + void + add(const ItemId item_id, const int item_number) + { + ssize_t begin = 0; + ssize_t end = m_item_number_vector.size(); + + while (begin < end) { + const ssize_t mid = (begin + end) / 2; + const auto& mid_number = m_item_number_vector[mid]; + + if (mid_number == item_number) { + return; // We found the value + } else if (mid_number < item_number) { + if (begin == mid) { + break; + } + begin = mid; + } else { + if (end == mid) { + break; + } + end = mid; + } + } + + m_item_id_vector.push_back(item_id); + m_item_number_vector.push_back(item_number); + + const auto& begin_number = m_item_number_vector[begin]; + + if (begin_number > item_number) { + for (ssize_t i = m_item_number_vector.size() - 2; i >= begin; --i) { + std::swap(m_item_number_vector[i], m_item_number_vector[i + 1]); + std::swap(m_item_id_vector[i], m_item_id_vector[i + 1]); + } + } else if (begin_number < item_number) { + for (ssize_t i = m_item_number_vector.size() - 2; i > begin; --i) { + std::swap(m_item_number_vector[i], m_item_number_vector[i + 1]); + std::swap(m_item_id_vector[i], m_item_id_vector[i + 1]); + } + } + } + + Layer() = default; + + Layer(const Layer&) = default; + Layer(Layer&&) = default; + + ~Layer() = default; +}; + template <ItemType connecting_item_type, typename ConnectivityType> auto StencilBuilder::_buildSymmetryConnectingItemList(const ConnectivityType& connectivity, @@ -203,16 +301,60 @@ StencilBuilder::_build_for_same_source_and_target(const ConnectivityType& connec auto item_to_connecting_item_matrix = connectivity.template getItemToItemMatrix<item_type, connecting_item_type>(); auto connecting_item_to_item_matrix = connectivity.template getItemToItemMatrix<connecting_item_type, item_type>(); - auto item_is_owned = connectivity.template isOwned<item_type>(); - auto item_number = connectivity.template number<item_type>(); + auto item_is_owned = connectivity.template isOwned<item_type>(); + auto item_number = connectivity.template number<item_type>(); + auto connecting_item_number = connectivity.template number<connecting_item_type>(); using ItemId = ItemIdT<item_type>; using ConnectingItemId = ItemIdT<connecting_item_type>; if (symmetry_boundary_descriptor_list.size() == 0) { if (number_of_layers == 1) { - Array<const uint32_t> row_map = this->_getRowMap(connectivity); - Array<const uint32_t> column_indices = this->_getColumnIndices(connectivity, row_map); + Array<uint32_t> row_map{connectivity.template numberOf<item_type>() + 1}; + row_map[0] = 0; + + std::vector<ItemId> column_indices_vector; + + for (ItemId item_id = 0; item_id < connectivity.template numberOf<item_type>(); ++item_id) { + // First layer is a special case + + Layer<item_type> item_layer; + Layer<connecting_item_type> connecting_layer; + + if (item_is_owned[item_id]) { + for (size_t i_connecting_item_1 = 0; i_connecting_item_1 < item_to_connecting_item_matrix[item_id].size(); + ++i_connecting_item_1) { + const ConnectingItemId layer_1_connecting_item_id = + item_to_connecting_item_matrix[item_id][i_connecting_item_1]; + connecting_layer.add(layer_1_connecting_item_id, connecting_item_number[layer_1_connecting_item_id]); + } + + for (auto connecting_item_id : connecting_layer.itemIdList()) { + for (size_t i_item_1 = 0; i_item_1 < connecting_item_to_item_matrix[connecting_item_id].size(); + ++i_item_1) { + const ItemId layer_1_item_id = connecting_item_to_item_matrix[connecting_item_id][i_item_1]; + if (layer_1_item_id != item_id) { + item_layer.add(layer_1_item_id, item_number[layer_1_item_id]); + } + } + } + } + + for (auto layer_item_id : item_layer.itemIdList()) { + column_indices_vector.push_back(layer_item_id); + } + row_map[item_id + 1] = row_map[item_id] + item_layer.itemIdList().size(); + } + + if (row_map[row_map.size() - 1] != column_indices_vector.size()) { + throw UnexpectedError("incorrect stencil size"); + } + Array<uint32_t> column_indices(row_map[row_map.size() - 1]); + column_indices.fill(std::numeric_limits<uint32_t>::max()); + + for (size_t i = 0; i < column_indices.size(); ++i) { + column_indices[i] = column_indices_vector[i]; + } return {ConnectivityMatrix{row_map, column_indices}, {}}; } else { diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index 5bfd821ca..4f7ead00b 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -16,6 +16,9 @@ class StencilBuilder using BoundaryDescriptorList = std::vector<std::shared_ptr<const IBoundaryDescriptor>>; private: + template <ItemType item_type> + class Layer; + template <typename ConnectivityType> Array<const uint32_t> _getRowMap(const ConnectivityType& connectivity) const; -- GitLab From 79beaa051e8b00d39aacec7fca030fe0435bf738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 19 Nov 2024 15:13:00 +0100 Subject: [PATCH 078/122] Allow TinyVector and TinyMatrix as data type for DiscreteFunctionP0Vector --- .../EmbeddedDiscreteFunctionMathFunctions.cpp | 12 ++-- src/output/OutputNamedItemValueSet.hpp | 28 +++++++++- src/output/VTKWriter.cpp | 56 +++++++++++++++---- src/scheme/DiscreteFunctionP0Vector.hpp | 16 ++++-- src/scheme/DiscreteFunctionVariant.hpp | 16 +++++- src/scheme/FluxingAdvectionSolver.cpp | 17 ++++-- src/scheme/PolynomialReconstruction.cpp | 51 ++++++++++------- 7 files changed, 145 insertions(+), 51 deletions(-) diff --git a/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp b/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp index 5d000813a..892037219 100644 --- a/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp +++ b/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp @@ -234,10 +234,14 @@ dot(const std::shared_ptr<const DiscreteFunctionVariant>& f_v, throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(f)); } } else if constexpr (is_discrete_function_P0_vector_v<TypeOfF>) { - if (f.size() == g.size()) { - return std::make_shared<DiscreteFunctionVariant>(dot(f, g)); + if constexpr (std::is_arithmetic_v<DataType>) { + if (f.size() == g.size()) { + return std::make_shared<DiscreteFunctionVariant>(dot(f, g)); + } else { + throw NormalError("operands have different dimension"); + } } else { - throw NormalError("operands have different dimension"); + throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(f)); } } else { throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(f)); @@ -691,8 +695,6 @@ sum_of_Vh_components(const std::shared_ptr<const DiscreteFunctionVariant>& f) [&](auto&& discrete_function) -> std::shared_ptr<const DiscreteFunctionVariant> { using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - static_assert(std::is_same_v<DataType, double>); return std::make_shared<DiscreteFunctionVariant>(sumOfComponents(discrete_function)); } else { throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(f)); diff --git a/src/output/OutputNamedItemValueSet.hpp b/src/output/OutputNamedItemValueSet.hpp index aa2c5e2ed..8c4333042 100644 --- a/src/output/OutputNamedItemValueSet.hpp +++ b/src/output/OutputNamedItemValueSet.hpp @@ -38,7 +38,7 @@ class NamedItemData } NamedItemData& operator=(const NamedItemData&) = default; - NamedItemData& operator=(NamedItemData&&) = default; + NamedItemData& operator=(NamedItemData&&) = default; NamedItemData(const std::string& name, const ItemDataT<DataType, item_type, ConnectivityPtr>& item_data) : m_name(name), m_item_data(item_data) @@ -113,24 +113,48 @@ class OutputNamedItemDataSet NodeArray<const long int>, NodeArray<const unsigned long int>, NodeArray<const double>, + NodeArray<const TinyVector<1, double>>, + NodeArray<const TinyVector<2, double>>, + NodeArray<const TinyVector<3, double>>, + NodeArray<const TinyMatrix<1, 1, double>>, + NodeArray<const TinyMatrix<2, 2, double>>, + NodeArray<const TinyMatrix<3, 3, double>>, EdgeArray<const bool>, EdgeArray<const int>, EdgeArray<const long int>, EdgeArray<const unsigned long int>, EdgeArray<const double>, + EdgeArray<const TinyVector<1, double>>, + EdgeArray<const TinyVector<2, double>>, + EdgeArray<const TinyVector<3, double>>, + EdgeArray<const TinyMatrix<1, 1, double>>, + EdgeArray<const TinyMatrix<2, 2, double>>, + EdgeArray<const TinyMatrix<3, 3, double>>, FaceArray<const bool>, FaceArray<const int>, FaceArray<const long int>, FaceArray<const unsigned long int>, FaceArray<const double>, + FaceArray<const TinyVector<1, double>>, + FaceArray<const TinyVector<2, double>>, + FaceArray<const TinyVector<3, double>>, + FaceArray<const TinyMatrix<1, 1, double>>, + FaceArray<const TinyMatrix<2, 2, double>>, + FaceArray<const TinyMatrix<3, 3, double>>, CellArray<const bool>, CellArray<const int>, CellArray<const long int>, CellArray<const unsigned long int>, - CellArray<const double>>; + CellArray<const double>, + CellArray<const TinyVector<1, double>>, + CellArray<const TinyVector<2, double>>, + CellArray<const TinyVector<3, double>>, + CellArray<const TinyMatrix<1, 1, double>>, + CellArray<const TinyMatrix<2, 2, double>>, + CellArray<const TinyMatrix<3, 3, double>>>; private: // We do not use a map, we want variables to be written in the diff --git a/src/output/VTKWriter.cpp b/src/output/VTKWriter.cpp index 64d354344..2aedce3b1 100644 --- a/src/output/VTKWriter.cpp +++ b/src/output/VTKWriter.cpp @@ -396,10 +396,17 @@ VTKWriter::_write(const MeshType& mesh, << "\">\n"; fout << "<CellData>\n"; for (const auto& [name, item_value_variant] : output_named_item_data_set) { - std::visit([&, var_name = name]( - auto&& - item_value) { return this->_write_cell_data(fout, var_name, item_value, serialize_data_list); }, - item_value_variant); + std::visit( + [&, var_name = name](auto&& item_value) { + using IVType = std::decay_t<decltype(item_value)>; + using DataType = typename IVType::data_type; + if constexpr (is_item_array_v<IVType> and not std::is_arithmetic_v<DataType>) { + throw NotImplementedError("DiscreteFunctionP0Vector of non arithmetic type"); + } else { + return this->_write_cell_data(fout, var_name, item_value, serialize_data_list); + } + }, + item_value_variant); } if (parallel::size() > 1) { CellValue<uint8_t> vtk_ghost_type{mesh.connectivity()}; @@ -413,10 +420,17 @@ VTKWriter::_write(const MeshType& mesh, fout << "</CellData>\n"; fout << "<PointData>\n"; for (const auto& [name, item_value_variant] : output_named_item_data_set) { - std::visit([&, var_name = name]( - auto&& - item_value) { return this->_write_node_data(fout, var_name, item_value, serialize_data_list); }, - item_value_variant); + std::visit( + [&, var_name = name](auto&& item_value) { + using IVType = std::decay_t<decltype(item_value)>; + using DataType = typename IVType::data_type; + if constexpr (is_item_array_v<IVType> and not std::is_arithmetic_v<DataType>) { + throw NotImplementedError("DiscreteFunctionP0Vector of non arithmetic type"); + } else { + return this->_write_node_data(fout, var_name, item_value, serialize_data_list); + } + }, + item_value_variant); } fout << "</PointData>\n"; fout << "<Points>\n"; @@ -644,15 +658,33 @@ VTKWriter::_write(const MeshType& mesh, fout << "<PPointData>\n"; for (const auto& [name, item_value_variant] : output_named_item_data_set) { - std::visit([&, var_name = name](auto&& item_value) { return this->_write_node_pvtu(fout, var_name, item_value); }, - item_value_variant); + std::visit( + [&, var_name = name](auto&& item_value) { + using IVType = std::decay_t<decltype(item_value)>; + using DataType = typename IVType::data_type; + if constexpr (is_item_array_v<IVType> and not std::is_arithmetic_v<DataType>) { + throw NotImplementedError("DiscreteFunctionP0Vector of non arithmetic type"); + } else { + return this->_write_node_pvtu(fout, var_name, item_value); + } + }, + item_value_variant); } fout << "</PPointData>\n"; fout << "<PCellData>\n"; for (const auto& [name, item_value_variant] : output_named_item_data_set) { - std::visit([&, var_name = name](auto&& item_value) { return this->_write_cell_pvtu(fout, var_name, item_value); }, - item_value_variant); + std::visit( + [&, var_name = name](auto&& item_value) { + using IVType = std::decay_t<decltype(item_value)>; + using DataType = typename IVType::data_type; + if constexpr (is_item_array_v<IVType> and not std::is_arithmetic_v<DataType>) { + throw NotImplementedError("DiscreteFunctionP0Vector of non arithmetic type"); + } else { + return this->_write_cell_pvtu(fout, var_name, item_value); + } + }, + item_value_variant); } if (parallel::size() > 1) { fout << "<PDataArray type=\"UInt8\" Name=\"vtkGhostType\" NumberOfComponents=\"1\"/>\n"; diff --git a/src/scheme/DiscreteFunctionP0Vector.hpp b/src/scheme/DiscreteFunctionP0Vector.hpp index da3904b2a..bc08cb2a7 100644 --- a/src/scheme/DiscreteFunctionP0Vector.hpp +++ b/src/scheme/DiscreteFunctionP0Vector.hpp @@ -19,8 +19,6 @@ class DiscreteFunctionP0Vector friend class DiscreteFunctionP0Vector<std::add_const_t<DataType>>; friend class DiscreteFunctionP0Vector<std::remove_const_t<DataType>>; - static_assert(std::is_arithmetic_v<DataType>, "DiscreteFunctionP0Vector are only defined for arithmetic data type"); - private: std::shared_ptr<const MeshVariant> m_mesh; CellArray<DataType> m_cell_arrays; @@ -188,16 +186,23 @@ class DiscreteFunctionP0Vector return product; } - PUGS_INLINE friend DiscreteFunctionP0<double> + PUGS_INLINE friend DiscreteFunctionP0<std::remove_const_t<DataType>> sumOfComponents(const DiscreteFunctionP0Vector& f) { - DiscreteFunctionP0<double> result{f.m_mesh}; + DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh}; parallel_for( f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { const auto& f_cell_id = f[cell_id]; - double sum = 0; + std::remove_const_t<DataType> sum = [] { + if constexpr (std::is_arithmetic_v<DataType>) { + return 0; + } else { + return zero; + } + }(); + for (size_t i = 0; i < f.size(); ++i) { sum += f_cell_id[i]; } @@ -213,6 +218,7 @@ class DiscreteFunctionP0Vector { Assert(f.meshVariant()->id() == g.meshVariant()->id(), "functions are nor defined on the same mesh"); Assert(f.size() == g.size()); + static_assert(std::is_arithmetic_v<std::decay_t<DataType>>); DiscreteFunctionP0<double> result{f.m_mesh}; parallel_for( f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { diff --git a/src/scheme/DiscreteFunctionVariant.hpp b/src/scheme/DiscreteFunctionVariant.hpp index 901b79890..616184298 100644 --- a/src/scheme/DiscreteFunctionVariant.hpp +++ b/src/scheme/DiscreteFunctionVariant.hpp @@ -19,7 +19,13 @@ class DiscreteFunctionVariant DiscreteFunctionP0<const TinyMatrix<2>>, DiscreteFunctionP0<const TinyMatrix<3>>, - DiscreteFunctionP0Vector<const double>>; + DiscreteFunctionP0Vector<const double>, + DiscreteFunctionP0Vector<const TinyVector<1>>, + DiscreteFunctionP0Vector<const TinyVector<2>>, + DiscreteFunctionP0Vector<const TinyVector<3>>, + DiscreteFunctionP0Vector<const TinyMatrix<1>>, + DiscreteFunctionP0Vector<const TinyMatrix<2>>, + DiscreteFunctionP0Vector<const TinyMatrix<3>>>; Variant m_discrete_function; @@ -70,7 +76,13 @@ class DiscreteFunctionVariant DiscreteFunctionVariant(const DiscreteFunctionP0Vector<DataType>& discrete_function) : m_discrete_function{DiscreteFunctionP0Vector<const DataType>{discrete_function}} { - static_assert(std::is_same_v<std::remove_const_t<DataType>, double>, + static_assert(std::is_same_v<std::remove_const_t<DataType>, double> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<3, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<1, 1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<2, 2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<3, 3, double>>, "DiscreteFunctionP0Vector with this DataType is not allowed in variant"); } diff --git a/src/scheme/FluxingAdvectionSolver.cpp b/src/scheme/FluxingAdvectionSolver.cpp index d57eae2c4..6f85610e0 100644 --- a/src/scheme/FluxingAdvectionSolver.cpp +++ b/src/scheme/FluxingAdvectionSolver.cpp @@ -86,10 +86,15 @@ class FluxingAdvectionSolver m_remapped_list.emplace_back(copy(old_q.cellValues())); } + template <typename DataType> void - _storeValues(const DiscreteFunctionP0Vector<const double>& old_q) + _storeValues(const DiscreteFunctionP0Vector<const DataType>& old_q) { - m_remapped_list.emplace_back(copy(old_q.cellArrays())); + if constexpr (std::is_arithmetic_v<DataType>) { + m_remapped_list.emplace_back(copy(old_q.cellArrays())); + } else { + throw NormalError("remapping DiscreteFunctionP0Vector of non arithmetic data type is not supported"); + } } template <typename DataType> @@ -741,8 +746,12 @@ FluxingAdvectionSolver<MeshType>::remap( new_variables.push_back(std::make_shared<DiscreteFunctionVariant>( DiscreteFunctionT(m_new_mesh, std::get<CellValue<DataType>>(m_remapped_list[i])))); } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - new_variables.push_back(std::make_shared<DiscreteFunctionVariant>( - DiscreteFunctionT(m_new_mesh, std::get<CellArray<DataType>>(m_remapped_list[i])))); + if constexpr (std::is_arithmetic_v<DataType>) { + new_variables.push_back(std::make_shared<DiscreteFunctionVariant>( + DiscreteFunctionT(m_new_mesh, std::get<CellArray<DataType>>(m_remapped_list[i])))); + } else { + throw NormalError("remapping DiscreteFunctionP0Vector of non arithmetic data type is not supported"); + } } else { throw UnexpectedError("invalid discrete function type"); } diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 2af32e157..7d2c58ac5 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -740,9 +740,13 @@ PolynomialReconstruction::_createMutableDiscreteFunctionDPKVariantList( DiscreteFunctionDPk<MeshType::Dimension, DataType>(p_mesh, m_descriptor.degree())); } 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, m_descriptor.degree(), - discrete_function.size())); + if constexpr (std::is_arithmetic_v<DataType>) { + mutable_discrete_function_dpk_variant_list.push_back( + DiscreteFunctionDPkVector<MeshType::Dimension, DataType>(p_mesh, m_descriptor.degree(), + discrete_function.size())); + } else { + throw NotImplementedError("reconstruction of DiscreteFunctionP0Vector of non arithmetic data type"); + } } else { // LCOV_EXCL_START throw UnexpectedError("unexpected discrete function type"); @@ -945,32 +949,37 @@ PolynomialReconstruction::_build( column_begin += DataType::Dimension; } } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - const auto qj_vector = discrete_function[cell_j_id]; - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - for (size_t l = 0; l < qj_vector.size(); ++l) { - const DataType& qj = qj_vector[l]; - const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; - B(index, column_begin + l) = qi_qj; - } - } + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); - ++i_symmetry) { - auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; + if constexpr (std::is_arithmetic_v<DataType>) { + const auto qj_vector = discrete_function[cell_j_id]; + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; for (size_t l = 0; l < qj_vector.size(); ++l) { const DataType& qj = qj_vector[l]; const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; B(index, column_begin + l) = qi_qj; } } + + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; + B(index, column_begin + l) = qi_qj; + } + } + } + column_begin += qj_vector.size(); + } else { + throw NotImplementedError("reconstruction of DiscreteFunctionP0Vector of non arithmetic data type"); } - column_begin += qj_vector.size(); } else { // LCOV_EXCL_START throw UnexpectedError("invalid discrete function type"); -- GitLab From 048c9123d5b9cddcda30d169288b6d968730b374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 19 Nov 2024 15:27:38 +0100 Subject: [PATCH 079/122] Allow TinyVector and TinyMatrix in DiscreteFunctionDPkVector --- src/scheme/DiscreteFunctionDPkVariant.hpp | 30 +++++++++++++++++++++-- src/scheme/PolynomialReconstruction.cpp | 22 ++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/scheme/DiscreteFunctionDPkVariant.hpp b/src/scheme/DiscreteFunctionDPkVariant.hpp index 0d683c178..2942f141f 100644 --- a/src/scheme/DiscreteFunctionDPkVariant.hpp +++ b/src/scheme/DiscreteFunctionDPkVariant.hpp @@ -35,8 +35,28 @@ class DiscreteFunctionDPkVariant DiscreteFunctionDPk<3, const TinyMatrix<3>>, DiscreteFunctionDPkVector<1, const double>, + DiscreteFunctionDPkVector<1, const TinyVector<1>>, + DiscreteFunctionDPkVector<1, const TinyVector<2>>, + DiscreteFunctionDPkVector<1, const TinyVector<3>>, + DiscreteFunctionDPkVector<1, const TinyMatrix<1>>, + DiscreteFunctionDPkVector<1, const TinyMatrix<2>>, + DiscreteFunctionDPkVector<1, const TinyMatrix<3>>, + DiscreteFunctionDPkVector<2, const double>, - DiscreteFunctionDPkVector<3, const double>>; + DiscreteFunctionDPkVector<2, const TinyVector<1>>, + DiscreteFunctionDPkVector<2, const TinyVector<2>>, + DiscreteFunctionDPkVector<2, const TinyVector<3>>, + DiscreteFunctionDPkVector<2, const TinyMatrix<1>>, + DiscreteFunctionDPkVector<2, const TinyMatrix<2>>, + DiscreteFunctionDPkVector<2, const TinyMatrix<3>>, + + DiscreteFunctionDPkVector<3, const double>, + DiscreteFunctionDPkVector<3, const TinyVector<1>>, + DiscreteFunctionDPkVector<3, const TinyVector<2>>, + DiscreteFunctionDPkVector<3, const TinyVector<3>>, + DiscreteFunctionDPkVector<3, const TinyMatrix<1>>, + DiscreteFunctionDPkVector<3, const TinyMatrix<2>>, + DiscreteFunctionDPkVector<3, const TinyMatrix<3>>>; private: Variant m_discrete_function_dpk; @@ -91,7 +111,13 @@ class DiscreteFunctionDPkVariant DiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) : m_discrete_function_dpk{DiscreteFunctionDPkVector<Dimension, const DataType>{discrete_function_dpk}} { - static_assert(std::is_same_v<std::remove_const_t<DataType>, double>, + static_assert(std::is_same_v<std::remove_const_t<DataType>, double> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyVector<3, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<1, 1, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<2, 2, double>> or // + std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<3, 3, double>>, "DiscreteFunctionDPkVector with this DataType is not allowed in variant"); } diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 7d2c58ac5..d23644a72 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -69,8 +69,28 @@ class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant DiscreteFunctionDPk<3, TinyMatrix<3>>, DiscreteFunctionDPkVector<1, double>, + DiscreteFunctionDPkVector<1, TinyVector<1>>, + DiscreteFunctionDPkVector<1, TinyVector<2>>, + DiscreteFunctionDPkVector<1, TinyVector<3>>, + DiscreteFunctionDPkVector<1, TinyMatrix<1>>, + DiscreteFunctionDPkVector<1, TinyMatrix<2>>, + DiscreteFunctionDPkVector<1, TinyMatrix<3>>, + DiscreteFunctionDPkVector<2, double>, - DiscreteFunctionDPkVector<3, double>>; + DiscreteFunctionDPkVector<2, TinyVector<1>>, + DiscreteFunctionDPkVector<2, TinyVector<2>>, + DiscreteFunctionDPkVector<2, TinyVector<3>>, + DiscreteFunctionDPkVector<2, TinyMatrix<1>>, + DiscreteFunctionDPkVector<2, TinyMatrix<2>>, + DiscreteFunctionDPkVector<2, TinyMatrix<3>>, + + DiscreteFunctionDPkVector<3, double>, + DiscreteFunctionDPkVector<3, TinyVector<1>>, + DiscreteFunctionDPkVector<3, TinyVector<2>>, + DiscreteFunctionDPkVector<3, TinyVector<3>>, + DiscreteFunctionDPkVector<3, TinyMatrix<1>>, + DiscreteFunctionDPkVector<3, TinyMatrix<2>>, + DiscreteFunctionDPkVector<3, TinyMatrix<3>>>; private: Variant m_mutable_discrete_function_dpk; -- GitLab From 68ef5ec9a822a8f7120e53e6fd51c9de76ec8bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 19 Nov 2024 19:23:03 +0100 Subject: [PATCH 080/122] [ci-skip] WIP: Add reconstrcution for DiscreteFunctionDPkVector of TinyVector Almost works, remains to fix dispatching after reconstruction --- src/scheme/PolynomialReconstruction.cpp | 99 +++++++++++++++++++++---- tests/test_PolynomialReconstruction.cpp | 80 ++++++++++++++++++++ 2 files changed, 164 insertions(+), 15 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index d23644a72..783ec01ad 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -143,7 +143,13 @@ class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) : m_mutable_discrete_function_dpk{discrete_function_dpk} { - static_assert(std::is_same_v<DataType, double>, + static_assert(std::is_same_v<DataType, double> or // + std::is_same_v<DataType, TinyVector<1, double>> or // + std::is_same_v<DataType, TinyVector<2, double>> or // + std::is_same_v<DataType, TinyVector<3, double>> or // + std::is_same_v<DataType, TinyMatrix<1, 1, double>> or // + std::is_same_v<DataType, TinyMatrix<2, 2, double>> or // + std::is_same_v<DataType, TinyMatrix<3, 3, double>>, "DiscreteFunctionDPkVector with this DataType is not allowed in variant"); } @@ -718,7 +724,7 @@ PolynomialReconstruction::_getNumberOfColumns( using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { using data_type = std::decay_t<typename DiscreteFunctionT::data_type>; - if constexpr (std::is_same_v<data_type, double>) { + if constexpr (std::is_arithmetic_v<data_type>) { return 1; } else if constexpr (is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>) { return data_type::Dimension; @@ -728,7 +734,16 @@ PolynomialReconstruction::_getNumberOfColumns( // LCOV_EXCL_STOP } } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - return discrete_function.size(); + using data_type = std::decay_t<typename DiscreteFunctionT::data_type>; + if constexpr (std::is_arithmetic_v<data_type>) { + return discrete_function.size(); + } else if constexpr (is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>) { + return discrete_function.size() * data_type::Dimension; + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected data type " + demangle<data_type>()); + // LCOV_EXCL_STOP + } } else { // LCOV_EXCL_START throw UnexpectedError("unexpected discrete function type"); @@ -760,13 +775,9 @@ PolynomialReconstruction::_createMutableDiscreteFunctionDPKVariantList( DiscreteFunctionDPk<MeshType::Dimension, DataType>(p_mesh, m_descriptor.degree())); } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { using DataType = std::remove_const_t<std::decay_t<typename DiscreteFunctionT::data_type>>; - if constexpr (std::is_arithmetic_v<DataType>) { - mutable_discrete_function_dpk_variant_list.push_back( - DiscreteFunctionDPkVector<MeshType::Dimension, DataType>(p_mesh, m_descriptor.degree(), - discrete_function.size())); - } else { - throw NotImplementedError("reconstruction of DiscreteFunctionP0Vector of non arithmetic data type"); - } + mutable_discrete_function_dpk_variant_list.push_back( + DiscreteFunctionDPkVector<MeshType::Dimension, DataType>(p_mesh, m_descriptor.degree(), + discrete_function.size())); } else { // LCOV_EXCL_START throw UnexpectedError("unexpected discrete function type"); @@ -951,7 +962,7 @@ PolynomialReconstruction::_build( } else if constexpr (is_tiny_matrix_v<DataType>) { if constexpr ((DataType::NumberOfColumns == DataType::NumberOfRows) and (DataType::NumberOfColumns == MeshType::Dimension)) { - throw NotImplementedError("TinyMatrix symmetries for reconstruction"); + throw NotImplementedError("TinyMatrix symmetries for reconstruction of DiscreteFunctionP0"); } const DataType& qi_qj = discrete_function[cell_i_id] - qj; for (size_t k = 0; k < DataType::NumberOfRows; ++k) { @@ -971,9 +982,10 @@ PolynomialReconstruction::_build( } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + const auto qj_vector = discrete_function[cell_j_id]; + if constexpr (std::is_arithmetic_v<DataType>) { - const auto qj_vector = discrete_function[cell_j_id]; - size_t index = 0; + size_t index = 0; for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { const CellId cell_i_id = stencil_cell_list[i]; for (size_t l = 0; l < qj_vector.size(); ++l) { @@ -996,10 +1008,47 @@ PolynomialReconstruction::_build( } } } + } else if constexpr (is_tiny_vector_v<DataType>) { + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; + for (size_t kB = column_begin + l * DataType::Dimension, k = 0; k < DataType::Dimension; + ++k, ++kB) { + B(index, kB) = qi_qj[k]; + } + } + } + + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + if (ghost_cell_list.size() > 0) { + throw NotImplementedError("TinyVector symmetries for reconstruction of DiscreteFunctionP0Vector"); + } + } + } else if constexpr (is_tiny_matrix_v<DataType>) { + throw NotImplementedError("NIY TinyMatrix data"); + + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + if (ghost_cell_list.size() > 0) { + throw NotImplementedError("TinyMatrix symmetries for reconstruction of DiscreteFunctionP0Vector"); + } + } + } + + if constexpr (std::is_arithmetic_v<DataType>) { column_begin += qj_vector.size(); - } else { - throw NotImplementedError("reconstruction of DiscreteFunctionP0Vector of non arithmetic data type"); + } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) { + column_begin += qj_vector.size() * DataType::Dimension; } + } else { // LCOV_EXCL_START throw UnexpectedError("invalid discrete function type"); @@ -1212,6 +1261,8 @@ PolynomialReconstruction::_build( Givens::solveCollectionInPlace(A, X, B); } + std::clog << "X = " << X << '\n'; + column_begin = 0; for (size_t i_dpk_variant = 0; i_dpk_variant < mutable_discrete_function_dpk_variant_list.size(); ++i_dpk_variant) { @@ -1318,6 +1369,24 @@ PolynomialReconstruction::_build( dpk_j_ip1 = X(i, column_begin); } ++column_begin; + } else if constexpr (is_tiny_vector_v<DataType>) { + if (m_descriptor.degree() > 1) { + auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + for (size_t i = 0; i < basis_dimension - 1; ++i) { + auto& dpk_j_0 = dpk_j[0]; + for (size_t k = 0; k < DataType::Dimension; ++k) { + dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; + } + } + } + + for (size_t i = 0; i < basis_dimension - 1; ++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 { // LCOV_EXCL_START throw UnexpectedError("unexpected data type"); diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index cdfb532f2..1c078129b 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -242,6 +242,86 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } } + SECTION("R3 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_affine0 = [](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 vector_affine1 = [](const R1& x) -> R3 { + return R3{+1.6 + 0.7 * x[0], -2.1 + 1.2 * x[0], +1.1 - 0.3 * x[0]}; + }; + + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0Vector<R3> Vh{p_mesh, 2}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + Vh[cell_id][0] = vector_affine0(xj[cell_id]); + Vh[cell_id][1] = vector_affine1(xj[cell_id]); + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R3>>(); + + { + 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_Vh(cell_id, 0)(xj[cell_id]) - vector_affine0(xj[cell_id]))); + max_mean_error = + std::max(max_mean_error, l2Norm(dpk_Vh(cell_id, 1)(xj[cell_id]) - vector_affine1(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + { + const TinyVector<3> slope0{+1.7, +2.1, -0.6}; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < R3::Dimension; ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R1{0.1} + xj[cell_id])[i] - + dpk_Vh(cell_id, 0)(xj[cell_id] - R1{0.1})[i]); + + std::clog << "reconstructed_slop0[" << i << "]=" << reconstructed_slope << " | expected slope0[" + << i << "]=" << slope0[i] << '\n'; + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope0[i])); + } + } + } + + { + const TinyVector<3> slope1{+0.7, +1.2, -0.3}; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < R3::Dimension; ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R1{0.1} + xj[cell_id])[i] - + dpk_Vh(cell_id, 1)(xj[cell_id] - R1{0.1})[i]); + + std::clog << "reconstructed_slop1[" << i << "]=" << reconstructed_slope << " | expected slope1[" + << i << "]=" << slope1[i] << '\n'; + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope1[i])); + } + } + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + } + SECTION("list of various types") { using R3x3 = TinyMatrix<3>; -- GitLab From 0a6dd5e6813f283c8644223d95b4bc88d57d361d Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Tue, 19 Nov 2024 22:22:19 +0100 Subject: [PATCH 081/122] Fix reconstrcution for DiscreteFunctionDPkVector of TinyVector --- src/scheme/PolynomialReconstruction.cpp | 4 +--- tests/test_PolynomialReconstruction.cpp | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 783ec01ad..0e0b2a5af 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -1261,8 +1261,6 @@ PolynomialReconstruction::_build( Givens::solveCollectionInPlace(A, X, B); } - std::clog << "X = " << X << '\n'; - column_begin = 0; for (size_t i_dpk_variant = 0; i_dpk_variant < mutable_discrete_function_dpk_variant_list.size(); ++i_dpk_variant) { @@ -1381,7 +1379,7 @@ PolynomialReconstruction::_build( } for (size_t i = 0; i < basis_dimension - 1; ++i) { - auto& dpk_j_ip1 = dpk_j[i + 1]; + auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; for (size_t k = 0; k < DataType::Dimension; ++k) { dpk_j_ip1[k] = X(i, column_begin + k); } diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp index 1c078129b..3fd31d952 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction.cpp @@ -295,8 +295,6 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R1{0.1} + xj[cell_id])[i] - dpk_Vh(cell_id, 0)(xj[cell_id] - R1{0.1})[i]); - std::clog << "reconstructed_slop0[" << i << "]=" << reconstructed_slope << " | expected slope0[" - << i << "]=" << slope0[i] << '\n'; max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope0[i])); } } @@ -310,8 +308,6 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R1{0.1} + xj[cell_id])[i] - dpk_Vh(cell_id, 1)(xj[cell_id] - R1{0.1})[i]); - std::clog << "reconstructed_slop1[" << i << "]=" << reconstructed_slope << " | expected slope1[" - << i << "]=" << slope1[i] << '\n'; max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope1[i])); } } -- GitLab From d4323f874fa27283b88233d2113ded6e9aed155e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 5 Dec 2024 19:27:19 +0100 Subject: [PATCH 082/122] Add arbitrary number of layers building for stencils and tests --- src/mesh/StencilBuilder.cpp | 567 ++++++------ src/mesh/StencilBuilder.hpp | 10 +- tests/NbGhostLayersTester.hpp | 27 + tests/test_ConnectivityDispatcher.cpp | 19 +- tests/test_StencilBuilder.cpp | 1149 ++++++++++++++++++++----- 5 files changed, 1231 insertions(+), 541 deletions(-) create mode 100644 tests/NbGhostLayersTester.hpp diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 31db762a1..96f36a14f 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -5,16 +5,23 @@ #include <utils/GlobalVariableManager.hpp> #include <utils/Messenger.hpp> -#include <set> - template <ItemType item_type> class StencilBuilder::Layer { using ItemId = ItemIdT<item_type>; + ItemValue<const int, item_type> m_number_of; + std::vector<ItemId> m_item_id_vector; std::vector<int> m_item_number_vector; public: + void + clear() + { + m_item_id_vector.clear(); + m_item_number_vector.clear(); + } + size_t size() const { @@ -22,9 +29,17 @@ class StencilBuilder::Layer return m_item_id_vector.size(); } + const std::vector<ItemId>& + itemIdList() const + { + return m_item_id_vector; + } + bool - hasItemNumber(const int item_number) const + hasItemNumber(const ItemId item_id) const { + const int item_number = m_number_of[item_id]; + ssize_t begin = 0; ssize_t end = m_item_number_vector.size(); @@ -37,26 +52,22 @@ class StencilBuilder::Layer if (begin == mid) { break; } - begin = mid - 1; + begin = mid; } else { if (end == mid) { break; } - end = mid + 1; + end = mid; } } return false; } - const std::vector<ItemId>& - itemIdList() const - { - return m_item_id_vector; - } - void - add(const ItemId item_id, const int item_number) + add(const ItemId item_id) { + const int item_number = m_number_of[item_id]; + ssize_t begin = 0; ssize_t end = m_item_number_vector.size(); @@ -97,7 +108,14 @@ class StencilBuilder::Layer } } - Layer() = default; + Layer& operator=(const Layer&) = default; + Layer& operator=(Layer&&) = default; + + template <size_t Dimension> + Layer(const Connectivity<Dimension>& connectivity) : m_number_of{connectivity.template number<item_type>()} + {} + + // Layer() = default; Layer(const Layer&) = default; Layer(Layer&&) = default; @@ -117,41 +135,8 @@ StencilBuilder::_buildSymmetryConnectingItemList(const ConnectivityType& connect symmetry_boundary_descriptor_list.size()}; symmetry_connecting_item_list.fill(false); - if constexpr (ConnectivityType::Dimension > 1) { - auto face_to_connecting_item_matrix = - connectivity.template getItemToItemMatrix<ItemType::face, connecting_item_type>(); - size_t i_symmetry_boundary = 0; - for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { - const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; - - bool found = false; - for (size_t i_ref_face_list = 0; i_ref_face_list < connectivity.template numberOfRefItemList<ItemType::face>(); - ++i_ref_face_list) { - const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i_ref_face_list); - if (ref_face_list.refId() == boundary_descriptor) { - found = true; - for (size_t i_face = 0; i_face < ref_face_list.list().size(); ++i_face) { - const FaceId face_id = ref_face_list.list()[i_face]; - auto connecting_item_list = face_to_connecting_item_matrix[face_id]; - for (size_t i_connecting_item = 0; i_connecting_item < connecting_item_list.size(); ++i_connecting_item) { - const ConnectingItemId connecting_item_id = connecting_item_list[i_connecting_item]; - - symmetry_connecting_item_list[connecting_item_id][i_symmetry_boundary] = true; - } - } - break; - } - } - ++i_symmetry_boundary; - if (not found) { - std::ostringstream error_msg; - error_msg << "cannot find boundary '" << rang::fgB::yellow << boundary_descriptor << rang::fg::reset << '\''; - throw NormalError(error_msg.str()); - } - } - - return symmetry_connecting_item_list; - } else { + if constexpr (ItemTypeId<ConnectivityType::Dimension>::itemTId(connecting_item_type) == + ItemTypeId<ConnectivityType::Dimension>::itemTId(ItemType::face)) { size_t i_symmetry_boundary = 0; for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; @@ -180,110 +165,41 @@ StencilBuilder::_buildSymmetryConnectingItemList(const ConnectivityType& connect throw NormalError(error_msg.str()); } } + } else { + auto face_to_connecting_item_matrix = + connectivity.template getItemToItemMatrix<ItemType::face, connecting_item_type>(); + size_t i_symmetry_boundary = 0; + for (auto p_boundary_descriptor : symmetry_boundary_descriptor_list) { + const IBoundaryDescriptor& boundary_descriptor = *p_boundary_descriptor; - return symmetry_connecting_item_list; - } -} - -template <typename ConnectivityType> -Array<const uint32_t> -StencilBuilder::_getRowMap(const ConnectivityType& connectivity) const -{ - auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); - auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); - - auto cell_is_owned = connectivity.cellIsOwned(); - - Array<uint32_t> row_map{connectivity.numberOfCells() + 1}; - row_map[0] = 0; - std::vector<CellId> neighbors; - for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { - neighbors.resize(0); - // The stencil is not built for ghost cells - if (cell_is_owned[cell_id]) { - auto cell_nodes = cell_to_node_matrix[cell_id]; - for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { - const NodeId node_id = cell_nodes[i_node]; - auto node_cells = node_to_cell_matrix[node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cells.size(); ++i_node_cell) { - const CellId node_cell_id = node_cells[i_node_cell]; - if (node_cell_id != cell_id) { - neighbors.push_back(node_cells[i_node_cell]); - } - } - } - std::sort(neighbors.begin(), neighbors.end()); - neighbors.erase(std::unique(neighbors.begin(), neighbors.end()), neighbors.end()); - } - // The cell itself is not counted - row_map[cell_id + 1] = row_map[cell_id] + neighbors.size(); - } - - return row_map; -} - -template <typename ConnectivityType> -Array<const uint32_t> -StencilBuilder::_getColumnIndices(const ConnectivityType& connectivity, const Array<const uint32_t>& row_map) const -{ - auto cell_number = connectivity.cellNumber(); - - Array<uint32_t> max_index(row_map.size() - 1); - parallel_for( - max_index.size(), PUGS_LAMBDA(size_t i) { max_index[i] = row_map[i]; }); - - auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); - auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); - - auto cell_is_owned = connectivity.cellIsOwned(); - - Array<uint32_t> column_indices(row_map[row_map.size() - 1]); - column_indices.fill(std::numeric_limits<uint32_t>::max()); - - for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { - // The stencil is not built for ghost cells - if (cell_is_owned[cell_id]) { - auto cell_nodes = cell_to_node_matrix[cell_id]; - for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { - const NodeId node_id = cell_nodes[i_node]; - auto node_cells = node_to_cell_matrix[node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cells.size(); ++i_node_cell) { - const CellId node_cell_id = node_cells[i_node_cell]; - if (node_cell_id != cell_id) { - bool found = false; - for (size_t i_index = row_map[cell_id]; i_index < max_index[cell_id]; ++i_index) { - if (column_indices[i_index] == node_cell_id) { - found = true; - break; - } - } - if (not found) { - const auto node_cell_number = cell_number[node_cell_id]; - size_t i_index = row_map[cell_id]; - // search for position for index - while ((i_index < max_index[cell_id])) { - if (node_cell_number > cell_number[CellId(column_indices[i_index])]) { - ++i_index; - } else { - break; - } - } - - for (size_t i_destination = max_index[cell_id]; i_destination > i_index; --i_destination) { - const size_t i_source = i_destination - 1; + bool found = false; + for (size_t i_ref_face_list = 0; i_ref_face_list < connectivity.template numberOfRefItemList<ItemType::face>(); + ++i_ref_face_list) { + const auto& ref_face_list = connectivity.template refItemList<ItemType::face>(i_ref_face_list); + if (ref_face_list.refId() == boundary_descriptor) { + found = true; + for (size_t i_face = 0; i_face < ref_face_list.list().size(); ++i_face) { + const FaceId face_id = ref_face_list.list()[i_face]; + auto connecting_item_list = face_to_connecting_item_matrix[face_id]; + for (size_t i_connecting_item = 0; i_connecting_item < connecting_item_list.size(); ++i_connecting_item) { + const ConnectingItemId connecting_item_id = connecting_item_list[i_connecting_item]; - column_indices[i_destination] = column_indices[i_source]; - } - ++max_index[cell_id]; - column_indices[i_index] = node_cell_id; + symmetry_connecting_item_list[connecting_item_id][i_symmetry_boundary] = true; } } + break; } } + ++i_symmetry_boundary; + if (not found) { + std::ostringstream error_msg; + error_msg << "cannot find boundary '" << rang::fgB::yellow << boundary_descriptor << rang::fg::reset << '\''; + throw NormalError(error_msg.str()); + } } } - return column_indices; + return symmetry_connecting_item_list; } template <ItemType item_type, ItemType connecting_item_type, typename ConnectivityType> @@ -295,9 +211,7 @@ StencilBuilder::_build_for_same_source_and_target(const ConnectivityType& connec if (number_of_layers == 0) { throw NormalError("number of layers must be greater than 0 to build stencils"); } - if (number_of_layers > 2) { - throw NotImplementedError("number of layers too large"); - } + auto item_to_connecting_item_matrix = connectivity.template getItemToItemMatrix<item_type, connecting_item_type>(); auto connecting_item_to_item_matrix = connectivity.template getItemToItemMatrix<connecting_item_type, item_type>(); @@ -308,215 +222,250 @@ StencilBuilder::_build_for_same_source_and_target(const ConnectivityType& connec using ItemId = ItemIdT<item_type>; using ConnectingItemId = ItemIdT<connecting_item_type>; - if (symmetry_boundary_descriptor_list.size() == 0) { - if (number_of_layers == 1) { - Array<uint32_t> row_map{connectivity.template numberOf<item_type>() + 1}; - row_map[0] = 0; + ItemArray<bool, connecting_item_type> symmetry_item_list = + this->_buildSymmetryConnectingItemList<connecting_item_type>(connectivity, symmetry_boundary_descriptor_list); - std::vector<ItemId> column_indices_vector; + Array<uint32_t> row_map{connectivity.template numberOf<item_type>() + 1}; + row_map[0] = 0; + std::vector<Array<uint32_t>> symmetry_row_map_list(symmetry_boundary_descriptor_list.size()); + for (auto&& symmetry_row_map : symmetry_row_map_list) { + symmetry_row_map = Array<uint32_t>{connectivity.template numberOf<item_type>() + 1}; + symmetry_row_map[0] = 0; + } - for (ItemId item_id = 0; item_id < connectivity.template numberOf<item_type>(); ++item_id) { - // First layer is a special case + std::vector<ItemId> column_indices_vector; + std::vector<std::vector<uint32_t>> symmetry_column_indices_vector(symmetry_boundary_descriptor_list.size()); - Layer<item_type> item_layer; - Layer<connecting_item_type> connecting_layer; + std::vector<Layer<item_type>> item_layer_list; + std::vector<Layer<connecting_item_type>> connecting_layer_list; - if (item_is_owned[item_id]) { - for (size_t i_connecting_item_1 = 0; i_connecting_item_1 < item_to_connecting_item_matrix[item_id].size(); - ++i_connecting_item_1) { - const ConnectingItemId layer_1_connecting_item_id = - item_to_connecting_item_matrix[item_id][i_connecting_item_1]; - connecting_layer.add(layer_1_connecting_item_id, connecting_item_number[layer_1_connecting_item_id]); - } + std::vector<Layer<item_type>> symmetry_item_layer_list; + std::vector<Layer<connecting_item_type>> symmetry_connecting_layer_list; - for (auto connecting_item_id : connecting_layer.itemIdList()) { - for (size_t i_item_1 = 0; i_item_1 < connecting_item_to_item_matrix[connecting_item_id].size(); - ++i_item_1) { - const ItemId layer_1_item_id = connecting_item_to_item_matrix[connecting_item_id][i_item_1]; - if (layer_1_item_id != item_id) { - item_layer.add(layer_1_item_id, item_number[layer_1_item_id]); - } - } - } - } + for (size_t i = 0; i < number_of_layers; ++i) { + item_layer_list.emplace_back(Layer<item_type>{connectivity}); + connecting_layer_list.emplace_back(Layer<connecting_item_type>{connectivity}); - for (auto layer_item_id : item_layer.itemIdList()) { - column_indices_vector.push_back(layer_item_id); - } - row_map[item_id + 1] = row_map[item_id] + item_layer.itemIdList().size(); - } + if (symmetry_boundary_descriptor_list.size() > 0) { + symmetry_item_layer_list.emplace_back(Layer<item_type>{connectivity}); + symmetry_connecting_layer_list.emplace_back(Layer<connecting_item_type>{connectivity}); + } + } - if (row_map[row_map.size() - 1] != column_indices_vector.size()) { - throw UnexpectedError("incorrect stencil size"); + for (ItemId item_id = 0; item_id < connectivity.template numberOf<item_type>(); ++item_id) { + if (item_is_owned[item_id]) { + for (auto&& item_layer : item_layer_list) { + item_layer.clear(); + } + for (auto&& connecting_layer : connecting_layer_list) { + connecting_layer.clear(); } - Array<uint32_t> column_indices(row_map[row_map.size() - 1]); - column_indices.fill(std::numeric_limits<uint32_t>::max()); - for (size_t i = 0; i < column_indices.size(); ++i) { - column_indices[i] = column_indices_vector[i]; + // First layer is a special case + { + auto& item_layer = item_layer_list[0]; + auto& connecting_layer = connecting_layer_list[0]; + + for (size_t i_connecting_item = 0; i_connecting_item < item_to_connecting_item_matrix[item_id].size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = item_to_connecting_item_matrix[item_id][i_connecting_item]; + + connecting_layer.add(connecting_item_id); + } + + for (auto connecting_item_id : connecting_layer.itemIdList()) { + for (size_t i_item = 0; i_item < connecting_item_to_item_matrix[connecting_item_id].size(); ++i_item) { + const ItemId layer_item_id = connecting_item_to_item_matrix[connecting_item_id][i_item]; + if (layer_item_id != item_id) { + item_layer.add(layer_item_id); + } + } + } } - return {ConnectivityMatrix{row_map, column_indices}, {}}; - } else { - Array<uint32_t> row_map{connectivity.template numberOf<item_type>() + 1}; - row_map[0] = 0; - - std::vector<ItemId> column_indices_vector; - - for (ItemId item_id = 0; item_id < connectivity.template numberOf<item_type>(); ++item_id) { - if (item_is_owned[item_id]) { - std::set<ItemId, std::function<bool(ItemId, ItemId)>> item_set( - [=](ItemId item_0, ItemId item_1) { return item_number[item_0] < item_number[item_1]; }); - - for (size_t i_connecting_item_1 = 0; i_connecting_item_1 < item_to_connecting_item_matrix[item_id].size(); - ++i_connecting_item_1) { - const ConnectingItemId layer_1_connecting_item_id = - item_to_connecting_item_matrix[item_id][i_connecting_item_1]; - - for (size_t i_item_1 = 0; i_item_1 < connecting_item_to_item_matrix[layer_1_connecting_item_id].size(); - ++i_item_1) { - ItemId item_1_id = connecting_item_to_item_matrix[layer_1_connecting_item_id][i_item_1]; - - for (size_t i_connecting_item_2 = 0; - i_connecting_item_2 < item_to_connecting_item_matrix[item_1_id].size(); ++i_connecting_item_2) { - const ConnectingItemId layer_2_connecting_item_id = - item_to_connecting_item_matrix[item_1_id][i_connecting_item_2]; - - for (size_t i_item_2 = 0; i_item_2 < connecting_item_to_item_matrix[layer_2_connecting_item_id].size(); - ++i_item_2) { - ItemId item_2_id = connecting_item_to_item_matrix[layer_2_connecting_item_id][i_item_2]; - - if (item_2_id != item_id) { - item_set.insert(item_2_id); - } - } - } + for (size_t i_layer = 1; i_layer < number_of_layers; ++i_layer) { + auto& connecting_layer = connecting_layer_list[i_layer]; + + auto has_connecting_item = [&i_layer, &connecting_layer_list](ConnectingItemId connecting_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (connecting_layer_list[i].hasItemNumber(connecting_item_id)) { + return true; } } - for (auto stencil_item_id : item_set) { - column_indices_vector.push_back(stencil_item_id); + return false; + }; + + for (auto&& previous_layer_item_id_list : item_layer_list[i_layer - 1].itemIdList()) { + const auto item_to_connecting_item_list = item_to_connecting_item_matrix[previous_layer_item_id_list]; + for (size_t i_connecting_item = 0; i_connecting_item < item_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = item_to_connecting_item_list[i_connecting_item]; + + if (not has_connecting_item(connecting_item_id)) { + connecting_layer.add(connecting_item_id); + } } - row_map[item_id + 1] = row_map[item_id] + item_set.size(); } - } - if (row_map[row_map.size() - 1] != column_indices_vector.size()) { - throw UnexpectedError("incorrect stencil size"); - } + auto& item_layer = item_layer_list[i_layer]; - Array<uint32_t> column_indices(row_map[row_map.size() - 1]); - column_indices.fill(std::numeric_limits<uint32_t>::max()); + auto has_layer_item = [&i_layer, &item_layer_list](ItemId layer_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (item_layer_list[i].hasItemNumber(layer_item_id)) { + return true; + } + } - for (size_t i = 0; i < column_indices.size(); ++i) { - column_indices[i] = column_indices_vector[i]; - } - ConnectivityMatrix primal_stencil{row_map, column_indices}; + return false; + }; - return {primal_stencil, {}}; - } - } else { - ItemArray<bool, connecting_item_type> symmetry_item_list = - this->_buildSymmetryConnectingItemList<connecting_item_type>(connectivity, symmetry_boundary_descriptor_list); - - Array<uint32_t> row_map{connectivity.template numberOf<item_type>() + 1}; - row_map[0] = 0; - std::vector<Array<uint32_t>> symmetry_row_map_list(symmetry_boundary_descriptor_list.size()); - for (auto&& symmetry_row_map : symmetry_row_map_list) { - symmetry_row_map = Array<uint32_t>{connectivity.template numberOf<item_type>() + 1}; - symmetry_row_map[0] = 0; - } + for (auto connecting_item_id : connecting_layer.itemIdList()) { + const auto& connecting_item_to_item_list = connecting_item_to_item_matrix[connecting_item_id]; + for (size_t i_item = 0; i_item < connecting_item_to_item_list.size(); ++i_item) { + const ItemId layer_item_id = connecting_item_to_item_list[i_item]; + if ((layer_item_id != item_id) and (not has_layer_item(layer_item_id))) { + item_layer.add(layer_item_id); + } + } + } + } - std::vector<uint32_t> column_indices_vector; - std::vector<std::vector<uint32_t>> symmetry_column_indices_vector(symmetry_boundary_descriptor_list.size()); + for (size_t i_symmetry = 0; i_symmetry < symmetry_boundary_descriptor_list.size(); ++i_symmetry) { + for (auto&& symmetry_item_layer : symmetry_item_layer_list) { + symmetry_item_layer.clear(); + } + for (auto&& symmetry_connecting_layer : symmetry_connecting_layer_list) { + symmetry_connecting_layer.clear(); + } - for (ItemId item_id = 0; item_id < connectivity.template numberOf<item_type>(); ++item_id) { - std::set<ItemId> item_set; - std::vector<std::set<ItemId>> by_boundary_symmetry_item(symmetry_boundary_descriptor_list.size()); + // First layer is a special case + for (auto&& connecting_item_id : connecting_layer_list[0].itemIdList()) { + if (symmetry_item_list[connecting_item_id][i_symmetry]) { + symmetry_connecting_layer_list[0].add(connecting_item_id); + } + } - if (item_is_owned[item_id]) { - auto item_to_connecting_item_list = item_to_connecting_item_matrix[item_id]; - for (size_t i_connecting_item_of_item = 0; i_connecting_item_of_item < item_to_connecting_item_list.size(); - ++i_connecting_item_of_item) { - const ConnectingItemId connecting_item_id_of_item = item_to_connecting_item_list[i_connecting_item_of_item]; - auto connecting_item_to_item_list = connecting_item_to_item_matrix[connecting_item_id_of_item]; - for (size_t i_item_of_connecting_item = 0; i_item_of_connecting_item < connecting_item_to_item_list.size(); + for (auto&& connecting_item_id : symmetry_connecting_layer_list[0].itemIdList()) { + auto item_of_connecting_item_list = connecting_item_to_item_matrix[connecting_item_id]; + for (size_t i_item_of_connecting_item = 0; i_item_of_connecting_item < item_of_connecting_item_list.size(); ++i_item_of_connecting_item) { - const ItemId item_id_of_connecting_item = connecting_item_to_item_list[i_item_of_connecting_item]; - if (item_id != item_id_of_connecting_item) { - item_set.insert(item_id_of_connecting_item); - } + const ItemId item_id_of_connecting_item = item_of_connecting_item_list[i_item_of_connecting_item]; + symmetry_item_layer_list[0].add(item_id_of_connecting_item); } } - { - std::vector<ItemId> item_vector; - for (auto&& set_item_id : item_set) { - item_vector.push_back(set_item_id); + for (size_t i_layer = 1; i_layer < number_of_layers; ++i_layer) { + auto has_connecting_item = [&i_layer, + &symmetry_connecting_layer_list](ConnectingItemId connecting_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (symmetry_connecting_layer_list[i].hasItemNumber(connecting_item_id)) { + return true; + } + } + + return false; + }; + + for (auto&& symmetry_connecting_item_id : connecting_layer_list[i_layer].itemIdList()) { + if (symmetry_item_list[symmetry_connecting_item_id][i_symmetry]) { + if (not has_connecting_item(symmetry_connecting_item_id)) { + symmetry_connecting_layer_list[i_layer].add(symmetry_connecting_item_id); + } + } } - std::sort(item_vector.begin(), item_vector.end(), - [&item_number](const ItemId& item0_id, const ItemId& item1_id) { - return item_number[item0_id] < item_number[item1_id]; - }); - for (auto&& vector_item_id : item_vector) { - column_indices_vector.push_back(vector_item_id); + for (auto&& previous_layer_item_id_list : symmetry_item_layer_list[i_layer - 1].itemIdList()) { + const auto item_to_connecting_item_list = item_to_connecting_item_matrix[previous_layer_item_id_list]; + for (size_t i_connecting_item = 0; i_connecting_item < item_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = item_to_connecting_item_list[i_connecting_item]; + + if (not has_connecting_item(connecting_item_id)) { + symmetry_connecting_layer_list[i_layer].add(connecting_item_id); + } + } } - } - for (size_t i = 0; i < symmetry_boundary_descriptor_list.size(); ++i) { - std::set<ItemId> symmetry_item_set; - for (size_t i_connecting_item_of_item = 0; i_connecting_item_of_item < item_to_connecting_item_list.size(); - ++i_connecting_item_of_item) { - const ConnectingItemId connecting_item_id_of_item = item_to_connecting_item_list[i_connecting_item_of_item]; - if (symmetry_item_list[connecting_item_id_of_item][i]) { - auto item_of_connecting_item_list = connecting_item_to_item_matrix[connecting_item_id_of_item]; - for (size_t i_item_of_connecting_item = 0; - i_item_of_connecting_item < item_of_connecting_item_list.size(); ++i_item_of_connecting_item) { - const ItemId item_id_of_connecting_item = item_of_connecting_item_list[i_item_of_connecting_item]; - symmetry_item_set.insert(item_id_of_connecting_item); + auto has_symmetry_layer_item = [&i_layer, &symmetry_item_layer_list](ItemId layer_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (symmetry_item_layer_list[i].hasItemNumber(layer_item_id)) { + return true; + } + } + + return false; + }; + + for (auto&& connecting_item_id : symmetry_connecting_layer_list[i_layer].itemIdList()) { + auto item_of_connecting_item_list = connecting_item_to_item_matrix[connecting_item_id]; + for (size_t i_item_of_connecting_item = 0; i_item_of_connecting_item < item_of_connecting_item_list.size(); + ++i_item_of_connecting_item) { + const ItemId item_id_of_connecting_item = item_of_connecting_item_list[i_item_of_connecting_item]; + if (not has_symmetry_layer_item(item_id_of_connecting_item)) { + symmetry_item_layer_list[i_layer].add(item_id_of_connecting_item); } } } - by_boundary_symmetry_item[i] = symmetry_item_set; + } - std::vector<ItemId> item_vector; - for (auto&& set_item_id : symmetry_item_set) { - item_vector.push_back(set_item_id); + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + for (auto symmetry_layer_item_id : symmetry_item_layer_list[i_layer].itemIdList()) { + symmetry_column_indices_vector[i_symmetry].push_back(symmetry_layer_item_id); } - std::sort(item_vector.begin(), item_vector.end(), - [&item_number](const ItemId& item0_id, const ItemId& item1_id) { - return item_number[item0_id] < item_number[item1_id]; - }); + } - for (auto&& vector_item_id : item_vector) { - symmetry_column_indices_vector[i].push_back(vector_item_id); + { + size_t stencil_size = 0; + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + auto& item_layer = symmetry_item_layer_list[i_layer]; + stencil_size += item_layer.size(); } + + symmetry_row_map_list[i_symmetry][item_id + 1] = symmetry_row_map_list[i_symmetry][item_id] + stencil_size; } } - row_map[item_id + 1] = row_map[item_id] + item_set.size(); - for (size_t i = 0; i < symmetry_row_map_list.size(); ++i) { - symmetry_row_map_list[i][item_id + 1] = symmetry_row_map_list[i][item_id] + by_boundary_symmetry_item[i].size(); + { + size_t stencil_size = 0; + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + auto& item_layer = item_layer_list[i_layer]; + for (auto stencil_item_id : item_layer.itemIdList()) { + column_indices_vector.push_back(stencil_item_id); + } + stencil_size += item_layer.size(); + } + row_map[item_id + 1] = row_map[item_id] + stencil_size; } - } - ConnectivityMatrix primal_stencil{row_map, convert_to_array(column_indices_vector)}; - - typename StencilArray<item_type, item_type>::BoundaryDescriptorStencilArrayList symmetry_boundary_stencil_list; - { - size_t i = 0; - for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { - symmetry_boundary_stencil_list.emplace_back( - typename StencilArray<item_type, item_type>:: - BoundaryDescriptorStencilArray{p_boundary_descriptor, - ConnectivityMatrix{symmetry_row_map_list[i], - convert_to_array(symmetry_column_indices_vector[i])}}); - ++i; + + } else { + row_map[item_id + 1] = row_map[item_id]; + for (size_t i = 0; i < symmetry_row_map_list.size(); ++i) { + symmetry_row_map_list[i][item_id + 1] = symmetry_row_map_list[i][item_id]; } } + } - return {{primal_stencil}, {symmetry_boundary_stencil_list}}; + Array<uint32_t> column_indices{column_indices_vector.size()}; + parallel_for( + column_indices_vector.size(), PUGS_LAMBDA(size_t i) { column_indices[i] = column_indices_vector[i]; }); + + ConnectivityMatrix primal_stencil{row_map, column_indices}; + + typename StencilArray<item_type, item_type>::BoundaryDescriptorStencilArrayList symmetry_boundary_stencil_list; + { + size_t i = 0; + for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { + symmetry_boundary_stencil_list.emplace_back( + typename StencilArray<item_type, item_type>:: + BoundaryDescriptorStencilArray{p_boundary_descriptor, + ConnectivityMatrix{symmetry_row_map_list[i], + convert_to_array(symmetry_column_indices_vector[i])}}); + ++i; + } } + + return {{primal_stencil}, {symmetry_boundary_stencil_list}}; } template <ItemType source_item_type, diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index 4f7ead00b..f504cde90 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -19,13 +19,6 @@ class StencilBuilder template <ItemType item_type> class Layer; - template <typename ConnectivityType> - Array<const uint32_t> _getRowMap(const ConnectivityType& connectivity) const; - - template <typename ConnectivityType> - Array<const uint32_t> _getColumnIndices(const ConnectivityType& connectivity, - const Array<const uint32_t>& row_map) const; - template <ItemType connecting_item, typename ConnectivityType> auto _buildSymmetryConnectingItemList(const ConnectivityType& connectivity, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; @@ -74,6 +67,9 @@ class StencilBuilder const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const; friend class StencilManager; + + public: +#warning REMAKE THIS FUNCTION PRIVATE template <ItemType source_item_type, ItemType target_item_type> StencilArray<source_item_type, target_item_type> build(const IConnectivity& connectivity, diff --git a/tests/NbGhostLayersTester.hpp b/tests/NbGhostLayersTester.hpp new file mode 100644 index 000000000..0468fe183 --- /dev/null +++ b/tests/NbGhostLayersTester.hpp @@ -0,0 +1,27 @@ +#ifndef NB_GHOST_LAYERS_TESTER_HPP +#define NB_GHOST_LAYERS_TESTER_HPP + +#include <cstddef> +#include <utils/GlobalVariableManager.hpp> + +class NbGhostLayersTester +{ + private: + const size_t m_original_number_of_ghost_layers; + + public: + PUGS_INLINE + NbGhostLayersTester(const size_t number_of_ghost_layers) + : m_original_number_of_ghost_layers{GlobalVariableManager::instance().getNumberOfGhostLayers()} + { + GlobalVariableManager::instance().m_number_of_ghost_layers = number_of_ghost_layers; + } + + PUGS_INLINE + ~NbGhostLayersTester() + { + GlobalVariableManager::instance().m_number_of_ghost_layers = m_original_number_of_ghost_layers; + } +}; + +#endif // NB_GHOST_LAYERS_TESTER_HPP diff --git a/tests/test_ConnectivityDispatcher.cpp b/tests/test_ConnectivityDispatcher.cpp index abe07cc21..09ec9982e 100644 --- a/tests/test_ConnectivityDispatcher.cpp +++ b/tests/test_ConnectivityDispatcher.cpp @@ -9,29 +9,12 @@ #include <utils/Messenger.hpp> #include <MeshDataBaseForTests.hpp> +#include <NbGhostLayersTester.hpp> #include <filesystem> // clazy:excludeall=non-pod-global-static -class NbGhostLayersTester -{ - private: - const size_t m_original_number_of_ghost_layers; - - public: - NbGhostLayersTester(const size_t number_of_ghost_layers) - : m_original_number_of_ghost_layers{GlobalVariableManager::instance().getNumberOfGhostLayers()} - { - GlobalVariableManager::instance().m_number_of_ghost_layers = number_of_ghost_layers; - } - - ~NbGhostLayersTester() - { - GlobalVariableManager::instance().m_number_of_ghost_layers = m_original_number_of_ghost_layers; - } -}; - TEST_CASE("ConnectivityDispatcher", "[mesh]") { auto check_number_of_ghost_layers = [](const auto& connectivity, const size_t number_of_layers) { diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder.cpp index b99ba218a..8759b36c5 100644 --- a/tests/test_StencilBuilder.cpp +++ b/tests/test_StencilBuilder.cpp @@ -2,138 +2,420 @@ #include <catch2/matchers/catch_matchers_all.hpp> #include <MeshDataBaseForTests.hpp> +#include <mesh/CartesianMeshBuilder.hpp> #include <mesh/Connectivity.hpp> #include <mesh/ConnectivityUtils.hpp> #include <mesh/ItemValue.hpp> #include <mesh/ItemValueUtils.hpp> #include <mesh/Mesh.hpp> -#include <mesh/MeshFlatNodeBoundary.hpp> +#include <mesh/MeshFaceBoundary.hpp> #include <mesh/MeshVariant.hpp> #include <mesh/NamedBoundaryDescriptor.hpp> #include <mesh/StencilManager.hpp> #include <utils/Messenger.hpp> +#include <NbGhostLayersTester.hpp> + // clazy:excludeall=non-pod-global-static TEST_CASE("StencilBuilder", "[mesh]") { - SECTION("inner stencil") - { - auto is_valid = [](const auto& connectivity, const auto& stencil_array) { - auto cell_to_node_matrix = connectivity.cellToNodeMatrix(); - auto node_to_cell_matrix = connectivity.nodeToCellMatrix(); + auto is_valid = []<ItemType connecting_item_type>(const auto& connectivity, const auto& stencil_array, + const size_t number_of_layers) { + auto cell_to_connecting_item_matrix = + connectivity.template getItemToItemMatrix<ItemType::cell, connecting_item_type>(); + auto connecting_to_cell_matrix = connectivity.template getItemToItemMatrix<connecting_item_type, ItemType::cell>(); + auto cell_is_owned = connectivity.cellIsOwned(); + auto cell_number = connectivity.cellNumber(); + + using ConnectingItemId = ItemIdT<connecting_item_type>; - auto cell_is_owned = connectivity.cellIsOwned(); - auto cell_number = connectivity.cellNumber(); + for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { + if (cell_is_owned[cell_id]) { + std::vector<CellId> expected_stencil; + + std::set<CellId> marked_cell_set; + marked_cell_set.insert(cell_id); + + std::set<ConnectingItemId> marked_connecting_item_set; + std::set<ConnectingItemId> layer_connecting_item_set; + { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } - for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { - if (cell_is_owned[cell_id]) { + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); - auto cell_nodes = cell_to_node_matrix[cell_id]; - for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { - const NodeId node_id = cell_nodes[i_node]; - auto node_cells = node_to_cell_matrix[node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cells.size(); ++i_node_cell) { - const CellId node_cell_id = node_cells[i_node_cell]; - if (node_cell_id != cell_id) { - cell_set.insert(node_cell_id); + + for (auto&& connecting_item_id : layer_connecting_item_set) { + auto connecting_item_to_cell_list = connecting_to_cell_matrix[connecting_item_id]; + for (size_t i_connecting_item_of_cell = 0; i_connecting_item_of_cell < connecting_item_to_cell_list.size(); + ++i_connecting_item_of_cell) { + const CellId connecting_item_id_of_cell = connecting_item_to_cell_list[i_connecting_item_of_cell]; + if (not marked_cell_set.contains(connecting_item_id_of_cell)) { + cell_set.insert(connecting_item_id_of_cell); + marked_cell_set.insert(connecting_item_id_of_cell); } } } - auto cell_stencil = stencil_array[cell_id]; - - auto i_set_cell = cell_set.begin(); - for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { - if (*i_set_cell != cell_stencil[index]) { - return false; + layer_connecting_item_set.clear(); + for (auto layer_cell_id : cell_set) { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[layer_cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + if (not marked_connecting_item_set.contains(connecting_item_id)) { + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } } } + + for (auto&& set_cell_id : cell_set) { + expected_stencil.push_back(set_cell_id); + } + } + + auto cell_stencil = stencil_array[cell_id]; + + auto i_set_cell = expected_stencil.begin(); + if (cell_stencil.size() != expected_stencil.size()) { + return false; + } + + for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { + if (*i_set_cell != cell_stencil[index]) { + return false; + } } } - return true; - }; + } + return true; + }; - SECTION("1D") + auto are_stencil_by_nodes_valid = [&](const auto& connectivity, const auto& stencil_array, + const size_t number_of_layers) { + return is_valid.template operator()<ItemType::node>(connectivity, stencil_array, number_of_layers); + }; + + auto are_stencil_by_faces_valid = [&](const auto& connectivity, const auto& stencil_array, + const size_t number_of_layers) { + return is_valid.template operator()<ItemType::face>(connectivity, stencil_array, number_of_layers); + }; + + auto are_stencil_by_edges_valid = [&](const auto& connectivity, const auto& stencil_array, + const size_t number_of_layers) { + return is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, number_of_layers); + }; + + SECTION("inner stencil") + { + SECTION("1 layer") { - SECTION("cartesian") + SECTION("1D") { - const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - const Connectivity<1>& connectivity = mesh.connectivity(); + const Connectivity<1>& connectivity = mesh.connectivity(); - auto stencil_array = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}); + REQUIRE( + are_stencil_by_nodes_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); - REQUIRE(is_valid(connectivity, stencil_array)); - } + REQUIRE( + are_stencil_by_edges_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); - SECTION("unordered") - { - const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + REQUIRE( + are_stencil_by_faces_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + + SECTION("unordered") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); - const Connectivity<1>& connectivity = mesh.connectivity(); - auto stencil_array = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}); + REQUIRE( + are_stencil_by_nodes_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); - REQUIRE(is_valid(connectivity, stencil_array)); + REQUIRE( + are_stencil_by_edges_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + are_stencil_by_faces_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } } - } - SECTION("2D") - { - SECTION("cartesian") + SECTION("2D") { - const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); - - const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE( - is_valid(connectivity, - StencilManager::instance() - .getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}))); + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + REQUIRE( + are_stencil_by_nodes_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + are_stencil_by_edges_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + are_stencil_by_faces_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + REQUIRE( + are_stencil_by_nodes_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + are_stencil_by_edges_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + are_stencil_by_faces_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } } - SECTION("hybrid") + SECTION("3D") { - const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); - - const Connectivity<2>& connectivity = mesh.connectivity(); - REQUIRE( - is_valid(connectivity, - StencilManager::instance() - .getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}))); + SECTION("carteian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + REQUIRE( + are_stencil_by_nodes_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + are_stencil_by_edges_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + are_stencil_by_faces_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + REQUIRE( + are_stencil_by_nodes_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + are_stencil_by_edges_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + are_stencil_by_faces_valid(connectivity, + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } } } - SECTION("3D") + SECTION("2 layers") { - SECTION("carteian") + NbGhostLayersTester nb_ghost_layers_tester(2); + + SECTION("1D") { - const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); - - const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE( - is_valid(connectivity, - StencilManager::instance() - .getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}))); + SECTION("cartesian") + { + auto mesh_v = CartesianMeshBuilder(TinyVector<1>{0}, TinyVector<1>{1}, TinyVector<1, uint64_t>(20)).mesh(); + + const auto& mesh = *mesh_v->get<Mesh<1>>(); + + REQUIRE( + are_stencil_by_nodes_valid(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_nodes}), + 2)); + + REQUIRE( + are_stencil_by_edges_valid(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_edges}), + 2)); + + REQUIRE( + are_stencil_by_faces_valid(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_faces}), + 2)); + } + } + + SECTION("2D") + { + SECTION("cartesian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<2>{0, 0}, TinyVector<2>{1, 2}, TinyVector<2, uint64_t>(5, 7)).mesh(); + const auto& mesh = *mesh_v->get<Mesh<2>>(); + + REQUIRE( + are_stencil_by_nodes_valid(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_nodes}), + 2)); + + REQUIRE( + are_stencil_by_edges_valid(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_edges}), + 2)); + + REQUIRE( + are_stencil_by_faces_valid(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_faces}), + 2)); + } } - SECTION("hybrid") + SECTION("3D") { - const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); - - const Connectivity<3>& connectivity = mesh.connectivity(); - REQUIRE( - is_valid(connectivity, - StencilManager::instance() - .getCellToCellStencilArray(connectivity, - StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}))); + SECTION("carteian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<3>{0, 0, 0}, TinyVector<3>{1, 1, 2}, TinyVector<3, uint64_t>(3, 4, 5)) + .mesh(); + const auto& mesh = *mesh_v->get<Mesh<3>>(); + + REQUIRE( + are_stencil_by_nodes_valid(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_nodes}), + 2)); + + REQUIRE( + are_stencil_by_edges_valid(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_edges}), + 2)); + + REQUIRE( + are_stencil_by_faces_valid(mesh.connectivity(), + StencilManager::instance() + .getCellToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_faces}), + 2)); + } } } } @@ -160,13 +442,21 @@ TEST_CASE("StencilBuilder", "[mesh]") return is_empty; }; - auto symmetry_stencils_are_valid = [](const auto& stencil_array, const auto& mesh) { - bool is_valid = true; + auto are_symmetry_stencils_valid = []<ItemType connecting_item_type>(const auto& stencil_array, const auto& mesh, + const size_t number_of_layers) { + bool are_valid_symmetries = true; - auto node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix(); - auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); - auto cell_is_owned = mesh.connectivity().cellIsOwned(); - auto cell_number = mesh.connectivity().cellNumber(); + auto connecting_to_cell_matrix = + mesh.connectivity().template getItemToItemMatrix<connecting_item_type, ItemType::cell>(); + auto cell_to_connecting_item_matrix = + mesh.connectivity().template getItemToItemMatrix<ItemType::cell, connecting_item_type>(); + auto cell_is_owned = mesh.connectivity().cellIsOwned(); + auto cell_number = mesh.connectivity().cellNumber(); + + auto connecting_number = mesh.connectivity().template number<connecting_item_type>(); + + using ConnectingItemId = ItemIdT<connecting_item_type>; + using MeshType = std::decay_t<decltype(mesh)>; for (size_t i_symmetry_stencil = 0; i_symmetry_stencil < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry_stencil) { @@ -174,175 +464,620 @@ TEST_CASE("StencilBuilder", "[mesh]") stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].boundaryDescriptor(); auto boundary_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].stencilArray(); - auto boundary_node_list = getMeshFlatNodeBoundary(mesh, boundary_descriptor); - - CellValue<bool> boundary_cell{mesh.connectivity()}; - boundary_cell.fill(false); - auto node_list = boundary_node_list.nodeList(); - for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { - const NodeId node_id = node_list[i_node]; - auto node_cell_list = node_to_cell_matrix[node_id]; - for (size_t i_cell = 0; i_cell < node_cell_list.size(); ++i_cell) { - const CellId cell_id = node_cell_list[i_cell]; - boundary_cell[cell_id] = true; + auto boundary_face_list = getMeshFaceBoundary(mesh, boundary_descriptor); + + std::set<ConnectingItemId> sym_connecting_item_set; + + if constexpr (ItemTypeId<MeshType::Dimension>::itemTId(connecting_item_type) == + ItemTypeId<MeshType::Dimension>::itemTId(ItemType::face)) { + for (size_t i_face = 0; i_face < boundary_face_list.faceList().size(); ++i_face) { + const FaceId face_id = boundary_face_list.faceList()[i_face]; + sym_connecting_item_set.insert(ConnectingItemId{FaceId::base_type{face_id}}); } - } - std::set<NodeId> symmetry_node; - for (size_t i_node = 0; i_node < node_list.size(); ++i_node) { - const NodeId node_id = node_list[i_node]; - symmetry_node.insert(node_id); + } else { + auto face_to_connecting_item_matrix = + mesh.connectivity().template getItemToItemMatrix<ItemType::face, connecting_item_type>(); + + for (size_t i_face = 0; i_face < boundary_face_list.faceList().size(); ++i_face) { + const FaceId face_id = boundary_face_list.faceList()[i_face]; + auto connecting_item_list = face_to_connecting_item_matrix[face_id]; + for (size_t i_connecting_item = 0; i_connecting_item < connecting_item_list.size(); ++i_connecting_item) { + sym_connecting_item_set.insert(connecting_item_list[i_connecting_item]); + } + } } for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - if ((not boundary_cell[cell_id]) or (not cell_is_owned[cell_id])) { - is_valid &= (boundary_stencil[cell_id].size() == 0); + if (not cell_is_owned[cell_id]) { + are_valid_symmetries &= (boundary_stencil[cell_id].size() == 0); } else { - auto cell_node_list = cell_to_node_matrix[cell_id]; - std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( - [&](CellId cell0_id, CellId cell1_id) { return cell_number[cell0_id] < cell_number[cell1_id]; }); - for (size_t i_node = 0; i_node < cell_node_list.size(); ++i_node) { - const NodeId node_id = cell_node_list[i_node]; - if (symmetry_node.contains(node_id)) { - auto node_cell_list = node_to_cell_matrix[node_id]; - for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) { - const CellId node_cell_id = node_cell_list[i_node_cell]; - cell_set.insert(node_cell_id); + std::vector<CellId> expected_stencil; + + std::set<CellId> marked_cell_set; + marked_cell_set.insert(cell_id); + + std::set<ConnectingItemId> marked_connecting_item_set; + std::set<ConnectingItemId> layer_connecting_item_set; + { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + + std::set<ConnectingItemId> marked_sym_connecting_item_set; + std::set<ConnectingItemId> layer_sym_connecting_item_set; + + for (auto&& connecting_item_id : marked_connecting_item_set) { + if (sym_connecting_item_set.contains(connecting_item_id)) { + marked_sym_connecting_item_set.insert(connecting_item_id); + layer_sym_connecting_item_set.insert(connecting_item_id); + } + } + + std::set<CellId> marked_sym_cell_set; + + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + + for (auto&& connecting_item_id : layer_connecting_item_set) { + auto connecting_item_to_cell_list = connecting_to_cell_matrix[connecting_item_id]; + for (size_t i_connecting_item_of_cell = 0; + i_connecting_item_of_cell < connecting_item_to_cell_list.size(); ++i_connecting_item_of_cell) { + const CellId connecting_item_id_of_cell = connecting_item_to_cell_list[i_connecting_item_of_cell]; + if (not marked_cell_set.contains(connecting_item_id_of_cell)) { + cell_set.insert(connecting_item_id_of_cell); + marked_cell_set.insert(connecting_item_id_of_cell); + } + } + } + + std::set<CellId, std::function<bool(CellId, CellId)>> sym_cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + + for (auto&& connecting_item_id : layer_sym_connecting_item_set) { + auto connecting_item_to_cell_list = connecting_to_cell_matrix[connecting_item_id]; + for (size_t i_connecting_item_of_cell = 0; + i_connecting_item_of_cell < connecting_item_to_cell_list.size(); ++i_connecting_item_of_cell) { + const CellId connecting_item_id_of_cell = connecting_item_to_cell_list[i_connecting_item_of_cell]; + if (not marked_sym_cell_set.contains(connecting_item_id_of_cell)) { + sym_cell_set.insert(connecting_item_id_of_cell); + marked_sym_cell_set.insert(connecting_item_id_of_cell); + } + } + } + + for (auto&& set_sym_cell_id : sym_cell_set) { + expected_stencil.push_back(set_sym_cell_id); + } + + layer_connecting_item_set.clear(); + for (auto&& layer_cell_id : cell_set) { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[layer_cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + if (not marked_connecting_item_set.contains(connecting_item_id)) { + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + } + + layer_sym_connecting_item_set.clear(); + + for (auto&& connecting_item_id : layer_connecting_item_set) { + if (sym_connecting_item_set.contains(connecting_item_id)) { + if (not marked_sym_connecting_item_set.contains(connecting_item_id)) { + marked_sym_connecting_item_set.insert(connecting_item_id); + layer_sym_connecting_item_set.insert(connecting_item_id); + } + } + } + + for (auto layer_sym_cell_id : sym_cell_set) { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[layer_sym_cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + if (not marked_sym_connecting_item_set.contains(connecting_item_id)) { + marked_sym_connecting_item_set.insert(connecting_item_id); + layer_sym_connecting_item_set.insert(connecting_item_id); + } } } } - if (cell_set.size() == boundary_stencil[cell_id].size()) { - size_t i = 0; - for (auto&& id : cell_set) { - is_valid &= (id == boundary_stencil[cell_id][i++]); + auto cell_stencil = boundary_stencil[cell_id]; + + if (cell_stencil.size() != expected_stencil.size()) { + are_valid_symmetries = false; + } + + auto i_set_cell = expected_stencil.begin(); + for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { + if (*i_set_cell != cell_stencil[index]) { + are_valid_symmetries = false; } - } else { - is_valid = false; } } } } - return is_valid; + return are_valid_symmetries; + }; + + auto are_symmetry_stencils_by_nodes_valid = [&](const auto& stencil_array, const auto& mesh, + const size_t number_of_layers) { + return are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, number_of_layers); + }; + + auto are_symmetry_stencils_by_edges_valid = [&](const auto& stencil_array, const auto& mesh, + const size_t number_of_layers) { + return are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, number_of_layers); }; - SECTION("1D") + auto are_symmetry_stencils_by_faces_valid = [&](const auto& stencil_array, const auto& mesh, + const size_t number_of_layers) { + return are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, number_of_layers); + }; + + SECTION("1 layer") { - StencilManager::BoundaryDescriptorList list; - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + SECTION("1D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + } + } + + SECTION("unordered") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + } - SECTION("cartesian") + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + } + } + } + + SECTION("2D") { - const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_edges_valid(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 1)); + REQUIRE(not(are_stencil_by_nodes_valid(connectivity, stencil_array, 1))); + REQUIRE(are_stencil_by_faces_valid(connectivity, stencil_array, 1)); + } + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + } - const Connectivity<1>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); - auto stencil_array = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, - list); + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_edges_valid(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); - REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil_array, mesh)); + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 1)); + REQUIRE(not(are_stencil_by_nodes_valid(connectivity, stencil_array, 1))); + REQUIRE(are_stencil_by_faces_valid(connectivity, stencil_array, 1)); + } + } } - SECTION("hybrid") + SECTION("3D") { - const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_edges_valid(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_faces_valid(connectivity, stencil_array, 1)); + } + } - const Connectivity<1>& connectivity = mesh.connectivity(); - auto stencil = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, - list); + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); - REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 2); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); + const Connectivity<3>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_edges_valid(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 1)); + REQUIRE(are_stencil_by_faces_valid(connectivity, stencil_array, 1)); + } + } } } - SECTION("2D") + SECTION("2 layers") { - StencilManager::BoundaryDescriptorList list; - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + NbGhostLayersTester nb_ghost_layers_tester(2); - SECTION("cartesian") + SECTION("1D") { - const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); - const Connectivity<2>& connectivity = mesh.connectivity(); + SECTION("cartesian") + { + auto mesh_v = CartesianMeshBuilder(TinyVector<1>{0}, TinyVector<1>{1}, TinyVector<1, uint64_t>(20)).mesh(); + const auto& mesh = *mesh_v->get<Mesh<1>>(); - auto stencil_array = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, - list); + const Connectivity<1>& connectivity = mesh.connectivity(); - REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil_array, mesh)); - } + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_nodes}, list); - SECTION("hybrid") - { - const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 2)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 2)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 2)); + } - const Connectivity<2>& connectivity = mesh.connectivity(); - auto stencil = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, - list); + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_faces}, list); - REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 4); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 2)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 2)); + } + } } - } - SECTION("3D") - { - StencilManager::BoundaryDescriptorList list; - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMIN")); - list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMAX")); - - SECTION("cartesian") + SECTION("2D") { - const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + + SECTION("cartesian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<2>{0, 0}, TinyVector<2>{1, 2}, TinyVector<2, uint64_t>(5, 7)).mesh(); + const auto& mesh = *mesh_v->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 2)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_edges}, list); - const Connectivity<3>& connectivity = mesh.connectivity(); - auto stencil = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, - list); + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 2)); + REQUIRE(are_stencil_by_edges_valid(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_faces}, list); - REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 6); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 2)); + REQUIRE(not(are_stencil_by_nodes_valid(connectivity, stencil_array, 2))); + REQUIRE(are_stencil_by_faces_valid(connectivity, stencil_array, 2)); + } + } } - SECTION("hybrid") + SECTION("3D") { - const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMAX")); - const Connectivity<3>& connectivity = mesh.connectivity(); - auto stencil = - StencilManager::instance() - .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, - list); + SECTION("cartesian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<3>{0, 0, 0}, TinyVector<3>{1, 1, 2}, TinyVector<3, uint64_t>(3, 4, 5)) + .mesh(); + const auto& mesh = *mesh_v->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 2)); + REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 2)); + } - REQUIRE(stencil.symmetryBoundaryStencilArrayList().size() == 6); - REQUIRE(check_ghost_cells_have_empty_stencils(stencil, connectivity)); - REQUIRE(symmetry_stencils_are_valid(stencil, mesh)); + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 2)); + REQUIRE(are_stencil_by_edges_valid(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getCellToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); + REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 2)); + REQUIRE(are_stencil_by_faces_valid(connectivity, stencil_array, 2)); + } + } } } } -- GitLab From 452fc0fb822503a96593a8b1f028e90c5325b6f4 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Sun, 22 Dec 2024 10:43:34 +0100 Subject: [PATCH 083/122] Rework code to a avoid clang-11 crash --- tests/test_StencilBuilder.cpp | 225 +++++++++++++++++----------------- 1 file changed, 111 insertions(+), 114 deletions(-) diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder.cpp index 8759b36c5..0666ca112 100644 --- a/tests/test_StencilBuilder.cpp +++ b/tests/test_StencilBuilder.cpp @@ -100,21 +100,6 @@ TEST_CASE("StencilBuilder", "[mesh]") return true; }; - auto are_stencil_by_nodes_valid = [&](const auto& connectivity, const auto& stencil_array, - const size_t number_of_layers) { - return is_valid.template operator()<ItemType::node>(connectivity, stencil_array, number_of_layers); - }; - - auto are_stencil_by_faces_valid = [&](const auto& connectivity, const auto& stencil_array, - const size_t number_of_layers) { - return is_valid.template operator()<ItemType::face>(connectivity, stencil_array, number_of_layers); - }; - - auto are_stencil_by_edges_valid = [&](const auto& connectivity, const auto& stencil_array, - const size_t number_of_layers) { - return is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, number_of_layers); - }; - SECTION("inner stencil") { SECTION("1 layer") @@ -128,7 +113,8 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<1>& connectivity = mesh.connectivity(); REQUIRE( - are_stencil_by_nodes_valid(connectivity, + is_valid.template + operator()<ItemType::node>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -136,7 +122,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 1)); REQUIRE( - are_stencil_by_edges_valid(connectivity, + is_valid.template + operator()<ItemType::edge>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -144,7 +131,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 1)); REQUIRE( - are_stencil_by_faces_valid(connectivity, + is_valid.template + operator()<ItemType::face>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -159,7 +147,8 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<1>& connectivity = mesh.connectivity(); REQUIRE( - are_stencil_by_nodes_valid(connectivity, + is_valid.template + operator()<ItemType::node>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -167,7 +156,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 1)); REQUIRE( - are_stencil_by_edges_valid(connectivity, + is_valid.template + operator()<ItemType::edge>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -175,7 +165,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 1)); REQUIRE( - are_stencil_by_faces_valid(connectivity, + is_valid.template + operator()<ItemType::face>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -192,7 +183,8 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<2>& connectivity = mesh.connectivity(); REQUIRE( - are_stencil_by_nodes_valid(connectivity, + is_valid.template + operator()<ItemType::node>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -200,7 +192,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 1)); REQUIRE( - are_stencil_by_edges_valid(connectivity, + is_valid.template + operator()<ItemType::edge>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -208,7 +201,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 1)); REQUIRE( - are_stencil_by_faces_valid(connectivity, + is_valid.template + operator()<ItemType::face>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -222,7 +216,8 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<2>& connectivity = mesh.connectivity(); REQUIRE( - are_stencil_by_nodes_valid(connectivity, + is_valid.template + operator()<ItemType::node>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -230,7 +225,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 1)); REQUIRE( - are_stencil_by_edges_valid(connectivity, + is_valid.template + operator()<ItemType::edge>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -238,7 +234,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 1)); REQUIRE( - are_stencil_by_faces_valid(connectivity, + is_valid.template + operator()<ItemType::face>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -255,7 +252,8 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<3>& connectivity = mesh.connectivity(); REQUIRE( - are_stencil_by_nodes_valid(connectivity, + is_valid.template + operator()<ItemType::node>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -263,7 +261,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 1)); REQUIRE( - are_stencil_by_edges_valid(connectivity, + is_valid.template + operator()<ItemType::edge>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -271,7 +270,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 1)); REQUIRE( - are_stencil_by_faces_valid(connectivity, + is_valid.template + operator()<ItemType::face>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -285,7 +285,8 @@ TEST_CASE("StencilBuilder", "[mesh]") const Connectivity<3>& connectivity = mesh.connectivity(); REQUIRE( - are_stencil_by_nodes_valid(connectivity, + is_valid.template + operator()<ItemType::node>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -293,7 +294,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 1)); REQUIRE( - are_stencil_by_edges_valid(connectivity, + is_valid.template + operator()<ItemType::edge>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -301,7 +303,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 1)); REQUIRE( - are_stencil_by_faces_valid(connectivity, + is_valid.template + operator()<ItemType::face>(connectivity, StencilManager::instance() .getCellToCellStencilArray(connectivity, StencilDescriptor{1, StencilDescriptor:: @@ -324,7 +327,8 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *mesh_v->get<Mesh<1>>(); REQUIRE( - are_stencil_by_nodes_valid(mesh.connectivity(), + is_valid.template + operator()<ItemType::node>(mesh.connectivity(), StencilManager::instance() .getCellToCellStencilArray(mesh.connectivity(), StencilDescriptor{2, StencilDescriptor:: @@ -332,7 +336,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 2)); REQUIRE( - are_stencil_by_edges_valid(mesh.connectivity(), + is_valid.template + operator()<ItemType::edge>(mesh.connectivity(), StencilManager::instance() .getCellToCellStencilArray(mesh.connectivity(), StencilDescriptor{2, StencilDescriptor:: @@ -340,7 +345,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 2)); REQUIRE( - are_stencil_by_faces_valid(mesh.connectivity(), + is_valid.template + operator()<ItemType::face>(mesh.connectivity(), StencilManager::instance() .getCellToCellStencilArray(mesh.connectivity(), StencilDescriptor{2, StencilDescriptor:: @@ -358,7 +364,8 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *mesh_v->get<Mesh<2>>(); REQUIRE( - are_stencil_by_nodes_valid(mesh.connectivity(), + is_valid.template + operator()<ItemType::node>(mesh.connectivity(), StencilManager::instance() .getCellToCellStencilArray(mesh.connectivity(), StencilDescriptor{2, StencilDescriptor:: @@ -366,7 +373,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 2)); REQUIRE( - are_stencil_by_edges_valid(mesh.connectivity(), + is_valid.template + operator()<ItemType::edge>(mesh.connectivity(), StencilManager::instance() .getCellToCellStencilArray(mesh.connectivity(), StencilDescriptor{2, StencilDescriptor:: @@ -374,7 +382,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 2)); REQUIRE( - are_stencil_by_faces_valid(mesh.connectivity(), + is_valid.template + operator()<ItemType::face>(mesh.connectivity(), StencilManager::instance() .getCellToCellStencilArray(mesh.connectivity(), StencilDescriptor{2, StencilDescriptor:: @@ -393,7 +402,8 @@ TEST_CASE("StencilBuilder", "[mesh]") const auto& mesh = *mesh_v->get<Mesh<3>>(); REQUIRE( - are_stencil_by_nodes_valid(mesh.connectivity(), + is_valid.template + operator()<ItemType::node>(mesh.connectivity(), StencilManager::instance() .getCellToCellStencilArray(mesh.connectivity(), StencilDescriptor{2, StencilDescriptor:: @@ -401,7 +411,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 2)); REQUIRE( - are_stencil_by_edges_valid(mesh.connectivity(), + is_valid.template + operator()<ItemType::edge>(mesh.connectivity(), StencilManager::instance() .getCellToCellStencilArray(mesh.connectivity(), StencilDescriptor{2, StencilDescriptor:: @@ -409,7 +420,8 @@ TEST_CASE("StencilBuilder", "[mesh]") 2)); REQUIRE( - are_stencil_by_faces_valid(mesh.connectivity(), + is_valid.template + operator()<ItemType::face>(mesh.connectivity(), StencilManager::instance() .getCellToCellStencilArray(mesh.connectivity(), StencilDescriptor{2, StencilDescriptor:: @@ -612,21 +624,6 @@ TEST_CASE("StencilBuilder", "[mesh]") return are_valid_symmetries; }; - auto are_symmetry_stencils_by_nodes_valid = [&](const auto& stencil_array, const auto& mesh, - const size_t number_of_layers) { - return are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, number_of_layers); - }; - - auto are_symmetry_stencils_by_edges_valid = [&](const auto& stencil_array, const auto& mesh, - const size_t number_of_layers) { - return are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, number_of_layers); - }; - - auto are_symmetry_stencils_by_faces_valid = [&](const auto& stencil_array, const auto& mesh, - const size_t number_of_layers) { - return are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, number_of_layers); - }; - SECTION("1 layer") { SECTION("1D") @@ -649,8 +646,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); } { @@ -661,8 +658,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); } { @@ -673,8 +670,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); } } @@ -692,8 +689,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); } { @@ -704,8 +701,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); } { @@ -716,8 +713,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); } } } @@ -744,8 +741,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); } { @@ -756,8 +753,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_edges_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, 1)); } { @@ -768,9 +765,9 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 1)); - REQUIRE(not(are_stencil_by_nodes_valid(connectivity, stencil_array, 1))); - REQUIRE(are_stencil_by_faces_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(not(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1))); + REQUIRE(is_valid.template operator()<ItemType::face>(connectivity, stencil_array, 1)); } } @@ -788,8 +785,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); } { @@ -800,8 +797,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_edges_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, 1)); } { @@ -812,9 +809,9 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 1)); - REQUIRE(not(are_stencil_by_nodes_valid(connectivity, stencil_array, 1))); - REQUIRE(are_stencil_by_faces_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(not(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1))); + REQUIRE(is_valid.template operator()<ItemType::face>(connectivity, stencil_array, 1)); } } } @@ -842,8 +839,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); } { @@ -854,8 +851,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_edges_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, 1)); } { @@ -866,8 +863,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_faces_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::face>(connectivity, stencil_array, 1)); } } @@ -884,8 +881,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 1)); } { @@ -896,8 +893,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_edges_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, 1)); } { @@ -908,8 +905,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 1)); - REQUIRE(are_stencil_by_faces_valid(connectivity, stencil_array, 1)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::face>(connectivity, stencil_array, 1)); } } } @@ -940,8 +937,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 2)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 2)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 2)); } { @@ -952,8 +949,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 2)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 2)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 2)); } { @@ -964,8 +961,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 2)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 2)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 2)); } } } @@ -993,8 +990,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 2)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 2)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 2)); } { @@ -1005,8 +1002,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 2)); - REQUIRE(are_stencil_by_edges_valid(connectivity, stencil_array, 2)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, 2)); } { @@ -1017,9 +1014,9 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 2)); - REQUIRE(not(are_stencil_by_nodes_valid(connectivity, stencil_array, 2))); - REQUIRE(are_stencil_by_faces_valid(connectivity, stencil_array, 2)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 2)); + REQUIRE(not(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 2))); + REQUIRE(is_valid.template operator()<ItemType::face>(connectivity, stencil_array, 2)); } } } @@ -1050,8 +1047,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_nodes_valid(stencil_array, mesh, 2)); - REQUIRE(are_stencil_by_nodes_valid(connectivity, stencil_array, 2)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::node>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node>(connectivity, stencil_array, 2)); } { @@ -1062,8 +1059,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_edges_valid(stencil_array, mesh, 2)); - REQUIRE(are_stencil_by_edges_valid(connectivity, stencil_array, 2)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::edge>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::edge>(connectivity, stencil_array, 2)); } { @@ -1074,8 +1071,8 @@ TEST_CASE("StencilBuilder", "[mesh]") REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); REQUIRE(check_ghost_cells_have_empty_stencils(stencil_array, connectivity)); - REQUIRE(are_symmetry_stencils_by_faces_valid(stencil_array, mesh, 2)); - REQUIRE(are_stencil_by_faces_valid(connectivity, stencil_array, 2)); + REQUIRE(are_symmetry_stencils_valid.template operator()<ItemType::face>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::face>(connectivity, stencil_array, 2)); } } } -- GitLab From b7b7d4540000967c74e2e3e7df79e286466e7ebf Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Sun, 22 Dec 2024 10:44:08 +0100 Subject: [PATCH 084/122] Warning fixes --- src/algebra/ShrinkVectorView.hpp | 4 ++-- src/scheme/PolynomialReconstruction.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/algebra/ShrinkVectorView.hpp b/src/algebra/ShrinkVectorView.hpp index cdf786c5a..83aed7cb2 100644 --- a/src/algebra/ShrinkVectorView.hpp +++ b/src/algebra/ShrinkVectorView.hpp @@ -23,10 +23,10 @@ class ShrinkVectorView friend std::ostream& operator<<(std::ostream& os, const ShrinkVectorView& x) { - if (x.size() > 0) { + if (x.dimension() > 0) { os << 0 << ':' << NaNHelper(x[0]); } - for (size_t i = 1; i < x.size(); ++i) { + for (size_t i = 1; i < x.dimension(); ++i) { os << ' ' << i << ':' << NaNHelper(x[i]); } return os; diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 0e0b2a5af..55a8271a2 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -897,7 +897,7 @@ PolynomialReconstruction::_build( } parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_j_id) { + mesh.numberOfCells(), PUGS_CLASS_LAMBDA(const CellId cell_j_id) { if (cell_is_owned[cell_j_id]) { const int32_t t = tokens.acquire(); -- GitLab From d4a0b910ba7277321d04c5e023ed0926286239e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 13 Jan 2025 19:16:44 +0100 Subject: [PATCH 085/122] Add construction cell stencils based defined by a node Actually, this should work for any kind of item based stencil (face to cells, edge to nodes) using a third kind of item to define connections. This is not plugged since by applications for these stencils are not clear. --- src/mesh/StencilArray.hpp | 7 +- src/mesh/StencilBuilder.cpp | 319 ++++- src/scheme/DiscreteFunctionDPkVector.hpp | 10 + tests/CMakeLists.txt | 3 +- ....cpp => test_StencilBuilder_cell2cell.cpp} | 2 +- tests/test_StencilBuilder_node2cell.cpp | 1184 +++++++++++++++++ 6 files changed, 1517 insertions(+), 8 deletions(-) rename tests/{test_StencilBuilder.cpp => test_StencilBuilder_cell2cell.cpp} (99%) create mode 100644 tests/test_StencilBuilder_node2cell.cpp diff --git a/src/mesh/StencilArray.hpp b/src/mesh/StencilArray.hpp index 9571c346b..ffe8638bf 100644 --- a/src/mesh/StencilArray.hpp +++ b/src/mesh/StencilArray.hpp @@ -12,6 +12,9 @@ class StencilArray public: using ItemToItemMatrixT = ItemToItemMatrix<source_item_type, target_item_type>; + using SourceItemId = ItemIdT<source_item_type>; + using TargetItemId = ItemIdT<target_item_type>; + class BoundaryDescriptorStencilArray { private: @@ -71,9 +74,9 @@ class StencilArray PUGS_INLINE auto - operator[](CellId cell_id) const + operator[](SourceItemId source_item_id) const { - return m_stencil_array[cell_id]; + return m_stencil_array[source_item_id]; } StencilArray(const ConnectivityMatrix& connectivity_matrix, diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 96f36a14f..20fc3bf61 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -478,8 +478,291 @@ StencilBuilder::_build_for_different_source_and_target( const size_t& number_of_layers, const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { - static_assert(source_item_type != target_item_type); - throw NotImplementedError("different source target"); + constexpr size_t Dimension = ConnectivityType::Dimension; + + if (number_of_layers == 0) { + throw NormalError("number of layers must be greater than 0 to build stencils"); + } + + auto connecting_item_to_target_item_matrix = + connectivity.template getItemToItemMatrix<connecting_item_type, target_item_type>(); + auto target_item_to_connecting_item_matrix = + connectivity.template getItemToItemMatrix<target_item_type, connecting_item_type>(); + + auto source_item_to_connecting_item_matrix = + [](const auto& given_connectivity) -> ItemToItemMatrix<source_item_type, connecting_item_type> { + if constexpr (ItemTypeId<Dimension>::itemTId(source_item_type) == + ItemTypeId<Dimension>::itemTId(connecting_item_type)) { + return {ConnectivityMatrix{}}; + } else { + return given_connectivity.template getItemToItemMatrix<source_item_type, connecting_item_type>(); + } + }(connectivity); + + auto source_item_is_owned = connectivity.template isOwned<source_item_type>(); + auto target_item_number = connectivity.template number<target_item_type>(); + auto connecting_item_number = connectivity.template number<connecting_item_type>(); + + using SourceItemId = ItemIdT<source_item_type>; + using TargetItemId = ItemIdT<target_item_type>; + using ConnectingItemId = ItemIdT<connecting_item_type>; + + ItemArray<bool, connecting_item_type> symmetry_item_list = + this->_buildSymmetryConnectingItemList<connecting_item_type>(connectivity, symmetry_boundary_descriptor_list); + + Array<uint32_t> row_map{connectivity.template numberOf<source_item_type>() + 1}; + row_map[0] = 0; + std::vector<Array<uint32_t>> symmetry_row_map_list(symmetry_boundary_descriptor_list.size()); + for (auto&& symmetry_row_map : symmetry_row_map_list) { + symmetry_row_map = Array<uint32_t>{connectivity.template numberOf<source_item_type>() + 1}; + symmetry_row_map[0] = 0; + } + + std::vector<TargetItemId> column_indices_vector; + std::vector<std::vector<uint32_t>> symmetry_column_indices_vector(symmetry_boundary_descriptor_list.size()); + + std::vector<Layer<target_item_type>> target_item_layer_list; + std::vector<Layer<connecting_item_type>> connecting_layer_list; + + std::vector<Layer<target_item_type>> symmetry_target_item_layer_list; + std::vector<Layer<connecting_item_type>> symmetry_connecting_layer_list; + + for (size_t i = 0; i < number_of_layers; ++i) { + target_item_layer_list.emplace_back(Layer<target_item_type>{connectivity}); + connecting_layer_list.emplace_back(Layer<connecting_item_type>{connectivity}); + + if (symmetry_boundary_descriptor_list.size() > 0) { + symmetry_target_item_layer_list.emplace_back(Layer<target_item_type>{connectivity}); + symmetry_connecting_layer_list.emplace_back(Layer<connecting_item_type>{connectivity}); + } + } + + for (SourceItemId source_item_id = 0; source_item_id < connectivity.template numberOf<source_item_type>(); + ++source_item_id) { + if (source_item_is_owned[source_item_id]) { + for (auto&& target_item_layer : target_item_layer_list) { + target_item_layer.clear(); + } + for (auto&& connecting_layer : connecting_layer_list) { + connecting_layer.clear(); + } + + // First layer is a special case + { + auto& target_item_layer = target_item_layer_list[0]; + auto& connecting_layer = connecting_layer_list[0]; + + if constexpr (ItemTypeId<Dimension>::itemTId(source_item_type) == + ItemTypeId<Dimension>::itemTId(connecting_item_type)) { + connecting_layer.add(ConnectingItemId{static_cast<typename ConnectingItemId::base_type>(source_item_id)}); + } else { + for (size_t i_connecting_item = 0; + i_connecting_item < source_item_to_connecting_item_matrix[source_item_id].size(); ++i_connecting_item) { + const ConnectingItemId connecting_item_id = + source_item_to_connecting_item_matrix[source_item_id][i_connecting_item]; + + connecting_layer.add(connecting_item_id); + } + } + + for (auto connecting_item_id : connecting_layer.itemIdList()) { + for (size_t i_item = 0; i_item < connecting_item_to_target_item_matrix[connecting_item_id].size(); ++i_item) { + const TargetItemId layer_item_id = connecting_item_to_target_item_matrix[connecting_item_id][i_item]; + target_item_layer.add(layer_item_id); + } + } + } + + for (size_t i_layer = 1; i_layer < number_of_layers; ++i_layer) { + auto& connecting_layer = connecting_layer_list[i_layer]; + + auto has_connecting_item = [&i_layer, &connecting_layer_list](ConnectingItemId connecting_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (connecting_layer_list[i].hasItemNumber(connecting_item_id)) { + return true; + } + } + + return false; + }; + + for (auto&& previous_layer_item_id_list : target_item_layer_list[i_layer - 1].itemIdList()) { + const auto target_item_to_connecting_item_list = + target_item_to_connecting_item_matrix[previous_layer_item_id_list]; + for (size_t i_connecting_item = 0; i_connecting_item < target_item_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = target_item_to_connecting_item_list[i_connecting_item]; + + if (not has_connecting_item(connecting_item_id)) { + connecting_layer.add(connecting_item_id); + } + } + } + + auto& target_item_layer = target_item_layer_list[i_layer]; + + auto has_layer_item = [&i_layer, &target_item_layer_list](TargetItemId layer_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (target_item_layer_list[i].hasItemNumber(layer_item_id)) { + return true; + } + } + + return false; + }; + + for (auto connecting_item_id : connecting_layer.itemIdList()) { + const auto& connecting_item_to_target_item_list = connecting_item_to_target_item_matrix[connecting_item_id]; + for (size_t i_item = 0; i_item < connecting_item_to_target_item_list.size(); ++i_item) { + const TargetItemId layer_item_id = connecting_item_to_target_item_list[i_item]; + if (not has_layer_item(layer_item_id)) { + target_item_layer.add(layer_item_id); + } + } + } + } + + for (size_t i_symmetry = 0; i_symmetry < symmetry_boundary_descriptor_list.size(); ++i_symmetry) { + for (auto&& symmetry_target_item_layer : symmetry_target_item_layer_list) { + symmetry_target_item_layer.clear(); + } + for (auto&& symmetry_connecting_layer : symmetry_connecting_layer_list) { + symmetry_connecting_layer.clear(); + } + + // First layer is a special case + for (auto&& connecting_item_id : connecting_layer_list[0].itemIdList()) { + if (symmetry_item_list[connecting_item_id][i_symmetry]) { + symmetry_connecting_layer_list[0].add(connecting_item_id); + } + } + + for (auto&& connecting_item_id : symmetry_connecting_layer_list[0].itemIdList()) { + auto item_of_connecting_item_list = connecting_item_to_target_item_matrix[connecting_item_id]; + for (size_t i_item_of_connecting_item = 0; i_item_of_connecting_item < item_of_connecting_item_list.size(); + ++i_item_of_connecting_item) { + const TargetItemId item_id_of_connecting_item = item_of_connecting_item_list[i_item_of_connecting_item]; + symmetry_target_item_layer_list[0].add(item_id_of_connecting_item); + } + } + + for (size_t i_layer = 1; i_layer < number_of_layers; ++i_layer) { + auto has_connecting_item = [&i_layer, + &symmetry_connecting_layer_list](ConnectingItemId connecting_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (symmetry_connecting_layer_list[i].hasItemNumber(connecting_item_id)) { + return true; + } + } + + return false; + }; + + for (auto&& symmetry_connecting_item_id : connecting_layer_list[i_layer].itemIdList()) { + if (symmetry_item_list[symmetry_connecting_item_id][i_symmetry]) { + if (not has_connecting_item(symmetry_connecting_item_id)) { + symmetry_connecting_layer_list[i_layer].add(symmetry_connecting_item_id); + } + } + } + + for (auto&& previous_layer_target_item_id_list : symmetry_target_item_layer_list[i_layer - 1].itemIdList()) { + const auto item_to_connecting_item_list = + target_item_to_connecting_item_matrix[previous_layer_target_item_id_list]; + for (size_t i_connecting_item = 0; i_connecting_item < item_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = item_to_connecting_item_list[i_connecting_item]; + + if (not has_connecting_item(connecting_item_id)) { + symmetry_connecting_layer_list[i_layer].add(connecting_item_id); + } + } + } + + auto has_symmetry_layer_item = [&i_layer, + &symmetry_target_item_layer_list](TargetItemId layer_target_item_id) -> bool { + for (ssize_t i = i_layer - 1; i >= 0; --i) { + if (symmetry_target_item_layer_list[i].hasItemNumber(layer_target_item_id)) { + return true; + } + } + + return false; + }; + + for (auto&& connecting_item_id : symmetry_connecting_layer_list[i_layer].itemIdList()) { + auto item_of_connecting_item_list = connecting_item_to_target_item_matrix[connecting_item_id]; + for (size_t i_target_item_of_connecting_item = 0; + i_target_item_of_connecting_item < item_of_connecting_item_list.size(); + ++i_target_item_of_connecting_item) { + const TargetItemId target_item_id_of_connecting_item = + item_of_connecting_item_list[i_target_item_of_connecting_item]; + if (not has_symmetry_layer_item(target_item_id_of_connecting_item)) { + symmetry_target_item_layer_list[i_layer].add(target_item_id_of_connecting_item); + } + } + } + } + + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + for (auto symmetry_layer_target_item_id : symmetry_target_item_layer_list[i_layer].itemIdList()) { + symmetry_column_indices_vector[i_symmetry].push_back(symmetry_layer_target_item_id); + } + } + + { + size_t stencil_size = 0; + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + auto& target_item_layer = symmetry_target_item_layer_list[i_layer]; + stencil_size += target_item_layer.size(); + } + + symmetry_row_map_list[i_symmetry][source_item_id + 1] = + symmetry_row_map_list[i_symmetry][source_item_id] + stencil_size; + } + } + + { + size_t stencil_size = 0; + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + auto& item_layer = target_item_layer_list[i_layer]; + for (auto stencil_item_id : item_layer.itemIdList()) { + column_indices_vector.push_back(stencil_item_id); + } + stencil_size += item_layer.size(); + } + row_map[source_item_id + 1] = row_map[source_item_id] + stencil_size; + } + + } else { + row_map[source_item_id + 1] = row_map[source_item_id]; + for (size_t i = 0; i < symmetry_row_map_list.size(); ++i) { + symmetry_row_map_list[i][source_item_id + 1] = symmetry_row_map_list[i][source_item_id]; + } + } + } + + Array<uint32_t> column_indices{column_indices_vector.size()}; + parallel_for( + column_indices_vector.size(), PUGS_LAMBDA(size_t i) { column_indices[i] = column_indices_vector[i]; }); + + ConnectivityMatrix primal_stencil{row_map, column_indices}; + + typename StencilArray<source_item_type, target_item_type>::BoundaryDescriptorStencilArrayList + symmetry_boundary_stencil_list; + { + size_t i = 0; + for (auto&& p_boundary_descriptor : symmetry_boundary_descriptor_list) { + symmetry_boundary_stencil_list.emplace_back( + typename StencilArray<source_item_type, target_item_type>:: + BoundaryDescriptorStencilArray{p_boundary_descriptor, + ConnectivityMatrix{symmetry_row_map_list[i], + convert_to_array(symmetry_column_indices_vector[i])}}); + ++i; + } + } + + return {{primal_stencil}, {symmetry_boundary_stencil_list}}; } template <ItemType source_item_type, @@ -580,7 +863,35 @@ StencilBuilder::buildC2C(const IConnectivity& connectivity, } NodeToCellStencilArray -StencilBuilder::buildN2C(const IConnectivity&, const StencilDescriptor&, const BoundaryDescriptorList&) const +StencilBuilder::buildN2C(const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { - throw NotImplementedError("node to cell stencil"); + if ((parallel::size() > 1) and + (stencil_descriptor.numberOfLayers() > GlobalVariableManager::instance().getNumberOfGhostLayers())) { + std::ostringstream error_msg; + error_msg << "Stencil builder requires" << rang::fgB::yellow << stencil_descriptor.numberOfLayers() + << rang::fg::reset << " layers while parallel number of ghost layer is " + << GlobalVariableManager::instance().getNumberOfGhostLayers() << ".\n"; + error_msg << "Increase the number of ghost layers (using the '--number-of-ghost-layers' option)."; + throw NormalError(error_msg.str()); + } + + switch (connectivity.dimension()) { + case 1: { + return this->_build<ItemType::node, ItemType::cell>(dynamic_cast<const Connectivity<1>&>(connectivity), + stencil_descriptor, symmetry_boundary_descriptor_list); + } + case 2: { + return this->_build<ItemType::node, ItemType::cell>(dynamic_cast<const Connectivity<2>&>(connectivity), + stencil_descriptor, symmetry_boundary_descriptor_list); + } + case 3: { + return this->_build<ItemType::node, ItemType::cell>(dynamic_cast<const Connectivity<3>&>(connectivity), + stencil_descriptor, symmetry_boundary_descriptor_list); + } + default: { + throw UnexpectedError("invalid connectivity dimension"); + } + } } diff --git a/src/scheme/DiscreteFunctionDPkVector.hpp b/src/scheme/DiscreteFunctionDPkVector.hpp index 2fefc078b..e8ac729f4 100644 --- a/src/scheme/DiscreteFunctionDPkVector.hpp +++ b/src/scheme/DiscreteFunctionDPkVector.hpp @@ -139,6 +139,16 @@ class DiscreteFunctionDPkVector return m_by_cell_coefficients[cell_id]; } + PUGS_INLINE auto + componentCoefficients(const CellId cell_id, size_t i_component) const noexcept(NO_ASSERT) + { + Assert(m_mesh_v.use_count() > 0, "DiscreteFunctionDPkVector is not built"); + Assert(i_component < m_number_of_components, "incorrect component number"); + + return ComponentCoefficientView{&m_by_cell_coefficients[cell_id][i_component * m_nb_coefficients_per_component], + m_nb_coefficients_per_component}; + } + PUGS_FORCEINLINE BasisView operator()(const CellId cell_id, size_t i_component) const noexcept(NO_ASSERT) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 874df648b..c5f872d4e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -264,7 +264,8 @@ add_executable (mpi_unit_tests test_PolynomialReconstruction.cpp test_PolynomialReconstructionDescriptor.cpp test_RandomEngine.cpp - test_StencilBuilder.cpp + test_StencilBuilder_cell2cell.cpp + test_StencilBuilder_node2cell.cpp test_SubItemArrayPerItemVariant.cpp test_SubItemValuePerItem.cpp test_SubItemValuePerItemVariant.cpp diff --git a/tests/test_StencilBuilder.cpp b/tests/test_StencilBuilder_cell2cell.cpp similarity index 99% rename from tests/test_StencilBuilder.cpp rename to tests/test_StencilBuilder_cell2cell.cpp index 0666ca112..42ceb6a5d 100644 --- a/tests/test_StencilBuilder.cpp +++ b/tests/test_StencilBuilder_cell2cell.cpp @@ -18,7 +18,7 @@ // clazy:excludeall=non-pod-global-static -TEST_CASE("StencilBuilder", "[mesh]") +TEST_CASE("StencilBuilder cell2cell", "[mesh]") { auto is_valid = []<ItemType connecting_item_type>(const auto& connectivity, const auto& stencil_array, const size_t number_of_layers) { diff --git a/tests/test_StencilBuilder_node2cell.cpp b/tests/test_StencilBuilder_node2cell.cpp new file mode 100644 index 000000000..6d9c429c6 --- /dev/null +++ b/tests/test_StencilBuilder_node2cell.cpp @@ -0,0 +1,1184 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <mesh/CartesianMeshBuilder.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/ConnectivityUtils.hpp> +#include <mesh/ItemValue.hpp> +#include <mesh/ItemValueUtils.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshFaceBoundary.hpp> +#include <mesh/MeshVariant.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <mesh/StencilManager.hpp> +#include <utils/Messenger.hpp> + +#include <NbGhostLayersTester.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("StencilBuilder node2cell", "[mesh]") +{ + auto is_valid = []<ItemType source_item_type, ItemType connecting_item_type>(const auto& connectivity, + const auto& stencil_array, + const size_t number_of_layers) { + constexpr size_t Dimension = std::decay_t<decltype(connectivity)>::Dimension; + + auto cell_to_connecting_item_matrix = + connectivity.template getItemToItemMatrix<ItemType::cell, connecting_item_type>(); + + auto source_item_to_connecting_item_matrix = + [](const auto& given_connectivity) -> ItemToItemMatrix<source_item_type, connecting_item_type> { + if constexpr (ItemTypeId<Dimension>::itemTId(source_item_type) == + ItemTypeId<Dimension>::itemTId(connecting_item_type)) { + return ConnectivityMatrix{}; + } else { + return given_connectivity.template getItemToItemMatrix<source_item_type, connecting_item_type>(); + } + }(connectivity); + + auto connecting_to_cell_matrix = connectivity.template getItemToItemMatrix<connecting_item_type, ItemType::cell>(); + auto cell_is_owned = connectivity.cellIsOwned(); + auto cell_number = connectivity.cellNumber(); + auto source_item_is_owned = connectivity.template isOwned<source_item_type>(); + + using SourceItemId = ItemIdT<source_item_type>; + using ConnectingItemId = ItemIdT<connecting_item_type>; + + for (SourceItemId source_item_id = 0; source_item_id < connectivity.template numberOf<source_item_type>(); + ++source_item_id) { + if (source_item_is_owned[source_item_id]) { + std::vector<CellId> expected_stencil; + + std::set<CellId> marked_cell_set; + if constexpr (source_item_type == ItemType::cell) { + marked_cell_set.insert(source_item_id); + } + + std::set<ConnectingItemId> marked_connecting_item_set; + std::set<ConnectingItemId> layer_connecting_item_set; + { + if constexpr (ItemTypeId<Dimension>::itemTId(source_item_type) == + ItemTypeId<Dimension>::itemTId(connecting_item_type)) { + ConnectingItemId connecting_id = + ConnectingItemId{static_cast<typename SourceItemId::base_type>(source_item_id)}; + layer_connecting_item_set.insert(connecting_id); + marked_connecting_item_set.insert(connecting_id); + } else { + auto source_item_to_connecting_item_list = source_item_to_connecting_item_matrix[source_item_id]; + for (size_t i_connecting_item = 0; i_connecting_item < source_item_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = source_item_to_connecting_item_list[i_connecting_item]; + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + } + + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + + for (auto&& connecting_item_id : layer_connecting_item_set) { + auto connecting_item_to_cell_list = connecting_to_cell_matrix[connecting_item_id]; + for (size_t i_connecting_item_of_cell = 0; i_connecting_item_of_cell < connecting_item_to_cell_list.size(); + ++i_connecting_item_of_cell) { + const CellId connecting_item_id_of_cell = connecting_item_to_cell_list[i_connecting_item_of_cell]; + if (not marked_cell_set.contains(connecting_item_id_of_cell)) { + cell_set.insert(connecting_item_id_of_cell); + marked_cell_set.insert(connecting_item_id_of_cell); + } + } + } + + layer_connecting_item_set.clear(); + for (auto layer_cell_id : cell_set) { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[layer_cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + if (not marked_connecting_item_set.contains(connecting_item_id)) { + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + } + + for (auto&& set_cell_id : cell_set) { + expected_stencil.push_back(set_cell_id); + } + } + + auto cell_stencil = stencil_array[source_item_id]; + + auto i_set_cell = expected_stencil.begin(); + if (cell_stencil.size() != expected_stencil.size()) { + return false; + } + + for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { + if (*i_set_cell != cell_stencil[index]) { + return false; + } + } + } + } + return true; + }; + + SECTION("inner stencil") + { + SECTION("1 layer") + { + SECTION("1D") + { + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + + SECTION("unordered") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + } + + SECTION("2D") + { + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + } + + SECTION("3D") + { + SECTION("carteian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_nodes}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_edges}), + 1)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(connectivity, + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor:: + ConnectionType::by_faces}), + 1)); + } + } + } + + SECTION("2 layers") + { + NbGhostLayersTester nb_ghost_layers_tester(2); + + SECTION("1D") + { + SECTION("cartesian") + { + auto mesh_v = CartesianMeshBuilder(TinyVector<1>{0}, TinyVector<1>{1}, TinyVector<1, uint64_t>(20)).mesh(); + + const auto& mesh = *mesh_v->get<Mesh<1>>(); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_nodes}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_edges}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_faces}), + 2)); + } + } + + SECTION("2D") + { + SECTION("cartesian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<2>{0, 0}, TinyVector<2>{1, 2}, TinyVector<2, uint64_t>(5, 7)).mesh(); + const auto& mesh = *mesh_v->get<Mesh<2>>(); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_nodes}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_edges}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_faces}), + 2)); + } + } + + SECTION("3D") + { + SECTION("carteian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<3>{0, 0, 0}, TinyVector<3>{1, 1, 2}, TinyVector<3, uint64_t>(3, 4, 5)) + .mesh(); + const auto& mesh = *mesh_v->get<Mesh<3>>(); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::node>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_nodes}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::edge>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_edges}), + 2)); + + REQUIRE( + is_valid.template + operator()<ItemType::node, + ItemType::face>(mesh.connectivity(), + StencilManager::instance() + .getNodeToCellStencilArray(mesh.connectivity(), + StencilDescriptor{2, StencilDescriptor:: + ConnectionType::by_faces}), + 2)); + } + } + } + } + + SECTION("Stencil using symmetries") + { + auto check_ghost_nodes_have_empty_stencils = [](const auto& stencil_array, const auto& connecticity) { + bool is_empty = true; + + auto node_is_owned = connecticity.nodeIsOwned(); + + for (NodeId node_id = 0; node_id < connecticity.numberOfNodes(); ++node_id) { + if (not node_is_owned[node_id]) { + is_empty &= (stencil_array[node_id].size() == 0); + for (size_t i_symmetry_stencil = 0; + i_symmetry_stencil < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry_stencil) { + is_empty &= + (stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].stencilArray()[node_id].size() == + 0); + } + } + } + + return is_empty; + }; + + auto are_symmetry_stencils_valid = + []<ItemType source_item_type, ItemType connecting_item_type>(const auto& stencil_array, const auto& mesh, + const size_t number_of_layers) { + bool are_valid_symmetries = true; + + constexpr const size_t Dimension = std::decay_t<decltype(mesh)>::Dimension; + + auto connecting_to_cell_matrix = + mesh.connectivity().template getItemToItemMatrix<connecting_item_type, ItemType::cell>(); + auto cell_to_connecting_item_matrix = + mesh.connectivity().template getItemToItemMatrix<ItemType::cell, connecting_item_type>(); + // auto source_item_is_owned = mesh.connectivity().cellIsOwned(); + auto cell_number = mesh.connectivity().cellNumber(); + auto source_item_is_owned = mesh.connectivity().template isOwned<source_item_type>(); + + auto connecting_number = mesh.connectivity().template number<connecting_item_type>(); + + auto source_item_to_connecting_item_matrix = + [](const auto& given_connectivity) -> ItemToItemMatrix<source_item_type, connecting_item_type> { + if constexpr (ItemTypeId<Dimension>::itemTId(source_item_type) == + ItemTypeId<Dimension>::itemTId(connecting_item_type)) { + return ConnectivityMatrix{}; + } else { + return given_connectivity.template getItemToItemMatrix<source_item_type, connecting_item_type>(); + } + }(mesh.connectivity()); + + using SourceItemId = ItemIdT<source_item_type>; + using ConnectingItemId = ItemIdT<connecting_item_type>; + using MeshType = std::decay_t<decltype(mesh)>; + + for (size_t i_symmetry_stencil = 0; + i_symmetry_stencil < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry_stencil) { + const IBoundaryDescriptor& boundary_descriptor = + stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].boundaryDescriptor(); + + auto boundary_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry_stencil].stencilArray(); + auto boundary_face_list = getMeshFaceBoundary(mesh, boundary_descriptor); + + std::set<ConnectingItemId> sym_connecting_item_set; + + if constexpr (ItemTypeId<MeshType::Dimension>::itemTId(connecting_item_type) == + ItemTypeId<MeshType::Dimension>::itemTId(ItemType::face)) { + for (size_t i_face = 0; i_face < boundary_face_list.faceList().size(); ++i_face) { + const FaceId face_id = boundary_face_list.faceList()[i_face]; + sym_connecting_item_set.insert(ConnectingItemId{FaceId::base_type{face_id}}); + } + + } else { + auto face_to_connecting_item_matrix = + mesh.connectivity().template getItemToItemMatrix<ItemType::face, connecting_item_type>(); + + for (size_t i_face = 0; i_face < boundary_face_list.faceList().size(); ++i_face) { + const FaceId face_id = boundary_face_list.faceList()[i_face]; + auto connecting_item_list = face_to_connecting_item_matrix[face_id]; + for (size_t i_connecting_item = 0; i_connecting_item < connecting_item_list.size(); ++i_connecting_item) { + sym_connecting_item_set.insert(connecting_item_list[i_connecting_item]); + } + } + } + + for (SourceItemId source_item_id = 0; source_item_id < mesh.template numberOf<source_item_type>(); + ++source_item_id) { + if (not source_item_is_owned[source_item_id]) { + are_valid_symmetries &= (boundary_stencil[source_item_id].size() == 0); + } else { + std::vector<CellId> expected_stencil; + + std::set<CellId> marked_cell_set; + if constexpr (source_item_type == ItemType::cell) { + marked_cell_set.insert(source_item_id); + } + + std::set<ConnectingItemId> marked_connecting_item_set; + std::set<ConnectingItemId> layer_connecting_item_set; + if constexpr (ItemTypeId<Dimension>::itemTId(source_item_type) == + ItemTypeId<Dimension>::itemTId(connecting_item_type)) { + const ConnectingItemId connecting_item_id = + static_cast<typename SourceItemId::base_type>(source_item_id); + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } else { + auto source_item_to_connecting_item_list = source_item_to_connecting_item_matrix[source_item_id]; + for (size_t i_connecting_item = 0; i_connecting_item < source_item_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = source_item_to_connecting_item_list[i_connecting_item]; + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + + std::set<ConnectingItemId> marked_sym_connecting_item_set; + std::set<ConnectingItemId> layer_sym_connecting_item_set; + + for (auto&& connecting_item_id : marked_connecting_item_set) { + if (sym_connecting_item_set.contains(connecting_item_id)) { + marked_sym_connecting_item_set.insert(connecting_item_id); + layer_sym_connecting_item_set.insert(connecting_item_id); + } + } + + std::set<CellId> marked_sym_cell_set; + + for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) { + std::set<CellId, std::function<bool(CellId, CellId)>> cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + + for (auto&& connecting_item_id : layer_connecting_item_set) { + auto connecting_item_to_cell_list = connecting_to_cell_matrix[connecting_item_id]; + for (size_t i_connecting_item_of_cell = 0; + i_connecting_item_of_cell < connecting_item_to_cell_list.size(); ++i_connecting_item_of_cell) { + const CellId connecting_item_id_of_cell = connecting_item_to_cell_list[i_connecting_item_of_cell]; + if (not marked_cell_set.contains(connecting_item_id_of_cell)) { + cell_set.insert(connecting_item_id_of_cell); + marked_cell_set.insert(connecting_item_id_of_cell); + } + } + } + + std::set<CellId, std::function<bool(CellId, CellId)>> sym_cell_set( + [=](CellId cell_0, CellId cell_1) { return cell_number[cell_0] < cell_number[cell_1]; }); + + for (auto&& connecting_item_id : layer_sym_connecting_item_set) { + auto connecting_item_to_cell_list = connecting_to_cell_matrix[connecting_item_id]; + for (size_t i_connecting_item_of_cell = 0; + i_connecting_item_of_cell < connecting_item_to_cell_list.size(); ++i_connecting_item_of_cell) { + const CellId connecting_item_id_of_cell = connecting_item_to_cell_list[i_connecting_item_of_cell]; + if (not marked_sym_cell_set.contains(connecting_item_id_of_cell)) { + sym_cell_set.insert(connecting_item_id_of_cell); + marked_sym_cell_set.insert(connecting_item_id_of_cell); + } + } + } + + for (auto&& set_sym_cell_id : sym_cell_set) { + expected_stencil.push_back(set_sym_cell_id); + } + + layer_connecting_item_set.clear(); + for (auto&& layer_cell_id : cell_set) { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[layer_cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + if (not marked_connecting_item_set.contains(connecting_item_id)) { + layer_connecting_item_set.insert(connecting_item_id); + marked_connecting_item_set.insert(connecting_item_id); + } + } + } + + layer_sym_connecting_item_set.clear(); + + for (auto&& connecting_item_id : layer_connecting_item_set) { + if (sym_connecting_item_set.contains(connecting_item_id)) { + if (not marked_sym_connecting_item_set.contains(connecting_item_id)) { + marked_sym_connecting_item_set.insert(connecting_item_id); + layer_sym_connecting_item_set.insert(connecting_item_id); + } + } + } + + for (auto layer_sym_cell_id : sym_cell_set) { + auto cell_to_connecting_item_list = cell_to_connecting_item_matrix[layer_sym_cell_id]; + for (size_t i_connecting_item = 0; i_connecting_item < cell_to_connecting_item_list.size(); + ++i_connecting_item) { + const ConnectingItemId connecting_item_id = cell_to_connecting_item_list[i_connecting_item]; + if (not marked_sym_connecting_item_set.contains(connecting_item_id)) { + marked_sym_connecting_item_set.insert(connecting_item_id); + layer_sym_connecting_item_set.insert(connecting_item_id); + } + } + } + } + + auto cell_stencil = boundary_stencil[source_item_id]; + + if (cell_stencil.size() != expected_stencil.size()) { + are_valid_symmetries = false; + } + + auto i_set_cell = expected_stencil.begin(); + for (size_t index = 0; index < cell_stencil.size(); ++index, ++i_set_cell) { + if (*i_set_cell != cell_stencil[index]) { + are_valid_symmetries = false; + } + } + } + } + } + + return are_valid_symmetries; + }; + + SECTION("1 layer") + { + SECTION("1D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + } + + SECTION("unordered") + { + const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + } + } + + SECTION("2D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::edge>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::face>(connectivity, stencil_array, 1)); + } + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::edge>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::face>(connectivity, stencil_array, 1)); + } + } + } + + SECTION("3D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMAX")); + + SECTION("cartesian") + { + const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::edge>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::face>(connectivity, stencil_array, 1)); + } + } + + SECTION("hybrid") + { + const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::edge>(connectivity, stencil_array, 1)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{1, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 1)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::face>(connectivity, stencil_array, 1)); + } + } + } + } + + SECTION("2 layers") + { + NbGhostLayersTester nb_ghost_layers_tester(2); + + SECTION("1D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + + SECTION("cartesian") + { + auto mesh_v = CartesianMeshBuilder(TinyVector<1>{0}, TinyVector<1>{1}, TinyVector<1, uint64_t>(20)).mesh(); + const auto& mesh = *mesh_v->get<Mesh<1>>(); + + const Connectivity<1>& connectivity = mesh.connectivity(); + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 2); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 2)); + } + } + } + + SECTION("2D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + + SECTION("cartesian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<2>{0, 0}, TinyVector<2>{1, 2}, TinyVector<2, uint64_t>(5, 7)).mesh(); + const auto& mesh = *mesh_v->get<Mesh<2>>(); + + const Connectivity<2>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::edge>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 4); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 2)); + REQUIRE(not(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 2))); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::face>(connectivity, stencil_array, 2)); + } + } + } + + SECTION("3D") + { + StencilManager::BoundaryDescriptorList list; + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("XMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("YMAX")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMIN")); + list.emplace_back(std::make_shared<NamedBoundaryDescriptor>("ZMAX")); + + SECTION("cartesian") + { + auto mesh_v = + CartesianMeshBuilder(TinyVector<3>{0, 0, 0}, TinyVector<3>{1, 1, 2}, TinyVector<3, uint64_t>(3, 4, 5)) + .mesh(); + const auto& mesh = *mesh_v->get<Mesh<3>>(); + + const Connectivity<3>& connectivity = mesh.connectivity(); + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_nodes}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::node>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::node>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_edges}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::edge>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::edge>(connectivity, stencil_array, 2)); + } + + { + auto stencil_array = + StencilManager::instance() + .getNodeToCellStencilArray(connectivity, + StencilDescriptor{2, StencilDescriptor::ConnectionType::by_faces}, list); + + REQUIRE(stencil_array.symmetryBoundaryStencilArrayList().size() == 6); + REQUIRE(check_ghost_nodes_have_empty_stencils(stencil_array, connectivity)); + REQUIRE( + are_symmetry_stencils_valid.template operator()<ItemType::node, ItemType::face>(stencil_array, mesh, 2)); + REQUIRE(is_valid.template operator()<ItemType::node, ItemType::face>(connectivity, stencil_array, 2)); + } + } + } + } + } +} -- GitLab From e39f9fe4963ab8d9d1910496a014494f14f92e15 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Mon, 13 Jan 2025 23:09:12 +0100 Subject: [PATCH 086/122] Remove development warning --- src/mesh/StencilBuilder.hpp | 8 +++----- src/mesh/StencilManager.cpp | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/mesh/StencilBuilder.hpp b/src/mesh/StencilBuilder.hpp index f504cde90..2ed1d3a6c 100644 --- a/src/mesh/StencilBuilder.hpp +++ b/src/mesh/StencilBuilder.hpp @@ -68,13 +68,11 @@ class StencilBuilder friend class StencilManager; - public: -#warning REMAKE THIS FUNCTION PRIVATE template <ItemType source_item_type, ItemType target_item_type> StencilArray<source_item_type, target_item_type> - build(const IConnectivity& connectivity, - const StencilDescriptor& stencil_descriptor, - const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const + _build(const IConnectivity& connectivity, + const StencilDescriptor& stencil_descriptor, + const BoundaryDescriptorList& symmetry_boundary_descriptor_list) const { if constexpr ((source_item_type == ItemType::cell) and (target_item_type == ItemType::cell)) { return buildC2C(connectivity, stencil_descriptor, symmetry_boundary_descriptor_list); diff --git a/src/mesh/StencilManager.cpp b/src/mesh/StencilManager.cpp index 842f8858f..04a8e32a1 100644 --- a/src/mesh/StencilManager.cpp +++ b/src/mesh/StencilManager.cpp @@ -49,8 +49,8 @@ StencilManager::_getStencilArray( Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list})) { stored_source_to_target_stencil_map[Key{connectivity.id(), stencil_descriptor, symmetry_boundary_descriptor_list}] = std::make_shared<StencilArray<source_item_type, target_item_type>>( - StencilBuilder{}.template build<source_item_type, target_item_type>(connectivity, stencil_descriptor, - symmetry_boundary_descriptor_list)); + StencilBuilder{}.template _build<source_item_type, target_item_type>(connectivity, stencil_descriptor, + symmetry_boundary_descriptor_list)); } return *stored_source_to_target_stencil_map.at( -- GitLab From 25660528160b81ea3a78f27b88641d6b2c6fc315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 16 Jan 2025 19:14:42 +0100 Subject: [PATCH 087/122] Add symmetry bc for vector and matrix value discrete functions --- src/scheme/PolynomialReconstruction.cpp | 83 +++++++++++++++++++++---- src/scheme/PolynomialReconstruction.hpp | 4 ++ 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 55a8271a2..0bc65fc85 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -32,6 +32,14 @@ symmetrize_vector(const TinyVector<Dimension>& normal, const TinyVector<Dimensio return u - 2 * dot(u, normal) * normal; } +template <size_t Dimension> +PUGS_INLINE auto +symmetrize_matrix(const TinyVector<Dimension>& normal, const TinyMatrix<Dimension>& A) +{ + const TinyMatrix S = TinyMatrix<Dimension>{identity} - 2 * tensorProduct(normal, normal); + return S * A * S; +} + template <size_t Dimension> PUGS_INLINE auto symmetrize_coordinates(const TinyVector<Dimension>& origin, @@ -790,6 +798,44 @@ PolynomialReconstruction::_createMutableDiscreteFunctionDPKVariantList( return mutable_discrete_function_dpk_variant_list; } +template <MeshConcept MeshType> +void +PolynomialReconstruction::_checkDataAndSymmetriesCompatibility( + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const +{ + for (auto&& discrete_function_variant : discrete_function_variant_list) { + std::visit( + [&](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT> or + is_discrete_function_P0_vector_v<DiscreteFunctionT>) { + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + if constexpr (is_tiny_vector_v<DataType>) { + if constexpr (DataType::Dimension != MeshType::Dimension) { + std::stringstream error_msg; + error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension + << " using a mesh of dimension " << MeshType::Dimension; + throw NormalError(error_msg.str()); + } + } else if constexpr (is_tiny_matrix_v<DataType>) { + if constexpr ((DataType::NumberOfRows != MeshType::Dimension) or + (DataType::NumberOfColumns != MeshType::Dimension)) { + std::stringstream error_msg; + error_msg << "cannot symmetrize matrices of dimensions " << DataType::NumberOfRows << 'x' + << DataType::NumberOfColumns << " using a mesh of dimension " << MeshType::Dimension; + throw NormalError(error_msg.str()); + } + } + } else { + // LCOV_EXCL_START + throw UnexpectedError("invalid discrete function type"); + // LCOV_EXCL_STOP + } + }, + discrete_function_variant->discreteFunction()); + } +} + template <MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::_build( @@ -800,6 +846,10 @@ PolynomialReconstruction::_build( using Rd = TinyVector<MeshType::Dimension>; + if (m_descriptor.symmetryBoundaryDescriptorList().size() > 0) { + this->_checkDataAndSymmetriesCompatibility<MeshType>(discrete_function_variant_list); + } + const size_t number_of_columns = this->_getNumberOfColumns(discrete_function_variant_list); const size_t basis_dimension = @@ -954,21 +1004,32 @@ PolynomialReconstruction::_build( B(index, kB) = qi_qj[k]; } } else { - const DataType& qi_qj = discrete_function[cell_i_id] - qj; - for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { - B(index, kB) = qi_qj[k]; - } + // LCOV_EXCL_START + std::stringstream error_msg; + error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension + << " using a mesh of dimension " << MeshType::Dimension; + throw UnexpectedError(error_msg.str()); + // LCOV_EXCL_STOP } } else if constexpr (is_tiny_matrix_v<DataType>) { if constexpr ((DataType::NumberOfColumns == DataType::NumberOfRows) and (DataType::NumberOfColumns == MeshType::Dimension)) { - throw NotImplementedError("TinyMatrix symmetries for reconstruction of DiscreteFunctionP0"); - } - const DataType& qi_qj = discrete_function[cell_i_id] - qj; - for (size_t k = 0; k < DataType::NumberOfRows; ++k) { - for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - B(index, column_begin + k * DataType::NumberOfColumns + l) = qi_qj(k, l); + const Rd& normal = symmetry_normal_list[i_symmetry]; + + const DataType& qi = discrete_function[cell_i_id]; + const DataType& qi_qj = symmetrize_matrix(normal, qi) - qj; + for (size_t k = 0; k < DataType::NumberOfRows; ++k) { + for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { + B(index, column_begin + k * DataType::NumberOfColumns + l) = qi_qj(k, l); + } } + } else { + // LCOV_EXCL_START + std::stringstream error_msg; + error_msg << "cannot symmetrize matrices of dimensions " << DataType::NumberOfRows << 'x' + << DataType::NumberOfColumns << " using a mesh of dimension " << MeshType::Dimension; + throw UnexpectedError(error_msg.str()); + // LCOV_EXCL_STOP } } } @@ -1031,8 +1092,6 @@ PolynomialReconstruction::_build( } } } else if constexpr (is_tiny_matrix_v<DataType>) { - throw NotImplementedError("NIY TinyMatrix data"); - for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index b2b855271..bf6013639 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -17,6 +17,10 @@ class PolynomialReconstruction size_t _getNumberOfColumns( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + template <MeshConcept MeshType> + void _checkDataAndSymmetriesCompatibility( + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + template <MeshConcept MeshType> std::vector<MutableDiscreteFunctionDPkVariant> _createMutableDiscreteFunctionDPKVariantList( const std::shared_ptr<const MeshType>& p_mesh, -- GitLab From 67fc93c37f2dd8d782dfbc9d162d90515e94f928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 3 Feb 2025 15:13:52 +0100 Subject: [PATCH 088/122] Add symmetry treatment for DiscreteFunctionP0Vector of TinyVector --- src/scheme/PolynomialReconstruction.cpp | 30 +- tests/CMakeLists.txt | 2 +- ...est_PolynomialReconstruction_degree_1.cpp} | 865 +++++++++++++++++- 3 files changed, 886 insertions(+), 11 deletions(-) rename tests/{test_PolynomialReconstruction.cpp => test_PolynomialReconstruction_degree_1.cpp} (52%) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 0bc65fc85..1a2b1e072 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -1085,10 +1085,32 @@ PolynomialReconstruction::_build( for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - if (ghost_cell_list.size() > 0) { - throw NotImplementedError("TinyVector symmetries for reconstruction of DiscreteFunctionP0Vector"); + if constexpr (DataType::Dimension == MeshType::Dimension) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& normal = symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi = discrete_function[cell_i_id][l]; + const DataType& qi_qj = symmetrize_vector(normal, qi) - qj; + for (size_t kB = column_begin + l * DataType::Dimension, k = 0; k < DataType::Dimension; + ++k, ++kB) { + B(index, kB) = qi_qj[k]; + } + } + } + } else { + // LCOV_EXCL_START + std::stringstream error_msg; + error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension + << " using a mesh of dimension " << MeshType::Dimension; + throw UnexpectedError(error_msg.str()); + // LCOV_EXCL_STOP } } } else if constexpr (is_tiny_matrix_v<DataType>) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 690030bb8..6136f26ac 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -275,7 +275,7 @@ add_executable (mpi_unit_tests test_OFStream.cpp test_ParallelChecker_read.cpp test_Partitioner.cpp - test_PolynomialReconstruction.cpp + test_PolynomialReconstruction_degree_1.cpp test_PolynomialReconstructionDescriptor.cpp test_RandomEngine.cpp test_StencilBuilder_cell2cell.cpp diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction_degree_1.cpp similarity index 52% rename from tests/test_PolynomialReconstruction.cpp rename to tests/test_PolynomialReconstruction_degree_1.cpp index 3fd31d952..7c4f07266 100644 --- a/tests/test_PolynomialReconstruction.cpp +++ b/tests/test_PolynomialReconstruction_degree_1.cpp @@ -9,8 +9,13 @@ #include <algebra/SmallMatrix.hpp> #include <algebra/SmallVector.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/LineTransformation.hpp> #include <mesh/Mesh.hpp> #include <mesh/MeshDataManager.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> #include <scheme/DiscreteFunctionDPkVariant.hpp> #include <scheme/DiscreteFunctionP0.hpp> #include <scheme/DiscreteFunctionVariant.hpp> @@ -20,14 +25,14 @@ // clazy:excludeall=non-pod-global-static -TEST_CASE("PolynomialReconstruction", "[scheme]") +TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") { - SECTION("degree 1") + SECTION("without symmetries") { - std::vector<PolynomialReconstructionDescriptor> descriptor_list = { - PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1}, - PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1}, - }; + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1}}; for (auto descriptor : descriptor_list) { SECTION(name(descriptor.integrationMethodType())) @@ -46,6 +51,9 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + auto xr = mesh.xr(); + + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); DiscreteFunctionP0<double> fh{p_mesh}; @@ -56,6 +64,21 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{2}); + { + double max_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto cell_nodes = cell_to_node_matrix[cell_id]; + LineTransformation<1> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + max_error = std::max(max_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + } + } + + REQUIRE(parallel::allReduceMax(max_error) == 0 // Catch::Approx(0).margin(0 * 1E-14) + ); + } + { double max_mean_error = 0; for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { @@ -1056,4 +1079,834 @@ TEST_CASE("PolynomialReconstruction", "[scheme]") } } } + + SECTION("with symmetries") + { + SECTION("errors") + { + SECTION("1D") + { + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}; + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyVector<2>>{p_mesh}), + "error: cannot symmetrize vectors of dimension 2 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyVector<3>>{p_mesh}), + "error: cannot symmetrize vectors of dimension 3 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyVector<2>>{p_mesh, 1}), + "error: cannot symmetrize vectors of dimension 2 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyVector<3>>{p_mesh, 1}), + "error: cannot symmetrize vectors of dimension 3 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyMatrix<2>>{p_mesh}), + "error: cannot symmetrize matrices of dimensions 2x2 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyMatrix<3>>{p_mesh}), + "error: cannot symmetrize matrices of dimensions 3x3 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyMatrix<2>>{p_mesh, 1}), + "error: cannot symmetrize matrices of dimensions 2x2 using a mesh of dimension 1"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyMatrix<3>>{p_mesh, 1}), + "error: cannot symmetrize matrices of dimensions 3x3 using a mesh of dimension 1"); + } + + SECTION("2D") + { + auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}; + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyVector<1>>{p_mesh}), + "error: cannot symmetrize vectors of dimension 1 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyVector<3>>{p_mesh}), + "error: cannot symmetrize vectors of dimension 3 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyVector<1>>{p_mesh, 1}), + "error: cannot symmetrize vectors of dimension 1 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyVector<3>>{p_mesh, 1}), + "error: cannot symmetrize vectors of dimension 3 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyMatrix<1>>{p_mesh}), + "error: cannot symmetrize matrices of dimensions 1x1 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyMatrix<3>>{p_mesh}), + "error: cannot symmetrize matrices of dimensions 3x3 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyMatrix<1>>{p_mesh, 1}), + "error: cannot symmetrize matrices of dimensions 1x1 using a mesh of dimension 2"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyMatrix<3>>{p_mesh, 1}), + "error: cannot symmetrize matrices of dimensions 3x3 using a mesh of dimension 2"); + } + + SECTION("3D") + { + auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}; + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyVector<1>>{p_mesh}), + "error: cannot symmetrize vectors of dimension 1 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyVector<2>>{p_mesh}), + "error: cannot symmetrize vectors of dimension 2 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyVector<1>>{p_mesh, 1}), + "error: cannot symmetrize vectors of dimension 1 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyVector<2>>{p_mesh, 1}), + "error: cannot symmetrize vectors of dimension 2 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyMatrix<1>>{p_mesh}), + "error: cannot symmetrize matrices of dimensions 1x1 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build(DiscreteFunctionP0<TinyMatrix<2>>{p_mesh}), + "error: cannot symmetrize matrices of dimensions 2x2 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyMatrix<1>>{p_mesh, 1}), + "error: cannot symmetrize matrices of dimensions 1x1 using a mesh of dimension 3"); + + REQUIRE_THROWS_WITH(PolynomialReconstruction{descriptor}.build( + DiscreteFunctionP0Vector<TinyMatrix<2>>{p_mesh, 1}), + "error: cannot symmetrize matrices of dimensions 2x2 using a mesh of dimension 3"); + } + } + + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("1D") + { + using R1 = TinyVector<1>; + + SECTION("R^1 data") + { + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + auto& mesh = *p_mesh; + + auto R1_affine = [](const R1& x) { return R1{1.7 * (x[0] + 1)}; }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<R1> fh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R1_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R1>>(); + + { + 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_fh[cell_id](xj[cell_id]) - R1_affine(xj[cell_id]))[0])); + } + REQUIRE(parallel::allReduceMax(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_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1}))[0] / 0.2; + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } + } + + SECTION("R1 vector 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 vector_affine0 = [](const R1& x) -> R1 { return R1{+1.7 * (x[0] + 1)}; }; + + auto vector_affine1 = [](const R1& x) -> R1 { return R1{-0.3 * (x[0] + 1)}; }; + + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0Vector<R1> Vh{p_mesh, 2}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + Vh[cell_id][0] = vector_affine0(xj[cell_id]); + Vh[cell_id][1] = vector_affine1(xj[cell_id]); + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R1>>(); + + { + 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_Vh(cell_id, 0)(xj[cell_id]) - vector_affine0(xj[cell_id]))); + max_mean_error = + std::max(max_mean_error, l2Norm(dpk_Vh(cell_id, 1)(xj[cell_id]) - vector_affine1(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_slope_error = 0; + { + const TinyVector<1> slope0{+1.7}; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < R1::Dimension; ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R1{0.1} + xj[cell_id])[i] - + dpk_Vh(cell_id, 0)(xj[cell_id] - R1{0.1})[i]); + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope0[i])); + } + } + } + + { + const TinyVector<1> slope1{-0.3}; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + for (size_t i = 0; i < R1::Dimension; ++i) { + const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R1{0.1} + xj[cell_id])[i] - + dpk_Vh(cell_id, 1)(xj[cell_id] - R1{0.1})[i]); + + max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope1[i])); + } + } + } + REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + } + } + + SECTION("2D") + { +#warning not done + 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 R_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] = R_affine(xj[cell_id]); }); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + // auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(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_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(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 double reconstructed_slope = + // (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); + // } + // } + // } + // } + + // SECTION("R^3 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 R3_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> uh{p_mesh}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); + + // { + // 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(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_uh[cell_id](R2{0.1, 0} + xj[cell_id]) - + // dpk_uh[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(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R2{0, 0.1} + xj[cell_id]) - + // dpk_uh[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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + // } + // } + // } + + // SECTION("R^2x2 data") + // { + // using R2x2 = TinyMatrix<2, 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 R2x2_affine = [](const R2& x) -> R2x2 { + // return R2x2{+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(); + + // DiscreteFunctionP0<R2x2> Ah{p_mesh}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); + // }); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + // auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + + // { + // 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_Ah[cell_id](xj[cell_id]) - + // R2x2_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0.1, 0} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R2{0.1, 0})); + + // max_x_slope_error = + // std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // + // -0.6, -2.3})); + // } + // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0, 0.1} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R2{0, 0.1})); + + // max_y_slope_error = + // std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + // -2.1, +1.3})); + // } + // REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + // } + // } + // } + + // 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{descriptor}.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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + // } + + // { + // 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + + // { + // 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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + // } + // } + // } + } + + SECTION("3D") + { + using R3 = TinyVector<3>; +#warning not done + + // 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 R_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] = R_affine(xj[cell_id]); }); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + // auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(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_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(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 double reconstructed_slope = + // (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(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 double reconstructed_slope = + // (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + + // SECTION("R^3 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 R3_affine = [](const R3& x) -> R3 { + // return R3{+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]}; + // }; + // auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + // DiscreteFunctionP0<R3> uh{p_mesh}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + // { + // 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(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_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + // dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + + // max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, + // -0.6, 3.1})); + // } + // REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + // dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + + // max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, + // -1.1})); + // } + // REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + // dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + + // max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, + // -3.7, 1.9})); + // } + // REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + + // SECTION("R^2x2 data") + // { + // using R2x2 = TinyMatrix<2, 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 R2x2_affine = [](const R3& x) -> R2x2 { + // return R2x2{+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(); + + // DiscreteFunctionP0<R2x2> Ah{p_mesh}; + + // parallel_for( + // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); + // }); + + // descriptor.setRowWeighting(false); + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + // auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + + // { + // 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_Ah[cell_id](xj[cell_id]) - + // R2x2_affine(xj[cell_id]))); + // } + // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + + // max_x_slope_error = + // std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // + // -2.3, +3.1})); + // } + // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + + // max_y_slope_error = + // std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // + // 1.3, +0.8})); + // } + // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - + // dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + + // max_z_slope_error = + // std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // + // +1.4, -1.8})); + // } + // REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + + // 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]; + // } + // }); + + // descriptor.setPreconditioning(false); + // auto reconstructions = PolynomialReconstruction{descriptor}.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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + // } + + // { + // 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + + // { + // 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(parallel::allReduceMax(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(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); + // } + // } + // } + // } + } + } + } + } } -- GitLab From 435b4bcc3c152b201b089f8e5b9bd0c195db2f4b Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Tue, 18 Mar 2025 23:26:30 +0100 Subject: [PATCH 089/122] Add tests for ConnectivityDispatcher for high number of ghost layers --- tests/test_ConnectivityDispatcher.cpp | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/test_ConnectivityDispatcher.cpp b/tests/test_ConnectivityDispatcher.cpp index 09ec9982e..7cc747588 100644 --- a/tests/test_ConnectivityDispatcher.cpp +++ b/tests/test_ConnectivityDispatcher.cpp @@ -3,6 +3,7 @@ #include <mesh/CartesianMeshBuilder.hpp> #include <mesh/Connectivity.hpp> +#include <mesh/ConnectivityDispatcher.hpp> #include <mesh/GmshReader.hpp> #include <mesh/Mesh.hpp> #include <mesh/MeshVariant.hpp> @@ -125,6 +126,14 @@ TEST_CASE("ConnectivityDispatcher", "[mesh]") } } + constexpr bool has_partitioner = []() { +#if defined(PUGS_HAS_PARMETIS) || defined(PUGS_HAS_PTSCOTCH) + return true; +#else + return false; +#endif + }(); + for (size_t nb_ghost_layers = 2; nb_ghost_layers < 5; ++nb_ghost_layers) { std::stringstream os; os << nb_ghost_layers << " layer meshes"; @@ -143,6 +152,11 @@ TEST_CASE("ConnectivityDispatcher", "[mesh]") CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{23}}.mesh(); const std::shared_ptr p_mesh = cartesian_1d_mesh->get<const Mesh<1>>(); check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers); + + if (has_partitioner) { + ConnectivityDispatcher cd{p_mesh->connectivity()}; + check_number_of_ghost_layers(*cd.dispatchedConnectivity(), nb_ghost_layers); + } } SECTION("Cartesian 2D mesh") @@ -151,6 +165,11 @@ TEST_CASE("ConnectivityDispatcher", "[mesh]") CartesianMeshBuilder{TinyVector<2>{0, -1}, TinyVector<2>{3, 2}, TinyVector<2, size_t>{6, 7}}.mesh(); const std::shared_ptr p_mesh = cartesian_2d_mesh->get<const Mesh<2>>(); check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers); + + if (has_partitioner) { + ConnectivityDispatcher cd{p_mesh->connectivity()}; + check_number_of_ghost_layers(*cd.dispatchedConnectivity(), nb_ghost_layers); + } } SECTION("Cartesian 3D mesh") @@ -159,6 +178,11 @@ TEST_CASE("ConnectivityDispatcher", "[mesh]") CartesianMeshBuilder{TinyVector<3>{0, 1, 0}, TinyVector<3>{2, -1, 3}, TinyVector<3, size_t>{6, 7, 4}}.mesh(); const std::shared_ptr p_mesh = cartesian_3d_mesh->get<const Mesh<3>>(); check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers); + + if (has_partitioner) { + ConnectivityDispatcher cd{p_mesh->connectivity()}; + check_number_of_ghost_layers(*cd.dispatchedConnectivity(), nb_ghost_layers); + } } SECTION("unordered 1d mesh") @@ -169,6 +193,11 @@ TEST_CASE("ConnectivityDispatcher", "[mesh]") const std::shared_ptr p_mesh = mesh_v->get<const Mesh<1>>(); check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers); + + if (has_partitioner) { + ConnectivityDispatcher cd{p_mesh->connectivity()}; + check_number_of_ghost_layers(*cd.dispatchedConnectivity(), nb_ghost_layers); + } } SECTION("hybrid 2d mesh") @@ -179,6 +208,11 @@ TEST_CASE("ConnectivityDispatcher", "[mesh]") const std::shared_ptr p_mesh = mesh_v->get<const Mesh<2>>(); check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers); + + if (has_partitioner) { + ConnectivityDispatcher cd{p_mesh->connectivity()}; + check_number_of_ghost_layers(*cd.dispatchedConnectivity(), nb_ghost_layers); + } } SECTION("hybrid 3d mesh") @@ -189,6 +223,11 @@ TEST_CASE("ConnectivityDispatcher", "[mesh]") const std::shared_ptr p_mesh = mesh_v->get<const Mesh<3>>(); check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers); + + if (has_partitioner) { + ConnectivityDispatcher cd{p_mesh->connectivity()}; + check_number_of_ghost_layers(*cd.dispatchedConnectivity(), nb_ghost_layers); + } } } -- GitLab From b9123b3db24954498a95eef8fc0ccb6b0389740b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 27 Mar 2025 11:42:09 +0100 Subject: [PATCH 090/122] Add tests for polynomial reconstruction of degree 1 with symmetry Tests are only performed one vectors or R^d in dimension d The reason is that we check exact reconstructions with symmetries. Affine reconstruction with symmetries cannot be exact for other types (R, and R^dxd). --- ...test_PolynomialReconstruction_degree_1.cpp | 979 +++++++----------- 1 file changed, 386 insertions(+), 593 deletions(-) diff --git a/tests/test_PolynomialReconstruction_degree_1.cpp b/tests/test_PolynomialReconstruction_degree_1.cpp index 7c4f07266..6c333913e 100644 --- a/tests/test_PolynomialReconstruction_degree_1.cpp +++ b/tests/test_PolynomialReconstruction_degree_1.cpp @@ -1196,24 +1196,24 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") } } - std::vector<PolynomialReconstructionDescriptor> descriptor_list = - {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1, - std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ - std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, - PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1, - std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ - std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, - PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1, - std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ - std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; - - for (auto descriptor : descriptor_list) { - SECTION(name(descriptor.integrationMethodType())) - { - SECTION("1D") + SECTION("1D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; + + using R1 = TinyVector<1>; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) { - using R1 = TinyVector<1>; - SECTION("R^1 data") { auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); @@ -1323,588 +1323,381 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") } } } + } + } - SECTION("2D") + SECTION("2D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}}; + + using R2 = TinyVector<2>; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) { -#warning not done - using R2 = TinyVector<2>; + SECTION("R^2 data") + { + auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + auto& mesh = *p_mesh; + + auto R2_affine = [](const R2& x) -> R2 { return R2{2.3 * (x[0] - 2), -1.3 * (x[1] - 1)}; }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0<R2> fh{p_mesh}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R2_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - // 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 R_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] = R_affine(xj[cell_id]); }); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - - // auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - // } - // REQUIRE(parallel::allReduceMax(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_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(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 double reconstructed_slope = - // (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); - // } - // } - // } - // } - - // SECTION("R^3 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 R3_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> uh{p_mesh}; - - // parallel_for( - // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - - // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); - - // { - // 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - // } - // REQUIRE(parallel::allReduceMax(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_uh[cell_id](R2{0.1, 0} + xj[cell_id]) - - // dpk_uh[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(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R2{0, 0.1} + xj[cell_id]) - - // dpk_uh[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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - // } - // } - // } - // } - - // SECTION("R^2x2 data") - // { - // using R2x2 = TinyMatrix<2, 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 R2x2_affine = [](const R2& x) -> R2x2 { - // return R2x2{+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(); - - // DiscreteFunctionP0<R2x2> Ah{p_mesh}; - - // parallel_for( - // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); - // }); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - - // auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); - - // { - // 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_Ah[cell_id](xj[cell_id]) - - // R2x2_affine(xj[cell_id]))); - // } - // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0.1, 0} + xj[cell_id]) - - // dpk_Ah[cell_id](xj[cell_id] - R2{0.1, 0})); - - // max_x_slope_error = - // std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // - // -0.6, -2.3})); - // } - // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0, 0.1} + xj[cell_id]) - - // dpk_Ah[cell_id](xj[cell_id] - R2{0, 0.1})); - - // max_y_slope_error = - // std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // - // -2.1, +1.3})); - // } - // REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - // } - // } - // } - // } - - // 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{descriptor}.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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - // } - - // { - // 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - // } - - // { - // 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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - // } - // } - // } - // } + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2>>(); + + { + 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_fh[cell_id](xj[cell_id]) - R2_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(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 R2 reconstructed_slope = + 1. / 0.2 * (dpk_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0.1, 0})); + + max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R2{2.3, 0})); + } + REQUIRE(parallel::allReduceMax(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 R2 reconstructed_slope = + 1 / 0.2 * (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0, 0.1})); + + max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - (R2{0, -1.3}))); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); + } + } + + SECTION("vector of R2") + { + using R4 = TinyVector<4>; + + auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + auto& mesh = *p_mesh; + + auto vector_affine = [](const R2& x) -> R4 { + return R4{+1.7 * (x[0] - 2), // + -0.6 * (x[1] - 1), // + -2.3 * (x[0] - 2), // + +1.1 * (x[1] - 1)}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0Vector<R2> Vh{p_mesh, 2}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto vector = vector_affine(xj[cell_id]); + Vh[cell_id][0][0] = vector[0]; + Vh[cell_id][0][1] = vector[1]; + Vh[cell_id][1][0] = vector[2]; + Vh[cell_id][1][1] = vector[3]; + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const R2>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto vector = vector_affine(xj[cell_id]); + + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[0] - vector[0])); + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[1] - vector[1])); + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[0] - vector[2])); + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[1] - vector[3])); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_x_slope_error = 0; + const R4 slope{+1.7, 0, -2.3, 0}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R2{0.1, 0} + xj[cell_id]) - + dpk_Vh(cell_id, 0)(xj[cell_id] - R2{0.1, 0})); + + const R2 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R2{0.1, 0} + xj[cell_id]) - + dpk_Vh(cell_id, 1)(xj[cell_id] - R2{0.1, 0})); + + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[0] - slope[0])); + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[1] - slope[1])); + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[0] - slope[2])); + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[1] - slope[3])); + } + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double max_y_slope_error = 0; + const R4 slope{0, -0.6, 0, 1.1}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R2 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R2{0, 0.1} + xj[cell_id]) - + dpk_Vh(cell_id, 0)(xj[cell_id] - R2{0, 0.1})); + + const R2 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R2{0, 0.1} + xj[cell_id]) - + dpk_Vh(cell_id, 1)(xj[cell_id] - R2{0, 0.1})); + + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[0] - slope[0])); + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[1] - slope[1])); + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[0] - slope[2])); + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[1] - slope[3])); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + } + } } + } + } - SECTION("3D") + SECTION("3D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}}; + + using R3 = TinyVector<3>; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) { - using R3 = TinyVector<3>; -#warning not done - - // 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 R_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] = R_affine(xj[cell_id]); }); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - - // auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - // } - // REQUIRE(parallel::allReduceMax(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_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(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 double reconstructed_slope = - // (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(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 double reconstructed_slope = - // (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } - // } - - // SECTION("R^3 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 R3_affine = [](const R3& x) -> R3 { - // return R3{+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]}; - // }; - // auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - // DiscreteFunctionP0<R3> uh{p_mesh}; - - // parallel_for( - // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - - // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); - - // { - // 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - // } - // REQUIRE(parallel::allReduceMax(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_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - - // dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - - // max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, - // -0.6, 3.1})); - // } - // REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - - // dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - - // max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, - // -1.1})); - // } - // REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - - // dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - - // max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, - // -3.7, 1.9})); - // } - // REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } - // } - - // SECTION("R^2x2 data") - // { - // using R2x2 = TinyMatrix<2, 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 R2x2_affine = [](const R3& x) -> R2x2 { - // return R2x2{+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(); - - // DiscreteFunctionP0<R2x2> Ah{p_mesh}; - - // parallel_for( - // mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); - // }); - - // descriptor.setRowWeighting(false); - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - - // auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); - - // { - // 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_Ah[cell_id](xj[cell_id]) - - // R2x2_affine(xj[cell_id]))); - // } - // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - - // dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - - // max_x_slope_error = - // std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // - // -2.3, +3.1})); - // } - // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - - // dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - - // max_y_slope_error = - // std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // - // 1.3, +0.8})); - // } - // REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - - // dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - - // max_z_slope_error = - // std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // - // +1.4, -1.8})); - // } - // REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } - // } - - // 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]; - // } - // }); - - // descriptor.setPreconditioning(false); - // auto reconstructions = PolynomialReconstruction{descriptor}.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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - // } - - // { - // 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - // } - - // { - // 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(parallel::allReduceMax(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(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); - // } - // } - // } - // } + SECTION("R^3 data") + { + auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + auto& mesh = *p_mesh; + + auto R3_affine = [](const R3& x) -> R3 { + return R3{2.3 * (x[0] - 2), -1.3 * (x[1] - 1), 1.4 * (x[2] - 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] = R3_affine(xj[cell_id]); }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + { + 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_fh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); + } + REQUIRE(parallel::allReduceMax(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_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); + + max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{2.3, 0, 0})); + } + REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = + 1 / 0.2 * + (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); + + max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - (R3{0, -1.3, 0}))); + } + REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = + 1 / 0.2 * + (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); + + max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - (R3{0, 0, 1.4}))); + } + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); + } + } + + SECTION("vector of R2") + { + using R6 = TinyVector<6>; + + auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + auto& mesh = *p_mesh; + + auto vector_affine = [](const R3& x) -> R6 { + return R6{+1.7 * (x[0] - 2), // + -0.6 * (x[1] - 1), // + +1.2 * (x[2] - 1), // + -2.3 * (x[0] - 2), // + +1.1 * (x[1] - 1), // + -0.3 * (x[2] - 1)}; + }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + + DiscreteFunctionP0Vector<R3> Vh{p_mesh, 2}; + + parallel_for( + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + auto vector = vector_affine(xj[cell_id]); + Vh[cell_id][0][0] = vector[0]; + Vh[cell_id][0][1] = vector[1]; + Vh[cell_id][0][2] = vector[2]; + Vh[cell_id][1][0] = vector[3]; + Vh[cell_id][1][1] = vector[4]; + Vh[cell_id][1][2] = vector[5]; + }); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const R3>>(); + + { + double max_mean_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto vector = vector_affine(xj[cell_id]); + + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[0] - vector[0])); + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[1] - vector[1])); + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[2] - vector[2])); + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[0] - vector[3])); + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[1] - vector[4])); + max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[2] - vector[5])); + } + REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + } + + { + double max_x_slope_error = 0; + const R6 slope{+1.7, 0, 0, -2.3, 0, 0}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R3{0.1, 0, 0} + xj[cell_id]) - + dpk_Vh(cell_id, 0)(xj[cell_id] - R3{0.1, 0, 0})); + + const R3 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R3{0.1, 0, 0} + xj[cell_id]) - + dpk_Vh(cell_id, 1)(xj[cell_id] - R3{0.1, 0, 0})); + + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[0] - slope[0])); + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[1] - slope[1])); + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[2] - slope[2])); + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[0] - slope[3])); + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[1] - slope[4])); + max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[2] - slope[5])); + } + REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double max_y_slope_error = 0; + const R6 slope{0, -0.6, 0, 0, 1.1, 0}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R3{0, 0.1, 0} + xj[cell_id]) - + dpk_Vh(cell_id, 0)(xj[cell_id] - R3{0, 0.1, 0})); + + const R3 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R3{0, 0.1, 0} + xj[cell_id]) - + dpk_Vh(cell_id, 1)(xj[cell_id] - R3{0, 0.1, 0})); + + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[0] - slope[0])); + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[1] - slope[1])); + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[2] - slope[2])); + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[0] - slope[3])); + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[1] - slope[4])); + max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[2] - slope[5])); + } + REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); + } + + { + double max_z_slope_error = 0; + const R6 slope{0, 0, 1.2, 0, 0, -0.3}; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + const R3 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R3{0, 0, 0.1} + xj[cell_id]) - + dpk_Vh(cell_id, 0)(xj[cell_id] - R3{0, 0, 0.1})); + + const R3 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R3{0, 0, 0.1} + xj[cell_id]) - + dpk_Vh(cell_id, 1)(xj[cell_id] - R3{0, 0, 0.1})); + + max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope0[0] - slope[0])); + max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope0[1] - slope[1])); + max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope0[2] - slope[2])); + max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope1[0] - slope[3])); + max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope1[1] - slope[4])); + max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope1[2] - slope[5])); + } + REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); + } + } } } } -- GitLab From 0c8cd0a1dde5a910fef9fc04ff85af52981231b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 27 Mar 2025 19:28:42 +0100 Subject: [PATCH 091/122] Improve tests for degree 1 reconstruction in view of degree 2 (WIP) --- ...test_PolynomialReconstruction_degree_1.cpp | 694 +++++++----------- 1 file changed, 272 insertions(+), 422 deletions(-) diff --git a/tests/test_PolynomialReconstruction_degree_1.cpp b/tests/test_PolynomialReconstruction_degree_1.cpp index 6c333913e..927a5f4c3 100644 --- a/tests/test_PolynomialReconstruction_degree_1.cpp +++ b/tests/test_PolynomialReconstruction_degree_1.cpp @@ -13,6 +13,9 @@ #include <analysis/QuadratureFormula.hpp> #include <analysis/QuadratureManager.hpp> #include <geometry/LineTransformation.hpp> +#include <geometry/SquareTransformation.hpp> +#include <geometry/TetrahedronTransformation.hpp> +#include <geometry/TriangleTransformation.hpp> #include <mesh/Mesh.hpp> #include <mesh/MeshDataManager.hpp> #include <mesh/NamedBoundaryDescriptor.hpp> @@ -23,8 +26,195 @@ #include <MeshDataBaseForTests.hpp> +#include <type_traits> + // clazy:excludeall=non-pod-global-static +namespace test_only +{ + +template <typename DataType> +PUGS_INLINE double +get_max_error(const DataType& x, const DataType& y) +{ + if constexpr (is_tiny_matrix_v<DataType>) { + return frobeniusNorm(x - y); + } else if constexpr (is_tiny_vector_v<DataType>) { + return l2Norm(x - y); + } else { + static_assert(std::is_arithmetic_v<DataType>, "expecting arithmetic type"); + return std::abs(x - y); + } +} + +template <MeshConcept MeshType, typename DataType> +double +max_reconstruction_error(const MeshType& mesh, + DiscreteFunctionDPk<MeshType::Dimension, const DataType> dpk_f, + std::function<DataType(const TinyVector<MeshType::Dimension>&)> exact) +{ + auto xr = mesh.xr(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + double max_error = 0; + auto cell_type = mesh.connectivity().cellType(); + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto cell_nodes = cell_to_node_matrix[cell_id]; + if constexpr (MeshType::Dimension == 1) { + Assert(cell_type[cell_id] == CellType::Line); + LineTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; + auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + } else if constexpr (MeshType::Dimension == 2) { + switch (cell_type[cell_id]) { + case CellType::Triangle: { + TriangleTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]]}; + auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + case CellType::Quadrangle: { + SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } else if constexpr (MeshType::Dimension == 3) { + switch (cell_type[cell_id]) { + case CellType::Tetrahedron: { + TetrahedronTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + // case CellType::Pyramid: { + // SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + // xr[cell_nodes[3]]}; + // auto qf = QuadratureManager::instance().getSquareFormula(GaussLobattoQuadratureDescriptor{dpk_f.degree() + 1}); + // for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + // auto x = T(qf.point(i_quadrarture)); + // max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + // } + // break; + // } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } + } + return max_error; +} + +template <MeshConcept MeshType, typename DataType, size_t NbComponents> +double +max_reconstruction_error( + const MeshType& mesh, + DiscreteFunctionDPkVector<MeshType::Dimension, const DataType> dpk_v, + const std::array<std::function<DataType(const TinyVector<MeshType::Dimension>&)>, NbComponents>& vector_exact) +{ + auto xr = mesh.xr(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + double max_error = 0; + auto cell_type = mesh.connectivity().cellType(); + + REQUIRE(NbComponents == dpk_v.numberOfComponents()); + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto cell_nodes = cell_to_node_matrix[cell_id]; + if constexpr (MeshType::Dimension == 1) { + Assert(cell_type[cell_id] == CellType::Line); + LineTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; + auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + } else if constexpr (MeshType::Dimension == 2) { + switch (cell_type[cell_id]) { + case CellType::Triangle: { + TriangleTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]]}; + auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + case CellType::Quadrangle: { + SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } else if constexpr (MeshType::Dimension == 3) { + switch (cell_type[cell_id]) { + case CellType::Tetrahedron: { + TetrahedronTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + // case CellType::Pyramid: { + // SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + // xr[cell_nodes[3]]}; + // auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + // for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + // auto x = T(qf.point(i_quadrarture)); + // max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + // } + // break; + // } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } + } + return max_error; +} + +} // namespace test_only + TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") { SECTION("without symmetries") @@ -49,55 +239,20 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - auto xr = mesh.xr(); - - auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + auto R_exact = [](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] = R_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_exact(xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); - auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{2}); - { - double max_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto cell_nodes = cell_to_node_matrix[cell_id]; - LineTransformation<1> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - max_error = std::max(max_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - } - - REQUIRE(parallel::allReduceMax(max_error) == 0 // Catch::Approx(0).margin(0 * 1E-14) - ); - } - - { - 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -112,7 +267,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto R3_affine = [](const R1& x) -> R3 { + auto R3_exact = [](const R1& x) -> R3 { return R3{+2.3 + 1.7 * x[0], // +1.4 - 0.6 * x[0], // -0.2 + 3.1 * x[0]}; @@ -122,31 +277,14 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") DiscreteFunctionP0<R3> uh{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_exact(xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); - { - 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[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(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -161,7 +299,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto R3x3_affine = [](const R1& x) -> R3x3 { + auto R3x3_exact = [](const R1& x) -> R3x3 { return R3x3{ +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], @@ -173,94 +311,46 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") DiscreteFunctionP0<R3x3> Ah{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_exact(xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); - { - 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_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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 R3x3 reconstructed_slope = - (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); - - R3x3 slops = R3x3{+1.7, +2.1, -0.6, // - -2.3, +3.1, -3.6, // - +3.1, +2.9, +2.3}; - - max_slope_error = std::max(max_slope_error, // - frobeniusNorm(reconstructed_slope - slops)); - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-13)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } 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; + std::array<std::function<double(const R1&)>, 3> vector_exact = + {[](const R1& x) -> double { return +2.3 + 1.7 * x[0]; }, + [](const R1& x) -> double { return -1.7 + 2.1 * x[0]; }, + [](const R1& x) -> double { return +1.4 - 0.6 * x[0]; }}; - 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]; + for (size_t i = 0; i < vector_exact.size(); ++i) { + Vh[cell_id][i] = vector_exact[i](xj[cell_id]); } }); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); - 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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - 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(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -275,13 +365,13 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto vector_affine0 = [](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 vector_affine1 = [](const R1& x) -> R3 { - return R3{+1.6 + 0.7 * x[0], -2.1 + 1.2 * x[0], +1.1 - 0.3 * x[0]}; - }; + std::array<std::function<R3(const R1&)>, 2> vector_exact = + {[](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; + }, + [](const R1& x) -> R3 { + return R3{+1.6 + 0.7 * x[0], -2.1 + 1.2 * x[0], +1.1 - 0.3 * x[0]}; + }}; auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); @@ -289,54 +379,16 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - Vh[cell_id][0] = vector_affine0(xj[cell_id]); - Vh[cell_id][1] = vector_affine1(xj[cell_id]); + Vh[cell_id][0] = vector_exact[0](xj[cell_id]); + Vh[cell_id][1] = vector_exact[1](xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R3>>(); - { - 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_Vh(cell_id, 0)(xj[cell_id]) - vector_affine0(xj[cell_id]))); - max_mean_error = - std::max(max_mean_error, l2Norm(dpk_Vh(cell_id, 1)(xj[cell_id]) - vector_affine1(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_slope_error = 0; - { - const TinyVector<3> slope0{+1.7, +2.1, -0.6}; - - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - for (size_t i = 0; i < R3::Dimension; ++i) { - const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R1{0.1} + xj[cell_id])[i] - - dpk_Vh(cell_id, 0)(xj[cell_id] - R1{0.1})[i]); - - max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope0[i])); - } - } - } - - { - const TinyVector<3> slope1{+0.7, +1.2, -0.3}; - - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - for (size_t i = 0; i < R3::Dimension; ++i) { - const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R1{0.1} + xj[cell_id])[i] - - dpk_Vh(cell_id, 1)(xj[cell_id] - R1{0.1})[i]); - - max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope1[i])); - } - } - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -352,15 +404,15 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; + auto R_exact = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; - auto R3_affine = [](const R1& x) -> R3 { + auto R3_exact = [](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 R3x3_affine = [](const R1& x) -> R3x3 { + auto R3x3_exact = [](const R1& x) -> R3x3 { return R3x3{ +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], @@ -368,30 +420,30 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[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]}; - }; + std::array<std::function<double(const R1&)>, 3> vector_exact = + {[](const R1& x) -> double { return +2.3 + 1.7 * x[0]; }, + [](const R1& x) -> double { return -1.7 + 2.1 * x[0]; }, + [](const R1& x) -> double { return +1.4 - 0.6 * 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] = R_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_exact(xj[cell_id]); }); DiscreteFunctionP0<R3> uh{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_exact(xj[cell_id]); }); DiscreteFunctionP0<R3x3> Ah{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_exact(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]; + for (size_t i = 0; i < vector_exact.size(); ++i) { + Vh[cell_id][i] = vector_exact[i](xj[cell_id]); } }); @@ -400,103 +452,28 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), DiscreteFunctionVariant(Vh)); - auto dpk_fh = 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } - - auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); - - { - 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[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(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } - auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); - { - 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_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } { - double max_slope_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3x3 reconstructed_slope = - (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1})); - - R3x3 slops = R3x3{+1.7, +2.1, -0.6, // - -2.3, +3.1, -3.6, // - +3.1, +2.9, +2.3}; - - max_slope_error = std::max(max_slope_error, // - frobeniusNorm(reconstructed_slope - slops)); - } - REQUIRE(parallel::allReduceMax(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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); + auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } { - 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(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); + auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -515,48 +492,20 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - auto R_affine = [](const R2& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1]; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + auto R_exact = [](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] = R_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_exact(xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(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 double reconstructed_slope = - (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -571,7 +520,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - auto R3_affine = [](const R2& x) -> R3 { + auto R3_exact = [](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]}; @@ -581,42 +530,14 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") DiscreteFunctionP0<R3> uh{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_exact(xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); - { - 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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_uh[cell_id](R2{0.1, 0} + xj[cell_id]) - - dpk_uh[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(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R2{0, 0.1} + xj[cell_id]) - - dpk_uh[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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -631,7 +552,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - auto R2x2_affine = [](const R2& x) -> R2x2 { + auto R2x2_exact = [](const R2& x) -> R2x2 { return R2x2{+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]}; }; @@ -640,118 +561,47 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") DiscreteFunctionP0<R2x2> Ah{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_exact(xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); - - { - 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_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0.1, 0} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R2{0.1, 0})); - - max_x_slope_error = - std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1, // - -0.6, -2.3})); - } - REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0, 0.1} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R2{0, 0.1})); - - max_y_slope_error = - std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // - -2.1, +1.3})); - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - } + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } 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]}; - }; + std::array<std::function<double(const R2&)>, 4> vector_exact = + {[](const R2& x) -> double { return +2.3 + 1.7 * x[0] + 1.2 * x[1]; }, + [](const R2& x) -> double { return -1.7 + 2.1 * x[0] - 2.2 * x[1]; }, + [](const R2& x) -> double { return +1.4 - 0.6 * x[0] - 2.1 * x[1]; }, + [](const R2& x) -> double { return +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]; + for (size_t i = 0; i < vector_exact.size(); ++i) { + Vh[cell_id][i] = vector_exact[i](xj[cell_id]); } }); auto reconstructions = PolynomialReconstruction{descriptor}.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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - 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(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - } + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } -- GitLab From 647f38baadb47467d495cfe07665968287776d6d Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Fri, 28 Mar 2025 00:17:21 +0100 Subject: [PATCH 092/122] Improve tests in view of testing reconstructions of degree 2+ (WIP) --- ...test_PolynomialReconstruction_degree_1.cpp | 668 ++++-------------- 1 file changed, 137 insertions(+), 531 deletions(-) diff --git a/tests/test_PolynomialReconstruction_degree_1.cpp b/tests/test_PolynomialReconstruction_degree_1.cpp index 927a5f4c3..4e8b8ca57 100644 --- a/tests/test_PolynomialReconstruction_degree_1.cpp +++ b/tests/test_PolynomialReconstruction_degree_1.cpp @@ -12,7 +12,10 @@ #include <analysis/GaussQuadratureDescriptor.hpp> #include <analysis/QuadratureFormula.hpp> #include <analysis/QuadratureManager.hpp> +#include <geometry/CubeTransformation.hpp> #include <geometry/LineTransformation.hpp> +#include <geometry/PrismTransformation.hpp> +#include <geometry/PyramidTransformation.hpp> #include <geometry/SquareTransformation.hpp> #include <geometry/TetrahedronTransformation.hpp> #include <geometry/TriangleTransformation.hpp> @@ -104,16 +107,36 @@ max_reconstruction_error(const MeshType& mesh, } break; } - // case CellType::Pyramid: { - // SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], - // xr[cell_nodes[3]]}; - // auto qf = QuadratureManager::instance().getSquareFormula(GaussLobattoQuadratureDescriptor{dpk_f.degree() + 1}); - // for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - // auto x = T(qf.point(i_quadrarture)); - // max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); - // } - // break; - // } + case CellType::Pyramid: { + PyramidTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]]}; + auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + case CellType::Prism: { + PrismTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]], xr[cell_nodes[4]], xr[cell_nodes[5]]}; + auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + case CellType::Hexahedron: { + CubeTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]], xr[cell_nodes[5]], xr[cell_nodes[6]], xr[cell_nodes[7]]}; + auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } default: { throw UnexpectedError("unexpected cell type"); } @@ -194,16 +217,45 @@ max_reconstruction_error( } break; } - // case CellType::Pyramid: { - // SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], - // xr[cell_nodes[3]]}; - // auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); - // for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - // auto x = T(qf.point(i_quadrarture)); - // max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); - // } - // break; - // } + case CellType::Pyramid: { + PyramidTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]]}; + auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + case CellType::Prism: { + PrismTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]], xr[cell_nodes[4]], xr[cell_nodes[5]]}; + auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + case CellType::Hexahedron: { + CubeTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]], xr[cell_nodes[5]], xr[cell_nodes[6]], xr[cell_nodes[7]]}; + auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } default: { throw UnexpectedError("unexpected cell type"); } @@ -366,12 +418,8 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto& mesh = *p_mesh; std::array<std::function<R3(const R1&)>, 2> vector_exact = - {[](const R1& x) -> R3 { - return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; - }, - [](const R1& x) -> R3 { - return R3{+1.6 + 0.7 * x[0], -2.1 + 1.2 * x[0], +1.1 - 0.3 * x[0]}; - }}; + {[](const R1& x) -> R3 { return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; }, + [](const R1& x) -> R3 { return R3{+1.6 + 0.7 * x[0], -2.1 + 1.2 * x[0], +1.1 - 0.3 * x[0]}; }}; auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); @@ -619,62 +667,20 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto R_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(); + auto R_exact = [](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] = R_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_exact(xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, 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_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(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 double reconstructed_slope = - (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(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 double reconstructed_slope = - (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[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(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); } } } @@ -687,7 +693,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto R3_affine = [](const R3& x) -> R3 { + auto R3_exact = [](const R3& x) -> R3 { return R3{+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]}; @@ -697,53 +703,14 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") DiscreteFunctionP0<R3> uh{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_exact(xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); - { - 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_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - - max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1})); - } - REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - - max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1})); - } - REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - - dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - - max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9})); - } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -758,7 +725,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto R2x2_affine = [](const R3& x) -> R2x2 { + auto R2x2_exact = [](const R3& x) -> R2x2 { return R2x2{+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]}; @@ -768,88 +735,40 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") DiscreteFunctionP0<R2x2> Ah{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_exact(xj[cell_id]); }); descriptor.setRowWeighting(false); auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); - - { - 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_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - - max_x_slope_error = - std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1, // - -2.3, +3.1})); - } - REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - - max_y_slope_error = - std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2, // - 1.3, +0.8})); - } - REQUIRE(parallel::allReduceMax(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 R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - - dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - - max_z_slope_error = - std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4, // - +1.4, -1.8})); - } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); - } + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } 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]}; - }; + std::array<std::function<double(const R3&)>, 4> vector_exact = + {[](const R3& x) -> double { return +2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2]; }, + [](const R3& x) -> double { return -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2]; }, + [](const R3& x) -> double { return +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2]; }, + [](const R3& x) -> double { return -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]; + for (size_t i = 0; i < vector_exact.size(); ++i) { + Vh[cell_id][i] = vector_exact[i](xj[cell_id]); } }); @@ -858,58 +777,8 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") 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(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - 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(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - 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(parallel::allReduceMax(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(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -1070,37 +939,20 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto& mesh = *p_mesh; - auto R1_affine = [](const R1& x) { return R1{1.7 * (x[0] + 1)}; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + auto R1_exact = [](const R1& x) { return R1{1.7 * (x[0] + 1)}; }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); DiscreteFunctionP0<R1> fh{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R1_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R1_exact(xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R1>>(); - { - 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_fh[cell_id](xj[cell_id]) - R1_affine(xj[cell_id]))[0])); - } - REQUIRE(parallel::allReduceMax(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_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1}))[0] / 0.2; - - max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7)); - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R1_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } SECTION("R1 vector data") @@ -1111,9 +963,9 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto vector_affine0 = [](const R1& x) -> R1 { return R1{+1.7 * (x[0] + 1)}; }; - - auto vector_affine1 = [](const R1& x) -> R1 { return R1{-0.3 * (x[0] + 1)}; }; + std::array<std::function<R1(const R1&)>, 2> vector_exact // + = {[](const R1& x) -> R1 { return R1{+1.7 * (x[0] + 1)}; }, + [](const R1& x) -> R1 { return R1{-0.3 * (x[0] + 1)}; }}; auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); @@ -1121,54 +973,16 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - Vh[cell_id][0] = vector_affine0(xj[cell_id]); - Vh[cell_id][1] = vector_affine1(xj[cell_id]); + Vh[cell_id][0] = vector_exact[0](xj[cell_id]); + Vh[cell_id][1] = vector_exact[1](xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R1>>(); - { - 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_Vh(cell_id, 0)(xj[cell_id]) - vector_affine0(xj[cell_id]))); - max_mean_error = - std::max(max_mean_error, l2Norm(dpk_Vh(cell_id, 1)(xj[cell_id]) - vector_affine1(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_slope_error = 0; - { - const TinyVector<1> slope0{+1.7}; - - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - for (size_t i = 0; i < R1::Dimension; ++i) { - const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R1{0.1} + xj[cell_id])[i] - - dpk_Vh(cell_id, 0)(xj[cell_id] - R1{0.1})[i]); - - max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope0[i])); - } - } - } - - { - const TinyVector<1> slope1{-0.3}; - - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - for (size_t i = 0; i < R1::Dimension; ++i) { - const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R1{0.1} + xj[cell_id])[i] - - dpk_Vh(cell_id, 1)(xj[cell_id] - R1{0.1})[i]); - - max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - slope1[i])); - } - } - } - REQUIRE(parallel::allReduceMax(max_slope_error) == Catch::Approx(0).margin(1E-14)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -1211,128 +1025,47 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - auto R2_affine = [](const R2& x) -> R2 { return R2{2.3 * (x[0] - 2), -1.3 * (x[1] - 1)}; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + auto R2_exact = [](const R2& x) -> R2 { return R2{2.3 * (x[0] - 2), -1.3 * (x[1] - 1)}; }; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); DiscreteFunctionP0<R2> fh{p_mesh}; parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R2_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R2_exact(xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2>>(); - { - 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_fh[cell_id](xj[cell_id]) - R2_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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 R2 reconstructed_slope = - 1. / 0.2 * (dpk_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0.1, 0})); - - max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R2{2.3, 0})); - } - REQUIRE(parallel::allReduceMax(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 R2 reconstructed_slope = - 1 / 0.2 * (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0, 0.1})); - - max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - (R2{0, -1.3}))); - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } SECTION("vector of R2") { - using R4 = TinyVector<4>; - auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - auto vector_affine = [](const R2& x) -> R4 { - return R4{+1.7 * (x[0] - 2), // - -0.6 * (x[1] - 1), // - -2.3 * (x[0] - 2), // - +1.1 * (x[1] - 1)}; - }; + std::array<std::function<R2(const R2&)>, 2> vector_exact // + = {[](const R2& x) -> R2 { return R2{+1.7 * (x[0] - 2), -0.6 * (x[1] - 1)}; }, + [](const R2& x) -> R2 { return R2{-2.3 * (x[0] - 2), +1.1 * (x[1] - 1)}; }}; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); DiscreteFunctionP0Vector<R2> Vh{p_mesh, 2}; parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - auto vector = vector_affine(xj[cell_id]); - Vh[cell_id][0][0] = vector[0]; - Vh[cell_id][0][1] = vector[1]; - Vh[cell_id][1][0] = vector[2]; - Vh[cell_id][1][1] = vector[3]; + Vh[cell_id][0] = vector_exact[0](xj[cell_id]); + Vh[cell_id][1] = vector_exact[1](xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const R2>>(); - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto vector = vector_affine(xj[cell_id]); - - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[0] - vector[0])); - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[1] - vector[1])); - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[0] - vector[2])); - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[1] - vector[3])); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_x_slope_error = 0; - const R4 slope{+1.7, 0, -2.3, 0}; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R2 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R2{0.1, 0} + xj[cell_id]) - - dpk_Vh(cell_id, 0)(xj[cell_id] - R2{0.1, 0})); - - const R2 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R2{0.1, 0} + xj[cell_id]) - - dpk_Vh(cell_id, 1)(xj[cell_id] - R2{0.1, 0})); - - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[0] - slope[0])); - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[1] - slope[1])); - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[0] - slope[2])); - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[1] - slope[3])); - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - double max_y_slope_error = 0; - const R4 slope{0, -0.6, 0, 1.1}; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R2 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R2{0, 0.1} + xj[cell_id]) - - dpk_Vh(cell_id, 0)(xj[cell_id] - R2{0, 0.1})); - - const R2 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R2{0, 0.1} + xj[cell_id]) - - dpk_Vh(cell_id, 1)(xj[cell_id] - R2{0, 0.1})); - - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[0] - slope[0])); - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[1] - slope[1])); - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[0] - slope[2])); - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[1] - slope[3])); - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -1379,174 +1112,47 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto R3_affine = [](const R3& x) -> R3 { - return R3{2.3 * (x[0] - 2), -1.3 * (x[1] - 1), 1.4 * (x[2] - 1)}; - }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + auto R3_exact = [](const R3& x) -> R3 { return R3{2.3 * (x[0] - 2), -1.3 * (x[1] - 1), 1.4 * (x[2] - 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] = R3_affine(xj[cell_id]); }); + mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R3_exact(xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); - { - 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_fh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id]))); - } - REQUIRE(parallel::allReduceMax(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_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0.1, 0, 0})); - - max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{2.3, 0, 0})); - } - REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = - 1 / 0.2 * - (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0.1, 0})); - - max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - (R3{0, -1.3, 0}))); - } - REQUIRE(parallel::allReduceMax(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 R3 reconstructed_slope = - 1 / 0.2 * - (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0, 0.1})); - - max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - (R3{0, 0, 1.4}))); - } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } - SECTION("vector of R2") + SECTION("vector of R3") { - using R6 = TinyVector<6>; - auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto vector_affine = [](const R3& x) -> R6 { - return R6{+1.7 * (x[0] - 2), // - -0.6 * (x[1] - 1), // - +1.2 * (x[2] - 1), // - -2.3 * (x[0] - 2), // - +1.1 * (x[1] - 1), // - -0.3 * (x[2] - 1)}; - }; + std::array<std::function<R3(const R3&)>, 2> vector_exact // + = {[](const R3& x) -> R3 { return R3{+1.7 * (x[0] - 2), -0.6 * (x[1] - 1), +1.2 * (x[2] - 1)}; }, + [](const R3& x) -> R3 { return R3{-2.3 * (x[0] - 2), +1.1 * (x[1] - 1), -0.3 * (x[2] - 1)}; }}; + auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); DiscreteFunctionP0Vector<R3> Vh{p_mesh, 2}; parallel_for( mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - auto vector = vector_affine(xj[cell_id]); - Vh[cell_id][0][0] = vector[0]; - Vh[cell_id][0][1] = vector[1]; - Vh[cell_id][0][2] = vector[2]; - Vh[cell_id][1][0] = vector[3]; - Vh[cell_id][1][1] = vector[4]; - Vh[cell_id][1][2] = vector[5]; + Vh[cell_id][0] = vector_exact[0](xj[cell_id]); + Vh[cell_id][1] = vector_exact[1](xj[cell_id]); }); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const R3>>(); - { - double max_mean_error = 0; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto vector = vector_affine(xj[cell_id]); - - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[0] - vector[0])); - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[1] - vector[1])); - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[2] - vector[2])); - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[0] - vector[3])); - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[1] - vector[4])); - max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[2] - vector[5])); - } - REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14)); - } - - { - double max_x_slope_error = 0; - const R6 slope{+1.7, 0, 0, -2.3, 0, 0}; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R3{0.1, 0, 0} + xj[cell_id]) - - dpk_Vh(cell_id, 0)(xj[cell_id] - R3{0.1, 0, 0})); - - const R3 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R3{0.1, 0, 0} + xj[cell_id]) - - dpk_Vh(cell_id, 1)(xj[cell_id] - R3{0.1, 0, 0})); - - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[0] - slope[0])); - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[1] - slope[1])); - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[2] - slope[2])); - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[0] - slope[3])); - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[1] - slope[4])); - max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[2] - slope[5])); - } - REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - double max_y_slope_error = 0; - const R6 slope{0, -0.6, 0, 0, 1.1, 0}; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R3{0, 0.1, 0} + xj[cell_id]) - - dpk_Vh(cell_id, 0)(xj[cell_id] - R3{0, 0.1, 0})); - - const R3 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R3{0, 0.1, 0} + xj[cell_id]) - - dpk_Vh(cell_id, 1)(xj[cell_id] - R3{0, 0.1, 0})); - - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[0] - slope[0])); - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[1] - slope[1])); - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[2] - slope[2])); - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[0] - slope[3])); - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[1] - slope[4])); - max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[2] - slope[5])); - } - REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13)); - } - - { - double max_z_slope_error = 0; - const R6 slope{0, 0, 1.2, 0, 0, -0.3}; - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - const R3 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R3{0, 0, 0.1} + xj[cell_id]) - - dpk_Vh(cell_id, 0)(xj[cell_id] - R3{0, 0, 0.1})); - - const R3 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R3{0, 0, 0.1} + xj[cell_id]) - - dpk_Vh(cell_id, 1)(xj[cell_id] - R3{0, 0, 0.1})); - - max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope0[0] - slope[0])); - max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope0[1] - slope[1])); - max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope0[2] - slope[2])); - max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope1[0] - slope[3])); - max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope1[1] - slope[4])); - max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope1[2] - slope[5])); - } - REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13)); - } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } -- GitLab From cc55859187a3dbc844fb7df3414b5b7f2f7717b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 28 Mar 2025 16:01:47 +0100 Subject: [PATCH 093/122] Add a jacobianDeterminant version with argument for genericity --- src/geometry/LineTransformation.hpp | 8 ++++++++ src/geometry/TetrahedronTransformation.hpp | 8 ++++++++ src/geometry/TriangleTransformation.hpp | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/src/geometry/LineTransformation.hpp b/src/geometry/LineTransformation.hpp index c9f7def71..4576c0b64 100644 --- a/src/geometry/LineTransformation.hpp +++ b/src/geometry/LineTransformation.hpp @@ -24,12 +24,20 @@ class LineTransformation<1> return m_jacobian * x + m_shift; } + PUGS_INLINE double jacobianDeterminant() const { return m_jacobian; } + PUGS_INLINE + double + jacobianDeterminant(const TinyVector<1>&) const + { + return m_jacobian; + } + PUGS_INLINE LineTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b) { diff --git a/src/geometry/TetrahedronTransformation.hpp b/src/geometry/TetrahedronTransformation.hpp index 642442669..a81a344bf 100644 --- a/src/geometry/TetrahedronTransformation.hpp +++ b/src/geometry/TetrahedronTransformation.hpp @@ -24,12 +24,20 @@ class TetrahedronTransformation return m_jacobian * x + m_shift; } + PUGS_INLINE double jacobianDeterminant() const { return m_jacobian_determinant; } + PUGS_INLINE + double + jacobianDeterminant(const TinyVector<3>&) const + { + return m_jacobian_determinant; + } + PUGS_INLINE TetrahedronTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b, diff --git a/src/geometry/TriangleTransformation.hpp b/src/geometry/TriangleTransformation.hpp index 9210e0bda..df8444d89 100644 --- a/src/geometry/TriangleTransformation.hpp +++ b/src/geometry/TriangleTransformation.hpp @@ -26,12 +26,20 @@ class TriangleTransformation<2> return m_jacobian * x + m_shift; } + PUGS_INLINE double jacobianDeterminant() const { return m_jacobian_determinant; } + PUGS_INLINE + double + jacobianDeterminant(const TinyVector<2>&) const + { + return m_jacobian_determinant; + } + PUGS_INLINE TriangleTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b, const TinyVector<Dimension>& c) { -- GitLab From fb4518e03ee683727a078e293b22fc381d624faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 28 Mar 2025 16:02:30 +0100 Subject: [PATCH 094/122] Finalize tests improvements in view of degree 2+ --- tests/DiscreteFunctionDPkForTests.hpp | 365 +++++++++++++ ...test_PolynomialReconstruction_degree_1.cpp | 497 +++--------------- 2 files changed, 433 insertions(+), 429 deletions(-) create mode 100644 tests/DiscreteFunctionDPkForTests.hpp diff --git a/tests/DiscreteFunctionDPkForTests.hpp b/tests/DiscreteFunctionDPkForTests.hpp new file mode 100644 index 000000000..5b51861d8 --- /dev/null +++ b/tests/DiscreteFunctionDPkForTests.hpp @@ -0,0 +1,365 @@ +#ifndef DISCRETE_FUNCTION_DPK_FOR_TESTS_HPP +#define DISCRETE_FUNCTION_DPK_FOR_TESTS_HPP + +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/CubeTransformation.hpp> +#include <geometry/LineTransformation.hpp> +#include <geometry/PrismTransformation.hpp> +#include <geometry/PyramidTransformation.hpp> +#include <geometry/SquareTransformation.hpp> +#include <geometry/TetrahedronTransformation.hpp> +#include <geometry/TriangleTransformation.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshDataManager.hpp> +#include <type_traits> + +namespace test_only +{ + +template <MeshConcept MeshType, typename DataType> +DiscreteFunctionP0<std::remove_const_t<DataType>> +exact_projection(const MeshType& mesh, + size_t degree, + std::function<DataType(const TinyVector<MeshType::Dimension>&)> exact_function) +{ + DiscreteFunctionP0<std::remove_const_t<DataType>> P0_function{mesh.meshVariant()}; + + auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); + + auto xr = mesh.xr(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + auto cell_type = mesh.connectivity().cellType(); + + auto sum = [&exact_function, &Vj](const CellId cell_id, const auto& T, + const auto& qf) -> std::remove_const_t<DataType> { + std::remove_const_t<DataType> integral = + (qf.weight(0) * T.jacobianDeterminant(qf.point(0))) * exact_function(T(qf.point(0))); + for (size_t i_quadrarture = 1; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + integral += (qf.weight(i_quadrarture) * T.jacobianDeterminant(qf.point(i_quadrarture))) * + exact_function(T(qf.point(i_quadrarture))); + } + return 1. / Vj[cell_id] * integral; + }; + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto cell_nodes = cell_to_node_matrix[cell_id]; + if constexpr (MeshType::Dimension == 1) { + LineTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; + auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{degree + 1}); + P0_function[cell_id] = sum(cell_id, T, qf); + } else if constexpr (MeshType::Dimension == 2) { + switch (cell_type[cell_id]) { + case CellType::Triangle: { + TriangleTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]]}; + auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{degree + 2}); + P0_function[cell_id] = sum(cell_id, T, qf); + break; + } + case CellType::Quadrangle: { + SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor{degree + 2}); + P0_function[cell_id] = sum(cell_id, T, qf); + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } else if constexpr (MeshType::Dimension == 3) { + switch (cell_type[cell_id]) { + case CellType::Tetrahedron: { + TetrahedronTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{degree + 3}); + P0_function[cell_id] = sum(cell_id, T, qf); + break; + } + case CellType::Pyramid: { + PyramidTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]]}; + auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{degree + 3}); + P0_function[cell_id] = sum(cell_id, T, qf); + break; + } + case CellType::Prism: { + PrismTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]], xr[cell_nodes[4]], xr[cell_nodes[5]]}; + auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{degree + 3}); + P0_function[cell_id] = sum(cell_id, T, qf); + break; + } + case CellType::Hexahedron: { + CubeTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]], xr[cell_nodes[5]], xr[cell_nodes[6]], xr[cell_nodes[7]]}; + auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor{degree + 3}); + P0_function[cell_id] = sum(cell_id, T, qf); + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } else { + throw UnexpectedError("invalid mesh dimension"); + } + } + + return P0_function; +} + +template <MeshConcept MeshType, typename DataType, size_t NbComponents> +DiscreteFunctionP0Vector<std::remove_const_t<DataType>> +exact_projection( + const MeshType& mesh, + size_t degree, + const std::array<std::function<DataType(const TinyVector<MeshType::Dimension>&)>, NbComponents>& vector_exact) +{ + DiscreteFunctionP0Vector<std::remove_const_t<DataType>> P0_function_vector{mesh.meshVariant(), vector_exact.size()}; + + for (size_t i_component = 0; i_component < vector_exact.size(); ++i_component) { + auto exact_function = vector_exact[i_component]; + + DiscreteFunctionP0 P0_function = exact_projection(mesh, degree, vector_exact[i_component]); + + parallel_for( + mesh.numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { P0_function_vector[cell_id][i_component] = P0_function[cell_id]; }); + } + + return P0_function_vector; +} + +template <typename DataType> +PUGS_INLINE double +get_max_error(const DataType& x, const DataType& y) +{ + if constexpr (is_tiny_matrix_v<DataType>) { + return frobeniusNorm(x - y); + } else if constexpr (is_tiny_vector_v<DataType>) { + return l2Norm(x - y); + } else { + static_assert(std::is_arithmetic_v<DataType>, "expecting arithmetic type"); + return std::abs(x - y); + } +} + +template <MeshConcept MeshType, typename DataType> +double +max_reconstruction_error(const MeshType& mesh, + DiscreteFunctionDPk<MeshType::Dimension, const DataType> dpk_f, + std::function<DataType(const TinyVector<MeshType::Dimension>&)> exact) +{ + auto xr = mesh.xr(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + auto cell_type = mesh.connectivity().cellType(); + + double max_error = 0; + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto cell_nodes = cell_to_node_matrix[cell_id]; + if constexpr (MeshType::Dimension == 1) { + Assert(cell_type[cell_id] == CellType::Line); + LineTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; + auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + } else if constexpr (MeshType::Dimension == 2) { + switch (cell_type[cell_id]) { + case CellType::Triangle: { + TriangleTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]]}; + auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + case CellType::Quadrangle: { + SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } else if constexpr (MeshType::Dimension == 3) { + switch (cell_type[cell_id]) { + case CellType::Tetrahedron: { + TetrahedronTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + case CellType::Pyramid: { + PyramidTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]]}; + auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + case CellType::Prism: { + PrismTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]], xr[cell_nodes[4]], xr[cell_nodes[5]]}; + auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + case CellType::Hexahedron: { + CubeTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]], xr[cell_nodes[5]], xr[cell_nodes[6]], xr[cell_nodes[7]]}; + auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); + } + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } + } + return max_error; +} + +template <MeshConcept MeshType, typename DataType, size_t NbComponents> +double +max_reconstruction_error( + const MeshType& mesh, + DiscreteFunctionDPkVector<MeshType::Dimension, const DataType> dpk_v, + const std::array<std::function<DataType(const TinyVector<MeshType::Dimension>&)>, NbComponents>& vector_exact) +{ + auto xr = mesh.xr(); + auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); + double max_error = 0; + auto cell_type = mesh.connectivity().cellType(); + + REQUIRE(NbComponents == dpk_v.numberOfComponents()); + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + auto cell_nodes = cell_to_node_matrix[cell_id]; + if constexpr (MeshType::Dimension == 1) { + Assert(cell_type[cell_id] == CellType::Line); + LineTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; + auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + } else if constexpr (MeshType::Dimension == 2) { + switch (cell_type[cell_id]) { + case CellType::Triangle: { + TriangleTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]]}; + auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + case CellType::Quadrangle: { + SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } else if constexpr (MeshType::Dimension == 3) { + switch (cell_type[cell_id]) { + case CellType::Tetrahedron: { + TetrahedronTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]]}; + auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + case CellType::Pyramid: { + PyramidTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]]}; + auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + case CellType::Prism: { + PrismTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], + xr[cell_nodes[3]], xr[cell_nodes[4]], xr[cell_nodes[5]]}; + auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + case CellType::Hexahedron: { + CubeTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], + xr[cell_nodes[4]], xr[cell_nodes[5]], xr[cell_nodes[6]], xr[cell_nodes[7]]}; + auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); + for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { + auto x = T(qf.point(i_quadrarture)); + for (size_t i_component = 0; i_component < NbComponents; ++i_component) { + max_error = + std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); + } + } + break; + } + default: { + throw UnexpectedError("unexpected cell type"); + } + } + } + } + return max_error; +} + +} // namespace test_only + +#endif // DISCRETE_FUNCTION_DPK_FOR_TESTS_HPP diff --git a/tests/test_PolynomialReconstruction_degree_1.cpp b/tests/test_PolynomialReconstruction_degree_1.cpp index 4e8b8ca57..fb2c7b2ec 100644 --- a/tests/test_PolynomialReconstruction_degree_1.cpp +++ b/tests/test_PolynomialReconstruction_degree_1.cpp @@ -2,279 +2,30 @@ #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 <analysis/GaussQuadratureDescriptor.hpp> -#include <analysis/QuadratureFormula.hpp> -#include <analysis/QuadratureManager.hpp> -#include <geometry/CubeTransformation.hpp> -#include <geometry/LineTransformation.hpp> -#include <geometry/PrismTransformation.hpp> -#include <geometry/PyramidTransformation.hpp> -#include <geometry/SquareTransformation.hpp> -#include <geometry/TetrahedronTransformation.hpp> -#include <geometry/TriangleTransformation.hpp> + #include <mesh/Mesh.hpp> -#include <mesh/MeshDataManager.hpp> #include <mesh/NamedBoundaryDescriptor.hpp> #include <scheme/DiscreteFunctionDPkVariant.hpp> #include <scheme/DiscreteFunctionP0.hpp> #include <scheme/DiscreteFunctionVariant.hpp> #include <scheme/PolynomialReconstruction.hpp> +#include <DiscreteFunctionDPkForTests.hpp> #include <MeshDataBaseForTests.hpp> -#include <type_traits> - // clazy:excludeall=non-pod-global-static -namespace test_only -{ - -template <typename DataType> -PUGS_INLINE double -get_max_error(const DataType& x, const DataType& y) -{ - if constexpr (is_tiny_matrix_v<DataType>) { - return frobeniusNorm(x - y); - } else if constexpr (is_tiny_vector_v<DataType>) { - return l2Norm(x - y); - } else { - static_assert(std::is_arithmetic_v<DataType>, "expecting arithmetic type"); - return std::abs(x - y); - } -} - -template <MeshConcept MeshType, typename DataType> -double -max_reconstruction_error(const MeshType& mesh, - DiscreteFunctionDPk<MeshType::Dimension, const DataType> dpk_f, - std::function<DataType(const TinyVector<MeshType::Dimension>&)> exact) -{ - auto xr = mesh.xr(); - auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); - double max_error = 0; - auto cell_type = mesh.connectivity().cellType(); - - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto cell_nodes = cell_to_node_matrix[cell_id]; - if constexpr (MeshType::Dimension == 1) { - Assert(cell_type[cell_id] == CellType::Line); - LineTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; - auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); - } - } else if constexpr (MeshType::Dimension == 2) { - switch (cell_type[cell_id]) { - case CellType::Triangle: { - TriangleTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]]}; - auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); - } - break; - } - case CellType::Quadrangle: { - SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], - xr[cell_nodes[3]]}; - auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); - } - break; - } - default: { - throw UnexpectedError("unexpected cell type"); - } - } - } else if constexpr (MeshType::Dimension == 3) { - switch (cell_type[cell_id]) { - case CellType::Tetrahedron: { - TetrahedronTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]]}; - auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); - } - break; - } - case CellType::Pyramid: { - PyramidTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], - xr[cell_nodes[4]]}; - auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); - } - break; - } - case CellType::Prism: { - PrismTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], - xr[cell_nodes[3]], xr[cell_nodes[4]], xr[cell_nodes[5]]}; - auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); - } - break; - } - case CellType::Hexahedron: { - CubeTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], - xr[cell_nodes[4]], xr[cell_nodes[5]], xr[cell_nodes[6]], xr[cell_nodes[7]]}; - auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor{dpk_f.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - max_error = std::max(max_error, get_max_error(dpk_f[cell_id](x), exact(x))); - } - break; - } - default: { - throw UnexpectedError("unexpected cell type"); - } - } - } - } - return max_error; -} - -template <MeshConcept MeshType, typename DataType, size_t NbComponents> -double -max_reconstruction_error( - const MeshType& mesh, - DiscreteFunctionDPkVector<MeshType::Dimension, const DataType> dpk_v, - const std::array<std::function<DataType(const TinyVector<MeshType::Dimension>&)>, NbComponents>& vector_exact) -{ - auto xr = mesh.xr(); - auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); - double max_error = 0; - auto cell_type = mesh.connectivity().cellType(); - - REQUIRE(NbComponents == dpk_v.numberOfComponents()); - - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - auto cell_nodes = cell_to_node_matrix[cell_id]; - if constexpr (MeshType::Dimension == 1) { - Assert(cell_type[cell_id] == CellType::Line); - LineTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]]}; - auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - for (size_t i_component = 0; i_component < NbComponents; ++i_component) { - max_error = std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); - } - } - } else if constexpr (MeshType::Dimension == 2) { - switch (cell_type[cell_id]) { - case CellType::Triangle: { - TriangleTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]]}; - auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - for (size_t i_component = 0; i_component < NbComponents; ++i_component) { - max_error = - std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); - } - } - break; - } - case CellType::Quadrangle: { - SquareTransformation<MeshType::Dimension> T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], - xr[cell_nodes[3]]}; - auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - for (size_t i_component = 0; i_component < NbComponents; ++i_component) { - max_error = - std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); - } - } - break; - } - default: { - throw UnexpectedError("unexpected cell type"); - } - } - } else if constexpr (MeshType::Dimension == 3) { - switch (cell_type[cell_id]) { - case CellType::Tetrahedron: { - TetrahedronTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]]}; - auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - for (size_t i_component = 0; i_component < NbComponents; ++i_component) { - max_error = - std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); - } - } - break; - } - case CellType::Pyramid: { - PyramidTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], - xr[cell_nodes[4]]}; - auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - for (size_t i_component = 0; i_component < NbComponents; ++i_component) { - max_error = - std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); - } - } - break; - } - case CellType::Prism: { - PrismTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], - xr[cell_nodes[3]], xr[cell_nodes[4]], xr[cell_nodes[5]]}; - auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - for (size_t i_component = 0; i_component < NbComponents; ++i_component) { - max_error = - std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); - } - } - break; - } - case CellType::Hexahedron: { - CubeTransformation T{xr[cell_nodes[0]], xr[cell_nodes[1]], xr[cell_nodes[2]], xr[cell_nodes[3]], - xr[cell_nodes[4]], xr[cell_nodes[5]], xr[cell_nodes[6]], xr[cell_nodes[7]]}; - auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor{dpk_v.degree() + 1}); - for (size_t i_quadrarture = 0; i_quadrarture < qf.numberOfPoints(); ++i_quadrarture) { - auto x = T(qf.point(i_quadrarture)); - for (size_t i_component = 0; i_component < NbComponents; ++i_component) { - max_error = - std::max(max_error, get_max_error(dpk_v(cell_id, i_component)(x), vector_exact[i_component](x))); - } - } - break; - } - default: { - throw UnexpectedError("unexpected cell type"); - } - } - } - } - return max_error; -} - -} // namespace test_only - TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") { + constexpr size_t degree = 1; + SECTION("without symmetries") { std::vector<PolynomialReconstructionDescriptor> descriptor_list = - {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1}, - PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1}, - PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1}}; + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, degree}, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree}}; for (auto descriptor : descriptor_list) { SECTION(name(descriptor.integrationMethodType())) @@ -292,12 +43,8 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto& mesh = *p_mesh; auto R_exact = [](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] = R_exact(xj[cell_id]); }); + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); @@ -324,12 +71,8 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") +1.4 - 0.6 * x[0], // -0.2 + 3.1 * x[0]}; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<R3> uh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_exact(xj[cell_id]); }); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); @@ -358,12 +101,8 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0], }; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - DiscreteFunctionP0<R3x3> Ah{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_exact(xj[cell_id]); }); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); @@ -387,16 +126,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") [](const R1& x) -> double { return -1.7 + 2.1 * x[0]; }, [](const R1& x) -> double { return +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) { - for (size_t i = 0; i < vector_exact.size(); ++i) { - Vh[cell_id][i] = vector_exact[i](xj[cell_id]); - } - }); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); @@ -418,18 +148,14 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto& mesh = *p_mesh; std::array<std::function<R3(const R1&)>, 2> vector_exact = - {[](const R1& x) -> R3 { return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; }, - [](const R1& x) -> R3 { return R3{+1.6 + 0.7 * x[0], -2.1 + 1.2 * x[0], +1.1 - 0.3 * x[0]}; }}; + {[](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0]}; + }, + [](const R1& x) -> R3 { + return R3{+1.6 + 0.7 * x[0], -2.1 + 1.2 * x[0], +1.1 - 0.3 * x[0]}; + }}; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0Vector<R3> Vh{p_mesh, 2}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - Vh[cell_id][0] = vector_exact[0](xj[cell_id]); - Vh[cell_id][1] = vector_exact[1](xj[cell_id]); - }); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); @@ -473,27 +199,10 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") [](const R1& x) -> double { return -1.7 + 2.1 * x[0]; }, [](const R1& x) -> double { return +1.4 - 0.6 * 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] = R_exact(xj[cell_id]); }); - - DiscreteFunctionP0<R3> uh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_exact(xj[cell_id]); }); - - DiscreteFunctionP0<R3x3> Ah{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_exact(xj[cell_id]); }); - - DiscreteFunctionP0Vector<double> Vh{p_mesh, 3}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - for (size_t i = 0; i < vector_exact.size(); ++i) { - Vh[cell_id][i] = vector_exact[i](xj[cell_id]); - } - }); + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); auto reconstructions = PolynomialReconstruction{descriptor}.build(std::make_shared<DiscreteFunctionVariant>(fh), uh, @@ -541,12 +250,8 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto& mesh = *p_mesh; auto R_exact = [](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] = R_exact(xj[cell_id]); }); + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); @@ -573,12 +278,8 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") +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> uh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_exact(xj[cell_id]); }); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); @@ -604,12 +305,8 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") return R2x2{+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(); - - DiscreteFunctionP0<R2x2> Ah{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_exact(xj[cell_id]); }); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); @@ -634,16 +331,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") [](const R2& x) -> double { return +1.4 - 0.6 * x[0] - 2.1 * x[1]; }, [](const R2& x) -> double { return +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) { - for (size_t i = 0; i < vector_exact.size(); ++i) { - Vh[cell_id][i] = vector_exact[i](xj[cell_id]); - } - }); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); @@ -668,19 +356,15 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto& mesh = *p_mesh; auto R_exact = [](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] = R_exact(xj[cell_id]); }); + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); - REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -698,12 +382,8 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") +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]}; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0<R3> uh{p_mesh}; - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_exact(xj[cell_id]); }); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); @@ -730,12 +410,8 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") // +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(); - DiscreteFunctionP0<R2x2> Ah{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_exact(xj[cell_id]); }); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); descriptor.setRowWeighting(false); auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); @@ -761,16 +437,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") [](const R3& x) -> double { return +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2]; }, [](const R3& x) -> double { return -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) { - for (size_t i = 0; i < vector_exact.size(); ++i) { - Vh[cell_id][i] = vector_exact[i](xj[cell_id]); - } - }); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); descriptor.setPreconditioning(false); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); @@ -807,7 +474,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") { auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 1, + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, degree, std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ std::make_shared<NamedBoundaryDescriptor>("XMIN")}}; @@ -844,7 +511,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") { auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); - PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 1, + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, degree, std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ std::make_shared<NamedBoundaryDescriptor>("XMIN")}}; @@ -881,7 +548,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") { auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); - PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, 1, + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, degree, std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ std::make_shared<NamedBoundaryDescriptor>("XMIN")}}; @@ -918,13 +585,13 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") SECTION("1D") { std::vector<PolynomialReconstructionDescriptor> descriptor_list = - {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1, + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, degree, std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, - PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, - PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; @@ -940,12 +607,8 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto& mesh = *p_mesh; auto R1_exact = [](const R1& x) { return R1{1.7 * (x[0] + 1)}; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - DiscreteFunctionP0<R1> fh{p_mesh}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R1_exact(xj[cell_id]); }); + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R1_exact)); auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); @@ -967,15 +630,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") = {[](const R1& x) -> R1 { return R1{+1.7 * (x[0] + 1)}; }, [](const R1& x) -> R1 { return R1{-0.3 * (x[0] + 1)}; }}; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0Vector<R1> Vh{p_mesh, 2}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - Vh[cell_id][0] = vector_exact[0](xj[cell_id]); - Vh[cell_id][1] = vector_exact[1](xj[cell_id]); - }); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); @@ -993,21 +648,21 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") SECTION("2D") { std::vector<PolynomialReconstructionDescriptor> descriptor_list = - {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1, + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, degree, std::vector<std::shared_ptr< const IBoundaryDescriptor>>{std:: make_shared<NamedBoundaryDescriptor>( "XMAX"), std::make_shared<NamedBoundaryDescriptor>( "YMAX")}}, - PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, std::vector<std::shared_ptr< const IBoundaryDescriptor>>{std:: make_shared<NamedBoundaryDescriptor>( "XMAX"), std::make_shared<NamedBoundaryDescriptor>( "YMAX")}}, - PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, std::vector<std::shared_ptr< const IBoundaryDescriptor>>{std:: make_shared<NamedBoundaryDescriptor>( @@ -1026,18 +681,14 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto& mesh = *p_mesh; auto R2_exact = [](const R2& x) -> R2 { return R2{2.3 * (x[0] - 2), -1.3 * (x[1] - 1)}; }; - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - DiscreteFunctionP0<R2> fh{p_mesh}; + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R2_exact)); - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R2_exact(xj[cell_id]); }); + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2>>(); + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2>>(); - double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R2_exact)); + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R2_exact)); REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } @@ -1047,18 +698,14 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto& mesh = *p_mesh; std::array<std::function<R2(const R2&)>, 2> vector_exact // - = {[](const R2& x) -> R2 { return R2{+1.7 * (x[0] - 2), -0.6 * (x[1] - 1)}; }, - [](const R2& x) -> R2 { return R2{-2.3 * (x[0] - 2), +1.1 * (x[1] - 1)}; }}; - - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); + = {[](const R2& x) -> R2 { + return R2{+1.7 * (x[0] - 2), -0.6 * (x[1] - 1)}; + }, + [](const R2& x) -> R2 { + return R2{-2.3 * (x[0] - 2), +1.1 * (x[1] - 1)}; + }}; - DiscreteFunctionP0Vector<R2> Vh{p_mesh, 2}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - Vh[cell_id][0] = vector_exact[0](xj[cell_id]); - Vh[cell_id][1] = vector_exact[1](xj[cell_id]); - }); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); @@ -1074,7 +721,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") SECTION("3D") { std::vector<PolynomialReconstructionDescriptor> descriptor_list = - {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1, + {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, degree, std::vector<std::shared_ptr< const IBoundaryDescriptor>>{std:: make_shared<NamedBoundaryDescriptor>( @@ -1083,7 +730,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") "YMAX"), std::make_shared<NamedBoundaryDescriptor>( "ZMAX")}}, - PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1, + PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, std::vector<std::shared_ptr< const IBoundaryDescriptor>>{std:: make_shared<NamedBoundaryDescriptor>( @@ -1092,7 +739,7 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") "YMAX"), std::make_shared<NamedBoundaryDescriptor>( "ZMAX")}}, - PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, std::vector<std::shared_ptr< const IBoundaryDescriptor>>{std:: make_shared<NamedBoundaryDescriptor>( @@ -1113,18 +760,14 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto& mesh = *p_mesh; auto R3_exact = [](const R3& x) -> R3 { return R3{2.3 * (x[0] - 2), -1.3 * (x[1] - 1), 1.4 * (x[2] - 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] = R3_exact(xj[cell_id]); }); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); - auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); - double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R3_exact)); + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } @@ -1134,18 +777,14 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]") auto& mesh = *p_mesh; std::array<std::function<R3(const R3&)>, 2> vector_exact // - = {[](const R3& x) -> R3 { return R3{+1.7 * (x[0] - 2), -0.6 * (x[1] - 1), +1.2 * (x[2] - 1)}; }, - [](const R3& x) -> R3 { return R3{-2.3 * (x[0] - 2), +1.1 * (x[1] - 1), -0.3 * (x[2] - 1)}; }}; - - auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); - - DiscreteFunctionP0Vector<R3> Vh{p_mesh, 2}; - - parallel_for( - mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - Vh[cell_id][0] = vector_exact[0](xj[cell_id]); - Vh[cell_id][1] = vector_exact[1](xj[cell_id]); - }); + = {[](const R3& x) -> R3 { + return R3{+1.7 * (x[0] - 2), -0.6 * (x[1] - 1), +1.2 * (x[2] - 1)}; + }, + [](const R3& x) -> R3 { + return R3{-2.3 * (x[0] - 2), +1.1 * (x[1] - 1), -0.3 * (x[2] - 1)}; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); -- GitLab From a984178fef4e4da38321496080c77584d4128ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 28 Mar 2025 19:19:09 +0100 Subject: [PATCH 095/122] Begin degree 2 reconstructions tests --- tests/CMakeLists.txt | 1 + ...test_PolynomialReconstruction_degree_2.cpp | 713 ++++++++++++++++++ 2 files changed, 714 insertions(+) create mode 100644 tests/test_PolynomialReconstruction_degree_2.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d7ba06548..e3739abe7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -280,6 +280,7 @@ add_executable (mpi_unit_tests test_ParallelChecker_read.cpp test_Partitioner.cpp test_PolynomialReconstruction_degree_1.cpp + test_PolynomialReconstruction_degree_2.cpp test_PolynomialReconstructionDescriptor.cpp test_RandomEngine.cpp test_StencilBuilder_cell2cell.cpp diff --git a/tests/test_PolynomialReconstruction_degree_2.cpp b/tests/test_PolynomialReconstruction_degree_2.cpp new file mode 100644 index 000000000..350a57426 --- /dev/null +++ b/tests/test_PolynomialReconstruction_degree_2.cpp @@ -0,0 +1,713 @@ +#include <catch2/catch_approx.hpp> +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <utils/PugsAssert.hpp> + +#include <mesh/Mesh.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <scheme/DiscreteFunctionDPkVariant.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/PolynomialReconstruction.hpp> + +#include <DiscreteFunctionDPkForTests.hpp> +#include <MeshDataBaseForTests.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") +{ + constexpr size_t degree = 2; + + SECTION("without symmetries") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree}}; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + 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 R_exact = [](const R1& x) { return 2.3 + 1.7 * x[0] - 1.4 * x[0] * x[0]; }; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + + SECTION("R^3 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 R3_exact = [](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0] - x[0] * x[0], // + +1.4 - 0.6 * x[0] + 2 * x[0] * x[0], // + -0.2 + 3.1 * x[0] + 1.4 * x[0] * x[0]}; + }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + + SECTION("R^3x3 data") + { + using R3x3 = TinyMatrix<3, 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 R3x3_exact = [](const R1& x) -> R3x3 { + return R3x3{+2.3 + 1.7 * x[0] - 2.3 * x[0] * x[0], // + -1.7 + 2.1 * x[0] + 1.2 * x[0] * x[0], // + +1.4 - 0.6 * x[0] - 2.0 * x[0] * x[0], // + // + +2.4 - 2.3 * x[0] + 1.1 * x[0] * x[0], // + -0.2 + 3.1 * x[0] - 0.7 * x[0] * x[0], // + -3.2 - 3.6 * x[0] + 0.1 * x[0] * x[0], // + // + -4.1 + 3.1 * x[0] - 0.2 * x[0] * x[0], // + +0.8 + 2.9 * x[0] + 4.1 * x[0] * x[0], // + -1.6 + 2.3 * x[0] - 1.7 * x[0] * x[0]}; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + + SECTION("R vector 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; + std::array<std::function<double(const R1&)>, 3> vector_exact = + {[](const R1& x) -> double { return +2.3 + 1.7 * x[0] + 1.2 * x[0] * x[0]; }, + [](const R1& x) -> double { return -1.7 + 2.1 * x[0] + 2.1 * x[0] * x[0]; }, + [](const R1& x) -> double { return +1.4 - 0.6 * x[0] - 1.3 * x[0] * x[0]; }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + + SECTION("R3 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; + + std::array<std::function<R3(const R1&)>, 2> vector_exact = + {[](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0] + 0.8 * x[0] * x[0], // + -1.7 + 2.1 * x[0] - 0.7 * x[0] * x[0], // + +1.4 - 0.6 * x[0] + 1.9 * x[0] * x[0]}; + }, + [](const R1& x) -> R3 { + return R3{+1.6 + 0.7 * x[0], -2.1 + 1.2 * x[0], +1.1 - 0.3 * x[0]}; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + + SECTION("list of various types") + { + using R3x3 = TinyMatrix<3>; + 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 R_exact = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; + + auto R3_exact = [](const R1& x) -> R3 { + return R3{+2.3 + 1.7 * x[0] + 2.3 * x[0] * x[0], // + +1.4 - 0.6 * x[0] - 1.5 * x[0] * x[0], // + -0.2 + 3.1 * x[0] + 2.9 * x[0] * x[0]}; + }; + + auto R3x3_exact = [](const R1& x) -> R3x3 { + return R3x3{ + +2.3 + 1.7 * x[0] - 0.3 * x[0] * x[0], + -1.7 + 2.1 * x[0] + 1.4 * x[0] * x[0], + +1.4 - 0.6 * x[0] - 2.3 * x[0] * x[0], + // + +2.4 - 2.3 * x[0] + 1.8 * x[0] * x[0], + -0.2 + 3.1 * x[0] - 1.7 * x[0] * x[0], + -3.2 - 3.6 * x[0] + 0.7 * x[0] * x[0], + // + -4.1 + 3.1 * x[0] - 1.9 * x[0] * x[0], + +0.8 + 2.9 * x[0] + 2.2 * x[0] * x[0], + -1.6 + 2.3 * x[0] - 1.3 * x[0] * x[0], + }; + }; + + std::array<std::function<double(const R1&)>, 3> vector_exact = + {[](const R1& x) -> double { return +2.3 + 1.7 * x[0] + 2.2 * x[0] * x[0]; }, + [](const R1& x) -> double { return -1.7 + 2.1 * x[0] - 1.9 * x[0] * x[0]; }, + [](const R1& x) -> double { return +1.4 - 0.6 * x[0] + 3.1 * x[0] * x[0]; }}; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = + PolynomialReconstruction{descriptor}.build(std::make_shared<DiscreteFunctionVariant>(fh), uh, + std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), + DiscreteFunctionVariant(Vh)); + + { + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + + { + auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + + { + auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + + { + auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + } + } + } + } + } + + 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 R_exact = [](const R2& x) { + return 2.3 + 1.7 * x[0] - 1.3 * x[1] // + + 1.2 * x[0] * x[0] + 1.3 * x[0] * x[1] - 3.2 * x[1] * x[1]; + }; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("R^3 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 R3_exact = [](const R2& x) -> R3 { + return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1] // + - 2.1 * x[0] * x[0] - 2.3 * x[0] * x[1] - 3.2 * x[1] * x[1], // + +1.4 - 0.6 * x[0] + 1.3 * x[1] // + + 2.3 * x[0] * x[0] - 1.3 * x[0] * x[1] + 1.2 * x[1] * x[1], // + -0.2 + 3.1 * x[0] - 1.1 * x[1] // + - 2.1 * x[0] * x[0] + 1.3 * x[0] * x[1] - 1.1 * x[1] * x[1]}; + }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("R^2x2 data") + { + using R2x2 = TinyMatrix<2, 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 R2x2_exact = [](const R2& x) -> R2x2 { + return R2x2{+2.3 + 1.7 * x[0] + 1.2 * x[1] // + - 2.1 * x[0] * x[0] + 1.3 * x[0] * x[1] + 1.2 * x[1] * x[1], // + -1.7 + 2.1 * x[0] - 2.2 * x[1] // + - 1.2 * x[0] * x[0] + 2.1 * x[0] * x[1] - 1.3 * x[1] * x[1], // + +1.4 - 0.6 * x[0] - 2.1 * x[1] // + - 1.1 * x[0] * x[0] - 2.3 * x[0] * x[1] + 2.1 * x[1] * x[1], + +2.4 - 2.3 * x[0] + 1.3 * x[1] // + + 2.7 * x[0] * x[0] + 2.1 * x[0] * x[1] - 2.7 * x[1] * x[1]}; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("vector 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; + + std::array<std::function<double(const R2&)>, 4> vector_exact = + {[](const R2& x) -> double { + return +2.3 + 1.7 * x[0] + 1.2 * x[1] + 1.2 * x[0] * x[0] - 2.1 * x[0] * x[1] + 3.1 * x[1] * x[1]; + }, + [](const R2& x) -> double { + return -1.7 + 2.1 * x[0] - 2.2 * x[1] - 0.7 * x[0] * x[0] + 2.2 * x[0] * x[1] - 1.6 * x[1] * x[1]; + }, + [](const R2& x) -> double { + return +1.4 - 0.6 * x[0] - 2.1 * x[1] + 2.3 * x[0] * x[0] + 2.3 * x[0] * x[1] - 2.9 * x[1] * x[1]; + }, + [](const R2& x) -> double { + return +2.4 - 2.3 * x[0] + 1.3 * x[1] - 2.7 * x[0] * x[0] - 1.2 * x[0] * x[1] - 0.7 * x[1] * x[1]; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_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 R_exact = [](const R3& x) { + return 2.3 + 1.7 * x[0] - 1.3 * x[1] + 2.1 * x[2] // + + 1.7 * x[0] * x[0] + 1.4 * x[1] * x[1] + 1.7 * x[2] * x[2] // + - 2.3 * x[0] * x[1] + 1.6 * x[0] * x[2] - 1.9 * x[1] * x[2]; + }; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("R^3 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 R3_exact = [](const R3& x) -> R3 { + return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1] + 1.8 * x[2] // + + 1.7 * x[0] * x[0] - 2.4 * x[1] * x[1] - 2.3 * x[2] * x[2] // + - 2.1 * x[0] * x[1] + 2.6 * x[0] * x[2] + 1.6 * x[1] * x[2], // + +1.4 - 0.6 * x[0] + 1.3 * x[1] - 3.7 * x[2] // + + 3.1 * x[0] * x[0] - 1.1 * x[1] * x[1] + 1.7 * x[2] * x[2] // + - 2.3 * x[0] * x[1] - 2.6 * x[0] * x[2] - 1.9 * x[1] * x[2], // + -0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2] // + - 1.5 * x[0] * x[0] + 1.4 * x[1] * x[1] - 1.2 * x[2] * x[2] // + - 1.7 * x[0] * x[1] - 1.3 * x[0] * x[2] + 2.1 * x[1] * x[2]}; + }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("R^2x2 data") + { + using R2x2 = TinyMatrix<2, 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 R2x2_exact = [](const R3& x) -> R2x2 { + return R2x2{ + +2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2] // + - 1.7 * x[0] * x[0] + 1.4 * x[1] * x[1] + 1.7 * x[2] * x[2] // + - 1.3 * x[0] * x[1] + 1.6 * x[0] * x[2] - 1.9 * x[1] * x[2], // + -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2] // + + 3.7 * x[0] * x[0] + 1.3 * x[1] * x[1] + 1.6 * x[2] * x[2] // + - 2.1 * x[0] * x[1] - 1.5 * x[0] * x[2] - 1.7 * x[1] * x[2], // + // + +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2] // + - 2.1 * x[0] * x[0] + 1.7 * x[1] * x[1] + 1.8 * x[2] * x[2] // + - 1.4 * x[0] * x[1] + 1.3 * x[0] * x[2] - 2.9 * x[1] * x[2], // + -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 * x[2] // + + 1.6 * x[0] * x[0] + 2.1 * x[1] * x[1] - 2.1 * x[2] * x[2] // + - 1.1 * x[0] * x[1] - 1.3 * x[0] * x[2] + 1.6 * x[1] * x[2], // + }; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + descriptor.setRowWeighting(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("vector 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; +#warning continue here + std::array<std::function<double(const R3&)>, 4> vector_exact = + {[](const R3& x) -> double { return +2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2]; }, + [](const R3& x) -> double { return -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2]; }, + [](const R3& x) -> double { return +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2]; }, + [](const R3& x) -> double { return -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 * x[2]; }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + descriptor.setPreconditioning(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + } + } + } + + SECTION("with symmetries") + { + // SECTION("1D") + // { + // std::vector<PolynomialReconstructionDescriptor> descriptor_list = + // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + // std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + // std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + // std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + // std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; + + // using R1 = TinyVector<1>; + + // for (auto descriptor : descriptor_list) { + // SECTION(name(descriptor.integrationMethodType())) + // { + // SECTION("R^1 data") + // { + // auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + // auto& mesh = *p_mesh; + + // auto R1_exact = [](const R1& x) { return R1{1.7 * (x[0] + 1)}; }; + + // DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R1_exact)); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + // auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R1>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R1_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("R1 vector 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; + + // std::array<std::function<R1(const R1&)>, 2> vector_exact // + // = {[](const R1& x) -> R1 { return R1{+1.7 * (x[0] + 1)}; }, + // [](const R1& x) -> R1 { return R1{-0.3 * (x[0] + 1)}; }}; + + // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R1>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + // } + // } + + // SECTION("2D") + // { + // std::vector<PolynomialReconstructionDescriptor> descriptor_list = + // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + // std::vector<std::shared_ptr< + // const IBoundaryDescriptor>>{std:: + // make_shared<NamedBoundaryDescriptor>( + // "XMAX"), + // std::make_shared<NamedBoundaryDescriptor>( + // "YMAX")}}, + // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + // std::vector<std::shared_ptr< + // const IBoundaryDescriptor>>{std:: + // make_shared<NamedBoundaryDescriptor>( + // "XMAX"), + // std::make_shared<NamedBoundaryDescriptor>( + // "YMAX")}}}; + + // using R2 = TinyVector<2>; + + // for (auto descriptor : descriptor_list) { + // SECTION(name(descriptor.integrationMethodType())) + // { + // SECTION("R^2 data") + // { + // auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + // auto& mesh = *p_mesh; + + // auto R2_exact = [](const R2& x) -> R2 { return R2{2.3 * (x[0] - 2), -1.3 * (x[1] - 1)}; }; + + // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R2_exact)); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R2_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("vector of R2") + // { + // auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + // auto& mesh = *p_mesh; + + // std::array<std::function<R2(const R2&)>, 2> vector_exact // + // = {[](const R2& x) -> R2 { + // return R2{+1.7 * (x[0] - 2), -0.6 * (x[1] - 1)}; + // }, + // [](const R2& x) -> R2 { + // return R2{-2.3 * (x[0] - 2), +1.1 * (x[1] - 1)}; + // }}; + + // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const R2>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + + // SECTION("3D") + // { + // std::vector<PolynomialReconstructionDescriptor> descriptor_list = + // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + // std::vector<std::shared_ptr< + // const IBoundaryDescriptor>>{std:: + // make_shared<NamedBoundaryDescriptor>( + // "XMAX"), + // std::make_shared<NamedBoundaryDescriptor>( + // "YMAX"), + // std::make_shared<NamedBoundaryDescriptor>( + // "ZMAX")}}, + // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + // std::vector<std::shared_ptr< + // const IBoundaryDescriptor>>{std:: + // make_shared<NamedBoundaryDescriptor>( + // "XMAX"), + // std::make_shared<NamedBoundaryDescriptor>( + // "YMAX"), + // std::make_shared<NamedBoundaryDescriptor>( + // "ZMAX")}}}; + + // using R3 = TinyVector<3>; + + // for (auto descriptor : descriptor_list) { + // SECTION(name(descriptor.integrationMethodType())) + // { + // SECTION("R^3 data") + // { + // auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + // auto& mesh = *p_mesh; + + // auto R3_exact = [](const R3& x) -> R3 { return R3{2.3 * (x[0] - 2), -1.3 * (x[1] - 1), 1.4 * (x[2] - 1)}; + // }; + + // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("vector of R3") + // { + // auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + // auto& mesh = *p_mesh; + + // std::array<std::function<R3(const R3&)>, 2> vector_exact // + // = {[](const R3& x) -> R3 { + // return R3{+1.7 * (x[0] - 2), -0.6 * (x[1] - 1), +1.2 * (x[2] - 1)}; + // }, + // [](const R3& x) -> R3 { + // return R3{-2.3 * (x[0] - 2), +1.1 * (x[1] - 1), -0.3 * (x[2] - 1)}; + // }}; + + // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const R3>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + } +} -- GitLab From 93ebce774b17e02d1ae67fd2d73ee8250243d2b1 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Fri, 28 Mar 2025 21:47:22 +0100 Subject: [PATCH 096/122] Fix error message typo --- src/mesh/StencilBuilder.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/mesh/StencilBuilder.cpp b/src/mesh/StencilBuilder.cpp index 20fc3bf61..de7501d44 100644 --- a/src/mesh/StencilBuilder.cpp +++ b/src/mesh/StencilBuilder.cpp @@ -447,8 +447,7 @@ StencilBuilder::_build_for_same_source_and_target(const ConnectivityType& connec } Array<uint32_t> column_indices{column_indices_vector.size()}; - parallel_for( - column_indices_vector.size(), PUGS_LAMBDA(size_t i) { column_indices[i] = column_indices_vector[i]; }); + parallel_for(column_indices_vector.size(), PUGS_LAMBDA(size_t i) { column_indices[i] = column_indices_vector[i]; }); ConnectivityMatrix primal_stencil{row_map, column_indices}; @@ -743,8 +742,7 @@ StencilBuilder::_build_for_different_source_and_target( } Array<uint32_t> column_indices{column_indices_vector.size()}; - parallel_for( - column_indices_vector.size(), PUGS_LAMBDA(size_t i) { column_indices[i] = column_indices_vector[i]; }); + parallel_for(column_indices_vector.size(), PUGS_LAMBDA(size_t i) { column_indices[i] = column_indices_vector[i]; }); ConnectivityMatrix primal_stencil{row_map, column_indices}; @@ -836,7 +834,7 @@ StencilBuilder::buildC2C(const IConnectivity& connectivity, if ((parallel::size() > 1) and (stencil_descriptor.numberOfLayers() > GlobalVariableManager::instance().getNumberOfGhostLayers())) { std::ostringstream error_msg; - error_msg << "Stencil builder requires" << rang::fgB::yellow << stencil_descriptor.numberOfLayers() + error_msg << "Stencil builder requires " << rang::fgB::yellow << stencil_descriptor.numberOfLayers() << rang::fg::reset << " layers while parallel number of ghost layer is " << GlobalVariableManager::instance().getNumberOfGhostLayers() << ".\n"; error_msg << "Increase the number of ghost layers (using the '--number-of-ghost-layers' option)."; @@ -870,7 +868,7 @@ StencilBuilder::buildN2C(const IConnectivity& connectivity, if ((parallel::size() > 1) and (stencil_descriptor.numberOfLayers() > GlobalVariableManager::instance().getNumberOfGhostLayers())) { std::ostringstream error_msg; - error_msg << "Stencil builder requires" << rang::fgB::yellow << stencil_descriptor.numberOfLayers() + error_msg << "Stencil builder requires " << rang::fgB::yellow << stencil_descriptor.numberOfLayers() << rang::fg::reset << " layers while parallel number of ghost layer is " << GlobalVariableManager::instance().getNumberOfGhostLayers() << ".\n"; error_msg << "Increase the number of ghost layers (using the '--number-of-ghost-layers' option)."; -- GitLab From 70ef6a6051d6b5508320b6961c07f466d5c01bda Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Mon, 31 Mar 2025 00:53:23 +0200 Subject: [PATCH 097/122] Handles various ghost layers in mesh data base for tests --- tests/MeshDataBaseForTests.cpp | 49 +++++++++++++------ tests/MeshDataBaseForTests.hpp | 15 +++--- ...test_PolynomialReconstruction_degree_2.cpp | 9 ++-- 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/tests/MeshDataBaseForTests.cpp b/tests/MeshDataBaseForTests.cpp index 1f2b485ed..1615673d0 100644 --- a/tests/MeshDataBaseForTests.cpp +++ b/tests/MeshDataBaseForTests.cpp @@ -1,11 +1,15 @@ #include <MeshDataBaseForTests.hpp> + #include <mesh/CartesianMeshBuilder.hpp> #include <mesh/Connectivity.hpp> #include <mesh/GmshReader.hpp> #include <mesh/MeshVariant.hpp> +#include <utils/GlobalVariableManager.hpp> #include <utils/Messenger.hpp> #include <utils/PugsAssert.hpp> +#include <NbGhostLayersTester.hpp> + #include <filesystem> #include <fstream> @@ -13,17 +17,22 @@ const MeshDataBaseForTests* MeshDataBaseForTests::m_instance = nullptr; MeshDataBaseForTests::MeshDataBaseForTests() { - m_cartesian_1d_mesh = CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{23}}.mesh(); + for (size_t nb_ghost_layers = 1; nb_ghost_layers <= m_max_nb_ghost_layers; ++nb_ghost_layers) { + NbGhostLayersTester t{nb_ghost_layers}; - m_cartesian_2d_mesh = - CartesianMeshBuilder{TinyVector<2>{0, -1}, TinyVector<2>{3, 2}, TinyVector<2, size_t>{6, 7}}.mesh(); + m_cartesian_1d_mesh.push_back( + CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{23}}.mesh()); - m_cartesian_3d_mesh = - CartesianMeshBuilder{TinyVector<3>{0, 1, 0}, TinyVector<3>{2, -1, 3}, TinyVector<3, size_t>{6, 7, 4}}.mesh(); + m_cartesian_2d_mesh.push_back( + CartesianMeshBuilder{TinyVector<2>{0, -1}, TinyVector<2>{3, 2}, TinyVector<2, size_t>{6, 7}}.mesh()); - m_unordered_1d_mesh = _buildUnordered1dMesh(); - m_hybrid_2d_mesh = _buildHybrid2dMesh(); - m_hybrid_3d_mesh = _buildHybrid3dMesh(); + m_cartesian_3d_mesh.push_back( + CartesianMeshBuilder{TinyVector<3>{0, 1, 0}, TinyVector<3>{2, -1, 3}, TinyVector<3, size_t>{6, 7, 4}}.mesh()); + + m_unordered_1d_mesh.push_back(_buildUnordered1dMesh()); + m_hybrid_2d_mesh.push_back(_buildHybrid2dMesh()); + m_hybrid_3d_mesh.push_back(_buildHybrid3dMesh()); + } } const MeshDataBaseForTests& @@ -50,37 +59,49 @@ MeshDataBaseForTests::destroy() std::shared_ptr<const MeshVariant> MeshDataBaseForTests::cartesian1DMesh() const { - return m_cartesian_1d_mesh; + const size_t nb_ghost_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + Assert((nb_ghost_layers >= 1) and (nb_ghost_layers <= m_max_nb_ghost_layers)); + return m_cartesian_1d_mesh[nb_ghost_layers - 1]; } std::shared_ptr<const MeshVariant> MeshDataBaseForTests::cartesian2DMesh() const { - return m_cartesian_2d_mesh; + const size_t nb_ghost_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + Assert((nb_ghost_layers >= 1) and (nb_ghost_layers <= m_max_nb_ghost_layers)); + return m_cartesian_2d_mesh[nb_ghost_layers - 1]; } std::shared_ptr<const MeshVariant> MeshDataBaseForTests::cartesian3DMesh() const { - return m_cartesian_3d_mesh; + const size_t nb_ghost_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + Assert((nb_ghost_layers >= 1) and (nb_ghost_layers <= m_max_nb_ghost_layers)); + return m_cartesian_3d_mesh[nb_ghost_layers - 1]; } std::shared_ptr<const MeshVariant> MeshDataBaseForTests::unordered1DMesh() const { - return m_unordered_1d_mesh; + const size_t nb_ghost_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + Assert((nb_ghost_layers >= 1) and (nb_ghost_layers <= m_max_nb_ghost_layers)); + return m_unordered_1d_mesh[nb_ghost_layers - 1]; } std::shared_ptr<const MeshVariant> MeshDataBaseForTests::hybrid2DMesh() const { - return m_hybrid_2d_mesh; + const size_t nb_ghost_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + Assert((nb_ghost_layers >= 1) and (nb_ghost_layers <= m_max_nb_ghost_layers)); + return m_hybrid_2d_mesh[nb_ghost_layers - 1]; } std::shared_ptr<const MeshVariant> MeshDataBaseForTests::hybrid3DMesh() const { - return m_hybrid_3d_mesh; + const size_t nb_ghost_layers = GlobalVariableManager::instance().getNumberOfGhostLayers(); + Assert((nb_ghost_layers >= 1) and (nb_ghost_layers <= m_max_nb_ghost_layers)); + return m_hybrid_3d_mesh[nb_ghost_layers - 1]; } std::shared_ptr<const MeshVariant> diff --git a/tests/MeshDataBaseForTests.hpp b/tests/MeshDataBaseForTests.hpp index 2b092d544..f0ab29fa9 100644 --- a/tests/MeshDataBaseForTests.hpp +++ b/tests/MeshDataBaseForTests.hpp @@ -4,6 +4,7 @@ #include <array> #include <memory> #include <string> +#include <vector> class MeshVariant; @@ -35,15 +36,17 @@ class MeshDataBaseForTests private: explicit MeshDataBaseForTests(); + constexpr static size_t m_max_nb_ghost_layers = 3; + static const MeshDataBaseForTests* m_instance; - std::shared_ptr<const MeshVariant> m_cartesian_1d_mesh; - std::shared_ptr<const MeshVariant> m_cartesian_2d_mesh; - std::shared_ptr<const MeshVariant> m_cartesian_3d_mesh; + std::vector<std::shared_ptr<const MeshVariant>> m_cartesian_1d_mesh; + std::vector<std::shared_ptr<const MeshVariant>> m_cartesian_2d_mesh; + std::vector<std::shared_ptr<const MeshVariant>> m_cartesian_3d_mesh; - std::shared_ptr<const MeshVariant> m_unordered_1d_mesh; - std::shared_ptr<const MeshVariant> m_hybrid_2d_mesh; - std::shared_ptr<const MeshVariant> m_hybrid_3d_mesh; + std::vector<std::shared_ptr<const MeshVariant>> m_unordered_1d_mesh; + std::vector<std::shared_ptr<const MeshVariant>> m_hybrid_2d_mesh; + std::vector<std::shared_ptr<const MeshVariant>> m_hybrid_3d_mesh; std::shared_ptr<const MeshVariant> _buildUnordered1dMesh(); std::shared_ptr<const MeshVariant> _buildHybrid2dMesh(); diff --git a/tests/test_PolynomialReconstruction_degree_2.cpp b/tests/test_PolynomialReconstruction_degree_2.cpp index 350a57426..dce627cab 100644 --- a/tests/test_PolynomialReconstruction_degree_2.cpp +++ b/tests/test_PolynomialReconstruction_degree_2.cpp @@ -14,12 +14,17 @@ #include <DiscreteFunctionDPkForTests.hpp> #include <MeshDataBaseForTests.hpp> +#include <NbGhostLayersTester.hpp> + // clazy:excludeall=non-pod-global-static TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") { constexpr size_t degree = 2; + constexpr size_t nb_ghost_layers = 2; + NbGhostLayersTester t{nb_ghost_layers}; + SECTION("without symmetries") { std::vector<PolynomialReconstructionDescriptor> descriptor_list = @@ -158,9 +163,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") -1.7 + 2.1 * x[0] - 0.7 * x[0] * x[0], // +1.4 - 0.6 * x[0] + 1.9 * x[0] * x[0]}; }, - [](const R1& x) -> R3 { - return R3{+1.6 + 0.7 * x[0], -2.1 + 1.2 * x[0], +1.1 - 0.3 * x[0]}; - }}; + [](const R1& x) -> R3 { return R3{+1.6 + 0.7 * x[0], -2.1 + 1.2 * x[0], +1.1 - 0.3 * x[0]}; }}; DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); -- GitLab From 3adb6a451f1deee3956dfb7c2572236976c07e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 31 Mar 2025 19:16:38 +0200 Subject: [PATCH 098/122] Fix reconstruction for DiscreteFunctionP0Vector of TinyVector --- src/scheme/PolynomialReconstruction.cpp | 2 +- ...test_PolynomialReconstruction_degree_2.cpp | 215 +++++++----------- 2 files changed, 89 insertions(+), 128 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 1a2b1e072..5101d5da2 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -1452,7 +1452,7 @@ PolynomialReconstruction::_build( if (m_descriptor.degree() > 1) { auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; for (size_t i = 0; i < basis_dimension - 1; ++i) { - auto& dpk_j_0 = dpk_j[0]; + auto& dpk_j_0 = dpk_j[component_begin]; for (size_t k = 0; k < DataType::Dimension; ++k) { dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; } diff --git a/tests/test_PolynomialReconstruction_degree_2.cpp b/tests/test_PolynomialReconstruction_degree_2.cpp index dce627cab..435084af0 100644 --- a/tests/test_PolynomialReconstruction_degree_2.cpp +++ b/tests/test_PolynomialReconstruction_degree_2.cpp @@ -38,6 +38,17 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") { using R1 = TinyVector<1>; + auto p0 = [](const R1& x) { return +2.3 + 1.7 * x[0] - 2.3 * x[0] * x[0]; }; + auto p1 = [](const R1& x) { return -1.7 + 2.1 * x[0] + 1.2 * x[0] * x[0]; }; + auto p2 = [](const R1& x) { return +1.4 - 0.6 * x[0] - 2.0 * x[0] * x[0]; }; + auto p3 = [](const R1& x) { return +2.4 - 2.3 * x[0] + 1.1 * x[0] * x[0]; }; + auto p4 = [](const R1& x) { return -0.2 + 3.1 * x[0] - 0.7 * x[0] * x[0]; }; + auto p5 = [](const R1& x) { return -3.2 - 3.6 * x[0] + 0.1 * x[0] * x[0]; }; + auto p6 = [](const R1& x) { return -4.1 + 3.1 * x[0] - 0.2 * x[0] * x[0]; }; + auto p7 = [](const R1& x) { return +0.8 + 2.9 * x[0] + 4.1 * x[0] * x[0]; }; + auto p8 = [](const R1& x) { return -1.6 + 2.3 * x[0] - 1.7 * x[0] * x[0]; }; + auto p9 = [](const R1& x) { return +2.3 + 1.7 * x[0] - 1.4 * x[0] * x[0]; }; + SECTION("R data") { for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) { @@ -46,7 +57,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto R_exact = [](const R1& x) { return 2.3 + 1.7 * x[0] - 1.4 * x[0] * x[0]; }; + auto R_exact = p0; DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); @@ -70,11 +81,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto R3_exact = [](const R1& x) -> R3 { - return R3{+2.3 + 1.7 * x[0] - x[0] * x[0], // - +1.4 - 0.6 * x[0] + 2 * x[0] * x[0], // - -0.2 + 3.1 * x[0] + 1.4 * x[0] * x[0]}; - }; + auto R3_exact = [&](const R1& x) -> R3 { return R3{p2(x), p4(x), p1(x)}; }; DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); @@ -98,18 +105,10 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto R3x3_exact = [](const R1& x) -> R3x3 { - return R3x3{+2.3 + 1.7 * x[0] - 2.3 * x[0] * x[0], // - -1.7 + 2.1 * x[0] + 1.2 * x[0] * x[0], // - +1.4 - 0.6 * x[0] - 2.0 * x[0] * x[0], // - // - +2.4 - 2.3 * x[0] + 1.1 * x[0] * x[0], // - -0.2 + 3.1 * x[0] - 0.7 * x[0] * x[0], // - -3.2 - 3.6 * x[0] + 0.1 * x[0] * x[0], // - // - -4.1 + 3.1 * x[0] - 0.2 * x[0] * x[0], // - +0.8 + 2.9 * x[0] + 4.1 * x[0] * x[0], // - -1.6 + 2.3 * x[0] - 1.7 * x[0] * x[0]}; + auto R3x3_exact = [&](const R1& x) -> R3x3 { + return R3x3{p1(x), p2(x), p3(x), // + p4(x), p5(x), p6(x), // + p7(x), p8(x), p9(x)}; }; DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); @@ -131,10 +130,8 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") { auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - std::array<std::function<double(const R1&)>, 3> vector_exact = - {[](const R1& x) -> double { return +2.3 + 1.7 * x[0] + 1.2 * x[0] * x[0]; }, - [](const R1& x) -> double { return -1.7 + 2.1 * x[0] + 2.1 * x[0] * x[0]; }, - [](const R1& x) -> double { return +1.4 - 0.6 * x[0] - 1.3 * x[0] * x[0]; }}; + + std::array<std::function<double(const R1&)>, 3> vector_exact = {p1, p7, p9}; DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); @@ -157,13 +154,16 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - std::array<std::function<R3(const R1&)>, 2> vector_exact = - {[](const R1& x) -> R3 { - return R3{+2.3 + 1.7 * x[0] + 0.8 * x[0] * x[0], // - -1.7 + 2.1 * x[0] - 0.7 * x[0] * x[0], // - +1.4 - 0.6 * x[0] + 1.9 * x[0] * x[0]}; - }, - [](const R1& x) -> R3 { return R3{+1.6 + 0.7 * x[0], -2.1 + 1.2 * x[0], +1.1 - 0.3 * x[0]}; }}; + std::array<std::function<R3(const R1&)>, 3> vector_exact // + = {[&](const R1& x) -> R3 { + return R3{p1(x), p2(x), p3(x)}; + }, + [&](const R1& x) -> R3 { + return R3{p5(x), p7(x), p0(x)}; + }, + [&](const R1& x) -> R3 { + return R3{p9(x), p8(x), p4(x)}; + }}; DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); @@ -188,34 +188,17 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<1>>(); auto& mesh = *p_mesh; - auto R_exact = [](const R1& x) { return 2.3 + 1.7 * x[0]; }; + auto R_exact = p0; - auto R3_exact = [](const R1& x) -> R3 { - return R3{+2.3 + 1.7 * x[0] + 2.3 * x[0] * x[0], // - +1.4 - 0.6 * x[0] - 1.5 * x[0] * x[0], // - -0.2 + 3.1 * x[0] + 2.9 * x[0] * x[0]}; - }; + auto R3_exact = [&](const R1& x) -> R3 { return R3{p9(x), p4(x), p7(x)}; }; - auto R3x3_exact = [](const R1& x) -> R3x3 { - return R3x3{ - +2.3 + 1.7 * x[0] - 0.3 * x[0] * x[0], - -1.7 + 2.1 * x[0] + 1.4 * x[0] * x[0], - +1.4 - 0.6 * x[0] - 2.3 * x[0] * x[0], - // - +2.4 - 2.3 * x[0] + 1.8 * x[0] * x[0], - -0.2 + 3.1 * x[0] - 1.7 * x[0] * x[0], - -3.2 - 3.6 * x[0] + 0.7 * x[0] * x[0], - // - -4.1 + 3.1 * x[0] - 1.9 * x[0] * x[0], - +0.8 + 2.9 * x[0] + 2.2 * x[0] * x[0], - -1.6 + 2.3 * x[0] - 1.3 * x[0] * x[0], - }; + auto R3x3_exact = [&](const R1& x) -> R3x3 { + return R3x3{p2(x), p1(x), p0(x), // + p3(x), p2(x), p4(x), // + p6(x), p5(x), p9(x)}; }; - std::array<std::function<double(const R1&)>, 3> vector_exact = - {[](const R1& x) -> double { return +2.3 + 1.7 * x[0] + 2.2 * x[0] * x[0]; }, - [](const R1& x) -> double { return -1.7 + 2.1 * x[0] - 1.9 * x[0] * x[0]; }, - [](const R1& x) -> double { return +1.4 - 0.6 * x[0] + 3.1 * x[0] * x[0]; }}; + std::array<std::function<double(const R1&)>, 3> vector_exact = {p1, p8, p7}; DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); @@ -258,6 +241,19 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") SECTION("2D") { using R2 = TinyVector<2>; + auto p0 = [](const R2& x) { + return +2.3 + 1.7 * x[0] - 1.3 * x[1] + 1.2 * x[0] * x[0] + 1.3 * x[0] * x[1] - 3.2 * x[1] * x[1]; + }; + + auto p1 = [](const R2& x) { + return +2.3 + 1.7 * x[0] - 2.2 * x[1] - 2.1 * x[0] * x[0] - 2.3 * x[0] * x[1] - 3.2 * x[1] * x[1]; + }; + auto p2 = [](const R2& x) { + return +1.4 - 0.6 * x[0] + 1.3 * x[1] + 2.3 * x[0] * x[0] - 1.3 * x[0] * x[1] + 1.2 * x[1] * x[1]; + }; + auto p3 = [](const R2& x) { + return -0.2 + 3.1 * x[0] - 1.1 * x[1] - 2.1 * x[0] * x[0] + 1.3 * x[0] * x[1] - 1.1 * x[1] * x[1]; + }; SECTION("R data") { @@ -267,10 +263,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - auto R_exact = [](const R2& x) { - return 2.3 + 1.7 * x[0] - 1.3 * x[1] // - + 1.2 * x[0] * x[0] + 1.3 * x[0] * x[1] - 3.2 * x[1] * x[1]; - }; + auto R_exact = p0; DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); @@ -294,14 +287,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - auto R3_exact = [](const R2& x) -> R3 { - return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1] // - - 2.1 * x[0] * x[0] - 2.3 * x[0] * x[1] - 3.2 * x[1] * x[1], // - +1.4 - 0.6 * x[0] + 1.3 * x[1] // - + 2.3 * x[0] * x[0] - 1.3 * x[0] * x[1] + 1.2 * x[1] * x[1], // - -0.2 + 3.1 * x[0] - 1.1 * x[1] // - - 2.1 * x[0] * x[0] + 1.3 * x[0] * x[1] - 1.1 * x[1] * x[1]}; - }; + auto R3_exact = [&](const R2& x) -> R3 { return R3{p1(x), p2(x), p3(x)}; }; DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); @@ -325,15 +311,9 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - auto R2x2_exact = [](const R2& x) -> R2x2 { - return R2x2{+2.3 + 1.7 * x[0] + 1.2 * x[1] // - - 2.1 * x[0] * x[0] + 1.3 * x[0] * x[1] + 1.2 * x[1] * x[1], // - -1.7 + 2.1 * x[0] - 2.2 * x[1] // - - 1.2 * x[0] * x[0] + 2.1 * x[0] * x[1] - 1.3 * x[1] * x[1], // - +1.4 - 0.6 * x[0] - 2.1 * x[1] // - - 1.1 * x[0] * x[0] - 2.3 * x[0] * x[1] + 2.1 * x[1] * x[1], - +2.4 - 2.3 * x[0] + 1.3 * x[1] // - + 2.7 * x[0] * x[0] + 2.1 * x[0] * x[1] - 2.7 * x[1] * x[1]}; + auto R2x2_exact = [&](const R2& x) -> R2x2 { + return R2x2{p0(x), p1(x), // + p2(x), p3(x)}; }; DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); @@ -355,19 +335,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<2>>(); auto& mesh = *p_mesh; - std::array<std::function<double(const R2&)>, 4> vector_exact = - {[](const R2& x) -> double { - return +2.3 + 1.7 * x[0] + 1.2 * x[1] + 1.2 * x[0] * x[0] - 2.1 * x[0] * x[1] + 3.1 * x[1] * x[1]; - }, - [](const R2& x) -> double { - return -1.7 + 2.1 * x[0] - 2.2 * x[1] - 0.7 * x[0] * x[0] + 2.2 * x[0] * x[1] - 1.6 * x[1] * x[1]; - }, - [](const R2& x) -> double { - return +1.4 - 0.6 * x[0] - 2.1 * x[1] + 2.3 * x[0] * x[0] + 2.3 * x[0] * x[1] - 2.9 * x[1] * x[1]; - }, - [](const R2& x) -> double { - return +2.4 - 2.3 * x[0] + 1.3 * x[1] - 2.7 * x[0] * x[0] - 1.2 * x[0] * x[1] - 0.7 * x[1] * x[1]; - }}; + std::array<std::function<double(const R2&)>, 4> vector_exact = {p0, p1, p2, p3}; DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); @@ -385,6 +353,30 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") { using R3 = TinyVector<3>; + auto p0 = [](const R3& x) { + return 2.3 + 1.7 * x[0] - 1.3 * x[1] + 2.1 * x[2] // + + 1.7 * x[0] * x[0] + 1.4 * x[1] * x[1] + 1.7 * x[2] * x[2] // + - 2.3 * x[0] * x[1] + 1.6 * x[0] * x[2] - 1.9 * x[1] * x[2]; + }; + + auto p1 = [](const R3& x) { + return +2.3 + 1.7 * x[0] - 2.2 * x[1] + 1.8 * x[2] // + + 1.7 * x[0] * x[0] - 2.4 * x[1] * x[1] - 2.3 * x[2] * x[2] // + - 2.1 * x[0] * x[1] + 2.6 * x[0] * x[2] + 1.6 * x[1] * x[2]; + }; + + auto p2 = [](const R3& x) { + return +1.4 - 0.6 * x[0] + 1.3 * x[1] - 3.7 * x[2] // + + 3.1 * x[0] * x[0] - 1.1 * x[1] * x[1] + 1.7 * x[2] * x[2] // + - 2.3 * x[0] * x[1] - 2.6 * x[0] * x[2] - 1.9 * x[1] * x[2]; + }; + + auto p3 = [](const R3& x) { + return -0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2] // + - 1.5 * x[0] * x[0] + 1.4 * x[1] * x[1] - 1.2 * x[2] * x[2] // + - 1.7 * x[0] * x[1] - 1.3 * x[0] * x[2] + 2.1 * x[1] * x[2]; + }; + SECTION("R data") { for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { @@ -393,11 +385,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto R_exact = [](const R3& x) { - return 2.3 + 1.7 * x[0] - 1.3 * x[1] + 2.1 * x[2] // - + 1.7 * x[0] * x[0] + 1.4 * x[1] * x[1] + 1.7 * x[2] * x[2] // - - 2.3 * x[0] * x[1] + 1.6 * x[0] * x[2] - 1.9 * x[1] * x[2]; - }; + auto R_exact = p0; DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); @@ -419,17 +407,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto R3_exact = [](const R3& x) -> R3 { - return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1] + 1.8 * x[2] // - + 1.7 * x[0] * x[0] - 2.4 * x[1] * x[1] - 2.3 * x[2] * x[2] // - - 2.1 * x[0] * x[1] + 2.6 * x[0] * x[2] + 1.6 * x[1] * x[2], // - +1.4 - 0.6 * x[0] + 1.3 * x[1] - 3.7 * x[2] // - + 3.1 * x[0] * x[0] - 1.1 * x[1] * x[1] + 1.7 * x[2] * x[2] // - - 2.3 * x[0] * x[1] - 2.6 * x[0] * x[2] - 1.9 * x[1] * x[2], // - -0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2] // - - 1.5 * x[0] * x[0] + 1.4 * x[1] * x[1] - 1.2 * x[2] * x[2] // - - 1.7 * x[0] * x[1] - 1.3 * x[0] * x[2] + 2.1 * x[1] * x[2]}; - }; + auto R3_exact = [&](const R3& x) -> R3 { return R3{p1(x), p2(x), p3(x)}; }; DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); @@ -453,22 +431,9 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; - auto R2x2_exact = [](const R3& x) -> R2x2 { - return R2x2{ - +2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2] // - - 1.7 * x[0] * x[0] + 1.4 * x[1] * x[1] + 1.7 * x[2] * x[2] // - - 1.3 * x[0] * x[1] + 1.6 * x[0] * x[2] - 1.9 * x[1] * x[2], // - -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2] // - + 3.7 * x[0] * x[0] + 1.3 * x[1] * x[1] + 1.6 * x[2] * x[2] // - - 2.1 * x[0] * x[1] - 1.5 * x[0] * x[2] - 1.7 * x[1] * x[2], // - // - +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2] // - - 2.1 * x[0] * x[0] + 1.7 * x[1] * x[1] + 1.8 * x[2] * x[2] // - - 1.4 * x[0] * x[1] + 1.3 * x[0] * x[2] - 2.9 * x[1] * x[2], // - -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 * x[2] // - + 1.6 * x[0] * x[0] + 2.1 * x[1] * x[1] - 2.1 * x[2] * x[2] // - - 1.1 * x[0] * x[1] - 1.3 * x[0] * x[2] + 1.6 * x[1] * x[2], // - }; + auto R2x2_exact = [&](const R3& x) -> R2x2 { + return R2x2{p0(x), p1(x), // + p2(x), p3(x)}; }; DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); @@ -490,12 +455,8 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") { auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); auto& mesh = *p_mesh; -#warning continue here - std::array<std::function<double(const R3&)>, 4> vector_exact = - {[](const R3& x) -> double { return +2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2]; }, - [](const R3& x) -> double { return -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2]; }, - [](const R3& x) -> double { return +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2]; }, - [](const R3& x) -> double { return -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 * x[2]; }}; + + std::array<std::function<double(const R3&)>, 4> vector_exact = {p0, p1, p2, p3}; DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); @@ -516,6 +477,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") SECTION("with symmetries") { +#warning continue here // SECTION("1D") // { // std::vector<PolynomialReconstructionDescriptor> descriptor_list = @@ -529,8 +491,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") // using R1 = TinyVector<1>; // for (auto descriptor : descriptor_list) { - // SECTION(name(descriptor.integrationMethodType())) - // { + // SECTION(name(descriptor.integrationMethodType()))// { // SECTION("R^1 data") // { // auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); -- GitLab From 00d64dec8cc8ce7accc7e7dfa51fce3b878f7d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 10 Apr 2025 09:29:56 +0200 Subject: [PATCH 099/122] Add reconstruction for DiscreteFunctionP0Vector of TinyMatrix --- src/scheme/PolynomialReconstruction.cpp | 87 ++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 10 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 5101d5da2..77fed2565 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -977,9 +977,10 @@ PolynomialReconstruction::_build( B(index, kB) = qi_qj[k]; } } else if constexpr (is_tiny_matrix_v<DataType>) { - for (size_t k = 0; k < DataType::NumberOfRows; ++k) { - for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - B(index, column_begin + k * DataType::NumberOfColumns + l) = qi_qj(k, l); + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + const size_t kB = column_begin + p * DataType::NumberOfColumns; + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + B(index, kB + q) = qi_qj(p, q); } } } @@ -1018,9 +1019,9 @@ PolynomialReconstruction::_build( const DataType& qi = discrete_function[cell_i_id]; const DataType& qi_qj = symmetrize_matrix(normal, qi) - qj; - for (size_t k = 0; k < DataType::NumberOfRows; ++k) { - for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - B(index, column_begin + k * DataType::NumberOfColumns + l) = qi_qj(k, l); + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + B(index, column_begin + p * DataType::NumberOfColumns + q) = qi_qj(p, q); } } } else { @@ -1114,12 +1115,55 @@ PolynomialReconstruction::_build( } } } else if constexpr (is_tiny_matrix_v<DataType>) { + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi = discrete_function[cell_i_id][l]; + const DataType& qi_qj = qi - qj; + + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + const size_t kB = column_begin + l * DataType::Dimension + p * DataType::NumberOfColumns; + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + B(index, kB + q) = qi_qj(p, q); + } + } + } + } + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { - auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - if (ghost_cell_list.size() > 0) { - throw NotImplementedError("TinyMatrix symmetries for reconstruction of DiscreteFunctionP0Vector"); + if constexpr ((DataType::NumberOfRows == MeshType::Dimension) and + (DataType::NumberOfColumns == MeshType::Dimension)) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& normal = symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi = discrete_function[cell_i_id][l]; + const DataType& qi_qj = symmetrize_matrix(normal, qi) - qj; + + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + const size_t kB = column_begin + l * DataType::Dimension + p * DataType::NumberOfColumns; + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + B(index, kB + q) = qi_qj(p, q); + } + } + } + } + } else { + // LCOV_EXCL_START + std::stringstream error_msg; + error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension + << " using a mesh of dimension " << MeshType::Dimension; + throw UnexpectedError(error_msg.str()); + // LCOV_EXCL_STOP } } } @@ -1466,6 +1510,29 @@ PolynomialReconstruction::_build( } } column_begin += DataType::Dimension; + } else if constexpr (is_tiny_matrix_v<DataType>) { + if (m_descriptor.degree() > 1) { + auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + for (size_t i = 0; i < basis_dimension - 1; ++i) { + auto& dpk_j_0 = dpk_j[component_begin]; + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + dpk_j_0(p, q) -= + X(i, column_begin + p * DataType::NumberOfColumns + q) * mean_j_of_ejk[i]; + } + } + } + } + + for (size_t i = 0; i < basis_dimension - 1; ++i) { + auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + dpk_j_ip1(p, q) = X(i, column_begin + p * DataType::NumberOfColumns + q); + } + } + } + column_begin += DataType::Dimension; } else { // LCOV_EXCL_START throw UnexpectedError("unexpected data type"); -- GitLab From 1d505a118cdcb7be7bf431caf39b0ec264ac24b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 14 Apr 2025 13:44:16 +0200 Subject: [PATCH 100/122] Add tests for PolynomialReconstruction of degree 2 --- ...test_PolynomialReconstruction_degree_2.cpp | 758 +++++++++++++----- 1 file changed, 562 insertions(+), 196 deletions(-) diff --git a/tests/test_PolynomialReconstruction_degree_2.cpp b/tests/test_PolynomialReconstruction_degree_2.cpp index 435084af0..a1c5c0589 100644 --- a/tests/test_PolynomialReconstruction_degree_2.cpp +++ b/tests/test_PolynomialReconstruction_degree_2.cpp @@ -477,201 +477,567 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") SECTION("with symmetries") { -#warning continue here - // SECTION("1D") - // { - // std::vector<PolynomialReconstructionDescriptor> descriptor_list = - // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, - // std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ - // std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, - // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, - // std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ - // std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; - - // using R1 = TinyVector<1>; - - // for (auto descriptor : descriptor_list) { - // SECTION(name(descriptor.integrationMethodType()))// { - // SECTION("R^1 data") - // { - // auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - - // auto& mesh = *p_mesh; - - // auto R1_exact = [](const R1& x) { return R1{1.7 * (x[0] + 1)}; }; - - // DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R1_exact)); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - - // auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R1>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R1_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - - // SECTION("R1 vector 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; - - // std::array<std::function<R1(const R1&)>, 2> vector_exact // - // = {[](const R1& x) -> R1 { return R1{+1.7 * (x[0] + 1)}; }, - // [](const R1& x) -> R1 { return R1{-0.3 * (x[0] + 1)}; }}; - - // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - - // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R1>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } - // } - // } - // } - - // SECTION("2D") - // { - // std::vector<PolynomialReconstructionDescriptor> descriptor_list = - // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, - // std::vector<std::shared_ptr< - // const IBoundaryDescriptor>>{std:: - // make_shared<NamedBoundaryDescriptor>( - // "XMAX"), - // std::make_shared<NamedBoundaryDescriptor>( - // "YMAX")}}, - // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, - // std::vector<std::shared_ptr< - // const IBoundaryDescriptor>>{std:: - // make_shared<NamedBoundaryDescriptor>( - // "XMAX"), - // std::make_shared<NamedBoundaryDescriptor>( - // "YMAX")}}}; - - // using R2 = TinyVector<2>; - - // for (auto descriptor : descriptor_list) { - // SECTION(name(descriptor.integrationMethodType())) - // { - // SECTION("R^2 data") - // { - // auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); - // auto& mesh = *p_mesh; - - // auto R2_exact = [](const R2& x) -> R2 { return R2{2.3 * (x[0] - 2), -1.3 * (x[1] - 1)}; }; - - // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R2_exact)); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - - // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R2_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - - // SECTION("vector of R2") - // { - // auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); - // auto& mesh = *p_mesh; - - // std::array<std::function<R2(const R2&)>, 2> vector_exact // - // = {[](const R2& x) -> R2 { - // return R2{+1.7 * (x[0] - 2), -0.6 * (x[1] - 1)}; - // }, - // [](const R2& x) -> R2 { - // return R2{-2.3 * (x[0] - 2), +1.1 * (x[1] - 1)}; - // }}; - - // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - - // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const R2>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } - // } - - // SECTION("3D") - // { - // std::vector<PolynomialReconstructionDescriptor> descriptor_list = - // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, - // std::vector<std::shared_ptr< - // const IBoundaryDescriptor>>{std:: - // make_shared<NamedBoundaryDescriptor>( - // "XMAX"), - // std::make_shared<NamedBoundaryDescriptor>( - // "YMAX"), - // std::make_shared<NamedBoundaryDescriptor>( - // "ZMAX")}}, - // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, - // std::vector<std::shared_ptr< - // const IBoundaryDescriptor>>{std:: - // make_shared<NamedBoundaryDescriptor>( - // "XMAX"), - // std::make_shared<NamedBoundaryDescriptor>( - // "YMAX"), - // std::make_shared<NamedBoundaryDescriptor>( - // "ZMAX")}}}; - - // using R3 = TinyVector<3>; - - // for (auto descriptor : descriptor_list) { - // SECTION(name(descriptor.integrationMethodType())) - // { - // SECTION("R^3 data") - // { - // auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); - // auto& mesh = *p_mesh; - - // auto R3_exact = [](const R3& x) -> R3 { return R3{2.3 * (x[0] - 2), -1.3 * (x[1] - 1), 1.4 * (x[2] - 1)}; - // }; - - // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - - // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - - // SECTION("vector of R3") - // { - // auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); - // auto& mesh = *p_mesh; - - // std::array<std::function<R3(const R3&)>, 2> vector_exact // - // = {[](const R3& x) -> R3 { - // return R3{+1.7 * (x[0] - 2), -0.6 * (x[1] - 1), +1.2 * (x[2] - 1)}; - // }, - // [](const R3& x) -> R3 { - // return R3{-2.3 * (x[0] - 2), +1.1 * (x[1] - 1), -0.3 * (x[2] - 1)}; - // }}; - - // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - - // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const R3>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } - // } + SECTION("1D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; + using R1 = TinyVector<1>; + + auto p0 = [](const R1& x) { return +1.7 * (x[0] + 1) * (x[0] + 1) - 1.1; }; + auto p1 = [](const R1& x) { return -1.2 * (x[0] + 1) * (x[0] + 1) + 1.3; }; + auto p2 = [](const R1& x) { return +1.4 * (x[0] + 1) * (x[0] + 1) - 0.6; }; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("R data") + { + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + auto& mesh = *p_mesh; + + auto R_exact = p0; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("R1x1 data") + { + using R1x1 = TinyMatrix<1>; + + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + auto& mesh = *p_mesh; + + auto R1x1_exact = [&](const R1& x) { return R1x1{p0(x)}; }; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R1x1_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R1x1>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R1x1_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("R vector data") + { + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; + + std::array<std::function<double(const R1&)>, 3> vector_exact // + = {p0, p1, p2}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("R1x1 vector data") + { + using R1x1 = TinyMatrix<1>; + + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; + + std::array<std::function<R1x1(const R1&)>, 3> vector_exact // + = {[&](const R1& x) { return R1x1{p2(x)}; }, // + [&](const R1& x) { return R1x1{p0(x)}; }, // + [&](const R1& x) { return R1x1{p1(x)}; }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R1x1>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + + SECTION("2D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}}; + + using R2 = TinyVector<2>; + + auto p_initial_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + auto& initial_mesh = *p_initial_mesh; + + constexpr double theta = 1; + TinyMatrix<2> T{std::cos(theta), -std::sin(theta), // + std::sin(theta), std::cos(theta)}; + + auto xr = initial_mesh.xr(); + + NodeValue<R2> new_xr{initial_mesh.connectivity()}; + parallel_for( + initial_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = T * xr[node_id]; }); + + std::shared_ptr p_mesh = std::make_shared<const Mesh<2>>(initial_mesh.shared_connectivity(), new_xr); + const Mesh<2>& mesh = *p_mesh; + // inverse rotation + TinyMatrix<2> inv_T{std::cos(theta), std::sin(theta), // + -std::sin(theta), std::cos(theta)}; + + auto p0 = [&inv_T](const R2& X) { + const R2 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + return +1.7 * x * x + 2 * y * y - 1.1; + }; + + auto p1 = [&inv_T](const R2& X) { + const R2 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + return -1.3 * x * x - 0.2 * y * y + 0.7; + }; + + auto p2 = [&inv_T](const R2& X) { + const R2 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + return +2.6 * x * x - 1.4 * y * y - 1.9; + }; + + auto p3 = [&inv_T](const R2& X) { + const R2 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + return -0.6 * x * x + 1.4 * y * y + 2.3; + }; + + auto q0 = [&inv_T](const R2& X) { + const R2 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + return 3 * x * y; + }; + + auto q1 = [&inv_T](const R2& X) { + const R2 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + return -1.3 * x * y; + }; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("R data") + { + auto R_exact = p0; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("R2x2 data") + { + using R2x2 = TinyMatrix<2>; + auto R2x2_exact = [&](const R2& X) -> R2x2 { + return T * TinyMatrix<2>{p0(X), q0(X), q1(X), p1(X)} * inv_T; + }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector of R") + { + std::array<std::function<double(const R2&)>, 4> vector_exact // + = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector of R2x2") + { + using R2x2 = TinyMatrix<2>; + + std::array<std::function<R2x2(const R2&)>, 2> vector_R2x2_exact // + = {[&](const R2& X) { + return T * R2x2{p0(X), q0(X), q1(X), p1(X)} * inv_T; + }, + [&](const R2& X) { + return T * R2x2{p0(X), q1(X), 0, p1(X)} * inv_T; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_R2x2_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const R2x2>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_R2x2_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("list") + { + using R2x2 = TinyMatrix<2>; + + auto R_exact = p0; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + std::array<std::function<double(const R2&)>, 4> vector_exact // + = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + std::array<std::function<R2x2(const R2&)>, 2> vector_R2x2_exact // + = {[&](const R2& X) { + return T * R2x2{p0(X), q0(X), q1(X), p1(X)} * inv_T; + }, + [&](const R2& X) { + return T * R2x2{p2(X), q1(X), 0, p3(X)} * inv_T; + }}; + + DiscreteFunctionP0Vector Wh = test_only::exact_projection(mesh, degree, vector_R2x2_exact); + + auto R2x2_exact = [&](const R2& X) -> R2x2 { + return T * TinyMatrix<2>{p0(X), q1(X), q0(X), p3(X)} * inv_T; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh, Vh, Wh, Ah); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + auto dpk_Vh = reconstructions[1]->get<DiscreteFunctionDPkVector<2, const double>>(); + auto dpk_Wh = reconstructions[2]->get<DiscreteFunctionDPkVector<2, const R2x2>>(); + auto dpk_Ah = reconstructions[3]->get<DiscreteFunctionDPk<2, const R2x2>>(); + + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_Wh, vector_R2x2_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + } + + SECTION("3D") + { + using R3 = TinyVector<3>; + + auto p_initial_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + auto& initial_mesh = *p_initial_mesh; + + constexpr double theta = 1; + TinyMatrix<3> T{std::cos(theta), -std::sin(theta), 0, + // + std::sin(theta), std::cos(theta), 0, + // + 0, 0, 1}; + + auto xr = initial_mesh.xr(); + + NodeValue<R3> new_xr{initial_mesh.connectivity()}; + parallel_for( + initial_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = T * xr[node_id]; }); + + std::shared_ptr p_mesh = std::make_shared<const Mesh<3>>(initial_mesh.shared_connectivity(), new_xr); + const Mesh<3>& mesh = *p_mesh; + // inverse rotation + TinyMatrix<3> inv_T{std::cos(theta), std::sin(theta), 0, + // + -std::sin(theta), std::cos(theta), 0, + // + 0, 0, 1}; + + auto p0 = [&inv_T](const R3& X) { + const R3 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + const double z = Y[2] - 1; + return +1.7 * x * x + 2 * y * y + 1.3 * z * z - 1.1; + }; + + auto p1 = [&inv_T](const R3& X) { + const R3 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + const double z = Y[2] - 1; + return +2.1 * x * x - 1.4 * y * y - 3.1 * z * z + 2.2; + }; + + auto p2 = [&inv_T](const R3& X) { + const R3 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + const double z = Y[2] - 1; + return -1.1 * x * x - 1.2 * y * y + 1.3 * z * z - 1.7; + }; + + auto p3 = [&inv_T](const R3& X) { + const R3 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + const double z = Y[2] - 1; + return 1.9 * x * x + 2.1 * y * y - 3.1 * z * z + 1.6; + }; + + auto p4 = [&inv_T](const R3& X) { + const R3 Y = inv_T * X; + const double x = Y[0] - 2; + const double y = Y[1] - 1; + const double z = Y[2] - 1; + return -2.4 * x * x + 3.3 * y * y - 1.7 * z * z + 2.1; + }; + + SECTION("3 symmetries") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}}; + + for (auto descriptor : descriptor_list) { + SECTION("R data") + { + auto R_exact = p0; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector of R") + { + std::array<std::function<double(const R3&)>, 4> vector_exact // + = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("1 symmetry") + { + // Matrix and their transformations are kept simple to + // derive exact solutions + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMAX")}}}; + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("R3x3 data") + { + // Matrix and their transformations are kept simple to + // derive exact solutions + + using R3x3 = TinyMatrix<3>; + auto R2x2_exact = [&](const R3& X) -> R3x3 { + return T * TinyMatrix<3>{p0(X), 0, 0, // + 0, p1(X), p2(X), // + 0, p3(X), p4(X)} * + inv_T; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3x3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("vector of R3x3") + { + using R3x3 = TinyMatrix<3>; + + std::array<std::function<R3x3(const R3&)>, 2> vector_R3x3_exact // + = {[&](const R3& X) { + return T * R3x3{p0(X), 0, 0, // + 0, p1(X), p2(X), // + 0, p3(X), p4(X)} * + inv_T; + }, + [&](const R3& X) { + return T * R3x3{p0(X), 0, 0, // + 0, p2(X), p4(X), // + 0, p3(X), p1(X)} * + inv_T; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_R3x3_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const R3x3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_R3x3_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + SECTION("list") + { + using R3x3 = TinyMatrix<3>; + + auto R_exact = p0; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + std::array<std::function<double(const R3&)>, 4> vector_exact // + = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + std::array<std::function<R3x3(const R3&)>, 2> vector_R3x3_exact // + = {[&](const R3& X) { + return T * R3x3{p1(X), 0, 0, // + 0, p3(X), p0(X), // + 0, p2(X), p4(X)} * + inv_T; + }, + [&](const R3& X) { + return T * R3x3{p2(X), 0, 0, // + 0, p0(X), p1(X), // + 0, p3(X), p4(X)} * + inv_T; + }}; + + DiscreteFunctionP0Vector Wh = test_only::exact_projection(mesh, degree, vector_R3x3_exact); + + auto R3x3_exact = [&](const R3& X) -> R3x3 { + return T * R3x3{p0(X), 0, 0, // + 0, p1(X), p2(X), // + 0, p3(X), p4(X)} * + inv_T; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh, Vh, Wh, Ah); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + auto dpk_Vh = reconstructions[1]->get<DiscreteFunctionDPkVector<3, const double>>(); + auto dpk_Wh = reconstructions[2]->get<DiscreteFunctionDPkVector<3, const R3x3>>(); + auto dpk_Ah = reconstructions[3]->get<DiscreteFunctionDPk<3, const R3x3>>(); + + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_Wh, vector_R3x3_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + { + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + } + } } } -- GitLab From 8c69f47fd21aa7cb2a8ef589b96b85c65a1c3175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 14 Apr 2025 19:33:59 +0200 Subject: [PATCH 101/122] WIP tests for polynomial reconstruction of degree 3 [ci-skip] Seems buggy in 3d for degree 3+ --- src/scheme/PolynomialReconstruction.cpp | 16 + tests/CMakeLists.txt | 1 + ...test_PolynomialReconstruction_degree_3.cpp | 1230 +++++++++++++++++ 3 files changed, 1247 insertions(+) create mode 100644 tests/test_PolynomialReconstruction_degree_3.cpp diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 77fed2565..386d741d6 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -1384,6 +1384,22 @@ PolynomialReconstruction::_build( } } else { Givens::solveCollectionInPlace(A, X, B); +#warning REMOVE + if (cell_j_id == 0) { + for (size_t l = 0; l < B.numberOfRows(); ++l) { + for (size_t i = 0; i < B.numberOfColumns(); ++i) { + std::clog << "B(" << l << ", " << i << ") =" << B(l, i) << '\n'; + ; + } + } + + for (size_t l = 0; l < X.numberOfRows(); ++l) { + for (size_t i = 0; i < X.numberOfColumns(); ++i) { + std::clog << "X(" << l << ", " << i << ") =" << X(l, i) << '\n'; + ; + } + } + } } column_begin = 0; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e3739abe7..fd8915b83 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -281,6 +281,7 @@ add_executable (mpi_unit_tests test_Partitioner.cpp test_PolynomialReconstruction_degree_1.cpp test_PolynomialReconstruction_degree_2.cpp + test_PolynomialReconstruction_degree_3.cpp test_PolynomialReconstructionDescriptor.cpp test_RandomEngine.cpp test_StencilBuilder_cell2cell.cpp diff --git a/tests/test_PolynomialReconstruction_degree_3.cpp b/tests/test_PolynomialReconstruction_degree_3.cpp new file mode 100644 index 000000000..d04ac14b7 --- /dev/null +++ b/tests/test_PolynomialReconstruction_degree_3.cpp @@ -0,0 +1,1230 @@ +#include <catch2/catch_approx.hpp> +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <utils/PugsAssert.hpp> + +#include <mesh/Mesh.hpp> +#include <mesh/NamedBoundaryDescriptor.hpp> +#include <scheme/DiscreteFunctionDPkVariant.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/PolynomialReconstruction.hpp> + +#include <mesh/CartesianMeshBuilder.hpp> + +#include <DiscreteFunctionDPkForTests.hpp> +#include <MeshDataBaseForTests.hpp> + +#include <NbGhostLayersTester.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("PolynomialReconstruction_degree_3", "[scheme]") +{ + constexpr size_t degree = 3; + + constexpr size_t nb_ghost_layers = 3; + NbGhostLayersTester t{nb_ghost_layers}; + + SECTION("without symmetries") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree}}; + + for (auto descriptor : descriptor_list) { +#warning remove + descriptor.setPreconditioning(false); + descriptor.setRowWeighting(false); + + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("1D") + { + using R1 = TinyVector<1>; + + auto p0 = [](const R1& X) { + const double x = X[0]; + return +2.3 + (1.4 + (1.7 - 2.3 * x) * x) * x; + }; + auto p1 = [](const R1& X) { + const double x = X[0]; + return -1.2 - (2.3 - (1.1 + 2.1 * x) * x) * x; + }; + auto p2 = [](const R1& X) { + const double x = X[0]; + return +2.1 + (2.1 + (3.1 - 1.7 * x) * x) * x; + }; + auto p3 = [](const R1& X) { + const double x = X[0]; + return -1.7 + (1.4 + (1.6 - 3.1 * x) * x) * x; + }; + auto p4 = [](const R1& X) { + const double x = X[0]; + return +1.1 - (1.3 - (2.1 + 1.5 * x) * x) * x; + }; + auto p5 = [](const R1& X) { + const double x = X[0]; + return +1.9 - (2.1 + (1.6 - 2.7 * x) * x) * x; + }; + auto p6 = [](const R1& X) { + const double x = X[0]; + return -0.7 + (1.4 + (2.1 + 1.1 * x) * x) * x; + }; + auto p7 = [](const R1& X) { + const double x = X[0]; + return -1.4 - (1.2 + (1.5 - 2.1 * x) * x) * x; + }; + auto p8 = [](const R1& X) { + const double x = X[0]; + return -2.1 + (1.1 - (1.7 + 1.2 * x) * x) * x; + }; + auto p9 = [](const R1& X) { + const double x = X[0]; + return +1.8 - (3.1 + (2.1 - 2.4 * x) * x) * x; + }; + + 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 R_exact = p0; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("R^3 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 R3_exact = [&](const R1& x) -> R3 { return R3{p2(x), p4(x), p1(x)}; }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("R^3x3 data") + { + using R3x3 = TinyMatrix<3, 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 R3x3_exact = [&](const R1& x) -> R3x3 { + return R3x3{p1(x), p2(x), p3(x), // + p4(x), p5(x), p6(x), // + p7(x), p8(x), p9(x)}; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("R vector 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; + + std::array<std::function<double(const R1&)>, 3> vector_exact = {p1, p7, p9}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("R3 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; + + std::array<std::function<R3(const R1&)>, 3> vector_exact // + = {[&](const R1& x) -> R3 { + return R3{p1(x), p2(x), p3(x)}; + }, + [&](const R1& x) -> R3 { + return R3{p5(x), p7(x), p0(x)}; + }, + [&](const R1& x) -> R3 { + return R3{p9(x), p8(x), p4(x)}; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("list of various types") + { + using R3x3 = TinyMatrix<3>; + 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 R_exact = p0; + + auto R3_exact = [&](const R1& x) -> R3 { return R3{p9(x), p4(x), p7(x)}; }; + + auto R3x3_exact = [&](const R1& x) -> R3x3 { + return R3x3{p2(x), p1(x), p0(x), // + p3(x), p2(x), p4(x), // + p6(x), p5(x), p9(x)}; + }; + + std::array<std::function<double(const R1&)>, 3> vector_exact = {p1, p8, p7}; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = + PolynomialReconstruction{descriptor}.build(std::make_shared<DiscreteFunctionVariant>(fh), uh, + std::make_shared<DiscreteFunctionP0<R3x3>>(Ah), + DiscreteFunctionVariant(Vh)); + + { + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + { + auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + { + auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + + { + auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + } + + SECTION("2D") + { + using R2 = TinyVector<2>; + auto p0 = [](const R2& X) { + const double x = X[0]; + const double y = X[1]; + const double x2 = x * x; + const double xy = x * y; + const double y2 = y * y; + const double x3 = x2 * x; + const double x2y = x2 * y; + const double xy2 = x * y2; + const double y3 = y * y2; + + return +2.3 // + + 1.7 * x - 1.3 * y // + + 1.2 * x2 + 1.3 * xy - 3.2 * y2 // + - 1.3 * x3 + 2.1 * x2y - 1.6 * xy2 + 2.1 * y3; + }; + + auto p1 = [](const R2& X) { + const double x = X[0]; + const double y = X[1]; + const double x2 = x * x; + const double xy = x * y; + const double y2 = y * y; + const double x3 = x2 * x; + const double x2y = x2 * y; + const double xy2 = x * y2; + const double y3 = y * y2; + + return +1.4 // + + 1.6 * x - 2.1 * y // + + 1.3 * x2 + 2.6 * xy - 1.4 * y2 // + - 1.2 * x3 - 1.7 * x2y + 2.1 * xy2 - 2.2 * y3; + }; + + auto p2 = [](const R2& X) { + const double x = X[0]; + const double y = X[1]; + const double x2 = x * x; + const double xy = x * y; + const double y2 = y * y; + const double x3 = x2 * x; + const double x2y = x2 * y; + const double xy2 = x * y2; + const double y3 = y * y2; + + return -1.2 // + + 2.3 * x - 1.6 * y // + - 1.2 * x2 + 2.4 * xy - 1.9 * y2 // + + 0.9 * x3 + 1.5 * x2y - 2.3 * xy2 - 1.6 * y3; + }; + + auto p3 = [](const R2& X) { + const double x = X[0]; + const double y = X[1]; + const double x2 = x * x; + const double xy = x * y; + const double y2 = y * y; + const double x3 = x2 * x; + const double x2y = x2 * y; + const double xy2 = x * y2; + const double y3 = y * y2; + + return +2.4 // + + 2.5 * x + 1.4 * y // + - 2.7 * x2 + 1.9 * xy - 2.2 * y2 // + - 1.3 * x3 + 2.3 * x2y - 1.4 * xy2 + 2.2 * y3; + }; + + 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 R_exact = p0; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree + 1, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("R^3 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 R3_exact = [&](const R2& x) -> R3 { return R3{p1(x), p2(x), p3(x)}; }; + + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree + 1, std::function(R3_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("R^2x2 data") + { + using R2x2 = TinyMatrix<2, 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 R2x2_exact = [&](const R2& x) -> R2x2 { + return R2x2{p0(x), p1(x), // + p2(x), p3(x)}; + }; + + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree + 1, std::function(R2x2_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + SECTION("vector 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; + + std::array<std::function<double(const R2&)>, 4> vector_exact = {p0, p1, p2, p3}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree + 1, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + + SECTION("3D") + { + using R3 = TinyVector<3>; + + auto p0 = [](const R3& X) -> double { + const double x = X[0]; + const double y = X[1]; + const double z = X[2]; + const double xy = x * y; + const double xz = x * z; + const double yz = y * z; + const double x2 = x * x; + const double y2 = y * y; + const double z2 = z * z; + const double xyz = x * y * z; + const double x2y = x2 * y; + const double x2z = x2 * z; + const double xy2 = x * y2; + const double y2z = y2 * z; + const double xz2 = x * z2; + const double yz2 = y * z2; + const double x3 = x2 * x; + const double y3 = y2 * y; + const double z3 = z2 * z; + + return (y - 0.5) * (z - 0.5) * (z - 0.5); + + // return 2.3 + 1.7 * x - 1.3 * y + 2.1 * z // + // + 1.7 * x2 + 1.4 * y2 + 1.7 * z2 // + // - 2.3 * xy + 1.6 * xz - 1.9 * yz // + // + 1.2 * x3 - 2.1 * y3 - 1.1 * z3 // + // - 1.7 * x2y - 1.3 * x2z + 0.9 * xy2 - 0.7 * y2z + 1.5 * xz2 //- 0.7 * yz2 + // + 2.8 * xyz // + // ; + }; + + // auto p1 = [](const R3& x) { + // return +2.3 + 1.7 * x[0] - 2.2 * x[1] + 1.8 * x[2] // + // + 1.7 * x[0] * x[0] - 2.4 * x[1] * x[1] - 2.3 * x[2] * x[2] // + // - 2.1 * x[0] * x[1] + 2.6 * x[0] * x[2] + 1.6 * x[1] * x[2]; + // }; + + // auto p2 = [](const R3& x) { + // return +1.4 - 0.6 * x[0] + 1.3 * x[1] - 3.7 * x[2] // + // + 3.1 * x[0] * x[0] - 1.1 * x[1] * x[1] + 1.7 * x[2] * x[2] // + // - 2.3 * x[0] * x[1] - 2.6 * x[0] * x[2] - 1.9 * x[1] * x[2]; + // }; + + // auto p3 = [](const R3& x) { + // return -0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2] // + // - 1.5 * x[0] * x[0] + 1.4 * x[1] * x[1] - 1.2 * x[2] * x[2] // + // - 1.7 * x[0] * x[1] - 1.3 * x[0] * x[2] + 2.1 * x[1] * x[2]; + // }; + + SECTION("R data") + { + for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { + SECTION(named_mesh.name()) + { + auto p_initial_mesh = + CartesianMeshBuilder{TinyVector<3>{0, 0, 0}, TinyVector<3>{4, 4, 4}, TinyVector<3, size_t>{4, 4, 4}} + .mesh() + ->get<Mesh<3>>(); // named_mesh.mesh()->get<Mesh<3>>(); + auto& initial_mesh = *p_initial_mesh; + + constexpr double theta = 0; + TinyMatrix<3> T0{std::cos(theta), -std::sin(theta), 0, + // + std::sin(theta), std::cos(theta), 0, + // + 0, 0, 1}; + constexpr double phi = 0; + TinyMatrix<3> T1{std::cos(phi), 0, -std::sin(phi), + // + 0, 1, 0, + // + std::sin(phi), 0, std::cos(phi)}; + + TinyMatrix T = T0 * T1; + + auto xr = initial_mesh.xr(); + + NodeValue<R3> new_xr{initial_mesh.connectivity()}; + parallel_for( + initial_mesh.numberOfNodes(), + PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = T * xr[node_id]; }); + + std::shared_ptr p_mesh = std::make_shared<const Mesh<3>>(initial_mesh.shared_connectivity(), new_xr); + const Mesh<3>& mesh = *p_mesh; + // inverse rotation + TinyMatrix<3> inv_T{std::cos(theta), std::sin(theta), 0, + // + -std::sin(theta), std::cos(theta), 0, + // + 0, 0, 1}; + + auto R_exact = p0; + + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree + 3, std::function(R_exact)); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + + { + // PolynomialReconstructionDescriptor descriptor2{IntegrationMethodType::element, 2}; + // DiscreteFunctionP0 f2h = test_only::exact_projection(mesh, 2, std::function(R_exact)); + // auto reconstructions2 = PolynomialReconstruction{descriptor2}.build(fh); + // auto dpk_fh2 = reconstructions2[0]->get<DiscreteFunctionDPk<3, const double>>(); + // double max_error = test_only::max_reconstruction_error(mesh, dpk_fh2, + // std::function(R_exact)); REQUIRE(parallel::allReduceMax(max_error) == + // Catch::Approx(0).margin(1E-12)); + + auto cell_type = mesh.connectivity().cellType(); + auto cell_number = mesh.connectivity().cellNumber(); + + for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { + std::clog << cell_id << "(" << cell_number[cell_id] << ")" + << ":---- " << name(cell_type[cell_id]) << "\n2| "; + // { + // auto coeffs = dpk_fh2.coefficients(cell_id); + // for (size_t i = 0; i < coeffs.size(); ++i) { + // if (std::abs(coeffs[i]) > 1e-12) { + // std::clog << ' ' << i << ':' << coeffs[i]; + // } + // } + // } + std::clog << '\n'; + std::clog << "3| "; + { + auto coeffs = dpk_fh.coefficients(cell_id); + for (size_t i = 0; i < coeffs.size(); ++i) { + if (std::abs(coeffs[i]) > 1e-12) { + std::clog << ' ' << i << ':' << coeffs[i]; + } + } + } + std::clog << '\n'; + break; + } + } + + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + + // SECTION("R^3 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 R3_exact = [&](const R3& x) -> R3 { return R3{p1(x), p2(x), p3(x)}; }; + + // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + + // SECTION("R^2x2 data") + // { + // using R2x2 = TinyMatrix<2, 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 R2x2_exact = [&](const R3& x) -> R2x2 { + // return R2x2{p0(x), p1(x), // + // p2(x), p3(x)}; + // }; + + // DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + // descriptor.setRowWeighting(false); + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + // auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + + // SECTION("vector 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; + + // std::array<std::function<double(const R3&)>, 4> vector_exact = {p0, p1, p2, p3}; + + // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + // descriptor.setPreconditioning(false); + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + } + } + } + } + + // SECTION("with symmetries") + // { + // SECTION("1D") + // { + // std::vector<PolynomialReconstructionDescriptor> descriptor_list = + // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + // std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + // std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + // std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + // std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; + // using R1 = TinyVector<1>; + + // auto p0 = [](const R1& x) { return +1.7 * (x[0] + 1) * (x[0] + 1) - 1.1; }; + // auto p1 = [](const R1& x) { return -1.2 * (x[0] + 1) * (x[0] + 1) + 1.3; }; + // auto p2 = [](const R1& x) { return +1.4 * (x[0] + 1) * (x[0] + 1) - 0.6; }; + + // for (auto descriptor : descriptor_list) { + // SECTION(name(descriptor.integrationMethodType())) + // { + // SECTION("R data") + // { + // auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + // auto& mesh = *p_mesh; + + // auto R_exact = p0; + + // DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + // auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("R1x1 data") + // { + // using R1x1 = TinyMatrix<1>; + + // auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + + // auto& mesh = *p_mesh; + + // auto R1x1_exact = [&](const R1& x) { return R1x1{p0(x)}; }; + + // DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R1x1_exact)); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + + // auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R1x1>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R1x1_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("R vector data") + // { + // auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + // auto& mesh = *p_mesh; + + // std::array<std::function<double(const R1&)>, 3> vector_exact // + // = {p0, p1, p2}; + + // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("R1x1 vector data") + // { + // using R1x1 = TinyMatrix<1>; + + // auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + // auto& mesh = *p_mesh; + + // std::array<std::function<R1x1(const R1&)>, 3> vector_exact // + // = {[&](const R1& x) { return R1x1{p2(x)}; }, // + // [&](const R1& x) { return R1x1{p0(x)}; }, // + // [&](const R1& x) { return R1x1{p1(x)}; }}; + + // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R1x1>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + + // SECTION("2D") + // { + // std::vector<PolynomialReconstructionDescriptor> descriptor_list = + // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + // std::vector<std::shared_ptr< + // const IBoundaryDescriptor>>{std:: + // make_shared<NamedBoundaryDescriptor>( + // "XMAX"), + // std::make_shared<NamedBoundaryDescriptor>( + // "YMAX")}}, + // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + // std::vector<std::shared_ptr< + // const IBoundaryDescriptor>>{std:: + // make_shared<NamedBoundaryDescriptor>( + // "XMAX"), + // std::make_shared<NamedBoundaryDescriptor>( + // "YMAX")}}}; + + // using R2 = TinyVector<2>; + + // auto p_initial_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); + // auto& initial_mesh = *p_initial_mesh; + + // constexpr double theta = 1; + // TinyMatrix<2> T{std::cos(theta), -std::sin(theta), // + // std::sin(theta), std::cos(theta)}; + + // auto xr = initial_mesh.xr(); + + // NodeValue<R2> new_xr{initial_mesh.connectivity()}; + // parallel_for( + // initial_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = T * xr[node_id]; }); + + // std::shared_ptr p_mesh = std::make_shared<const Mesh<2>>(initial_mesh.shared_connectivity(), new_xr); + // const Mesh<2>& mesh = *p_mesh; + // // inverse rotation + // TinyMatrix<2> inv_T{std::cos(theta), std::sin(theta), // + // -std::sin(theta), std::cos(theta)}; + + // auto p0 = [&inv_T](const R2& X) { + // const R2 Y = inv_T * X; + // const double x = Y[0] - 2; + // const double y = Y[1] - 1; + // return +1.7 * x * x + 2 * y * y - 1.1; + // }; + + // auto p1 = [&inv_T](const R2& X) { + // const R2 Y = inv_T * X; + // const double x = Y[0] - 2; + // const double y = Y[1] - 1; + // return -1.3 * x * x - 0.2 * y * y + 0.7; + // }; + + // auto p2 = [&inv_T](const R2& X) { + // const R2 Y = inv_T * X; + // const double x = Y[0] - 2; + // const double y = Y[1] - 1; + // return +2.6 * x * x - 1.4 * y * y - 1.9; + // }; + + // auto p3 = [&inv_T](const R2& X) { + // const R2 Y = inv_T * X; + // const double x = Y[0] - 2; + // const double y = Y[1] - 1; + // return -0.6 * x * x + 1.4 * y * y + 2.3; + // }; + + // auto q0 = [&inv_T](const R2& X) { + // const R2 Y = inv_T * X; + // const double x = Y[0] - 2; + // const double y = Y[1] - 1; + // return 3 * x * y; + // }; + + // auto q1 = [&inv_T](const R2& X) { + // const R2 Y = inv_T * X; + // const double x = Y[0] - 2; + // const double y = Y[1] - 1; + // return -1.3 * x * y; + // }; + + // for (auto descriptor : descriptor_list) { + // SECTION(name(descriptor.integrationMethodType())) + // { + // SECTION("R data") + // { + // auto R_exact = p0; + + // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("R2x2 data") + // { + // using R2x2 = TinyMatrix<2>; + // auto R2x2_exact = [&](const R2& X) -> R2x2 { + // return T * TinyMatrix<2>{p0(X), q0(X), q1(X), p1(X)} * inv_T; + // }; + + // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R2x2_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("vector of R") + // { + // std::array<std::function<double(const R2&)>, 4> vector_exact // + // = {p0, p1, p2, p3}; + + // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("vector of R2x2") + // { + // using R2x2 = TinyMatrix<2>; + + // std::array<std::function<R2x2(const R2&)>, 2> vector_R2x2_exact // + // = {[&](const R2& X) { + // return T * R2x2{p0(X), q0(X), q1(X), p1(X)} * inv_T; + // }, + // [&](const R2& X) { + // return T * R2x2{p0(X), q1(X), 0, p1(X)} * inv_T; + // }}; + + // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_R2x2_exact); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const R2x2>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_R2x2_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("list") + // { + // using R2x2 = TinyMatrix<2>; + + // auto R_exact = p0; + + // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + // std::array<std::function<double(const R2&)>, 4> vector_exact // + // = {p0, p1, p2, p3}; + + // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + // std::array<std::function<R2x2(const R2&)>, 2> vector_R2x2_exact // + // = {[&](const R2& X) { + // return T * R2x2{p0(X), q0(X), q1(X), p1(X)} * inv_T; + // }, + // [&](const R2& X) { + // return T * R2x2{p2(X), q1(X), 0, p3(X)} * inv_T; + // }}; + + // DiscreteFunctionP0Vector Wh = test_only::exact_projection(mesh, degree, vector_R2x2_exact); + + // auto R2x2_exact = [&](const R2& X) -> R2x2 { + // return T * TinyMatrix<2>{p0(X), q1(X), q0(X), p3(X)} * inv_T; + // }; + + // DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh, Vh, Wh, Ah); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); + // auto dpk_Vh = reconstructions[1]->get<DiscreteFunctionDPkVector<2, const double>>(); + // auto dpk_Wh = reconstructions[2]->get<DiscreteFunctionDPkVector<2, const R2x2>>(); + // auto dpk_Ah = reconstructions[3]->get<DiscreteFunctionDPk<2, const R2x2>>(); + + // { + // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // { + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // { + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Wh, vector_R2x2_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // { + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + // } + + // SECTION("3D") + // { + // using R3 = TinyVector<3>; + + // auto p_initial_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); + // auto& initial_mesh = *p_initial_mesh; + + // constexpr double theta = 1; + // TinyMatrix<3> T{std::cos(theta), -std::sin(theta), 0, + // // + // std::sin(theta), std::cos(theta), 0, + // // + // 0, 0, 1}; + + // auto xr = initial_mesh.xr(); + + // NodeValue<R3> new_xr{initial_mesh.connectivity()}; + // parallel_for( + // initial_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = T * xr[node_id]; }); + + // std::shared_ptr p_mesh = std::make_shared<const Mesh<3>>(initial_mesh.shared_connectivity(), new_xr); + // const Mesh<3>& mesh = *p_mesh; + // // inverse rotation + // TinyMatrix<3> inv_T{std::cos(theta), std::sin(theta), 0, + // // + // -std::sin(theta), std::cos(theta), 0, + // // + // 0, 0, 1}; + + // auto p0 = [&inv_T](const R3& X) { + // const R3 Y = inv_T * X; + // const double x = Y[0] - 2; + // const double y = Y[1] - 1; + // const double z = Y[2] - 1; + // return +1.7 * x * x + 2 * y * y + 1.3 * z * z - 1.1; + // }; + + // auto p1 = [&inv_T](const R3& X) { + // const R3 Y = inv_T * X; + // const double x = Y[0] - 2; + // const double y = Y[1] - 1; + // const double z = Y[2] - 1; + // return +2.1 * x * x - 1.4 * y * y - 3.1 * z * z + 2.2; + // }; + + // auto p2 = [&inv_T](const R3& X) { + // const R3 Y = inv_T * X; + // const double x = Y[0] - 2; + // const double y = Y[1] - 1; + // const double z = Y[2] - 1; + // return -1.1 * x * x - 1.2 * y * y + 1.3 * z * z - 1.7; + // }; + + // auto p3 = [&inv_T](const R3& X) { + // const R3 Y = inv_T * X; + // const double x = Y[0] - 2; + // const double y = Y[1] - 1; + // const double z = Y[2] - 1; + // return 1.9 * x * x + 2.1 * y * y - 3.1 * z * z + 1.6; + // }; + + // auto p4 = [&inv_T](const R3& X) { + // const R3 Y = inv_T * X; + // const double x = Y[0] - 2; + // const double y = Y[1] - 1; + // const double z = Y[2] - 1; + // return -2.4 * x * x + 3.3 * y * y - 1.7 * z * z + 2.1; + // }; + + // SECTION("3 symmetries") + // { + // std::vector<PolynomialReconstructionDescriptor> descriptor_list = + // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + // std::vector<std::shared_ptr< + // const IBoundaryDescriptor>>{std:: + // make_shared<NamedBoundaryDescriptor>( + // "XMAX"), + // std::make_shared<NamedBoundaryDescriptor>( + // "YMAX"), + // std::make_shared<NamedBoundaryDescriptor>( + // "ZMAX")}}, + // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + // std::vector<std::shared_ptr< + // const IBoundaryDescriptor>>{std:: + // make_shared<NamedBoundaryDescriptor>( + // "XMAX"), + // std::make_shared<NamedBoundaryDescriptor>( + // "YMAX"), + // std::make_shared<NamedBoundaryDescriptor>( + // "ZMAX")}}}; + + // for (auto descriptor : descriptor_list) { + // SECTION("R data") + // { + // auto R_exact = p0; + + // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("vector of R") + // { + // std::array<std::function<double(const R3&)>, 4> vector_exact // + // = {p0, p1, p2, p3}; + + // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + + // SECTION("1 symmetry") + // { + // // Matrix and their transformations are kept simple to + // // derive exact solutions + // std::vector<PolynomialReconstructionDescriptor> descriptor_list = + // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + // std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + // std::make_shared<NamedBoundaryDescriptor>("XMAX")}}, + // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + // std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + // std::make_shared<NamedBoundaryDescriptor>("XMAX")}}}; + // for (auto descriptor : descriptor_list) { + // SECTION(name(descriptor.integrationMethodType())) + // { + // SECTION("R3x3 data") + // { + // // Matrix and their transformations are kept simple to + // // derive exact solutions + + // using R3x3 = TinyMatrix<3>; + // auto R2x2_exact = [&](const R3& X) -> R3x3 { + // return T * TinyMatrix<3>{p0(X), 0, 0, // + // 0, p1(X), p2(X), // + // 0, p3(X), p4(X)} * + // inv_T; + // }; + + // DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + + // auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3x3>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("vector of R3x3") + // { + // using R3x3 = TinyMatrix<3>; + + // std::array<std::function<R3x3(const R3&)>, 2> vector_R3x3_exact // + // = {[&](const R3& X) { + // return T * R3x3{p0(X), 0, 0, // + // 0, p1(X), p2(X), // + // 0, p3(X), p4(X)} * + // inv_T; + // }, + // [&](const R3& X) { + // return T * R3x3{p0(X), 0, 0, // + // 0, p2(X), p4(X), // + // 0, p3(X), p1(X)} * + // inv_T; + // }}; + + // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_R3x3_exact); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const R3x3>>(); + + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_R3x3_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + + // SECTION("list") + // { + // using R3x3 = TinyMatrix<3>; + + // auto R_exact = p0; + + // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + + // std::array<std::function<double(const R3&)>, 4> vector_exact // + // = {p0, p1, p2, p3}; + + // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + // std::array<std::function<R3x3(const R3&)>, 2> vector_R3x3_exact // + // = {[&](const R3& X) { + // return T * R3x3{p1(X), 0, 0, // + // 0, p3(X), p0(X), // + // 0, p2(X), p4(X)} * + // inv_T; + // }, + // [&](const R3& X) { + // return T * R3x3{p2(X), 0, 0, // + // 0, p0(X), p1(X), // + // 0, p3(X), p4(X)} * + // inv_T; + // }}; + + // DiscreteFunctionP0Vector Wh = test_only::exact_projection(mesh, degree, vector_R3x3_exact); + + // auto R3x3_exact = [&](const R3& X) -> R3x3 { + // return T * R3x3{p0(X), 0, 0, // + // 0, p1(X), p2(X), // + // 0, p3(X), p4(X)} * + // inv_T; + // }; + + // DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); + + // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh, Vh, Wh, Ah); + + // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + // auto dpk_Vh = reconstructions[1]->get<DiscreteFunctionDPkVector<3, const double>>(); + // auto dpk_Wh = reconstructions[2]->get<DiscreteFunctionDPkVector<3, const R3x3>>(); + // auto dpk_Ah = reconstructions[3]->get<DiscreteFunctionDPk<3, const R3x3>>(); + + // { + // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // { + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // { + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Wh, vector_R3x3_exact); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // { + // double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); + // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + // } + // } + // } + // } + // } + // } + // } +} -- GitLab From 65ecdcce99799d89851abf8ec7bd1565112403a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Wed, 16 Apr 2025 19:13:26 +0200 Subject: [PATCH 102/122] WIP reconstruction for polynomial of degree 3+ in 3D [ci-skip] --- src/scheme/PolynomialReconstruction.cpp | 111 +++++++++++++++--- ...test_PolynomialReconstruction_degree_3.cpp | 10 +- 2 files changed, 97 insertions(+), 24 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 386d741d6..a6e35649e 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -291,29 +291,68 @@ _computeEjkMean(const QuadratureFormula<3>& quadrature, const double y_yj = X_Xj[1]; const double z_zj = X_Xj[2]; + size_t n = degree; + std::clog << "DEGREE = " << degree << '\n'; + Array<size_t> yz_begining((n + 2) * (n + 1) / 2 + 1); + Array<size_t> z_index(n + 1); + + { + size_t i_z = 0; + size_t i_yz = 0; + + yz_begining[i_yz++] = 0; + for (ssize_t m = n + 1; m >= 1; --m) { + z_index[i_z++] = i_yz - 1; + for (ssize_t i = m; i >= 1; --i) { + yz_begining[i_yz] = yz_begining[i_yz - 1] + i; + ++i_yz; + } + } + } + + Array<size_t> yz_nb_monomes{yz_begining.size() - 1}; + for (size_t i = 0; i < yz_nb_monomes.size(); ++i) { + yz_nb_monomes[i] = yz_begining[i + 1] - yz_begining[i]; + } + { size_t k = 0; inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; for (; k <= degree; ++k) { inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; + std::clog << "[" << k << "] <- x*[" << k - 1 << "]\n"; } for (size_t i_y = 1; i_y <= degree; ++i_y) { - const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; - for (size_t l = 0; l <= degree - i_y; ++l) { + const size_t begin_i_y_1 = yz_begining[i_y - 1]; + std::clog << "COMP 1: " << degree - i_y << " vs " << yz_nb_monomes[i_y] - 1 << '\n'; + for (size_t l = 0; l < yz_nb_monomes[i_y]; ++l) { + std::clog << "[" << k << "] <- y*[" << begin_i_y_1 + l << "]\n"; inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; } } for (size_t i_z = 1; i_z <= degree; ++i_z) { - const size_t begin_i_z_1 = ((i_z - 1) * (3 * (degree + 2) * (degree + 3) + (i_z - (3 * degree + 8)) * i_z)) / 6; + const size_t index_z = z_index[i_z]; + const size_t index_z_1 = z_index[i_z - 1]; + std::clog << "index_z_1 = " << index_z_1 << '\n'; + std::clog << "COMP 2: " << degree - i_z << " vs " << yz_nb_monomes[index_z] - 1 << '\n'; for (size_t i_y = 0; i_y <= degree - i_z; ++i_y) { - const size_t begin_i_y_1 = (i_y * (2 * degree - i_y + 3)) / 2 + begin_i_z_1; + const size_t begin_i_yz_1 = yz_begining[index_z_1 + i_y]; + + std::clog << "COMP 3: " << degree - i_y - i_z << " vs " << yz_nb_monomes[index_z + i_y] - 1 << '\n'; for (size_t l = 0; l <= degree - i_y - i_z; ++l) { - inv_Vj_wq_detJ_ek[k++] = z_zj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; + std::clog << "[" << k << "] <- z*[" << begin_i_yz_1 + l << "]\n"; + inv_Vj_wq_detJ_ek[k++] = z_zj * inv_Vj_wq_detJ_ek[begin_i_yz_1 + l]; } } } + + std::clog << "z_begining = " << z_index << '\n'; + std::clog << "yz_begining = " << yz_begining << '\n'; + std::clog << "yz_nb_monomes = " << yz_nb_monomes << '\n'; + + std::exit(0); } for (size_t k = 1; k < dimension; ++k) { @@ -1277,6 +1316,29 @@ PolynomialReconstruction::_build( _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_j_id], cell_type[cell_j_id], Vj[cell_j_id], m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_j_of_ejk); + auto exact_xz2 = [](const Rd& X) { + const double x0 = X[0] - 0.5; + const double y0 = X[1] - 0.5; + const double z0 = X[2] - 0.5; + const double x1 = X[0] + 0.5; + const double y1 = X[1] + 0.5; + const double z1 = X[2] + 0.5; + return (0.5 * x1 * x1 - 0.5 * x0 * x0) * (1. / 3 * z1 * z1 * z1 - 1. / 3 * z0 * z0 * z0); + }; + auto exact_yz2 = [](const Rd& X) { + const double x0 = X[0] - 0.5; + const double y0 = X[1] - 0.5; + const double z0 = X[2] - 0.5; + const double x1 = X[0] + 0.5; + const double y1 = X[1] + 0.5; + const double z1 = X[2] + 0.5; + return (0.5 * y1 * y1 - 0.5 * y0 * y0) * (1. / 3 * z1 * z1 * z1 - 1. / 3 * z0 * z0 * z0); + }; + if (cell_j_id == 0) { + std::clog << "- mean_j_of_ejk[xz2] = " << mean_j_of_ejk[16] << " vs " << exact_xz2(Xj) << '\n'; + std::clog << "- mean_j_of_ejk[yz2] = " << mean_j_of_ejk[17] << " vs " << exact_yz2(Xj) << '\n'; + } + size_t index = 0; for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { const CellId cell_i_id = stencil_cell_list[i]; @@ -1284,6 +1346,17 @@ PolynomialReconstruction::_build( _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], Vj[cell_i_id], m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_i_of_ejk); + const Rd& Xi = xj[cell_i_id]; + + if (cell_j_id == 0) { + std::clog << "- mean_i_of_ejk[" << rang::fgB::cyan << "xz2" << rang::fg::reset + << "] = " << mean_i_of_ejk[16] << " vs " << exact_xz2(Xi) + << "\tdelta=" << std::abs(mean_i_of_ejk[16] - exact_xz2(Xi)) << '\n'; + std::clog << "- mean_i_of_ejk[" << rang::fgB::yellow << "yz2" << rang::fg::reset + << "] = " << mean_i_of_ejk[17] << " vs " << exact_yz2(Xi) + << "\tdelta=" << std::abs(mean_i_of_ejk[17] - exact_yz2(Xi)) << '\n'; + } + for (size_t l = 0; l < basis_dimension - 1; ++l) { A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; } @@ -1383,23 +1456,23 @@ PolynomialReconstruction::_build( } } } else { - Givens::solveCollectionInPlace(A, X, B); #warning REMOVE if (cell_j_id == 0) { - for (size_t l = 0; l < B.numberOfRows(); ++l) { - for (size_t i = 0; i < B.numberOfColumns(); ++i) { - std::clog << "B(" << l << ", " << i << ") =" << B(l, i) << '\n'; - ; - } - } - - for (size_t l = 0; l < X.numberOfRows(); ++l) { - for (size_t i = 0; i < X.numberOfColumns(); ++i) { - std::clog << "X(" << l << ", " << i << ") =" << X(l, i) << '\n'; - ; - } - } + // for (size_t l = 0; l < B.numberOfRows(); ++l) { + // for (size_t i = 0; i < B.numberOfColumns(); ++i) { + // std::clog << "B(" << l << ", " << i << ") =" << B(l, i) << '\n'; + // ; + // } + // } + + // for (size_t l = 0; l < X.numberOfRows(); ++l) { + // for (size_t i = 0; i < X.numberOfColumns(); ++i) { + // std::clog << "X(" << l << ", " << i << ") =" << X(l, i) << '\n'; + // ; + // } + // } } + Givens::solveCollectionInPlace(A, X, B); } column_begin = 0; diff --git a/tests/test_PolynomialReconstruction_degree_3.cpp b/tests/test_PolynomialReconstruction_degree_3.cpp index d04ac14b7..4f50a7e46 100644 --- a/tests/test_PolynomialReconstruction_degree_3.cpp +++ b/tests/test_PolynomialReconstruction_degree_3.cpp @@ -464,7 +464,7 @@ TEST_CASE("PolynomialReconstruction_degree_3", "[scheme]") const double y3 = y2 * y; const double z3 = z2 * z; - return (y - 0.5) * (z - 0.5) * (z - 0.5); + return yz2; // return 2.3 + 1.7 * x - 1.3 * y + 2.1 * z // // + 1.7 * x2 + 1.4 * y2 + 1.7 * z2 // @@ -498,10 +498,10 @@ TEST_CASE("PolynomialReconstruction_degree_3", "[scheme]") for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { SECTION(named_mesh.name()) { - auto p_initial_mesh = - CartesianMeshBuilder{TinyVector<3>{0, 0, 0}, TinyVector<3>{4, 4, 4}, TinyVector<3, size_t>{4, 4, 4}} - .mesh() - ->get<Mesh<3>>(); // named_mesh.mesh()->get<Mesh<3>>(); + auto p_initial_mesh = CartesianMeshBuilder{TinyVector<3>{-0.5, -0.5, -0.5}, + TinyVector<3>{3.5, 3.5, 3.5}, TinyVector<3, size_t>{4, 4, 4}} + .mesh() + ->get<Mesh<3>>(); // named_mesh.mesh()->get<Mesh<3>>(); auto& initial_mesh = *p_initial_mesh; constexpr double theta = 0; -- GitLab From ce8df74e0c281cbb4ea64311b57793c0a1d107ef Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Wed, 16 Apr 2025 23:35:29 +0200 Subject: [PATCH 103/122] Fix polynomial reconstruction of degree 3+ in 3d Still requires a lot of design improvements and cleanup Must measure performances improvements --- src/scheme/PolynomialReconstruction.cpp | 130 +++++------------- ...test_PolynomialReconstruction_degree_3.cpp | 69 ++-------- 2 files changed, 48 insertions(+), 151 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index a6e35649e..b01d11a79 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -247,6 +247,7 @@ _computeEjkMean(const QuadratureFormula<2>& quadrature, } for (size_t i_y = 1; i_y <= degree; ++i_y) { +#warning store y_row index and size const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; for (size_t l = 0; l <= degree - i_y; ++l) { inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; @@ -271,6 +272,29 @@ _computeEjkMean(const QuadratureFormula<3>& quadrature, SmallArray<double>& inv_Vj_wq_detJ_ek, SmallArray<double>& mean_of_ejk) { +#warning Compute this one for all and rework design + SmallArray<size_t> m_yz_row_index((degree + 2) * (degree + 1) / 2 + 1); + SmallArray<size_t> m_z_triangle_index(degree + 1); + + { + size_t i_z = 0; + size_t i_yz = 0; + + m_yz_row_index[i_yz++] = 0; + for (ssize_t n = degree + 1; n >= 1; --n) { + m_z_triangle_index[i_z++] = i_yz - 1; + for (ssize_t i = n; i >= 1; --i) { + m_yz_row_index[i_yz] = m_yz_row_index[i_yz - 1] + i; + ++i_yz; + } + } + } + + SmallArray<size_t> m_yz_row_size{m_yz_row_index.size() - 1}; + for (size_t i = 0; i < m_yz_row_size.size(); ++i) { + m_yz_row_size[i] = m_yz_row_index[i + 1] - m_yz_row_index[i]; + } + using Rd = TinyVector<3>; mean_of_ejk.fill(0); @@ -291,68 +315,33 @@ _computeEjkMean(const QuadratureFormula<3>& quadrature, const double y_yj = X_Xj[1]; const double z_zj = X_Xj[2]; - size_t n = degree; - std::clog << "DEGREE = " << degree << '\n'; - Array<size_t> yz_begining((n + 2) * (n + 1) / 2 + 1); - Array<size_t> z_index(n + 1); - - { - size_t i_z = 0; - size_t i_yz = 0; - - yz_begining[i_yz++] = 0; - for (ssize_t m = n + 1; m >= 1; --m) { - z_index[i_z++] = i_yz - 1; - for (ssize_t i = m; i >= 1; --i) { - yz_begining[i_yz] = yz_begining[i_yz - 1] + i; - ++i_yz; - } - } - } - - Array<size_t> yz_nb_monomes{yz_begining.size() - 1}; - for (size_t i = 0; i < yz_nb_monomes.size(); ++i) { - yz_nb_monomes[i] = yz_begining[i + 1] - yz_begining[i]; - } - { size_t k = 0; inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; for (; k <= degree; ++k) { inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; - std::clog << "[" << k << "] <- x*[" << k - 1 << "]\n"; } for (size_t i_y = 1; i_y <= degree; ++i_y) { - const size_t begin_i_y_1 = yz_begining[i_y - 1]; - std::clog << "COMP 1: " << degree - i_y << " vs " << yz_nb_monomes[i_y] - 1 << '\n'; - for (size_t l = 0; l < yz_nb_monomes[i_y]; ++l) { - std::clog << "[" << k << "] <- y*[" << begin_i_y_1 + l << "]\n"; + const size_t begin_i_y_1 = m_yz_row_index[i_y - 1]; + const size_t nb_monoms = m_yz_row_size[i_y]; + for (size_t l = 0; l < nb_monoms; ++l) { inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; } } for (size_t i_z = 1; i_z <= degree; ++i_z) { - const size_t index_z = z_index[i_z]; - const size_t index_z_1 = z_index[i_z - 1]; - std::clog << "index_z_1 = " << index_z_1 << '\n'; - std::clog << "COMP 2: " << degree - i_z << " vs " << yz_nb_monomes[index_z] - 1 << '\n'; - for (size_t i_y = 0; i_y <= degree - i_z; ++i_y) { - const size_t begin_i_yz_1 = yz_begining[index_z_1 + i_y]; - - std::clog << "COMP 3: " << degree - i_y - i_z << " vs " << yz_nb_monomes[index_z + i_y] - 1 << '\n'; - for (size_t l = 0; l <= degree - i_y - i_z; ++l) { - std::clog << "[" << k << "] <- z*[" << begin_i_yz_1 + l << "]\n"; + const size_t nb_y = m_yz_row_size[m_z_triangle_index[i_z]]; + const size_t index_z = m_z_triangle_index[i_z]; + const size_t index_z_1 = m_z_triangle_index[i_z - 1]; + for (size_t i_y = 0; i_y < nb_y; ++i_y) { + const size_t begin_i_yz_1 = m_yz_row_index[index_z_1 + i_y]; + const size_t nb_monoms = m_yz_row_size[index_z + i_y]; + for (size_t l = 0; l < nb_monoms; ++l) { inv_Vj_wq_detJ_ek[k++] = z_zj * inv_Vj_wq_detJ_ek[begin_i_yz_1 + l]; } } } - - std::clog << "z_begining = " << z_index << '\n'; - std::clog << "yz_begining = " << yz_begining << '\n'; - std::clog << "yz_nb_monomes = " << yz_nb_monomes << '\n'; - - std::exit(0); } for (size_t k = 1; k < dimension; ++k) { @@ -1316,29 +1305,6 @@ PolynomialReconstruction::_build( _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_j_id], cell_type[cell_j_id], Vj[cell_j_id], m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_j_of_ejk); - auto exact_xz2 = [](const Rd& X) { - const double x0 = X[0] - 0.5; - const double y0 = X[1] - 0.5; - const double z0 = X[2] - 0.5; - const double x1 = X[0] + 0.5; - const double y1 = X[1] + 0.5; - const double z1 = X[2] + 0.5; - return (0.5 * x1 * x1 - 0.5 * x0 * x0) * (1. / 3 * z1 * z1 * z1 - 1. / 3 * z0 * z0 * z0); - }; - auto exact_yz2 = [](const Rd& X) { - const double x0 = X[0] - 0.5; - const double y0 = X[1] - 0.5; - const double z0 = X[2] - 0.5; - const double x1 = X[0] + 0.5; - const double y1 = X[1] + 0.5; - const double z1 = X[2] + 0.5; - return (0.5 * y1 * y1 - 0.5 * y0 * y0) * (1. / 3 * z1 * z1 * z1 - 1. / 3 * z0 * z0 * z0); - }; - if (cell_j_id == 0) { - std::clog << "- mean_j_of_ejk[xz2] = " << mean_j_of_ejk[16] << " vs " << exact_xz2(Xj) << '\n'; - std::clog << "- mean_j_of_ejk[yz2] = " << mean_j_of_ejk[17] << " vs " << exact_yz2(Xj) << '\n'; - } - size_t index = 0; for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { const CellId cell_i_id = stencil_cell_list[i]; @@ -1346,21 +1312,11 @@ PolynomialReconstruction::_build( _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], Vj[cell_i_id], m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_i_of_ejk); - const Rd& Xi = xj[cell_i_id]; - - if (cell_j_id == 0) { - std::clog << "- mean_i_of_ejk[" << rang::fgB::cyan << "xz2" << rang::fg::reset - << "] = " << mean_i_of_ejk[16] << " vs " << exact_xz2(Xi) - << "\tdelta=" << std::abs(mean_i_of_ejk[16] - exact_xz2(Xi)) << '\n'; - std::clog << "- mean_i_of_ejk[" << rang::fgB::yellow << "yz2" << rang::fg::reset - << "] = " << mean_i_of_ejk[17] << " vs " << exact_yz2(Xi) - << "\tdelta=" << std::abs(mean_i_of_ejk[17] - exact_yz2(Xi)) << '\n'; - } - for (size_t l = 0; l < basis_dimension - 1; ++l) { A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; } } + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); @@ -1456,22 +1412,6 @@ PolynomialReconstruction::_build( } } } else { -#warning REMOVE - if (cell_j_id == 0) { - // for (size_t l = 0; l < B.numberOfRows(); ++l) { - // for (size_t i = 0; i < B.numberOfColumns(); ++i) { - // std::clog << "B(" << l << ", " << i << ") =" << B(l, i) << '\n'; - // ; - // } - // } - - // for (size_t l = 0; l < X.numberOfRows(); ++l) { - // for (size_t i = 0; i < X.numberOfColumns(); ++i) { - // std::clog << "X(" << l << ", " << i << ") =" << X(l, i) << '\n'; - // ; - // } - // } - } Givens::solveCollectionInPlace(A, X, B); } diff --git a/tests/test_PolynomialReconstruction_degree_3.cpp b/tests/test_PolynomialReconstruction_degree_3.cpp index 4f50a7e46..d8badda38 100644 --- a/tests/test_PolynomialReconstruction_degree_3.cpp +++ b/tests/test_PolynomialReconstruction_degree_3.cpp @@ -191,15 +191,9 @@ TEST_CASE("PolynomialReconstruction_degree_3", "[scheme]") auto& mesh = *p_mesh; std::array<std::function<R3(const R1&)>, 3> vector_exact // - = {[&](const R1& x) -> R3 { - return R3{p1(x), p2(x), p3(x)}; - }, - [&](const R1& x) -> R3 { - return R3{p5(x), p7(x), p0(x)}; - }, - [&](const R1& x) -> R3 { - return R3{p9(x), p8(x), p4(x)}; - }}; + = {[&](const R1& x) -> R3 { return R3{p1(x), p2(x), p3(x)}; }, + [&](const R1& x) -> R3 { return R3{p5(x), p7(x), p0(x)}; }, + [&](const R1& x) -> R3 { return R3{p9(x), p8(x), p4(x)}; }}; DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); @@ -464,15 +458,14 @@ TEST_CASE("PolynomialReconstruction_degree_3", "[scheme]") const double y3 = y2 * y; const double z3 = z2 * z; - return yz2; - - // return 2.3 + 1.7 * x - 1.3 * y + 2.1 * z // - // + 1.7 * x2 + 1.4 * y2 + 1.7 * z2 // - // - 2.3 * xy + 1.6 * xz - 1.9 * yz // - // + 1.2 * x3 - 2.1 * y3 - 1.1 * z3 // - // - 1.7 * x2y - 1.3 * x2z + 0.9 * xy2 - 0.7 * y2z + 1.5 * xz2 //- 0.7 * yz2 - // + 2.8 * xyz // - // ; + return 2.3 + 1.7 * x - 1.3 * y + 2.1 * z // + + 1.7 * x2 + 1.4 * y2 + 1.7 * z2 // + - 2.3 * xy + 1.6 * xz - 1.9 * yz // + + 1.2 * x3 - 2.1 * y3 - 1.1 * z3 // + - 1.7 * x2y - 1.3 * x2z + 0.9 * xy2 // + - 0.7 * y2z + 1.5 * xz2 - 0.7 * yz2 // + + 2.8 * xyz // + ; }; // auto p1 = [](const R3& x) { @@ -542,48 +535,12 @@ TEST_CASE("PolynomialReconstruction_degree_3", "[scheme]") auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); - { - // PolynomialReconstructionDescriptor descriptor2{IntegrationMethodType::element, 2}; - // DiscreteFunctionP0 f2h = test_only::exact_projection(mesh, 2, std::function(R_exact)); - // auto reconstructions2 = PolynomialReconstruction{descriptor2}.build(fh); - // auto dpk_fh2 = reconstructions2[0]->get<DiscreteFunctionDPk<3, const double>>(); - // double max_error = test_only::max_reconstruction_error(mesh, dpk_fh2, - // std::function(R_exact)); REQUIRE(parallel::allReduceMax(max_error) == - // Catch::Approx(0).margin(1E-12)); - - auto cell_type = mesh.connectivity().cellType(); - auto cell_number = mesh.connectivity().cellNumber(); - - for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) { - std::clog << cell_id << "(" << cell_number[cell_id] << ")" - << ":---- " << name(cell_type[cell_id]) << "\n2| "; - // { - // auto coeffs = dpk_fh2.coefficients(cell_id); - // for (size_t i = 0; i < coeffs.size(); ++i) { - // if (std::abs(coeffs[i]) > 1e-12) { - // std::clog << ' ' << i << ':' << coeffs[i]; - // } - // } - // } - std::clog << '\n'; - std::clog << "3| "; - { - auto coeffs = dpk_fh.coefficients(cell_id); - for (size_t i = 0; i < coeffs.size(); ++i) { - if (std::abs(coeffs[i]) > 1e-12) { - std::clog << ' ' << i << ':' << coeffs[i]; - } - } - } - std::clog << '\n'; - break; - } - } - double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } + +#warning NOT FINISHED } // SECTION("R^3 data") -- GitLab From b5bd7af998d536458186ff805676620ebd03d8f7 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Fri, 25 Apr 2025 18:38:52 +0200 Subject: [PATCH 104/122] Add missing tests for degree 3 reconstruction --- src/scheme/PolynomialReconstruction.cpp | 1 + ...test_PolynomialReconstruction_degree_3.cpp | 917 +++++------------- 2 files changed, 249 insertions(+), 669 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index b01d11a79..2201e5f0f 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -1243,6 +1243,7 @@ PolynomialReconstruction::_build( } else if ((m_descriptor.integrationMethodType() == IntegrationMethodType::element) or (m_descriptor.integrationMethodType() == IntegrationMethodType::boundary)) { +#warning implement boundary based reconstruction for 1d and 3d if ((m_descriptor.integrationMethodType() == IntegrationMethodType::boundary) and (MeshType::Dimension == 2)) { if constexpr (MeshType::Dimension == 2) { diff --git a/tests/test_PolynomialReconstruction_degree_3.cpp b/tests/test_PolynomialReconstruction_degree_3.cpp index d8badda38..e20fa89a3 100644 --- a/tests/test_PolynomialReconstruction_degree_3.cpp +++ b/tests/test_PolynomialReconstruction_degree_3.cpp @@ -34,10 +34,6 @@ TEST_CASE("PolynomialReconstruction_degree_3", "[scheme]") PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree}}; for (auto descriptor : descriptor_list) { -#warning remove - descriptor.setPreconditioning(false); - descriptor.setRowWeighting(false); - SECTION(name(descriptor.integrationMethodType())) { SECTION("1D") @@ -437,7 +433,7 @@ TEST_CASE("PolynomialReconstruction_degree_3", "[scheme]") { using R3 = TinyVector<3>; - auto p0 = [](const R3& X) -> double { + auto p = [](const R3& X, const std::array<double, 20>& a) -> double { const double x = X[0]; const double y = X[1]; const double z = X[2]; @@ -458,730 +454,313 @@ TEST_CASE("PolynomialReconstruction_degree_3", "[scheme]") const double y3 = y2 * y; const double z3 = z2 * z; - return 2.3 + 1.7 * x - 1.3 * y + 2.1 * z // - + 1.7 * x2 + 1.4 * y2 + 1.7 * z2 // - - 2.3 * xy + 1.6 * xz - 1.9 * yz // - + 1.2 * x3 - 2.1 * y3 - 1.1 * z3 // - - 1.7 * x2y - 1.3 * x2z + 0.9 * xy2 // - - 0.7 * y2z + 1.5 * xz2 - 0.7 * yz2 // - + 2.8 * xyz // + return a[0] + a[1] * x + a[2] * y + a[3] * z // + + a[4] * x2 + a[5] * y2 + a[6] * z2 // + + a[7] * xy + a[8] * xz + a[9] * yz // + + a[10] * x3 + a[11] * y3 + a[12] * z3 // + + a[13] * x2y + a[14] * x2z + a[15] * xy2 // + + a[16] * y2z + a[17] * xz2 + a[18] * yz2 // + + a[19] * xyz // ; }; - // auto p1 = [](const R3& x) { - // return +2.3 + 1.7 * x[0] - 2.2 * x[1] + 1.8 * x[2] // - // + 1.7 * x[0] * x[0] - 2.4 * x[1] * x[1] - 2.3 * x[2] * x[2] // - // - 2.1 * x[0] * x[1] + 2.6 * x[0] * x[2] + 1.6 * x[1] * x[2]; - // }; + constexpr std::array<double, 20> a0 = {+2.3, +1.7, -1.3, +2.1, +1.7, +1.4, +1.7, -2.3, +1.6, -1.9, + +1.2, -2.1, -1.1, -1.7, -1.3, +0.9, -0.7, +1.5, -0.7, +2.8}; - // auto p2 = [](const R3& x) { - // return +1.4 - 0.6 * x[0] + 1.3 * x[1] - 3.7 * x[2] // - // + 3.1 * x[0] * x[0] - 1.1 * x[1] * x[1] + 1.7 * x[2] * x[2] // - // - 2.3 * x[0] * x[1] - 2.6 * x[0] * x[2] - 1.9 * x[1] * x[2]; - // }; + auto p0 = [&p, &a0](const R3& X) -> double { return p(X, a0); }; - // auto p3 = [](const R3& x) { - // return -0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2] // - // - 1.5 * x[0] * x[0] + 1.4 * x[1] * x[1] - 1.2 * x[2] * x[2] // - // - 1.7 * x[0] * x[1] - 1.3 * x[0] * x[2] + 2.1 * x[1] * x[2]; - // }; + constexpr std::array<double, 20> a1 = {-1.3, +2.2, +0.1, -2.5, +0.2, -2.3, -1.4, +0.9, +0.2, -0.3, + +2.4, -1.2, +1.7, -2.2, +0.6, +1.9, +1.0, -0.8, +2.4, +2.4}; - SECTION("R data") - { - for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { - SECTION(named_mesh.name()) - { - auto p_initial_mesh = CartesianMeshBuilder{TinyVector<3>{-0.5, -0.5, -0.5}, - TinyVector<3>{3.5, 3.5, 3.5}, TinyVector<3, size_t>{4, 4, 4}} - .mesh() - ->get<Mesh<3>>(); // named_mesh.mesh()->get<Mesh<3>>(); - auto& initial_mesh = *p_initial_mesh; - - constexpr double theta = 0; - TinyMatrix<3> T0{std::cos(theta), -std::sin(theta), 0, - // - std::sin(theta), std::cos(theta), 0, - // - 0, 0, 1}; - constexpr double phi = 0; - TinyMatrix<3> T1{std::cos(phi), 0, -std::sin(phi), - // - 0, 1, 0, - // - std::sin(phi), 0, std::cos(phi)}; - - TinyMatrix T = T0 * T1; - - auto xr = initial_mesh.xr(); - - NodeValue<R3> new_xr{initial_mesh.connectivity()}; - parallel_for( - initial_mesh.numberOfNodes(), - PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = T * xr[node_id]; }); - - std::shared_ptr p_mesh = std::make_shared<const Mesh<3>>(initial_mesh.shared_connectivity(), new_xr); - const Mesh<3>& mesh = *p_mesh; - // inverse rotation - TinyMatrix<3> inv_T{std::cos(theta), std::sin(theta), 0, - // - -std::sin(theta), std::cos(theta), 0, - // - 0, 0, 1}; + auto p1 = [&p, &a1](const R3& X) -> double { return p(X, a1); }; - auto R_exact = p0; + constexpr std::array<double, 20> a2 = {+1.9, -1.2, -0.4, -1.2, -0.8, +1.4, +0.5, -1.6, +1.1, -0.7, + +0.6, +2.3, -1.8, -1.9, -0.3, -2.4, -1.7, +0.2, -2.4, +1.9}; - DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree + 3, std::function(R_exact)); + auto p2 = [&p, &a2](const R3& X) -> double { return p(X, a2); }; - auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); + constexpr std::array<double, 20> a3 = {+0.8, +0.5, +1.3, -2.3, +0.9, -0.4, -2.0, +1.8, +0.5, +0.7, + +1.0, -0.4, +1.1, +1.8, -0.4, +1.1, -0.0, +1.4, +1.9, -2.2}; - double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); - REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - } - } + auto p3 = [&p, &a3](const R3& X) -> double { return p(X, a3); }; -#warning NOT FINISHED - } + auto p_mesh = CartesianMeshBuilder{TinyVector<3>{-0.5, -0.5, -0.5}, TinyVector<3>{3.5, 3.5, 3.5}, + TinyVector<3, size_t>{4, 4, 4}} + .mesh() + ->get<Mesh<3>>(); + const auto& mesh = *p_mesh; + + SECTION("R data") + { + auto R_exact = p0; - // SECTION("R^3 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; + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree + 3, std::function(R_exact)); - // auto R3_exact = [&](const R3& x) -> R3 { return R3{p1(x), p2(x), p3(x)}; }; + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); - // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } - // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); + SECTION("R^3 data") + { + auto R3_exact = [&](const R3& x) -> R3 { return R3{p1(x), p2(x), p3(x)}; }; - // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); - // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - // SECTION("R^2x2 data") - // { - // using R2x2 = TinyMatrix<2, 2>; + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); - // for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) { - // SECTION(named_mesh.name()) - // { - // auto p_mesh = named_mesh.mesh()->get<Mesh<3>>(); - // auto& mesh = *p_mesh; + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } - // auto R2x2_exact = [&](const R3& x) -> R2x2 { - // return R2x2{p0(x), p1(x), // - // p2(x), p3(x)}; - // }; + SECTION("R^2x2 data") + { + using R2x2 = TinyMatrix<2, 2>; - // DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); + auto R2x2_exact = [&](const R3& x) -> R2x2 { + return R2x2{p0(x), p1(x), // + p2(x), p3(x)}; + }; - // descriptor.setRowWeighting(false); - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); + DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); - // auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } + descriptor.setRowWeighting(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - // SECTION("vector 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 dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } - // std::array<std::function<double(const R3&)>, 4> vector_exact = {p0, p1, p2, p3}; + SECTION("vector data") + { + std::array<std::function<double(const R3&)>, 4> vector_exact = {p0, p1, p2, p3}; - // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); - // descriptor.setPreconditioning(false); - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + descriptor.setPreconditioning(false); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } } } } } - // SECTION("with symmetries") - // { - // SECTION("1D") - // { - // std::vector<PolynomialReconstructionDescriptor> descriptor_list = - // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, - // std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ - // std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, - // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, - // std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ - // std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; - // using R1 = TinyVector<1>; - - // auto p0 = [](const R1& x) { return +1.7 * (x[0] + 1) * (x[0] + 1) - 1.1; }; - // auto p1 = [](const R1& x) { return -1.2 * (x[0] + 1) * (x[0] + 1) + 1.3; }; - // auto p2 = [](const R1& x) { return +1.4 * (x[0] + 1) * (x[0] + 1) - 0.6; }; - - // for (auto descriptor : descriptor_list) { - // SECTION(name(descriptor.integrationMethodType())) - // { - // SECTION("R data") - // { - // auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - - // auto& mesh = *p_mesh; + SECTION("with symmetries") + { + SECTION("1D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ + std::make_shared<NamedBoundaryDescriptor>("XMIN")}}}; + using R1 = TinyVector<1>; + + auto p0 = [](const R1& x) -> R1 { return R1{+1.7 * (x[0] + 1) * (x[0] + 1) * (x[0] + 1)}; }; + auto p1 = [](const R1& x) -> R1 { return R1{-1.2 * (x[0] + 1) * (x[0] + 1) * (x[0] + 1)}; }; + auto p2 = [](const R1& x) -> R1 { return R1{+1.4 * (x[0] + 1) * (x[0] + 1) * (x[0] + 1)}; }; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("R1 data") + { + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - // auto R_exact = p0; + auto& mesh = *p_mesh; - // DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R_exact)); + auto R1_exact = p0; - // auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R1_exact)); - // auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const double>>(); + auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); - // double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } + auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R1>>(); - // SECTION("R1x1 data") - // { - // using R1x1 = TinyMatrix<1>; + double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R1_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } - // auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + SECTION("R1 vector data") + { + auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); + auto& mesh = *p_mesh; - // auto& mesh = *p_mesh; + std::array<std::function<R1(const R1&)>, 3> vector_exact // + = {p0, p1, p2}; - // auto R1x1_exact = [&](const R1& x) { return R1x1{p0(x)}; }; + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); - // DiscreteFunctionP0 fh = test_only::exact_projection(mesh, degree, std::function(R1x1_exact)); + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - // auto reconstructions = PolynomialReconstruction{descriptor}.build(fh); + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R1>>(); - // auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, const R1x1>>(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } - // double max_error = test_only::max_reconstruction_error(mesh, dpk_fh, std::function(R1x1_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } + SECTION("2D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX")}}}; + + using R2 = TinyVector<2>; + + auto p_mesh = CartesianMeshBuilder{TinyVector<2>{0, 0}, TinyVector<2>{2, 1}, TinyVector<2, size_t>{3, 3}} + .mesh() + ->get<Mesh<2>>(); + + auto& mesh = *p_mesh; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("R^2 data") + { + auto R2_exact = [](const R2& x) -> R2 { + return R2{+2.3 * (x[0] - 2) * (x[0] - 2) * (x[0] - 2), // + -1.3 * (x[1] - 1) * (x[1] - 1) * (x[1] - 1)}; + }; - // SECTION("R vector data") - // { - // auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - // auto& mesh = *p_mesh; + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R2_exact)); - // std::array<std::function<double(const R1&)>, 3> vector_exact // - // = {p0, p1, p2}; + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2>>(); - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R2_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } - // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - - // SECTION("R1x1 vector data") - // { - // using R1x1 = TinyMatrix<1>; - - // auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>(); - // auto& mesh = *p_mesh; - - // std::array<std::function<R1x1(const R1&)>, 3> vector_exact // - // = {[&](const R1& x) { return R1x1{p2(x)}; }, // - // [&](const R1& x) { return R1x1{p0(x)}; }, // - // [&](const R1& x) { return R1x1{p1(x)}; }}; - - // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - - // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R1x1>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } - // } - - // SECTION("2D") - // { - // std::vector<PolynomialReconstructionDescriptor> descriptor_list = - // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, - // std::vector<std::shared_ptr< - // const IBoundaryDescriptor>>{std:: - // make_shared<NamedBoundaryDescriptor>( - // "XMAX"), - // std::make_shared<NamedBoundaryDescriptor>( - // "YMAX")}}, - // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, - // std::vector<std::shared_ptr< - // const IBoundaryDescriptor>>{std:: - // make_shared<NamedBoundaryDescriptor>( - // "XMAX"), - // std::make_shared<NamedBoundaryDescriptor>( - // "YMAX")}}}; + SECTION("vector of R2") + { + std::array<std::function<R2(const R2&)>, 2> vector_exact // + = {[](const R2& x) -> R2 { + return R2{+1.7 * (x[0] - 2) * (x[0] - 2) * (x[0] - 2), // + -0.6 * (x[1] - 1) * (x[1] - 1) * (x[1] - 1)}; + }, + [](const R2& x) -> R2 { + return R2{-2.3 * (x[0] - 2) * (x[0] - 2) * (x[0] - 2), // + +1.1 * (x[1] - 1) * (x[1] - 1) * (x[1] - 1)}; + }}; - // using R2 = TinyVector<2>; + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); - // auto p_initial_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>(); - // auto& initial_mesh = *p_initial_mesh; + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - // constexpr double theta = 1; - // TinyMatrix<2> T{std::cos(theta), -std::sin(theta), // - // std::sin(theta), std::cos(theta)}; + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const R2>>(); - // auto xr = initial_mesh.xr(); + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } - // NodeValue<R2> new_xr{initial_mesh.connectivity()}; - // parallel_for( - // initial_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = T * xr[node_id]; }); + SECTION("3D") + { + std::vector<PolynomialReconstructionDescriptor> descriptor_list = + {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}, + PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, + std::vector<std::shared_ptr< + const IBoundaryDescriptor>>{std:: + make_shared<NamedBoundaryDescriptor>( + "XMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "YMAX"), + std::make_shared<NamedBoundaryDescriptor>( + "ZMAX")}}}; + + using R3 = TinyVector<3>; + + auto p_mesh = CartesianMeshBuilder{TinyVector<3>{0, 0, 0}, TinyVector<3>{2, 1, 1}, TinyVector<3, size_t>{3, 3, 3}} + .mesh() + ->get<Mesh<3>>(); + + auto& mesh = *p_mesh; + + for (auto descriptor : descriptor_list) { + SECTION(name(descriptor.integrationMethodType())) + { + SECTION("R^3 data") + { + auto R3_exact = [](const R3& x) -> R3 { + return R3{+2.3 * (x[0] - 2) * (x[0] - 2) * (x[0] - 2), // + -1.3 * (x[1] - 1) * (x[1] - 1) * (x[1] - 1), // + +1.4 * (x[2] - 1) * (x[2] - 1) * (x[2] - 1)}; + }; - // std::shared_ptr p_mesh = std::make_shared<const Mesh<2>>(initial_mesh.shared_connectivity(), new_xr); - // const Mesh<2>& mesh = *p_mesh; - // // inverse rotation - // TinyMatrix<2> inv_T{std::cos(theta), std::sin(theta), // - // -std::sin(theta), std::cos(theta)}; + DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R3_exact)); - // auto p0 = [&inv_T](const R2& X) { - // const R2 Y = inv_T * X; - // const double x = Y[0] - 2; - // const double y = Y[1] - 1; - // return +1.7 * x * x + 2 * y * y - 1.1; - // }; + auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - // auto p1 = [&inv_T](const R2& X) { - // const R2 Y = inv_T * X; - // const double x = Y[0] - 2; - // const double y = Y[1] - 1; - // return -1.3 * x * x - 0.2 * y * y + 0.7; - // }; - - // auto p2 = [&inv_T](const R2& X) { - // const R2 Y = inv_T * X; - // const double x = Y[0] - 2; - // const double y = Y[1] - 1; - // return +2.6 * x * x - 1.4 * y * y - 1.9; - // }; + auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>(); - // auto p3 = [&inv_T](const R2& X) { - // const R2 Y = inv_T * X; - // const double x = Y[0] - 2; - // const double y = Y[1] - 1; - // return -0.6 * x * x + 1.4 * y * y + 2.3; - // }; + double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } - // auto q0 = [&inv_T](const R2& X) { - // const R2 Y = inv_T * X; - // const double x = Y[0] - 2; - // const double y = Y[1] - 1; - // return 3 * x * y; - // }; - - // auto q1 = [&inv_T](const R2& X) { - // const R2 Y = inv_T * X; - // const double x = Y[0] - 2; - // const double y = Y[1] - 1; - // return -1.3 * x * y; - // }; - - // for (auto descriptor : descriptor_list) { - // SECTION(name(descriptor.integrationMethodType())) - // { - // SECTION("R data") - // { - // auto R_exact = p0; - - // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - - // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - - // SECTION("R2x2 data") - // { - // using R2x2 = TinyMatrix<2>; - // auto R2x2_exact = [&](const R2& X) -> R2x2 { - // return T * TinyMatrix<2>{p0(X), q0(X), q1(X), p1(X)} * inv_T; - // }; - - // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - - // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R2x2_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - - // SECTION("vector of R") - // { - // std::array<std::function<double(const R2&)>, 4> vector_exact // - // = {p0, p1, p2, p3}; - - // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - - // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - - // SECTION("vector of R2x2") - // { - // using R2x2 = TinyMatrix<2>; - - // std::array<std::function<R2x2(const R2&)>, 2> vector_R2x2_exact // - // = {[&](const R2& X) { - // return T * R2x2{p0(X), q0(X), q1(X), p1(X)} * inv_T; - // }, - // [&](const R2& X) { - // return T * R2x2{p0(X), q1(X), 0, p1(X)} * inv_T; - // }}; - - // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_R2x2_exact); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - - // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const R2x2>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_R2x2_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - - // SECTION("list") - // { - // using R2x2 = TinyMatrix<2>; - - // auto R_exact = p0; - - // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); - - // std::array<std::function<double(const R2&)>, 4> vector_exact // - // = {p0, p1, p2, p3}; - - // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); - - // std::array<std::function<R2x2(const R2&)>, 2> vector_R2x2_exact // - // = {[&](const R2& X) { - // return T * R2x2{p0(X), q0(X), q1(X), p1(X)} * inv_T; - // }, - // [&](const R2& X) { - // return T * R2x2{p2(X), q1(X), 0, p3(X)} * inv_T; - // }}; - - // DiscreteFunctionP0Vector Wh = test_only::exact_projection(mesh, degree, vector_R2x2_exact); - - // auto R2x2_exact = [&](const R2& X) -> R2x2 { - // return T * TinyMatrix<2>{p0(X), q1(X), q0(X), p3(X)} * inv_T; - // }; - - // DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh, Vh, Wh, Ah); - - // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>(); - // auto dpk_Vh = reconstructions[1]->get<DiscreteFunctionDPkVector<2, const double>>(); - // auto dpk_Wh = reconstructions[2]->get<DiscreteFunctionDPkVector<2, const R2x2>>(); - // auto dpk_Ah = reconstructions[3]->get<DiscreteFunctionDPk<2, const R2x2>>(); - - // { - // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // { - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // { - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Wh, vector_R2x2_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // { - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } - // } - // } - - // SECTION("3D") - // { - // using R3 = TinyVector<3>; - - // auto p_initial_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>(); - // auto& initial_mesh = *p_initial_mesh; - - // constexpr double theta = 1; - // TinyMatrix<3> T{std::cos(theta), -std::sin(theta), 0, - // // - // std::sin(theta), std::cos(theta), 0, - // // - // 0, 0, 1}; - - // auto xr = initial_mesh.xr(); - - // NodeValue<R3> new_xr{initial_mesh.connectivity()}; - // parallel_for( - // initial_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { new_xr[node_id] = T * xr[node_id]; }); - - // std::shared_ptr p_mesh = std::make_shared<const Mesh<3>>(initial_mesh.shared_connectivity(), new_xr); - // const Mesh<3>& mesh = *p_mesh; - // // inverse rotation - // TinyMatrix<3> inv_T{std::cos(theta), std::sin(theta), 0, - // // - // -std::sin(theta), std::cos(theta), 0, - // // - // 0, 0, 1}; - - // auto p0 = [&inv_T](const R3& X) { - // const R3 Y = inv_T * X; - // const double x = Y[0] - 2; - // const double y = Y[1] - 1; - // const double z = Y[2] - 1; - // return +1.7 * x * x + 2 * y * y + 1.3 * z * z - 1.1; - // }; - - // auto p1 = [&inv_T](const R3& X) { - // const R3 Y = inv_T * X; - // const double x = Y[0] - 2; - // const double y = Y[1] - 1; - // const double z = Y[2] - 1; - // return +2.1 * x * x - 1.4 * y * y - 3.1 * z * z + 2.2; - // }; - - // auto p2 = [&inv_T](const R3& X) { - // const R3 Y = inv_T * X; - // const double x = Y[0] - 2; - // const double y = Y[1] - 1; - // const double z = Y[2] - 1; - // return -1.1 * x * x - 1.2 * y * y + 1.3 * z * z - 1.7; - // }; - - // auto p3 = [&inv_T](const R3& X) { - // const R3 Y = inv_T * X; - // const double x = Y[0] - 2; - // const double y = Y[1] - 1; - // const double z = Y[2] - 1; - // return 1.9 * x * x + 2.1 * y * y - 3.1 * z * z + 1.6; - // }; - - // auto p4 = [&inv_T](const R3& X) { - // const R3 Y = inv_T * X; - // const double x = Y[0] - 2; - // const double y = Y[1] - 1; - // const double z = Y[2] - 1; - // return -2.4 * x * x + 3.3 * y * y - 1.7 * z * z + 2.1; - // }; - - // SECTION("3 symmetries") - // { - // std::vector<PolynomialReconstructionDescriptor> descriptor_list = - // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, - // std::vector<std::shared_ptr< - // const IBoundaryDescriptor>>{std:: - // make_shared<NamedBoundaryDescriptor>( - // "XMAX"), - // std::make_shared<NamedBoundaryDescriptor>( - // "YMAX"), - // std::make_shared<NamedBoundaryDescriptor>( - // "ZMAX")}}, - // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, - // std::vector<std::shared_ptr< - // const IBoundaryDescriptor>>{std:: - // make_shared<NamedBoundaryDescriptor>( - // "XMAX"), - // std::make_shared<NamedBoundaryDescriptor>( - // "YMAX"), - // std::make_shared<NamedBoundaryDescriptor>( - // "ZMAX")}}}; - - // for (auto descriptor : descriptor_list) { - // SECTION("R data") - // { - // auto R_exact = p0; - - // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh); - - // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - - // SECTION("vector of R") - // { - // std::array<std::function<double(const R3&)>, 4> vector_exact // - // = {p0, p1, p2, p3}; - - // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - - // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } - - // SECTION("1 symmetry") - // { - // // Matrix and their transformations are kept simple to - // // derive exact solutions - // std::vector<PolynomialReconstructionDescriptor> descriptor_list = - // {PolynomialReconstructionDescriptor{IntegrationMethodType::element, degree, - // std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ - // std::make_shared<NamedBoundaryDescriptor>("XMAX")}}, - // PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, degree, - // std::vector<std::shared_ptr<const IBoundaryDescriptor>>{ - // std::make_shared<NamedBoundaryDescriptor>("XMAX")}}}; - // for (auto descriptor : descriptor_list) { - // SECTION(name(descriptor.integrationMethodType())) - // { - // SECTION("R3x3 data") - // { - // // Matrix and their transformations are kept simple to - // // derive exact solutions - - // using R3x3 = TinyMatrix<3>; - // auto R2x2_exact = [&](const R3& X) -> R3x3 { - // return T * TinyMatrix<3>{p0(X), 0, 0, // - // 0, p1(X), p2(X), // - // 0, p3(X), p4(X)} * - // inv_T; - // }; - - // DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R2x2_exact)); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah); - - // auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3x3>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R2x2_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - - // SECTION("vector of R3x3") - // { - // using R3x3 = TinyMatrix<3>; - - // std::array<std::function<R3x3(const R3&)>, 2> vector_R3x3_exact // - // = {[&](const R3& X) { - // return T * R3x3{p0(X), 0, 0, // - // 0, p1(X), p2(X), // - // 0, p3(X), p4(X)} * - // inv_T; - // }, - // [&](const R3& X) { - // return T * R3x3{p0(X), 0, 0, // - // 0, p2(X), p4(X), // - // 0, p3(X), p1(X)} * - // inv_T; - // }}; - - // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_R3x3_exact); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); - - // auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const R3x3>>(); - - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_R3x3_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - - // SECTION("list") - // { - // using R3x3 = TinyMatrix<3>; - - // auto R_exact = p0; - - // DiscreteFunctionP0 uh = test_only::exact_projection(mesh, degree, std::function(R_exact)); - - // std::array<std::function<double(const R3&)>, 4> vector_exact // - // = {p0, p1, p2, p3}; - - // DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); - - // std::array<std::function<R3x3(const R3&)>, 2> vector_R3x3_exact // - // = {[&](const R3& X) { - // return T * R3x3{p1(X), 0, 0, // - // 0, p3(X), p0(X), // - // 0, p2(X), p4(X)} * - // inv_T; - // }, - // [&](const R3& X) { - // return T * R3x3{p2(X), 0, 0, // - // 0, p0(X), p1(X), // - // 0, p3(X), p4(X)} * - // inv_T; - // }}; - - // DiscreteFunctionP0Vector Wh = test_only::exact_projection(mesh, degree, vector_R3x3_exact); - - // auto R3x3_exact = [&](const R3& X) -> R3x3 { - // return T * R3x3{p0(X), 0, 0, // - // 0, p1(X), p2(X), // - // 0, p3(X), p4(X)} * - // inv_T; - // }; - - // DiscreteFunctionP0 Ah = test_only::exact_projection(mesh, degree, std::function(R3x3_exact)); - - // auto reconstructions = PolynomialReconstruction{descriptor}.build(uh, Vh, Wh, Ah); - - // auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>(); - // auto dpk_Vh = reconstructions[1]->get<DiscreteFunctionDPkVector<3, const double>>(); - // auto dpk_Wh = reconstructions[2]->get<DiscreteFunctionDPkVector<3, const R3x3>>(); - // auto dpk_Ah = reconstructions[3]->get<DiscreteFunctionDPk<3, const R3x3>>(); - - // { - // double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // { - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // { - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Wh, vector_R3x3_exact); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // { - // double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); - // REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); - // } - // } - // } - // } - // } - // } - // } + SECTION("vector of R3") + { + std::array<std::function<R3(const R3&)>, 2> vector_exact // + = {[](const R3& x) -> R3 { + return R3{+1.7 * (x[0] - 2) * (x[0] - 2) * (x[0] - 2), // + -0.6 * (x[1] - 1) * (x[1] - 1) * (x[1] - 1), // + +1.2 * (x[2] - 1) * (x[2] - 1) * (x[2] - 1)}; + }, + [](const R3& x) -> R3 { + return R3{-2.3 * (x[0] - 2) * (x[0] - 2) * (x[0] - 2), // + +1.1 * (x[1] - 1) * (x[1] - 1) * (x[1] - 1), // + -0.3 * (x[2] - 1) * (x[2] - 1) * (x[2] - 1)}; + }}; + + DiscreteFunctionP0Vector Vh = test_only::exact_projection(mesh, degree, vector_exact); + + auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh); + + auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const R3>>(); + + double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); + } + } + } + } + } } -- GitLab From 6a5fcd4b13b81249de3d1f752908a732dc8d3772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Wed, 30 Apr 2025 18:50:56 +0200 Subject: [PATCH 105/122] Begin code clean-up --- src/scheme/PolynomialReconstruction.cpp | 937 +++++++++++++++--------- src/scheme/PolynomialReconstruction.hpp | 9 + 2 files changed, 585 insertions(+), 361 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 2201e5f0f..661630187 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -49,126 +49,64 @@ symmetrize_coordinates(const TinyVector<Dimension>& origin, return u - 2 * dot(u - origin, normal) * normal; } -class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant +template <MeshConcept MeshType> +class PolynomialReconstruction::CellCenterReconstructionMatrixBuilder { - public: - using Variant = std::variant<DiscreteFunctionDPk<1, double>, - DiscreteFunctionDPk<1, TinyVector<1>>, - DiscreteFunctionDPk<1, TinyVector<2>>, - DiscreteFunctionDPk<1, TinyVector<3>>, - DiscreteFunctionDPk<1, TinyMatrix<1>>, - DiscreteFunctionDPk<1, TinyMatrix<2>>, - DiscreteFunctionDPk<1, TinyMatrix<3>>, - - DiscreteFunctionDPk<2, double>, - DiscreteFunctionDPk<2, TinyVector<1>>, - DiscreteFunctionDPk<2, TinyVector<2>>, - DiscreteFunctionDPk<2, TinyVector<3>>, - DiscreteFunctionDPk<2, TinyMatrix<1>>, - DiscreteFunctionDPk<2, TinyMatrix<2>>, - DiscreteFunctionDPk<2, TinyMatrix<3>>, - - DiscreteFunctionDPk<3, double>, - DiscreteFunctionDPk<3, TinyVector<1>>, - DiscreteFunctionDPk<3, TinyVector<2>>, - DiscreteFunctionDPk<3, TinyVector<3>>, - DiscreteFunctionDPk<3, TinyMatrix<1>>, - DiscreteFunctionDPk<3, TinyMatrix<2>>, - DiscreteFunctionDPk<3, TinyMatrix<3>>, - - DiscreteFunctionDPkVector<1, double>, - DiscreteFunctionDPkVector<1, TinyVector<1>>, - DiscreteFunctionDPkVector<1, TinyVector<2>>, - DiscreteFunctionDPkVector<1, TinyVector<3>>, - DiscreteFunctionDPkVector<1, TinyMatrix<1>>, - DiscreteFunctionDPkVector<1, TinyMatrix<2>>, - DiscreteFunctionDPkVector<1, TinyMatrix<3>>, - - DiscreteFunctionDPkVector<2, double>, - DiscreteFunctionDPkVector<2, TinyVector<1>>, - DiscreteFunctionDPkVector<2, TinyVector<2>>, - DiscreteFunctionDPkVector<2, TinyVector<3>>, - DiscreteFunctionDPkVector<2, TinyMatrix<1>>, - DiscreteFunctionDPkVector<2, TinyMatrix<2>>, - DiscreteFunctionDPkVector<2, TinyMatrix<3>>, + private: + using Rd = TinyVector<MeshType::Dimension>; - DiscreteFunctionDPkVector<3, double>, - DiscreteFunctionDPkVector<3, TinyVector<1>>, - DiscreteFunctionDPkVector<3, TinyVector<2>>, - DiscreteFunctionDPkVector<3, TinyVector<3>>, - DiscreteFunctionDPkVector<3, TinyMatrix<1>>, - DiscreteFunctionDPkVector<3, TinyMatrix<2>>, - DiscreteFunctionDPkVector<3, TinyMatrix<3>>>; + const size_t m_basis_dimension; + const SmallArray<const Rd> m_symmetry_origin_list; + const SmallArray<const Rd> m_symmetry_normal_list; - private: - Variant m_mutable_discrete_function_dpk; + const CellToCellStencilArray& m_stencil_array; + const CellValue<const Rd> m_xj; public: - PUGS_INLINE - const Variant& - mutableDiscreteFunctionDPk() const - { - return m_mutable_discrete_function_dpk; - } - - template <typename DiscreteFunctionDPkT> - PUGS_INLINE auto&& - get() const + template <typename MatrixType> + void + build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) { - static_assert(is_discrete_function_dpk_v<DiscreteFunctionDPkT>, "invalid template argument"); - -#ifndef NDEBUG - if (not std::holds_alternative<DiscreteFunctionDPkT>(this->m_mutable_discrete_function_dpk)) { - std::ostringstream error_msg; - error_msg << "invalid discrete function type\n"; - error_msg << "- required " << rang::fgB::red << demangle<DiscreteFunctionDPkT>() << rang::fg::reset << '\n'; - error_msg << "- contains " << rang::fgB::yellow - << std::visit([](auto&& f) -> std::string { return demangle<decltype(f)>(); }, - this->m_mutable_discrete_function_dpk) - << rang::fg::reset; - throw NormalError(error_msg.str()); + const auto& stencil_cell_list = m_stencil_array[cell_j_id]; + + const Rd& Xj = m_xj[cell_j_id]; + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + const Rd Xi_Xj = m_xj[cell_i_id] - Xj; + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = Xi_Xj[l]; + } + } + for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { + auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = m_symmetry_origin_list[i_symmetry]; + const Rd& normal = m_symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + const Rd Xi_Xj = symmetrize_coordinates(origin, normal, m_xj[cell_i_id]) - Xj; + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = Xi_Xj[l]; + } + } } -#endif // NDEBUG - - return std::get<DiscreteFunctionDPkT>(this->mutableDiscreteFunctionDPk()); - } - - template <size_t Dimension, typename DataType> - MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPk<Dimension, DataType>& discrete_function_dpk) - : m_mutable_discrete_function_dpk{discrete_function_dpk} - { - static_assert(std::is_same_v<DataType, double> or // - std::is_same_v<DataType, TinyVector<1, double>> or // - std::is_same_v<DataType, TinyVector<2, double>> or // - std::is_same_v<DataType, TinyVector<3, double>> or // - std::is_same_v<DataType, TinyMatrix<1, 1, double>> or // - std::is_same_v<DataType, TinyMatrix<2, 2, double>> or // - std::is_same_v<DataType, TinyMatrix<3, 3, double>>, - "DiscreteFunctionDPk with this DataType is not allowed in variant"); - } - - template <size_t Dimension, typename DataType> - MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) - : m_mutable_discrete_function_dpk{discrete_function_dpk} - { - static_assert(std::is_same_v<DataType, double> or // - std::is_same_v<DataType, TinyVector<1, double>> or // - std::is_same_v<DataType, TinyVector<2, double>> or // - std::is_same_v<DataType, TinyVector<3, double>> or // - std::is_same_v<DataType, TinyMatrix<1, 1, double>> or // - std::is_same_v<DataType, TinyMatrix<2, 2, double>> or // - std::is_same_v<DataType, TinyMatrix<3, 3, double>>, - "DiscreteFunctionDPkVector with this DataType is not allowed in variant"); } - MutableDiscreteFunctionDPkVariant& operator=(MutableDiscreteFunctionDPkVariant&&) = default; - MutableDiscreteFunctionDPkVariant& operator=(const MutableDiscreteFunctionDPkVariant&) = default; - - MutableDiscreteFunctionDPkVariant(const MutableDiscreteFunctionDPkVariant&) = default; - MutableDiscreteFunctionDPkVariant(MutableDiscreteFunctionDPkVariant&&) = default; - - MutableDiscreteFunctionDPkVariant() = delete; - ~MutableDiscreteFunctionDPkVariant() = default; + CellCenterReconstructionMatrixBuilder(const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array, + const CellValue<const Rd>& xj) + : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(1)}, + m_symmetry_origin_list{symmetry_origin_list}, + m_symmetry_normal_list{symmetry_normal_list}, + m_stencil_array{stencil_array}, + m_xj{xj} + {} + + ~CellCenterReconstructionMatrixBuilder() = default; }; template <typename ConformTransformationT> @@ -179,7 +117,7 @@ _computeEjkMean(const QuadratureFormula<1>& quadrature, const double Vi, const size_t degree, const size_t dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, + const SmallArray<double>& inv_Vj_wq_detJ_ek, SmallArray<double>& mean_of_ejk) { using Rd = TinyVector<1>; @@ -216,7 +154,7 @@ _computeEjkMean(const QuadratureFormula<2>& quadrature, const double Vi, const size_t degree, const size_t dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, + const SmallArray<double>& inv_Vj_wq_detJ_ek, SmallArray<double>& mean_of_ejk) { using Rd = TinyVector<2>; @@ -269,7 +207,7 @@ _computeEjkMean(const QuadratureFormula<3>& quadrature, const double Vi, const size_t degree, const size_t dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, + const SmallArray<double>& inv_Vj_wq_detJ_ek, SmallArray<double>& mean_of_ejk) { #warning Compute this one for all and rework design @@ -358,7 +296,7 @@ void _computeEjkMean(const TinyVector<Dimension>& Xj, const double Vi, const size_t degree, const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, + const SmallArray<double>& inv_Vj_wq_detJ_ek, SmallArray<double>& mean_of_ejk); template <typename NodeListT> @@ -370,7 +308,7 @@ _computeEjkMean(const TinyVector<1>& Xj, const double Vi, const size_t degree, const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, + const SmallArray<double>& inv_Vj_wq_detJ_ek, SmallArray<double>& mean_of_ejk) { if (cell_type == CellType::Line) { @@ -396,7 +334,7 @@ _computeEjkMeanInSymmetricCell(const TinyVector<1>& origin, const double Vi, const size_t degree, const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, + const SmallArray<double>& inv_Vj_wq_detJ_ek, SmallArray<double>& mean_of_ejk) { if (cell_type == CellType::Line) { @@ -414,6 +352,308 @@ _computeEjkMeanInSymmetricCell(const TinyVector<1>& origin, } } +template <typename NodeListT> +void +_computeEjkMean(const TinyVector<2>& Xj, + const NodeValue<const TinyVector<2>>& xr, + const NodeListT& node_list, + const CellType& cell_type, + const double Vi, + const size_t degree, + const size_t basis_dimension, + const SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + switch (cell_type) { + case CellType::Triangle: { + const TriangleTransformation<2> T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]]}; + const auto& quadrature = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + break; + } + case CellType::Quadrangle: { + const SquareTransformation<2> T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]]}; + const auto& quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } +} + +template <typename NodeListT> +void +_computeEjkMeanInSymmetricCell(const TinyVector<2>& origin, + const TinyVector<2>& normal, + const TinyVector<2>& Xj, + const NodeValue<const TinyVector<2>>& xr, + const NodeListT& node_list, + const CellType& cell_type, + const double Vi, + const size_t degree, + const size_t basis_dimension, + const SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + switch (cell_type) { + case CellType::Triangle: { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + + const TriangleTransformation<2> T{x0, x1, x2}; + const auto& quadrature = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + break; + } + case CellType::Quadrangle: { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + + const SquareTransformation<2> T{x0, x1, x2, x3}; + const auto& quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } +} + +template <typename NodeListT> +void +_computeEjkMean(const TinyVector<3>& Xj, + const NodeValue<const TinyVector<3>>& xr, + const NodeListT& node_list, + const CellType& cell_type, + const double Vi, + const size_t degree, + const size_t basis_dimension, + const SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + if (cell_type == CellType::Tetrahedron) { + const TetrahedronTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]]}; + + const auto& quadrature = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else if (cell_type == CellType::Prism) { + const PrismTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], // + xr[node_list[3]], xr[node_list[4]], xr[node_list[5]]}; + + const auto& quadrature = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else if (cell_type == CellType::Pyramid) { + const PyramidTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]], + xr[node_list[4]]}; + + const auto& quadrature = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else if (cell_type == CellType::Hexahedron) { + const CubeTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]], + xr[node_list[4]], xr[node_list[5]], xr[node_list[6]], xr[node_list[7]]}; + + const auto& quadrature = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } +} + +template <typename NodeListT> +void +_computeEjkMeanInSymmetricCell(const TinyVector<3>& origin, + const TinyVector<3>& normal, + const TinyVector<3>& Xj, + const NodeValue<const TinyVector<3>>& xr, + const NodeListT& node_list, + const CellType& cell_type, + const double Vi, + const size_t degree, + const size_t basis_dimension, + const SmallArray<double>& inv_Vj_wq_detJ_ek, + SmallArray<double>& mean_of_ejk) +{ + if (cell_type == CellType::Tetrahedron) { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); + const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); + + const TetrahedronTransformation T{x0, x1, x2, x3}; + + const auto& quadrature = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else if (cell_type == CellType::Prism) { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); + const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); + const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); + const auto x5 = symmetrize_coordinates(origin, normal, xr[node_list[5]]); + + const PrismTransformation T{x0, x1, x2, // + x3, x4, x5}; + + const auto& quadrature = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else if (cell_type == CellType::Pyramid) { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); + const PyramidTransformation T{x0, x1, x2, x3, x4}; + + const auto& quadrature = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else if (cell_type == CellType::Hexahedron) { + const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); + const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[7]]); + const auto x5 = symmetrize_coordinates(origin, normal, xr[node_list[6]]); + const auto x6 = symmetrize_coordinates(origin, normal, xr[node_list[5]]); + const auto x7 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); + + const CubeTransformation T{x0, x1, x2, x3, x4, x5, x6, x7}; + + const auto& quadrature = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } +} + +template <MeshConcept MeshType> +class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder +{ + private: + using Rd = TinyVector<MeshType::Dimension>; + + const size_t m_basis_dimension; + const size_t m_polynomial_degree; + + const SmallArray<double> m_inv_Vj_wq_detJ_ek; + SmallArray<double> m_mean_j_of_ejk; + SmallArray<double> m_mean_i_of_ejk; + + const ItemToItemMatrix<ItemType::cell, ItemType::node> m_cell_to_node_matrix; + const CellToCellStencilArray& m_stencil_array; + + const SmallArray<const Rd> m_symmetry_origin_list; + const SmallArray<const Rd> m_symmetry_normal_list; + + const CellValue<const CellType> m_cell_type; + const CellValue<const double> m_Vj; + const CellValue<const Rd> m_xj; + const NodeValue<const Rd> m_xr; + + public: + template <typename MatrixType> + void + build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) + { + const auto& stencil_cell_list = m_stencil_array[cell_j_id]; + + const Rd& Xj = m_xj[cell_j_id]; + + _computeEjkMean(Xj, m_xr, m_cell_to_node_matrix[cell_j_id], m_cell_type[cell_j_id], m_Vj[cell_j_id], + m_polynomial_degree, m_basis_dimension, m_inv_Vj_wq_detJ_ek, m_mean_j_of_ejk); + + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + + _computeEjkMean(Xj, m_xr, m_cell_to_node_matrix[cell_i_id], m_cell_type[cell_i_id], m_Vj[cell_i_id], + m_polynomial_degree, m_basis_dimension, m_inv_Vj_wq_detJ_ek, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + + for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { + auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = m_symmetry_origin_list[i_symmetry]; + const Rd& normal = m_symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + + _computeEjkMeanInSymmetricCell(origin, normal, // + Xj, m_xr, m_cell_to_node_matrix[cell_i_id], m_cell_type[cell_i_id], + m_Vj[cell_i_id], m_polynomial_degree, m_basis_dimension, m_inv_Vj_wq_detJ_ek, + m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + } + } + + ElementIntegralReconstructionMatrixBuilder(const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<double>& inv_Vj_wq_detJ_ek, + const SmallArray<double>& mean_j_of_ejk, + const SmallArray<double>& mean_i_of_ejk, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array) + : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree( + polynomial_degree)}, + m_polynomial_degree{polynomial_degree}, + + m_inv_Vj_wq_detJ_ek{inv_Vj_wq_detJ_ek}, + m_mean_j_of_ejk{mean_j_of_ejk}, + m_mean_i_of_ejk{mean_i_of_ejk}, + + m_cell_to_node_matrix{mesh.connectivity().cellToNodeMatrix()}, + m_stencil_array{stencil_array}, + m_symmetry_origin_list{symmetry_origin_list}, + m_symmetry_normal_list{symmetry_normal_list}, + m_cell_type{mesh.connectivity().cellType()}, + m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, + m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, + m_xr{mesh.xr()} + {} + + ~ElementIntegralReconstructionMatrixBuilder() = default; +}; + template <typename ConformTransformationT> void _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, const ConformTransformationT& T, @@ -421,7 +661,7 @@ void _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, const double Vi, const size_t degree, const size_t dimension, - SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + const SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, SmallArray<double>& mean_of_ejk); template <> @@ -432,7 +672,7 @@ _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, const double Vi, const size_t degree, const size_t dimension, - SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + const SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, SmallArray<double>& mean_of_ejk) { using Rd = TinyVector<2>; @@ -481,7 +721,7 @@ _computeEjkMeanByBoundary(const MeshType& mesh, const CellValue<const double>& Vi, const size_t degree, const size_t basis_dimension, - SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + const SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, SmallArray<double>& mean_of_ejk) { const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(degree + 1)); @@ -518,7 +758,7 @@ _computeEjkMeanByBoundaryInSymmetricCell(const TinyVector<2>& origin, const CellValue<const double>& Vi, const size_t degree, const size_t basis_dimension, - SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + const SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, SmallArray<double>& mean_of_ejk) { const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(degree + 1)); @@ -540,214 +780,234 @@ _computeEjkMeanByBoundaryInSymmetricCell(const TinyVector<2>& origin, inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); } else { const LineTransformation<2> T{x0, x1}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } - } -} - -template <typename NodeListT> -void -_computeEjkMean(const TinyVector<2>& Xj, - const NodeValue<const TinyVector<2>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - switch (cell_type) { - case CellType::Triangle: { - const TriangleTransformation<2> T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]]}; - const auto& quadrature = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - break; - } - case CellType::Quadrangle: { - const SquareTransformation<2> T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]]}; - const auto& quadrature = - QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - break; - } - default: { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } - } -} - -template <typename NodeListT> -void -_computeEjkMeanInSymmetricCell(const TinyVector<2>& origin, - const TinyVector<2>& normal, - const TinyVector<2>& Xj, - const NodeValue<const TinyVector<2>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - switch (cell_type) { - case CellType::Triangle: { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - - const TriangleTransformation<2> T{x0, x1, x2}; - const auto& quadrature = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - break; - } - case CellType::Quadrangle: { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - - const SquareTransformation<2> T{x0, x1, x2, x3}; - const auto& quadrature = - QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - break; - } - default: { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } + _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, + inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); + } } } -template <typename NodeListT> -void -_computeEjkMean(const TinyVector<3>& Xj, - const NodeValue<const TinyVector<3>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) +template <MeshConcept MeshType> +class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder { - if (cell_type == CellType::Tetrahedron) { - const TetrahedronTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]]}; + private: + using Rd = TinyVector<MeshType::Dimension>; - const auto& quadrature = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{degree}); + const MeshType& m_mesh; + const size_t m_basis_dimension; + const size_t m_polynomial_degree; - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + const SmallArray<double> m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek; + SmallArray<double> m_mean_j_of_ejk; + SmallArray<double> m_mean_i_of_ejk; - } else if (cell_type == CellType::Prism) { - const PrismTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], // - xr[node_list[3]], xr[node_list[4]], xr[node_list[5]]}; + const ItemToItemMatrix<ItemType::cell, ItemType::face> m_cell_to_face_matrix; + const CellToCellStencilArray& m_stencil_array; - const auto& quadrature = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{degree + 1}); + const SmallArray<const Rd> m_symmetry_origin_list; + const SmallArray<const Rd> m_symmetry_normal_list; - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + const CellValue<const double> m_Vj; + const CellValue<const Rd> m_xj; + const NodeValue<const Rd> m_xr; - } else if (cell_type == CellType::Pyramid) { - const PyramidTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]], - xr[node_list[4]]}; + public: + template <typename MatrixType> + void + build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) + { + const auto& stencil_cell_list = m_stencil_array[cell_j_id]; - const auto& quadrature = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{degree + 1}); + auto face_to_node_matrix = m_mesh.connectivity().faceToNodeMatrix(); + auto cell_face_is_reversed = m_mesh.connectivity().cellFaceIsReversed(); + const Rd& Xj = m_xj[cell_j_id]; - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + _computeEjkMeanByBoundary(m_mesh, Xj, cell_j_id, m_cell_to_face_matrix, face_to_node_matrix, cell_face_is_reversed, + m_Vj, m_polynomial_degree, m_basis_dimension, m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + m_mean_j_of_ejk); - } else if (cell_type == CellType::Hexahedron) { - const CubeTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]], - xr[node_list[4]], xr[node_list[5]], xr[node_list[6]], xr[node_list[7]]}; + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; - const auto& quadrature = - QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{degree + 1}); + _computeEjkMeanByBoundary(m_mesh, Xj, cell_i_id, m_cell_to_face_matrix, face_to_node_matrix, + cell_face_is_reversed, m_Vj, m_polynomial_degree, m_basis_dimension, + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek, m_mean_i_of_ejk); - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { + auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } -} + const Rd& origin = m_symmetry_origin_list[i_symmetry]; + const Rd& normal = m_symmetry_normal_list[i_symmetry]; -template <typename NodeListT> -void -_computeEjkMeanInSymmetricCell(const TinyVector<3>& origin, - const TinyVector<3>& normal, - const TinyVector<3>& Xj, - const NodeValue<const TinyVector<3>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - if (cell_type == CellType::Tetrahedron) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; - const TetrahedronTransformation T{x0, x1, x2, x3}; + _computeEjkMeanByBoundaryInSymmetricCell(origin, normal, // + Xj, cell_i_id, m_xr, m_cell_to_face_matrix, face_to_node_matrix, + cell_face_is_reversed, m_Vj, m_polynomial_degree, m_basis_dimension, + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek, m_mean_i_of_ejk); - const auto& quadrature = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{degree}); + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + } + } - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + BoundaryIntegralReconstructionMatrixBuilder(const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + const SmallArray<double>& mean_j_of_ejk, + const SmallArray<double>& mean_i_of_ejk, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array) + : m_mesh{mesh}, + m_basis_dimension{ + DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(polynomial_degree)}, + m_polynomial_degree{polynomial_degree}, + + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek{inv_Vj_alpha_p_1_wq_X_prime_orth_ek}, + m_mean_j_of_ejk{mean_j_of_ejk}, + m_mean_i_of_ejk{mean_i_of_ejk}, + + m_cell_to_face_matrix{mesh.connectivity().cellToFaceMatrix()}, + m_stencil_array{stencil_array}, + m_symmetry_origin_list{symmetry_origin_list}, + m_symmetry_normal_list{symmetry_normal_list}, + m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, + m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, + m_xr{mesh.xr()} + {} + BoundaryIntegralReconstructionMatrixBuilder() = default; + ~BoundaryIntegralReconstructionMatrixBuilder() = default; +}; - } else if (cell_type == CellType::Prism) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); - const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - const auto x5 = symmetrize_coordinates(origin, normal, xr[node_list[5]]); +class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant +{ + public: + using Variant = std::variant<DiscreteFunctionDPk<1, double>, + DiscreteFunctionDPk<1, TinyVector<1>>, + DiscreteFunctionDPk<1, TinyVector<2>>, + DiscreteFunctionDPk<1, TinyVector<3>>, + DiscreteFunctionDPk<1, TinyMatrix<1>>, + DiscreteFunctionDPk<1, TinyMatrix<2>>, + DiscreteFunctionDPk<1, TinyMatrix<3>>, - const PrismTransformation T{x0, x1, x2, // - x3, x4, x5}; + DiscreteFunctionDPk<2, double>, + DiscreteFunctionDPk<2, TinyVector<1>>, + DiscreteFunctionDPk<2, TinyVector<2>>, + DiscreteFunctionDPk<2, TinyVector<3>>, + DiscreteFunctionDPk<2, TinyMatrix<1>>, + DiscreteFunctionDPk<2, TinyMatrix<2>>, + DiscreteFunctionDPk<2, TinyMatrix<3>>, - const auto& quadrature = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{degree + 1}); + DiscreteFunctionDPk<3, double>, + DiscreteFunctionDPk<3, TinyVector<1>>, + DiscreteFunctionDPk<3, TinyVector<2>>, + DiscreteFunctionDPk<3, TinyVector<3>>, + DiscreteFunctionDPk<3, TinyMatrix<1>>, + DiscreteFunctionDPk<3, TinyMatrix<2>>, + DiscreteFunctionDPk<3, TinyMatrix<3>>, - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + DiscreteFunctionDPkVector<1, double>, + DiscreteFunctionDPkVector<1, TinyVector<1>>, + DiscreteFunctionDPkVector<1, TinyVector<2>>, + DiscreteFunctionDPkVector<1, TinyVector<3>>, + DiscreteFunctionDPkVector<1, TinyMatrix<1>>, + DiscreteFunctionDPkVector<1, TinyMatrix<2>>, + DiscreteFunctionDPkVector<1, TinyMatrix<3>>, - } else if (cell_type == CellType::Pyramid) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); - const PyramidTransformation T{x0, x1, x2, x3, x4}; + DiscreteFunctionDPkVector<2, double>, + DiscreteFunctionDPkVector<2, TinyVector<1>>, + DiscreteFunctionDPkVector<2, TinyVector<2>>, + DiscreteFunctionDPkVector<2, TinyVector<3>>, + DiscreteFunctionDPkVector<2, TinyMatrix<1>>, + DiscreteFunctionDPkVector<2, TinyMatrix<2>>, + DiscreteFunctionDPkVector<2, TinyMatrix<3>>, - const auto& quadrature = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{degree + 1}); + DiscreteFunctionDPkVector<3, double>, + DiscreteFunctionDPkVector<3, TinyVector<1>>, + DiscreteFunctionDPkVector<3, TinyVector<2>>, + DiscreteFunctionDPkVector<3, TinyVector<3>>, + DiscreteFunctionDPkVector<3, TinyMatrix<1>>, + DiscreteFunctionDPkVector<3, TinyMatrix<2>>, + DiscreteFunctionDPkVector<3, TinyMatrix<3>>>; - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + private: + Variant m_mutable_discrete_function_dpk; - } else if (cell_type == CellType::Hexahedron) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[7]]); - const auto x5 = symmetrize_coordinates(origin, normal, xr[node_list[6]]); - const auto x6 = symmetrize_coordinates(origin, normal, xr[node_list[5]]); - const auto x7 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); + public: + PUGS_INLINE + const Variant& + mutableDiscreteFunctionDPk() const + { + return m_mutable_discrete_function_dpk; + } - const CubeTransformation T{x0, x1, x2, x3, x4, x5, x6, x7}; + template <typename DiscreteFunctionDPkT> + PUGS_INLINE auto&& + get() const + { + static_assert(is_discrete_function_dpk_v<DiscreteFunctionDPkT>, "invalid template argument"); +#ifndef NDEBUG + if (not std::holds_alternative<DiscreteFunctionDPkT>(this->m_mutable_discrete_function_dpk)) { + std::ostringstream error_msg; + error_msg << "invalid discrete function type\n"; + error_msg << "- required " << rang::fgB::red << demangle<DiscreteFunctionDPkT>() << rang::fg::reset << '\n'; + error_msg << "- contains " << rang::fgB::yellow + << std::visit([](auto&& f) -> std::string { return demangle<decltype(f)>(); }, + this->m_mutable_discrete_function_dpk) + << rang::fg::reset; + throw NormalError(error_msg.str()); + } +#endif // NDEBUG - const auto& quadrature = - QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{degree + 1}); + return std::get<DiscreteFunctionDPkT>(this->mutableDiscreteFunctionDPk()); + } - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); + template <size_t Dimension, typename DataType> + MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPk<Dimension, DataType>& discrete_function_dpk) + : m_mutable_discrete_function_dpk{discrete_function_dpk} + { + static_assert(std::is_same_v<DataType, double> or // + std::is_same_v<DataType, TinyVector<1, double>> or // + std::is_same_v<DataType, TinyVector<2, double>> or // + std::is_same_v<DataType, TinyVector<3, double>> or // + std::is_same_v<DataType, TinyMatrix<1, 1, double>> or // + std::is_same_v<DataType, TinyMatrix<2, 2, double>> or // + std::is_same_v<DataType, TinyMatrix<3, 3, double>>, + "DiscreteFunctionDPk with this DataType is not allowed in variant"); + } - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + template <size_t Dimension, typename DataType> + MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) + : m_mutable_discrete_function_dpk{discrete_function_dpk} + { + static_assert(std::is_same_v<DataType, double> or // + std::is_same_v<DataType, TinyVector<1, double>> or // + std::is_same_v<DataType, TinyVector<2, double>> or // + std::is_same_v<DataType, TinyVector<3, double>> or // + std::is_same_v<DataType, TinyMatrix<1, 1, double>> or // + std::is_same_v<DataType, TinyMatrix<2, 2, double>> or // + std::is_same_v<DataType, TinyMatrix<3, 3, double>>, + "DiscreteFunctionDPkVector with this DataType is not allowed in variant"); } -} + + MutableDiscreteFunctionDPkVariant& operator=(MutableDiscreteFunctionDPkVariant&&) = default; + MutableDiscreteFunctionDPkVariant& operator=(const MutableDiscreteFunctionDPkVariant&) = default; + + MutableDiscreteFunctionDPkVariant(const MutableDiscreteFunctionDPkVariant&) = default; + MutableDiscreteFunctionDPkVariant(MutableDiscreteFunctionDPkVariant&&) = default; + + MutableDiscreteFunctionDPkVariant() = delete; + ~MutableDiscreteFunctionDPkVariant() = default; +}; size_t PolynomialReconstruction::_getNumberOfColumns( @@ -893,7 +1153,6 @@ PolynomialReconstruction::_build( auto cell_is_owned = mesh.connectivity().cellIsOwned(); auto cell_type = mesh.connectivity().cellType(); - auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix(); auto cell_to_face_matrix = mesh.connectivity().cellToFaceMatrix(); auto full_stencil_size = [&](const CellId cell_id) { @@ -959,7 +1218,6 @@ PolynomialReconstruction::_build( SmallArray<SmallArray<double>> mean_i_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallArray<double>> inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallArray<double>> mean_l_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); for (size_t i = 0; i < A_pool.size(); ++i) { A_pool[i] = SmallMatrix<double>(max_stencil_size, basis_dimension - 1); @@ -974,6 +1232,10 @@ PolynomialReconstruction::_build( inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[i] = SmallArray<double>(basis_dimension); } + std::shared_ptr p_cell_center_reconstruction_matrix_builder = + std::make_shared<CellCenterReconstructionMatrixBuilder<MeshType>>(symmetry_origin_list, symmetry_normal_list, + stencil_array, xj); + parallel_for( mesh.numberOfCells(), PUGS_CLASS_LAMBDA(const CellId cell_j_id) { if (cell_is_owned[cell_j_id]) { @@ -1215,32 +1477,7 @@ PolynomialReconstruction::_build( if ((m_descriptor.degree() == 1) and (m_descriptor.integrationMethodType() == IntegrationMethodType::cell_center)) { - const Rd& Xj = xj[cell_j_id]; - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - const Rd Xi_Xj = xj[cell_i_id] - Xj; - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) = Xi_Xj[l]; - } - } - for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); - ++i_symmetry) { - auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - - const Rd& origin = symmetry_origin_list[i_symmetry]; - const Rd& normal = symmetry_normal_list[i_symmetry]; - - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - const Rd Xi_Xj = symmetrize_coordinates(origin, normal, xj[cell_i_id]) - Xj; - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) = Xi_Xj[l]; - } - } - } - + p_cell_center_reconstruction_matrix_builder->build(cell_j_id, A); } else if ((m_descriptor.integrationMethodType() == IntegrationMethodType::element) or (m_descriptor.integrationMethodType() == IntegrationMethodType::boundary)) { #warning implement boundary based reconstruction for 1d and 3d @@ -1251,6 +1488,14 @@ PolynomialReconstruction::_build( SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; + BoundaryIntegralReconstructionMatrixBuilder + boundary_integral_reconstruction_matrix_builder(*p_mesh, m_descriptor.degree(), + inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_j_of_ejk, + mean_i_of_ejk, symmetry_origin_list, + symmetry_normal_list, stencil_array); + + boundary_integral_reconstruction_matrix_builder.build(cell_j_id, A); + auto face_to_node_matrix = p_mesh->connectivity().faceToNodeMatrix(); auto cell_face_is_reversed = p_mesh->connectivity().cellFaceIsReversed(); const Rd& Xj = xj[cell_j_id]; @@ -1297,48 +1542,18 @@ PolynomialReconstruction::_build( throw UnexpectedError("invalid mesh dimension"); } } else { + +#warning incorporate in reconstruction_matrix_builder SmallArray<double>& inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t]; SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; - const Rd& Xj = xj[cell_j_id]; + ElementIntegralReconstructionMatrixBuilder + element_integral_reconstruction_matrix_builder(*p_mesh, m_descriptor.degree(), inv_Vj_wq_detJ_ek, + mean_j_of_ejk, mean_i_of_ejk, symmetry_origin_list, + symmetry_normal_list, stencil_array); - _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_j_id], cell_type[cell_j_id], Vj[cell_j_id], - m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_j_of_ejk); - - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - - _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], Vj[cell_i_id], - m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_i_of_ejk); - - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; - } - } - - for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); - ++i_symmetry) { - auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - - const Rd& origin = symmetry_origin_list[i_symmetry]; - const Rd& normal = symmetry_normal_list[i_symmetry]; - - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - - _computeEjkMeanInSymmetricCell(origin, normal, // - Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], - Vj[cell_i_id], m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, - mean_i_of_ejk); - - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; - } - } - } + element_integral_reconstruction_matrix_builder.build(cell_j_id, A); } } else { throw UnexpectedError("invalid integration strategy"); diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index bf6013639..349ae4577 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -12,6 +12,15 @@ class PolynomialReconstruction private: class MutableDiscreteFunctionDPkVariant; + template <MeshConcept MeshType> + class CellCenterReconstructionMatrixBuilder; + + template <MeshConcept MeshType> + class ElementIntegralReconstructionMatrixBuilder; + + template <MeshConcept MeshType> + class BoundaryIntegralReconstructionMatrixBuilder; + const PolynomialReconstructionDescriptor m_descriptor; size_t _getNumberOfColumns( -- GitLab From e28c612ac893f0a355b5411b14a6df4dc2081440 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Mon, 5 May 2025 00:36:10 +0200 Subject: [PATCH 106/122] Continue code clean-up --- src/scheme/PolynomialReconstruction.cpp | 608 +----------------- .../CellCenterReconstructionMatrixBuilder.hpp | 71 ++ ...entIntegralReconstructionMatrixBuilder.hpp | 534 +++++++++++++++ 3 files changed, 608 insertions(+), 605 deletions(-) create mode 100644 src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp create mode 100644 src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 661630187..253233910 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -25,6 +25,9 @@ #include <scheme/DiscreteFunctionUtils.hpp> #include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp> +#include <scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp> + template <size_t Dimension> PUGS_INLINE auto symmetrize_vector(const TinyVector<Dimension>& normal, const TinyVector<Dimension>& u) @@ -49,611 +52,6 @@ symmetrize_coordinates(const TinyVector<Dimension>& origin, return u - 2 * dot(u - origin, normal) * normal; } -template <MeshConcept MeshType> -class PolynomialReconstruction::CellCenterReconstructionMatrixBuilder -{ - private: - using Rd = TinyVector<MeshType::Dimension>; - - const size_t m_basis_dimension; - const SmallArray<const Rd> m_symmetry_origin_list; - const SmallArray<const Rd> m_symmetry_normal_list; - - const CellToCellStencilArray& m_stencil_array; - const CellValue<const Rd> m_xj; - - public: - template <typename MatrixType> - void - build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) - { - const auto& stencil_cell_list = m_stencil_array[cell_j_id]; - - const Rd& Xj = m_xj[cell_j_id]; - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - const Rd Xi_Xj = m_xj[cell_i_id] - Xj; - for (size_t l = 0; l < m_basis_dimension - 1; ++l) { - A(index, l) = Xi_Xj[l]; - } - } - for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { - auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - - const Rd& origin = m_symmetry_origin_list[i_symmetry]; - const Rd& normal = m_symmetry_normal_list[i_symmetry]; - - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - const Rd Xi_Xj = symmetrize_coordinates(origin, normal, m_xj[cell_i_id]) - Xj; - for (size_t l = 0; l < m_basis_dimension - 1; ++l) { - A(index, l) = Xi_Xj[l]; - } - } - } - } - - CellCenterReconstructionMatrixBuilder(const SmallArray<const Rd>& symmetry_origin_list, - const SmallArray<const Rd>& symmetry_normal_list, - const CellToCellStencilArray& stencil_array, - const CellValue<const Rd>& xj) - : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(1)}, - m_symmetry_origin_list{symmetry_origin_list}, - m_symmetry_normal_list{symmetry_normal_list}, - m_stencil_array{stencil_array}, - m_xj{xj} - {} - - ~CellCenterReconstructionMatrixBuilder() = default; -}; - -template <typename ConformTransformationT> -void -_computeEjkMean(const QuadratureFormula<1>& quadrature, - const ConformTransformationT& T, - const TinyVector<1>& Xj, - const double Vi, - const size_t degree, - const size_t dimension, - const SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - using Rd = TinyVector<1>; - mean_of_ejk.fill(0); - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const Rd& xi_q = quadrature.point(i_q); - const double detT = T.jacobianDeterminant(); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - - { - size_t k = 0; - inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; - for (; k <= degree; ++k) { - inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; - } - } - - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; - } - } -} - -template <typename ConformTransformationT> -void -_computeEjkMean(const QuadratureFormula<2>& quadrature, - const ConformTransformationT& T, - const TinyVector<2>& Xj, - const double Vi, - const size_t degree, - const size_t dimension, - const SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - using Rd = TinyVector<2>; - - mean_of_ejk.fill(0); - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const Rd& xi_q = quadrature.point(i_q); - const double detT = [&] { - if constexpr (std::is_same_v<TriangleTransformation<2>, std::decay_t<decltype(T)>>) { - return T.jacobianDeterminant(); - } else { - return T.jacobianDeterminant(xi_q); - } - }(); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - const double y_yj = X_Xj[1]; - - { - size_t k = 0; - inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; - for (; k <= degree; ++k) { - inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; - } - - for (size_t i_y = 1; i_y <= degree; ++i_y) { -#warning store y_row index and size - const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; - for (size_t l = 0; l <= degree - i_y; ++l) { - inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; - } - } - } - - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; - } - } -} - -template <typename ConformTransformationT> -void -_computeEjkMean(const QuadratureFormula<3>& quadrature, - const ConformTransformationT& T, - const TinyVector<3>& Xj, - const double Vi, - const size_t degree, - const size_t dimension, - const SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ -#warning Compute this one for all and rework design - SmallArray<size_t> m_yz_row_index((degree + 2) * (degree + 1) / 2 + 1); - SmallArray<size_t> m_z_triangle_index(degree + 1); - - { - size_t i_z = 0; - size_t i_yz = 0; - - m_yz_row_index[i_yz++] = 0; - for (ssize_t n = degree + 1; n >= 1; --n) { - m_z_triangle_index[i_z++] = i_yz - 1; - for (ssize_t i = n; i >= 1; --i) { - m_yz_row_index[i_yz] = m_yz_row_index[i_yz - 1] + i; - ++i_yz; - } - } - } - - SmallArray<size_t> m_yz_row_size{m_yz_row_index.size() - 1}; - for (size_t i = 0; i < m_yz_row_size.size(); ++i) { - m_yz_row_size[i] = m_yz_row_index[i + 1] - m_yz_row_index[i]; - } - - using Rd = TinyVector<3>; - mean_of_ejk.fill(0); - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const Rd& xi_q = quadrature.point(i_q); - const double detT = [&] { - if constexpr (std::is_same_v<TetrahedronTransformation, std::decay_t<decltype(T)>>) { - return T.jacobianDeterminant(); - } else { - return T.jacobianDeterminant(xi_q); - } - }(); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - const double y_yj = X_Xj[1]; - const double z_zj = X_Xj[2]; - - { - size_t k = 0; - inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; - for (; k <= degree; ++k) { - inv_Vj_wq_detJ_ek[k] = x_xj * inv_Vj_wq_detJ_ek[k - 1]; - } - - for (size_t i_y = 1; i_y <= degree; ++i_y) { - const size_t begin_i_y_1 = m_yz_row_index[i_y - 1]; - const size_t nb_monoms = m_yz_row_size[i_y]; - for (size_t l = 0; l < nb_monoms; ++l) { - inv_Vj_wq_detJ_ek[k++] = y_yj * inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; - } - } - - for (size_t i_z = 1; i_z <= degree; ++i_z) { - const size_t nb_y = m_yz_row_size[m_z_triangle_index[i_z]]; - const size_t index_z = m_z_triangle_index[i_z]; - const size_t index_z_1 = m_z_triangle_index[i_z - 1]; - for (size_t i_y = 0; i_y < nb_y; ++i_y) { - const size_t begin_i_yz_1 = m_yz_row_index[index_z_1 + i_y]; - const size_t nb_monoms = m_yz_row_size[index_z + i_y]; - for (size_t l = 0; l < nb_monoms; ++l) { - inv_Vj_wq_detJ_ek[k++] = z_zj * inv_Vj_wq_detJ_ek[begin_i_yz_1 + l]; - } - } - } - } - - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_wq_detJ_ek[k]; - } - } -} - -template <typename NodeListT, size_t Dimension> -void _computeEjkMean(const TinyVector<Dimension>& Xj, - const NodeValue<const TinyVector<Dimension>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - const SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk); - -template <typename NodeListT> -void -_computeEjkMean(const TinyVector<1>& Xj, - const NodeValue<const TinyVector<1>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - const SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - if (cell_type == CellType::Line) { - const LineTransformation<1> T{xr[node_list[0]], xr[node_list[1]]}; - - const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } -} - -template <typename NodeListT> -void -_computeEjkMeanInSymmetricCell(const TinyVector<1>& origin, - const TinyVector<1>& normal, - const TinyVector<1>& Xj, - const NodeValue<const TinyVector<1>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - const SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - if (cell_type == CellType::Line) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - - const LineTransformation<1> T{x0, x1}; - - const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } -} - -template <typename NodeListT> -void -_computeEjkMean(const TinyVector<2>& Xj, - const NodeValue<const TinyVector<2>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - const SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - switch (cell_type) { - case CellType::Triangle: { - const TriangleTransformation<2> T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]]}; - const auto& quadrature = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - break; - } - case CellType::Quadrangle: { - const SquareTransformation<2> T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]]}; - const auto& quadrature = - QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - break; - } - default: { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } - } -} - -template <typename NodeListT> -void -_computeEjkMeanInSymmetricCell(const TinyVector<2>& origin, - const TinyVector<2>& normal, - const TinyVector<2>& Xj, - const NodeValue<const TinyVector<2>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - const SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - switch (cell_type) { - case CellType::Triangle: { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - - const TriangleTransformation<2> T{x0, x1, x2}; - const auto& quadrature = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - break; - } - case CellType::Quadrangle: { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - - const SquareTransformation<2> T{x0, x1, x2, x3}; - const auto& quadrature = - QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - break; - } - default: { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } - } -} - -template <typename NodeListT> -void -_computeEjkMean(const TinyVector<3>& Xj, - const NodeValue<const TinyVector<3>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - const SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - if (cell_type == CellType::Tetrahedron) { - const TetrahedronTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]]}; - - const auto& quadrature = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else if (cell_type == CellType::Prism) { - const PrismTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], // - xr[node_list[3]], xr[node_list[4]], xr[node_list[5]]}; - - const auto& quadrature = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else if (cell_type == CellType::Pyramid) { - const PyramidTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]], - xr[node_list[4]]}; - - const auto& quadrature = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else if (cell_type == CellType::Hexahedron) { - const CubeTransformation T{xr[node_list[0]], xr[node_list[1]], xr[node_list[2]], xr[node_list[3]], - xr[node_list[4]], xr[node_list[5]], xr[node_list[6]], xr[node_list[7]]}; - - const auto& quadrature = - QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } -} - -template <typename NodeListT> -void -_computeEjkMeanInSymmetricCell(const TinyVector<3>& origin, - const TinyVector<3>& normal, - const TinyVector<3>& Xj, - const NodeValue<const TinyVector<3>>& xr, - const NodeListT& node_list, - const CellType& cell_type, - const double Vi, - const size_t degree, - const size_t basis_dimension, - const SmallArray<double>& inv_Vj_wq_detJ_ek, - SmallArray<double>& mean_of_ejk) -{ - if (cell_type == CellType::Tetrahedron) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - - const TetrahedronTransformation T{x0, x1, x2, x3}; - - const auto& quadrature = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else if (cell_type == CellType::Prism) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); - const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - const auto x5 = symmetrize_coordinates(origin, normal, xr[node_list[5]]); - - const PrismTransformation T{x0, x1, x2, // - x3, x4, x5}; - - const auto& quadrature = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else if (cell_type == CellType::Pyramid) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); - const PyramidTransformation T{x0, x1, x2, x3, x4}; - - const auto& quadrature = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else if (cell_type == CellType::Hexahedron) { - const auto x0 = symmetrize_coordinates(origin, normal, xr[node_list[3]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[node_list[2]]); - const auto x2 = symmetrize_coordinates(origin, normal, xr[node_list[1]]); - const auto x3 = symmetrize_coordinates(origin, normal, xr[node_list[0]]); - const auto x4 = symmetrize_coordinates(origin, normal, xr[node_list[7]]); - const auto x5 = symmetrize_coordinates(origin, normal, xr[node_list[6]]); - const auto x6 = symmetrize_coordinates(origin, normal, xr[node_list[5]]); - const auto x7 = symmetrize_coordinates(origin, normal, xr[node_list[4]]); - - const CubeTransformation T{x0, x1, x2, x3, x4, x5, x6, x7}; - - const auto& quadrature = - QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, degree, basis_dimension, inv_Vj_wq_detJ_ek, mean_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } -} - -template <MeshConcept MeshType> -class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder -{ - private: - using Rd = TinyVector<MeshType::Dimension>; - - const size_t m_basis_dimension; - const size_t m_polynomial_degree; - - const SmallArray<double> m_inv_Vj_wq_detJ_ek; - SmallArray<double> m_mean_j_of_ejk; - SmallArray<double> m_mean_i_of_ejk; - - const ItemToItemMatrix<ItemType::cell, ItemType::node> m_cell_to_node_matrix; - const CellToCellStencilArray& m_stencil_array; - - const SmallArray<const Rd> m_symmetry_origin_list; - const SmallArray<const Rd> m_symmetry_normal_list; - - const CellValue<const CellType> m_cell_type; - const CellValue<const double> m_Vj; - const CellValue<const Rd> m_xj; - const NodeValue<const Rd> m_xr; - - public: - template <typename MatrixType> - void - build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) - { - const auto& stencil_cell_list = m_stencil_array[cell_j_id]; - - const Rd& Xj = m_xj[cell_j_id]; - - _computeEjkMean(Xj, m_xr, m_cell_to_node_matrix[cell_j_id], m_cell_type[cell_j_id], m_Vj[cell_j_id], - m_polynomial_degree, m_basis_dimension, m_inv_Vj_wq_detJ_ek, m_mean_j_of_ejk); - - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - - _computeEjkMean(Xj, m_xr, m_cell_to_node_matrix[cell_i_id], m_cell_type[cell_i_id], m_Vj[cell_i_id], - m_polynomial_degree, m_basis_dimension, m_inv_Vj_wq_detJ_ek, m_mean_i_of_ejk); - - for (size_t l = 0; l < m_basis_dimension - 1; ++l) { - A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; - } - } - - for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { - auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - - const Rd& origin = m_symmetry_origin_list[i_symmetry]; - const Rd& normal = m_symmetry_normal_list[i_symmetry]; - - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - - _computeEjkMeanInSymmetricCell(origin, normal, // - Xj, m_xr, m_cell_to_node_matrix[cell_i_id], m_cell_type[cell_i_id], - m_Vj[cell_i_id], m_polynomial_degree, m_basis_dimension, m_inv_Vj_wq_detJ_ek, - m_mean_i_of_ejk); - - for (size_t l = 0; l < m_basis_dimension - 1; ++l) { - A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; - } - } - } - } - - ElementIntegralReconstructionMatrixBuilder(const MeshType& mesh, - const size_t polynomial_degree, - const SmallArray<double>& inv_Vj_wq_detJ_ek, - const SmallArray<double>& mean_j_of_ejk, - const SmallArray<double>& mean_i_of_ejk, - const SmallArray<const Rd>& symmetry_origin_list, - const SmallArray<const Rd>& symmetry_normal_list, - const CellToCellStencilArray& stencil_array) - : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree( - polynomial_degree)}, - m_polynomial_degree{polynomial_degree}, - - m_inv_Vj_wq_detJ_ek{inv_Vj_wq_detJ_ek}, - m_mean_j_of_ejk{mean_j_of_ejk}, - m_mean_i_of_ejk{mean_i_of_ejk}, - - m_cell_to_node_matrix{mesh.connectivity().cellToNodeMatrix()}, - m_stencil_array{stencil_array}, - m_symmetry_origin_list{symmetry_origin_list}, - m_symmetry_normal_list{symmetry_normal_list}, - m_cell_type{mesh.connectivity().cellType()}, - m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, - m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, - m_xr{mesh.xr()} - {} - - ~ElementIntegralReconstructionMatrixBuilder() = default; -}; - template <typename ConformTransformationT> void _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, const ConformTransformationT& T, diff --git a/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp new file mode 100644 index 000000000..9f3ee9524 --- /dev/null +++ b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp @@ -0,0 +1,71 @@ +#ifndef CELL_CENTER_RECONSTRUCTION_MATRIX_BUILDER_HPP +#define CELL_CENTER_RECONSTRUCTION_MATRIX_BUILDER_HPP + +#include <algebra/ShrinkMatrixView.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/StencilArray.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> +#include <scheme/PolynomialReconstruction.hpp> +#include <utils/SmallArray.hpp> + +template <MeshConcept MeshType> +class PolynomialReconstruction::CellCenterReconstructionMatrixBuilder +{ + private: + using Rd = TinyVector<MeshType::Dimension>; + + const size_t m_basis_dimension; + const SmallArray<const Rd> m_symmetry_origin_list; + const SmallArray<const Rd> m_symmetry_normal_list; + + const CellToCellStencilArray& m_stencil_array; + const CellValue<const Rd> m_xj; + + public: + template <typename MatrixType> + void + build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) + { + const auto& stencil_cell_list = m_stencil_array[cell_j_id]; + + const Rd& Xj = m_xj[cell_j_id]; + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + const Rd Xi_Xj = m_xj[cell_i_id] - Xj; + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = Xi_Xj[l]; + } + } + for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { + auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = m_symmetry_origin_list[i_symmetry]; + const Rd& normal = m_symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + const Rd Xi_Xj = symmetrize_coordinates(origin, normal, m_xj[cell_i_id]) - Xj; + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = Xi_Xj[l]; + } + } + } + } + + CellCenterReconstructionMatrixBuilder(const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array, + const CellValue<const Rd>& xj) + : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(1)}, + m_symmetry_origin_list{symmetry_origin_list}, + m_symmetry_normal_list{symmetry_normal_list}, + m_stencil_array{stencil_array}, + m_xj{xj} + {} + + ~CellCenterReconstructionMatrixBuilder() = default; +}; + +#endif // CELL_CENTER_RECONSTRUCTION_MATRIX_BUILDER_HPP diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp new file mode 100644 index 000000000..8477f57ea --- /dev/null +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp @@ -0,0 +1,534 @@ +#ifndef ELEMENT_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP +#define ELEMENT_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP + +#include <algebra/ShrinkMatrixView.hpp> +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/CubeTransformation.hpp> +#include <geometry/LineTransformation.hpp> +#include <geometry/PrismTransformation.hpp> +#include <geometry/PyramidTransformation.hpp> +#include <geometry/SquareTransformation.hpp> +#include <geometry/TetrahedronTransformation.hpp> +#include <geometry/TriangleTransformation.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/StencilArray.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> +#include <scheme/PolynomialReconstruction.hpp> +#include <utils/SmallArray.hpp> + +template <MeshConcept MeshType> +class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder +{ + private: + using Rd = TinyVector<MeshType::Dimension>; + + const size_t m_basis_dimension; + const size_t m_polynomial_degree; + + const SmallArray<double> m_inv_Vj_wq_detJ_ek; + SmallArray<double> m_mean_j_of_ejk; + SmallArray<double> m_mean_i_of_ejk; + + const ItemToItemMatrix<ItemType::cell, ItemType::node> m_cell_to_node_matrix; + const CellToCellStencilArray& m_stencil_array; + + const SmallArray<const Rd> m_symmetry_origin_list; + const SmallArray<const Rd> m_symmetry_normal_list; + + const CellValue<const CellType> m_cell_type; + const CellValue<const double> m_Vj; + const CellValue<const Rd> m_xj; + const NodeValue<const Rd> m_xr; + + template <typename ConformTransformationT> + void + _computeEjkMean(const QuadratureFormula<1>& quadrature, + const ConformTransformationT& T, + const TinyVector<1>& Xj, + const double Vi, + SmallArray<double>& mean_of_ejk) + { + mean_of_ejk.fill(0); + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const Rd& xi_q = quadrature.point(i_q); + const double detT = T.jacobianDeterminant(); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + + { + size_t k = 0; + m_inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; + for (; k <= m_polynomial_degree; ++k) { + m_inv_Vj_wq_detJ_ek[k] = x_xj * m_inv_Vj_wq_detJ_ek[k - 1]; + } + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] += m_inv_Vj_wq_detJ_ek[k]; + } + } + } + + template <typename ConformTransformationT> + void + _computeEjkMean(const QuadratureFormula<2>& quadrature, + const ConformTransformationT& T, + const TinyVector<2>& Xj, + const double Vi, + SmallArray<double>& mean_of_ejk) + { + mean_of_ejk.fill(0); + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const Rd& xi_q = quadrature.point(i_q); + const double detT = [&] { + if constexpr (std::is_same_v<TriangleTransformation<2>, std::decay_t<decltype(T)>>) { + return T.jacobianDeterminant(); + } else { + return T.jacobianDeterminant(xi_q); + } + }(); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + + { + size_t k = 0; + m_inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; + for (; k <= m_polynomial_degree; ++k) { + m_inv_Vj_wq_detJ_ek[k] = x_xj * m_inv_Vj_wq_detJ_ek[k - 1]; + } + + for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { +#warning store y_row index and size + const size_t begin_i_y_1 = ((i_y - 1) * (2 * m_polynomial_degree - i_y + 4)) / 2; + for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l) { + m_inv_Vj_wq_detJ_ek[k++] = y_yj * m_inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; + } + } + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] += m_inv_Vj_wq_detJ_ek[k]; + } + } + } + + template <typename ConformTransformationT> + void + _computeEjkMean(const QuadratureFormula<3>& quadrature, + const ConformTransformationT& T, + const TinyVector<3>& Xj, + const double Vi, + SmallArray<double>& mean_of_ejk) + { +#warning Compute this one for all and rework design + SmallArray<size_t> m_yz_row_index((m_polynomial_degree + 2) * (m_polynomial_degree + 1) / 2 + 1); + SmallArray<size_t> m_z_triangle_index(m_polynomial_degree + 1); + + { + size_t i_z = 0; + size_t i_yz = 0; + + m_yz_row_index[i_yz++] = 0; + for (ssize_t n = m_polynomial_degree + 1; n >= 1; --n) { + m_z_triangle_index[i_z++] = i_yz - 1; + for (ssize_t i = n; i >= 1; --i) { + m_yz_row_index[i_yz] = m_yz_row_index[i_yz - 1] + i; + ++i_yz; + } + } + } + + SmallArray<size_t> m_yz_row_size{m_yz_row_index.size() - 1}; + for (size_t i = 0; i < m_yz_row_size.size(); ++i) { + m_yz_row_size[i] = m_yz_row_index[i + 1] - m_yz_row_index[i]; + } + + mean_of_ejk.fill(0); + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const Rd& xi_q = quadrature.point(i_q); + const double detT = [&] { + if constexpr (std::is_same_v<TetrahedronTransformation, std::decay_t<decltype(T)>>) { + return T.jacobianDeterminant(); + } else { + return T.jacobianDeterminant(xi_q); + } + }(); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + const double z_zj = X_Xj[2]; + + { + size_t k = 0; + m_inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; + for (; k <= m_polynomial_degree; ++k) { + m_inv_Vj_wq_detJ_ek[k] = x_xj * m_inv_Vj_wq_detJ_ek[k - 1]; + } + + for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { + const size_t begin_i_y_1 = m_yz_row_index[i_y - 1]; + const size_t nb_monoms = m_yz_row_size[i_y]; + for (size_t l = 0; l < nb_monoms; ++l) { + m_inv_Vj_wq_detJ_ek[k++] = y_yj * m_inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; + } + } + + for (size_t i_z = 1; i_z <= m_polynomial_degree; ++i_z) { + const size_t nb_y = m_yz_row_size[m_z_triangle_index[i_z]]; + const size_t index_z = m_z_triangle_index[i_z]; + const size_t index_z_1 = m_z_triangle_index[i_z - 1]; + for (size_t i_y = 0; i_y < nb_y; ++i_y) { + const size_t begin_i_yz_1 = m_yz_row_index[index_z_1 + i_y]; + const size_t nb_monoms = m_yz_row_size[index_z + i_y]; + for (size_t l = 0; l < nb_monoms; ++l) { + m_inv_Vj_wq_detJ_ek[k++] = z_zj * m_inv_Vj_wq_detJ_ek[begin_i_yz_1 + l]; + } + } + } + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] += m_inv_Vj_wq_detJ_ek[k]; + } + } + } + + void + _computeEjkMean(const TinyVector<1>& Xj, const CellId& cell_i_id, SmallArray<double>& mean_of_ejk) + { + const CellType cell_type = m_cell_type[cell_i_id]; + const auto node_list = m_cell_to_node_matrix[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + + if (m_cell_type[cell_i_id] == CellType::Line) { + const LineTransformation<1> T{m_xr[node_list[0]], m_xr[node_list[1]]}; + + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + + void + _computeEjkMean(const TinyVector<2>& Xj, const CellId& cell_i_id, SmallArray<double>& mean_of_ejk) + { + const CellType cell_type = m_cell_type[cell_i_id]; + const auto node_list = m_cell_to_node_matrix[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + + switch (cell_type) { + case CellType::Triangle: { + const TriangleTransformation<2> T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]]}; + const auto& quadrature = + QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_polynomial_degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Quadrangle: { + const SquareTransformation<2> T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]]}; + const auto& quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + } + + void + _computeEjkMean(const TinyVector<3>& Xj, const CellId& cell_i_id, SmallArray<double>& mean_of_ejk) + { + const CellType cell_type = m_cell_type[cell_i_id]; + const auto node_list = m_cell_to_node_matrix[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + + switch (cell_type) { + case CellType::Tetrahedron: { + const TetrahedronTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]]}; + + const auto& quadrature = + QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_polynomial_degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + break; + } + case CellType::Prism: { + const PrismTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], // + m_xr[node_list[3]], m_xr[node_list[4]], m_xr[node_list[5]]}; + + const auto& quadrature = + QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + break; + } + case CellType::Pyramid: { + const PyramidTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]], + m_xr[node_list[4]]}; + + const auto& quadrature = + QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Hexahedron: { + const CubeTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]], + m_xr[node_list[4]], m_xr[node_list[5]], m_xr[node_list[6]], m_xr[node_list[7]]}; + + const auto& quadrature = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + } + + void + _computeEjkMeanInSymmetricCell(const TinyVector<1>& origin, + const TinyVector<1>& normal, + const TinyVector<1>& Xj, + const CellId& cell_i_id, + SmallArray<double>& mean_of_ejk) + { + auto node_list = m_cell_to_node_matrix[cell_i_id]; + const CellType cell_type = m_cell_type[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + + if (cell_type == CellType::Line) { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + + const LineTransformation<1> T{x0, x1}; + + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + + void + _computeEjkMeanInSymmetricCell(const TinyVector<2>& origin, + const TinyVector<2>& normal, + const TinyVector<2>& Xj, + const CellId& cell_i_id, + SmallArray<double>& mean_of_ejk) + { + auto node_list = m_cell_to_node_matrix[cell_i_id]; + const CellType cell_type = m_cell_type[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + + switch (cell_type) { + case CellType::Triangle: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + + const TriangleTransformation<2> T{x0, x1, x2}; + const auto& quadrature = + QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_polynomial_degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Quadrangle: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + + const SquareTransformation<2> T{x0, x1, x2, x3}; + const auto& quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + } + + void + _computeEjkMeanInSymmetricCell(const TinyVector<3>& origin, + const TinyVector<3>& normal, + const TinyVector<3>& Xj, + const CellId cell_i_id, + SmallArray<double>& mean_of_ejk) + { + auto node_list = m_cell_to_node_matrix[cell_i_id]; + const CellType cell_type = m_cell_type[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + + if (cell_type == CellType::Tetrahedron) { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + + const TetrahedronTransformation T{x0, x1, x2, x3}; + + const auto& quadrature = + QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_polynomial_degree}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + } else if (cell_type == CellType::Prism) { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[4]]); + const auto x4 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + const auto x5 = symmetrize_coordinates(origin, normal, m_xr[node_list[5]]); + + const PrismTransformation T{x0, x1, x2, // + x3, x4, x5}; + + const auto& quadrature = + QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + } else if (cell_type == CellType::Pyramid) { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + const auto x4 = symmetrize_coordinates(origin, normal, m_xr[node_list[4]]); + const PyramidTransformation T{x0, x1, x2, x3, x4}; + + const auto& quadrature = + QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + } else if (cell_type == CellType::Hexahedron) { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + const auto x4 = symmetrize_coordinates(origin, normal, m_xr[node_list[7]]); + const auto x5 = symmetrize_coordinates(origin, normal, m_xr[node_list[6]]); + const auto x6 = symmetrize_coordinates(origin, normal, m_xr[node_list[5]]); + const auto x7 = symmetrize_coordinates(origin, normal, m_xr[node_list[4]]); + + const CubeTransformation T{x0, x1, x2, x3, x4, x5, x6, x7}; + + const auto& quadrature = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); + + _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + + public: + template <typename MatrixType> + void + build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) + { + const auto& stencil_cell_list = m_stencil_array[cell_j_id]; + + const Rd& Xj = m_xj[cell_j_id]; + + _computeEjkMean(Xj, cell_j_id, m_mean_j_of_ejk); + + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + + _computeEjkMean(Xj, cell_i_id, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + + for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { + auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = m_symmetry_origin_list[i_symmetry]; + const Rd& normal = m_symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + + _computeEjkMeanInSymmetricCell(origin, normal, Xj, cell_i_id, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + } + } + + ElementIntegralReconstructionMatrixBuilder(const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<double>& inv_Vj_wq_detJ_ek, + const SmallArray<double>& mean_j_of_ejk, + const SmallArray<double>& mean_i_of_ejk, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array) + : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree( + polynomial_degree)}, + m_polynomial_degree{polynomial_degree}, + + m_inv_Vj_wq_detJ_ek{inv_Vj_wq_detJ_ek}, + m_mean_j_of_ejk{mean_j_of_ejk}, + m_mean_i_of_ejk{mean_i_of_ejk}, + + m_cell_to_node_matrix{mesh.connectivity().cellToNodeMatrix()}, + m_stencil_array{stencil_array}, + m_symmetry_origin_list{symmetry_origin_list}, + m_symmetry_normal_list{symmetry_normal_list}, + m_cell_type{mesh.connectivity().cellType()}, + m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, + m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, + m_xr{mesh.xr()} + {} + + ~ElementIntegralReconstructionMatrixBuilder() = default; +}; + +#endif // ELEMENT_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP -- GitLab From 1e6b57a8e0cebe5dad40f3b70e0804bfd6c8cc5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 5 May 2025 12:00:17 +0200 Subject: [PATCH 107/122] Create BoundaryIntegralReconstructionMatrixBuilder initial version --- src/scheme/PolynomialReconstruction.cpp | 280 +----------------- ...aryIntegralReconstructionMatrixBuilder.hpp | 212 +++++++++++++ ...entIntegralReconstructionMatrixBuilder.hpp | 2 +- 3 files changed, 216 insertions(+), 278 deletions(-) create mode 100644 src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 253233910..2e6e1fabb 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -25,6 +25,7 @@ #include <scheme/DiscreteFunctionUtils.hpp> #include <scheme/DiscreteFunctionVariant.hpp> +#include <scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp> #include <scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp> #include <scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp> @@ -52,240 +53,6 @@ symmetrize_coordinates(const TinyVector<Dimension>& origin, return u - 2 * dot(u - origin, normal) * normal; } -template <typename ConformTransformationT> -void _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, - const ConformTransformationT& T, - const TinyVector<2>& Xj, - const double Vi, - const size_t degree, - const size_t dimension, - const SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - SmallArray<double>& mean_of_ejk); - -template <> -void -_computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, - const LineTransformation<2>& T, - const TinyVector<2>& Xj, - const double Vi, - const size_t degree, - const size_t dimension, - const SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - SmallArray<double>& mean_of_ejk) -{ - using Rd = TinyVector<2>; - - const double velocity_perp_e1 = T.velocity()[1]; - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const TinyVector<1> xi_q = quadrature.point(i_q); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - const double y_yj = X_Xj[1]; - - { - size_t k = 0; - inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1 / Vi; - for (; k <= degree; ++k) { - inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k] = - x_xj * inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * ((1. * k) / (k + 1)); - } - - for (size_t i_y = 1; i_y <= degree; ++i_y) { - const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2; - for (size_t l = 0; l <= degree - i_y; ++l) { - inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = y_yj * inv_Vj_alpha_p_1_wq_X_prime_orth_ek[begin_i_y_1 + l]; - } - } - } - - for (size_t k = 1; k < dimension; ++k) { - mean_of_ejk[k - 1] += inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k]; - } - } -} - -template <MeshConcept MeshType> -void -_computeEjkMeanByBoundary(const MeshType& mesh, - const TinyVector<2>& Xj, - const CellId& cell_id, - const auto& cell_to_face_matrix, - const auto& face_to_node_matrix, - const auto& cell_face_is_reversed, - const CellValue<const double>& Vi, - const size_t degree, - const size_t basis_dimension, - const SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - SmallArray<double>& mean_of_ejk) -{ - const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(degree + 1)); - - auto xr = mesh.xr(); - mean_of_ejk.fill(0); - auto cell_face_list = cell_to_face_matrix[cell_id]; - for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { - bool is_reversed = cell_face_is_reversed[cell_id][i_face]; - - const FaceId face_id = cell_face_list[i_face]; - auto face_node_list = face_to_node_matrix[face_id]; - if (is_reversed) { - const LineTransformation<2> T{xr[face_node_list[1]], xr[face_node_list[0]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } else { - const LineTransformation<2> T{xr[face_node_list[0]], xr[face_node_list[1]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } - } -} - -void -_computeEjkMeanByBoundaryInSymmetricCell(const TinyVector<2>& origin, - const TinyVector<2>& normal, - const TinyVector<2>& Xj, - const CellId& cell_id, - const NodeValue<const TinyVector<2>>& xr, - const auto& cell_to_face_matrix, - const auto& face_to_node_matrix, - const auto& cell_face_is_reversed, - const CellValue<const double>& Vi, - const size_t degree, - const size_t basis_dimension, - const SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - SmallArray<double>& mean_of_ejk) -{ - const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(degree + 1)); - - mean_of_ejk.fill(0); - auto cell_face_list = cell_to_face_matrix[cell_id]; - for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { - bool is_reversed = cell_face_is_reversed[cell_id][i_face]; - - const FaceId face_id = cell_face_list[i_face]; - auto face_node_list = face_to_node_matrix[face_id]; - - const auto x0 = symmetrize_coordinates(origin, normal, xr[face_node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, xr[face_node_list[0]]); - - if (is_reversed) { - const LineTransformation<2> T{x1, x0}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } else { - const LineTransformation<2> T{x0, x1}; - _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk); - } - } -} - -template <MeshConcept MeshType> -class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder -{ - private: - using Rd = TinyVector<MeshType::Dimension>; - - const MeshType& m_mesh; - const size_t m_basis_dimension; - const size_t m_polynomial_degree; - - const SmallArray<double> m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek; - SmallArray<double> m_mean_j_of_ejk; - SmallArray<double> m_mean_i_of_ejk; - - const ItemToItemMatrix<ItemType::cell, ItemType::face> m_cell_to_face_matrix; - const CellToCellStencilArray& m_stencil_array; - - const SmallArray<const Rd> m_symmetry_origin_list; - const SmallArray<const Rd> m_symmetry_normal_list; - - const CellValue<const double> m_Vj; - const CellValue<const Rd> m_xj; - const NodeValue<const Rd> m_xr; - - public: - template <typename MatrixType> - void - build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) - { - const auto& stencil_cell_list = m_stencil_array[cell_j_id]; - - auto face_to_node_matrix = m_mesh.connectivity().faceToNodeMatrix(); - auto cell_face_is_reversed = m_mesh.connectivity().cellFaceIsReversed(); - const Rd& Xj = m_xj[cell_j_id]; - - _computeEjkMeanByBoundary(m_mesh, Xj, cell_j_id, m_cell_to_face_matrix, face_to_node_matrix, cell_face_is_reversed, - m_Vj, m_polynomial_degree, m_basis_dimension, m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - m_mean_j_of_ejk); - - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - - _computeEjkMeanByBoundary(m_mesh, Xj, cell_i_id, m_cell_to_face_matrix, face_to_node_matrix, - cell_face_is_reversed, m_Vj, m_polynomial_degree, m_basis_dimension, - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek, m_mean_i_of_ejk); - - for (size_t l = 0; l < m_basis_dimension - 1; ++l) { - A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; - } - } - for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { - auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - - const Rd& origin = m_symmetry_origin_list[i_symmetry]; - const Rd& normal = m_symmetry_normal_list[i_symmetry]; - - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - - _computeEjkMeanByBoundaryInSymmetricCell(origin, normal, // - Xj, cell_i_id, m_xr, m_cell_to_face_matrix, face_to_node_matrix, - cell_face_is_reversed, m_Vj, m_polynomial_degree, m_basis_dimension, - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek, m_mean_i_of_ejk); - - for (size_t l = 0; l < m_basis_dimension - 1; ++l) { - A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; - } - } - } - } - - BoundaryIntegralReconstructionMatrixBuilder(const MeshType& mesh, - const size_t polynomial_degree, - const SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - const SmallArray<double>& mean_j_of_ejk, - const SmallArray<double>& mean_i_of_ejk, - const SmallArray<const Rd>& symmetry_origin_list, - const SmallArray<const Rd>& symmetry_normal_list, - const CellToCellStencilArray& stencil_array) - : m_mesh{mesh}, - m_basis_dimension{ - DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(polynomial_degree)}, - m_polynomial_degree{polynomial_degree}, - - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek{inv_Vj_alpha_p_1_wq_X_prime_orth_ek}, - m_mean_j_of_ejk{mean_j_of_ejk}, - m_mean_i_of_ejk{mean_i_of_ejk}, - - m_cell_to_face_matrix{mesh.connectivity().cellToFaceMatrix()}, - m_stencil_array{stencil_array}, - m_symmetry_origin_list{symmetry_origin_list}, - m_symmetry_normal_list{symmetry_normal_list}, - m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, - m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, - m_xr{mesh.xr()} - {} - BoundaryIntegralReconstructionMatrixBuilder() = default; - ~BoundaryIntegralReconstructionMatrixBuilder() = default; -}; - class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant { public: @@ -882,6 +649,7 @@ PolynomialReconstruction::_build( if ((m_descriptor.integrationMethodType() == IntegrationMethodType::boundary) and (MeshType::Dimension == 2)) { if constexpr (MeshType::Dimension == 2) { +#warning incorporate in reconstruction_matrix_builder SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek = inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[t]; SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; @@ -894,50 +662,8 @@ PolynomialReconstruction::_build( boundary_integral_reconstruction_matrix_builder.build(cell_j_id, A); - auto face_to_node_matrix = p_mesh->connectivity().faceToNodeMatrix(); - auto cell_face_is_reversed = p_mesh->connectivity().cellFaceIsReversed(); - const Rd& Xj = xj[cell_j_id]; - - _computeEjkMeanByBoundary(*p_mesh, Xj, cell_j_id, cell_to_face_matrix, face_to_node_matrix, - cell_face_is_reversed, Vj, m_descriptor.degree(), basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_j_of_ejk); - - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - - _computeEjkMeanByBoundary(*p_mesh, Xj, cell_i_id, cell_to_face_matrix, face_to_node_matrix, - cell_face_is_reversed, Vj, m_descriptor.degree(), basis_dimension, - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_i_of_ejk); - - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; - } - } - for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); - ++i_symmetry) { - auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - - const Rd& origin = symmetry_origin_list[i_symmetry]; - const Rd& normal = symmetry_normal_list[i_symmetry]; - - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - - _computeEjkMeanByBoundaryInSymmetricCell(origin, normal, // - Xj, cell_i_id, xr, cell_to_face_matrix, face_to_node_matrix, - cell_face_is_reversed, Vj, m_descriptor.degree(), - basis_dimension, inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - mean_i_of_ejk); - - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l]; - } - } - } } else { - throw UnexpectedError("invalid mesh dimension"); + throw NotImplementedError("invalid mesh dimension"); } } else { diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp new file mode 100644 index 000000000..a4a282fde --- /dev/null +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp @@ -0,0 +1,212 @@ +#ifndef BOUNDARY_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP +#define BOUNDARY_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP + +#include <algebra/ShrinkMatrixView.hpp> +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/LineTransformation.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/StencilArray.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> +#include <scheme/PolynomialReconstruction.hpp> +#include <utils/SmallArray.hpp> + +template <MeshConcept MeshType> +class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder +{ + private: + using Rd = TinyVector<MeshType::Dimension>; + + const MeshType& m_mesh; + const size_t m_basis_dimension; + const size_t m_polynomial_degree; + + const SmallArray<double> m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek; + SmallArray<double> m_mean_j_of_ejk; + SmallArray<double> m_mean_i_of_ejk; + + const ItemToItemMatrix<ItemType::cell, ItemType::face> m_cell_to_face_matrix; + const ItemToItemMatrix<ItemType::face, ItemType::node> m_face_to_node_matrix; + const FaceValuePerCell<const bool> m_cell_face_is_reversed; + + const CellToCellStencilArray& m_stencil_array; + + const SmallArray<const Rd> m_symmetry_origin_list; + const SmallArray<const Rd> m_symmetry_normal_list; + + const CellValue<const double> m_Vj; + const CellValue<const Rd> m_xj; + const NodeValue<const Rd> m_xr; + + void + _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, + const LineTransformation<2>& T, + const Rd& Xj, + const double Vi, + SmallArray<double>& mean_of_ejk) + { + const double velocity_perp_e1 = T.velocity()[1]; + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const TinyVector<1> xi_q = quadrature.point(i_q); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + + { + size_t k = 0; + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1 / Vi; + for (; k <= m_polynomial_degree; ++k) { + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k] = + x_xj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * ((1. * k) / (k + 1)); + } + + for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { + const size_t begin_i_y_1 = ((i_y - 1) * (2 * m_polynomial_degree - i_y + 4)) / 2; + for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l) { + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = y_yj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[begin_i_y_1 + l]; + } + } + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] += m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k]; + } + } + } + + void + _computeEjkMeanByBoundary(const Rd& Xj, const CellId& cell_id, SmallArray<double>& mean_of_ejk) + { + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + mean_of_ejk.fill(0); + auto cell_face_list = m_cell_to_face_matrix[cell_id]; + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + bool is_reversed = m_cell_face_is_reversed[cell_id][i_face]; + + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = m_face_to_node_matrix[face_id]; + if (is_reversed) { + const LineTransformation<2> T{m_xr[face_node_list[1]], m_xr[face_node_list[0]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, m_Vj[cell_id], mean_of_ejk); + } else { + const LineTransformation<2> T{m_xr[face_node_list[0]], m_xr[face_node_list[1]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, m_Vj[cell_id], mean_of_ejk); + } + } + } + + void + _computeEjkMeanByBoundaryInSymmetricCell(const Rd& origin, + const Rd& normal, + const Rd& Xj, + const CellId& cell_id, + SmallArray<double>& mean_of_ejk) + { + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + mean_of_ejk.fill(0); + auto cell_face_list = m_cell_to_face_matrix[cell_id]; + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + bool is_reversed = m_cell_face_is_reversed[cell_id][i_face]; + + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = m_face_to_node_matrix[face_id]; + + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); + + if (is_reversed) { + const LineTransformation<2> T{x1, x0}; + _computeEjkBoundaryMean(quadrature, T, Xj, m_Vj[cell_id], mean_of_ejk); + } else { + const LineTransformation<2> T{x0, x1}; + _computeEjkBoundaryMean(quadrature, T, Xj, m_Vj[cell_id], mean_of_ejk); + } + } + } + + public: + template <typename MatrixType> + void + build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) + { + if constexpr (MeshType::Dimension == 2) { + const auto& stencil_cell_list = m_stencil_array[cell_j_id]; + + const Rd& Xj = m_xj[cell_j_id]; + + _computeEjkMeanByBoundary(Xj, cell_j_id, m_mean_j_of_ejk); + + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + + _computeEjkMeanByBoundary(Xj, cell_i_id, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = m_symmetry_origin_list[i_symmetry]; + const Rd& normal = m_symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + + _computeEjkMeanByBoundaryInSymmetricCell(origin, normal, Xj, cell_i_id, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + } + } else { + throw NotImplementedError("invalid mesh dimension"); + } + } + + BoundaryIntegralReconstructionMatrixBuilder(const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + const SmallArray<double>& mean_j_of_ejk, + const SmallArray<double>& mean_i_of_ejk, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array) + : m_mesh{mesh}, + m_basis_dimension{ + DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(polynomial_degree)}, + m_polynomial_degree{polynomial_degree}, + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek{inv_Vj_alpha_p_1_wq_X_prime_orth_ek}, + m_mean_j_of_ejk{mean_j_of_ejk}, + m_mean_i_of_ejk{mean_i_of_ejk}, + m_cell_to_face_matrix{mesh.connectivity().cellToFaceMatrix()}, + m_face_to_node_matrix{mesh.connectivity().faceToNodeMatrix()}, + m_cell_face_is_reversed{mesh.connectivity().cellFaceIsReversed()}, + m_stencil_array{stencil_array}, + m_symmetry_origin_list{symmetry_origin_list}, + m_symmetry_normal_list{symmetry_normal_list}, + m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, + m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, + m_xr{mesh.xr()} + {} + BoundaryIntegralReconstructionMatrixBuilder() = default; + ~BoundaryIntegralReconstructionMatrixBuilder() = default; +}; + +#endif // BOUNDARY_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp index 8477f57ea..8e1ce8660 100644 --- a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp @@ -134,7 +134,7 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder const double Vi, SmallArray<double>& mean_of_ejk) { -#warning Compute this one for all and rework design +#warning Compute this once for all and rework design SmallArray<size_t> m_yz_row_index((m_polynomial_degree + 2) * (m_polynomial_degree + 1) / 2 + 1); SmallArray<size_t> m_z_triangle_index(m_polynomial_degree + 1); -- GitLab From 068152db19eb2e74b77323057cc1293fc479d388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 5 May 2025 12:32:10 +0200 Subject: [PATCH 108/122] Displace MutableDiscreteFunctionDPkVariant in its own file --- src/scheme/PolynomialReconstruction.cpp | 135 +----------------- .../MutableDiscreteFunctionDPkVariant.hpp | 130 +++++++++++++++++ 2 files changed, 132 insertions(+), 133 deletions(-) create mode 100644 src/scheme/reconstruction_utils/MutableDiscreteFunctionDPkVariant.hpp diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 2e6e1fabb..0f3982c4f 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -4,17 +4,6 @@ #include <algebra/ShrinkMatrixView.hpp> #include <algebra/ShrinkVectorView.hpp> #include <algebra/SmallMatrix.hpp> -#include <analysis/GaussLegendreQuadratureDescriptor.hpp> -#include <analysis/GaussQuadratureDescriptor.hpp> -#include <analysis/QuadratureFormula.hpp> -#include <analysis/QuadratureManager.hpp> -#include <geometry/CubeTransformation.hpp> -#include <geometry/LineTransformation.hpp> -#include <geometry/PrismTransformation.hpp> -#include <geometry/PyramidTransformation.hpp> -#include <geometry/SquareTransformation.hpp> -#include <geometry/TetrahedronTransformation.hpp> -#include <geometry/TriangleTransformation.hpp> #include <mesh/MeshData.hpp> #include <mesh/MeshDataManager.hpp> #include <mesh/MeshFlatFaceBoundary.hpp> @@ -24,11 +13,12 @@ #include <scheme/DiscreteFunctionDPkVariant.hpp> #include <scheme/DiscreteFunctionUtils.hpp> #include <scheme/DiscreteFunctionVariant.hpp> - #include <scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp> #include <scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp> #include <scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp> +#include <scheme/reconstruction_utils/MutableDiscreteFunctionDPkVariant.hpp> +#warning put in a file in geometry template <size_t Dimension> PUGS_INLINE auto symmetrize_vector(const TinyVector<Dimension>& normal, const TinyVector<Dimension>& u) @@ -53,127 +43,6 @@ symmetrize_coordinates(const TinyVector<Dimension>& origin, return u - 2 * dot(u - origin, normal) * normal; } -class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant -{ - public: - using Variant = std::variant<DiscreteFunctionDPk<1, double>, - DiscreteFunctionDPk<1, TinyVector<1>>, - DiscreteFunctionDPk<1, TinyVector<2>>, - DiscreteFunctionDPk<1, TinyVector<3>>, - DiscreteFunctionDPk<1, TinyMatrix<1>>, - DiscreteFunctionDPk<1, TinyMatrix<2>>, - DiscreteFunctionDPk<1, TinyMatrix<3>>, - - DiscreteFunctionDPk<2, double>, - DiscreteFunctionDPk<2, TinyVector<1>>, - DiscreteFunctionDPk<2, TinyVector<2>>, - DiscreteFunctionDPk<2, TinyVector<3>>, - DiscreteFunctionDPk<2, TinyMatrix<1>>, - DiscreteFunctionDPk<2, TinyMatrix<2>>, - DiscreteFunctionDPk<2, TinyMatrix<3>>, - - DiscreteFunctionDPk<3, double>, - DiscreteFunctionDPk<3, TinyVector<1>>, - DiscreteFunctionDPk<3, TinyVector<2>>, - DiscreteFunctionDPk<3, TinyVector<3>>, - DiscreteFunctionDPk<3, TinyMatrix<1>>, - DiscreteFunctionDPk<3, TinyMatrix<2>>, - DiscreteFunctionDPk<3, TinyMatrix<3>>, - - DiscreteFunctionDPkVector<1, double>, - DiscreteFunctionDPkVector<1, TinyVector<1>>, - DiscreteFunctionDPkVector<1, TinyVector<2>>, - DiscreteFunctionDPkVector<1, TinyVector<3>>, - DiscreteFunctionDPkVector<1, TinyMatrix<1>>, - DiscreteFunctionDPkVector<1, TinyMatrix<2>>, - DiscreteFunctionDPkVector<1, TinyMatrix<3>>, - - DiscreteFunctionDPkVector<2, double>, - DiscreteFunctionDPkVector<2, TinyVector<1>>, - DiscreteFunctionDPkVector<2, TinyVector<2>>, - DiscreteFunctionDPkVector<2, TinyVector<3>>, - DiscreteFunctionDPkVector<2, TinyMatrix<1>>, - DiscreteFunctionDPkVector<2, TinyMatrix<2>>, - DiscreteFunctionDPkVector<2, TinyMatrix<3>>, - - DiscreteFunctionDPkVector<3, double>, - DiscreteFunctionDPkVector<3, TinyVector<1>>, - DiscreteFunctionDPkVector<3, TinyVector<2>>, - DiscreteFunctionDPkVector<3, TinyVector<3>>, - DiscreteFunctionDPkVector<3, TinyMatrix<1>>, - DiscreteFunctionDPkVector<3, TinyMatrix<2>>, - DiscreteFunctionDPkVector<3, TinyMatrix<3>>>; - - private: - Variant m_mutable_discrete_function_dpk; - - public: - PUGS_INLINE - const Variant& - mutableDiscreteFunctionDPk() const - { - return m_mutable_discrete_function_dpk; - } - - template <typename DiscreteFunctionDPkT> - PUGS_INLINE auto&& - get() const - { - static_assert(is_discrete_function_dpk_v<DiscreteFunctionDPkT>, "invalid template argument"); -#ifndef NDEBUG - if (not std::holds_alternative<DiscreteFunctionDPkT>(this->m_mutable_discrete_function_dpk)) { - std::ostringstream error_msg; - error_msg << "invalid discrete function type\n"; - error_msg << "- required " << rang::fgB::red << demangle<DiscreteFunctionDPkT>() << rang::fg::reset << '\n'; - error_msg << "- contains " << rang::fgB::yellow - << std::visit([](auto&& f) -> std::string { return demangle<decltype(f)>(); }, - this->m_mutable_discrete_function_dpk) - << rang::fg::reset; - throw NormalError(error_msg.str()); - } -#endif // NDEBUG - - return std::get<DiscreteFunctionDPkT>(this->mutableDiscreteFunctionDPk()); - } - - template <size_t Dimension, typename DataType> - MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPk<Dimension, DataType>& discrete_function_dpk) - : m_mutable_discrete_function_dpk{discrete_function_dpk} - { - static_assert(std::is_same_v<DataType, double> or // - std::is_same_v<DataType, TinyVector<1, double>> or // - std::is_same_v<DataType, TinyVector<2, double>> or // - std::is_same_v<DataType, TinyVector<3, double>> or // - std::is_same_v<DataType, TinyMatrix<1, 1, double>> or // - std::is_same_v<DataType, TinyMatrix<2, 2, double>> or // - std::is_same_v<DataType, TinyMatrix<3, 3, double>>, - "DiscreteFunctionDPk with this DataType is not allowed in variant"); - } - - template <size_t Dimension, typename DataType> - MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) - : m_mutable_discrete_function_dpk{discrete_function_dpk} - { - static_assert(std::is_same_v<DataType, double> or // - std::is_same_v<DataType, TinyVector<1, double>> or // - std::is_same_v<DataType, TinyVector<2, double>> or // - std::is_same_v<DataType, TinyVector<3, double>> or // - std::is_same_v<DataType, TinyMatrix<1, 1, double>> or // - std::is_same_v<DataType, TinyMatrix<2, 2, double>> or // - std::is_same_v<DataType, TinyMatrix<3, 3, double>>, - "DiscreteFunctionDPkVector with this DataType is not allowed in variant"); - } - - MutableDiscreteFunctionDPkVariant& operator=(MutableDiscreteFunctionDPkVariant&&) = default; - MutableDiscreteFunctionDPkVariant& operator=(const MutableDiscreteFunctionDPkVariant&) = default; - - MutableDiscreteFunctionDPkVariant(const MutableDiscreteFunctionDPkVariant&) = default; - MutableDiscreteFunctionDPkVariant(MutableDiscreteFunctionDPkVariant&&) = default; - - MutableDiscreteFunctionDPkVariant() = delete; - ~MutableDiscreteFunctionDPkVariant() = default; -}; - size_t PolynomialReconstruction::_getNumberOfColumns( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const diff --git a/src/scheme/reconstruction_utils/MutableDiscreteFunctionDPkVariant.hpp b/src/scheme/reconstruction_utils/MutableDiscreteFunctionDPkVariant.hpp new file mode 100644 index 000000000..761f3c112 --- /dev/null +++ b/src/scheme/reconstruction_utils/MutableDiscreteFunctionDPkVariant.hpp @@ -0,0 +1,130 @@ +#ifndef MUTABLE_DISCRETE_FUNCTION_D_PK_VARIANT_HPP +#define MUTABLE_DISCRETE_FUNCTION_D_PK_VARIANT_HPP + +#include <scheme/DiscreteFunctionDPk.hpp> +#include <scheme/PolynomialReconstruction.hpp> + +#include <variant> + +class PolynomialReconstruction::MutableDiscreteFunctionDPkVariant +{ + public: + using Variant = std::variant<DiscreteFunctionDPk<1, double>, + DiscreteFunctionDPk<1, TinyVector<1>>, + DiscreteFunctionDPk<1, TinyVector<2>>, + DiscreteFunctionDPk<1, TinyVector<3>>, + DiscreteFunctionDPk<1, TinyMatrix<1>>, + DiscreteFunctionDPk<1, TinyMatrix<2>>, + DiscreteFunctionDPk<1, TinyMatrix<3>>, + + DiscreteFunctionDPk<2, double>, + DiscreteFunctionDPk<2, TinyVector<1>>, + DiscreteFunctionDPk<2, TinyVector<2>>, + DiscreteFunctionDPk<2, TinyVector<3>>, + DiscreteFunctionDPk<2, TinyMatrix<1>>, + DiscreteFunctionDPk<2, TinyMatrix<2>>, + DiscreteFunctionDPk<2, TinyMatrix<3>>, + + DiscreteFunctionDPk<3, double>, + DiscreteFunctionDPk<3, TinyVector<1>>, + DiscreteFunctionDPk<3, TinyVector<2>>, + DiscreteFunctionDPk<3, TinyVector<3>>, + DiscreteFunctionDPk<3, TinyMatrix<1>>, + DiscreteFunctionDPk<3, TinyMatrix<2>>, + DiscreteFunctionDPk<3, TinyMatrix<3>>, + + DiscreteFunctionDPkVector<1, double>, + DiscreteFunctionDPkVector<1, TinyVector<1>>, + DiscreteFunctionDPkVector<1, TinyVector<2>>, + DiscreteFunctionDPkVector<1, TinyVector<3>>, + DiscreteFunctionDPkVector<1, TinyMatrix<1>>, + DiscreteFunctionDPkVector<1, TinyMatrix<2>>, + DiscreteFunctionDPkVector<1, TinyMatrix<3>>, + + DiscreteFunctionDPkVector<2, double>, + DiscreteFunctionDPkVector<2, TinyVector<1>>, + DiscreteFunctionDPkVector<2, TinyVector<2>>, + DiscreteFunctionDPkVector<2, TinyVector<3>>, + DiscreteFunctionDPkVector<2, TinyMatrix<1>>, + DiscreteFunctionDPkVector<2, TinyMatrix<2>>, + DiscreteFunctionDPkVector<2, TinyMatrix<3>>, + + DiscreteFunctionDPkVector<3, double>, + DiscreteFunctionDPkVector<3, TinyVector<1>>, + DiscreteFunctionDPkVector<3, TinyVector<2>>, + DiscreteFunctionDPkVector<3, TinyVector<3>>, + DiscreteFunctionDPkVector<3, TinyMatrix<1>>, + DiscreteFunctionDPkVector<3, TinyMatrix<2>>, + DiscreteFunctionDPkVector<3, TinyMatrix<3>>>; + + private: + Variant m_mutable_discrete_function_dpk; + + public: + PUGS_INLINE + const Variant& + mutableDiscreteFunctionDPk() const + { + return m_mutable_discrete_function_dpk; + } + + template <typename DiscreteFunctionDPkT> + PUGS_INLINE auto&& + get() const + { + static_assert(is_discrete_function_dpk_v<DiscreteFunctionDPkT>, "invalid template argument"); +#ifndef NDEBUG + if (not std::holds_alternative<DiscreteFunctionDPkT>(this->m_mutable_discrete_function_dpk)) { + std::ostringstream error_msg; + error_msg << "invalid discrete function type\n"; + error_msg << "- required " << rang::fgB::red << demangle<DiscreteFunctionDPkT>() << rang::fg::reset << '\n'; + error_msg << "- contains " << rang::fgB::yellow + << std::visit([](auto&& f) -> std::string { return demangle<decltype(f)>(); }, + this->m_mutable_discrete_function_dpk) + << rang::fg::reset; + throw NormalError(error_msg.str()); + } +#endif // NDEBUG + + return std::get<DiscreteFunctionDPkT>(this->mutableDiscreteFunctionDPk()); + } + + template <size_t Dimension, typename DataType> + MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPk<Dimension, DataType>& discrete_function_dpk) + : m_mutable_discrete_function_dpk{discrete_function_dpk} + { + static_assert(std::is_same_v<DataType, double> or // + std::is_same_v<DataType, TinyVector<1, double>> or // + std::is_same_v<DataType, TinyVector<2, double>> or // + std::is_same_v<DataType, TinyVector<3, double>> or // + std::is_same_v<DataType, TinyMatrix<1, 1, double>> or // + std::is_same_v<DataType, TinyMatrix<2, 2, double>> or // + std::is_same_v<DataType, TinyMatrix<3, 3, double>>, + "DiscreteFunctionDPk with this DataType is not allowed in variant"); + } + + template <size_t Dimension, typename DataType> + MutableDiscreteFunctionDPkVariant(const DiscreteFunctionDPkVector<Dimension, DataType>& discrete_function_dpk) + : m_mutable_discrete_function_dpk{discrete_function_dpk} + { + static_assert(std::is_same_v<DataType, double> or // + std::is_same_v<DataType, TinyVector<1, double>> or // + std::is_same_v<DataType, TinyVector<2, double>> or // + std::is_same_v<DataType, TinyVector<3, double>> or // + std::is_same_v<DataType, TinyMatrix<1, 1, double>> or // + std::is_same_v<DataType, TinyMatrix<2, 2, double>> or // + std::is_same_v<DataType, TinyMatrix<3, 3, double>>, + "DiscreteFunctionDPkVector with this DataType is not allowed in variant"); + } + + MutableDiscreteFunctionDPkVariant& operator=(MutableDiscreteFunctionDPkVariant&&) = default; + MutableDiscreteFunctionDPkVariant& operator=(const MutableDiscreteFunctionDPkVariant&) = default; + + MutableDiscreteFunctionDPkVariant(const MutableDiscreteFunctionDPkVariant&) = default; + MutableDiscreteFunctionDPkVariant(MutableDiscreteFunctionDPkVariant&&) = default; + + MutableDiscreteFunctionDPkVariant() = delete; + ~MutableDiscreteFunctionDPkVariant() = default; +}; + +#endif // MUTABLE_DISCRETE_FUNCTION_D_PK_VARIANT_HPP -- GitLab From dae61de8386f622c175c8f77317fdd880966b4fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Wed, 7 May 2025 15:26:28 +0200 Subject: [PATCH 109/122] Improve ElementIntegralReconstructionMatrixBuilder code --- src/scheme/PolynomialReconstruction.cpp | 65 ++++---- ...entIntegralReconstructionMatrixBuilder.hpp | 139 +++++++++++------- 2 files changed, 126 insertions(+), 78 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 0f3982c4f..3c257c2d9 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -185,9 +185,8 @@ PolynomialReconstruction::_build( auto xj = MeshDataManager::instance().getMeshData(mesh).xj(); auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj(); - auto cell_is_owned = mesh.connectivity().cellIsOwned(); - auto cell_type = mesh.connectivity().cellType(); - auto cell_to_face_matrix = mesh.connectivity().cellToFaceMatrix(); + auto cell_is_owned = mesh.connectivity().cellIsOwned(); + auto cell_type = mesh.connectivity().cellType(); auto full_stencil_size = [&](const CellId cell_id) { auto stencil_cell_list = stencil_array[cell_id]; @@ -270,6 +269,39 @@ PolynomialReconstruction::_build( std::make_shared<CellCenterReconstructionMatrixBuilder<MeshType>>(symmetry_origin_list, symmetry_normal_list, stencil_array, xj); + SmallArray<std::shared_ptr<ElementIntegralReconstructionMatrixBuilder<MeshType>>> + element_integral_reconstruction_matrix_builder_pool{A_pool.size()}; + + for (size_t t = 0; t < element_integral_reconstruction_matrix_builder_pool.size(); ++t) { + SmallArray<double>& inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t]; + SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; + + element_integral_reconstruction_matrix_builder_pool[t] = + std::make_shared<ElementIntegralReconstructionMatrixBuilder<MeshType>>(*p_mesh, m_descriptor.degree(), + inv_Vj_wq_detJ_ek, mean_j_of_ejk, + mean_i_of_ejk, symmetry_origin_list, + symmetry_normal_list, stencil_array); + } + + SmallArray<std::shared_ptr<BoundaryIntegralReconstructionMatrixBuilder<MeshType>>> + boundary_integral_reconstruction_matrix_builder_pool{A_pool.size()}; + + if constexpr (MeshType::Dimension == 2) { + for (size_t t = 0; t < boundary_integral_reconstruction_matrix_builder_pool.size(); ++t) { + SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek = inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[t]; + SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; + + boundary_integral_reconstruction_matrix_builder_pool[t] = + std::make_shared<BoundaryIntegralReconstructionMatrixBuilder<MeshType>>(*p_mesh, m_descriptor.degree(), + inv_Vj_alpha_p_1_wq_X_prime_orth_ek, + mean_j_of_ejk, mean_i_of_ejk, + symmetry_origin_list, + symmetry_normal_list, stencil_array); + } + } + parallel_for( mesh.numberOfCells(), PUGS_CLASS_LAMBDA(const CellId cell_j_id) { if (cell_is_owned[cell_j_id]) { @@ -518,35 +550,12 @@ PolynomialReconstruction::_build( if ((m_descriptor.integrationMethodType() == IntegrationMethodType::boundary) and (MeshType::Dimension == 2)) { if constexpr (MeshType::Dimension == 2) { -#warning incorporate in reconstruction_matrix_builder - SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek = inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[t]; - SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; - - BoundaryIntegralReconstructionMatrixBuilder - boundary_integral_reconstruction_matrix_builder(*p_mesh, m_descriptor.degree(), - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_j_of_ejk, - mean_i_of_ejk, symmetry_origin_list, - symmetry_normal_list, stencil_array); - - boundary_integral_reconstruction_matrix_builder.build(cell_j_id, A); - + boundary_integral_reconstruction_matrix_builder_pool[t]->build(cell_j_id, A); } else { throw NotImplementedError("invalid mesh dimension"); } } else { - -#warning incorporate in reconstruction_matrix_builder - SmallArray<double>& inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t]; - SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; - - ElementIntegralReconstructionMatrixBuilder - element_integral_reconstruction_matrix_builder(*p_mesh, m_descriptor.degree(), inv_Vj_wq_detJ_ek, - mean_j_of_ejk, mean_i_of_ejk, symmetry_origin_list, - symmetry_normal_list, stencil_array); - - element_integral_reconstruction_matrix_builder.build(cell_j_id, A); + element_integral_reconstruction_matrix_builder_pool[t]->build(cell_j_id, A); } } else { throw UnexpectedError("invalid integration strategy"); diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp index 8e1ce8660..efb92d0db 100644 --- a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp @@ -30,7 +30,7 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder const size_t m_basis_dimension; const size_t m_polynomial_degree; - const SmallArray<double> m_inv_Vj_wq_detJ_ek; + const SmallArray<double> m_wq_detJ_ek; SmallArray<double> m_mean_j_of_ejk; SmallArray<double> m_mean_i_of_ejk; @@ -45,13 +45,21 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder const CellValue<const Rd> m_xj; const NodeValue<const Rd> m_xr; + // 2D + SmallArray<const size_t> m_y_row_index; + + // 3D + SmallArray<const size_t> m_yz_row_index; + SmallArray<const size_t> m_z_triangle_index; + SmallArray<const size_t> m_yz_row_size; + template <typename ConformTransformationT> void _computeEjkMean(const QuadratureFormula<1>& quadrature, const ConformTransformationT& T, const TinyVector<1>& Xj, const double Vi, - SmallArray<double>& mean_of_ejk) + SmallArray<double>& mean_of_ejk) noexcept(NO_ASSERT) { mean_of_ejk.fill(0); @@ -65,17 +73,21 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder const double x_xj = X_Xj[0]; { - size_t k = 0; - m_inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; - for (; k <= m_polynomial_degree; ++k) { - m_inv_Vj_wq_detJ_ek[k] = x_xj * m_inv_Vj_wq_detJ_ek[k - 1]; + m_wq_detJ_ek[0] = wq * detT; + for (size_t k = 1; k <= m_polynomial_degree; ++k) { + m_wq_detJ_ek[k] = x_xj * m_wq_detJ_ek[k - 1]; } } for (size_t k = 1; k < m_basis_dimension; ++k) { - mean_of_ejk[k - 1] += m_inv_Vj_wq_detJ_ek[k]; + mean_of_ejk[k - 1] += m_wq_detJ_ek[k]; } } + + const double inv_Vi = 1. / Vi; + for (size_t k = 0; k < mean_of_ejk.size(); ++k) { + mean_of_ejk[k] *= inv_Vi; + } } template <typename ConformTransformationT> @@ -84,7 +96,7 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder const ConformTransformationT& T, const TinyVector<2>& Xj, const double Vi, - SmallArray<double>& mean_of_ejk) + SmallArray<double>& mean_of_ejk) noexcept(NO_ASSERT) { mean_of_ejk.fill(0); @@ -105,25 +117,29 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder const double y_yj = X_Xj[1]; { - size_t k = 0; - m_inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; + size_t k = 0; + m_wq_detJ_ek[k++] = wq * detT; for (; k <= m_polynomial_degree; ++k) { - m_inv_Vj_wq_detJ_ek[k] = x_xj * m_inv_Vj_wq_detJ_ek[k - 1]; + m_wq_detJ_ek[k] = x_xj * m_wq_detJ_ek[k - 1]; } for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { -#warning store y_row index and size - const size_t begin_i_y_1 = ((i_y - 1) * (2 * m_polynomial_degree - i_y + 4)) / 2; - for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l) { - m_inv_Vj_wq_detJ_ek[k++] = y_yj * m_inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; + const size_t begin_i_y_1 = m_y_row_index[i_y - 1]; + for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l, ++k) { + m_wq_detJ_ek[k] = y_yj * m_wq_detJ_ek[begin_i_y_1 + l]; } } } for (size_t k = 1; k < m_basis_dimension; ++k) { - mean_of_ejk[k - 1] += m_inv_Vj_wq_detJ_ek[k]; + mean_of_ejk[k - 1] += m_wq_detJ_ek[k]; } } + + const double inv_Vi = 1. / Vi; + for (size_t k = 0; k < mean_of_ejk.size(); ++k) { + mean_of_ejk[k] *= inv_Vi; + } } template <typename ConformTransformationT> @@ -132,31 +148,8 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder const ConformTransformationT& T, const TinyVector<3>& Xj, const double Vi, - SmallArray<double>& mean_of_ejk) + SmallArray<double>& mean_of_ejk) noexcept(NO_ASSERT) { -#warning Compute this once for all and rework design - SmallArray<size_t> m_yz_row_index((m_polynomial_degree + 2) * (m_polynomial_degree + 1) / 2 + 1); - SmallArray<size_t> m_z_triangle_index(m_polynomial_degree + 1); - - { - size_t i_z = 0; - size_t i_yz = 0; - - m_yz_row_index[i_yz++] = 0; - for (ssize_t n = m_polynomial_degree + 1; n >= 1; --n) { - m_z_triangle_index[i_z++] = i_yz - 1; - for (ssize_t i = n; i >= 1; --i) { - m_yz_row_index[i_yz] = m_yz_row_index[i_yz - 1] + i; - ++i_yz; - } - } - } - - SmallArray<size_t> m_yz_row_size{m_yz_row_index.size() - 1}; - for (size_t i = 0; i < m_yz_row_size.size(); ++i) { - m_yz_row_size[i] = m_yz_row_index[i + 1] - m_yz_row_index[i]; - } - mean_of_ejk.fill(0); for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { @@ -177,17 +170,17 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder const double z_zj = X_Xj[2]; { - size_t k = 0; - m_inv_Vj_wq_detJ_ek[k++] = wq * detT / Vi; + size_t k = 0; + m_wq_detJ_ek[k++] = wq * detT; for (; k <= m_polynomial_degree; ++k) { - m_inv_Vj_wq_detJ_ek[k] = x_xj * m_inv_Vj_wq_detJ_ek[k - 1]; + m_wq_detJ_ek[k] = x_xj * m_wq_detJ_ek[k - 1]; } for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { const size_t begin_i_y_1 = m_yz_row_index[i_y - 1]; const size_t nb_monoms = m_yz_row_size[i_y]; - for (size_t l = 0; l < nb_monoms; ++l) { - m_inv_Vj_wq_detJ_ek[k++] = y_yj * m_inv_Vj_wq_detJ_ek[begin_i_y_1 + l]; + for (size_t l = 0; l < nb_monoms; ++l, ++k) { + m_wq_detJ_ek[k] = y_yj * m_wq_detJ_ek[begin_i_y_1 + l]; } } @@ -198,17 +191,22 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder for (size_t i_y = 0; i_y < nb_y; ++i_y) { const size_t begin_i_yz_1 = m_yz_row_index[index_z_1 + i_y]; const size_t nb_monoms = m_yz_row_size[index_z + i_y]; - for (size_t l = 0; l < nb_monoms; ++l) { - m_inv_Vj_wq_detJ_ek[k++] = z_zj * m_inv_Vj_wq_detJ_ek[begin_i_yz_1 + l]; + for (size_t l = 0; l < nb_monoms; ++l, ++k) { + m_wq_detJ_ek[k] = z_zj * m_wq_detJ_ek[begin_i_yz_1 + l]; } } } } for (size_t k = 1; k < m_basis_dimension; ++k) { - mean_of_ejk[k - 1] += m_inv_Vj_wq_detJ_ek[k]; + mean_of_ejk[k - 1] += m_wq_detJ_ek[k]; } } + + const double inv_Vi = 1. / Vi; + for (size_t k = 0; k < mean_of_ejk.size(); ++k) { + mean_of_ejk[k] *= inv_Vi; + } } void @@ -514,7 +512,7 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder polynomial_degree)}, m_polynomial_degree{polynomial_degree}, - m_inv_Vj_wq_detJ_ek{inv_Vj_wq_detJ_ek}, + m_wq_detJ_ek{inv_Vj_wq_detJ_ek}, m_mean_j_of_ejk{mean_j_of_ejk}, m_mean_i_of_ejk{mean_i_of_ejk}, @@ -526,7 +524,48 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, m_xr{mesh.xr()} - {} + { + if constexpr (MeshType::Dimension == 2) { + SmallArray<size_t> y_row_index(m_polynomial_degree + 2); + + size_t i_y = 0; + + y_row_index[i_y++] = 0; + for (ssize_t n = m_polynomial_degree + 1; n >= 1; --n) { + y_row_index[i_y] = y_row_index[i_y - 1] + n; + ++i_y; + } + + m_y_row_index = y_row_index; + + } else if constexpr (MeshType::Dimension == 3) { + SmallArray<size_t> yz_row_index((m_polynomial_degree + 2) * (m_polynomial_degree + 1) / 2 + 1); + SmallArray<size_t> z_triangle_index(m_polynomial_degree + 1); + + { + size_t i_z = 0; + size_t i_yz = 0; + + yz_row_index[i_yz++] = 0; + for (ssize_t n = m_polynomial_degree + 1; n >= 1; --n) { + z_triangle_index[i_z++] = i_yz - 1; + for (ssize_t i = n; i >= 1; --i) { + yz_row_index[i_yz] = yz_row_index[i_yz - 1] + i; + ++i_yz; + } + } + } + + SmallArray<size_t> yz_row_size{yz_row_index.size() - 1}; + for (size_t i = 0; i < yz_row_size.size(); ++i) { + yz_row_size[i] = yz_row_index[i + 1] - yz_row_index[i]; + } + + m_yz_row_index = yz_row_index; + m_z_triangle_index = z_triangle_index; + m_yz_row_size = yz_row_size; + } + } ~ElementIntegralReconstructionMatrixBuilder() = default; }; -- GitLab From ffe899397ff5ac711608fc73292e0b5fa78df817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Wed, 7 May 2025 17:38:17 +0200 Subject: [PATCH 110/122] Begin BoundaryIntegralReconstructionMatrixBuilder cleaning --- ...aryIntegralReconstructionMatrixBuilder.hpp | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp index a4a282fde..350618f56 100644 --- a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp @@ -41,14 +41,16 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder const CellValue<const Rd> m_xj; const NodeValue<const Rd> m_xr; + SmallArray<const double> m_antiderivative_coef; + void _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, const LineTransformation<2>& T, const Rd& Xj, - const double Vi, + const double inv_Vi, SmallArray<double>& mean_of_ejk) { - const double velocity_perp_e1 = T.velocity()[1]; + const double velocity_perp_e1 = T.velocity()[1] * inv_Vi; for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { const double wq = quadrature.weight(i_q); @@ -61,10 +63,10 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder { size_t k = 0; - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1 / Vi; + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1; for (; k <= m_polynomial_degree; ++k) { m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k] = - x_xj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * ((1. * k) / (k + 1)); + x_xj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * m_antiderivative_coef[k]; // ((1. * k) / (k + 1)); } for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { @@ -87,6 +89,8 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + const double inv_Vi = 1. / m_Vj[cell_id]; + mean_of_ejk.fill(0); auto cell_face_list = m_cell_to_face_matrix[cell_id]; for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { @@ -96,10 +100,10 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder auto face_node_list = m_face_to_node_matrix[face_id]; if (is_reversed) { const LineTransformation<2> T{m_xr[face_node_list[1]], m_xr[face_node_list[0]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, m_Vj[cell_id], mean_of_ejk); + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); } else { const LineTransformation<2> T{m_xr[face_node_list[0]], m_xr[face_node_list[1]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, m_Vj[cell_id], mean_of_ejk); + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); } } } @@ -114,6 +118,8 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + const double inv_Vi = 1. / m_Vj[cell_id]; + mean_of_ejk.fill(0); auto cell_face_list = m_cell_to_face_matrix[cell_id]; for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { @@ -127,10 +133,10 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder if (is_reversed) { const LineTransformation<2> T{x1, x0}; - _computeEjkBoundaryMean(quadrature, T, Xj, m_Vj[cell_id], mean_of_ejk); + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); } else { const LineTransformation<2> T{x0, x1}; - _computeEjkBoundaryMean(quadrature, T, Xj, m_Vj[cell_id], mean_of_ejk); + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); } } } @@ -204,7 +210,15 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, m_xr{mesh.xr()} - {} + { + SmallArray<double> antiderivative_coef(m_polynomial_degree + 1); + for (size_t k = 0; k < antiderivative_coef.size(); ++k) { + antiderivative_coef[k] = ((1. * k) / (k + 1)); + } + + m_antiderivative_coef = antiderivative_coef; + } + BoundaryIntegralReconstructionMatrixBuilder() = default; ~BoundaryIntegralReconstructionMatrixBuilder() = default; }; -- GitLab From da53221aadbf652fd8483afb88085754401a311e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Wed, 7 May 2025 17:48:45 +0200 Subject: [PATCH 111/122] Add temporary test files --- src/language/modules/SchemeModule.cpp | 9 +++++++++ src/scheme/CMakeLists.txt | 2 ++ src/scheme/test_reconstruction.cpp | 19 +++++++++++++++++++ src/scheme/test_reconstruction.hpp | 8 ++++++++ 4 files changed, 38 insertions(+) create mode 100644 src/scheme/test_reconstruction.cpp create mode 100644 src/scheme/test_reconstruction.hpp diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index b9b789cfa..f2570a3c3 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -65,6 +65,9 @@ #include <utils/checkpointing/WriteIQuadratureDescriptor.hpp> #include <utils/checkpointing/WriteVariableBCDescriptor.hpp> +#warning REMOVE +#include <scheme/test_reconstruction.hpp> + #include <memory> SchemeModule::SchemeModule() @@ -756,6 +759,12 @@ SchemeModule::SchemeModule() )); + this->_addBuiltinFunction("test_reconstruction", std::function{[](std::shared_ptr<const DiscreteFunctionVariant> df, + size_t degree, size_t number) -> void { + // + test_reconstruction(df, degree, number); + }}); + MathFunctionRegisterForVh{*this}; } diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt index e5ce50e9e..c299cd24f 100644 --- a/src/scheme/CMakeLists.txt +++ b/src/scheme/CMakeLists.txt @@ -12,6 +12,8 @@ add_library( HyperelasticSolver.cpp LoadBalancer.cpp PolynomialReconstruction.cpp + + test_reconstruction.cpp ) target_link_libraries( diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp new file mode 100644 index 000000000..ec106f497 --- /dev/null +++ b/src/scheme/test_reconstruction.cpp @@ -0,0 +1,19 @@ +#include <scheme/test_reconstruction.hpp> + +#include <scheme/PolynomialReconstruction.hpp> +#include <utils/Timer.hpp> + +void +test_reconstruction(std::shared_ptr<const DiscreteFunctionVariant> df, size_t degree, size_t number) +{ + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::boundary, degree}; + PolynomialReconstruction rec_builder{descriptor}; + + Timer t; + + for (size_t i = 0; i < number; ++i) { + auto rec = rec_builder.build(df); + } + + std::cout << "*** Elapsed time for " << number << " reconstructions: " << t.seconds() << "s ***\n"; +} diff --git a/src/scheme/test_reconstruction.hpp b/src/scheme/test_reconstruction.hpp new file mode 100644 index 000000000..36ed481ec --- /dev/null +++ b/src/scheme/test_reconstruction.hpp @@ -0,0 +1,8 @@ +#ifndef TEST_RECONSTRUCTION_HPP +#define TEST_RECONSTRUCTION_HPP + +#include <scheme/DiscreteFunctionVariant.hpp> +#warning REMOVE +void test_reconstruction(std::shared_ptr<const DiscreteFunctionVariant> df, size_t degree, size_t number); + +#endif // TEST_RECONSTRUCTION_HPP -- GitLab From dc5cccb476534edfc91193a297fb2b833023876f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 12 May 2025 15:06:51 +0200 Subject: [PATCH 112/122] Continue clean-up and prepare higher level threads data handling --- src/scheme/PolynomialReconstruction.cpp | 998 +++++++++--------- src/scheme/PolynomialReconstruction.hpp | 3 + ...aryIntegralReconstructionMatrixBuilder.hpp | 6 +- ...entIntegralReconstructionMatrixBuilder.hpp | 6 +- src/scheme/test_reconstruction.cpp | 26 +- 5 files changed, 543 insertions(+), 496 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 3c257c2d9..464ce1a0f 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -43,6 +43,508 @@ symmetrize_coordinates(const TinyVector<Dimension>& origin, return u - 2 * dot(u - origin, normal) * normal; } +template <MeshConcept MeshType> +class PolynomialReconstruction::Internal +{ + private: + using Rd = TinyVector<MeshType::Dimension>; + + friend PolynomialReconstruction; + + template <typename MatrixType> + static void + buildB(const CellId& cell_j_id, + const CellToCellStencilArray& stencil_array, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, + SmallArray<const Rd> symmetry_normal_list, + ShrinkMatrixView<MatrixType>& B) + { + auto stencil_cell_list = stencil_array[cell_j_id]; + + size_t column_begin = 0; + for (size_t i_discrete_function_variant = 0; i_discrete_function_variant < discrete_function_variant_list.size(); + ++i_discrete_function_variant) { + const auto& discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; + + std::visit( + [&](auto&& discrete_function) { + using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; + if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + const DataType& qj = discrete_function[cell_j_id]; + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + const DataType& qi_qj = discrete_function[cell_i_id] - qj; + if constexpr (std::is_arithmetic_v<DataType>) { + B(index, column_begin) = qi_qj; + } else if constexpr (is_tiny_vector_v<DataType>) { + for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { + B(index, kB) = qi_qj[k]; + } + } else if constexpr (is_tiny_matrix_v<DataType>) { + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + const size_t kB = column_begin + p * DataType::NumberOfColumns; + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + B(index, kB + q) = qi_qj(p, q); + } + } + } + } + + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + if constexpr (std::is_arithmetic_v<DataType>) { + const DataType& qi_qj = discrete_function[cell_i_id] - qj; + B(index, column_begin) = qi_qj; + } else if constexpr (is_tiny_vector_v<DataType>) { + if constexpr (DataType::Dimension == MeshType::Dimension) { + const Rd& normal = symmetry_normal_list[i_symmetry]; + + const DataType& qi = discrete_function[cell_i_id]; + const DataType& qi_qj = symmetrize_vector(normal, qi) - qj; + for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { + B(index, kB) = qi_qj[k]; + } + } else { + // LCOV_EXCL_START + std::stringstream error_msg; + error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension + << " using a mesh of dimension " << MeshType::Dimension; + throw UnexpectedError(error_msg.str()); + // LCOV_EXCL_STOP + } + } else if constexpr (is_tiny_matrix_v<DataType>) { + if constexpr ((DataType::NumberOfColumns == DataType::NumberOfRows) and + (DataType::NumberOfColumns == MeshType::Dimension)) { + const Rd& normal = symmetry_normal_list[i_symmetry]; + + const DataType& qi = discrete_function[cell_i_id]; + const DataType& qi_qj = symmetrize_matrix(normal, qi) - qj; + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + B(index, column_begin + p * DataType::NumberOfColumns + q) = qi_qj(p, q); + } + } + } else { + // LCOV_EXCL_START + std::stringstream error_msg; + error_msg << "cannot symmetrize matrices of dimensions " << DataType::NumberOfRows << 'x' + << DataType::NumberOfColumns << " using a mesh of dimension " << MeshType::Dimension; + throw UnexpectedError(error_msg.str()); + // LCOV_EXCL_STOP + } + } + } + } + + if constexpr (std::is_arithmetic_v<DataType>) { + ++column_begin; + } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) { + column_begin += DataType::Dimension; + } + } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { + using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; + + const auto qj_vector = discrete_function[cell_j_id]; + + if constexpr (std::is_arithmetic_v<DataType>) { + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; + B(index, column_begin + l) = qi_qj; + } + } + + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; + B(index, column_begin + l) = qi_qj; + } + } + } + } else if constexpr (is_tiny_vector_v<DataType>) { + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; + for (size_t kB = column_begin + l * DataType::Dimension, k = 0; k < DataType::Dimension; ++k, ++kB) { + B(index, kB) = qi_qj[k]; + } + } + } + + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + if constexpr (DataType::Dimension == MeshType::Dimension) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& normal = symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi = discrete_function[cell_i_id][l]; + const DataType& qi_qj = symmetrize_vector(normal, qi) - qj; + for (size_t kB = column_begin + l * DataType::Dimension, k = 0; k < DataType::Dimension; + ++k, ++kB) { + B(index, kB) = qi_qj[k]; + } + } + } + } else { + // LCOV_EXCL_START + std::stringstream error_msg; + error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension + << " using a mesh of dimension " << MeshType::Dimension; + throw UnexpectedError(error_msg.str()); + // LCOV_EXCL_STOP + } + } + } else if constexpr (is_tiny_matrix_v<DataType>) { + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi = discrete_function[cell_i_id][l]; + const DataType& qi_qj = qi - qj; + + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + const size_t kB = column_begin + l * DataType::Dimension + p * DataType::NumberOfColumns; + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + B(index, kB + q) = qi_qj(p, q); + } + } + } + } + + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); + ++i_symmetry) { + if constexpr ((DataType::NumberOfRows == MeshType::Dimension) and + (DataType::NumberOfColumns == MeshType::Dimension)) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& normal = symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + + for (size_t l = 0; l < qj_vector.size(); ++l) { + const DataType& qj = qj_vector[l]; + const DataType& qi = discrete_function[cell_i_id][l]; + const DataType& qi_qj = symmetrize_matrix(normal, qi) - qj; + + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + const size_t kB = column_begin + l * DataType::Dimension + p * DataType::NumberOfColumns; + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + B(index, kB + q) = qi_qj(p, q); + } + } + } + } + } else { + // LCOV_EXCL_START + std::stringstream error_msg; + error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension + << " using a mesh of dimension " << MeshType::Dimension; + throw UnexpectedError(error_msg.str()); + // LCOV_EXCL_STOP + } + } + } + + if constexpr (std::is_arithmetic_v<DataType>) { + column_begin += qj_vector.size(); + } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) { + column_begin += qj_vector.size() * DataType::Dimension; + } + + } else { + // LCOV_EXCL_START + throw UnexpectedError("invalid discrete function type"); + // LCOV_EXCL_STOP + } + }, + discrete_function_variant->discreteFunction()); + } + } + + template <typename MatrixType> + static void + rowWeighting(const CellId& cell_j_id, + const CellToCellStencilArray& stencil_array, + const CellValue<const Rd>& xj, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + ShrinkMatrixView<MatrixType>& A, + ShrinkMatrixView<MatrixType>& B) + { + // Add row weighting (give more importance to cells that are + // closer to j) + auto stencil_cell_list = stencil_array[cell_j_id]; + + const Rd& Xj = xj[cell_j_id]; + + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); + for (size_t l = 0; l < A.numberOfColumns(); ++l) { + A(index, l) *= weight; + } + for (size_t l = 0; l < B.numberOfColumns(); ++l) { + B(index, l) *= weight; + } + } + for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { + auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = symmetry_origin_list[i_symmetry]; + const Rd& normal = symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + const double weight = 1. / l2Norm(symmetrize_coordinates(origin, normal, xj[cell_i_id]) - Xj); + for (size_t l = 0; l < A.numberOfColumns(); ++l) { + A(index, l) *= weight; + } + for (size_t l = 0; l < B.numberOfColumns(); ++l) { + B(index, l) *= weight; + } + } + } + } + + template <typename MatrixType> + static void + solveCollectionInPlaceWithPreconditionner(const ShrinkMatrixView<MatrixType>& A, + const SmallMatrix<double>& X, + const ShrinkMatrixView<MatrixType>& B, + const SmallVector<double>& G) + { + for (size_t l = 0; l < A.numberOfColumns(); ++l) { + double g = 0; + for (size_t i = 0; i < A.numberOfRows(); ++i) { + const double Ail = A(i, l); + + g += Ail * Ail; + } + G[l] = std::sqrt(g); + } + + for (size_t l = 0; l < A.numberOfColumns(); ++l) { + const double Gl = G[l]; + for (size_t i = 0; i < A.numberOfRows(); ++i) { + A(i, l) *= Gl; + } + } + + Givens::solveCollectionInPlace(A, X, B); + + for (size_t l = 0; l < X.numberOfRows(); ++l) { + const double Gl = G[l]; + for (size_t i = 0; i < X.numberOfColumns(); ++i) { + X(l, i) *= Gl; + } + } + } + + static void + populateDiscreteFunctionDPkByCell( + const CellId& cell_j_id, + const size_t& degree, + const SmallMatrix<double>& X, + const SmallArray<double>& mean_j_of_ejk, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, + const std::vector<PolynomialReconstruction::MutableDiscreteFunctionDPkVariant>& + mutable_discrete_function_dpk_variant_list) + { + size_t column_begin = 0; + for (size_t i_dpk_variant = 0; i_dpk_variant < mutable_discrete_function_dpk_variant_list.size(); ++i_dpk_variant) { + const auto& dpk_variant = mutable_discrete_function_dpk_variant_list[i_dpk_variant]; + + const auto& discrete_function_variant = discrete_function_variant_list[i_dpk_variant]; + + std::visit( + [&](auto&& dpk_function, auto&& p0_function) { + using DPkFunctionT = std::decay_t<decltype(dpk_function)>; + using P0FunctionT = std::decay_t<decltype(p0_function)>; + using DataType = std::remove_const_t<std::decay_t<typename DPkFunctionT::data_type>>; + using P0DataType = std::remove_const_t<std::decay_t<typename P0FunctionT::data_type>>; + + if constexpr (std::is_same_v<DataType, P0DataType>) { + if constexpr (is_discrete_function_P0_v<P0FunctionT>) { + 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>) { + if (degree > 1) { + for (size_t i = 0; i < X.numberOfRows(); ++i) { + dpk_j[0] -= X(i, column_begin) * mean_j_of_ejk[i]; + } + } + + for (size_t i = 0; i < X.numberOfRows(); ++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>) { + if (degree > 1) { + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_0 = dpk_j[0]; + for (size_t k = 0; k < DataType::Dimension; ++k) { + dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; + } + } + } + + for (size_t i = 0; i < X.numberOfRows(); ++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>) { + if (degree > 1) { + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_0 = dpk_j[0]; + for (size_t k = 0; k < DataType::NumberOfRows; ++k) { + for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { + dpk_j_0(k, l) -= X(i, column_begin + k * DataType::NumberOfColumns + l) * mean_j_of_ejk[i]; + } + } + } + } + + for (size_t i = 0; i < X.numberOfRows(); ++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 + } + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected discrete dpk function type"); + // LCOV_EXCL_STOP + } + } else if constexpr (is_discrete_function_P0_vector_v<P0FunctionT>) { + 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 = X.numberOfRows() + 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>) { + if (degree > 1) { + for (size_t i = 0; i < X.numberOfRows(); ++i) { + dpk_j[component_begin] -= X(i, column_begin) * mean_j_of_ejk[i]; + } + } + + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; + dpk_j_ip1 = X(i, column_begin); + } + ++column_begin; + } else if constexpr (is_tiny_vector_v<DataType>) { + if (degree > 1) { + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_0 = dpk_j[component_begin]; + for (size_t k = 0; k < DataType::Dimension; ++k) { + dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; + } + } + } + + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_ip1 = dpk_j[component_begin + 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>) { + if (degree > 1) { + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_0 = dpk_j[component_begin]; + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + dpk_j_0(p, q) -= X(i, column_begin + p * DataType::NumberOfColumns + q) * mean_j_of_ejk[i]; + } + } + } + } + + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + dpk_j_ip1(p, q) = X(i, column_begin + p * DataType::NumberOfColumns + q); + } + } + } + column_begin += DataType::Dimension; + } else { + // LCOV_EXCL_START + throw UnexpectedError("unexpected data type"); + // LCOV_EXCL_STOP + } + } + } 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()); + } + } +}; + size_t PolynomialReconstruction::_getNumberOfColumns( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const @@ -246,11 +748,7 @@ PolynomialReconstruction::_build( SmallArray<SmallVector<double>> G_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallMatrix<double>> X_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallArray<double>> inv_Vj_wq_detJ_ek_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallArray<double>> mean_j_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallArray<double>> mean_i_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); - - SmallArray<SmallArray<double>> inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool(Kokkos::DefaultExecutionSpace::concurrency()); for (size_t i = 0; i < A_pool.size(); ++i) { A_pool[i] = SmallMatrix<double>(max_stencil_size, basis_dimension - 1); @@ -258,11 +756,7 @@ PolynomialReconstruction::_build( G_pool[i] = SmallVector<double>(basis_dimension - 1); X_pool[i] = SmallMatrix<double>(basis_dimension - 1, number_of_columns); - inv_Vj_wq_detJ_ek_pool[i] = SmallArray<double>(basis_dimension); - mean_j_of_ejk_pool[i] = SmallArray<double>(basis_dimension - 1); - mean_i_of_ejk_pool[i] = SmallArray<double>(basis_dimension - 1); - - inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[i] = SmallArray<double>(basis_dimension); + mean_j_of_ejk_pool[i] = SmallArray<double>(basis_dimension - 1); } std::shared_ptr p_cell_center_reconstruction_matrix_builder = @@ -273,14 +767,11 @@ PolynomialReconstruction::_build( element_integral_reconstruction_matrix_builder_pool{A_pool.size()}; for (size_t t = 0; t < element_integral_reconstruction_matrix_builder_pool.size(); ++t) { - SmallArray<double>& inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t]; - SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; + SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; element_integral_reconstruction_matrix_builder_pool[t] = std::make_shared<ElementIntegralReconstructionMatrixBuilder<MeshType>>(*p_mesh, m_descriptor.degree(), - inv_Vj_wq_detJ_ek, mean_j_of_ejk, - mean_i_of_ejk, symmetry_origin_list, + mean_j_of_ejk, symmetry_origin_list, symmetry_normal_list, stencil_array); } @@ -289,15 +780,11 @@ PolynomialReconstruction::_build( if constexpr (MeshType::Dimension == 2) { for (size_t t = 0; t < boundary_integral_reconstruction_matrix_builder_pool.size(); ++t) { - SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek = inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[t]; - SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - SmallArray<double>& mean_i_of_ejk = mean_i_of_ejk_pool[t]; + SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; boundary_integral_reconstruction_matrix_builder_pool[t] = std::make_shared<BoundaryIntegralReconstructionMatrixBuilder<MeshType>>(*p_mesh, m_descriptor.degree(), - inv_Vj_alpha_p_1_wq_X_prime_orth_ek, - mean_j_of_ejk, mean_i_of_ejk, - symmetry_origin_list, + mean_j_of_ejk, symmetry_origin_list, symmetry_normal_list, stencil_array); } } @@ -307,239 +794,10 @@ PolynomialReconstruction::_build( if (cell_is_owned[cell_j_id]) { const int32_t t = tokens.acquire(); - auto stencil_cell_list = stencil_array[cell_j_id]; - + ShrinkMatrixView A(A_pool[t], full_stencil_size(cell_j_id)); ShrinkMatrixView B(B_pool[t], full_stencil_size(cell_j_id)); - size_t column_begin = 0; - for (size_t i_discrete_function_variant = 0; - i_discrete_function_variant < discrete_function_variant_list.size(); ++i_discrete_function_variant) { - const auto& discrete_function_variant = discrete_function_variant_list[i_discrete_function_variant]; - - std::visit( - [&](auto&& discrete_function) { - using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>; - if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) { - using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - const DataType& qj = discrete_function[cell_j_id]; - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - const DataType& qi_qj = discrete_function[cell_i_id] - qj; - if constexpr (std::is_arithmetic_v<DataType>) { - B(index, column_begin) = qi_qj; - } else if constexpr (is_tiny_vector_v<DataType>) { - for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { - B(index, kB) = qi_qj[k]; - } - } else if constexpr (is_tiny_matrix_v<DataType>) { - for (size_t p = 0; p < DataType::NumberOfRows; ++p) { - const size_t kB = column_begin + p * DataType::NumberOfColumns; - for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { - B(index, kB + q) = qi_qj(p, q); - } - } - } - } - - for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); - ++i_symmetry) { - auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - if constexpr (std::is_arithmetic_v<DataType>) { - const DataType& qi_qj = discrete_function[cell_i_id] - qj; - B(index, column_begin) = qi_qj; - } else if constexpr (is_tiny_vector_v<DataType>) { - if constexpr (DataType::Dimension == MeshType::Dimension) { - const Rd& normal = symmetry_normal_list[i_symmetry]; - - const DataType& qi = discrete_function[cell_i_id]; - const DataType& qi_qj = symmetrize_vector(normal, qi) - qj; - for (size_t kB = column_begin, k = 0; k < DataType::Dimension; ++k, ++kB) { - B(index, kB) = qi_qj[k]; - } - } else { - // LCOV_EXCL_START - std::stringstream error_msg; - error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension - << " using a mesh of dimension " << MeshType::Dimension; - throw UnexpectedError(error_msg.str()); - // LCOV_EXCL_STOP - } - } else if constexpr (is_tiny_matrix_v<DataType>) { - if constexpr ((DataType::NumberOfColumns == DataType::NumberOfRows) and - (DataType::NumberOfColumns == MeshType::Dimension)) { - const Rd& normal = symmetry_normal_list[i_symmetry]; - - const DataType& qi = discrete_function[cell_i_id]; - const DataType& qi_qj = symmetrize_matrix(normal, qi) - qj; - for (size_t p = 0; p < DataType::NumberOfRows; ++p) { - for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { - B(index, column_begin + p * DataType::NumberOfColumns + q) = qi_qj(p, q); - } - } - } else { - // LCOV_EXCL_START - std::stringstream error_msg; - error_msg << "cannot symmetrize matrices of dimensions " << DataType::NumberOfRows << 'x' - << DataType::NumberOfColumns << " using a mesh of dimension " << MeshType::Dimension; - throw UnexpectedError(error_msg.str()); - // LCOV_EXCL_STOP - } - } - } - } - - if constexpr (std::is_arithmetic_v<DataType>) { - ++column_begin; - } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) { - column_begin += DataType::Dimension; - } - } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) { - using DataType = std::decay_t<typename DiscreteFunctionT::data_type>; - - const auto qj_vector = discrete_function[cell_j_id]; - - if constexpr (std::is_arithmetic_v<DataType>) { - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - for (size_t l = 0; l < qj_vector.size(); ++l) { - const DataType& qj = qj_vector[l]; - const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; - B(index, column_begin + l) = qi_qj; - } - } - - for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); - ++i_symmetry) { - auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - for (size_t l = 0; l < qj_vector.size(); ++l) { - const DataType& qj = qj_vector[l]; - const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; - B(index, column_begin + l) = qi_qj; - } - } - } - } else if constexpr (is_tiny_vector_v<DataType>) { - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - for (size_t l = 0; l < qj_vector.size(); ++l) { - const DataType& qj = qj_vector[l]; - const DataType& qi_qj = discrete_function[cell_i_id][l] - qj; - for (size_t kB = column_begin + l * DataType::Dimension, k = 0; k < DataType::Dimension; - ++k, ++kB) { - B(index, kB) = qi_qj[k]; - } - } - } - - for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); - ++i_symmetry) { - if constexpr (DataType::Dimension == MeshType::Dimension) { - auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - - const Rd& normal = symmetry_normal_list[i_symmetry]; - - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - - for (size_t l = 0; l < qj_vector.size(); ++l) { - const DataType& qj = qj_vector[l]; - const DataType& qi = discrete_function[cell_i_id][l]; - const DataType& qi_qj = symmetrize_vector(normal, qi) - qj; - for (size_t kB = column_begin + l * DataType::Dimension, k = 0; k < DataType::Dimension; - ++k, ++kB) { - B(index, kB) = qi_qj[k]; - } - } - } - } else { - // LCOV_EXCL_START - std::stringstream error_msg; - error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension - << " using a mesh of dimension " << MeshType::Dimension; - throw UnexpectedError(error_msg.str()); - // LCOV_EXCL_STOP - } - } - } else if constexpr (is_tiny_matrix_v<DataType>) { - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - for (size_t l = 0; l < qj_vector.size(); ++l) { - const DataType& qj = qj_vector[l]; - const DataType& qi = discrete_function[cell_i_id][l]; - const DataType& qi_qj = qi - qj; - - for (size_t p = 0; p < DataType::NumberOfRows; ++p) { - const size_t kB = column_begin + l * DataType::Dimension + p * DataType::NumberOfColumns; - for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { - B(index, kB + q) = qi_qj(p, q); - } - } - } - } - - for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); - ++i_symmetry) { - if constexpr ((DataType::NumberOfRows == MeshType::Dimension) and - (DataType::NumberOfColumns == MeshType::Dimension)) { - auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - - const Rd& normal = symmetry_normal_list[i_symmetry]; - - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - - for (size_t l = 0; l < qj_vector.size(); ++l) { - const DataType& qj = qj_vector[l]; - const DataType& qi = discrete_function[cell_i_id][l]; - const DataType& qi_qj = symmetrize_matrix(normal, qi) - qj; - - for (size_t p = 0; p < DataType::NumberOfRows; ++p) { - const size_t kB = column_begin + l * DataType::Dimension + p * DataType::NumberOfColumns; - for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { - B(index, kB + q) = qi_qj(p, q); - } - } - } - } - } else { - // LCOV_EXCL_START - std::stringstream error_msg; - error_msg << "cannot symmetrize vectors of dimension " << DataType::Dimension - << " using a mesh of dimension " << MeshType::Dimension; - throw UnexpectedError(error_msg.str()); - // LCOV_EXCL_STOP - } - } - } - - if constexpr (std::is_arithmetic_v<DataType>) { - column_begin += qj_vector.size(); - } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) { - column_begin += qj_vector.size() * DataType::Dimension; - } - - } else { - // LCOV_EXCL_START - throw UnexpectedError("invalid discrete function type"); - // LCOV_EXCL_STOP - } - }, - discrete_function_variant->discreteFunction()); - } - - ShrinkMatrixView A(A_pool[t], full_stencil_size(cell_j_id)); + Internal<MeshType>::buildB(cell_j_id, stencil_array, discrete_function_variant_list, symmetry_normal_list, B); if ((m_descriptor.degree() == 1) and (m_descriptor.integrationMethodType() == IntegrationMethodType::cell_center)) { @@ -562,248 +820,24 @@ PolynomialReconstruction::_build( } if (m_descriptor.rowWeighting()) { - // Add row weighting (give more importance to cells that are - // closer to j) - const Rd& Xj = xj[cell_j_id]; - - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - const double weight = 1. / l2Norm(xj[cell_i_id] - Xj); - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) *= weight; - } - for (size_t l = 0; l < number_of_columns; ++l) { - B(index, l) *= weight; - } - } - for (size_t i_symmetry = 0; i_symmetry < stencil_array.symmetryBoundaryStencilArrayList().size(); - ++i_symmetry) { - auto& ghost_stencil = stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - - const Rd& origin = symmetry_origin_list[i_symmetry]; - const Rd& normal = symmetry_normal_list[i_symmetry]; - - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - const double weight = 1. / l2Norm(symmetrize_coordinates(origin, normal, xj[cell_i_id]) - Xj); - for (size_t l = 0; l < basis_dimension - 1; ++l) { - A(index, l) *= weight; - } - for (size_t l = 0; l < number_of_columns; ++l) { - B(index, l) *= weight; - } - } - } + Internal<MeshType>::rowWeighting(cell_j_id, stencil_array, xj, symmetry_origin_list, symmetry_normal_list, A, + B); } const SmallMatrix<double>& X = X_pool[t]; if (m_descriptor.preconditioning()) { - // Add column weighting preconditioning (increase the presition) + // Add column weighting preconditioning (increase the precision) SmallVector<double>& G = G_pool[t]; - for (size_t l = 0; l < A.numberOfColumns(); ++l) { - double g = 0; - for (size_t i = 0; i < A.numberOfRows(); ++i) { - const double Ail = A(i, l); - - g += Ail * Ail; - } - G[l] = std::sqrt(g); - } - - for (size_t l = 0; l < A.numberOfColumns(); ++l) { - const double Gl = G[l]; - for (size_t i = 0; i < A.numberOfRows(); ++i) { - A(i, l) *= Gl; - } - } - - Givens::solveCollectionInPlace(A, X, B); - - for (size_t l = 0; l < X.numberOfRows(); ++l) { - const double Gl = G[l]; - for (size_t i = 0; i < X.numberOfColumns(); ++i) { - X(l, i) *= Gl; - } - } + Internal<MeshType>::solveCollectionInPlaceWithPreconditionner(A, X, B, G); } else { Givens::solveCollectionInPlace(A, X, B); } - column_begin = 0; - for (size_t i_dpk_variant = 0; i_dpk_variant < mutable_discrete_function_dpk_variant_list.size(); - ++i_dpk_variant) { - const auto& dpk_variant = mutable_discrete_function_dpk_variant_list[i_dpk_variant]; - - const auto& discrete_function_variant = discrete_function_variant_list[i_dpk_variant]; - - std::visit( - [&](auto&& dpk_function, auto&& p0_function) { - using DPkFunctionT = std::decay_t<decltype(dpk_function)>; - using P0FunctionT = std::decay_t<decltype(p0_function)>; - using DataType = std::remove_const_t<std::decay_t<typename DPkFunctionT::data_type>>; - using P0DataType = std::remove_const_t<std::decay_t<typename P0FunctionT::data_type>>; - - if constexpr (std::is_same_v<DataType, P0DataType>) { - if constexpr (is_discrete_function_P0_v<P0FunctionT>) { - 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>) { - if (m_descriptor.degree() > 1) { - auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - for (size_t i = 0; i < basis_dimension - 1; ++i) { - dpk_j[0] -= X(i, column_begin) * mean_j_of_ejk[i]; - } - } - - for (size_t i = 0; i < basis_dimension - 1; ++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>) { - if (m_descriptor.degree() > 1) { - auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - for (size_t i = 0; i < basis_dimension - 1; ++i) { - auto& dpk_j_0 = dpk_j[0]; - for (size_t k = 0; k < DataType::Dimension; ++k) { - dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; - } - } - } - - for (size_t i = 0; i < basis_dimension - 1; ++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>) { - if (m_descriptor.degree() > 1) { - auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - for (size_t i = 0; i < basis_dimension - 1; ++i) { - auto& dpk_j_0 = dpk_j[0]; - for (size_t k = 0; k < DataType::NumberOfRows; ++k) { - for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - dpk_j_0(k, l) -= - X(i, column_begin + k * DataType::NumberOfColumns + l) * mean_j_of_ejk[i]; - } - } - } - } - - for (size_t i = 0; i < basis_dimension - 1; ++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 - } - } else { - // LCOV_EXCL_START - throw UnexpectedError("unexpected discrete dpk function type"); - // LCOV_EXCL_STOP - } - } else if constexpr (is_discrete_function_P0_vector_v<P0FunctionT>) { - 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 = basis_dimension; - - 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>) { - if (m_descriptor.degree() > 1) { - auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - for (size_t i = 0; i < basis_dimension - 1; ++i) { - dpk_j[component_begin] -= X(i, column_begin) * mean_j_of_ejk[i]; - } - } - - for (size_t i = 0; i < basis_dimension - 1; ++i) { - auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; - dpk_j_ip1 = X(i, column_begin); - } - ++column_begin; - } else if constexpr (is_tiny_vector_v<DataType>) { - if (m_descriptor.degree() > 1) { - auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - for (size_t i = 0; i < basis_dimension - 1; ++i) { - auto& dpk_j_0 = dpk_j[component_begin]; - for (size_t k = 0; k < DataType::Dimension; ++k) { - dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; - } - } - } - - for (size_t i = 0; i < basis_dimension - 1; ++i) { - auto& dpk_j_ip1 = dpk_j[component_begin + 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>) { - if (m_descriptor.degree() > 1) { - auto& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - for (size_t i = 0; i < basis_dimension - 1; ++i) { - auto& dpk_j_0 = dpk_j[component_begin]; - for (size_t p = 0; p < DataType::NumberOfRows; ++p) { - for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { - dpk_j_0(p, q) -= - X(i, column_begin + p * DataType::NumberOfColumns + q) * mean_j_of_ejk[i]; - } - } - } - } - - for (size_t i = 0; i < basis_dimension - 1; ++i) { - auto& dpk_j_ip1 = dpk_j[component_begin + i + 1]; - for (size_t p = 0; p < DataType::NumberOfRows; ++p) { - for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { - dpk_j_ip1(p, q) = X(i, column_begin + p * DataType::NumberOfColumns + q); - } - } - } - column_begin += DataType::Dimension; - } else { - // LCOV_EXCL_START - throw UnexpectedError("unexpected data type"); - // LCOV_EXCL_STOP - } - } - } 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()); - } + Internal<MeshType>::populateDiscreteFunctionDPkByCell(cell_j_id, m_descriptor.degree(), X, + mean_j_of_ejk_pool[t], discrete_function_variant_list, + mutable_discrete_function_dpk_variant_list); tokens.release(t); } diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index 349ae4577..fabd3e8ee 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -10,6 +10,9 @@ class DiscreteFunctionVariant; class PolynomialReconstruction { private: + template <MeshConcept MeshType> + class Internal; + class MutableDiscreteFunctionDPkVariant; template <MeshConcept MeshType> diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp index 350618f56..d22ecbb14 100644 --- a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp @@ -188,9 +188,7 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder BoundaryIntegralReconstructionMatrixBuilder(const MeshType& mesh, const size_t polynomial_degree, - const SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek, const SmallArray<double>& mean_j_of_ejk, - const SmallArray<double>& mean_i_of_ejk, const SmallArray<const Rd>& symmetry_origin_list, const SmallArray<const Rd>& symmetry_normal_list, const CellToCellStencilArray& stencil_array) @@ -198,9 +196,9 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder m_basis_dimension{ DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(polynomial_degree)}, m_polynomial_degree{polynomial_degree}, - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek{inv_Vj_alpha_p_1_wq_X_prime_orth_ek}, + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek{m_basis_dimension}, m_mean_j_of_ejk{mean_j_of_ejk}, - m_mean_i_of_ejk{mean_i_of_ejk}, + m_mean_i_of_ejk{m_basis_dimension - 1}, m_cell_to_face_matrix{mesh.connectivity().cellToFaceMatrix()}, m_face_to_node_matrix{mesh.connectivity().faceToNodeMatrix()}, m_cell_face_is_reversed{mesh.connectivity().cellFaceIsReversed()}, diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp index efb92d0db..1e5b7d715 100644 --- a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp @@ -502,9 +502,7 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder ElementIntegralReconstructionMatrixBuilder(const MeshType& mesh, const size_t polynomial_degree, - const SmallArray<double>& inv_Vj_wq_detJ_ek, const SmallArray<double>& mean_j_of_ejk, - const SmallArray<double>& mean_i_of_ejk, const SmallArray<const Rd>& symmetry_origin_list, const SmallArray<const Rd>& symmetry_normal_list, const CellToCellStencilArray& stencil_array) @@ -512,9 +510,9 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder polynomial_degree)}, m_polynomial_degree{polynomial_degree}, - m_wq_detJ_ek{inv_Vj_wq_detJ_ek}, + m_wq_detJ_ek{m_basis_dimension}, m_mean_j_of_ejk{mean_j_of_ejk}, - m_mean_i_of_ejk{mean_i_of_ejk}, + m_mean_i_of_ejk{m_basis_dimension - 1}, m_cell_to_node_matrix{mesh.connectivity().cellToNodeMatrix()}, m_stencil_array{stencil_array}, diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index ec106f497..c0bfa81f1 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -6,14 +6,28 @@ void test_reconstruction(std::shared_ptr<const DiscreteFunctionVariant> df, size_t degree, size_t number) { - PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::boundary, degree}; - PolynomialReconstruction rec_builder{descriptor}; + { + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::boundary, degree}; + PolynomialReconstruction rec_builder{descriptor}; - Timer t; + Timer t; - for (size_t i = 0; i < number; ++i) { - auto rec = rec_builder.build(df); + for (size_t i = 0; i < number; ++i) { + auto rec = rec_builder.build(df); + } + + std::cout << "*** Elapsed time for " << number << " boundary reconstructions: " << t.seconds() << "s ***\n"; } + { + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, degree}; + PolynomialReconstruction rec_builder{descriptor}; + + Timer t; - std::cout << "*** Elapsed time for " << number << " reconstructions: " << t.seconds() << "s ***\n"; + for (size_t i = 0; i < number; ++i) { + auto rec = rec_builder.build(df); + } + + std::cout << "*** Elapsed time for " << number << " element reconstructions: " << t.seconds() << "s ***\n"; + } } -- GitLab From 4926b9a07c12e66e83a35269a9784f18c61cff3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Wed, 14 May 2025 12:50:27 +0200 Subject: [PATCH 113/122] Instanciate relevant ReconstructionMatrixBuilderType at compile time --- src/scheme/PolynomialReconstruction.cpp | 174 +++++++++--------- src/scheme/PolynomialReconstruction.hpp | 5 + ...aryIntegralReconstructionMatrixBuilder.hpp | 17 +- .../CellCenterReconstructionMatrixBuilder.hpp | 25 ++- ...entIntegralReconstructionMatrixBuilder.hpp | 22 ++- src/scheme/test_reconstruction.cpp | 13 ++ 6 files changed, 157 insertions(+), 99 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 464ce1a0f..312306336 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -370,12 +370,13 @@ class PolynomialReconstruction::Internal } } + template <typename ReconstructionMatrixBuilderType> static void populateDiscreteFunctionDPkByCell( const CellId& cell_j_id, const size_t& degree, const SmallMatrix<double>& X, - const SmallArray<double>& mean_j_of_ejk, + const ReconstructionMatrixBuilderType& reconstruction_matrix_builder, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list, const std::vector<PolynomialReconstruction::MutableDiscreteFunctionDPkVariant>& mutable_discrete_function_dpk_variant_list) @@ -400,9 +401,12 @@ class PolynomialReconstruction::Internal dpk_j[0] = p0_function[cell_j_id]; if constexpr (std::is_arithmetic_v<DataType>) { - if (degree > 1) { - for (size_t i = 0; i < X.numberOfRows(); ++i) { - dpk_j[0] -= X(i, column_begin) * mean_j_of_ejk[i]; + if constexpr (ReconstructionMatrixBuilderType::handles_high_degrees) { + if (degree > 1) { + const auto& mean_j_of_ejk = reconstruction_matrix_builder.meanjOfEjk(); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + dpk_j[0] -= X(i, column_begin) * mean_j_of_ejk[i]; + } } } @@ -412,11 +416,14 @@ class PolynomialReconstruction::Internal } ++column_begin; } else if constexpr (is_tiny_vector_v<DataType>) { - if (degree > 1) { - for (size_t i = 0; i < X.numberOfRows(); ++i) { - auto& dpk_j_0 = dpk_j[0]; - for (size_t k = 0; k < DataType::Dimension; ++k) { - dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; + if constexpr (ReconstructionMatrixBuilderType::handles_high_degrees) { + if (degree > 1) { + const auto& mean_j_of_ejk = reconstruction_matrix_builder.meanjOfEjk(); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_0 = dpk_j[0]; + for (size_t k = 0; k < DataType::Dimension; ++k) { + dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; + } } } } @@ -429,12 +436,15 @@ class PolynomialReconstruction::Internal } column_begin += DataType::Dimension; } else if constexpr (is_tiny_matrix_v<DataType>) { - if (degree > 1) { - for (size_t i = 0; i < X.numberOfRows(); ++i) { - auto& dpk_j_0 = dpk_j[0]; - for (size_t k = 0; k < DataType::NumberOfRows; ++k) { - for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { - dpk_j_0(k, l) -= X(i, column_begin + k * DataType::NumberOfColumns + l) * mean_j_of_ejk[i]; + if constexpr (ReconstructionMatrixBuilderType::handles_high_degrees) { + if (degree > 1) { + const auto& mean_j_of_ejk = reconstruction_matrix_builder.meanjOfEjk(); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_0 = dpk_j[0]; + for (size_t k = 0; k < DataType::NumberOfRows; ++k) { + for (size_t l = 0; l < DataType::NumberOfColumns; ++l) { + dpk_j_0(k, l) -= X(i, column_begin + k * DataType::NumberOfColumns + l) * mean_j_of_ejk[i]; + } } } } @@ -469,9 +479,12 @@ class PolynomialReconstruction::Internal const size_t component_begin = l * size; dpk_j[component_begin] = cell_vector[l]; if constexpr (std::is_arithmetic_v<DataType>) { - if (degree > 1) { - for (size_t i = 0; i < X.numberOfRows(); ++i) { - dpk_j[component_begin] -= X(i, column_begin) * mean_j_of_ejk[i]; + if constexpr (ReconstructionMatrixBuilderType::handles_high_degrees) { + const auto& mean_j_of_ejk = reconstruction_matrix_builder.meanjOfEjk(); + if (degree > 1) { + for (size_t i = 0; i < X.numberOfRows(); ++i) { + dpk_j[component_begin] -= X(i, column_begin) * mean_j_of_ejk[i]; + } } } @@ -481,11 +494,14 @@ class PolynomialReconstruction::Internal } ++column_begin; } else if constexpr (is_tiny_vector_v<DataType>) { - if (degree > 1) { - for (size_t i = 0; i < X.numberOfRows(); ++i) { - auto& dpk_j_0 = dpk_j[component_begin]; - for (size_t k = 0; k < DataType::Dimension; ++k) { - dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; + if constexpr (ReconstructionMatrixBuilderType::handles_high_degrees) { + if (degree > 1) { + const auto& mean_j_of_ejk = reconstruction_matrix_builder.meanjOfEjk(); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_0 = dpk_j[component_begin]; + for (size_t k = 0; k < DataType::Dimension; ++k) { + dpk_j_0[k] -= X(i, column_begin + k) * mean_j_of_ejk[i]; + } } } } @@ -498,12 +514,16 @@ class PolynomialReconstruction::Internal } column_begin += DataType::Dimension; } else if constexpr (is_tiny_matrix_v<DataType>) { - if (degree > 1) { - for (size_t i = 0; i < X.numberOfRows(); ++i) { - auto& dpk_j_0 = dpk_j[component_begin]; - for (size_t p = 0; p < DataType::NumberOfRows; ++p) { - for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { - dpk_j_0(p, q) -= X(i, column_begin + p * DataType::NumberOfColumns + q) * mean_j_of_ejk[i]; + if constexpr (ReconstructionMatrixBuilderType::handles_high_degrees) { + if (degree > 1) { + const auto& mean_j_of_ejk = reconstruction_matrix_builder.meanjOfEjk(); + for (size_t i = 0; i < X.numberOfRows(); ++i) { + auto& dpk_j_0 = dpk_j[component_begin]; + for (size_t p = 0; p < DataType::NumberOfRows; ++p) { + for (size_t q = 0; q < DataType::NumberOfColumns; ++q) { + dpk_j_0(p, q) -= + X(i, column_begin + p * DataType::NumberOfColumns + q) * mean_j_of_ejk[i]; + } } } } @@ -660,12 +680,14 @@ PolynomialReconstruction::_checkDataAndSymmetriesCompatibility( } } -template <MeshConcept MeshType> +template <typename ReconstructionMatrixBuilderType, MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::_build( const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const { + static_assert(std::is_same_v<MeshType, typename ReconstructionMatrixBuilderType::MeshType>); + const MeshType& mesh = *p_mesh; using Rd = TinyVector<MeshType::Dimension>; @@ -748,45 +770,19 @@ PolynomialReconstruction::_build( SmallArray<SmallVector<double>> G_pool(Kokkos::DefaultExecutionSpace::concurrency()); SmallArray<SmallMatrix<double>> X_pool(Kokkos::DefaultExecutionSpace::concurrency()); - SmallArray<SmallArray<double>> mean_j_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency()); - for (size_t i = 0; i < A_pool.size(); ++i) { A_pool[i] = SmallMatrix<double>(max_stencil_size, basis_dimension - 1); B_pool[i] = SmallMatrix<double>(max_stencil_size, number_of_columns); G_pool[i] = SmallVector<double>(basis_dimension - 1); X_pool[i] = SmallMatrix<double>(basis_dimension - 1, number_of_columns); - - mean_j_of_ejk_pool[i] = SmallArray<double>(basis_dimension - 1); } - std::shared_ptr p_cell_center_reconstruction_matrix_builder = - std::make_shared<CellCenterReconstructionMatrixBuilder<MeshType>>(symmetry_origin_list, symmetry_normal_list, - stencil_array, xj); - - SmallArray<std::shared_ptr<ElementIntegralReconstructionMatrixBuilder<MeshType>>> - element_integral_reconstruction_matrix_builder_pool{A_pool.size()}; - - for (size_t t = 0; t < element_integral_reconstruction_matrix_builder_pool.size(); ++t) { - SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; - - element_integral_reconstruction_matrix_builder_pool[t] = - std::make_shared<ElementIntegralReconstructionMatrixBuilder<MeshType>>(*p_mesh, m_descriptor.degree(), - mean_j_of_ejk, symmetry_origin_list, - symmetry_normal_list, stencil_array); - } - - SmallArray<std::shared_ptr<BoundaryIntegralReconstructionMatrixBuilder<MeshType>>> - boundary_integral_reconstruction_matrix_builder_pool{A_pool.size()}; - - if constexpr (MeshType::Dimension == 2) { - for (size_t t = 0; t < boundary_integral_reconstruction_matrix_builder_pool.size(); ++t) { - SmallArray<double>& mean_j_of_ejk = mean_j_of_ejk_pool[t]; + SmallArray<std::shared_ptr<ReconstructionMatrixBuilderType>> reconstruction_matrix_builder_pool(A_pool.size()); - boundary_integral_reconstruction_matrix_builder_pool[t] = - std::make_shared<BoundaryIntegralReconstructionMatrixBuilder<MeshType>>(*p_mesh, m_descriptor.degree(), - mean_j_of_ejk, symmetry_origin_list, - symmetry_normal_list, stencil_array); - } + for (size_t t = 0; t < reconstruction_matrix_builder_pool.size(); ++t) { + reconstruction_matrix_builder_pool[t] = + std::make_shared<ReconstructionMatrixBuilderType>(*p_mesh, m_descriptor.degree(), symmetry_origin_list, + symmetry_normal_list, stencil_array); } parallel_for( @@ -799,25 +795,8 @@ PolynomialReconstruction::_build( Internal<MeshType>::buildB(cell_j_id, stencil_array, discrete_function_variant_list, symmetry_normal_list, B); - if ((m_descriptor.degree() == 1) and - (m_descriptor.integrationMethodType() == IntegrationMethodType::cell_center)) { - p_cell_center_reconstruction_matrix_builder->build(cell_j_id, A); - } else if ((m_descriptor.integrationMethodType() == IntegrationMethodType::element) or - (m_descriptor.integrationMethodType() == IntegrationMethodType::boundary)) { -#warning implement boundary based reconstruction for 1d and 3d - if ((m_descriptor.integrationMethodType() == IntegrationMethodType::boundary) and - (MeshType::Dimension == 2)) { - if constexpr (MeshType::Dimension == 2) { - boundary_integral_reconstruction_matrix_builder_pool[t]->build(cell_j_id, A); - } else { - throw NotImplementedError("invalid mesh dimension"); - } - } else { - element_integral_reconstruction_matrix_builder_pool[t]->build(cell_j_id, A); - } - } else { - throw UnexpectedError("invalid integration strategy"); - } + ReconstructionMatrixBuilderType& reconstruction_matrix_builder = *reconstruction_matrix_builder_pool[t]; + reconstruction_matrix_builder.build(cell_j_id, A); if (m_descriptor.rowWeighting()) { Internal<MeshType>::rowWeighting(cell_j_id, stencil_array, xj, symmetry_origin_list, symmetry_normal_list, A, @@ -835,9 +814,10 @@ PolynomialReconstruction::_build( Givens::solveCollectionInPlace(A, X, B); } - Internal<MeshType>::populateDiscreteFunctionDPkByCell(cell_j_id, m_descriptor.degree(), X, - mean_j_of_ejk_pool[t], discrete_function_variant_list, - mutable_discrete_function_dpk_variant_list); + Internal<MeshType>::template populateDiscreteFunctionDPkByCell(cell_j_id, m_descriptor.degree(), X, + reconstruction_matrix_builder, + discrete_function_variant_list, + mutable_discrete_function_dpk_variant_list); tokens.release(t); } @@ -858,6 +838,34 @@ PolynomialReconstruction::_build( return discrete_function_dpk_variant_list; } +template <MeshConcept MeshType> +[[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> +PolynomialReconstruction::_build( + const std::shared_ptr<const MeshType>& p_mesh, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const +{ + switch (m_descriptor.integrationMethodType()) { + case IntegrationMethodType::cell_center: { + return this->_build<CellCenterReconstructionMatrixBuilder<MeshType>>(p_mesh, discrete_function_variant_list); + } + case IntegrationMethodType::boundary: { + if constexpr (MeshType::Dimension == 2) { + return this->_build<BoundaryIntegralReconstructionMatrixBuilder<MeshType>>(p_mesh, + discrete_function_variant_list); + } + [[fallthrough]]; + } + case IntegrationMethodType::element: { + return this->_build<ElementIntegralReconstructionMatrixBuilder<MeshType>>(p_mesh, discrete_function_variant_list); + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("invalid reconstruction matrix builder type"); + } + // LCOV_EXCL_STOP + } +} + std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> PolynomialReconstruction::build( const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const diff --git a/src/scheme/PolynomialReconstruction.hpp b/src/scheme/PolynomialReconstruction.hpp index fabd3e8ee..4f7684471 100644 --- a/src/scheme/PolynomialReconstruction.hpp +++ b/src/scheme/PolynomialReconstruction.hpp @@ -38,6 +38,11 @@ class PolynomialReconstruction const std::shared_ptr<const MeshType>& p_mesh, const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + template <typename ReconstructionMatrixBuilderType, MeshConcept MeshType> + [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> _build( + const std::shared_ptr<const MeshType>& p_mesh, + const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const; + template <MeshConcept MeshType> [[nodiscard]] std::vector<std::shared_ptr<const DiscreteFunctionDPkVariant>> _build( const std::shared_ptr<const MeshType>& p_mesh, diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp index d22ecbb14..105c4640e 100644 --- a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp @@ -14,9 +14,14 @@ #include <scheme/PolynomialReconstruction.hpp> #include <utils/SmallArray.hpp> -template <MeshConcept MeshType> +template <MeshConcept MeshTypeT> class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder { + public: + using MeshType = MeshTypeT; + + constexpr static bool handles_high_degrees = true; + private: using Rd = TinyVector<MeshType::Dimension>; @@ -142,6 +147,13 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder } public: + PUGS_INLINE + SmallArray<const double> + meanjOfEjk() const + { + return m_mean_j_of_ejk; + } + template <typename MatrixType> void build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) @@ -188,7 +200,6 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder BoundaryIntegralReconstructionMatrixBuilder(const MeshType& mesh, const size_t polynomial_degree, - const SmallArray<double>& mean_j_of_ejk, const SmallArray<const Rd>& symmetry_origin_list, const SmallArray<const Rd>& symmetry_normal_list, const CellToCellStencilArray& stencil_array) @@ -197,7 +208,7 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(polynomial_degree)}, m_polynomial_degree{polynomial_degree}, m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek{m_basis_dimension}, - m_mean_j_of_ejk{mean_j_of_ejk}, + m_mean_j_of_ejk{m_basis_dimension - 1}, m_mean_i_of_ejk{m_basis_dimension - 1}, m_cell_to_face_matrix{mesh.connectivity().cellToFaceMatrix()}, m_face_to_node_matrix{mesh.connectivity().faceToNodeMatrix()}, diff --git a/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp index 9f3ee9524..ae3488d81 100644 --- a/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp @@ -8,9 +8,14 @@ #include <scheme/PolynomialReconstruction.hpp> #include <utils/SmallArray.hpp> -template <MeshConcept MeshType> +template <MeshConcept MeshTypeT> class PolynomialReconstruction::CellCenterReconstructionMatrixBuilder { + public: + using MeshType = MeshTypeT; + + constexpr static bool handles_high_degrees = false; + private: using Rd = TinyVector<MeshType::Dimension>; @@ -54,16 +59,22 @@ class PolynomialReconstruction::CellCenterReconstructionMatrixBuilder } } - CellCenterReconstructionMatrixBuilder(const SmallArray<const Rd>& symmetry_origin_list, + CellCenterReconstructionMatrixBuilder(const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<const Rd>& symmetry_origin_list, const SmallArray<const Rd>& symmetry_normal_list, - const CellToCellStencilArray& stencil_array, - const CellValue<const Rd>& xj) - : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(1)}, + const CellToCellStencilArray& stencil_array) + : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree( + polynomial_degree)}, m_symmetry_origin_list{symmetry_origin_list}, m_symmetry_normal_list{symmetry_normal_list}, m_stencil_array{stencil_array}, - m_xj{xj} - {} + m_xj{MeshDataManager::instance().getMeshData(mesh).xj()} + { + if (polynomial_degree != 1) { + throw NormalError("cell center based reconstruction is only valid for first order"); + } + } ~CellCenterReconstructionMatrixBuilder() = default; }; diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp index 1e5b7d715..fd877ccdb 100644 --- a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp @@ -21,9 +21,14 @@ #include <scheme/PolynomialReconstruction.hpp> #include <utils/SmallArray.hpp> -template <MeshConcept MeshType> +template <MeshConcept MeshTypeT> class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder { + public: + using MeshType = MeshTypeT; + + constexpr static bool handles_high_degrees = true; + private: using Rd = TinyVector<MeshType::Dimension>; @@ -460,6 +465,13 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder } public: + PUGS_INLINE + SmallArray<const double> + meanjOfEjk() const + { + return m_mean_j_of_ejk; + } + template <typename MatrixType> void build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) @@ -502,7 +514,6 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder ElementIntegralReconstructionMatrixBuilder(const MeshType& mesh, const size_t polynomial_degree, - const SmallArray<double>& mean_j_of_ejk, const SmallArray<const Rd>& symmetry_origin_list, const SmallArray<const Rd>& symmetry_normal_list, const CellToCellStencilArray& stencil_array) @@ -511,7 +522,7 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder m_polynomial_degree{polynomial_degree}, m_wq_detJ_ek{m_basis_dimension}, - m_mean_j_of_ejk{mean_j_of_ejk}, + m_mean_j_of_ejk{m_basis_dimension - 1}, m_mean_i_of_ejk{m_basis_dimension - 1}, m_cell_to_node_matrix{mesh.connectivity().cellToNodeMatrix()}, @@ -524,14 +535,13 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder m_xr{mesh.xr()} { if constexpr (MeshType::Dimension == 2) { - SmallArray<size_t> y_row_index(m_polynomial_degree + 2); + SmallArray<size_t> y_row_index(m_polynomial_degree + 1); size_t i_y = 0; y_row_index[i_y++] = 0; - for (ssize_t n = m_polynomial_degree + 1; n >= 1; --n) { + for (ssize_t n = m_polynomial_degree + 1; n > 1; --n, ++i_y) { y_row_index[i_y] = y_row_index[i_y - 1] + n; - ++i_y; } m_y_row_index = y_row_index; diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp index c0bfa81f1..1353ab461 100644 --- a/src/scheme/test_reconstruction.cpp +++ b/src/scheme/test_reconstruction.cpp @@ -6,6 +6,19 @@ void test_reconstruction(std::shared_ptr<const DiscreteFunctionVariant> df, size_t degree, size_t number) { + if (degree == 1) { + PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, degree}; + PolynomialReconstruction rec_builder{descriptor}; + + Timer t; + + for (size_t i = 0; i < number; ++i) { + auto rec = rec_builder.build(df); + } + + std::cout << "*** Elapsed time for " << number << " cell center reconstructions: " << t.seconds() << "s ***\n"; + } + { PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::boundary, degree}; PolynomialReconstruction rec_builder{descriptor}; -- GitLab From 5d4047d3701392a7cef28e00352af0c8c7fa7211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Wed, 14 May 2025 15:12:53 +0200 Subject: [PATCH 114/122] Displace symmetry utilities in `geometry/SymmetryUtils.hpp` --- src/geometry/SymmetryUtils.hpp | 32 +++++++++++++++++++++++++ src/scheme/PolynomialReconstruction.cpp | 27 +-------------------- 2 files changed, 33 insertions(+), 26 deletions(-) create mode 100644 src/geometry/SymmetryUtils.hpp diff --git a/src/geometry/SymmetryUtils.hpp b/src/geometry/SymmetryUtils.hpp new file mode 100644 index 000000000..6524a0791 --- /dev/null +++ b/src/geometry/SymmetryUtils.hpp @@ -0,0 +1,32 @@ +#ifndef SYMMETRY_UTILS_HPP +#define SYMMETRY_UTILS_HPP + +#include <algebra/TinyMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <utils/PugsMacros.hpp> + +template <size_t Dimension> +PUGS_INLINE auto +symmetrize_vector(const TinyVector<Dimension>& normal, const TinyVector<Dimension>& u) +{ + return u - 2 * dot(u, normal) * normal; +} + +template <size_t Dimension> +PUGS_INLINE auto +symmetrize_matrix(const TinyVector<Dimension>& normal, const TinyMatrix<Dimension>& A) +{ + const TinyMatrix S = TinyMatrix<Dimension>{identity} - 2 * tensorProduct(normal, normal); + return S * A * S; +} + +template <size_t Dimension> +PUGS_INLINE auto +symmetrize_coordinates(const TinyVector<Dimension>& origin, + const TinyVector<Dimension>& normal, + const TinyVector<Dimension>& u) +{ + return u - 2 * dot(u - origin, normal) * normal; +} + +#endif // SYMMETRY_UTILS_HPP diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 312306336..6073da9c8 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -2,8 +2,8 @@ #include <algebra/Givens.hpp> #include <algebra/ShrinkMatrixView.hpp> -#include <algebra/ShrinkVectorView.hpp> #include <algebra/SmallMatrix.hpp> +#include <geometry/SymmetryUtils.hpp> #include <mesh/MeshData.hpp> #include <mesh/MeshDataManager.hpp> #include <mesh/MeshFlatFaceBoundary.hpp> @@ -18,31 +18,6 @@ #include <scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp> #include <scheme/reconstruction_utils/MutableDiscreteFunctionDPkVariant.hpp> -#warning put in a file in geometry -template <size_t Dimension> -PUGS_INLINE auto -symmetrize_vector(const TinyVector<Dimension>& normal, const TinyVector<Dimension>& u) -{ - return u - 2 * dot(u, normal) * normal; -} - -template <size_t Dimension> -PUGS_INLINE auto -symmetrize_matrix(const TinyVector<Dimension>& normal, const TinyMatrix<Dimension>& A) -{ - const TinyMatrix S = TinyMatrix<Dimension>{identity} - 2 * tensorProduct(normal, normal); - return S * A * S; -} - -template <size_t Dimension> -PUGS_INLINE auto -symmetrize_coordinates(const TinyVector<Dimension>& origin, - const TinyVector<Dimension>& normal, - const TinyVector<Dimension>& u) -{ - return u - 2 * dot(u - origin, normal) * normal; -} - template <MeshConcept MeshType> class PolynomialReconstruction::Internal { -- GitLab From 95c10662caca3506b20359b805ee3535126392b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 15 May 2025 11:54:36 +0200 Subject: [PATCH 115/122] Rework ElementIntegralReconstructionMatrixBuilder implementation --- CMakeLists.txt | 3 + src/scheme/CMakeLists.txt | 8 + .../reconstruction_utils/CMakeLists.txt | 15 + ...entIntegralReconstructionMatrixBuilder.cpp | 505 +++++++++++++++++ ...entIntegralReconstructionMatrixBuilder.hpp | 534 +----------------- tests/CMakeLists.txt | 2 + 6 files changed, 553 insertions(+), 514 deletions(-) create mode 100644 src/scheme/reconstruction_utils/CMakeLists.txt create mode 100644 src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d3306db95..bb8832a50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -737,6 +737,7 @@ target_link_libraries( PugsMesh PugsAlgebra PugsScheme + PugsSchemeReconstructionUtils PugsUtils PugsOutput PugsLanguageUtils @@ -774,6 +775,7 @@ target_link_libraries( PugsLanguageModules PugsLanguageUtils PugsScheme + PugsSchemeReconstructionUtils PugsDev PugsAnalysis PugsAlgebra @@ -816,6 +818,7 @@ install(TARGETS PugsMesh PugsOutput PugsScheme + PugsSchemeReconstructionUtils kokkos Catch2 diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt index c299cd24f..5a555c426 100644 --- a/src/scheme/CMakeLists.txt +++ b/src/scheme/CMakeLists.txt @@ -1,5 +1,7 @@ # ------------------- Source files -------------------- +add_subdirectory(reconstruction_utils) + add_library( PugsScheme AcousticSolver.cpp @@ -20,3 +22,9 @@ target_link_libraries( PugsScheme ${HIGHFIVE_TARGET} ) + +# Additional dependencies +add_dependencies( + PugsScheme + PugsSchemeReconstructionUtils +) diff --git a/src/scheme/reconstruction_utils/CMakeLists.txt b/src/scheme/reconstruction_utils/CMakeLists.txt new file mode 100644 index 000000000..66aaaa241 --- /dev/null +++ b/src/scheme/reconstruction_utils/CMakeLists.txt @@ -0,0 +1,15 @@ +# ------------------- Source files -------------------- + +add_library(PugsSchemeReconstructionUtils + ElementIntegralReconstructionMatrixBuilder.cpp +) + +add_dependencies( + PugsUtils + PugsMesh +) + +target_link_libraries( + PugsSchemeReconstructionUtils + ${HIGHFIVE_TARGET} +) diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp new file mode 100644 index 000000000..faededf69 --- /dev/null +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp @@ -0,0 +1,505 @@ +#include <scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp> + +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> +#include <geometry/CubeTransformation.hpp> +#include <geometry/LineTransformation.hpp> +#include <geometry/PrismTransformation.hpp> +#include <geometry/PyramidTransformation.hpp> +#include <geometry/SquareTransformation.hpp> +#include <geometry/SymmetryUtils.hpp> +#include <geometry/TetrahedronTransformation.hpp> +#include <geometry/TriangleTransformation.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshDataManager.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> + +template <MeshConcept MeshTypeT> +template <typename ConformTransformationT> +void +PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<MeshTypeT>::_computeEjkMean( + const QuadratureFormula<MeshType::Dimension>& quadrature, + const ConformTransformationT& T, + const Rd& Xj, + const double Vi, + SmallArray<double>& mean_of_ejk) noexcept(NO_ASSERT) +{ + mean_of_ejk.fill(0); + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const Rd& xi_q = quadrature.point(i_q); + + const Rd X_Xj = T(xi_q) - Xj; + + if constexpr (MeshType::Dimension == 1) { + const double detT = T.jacobianDeterminant(); + + const double x_xj = X_Xj[0]; + + { + m_wq_detJ_ek[0] = wq * detT; + for (size_t k = 1; k <= m_polynomial_degree; ++k) { + m_wq_detJ_ek[k] = x_xj * m_wq_detJ_ek[k - 1]; + } + } + + } else if constexpr (MeshType::Dimension == 2) { + const double detT = [&] { + if constexpr (std::is_same_v<TriangleTransformation<2>, std::decay_t<decltype(T)>>) { + return T.jacobianDeterminant(); + } else { + return T.jacobianDeterminant(xi_q); + } + }(); + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + + { + size_t k = 0; + m_wq_detJ_ek[k++] = wq * detT; + for (; k <= m_polynomial_degree; ++k) { + m_wq_detJ_ek[k] = x_xj * m_wq_detJ_ek[k - 1]; + } + + for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { + const size_t begin_i_y_1 = m_y_row_index[i_y - 1]; + for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l, ++k) { + m_wq_detJ_ek[k] = y_yj * m_wq_detJ_ek[begin_i_y_1 + l]; + } + } + } + + } else if constexpr (MeshType::Dimension == 3) { + static_assert(MeshType::Dimension == 3); + + const double detT = [&] { + if constexpr (std::is_same_v<TetrahedronTransformation, std::decay_t<decltype(T)>>) { + return T.jacobianDeterminant(); + } else { + return T.jacobianDeterminant(xi_q); + } + }(); + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + const double z_zj = X_Xj[2]; + + { + size_t k = 0; + m_wq_detJ_ek[k++] = wq * detT; + for (; k <= m_polynomial_degree; ++k) { + m_wq_detJ_ek[k] = x_xj * m_wq_detJ_ek[k - 1]; + } + + for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { + const size_t begin_i_y_1 = m_yz_row_index[i_y - 1]; + const size_t nb_monoms = m_yz_row_size[i_y]; + for (size_t l = 0; l < nb_monoms; ++l, ++k) { + m_wq_detJ_ek[k] = y_yj * m_wq_detJ_ek[begin_i_y_1 + l]; + } + } + + for (size_t i_z = 1; i_z <= m_polynomial_degree; ++i_z) { + const size_t nb_y = m_yz_row_size[m_z_triangle_index[i_z]]; + const size_t index_z = m_z_triangle_index[i_z]; + const size_t index_z_1 = m_z_triangle_index[i_z - 1]; + for (size_t i_y = 0; i_y < nb_y; ++i_y) { + const size_t begin_i_yz_1 = m_yz_row_index[index_z_1 + i_y]; + const size_t nb_monoms = m_yz_row_size[index_z + i_y]; + for (size_t l = 0; l < nb_monoms; ++l, ++k) { + m_wq_detJ_ek[k] = z_zj * m_wq_detJ_ek[begin_i_yz_1 + l]; + } + } + } + } + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] += m_wq_detJ_ek[k]; + } + } + + const double inv_Vi = 1. / Vi; + for (size_t k = 0; k < mean_of_ejk.size(); ++k) { + mean_of_ejk[k] *= inv_Vi; + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<MeshTypeT>::_computeEjkMean( + const Rd& Xj, + const CellId& cell_i_id, + SmallArray<double>& mean_of_ejk) +{ + const CellType cell_type = m_cell_type[cell_i_id]; + const auto node_list = m_cell_to_node_matrix[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + + if constexpr (MeshType::Dimension == 1) { + if (m_cell_type[cell_i_id] == CellType::Line) { + const LineTransformation<1> T{m_xr[node_list[0]], m_xr[node_list[1]]}; + + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } else if constexpr (MeshType::Dimension == 2) { + switch (cell_type) { + case CellType::Triangle: { + const TriangleTransformation<2> T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]]}; + const auto& quadrature = + QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_polynomial_degree}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Quadrangle: { + const SquareTransformation<2> T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]]}; + const auto& quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + } else { + static_assert(MeshType::Dimension == 3); + + switch (cell_type) { + case CellType::Tetrahedron: { + const TetrahedronTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]]}; + + const auto& quadrature = + QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_polynomial_degree}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + break; + } + case CellType::Prism: { + const PrismTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], // + m_xr[node_list[3]], m_xr[node_list[4]], m_xr[node_list[5]]}; + + const auto& quadrature = + QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + break; + } + case CellType::Pyramid: { + const PyramidTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]], + m_xr[node_list[4]]}; + + const auto& quadrature = + QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Hexahedron: { + const CubeTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]], + m_xr[node_list[4]], m_xr[node_list[5]], m_xr[node_list[6]], m_xr[node_list[7]]}; + + const auto& quadrature = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<MeshTypeT>::_computeEjkMeanInSymmetricCell( + const Rd& origin, + const Rd& normal, + const Rd& Xj, + const CellId& cell_i_id, + SmallArray<double>& mean_of_ejk) +{ + if constexpr (MeshType::Dimension == 1) { + auto node_list = m_cell_to_node_matrix[cell_i_id]; + const CellType cell_type = m_cell_type[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + + if (cell_type == CellType::Line) { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + + const LineTransformation<1> T{x0, x1}; + + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + + } else { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } else if constexpr (MeshType::Dimension == 2) { + auto node_list = m_cell_to_node_matrix[cell_i_id]; + const CellType cell_type = m_cell_type[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + + switch (cell_type) { + case CellType::Triangle: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + + const TriangleTransformation<2> T{x0, x1, x2}; + const auto& quadrature = + QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_polynomial_degree}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Quadrangle: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + + const SquareTransformation<2> T{x0, x1, x2, x3}; + const auto& quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + } else { + static_assert(MeshType::Dimension == 3); + auto node_list = m_cell_to_node_matrix[cell_i_id]; + const CellType cell_type = m_cell_type[cell_i_id]; + const double Vi = m_Vj[cell_i_id]; + switch (cell_type) { + case CellType::Tetrahedron: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + + const TetrahedronTransformation T{x0, x1, x2, x3}; + + const auto& quadrature = + QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_polynomial_degree}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Prism: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[4]]); + const auto x4 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + const auto x5 = symmetrize_coordinates(origin, normal, m_xr[node_list[5]]); + + const PrismTransformation T{x0, x1, x2, // + x3, x4, x5}; + + const auto& quadrature = + QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Pyramid: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + const auto x4 = symmetrize_coordinates(origin, normal, m_xr[node_list[4]]); + const PyramidTransformation T{x0, x1, x2, x3, x4}; + + const auto& quadrature = + QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + case CellType::Hexahedron: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); + const auto x4 = symmetrize_coordinates(origin, normal, m_xr[node_list[7]]); + const auto x5 = symmetrize_coordinates(origin, normal, m_xr[node_list[6]]); + const auto x6 = symmetrize_coordinates(origin, normal, m_xr[node_list[5]]); + const auto x7 = symmetrize_coordinates(origin, normal, m_xr[node_list[4]]); + + const CubeTransformation T{x0, x1, x2, x3, x4, x5, x6, x7}; + + const auto& quadrature = + QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); + + this->_computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); + break; + } + default: { + throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); + } + } + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<MeshTypeT>::build( + const CellId cell_j_id, + ShrinkMatrixView<SmallMatrix<double>>& A) +{ + const auto& stencil_cell_list = m_stencil_array[cell_j_id]; + + const Rd& Xj = m_xj[cell_j_id]; + + this->_computeEjkMean(Xj, cell_j_id, m_mean_j_of_ejk); + + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + + this->_computeEjkMean(Xj, cell_i_id, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + + for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { + auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = m_symmetry_origin_list[i_symmetry]; + const Rd& normal = m_symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + + this->_computeEjkMeanInSymmetricCell(origin, normal, Xj, cell_i_id, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + } +} + +template <MeshConcept MeshTypeT> +PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder< + MeshTypeT>::ElementIntegralReconstructionMatrixBuilder(const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array) + : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree( + polynomial_degree)}, + m_polynomial_degree{polynomial_degree}, + + m_wq_detJ_ek{m_basis_dimension}, + m_mean_j_of_ejk{m_basis_dimension - 1}, + m_mean_i_of_ejk{m_basis_dimension - 1}, + + m_cell_to_node_matrix{mesh.connectivity().cellToNodeMatrix()}, + m_stencil_array{stencil_array}, + m_symmetry_origin_list{symmetry_origin_list}, + m_symmetry_normal_list{symmetry_normal_list}, + m_cell_type{mesh.connectivity().cellType()}, + m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, + m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, + m_xr{mesh.xr()} +{ + if constexpr (MeshType::Dimension == 2) { + SmallArray<size_t> y_row_index(m_polynomial_degree + 1); + + size_t i_y = 0; + + y_row_index[i_y++] = 0; + for (ssize_t n = m_polynomial_degree + 1; n > 1; --n, ++i_y) { + y_row_index[i_y] = y_row_index[i_y - 1] + n; + } + + m_y_row_index = y_row_index; + + } else if constexpr (MeshType::Dimension == 3) { + SmallArray<size_t> yz_row_index((m_polynomial_degree + 2) * (m_polynomial_degree + 1) / 2 + 1); + SmallArray<size_t> z_triangle_index(m_polynomial_degree + 1); + + { + size_t i_z = 0; + size_t i_yz = 0; + + yz_row_index[i_yz++] = 0; + for (ssize_t n = m_polynomial_degree + 1; n >= 1; --n) { + z_triangle_index[i_z++] = i_yz - 1; + for (ssize_t i = n; i >= 1; --i) { + yz_row_index[i_yz] = yz_row_index[i_yz - 1] + i; + ++i_yz; + } + } + } + + SmallArray<size_t> yz_row_size{yz_row_index.size() - 1}; + for (size_t i = 0; i < yz_row_size.size(); ++i) { + yz_row_size[i] = yz_row_index[i + 1] - yz_row_index[i]; + } + + m_yz_row_index = yz_row_index; + m_z_triangle_index = z_triangle_index; + m_yz_row_size = yz_row_size; + } +} + +template void PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<Mesh<1>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<Mesh<2>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<Mesh<3>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder< + Mesh<1>>::ElementIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder< + Mesh<2>>::ElementIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder< + Mesh<3>>::ElementIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp index fd877ccdb..1f4573438 100644 --- a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp @@ -2,22 +2,12 @@ #define ELEMENT_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP #include <algebra/ShrinkMatrixView.hpp> -#include <analysis/GaussLegendreQuadratureDescriptor.hpp> -#include <analysis/GaussQuadratureDescriptor.hpp> +#include <algebra/SmallMatrix.hpp> #include <analysis/QuadratureFormula.hpp> #include <analysis/QuadratureManager.hpp> -#include <geometry/CubeTransformation.hpp> -#include <geometry/LineTransformation.hpp> -#include <geometry/PrismTransformation.hpp> -#include <geometry/PyramidTransformation.hpp> -#include <geometry/SquareTransformation.hpp> -#include <geometry/TetrahedronTransformation.hpp> -#include <geometry/TriangleTransformation.hpp> -#include <mesh/Connectivity.hpp> -#include <mesh/Mesh.hpp> -#include <mesh/MeshDataManager.hpp> +#include <mesh/CellType.hpp> +#include <mesh/ItemValue.hpp> #include <mesh/StencilArray.hpp> -#include <scheme/DiscreteFunctionDPk.hpp> #include <scheme/PolynomialReconstruction.hpp> #include <utils/SmallArray.hpp> @@ -59,410 +49,21 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder SmallArray<const size_t> m_yz_row_size; template <typename ConformTransformationT> - void - _computeEjkMean(const QuadratureFormula<1>& quadrature, - const ConformTransformationT& T, - const TinyVector<1>& Xj, - const double Vi, - SmallArray<double>& mean_of_ejk) noexcept(NO_ASSERT) - { - mean_of_ejk.fill(0); - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const Rd& xi_q = quadrature.point(i_q); - const double detT = T.jacobianDeterminant(); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - - { - m_wq_detJ_ek[0] = wq * detT; - for (size_t k = 1; k <= m_polynomial_degree; ++k) { - m_wq_detJ_ek[k] = x_xj * m_wq_detJ_ek[k - 1]; - } - } - - for (size_t k = 1; k < m_basis_dimension; ++k) { - mean_of_ejk[k - 1] += m_wq_detJ_ek[k]; - } - } - - const double inv_Vi = 1. / Vi; - for (size_t k = 0; k < mean_of_ejk.size(); ++k) { - mean_of_ejk[k] *= inv_Vi; - } - } - - template <typename ConformTransformationT> - void - _computeEjkMean(const QuadratureFormula<2>& quadrature, - const ConformTransformationT& T, - const TinyVector<2>& Xj, - const double Vi, - SmallArray<double>& mean_of_ejk) noexcept(NO_ASSERT) - { - mean_of_ejk.fill(0); - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const Rd& xi_q = quadrature.point(i_q); - const double detT = [&] { - if constexpr (std::is_same_v<TriangleTransformation<2>, std::decay_t<decltype(T)>>) { - return T.jacobianDeterminant(); - } else { - return T.jacobianDeterminant(xi_q); - } - }(); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - const double y_yj = X_Xj[1]; - - { - size_t k = 0; - m_wq_detJ_ek[k++] = wq * detT; - for (; k <= m_polynomial_degree; ++k) { - m_wq_detJ_ek[k] = x_xj * m_wq_detJ_ek[k - 1]; - } - - for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { - const size_t begin_i_y_1 = m_y_row_index[i_y - 1]; - for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l, ++k) { - m_wq_detJ_ek[k] = y_yj * m_wq_detJ_ek[begin_i_y_1 + l]; - } - } - } - - for (size_t k = 1; k < m_basis_dimension; ++k) { - mean_of_ejk[k - 1] += m_wq_detJ_ek[k]; - } - } - - const double inv_Vi = 1. / Vi; - for (size_t k = 0; k < mean_of_ejk.size(); ++k) { - mean_of_ejk[k] *= inv_Vi; - } - } - - template <typename ConformTransformationT> - void - _computeEjkMean(const QuadratureFormula<3>& quadrature, - const ConformTransformationT& T, - const TinyVector<3>& Xj, - const double Vi, - SmallArray<double>& mean_of_ejk) noexcept(NO_ASSERT) - { - mean_of_ejk.fill(0); - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const Rd& xi_q = quadrature.point(i_q); - const double detT = [&] { - if constexpr (std::is_same_v<TetrahedronTransformation, std::decay_t<decltype(T)>>) { - return T.jacobianDeterminant(); - } else { - return T.jacobianDeterminant(xi_q); - } - }(); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - const double y_yj = X_Xj[1]; - const double z_zj = X_Xj[2]; - - { - size_t k = 0; - m_wq_detJ_ek[k++] = wq * detT; - for (; k <= m_polynomial_degree; ++k) { - m_wq_detJ_ek[k] = x_xj * m_wq_detJ_ek[k - 1]; - } - - for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { - const size_t begin_i_y_1 = m_yz_row_index[i_y - 1]; - const size_t nb_monoms = m_yz_row_size[i_y]; - for (size_t l = 0; l < nb_monoms; ++l, ++k) { - m_wq_detJ_ek[k] = y_yj * m_wq_detJ_ek[begin_i_y_1 + l]; - } - } - - for (size_t i_z = 1; i_z <= m_polynomial_degree; ++i_z) { - const size_t nb_y = m_yz_row_size[m_z_triangle_index[i_z]]; - const size_t index_z = m_z_triangle_index[i_z]; - const size_t index_z_1 = m_z_triangle_index[i_z - 1]; - for (size_t i_y = 0; i_y < nb_y; ++i_y) { - const size_t begin_i_yz_1 = m_yz_row_index[index_z_1 + i_y]; - const size_t nb_monoms = m_yz_row_size[index_z + i_y]; - for (size_t l = 0; l < nb_monoms; ++l, ++k) { - m_wq_detJ_ek[k] = z_zj * m_wq_detJ_ek[begin_i_yz_1 + l]; - } - } - } - } - - for (size_t k = 1; k < m_basis_dimension; ++k) { - mean_of_ejk[k - 1] += m_wq_detJ_ek[k]; - } - } - - const double inv_Vi = 1. / Vi; - for (size_t k = 0; k < mean_of_ejk.size(); ++k) { - mean_of_ejk[k] *= inv_Vi; - } - } - - void - _computeEjkMean(const TinyVector<1>& Xj, const CellId& cell_i_id, SmallArray<double>& mean_of_ejk) - { - const CellType cell_type = m_cell_type[cell_i_id]; - const auto node_list = m_cell_to_node_matrix[cell_i_id]; - const double Vi = m_Vj[cell_i_id]; - - if (m_cell_type[cell_i_id] == CellType::Line) { - const LineTransformation<1> T{m_xr[node_list[0]], m_xr[node_list[1]]}; - - const auto& quadrature = - QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } - } - - void - _computeEjkMean(const TinyVector<2>& Xj, const CellId& cell_i_id, SmallArray<double>& mean_of_ejk) - { - const CellType cell_type = m_cell_type[cell_i_id]; - const auto node_list = m_cell_to_node_matrix[cell_i_id]; - const double Vi = m_Vj[cell_i_id]; - - switch (cell_type) { - case CellType::Triangle: { - const TriangleTransformation<2> T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]]}; - const auto& quadrature = - QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_polynomial_degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - break; - } - case CellType::Quadrangle: { - const SquareTransformation<2> T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]]}; - const auto& quadrature = - QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - break; - } - default: { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } - } - } - - void - _computeEjkMean(const TinyVector<3>& Xj, const CellId& cell_i_id, SmallArray<double>& mean_of_ejk) - { - const CellType cell_type = m_cell_type[cell_i_id]; - const auto node_list = m_cell_to_node_matrix[cell_i_id]; - const double Vi = m_Vj[cell_i_id]; - - switch (cell_type) { - case CellType::Tetrahedron: { - const TetrahedronTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]]}; - - const auto& quadrature = - QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_polynomial_degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - - break; - } - case CellType::Prism: { - const PrismTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], // - m_xr[node_list[3]], m_xr[node_list[4]], m_xr[node_list[5]]}; - - const auto& quadrature = - QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - - break; - } - case CellType::Pyramid: { - const PyramidTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]], - m_xr[node_list[4]]}; - - const auto& quadrature = - QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - break; - } - case CellType::Hexahedron: { - const CubeTransformation T{m_xr[node_list[0]], m_xr[node_list[1]], m_xr[node_list[2]], m_xr[node_list[3]], - m_xr[node_list[4]], m_xr[node_list[5]], m_xr[node_list[6]], m_xr[node_list[7]]}; - - const auto& quadrature = - QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - break; - } - default: { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } - } - } - - void - _computeEjkMeanInSymmetricCell(const TinyVector<1>& origin, - const TinyVector<1>& normal, - const TinyVector<1>& Xj, - const CellId& cell_i_id, - SmallArray<double>& mean_of_ejk) - { - auto node_list = m_cell_to_node_matrix[cell_i_id]; - const CellType cell_type = m_cell_type[cell_i_id]; - const double Vi = m_Vj[cell_i_id]; - - if (cell_type == CellType::Line) { - const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); - - const LineTransformation<1> T{x0, x1}; - - const auto& quadrature = - QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } - } - - void - _computeEjkMeanInSymmetricCell(const TinyVector<2>& origin, - const TinyVector<2>& normal, - const TinyVector<2>& Xj, - const CellId& cell_i_id, - SmallArray<double>& mean_of_ejk) - { - auto node_list = m_cell_to_node_matrix[cell_i_id]; - const CellType cell_type = m_cell_type[cell_i_id]; - const double Vi = m_Vj[cell_i_id]; - - switch (cell_type) { - case CellType::Triangle: { - const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); - const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); - const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); - - const TriangleTransformation<2> T{x0, x1, x2}; - const auto& quadrature = - QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor{m_polynomial_degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - break; - } - case CellType::Quadrangle: { - const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); - const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); - const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); - const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); - - const SquareTransformation<2> T{x0, x1, x2, x3}; - const auto& quadrature = - QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - break; - } - default: { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } - } - } - - void - _computeEjkMeanInSymmetricCell(const TinyVector<3>& origin, - const TinyVector<3>& normal, - const TinyVector<3>& Xj, - const CellId cell_i_id, - SmallArray<double>& mean_of_ejk) - { - auto node_list = m_cell_to_node_matrix[cell_i_id]; - const CellType cell_type = m_cell_type[cell_i_id]; - const double Vi = m_Vj[cell_i_id]; - - if (cell_type == CellType::Tetrahedron) { - const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); - const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); - const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); - - const TetrahedronTransformation T{x0, x1, x2, x3}; - - const auto& quadrature = - QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor{m_polynomial_degree}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - - } else if (cell_type == CellType::Prism) { - const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); - const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); - const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[4]]); - const auto x4 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); - const auto x5 = symmetrize_coordinates(origin, normal, m_xr[node_list[5]]); - - const PrismTransformation T{x0, x1, x2, // - x3, x4, x5}; - - const auto& quadrature = - QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - - } else if (cell_type == CellType::Pyramid) { - const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); - const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); - const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); - const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); - const auto x4 = symmetrize_coordinates(origin, normal, m_xr[node_list[4]]); - const PyramidTransformation T{x0, x1, x2, x3, x4}; - - const auto& quadrature = - QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor{m_polynomial_degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - - } else if (cell_type == CellType::Hexahedron) { - const auto x0 = symmetrize_coordinates(origin, normal, m_xr[node_list[3]]); - const auto x1 = symmetrize_coordinates(origin, normal, m_xr[node_list[2]]); - const auto x2 = symmetrize_coordinates(origin, normal, m_xr[node_list[1]]); - const auto x3 = symmetrize_coordinates(origin, normal, m_xr[node_list[0]]); - const auto x4 = symmetrize_coordinates(origin, normal, m_xr[node_list[7]]); - const auto x5 = symmetrize_coordinates(origin, normal, m_xr[node_list[6]]); - const auto x6 = symmetrize_coordinates(origin, normal, m_xr[node_list[5]]); - const auto x7 = symmetrize_coordinates(origin, normal, m_xr[node_list[4]]); - - const CubeTransformation T{x0, x1, x2, x3, x4, x5, x6, x7}; - - const auto& quadrature = - QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor{m_polynomial_degree + 1}); - - _computeEjkMean(quadrature, T, Xj, Vi, mean_of_ejk); - - } else { - throw NotImplementedError("unexpected cell type: " + std::string{name(cell_type)}); - } - } + void _computeEjkMean(const QuadratureFormula<MeshType::Dimension>& quadrature, + const ConformTransformationT& T, + const Rd& Xj, + const double Vi, + SmallArray<double>& mean_of_ejk) noexcept(NO_ASSERT); + + void _computeEjkMean(const TinyVector<MeshType::Dimension>& Xj, + const CellId& cell_i_id, + SmallArray<double>& mean_of_ejk); + + void _computeEjkMeanInSymmetricCell(const Rd& origin, + const Rd& normal, + const Rd& Xj, + const CellId& cell_i_id, + SmallArray<double>& mean_of_ejk); public: PUGS_INLINE @@ -472,108 +73,13 @@ class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder return m_mean_j_of_ejk; } - template <typename MatrixType> - void - build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) - { - const auto& stencil_cell_list = m_stencil_array[cell_j_id]; - - const Rd& Xj = m_xj[cell_j_id]; - - _computeEjkMean(Xj, cell_j_id, m_mean_j_of_ejk); - - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - - _computeEjkMean(Xj, cell_i_id, m_mean_i_of_ejk); - - for (size_t l = 0; l < m_basis_dimension - 1; ++l) { - A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; - } - } - - for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { - auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - - const Rd& origin = m_symmetry_origin_list[i_symmetry]; - const Rd& normal = m_symmetry_normal_list[i_symmetry]; - - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - - _computeEjkMeanInSymmetricCell(origin, normal, Xj, cell_i_id, m_mean_i_of_ejk); - - for (size_t l = 0; l < m_basis_dimension - 1; ++l) { - A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; - } - } - } - } + void build(const CellId cell_j_id, ShrinkMatrixView<SmallMatrix<double>>& A); ElementIntegralReconstructionMatrixBuilder(const MeshType& mesh, const size_t polynomial_degree, const SmallArray<const Rd>& symmetry_origin_list, const SmallArray<const Rd>& symmetry_normal_list, - const CellToCellStencilArray& stencil_array) - : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree( - polynomial_degree)}, - m_polynomial_degree{polynomial_degree}, - - m_wq_detJ_ek{m_basis_dimension}, - m_mean_j_of_ejk{m_basis_dimension - 1}, - m_mean_i_of_ejk{m_basis_dimension - 1}, - - m_cell_to_node_matrix{mesh.connectivity().cellToNodeMatrix()}, - m_stencil_array{stencil_array}, - m_symmetry_origin_list{symmetry_origin_list}, - m_symmetry_normal_list{symmetry_normal_list}, - m_cell_type{mesh.connectivity().cellType()}, - m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, - m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, - m_xr{mesh.xr()} - { - if constexpr (MeshType::Dimension == 2) { - SmallArray<size_t> y_row_index(m_polynomial_degree + 1); - - size_t i_y = 0; - - y_row_index[i_y++] = 0; - for (ssize_t n = m_polynomial_degree + 1; n > 1; --n, ++i_y) { - y_row_index[i_y] = y_row_index[i_y - 1] + n; - } - - m_y_row_index = y_row_index; - - } else if constexpr (MeshType::Dimension == 3) { - SmallArray<size_t> yz_row_index((m_polynomial_degree + 2) * (m_polynomial_degree + 1) / 2 + 1); - SmallArray<size_t> z_triangle_index(m_polynomial_degree + 1); - - { - size_t i_z = 0; - size_t i_yz = 0; - - yz_row_index[i_yz++] = 0; - for (ssize_t n = m_polynomial_degree + 1; n >= 1; --n) { - z_triangle_index[i_z++] = i_yz - 1; - for (ssize_t i = n; i >= 1; --i) { - yz_row_index[i_yz] = yz_row_index[i_yz - 1] + i; - ++i_yz; - } - } - } - - SmallArray<size_t> yz_row_size{yz_row_index.size() - 1}; - for (size_t i = 0; i < yz_row_size.size(); ++i) { - yz_row_size[i] = yz_row_index[i + 1] - yz_row_index[i]; - } - - m_yz_row_index = yz_row_index; - m_z_triangle_index = z_triangle_index; - m_yz_row_size = yz_row_size; - } - } + const CellToCellStencilArray& stencil_array); ~ElementIntegralReconstructionMatrixBuilder() = default; }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fd8915b83..8c05b9ed0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -317,6 +317,7 @@ target_link_libraries (unit_tests PugsAlgebra PugsAnalysis PugsScheme + PugsSchemeReconstructionUtils PugsOutput PugsUtils PugsCheckpointing @@ -347,6 +348,7 @@ target_link_libraries (mpi_unit_tests PugsUtils PugsLanguageUtils PugsScheme + PugsSchemeReconstructionUtils PugsOutput PugsUtils PugsCheckpointing -- GitLab From 87a3e1987cc428a89e1972ef43841c8cf58d0c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 15 May 2025 14:07:59 +0200 Subject: [PATCH 116/122] Slightly redesign of BoundaryIntegralReconstructionMatrixBuilder --- ...aryIntegralReconstructionMatrixBuilder.cpp | 206 ++++++++++++++++++ ...aryIntegralReconstructionMatrixBuilder.hpp | 186 ++-------------- .../reconstruction_utils/CMakeLists.txt | 1 + 3 files changed, 224 insertions(+), 169 deletions(-) create mode 100644 src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp new file mode 100644 index 000000000..2a481517f --- /dev/null +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp @@ -0,0 +1,206 @@ +#include <scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp> + +#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/QuadratureManager.hpp> +#include <geometry/SymmetryUtils.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshDataManager.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> + +template <MeshConcept MeshTypeT> +template <typename ConformTransformationT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT>::_computeEjkBoundaryMean( + const QuadratureFormula<MeshType::Dimension - 1>& quadrature, + const ConformTransformationT& T, + const Rd& Xj, + const double inv_Vi, + SmallArray<double>& mean_of_ejk) +{ + const double velocity_perp_e1 = T.velocity()[1] * inv_Vi; + + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const TinyVector<1> xi_q = quadrature.point(i_q); + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + + { + size_t k = 0; + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1; + for (; k <= m_polynomial_degree; ++k) { + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k] = + x_xj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * m_antiderivative_coef[k]; // ((1. * k) / (k + 1)); + } + + for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { + const size_t begin_i_y_1 = ((i_y - 1) * (2 * m_polynomial_degree - i_y + 4)) / 2; + for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l) { + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = y_yj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[begin_i_y_1 + l]; + } + } + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] += m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k]; + } + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT>::_computeEjkMeanByBoundary( + const Rd& Xj, + const CellId& cell_id, + SmallArray<double>& mean_of_ejk) +{ + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + const double inv_Vi = 1. / m_Vj[cell_id]; + + mean_of_ejk.fill(0); + auto cell_face_list = m_cell_to_face_matrix[cell_id]; + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + bool is_reversed = m_cell_face_is_reversed[cell_id][i_face]; + + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = m_face_to_node_matrix[face_id]; + if (is_reversed) { + const LineTransformation<2> T{m_xr[face_node_list[1]], m_xr[face_node_list[0]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const LineTransformation<2> T{m_xr[face_node_list[0]], m_xr[face_node_list[1]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + MeshTypeT>::_computeEjkMeanByBoundaryInSymmetricCell(const Rd& origin, + const Rd& normal, + const Rd& Xj, + const CellId& cell_id, + SmallArray<double>& mean_of_ejk) +{ + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + const double inv_Vi = 1. / m_Vj[cell_id]; + + mean_of_ejk.fill(0); + auto cell_face_list = m_cell_to_face_matrix[cell_id]; + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + bool is_reversed = m_cell_face_is_reversed[cell_id][i_face]; + + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = m_face_to_node_matrix[face_id]; + + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); + + if (is_reversed) { + const LineTransformation<2> T{x1, x0}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const LineTransformation<2> T{x0, x1}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + } +} + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT>::build( + const CellId cell_j_id, + ShrinkMatrixView<SmallMatrix<double>>& A) +{ + if constexpr (MeshType::Dimension == 2) { + const auto& stencil_cell_list = m_stencil_array[cell_j_id]; + + const Rd& Xj = m_xj[cell_j_id]; + + _computeEjkMeanByBoundary(Xj, cell_j_id, m_mean_j_of_ejk); + + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + + _computeEjkMeanByBoundary(Xj, cell_i_id, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { + auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = m_symmetry_origin_list[i_symmetry]; + const Rd& normal = m_symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + + _computeEjkMeanByBoundaryInSymmetricCell(origin, normal, Xj, cell_i_id, m_mean_i_of_ejk); + + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; + } + } + } + } else { + throw NotImplementedError("invalid mesh dimension"); + } +} + +template <MeshConcept MeshTypeT> +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + MeshTypeT>::BoundaryIntegralReconstructionMatrixBuilder(const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array) + : m_mesh{mesh}, + m_basis_dimension{ + DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(polynomial_degree)}, + m_polynomial_degree{polynomial_degree}, + m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek{m_basis_dimension}, + m_mean_j_of_ejk{m_basis_dimension - 1}, + m_mean_i_of_ejk{m_basis_dimension - 1}, + m_cell_to_face_matrix{mesh.connectivity().cellToFaceMatrix()}, + m_face_to_node_matrix{mesh.connectivity().faceToNodeMatrix()}, + m_cell_face_is_reversed{mesh.connectivity().cellFaceIsReversed()}, + m_stencil_array{stencil_array}, + m_symmetry_origin_list{symmetry_origin_list}, + m_symmetry_normal_list{symmetry_normal_list}, + m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, + m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, + m_xr{mesh.xr()} +{ + if constexpr (MeshType::Dimension == 2) { + SmallArray<double> antiderivative_coef(m_polynomial_degree + 1); + for (size_t k = 0; k < antiderivative_coef.size(); ++k) { + antiderivative_coef[k] = ((1. * k) / (k + 1)); + } + + m_antiderivative_coef = antiderivative_coef; + } +} + +template void PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<2>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + Mesh<2>>::BoundaryIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp index 105c4640e..df24d76a7 100644 --- a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp @@ -2,15 +2,12 @@ #define BOUNDARY_INTEGRAL_RECONSTRUCTION_MATRIX_BUILDER_HPP #include <algebra/ShrinkMatrixView.hpp> -#include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <algebra/SmallMatrix.hpp> #include <analysis/QuadratureFormula.hpp> -#include <analysis/QuadratureManager.hpp> #include <geometry/LineTransformation.hpp> -#include <mesh/Connectivity.hpp> -#include <mesh/Mesh.hpp> -#include <mesh/MeshDataManager.hpp> +#include <mesh/ItemValue.hpp> #include <mesh/StencilArray.hpp> -#include <scheme/DiscreteFunctionDPk.hpp> +#include <mesh/SubItemValuePerItem.hpp> #include <scheme/PolynomialReconstruction.hpp> #include <utils/SmallArray.hpp> @@ -48,103 +45,20 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder SmallArray<const double> m_antiderivative_coef; - void - _computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature, - const LineTransformation<2>& T, - const Rd& Xj, - const double inv_Vi, - SmallArray<double>& mean_of_ejk) - { - const double velocity_perp_e1 = T.velocity()[1] * inv_Vi; - - for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { - const double wq = quadrature.weight(i_q); - const TinyVector<1> xi_q = quadrature.point(i_q); - - const Rd X_Xj = T(xi_q) - Xj; - - const double x_xj = X_Xj[0]; - const double y_yj = X_Xj[1]; - - { - size_t k = 0; - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1; - for (; k <= m_polynomial_degree; ++k) { - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k] = - x_xj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * m_antiderivative_coef[k]; // ((1. * k) / (k + 1)); - } - - for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { - const size_t begin_i_y_1 = ((i_y - 1) * (2 * m_polynomial_degree - i_y + 4)) / 2; - for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l) { - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = y_yj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[begin_i_y_1 + l]; - } - } - } - - for (size_t k = 1; k < m_basis_dimension; ++k) { - mean_of_ejk[k - 1] += m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k]; - } - } - } + template <typename ConformTransformationT> + void _computeEjkBoundaryMean(const QuadratureFormula<MeshType::Dimension - 1>& quadrature, + const ConformTransformationT& T, + const Rd& Xj, + const double inv_Vi, + SmallArray<double>& mean_of_ejk); - void - _computeEjkMeanByBoundary(const Rd& Xj, const CellId& cell_id, SmallArray<double>& mean_of_ejk) - { - const auto& quadrature = - QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); - - const double inv_Vi = 1. / m_Vj[cell_id]; - - mean_of_ejk.fill(0); - auto cell_face_list = m_cell_to_face_matrix[cell_id]; - for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { - bool is_reversed = m_cell_face_is_reversed[cell_id][i_face]; - - const FaceId face_id = cell_face_list[i_face]; - auto face_node_list = m_face_to_node_matrix[face_id]; - if (is_reversed) { - const LineTransformation<2> T{m_xr[face_node_list[1]], m_xr[face_node_list[0]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); - } else { - const LineTransformation<2> T{m_xr[face_node_list[0]], m_xr[face_node_list[1]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); - } - } - } + void _computeEjkMeanByBoundary(const Rd& Xj, const CellId& cell_id, SmallArray<double>& mean_of_ejk); - void - _computeEjkMeanByBoundaryInSymmetricCell(const Rd& origin, - const Rd& normal, - const Rd& Xj, - const CellId& cell_id, - SmallArray<double>& mean_of_ejk) - { - const auto& quadrature = - QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); - - const double inv_Vi = 1. / m_Vj[cell_id]; - - mean_of_ejk.fill(0); - auto cell_face_list = m_cell_to_face_matrix[cell_id]; - for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { - bool is_reversed = m_cell_face_is_reversed[cell_id][i_face]; - - const FaceId face_id = cell_face_list[i_face]; - auto face_node_list = m_face_to_node_matrix[face_id]; - - const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); - - if (is_reversed) { - const LineTransformation<2> T{x1, x0}; - _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); - } else { - const LineTransformation<2> T{x0, x1}; - _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); - } - } - } + void _computeEjkMeanByBoundaryInSymmetricCell(const Rd& origin, + const Rd& normal, + const Rd& Xj, + const CellId& cell_id, + SmallArray<double>& mean_of_ejk); public: PUGS_INLINE @@ -154,79 +68,13 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder return m_mean_j_of_ejk; } - template <typename MatrixType> - void - build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) - { - if constexpr (MeshType::Dimension == 2) { - const auto& stencil_cell_list = m_stencil_array[cell_j_id]; - - const Rd& Xj = m_xj[cell_j_id]; - - _computeEjkMeanByBoundary(Xj, cell_j_id, m_mean_j_of_ejk); - - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - - _computeEjkMeanByBoundary(Xj, cell_i_id, m_mean_i_of_ejk); - - for (size_t l = 0; l < m_basis_dimension - 1; ++l) { - A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; - } - } - for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); - ++i_symmetry) { - auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - - const Rd& origin = m_symmetry_origin_list[i_symmetry]; - const Rd& normal = m_symmetry_normal_list[i_symmetry]; - - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - - _computeEjkMeanByBoundaryInSymmetricCell(origin, normal, Xj, cell_i_id, m_mean_i_of_ejk); - - for (size_t l = 0; l < m_basis_dimension - 1; ++l) { - A(index, l) = m_mean_i_of_ejk[l] - m_mean_j_of_ejk[l]; - } - } - } - } else { - throw NotImplementedError("invalid mesh dimension"); - } - } + void build(const CellId cell_j_id, ShrinkMatrixView<SmallMatrix<double>>& A); BoundaryIntegralReconstructionMatrixBuilder(const MeshType& mesh, const size_t polynomial_degree, const SmallArray<const Rd>& symmetry_origin_list, const SmallArray<const Rd>& symmetry_normal_list, - const CellToCellStencilArray& stencil_array) - : m_mesh{mesh}, - m_basis_dimension{ - DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(polynomial_degree)}, - m_polynomial_degree{polynomial_degree}, - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek{m_basis_dimension}, - m_mean_j_of_ejk{m_basis_dimension - 1}, - m_mean_i_of_ejk{m_basis_dimension - 1}, - m_cell_to_face_matrix{mesh.connectivity().cellToFaceMatrix()}, - m_face_to_node_matrix{mesh.connectivity().faceToNodeMatrix()}, - m_cell_face_is_reversed{mesh.connectivity().cellFaceIsReversed()}, - m_stencil_array{stencil_array}, - m_symmetry_origin_list{symmetry_origin_list}, - m_symmetry_normal_list{symmetry_normal_list}, - m_Vj{MeshDataManager::instance().getMeshData(mesh).Vj()}, - m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, - m_xr{mesh.xr()} - { - SmallArray<double> antiderivative_coef(m_polynomial_degree + 1); - for (size_t k = 0; k < antiderivative_coef.size(); ++k) { - antiderivative_coef[k] = ((1. * k) / (k + 1)); - } - - m_antiderivative_coef = antiderivative_coef; - } + const CellToCellStencilArray& stencil_array); BoundaryIntegralReconstructionMatrixBuilder() = default; ~BoundaryIntegralReconstructionMatrixBuilder() = default; diff --git a/src/scheme/reconstruction_utils/CMakeLists.txt b/src/scheme/reconstruction_utils/CMakeLists.txt index 66aaaa241..fa1395b2d 100644 --- a/src/scheme/reconstruction_utils/CMakeLists.txt +++ b/src/scheme/reconstruction_utils/CMakeLists.txt @@ -1,6 +1,7 @@ # ------------------- Source files -------------------- add_library(PugsSchemeReconstructionUtils + BoundaryIntegralReconstructionMatrixBuilder.cpp ElementIntegralReconstructionMatrixBuilder.cpp ) -- GitLab From 3687847f3ab96dc4cae775e37e98714b76a15836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 15 May 2025 15:18:07 +0200 Subject: [PATCH 117/122] Displace CellCenterReconstructionMatrixBuilder implementation --- .../reconstruction_utils/CMakeLists.txt | 1 + .../CellCenterReconstructionMatrixBuilder.cpp | 92 +++++++++++++++++++ .../CellCenterReconstructionMatrixBuilder.hpp | 49 +--------- 3 files changed, 97 insertions(+), 45 deletions(-) create mode 100644 src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.cpp diff --git a/src/scheme/reconstruction_utils/CMakeLists.txt b/src/scheme/reconstruction_utils/CMakeLists.txt index fa1395b2d..1d4ebcb48 100644 --- a/src/scheme/reconstruction_utils/CMakeLists.txt +++ b/src/scheme/reconstruction_utils/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(PugsSchemeReconstructionUtils BoundaryIntegralReconstructionMatrixBuilder.cpp + CellCenterReconstructionMatrixBuilder.cpp ElementIntegralReconstructionMatrixBuilder.cpp ) diff --git a/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.cpp b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.cpp new file mode 100644 index 000000000..94d1ef9a7 --- /dev/null +++ b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.cpp @@ -0,0 +1,92 @@ +#include <scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp> + +#include <geometry/SymmetryUtils.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <scheme/DiscreteFunctionDPk.hpp> + +template <MeshConcept MeshTypeT> +void +PolynomialReconstruction::CellCenterReconstructionMatrixBuilder<MeshTypeT>::build( + const CellId cell_j_id, + ShrinkMatrixView<SmallMatrix<double>>& A) +{ + const auto& stencil_cell_list = m_stencil_array[cell_j_id]; + + const Rd& Xj = m_xj[cell_j_id]; + size_t index = 0; + for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = stencil_cell_list[i]; + const Rd Xi_Xj = m_xj[cell_i_id] - Xj; + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = Xi_Xj[l]; + } + } + for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { + auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); + auto ghost_cell_list = ghost_stencil[cell_j_id]; + + const Rd& origin = m_symmetry_origin_list[i_symmetry]; + const Rd& normal = m_symmetry_normal_list[i_symmetry]; + + for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { + const CellId cell_i_id = ghost_cell_list[i]; + const Rd Xi_Xj = symmetrize_coordinates(origin, normal, m_xj[cell_i_id]) - Xj; + for (size_t l = 0; l < m_basis_dimension - 1; ++l) { + A(index, l) = Xi_Xj[l]; + } + } + } +} + +template <MeshConcept MeshTypeT> +PolynomialReconstruction::CellCenterReconstructionMatrixBuilder<MeshTypeT>::CellCenterReconstructionMatrixBuilder( + const MeshType& mesh, + const size_t polynomial_degree, + const SmallArray<const Rd>& symmetry_origin_list, + const SmallArray<const Rd>& symmetry_normal_list, + const CellToCellStencilArray& stencil_array) + : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree( + polynomial_degree)}, + m_symmetry_origin_list{symmetry_origin_list}, + m_symmetry_normal_list{symmetry_normal_list}, + m_stencil_array{stencil_array}, + m_xj{MeshDataManager::instance().getMeshData(mesh).xj()} +{ + if (polynomial_degree != 1) { + throw NormalError("cell center based reconstruction is only valid for first order"); + } +} + +template void PolynomialReconstruction::CellCenterReconstructionMatrixBuilder<Mesh<1>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::CellCenterReconstructionMatrixBuilder<Mesh<2>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template void PolynomialReconstruction::CellCenterReconstructionMatrixBuilder<Mesh<3>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + +template PolynomialReconstruction::CellCenterReconstructionMatrixBuilder< + Mesh<1>>::CellCenterReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::CellCenterReconstructionMatrixBuilder< + Mesh<2>>::CellCenterReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + +template PolynomialReconstruction::CellCenterReconstructionMatrixBuilder< + Mesh<3>>::CellCenterReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); diff --git a/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp index ae3488d81..a8205a625 100644 --- a/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/CellCenterReconstructionMatrixBuilder.hpp @@ -2,9 +2,9 @@ #define CELL_CENTER_RECONSTRUCTION_MATRIX_BUILDER_HPP #include <algebra/ShrinkMatrixView.hpp> -#include <mesh/Connectivity.hpp> +#include <algebra/SmallMatrix.hpp> +#include <mesh/ItemValue.hpp> #include <mesh/StencilArray.hpp> -#include <scheme/DiscreteFunctionDPk.hpp> #include <scheme/PolynomialReconstruction.hpp> #include <utils/SmallArray.hpp> @@ -27,54 +27,13 @@ class PolynomialReconstruction::CellCenterReconstructionMatrixBuilder const CellValue<const Rd> m_xj; public: - template <typename MatrixType> - void - build(const CellId cell_j_id, ShrinkMatrixView<MatrixType>& A) - { - const auto& stencil_cell_list = m_stencil_array[cell_j_id]; - - const Rd& Xj = m_xj[cell_j_id]; - size_t index = 0; - for (size_t i = 0; i < stencil_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = stencil_cell_list[i]; - const Rd Xi_Xj = m_xj[cell_i_id] - Xj; - for (size_t l = 0; l < m_basis_dimension - 1; ++l) { - A(index, l) = Xi_Xj[l]; - } - } - for (size_t i_symmetry = 0; i_symmetry < m_stencil_array.symmetryBoundaryStencilArrayList().size(); ++i_symmetry) { - auto& ghost_stencil = m_stencil_array.symmetryBoundaryStencilArrayList()[i_symmetry].stencilArray(); - auto ghost_cell_list = ghost_stencil[cell_j_id]; - - const Rd& origin = m_symmetry_origin_list[i_symmetry]; - const Rd& normal = m_symmetry_normal_list[i_symmetry]; - - for (size_t i = 0; i < ghost_cell_list.size(); ++i, ++index) { - const CellId cell_i_id = ghost_cell_list[i]; - const Rd Xi_Xj = symmetrize_coordinates(origin, normal, m_xj[cell_i_id]) - Xj; - for (size_t l = 0; l < m_basis_dimension - 1; ++l) { - A(index, l) = Xi_Xj[l]; - } - } - } - } + void build(const CellId cell_j_id, ShrinkMatrixView<SmallMatrix<double>>& A); CellCenterReconstructionMatrixBuilder(const MeshType& mesh, const size_t polynomial_degree, const SmallArray<const Rd>& symmetry_origin_list, const SmallArray<const Rd>& symmetry_normal_list, - const CellToCellStencilArray& stencil_array) - : m_basis_dimension{DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree( - polynomial_degree)}, - m_symmetry_origin_list{symmetry_origin_list}, - m_symmetry_normal_list{symmetry_normal_list}, - m_stencil_array{stencil_array}, - m_xj{MeshDataManager::instance().getMeshData(mesh).xj()} - { - if (polynomial_degree != 1) { - throw NormalError("cell center based reconstruction is only valid for first order"); - } - } + const CellToCellStencilArray& stencil_array); ~CellCenterReconstructionMatrixBuilder() = default; }; -- GitLab From 6b4eb1570457dc5a0427e861266b961c8afd73ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 15 May 2025 15:28:36 +0200 Subject: [PATCH 118/122] Remove reconstruction performance tests --- src/language/modules/SchemeModule.cpp | 9 ------ src/scheme/CMakeLists.txt | 2 -- src/scheme/test_reconstruction.cpp | 46 --------------------------- src/scheme/test_reconstruction.hpp | 8 ----- 4 files changed, 65 deletions(-) delete mode 100644 src/scheme/test_reconstruction.cpp delete mode 100644 src/scheme/test_reconstruction.hpp diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index f2570a3c3..b9b789cfa 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -65,9 +65,6 @@ #include <utils/checkpointing/WriteIQuadratureDescriptor.hpp> #include <utils/checkpointing/WriteVariableBCDescriptor.hpp> -#warning REMOVE -#include <scheme/test_reconstruction.hpp> - #include <memory> SchemeModule::SchemeModule() @@ -759,12 +756,6 @@ SchemeModule::SchemeModule() )); - this->_addBuiltinFunction("test_reconstruction", std::function{[](std::shared_ptr<const DiscreteFunctionVariant> df, - size_t degree, size_t number) -> void { - // - test_reconstruction(df, degree, number); - }}); - MathFunctionRegisterForVh{*this}; } diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt index 5a555c426..852a6cd75 100644 --- a/src/scheme/CMakeLists.txt +++ b/src/scheme/CMakeLists.txt @@ -14,8 +14,6 @@ add_library( HyperelasticSolver.cpp LoadBalancer.cpp PolynomialReconstruction.cpp - - test_reconstruction.cpp ) target_link_libraries( diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp deleted file mode 100644 index 1353ab461..000000000 --- a/src/scheme/test_reconstruction.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include <scheme/test_reconstruction.hpp> - -#include <scheme/PolynomialReconstruction.hpp> -#include <utils/Timer.hpp> - -void -test_reconstruction(std::shared_ptr<const DiscreteFunctionVariant> df, size_t degree, size_t number) -{ - if (degree == 1) { - PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::cell_center, degree}; - PolynomialReconstruction rec_builder{descriptor}; - - Timer t; - - for (size_t i = 0; i < number; ++i) { - auto rec = rec_builder.build(df); - } - - std::cout << "*** Elapsed time for " << number << " cell center reconstructions: " << t.seconds() << "s ***\n"; - } - - { - PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::boundary, degree}; - PolynomialReconstruction rec_builder{descriptor}; - - Timer t; - - for (size_t i = 0; i < number; ++i) { - auto rec = rec_builder.build(df); - } - - std::cout << "*** Elapsed time for " << number << " boundary reconstructions: " << t.seconds() << "s ***\n"; - } - { - PolynomialReconstructionDescriptor descriptor{IntegrationMethodType::element, degree}; - PolynomialReconstruction rec_builder{descriptor}; - - Timer t; - - for (size_t i = 0; i < number; ++i) { - auto rec = rec_builder.build(df); - } - - std::cout << "*** Elapsed time for " << number << " element reconstructions: " << t.seconds() << "s ***\n"; - } -} diff --git a/src/scheme/test_reconstruction.hpp b/src/scheme/test_reconstruction.hpp deleted file mode 100644 index 36ed481ec..000000000 --- a/src/scheme/test_reconstruction.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef TEST_RECONSTRUCTION_HPP -#define TEST_RECONSTRUCTION_HPP - -#include <scheme/DiscreteFunctionVariant.hpp> -#warning REMOVE -void test_reconstruction(std::shared_ptr<const DiscreteFunctionVariant> df, size_t degree, size_t number); - -#endif // TEST_RECONSTRUCTION_HPP -- GitLab From 16ccf08d6d72831cbf610da17e3bfd4b80054ec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Mon, 19 May 2025 14:37:14 +0200 Subject: [PATCH 119/122] Prepare boundary integration in 1d --- src/scheme/PolynomialReconstruction.cpp | 1 + ...aryIntegralReconstructionMatrixBuilder.cpp | 70 +++++++++++++++---- ...aryIntegralReconstructionMatrixBuilder.hpp | 12 ++-- ...entIntegralReconstructionMatrixBuilder.cpp | 2 + ...entIntegralReconstructionMatrixBuilder.hpp | 5 +- 5 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index 6073da9c8..e29e8b8da 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -828,6 +828,7 @@ PolynomialReconstruction::_build( return this->_build<BoundaryIntegralReconstructionMatrixBuilder<MeshType>>(p_mesh, discrete_function_variant_list); } + std::clog << "falling back to element integration in 3d\n"; [[fallthrough]]; } case IntegrationMethodType::element: { diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp index 2a481517f..0e3c01a7d 100644 --- a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp @@ -1,13 +1,53 @@ #include <scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp> #include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/QuadratureFormula.hpp> #include <analysis/QuadratureManager.hpp> +#include <geometry/LineTransformation.hpp> #include <geometry/SymmetryUtils.hpp> -#include <mesh/Connectivity.hpp> #include <mesh/Mesh.hpp> #include <mesh/MeshDataManager.hpp> #include <scheme/DiscreteFunctionDPk.hpp> +template <> +class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<2>>::ConnectivityData +{ + private: + const ItemToItemMatrix<ItemType::cell, ItemType::face> m_cell_to_face_matrix; + const ItemToItemMatrix<ItemType::face, ItemType::node> m_face_to_node_matrix; + const FaceValuePerCell<const bool> m_cell_face_is_reversed; + + public: + PUGS_INLINE + const auto& + cellToFaceMatrix() const noexcept + { + return m_cell_to_face_matrix; + } + + PUGS_INLINE + const auto& + faceToNodeMatrix() const noexcept + { + return m_face_to_node_matrix; + } + + PUGS_INLINE + const auto& + cellFaceIsReversed() const noexcept + { + return m_cell_face_is_reversed; + } + + ConnectivityData(const Connectivity<2>& connectivity) + : m_cell_to_face_matrix{connectivity.cellToFaceMatrix()}, + m_face_to_node_matrix{connectivity.faceToNodeMatrix()}, + m_cell_face_is_reversed{connectivity.cellFaceIsReversed()} + {} + + ~ConnectivityData() = default; +}; + template <MeshConcept MeshTypeT> template <typename ConformTransformationT> void @@ -63,14 +103,15 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT> const double inv_Vi = 1. / m_Vj[cell_id]; + const auto& face_is_reversed = m_dimensional_data->cellFaceIsReversed()[cell_id]; + const auto& cell_face_list = m_dimensional_data->cellToFaceMatrix()[cell_id]; + const auto& face_to_node_matrix = m_dimensional_data->faceToNodeMatrix(); + mean_of_ejk.fill(0); - auto cell_face_list = m_cell_to_face_matrix[cell_id]; for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { - bool is_reversed = m_cell_face_is_reversed[cell_id][i_face]; - const FaceId face_id = cell_face_list[i_face]; - auto face_node_list = m_face_to_node_matrix[face_id]; - if (is_reversed) { + auto face_node_list = face_to_node_matrix[face_id]; + if (face_is_reversed[i_face]) { const LineTransformation<2> T{m_xr[face_node_list[1]], m_xr[face_node_list[0]]}; _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); } else { @@ -94,18 +135,19 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< const double inv_Vi = 1. / m_Vj[cell_id]; + const auto& face_is_reversed = m_dimensional_data->cellFaceIsReversed()[cell_id]; + const auto& cell_face_list = m_dimensional_data->cellToFaceMatrix()[cell_id]; + const auto& face_to_node_matrix = m_dimensional_data->faceToNodeMatrix(); + mean_of_ejk.fill(0); - auto cell_face_list = m_cell_to_face_matrix[cell_id]; for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { - bool is_reversed = m_cell_face_is_reversed[cell_id][i_face]; - const FaceId face_id = cell_face_list[i_face]; - auto face_node_list = m_face_to_node_matrix[face_id]; + auto face_node_list = face_to_node_matrix[face_id]; const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); const auto x1 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); - if (is_reversed) { + if (face_is_reversed[i_face]) { const LineTransformation<2> T{x1, x0}; _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); } else { @@ -174,9 +216,9 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek{m_basis_dimension}, m_mean_j_of_ejk{m_basis_dimension - 1}, m_mean_i_of_ejk{m_basis_dimension - 1}, - m_cell_to_face_matrix{mesh.connectivity().cellToFaceMatrix()}, - m_face_to_node_matrix{mesh.connectivity().faceToNodeMatrix()}, - m_cell_face_is_reversed{mesh.connectivity().cellFaceIsReversed()}, + + m_dimensional_data{std::make_shared<ConnectivityData>(mesh.connectivity())}, + m_stencil_array{stencil_array}, m_symmetry_origin_list{symmetry_origin_list}, m_symmetry_normal_list{symmetry_normal_list}, diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp index df24d76a7..cb638fa2b 100644 --- a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp @@ -3,14 +3,14 @@ #include <algebra/ShrinkMatrixView.hpp> #include <algebra/SmallMatrix.hpp> -#include <analysis/QuadratureFormula.hpp> -#include <geometry/LineTransformation.hpp> #include <mesh/ItemValue.hpp> #include <mesh/StencilArray.hpp> -#include <mesh/SubItemValuePerItem.hpp> #include <scheme/PolynomialReconstruction.hpp> #include <utils/SmallArray.hpp> +template <size_t Dimension> +class QuadratureFormula; + template <MeshConcept MeshTypeT> class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder { @@ -30,9 +30,8 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder SmallArray<double> m_mean_j_of_ejk; SmallArray<double> m_mean_i_of_ejk; - const ItemToItemMatrix<ItemType::cell, ItemType::face> m_cell_to_face_matrix; - const ItemToItemMatrix<ItemType::face, ItemType::node> m_face_to_node_matrix; - const FaceValuePerCell<const bool> m_cell_face_is_reversed; + class ConnectivityData; + std::shared_ptr<ConnectivityData> m_dimensional_data; const CellToCellStencilArray& m_stencil_array; @@ -76,7 +75,6 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder const SmallArray<const Rd>& symmetry_normal_list, const CellToCellStencilArray& stencil_array); - BoundaryIntegralReconstructionMatrixBuilder() = default; ~BoundaryIntegralReconstructionMatrixBuilder() = default; }; diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp index faededf69..18306a8ec 100644 --- a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp @@ -2,6 +2,8 @@ #include <analysis/GaussLegendreQuadratureDescriptor.hpp> #include <analysis/GaussQuadratureDescriptor.hpp> +#include <analysis/QuadratureFormula.hpp> +#include <analysis/QuadratureManager.hpp> #include <geometry/CubeTransformation.hpp> #include <geometry/LineTransformation.hpp> #include <geometry/PrismTransformation.hpp> diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp index 1f4573438..7a3e8f7fc 100644 --- a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.hpp @@ -3,14 +3,15 @@ #include <algebra/ShrinkMatrixView.hpp> #include <algebra/SmallMatrix.hpp> -#include <analysis/QuadratureFormula.hpp> -#include <analysis/QuadratureManager.hpp> #include <mesh/CellType.hpp> #include <mesh/ItemValue.hpp> #include <mesh/StencilArray.hpp> #include <scheme/PolynomialReconstruction.hpp> #include <utils/SmallArray.hpp> +template <size_t Dimension> +class QuadratureFormula; + template <MeshConcept MeshTypeT> class PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder { -- GitLab From d51fff1fb6c9c44dbaebbf21b866e3a3ca80cfab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Tue, 20 May 2025 12:26:34 +0200 Subject: [PATCH 120/122] Implement BoundaryIntegralReconstructionMatrixBuilder in 1D --- src/scheme/PolynomialReconstruction.cpp | 2 +- ...aryIntegralReconstructionMatrixBuilder.cpp | 159 +++++++++++++++--- ...aryIntegralReconstructionMatrixBuilder.hpp | 8 +- ...test_PolynomialReconstruction_degree_2.cpp | 10 +- 4 files changed, 141 insertions(+), 38 deletions(-) diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index e29e8b8da..b9cfb57e1 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -824,7 +824,7 @@ PolynomialReconstruction::_build( return this->_build<CellCenterReconstructionMatrixBuilder<MeshType>>(p_mesh, discrete_function_variant_list); } case IntegrationMethodType::boundary: { - if constexpr (MeshType::Dimension == 2) { + if constexpr (MeshType::Dimension < 3) { return this->_build<BoundaryIntegralReconstructionMatrixBuilder<MeshType>>(p_mesh, discrete_function_variant_list); } diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp index 0e3c01a7d..5c7c5af3e 100644 --- a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp @@ -9,8 +9,8 @@ #include <mesh/MeshDataManager.hpp> #include <scheme/DiscreteFunctionDPk.hpp> -template <> -class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<2>>::ConnectivityData +template <MeshConcept MeshTypeT> +class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT>::SpecificDimensionalData { private: const ItemToItemMatrix<ItemType::cell, ItemType::face> m_cell_to_face_matrix; @@ -18,8 +18,7 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh const FaceValuePerCell<const bool> m_cell_face_is_reversed; public: - PUGS_INLINE - const auto& + PUGS_INLINE const auto& cellToFaceMatrix() const noexcept { return m_cell_to_face_matrix; @@ -39,13 +38,33 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh return m_cell_face_is_reversed; } - ConnectivityData(const Connectivity<2>& connectivity) + SpecificDimensionalData(const Connectivity<2>& connectivity) : m_cell_to_face_matrix{connectivity.cellToFaceMatrix()}, m_face_to_node_matrix{connectivity.faceToNodeMatrix()}, m_cell_face_is_reversed{connectivity.cellFaceIsReversed()} {} - ~ConnectivityData() = default; + ~SpecificDimensionalData() = default; +}; + +template <> +class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<1>>::SpecificDimensionalData +{ + private: + const ItemToItemMatrix<ItemType::cell, ItemType::node> m_cell_to_node_matrix; + + public: + PUGS_INLINE + const auto& + cellToNodeMatrix() const noexcept + { + return m_cell_to_node_matrix; + } + + SpecificDimensionalData(const Connectivity<1>& connectivity) : m_cell_to_node_matrix{connectivity.cellToNodeMatrix()} + {} + + ~SpecificDimensionalData() = default; }; template <MeshConcept MeshTypeT> @@ -70,23 +89,23 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT> const double y_yj = X_Xj[1]; { - size_t k = 0; - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1; + size_t k = 0; + m_tmp_array[k++] = x_xj * wq * velocity_perp_e1; for (; k <= m_polynomial_degree; ++k) { - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k] = - x_xj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * m_antiderivative_coef[k]; // ((1. * k) / (k + 1)); + m_tmp_array[k] // + = x_xj * m_tmp_array[k - 1] * m_antiderivative_correction_coef[k]; // ((1. * k) / (k + 1)); } for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { const size_t begin_i_y_1 = ((i_y - 1) * (2 * m_polynomial_degree - i_y + 4)) / 2; for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l) { - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = y_yj * m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[begin_i_y_1 + l]; + m_tmp_array[k++] = y_yj * m_tmp_array[begin_i_y_1 + l]; } } } for (size_t k = 1; k < m_basis_dimension; ++k) { - mean_of_ejk[k - 1] += m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k]; + mean_of_ejk[k - 1] += m_tmp_array[k]; } } } @@ -103,9 +122,9 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT> const double inv_Vi = 1. / m_Vj[cell_id]; - const auto& face_is_reversed = m_dimensional_data->cellFaceIsReversed()[cell_id]; - const auto& cell_face_list = m_dimensional_data->cellToFaceMatrix()[cell_id]; - const auto& face_to_node_matrix = m_dimensional_data->faceToNodeMatrix(); + const auto& face_is_reversed = m_dimensional_data_ptr->cellFaceIsReversed()[cell_id]; + const auto& cell_face_list = m_dimensional_data_ptr->cellToFaceMatrix()[cell_id]; + const auto& face_to_node_matrix = m_dimensional_data_ptr->faceToNodeMatrix(); mean_of_ejk.fill(0); for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { @@ -135,9 +154,9 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< const double inv_Vi = 1. / m_Vj[cell_id]; - const auto& face_is_reversed = m_dimensional_data->cellFaceIsReversed()[cell_id]; - const auto& cell_face_list = m_dimensional_data->cellToFaceMatrix()[cell_id]; - const auto& face_to_node_matrix = m_dimensional_data->faceToNodeMatrix(); + const auto& face_is_reversed = m_dimensional_data_ptr->cellFaceIsReversed()[cell_id]; + const auto& cell_face_list = m_dimensional_data_ptr->cellToFaceMatrix()[cell_id]; + const auto& face_to_node_matrix = m_dimensional_data_ptr->faceToNodeMatrix(); mean_of_ejk.fill(0); for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { @@ -157,13 +176,87 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< } } +template <> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<1>>::_computeEjkMeanByBoundary( + const Rd& Xj, + const CellId& cell_id, + SmallArray<double>& mean_of_ejk) +{ + const double inv_Vi = 1. / m_Vj[cell_id]; + + const auto& cell_node_list = m_dimensional_data_ptr->cellToNodeMatrix()[cell_id]; + + const double xr1_xj = (m_xr[cell_node_list[1]] - Xj)[0]; + + m_tmp_array[0] = xr1_xj * inv_Vi; + for (size_t k = 1; k <= m_polynomial_degree; ++k) { + m_tmp_array[k] // + = xr1_xj * m_tmp_array[k - 1] * m_antiderivative_correction_coef[k]; // ((1. * k) / (k + 1)); + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] = m_tmp_array[k]; + } + + const double xr0_xj = (m_xr[cell_node_list[0]] - Xj)[0]; + + m_tmp_array[0] = xr0_xj * inv_Vi; + for (size_t k = 1; k <= m_polynomial_degree; ++k) { + m_tmp_array[k] // + = xr0_xj * m_tmp_array[k - 1] * m_antiderivative_correction_coef[k]; // ((1. * k) / (k + 1)); + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] -= m_tmp_array[k]; + } +} + +template <> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + Mesh<1>>::_computeEjkMeanByBoundaryInSymmetricCell(const Rd& origin, + const Rd& normal, + const Rd& Xj, + const CellId& cell_id, + SmallArray<double>& mean_of_ejk) +{ + const double inv_Vi = 1. / m_Vj[cell_id]; + + const auto& cell_node_list = m_dimensional_data_ptr->cellToNodeMatrix()[cell_id]; + + const double xr1_xj = (symmetrize_coordinates(origin, normal, m_xr[cell_node_list[0]]) - Xj)[0]; + + m_tmp_array[0] = xr1_xj * inv_Vi; + for (size_t k = 1; k <= m_polynomial_degree; ++k) { + m_tmp_array[k] // + = xr1_xj * m_tmp_array[k - 1] * m_antiderivative_correction_coef[k]; // ((1. * k) / (k + 1)); + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] = m_tmp_array[k]; + } + + const double xr0_xj = (symmetrize_coordinates(origin, normal, m_xr[cell_node_list[1]]) - Xj)[0]; + + m_tmp_array[0] = xr0_xj * inv_Vi; + for (size_t k = 1; k <= m_polynomial_degree; ++k) { + m_tmp_array[k] // + = xr0_xj * m_tmp_array[k - 1] * m_antiderivative_correction_coef[k]; // ((1. * k) / (k + 1)); + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] -= m_tmp_array[k]; + } +} + template <MeshConcept MeshTypeT> void PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT>::build( const CellId cell_j_id, ShrinkMatrixView<SmallMatrix<double>>& A) { - if constexpr (MeshType::Dimension == 2) { + if constexpr (MeshType::Dimension < 3) { const auto& stencil_cell_list = m_stencil_array[cell_j_id]; const Rd& Xj = m_xj[cell_j_id]; @@ -213,11 +306,11 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< m_basis_dimension{ DiscreteFunctionDPk<MeshType::Dimension, double>::BasisViewType::dimensionFromDegree(polynomial_degree)}, m_polynomial_degree{polynomial_degree}, - m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek{m_basis_dimension}, + m_tmp_array{m_basis_dimension}, m_mean_j_of_ejk{m_basis_dimension - 1}, m_mean_i_of_ejk{m_basis_dimension - 1}, - m_dimensional_data{std::make_shared<ConnectivityData>(mesh.connectivity())}, + m_dimensional_data_ptr{std::make_shared<SpecificDimensionalData>(mesh.connectivity())}, m_stencil_array{stencil_array}, m_symmetry_origin_list{symmetry_origin_list}, @@ -226,20 +319,30 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< m_xj{MeshDataManager::instance().getMeshData(mesh).xj()}, m_xr{mesh.xr()} { - if constexpr (MeshType::Dimension == 2) { - SmallArray<double> antiderivative_coef(m_polynomial_degree + 1); - for (size_t k = 0; k < antiderivative_coef.size(); ++k) { - antiderivative_coef[k] = ((1. * k) / (k + 1)); - } - - m_antiderivative_coef = antiderivative_coef; + SmallArray<double> antiderivative_correction_coef(m_polynomial_degree + 1); + for (size_t k = 0; k < antiderivative_correction_coef.size(); ++k) { + // The antiderivative of x^k is k/(k+1) times the antiderivative of x^(k-1) + antiderivative_correction_coef[k] = ((1. * k) / (k + 1)); } + + m_antiderivative_correction_coef = antiderivative_correction_coef; } +template void PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<1>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + template void PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<2>>::build( const CellId, ShrinkMatrixView<SmallMatrix<double>>&); +template PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + Mesh<1>>::BoundaryIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); + template PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< Mesh<2>>::BoundaryIntegralReconstructionMatrixBuilder(const MeshType&, const size_t, diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp index cb638fa2b..824aa9027 100644 --- a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp @@ -26,12 +26,12 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder const size_t m_basis_dimension; const size_t m_polynomial_degree; - const SmallArray<double> m_inv_Vj_alpha_p_1_wq_X_prime_orth_ek; + const SmallArray<double> m_tmp_array; SmallArray<double> m_mean_j_of_ejk; SmallArray<double> m_mean_i_of_ejk; - class ConnectivityData; - std::shared_ptr<ConnectivityData> m_dimensional_data; + class SpecificDimensionalData; + std::shared_ptr<SpecificDimensionalData> m_dimensional_data_ptr; const CellToCellStencilArray& m_stencil_array; @@ -42,7 +42,7 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder const CellValue<const Rd> m_xj; const NodeValue<const Rd> m_xr; - SmallArray<const double> m_antiderivative_coef; + SmallArray<const double> m_antiderivative_correction_coef; template <typename ConformTransformationT> void _computeEjkBoundaryMean(const QuadratureFormula<MeshType::Dimension - 1>& quadrature, diff --git a/tests/test_PolynomialReconstruction_degree_2.cpp b/tests/test_PolynomialReconstruction_degree_2.cpp index a1c5c0589..db13612c1 100644 --- a/tests/test_PolynomialReconstruction_degree_2.cpp +++ b/tests/test_PolynomialReconstruction_degree_2.cpp @@ -118,7 +118,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>(); double max_error = test_only::max_reconstruction_error(mesh, dpk_Ah, std::function(R3x3_exact)); - REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -139,7 +139,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const double>>(); double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -172,7 +172,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<1, const R3>>(); double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } @@ -219,7 +219,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") { auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>(); double max_error = test_only::max_reconstruction_error(mesh, dpk_uh, std::function(R3_exact)); - REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } { @@ -231,7 +231,7 @@ TEST_CASE("PolynomialReconstruction_degree_2", "[scheme]") { auto dpk_Vh = reconstructions[3]->get<DiscreteFunctionDPkVector<1, const double>>(); double max_error = test_only::max_reconstruction_error(mesh, dpk_Vh, vector_exact); - REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-14)); + REQUIRE(parallel::allReduceMax(max_error) == Catch::Approx(0).margin(1E-12)); } } } -- GitLab From eeb0e063185f50a87f681b3a71f07576e495a9d6 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Thu, 22 May 2025 00:42:20 +0200 Subject: [PATCH 121/122] Fix error/signal handling raised in a multi-threaded context --- src/utils/SignalManager.cpp | 104 +++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/src/utils/SignalManager.cpp b/src/utils/SignalManager.cpp index 4b2046a98..5054622c0 100644 --- a/src/utils/SignalManager.cpp +++ b/src/utils/SignalManager.cpp @@ -52,17 +52,27 @@ SignalManager::signalName(int signal) void SignalManager::pauseForDebug(int signal) { - if (std::string(PUGS_BUILD_TYPE) != "Release") { - if (s_pause_on_error) { - // Each failing process must write - std::cerr.clear(); + if (s_pause_on_error) { + // Each failing process must write + std::cerr.clear(); + if (std::string(PUGS_BUILD_TYPE) != "Release") { + char hostname[HOST_NAME_MAX + 1]; + gethostname(hostname, HOST_NAME_MAX + 1); std::cerr << "\n======================================\n" - << rang::style::reset << rang::fg::reset << rang::style::bold << "to attach gdb to this process run\n" + << rang::style::reset << rang::fg::reset << rang::style::bold << "Process paused on host \"" + << rang::fg::yellow << hostname << rang::fg::reset << "\"\n" + << "to attach gdb to this process run\n" << "\tgdb -pid " << rang::fg::red << getpid() << rang::fg::reset << '\n' << "else press Control-C to exit\n" << rang::style::reset << "======================================\n" << std::flush; pause(); + } else { + std::cerr << '\n' + << rang::style::bold + << "Pausing is useless for Release version.\n" + "To attach debugger use Debug built type." + << rang::style::reset << '\n'; } } std::exit(signal); @@ -73,56 +83,52 @@ SignalManager::handler(int signal) { static std::mutex mutex; - if (mutex.try_lock()) { - std::signal(SIGTERM, SIG_DFL); - std::signal(SIGINT, SIG_DFL); - std::signal(SIGABRT, SIG_DFL); - - // Each failing process must write - std::cerr.clear(); + std::lock_guard<std::mutex> lock(mutex); - std::cerr << BacktraceManager{} << '\n'; + std::signal(SIGINT, SIG_BLOCK); - std::cerr << "\n *** " << rang::style::reset << rang::fg::reset << rang::style::bold << "Signal " << rang::fgB::red - << signalName(signal) << rang::fg::reset << " caught" << rang::style::reset << " ***\n\n"; + // Each failing process must write + std::cerr.clear(); - std::exception_ptr eptr = std::current_exception(); - try { - if (eptr) { - std::rethrow_exception(eptr); - } else { - std::ostringstream error_msg; - error_msg << "received " << signalName(signal); - std::cerr << ASTExecutionStack::getInstance().errorMessageAt(error_msg.str()) << '\n'; - } - } - catch (const IBacktraceError& backtrace_error) { - auto source_location = backtrace_error.sourceLocation(); - std::cerr << rang::fgB::cyan << source_location.file_name() << ':' << source_location.line() << ':' - << source_location.column() << ':' << rang::fg::reset << rang::fgB::yellow - << " threw the following exception" << rang::fg::reset << "\n\n"; - std::cerr << ASTExecutionStack::getInstance().errorMessageAt(backtrace_error.what()) << '\n'; - } - catch (const ParseError& parse_error) { - auto p = parse_error.positions().front(); - std::cerr << rang::style::bold << p.source << ':' << p.line << ':' << p.column << ':' << rang::style::reset - << rang::fgB::red << " error: " << rang::fg::reset << rang::style::bold << parse_error.what() - << rang::style::reset << '\n'; - } - catch (const IExitError& exit_error) { - std::cerr << ASTExecutionStack::getInstance().errorMessageAt(exit_error.what()) << '\n'; - } - catch (const AssertError& assert_error) { - std::cerr << assert_error << '\n'; - } - catch (...) { - std::cerr << "Unknown exception!\n"; - } + std::cerr << BacktraceManager{} << '\n'; - SignalManager::pauseForDebug(signal); + std::cerr << "\n *** " << rang::style::reset << rang::fg::reset << rang::style::bold << "Signal " << rang::fgB::red + << signalName(signal) << rang::fg::reset << " caught" << rang::style::reset << " ***\n\n"; - mutex.unlock(); + std::exception_ptr eptr = std::current_exception(); + try { + if (eptr) { + std::rethrow_exception(eptr); + } else { + std::ostringstream error_msg; + error_msg << "received " << signalName(signal); + std::cerr << ASTExecutionStack::getInstance().errorMessageAt(error_msg.str()) << '\n'; + } + } + catch (const IBacktraceError& backtrace_error) { + auto source_location = backtrace_error.sourceLocation(); + std::cerr << rang::fgB::cyan << source_location.file_name() << ':' << source_location.line() << ':' + << source_location.column() << ':' << rang::fg::reset << rang::fgB::yellow + << " threw the following exception" << rang::fg::reset << "\n\n"; + std::cerr << ASTExecutionStack::getInstance().errorMessageAt(backtrace_error.what()) << '\n'; } + catch (const ParseError& parse_error) { + auto p = parse_error.positions().front(); + std::cerr << rang::style::bold << p.source << ':' << p.line << ':' << p.column << ':' << rang::style::reset + << rang::fgB::red << " error: " << rang::fg::reset << rang::style::bold << parse_error.what() + << rang::style::reset << '\n'; + } + catch (const IExitError& exit_error) { + std::cerr << ASTExecutionStack::getInstance().errorMessageAt(exit_error.what()) << '\n'; + } + catch (const AssertError& assert_error) { + std::cerr << assert_error << '\n'; + } + catch (...) { + std::cerr << "Unknown exception!\n"; + } + + SignalManager::pauseForDebug(signal); } void -- GitLab From 3f8a4fe194396cabdf5046ee1b345fb8c0113da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 22 May 2025 16:09:44 +0200 Subject: [PATCH 122/122] Add boundary integral reconstruction in 3D --- src/geometry/SquareTransformation.hpp | 19 ++ src/geometry/TriangleTransformation.hpp | 19 +- src/scheme/PolynomialReconstruction.cpp | 7 +- ...aryIntegralReconstructionMatrixBuilder.cpp | 268 +++++++++++++++--- ...aryIntegralReconstructionMatrixBuilder.hpp | 8 + ...entIntegralReconstructionMatrixBuilder.cpp | 8 +- 6 files changed, 282 insertions(+), 47 deletions(-) diff --git a/src/geometry/SquareTransformation.hpp b/src/geometry/SquareTransformation.hpp index d6474f33e..b0e970c6e 100644 --- a/src/geometry/SquareTransformation.hpp +++ b/src/geometry/SquareTransformation.hpp @@ -84,6 +84,25 @@ class SquareTransformation return m_coefficients * X + m_shift; } + PUGS_INLINE + TinyVector<Dimension> + areaVariation(const TinyVector<2>& X) const + { + const auto& T = m_coefficients; + const double& x = X[0]; + const double& y = X[1]; + + const TinyVector<Dimension> dxT{T(0, 0) + T(0, 2) * y, // + T(1, 0) + T(1, 2) * y, // + T(2, 0) + T(2, 2) * y}; + + const TinyVector<Dimension> dyT{T(0, 1) + T(0, 2) * x, // + T(1, 1) + T(1, 2) * x, // + T(2, 1) + T(2, 2) * x}; + + return crossProduct(dxT, dyT); + } + PUGS_INLINE double areaVariationNorm(const TinyVector<2>& X) const { diff --git a/src/geometry/TriangleTransformation.hpp b/src/geometry/TriangleTransformation.hpp index df8444d89..c49db3686 100644 --- a/src/geometry/TriangleTransformation.hpp +++ b/src/geometry/TriangleTransformation.hpp @@ -66,6 +66,7 @@ class TriangleTransformation private: TinyMatrix<Dimension, 2> m_jacobian; TinyVector<Dimension> m_shift; + TinyVector<Dimension> m_area_variation; double m_area_variation_norm; public: @@ -76,6 +77,21 @@ class TriangleTransformation return m_jacobian * x + m_shift; } + PUGS_INLINE + TinyVector<Dimension> + areaVariation() const + { + return m_area_variation; + } + + PUGS_INLINE + TinyVector<Dimension> + areaVariation(const TinyVector<2>&) const + { + return m_area_variation; + } + + PUGS_INLINE double areaVariationNorm() const { @@ -92,7 +108,8 @@ class TriangleTransformation m_shift = a; - m_area_variation_norm = l2Norm(crossProduct(b - a, c - a)); + m_area_variation = crossProduct(b - a, c - a); + m_area_variation_norm = l2Norm(m_area_variation); } ~TriangleTransformation() = default; diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp index b9cfb57e1..f3d3768d9 100644 --- a/src/scheme/PolynomialReconstruction.cpp +++ b/src/scheme/PolynomialReconstruction.cpp @@ -824,12 +824,7 @@ PolynomialReconstruction::_build( return this->_build<CellCenterReconstructionMatrixBuilder<MeshType>>(p_mesh, discrete_function_variant_list); } case IntegrationMethodType::boundary: { - if constexpr (MeshType::Dimension < 3) { - return this->_build<BoundaryIntegralReconstructionMatrixBuilder<MeshType>>(p_mesh, - discrete_function_variant_list); - } - std::clog << "falling back to element integration in 3d\n"; - [[fallthrough]]; + return this->_build<BoundaryIntegralReconstructionMatrixBuilder<MeshType>>(p_mesh, discrete_function_variant_list); } case IntegrationMethodType::element: { return this->_build<ElementIntegralReconstructionMatrixBuilder<MeshType>>(p_mesh, discrete_function_variant_list); diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp index 5c7c5af3e..58b8fcd5e 100644 --- a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.cpp @@ -1,10 +1,13 @@ #include <scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp> #include <analysis/GaussLegendreQuadratureDescriptor.hpp> +#include <analysis/GaussQuadratureDescriptor.hpp> #include <analysis/QuadratureFormula.hpp> #include <analysis/QuadratureManager.hpp> #include <geometry/LineTransformation.hpp> +#include <geometry/SquareTransformation.hpp> #include <geometry/SymmetryUtils.hpp> +#include <geometry/TriangleTransformation.hpp> #include <mesh/Mesh.hpp> #include <mesh/MeshDataManager.hpp> #include <scheme/DiscreteFunctionDPk.hpp> @@ -38,7 +41,7 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh return m_cell_face_is_reversed; } - SpecificDimensionalData(const Connectivity<2>& connectivity) + SpecificDimensionalData(const Connectivity<MeshType::Dimension>& connectivity) : m_cell_to_face_matrix{connectivity.cellToFaceMatrix()}, m_face_to_node_matrix{connectivity.faceToNodeMatrix()}, m_cell_face_is_reversed{connectivity.cellFaceIsReversed()} @@ -67,10 +70,68 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh ~SpecificDimensionalData() = default; }; -template <MeshConcept MeshTypeT> +template <> template <typename ConformTransformationT> void -PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT>::_computeEjkBoundaryMean( +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<3>>::_computeEjkBoundaryMean( + const QuadratureFormula<MeshType::Dimension - 1>& quadrature, + const ConformTransformationT& T, + const Rd& Xj, + const double inv_Vi, + SmallArray<double>& mean_of_ejk) +{ + for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) { + const double wq = quadrature.weight(i_q); + const TinyVector<2> xi_q = quadrature.point(i_q); + + const double area_variation_e1 = T.areaVariation(xi_q)[0] * inv_Vi; + + const Rd X_Xj = T(xi_q) - Xj; + + const double x_xj = X_Xj[0]; + const double y_yj = X_Xj[1]; + const double z_zj = X_Xj[2]; + + { + size_t k = 0; + m_tmp_array[k++] = x_xj * wq * area_variation_e1; + for (; k <= m_polynomial_degree; ++k) { + m_tmp_array[k] // + = x_xj * m_tmp_array[k - 1] * m_antiderivative_correction_coef[k]; // ((1. * k) / (k + 1)); + } + + for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { + const size_t begin_i_y_1 = m_yz_row_index[i_y - 1]; + const size_t nb_monoms = m_yz_row_size[i_y]; + for (size_t l = 0; l < nb_monoms; ++l, ++k) { + m_tmp_array[k] = y_yj * m_tmp_array[begin_i_y_1 + l]; + } + } + + for (size_t i_z = 1; i_z <= m_polynomial_degree; ++i_z) { + const size_t nb_y = m_yz_row_size[m_z_triangle_index[i_z]]; + const size_t index_z = m_z_triangle_index[i_z]; + const size_t index_z_1 = m_z_triangle_index[i_z - 1]; + for (size_t i_y = 0; i_y < nb_y; ++i_y) { + const size_t begin_i_yz_1 = m_yz_row_index[index_z_1 + i_y]; + const size_t nb_monoms = m_yz_row_size[index_z + i_y]; + for (size_t l = 0; l < nb_monoms; ++l, ++k) { + m_tmp_array[k] = z_zj * m_tmp_array[begin_i_yz_1 + l]; + } + } + } + } + + for (size_t k = 1; k < m_basis_dimension; ++k) { + mean_of_ejk[k - 1] += m_tmp_array[k]; + } + } +} + +template <> +template <typename ConformTransformationT> +void +PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<2>>::_computeEjkBoundaryMean( const QuadratureFormula<MeshType::Dimension - 1>& quadrature, const ConformTransformationT& T, const Rd& Xj, @@ -97,7 +158,7 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT> } for (size_t i_y = 1; i_y <= m_polynomial_degree; ++i_y) { - const size_t begin_i_y_1 = ((i_y - 1) * (2 * m_polynomial_degree - i_y + 4)) / 2; + const size_t begin_i_y_1 = m_y_row_index[i_y - 1]; for (size_t l = 0; l <= m_polynomial_degree - i_y; ++l) { m_tmp_array[k++] = y_yj * m_tmp_array[begin_i_y_1 + l]; } @@ -117,9 +178,6 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT> const CellId& cell_id, SmallArray<double>& mean_of_ejk) { - const auto& quadrature = - QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); - const double inv_Vi = 1. / m_Vj[cell_id]; const auto& face_is_reversed = m_dimensional_data_ptr->cellFaceIsReversed()[cell_id]; @@ -127,15 +185,60 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT> const auto& face_to_node_matrix = m_dimensional_data_ptr->faceToNodeMatrix(); mean_of_ejk.fill(0); - for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { - const FaceId face_id = cell_face_list[i_face]; - auto face_node_list = face_to_node_matrix[face_id]; - if (face_is_reversed[i_face]) { - const LineTransformation<2> T{m_xr[face_node_list[1]], m_xr[face_node_list[0]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); - } else { - const LineTransformation<2> T{m_xr[face_node_list[0]], m_xr[face_node_list[1]]}; - _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + + if constexpr (MeshType::Dimension == 2) { + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = face_to_node_matrix[face_id]; + if (face_is_reversed[i_face]) { + const LineTransformation<2> T{m_xr[face_node_list[1]], m_xr[face_node_list[0]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const LineTransformation<2> T{m_xr[face_node_list[0]], m_xr[face_node_list[1]]}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + } + } else { + static_assert(MeshType::Dimension == 3); + + const auto& triangle_quadrature = + QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(m_polynomial_degree + 1)); + const auto& square_quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = face_to_node_matrix[face_id]; + switch (face_node_list.size()) { + case 3: { + if (face_is_reversed[i_face]) { + const TriangleTransformation<3> T{m_xr[face_node_list[2]], m_xr[face_node_list[1]], m_xr[face_node_list[0]]}; + _computeEjkBoundaryMean(triangle_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const TriangleTransformation<3> T{m_xr[face_node_list[0]], m_xr[face_node_list[1]], m_xr[face_node_list[2]]}; + _computeEjkBoundaryMean(triangle_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + break; + } + case 4: { + if (face_is_reversed[i_face]) { + const SquareTransformation<3> T{m_xr[face_node_list[3]], m_xr[face_node_list[2]], m_xr[face_node_list[1]], + m_xr[face_node_list[0]]}; + _computeEjkBoundaryMean(square_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const SquareTransformation<3> T{m_xr[face_node_list[0]], m_xr[face_node_list[1]], m_xr[face_node_list[2]], + m_xr[face_node_list[3]]}; + _computeEjkBoundaryMean(square_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + break; + } + default: { + throw NotImplementedError("invalid face type"); + } + } } } } @@ -149,9 +252,6 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< const CellId& cell_id, SmallArray<double>& mean_of_ejk) { - const auto& quadrature = - QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); - const double inv_Vi = 1. / m_Vj[cell_id]; const auto& face_is_reversed = m_dimensional_data_ptr->cellFaceIsReversed()[cell_id]; @@ -159,19 +259,70 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< const auto& face_to_node_matrix = m_dimensional_data_ptr->faceToNodeMatrix(); mean_of_ejk.fill(0); - for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { - const FaceId face_id = cell_face_list[i_face]; - auto face_node_list = face_to_node_matrix[face_id]; - - const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); - const auto x1 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); - - if (face_is_reversed[i_face]) { - const LineTransformation<2> T{x1, x0}; - _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); - } else { - const LineTransformation<2> T{x0, x1}; - _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + if constexpr (MeshType::Dimension == 2) { + const auto& quadrature = + QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = face_to_node_matrix[face_id]; + + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); + + if (face_is_reversed[i_face]) { + const LineTransformation<2> T{x1, x0}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const LineTransformation<2> T{x0, x1}; + _computeEjkBoundaryMean(quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + } + } else { + static_assert(MeshType::Dimension == 3); + + const auto& triangle_quadrature = + QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(m_polynomial_degree + 1)); + const auto& square_quadrature = + QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(m_polynomial_degree + 1)); + + for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) { + const FaceId face_id = cell_face_list[i_face]; + auto face_node_list = face_to_node_matrix[face_id]; + switch (face_node_list.size()) { + case 3: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[2]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); + + if (face_is_reversed[i_face]) { + const TriangleTransformation<3> T{x2, x1, x0}; + _computeEjkBoundaryMean(triangle_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const TriangleTransformation<3> T{x0, x1, x2}; + _computeEjkBoundaryMean(triangle_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + break; + } + case 4: { + const auto x0 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[3]]); + const auto x1 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[2]]); + const auto x2 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[1]]); + const auto x3 = symmetrize_coordinates(origin, normal, m_xr[face_node_list[0]]); + + if (face_is_reversed[i_face]) { + const SquareTransformation<3> T{x3, x2, x1, x0}; + _computeEjkBoundaryMean(square_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } else { + const SquareTransformation<3> T{x0, x1, x2, x3}; + _computeEjkBoundaryMean(square_quadrature, T, Xj, inv_Vi, mean_of_ejk); + } + break; + } + default: { + throw NotImplementedError("invalid face type"); + } + } } } } @@ -256,7 +407,7 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<MeshTypeT> const CellId cell_j_id, ShrinkMatrixView<SmallMatrix<double>>& A) { - if constexpr (MeshType::Dimension < 3) { + if constexpr (MeshType::Dimension <= 3) { const auto& stencil_cell_list = m_stencil_array[cell_j_id]; const Rd& Xj = m_xj[cell_j_id]; @@ -326,6 +477,46 @@ PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< } m_antiderivative_correction_coef = antiderivative_correction_coef; + + if constexpr (MeshType::Dimension == 2) { + SmallArray<size_t> y_row_index(m_polynomial_degree + 1); + + size_t i_y = 0; + + y_row_index[i_y++] = 0; + for (ssize_t n = m_polynomial_degree + 1; n > 1; --n, ++i_y) { + y_row_index[i_y] = y_row_index[i_y - 1] + n; + } + + m_y_row_index = y_row_index; + + } else if constexpr (MeshType::Dimension == 3) { + SmallArray<size_t> yz_row_index((m_polynomial_degree + 2) * (m_polynomial_degree + 1) / 2 + 1); + SmallArray<size_t> z_triangle_index(m_polynomial_degree + 1); + + { + size_t i_z = 0; + size_t i_yz = 0; + + yz_row_index[i_yz++] = 0; + for (ssize_t n = m_polynomial_degree + 1; n >= 1; --n) { + z_triangle_index[i_z++] = i_yz - 1; + for (ssize_t i = n; i >= 1; --i) { + yz_row_index[i_yz] = yz_row_index[i_yz - 1] + i; + ++i_yz; + } + } + } + + SmallArray<size_t> yz_row_size{yz_row_index.size() - 1}; + for (size_t i = 0; i < yz_row_size.size(); ++i) { + yz_row_size[i] = yz_row_index[i + 1] - yz_row_index[i]; + } + + m_yz_row_index = yz_row_index; + m_z_triangle_index = z_triangle_index; + m_yz_row_size = yz_row_size; + } } template void PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<1>>::build( @@ -336,6 +527,10 @@ template void PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuil const CellId, ShrinkMatrixView<SmallMatrix<double>>&); +template void PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder<Mesh<3>>::build( + const CellId, + ShrinkMatrixView<SmallMatrix<double>>&); + template PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< Mesh<1>>::BoundaryIntegralReconstructionMatrixBuilder(const MeshType&, const size_t, @@ -349,3 +544,10 @@ template PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< const SmallArray<const Rd>&, const SmallArray<const Rd>&, const CellToCellStencilArray&); + +template PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder< + Mesh<3>>::BoundaryIntegralReconstructionMatrixBuilder(const MeshType&, + const size_t, + const SmallArray<const Rd>&, + const SmallArray<const Rd>&, + const CellToCellStencilArray&); diff --git a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp index 824aa9027..60707189b 100644 --- a/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp +++ b/src/scheme/reconstruction_utils/BoundaryIntegralReconstructionMatrixBuilder.hpp @@ -42,6 +42,14 @@ class PolynomialReconstruction::BoundaryIntegralReconstructionMatrixBuilder const CellValue<const Rd> m_xj; const NodeValue<const Rd> m_xr; + // 2D + SmallArray<const size_t> m_y_row_index; + + // 3D + SmallArray<const size_t> m_yz_row_index; + SmallArray<const size_t> m_z_triangle_index; + SmallArray<const size_t> m_yz_row_size; + SmallArray<const double> m_antiderivative_correction_coef; template <typename ConformTransformationT> diff --git a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp index 18306a8ec..09e410811 100644 --- a/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp +++ b/src/scheme/reconstruction_utils/ElementIntegralReconstructionMatrixBuilder.cpp @@ -77,13 +77,7 @@ PolynomialReconstruction::ElementIntegralReconstructionMatrixBuilder<MeshTypeT>: } else if constexpr (MeshType::Dimension == 3) { static_assert(MeshType::Dimension == 3); - const double detT = [&] { - if constexpr (std::is_same_v<TetrahedronTransformation, std::decay_t<decltype(T)>>) { - return T.jacobianDeterminant(); - } else { - return T.jacobianDeterminant(xi_q); - } - }(); + const double detT = T.jacobianDeterminant(xi_q); const double x_xj = X_Xj[0]; const double y_yj = X_Xj[1]; -- GitLab