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)");
+        }
+      }
+    }
+  }
 }