diff --git a/src/mesh/Connectivity.hpp b/src/mesh/Connectivity.hpp
index 261437091656cdbbd7111e8f68d75c66ca8b925a..bebb67e014cddf792cd4cd47f462024edb448f6e 100644
--- a/src/mesh/Connectivity.hpp
+++ b/src/mesh/Connectivity.hpp
@@ -20,7 +20,6 @@
 #include <ConnectivityComputer.hpp>
 
 #include <vector>
-#include <unordered_map>
 #include <algorithm>
 
 #include <CellType.hpp>
@@ -214,6 +213,24 @@ class Connectivity final
     return m_node_number;
   }
 
+  template <ItemType item_type>
+  PASTIS_INLINE
+  const auto& number() const
+  {
+    if constexpr(item_type == ItemType::cell) {
+      return m_cell_number;
+    } else if constexpr(item_type == ItemType::face) {
+      return m_face_number;
+    } else if constexpr(item_type == ItemType::edge) {
+      return m_edge_number;
+    } else if constexpr(item_type == ItemType::node) {
+      return m_node_number;
+    } else {
+      static_assert(item_type == ItemType::cell, "unknown ItemType");
+      return m_cell_number;
+    }
+  }
+
   PASTIS_INLINE
   const auto& cellOwner() const
   {
@@ -242,19 +259,19 @@ class Connectivity final
 
   template <ItemType item_type>
   PASTIS_INLINE
-  ItemValue<const bool, item_type> isOwned() const
+  const auto& owner() const
   {
     if constexpr(item_type == ItemType::cell) {
-      return m_cell_is_owned;
+      return m_cell_owner;
     } else if constexpr(item_type == ItemType::face) {
-      return m_face_is_owned;
+      return m_face_owner;
     } else if constexpr(item_type == ItemType::edge) {
-      return m_edge_is_owned;
+      return m_edge_owner;
     } else if constexpr(item_type == ItemType::node) {
-      return m_node_is_owned;
+      return m_node_owner;
     } else {
       static_assert(item_type == ItemType::cell, "unknown ItemType");
-      return {};
+      return m_cell_owner;
     }
   }
 
@@ -284,6 +301,24 @@ class Connectivity final
     return m_node_is_owned;
   }
 
+  template <ItemType item_type>
+  PASTIS_INLINE
+  const auto& isOwned() const
+  {
+    if constexpr(item_type == ItemType::cell) {
+      return m_cell_is_owned;
+    } else if constexpr(item_type == ItemType::face) {
+      return m_face_is_owned;
+    } else if constexpr(item_type == ItemType::edge) {
+      return m_edge_is_owned;
+    } else if constexpr(item_type == ItemType::node) {
+      return m_node_is_owned;
+    } else {
+      static_assert(item_type == ItemType::cell, "unknown ItemType");
+      return m_cell_is_owned;
+    }
+  }
+
   PASTIS_INLINE
   const bool& isConnectivityMatrixBuilt(const ItemType& item_type_0,
                                         const ItemType& item_type_1) const
diff --git a/src/mesh/ItemValueSynchronizer.hpp b/src/mesh/ItemValueSynchronizer.hpp
index e4189014fdf1ce26b21d493bebe28dcd847ecffc..4a2c9939df80e95183b9d03a25e3b91a2c509455 100644
--- a/src/mesh/ItemValueSynchronizer.hpp
+++ b/src/mesh/ItemValueSynchronizer.hpp
@@ -4,8 +4,99 @@
 #include <ItemValue.hpp>
 #include <Connectivity.hpp>
 
+#include <unordered_map>
+
 class ItemValueSynchronizer
 {
+  template <typename ConnectivityType,
+            typename DataType,
+            ItemType item_type,
+            typename ConnectivityPtr>
+  PASTIS_INLINE
+  void _synchronize(const ConnectivityType& connectivity,
+                    ItemValue<DataType, item_type, ConnectivityPtr>& item_value)
+  {
+    static_assert(not std::is_abstract_v<ConnectivityType>,
+                  "_synchronize must be called on a concrete connectivity");
+    const auto& item_owner =  connectivity.template owner<item_type>();
+
+    using ItemId = ItemIdT<item_type>;
+    std::vector<std::vector<ItemId>> ghost_items_per_proc(parallel::size());
+    for (ItemId item_id=0; item_id<item_value.size(); ++item_id) {
+      if (const size_t owner = item_owner[item_id]; owner != parallel::rank()) {
+        ghost_items_per_proc[owner].emplace_back(item_id);
+      }
+    }
+
+    Array<unsigned int> local_number_of_requested_values(parallel::size());
+    for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) {
+      local_number_of_requested_values[i_rank] = ghost_items_per_proc[i_rank].size();
+    }
+
+    Array<unsigned int> local_number_of_values_to_send
+        = parallel::allToAll(local_number_of_requested_values);
+
+    std::vector<Array<const int>> requested_item_number_list_by_proc(parallel::size());
+    const auto& item_number = connectivity.template number<item_type>();
+    for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) {
+      const auto& ghost_items = ghost_items_per_proc[i_rank];
+      Array<int> item_number_list(ghost_items.size());
+      for (size_t i_item = 0; i_item<ghost_items.size(); ++i_item) {
+        item_number_list[i_item] = item_number[ghost_items[i_item]];
+      }
+      requested_item_number_list_by_proc[i_rank] = item_number_list;
+    }
+
+    std::vector<Array<int>> to_send_item_number_list_by_proc(parallel::size());
+    for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) {
+      to_send_item_number_list_by_proc[i_rank] = Array<int>{local_number_of_values_to_send[i_rank]};
+    }
+
+    parallel::exchange(requested_item_number_list_by_proc, to_send_item_number_list_by_proc);
+
+    std::unordered_map<int, ItemId> item_number_to_id_correspondance(connectivity.template numberOf<item_type>());
+    for (ItemId item_id=0; item_id<item_number.size(); ++item_id) {
+      item_number_to_id_correspondance[item_number[item_id]] = item_id;
+    }
+
+    std::vector<Array<const ItemId>> to_send_item_id_list_by_proc(parallel::size());
+    for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) {
+      Array<ItemId> to_send_item_id{local_number_of_values_to_send[i_rank]};
+      const Array<int>& to_send_item_number = to_send_item_number_list_by_proc[i_rank];
+      for (size_t i=0; i<to_send_item_number.size(); ++i) {
+        to_send_item_id[i] = item_number_to_id_correspondance[to_send_item_number[i]];
+      }
+      to_send_item_id_list_by_proc[i_rank] = to_send_item_id;
+    }
+
+    std::vector<Array<const DataType>> to_send_data_by_proc(parallel::size());
+    for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) {
+      Array<DataType> to_send_data{local_number_of_values_to_send[i_rank]};
+      const Array<const ItemId>& to_send_item_id = to_send_item_id_list_by_proc[i_rank];
+      for (size_t i=0; i<to_send_item_id.size(); ++i) {
+        to_send_data[i] = item_value[to_send_item_id[i]];
+      }
+      to_send_data_by_proc[i_rank] = to_send_data;
+    }
+
+    std::vector<Array<DataType>> requested_data_list_by_proc(parallel::size());
+    for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) {
+      const auto& ghost_items = ghost_items_per_proc[i_rank];
+      requested_data_list_by_proc[i_rank] = Array<DataType>{ghost_items.size()};
+    }
+
+    parallel::exchange(to_send_data_by_proc, requested_data_list_by_proc);
+
+    for (size_t i_rank=0; i_rank<parallel::size(); ++i_rank) {
+      const auto& ghost_items = ghost_items_per_proc[i_rank];
+      const auto& requested_data = requested_data_list_by_proc[i_rank];
+      for (size_t i=0; i<ghost_items.size(); ++i) {
+        item_value[ghost_items[i]] = requested_data[i];
+      }
+      requested_data_list_by_proc[i_rank] = Array<DataType>{ghost_items.size()};
+    }
+  }
+
  public:
   template <typename DataType,
             ItemType item_type,
@@ -13,13 +104,30 @@ class ItemValueSynchronizer
   PASTIS_INLINE
   void synchronize(ItemValue<DataType, item_type, ConnectivityPtr>& item_value)
   {
-    pout() << "Calling synchronize...\n";
-    auto connectivity_ptr = item_value.connectivity_ptr();
-    Assert(connectivity_ptr.use_count()>0, "No connectivity is associated to this ItemValue");
-    parallel::barrier();
-    parallel::Messenger::destroy();
-    pout() << __FILE__ << ':' << __LINE__ << ": NIY!\n";
-    std::exit(0);
+    static int cpt=0;
+    pout() << "Calling synchronize(" << cpt++ << ")...\n";
+
+    Assert(item_value.connectivity_ptr().use_count()>0, "No connectivity is associated to this ItemValue");
+    const IConnectivity& connectivity = *item_value.connectivity_ptr();
+
+    switch (connectivity.dimension()) {
+      case 1: {
+        this->_synchronize(static_cast<const Connectivity1D&>(connectivity), item_value);
+        break;
+      }
+      case 2: {
+        this->_synchronize(static_cast<const Connectivity2D&>(connectivity), item_value);
+        break;
+      }
+      case 3: {
+        this->_synchronize(static_cast<const Connectivity3D&>(connectivity), item_value);
+        break;
+      }
+      default: {
+        perr() << __FILE__ << ':' << __LINE__ << ": unexpected dimension\n";
+        std::terminate();
+      }
+    }
   }
 
   PASTIS_INLINE
diff --git a/src/mesh/ItemValueUtils.hpp b/src/mesh/ItemValueUtils.hpp
index cd1110fe78461856f5f82d31ab12789322776d07..771b4e2b675aaac2eab00ba8cb5575f3963e6fbb 100644
--- a/src/mesh/ItemValueUtils.hpp
+++ b/src/mesh/ItemValueUtils.hpp
@@ -17,8 +17,10 @@ min(const ItemValue<DataType, item_type>& item_value)
   using data_type = std::remove_const_t<typename ItemValueType::data_type>;
   using index_type = typename ItemValueType::index_type;
 
-  ItemValue<const bool, item_type> is_owned
+  const auto& is_owned
       = [&] (const IConnectivity& connectivity) {
+          Assert((connectivity.dimension()>0) and (connectivity.dimension()<=3),
+                 "unexpected connectivity dimension");
 
           switch (connectivity.dimension()) {
             case 1: {
@@ -37,18 +39,19 @@ min(const ItemValue<DataType, item_type>& item_value)
               break;
             }
             default: {
-              Assert((connectivity.dimension()>0) and (connectivity.dimension()<=3),
-                     "unexpected connectivity dimension");
-              return ItemValue<const bool, item_type>{};
+              perr() << __FILE__ << ':' << __LINE__ << ": unexpected dimension\n";
+              std::terminate();
             }
           }
         } (*item_value.connectivity_ptr());
 
+  using IsOwnedType = std::remove_reference_t<decltype(is_owned)>;
+
   class ItemValueMin
   {
    private:
     const ItemValueType& m_item_value;
-    const ItemValue<const bool, item_type>& m_is_owned;
+    const IsOwnedType& m_is_owned;
 
    public:
     PASTIS_INLINE
@@ -84,7 +87,7 @@ min(const ItemValue<DataType, item_type>& item_value)
 
     PASTIS_INLINE
     ItemValueMin(const ItemValueType& item_value,
-                 const ItemValue<const bool, item_type>& is_owned)
+                 const IsOwnedType& is_owned)
         : m_item_value(item_value),
           m_is_owned(is_owned)
     {
@@ -108,8 +111,10 @@ max(const ItemValue<DataType, item_type>& item_value)
   using data_type = std::remove_const_t<typename ItemValueType::data_type>;
   using index_type = typename ItemValueType::index_type;
 
-  ItemValue<const bool, item_type> is_owned
+  const auto& is_owned
       = [&] (const IConnectivity& connectivity) {
+          Assert((connectivity.dimension()>0) and (connectivity.dimension()<=3),
+                 "unexpected connectivity dimension");
 
           switch (connectivity.dimension()) {
             case 1: {
@@ -128,18 +133,19 @@ max(const ItemValue<DataType, item_type>& item_value)
               break;
             }
             default: {
-              Assert((connectivity.dimension()>0) and (connectivity.dimension()<=3),
-                     "unexpected connectivity dimension");
-              return ItemValue<const bool, item_type>{};
+              perr() << __FILE__ << ':' << __LINE__ << ": unexpected dimension\n";
+              std::terminate();
             }
           }
         } (*item_value.connectivity_ptr());
 
+  using IsOwnedType = std::remove_reference_t<decltype(is_owned)>;
+
   class ItemValueMax
   {
    private:
     const ItemValueType& m_item_value;
-    const ItemValue<const bool, item_type>& m_is_owned;
+    const IsOwnedType& m_is_owned;
 
    public:
     PASTIS_INLINE
@@ -175,7 +181,7 @@ max(const ItemValue<DataType, item_type>& item_value)
 
     PASTIS_INLINE
     ItemValueMax(const ItemValueType& item_value,
-                 const ItemValue<const bool, item_type>& is_owned)
+                 const IsOwnedType& is_owned)
         : m_item_value(item_value),
           m_is_owned(is_owned)
     {
@@ -200,8 +206,10 @@ sum(const ItemValue<DataType, item_type>& item_value)
   using data_type = std::remove_const_t<typename ItemValueType::data_type>;
   using index_type = typename ItemValueType::index_type;
 
-  ItemValue<const bool, item_type> is_owned
+  const auto& is_owned
       = [&] (const IConnectivity& connectivity) {
+          Assert((connectivity.dimension()>0) and (connectivity.dimension()<=3),
+                 "unexpected connectivity dimension");
 
           switch (connectivity.dimension()) {
             case 1: {
@@ -220,18 +228,19 @@ sum(const ItemValue<DataType, item_type>& item_value)
               break;
             }
             default: {
-              Assert((connectivity.dimension()>0) and (connectivity.dimension()<=3),
-                     "unexpected connectivity dimension");
-              return ItemValue<const bool, item_type>{};
+              perr() << __FILE__ << ':' << __LINE__ << ": unexpected dimension\n";
+              std::terminate();
             }
           }
         } (*item_value.connectivity_ptr());
 
+  using IsOwnedType = std::remove_reference_t<decltype(is_owned)>;
+
   class ItemValueSum
   {
    private:
     const ItemValueType& m_item_value;
-    const ItemValue<const bool, item_type>& m_is_owned;
+    const IsOwnedType& m_is_owned;
 
    public:
     PASTIS_INLINE
@@ -269,7 +278,7 @@ sum(const ItemValue<DataType, item_type>& item_value)
 
     PASTIS_INLINE
     ItemValueSum(const ItemValueType& item_value,
-                 const ItemValue<const bool, item_type>& is_owned)
+                 const IsOwnedType& is_owned)
         : m_item_value(item_value),
           m_is_owned(is_owned)
     {