diff --git a/src/mesh/Synchronizer.hpp b/src/mesh/Synchronizer.hpp index fcf5cc740d898ee9f5ec2cc5648205a1f8339c14..4f688e18e4d21545d8b4b6b59cac9b3f74223f49 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> @@ -33,6 +35,11 @@ class Synchronizer 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& _getRequestedItemInfo() @@ -131,6 +138,67 @@ class Synchronizer } 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> @@ -243,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.itemValues(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.itemValues(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 @@ -274,22 +492,82 @@ 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 + 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_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 @@ -320,13 +598,113 @@ 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; 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/test_Synchronizer.cpp b/tests/test_Synchronizer.cpp index 29f2fbac50c20180ce048557e38c961f2b700513..54a065b475c757bdf8e108e01d1b8f859daa262d 100644 --- a/tests/test_Synchronizer.cpp +++ b/tests/test_Synchronizer.cpp @@ -757,4 +757,1566 @@ TEST_CASE("Synchronizer", "[mesh]") } } } + + SECTION("SubItemValuePerItem") + { + 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; + } + } + } + }; + + 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 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") + { + 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)); + } + } + + 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("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)); + } + } + + 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)); + } + } + + 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("3D") + { + 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)); + } + } + + 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("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)); + } + } + + 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)); + } + } + + 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)); + } + } + + 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("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); + } + } + } + }; + + 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)); + } + } + + 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)); + } + } + + 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("2D") + { + 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)); + } + } + + 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)); + } + } + + 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)); + } + } + + 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("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("3D") + { + 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)); + } + } + + 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)); + } + } + + 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)); + } + } + + 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)); + } + } + + 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)"); + } + } + } + } }