From 3f96ea84602615e8aed28b3b8ae5531279cbdc59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Fri, 18 Feb 2022 16:07:12 +0100 Subject: [PATCH] Add synchronize, isSynchronized, min, max and sum utilities for SubItemArrayPerItem --- src/mesh/SubItemArrayPerItem.hpp | 2 +- src/mesh/SubItemArrayPerItemUtils.hpp | 359 ++++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/test_SubItemArrayPerItemUtils.cpp | 430 ++++++++++++++++++++++++ 4 files changed, 791 insertions(+), 1 deletion(-) create mode 100644 src/mesh/SubItemArrayPerItemUtils.hpp create mode 100644 tests/test_SubItemArrayPerItemUtils.cpp diff --git a/src/mesh/SubItemArrayPerItem.hpp b/src/mesh/SubItemArrayPerItem.hpp index f2103789e..7b204d219 100644 --- a/src/mesh/SubItemArrayPerItem.hpp +++ b/src/mesh/SubItemArrayPerItem.hpp @@ -50,7 +50,7 @@ class SubItemArrayPerItem public: friend PUGS_INLINE SubItemArrayPerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityPtr> - copy(SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& source) + copy(const SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& source) { SubItemArrayPerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityPtr> image; diff --git a/src/mesh/SubItemArrayPerItemUtils.hpp b/src/mesh/SubItemArrayPerItemUtils.hpp new file mode 100644 index 000000000..5e4f122e3 --- /dev/null +++ b/src/mesh/SubItemArrayPerItemUtils.hpp @@ -0,0 +1,359 @@ +#ifndef SUB_ITEM_ARRAY_PER_ITEM_UTILS_HPP +#define SUB_ITEM_ARRAY_PER_ITEM_UTILS_HPP + +#include <utils/Messenger.hpp> + +#include <mesh/Connectivity.hpp> +#include <mesh/SubItemArrayPerItem.hpp> +#include <mesh/Synchronizer.hpp> +#include <mesh/SynchronizerManager.hpp> +#include <utils/PugsTraits.hpp> + +#include <iostream> + +template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> +std::remove_const_t<DataType> +min(const SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_array_per_item) +{ + using SubItemArrayPerItemType = SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>; + constexpr ItemType item_type = ItemOfItem::item_type; + using ItemIsOwnedType = ItemValue<const bool, item_type>; + using data_type = std::remove_const_t<typename SubItemArrayPerItemType::data_type>; + using index_type = typename SubItemArrayPerItemType::index_type; + + static_assert(std::is_arithmetic_v<data_type>, "min cannot be called on non-arithmetic data"); + static_assert(not std::is_same_v<data_type, bool>, "min cannot be called on boolean data"); + + class SubItemArrayPerItemMin + { + private: + const SubItemArrayPerItemType& m_sub_item_array_per_item; + const ItemIsOwnedType m_is_owned; + + public: + PUGS_INLINE + operator data_type() + { + data_type reduced_array; + parallel_reduce(m_sub_item_array_per_item.numberOfItems(), *this, reduced_array); + return reduced_array; + } + + PUGS_INLINE + void + operator()(const index_type& item_id, data_type& data) const + { + if (m_is_owned[item_id]) { + Table sub_item_table = m_sub_item_array_per_item.itemTable(item_id); + for (size_t i = 0; i < sub_item_table.numberOfRows(); ++i) { + for (size_t j = 0; j < sub_item_table.numberOfColumns(); ++j) { + if (sub_item_table(i, j) < data) { + data = sub_item_table(i, j); + } + } + } + } + } + + PUGS_INLINE + void + join(volatile data_type& dst, const volatile data_type& src) const + { + if (src < dst) { + // cannot be reached if initial array is the min + dst = src; // LCOV_EXCL_LINE + } + } + + PUGS_INLINE + void + init(data_type& array) const + { + array = std::numeric_limits<data_type>::max(); + } + + PUGS_INLINE + SubItemArrayPerItemMin(const SubItemArrayPerItemType& item_array) + : m_sub_item_array_per_item(item_array), m_is_owned([&](const IConnectivity& connectivity) { + Assert((connectivity.dimension() > 0) and (connectivity.dimension() <= 3), + "unexpected connectivity dimension"); + + switch (connectivity.dimension()) { + case 1: { + const auto& connectivity_1d = static_cast<const Connectivity1D&>(connectivity); + return connectivity_1d.isOwned<item_type>(); + break; + } + case 2: { + const auto& connectivity_2d = static_cast<const Connectivity2D&>(connectivity); + return connectivity_2d.isOwned<item_type>(); + break; + } + case 3: { + const auto& connectivity_3d = static_cast<const Connectivity3D&>(connectivity); + return connectivity_3d.isOwned<item_type>(); + break; + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("unexpected dimension"); + } + // LCOV_EXCL_STOP + } + }(*item_array.connectivity_ptr())) + { + ; + } + + PUGS_INLINE + ~SubItemArrayPerItemMin() = default; + }; + + const DataType local_min = SubItemArrayPerItemMin{sub_item_array_per_item}; + return parallel::allReduceMin(local_min); +} + +template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> +std::remove_const_t<DataType> +max(const SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_array_per_item) +{ + using SubItemArrayPerItemType = SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>; + constexpr ItemType item_type = ItemOfItem::item_type; + using ItemIsOwnedType = ItemValue<const bool, item_type>; + using data_type = std::remove_const_t<typename SubItemArrayPerItemType::data_type>; + using index_type = typename SubItemArrayPerItemType::index_type; + + static_assert(std::is_arithmetic_v<data_type>, "min cannot be called on non-arithmetic data"); + static_assert(not std::is_same_v<data_type, bool>, "max cannot be called on boolean data"); + + class SubItemArrayPerItemMax + { + private: + const SubItemArrayPerItemType& m_sub_item_array_per_item; + const ItemIsOwnedType m_is_owned; + + public: + PUGS_INLINE + operator data_type() + { + data_type reduced_array; + parallel_reduce(m_sub_item_array_per_item.numberOfItems(), *this, reduced_array); + return reduced_array; + } + + PUGS_INLINE + void + operator()(const index_type& item_id, data_type& data) const + { + if (m_is_owned[item_id]) { + Table sub_item_table = m_sub_item_array_per_item.itemTable(item_id); + for (size_t i = 0; i < sub_item_table.numberOfRows(); ++i) { + for (size_t j = 0; j < sub_item_table.numberOfColumns(); ++j) { + if (sub_item_table(i, j) > data) { + data = sub_item_table(i, j); + } + } + } + } + } + + PUGS_INLINE + void + join(volatile data_type& dst, const volatile data_type& src) const + { + if (src > dst) { + // cannot be reached if initial array is the max + dst = src; // LCOV_EXCL_LINE + } + } + + PUGS_INLINE + void + init(data_type& array) const + { + array = std::numeric_limits<data_type>::min(); + } + + PUGS_INLINE + SubItemArrayPerItemMax(const SubItemArrayPerItemType& item_array) + : m_sub_item_array_per_item(item_array), m_is_owned([&](const IConnectivity& connectivity) { + Assert((connectivity.dimension() > 0) and (connectivity.dimension() <= 3), + "unexpected connectivity dimension"); + + switch (connectivity.dimension()) { + case 1: { + const auto& connectivity_1d = static_cast<const Connectivity1D&>(connectivity); + return connectivity_1d.isOwned<item_type>(); + break; + } + case 2: { + const auto& connectivity_2d = static_cast<const Connectivity2D&>(connectivity); + return connectivity_2d.isOwned<item_type>(); + break; + } + case 3: { + const auto& connectivity_3d = static_cast<const Connectivity3D&>(connectivity); + return connectivity_3d.isOwned<item_type>(); + break; + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("unexpected dimension"); + } + // LCOV_EXCL_STOP + } + }(*item_array.connectivity_ptr())) + { + ; + } + + PUGS_INLINE + ~SubItemArrayPerItemMax() = default; + }; + + const DataType local_max = SubItemArrayPerItemMax{sub_item_array_per_item}; + return parallel::allReduceMax(local_max); +} + +template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> +std::remove_const_t<DataType> +sum(const SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& item_array) +{ + using SubItemArrayPerItemType = SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>; + constexpr ItemType item_type = ItemOfItem::item_type; + using ItemIsOwnedType = ItemValue<const bool, item_type>; + using data_type = std::remove_const_t<typename SubItemArrayPerItemType::data_type>; + using index_type = typename SubItemArrayPerItemType::index_type; + + static_assert(not std::is_same_v<data_type, bool>, "sum cannot be called on boolean data"); + + class SubItemArrayPerItemSum + { + private: + const SubItemArrayPerItemType& m_sub_item_array_per_item; + const ItemIsOwnedType m_is_owned; + + public: + PUGS_INLINE + operator data_type() + { + data_type reduced_array; + parallel_reduce(m_sub_item_array_per_item.numberOfItems(), *this, reduced_array); + return reduced_array; + } + + PUGS_INLINE + void + operator()(const index_type& item_id, data_type& data) const + { + if (m_is_owned[item_id]) { + Table sub_item_table = m_sub_item_array_per_item.itemTable(item_id); + for (size_t i = 0; i < sub_item_table.numberOfRows(); ++i) { + for (size_t j = 0; j < sub_item_table.numberOfColumns(); ++j) { + data += sub_item_table(i, j); + } + } + } + } + + PUGS_INLINE + void + join(volatile data_type& dst, const volatile data_type& src) const + { + dst += src; + } + + PUGS_INLINE + void + init(data_type& array) const + { + if constexpr (std::is_arithmetic_v<data_type>) { + array = 0; + } else { + static_assert(is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>, "invalid data type"); + array = zero; + } + } + + PUGS_INLINE + SubItemArrayPerItemSum(const SubItemArrayPerItemType& item_array) + : m_sub_item_array_per_item(item_array), m_is_owned([&](const IConnectivity& connectivity) { + Assert((connectivity.dimension() > 0) and (connectivity.dimension() <= 3), + "unexpected connectivity dimension"); + + switch (connectivity.dimension()) { + case 1: { + const auto& connectivity_1d = static_cast<const Connectivity1D&>(connectivity); + return connectivity_1d.isOwned<item_type>(); + break; + } + case 2: { + const auto& connectivity_2d = static_cast<const Connectivity2D&>(connectivity); + return connectivity_2d.isOwned<item_type>(); + break; + } + case 3: { + const auto& connectivity_3d = static_cast<const Connectivity3D&>(connectivity); + return connectivity_3d.isOwned<item_type>(); + break; + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("unexpected dimension"); + } + // LCOV_EXCL_STOP + } + }(*item_array.connectivity_ptr())) + { + ; + } + + PUGS_INLINE + ~SubItemArrayPerItemSum() = default; + }; + + const DataType local_sum = SubItemArrayPerItemSum{item_array}; + return parallel::allReduceSum(local_sum); +} + +template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> +void +synchronize(SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_array_per_item) +{ + static_assert(not std::is_const_v<DataType>, "cannot synchronize SubItemArrayPerItem of const data"); + if (parallel::size() > 1) { + auto& manager = SynchronizerManager::instance(); + const IConnectivity* connectivity = sub_item_array_per_item.connectivity_ptr().get(); + Synchronizer& synchronizer = manager.getConnectivitySynchronizer(connectivity); + synchronizer.synchronize(sub_item_array_per_item); + } +} + +template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> +bool +isSynchronized(const SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_array_per_item) +{ + bool is_synchronized = true; + if (parallel::size() > 1) { + SubItemArrayPerItem<std::remove_const_t<DataType>, ItemOfItem> sub_item_array_per_item_copy = + copy(sub_item_array_per_item); + + synchronize(sub_item_array_per_item_copy); + + for (size_t i = 0; i < sub_item_array_per_item.numberOfArrays(); ++i) { + Array sub_item_array = sub_item_array_per_item[i]; + Array sub_item_array_copy = sub_item_array_per_item_copy[i]; + for (size_t j = 0; j < sub_item_array.size(); ++j) { + if (sub_item_array_copy[j] != sub_item_array[j]) { + is_synchronized = false; + } + } + } + + is_synchronized = parallel::allReduceAnd(is_synchronized); + } + + return is_synchronized; +} + +#endif // SUB_ITEM_ARRAY_PER_ITEM_UTILS_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 594384931..cbd6b32bc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -175,6 +175,7 @@ add_executable (mpi_unit_tests test_SubItemValuePerItem.cpp test_SubItemValuePerItemUtils.cpp test_SubItemArrayPerItem.cpp + test_SubItemArrayPerItemUtils.cpp test_Synchronizer.cpp ) diff --git a/tests/test_SubItemArrayPerItemUtils.cpp b/tests/test_SubItemArrayPerItemUtils.cpp new file mode 100644 index 000000000..d3f29a381 --- /dev/null +++ b/tests/test_SubItemArrayPerItemUtils.cpp @@ -0,0 +1,430 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <algebra/TinyMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/SubItemArrayPerItem.hpp> +#include <mesh/SubItemArrayPerItemUtils.hpp> +#include <utils/Messenger.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("SubItemArrayPerItemUtils", "[mesh]") +{ + SECTION("Synchronize") + { + std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_2d = named_mesh.mesh(); + + const Connectivity<2>& connectivity = mesh_2d->connectivity(); + + NodeArrayPerFace<int> weak_node_array_per_face{connectivity, 3}; + + for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) { + auto face_table = weak_node_array_per_face.itemTable(face_id); + for (size_t i_node = 0; i_node < face_table.numberOfRows(); ++i_node) { + for (size_t l = 0; l < face_table.numberOfColumns(); ++l) { + face_table(i_node, l) = parallel::rank() + 2 * i_node + l; + } + } + } + + NodeArrayPerFace<const int> node_array_per_face{weak_node_array_per_face}; + + REQUIRE(node_array_per_face.connectivity_ptr() == weak_node_array_per_face.connectivity_ptr()); + + if (parallel::size() > 1) { + // before synchronization + auto face_owner = connectivity.faceOwner(); + + bool is_synchronized = true; + for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) { + auto face_table = node_array_per_face.itemTable(face_id); + for (size_t i_node = 0; i_node < face_table.numberOfRows(); ++i_node) { + for (size_t l = 0; l < face_table.numberOfColumns(); ++l) { + if (face_table(i_node, l) != static_cast<int>(face_owner[face_id] + 2 * i_node + l)) { + is_synchronized = false; + break; + } + } + } + } + + REQUIRE(not is_synchronized); + REQUIRE(not isSynchronized(node_array_per_face)); + } + + synchronize(weak_node_array_per_face); + + { // after synchronization + auto face_owner = connectivity.faceOwner(); + + bool is_synchronized = true; + for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) { + auto face_table = node_array_per_face.itemTable(face_id); + for (size_t i_node = 0; i_node < face_table.numberOfRows(); ++i_node) { + for (size_t l = 0; l < face_table.numberOfColumns(); ++l) { + if (face_table(i_node, l) != static_cast<int>(face_owner[face_id] + 2 * i_node + l)) { + is_synchronized = false; + } + } + } + } + REQUIRE(is_synchronized); + } + REQUIRE(isSynchronized(node_array_per_face)); + } + } + } + + SECTION("min") + { + SECTION("1D") + { + std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_1d = named_mesh.mesh(); + + const Connectivity<1>& connectivity = mesh_1d->connectivity(); + + NodeArrayPerCell<int> node_array_per_cell{connectivity, 3}; + node_array_per_cell.fill(-1); + + auto cell_is_owned = connectivity.cellIsOwned(); + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + if (cell_is_owned[cell_id]) { + auto cell_table = node_array_per_cell.itemTable(cell_id); + for (size_t i = 0; i < cell_table.numberOfRows(); ++i) { + for (size_t j = 0; j < cell_table.numberOfColumns(); ++j) { + cell_table(i, j) = 10 + parallel::rank() + i + 2 * j; + } + } + } + }); + + REQUIRE(min(node_array_per_cell) == 10); + } + } + } + + SECTION("2D") + { + std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_2d = named_mesh.mesh(); + + const Connectivity<2>& connectivity = mesh_2d->connectivity(); + + FaceArrayPerCell<int> face_array_per_cell{connectivity, 3}; + face_array_per_cell.fill(-1); + + auto cell_is_owned = connectivity.cellIsOwned(); + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + if (cell_is_owned[cell_id]) { + auto cell_table = face_array_per_cell.itemTable(cell_id); + for (size_t i = 0; i < cell_table.numberOfRows(); ++i) { + for (size_t j = 0; j < cell_table.numberOfColumns(); ++j) { + cell_table(i, j) = 10 + parallel::rank() + i + j; + } + } + } + }); + + REQUIRE(min(face_array_per_cell) == 10); + } + } + } + + SECTION("3D") + { + std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_3d = named_mesh.mesh(); + + const Connectivity<3>& connectivity = mesh_3d->connectivity(); + + EdgeArrayPerFace<int> edge_array_per_face{connectivity, 3}; + edge_array_per_face.fill(-1); + + auto face_is_owned = connectivity.faceIsOwned(); + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(FaceId face_id) { + if (face_is_owned[face_id]) { + auto face_table = edge_array_per_face.itemTable(face_id); + for (size_t i = 0; i < face_table.numberOfRows(); ++i) { + for (size_t j = 0; j < face_table.numberOfColumns(); ++j) { + face_table(i, j) = 10 + parallel::rank() + i + j; + } + } + } + }); + + REQUIRE(min(edge_array_per_face) == 10); + } + } + } + } + + SECTION("max") + { + SECTION("1D") + { + std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_1d = named_mesh.mesh(); + + const Connectivity<1>& connectivity = mesh_1d->connectivity(); + + EdgeArrayPerCell<size_t> edge_array_per_cell{connectivity, 3}; + edge_array_per_cell.fill(std::numeric_limits<size_t>::max()); + + auto cell_is_owned = connectivity.cellIsOwned(); + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + if (cell_is_owned[cell_id]) { + auto cell_table = edge_array_per_cell.itemTable(cell_id); + for (size_t i = 0; i < cell_table.numberOfRows(); ++i) { + for (size_t j = 0; j < cell_table.numberOfColumns(); ++j) { + cell_table(i, j) = 10 + parallel::rank() - i - j; + } + } + } + }); + + REQUIRE(max(edge_array_per_cell) == 9 + parallel::size()); + } + } + } + + SECTION("2D") + { + std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_2d = named_mesh.mesh(); + + const Connectivity<2>& connectivity = mesh_2d->connectivity(); + + EdgeArrayPerCell<size_t> edge_array_per_cell{connectivity, 3}; + edge_array_per_cell.fill(std::numeric_limits<size_t>::max()); + + auto cell_is_owned = connectivity.cellIsOwned(); + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + if (cell_is_owned[cell_id]) { + auto cell_table = edge_array_per_cell.itemTable(cell_id); + for (size_t i = 0; i < cell_table.numberOfRows(); ++i) { + for (size_t j = 0; j < cell_table.numberOfColumns(); ++j) { + cell_table(i, j) = 10 + parallel::rank() - i - j; + } + } + } + }); + + REQUIRE(max(edge_array_per_cell) == 9 + parallel::size()); + } + } + } + + SECTION("3D") + { + std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_3d = named_mesh.mesh(); + + const Connectivity<3>& connectivity = mesh_3d->connectivity(); + + NodeArrayPerEdge<size_t> node_array_per_edge{connectivity, 3}; + node_array_per_edge.fill(std::numeric_limits<size_t>::max()); + + auto edge_is_owned = connectivity.edgeIsOwned(); + + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(EdgeId edge_id) { + if (edge_is_owned[edge_id]) { + auto edge_table = node_array_per_edge.itemTable(edge_id); + for (size_t i = 0; i < edge_table.numberOfRows(); ++i) { + for (size_t j = 0; j < edge_table.numberOfColumns(); ++j) { + edge_table(i, j) = 10 + parallel::rank() - i - j; + } + } + } + }); + + REQUIRE(max(node_array_per_edge) == 9 + parallel::size()); + } + } + } + } + + SECTION("sum") + { + SECTION("1D") + { + std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name()) + { + auto mesh_1d = named_mesh.mesh(); + + const Connectivity<1>& connectivity = mesh_1d->connectivity(); + + NodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3}; + node_array_per_cell.fill(5); + + auto cell_is_owned = connectivity.cellIsOwned(); + + const size_t global_number_of_nodes_per_cells = [&] { + size_t number_of_nodes_per_cells = 0; + for (CellId cell_id = 0; cell_id < cell_is_owned.numberOfItems(); ++cell_id) { + number_of_nodes_per_cells += cell_is_owned[cell_id] * node_array_per_cell.numberOfSubArrays(cell_id); + } + return parallel::allReduceSum(number_of_nodes_per_cells); + }(); + + REQUIRE(sum(node_array_per_cell) == + 5 * global_number_of_nodes_per_cells * node_array_per_cell.sizeOfArrays()); + } + } + } + + SECTION("2D") + { + std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name() + " for size_t data") + { + auto mesh_2d = named_mesh.mesh(); + + const Connectivity<2>& connectivity = mesh_2d->connectivity(); + + NodeArrayPerFace<size_t> node_array_per_face{connectivity, 3}; + node_array_per_face.fill(2); + + auto face_is_owned = connectivity.faceIsOwned(); + + const size_t global_number_of_nodes_per_faces = [&] { + size_t number_of_nodes_per_faces = 0; + for (FaceId face_id = 0; face_id < face_is_owned.numberOfItems(); ++face_id) { + number_of_nodes_per_faces += face_is_owned[face_id] * node_array_per_face.numberOfSubArrays(face_id); + } + return parallel::allReduceSum(number_of_nodes_per_faces); + }(); + + REQUIRE(sum(node_array_per_face) == + 2 * global_number_of_nodes_per_faces * node_array_per_face.sizeOfArrays()); + } + + SECTION(named_mesh.name() + " for N^2 data") + { + auto mesh_2d = named_mesh.mesh(); + + const Connectivity<2>& connectivity = mesh_2d->connectivity(); + + using N2 = TinyVector<2, size_t>; + NodeArrayPerFace<N2> node_array_per_face{connectivity, 3}; + + const N2 data(3, 2); + node_array_per_face.fill(data); + + auto face_is_owned = connectivity.faceIsOwned(); + + const size_t global_number_of_nodes_per_faces = [&] { + size_t number_of_nodes_per_faces = 0; + for (FaceId face_id = 0; face_id < face_is_owned.numberOfItems(); ++face_id) { + number_of_nodes_per_faces += face_is_owned[face_id] * node_array_per_face.numberOfSubArrays(face_id); + } + return parallel::allReduceSum(number_of_nodes_per_faces); + }(); + + REQUIRE(sum(node_array_per_face) == + global_number_of_nodes_per_faces * node_array_per_face.sizeOfArrays() * data); + } + } + } + + SECTION("3D") + { + std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes(); + + for (auto named_mesh : mesh_list) { + SECTION(named_mesh.name() + " for size_t data") + { + auto mesh_3d = named_mesh.mesh(); + + const Connectivity<3>& connectivity = mesh_3d->connectivity(); + + EdgeArrayPerFace<size_t> edge_array_per_face{connectivity, 3}; + edge_array_per_face.fill(3); + + auto face_is_owned = connectivity.faceIsOwned(); + + const size_t global_number_of_edges_per_faces = [&] { + size_t number_of_edges_per_faces = 0; + for (FaceId face_id = 0; face_id < face_is_owned.numberOfItems(); ++face_id) { + number_of_edges_per_faces += face_is_owned[face_id] * edge_array_per_face.numberOfSubArrays(face_id); + } + return parallel::allReduceSum(number_of_edges_per_faces); + }(); + + REQUIRE(sum(edge_array_per_face) == + 3 * global_number_of_edges_per_faces * edge_array_per_face.sizeOfArrays()); + } + + SECTION(named_mesh.name() + " for N^2x3 data") + { + auto mesh_3d = named_mesh.mesh(); + + const Connectivity<3>& connectivity = mesh_3d->connectivity(); + + using N2x3 = TinyMatrix<2, 3, size_t>; + EdgeArrayPerFace<N2x3> edge_array_per_face{connectivity, 3}; + + const N2x3 data(1, 3, 4, 6, 2, 5); + edge_array_per_face.fill(data); + + auto face_is_owned = connectivity.faceIsOwned(); + + const size_t global_number_of_edges_per_faces = [&] { + size_t number_of_edges_per_faces = 0; + for (FaceId face_id = 0; face_id < face_is_owned.numberOfItems(); ++face_id) { + number_of_edges_per_faces += face_is_owned[face_id] * edge_array_per_face.numberOfSubArrays(face_id); + } + return parallel::allReduceSum(number_of_edges_per_faces); + }(); + + REQUIRE(sum(edge_array_per_face) == + global_number_of_edges_per_faces * edge_array_per_face.sizeOfArrays() * data); + } + } + } + } +} -- GitLab