diff --git a/src/mesh/ConnectivityDispatcher.cpp b/src/mesh/ConnectivityDispatcher.cpp index 1502bed705b252d9be19e1783ab7719488451c8c..769eb23bcfc75c1118fe5b19e4428dd1e36ede33 100644 --- a/src/mesh/ConnectivityDispatcher.cpp +++ b/src/mesh/ConnectivityDispatcher.cpp @@ -183,7 +183,7 @@ ConnectivityDispatcher<Dimension>::_gatherFrom( 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]; - const auto& item_data = data_to_gather.itemValues(item_id); + 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]); } diff --git a/src/mesh/ItemArrayUtils.hpp b/src/mesh/ItemArrayUtils.hpp index 6025bd0f3b17763be3f4b6646ab6730be0d6de18..ca339de9a10809d939c8924844c507442c83b8ef 100644 --- a/src/mesh/ItemArrayUtils.hpp +++ b/src/mesh/ItemArrayUtils.hpp @@ -10,6 +10,302 @@ #include <iostream> +template <typename DataType, ItemType item_type, typename ConnectivityPtr> +std::remove_const_t<DataType> +min(const ItemArray<DataType, item_type, ConnectivityPtr>& item_value) +{ + using ItemArrayType = ItemArray<DataType, item_type, ConnectivityPtr>; + using ItemIsOwnedType = ItemValue<const bool, item_type>; + using data_type = std::remove_const_t<typename ItemArrayType::data_type>; + using index_type = typename ItemArrayType::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 ItemArrayMin + { + private: + const ItemArrayType& m_item_value; + const ItemIsOwnedType m_is_owned; + + public: + PUGS_INLINE + operator data_type() + { + data_type reduced_value; + parallel_reduce(m_item_value.numberOfItems(), *this, reduced_value); + return reduced_value; + } + + PUGS_INLINE + void + operator()(const index_type& i_item, data_type& data) const + { + if (m_is_owned[i_item]) { + Array array = m_item_value[i_item]; + for (size_t i = 0; i < m_item_value.sizeOfArrays(); ++i) { + if (array[i] < data) { + data = array[i]; + } + } + } + } + + PUGS_INLINE + void + join(volatile data_type& dst, const volatile data_type& src) const + { + if (src < dst) { + // cannot be reached if initial value is the min + dst = src; // LCOV_EXCL_LINE + } + } + + PUGS_INLINE + void + init(data_type& value) const + { + value = std::numeric_limits<data_type>::max(); + } + + PUGS_INLINE + ItemArrayMin(const ItemArrayType& item_value) + : m_item_value(item_value), 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_value.connectivity_ptr())) + { + ; + } + + PUGS_INLINE + ~ItemArrayMin() = default; + }; + + const DataType local_min = ItemArrayMin{item_value}; + return parallel::allReduceMin(local_min); +} + +template <typename DataType, ItemType item_type, typename ConnectivityPtr> +std::remove_const_t<DataType> +max(const ItemArray<DataType, item_type, ConnectivityPtr>& item_value) +{ + using ItemArrayType = ItemArray<DataType, item_type, ConnectivityPtr>; + using ItemIsOwnedType = ItemValue<const bool, item_type>; + using data_type = std::remove_const_t<typename ItemArrayType::data_type>; + using index_type = typename ItemArrayType::index_type; + + static_assert(std::is_arithmetic_v<data_type>, "max 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 ItemArrayMax + { + private: + const ItemArrayType& m_item_value; + const ItemIsOwnedType m_is_owned; + + public: + PUGS_INLINE + operator data_type() + { + data_type reduced_value; + parallel_reduce(m_item_value.numberOfItems(), *this, reduced_value); + return reduced_value; + } + + PUGS_INLINE + void + operator()(const index_type& i_item, data_type& data) const + { + if (m_is_owned[i_item]) { + Array array = m_item_value[i_item]; + for (size_t i = 0; i < m_item_value.sizeOfArrays(); ++i) { + if (array[i] > data) { + data = array[i]; + } + } + } + } + + PUGS_INLINE + void + join(volatile data_type& dst, const volatile data_type& src) const + { + if (src > dst) { + // cannot be reached if initial value is the max + dst = src; // LCOV_EXCL_LINE + } + } + + PUGS_INLINE + void + init(data_type& value) const + { + value = std::numeric_limits<data_type>::min(); + } + + PUGS_INLINE + ItemArrayMax(const ItemArrayType& item_value) + : m_item_value(item_value), 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_value.connectivity_ptr())) + { + ; + } + + PUGS_INLINE + ~ItemArrayMax() = default; + }; + + const DataType local_max = ItemArrayMax{item_value}; + return parallel::allReduceMax(local_max); +} + +template <typename DataType, ItemType item_type, typename ConnectivityPtr> +std::remove_const_t<DataType> +sum(const ItemArray<DataType, item_type, ConnectivityPtr>& item_value) +{ + using ItemArrayType = ItemArray<DataType, item_type, ConnectivityPtr>; + using ItemIsOwnedType = ItemValue<const bool, item_type>; + using data_type = std::remove_const_t<typename ItemArrayType::data_type>; + using index_type = typename ItemArrayType::index_type; + + static_assert(not std::is_same_v<data_type, bool>, "sum cannot be called on boolean data"); + + class ItemArraySum + { + private: + const ItemArrayType& m_item_value; + const ItemIsOwnedType m_is_owned; + + public: + PUGS_INLINE + operator data_type() + { + data_type reduced_value; + parallel_reduce(m_item_value.numberOfItems(), *this, reduced_value); + return reduced_value; + } + + PUGS_INLINE + void + operator()(const index_type& i_item, data_type& data) const + { + if (m_is_owned[i_item]) { + Array array = m_item_value[i_item]; + for (size_t i = 0; i < m_item_value.sizeOfArrays(); ++i) { + data += array[i]; + } + } + } + + PUGS_INLINE + void + join(volatile data_type& dst, const volatile data_type& src) const + { + dst += src; + } + + PUGS_INLINE + void + init(data_type& value) const + { + if constexpr (std::is_arithmetic_v<data_type>) { + value = 0; + } else { + static_assert(is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>, "invalid data type"); + value = zero; + } + } + + PUGS_INLINE + ItemArraySum(const ItemArrayType& item_value) + : m_item_value(item_value), 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_value.connectivity_ptr())) + { + ; + } + + PUGS_INLINE + ~ItemArraySum() = default; + }; + + const DataType local_sum = ItemArraySum{item_value}; + return parallel::allReduceSum(local_sum); +} + template <typename DataType, ItemType item_type, typename ConnectivityPtr> void synchronize(ItemArray<DataType, item_type, ConnectivityPtr>& item_array) diff --git a/src/mesh/ItemType.hpp b/src/mesh/ItemType.hpp index c2f2da131e417e931cce8d3c40d91fd83ad19d45..285806fe0599f66d5f70b55e0e4e2de3e5d9d919 100644 --- a/src/mesh/ItemType.hpp +++ b/src/mesh/ItemType.hpp @@ -59,8 +59,8 @@ struct ItemTypeId<1> i = 0; break; } - case ItemType::edge: case ItemType::face: + case ItemType::edge: case ItemType::node: { // in 1d, faces, edges and nodes are the same i = 1; @@ -69,6 +69,13 @@ struct ItemTypeId<1> } return i; } + + PUGS_INLINE + static constexpr size_t + dimension(ItemType item_type) + { + return 1 - itemTId(item_type); + } }; template <ItemType item_type> @@ -87,8 +94,8 @@ struct ItemTypeId<2> i = 0; break; } - case ItemType::edge: - case ItemType::face: { + case ItemType::face: + case ItemType::edge: { // in 2d, faces and edges are the same i = 1; break; @@ -100,6 +107,13 @@ struct ItemTypeId<2> } return i; } + + PUGS_INLINE + static constexpr size_t + dimension(ItemType item_type) + { + return 2 - itemTId(item_type); + } }; template <ItemType item_type> @@ -118,11 +132,11 @@ struct ItemTypeId<3> i = 0; break; } - case ItemType::edge: { + case ItemType::face: { i = 1; break; } - case ItemType::face: { + case ItemType::edge: { i = 2; break; } @@ -133,6 +147,13 @@ struct ItemTypeId<3> } return i; } + + PUGS_INLINE + static constexpr size_t + dimension(ItemType item_type) + { + return 3 - itemTId(item_type); + } }; template <ItemType item_type> diff --git a/src/mesh/ItemValueUtils.hpp b/src/mesh/ItemValueUtils.hpp index b5151953c8bff1501124b027ef82c259c7bc17b7..ef99f59e44f9f71a01267ca0c424220241573f60 100644 --- a/src/mesh/ItemValueUtils.hpp +++ b/src/mesh/ItemValueUtils.hpp @@ -20,7 +20,8 @@ min(const ItemValue<DataType, item_type, ConnectivityPtr>& item_value) using data_type = std::remove_const_t<typename ItemValueType::data_type>; using index_type = typename ItemValueType::index_type; - static_assert(not std::is_same_v<data_type, bool>, "min cannot be called on boolean arrays"); + 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 ItemValueMin { @@ -113,7 +114,9 @@ max(const ItemValue<DataType, item_type, ConnectivityPtr>& item_value) using data_type = std::remove_const_t<typename ItemValueType::data_type>; using index_type = typename ItemValueType::index_type; - static_assert(not std::is_same_v<data_type, bool>, "min cannot be called on boolean arrays"); + static_assert(std::is_arithmetic_v<data_type>, "max 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 ItemValueMax { private: @@ -196,16 +199,16 @@ max(const ItemValue<DataType, item_type, ConnectivityPtr>& item_value) return parallel::allReduceMax(local_max); } -template <typename DataType, ItemType item_type> +template <typename DataType, ItemType item_type, typename ConnectivityPtr> std::remove_const_t<DataType> -sum(const ItemValue<DataType, item_type>& item_value) +sum(const ItemValue<DataType, item_type, ConnectivityPtr>& item_value) { - using ItemValueType = ItemValue<DataType, item_type>; + using ItemValueType = ItemValue<DataType, item_type, ConnectivityPtr>; using ItemIsOwnedType = ItemValue<const bool, item_type>; using data_type = std::remove_const_t<typename ItemValueType::data_type>; using index_type = typename ItemValueType::index_type; - static_assert(not std::is_same_v<data_type, bool>, "sum cannot be called on boolean arrays"); + static_assert(not std::is_same_v<data_type, bool>, "sum cannot be called on boolean data"); class ItemValueSum { diff --git a/src/mesh/MeshData.hpp b/src/mesh/MeshData.hpp index 596ab38bd3713686e3b69b96e7b854b170bca5e0..8842fd8ac604221d869e046a7d5e9e0975916ad8 100644 --- a/src/mesh/MeshData.hpp +++ b/src/mesh/MeshData.hpp @@ -401,7 +401,7 @@ class MeshData : public IMeshData const auto& cell_nodes = cell_to_node_matrix[j]; const auto& cell_faces = cell_to_face_matrix[j]; - const auto& face_is_reversed = cell_face_is_reversed.itemValues(j); + const auto& face_is_reversed = cell_face_is_reversed.itemArray(j); for (size_t L = 0; L < cell_faces.size(); ++L) { const FaceId& l = cell_faces[L]; diff --git a/src/mesh/SubItemArrayPerItem.hpp b/src/mesh/SubItemArrayPerItem.hpp index f2103789e9a49c92b921fe2f8b24b3bdaf8f69ec..7b204d2193aa6cdb450f7e8d8b23f91d0a47e2c3 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 0000000000000000000000000000000000000000..5e4f122e3d8dc7a06858091a2e8f8baa402c95e4 --- /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/src/mesh/SubItemValuePerItem.hpp b/src/mesh/SubItemValuePerItem.hpp index defdc8fc9711c6be265e022e0bb0c710d078226d..ea0e900681279b306ee82a9615e843650cfb1ea7 100644 --- a/src/mesh/SubItemValuePerItem.hpp +++ b/src/mesh/SubItemValuePerItem.hpp @@ -49,7 +49,7 @@ class SubItemValuePerItem public: friend PUGS_INLINE SubItemValuePerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityPtr> - copy(SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& source) + copy(const SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& source) { SubItemValuePerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityPtr> image; @@ -137,7 +137,7 @@ class SubItemValuePerItem template <typename IndexType> PUGS_INLINE Array<DataType> - itemValues(IndexType item_id) noexcept(NO_ASSERT) + itemArray(IndexType item_id) noexcept(NO_ASSERT) { static_assert(std::is_same_v<IndexType, ItemId>, "index must be an ItemId"); Assert(this->isBuilt()); @@ -151,7 +151,7 @@ class SubItemValuePerItem // changes in data template <typename IndexType> PUGS_INLINE Array<DataType> - itemValues(IndexType item_id) const noexcept(NO_ASSERT) + itemArray(IndexType item_id) const noexcept(NO_ASSERT) { static_assert(std::is_same_v<IndexType, ItemId>, "index must be an ItemId"); Assert(this->isBuilt()); diff --git a/src/mesh/SubItemValuePerItemUtils.hpp b/src/mesh/SubItemValuePerItemUtils.hpp new file mode 100644 index 0000000000000000000000000000000000000000..88f36d1cc15704a947d0990f0a2190c9c375c53c --- /dev/null +++ b/src/mesh/SubItemValuePerItemUtils.hpp @@ -0,0 +1,350 @@ +#ifndef SUB_ITEM_VALUE_PER_ITEM_UTILS_HPP +#define SUB_ITEM_VALUE_PER_ITEM_UTILS_HPP + +#include <utils/Messenger.hpp> + +#include <mesh/Connectivity.hpp> +#include <mesh/SubItemValuePerItem.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 SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_value_per_item) +{ + using SubItemValuePerItemType = SubItemValuePerItem<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 SubItemValuePerItemType::data_type>; + using index_type = typename SubItemValuePerItemType::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 SubItemValuePerItemMin + { + private: + const SubItemValuePerItemType& m_sub_item_value_per_item; + const ItemIsOwnedType m_is_owned; + + public: + PUGS_INLINE + operator data_type() + { + data_type reduced_value; + parallel_reduce(m_sub_item_value_per_item.numberOfItems(), *this, reduced_value); + return reduced_value; + } + + PUGS_INLINE + void + operator()(const index_type& item_id, data_type& data) const + { + if (m_is_owned[item_id]) { + Array sub_item_array = m_sub_item_value_per_item.itemArray(item_id); + for (size_t i = 0; i < sub_item_array.size(); ++i) { + if (sub_item_array[i] < data) { + data = sub_item_array[i]; + } + } + } + } + + PUGS_INLINE + void + join(volatile data_type& dst, const volatile data_type& src) const + { + if (src < dst) { + // cannot be reached if initial value is the min + dst = src; // LCOV_EXCL_LINE + } + } + + PUGS_INLINE + void + init(data_type& value) const + { + value = std::numeric_limits<data_type>::max(); + } + + PUGS_INLINE + SubItemValuePerItemMin(const SubItemValuePerItemType& item_value) + : m_sub_item_value_per_item(item_value), 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_value.connectivity_ptr())) + { + ; + } + + PUGS_INLINE + ~SubItemValuePerItemMin() = default; + }; + + const DataType local_min = SubItemValuePerItemMin{sub_item_value_per_item}; + return parallel::allReduceMin(local_min); +} + +template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> +std::remove_const_t<DataType> +max(const SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_value_per_item) +{ + using SubItemValuePerItemType = SubItemValuePerItem<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 SubItemValuePerItemType::data_type>; + using index_type = typename SubItemValuePerItemType::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 SubItemValuePerItemMax + { + private: + const SubItemValuePerItemType& m_sub_item_value_per_item; + const ItemIsOwnedType m_is_owned; + + public: + PUGS_INLINE + operator data_type() + { + data_type reduced_value; + parallel_reduce(m_sub_item_value_per_item.numberOfItems(), *this, reduced_value); + return reduced_value; + } + + PUGS_INLINE + void + operator()(const index_type& item_id, data_type& data) const + { + if (m_is_owned[item_id]) { + Array sub_item_array = m_sub_item_value_per_item.itemArray(item_id); + for (size_t i = 0; i < sub_item_array.size(); ++i) { + if (sub_item_array[i] > data) { + data = sub_item_array[i]; + } + } + } + } + + PUGS_INLINE + void + join(volatile data_type& dst, const volatile data_type& src) const + { + if (src > dst) { + // cannot be reached if initial value is the max + dst = src; // LCOV_EXCL_LINE + } + } + + PUGS_INLINE + void + init(data_type& value) const + { + value = std::numeric_limits<data_type>::min(); + } + + PUGS_INLINE + SubItemValuePerItemMax(const SubItemValuePerItemType& item_value) + : m_sub_item_value_per_item(item_value), 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_value.connectivity_ptr())) + { + ; + } + + PUGS_INLINE + ~SubItemValuePerItemMax() = default; + }; + + const DataType local_max = SubItemValuePerItemMax{sub_item_value_per_item}; + return parallel::allReduceMax(local_max); +} + +template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> +std::remove_const_t<DataType> +sum(const SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& item_value) +{ + using SubItemValuePerItemType = SubItemValuePerItem<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 SubItemValuePerItemType::data_type>; + using index_type = typename SubItemValuePerItemType::index_type; + + static_assert(not std::is_same_v<data_type, bool>, "sum cannot be called on boolean data"); + + class SubItemValuePerItemSum + { + private: + const SubItemValuePerItemType& m_sub_item_value_per_item; + const ItemIsOwnedType m_is_owned; + + public: + PUGS_INLINE + operator data_type() + { + data_type reduced_value; + parallel_reduce(m_sub_item_value_per_item.numberOfItems(), *this, reduced_value); + return reduced_value; + } + + PUGS_INLINE + void + operator()(const index_type& item_id, data_type& data) const + { + if (m_is_owned[item_id]) { + Array sub_item_array = m_sub_item_value_per_item.itemArray(item_id); + for (size_t i = 0; i < sub_item_array.size(); ++i) { + data += sub_item_array[i]; + } + } + } + + PUGS_INLINE + void + join(volatile data_type& dst, const volatile data_type& src) const + { + dst += src; + } + + PUGS_INLINE + void + init(data_type& value) const + { + if constexpr (std::is_arithmetic_v<data_type>) { + value = 0; + } else { + static_assert(is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>, "invalid data type"); + value = zero; + } + } + + PUGS_INLINE + SubItemValuePerItemSum(const SubItemValuePerItemType& item_value) + : m_sub_item_value_per_item(item_value), 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_value.connectivity_ptr())) + { + ; + } + + PUGS_INLINE + ~SubItemValuePerItemSum() = default; + }; + + const DataType local_sum = SubItemValuePerItemSum{item_value}; + return parallel::allReduceSum(local_sum); +} + +template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> +void +synchronize(SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_value_per_item) +{ + static_assert(not std::is_const_v<DataType>, "cannot synchronize SubItemValuePerItem of const data"); + if (parallel::size() > 1) { + auto& manager = SynchronizerManager::instance(); + const IConnectivity* connectivity = sub_item_value_per_item.connectivity_ptr().get(); + Synchronizer& synchronizer = manager.getConnectivitySynchronizer(connectivity); + synchronizer.synchronize(sub_item_value_per_item); + } +} + +template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> +bool +isSynchronized(const SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_value_per_item) +{ + bool is_synchronized = true; + if (parallel::size() > 1) { + SubItemValuePerItem<std::remove_const_t<DataType>, ItemOfItem> sub_item_value_per_item_copy = + copy(sub_item_value_per_item); + + synchronize(sub_item_value_per_item_copy); + + for (size_t i = 0; i < sub_item_value_per_item.numberOfValues(); ++i) { + if (sub_item_value_per_item_copy[i] != sub_item_value_per_item[i]) { + is_synchronized = false; + break; + } + } + + is_synchronized = parallel::allReduceAnd(is_synchronized); + } + + return is_synchronized; +} + +#endif // SUB_ITEM_VALUE_PER_ITEM_UTILS_HPP diff --git a/src/mesh/Synchronizer.hpp b/src/mesh/Synchronizer.hpp index dc0773ace208cc83e863df0ffada54e1db33f936..63e4363ef7dc1b360ca75626ef01e31dcb0728b7 100644 --- a/src/mesh/Synchronizer.hpp +++ b/src/mesh/Synchronizer.hpp @@ -4,6 +4,8 @@ #include <mesh/Connectivity.hpp> #include <mesh/ItemArray.hpp> #include <mesh/ItemValue.hpp> +#include <mesh/SubItemArrayPerItem.hpp> +#include <mesh/SubItemValuePerItem.hpp> #include <utils/Exceptions.hpp> #include <utils/Messenger.hpp> @@ -11,25 +13,32 @@ #include <iostream> #include <map> +#include <memory> #ifdef PUGS_HAS_MPI class Synchronizer { + private: template <ItemType item_type> using ExchangeItemTypeInfo = std::vector<Array<const ItemIdT<item_type>>>; - ExchangeItemTypeInfo<ItemType::cell> m_requested_cell_info; - ExchangeItemTypeInfo<ItemType::cell> m_provided_cell_info; + std::unique_ptr<ExchangeItemTypeInfo<ItemType::cell>> m_requested_cell_info; + std::unique_ptr<ExchangeItemTypeInfo<ItemType::cell>> m_provided_cell_info; - ExchangeItemTypeInfo<ItemType::face> m_requested_face_info; - ExchangeItemTypeInfo<ItemType::face> m_provided_face_info; + std::unique_ptr<ExchangeItemTypeInfo<ItemType::face>> m_requested_face_info; + std::unique_ptr<ExchangeItemTypeInfo<ItemType::face>> m_provided_face_info; - ExchangeItemTypeInfo<ItemType::edge> m_requested_edge_info; - ExchangeItemTypeInfo<ItemType::edge> m_provided_edge_info; + std::unique_ptr<ExchangeItemTypeInfo<ItemType::edge>> m_requested_edge_info; + std::unique_ptr<ExchangeItemTypeInfo<ItemType::edge>> m_provided_edge_info; - ExchangeItemTypeInfo<ItemType::node> m_requested_node_info; - ExchangeItemTypeInfo<ItemType::node> m_provided_node_info; + std::unique_ptr<ExchangeItemTypeInfo<ItemType::node>> m_requested_node_info; + std::unique_ptr<ExchangeItemTypeInfo<ItemType::node>> m_provided_node_info; + + using ExchangeSubItemPerItemSize = std::vector<std::map<std::pair<ItemType, ItemType>, size_t>>; + + ExchangeSubItemPerItemSize m_sub_item_per_item_requested_size_list; + ExchangeSubItemPerItemSize m_sub_item_per_item_provided_size_list; template <ItemType item_type> PUGS_INLINE constexpr auto& @@ -68,22 +77,24 @@ class Synchronizer const auto& item_owner = connectivity.template owner<item_type>(); using ItemId = ItemIdT<item_type>; - auto& requested_item_info = this->_getRequestedItemInfo<item_type>(); - requested_item_info = [&]() { + auto& p_requested_item_info = this->_getRequestedItemInfo<item_type>(); + p_requested_item_info = [&]() { std::vector<std::vector<ItemId>> requested_item_vector_info(parallel::size()); for (ItemId item_id = 0; item_id < item_owner.numberOfItems(); ++item_id) { if (const size_t owner = item_owner[item_id]; owner != parallel::rank()) { requested_item_vector_info[owner].emplace_back(item_id); } } - std::vector<Array<const ItemId>> requested_item_info(parallel::size()); + ExchangeItemTypeInfo<item_type> requested_item_info(parallel::size()); for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { const auto& requested_item_vector = requested_item_vector_info[i_rank]; requested_item_info[i_rank] = convert_to_array(requested_item_vector); } - return requested_item_info; + return std::make_unique<ExchangeItemTypeInfo<item_type>>(std::move(requested_item_info)); }(); + auto& requested_item_info = *p_requested_item_info; + Array<unsigned int> local_number_of_requested_values(parallel::size()); for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { local_number_of_requested_values[i_rank] = requested_item_info[i_rank].size(); @@ -114,9 +125,9 @@ class Synchronizer item_number_to_id_correspondance[item_number[item_id]] = item_id; } - auto& provided_item_info = this->_getProvidedItemInfo<item_type>(); - provided_item_info = [&]() { - std::vector<Array<const ItemId>> provided_item_info(parallel::size()); + auto& p_provided_item_info = this->_getProvidedItemInfo<item_type>(); + p_provided_item_info = [&]() { + ExchangeItemTypeInfo<item_type> provided_item_info(parallel::size()); for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { Array<ItemId> provided_item_id_to_rank{local_number_of_values_to_send[i_rank]}; const Array<int>& provided_item_number_to_rank = provided_item_number_list_by_rank[i_rank]; @@ -125,8 +136,69 @@ class Synchronizer } provided_item_info[i_rank] = provided_item_id_to_rank; } - return provided_item_info; + return std::make_unique<ExchangeItemTypeInfo<item_type>>(provided_item_info); }(); + + m_sub_item_per_item_provided_size_list.resize(parallel::size()); + m_sub_item_per_item_requested_size_list.resize(parallel::size()); + } + + template <ItemType item_type, ItemType sub_item_type, size_t Dimension> + PUGS_INLINE size_t + _getSubItemPerItemRequestedSize(const Connectivity<Dimension>& connectivity, const size_t i_rank) + { + Assert(m_sub_item_per_item_requested_size_list.size() == parallel::size()); + + auto key = std::make_pair(item_type, sub_item_type); + if (auto i_size_list = m_sub_item_per_item_requested_size_list[i_rank].find(key); + i_size_list != m_sub_item_per_item_requested_size_list[i_rank].end()) { + return i_size_list->second; + } else { + const auto& p_requested_item_info = this->_getRequestedItemInfo<item_type>(); + + Assert(static_cast<bool>(p_requested_item_info) == true, + "this function should be called after calculation of exchange info"); + const auto& requested_item_info_from_rank = (*p_requested_item_info)[i_rank]; + + const auto& item_to_item_matrix = connectivity.template getItemToItemMatrix<item_type, sub_item_type>(); + + size_t count = 0; + for (size_t i = 0; i < requested_item_info_from_rank.size(); ++i) { + count += item_to_item_matrix[requested_item_info_from_rank[i]].size(); + } + + m_sub_item_per_item_requested_size_list[i_rank][key] = count; + return count; + } + } + + template <ItemType item_type, ItemType sub_item_type, size_t Dimension> + PUGS_INLINE size_t + _getSubItemPerItemProvidedSize(const Connectivity<Dimension>& connectivity, const size_t i_rank) + { + Assert(m_sub_item_per_item_provided_size_list.size() == parallel::size()); + + auto key = std::make_pair(item_type, sub_item_type); + if (auto i_size_list = m_sub_item_per_item_provided_size_list[i_rank].find(key); + i_size_list != m_sub_item_per_item_provided_size_list[i_rank].end()) { + return i_size_list->second; + } else { + const auto& p_provided_item_info = this->_getProvidedItemInfo<item_type>(); + + Assert(static_cast<bool>(p_provided_item_info) == true, + "this function should be called after calculation of exchange info"); + const auto& provided_item_info_from_rank = (*p_provided_item_info)[i_rank]; + + const auto& item_to_item_matrix = connectivity.template getItemToItemMatrix<item_type, sub_item_type>(); + + size_t count = 0; + for (size_t i = 0; i < provided_item_info_from_rank.size(); ++i) { + count += item_to_item_matrix[provided_item_info_from_rank[i]].size(); + } + + m_sub_item_per_item_provided_size_list[i_rank][key] = count; + return count; + } } template <typename ConnectivityType, typename DataType, ItemType item_type, typename ConnectivityPtr> @@ -137,15 +209,20 @@ class Synchronizer using ItemId = ItemIdT<item_type>; - const auto& provided_item_info = this->_getProvidedItemInfo<item_type>(); - const auto& requested_item_info = this->_getRequestedItemInfo<item_type>(); + const auto& p_provided_item_info = this->_getProvidedItemInfo<item_type>(); + const auto& p_requested_item_info = this->_getRequestedItemInfo<item_type>(); - Assert(requested_item_info.size() == provided_item_info.size()); + Assert(static_cast<bool>(p_provided_item_info) == static_cast<bool>(p_requested_item_info)); - if (provided_item_info.size() == 0) { + if (not p_provided_item_info) { this->_buildSynchronizeInfo<ConnectivityType, item_type>(connectivity); } + const auto& provided_item_info = *p_provided_item_info; + const auto& requested_item_info = *p_requested_item_info; + + Assert(requested_item_info.size() == provided_item_info.size()); + std::vector<Array<const DataType>> provided_data_list(parallel::size()); for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { const Array<const ItemId>& provided_item_info_to_rank = provided_item_info[i_rank]; @@ -181,15 +258,20 @@ class Synchronizer using ItemId = ItemIdT<item_type>; - const auto& provided_item_info = this->_getProvidedItemInfo<item_type>(); - const auto& requested_item_info = this->_getRequestedItemInfo<item_type>(); + const auto& p_provided_item_info = this->_getProvidedItemInfo<item_type>(); + const auto& p_requested_item_info = this->_getRequestedItemInfo<item_type>(); - Assert(requested_item_info.size() == provided_item_info.size()); + Assert(static_cast<bool>(p_provided_item_info) == static_cast<bool>(p_requested_item_info)); - if (provided_item_info.size() == 0) { + if (not p_provided_item_info) { this->_buildSynchronizeInfo<ConnectivityType, item_type>(connectivity); } + const auto& provided_item_info = *p_provided_item_info; + const auto& requested_item_info = *p_requested_item_info; + + Assert(requested_item_info.size() == provided_item_info.size()); + const size_t size_of_arrays = item_array.sizeOfArrays(); std::vector<Array<const DataType>> provided_data_list(parallel::size()); @@ -229,6 +311,156 @@ class Synchronizer } } + template <typename ConnectivityType, typename DataType, typename ItemOfItem, typename ConnectivityPtr> + PUGS_INLINE void + _synchronize(const ConnectivityType& connectivity, + SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_value_per_item) + { + static_assert(not std::is_abstract_v<ConnectivityType>, "_synchronize must be called on a concrete connectivity"); + if constexpr (ItemTypeId<ConnectivityType::Dimension>::dimension(ItemOfItem::item_type) > + ItemTypeId<ConnectivityType::Dimension>::dimension(ItemOfItem::sub_item_type)) { + constexpr ItemType item_type = ItemOfItem::item_type; + constexpr ItemType sub_item_type = ItemOfItem::sub_item_type; + + using ItemId = ItemIdT<item_type>; + + const auto& p_provided_item_info = this->_getProvidedItemInfo<item_type>(); + const auto& p_requested_item_info = this->_getRequestedItemInfo<item_type>(); + + Assert(static_cast<bool>(p_provided_item_info) == static_cast<bool>(p_requested_item_info)); + + if (not p_provided_item_info) { + this->_buildSynchronizeInfo<ConnectivityType, item_type>(connectivity); + } + + const auto& provided_item_info = *p_provided_item_info; + const auto& requested_item_info = *p_requested_item_info; + + Assert(requested_item_info.size() == provided_item_info.size()); + + std::vector<Array<const DataType>> provided_data_list(parallel::size()); + for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { + const Array<const ItemId>& provided_item_info_to_rank = provided_item_info[i_rank]; + const size_t send_size = _getSubItemPerItemProvidedSize<item_type, sub_item_type>(connectivity, i_rank); + + Array<DataType> provided_data{send_size}; + size_t index = 0; + for (size_t i = 0; i < provided_item_info_to_rank.size(); ++i) { + const ItemId item_id = provided_item_info_to_rank[i]; + const auto item_values = sub_item_value_per_item.itemArray(item_id); + for (size_t j = 0; j < item_values.size(); ++j) { + provided_data[index++] = item_values[j]; + } + } + provided_data_list[i_rank] = provided_data; + } + + std::vector<Array<DataType>> requested_data_list(parallel::size()); + for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { + const size_t recv_size = _getSubItemPerItemRequestedSize<item_type, sub_item_type>(connectivity, i_rank); + requested_data_list[i_rank] = Array<DataType>{recv_size}; + } + + parallel::exchange(provided_data_list, requested_data_list); + for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { + const auto& requested_item_info_from_rank = requested_item_info[i_rank]; + const auto& requested_data = requested_data_list[i_rank]; + + size_t index = 0; + for (size_t i = 0; i < requested_item_info_from_rank.size(); ++i) { + const ItemId item_id = requested_item_info_from_rank[i]; + const auto item_values = sub_item_value_per_item.itemArray(item_id); + for (size_t j = 0; j < item_values.size(); ++j) { + item_values[j] = requested_data[index++]; + } + } + } + } else { + std::ostringstream os; + os << "synchronization requires sub-item type (" << itemName(ItemOfItem::sub_item_type) + << ") to be of lower dimension than item (" << itemName(ItemOfItem::item_type) << ")"; + throw UnexpectedError(os.str()); + } + } + + template <typename ConnectivityType, typename DataType, typename ItemOfItem, typename ConnectivityPtr> + PUGS_INLINE void + _synchronize(const ConnectivityType& connectivity, + SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_array_per_item) + { + static_assert(not std::is_abstract_v<ConnectivityType>, "_synchronize must be called on a concrete connectivity"); + if constexpr (ItemTypeId<ConnectivityType::Dimension>::dimension(ItemOfItem::item_type) > + ItemTypeId<ConnectivityType::Dimension>::dimension(ItemOfItem::sub_item_type)) { + constexpr ItemType item_type = ItemOfItem::item_type; + constexpr ItemType sub_item_type = ItemOfItem::sub_item_type; + + using ItemId = ItemIdT<item_type>; + + const auto& p_provided_item_info = this->_getProvidedItemInfo<item_type>(); + const auto& p_requested_item_info = this->_getRequestedItemInfo<item_type>(); + + Assert(static_cast<bool>(p_provided_item_info) == static_cast<bool>(p_requested_item_info)); + + if (not p_provided_item_info) { + this->_buildSynchronizeInfo<ConnectivityType, item_type>(connectivity); + } + + const auto& provided_item_info = *p_provided_item_info; + const auto& requested_item_info = *p_requested_item_info; + + Assert(requested_item_info.size() == provided_item_info.size()); + + std::vector<Array<const DataType>> provided_data_list(parallel::size()); + for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { + const Array<const ItemId>& provided_item_info_to_rank = provided_item_info[i_rank]; + const size_t send_size = _getSubItemPerItemProvidedSize<item_type, sub_item_type>(connectivity, i_rank); + + Array<DataType> provided_data{send_size * sub_item_array_per_item.sizeOfArrays()}; + size_t index = 0; + for (size_t i = 0; i < provided_item_info_to_rank.size(); ++i) { + const ItemId item_id = provided_item_info_to_rank[i]; + const auto item_table = sub_item_array_per_item.itemTable(item_id); + for (size_t j = 0; j < item_table.numberOfRows(); ++j) { + Assert(item_table.numberOfColumns() == sub_item_array_per_item.sizeOfArrays()); + for (size_t k = 0; k < sub_item_array_per_item.sizeOfArrays(); ++k) { + provided_data[index++] = item_table(j, k); + } + } + } + provided_data_list[i_rank] = provided_data; + } + + std::vector<Array<DataType>> requested_data_list(parallel::size()); + for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { + const size_t recv_size = _getSubItemPerItemRequestedSize<item_type, sub_item_type>(connectivity, i_rank); + requested_data_list[i_rank] = Array<DataType>{recv_size * sub_item_array_per_item.sizeOfArrays()}; + } + + parallel::exchange(provided_data_list, requested_data_list); + for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { + const auto& requested_item_info_from_rank = requested_item_info[i_rank]; + const auto& requested_data = requested_data_list[i_rank]; + + size_t index = 0; + for (size_t i = 0; i < requested_item_info_from_rank.size(); ++i) { + const ItemId item_id = requested_item_info_from_rank[i]; + const auto item_table = sub_item_array_per_item.itemTable(item_id); + for (size_t j = 0; j < item_table.numberOfRows(); ++j) { + Assert(item_table.numberOfColumns() == sub_item_array_per_item.sizeOfArrays()); + for (size_t k = 0; k < sub_item_array_per_item.sizeOfArrays(); ++k) { + item_table(j, k) = requested_data[index++]; + } + } + } + } + } else { + std::ostringstream os; + os << "synchronization requires sub-item type (" << itemName(ItemOfItem::sub_item_type) + << ") to be of lower dimension than item (" << itemName(ItemOfItem::item_type) << ")"; + throw UnexpectedError(os.str()); + } + } + public: template <typename DataType, ItemType item_type, typename ConnectivityPtr> PUGS_INLINE void @@ -260,22 +492,52 @@ class Synchronizer template <typename DataType, ItemType item_type, typename ConnectivityPtr> PUGS_INLINE void - synchronize(ItemArray<DataType, item_type, ConnectivityPtr>& item_value) + synchronize(ItemArray<DataType, item_type, ConnectivityPtr>& item_array) { - Assert(item_value.connectivity_ptr().use_count() > 0, "No connectivity is associated to this ItemValue"); - const IConnectivity& connectivity = *item_value.connectivity_ptr(); + Assert(item_array.connectivity_ptr().use_count() > 0, "No connectivity is associated to this ItemArray"); + const IConnectivity& connectivity = *item_array.connectivity_ptr(); switch (connectivity.dimension()) { case 1: { - this->_synchronize(static_cast<const Connectivity1D&>(connectivity), item_value); + this->_synchronize(static_cast<const Connectivity1D&>(connectivity), item_array); break; } case 2: { - this->_synchronize(static_cast<const Connectivity2D&>(connectivity), item_value); + this->_synchronize(static_cast<const Connectivity2D&>(connectivity), item_array); break; } case 3: { - this->_synchronize(static_cast<const Connectivity3D&>(connectivity), item_value); + this->_synchronize(static_cast<const Connectivity3D&>(connectivity), item_array); + break; + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("unexpected dimension"); + } + // LCOV_EXCL_STOP + } + } + + template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> + PUGS_INLINE void + synchronize(SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_value_per_item) + { + Assert(sub_item_value_per_item.connectivity_ptr().use_count() > 0, + "No connectivity is associated to this SubItemValuePerItem"); + + const IConnectivity& connectivity = *sub_item_value_per_item.connectivity_ptr(); + + switch (connectivity.dimension()) { + case 1: { + this->_synchronize(static_cast<const Connectivity1D&>(connectivity), sub_item_value_per_item); + break; + } + case 2: { + this->_synchronize(static_cast<const Connectivity2D&>(connectivity), sub_item_value_per_item); + break; + } + case 3: { + this->_synchronize(static_cast<const Connectivity3D&>(connectivity), sub_item_value_per_item); break; } // LCOV_EXCL_START @@ -286,6 +548,42 @@ class Synchronizer } } + template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> + PUGS_INLINE void + synchronize(SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_value_per_item) + { + Assert(sub_item_value_per_item.connectivity_ptr().use_count() > 0, + "No connectivity is associated to this SubItemValuePerItem"); + + const IConnectivity& connectivity = *sub_item_value_per_item.connectivity_ptr(); + + switch (connectivity.dimension()) { + case 1: { + this->_synchronize(static_cast<const Connectivity1D&>(connectivity), sub_item_value_per_item); + break; + } + case 2: { + this->_synchronize(static_cast<const Connectivity2D&>(connectivity), sub_item_value_per_item); + break; + } + case 3: { + this->_synchronize(static_cast<const Connectivity3D&>(connectivity), sub_item_value_per_item); + break; + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("unexpected dimension"); + } + // LCOV_EXCL_STOP + } + } + + Synchronizer(const Synchronizer&) = delete; + Synchronizer(Synchronizer&&) = delete; + + private: + friend class SynchronizerManager; + PUGS_INLINE Synchronizer() { @@ -300,13 +598,119 @@ class Synchronizer public: template <typename DataType, ItemType item_type, typename ConnectivityPtr> PUGS_INLINE void - synchronize(ItemValue<DataType, item_type, ConnectivityPtr>&) - {} + synchronize(ItemValue<DataType, item_type, ConnectivityPtr>& item_value) + { + Assert(item_value.connectivity_ptr().use_count() > 0, "No connectivity is associated to this ItemValue"); + } template <typename DataType, ItemType item_type, typename ConnectivityPtr> PUGS_INLINE void - synchronize(ItemArray<DataType, item_type, ConnectivityPtr>&) - {} + synchronize(ItemArray<DataType, item_type, ConnectivityPtr>& item_value) + { + Assert(item_value.connectivity_ptr().use_count() > 0, "No connectivity is associated to this ItemValue"); + } + + template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> + PUGS_INLINE void + synchronize(SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_value_per_item) + { + Assert(sub_item_value_per_item.connectivity_ptr().use_count() > 0, + "No connectivity is associated to this SubItemValuePerItem"); + + const IConnectivity& connectivity = *sub_item_value_per_item.connectivity_ptr(); + + switch (connectivity.dimension()) { + case 1: { + if constexpr (ItemTypeId<1>::dimension(ItemOfItem::item_type) <= + ItemTypeId<1>::dimension(ItemOfItem::sub_item_type)) { + std::ostringstream os; + os << "synchronization requires sub-item type (" << itemName(ItemOfItem::sub_item_type) + << ") to be of lower dimension than item (" << itemName(ItemOfItem::item_type) << ")"; + throw UnexpectedError(os.str()); + } + break; + } + case 2: { + if constexpr (ItemTypeId<2>::dimension(ItemOfItem::item_type) <= + ItemTypeId<2>::dimension(ItemOfItem::sub_item_type)) { + std::ostringstream os; + os << "synchronization requires sub-item type (" << itemName(ItemOfItem::sub_item_type) + << ") to be of lower dimension than item (" << itemName(ItemOfItem::item_type) << ")"; + throw UnexpectedError(os.str()); + } + break; + } + case 3: { + if constexpr (ItemTypeId<3>::dimension(ItemOfItem::item_type) <= + ItemTypeId<3>::dimension(ItemOfItem::sub_item_type)) { + std::ostringstream os; + os << "synchronization requires sub-item type (" << itemName(ItemOfItem::sub_item_type) + << ") to be of lower dimension than item (" << itemName(ItemOfItem::item_type) << ")"; + throw UnexpectedError(os.str()); + } + break; + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("unexpected dimension"); + } + // LCOV_EXCL_STOP + } + } + + template <typename DataType, typename ItemOfItem, typename ConnectivityPtr> + PUGS_INLINE void + synchronize(SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& sub_item_array_per_item) + { + Assert(sub_item_array_per_item.connectivity_ptr().use_count() > 0, + "No connectivity is associated to this SubItemArrayPerItem"); + + const IConnectivity& connectivity = *sub_item_array_per_item.connectivity_ptr(); + + switch (connectivity.dimension()) { + case 1: { + if constexpr (ItemTypeId<1>::dimension(ItemOfItem::item_type) <= + ItemTypeId<1>::dimension(ItemOfItem::sub_item_type)) { + std::ostringstream os; + os << "synchronization requires sub-item type (" << itemName(ItemOfItem::sub_item_type) + << ") to be of lower dimension than item (" << itemName(ItemOfItem::item_type) << ")"; + throw UnexpectedError(os.str()); + } + break; + } + case 2: { + if constexpr (ItemTypeId<2>::dimension(ItemOfItem::item_type) <= + ItemTypeId<2>::dimension(ItemOfItem::sub_item_type)) { + std::ostringstream os; + os << "synchronization requires sub-item type (" << itemName(ItemOfItem::sub_item_type) + << ") to be of lower dimension than item (" << itemName(ItemOfItem::item_type) << ")"; + throw UnexpectedError(os.str()); + } + break; + } + case 3: { + if constexpr (ItemTypeId<3>::dimension(ItemOfItem::item_type) <= + ItemTypeId<3>::dimension(ItemOfItem::sub_item_type)) { + std::ostringstream os; + os << "synchronization requires sub-item type (" << itemName(ItemOfItem::sub_item_type) + << ") to be of lower dimension than item (" << itemName(ItemOfItem::item_type) << ")"; + throw UnexpectedError(os.str()); + } + break; + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("unexpected dimension"); + } + // LCOV_EXCL_STOP + } + } + + Synchronizer(const Synchronizer&) = delete; + Synchronizer(Synchronizer&&) = delete; + + private: + friend class SynchronizerManager; PUGS_INLINE Synchronizer() diff --git a/src/mesh/SynchronizerManager.cpp b/src/mesh/SynchronizerManager.cpp index a72191fd3bfc001c38e577384115bffaa441633c..45f2d17a4630a5268f8e7ba48cda9605235e0db7 100644 --- a/src/mesh/SynchronizerManager.cpp +++ b/src/mesh/SynchronizerManager.cpp @@ -45,7 +45,7 @@ SynchronizerManager::getConnectivitySynchronizer(const IConnectivity* connectivi connectivity_synchronizer != m_connectivity_synchronizer_map.end()) { return (*connectivity_synchronizer->second); } else { - std::shared_ptr synchronizer = std::make_shared<Synchronizer>(); + std::shared_ptr<Synchronizer> synchronizer(new Synchronizer); m_connectivity_synchronizer_map[connectivity] = synchronizer; return *synchronizer; } diff --git a/src/scheme/AcousticSolver.cpp b/src/scheme/AcousticSolver.cpp index 098b146e55efe52ffc7297b8ebb841206d9e3d43..db0a20dca4e640878a1c6e842b025bd32676a8ff 100644 --- a/src/scheme/AcousticSolver.cpp +++ b/src/scheme/AcousticSolver.cpp @@ -195,7 +195,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { Rdxd sum = zero; const auto& node_to_cell = node_to_cell_matrix[r]; - const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemValues(r); + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(r); for (size_t j = 0; j < node_to_cell.size(); ++j) { const CellId J = node_to_cell[j]; @@ -226,7 +226,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler parallel_for( mesh.numberOfNodes(), PUGS_LAMBDA(NodeId r) { const auto& node_to_cell = node_to_cell_matrix[r]; - const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemValues(r); + const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(r); Rd br = zero; for (size_t j = 0; j < node_to_cell.size(); ++j) { diff --git a/src/utils/Array.hpp b/src/utils/Array.hpp index f78f543080edcfa614e6ad549a73349ffb66da74..ca8a6ea6b349b2918c54ccd8de354bff8e999a6f 100644 --- a/src/utils/Array.hpp +++ b/src/utils/Array.hpp @@ -189,6 +189,9 @@ min(const Array<DataType>& array) using data_type = std::remove_const_t<typename ArrayType::data_type>; using index_type = typename ArrayType::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 ArrayMin { private: @@ -249,6 +252,9 @@ max(const Array<DataType>& array) using data_type = std::remove_const_t<typename ArrayType::data_type>; using index_type = typename ArrayType::index_type; + static_assert(std::is_arithmetic_v<data_type>, "max 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 ArrayMax { private: @@ -309,6 +315,8 @@ sum(const Array<DataType>& array) using data_type = std::remove_const_t<DataType>; using index_type = typename ArrayType::index_type; + static_assert(not std::is_same_v<data_type, bool>, "sum cannot be called on boolean data"); + class ArraySum { private: diff --git a/src/utils/Messenger.hpp b/src/utils/Messenger.hpp index 44772529e8539814eb30b36dad7b936e4c0269a6..416dbdf1d65e8b028b9ed39fff2a2acb4ebcb2d5 100644 --- a/src/utils/Messenger.hpp +++ b/src/utils/Messenger.hpp @@ -784,8 +784,9 @@ class Messenger for (size_t i = 0; i < m_size; ++i) { correct_sizes &= (recv_size[i] == recv_array_list[i].size()); } - Assert(correct_sizes); // LCOV_EXCL_LINE -#endif // NDEBUG + Assert(correct_sizes, "incompatible send/recv messages length"); // LCOV_EXCL_LINE + +#endif // NDEBUG if constexpr (std::is_arithmetic_v<DataType>) { _exchange(send_array_list, recv_array_list); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ddfa977a6ef8ca76df9f27bbdc2bda86395cc67f..cbd6b32bc1010a44fe13ed71cb55d03d10052895 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -173,7 +173,9 @@ add_executable (mpi_unit_tests test_Partitioner.cpp test_RandomEngine.cpp test_SubItemValuePerItem.cpp + test_SubItemValuePerItemUtils.cpp test_SubItemArrayPerItem.cpp + test_SubItemArrayPerItemUtils.cpp test_Synchronizer.cpp ) diff --git a/tests/test_ItemArrayUtils.cpp b/tests/test_ItemArrayUtils.cpp index af91ecb70897073a4069f20457190b6e20318e72..99958839ce3ce945945d0156ff3243ae1ebd5b85 100644 --- a/tests/test_ItemArrayUtils.cpp +++ b/tests/test_ItemArrayUtils.cpp @@ -2,15 +2,14 @@ #include <catch2/matchers/catch_matchers_all.hpp> #include <MeshDataBaseForTests.hpp> +#include <algebra/TinyMatrix.hpp> +#include <algebra/TinyVector.hpp> #include <mesh/Connectivity.hpp> #include <mesh/ItemArray.hpp> #include <mesh/ItemArrayUtils.hpp> #include <mesh/Mesh.hpp> #include <utils/Messenger.hpp> -// Instantiate to ensure full coverage is performed -template class ItemArray<int, ItemType::cell>; - // clazy:excludeall=non-pod-global-static TEST_CASE("ItemArrayUtils", "[mesh]") @@ -68,6 +67,9 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_valid); REQUIRE(not is_synchronized); + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(node_array)); + } } synchronize(weak_node_array); @@ -86,6 +88,7 @@ TEST_CASE("ItemArrayUtils", "[mesh]") } REQUIRE(is_synchronized); + REQUIRE(isSynchronized(node_array)); } } @@ -129,6 +132,9 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_valid); REQUIRE(not is_synchronized); + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(edge_array)); + } } synchronize(weak_edge_array); @@ -148,6 +154,7 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_synchronized); } + REQUIRE(isSynchronized(edge_array)); } SECTION("face") @@ -190,6 +197,9 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_valid); REQUIRE(not is_synchronized); + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(face_array)); + } } synchronize(weak_face_array); @@ -209,6 +219,7 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_synchronized); } + REQUIRE(isSynchronized(face_array)); } SECTION("cell") @@ -251,6 +262,9 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_valid); REQUIRE(not is_synchronized); + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(cell_array)); + } } synchronize(weak_cell_array); @@ -270,6 +284,7 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_synchronized); } + REQUIRE(isSynchronized(cell_array)); } } } @@ -326,6 +341,9 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_valid); REQUIRE(not is_synchronized); + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(node_array)); + } } synchronize(weak_node_array); @@ -344,6 +362,7 @@ TEST_CASE("ItemArrayUtils", "[mesh]") } REQUIRE(is_synchronized); + REQUIRE(isSynchronized(node_array)); } } @@ -387,6 +406,9 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_valid); REQUIRE(not is_synchronized); + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(edge_array)); + } } synchronize(weak_edge_array); @@ -406,6 +428,7 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_synchronized); } + REQUIRE(isSynchronized(edge_array)); } SECTION("face") @@ -448,6 +471,9 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_valid); REQUIRE(not is_synchronized); + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(face_array)); + } } synchronize(weak_face_array); @@ -467,6 +493,7 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_synchronized); } + REQUIRE(isSynchronized(face_array)); } SECTION("cell") @@ -509,6 +536,9 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_valid); REQUIRE(not is_synchronized); + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(cell_array)); + } } synchronize(weak_cell_array); @@ -528,6 +558,7 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_synchronized); } + REQUIRE(isSynchronized(cell_array)); } } } @@ -584,6 +615,9 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_valid); REQUIRE(not is_synchronized); + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(node_array)); + } } synchronize(weak_node_array); @@ -603,6 +637,7 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_synchronized); } + REQUIRE(isSynchronized(node_array)); } SECTION("edge") @@ -645,6 +680,9 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_valid); REQUIRE(not is_synchronized); + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(edge_array)); + } } synchronize(weak_edge_array); @@ -664,6 +702,7 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_synchronized); } + REQUIRE(isSynchronized(edge_array)); } SECTION("face") @@ -706,6 +745,9 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_valid); REQUIRE(not is_synchronized); + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(face_array)); + } } synchronize(weak_face_array); @@ -725,6 +767,7 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_synchronized); } + REQUIRE(isSynchronized(face_array)); } SECTION("cell") @@ -767,6 +810,9 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_valid); REQUIRE(not is_synchronized); + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(cell_array)); + } } synchronize(weak_cell_array); @@ -786,9 +832,331 @@ TEST_CASE("ItemArrayUtils", "[mesh]") REQUIRE(is_synchronized); } + REQUIRE(isSynchronized(cell_array)); } } } } } + + 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(); + + CellArray<int> cell_array{connectivity, 3}; + cell_array.fill(-1); + + auto cell_is_owned = connectivity.cellIsOwned(); + parallel_for( + mesh_1d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + if (cell_is_owned[cell_id]) { + for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) { + cell_array[cell_id][i] = 10 + parallel::rank() + i; + } + } + }); + + REQUIRE(min(cell_array) == 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(); + + CellArray<int> cell_array{connectivity, 3}; + cell_array.fill(-1); + + auto cell_is_owned = connectivity.cellIsOwned(); + parallel_for( + mesh_2d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + if (cell_is_owned[cell_id]) { + for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) { + cell_array[cell_id][i] = 10 + parallel::rank() - i; + } + } + }); + + REQUIRE(min(cell_array) == 8); + } + } + } + + 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(); + + CellArray<int> cell_array{connectivity, 3}; + cell_array.fill(-1); + + auto cell_is_owned = connectivity.cellIsOwned(); + parallel_for( + mesh_3d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + if (cell_is_owned[cell_id]) { + for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) { + cell_array[cell_id][i] = 10 + parallel::rank() + 2 - i; + } + } + }); + + REQUIRE(min(cell_array) == 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(); + + CellArray<size_t> cell_array{connectivity, 3}; + cell_array.fill(std::numeric_limits<size_t>::max()); + + auto cell_is_owned = connectivity.cellIsOwned(); + parallel_for( + mesh_1d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + if (cell_is_owned[cell_id]) { + for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) { + cell_array[cell_id][i] = parallel::rank() + 1 + i; + } + } + }); + + REQUIRE(max(cell_array) == parallel::size() + 2); + } + } + } + + 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(); + + CellArray<size_t> cell_array{connectivity, 3}; + cell_array.fill(std::numeric_limits<size_t>::max()); + + auto cell_is_owned = connectivity.cellIsOwned(); + parallel_for( + mesh_2d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + if (cell_is_owned[cell_id]) { + for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) { + cell_array[cell_id][i] = parallel::rank() + i; + } + } + }); + + REQUIRE(max(cell_array) == parallel::size() + 1); + } + } + } + + 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(); + + CellArray<size_t> cell_array{connectivity, 2}; + cell_array.fill(std::numeric_limits<size_t>::max()); + + auto cell_is_owned = connectivity.cellIsOwned(); + parallel_for( + mesh_3d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { + if (cell_is_owned[cell_id]) { + for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) { + cell_array[cell_id][i] = parallel::rank() + 1 + i; + } + } + }); + + REQUIRE(max(cell_array) == parallel::size() + 1); + } + } + } + } + + 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(); + + CellArray<size_t> cell_array{connectivity, 3}; + cell_array.fill(5); + + auto cell_is_owned = connectivity.cellIsOwned(); + + const size_t global_number_of_cells = [&] { + size_t number_of_cells = 0; + for (CellId cell_id = 0; cell_id < cell_is_owned.numberOfItems(); ++cell_id) { + number_of_cells += cell_is_owned[cell_id]; + } + return parallel::allReduceSum(number_of_cells); + }(); + + REQUIRE(sum(cell_array) == 5 * cell_array.sizeOfArrays() * global_number_of_cells); + } + } + } + + 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(); + + FaceArray<size_t> face_array{connectivity, 3}; + face_array.fill(2); + + auto face_is_owned = connectivity.faceIsOwned(); + + const size_t global_number_of_faces = [&] { + size_t number_of_faces = 0; + for (FaceId face_id = 0; face_id < face_is_owned.numberOfItems(); ++face_id) { + number_of_faces += face_is_owned[face_id]; + } + return parallel::allReduceSum(number_of_faces); + }(); + + REQUIRE(sum(face_array) == 2 * face_array.sizeOfArrays() * global_number_of_faces); + } + + 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>; + FaceArray<N2> face_array{connectivity, 3}; + + const N2 data(3, 1); + face_array.fill(data); + + auto face_is_owned = connectivity.faceIsOwned(); + + const size_t global_number_of_faces = [&] { + size_t number_of_faces = 0; + for (FaceId face_id = 0; face_id < face_is_owned.numberOfItems(); ++face_id) { + number_of_faces += face_is_owned[face_id]; + } + return parallel::allReduceSum(number_of_faces); + }(); + + REQUIRE(sum(face_array) == face_array.sizeOfArrays() * global_number_of_faces * 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(); + + NodeArray<size_t> node_array{connectivity, 3}; + node_array.fill(3); + + auto node_is_owned = connectivity.nodeIsOwned(); + + const size_t global_number_of_nodes = [&] { + size_t number_of_nodes = 0; + for (NodeId node_id = 0; node_id < node_is_owned.numberOfItems(); ++node_id) { + number_of_nodes += node_is_owned[node_id]; + } + return parallel::allReduceSum(number_of_nodes); + }(); + + REQUIRE(sum(node_array) == 3 * node_array.sizeOfArrays() * global_number_of_nodes); + } + + SECTION(named_mesh.name() + " for N^3x2 data") + { + auto mesh_3d = named_mesh.mesh(); + + const Connectivity<3>& connectivity = mesh_3d->connectivity(); + + using N3x2 = TinyMatrix<3, 2, size_t>; + + NodeArray<N3x2> node_array{connectivity, 3}; + + const N3x2 data(1, 3, 2, 4, 6, 5); + node_array.fill(data); + + auto node_is_owned = connectivity.nodeIsOwned(); + + const size_t global_number_of_nodes = [&] { + size_t number_of_nodes = 0; + for (NodeId node_id = 0; node_id < node_is_owned.numberOfItems(); ++node_id) { + number_of_nodes += node_is_owned[node_id]; + } + return parallel::allReduceSum(number_of_nodes); + }(); + + REQUIRE(sum(node_array) == node_array.sizeOfArrays() * global_number_of_nodes * data); + } + } + } + } } diff --git a/tests/test_ItemType.cpp b/tests/test_ItemType.cpp index d560a7098ee0e2cec80a1efb64ab599df5ef67ec..ba5d7fe603c4dfe148c2b7e209742da3e5c6cfe0 100644 --- a/tests/test_ItemType.cpp +++ b/tests/test_ItemType.cpp @@ -30,24 +30,39 @@ TEST_CASE("ItemType", "[connectivity]") SECTION("checking for item ids in 1d") { REQUIRE(ItemTypeId<1>::itemTId(cell_type) == 0); - REQUIRE(ItemTypeId<1>::itemTId(edge_type) == 1); REQUIRE(ItemTypeId<1>::itemTId(face_type) == 1); + REQUIRE(ItemTypeId<1>::itemTId(edge_type) == 1); REQUIRE(ItemTypeId<1>::itemTId(node_type) == 1); + + REQUIRE(ItemTypeId<1>::dimension(cell_type) == 1); + REQUIRE(ItemTypeId<1>::dimension(face_type) == 0); + REQUIRE(ItemTypeId<1>::dimension(edge_type) == 0); + REQUIRE(ItemTypeId<1>::dimension(node_type) == 0); } SECTION("checking for item ids in 2d") { REQUIRE(ItemTypeId<2>::itemTId(cell_type) == 0); - REQUIRE(ItemTypeId<2>::itemTId(edge_type) == 1); REQUIRE(ItemTypeId<2>::itemTId(face_type) == 1); + REQUIRE(ItemTypeId<2>::itemTId(edge_type) == 1); REQUIRE(ItemTypeId<2>::itemTId(node_type) == 2); + + REQUIRE(ItemTypeId<2>::dimension(cell_type) == 2); + REQUIRE(ItemTypeId<2>::dimension(face_type) == 1); + REQUIRE(ItemTypeId<2>::dimension(edge_type) == 1); + REQUIRE(ItemTypeId<2>::dimension(node_type) == 0); } SECTION("checking for item ids in 3d") { REQUIRE(ItemTypeId<3>::itemTId(cell_type) == 0); - REQUIRE(ItemTypeId<3>::itemTId(edge_type) == 1); - REQUIRE(ItemTypeId<3>::itemTId(face_type) == 2); + REQUIRE(ItemTypeId<3>::itemTId(face_type) == 1); + REQUIRE(ItemTypeId<3>::itemTId(edge_type) == 2); REQUIRE(ItemTypeId<3>::itemTId(node_type) == 3); + + REQUIRE(ItemTypeId<3>::dimension(cell_type) == 3); + REQUIRE(ItemTypeId<3>::dimension(face_type) == 2); + REQUIRE(ItemTypeId<3>::dimension(edge_type) == 1); + REQUIRE(ItemTypeId<3>::dimension(node_type) == 0); } } diff --git a/tests/test_ItemValueUtils.cpp b/tests/test_ItemValueUtils.cpp index bee143e751de72affa1134998d8561a997bc7f12..42ed106fde72ca0a04c05ab25956c5807475f51c 100644 --- a/tests/test_ItemValueUtils.cpp +++ b/tests/test_ItemValueUtils.cpp @@ -2,15 +2,14 @@ #include <catch2/matchers/catch_matchers_all.hpp> #include <MeshDataBaseForTests.hpp> +#include <algebra/TinyMatrix.hpp> +#include <algebra/TinyVector.hpp> #include <mesh/Connectivity.hpp> #include <mesh/ItemValue.hpp> #include <mesh/ItemValueUtils.hpp> #include <mesh/Mesh.hpp> #include <utils/Messenger.hpp> -// Instantiate to ensure full coverage is performed -template class ItemValue<int, ItemType::cell>; - // clazy:excludeall=non-pod-global-static TEST_CASE("ItemValueUtils", "[mesh]") @@ -47,6 +46,10 @@ TEST_CASE("ItemValueUtils", "[mesh]") } } + if (parallel::size() > 1) { + REQUIRE(not isSynchronized(face_value)); + } + synchronize(weak_face_value); { // after synchronization @@ -57,6 +60,7 @@ TEST_CASE("ItemValueUtils", "[mesh]") REQUIRE(face_owner[i_face] == face_value[i_face]); } } + REQUIRE(isSynchronized(face_value)); } } } @@ -265,7 +269,7 @@ TEST_CASE("ItemValueUtils", "[mesh]") std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes(); for (auto named_mesh : mesh_list) { - SECTION(named_mesh.name()) + SECTION(named_mesh.name() + "for size_t data") { auto mesh_2d = named_mesh.mesh(); @@ -286,6 +290,30 @@ TEST_CASE("ItemValueUtils", "[mesh]") REQUIRE(sum(face_value) == 2 * global_number_of_faces); } + + SECTION(named_mesh.name() + "for N^3 data") + { + auto mesh_2d = named_mesh.mesh(); + + const Connectivity<2>& connectivity = mesh_2d->connectivity(); + + using N3 = TinyVector<3, size_t>; + FaceValue<N3> face_value{connectivity}; + const N3 data(2, 1, 4); + face_value.fill(data); + + auto face_is_owned = connectivity.faceIsOwned(); + + const size_t global_number_of_faces = [&] { + size_t number_of_faces = 0; + for (FaceId face_id = 0; face_id < face_is_owned.numberOfItems(); ++face_id) { + number_of_faces += face_is_owned[face_id]; + } + return parallel::allReduceSum(number_of_faces); + }(); + + REQUIRE(sum(face_value) == global_number_of_faces * data); + } } } @@ -294,7 +322,7 @@ TEST_CASE("ItemValueUtils", "[mesh]") std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes(); for (auto named_mesh : mesh_list) { - SECTION(named_mesh.name()) + SECTION(named_mesh.name() + " for size_t data") { auto mesh_3d = named_mesh.mesh(); @@ -315,6 +343,32 @@ TEST_CASE("ItemValueUtils", "[mesh]") REQUIRE(sum(node_value) == 3 * global_number_of_nodes); } + + SECTION(named_mesh.name() + " for N^3x2 data") + { + auto mesh_3d = named_mesh.mesh(); + + const Connectivity<3>& connectivity = mesh_3d->connectivity(); + + using N3x2 = TinyMatrix<3, 2, size_t>; + + NodeValue<N3x2> node_value{connectivity}; + const N3x2 data(3, 6, 1, 4, 5, 7); + + node_value.fill(data); + + auto node_is_owned = connectivity.nodeIsOwned(); + + const size_t global_number_of_nodes = [&] { + size_t number_of_nodes = 0; + for (NodeId node_id = 0; node_id < node_is_owned.numberOfItems(); ++node_id) { + number_of_nodes += node_is_owned[node_id]; + } + return parallel::allReduceSum(number_of_nodes); + }(); + + REQUIRE(sum(node_value) == global_number_of_nodes * data); + } } } } diff --git a/tests/test_SubItemArrayPerItemUtils.cpp b/tests/test_SubItemArrayPerItemUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d3f29a3814fb88cc1fe75c06214dd463ac406525 --- /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); + } + } + } + } +} diff --git a/tests/test_SubItemValuePerItem.cpp b/tests/test_SubItemValuePerItem.cpp index 4063133a30f71e2304d865fb1250e975470c7ed0..dda38918acdb156f8a7c5de552d1f82319463c97 100644 --- a/tests/test_SubItemValuePerItem.cpp +++ b/tests/test_SubItemValuePerItem.cpp @@ -85,7 +85,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { is_correct &= (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id)) and - (node_value_per_cell.itemValues(cell_id).size() == node_value_per_cell.numberOfSubValues(cell_id)); + (node_value_per_cell.itemArray(cell_id).size() == node_value_per_cell.numberOfSubValues(cell_id)); } REQUIRE(is_correct); } @@ -94,7 +94,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") { bool is_correct = true; for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { - is_correct &= (const_node_value_per_cell.itemValues(cell_id).size() == + is_correct &= (const_node_value_per_cell.itemArray(cell_id).size() == node_value_per_cell.numberOfSubValues(cell_id)); } REQUIRE(is_correct); @@ -624,7 +624,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") size_t value = 0; for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) { for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face) { - face_values_per_node.itemValues(node_id)[i_face] = value++; + face_values_per_node.itemArray(node_id)[i_face] = value++; } } } @@ -644,7 +644,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") size_t i = 0; for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) { for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face, ++i) { - is_same &= (face_values_per_node.itemValues(node_id)[i_face] == 3 + i * i); + is_same &= (face_values_per_node.itemArray(node_id)[i_face] == 3 + i * i); } } REQUIRE(is_same); @@ -673,7 +673,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") size_t value = 0; for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) { - node_value_per_cell.itemValues(cell_id)[i_node] = value++; + node_value_per_cell.itemArray(cell_id)[i_node] = value++; } } } @@ -692,7 +692,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") { for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) { - node_value_per_cell.itemValues(cell_id)[i_node] = i_node; + node_value_per_cell.itemArray(cell_id)[i_node] = i_node; } } } @@ -715,7 +715,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") size_t value = 0; for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) { - node_value_per_cell.itemValues(cell_id)[i_node] = value++; + node_value_per_cell.itemArray(cell_id)[i_node] = value++; } } } @@ -735,7 +735,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") { for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) { for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) { - node_value_per_cell.itemValues(cell_id)[i_node] = i_node; + node_value_per_cell.itemArray(cell_id)[i_node] = i_node; } } } @@ -790,7 +790,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") { CellValuePerNode<int> cell_value_per_node; REQUIRE_THROWS_AS(cell_value_per_node[0], AssertError); - REQUIRE_THROWS_AS(cell_value_per_node.itemValues(NodeId{0}), AssertError); + REQUIRE_THROWS_AS(cell_value_per_node.itemArray(NodeId{0}), AssertError); REQUIRE_THROWS_AS(cell_value_per_node(NodeId{0}, 0), AssertError); REQUIRE_THROWS_AS(cell_value_per_node.numberOfValues(), AssertError); REQUIRE_THROWS_AS(cell_value_per_node.numberOfItems(), AssertError); @@ -798,7 +798,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") FaceValuePerCell<int> face_value_per_cell; REQUIRE_THROWS_AS(face_value_per_cell[0], AssertError); - REQUIRE_THROWS_AS(face_value_per_cell.itemValues(CellId{0}), AssertError); + REQUIRE_THROWS_AS(face_value_per_cell.itemArray(CellId{0}), AssertError); REQUIRE_THROWS_AS(face_value_per_cell(CellId{0}, 0), AssertError); REQUIRE_THROWS_AS(face_value_per_cell.numberOfValues(), AssertError); REQUIRE_THROWS_AS(face_value_per_cell.numberOfItems(), AssertError); @@ -806,7 +806,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") CellValuePerEdge<int> cell_value_per_edge; REQUIRE_THROWS_AS(cell_value_per_edge[0], AssertError); - REQUIRE_THROWS_AS(cell_value_per_edge.itemValues(EdgeId{0}), AssertError); + REQUIRE_THROWS_AS(cell_value_per_edge.itemArray(EdgeId{0}), AssertError); REQUIRE_THROWS_AS(cell_value_per_edge(EdgeId{0}, 0), AssertError); REQUIRE_THROWS_AS(cell_value_per_edge.numberOfValues(), AssertError); REQUIRE_THROWS_AS(cell_value_per_edge.numberOfItems(), AssertError); @@ -814,7 +814,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") NodeValuePerFace<int> node_value_per_face; REQUIRE_THROWS_AS(node_value_per_face[0], AssertError); - REQUIRE_THROWS_AS(node_value_per_face.itemValues(FaceId{0}), AssertError); + REQUIRE_THROWS_AS(node_value_per_face.itemArray(FaceId{0}), AssertError); REQUIRE_THROWS_AS(node_value_per_face(FaceId{0}, 0), AssertError); REQUIRE_THROWS_AS(node_value_per_face.numberOfValues(), AssertError); REQUIRE_THROWS_AS(node_value_per_face.numberOfItems(), AssertError); @@ -839,10 +839,10 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") } if (connectivity.numberOfFaces() > 0) { FaceId face_id = 0; - const auto& cell_values = cell_value_per_face.itemValues(face_id); + const auto& cell_values = cell_value_per_face.itemArray(face_id); REQUIRE_THROWS_AS(cell_value_per_face(face_id, cell_values.size()), AssertError); REQUIRE_THROWS_AS(cell_values[cell_values.size()], AssertError); - REQUIRE_THROWS_AS(cell_value_per_face.itemValues(face_id)[cell_values.size()] = 2, AssertError); + REQUIRE_THROWS_AS(cell_value_per_face.itemArray(face_id)[cell_values.size()] = 2, AssertError); } FaceValuePerNode<int> face_value_per_node{connectivity}; @@ -852,10 +852,10 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") } if (connectivity.numberOfNodes() > 0) { NodeId node_id = 0; - const auto& face_values = face_value_per_node.itemValues(node_id); + const auto& face_values = face_value_per_node.itemArray(node_id); REQUIRE_THROWS_AS(face_value_per_node(node_id, face_values.size()), AssertError); REQUIRE_THROWS_AS(face_values[face_values.size()], AssertError); - REQUIRE_THROWS_AS(face_value_per_node.itemValues(node_id)[face_values.size()] = 2, AssertError); + REQUIRE_THROWS_AS(face_value_per_node.itemArray(node_id)[face_values.size()] = 2, AssertError); } EdgeValuePerCell<int> edge_value_per_cell{connectivity}; @@ -865,10 +865,10 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") } if (connectivity.numberOfCells() > 0) { CellId cell_id = 0; - const auto& edge_values = edge_value_per_cell.itemValues(cell_id); + const auto& edge_values = edge_value_per_cell.itemArray(cell_id); REQUIRE_THROWS_AS(edge_value_per_cell(cell_id, edge_values.size()), AssertError); REQUIRE_THROWS_AS(edge_values[edge_values.size()], AssertError); - REQUIRE_THROWS_AS(edge_value_per_cell.itemValues(cell_id)[edge_values.size()] = 2, AssertError); + REQUIRE_THROWS_AS(edge_value_per_cell.itemArray(cell_id)[edge_values.size()] = 2, AssertError); } NodeValuePerEdge<int> node_value_per_edge{connectivity}; @@ -878,10 +878,10 @@ TEST_CASE("SubItemValuePerItem", "[mesh]") } if (connectivity.numberOfEdges() > 0) { EdgeId edge_id = 0; - const auto& node_values = node_value_per_edge.itemValues(edge_id); + const auto& node_values = node_value_per_edge.itemArray(edge_id); REQUIRE_THROWS_AS(node_value_per_edge(edge_id, node_values.size()), AssertError); REQUIRE_THROWS_AS(node_values[node_values.size()], AssertError); - REQUIRE_THROWS_AS(node_value_per_edge.itemValues(edge_id)[node_values.size()] = 2, AssertError); + REQUIRE_THROWS_AS(node_value_per_edge.itemArray(edge_id)[node_values.size()] = 2, AssertError); } } } diff --git a/tests/test_SubItemValuePerItemUtils.cpp b/tests/test_SubItemValuePerItemUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ccda4722d7f5ac6c56bacb0d94c2ed0689166eec --- /dev/null +++ b/tests/test_SubItemValuePerItemUtils.cpp @@ -0,0 +1,409 @@ +#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/SubItemValuePerItem.hpp> +#include <mesh/SubItemValuePerItemUtils.hpp> +#include <utils/Messenger.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("SubItemValuePerItemUtils", "[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(); + + NodeValuePerFace<int> weak_node_value_per_face{connectivity}; + + for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) { + auto face_array = weak_node_value_per_face.itemArray(face_id); + for (size_t i_node = 0; i_node < face_array.size(); ++i_node) { + face_array[i_node] = parallel::rank() + 2 * i_node; + } + } + + NodeValuePerFace<const int> node_value_per_face{weak_node_value_per_face}; + + REQUIRE(node_value_per_face.connectivity_ptr() == weak_node_value_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_array = node_value_per_face.itemArray(face_id); + for (size_t i_node = 0; i_node < face_array.size(); ++i_node) { + if (face_array[i_node] != static_cast<int>(face_owner[face_id] + 2 * i_node)) { + is_synchronized = false; + break; + } + } + } + + REQUIRE(not is_synchronized); + REQUIRE(not isSynchronized(node_value_per_face)); + } + + synchronize(weak_node_value_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_array = node_value_per_face.itemArray(face_id); + for (size_t i_node = 0; i_node < face_array.size(); ++i_node) { + if (face_array[i_node] != static_cast<int>(face_owner[face_id] + 2 * i_node)) { + is_synchronized = false; + break; + } + } + } + REQUIRE(is_synchronized); + } + REQUIRE(isSynchronized(node_value_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(); + + NodeValuePerCell<int> node_value_per_cell{connectivity}; + node_value_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_array = node_value_per_cell.itemArray(cell_id); + for (size_t i = 0; i < cell_array.size(); ++i) { + cell_array[i] = 10 + parallel::rank() + i; + } + } + }); + + REQUIRE(min(node_value_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(); + + FaceValuePerCell<int> face_value_per_cell{connectivity}; + face_value_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_array = face_value_per_cell.itemArray(cell_id); + for (size_t i = 0; i < cell_array.size(); ++i) { + cell_array[i] = 10 + parallel::rank() + i; + } + } + }); + + REQUIRE(min(face_value_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(); + + EdgeValuePerFace<int> edge_value_per_face{connectivity}; + edge_value_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_array = edge_value_per_face.itemArray(face_id); + for (size_t i = 0; i < face_array.size(); ++i) { + face_array[i] = 10 + parallel::rank() + i; + } + } + }); + + REQUIRE(min(edge_value_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(); + + EdgeValuePerCell<size_t> edge_value_per_cell{connectivity}; + edge_value_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_array = edge_value_per_cell.itemArray(cell_id); + for (size_t i = 0; i < cell_array.size(); ++i) { + cell_array[i] = 10 + parallel::rank() - i; + } + } + }); + + REQUIRE(max(edge_value_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(); + + EdgeValuePerCell<size_t> edge_value_per_cell{connectivity}; + edge_value_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_array = edge_value_per_cell.itemArray(cell_id); + for (size_t i = 0; i < cell_array.size(); ++i) { + cell_array[i] = 10 + parallel::rank() - i; + } + } + }); + + REQUIRE(max(edge_value_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(); + + CellValue<size_t> cell_value{connectivity}; + + NodeValuePerEdge<size_t> node_value_per_edge{connectivity}; + node_value_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_array = node_value_per_edge.itemArray(edge_id); + for (size_t i = 0; i < edge_array.size(); ++i) { + edge_array[i] = 10 + parallel::rank() - i; + } + } + }); + + REQUIRE(max(node_value_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(); + + NodeValuePerCell<size_t> node_value_per_cell{connectivity}; + node_value_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_value_per_cell.numberOfSubValues(cell_id); + } + return parallel::allReduceSum(number_of_nodes_per_cells); + }(); + + REQUIRE(sum(node_value_per_cell) == 5 * global_number_of_nodes_per_cells); + } + } + } + + 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(); + + NodeValuePerFace<size_t> node_value_per_face{connectivity}; + node_value_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_value_per_face.numberOfSubValues(face_id); + } + return parallel::allReduceSum(number_of_nodes_per_faces); + }(); + + REQUIRE(sum(node_value_per_face) == 2 * global_number_of_nodes_per_faces); + } + + 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>; + NodeValuePerFace<N2> node_value_per_face{connectivity}; + + const N2 data(3, 2); + node_value_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_value_per_face.numberOfSubValues(face_id); + } + return parallel::allReduceSum(number_of_nodes_per_faces); + }(); + + REQUIRE(sum(node_value_per_face) == global_number_of_nodes_per_faces * 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(); + + EdgeValuePerFace<size_t> edge_value_per_face{connectivity}; + edge_value_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_value_per_face.numberOfSubValues(face_id); + } + return parallel::allReduceSum(number_of_edges_per_faces); + }(); + + REQUIRE(sum(edge_value_per_face) == 3 * global_number_of_edges_per_faces); + } + + 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>; + EdgeValuePerFace<N2x3> edge_value_per_face{connectivity}; + + const N2x3 data(1, 3, 4, 6, 2, 5); + edge_value_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_value_per_face.numberOfSubValues(face_id); + } + return parallel::allReduceSum(number_of_edges_per_faces); + }(); + + REQUIRE(sum(edge_value_per_face) == global_number_of_edges_per_faces * data); + } + } + } + } +} diff --git a/tests/test_Synchronizer.cpp b/tests/test_Synchronizer.cpp index 6a4a9b52a6ce05a4a0b2758c868a49dc758d3e3d..54a065b475c757bdf8e108e01d1b8f859daa262d 100644 --- a/tests/test_Synchronizer.cpp +++ b/tests/test_Synchronizer.cpp @@ -5,446 +5,2318 @@ #include <mesh/Connectivity.hpp> #include <mesh/Mesh.hpp> #include <mesh/Synchronizer.hpp> +#include <mesh/SynchronizerManager.hpp> #include <utils/pugs_config.hpp> // clazy:excludeall=non-pod-global-static TEST_CASE("Synchronizer", "[mesh]") { - auto is_same_item_value = [](auto a, auto b) { - using IndexT = typename decltype(a)::index_type; - bool is_same = true; - for (IndexT i = 0; i < a.numberOfItems(); ++i) { - is_same &= (a[i] == b[i]); - } - return parallel::allReduceAnd(is_same); - }; + SECTION("ItemValue") + { + auto is_same_item_value = [](auto a, auto b) { + using IndexT = typename decltype(a)::index_type; + bool is_same = true; + for (IndexT i = 0; i < a.numberOfItems(); ++i) { + is_same &= (a[i] == b[i]); + } + return parallel::allReduceAnd(is_same); + }; + + SECTION("1D") + { + constexpr size_t Dimension = 1; + using ConnectivityType = Connectivity<Dimension>; + + const ConnectivityType& connectivity = MeshDataBaseForTests::get().unordered1DMesh()->connectivity(); + + SECTION("synchonize NodeValue") + { + const auto node_owner = connectivity.nodeOwner(); + const auto node_number = connectivity.nodeNumber(); + + NodeValue<int> node_value_ref{connectivity}; + parallel_for( + connectivity.numberOfNodes(), + PUGS_LAMBDA(const NodeId node_id) { node_value_ref[node_id] = node_owner[node_id] + node_number[node_id]; }); - auto is_same_item_array = [](auto a, auto b) { - using IndexT = typename decltype(a)::index_type; - bool is_same = true; - for (IndexT i = 0; i < a.numberOfItems(); ++i) { - for (size_t j = 0; j < a.sizeOfArrays(); ++j) { - is_same &= (a[i][j] == b[i][j]); + NodeValue<int> node_value{connectivity}; + parallel_for( + connectivity.numberOfNodes(), + PUGS_LAMBDA(const NodeId node_id) { node_value[node_id] = parallel::rank() + node_number[node_id]; }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(node_value, node_value_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_value); + + REQUIRE(is_same_item_value(node_value, node_value_ref)); } - } - return parallel::allReduceAnd(is_same); - }; - SECTION("1D") - { - constexpr size_t Dimension = 1; - using ConnectivityType = Connectivity<Dimension>; + SECTION("synchonize EdgeValue") + { + const auto edge_owner = connectivity.edgeOwner(); + const auto edge_number = connectivity.edgeNumber(); - const ConnectivityType& connectivity = MeshDataBaseForTests::get().unordered1DMesh()->connectivity(); + EdgeValue<int> edge_value_ref{connectivity}; + parallel_for( + connectivity.numberOfEdges(), + PUGS_LAMBDA(const EdgeId edge_id) { edge_value_ref[edge_id] = edge_owner[edge_id] + edge_number[edge_id]; }); - SECTION("synchonize NodeValue") - { - const auto node_owner = connectivity.nodeOwner(); - const auto node_number = connectivity.nodeNumber(); + EdgeValue<int> edge_value{connectivity}; + parallel_for( + connectivity.numberOfEdges(), + PUGS_LAMBDA(const EdgeId edge_id) { edge_value[edge_id] = parallel::rank() + edge_number[edge_id]; }); - NodeValue<int> node_value_ref{connectivity}; - parallel_for( - connectivity.numberOfNodes(), - PUGS_LAMBDA(const NodeId node_id) { node_value_ref[node_id] = node_owner[node_id] + node_number[node_id]; }); + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(edge_value, edge_value_ref)); + } - NodeValue<int> node_value{connectivity}; - parallel_for( - connectivity.numberOfNodes(), - PUGS_LAMBDA(const NodeId node_id) { node_value[node_id] = parallel::rank() + node_number[node_id]; }); + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_value); - if (parallel::size() > 1) { - REQUIRE(not is_same_item_value(node_value, node_value_ref)); + REQUIRE(is_same_item_value(edge_value, edge_value_ref)); } - Synchronizer synchronizer; - synchronizer.synchronize(node_value); + SECTION("synchonize FaceValue") + { + const auto face_owner = connectivity.faceOwner(); + const auto face_number = connectivity.faceNumber(); - REQUIRE(is_same_item_value(node_value, node_value_ref)); - } + FaceValue<int> face_value_ref{connectivity}; + parallel_for( + connectivity.numberOfFaces(), + PUGS_LAMBDA(const FaceId face_id) { face_value_ref[face_id] = face_owner[face_id] + face_number[face_id]; }); - SECTION("synchonize EdgeValue") - { - const auto edge_owner = connectivity.edgeOwner(); - const auto edge_number = connectivity.edgeNumber(); + FaceValue<int> face_value{connectivity}; + parallel_for( + connectivity.numberOfFaces(), + PUGS_LAMBDA(const FaceId face_id) { face_value[face_id] = parallel::rank() + face_number[face_id]; }); - EdgeValue<int> edge_value_ref{connectivity}; - parallel_for( - connectivity.numberOfEdges(), - PUGS_LAMBDA(const EdgeId edge_id) { edge_value_ref[edge_id] = edge_owner[edge_id] + edge_number[edge_id]; }); + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(face_value, face_value_ref)); + } - EdgeValue<int> edge_value{connectivity}; - parallel_for( - connectivity.numberOfEdges(), - PUGS_LAMBDA(const EdgeId edge_id) { edge_value[edge_id] = parallel::rank() + edge_number[edge_id]; }); + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(face_value); - if (parallel::size() > 1) { - REQUIRE(not is_same_item_value(edge_value, edge_value_ref)); + REQUIRE(is_same_item_value(face_value, face_value_ref)); } - Synchronizer synchronizer; - synchronizer.synchronize(edge_value); + SECTION("synchonize CellValue") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); - REQUIRE(is_same_item_value(edge_value, edge_value_ref)); + CellValue<int> cell_value_ref{connectivity}; + parallel_for( + connectivity.numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { cell_value_ref[cell_id] = cell_owner[cell_id] + cell_number[cell_id]; }); + + CellValue<int> cell_value{connectivity}; + parallel_for( + connectivity.numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = parallel::rank() + cell_number[cell_id]; }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(cell_value, cell_value_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(cell_value); + + REQUIRE(is_same_item_value(cell_value, cell_value_ref)); + } } - SECTION("synchonize FaceValue") + SECTION("2D") { - const auto face_owner = connectivity.faceOwner(); - const auto face_number = connectivity.faceNumber(); + constexpr size_t Dimension = 2; + using ConnectivityType = Connectivity<Dimension>; + + const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->connectivity(); + + SECTION("synchonize NodeValue") + { + const auto node_owner = connectivity.nodeOwner(); + const auto node_number = connectivity.nodeNumber(); + + NodeValue<int> node_value_ref{connectivity}; + parallel_for( + connectivity.numberOfNodes(), + PUGS_LAMBDA(const NodeId node_id) { node_value_ref[node_id] = node_owner[node_id] + node_number[node_id]; }); - FaceValue<int> face_value_ref{connectivity}; - parallel_for( - connectivity.numberOfFaces(), - PUGS_LAMBDA(const FaceId face_id) { face_value_ref[face_id] = face_owner[face_id] + face_number[face_id]; }); + NodeValue<int> node_value{connectivity}; + parallel_for( + connectivity.numberOfNodes(), + PUGS_LAMBDA(const NodeId node_id) { node_value[node_id] = parallel::rank() + node_number[node_id]; }); - FaceValue<int> face_value{connectivity}; - parallel_for( - connectivity.numberOfFaces(), - PUGS_LAMBDA(const FaceId face_id) { face_value[face_id] = parallel::rank() + face_number[face_id]; }); + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(node_value, node_value_ref)); + } - if (parallel::size() > 1) { - REQUIRE(not is_same_item_value(face_value, face_value_ref)); + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_value); + + REQUIRE(is_same_item_value(node_value, node_value_ref)); } - Synchronizer synchronizer; - synchronizer.synchronize(face_value); + SECTION("synchonize EdgeValue") + { + const auto edge_owner = connectivity.edgeOwner(); + const auto edge_number = connectivity.edgeNumber(); - REQUIRE(is_same_item_value(face_value, face_value_ref)); - } + EdgeValue<int> edge_value_ref{connectivity}; + parallel_for( + connectivity.numberOfEdges(), + PUGS_LAMBDA(const EdgeId edge_id) { edge_value_ref[edge_id] = edge_owner[edge_id] + edge_number[edge_id]; }); - SECTION("synchonize CellValue") - { - const auto cell_owner = connectivity.cellOwner(); - const auto cell_number = connectivity.cellNumber(); + EdgeValue<int> edge_value{connectivity}; + parallel_for( + connectivity.numberOfEdges(), + PUGS_LAMBDA(const EdgeId edge_id) { edge_value[edge_id] = parallel::rank() + edge_number[edge_id]; }); - CellValue<int> cell_value_ref{connectivity}; - parallel_for( - connectivity.numberOfCells(), - PUGS_LAMBDA(const CellId cell_id) { cell_value_ref[cell_id] = cell_owner[cell_id] + cell_number[cell_id]; }); + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(edge_value, edge_value_ref)); + } - CellValue<int> cell_value{connectivity}; - parallel_for( - connectivity.numberOfCells(), - PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = parallel::rank() + cell_number[cell_id]; }); + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_value); - if (parallel::size() > 1) { - REQUIRE(not is_same_item_value(cell_value, cell_value_ref)); + REQUIRE(is_same_item_value(edge_value, edge_value_ref)); } - Synchronizer synchronizer; - synchronizer.synchronize(cell_value); + SECTION("synchonize FaceValue") + { + const auto face_owner = connectivity.faceOwner(); + const auto face_number = connectivity.faceNumber(); - REQUIRE(is_same_item_value(cell_value, cell_value_ref)); - } + FaceValue<int> face_value_ref{connectivity}; + parallel_for( + connectivity.numberOfFaces(), + PUGS_LAMBDA(const FaceId face_id) { face_value_ref[face_id] = face_owner[face_id] + face_number[face_id]; }); - SECTION("synchonize CellArray") - { - const auto cell_owner = connectivity.cellOwner(); - const auto cell_number = connectivity.cellNumber(); - - CellArray<int> cell_array_ref{connectivity, 3}; - parallel_for( - connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - for (size_t i = 0; i < cell_array_ref.sizeOfArrays(); ++i) { - cell_array_ref[cell_id][i] = (i + 1) * cell_owner[cell_id] + i + cell_number[cell_id]; - } - }); + FaceValue<int> face_value{connectivity}; + parallel_for( + connectivity.numberOfFaces(), + PUGS_LAMBDA(const FaceId face_id) { face_value[face_id] = parallel::rank() + face_number[face_id]; }); - CellArray<int> cell_array{connectivity, 3}; - parallel_for( - connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) { - cell_array[cell_id][i] = (i + 1) * parallel::rank() + i + cell_number[cell_id]; - } - }); + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(face_value, face_value_ref)); + } - if (parallel::size() > 1) { - REQUIRE(not is_same_item_array(cell_array, cell_array_ref)); + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(face_value); + + REQUIRE(is_same_item_value(face_value, face_value_ref)); } - Synchronizer synchronizer; - synchronizer.synchronize(cell_array); + SECTION("synchonize CellValue") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); - REQUIRE(is_same_item_array(cell_array, cell_array_ref)); - } - } + CellValue<int> cell_value_ref{connectivity}; + parallel_for( + connectivity.numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { cell_value_ref[cell_id] = cell_owner[cell_id] + cell_number[cell_id]; }); - SECTION("2D") - { - constexpr size_t Dimension = 2; - using ConnectivityType = Connectivity<Dimension>; + CellValue<int> cell_value{connectivity}; + parallel_for( + connectivity.numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = parallel::rank() + cell_number[cell_id]; }); - const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->connectivity(); + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(cell_value, cell_value_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(cell_value); + + REQUIRE(is_same_item_value(cell_value, cell_value_ref)); + } + } - SECTION("synchonize NodeValue") + SECTION("3D") { - const auto node_owner = connectivity.nodeOwner(); - const auto node_number = connectivity.nodeNumber(); + constexpr size_t Dimension = 3; + using ConnectivityType = Connectivity<Dimension>; - NodeValue<int> node_value_ref{connectivity}; - parallel_for( - connectivity.numberOfNodes(), - PUGS_LAMBDA(const NodeId node_id) { node_value_ref[node_id] = node_owner[node_id] + node_number[node_id]; }); + const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->connectivity(); - NodeValue<int> node_value{connectivity}; - parallel_for( - connectivity.numberOfNodes(), - PUGS_LAMBDA(const NodeId node_id) { node_value[node_id] = parallel::rank() + node_number[node_id]; }); + SECTION("synchonize NodeValue") + { + const auto node_owner = connectivity.nodeOwner(); + const auto node_number = connectivity.nodeNumber(); - if (parallel::size() > 1) { - REQUIRE(not is_same_item_value(node_value, node_value_ref)); + NodeValue<int> node_value_ref{connectivity}; + parallel_for( + connectivity.numberOfNodes(), + PUGS_LAMBDA(const NodeId node_id) { node_value_ref[node_id] = node_owner[node_id] + node_number[node_id]; }); + + NodeValue<int> node_value{connectivity}; + parallel_for( + connectivity.numberOfNodes(), + PUGS_LAMBDA(const NodeId node_id) { node_value[node_id] = parallel::rank() + node_number[node_id]; }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(node_value, node_value_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_value); + + REQUIRE(is_same_item_value(node_value, node_value_ref)); } - Synchronizer synchronizer; - synchronizer.synchronize(node_value); + SECTION("synchonize EdgeValue") + { + const auto edge_owner = connectivity.edgeOwner(); + const auto edge_number = connectivity.edgeNumber(); - REQUIRE(is_same_item_value(node_value, node_value_ref)); - } + EdgeValue<int> edge_value_ref{connectivity}; + parallel_for( + connectivity.numberOfEdges(), + PUGS_LAMBDA(const EdgeId edge_id) { edge_value_ref[edge_id] = edge_owner[edge_id] + edge_number[edge_id]; }); - SECTION("synchonize EdgeValue") - { - const auto edge_owner = connectivity.edgeOwner(); - const auto edge_number = connectivity.edgeNumber(); + EdgeValue<int> edge_value{connectivity}; + parallel_for( + connectivity.numberOfEdges(), + PUGS_LAMBDA(const EdgeId edge_id) { edge_value[edge_id] = parallel::rank() + edge_number[edge_id]; }); - EdgeValue<int> edge_value_ref{connectivity}; - parallel_for( - connectivity.numberOfEdges(), - PUGS_LAMBDA(const EdgeId edge_id) { edge_value_ref[edge_id] = edge_owner[edge_id] + edge_number[edge_id]; }); + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(edge_value, edge_value_ref)); + } - EdgeValue<int> edge_value{connectivity}; - parallel_for( - connectivity.numberOfEdges(), - PUGS_LAMBDA(const EdgeId edge_id) { edge_value[edge_id] = parallel::rank() + edge_number[edge_id]; }); + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_value); - if (parallel::size() > 1) { - REQUIRE(not is_same_item_value(edge_value, edge_value_ref)); + REQUIRE(is_same_item_value(edge_value, edge_value_ref)); } - Synchronizer synchronizer; - synchronizer.synchronize(edge_value); + SECTION("synchonize FaceValue") + { + const auto face_owner = connectivity.faceOwner(); + const auto face_number = connectivity.faceNumber(); - REQUIRE(is_same_item_value(edge_value, edge_value_ref)); - } + FaceValue<int> face_value_ref{connectivity}; + parallel_for( + connectivity.numberOfFaces(), + PUGS_LAMBDA(const FaceId face_id) { face_value_ref[face_id] = face_owner[face_id] + face_number[face_id]; }); - SECTION("synchonize FaceValue") - { - const auto face_owner = connectivity.faceOwner(); - const auto face_number = connectivity.faceNumber(); + FaceValue<int> face_value{connectivity}; + parallel_for( + connectivity.numberOfFaces(), + PUGS_LAMBDA(const FaceId face_id) { face_value[face_id] = parallel::rank() + face_number[face_id]; }); - FaceValue<int> face_value_ref{connectivity}; - parallel_for( - connectivity.numberOfFaces(), - PUGS_LAMBDA(const FaceId face_id) { face_value_ref[face_id] = face_owner[face_id] + face_number[face_id]; }); + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(face_value, face_value_ref)); + } - FaceValue<int> face_value{connectivity}; - parallel_for( - connectivity.numberOfFaces(), - PUGS_LAMBDA(const FaceId face_id) { face_value[face_id] = parallel::rank() + face_number[face_id]; }); + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(face_value); - if (parallel::size() > 1) { - REQUIRE(not is_same_item_value(face_value, face_value_ref)); + REQUIRE(is_same_item_value(face_value, face_value_ref)); } - Synchronizer synchronizer; - synchronizer.synchronize(face_value); + SECTION("synchonize CellValue") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); - REQUIRE(is_same_item_value(face_value, face_value_ref)); + CellValue<int> cell_value_ref{connectivity}; + parallel_for( + connectivity.numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { cell_value_ref[cell_id] = cell_owner[cell_id] + cell_number[cell_id]; }); + + CellValue<int> cell_value{connectivity}; + parallel_for( + connectivity.numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = parallel::rank() + cell_number[cell_id]; }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(cell_value, cell_value_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(cell_value); + + REQUIRE(is_same_item_value(cell_value, cell_value_ref)); + } } + } - SECTION("synchonize CellValue") + SECTION("ItemArray") + { + auto is_same_item_array = [](auto a, auto b) { + using IndexT = typename decltype(a)::index_type; + bool is_same = true; + for (IndexT i = 0; i < a.numberOfItems(); ++i) { + for (size_t j = 0; j < a.sizeOfArrays(); ++j) { + is_same &= (a[i][j] == b[i][j]); + } + } + return parallel::allReduceAnd(is_same); + }; + + SECTION("1D") { - const auto cell_owner = connectivity.cellOwner(); - const auto cell_number = connectivity.cellNumber(); + constexpr size_t Dimension = 1; + using ConnectivityType = Connectivity<Dimension>; + + const ConnectivityType& connectivity = MeshDataBaseForTests::get().unordered1DMesh()->connectivity(); + + SECTION("synchonize NodeArray") + { + const auto node_owner = connectivity.nodeOwner(); + const auto node_number = connectivity.nodeNumber(); + + NodeArray<int> node_array_ref{connectivity, 3}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + for (size_t i = 0; i < node_array_ref.sizeOfArrays(); ++i) { + node_array_ref[node_id][i] = (i + 1) * node_owner[node_id] + i + node_number[node_id]; + } + }); + + NodeArray<int> node_array{connectivity, 3}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + for (size_t i = 0; i < node_array.sizeOfArrays(); ++i) { + node_array[node_id][i] = (i + 1) * parallel::rank() + i + node_number[node_id]; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(node_array, node_array_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_array); + + REQUIRE(is_same_item_array(node_array, node_array_ref)); + } + + SECTION("synchonize EdgeArray") + { + const auto edge_owner = connectivity.edgeOwner(); + const auto edge_number = connectivity.edgeNumber(); + + EdgeArray<int> edge_array_ref{connectivity, 3}; + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t i = 0; i < edge_array_ref.sizeOfArrays(); ++i) { + edge_array_ref[edge_id][i] = (i + 1) * edge_owner[edge_id] + i + edge_number[edge_id]; + } + }); + + EdgeArray<int> edge_array{connectivity, 3}; + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t i = 0; i < edge_array.sizeOfArrays(); ++i) { + edge_array[edge_id][i] = (i + 1) * parallel::rank() + i + edge_number[edge_id]; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(edge_array, edge_array_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_array); + + REQUIRE(is_same_item_array(edge_array, edge_array_ref)); + } - CellValue<int> cell_value_ref{connectivity}; - parallel_for( - connectivity.numberOfCells(), - PUGS_LAMBDA(const CellId cell_id) { cell_value_ref[cell_id] = cell_owner[cell_id] + cell_number[cell_id]; }); + SECTION("synchonize FaceArray") + { + const auto face_owner = connectivity.faceOwner(); + const auto face_number = connectivity.faceNumber(); + + FaceArray<int> face_array_ref{connectivity, 3}; + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t i = 0; i < face_array_ref.sizeOfArrays(); ++i) { + face_array_ref[face_id][i] = (i + 1) * face_owner[face_id] + i + face_number[face_id]; + } + }); + + FaceArray<int> face_array{connectivity, 3}; + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t i = 0; i < face_array.sizeOfArrays(); ++i) { + face_array[face_id][i] = (i + 1) * parallel::rank() + i + face_number[face_id]; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(face_array, face_array_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(face_array); + + REQUIRE(is_same_item_array(face_array, face_array_ref)); + } + + SECTION("synchonize CellArray") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + CellArray<int> cell_array_ref{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t i = 0; i < cell_array_ref.sizeOfArrays(); ++i) { + cell_array_ref[cell_id][i] = (i + 1) * cell_owner[cell_id] + i + cell_number[cell_id]; + } + }); + + CellArray<int> cell_array{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) { + cell_array[cell_id][i] = (i + 1) * parallel::rank() + i + cell_number[cell_id]; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(cell_array, cell_array_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(cell_array); + + REQUIRE(is_same_item_array(cell_array, cell_array_ref)); + } + } - CellValue<int> cell_value{connectivity}; - parallel_for( - connectivity.numberOfCells(), - PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = parallel::rank() + cell_number[cell_id]; }); + SECTION("2D") + { + constexpr size_t Dimension = 2; + using ConnectivityType = Connectivity<Dimension>; + + const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->connectivity(); + + SECTION("synchonize NodeArray") + { + const auto node_owner = connectivity.nodeOwner(); + const auto node_number = connectivity.nodeNumber(); + + NodeArray<int> node_array_ref{connectivity, 3}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + for (size_t i = 0; i < node_array_ref.sizeOfArrays(); ++i) { + node_array_ref[node_id][i] = (i + 1) * node_owner[node_id] + i + node_number[node_id]; + } + }); + + NodeArray<int> node_array{connectivity, 3}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + for (size_t i = 0; i < node_array.sizeOfArrays(); ++i) { + node_array[node_id][i] = (i + 1) * parallel::rank() + i + node_number[node_id]; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(node_array, node_array_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_array); + + REQUIRE(is_same_item_array(node_array, node_array_ref)); + } - if (parallel::size() > 1) { - REQUIRE(not is_same_item_value(cell_value, cell_value_ref)); + SECTION("synchonize EdgeArray") + { + const auto edge_owner = connectivity.edgeOwner(); + const auto edge_number = connectivity.edgeNumber(); + + EdgeArray<int> edge_array_ref{connectivity, 3}; + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t i = 0; i < edge_array_ref.sizeOfArrays(); ++i) { + edge_array_ref[edge_id][i] = (i + 1) * edge_owner[edge_id] + i + edge_number[edge_id]; + } + }); + + EdgeArray<int> edge_array{connectivity, 3}; + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t i = 0; i < edge_array.sizeOfArrays(); ++i) { + edge_array[edge_id][i] = (i + 1) * parallel::rank() + i + edge_number[edge_id]; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(edge_array, edge_array_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_array); + + REQUIRE(is_same_item_array(edge_array, edge_array_ref)); } - Synchronizer synchronizer; - synchronizer.synchronize(cell_value); + SECTION("synchonize FaceArray") + { + const auto face_owner = connectivity.faceOwner(); + const auto face_number = connectivity.faceNumber(); + + FaceArray<int> face_array_ref{connectivity, 3}; + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t i = 0; i < face_array_ref.sizeOfArrays(); ++i) { + face_array_ref[face_id][i] = (i + 1) * face_owner[face_id] + i + face_number[face_id]; + } + }); + + FaceArray<int> face_array{connectivity, 3}; + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t i = 0; i < face_array.sizeOfArrays(); ++i) { + face_array[face_id][i] = (i + 1) * parallel::rank() + i + face_number[face_id]; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(face_array, face_array_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(face_array); + + REQUIRE(is_same_item_array(face_array, face_array_ref)); + } - REQUIRE(is_same_item_value(cell_value, cell_value_ref)); + SECTION("synchonize CellArray") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + CellArray<int> cell_array_ref{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t i = 0; i < cell_array_ref.sizeOfArrays(); ++i) { + cell_array_ref[cell_id][i] = (i + 1) * cell_owner[cell_id] + i + cell_number[cell_id]; + } + }); + + CellArray<int> cell_array{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) { + cell_array[cell_id][i] = (i + 1) * parallel::rank() + i + cell_number[cell_id]; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(cell_array, cell_array_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(cell_array); + + REQUIRE(is_same_item_array(cell_array, cell_array_ref)); + } } - SECTION("synchonize CellArray") + SECTION("3D") { - const auto cell_owner = connectivity.cellOwner(); - const auto cell_number = connectivity.cellNumber(); - - CellArray<int> cell_array_ref{connectivity, 3}; - parallel_for( - connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - for (size_t i = 0; i < cell_array_ref.sizeOfArrays(); ++i) { - cell_array_ref[cell_id][i] = (i + 1) * cell_owner[cell_id] + i + cell_number[cell_id]; - } - }); - - CellArray<int> cell_array{connectivity, 3}; - parallel_for( - connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) { - cell_array[cell_id][i] = (i + 1) * parallel::rank() + i + cell_number[cell_id]; - } - }); + constexpr size_t Dimension = 3; + using ConnectivityType = Connectivity<Dimension>; + + const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->connectivity(); + + SECTION("synchonize NodeArray") + { + const auto node_owner = connectivity.nodeOwner(); + const auto node_number = connectivity.nodeNumber(); + + NodeArray<int> node_array_ref{connectivity, 3}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + for (size_t i = 0; i < node_array_ref.sizeOfArrays(); ++i) { + node_array_ref[node_id][i] = (i + 1) * node_owner[node_id] + i + node_number[node_id]; + } + }); + + NodeArray<int> node_array{connectivity, 3}; + parallel_for( + connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { + for (size_t i = 0; i < node_array.sizeOfArrays(); ++i) { + node_array[node_id][i] = (i + 1) * parallel::rank() + i + node_number[node_id]; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(node_array, node_array_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_array); + + REQUIRE(is_same_item_array(node_array, node_array_ref)); + } - if (parallel::size() > 1) { - REQUIRE(not is_same_item_array(cell_array, cell_array_ref)); + SECTION("synchonize EdgeArray") + { + const auto edge_owner = connectivity.edgeOwner(); + const auto edge_number = connectivity.edgeNumber(); + + EdgeArray<int> edge_array_ref{connectivity, 3}; + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t i = 0; i < edge_array_ref.sizeOfArrays(); ++i) { + edge_array_ref[edge_id][i] = (i + 1) * edge_owner[edge_id] + i + edge_number[edge_id]; + } + }); + + EdgeArray<int> edge_array{connectivity, 3}; + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t i = 0; i < edge_array.sizeOfArrays(); ++i) { + edge_array[edge_id][i] = (i + 1) * parallel::rank() + i + edge_number[edge_id]; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(edge_array, edge_array_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_array); + + REQUIRE(is_same_item_array(edge_array, edge_array_ref)); } - Synchronizer synchronizer; - synchronizer.synchronize(cell_array); + SECTION("synchonize FaceArray") + { + const auto face_owner = connectivity.faceOwner(); + const auto face_number = connectivity.faceNumber(); + + FaceArray<int> face_array_ref{connectivity, 3}; + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t i = 0; i < face_array_ref.sizeOfArrays(); ++i) { + face_array_ref[face_id][i] = (i + 1) * face_owner[face_id] + i + face_number[face_id]; + } + }); + + FaceArray<int> face_array{connectivity, 3}; + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t i = 0; i < face_array.sizeOfArrays(); ++i) { + face_array[face_id][i] = (i + 1) * parallel::rank() + i + face_number[face_id]; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(face_array, face_array_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(face_array); + + REQUIRE(is_same_item_array(face_array, face_array_ref)); + } - REQUIRE(is_same_item_array(cell_array, cell_array_ref)); + SECTION("synchonize CellArray") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + CellArray<int> cell_array_ref{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t i = 0; i < cell_array_ref.sizeOfArrays(); ++i) { + cell_array_ref[cell_id][i] = (i + 1) * cell_owner[cell_id] + i + cell_number[cell_id]; + } + }); + + CellArray<int> cell_array{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) { + cell_array[cell_id][i] = (i + 1) * parallel::rank() + i + cell_number[cell_id]; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(cell_array, cell_array_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(cell_array); + + REQUIRE(is_same_item_array(cell_array, cell_array_ref)); + } } } - SECTION("3D") + SECTION("SubItemValuePerItem") { - constexpr size_t Dimension = 3; - using ConnectivityType = Connectivity<Dimension>; + auto is_same_item_value = [](auto a, auto b) { + using IndexT = typename decltype(a)::index_type; + bool is_same = true; + for (IndexT i_item = 0; i_item < a.numberOfItems(); ++i_item) { + for (size_t l = 0; l < a.numberOfSubValues(i_item); ++l) { + is_same &= (a(i_item, l) == b(i_item, l)); + } + } + return parallel::allReduceAnd(is_same); + }; + + auto reset_ghost_values = [](auto sub_item_value_per_item, auto item_owner, auto value) { + using IndexT = typename decltype(sub_item_value_per_item)::index_type; + static_assert(std::is_same_v<typename decltype(sub_item_value_per_item)::index_type, + typename decltype(item_owner)::index_type>); + for (IndexT i_item = 0; i_item < sub_item_value_per_item.numberOfItems(); ++i_item) { + if (item_owner[i_item] != static_cast<int>(parallel::rank())) { + for (size_t l = 0; l < sub_item_value_per_item.numberOfSubValues(i_item); ++l) { + sub_item_value_per_item(i_item, l) = value; + } + } + } + }; - const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->connectivity(); + SECTION("1D") + { + constexpr size_t Dimension = 1; + using ConnectivityType = Connectivity<Dimension>; + + const ConnectivityType& connectivity = MeshDataBaseForTests::get().unordered1DMesh()->connectivity(); + + SECTION("synchonize NodeValuePerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + NodeValuePerCell<int> node_value_per_cell_ref{connectivity}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < node_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + node_value_per_cell_ref(cell_id, j) = cell_owner[cell_id] + cell_number[cell_id] + j; + } + }); + + NodeValuePerCell<int> node_value_per_cell{connectivity}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < node_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + node_value_per_cell(cell_id, j) = parallel::rank() + cell_number[cell_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(node_value_per_cell, node_value_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_value_per_cell); + + REQUIRE(is_same_item_value(node_value_per_cell, node_value_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(node_value_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_value(node_value_per_cell, node_value_per_cell_ref)); + synchronizer.synchronize(node_value_per_cell); + REQUIRE(is_same_item_value(node_value_per_cell, node_value_per_cell_ref)); + } + } - SECTION("synchonize NodeValue") + SECTION("synchonize EdgeValuePerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + EdgeValuePerCell<int> edge_value_per_cell_ref{connectivity}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < edge_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + edge_value_per_cell_ref(cell_id, j) = cell_owner[cell_id] + cell_number[cell_id] + j; + } + }); + + EdgeValuePerCell<int> edge_value_per_cell{connectivity}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < edge_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + edge_value_per_cell(cell_id, j) = parallel::rank() + cell_number[cell_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(edge_value_per_cell, edge_value_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_value_per_cell); + + REQUIRE(is_same_item_value(edge_value_per_cell, edge_value_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(edge_value_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_value(edge_value_per_cell, edge_value_per_cell_ref)); + synchronizer.synchronize(edge_value_per_cell); + REQUIRE(is_same_item_value(edge_value_per_cell, edge_value_per_cell_ref)); + } + } + + SECTION("synchonize FaceValuePerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + FaceValuePerCell<int> face_value_per_cell_ref{connectivity}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < face_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + face_value_per_cell_ref(cell_id, j) = cell_owner[cell_id] + cell_number[cell_id] + j; + } + }); + + FaceValuePerCell<int> face_value_per_cell{connectivity}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < face_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + face_value_per_cell(cell_id, j) = parallel::rank() + cell_number[cell_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(face_value_per_cell, face_value_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(face_value_per_cell); + + REQUIRE(is_same_item_value(face_value_per_cell, face_value_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(face_value_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_value(face_value_per_cell, face_value_per_cell_ref)); + synchronizer.synchronize(face_value_per_cell); + REQUIRE(is_same_item_value(face_value_per_cell, face_value_per_cell_ref)); + } + } + + SECTION("forbidden synchronization") + { + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + + SECTION("CellValuePerNode") + { + CellValuePerNode<int> cell_value_per_node{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_value_per_node), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (node)"); + } + + SECTION("CellValuePerEdge") + { + CellValuePerEdge<int> cell_value_per_edge{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_value_per_edge), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (edge)"); + } + + SECTION("CellValuePerFace") + { + CellValuePerFace<int> cell_value_per_face{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_value_per_face), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (face)"); + } + } + } + + SECTION("2D") { - const auto node_owner = connectivity.nodeOwner(); - const auto node_number = connectivity.nodeNumber(); + constexpr size_t Dimension = 2; + using ConnectivityType = Connectivity<Dimension>; + + const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->connectivity(); + + SECTION("synchonize NodeValuePerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + NodeValuePerCell<int> node_value_per_cell_ref{connectivity}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < node_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + node_value_per_cell_ref(cell_id, j) = cell_owner[cell_id] + cell_number[cell_id] + j; + } + }); + + NodeValuePerCell<int> node_value_per_cell{connectivity}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < node_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + node_value_per_cell(cell_id, j) = parallel::rank() + cell_number[cell_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(node_value_per_cell, node_value_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_value_per_cell); + + REQUIRE(is_same_item_value(node_value_per_cell, node_value_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(node_value_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_value(node_value_per_cell, node_value_per_cell_ref)); + synchronizer.synchronize(node_value_per_cell); + REQUIRE(is_same_item_value(node_value_per_cell, node_value_per_cell_ref)); + } + } - NodeValue<int> node_value_ref{connectivity}; - parallel_for( - connectivity.numberOfNodes(), - PUGS_LAMBDA(const NodeId node_id) { node_value_ref[node_id] = node_owner[node_id] + node_number[node_id]; }); + SECTION("synchonize EdgeValuePerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + EdgeValuePerCell<int> edge_value_per_cell_ref{connectivity}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < edge_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + edge_value_per_cell_ref(cell_id, j) = cell_owner[cell_id] + cell_number[cell_id] + j; + } + }); + + EdgeValuePerCell<int> edge_value_per_cell{connectivity}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < edge_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + edge_value_per_cell(cell_id, j) = parallel::rank() + cell_number[cell_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(edge_value_per_cell, edge_value_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_value_per_cell); + + REQUIRE(is_same_item_value(edge_value_per_cell, edge_value_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(edge_value_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_value(edge_value_per_cell, edge_value_per_cell_ref)); + synchronizer.synchronize(edge_value_per_cell); + REQUIRE(is_same_item_value(edge_value_per_cell, edge_value_per_cell_ref)); + } + } - NodeValue<int> node_value{connectivity}; - parallel_for( - connectivity.numberOfNodes(), - PUGS_LAMBDA(const NodeId node_id) { node_value[node_id] = parallel::rank() + node_number[node_id]; }); + SECTION("synchonize FaceValuePerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + FaceValuePerCell<int> face_value_per_cell_ref{connectivity}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < face_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + face_value_per_cell_ref(cell_id, j) = cell_owner[cell_id] + cell_number[cell_id] + j; + } + }); + + FaceValuePerCell<int> face_value_per_cell{connectivity}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < face_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + face_value_per_cell(cell_id, j) = parallel::rank() + cell_number[cell_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(face_value_per_cell, face_value_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(face_value_per_cell); + + REQUIRE(is_same_item_value(face_value_per_cell, face_value_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(face_value_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_value(face_value_per_cell, face_value_per_cell_ref)); + synchronizer.synchronize(face_value_per_cell); + REQUIRE(is_same_item_value(face_value_per_cell, face_value_per_cell_ref)); + } + } - if (parallel::size() > 1) { - REQUIRE(not is_same_item_value(node_value, node_value_ref)); + SECTION("synchonize NodeValuePerFace") + { + const auto face_owner = connectivity.faceOwner(); + const auto face_number = connectivity.faceNumber(); + + NodeValuePerFace<int> node_value_per_face_ref{connectivity}; + + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t j = 0; j < node_value_per_face_ref.numberOfSubValues(face_id); ++j) { + node_value_per_face_ref(face_id, j) = face_owner[face_id] + face_number[face_id] + j; + } + }); + + NodeValuePerFace<int> node_value_per_face{connectivity}; + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t j = 0; j < node_value_per_face_ref.numberOfSubValues(face_id); ++j) { + node_value_per_face(face_id, j) = parallel::rank() + face_number[face_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(node_value_per_face, node_value_per_face_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_value_per_face); + + REQUIRE(is_same_item_value(node_value_per_face, node_value_per_face_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(node_value_per_face, face_owner, 0); + REQUIRE(not is_same_item_value(node_value_per_face, node_value_per_face_ref)); + synchronizer.synchronize(node_value_per_face); + REQUIRE(is_same_item_value(node_value_per_face, node_value_per_face_ref)); + } } - Synchronizer synchronizer; - synchronizer.synchronize(node_value); + SECTION("synchonize NodeValuePerEdge") + { + const auto edge_owner = connectivity.edgeOwner(); + const auto edge_number = connectivity.edgeNumber(); + + NodeValuePerEdge<int> node_value_per_edge_ref{connectivity}; + + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t j = 0; j < node_value_per_edge_ref.numberOfSubValues(edge_id); ++j) { + node_value_per_edge_ref(edge_id, j) = edge_owner[edge_id] + edge_number[edge_id] + j; + } + }); + + NodeValuePerEdge<int> node_value_per_edge{connectivity}; + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t j = 0; j < node_value_per_edge_ref.numberOfSubValues(edge_id); ++j) { + node_value_per_edge(edge_id, j) = parallel::rank() + edge_number[edge_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(node_value_per_edge, node_value_per_edge_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_value_per_edge); + + REQUIRE(is_same_item_value(node_value_per_edge, node_value_per_edge_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(node_value_per_edge, edge_owner, 0); + REQUIRE(not is_same_item_value(node_value_per_edge, node_value_per_edge_ref)); + synchronizer.synchronize(node_value_per_edge); + REQUIRE(is_same_item_value(node_value_per_edge, node_value_per_edge_ref)); + } + } - REQUIRE(is_same_item_value(node_value, node_value_ref)); + SECTION("forbidden synchronization") + { + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + + SECTION("CellValuePerNode") + { + CellValuePerNode<int> cell_value_per_node{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_value_per_node), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (node)"); + } + + SECTION("CellValuePerEdge") + { + CellValuePerEdge<int> cell_value_per_edge{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_value_per_edge), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (edge)"); + } + + SECTION("CellValuePerFace") + { + CellValuePerFace<int> cell_value_per_face{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_value_per_face), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (face)"); + } + + SECTION("FaceValuePerNode") + { + FaceValuePerNode<int> face_value_per_node{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(face_value_per_node), + "unexpected error: synchronization requires sub-item type (face) to be of lower " + "dimension than item (node)"); + } + + SECTION("EdgeValuePerNode") + { + EdgeValuePerNode<int> edge_value_per_node{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(edge_value_per_node), + "unexpected error: synchronization requires sub-item type (edge) to be of lower " + "dimension than item (node)"); + } + } } - SECTION("synchonize EdgeValue") + SECTION("3D") { - const auto edge_owner = connectivity.edgeOwner(); - const auto edge_number = connectivity.edgeNumber(); + constexpr size_t Dimension = 3; + using ConnectivityType = Connectivity<Dimension>; + + const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->connectivity(); + + SECTION("synchonize NodeValuePerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + NodeValuePerCell<int> node_value_per_cell_ref{connectivity}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < node_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + node_value_per_cell_ref(cell_id, j) = cell_owner[cell_id] + cell_number[cell_id] + j; + } + }); + + NodeValuePerCell<int> node_value_per_cell{connectivity}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < node_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + node_value_per_cell(cell_id, j) = parallel::rank() + cell_number[cell_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(node_value_per_cell, node_value_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_value_per_cell); + + REQUIRE(is_same_item_value(node_value_per_cell, node_value_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(node_value_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_value(node_value_per_cell, node_value_per_cell_ref)); + synchronizer.synchronize(node_value_per_cell); + REQUIRE(is_same_item_value(node_value_per_cell, node_value_per_cell_ref)); + } + } + + SECTION("synchonize EdgeValuePerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + EdgeValuePerCell<int> edge_value_per_cell_ref{connectivity}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < edge_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + edge_value_per_cell_ref(cell_id, j) = cell_owner[cell_id] + cell_number[cell_id] + j; + } + }); + + EdgeValuePerCell<int> edge_value_per_cell{connectivity}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < edge_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + edge_value_per_cell(cell_id, j) = parallel::rank() + cell_number[cell_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(edge_value_per_cell, edge_value_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_value_per_cell); + + REQUIRE(is_same_item_value(edge_value_per_cell, edge_value_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(edge_value_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_value(edge_value_per_cell, edge_value_per_cell_ref)); + synchronizer.synchronize(edge_value_per_cell); + REQUIRE(is_same_item_value(edge_value_per_cell, edge_value_per_cell_ref)); + } + } - EdgeValue<int> edge_value_ref{connectivity}; - parallel_for( - connectivity.numberOfEdges(), - PUGS_LAMBDA(const EdgeId edge_id) { edge_value_ref[edge_id] = edge_owner[edge_id] + edge_number[edge_id]; }); + SECTION("synchonize FaceValuePerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + FaceValuePerCell<int> face_value_per_cell_ref{connectivity}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < face_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + face_value_per_cell_ref(cell_id, j) = cell_owner[cell_id] + cell_number[cell_id] + j; + } + }); + + FaceValuePerCell<int> face_value_per_cell{connectivity}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < face_value_per_cell_ref.numberOfSubValues(cell_id); ++j) { + face_value_per_cell(cell_id, j) = parallel::rank() + cell_number[cell_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(face_value_per_cell, face_value_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(face_value_per_cell); + + REQUIRE(is_same_item_value(face_value_per_cell, face_value_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(face_value_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_value(face_value_per_cell, face_value_per_cell_ref)); + synchronizer.synchronize(face_value_per_cell); + REQUIRE(is_same_item_value(face_value_per_cell, face_value_per_cell_ref)); + } + } - EdgeValue<int> edge_value{connectivity}; - parallel_for( - connectivity.numberOfEdges(), - PUGS_LAMBDA(const EdgeId edge_id) { edge_value[edge_id] = parallel::rank() + edge_number[edge_id]; }); + SECTION("synchonize NodeValuePerFace") + { + const auto face_owner = connectivity.faceOwner(); + const auto face_number = connectivity.faceNumber(); + + NodeValuePerFace<int> node_value_per_face_ref{connectivity}; + + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t j = 0; j < node_value_per_face_ref.numberOfSubValues(face_id); ++j) { + node_value_per_face_ref(face_id, j) = face_owner[face_id] + face_number[face_id] + j; + } + }); + + NodeValuePerFace<int> node_value_per_face{connectivity}; + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t j = 0; j < node_value_per_face_ref.numberOfSubValues(face_id); ++j) { + node_value_per_face(face_id, j) = parallel::rank() + face_number[face_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(node_value_per_face, node_value_per_face_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_value_per_face); + + REQUIRE(is_same_item_value(node_value_per_face, node_value_per_face_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(node_value_per_face, face_owner, 0); + REQUIRE(not is_same_item_value(node_value_per_face, node_value_per_face_ref)); + synchronizer.synchronize(node_value_per_face); + REQUIRE(is_same_item_value(node_value_per_face, node_value_per_face_ref)); + } + } - if (parallel::size() > 1) { - REQUIRE(not is_same_item_value(edge_value, edge_value_ref)); + SECTION("synchonize EdgeValuePerFace") + { + const auto face_owner = connectivity.faceOwner(); + const auto face_number = connectivity.faceNumber(); + + EdgeValuePerFace<int> edge_value_per_face_ref{connectivity}; + + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t j = 0; j < edge_value_per_face_ref.numberOfSubValues(face_id); ++j) { + edge_value_per_face_ref(face_id, j) = face_owner[face_id] + face_number[face_id] + j; + } + }); + + EdgeValuePerFace<int> edge_value_per_face{connectivity}; + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t j = 0; j < edge_value_per_face_ref.numberOfSubValues(face_id); ++j) { + edge_value_per_face(face_id, j) = parallel::rank() + face_number[face_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(edge_value_per_face, edge_value_per_face_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_value_per_face); + + REQUIRE(is_same_item_value(edge_value_per_face, edge_value_per_face_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(edge_value_per_face, face_owner, 0); + REQUIRE(not is_same_item_value(edge_value_per_face, edge_value_per_face_ref)); + synchronizer.synchronize(edge_value_per_face); + REQUIRE(is_same_item_value(edge_value_per_face, edge_value_per_face_ref)); + } } - Synchronizer synchronizer; - synchronizer.synchronize(edge_value); + SECTION("synchonize NodeValuePerEdge") + { + const auto edge_owner = connectivity.edgeOwner(); + const auto edge_number = connectivity.edgeNumber(); + + NodeValuePerEdge<int> node_value_per_edge_ref{connectivity}; + + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t j = 0; j < node_value_per_edge_ref.numberOfSubValues(edge_id); ++j) { + node_value_per_edge_ref(edge_id, j) = edge_owner[edge_id] + edge_number[edge_id] + j; + } + }); + + NodeValuePerEdge<int> node_value_per_edge{connectivity}; + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t j = 0; j < node_value_per_edge_ref.numberOfSubValues(edge_id); ++j) { + node_value_per_edge(edge_id, j) = parallel::rank() + edge_number[edge_id] + j; + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_value(node_value_per_edge, node_value_per_edge_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_value_per_edge); + + REQUIRE(is_same_item_value(node_value_per_edge, node_value_per_edge_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_values(node_value_per_edge, edge_owner, 0); + REQUIRE(not is_same_item_value(node_value_per_edge, node_value_per_edge_ref)); + synchronizer.synchronize(node_value_per_edge); + REQUIRE(is_same_item_value(node_value_per_edge, node_value_per_edge_ref)); + } + } - REQUIRE(is_same_item_value(edge_value, edge_value_ref)); + SECTION("forbidden synchronization") + { + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + + SECTION("CellValuePerNode") + { + CellValuePerNode<int> cell_value_per_node{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_value_per_node), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (node)"); + } + + SECTION("CellValuePerEdge") + { + CellValuePerEdge<int> cell_value_per_edge{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_value_per_edge), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (edge)"); + } + + SECTION("CellValuePerFace") + { + CellValuePerFace<int> cell_value_per_face{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_value_per_face), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (face)"); + } + + SECTION("FaceValuePerNode") + { + FaceValuePerNode<int> face_value_per_node{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(face_value_per_node), + "unexpected error: synchronization requires sub-item type (face) to be of lower " + "dimension than item (node)"); + } + + SECTION("FaceValuePerEdge") + { + FaceValuePerEdge<int> face_value_per_edge{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(face_value_per_edge), + "unexpected error: synchronization requires sub-item type (face) to be of lower " + "dimension than item (edge)"); + } + + SECTION("EdgeValuePerNode") + { + EdgeValuePerNode<int> edge_value_per_node{connectivity}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(edge_value_per_node), + "unexpected error: synchronization requires sub-item type (edge) to be of lower " + "dimension than item (node)"); + } + } } + } - SECTION("synchonize FaceValue") - { - const auto face_owner = connectivity.faceOwner(); - const auto face_number = connectivity.faceNumber(); - - FaceValue<int> face_value_ref{connectivity}; - parallel_for( - connectivity.numberOfFaces(), - PUGS_LAMBDA(const FaceId face_id) { face_value_ref[face_id] = face_owner[face_id] + face_number[face_id]; }); + SECTION("SubItemArrayPerItem") + { + auto is_same_item_array = [](auto a, auto b) { + using IndexT = typename decltype(a)::index_type; + bool is_same = true; + for (IndexT i_item = 0; i_item < a.numberOfItems(); ++i_item) { + for (size_t l = 0; l < a.numberOfSubArrays(i_item); ++l) { + for (size_t k = 0; k < a.sizeOfArrays(); ++k) { + is_same &= (a(i_item, l)[k] == b(i_item, l)[k]); + } + } + } + return parallel::allReduceAnd(is_same); + }; + + auto reset_ghost_arrays = [](auto sub_item_array_per_item, auto item_owner, auto value) { + using IndexT = typename decltype(sub_item_array_per_item)::index_type; + static_assert(std::is_same_v<typename decltype(sub_item_array_per_item)::index_type, + typename decltype(item_owner)::index_type>); + for (IndexT i_item = 0; i_item < sub_item_array_per_item.numberOfItems(); ++i_item) { + if (item_owner[i_item] != static_cast<int>(parallel::rank())) { + for (size_t l = 0; l < sub_item_array_per_item.numberOfSubArrays(i_item); ++l) { + sub_item_array_per_item(i_item, l).fill(value); + } + } + } + }; - FaceValue<int> face_value{connectivity}; - parallel_for( - connectivity.numberOfFaces(), - PUGS_LAMBDA(const FaceId face_id) { face_value[face_id] = parallel::rank() + face_number[face_id]; }); + SECTION("1D") + { + constexpr size_t Dimension = 1; + using ConnectivityType = Connectivity<Dimension>; + + const ConnectivityType& connectivity = MeshDataBaseForTests::get().unordered1DMesh()->connectivity(); + + SECTION("synchonize NodeArrayPerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + NodeArrayPerCell<int> node_array_per_cell_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < node_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < node_array_per_cell_ref.sizeOfArrays(); ++k) { + node_array_per_cell_ref(cell_id, j)[k] = cell_owner[cell_id] + cell_number[cell_id] + j + 2 * k; + } + } + }); + + NodeArrayPerCell<int> node_array_per_cell{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < node_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < node_array_per_cell_ref.sizeOfArrays(); ++k) { + node_array_per_cell(cell_id, j)[k] = parallel::rank() + cell_number[cell_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(node_array_per_cell, node_array_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_array_per_cell); + + REQUIRE(is_same_item_array(node_array_per_cell, node_array_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(node_array_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_array(node_array_per_cell, node_array_per_cell_ref)); + synchronizer.synchronize(node_array_per_cell); + REQUIRE(is_same_item_array(node_array_per_cell, node_array_per_cell_ref)); + } + } - if (parallel::size() > 1) { - REQUIRE(not is_same_item_value(face_value, face_value_ref)); + SECTION("synchonize EdgeArrayPerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + EdgeArrayPerCell<int> edge_array_per_cell_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < edge_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < edge_array_per_cell_ref.sizeOfArrays(); ++k) { + edge_array_per_cell_ref(cell_id, j)[k] = cell_owner[cell_id] + cell_number[cell_id] + j + 2 * k; + } + } + }); + + EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < edge_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < edge_array_per_cell_ref.sizeOfArrays(); ++k) { + edge_array_per_cell(cell_id, j)[k] = parallel::rank() + cell_number[cell_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(edge_array_per_cell, edge_array_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_array_per_cell); + + REQUIRE(is_same_item_array(edge_array_per_cell, edge_array_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(edge_array_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_array(edge_array_per_cell, edge_array_per_cell_ref)); + synchronizer.synchronize(edge_array_per_cell); + REQUIRE(is_same_item_array(edge_array_per_cell, edge_array_per_cell_ref)); + } } - Synchronizer synchronizer; - synchronizer.synchronize(face_value); + SECTION("synchonize FaceArrayPerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + FaceArrayPerCell<int> face_array_per_cell_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < face_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < face_array_per_cell_ref.sizeOfArrays(); ++k) { + face_array_per_cell_ref(cell_id, j)[k] = cell_owner[cell_id] + cell_number[cell_id] + j + 2 * k; + } + } + }); + + FaceArrayPerCell<int> face_array_per_cell{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < face_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < face_array_per_cell_ref.sizeOfArrays(); ++k) { + face_array_per_cell(cell_id, j)[k] = parallel::rank() + cell_number[cell_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(face_array_per_cell, face_array_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(face_array_per_cell); + + REQUIRE(is_same_item_array(face_array_per_cell, face_array_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(face_array_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_array(face_array_per_cell, face_array_per_cell_ref)); + synchronizer.synchronize(face_array_per_cell); + REQUIRE(is_same_item_array(face_array_per_cell, face_array_per_cell_ref)); + } + } - REQUIRE(is_same_item_value(face_value, face_value_ref)); + SECTION("forbidden synchronization") + { + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + + SECTION("CellArrayPerNode") + { + CellArrayPerNode<int> cell_array_per_node{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_array_per_node), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (node)"); + } + + SECTION("CellArrayPerEdge") + { + CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_array_per_edge), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (edge)"); + } + + SECTION("CellArrayPerFace") + { + CellArrayPerFace<int> cell_array_per_face{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_array_per_face), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (face)"); + } + } } - SECTION("synchonize CellValue") + SECTION("2D") { - const auto cell_owner = connectivity.cellOwner(); - const auto cell_number = connectivity.cellNumber(); + constexpr size_t Dimension = 2; + using ConnectivityType = Connectivity<Dimension>; + + const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->connectivity(); + + SECTION("synchonize NodeArrayPerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + NodeArrayPerCell<int> node_array_per_cell_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < node_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < node_array_per_cell_ref.sizeOfArrays(); ++k) { + node_array_per_cell_ref(cell_id, j)[k] = cell_owner[cell_id] + cell_number[cell_id] + j + 2 * k; + } + } + }); + + NodeArrayPerCell<int> node_array_per_cell{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < node_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < node_array_per_cell_ref.sizeOfArrays(); ++k) { + node_array_per_cell(cell_id, j)[k] = parallel::rank() + cell_number[cell_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(node_array_per_cell, node_array_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_array_per_cell); + + REQUIRE(is_same_item_array(node_array_per_cell, node_array_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(node_array_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_array(node_array_per_cell, node_array_per_cell_ref)); + synchronizer.synchronize(node_array_per_cell); + REQUIRE(is_same_item_array(node_array_per_cell, node_array_per_cell_ref)); + } + } - CellValue<int> cell_value_ref{connectivity}; - parallel_for( - connectivity.numberOfCells(), - PUGS_LAMBDA(const CellId cell_id) { cell_value_ref[cell_id] = cell_owner[cell_id] + cell_number[cell_id]; }); + SECTION("synchonize EdgeArrayPerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + EdgeArrayPerCell<int> edge_array_per_cell_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < edge_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < edge_array_per_cell_ref.sizeOfArrays(); ++k) { + edge_array_per_cell_ref(cell_id, j)[k] = cell_owner[cell_id] + cell_number[cell_id] + j + 2 * k; + } + } + }); + + EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < edge_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < edge_array_per_cell_ref.sizeOfArrays(); ++k) { + edge_array_per_cell(cell_id, j)[k] = parallel::rank() + cell_number[cell_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(edge_array_per_cell, edge_array_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_array_per_cell); + + REQUIRE(is_same_item_array(edge_array_per_cell, edge_array_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(edge_array_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_array(edge_array_per_cell, edge_array_per_cell_ref)); + synchronizer.synchronize(edge_array_per_cell); + REQUIRE(is_same_item_array(edge_array_per_cell, edge_array_per_cell_ref)); + } + } - CellValue<int> cell_value{connectivity}; - parallel_for( - connectivity.numberOfCells(), - PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = parallel::rank() + cell_number[cell_id]; }); + SECTION("synchonize FaceArrayPerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + FaceArrayPerCell<int> face_array_per_cell_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < face_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < face_array_per_cell_ref.sizeOfArrays(); ++k) { + face_array_per_cell_ref(cell_id, j)[k] = cell_owner[cell_id] + cell_number[cell_id] + j + 2 * k; + } + } + }); + + FaceArrayPerCell<int> face_array_per_cell{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < face_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < face_array_per_cell_ref.sizeOfArrays(); ++k) { + face_array_per_cell(cell_id, j)[k] = parallel::rank() + cell_number[cell_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(face_array_per_cell, face_array_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(face_array_per_cell); + + REQUIRE(is_same_item_array(face_array_per_cell, face_array_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(face_array_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_array(face_array_per_cell, face_array_per_cell_ref)); + synchronizer.synchronize(face_array_per_cell); + REQUIRE(is_same_item_array(face_array_per_cell, face_array_per_cell_ref)); + } + } - if (parallel::size() > 1) { - REQUIRE(not is_same_item_value(cell_value, cell_value_ref)); + SECTION("synchonize NodeArrayPerFace") + { + const auto face_owner = connectivity.faceOwner(); + const auto face_number = connectivity.faceNumber(); + + NodeArrayPerFace<int> node_array_per_face_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t j = 0; j < node_array_per_face_ref.numberOfSubArrays(face_id); ++j) { + for (size_t k = 0; k < node_array_per_face_ref.sizeOfArrays(); ++k) { + node_array_per_face_ref(face_id, j)[k] = face_owner[face_id] + face_number[face_id] + j + 2 * k; + } + } + }); + + NodeArrayPerFace<int> node_array_per_face{connectivity, 3}; + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t j = 0; j < node_array_per_face_ref.numberOfSubArrays(face_id); ++j) { + for (size_t k = 0; k < node_array_per_face_ref.sizeOfArrays(); ++k) { + node_array_per_face(face_id, j)[k] = parallel::rank() + face_number[face_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(node_array_per_face, node_array_per_face_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_array_per_face); + + REQUIRE(is_same_item_array(node_array_per_face, node_array_per_face_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(node_array_per_face, face_owner, 0); + REQUIRE(not is_same_item_array(node_array_per_face, node_array_per_face_ref)); + synchronizer.synchronize(node_array_per_face); + REQUIRE(is_same_item_array(node_array_per_face, node_array_per_face_ref)); + } } - Synchronizer synchronizer; - synchronizer.synchronize(cell_value); + SECTION("synchonize NodeArrayPerEdge") + { + const auto edge_owner = connectivity.edgeOwner(); + const auto edge_number = connectivity.edgeNumber(); + + NodeArrayPerEdge<int> node_array_per_edge_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t j = 0; j < node_array_per_edge_ref.numberOfSubArrays(edge_id); ++j) { + for (size_t k = 0; k < node_array_per_edge_ref.sizeOfArrays(); ++k) { + node_array_per_edge_ref(edge_id, j)[k] = edge_owner[edge_id] + edge_number[edge_id] + j + 2 * k; + } + } + }); + + NodeArrayPerEdge<int> node_array_per_edge{connectivity, 3}; + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t j = 0; j < node_array_per_edge_ref.numberOfSubArrays(edge_id); ++j) { + for (size_t k = 0; k < node_array_per_edge_ref.sizeOfArrays(); ++k) { + node_array_per_edge(edge_id, j)[k] = parallel::rank() + edge_number[edge_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(node_array_per_edge, node_array_per_edge_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_array_per_edge); + + REQUIRE(is_same_item_array(node_array_per_edge, node_array_per_edge_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(node_array_per_edge, edge_owner, 0); + REQUIRE(not is_same_item_array(node_array_per_edge, node_array_per_edge_ref)); + synchronizer.synchronize(node_array_per_edge); + REQUIRE(is_same_item_array(node_array_per_edge, node_array_per_edge_ref)); + } + } - REQUIRE(is_same_item_value(cell_value, cell_value_ref)); + SECTION("forbidden synchronization") + { + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + + SECTION("CellArrayPerNode") + { + CellArrayPerNode<int> cell_array_per_node{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_array_per_node), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (node)"); + } + + SECTION("CellArrayPerEdge") + { + CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_array_per_edge), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (edge)"); + } + + SECTION("CellArrayPerFace") + { + CellArrayPerFace<int> cell_array_per_face{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_array_per_face), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (face)"); + } + + SECTION("FaceArrayPerNode") + { + FaceArrayPerNode<int> face_array_per_node{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(face_array_per_node), + "unexpected error: synchronization requires sub-item type (face) to be of lower " + "dimension than item (node)"); + } + + SECTION("EdgeArrayPerNode") + { + EdgeArrayPerNode<int> edge_array_per_node{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(edge_array_per_node), + "unexpected error: synchronization requires sub-item type (edge) to be of lower " + "dimension than item (node)"); + } + } } - SECTION("synchonize CellArray") + SECTION("3D") { - const auto cell_owner = connectivity.cellOwner(); - const auto cell_number = connectivity.cellNumber(); - - CellArray<int> cell_array_ref{connectivity, 3}; - parallel_for( - connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - for (size_t i = 0; i < cell_array_ref.sizeOfArrays(); ++i) { - cell_array_ref[cell_id][i] = (i + 1) * cell_owner[cell_id] + i + cell_number[cell_id]; - } - }); + constexpr size_t Dimension = 3; + using ConnectivityType = Connectivity<Dimension>; + + const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->connectivity(); + + SECTION("synchonize NodeArrayPerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + NodeArrayPerCell<int> node_array_per_cell_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < node_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < node_array_per_cell_ref.sizeOfArrays(); ++k) { + node_array_per_cell_ref(cell_id, j)[k] = cell_owner[cell_id] + cell_number[cell_id] + j + 2 * k; + } + } + }); + + NodeArrayPerCell<int> node_array_per_cell{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < node_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < node_array_per_cell_ref.sizeOfArrays(); ++k) { + node_array_per_cell(cell_id, j)[k] = parallel::rank() + cell_number[cell_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(node_array_per_cell, node_array_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_array_per_cell); + + REQUIRE(is_same_item_array(node_array_per_cell, node_array_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(node_array_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_array(node_array_per_cell, node_array_per_cell_ref)); + synchronizer.synchronize(node_array_per_cell); + REQUIRE(is_same_item_array(node_array_per_cell, node_array_per_cell_ref)); + } + } - CellArray<int> cell_array{connectivity, 3}; - parallel_for( - connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { - for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) { - cell_array[cell_id][i] = (i + 1) * parallel::rank() + i + cell_number[cell_id]; - } - }); + SECTION("synchonize EdgeArrayPerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + EdgeArrayPerCell<int> edge_array_per_cell_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < edge_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < edge_array_per_cell_ref.sizeOfArrays(); ++k) { + edge_array_per_cell_ref(cell_id, j)[k] = cell_owner[cell_id] + cell_number[cell_id] + j + 2 * k; + } + } + }); + + EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < edge_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < edge_array_per_cell_ref.sizeOfArrays(); ++k) { + edge_array_per_cell(cell_id, j)[k] = parallel::rank() + cell_number[cell_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(edge_array_per_cell, edge_array_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_array_per_cell); + + REQUIRE(is_same_item_array(edge_array_per_cell, edge_array_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(edge_array_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_array(edge_array_per_cell, edge_array_per_cell_ref)); + synchronizer.synchronize(edge_array_per_cell); + REQUIRE(is_same_item_array(edge_array_per_cell, edge_array_per_cell_ref)); + } + } + + SECTION("synchonize FaceArrayPerCell") + { + const auto cell_owner = connectivity.cellOwner(); + const auto cell_number = connectivity.cellNumber(); + + FaceArrayPerCell<int> face_array_per_cell_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < face_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < face_array_per_cell_ref.sizeOfArrays(); ++k) { + face_array_per_cell_ref(cell_id, j)[k] = cell_owner[cell_id] + cell_number[cell_id] + j + 2 * k; + } + } + }); + + FaceArrayPerCell<int> face_array_per_cell{connectivity, 3}; + parallel_for( + connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + for (size_t j = 0; j < face_array_per_cell_ref.numberOfSubArrays(cell_id); ++j) { + for (size_t k = 0; k < face_array_per_cell_ref.sizeOfArrays(); ++k) { + face_array_per_cell(cell_id, j)[k] = parallel::rank() + cell_number[cell_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(face_array_per_cell, face_array_per_cell_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(face_array_per_cell); + + REQUIRE(is_same_item_array(face_array_per_cell, face_array_per_cell_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(face_array_per_cell, cell_owner, 0); + REQUIRE(not is_same_item_array(face_array_per_cell, face_array_per_cell_ref)); + synchronizer.synchronize(face_array_per_cell); + REQUIRE(is_same_item_array(face_array_per_cell, face_array_per_cell_ref)); + } + } - if (parallel::size() > 1) { - REQUIRE(not is_same_item_array(cell_array, cell_array_ref)); + SECTION("synchonize NodeArrayPerFace") + { + const auto face_owner = connectivity.faceOwner(); + const auto face_number = connectivity.faceNumber(); + + NodeArrayPerFace<int> node_array_per_face_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t j = 0; j < node_array_per_face_ref.numberOfSubArrays(face_id); ++j) { + for (size_t k = 0; k < node_array_per_face_ref.sizeOfArrays(); ++k) { + node_array_per_face_ref(face_id, j)[k] = face_owner[face_id] + face_number[face_id] + j + 2 * k; + } + } + }); + + NodeArrayPerFace<int> node_array_per_face{connectivity, 3}; + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t j = 0; j < node_array_per_face_ref.numberOfSubArrays(face_id); ++j) { + for (size_t k = 0; k < node_array_per_face_ref.sizeOfArrays(); ++k) { + node_array_per_face(face_id, j)[k] = parallel::rank() + face_number[face_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(node_array_per_face, node_array_per_face_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_array_per_face); + + REQUIRE(is_same_item_array(node_array_per_face, node_array_per_face_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(node_array_per_face, face_owner, 0); + REQUIRE(not is_same_item_array(node_array_per_face, node_array_per_face_ref)); + synchronizer.synchronize(node_array_per_face); + REQUIRE(is_same_item_array(node_array_per_face, node_array_per_face_ref)); + } } - Synchronizer synchronizer; - synchronizer.synchronize(cell_array); + SECTION("synchonize EdgeArrayPerFace") + { + const auto face_owner = connectivity.faceOwner(); + const auto face_number = connectivity.faceNumber(); + + EdgeArrayPerFace<int> edge_array_per_face_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t j = 0; j < edge_array_per_face_ref.numberOfSubArrays(face_id); ++j) { + for (size_t k = 0; k < edge_array_per_face_ref.sizeOfArrays(); ++k) { + edge_array_per_face_ref(face_id, j)[k] = face_owner[face_id] + face_number[face_id] + j + 2 * k; + } + } + }); + + EdgeArrayPerFace<int> edge_array_per_face{connectivity, 3}; + parallel_for( + connectivity.numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) { + for (size_t j = 0; j < edge_array_per_face_ref.numberOfSubArrays(face_id); ++j) { + for (size_t k = 0; k < edge_array_per_face_ref.sizeOfArrays(); ++k) { + edge_array_per_face(face_id, j)[k] = parallel::rank() + face_number[face_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(edge_array_per_face, edge_array_per_face_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(edge_array_per_face); + + REQUIRE(is_same_item_array(edge_array_per_face, edge_array_per_face_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(edge_array_per_face, face_owner, 0); + REQUIRE(not is_same_item_array(edge_array_per_face, edge_array_per_face_ref)); + synchronizer.synchronize(edge_array_per_face); + REQUIRE(is_same_item_array(edge_array_per_face, edge_array_per_face_ref)); + } + } - REQUIRE(is_same_item_array(cell_array, cell_array_ref)); + SECTION("synchonize NodeArrayPerEdge") + { + const auto edge_owner = connectivity.edgeOwner(); + const auto edge_number = connectivity.edgeNumber(); + + NodeArrayPerEdge<int> node_array_per_edge_ref{connectivity, 3}; + + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t j = 0; j < node_array_per_edge_ref.numberOfSubArrays(edge_id); ++j) { + for (size_t k = 0; k < node_array_per_edge_ref.sizeOfArrays(); ++k) { + node_array_per_edge_ref(edge_id, j)[k] = edge_owner[edge_id] + edge_number[edge_id] + j + 2 * k; + } + } + }); + + NodeArrayPerEdge<int> node_array_per_edge{connectivity, 3}; + parallel_for( + connectivity.numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) { + for (size_t j = 0; j < node_array_per_edge_ref.numberOfSubArrays(edge_id); ++j) { + for (size_t k = 0; k < node_array_per_edge_ref.sizeOfArrays(); ++k) { + node_array_per_edge(edge_id, j)[k] = parallel::rank() + edge_number[edge_id] + j + 2 * k; + } + } + }); + + if (parallel::size() > 1) { + REQUIRE(not is_same_item_array(node_array_per_edge, node_array_per_edge_ref)); + } + + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + synchronizer.synchronize(node_array_per_edge); + + REQUIRE(is_same_item_array(node_array_per_edge, node_array_per_edge_ref)); + + // Check that exchange sizes are correctly stored (require + // lines to be covered) + if (parallel::size() > 1) { + reset_ghost_arrays(node_array_per_edge, edge_owner, 0); + REQUIRE(not is_same_item_array(node_array_per_edge, node_array_per_edge_ref)); + synchronizer.synchronize(node_array_per_edge); + REQUIRE(is_same_item_array(node_array_per_edge, node_array_per_edge_ref)); + } + } + + SECTION("forbidden synchronization") + { + Synchronizer& synchronizer = SynchronizerManager::instance().getConnectivitySynchronizer(&connectivity); + + SECTION("CellArrayPerNode") + { + CellArrayPerNode<int> cell_array_per_node{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_array_per_node), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (node)"); + } + + SECTION("CellArrayPerEdge") + { + CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_array_per_edge), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (edge)"); + } + + SECTION("CellArrayPerFace") + { + CellArrayPerFace<int> cell_array_per_face{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(cell_array_per_face), + "unexpected error: synchronization requires sub-item type (cell) to be of lower " + "dimension than item (face)"); + } + + SECTION("FaceArrayPerNode") + { + FaceArrayPerNode<int> face_array_per_node{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(face_array_per_node), + "unexpected error: synchronization requires sub-item type (face) to be of lower " + "dimension than item (node)"); + } + + SECTION("FaceArrayPerEdge") + { + FaceArrayPerEdge<int> face_array_per_edge{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(face_array_per_edge), + "unexpected error: synchronization requires sub-item type (face) to be of lower " + "dimension than item (edge)"); + } + + SECTION("EdgeArrayPerNode") + { + EdgeArrayPerNode<int> edge_array_per_node{connectivity, 3}; + REQUIRE_THROWS_WITH(synchronizer.synchronize(edge_array_per_node), + "unexpected error: synchronization requires sub-item type (edge) to be of lower " + "dimension than item (node)"); + } + } } } }