diff --git a/src/mesh/SubItemValuePerItem.hpp b/src/mesh/SubItemValuePerItem.hpp
index 66af694c7255da41ff7570d294dd4777acb0164f..170ffd76d4d3bcf9908cb92c0dfa60edb5e0c07a 100644
--- a/src/mesh/SubItemValuePerItem.hpp
+++ b/src/mesh/SubItemValuePerItem.hpp
@@ -55,7 +55,8 @@ class SubItemValuePerItem
 
    public:
     template <typename IndexType>
-    PUGS_INLINE const DataType& operator[](IndexType i) const noexcept(NO_ASSERT)
+    PUGS_INLINE const DataType&
+    operator[](IndexType i) const noexcept(NO_ASSERT)
     {
       static_assert(std::is_integral_v<IndexType>, "SubView is indexed by integral values");
       Assert(i < m_size);
@@ -63,7 +64,8 @@ class SubItemValuePerItem
     }
 
     template <typename IndexType>
-    PUGS_FORCEINLINE DataType& operator[](IndexType i) noexcept(NO_ASSERT)
+    PUGS_FORCEINLINE DataType&
+    operator[](IndexType i) noexcept(NO_ASSERT)
     {
       static_assert(std::is_integral_v<IndexType>, "SubView is indexed by integral values");
       Assert(i < m_size);
@@ -91,6 +93,15 @@ class SubItemValuePerItem
     }
   };
 
+  friend PUGS_INLINE SubItemValuePerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityPtr>
+  copy(SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& source)
+  {
+    SubItemValuePerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityPtr> image(*source.m_connectivity_ptr);
+
+    image.m_values = copy(source.m_values);
+    return image;
+  }
+
   PUGS_INLINE
   bool
   isBuilt() const noexcept
@@ -109,18 +120,11 @@ class SubItemValuePerItem
     return m_values[m_host_row_map(size_t{item_id}) + i];
   }
 
-  PUGS_INLINE
-  size_t
-  numberOfValues() const noexcept(NO_ASSERT)
-  {
-    Assert(this->isBuilt());
-    return m_values.size();
-  }
-
   // Following Kokkos logic, these classes are view and const view does allow
   // changes in data
   template <typename ArrayIndexType>
-  DataType& operator[](const ArrayIndexType& i) const noexcept(NO_ASSERT)
+  DataType&
+  operator[](const ArrayIndexType& i) const noexcept(NO_ASSERT)
   {
     static_assert(std::is_integral_v<ArrayIndexType>, "index must be an integral type");
     Assert(this->isBuilt());
@@ -128,13 +132,21 @@ class SubItemValuePerItem
     return m_values[i];
   }
 
+  PUGS_INLINE
+  size_t
+  numberOfValues() const noexcept(NO_ASSERT)
+  {
+    Assert(this->isBuilt());
+    return m_values.size();
+  }
+
   PUGS_INLINE
   size_t
   numberOfItems() const noexcept(NO_ASSERT)
   {
     Assert(this->isBuilt());
-    Assert(m_host_row_map.extent(0) != 0);
-    return m_host_row_map.extent(0);
+    Assert(m_host_row_map.extent(0) > 0);
+    return m_host_row_map.extent(0) - 1;
   }
 
   template <typename IndexType>
@@ -143,7 +155,7 @@ class SubItemValuePerItem
   {
     static_assert(std::is_same_v<IndexType, ItemId>, "index must be an ItemId");
     Assert(this->isBuilt());
-    Assert(item_id < m_host_row_map.extent(0));
+    Assert(item_id < this->numberOfItems());
     return m_host_row_map(size_t{item_id} + 1) - m_host_row_map(size_t{item_id});
   }
 
@@ -153,7 +165,7 @@ class SubItemValuePerItem
   {
     static_assert(std::is_same_v<IndexType, ItemId>, "index must be an ItemId");
     Assert(this->isBuilt());
-    Assert(item_id < m_host_row_map.extent(0));
+    Assert(item_id < this->numberOfItems());
     const auto& item_begin = m_host_row_map(size_t{item_id});
     const auto& item_end   = m_host_row_map(size_t{item_id} + 1);
     return SubView(m_values, item_begin, item_end);
@@ -167,7 +179,7 @@ class SubItemValuePerItem
   {
     static_assert(std::is_same_v<IndexType, ItemId>, "index must be an ItemId");
     Assert(this->isBuilt());
-    Assert(item_id < m_host_row_map.extent(0));
+    Assert(item_id < this->numberOfItems());
     const auto& item_begin = m_host_row_map(size_t{item_id});
     const auto& item_end   = m_host_row_map(size_t{item_id} + 1);
     return SubView(m_values, item_begin, item_end);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 2fc30ddd2d0845b8d113bece1fc71b2b261b4a0d..d6fff1695d274cb71cf411f9045a41525122e26a 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -102,6 +102,7 @@ add_executable (mpi_unit_tests
   test_Partitioner.cpp
   test_ItemValue.cpp
   test_ItemValueUtils.cpp
+  test_SubItemValuePerItem.cpp
   )
 
 add_library(test_Pugs_MeshDataBase
diff --git a/tests/test_SubItemValuePerItem.cpp b/tests/test_SubItemValuePerItem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b7549c444f8a93e776d427b6ff6b308033bdde50
--- /dev/null
+++ b/tests/test_SubItemValuePerItem.cpp
@@ -0,0 +1,595 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/SubItemValuePerItem.hpp>
+#include <utils/Messenger.hpp>
+
+// Instantiate to ensure full coverage is performed
+template class SubItemValuePerItem<size_t, NodeOfCell>;
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("SubItemValuePerItem", "[mesh]")
+{
+  SECTION("default constructors")
+  {
+    REQUIRE_NOTHROW(NodeValuePerEdge<int>{});
+    REQUIRE_NOTHROW(NodeValuePerFace<int>{});
+    REQUIRE_NOTHROW(NodeValuePerCell<int>{});
+
+    REQUIRE_NOTHROW(EdgeValuePerNode<int>{});
+    REQUIRE_NOTHROW(EdgeValuePerFace<int>{});
+    REQUIRE_NOTHROW(EdgeValuePerCell<int>{});
+
+    REQUIRE_NOTHROW(FaceValuePerNode<int>{});
+    REQUIRE_NOTHROW(FaceValuePerEdge<int>{});
+    REQUIRE_NOTHROW(FaceValuePerCell<int>{});
+
+    REQUIRE_NOTHROW(CellValuePerNode<int>{});
+    REQUIRE_NOTHROW(CellValuePerEdge<int>{});
+    REQUIRE_NOTHROW(CellValuePerFace<int>{});
+
+    REQUIRE(not NodeValuePerEdge<int>{}.isBuilt());
+    REQUIRE(not NodeValuePerFace<int>{}.isBuilt());
+    REQUIRE(not NodeValuePerCell<int>{}.isBuilt());
+
+    REQUIRE(not EdgeValuePerNode<int>{}.isBuilt());
+    REQUIRE(not EdgeValuePerFace<int>{}.isBuilt());
+    REQUIRE(not EdgeValuePerCell<int>{}.isBuilt());
+
+    REQUIRE(not FaceValuePerNode<int>{}.isBuilt());
+    REQUIRE(not FaceValuePerEdge<int>{}.isBuilt());
+    REQUIRE(not FaceValuePerCell<int>{}.isBuilt());
+
+    REQUIRE(not CellValuePerNode<int>{}.isBuilt());
+    REQUIRE(not CellValuePerEdge<int>{}.isBuilt());
+    REQUIRE(not CellValuePerFace<int>{}.isBuilt());
+  }
+
+  SECTION("dimensions")
+  {
+    auto number_of_values = [](const auto& sub_item_value_per_item) -> size_t {
+      using SubItemValuePerItemType = std::decay_t<decltype(sub_item_value_per_item)>;
+      using ItemId                  = typename SubItemValuePerItemType::ItemId;
+
+      size_t number = 0;
+      for (ItemId item_id = 0; item_id < sub_item_value_per_item.numberOfItems(); ++item_id) {
+        number += sub_item_value_per_item.numberOfSubValues(item_id);
+      }
+      return number;
+    };
+
+    SECTION("1D")
+    {
+      const Mesh<Connectivity<1>>& mesh_1d = MeshDataBaseForTests::get().cartesianMesh<1>();
+      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+
+      SECTION("per cell")
+      {
+        NodeValuePerCell<int> node_value_per_cell{connectivity};
+        REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
+
+        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          REQUIRE(cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
+          REQUIRE(node_value_per_cell.itemValues(cell_id).size() == node_value_per_cell.numberOfSubValues(cell_id));
+        }
+
+        const NodeValuePerCell<const int> const_node_value_per_cell = node_value_per_cell;
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          REQUIRE(const_node_value_per_cell.itemValues(cell_id).size() ==
+                  node_value_per_cell.numberOfSubValues(cell_id));
+        }
+
+        EdgeValuePerCell<int> edge_value_per_cell{connectivity};
+        REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
+
+        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          REQUIRE(cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+        }
+
+        FaceValuePerCell<int> face_value_per_cell{connectivity};
+        REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
+
+        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          REQUIRE(cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+        }
+      }
+
+      SECTION("per face")
+      {
+        CellValuePerFace<int> cell_value_per_face{connectivity};
+        REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+        REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
+
+        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+          REQUIRE(face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
+        }
+      }
+
+      SECTION("per edge")
+      {
+        CellValuePerEdge<int> cell_value_per_edge{connectivity};
+        REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+        REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+
+        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+        for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+          REQUIRE(edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
+        }
+      }
+
+      SECTION("per node")
+      {
+        CellValuePerNode<int> cell_value_per_node{connectivity};
+        REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+
+        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+          REQUIRE(node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+        }
+      }
+    }
+
+    SECTION("2D")
+    {
+      const Mesh<Connectivity<2>>& mesh_2d = MeshDataBaseForTests::get().cartesianMesh<2>();
+      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+
+      SECTION("per cell")
+      {
+        NodeValuePerCell<int> node_value_per_cell{connectivity};
+        REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
+
+        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          REQUIRE(cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
+        }
+
+        EdgeValuePerCell<int> edge_value_per_cell{connectivity};
+        REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
+
+        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          REQUIRE(cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+        }
+
+        FaceValuePerCell<int> face_value_per_cell{connectivity};
+        REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
+
+        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          REQUIRE(cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+        }
+      }
+
+      SECTION("per face")
+      {
+        CellValuePerFace<int> cell_value_per_face{connectivity};
+        REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+        REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
+
+        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+          REQUIRE(face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
+        }
+
+        NodeValuePerFace<int> node_value_per_face{connectivity};
+        REQUIRE(node_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+        REQUIRE(node_value_per_face.numberOfValues() == number_of_values(node_value_per_face));
+
+        auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+          REQUIRE(face_to_node_matrix[face_id].size() == node_value_per_face.numberOfSubValues(face_id));
+        }
+      }
+
+      SECTION("per edge")
+      {
+        CellValuePerEdge<int> cell_value_per_edge{connectivity};
+        REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+        REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+
+        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+        for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+          REQUIRE(edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
+        }
+
+        NodeValuePerEdge<int> node_value_per_edge{connectivity};
+        REQUIRE(node_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+        REQUIRE(node_value_per_edge.numberOfValues() == number_of_values(node_value_per_edge));
+
+        auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+        for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+          REQUIRE(edge_to_node_matrix[edge_id].size() == node_value_per_edge.numberOfSubValues(edge_id));
+        }
+      }
+
+      SECTION("per node")
+      {
+        EdgeValuePerNode<int> edge_value_per_node{connectivity};
+        REQUIRE(edge_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(edge_value_per_node.numberOfValues() == number_of_values(edge_value_per_node));
+
+        auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
+        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+          REQUIRE(node_to_edge_matrix[node_id].size() == edge_value_per_node.numberOfSubValues(node_id));
+        }
+
+        FaceValuePerNode<int> face_value_per_node{connectivity};
+        REQUIRE(face_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(face_value_per_node.numberOfValues() == number_of_values(face_value_per_node));
+
+        auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+          REQUIRE(node_to_face_matrix[node_id].size() == face_value_per_node.numberOfSubValues(node_id));
+        }
+
+        CellValuePerNode<int> cell_value_per_node{connectivity};
+        REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+
+        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+          REQUIRE(node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
+      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+
+      SECTION("per cell")
+      {
+        NodeValuePerCell<int> node_value_per_cell{connectivity};
+        REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
+
+        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          REQUIRE(cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
+        }
+
+        EdgeValuePerCell<int> edge_value_per_cell{connectivity};
+        REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
+
+        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          REQUIRE(cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+        }
+
+        FaceValuePerCell<int> face_value_per_cell{connectivity};
+        REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
+
+        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          REQUIRE(cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+        }
+      }
+
+      SECTION("per face")
+      {
+        CellValuePerFace<int> cell_value_per_face{connectivity};
+        REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+        REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
+
+        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+          REQUIRE(face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
+        }
+
+        EdgeValuePerFace<int> edge_value_per_face{connectivity};
+        REQUIRE(edge_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+        REQUIRE(edge_value_per_face.numberOfValues() == number_of_values(edge_value_per_face));
+
+        auto face_to_edge_matrix = connectivity.faceToEdgeMatrix();
+        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+          REQUIRE(face_to_edge_matrix[face_id].size() == edge_value_per_face.numberOfSubValues(face_id));
+        }
+
+        NodeValuePerFace<int> node_value_per_face{connectivity};
+        REQUIRE(node_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+        REQUIRE(node_value_per_face.numberOfValues() == number_of_values(node_value_per_face));
+
+        auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+          REQUIRE(face_to_node_matrix[face_id].size() == node_value_per_face.numberOfSubValues(face_id));
+        }
+      }
+
+      SECTION("per edge")
+      {
+        CellValuePerEdge<int> cell_value_per_edge{connectivity};
+        REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+        REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+
+        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+        for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+          REQUIRE(edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
+        }
+
+        FaceValuePerEdge<int> face_value_per_edge{connectivity};
+        REQUIRE(face_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+        REQUIRE(face_value_per_edge.numberOfValues() == number_of_values(face_value_per_edge));
+
+        auto edge_to_face_matrix = connectivity.edgeToFaceMatrix();
+        for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+          REQUIRE(edge_to_face_matrix[edge_id].size() == face_value_per_edge.numberOfSubValues(edge_id));
+        }
+
+        NodeValuePerEdge<int> node_value_per_edge{connectivity};
+        REQUIRE(node_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+        REQUIRE(node_value_per_edge.numberOfValues() == number_of_values(node_value_per_edge));
+
+        auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+        for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+          REQUIRE(edge_to_node_matrix[edge_id].size() == node_value_per_edge.numberOfSubValues(edge_id));
+        }
+      }
+
+      SECTION("per node")
+      {
+        EdgeValuePerNode<int> edge_value_per_node{connectivity};
+        REQUIRE(edge_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(edge_value_per_node.numberOfValues() == number_of_values(edge_value_per_node));
+
+        auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
+        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+          REQUIRE(node_to_edge_matrix[node_id].size() == edge_value_per_node.numberOfSubValues(node_id));
+        }
+
+        FaceValuePerNode<int> face_value_per_node{connectivity};
+        REQUIRE(face_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(face_value_per_node.numberOfValues() == number_of_values(face_value_per_node));
+
+        auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+          REQUIRE(node_to_face_matrix[node_id].size() == face_value_per_node.numberOfSubValues(node_id));
+        }
+
+        CellValuePerNode<int> cell_value_per_node{connectivity};
+        REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+
+        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+          REQUIRE(node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+        }
+      }
+    }
+  }
+
+  SECTION("array view")
+  {
+    SECTION("1D")
+    {
+      const Mesh<Connectivity<1>>& mesh_1d = MeshDataBaseForTests::get().cartesianMesh<1>();
+      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+
+      EdgeValuePerCell<size_t> edge_values_per_cell{connectivity};
+      {
+        size_t value = 0;
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          for (size_t i_edge = 0; i_edge < edge_values_per_cell.numberOfSubValues(cell_id); ++i_edge) {
+            edge_values_per_cell(cell_id, i_edge) = value++;
+          }
+        }
+      }
+      for (size_t i = 0; i < edge_values_per_cell.numberOfValues(); ++i) {
+        REQUIRE(edge_values_per_cell[i] == i);
+      }
+
+      for (size_t i = 0; i < edge_values_per_cell.numberOfValues(); ++i) {
+        edge_values_per_cell[i] = i * i + 1;
+      }
+      {
+        size_t i = 0;
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          for (size_t i_edge = 0; i_edge < edge_values_per_cell.numberOfSubValues(cell_id); ++i_edge, ++i) {
+            REQUIRE(edge_values_per_cell(cell_id, i_edge) == i * i + 1);
+          }
+        }
+      }
+    }
+
+    SECTION("2D")
+    {
+      const Mesh<Connectivity<2>>& mesh_2d = MeshDataBaseForTests::get().cartesianMesh<2>();
+      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+
+      CellValuePerFace<size_t> cell_values_per_face{connectivity};
+      {
+        size_t value = 0;
+        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+          for (size_t i_cell = 0; i_cell < cell_values_per_face.numberOfSubValues(face_id); ++i_cell) {
+            cell_values_per_face(face_id, i_cell) = value++;
+          }
+        }
+      }
+      for (size_t i = 0; i < cell_values_per_face.numberOfValues(); ++i) {
+        REQUIRE(cell_values_per_face[i] == i);
+      }
+
+      for (size_t i = 0; i < cell_values_per_face.numberOfValues(); ++i) {
+        cell_values_per_face[i] = 3 * i + 1;
+      }
+      {
+        size_t i = 0;
+        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+          for (size_t i_cell = 0; i_cell < cell_values_per_face.numberOfSubValues(face_id); ++i_cell, ++i) {
+            REQUIRE(cell_values_per_face(face_id, i_cell) == 3 * i + 1);
+          }
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
+      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+
+      FaceValuePerNode<size_t> face_values_per_node{connectivity};
+      {
+        size_t value = 0;
+        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+          for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face) {
+            face_values_per_node.itemValues(node_id)[i_face] = value++;
+          }
+        }
+      }
+      for (size_t i = 0; i < face_values_per_node.numberOfValues(); ++i) {
+        REQUIRE(face_values_per_node[i] == i);
+      }
+
+      for (size_t i = 0; i < face_values_per_node.numberOfValues(); ++i) {
+        face_values_per_node[i] = 3 + i * i;
+      }
+      {
+        size_t i = 0;
+        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+          for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face, ++i) {
+            face_values_per_node(node_id, i_face) = 3 + i * i;
+          }
+        }
+      }
+
+      {
+        size_t i = 0;
+        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+          for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face, ++i) {
+            face_values_per_node.itemValues(node_id)[i_face] = 3 + i * i;
+          }
+        }
+      }
+    }
+  }
+
+  SECTION("copy")
+  {
+    const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
+    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+
+    NodeValuePerCell<size_t> node_value_per_cell{connectivity};
+
+    {
+      size_t value = 0;
+      for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+        for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
+          node_value_per_cell.itemValues(cell_id)[i_node] = value++;
+        }
+      }
+    }
+
+    NodeValuePerCell<size_t> copy_node_value_per_cell = copy(node_value_per_cell);
+
+    {
+      bool is_same = true;
+      for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
+        is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+      }
+
+      REQUIRE(is_same);
+    }
+
+    {
+      for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+        for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
+          node_value_per_cell.itemValues(cell_id)[i_node] = i_node;
+        }
+      }
+    }
+
+    {
+      bool is_same = true;
+      for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
+        is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+      }
+
+      REQUIRE(not is_same);
+    }
+  }
+
+  SECTION("WeakItemValue")
+  {
+    // const Mesh<Connectivity<2>>& mesh_2d = MeshDataBaseForTests::get().cartesianMesh<2>();
+    // const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+
+    // WeakFaceValue<int> weak_face_value{connectivity};
+
+    // weak_face_value.fill(parallel::rank());
+
+    // FaceValue<const int> face_value{weak_face_value};
+
+    // REQUIRE(face_value.connectivity_ptr() == weak_face_value.connectivity_ptr());
+  }
+
+#ifndef NDEBUG
+  SECTION("error")
+  {
+    SECTION("checking for build ItemValue")
+    {
+      // CellValue<int> cell_value;
+      // REQUIRE_THROWS_AS(cell_value[CellId{0}], AssertError);
+
+      // FaceValue<int> face_value;
+      // REQUIRE_THROWS_AS(face_value[FaceId{0}], AssertError);
+
+      // EdgeValue<int> edge_value;
+      // REQUIRE_THROWS_AS(edge_value[EdgeId{0}], AssertError);
+
+      // NodeValue<int> node_value;
+      // REQUIRE_THROWS_AS(node_value[NodeId{0}], AssertError);
+    }
+
+    SECTION("checking for bounds violation")
+    {
+      // const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
+      // const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+
+      // CellValue<int> cell_value{connectivity};
+      // CellId invalid_cell_id = connectivity.numberOfCells();
+      // REQUIRE_THROWS_AS(cell_value[invalid_cell_id], AssertError);
+
+      // FaceValue<int> face_value{connectivity};
+      // FaceId invalid_face_id = connectivity.numberOfFaces();
+      // REQUIRE_THROWS_AS(face_value[invalid_face_id], AssertError);
+
+      // EdgeValue<int> edge_value{connectivity};
+      // EdgeId invalid_edge_id = connectivity.numberOfEdges();
+      // REQUIRE_THROWS_AS(edge_value[invalid_edge_id], AssertError);
+
+      // NodeValue<int> node_value{connectivity};
+      // NodeId invalid_node_id = connectivity.numberOfNodes();
+      // REQUIRE_THROWS_AS(node_value[invalid_node_id], AssertError);
+    }
+
+    SECTION("set values from invalid array size")
+    {
+      // const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
+      // const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+
+      // CellValue<size_t> cell_value{connectivity};
+
+      // Array<size_t> values{3 + cell_value.size()};
+      // REQUIRE_THROWS_AS(cell_value = values, AssertError);
+    }
+  }
+#endif   // NDEBUG
+}
diff --git a/tests/test_SubItemValuePerItem.hpp b/tests/test_SubItemValuePerItem.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..eee086bd4eca8a91d19286c2661484482cf5dca1
--- /dev/null
+++ b/tests/test_SubItemValuePerItem.hpp
@@ -0,0 +1 @@
+edge_values_per_faceedge_values_per_faceedge_values_per_faceedge_values_per_faceedge_values_per_face