diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt index 78b72bcafc640ecc888c0997eeb3ff8f3056db58..d03051cae50a4f2c39f95fbd9f9fb8e8cb06cdf9 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 0000000000000000000000000000000000000000..d67aa09ed03252b5085c9238d4e968c5947c2678 --- /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 0000000000000000000000000000000000000000..a43dbef60cd76a280b8b3ab679c2d0c4e5b5406f --- /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 0000000000000000000000000000000000000000..3a42350795f0caf8e749bb6235874ea3fdb65eed --- /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 e7c75245c6c40f9f23b61d000d379c0211a62dda..68da0dc88f3ab393e8e7623bc4580fff21161b6e 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 0000000000000000000000000000000000000000..af6031ee499253c283683e7e15d7e779327def17 --- /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))); + } + } +}