diff --git a/src/mesh/IConnectivity.hpp b/src/mesh/IConnectivity.hpp
index 9bfb8e5043088a18880440235cd72b1677c1f519..90c49e4d818e6caa6414992e9ebaf7bdf46f15cc 100644
--- a/src/mesh/IConnectivity.hpp
+++ b/src/mesh/IConnectivity.hpp
@@ -13,6 +13,9 @@ class IConnectivity : public std::enable_shared_from_this<IConnectivity>
   template <typename DataType, typename ItemOfItem, typename ConnectivityPtr>
   friend class SubItemValuePerItem;
 
+  template <typename DataType, typename ItemOfItem, typename ConnectivityPtr>
+  friend class SubItemArrayPerItem;
+
   virtual const ConnectivityMatrix& _getMatrix(const ItemType& item_type_0, const ItemType& item_type_1) const = 0;
 
  public:
diff --git a/src/mesh/SubItemArrayPerItem.hpp b/src/mesh/SubItemArrayPerItem.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..431c3f4bee962857ff0b361c56a9ab5e987fde35
--- /dev/null
+++ b/src/mesh/SubItemArrayPerItem.hpp
@@ -0,0 +1,361 @@
+#ifndef SUBITEM_ARRAY_PER_ITEM_HPP
+#define SUBITEM_ARRAY_PER_ITEM_HPP
+
+#include <mesh/ConnectivityMatrix.hpp>
+#include <mesh/IConnectivity.hpp>
+#include <mesh/ItemId.hpp>
+#include <mesh/ItemOfItemType.hpp>
+#include <mesh/ItemType.hpp>
+#include <utils/Array.hpp>
+#include <utils/PugsAssert.hpp>
+#include <utils/SubArray.hpp>
+
+#include <memory>
+
+template <typename DataType, typename ItemOfItem, typename ConnectivityPtr = std::shared_ptr<const IConnectivity>>
+class SubItemArrayPerItem
+{
+ public:
+  static constexpr ItemType item_type{ItemOfItem::item_type};
+  static constexpr ItemType sub_item_type{ItemOfItem::sub_item_type};
+
+  using ItemOfItemType = ItemOfItem;
+  using data_type      = DataType;
+  using ItemId         = ItemIdT<item_type>;
+  using index_type     = ItemId;
+
+ private:
+  using ConnectivitySharedPtr = std::shared_ptr<const IConnectivity>;
+  using ConnectivityWeakPtr   = std::weak_ptr<const IConnectivity>;
+
+  static_assert(std::is_same_v<ConnectivityPtr, ConnectivitySharedPtr> or
+                std::is_same_v<ConnectivityPtr, ConnectivityWeakPtr>);
+
+  ConnectivityPtr m_connectivity_ptr;
+
+  typename ConnectivityMatrix::HostRowType m_host_row_map;
+  Array<DataType> m_arrays_values;
+
+  size_t m_size_of_arrays;
+
+  // Allow const std:shared_ptr version to access our data
+  friend SubItemArrayPerItem<std::add_const_t<DataType>, ItemOfItem, ConnectivitySharedPtr>;
+
+  // Allow const std:weak_ptr version to access our data
+  friend SubItemArrayPerItem<std::add_const_t<DataType>, ItemOfItem, ConnectivityWeakPtr>;
+
+  // Allow const std:shared_ptr version to access our data
+  friend SubItemArrayPerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivitySharedPtr>;
+
+  // Allow const std:weak_ptr version to access our data
+  friend SubItemArrayPerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityWeakPtr>;
+
+ public:
+  using ToShared = SubItemArrayPerItem<DataType, ItemOfItem, ConnectivitySharedPtr>;
+
+  class SubView
+  {
+   public:
+    using data_type = DataType;
+
+   private:
+    PUGS_RESTRICT DataType* const m_sub_arrays;
+    const size_t m_size;
+    const size_t m_size_of_arrays;
+
+   public:
+    template <typename IndexType>
+    PUGS_FORCEINLINE SubArray<DataType>
+    operator[](IndexType i) const noexcept(NO_ASSERT)
+    {
+      static_assert(std::is_integral_v<IndexType>, "SubView is indexed by integral arrays");
+      Assert(static_cast<size_t>(i) < m_size);
+      return SubArray<DataType>(m_sub_arrays, i * m_size_of_arrays, m_size_of_arrays);
+    }
+
+    template <typename IndexType>
+    PUGS_FORCEINLINE SubArray<DataType>
+    operator[](IndexType i) noexcept(NO_ASSERT)
+    {
+      static_assert(std::is_integral_v<IndexType>, "SubView is indexed by integral arrays");
+      Assert(static_cast<size_t>(i) < m_size);
+      return SubArray<DataType>(m_sub_arrays, i * m_size_of_arrays, m_size_of_arrays);
+    }
+
+    PUGS_INLINE
+    size_t
+    size() const noexcept
+    {
+      return m_size;
+    }
+
+    SubView(const SubView&) = delete;
+
+    PUGS_INLINE
+    SubView(SubView&&) noexcept = delete;
+
+    PUGS_INLINE
+    SubView(const Array<DataType>& arrays, size_t begin, size_t end, size_t size_of_arrays) noexcept(NO_ASSERT)
+      : m_sub_arrays{&(arrays[begin * size_of_arrays])}, m_size{end - begin}, m_size_of_arrays{size_of_arrays}
+    {
+      Assert(begin <= end);
+      Assert(end <= arrays.size());
+    }
+  };
+
+  friend PUGS_INLINE SubItemArrayPerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityPtr>
+  copy(SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& source)
+  {
+    SubItemArrayPerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityPtr> image;
+
+    image.m_connectivity_ptr = source.m_connectivity_ptr;
+    image.m_host_row_map     = source.m_host_row_map;
+    image.m_arrays_values    = copy(source.m_arrays_values);
+    image.m_size_of_arrays   = source.m_size_of_arrays;
+    return image;
+  }
+
+  PUGS_INLINE
+  bool
+  isBuilt() const noexcept
+  {
+    return m_connectivity_ptr.use_count() != 0;
+  }
+
+  PUGS_INLINE
+  std::shared_ptr<const IConnectivity>
+  connectivity_ptr() const noexcept
+  {
+    if constexpr (std::is_same_v<ConnectivityPtr, ConnectivitySharedPtr>) {
+      return m_connectivity_ptr;
+    } else {
+      return m_connectivity_ptr.lock();
+    }
+  }
+
+  template <typename IndexType, typename SubIndexType>
+  PUGS_FORCEINLINE SubArray<DataType>
+  operator()(IndexType item_id, SubIndexType i) const noexcept(NO_ASSERT)
+  {
+    static_assert(std::is_same_v<IndexType, ItemId>, "first index must be of the correct ItemId type");
+    static_assert(std::is_integral_v<SubIndexType>, "second index must be an integral type");
+    Assert(this->isBuilt());
+    Assert(i + m_host_row_map(size_t{item_id}) < m_host_row_map(size_t{item_id} + 1));
+    return SubArray(m_arrays_values, (m_host_row_map(size_t{item_id}) + i) * m_size_of_arrays, m_size_of_arrays);
+  }
+
+  // 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)
+  {
+    static_assert(std::is_integral_v<ArrayIndexType>, "index must be an integral type");
+    Assert(this->isBuilt());
+    Assert(static_cast<size_t>(i) < m_arrays_values.size());
+    return m_arrays_values[i];
+  }
+
+  PUGS_INLINE
+  size_t
+  numberOfValues() const noexcept(NO_ASSERT)
+  {
+    Assert(this->isBuilt());
+    return m_arrays_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) - 1;
+  }
+
+  PUGS_INLINE size_t
+  sizeOfArrays() const noexcept(NO_ASSERT)
+  {
+    Assert(this->isBuilt());
+    return m_size_of_arrays;
+  }
+
+  template <typename IndexType>
+  PUGS_INLINE size_t
+  numberOfSubArrays(IndexType item_id) const noexcept(NO_ASSERT)
+  {
+    static_assert(std::is_same_v<IndexType, ItemId>, "index must be an ItemId");
+    Assert(this->isBuilt());
+    Assert(item_id < this->numberOfItems());
+    return m_host_row_map(size_t{item_id} + 1) - m_host_row_map(size_t{item_id});
+  }
+
+  template <typename IndexType>
+  PUGS_INLINE SubView
+  itemArrays(IndexType item_id) noexcept(NO_ASSERT)
+  {
+    static_assert(std::is_same_v<IndexType, ItemId>, "index must be an ItemId");
+    Assert(this->isBuilt());
+    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_arrays_values, item_begin, item_end, m_size_of_arrays);
+  }
+
+  // Following Kokkos logic, these classes are view and const view does allow
+  // changes in data
+  template <typename IndexType>
+  PUGS_INLINE SubView
+  itemArrays(IndexType item_id) const noexcept(NO_ASSERT)
+  {
+    static_assert(std::is_same_v<IndexType, ItemId>, "index must be an ItemId");
+    Assert(this->isBuilt());
+    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_arrays_values, item_begin, item_end, m_size_of_arrays);
+  }
+
+  template <typename DataType2, typename ConnectivityPtr2>
+  PUGS_INLINE SubItemArrayPerItem&
+  operator=(const SubItemArrayPerItem<DataType2, ItemOfItem, ConnectivityPtr2>& sub_item_array_per_item) noexcept
+  {
+    // ensures that DataType is the same as source DataType2
+    static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>,
+                  "Cannot assign SubItemArrayPerItem of different type");
+    // ensures that const is not lost through copy
+    static_assert(((std::is_const_v<DataType2> and std::is_const_v<DataType>) or not std::is_const_v<DataType2>),
+                  "Cannot assign SubItemArrayPerItem of const data to "
+                  "SubItemArrayPerItem of non-const data");
+    m_host_row_map   = sub_item_array_per_item.m_host_row_map;
+    m_arrays_values  = sub_item_array_per_item.m_arrays_values;
+    m_size_of_arrays = sub_item_array_per_item.m_size_of_arrays;
+
+    if constexpr (std::is_same_v<ConnectivityPtr, ConnectivitySharedPtr> and
+                  std::is_same_v<ConnectivityPtr2, ConnectivityWeakPtr>) {
+      m_connectivity_ptr = sub_item_array_per_item.m_connectivity_ptr.lock();
+    } else {
+      m_connectivity_ptr = sub_item_array_per_item.m_connectivity_ptr;
+    }
+
+    return *this;
+  }
+
+  template <typename DataType2, typename ConnectivityPtr2>
+  PUGS_INLINE
+  SubItemArrayPerItem(
+    const SubItemArrayPerItem<DataType2, ItemOfItem, ConnectivityPtr2>& sub_item_array_per_item) noexcept
+  {
+    this->operator=(sub_item_array_per_item);
+  }
+
+  SubItemArrayPerItem() = default;
+
+  SubItemArrayPerItem(const IConnectivity& connectivity, size_t size_of_array) noexcept
+    : m_connectivity_ptr{connectivity.shared_ptr()}, m_size_of_arrays(size_of_array)
+  {
+    static_assert(not std::is_const_v<DataType>, "Cannot allocate SubItemArrayPerItem of const data: only "
+                                                 "view is supported");
+
+    ConnectivityMatrix connectivity_matrix = connectivity._getMatrix(item_type, sub_item_type);
+
+    m_host_row_map  = connectivity_matrix.rowsMap();
+    m_arrays_values = Array<std::remove_const_t<DataType>>(connectivity_matrix.numEntries() * m_size_of_arrays);
+  }
+
+  ~SubItemArrayPerItem() = default;
+};
+
+// Item arrays at nodes
+
+template <typename DataType>
+using NodeArrayPerEdge = SubItemArrayPerItem<DataType, NodeOfEdge>;
+
+template <typename DataType>
+using NodeArrayPerFace = SubItemArrayPerItem<DataType, NodeOfFace>;
+
+template <typename DataType>
+using NodeArrayPerCell = SubItemArrayPerItem<DataType, NodeOfCell>;
+
+// Item arrays at edges
+
+template <typename DataType>
+using EdgeArrayPerNode = SubItemArrayPerItem<DataType, EdgeOfNode>;
+
+template <typename DataType>
+using EdgeArrayPerFace = SubItemArrayPerItem<DataType, EdgeOfFace>;
+
+template <typename DataType>
+using EdgeArrayPerCell = SubItemArrayPerItem<DataType, EdgeOfCell>;
+
+// Item arrays at faces
+
+template <typename DataType>
+using FaceArrayPerNode = SubItemArrayPerItem<DataType, FaceOfNode>;
+
+template <typename DataType>
+using FaceArrayPerEdge = SubItemArrayPerItem<DataType, FaceOfEdge>;
+
+template <typename DataType>
+using FaceArrayPerCell = SubItemArrayPerItem<DataType, FaceOfCell>;
+
+// Item arrays at cells
+
+template <typename DataType>
+using CellArrayPerNode = SubItemArrayPerItem<DataType, CellOfNode>;
+
+template <typename DataType>
+using CellArrayPerEdge = SubItemArrayPerItem<DataType, CellOfEdge>;
+
+template <typename DataType>
+using CellArrayPerFace = SubItemArrayPerItem<DataType, CellOfFace>;
+
+// Weak versions: should not be used outside of Connectivity
+// Item arrays at nodes
+
+template <typename DataType, typename ItemOfItem>
+using WeakSubItemArrayPerItem = SubItemArrayPerItem<DataType, ItemOfItem, std::weak_ptr<const IConnectivity>>;
+
+template <typename DataType>
+using WeakNodeArrayPerEdge = WeakSubItemArrayPerItem<DataType, NodeOfEdge>;
+
+template <typename DataType>
+using WeakNodeArrayPerFace = WeakSubItemArrayPerItem<DataType, NodeOfFace>;
+
+template <typename DataType>
+using WeakNodeArrayPerCell = WeakSubItemArrayPerItem<DataType, NodeOfCell>;
+
+// Item arrays at edges
+
+template <typename DataType>
+using WeakEdgeArrayPerNode = WeakSubItemArrayPerItem<DataType, EdgeOfNode>;
+
+template <typename DataType>
+using WeakEdgeArrayPerFace = WeakSubItemArrayPerItem<DataType, EdgeOfFace>;
+
+template <typename DataType>
+using WeakEdgeArrayPerCell = WeakSubItemArrayPerItem<DataType, EdgeOfCell>;
+
+// Item arrays at faces
+
+template <typename DataType>
+using WeakFaceArrayPerNode = WeakSubItemArrayPerItem<DataType, FaceOfNode>;
+
+template <typename DataType>
+using WeakFaceArrayPerEdge = WeakSubItemArrayPerItem<DataType, FaceOfEdge>;
+
+template <typename DataType>
+using WeakFaceArrayPerCell = WeakSubItemArrayPerItem<DataType, FaceOfCell>;
+
+// Item arrays at cells
+
+template <typename DataType>
+using WeakCellArrayPerNode = WeakSubItemArrayPerItem<DataType, CellOfNode>;
+
+template <typename DataType>
+using WeakCellArrayPerEdge = WeakSubItemArrayPerItem<DataType, CellOfEdge>;
+
+template <typename DataType>
+using WeakCellArrayPerFace = WeakSubItemArrayPerItem<DataType, CellOfFace>;
+
+#endif   // SUBITEM_ARRAY_PER_ITEM_HPP
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 6f255448f4cdf002bc0d0df0a795bbca26935efe..c99154ff0dee78cf7aadf95c70cccfbeb36ffa75 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -105,6 +105,7 @@ add_executable (mpi_unit_tests
   test_ItemValue.cpp
   test_ItemValueUtils.cpp
   test_SubItemValuePerItem.cpp
+  test_SubItemArrayPerItem.cpp
   )
 
 add_library(test_Pugs_MeshDataBase
diff --git a/tests/test_SubItemArrayPerItem.cpp b/tests/test_SubItemArrayPerItem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5438398b0c1d5ba0559214473f41d13dba3cb8b4
--- /dev/null
+++ b/tests/test_SubItemArrayPerItem.cpp
@@ -0,0 +1,869 @@
+#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/SubItemArrayPerItem.hpp>
+#include <utils/Messenger.hpp>
+
+// Instantiate to ensure full coverage is performed
+template class SubItemArrayPerItem<size_t, NodeOfCell>;
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("SubItemArrayPerItem", "[mesh]")
+{
+  SECTION("default constructors")
+  {
+    REQUIRE_NOTHROW(NodeArrayPerEdge<int>{});
+    REQUIRE_NOTHROW(NodeArrayPerFace<int>{});
+    REQUIRE_NOTHROW(NodeArrayPerCell<int>{});
+
+    REQUIRE_NOTHROW(EdgeArrayPerNode<int>{});
+    REQUIRE_NOTHROW(EdgeArrayPerFace<int>{});
+    REQUIRE_NOTHROW(EdgeArrayPerCell<int>{});
+
+    REQUIRE_NOTHROW(FaceArrayPerNode<int>{});
+    REQUIRE_NOTHROW(FaceArrayPerEdge<int>{});
+    REQUIRE_NOTHROW(FaceArrayPerCell<int>{});
+
+    REQUIRE_NOTHROW(CellArrayPerNode<int>{});
+    REQUIRE_NOTHROW(CellArrayPerEdge<int>{});
+    REQUIRE_NOTHROW(CellArrayPerFace<int>{});
+
+    REQUIRE(not NodeArrayPerEdge<int>{}.isBuilt());
+    REQUIRE(not NodeArrayPerFace<int>{}.isBuilt());
+    REQUIRE(not NodeArrayPerCell<int>{}.isBuilt());
+
+    REQUIRE(not EdgeArrayPerNode<int>{}.isBuilt());
+    REQUIRE(not EdgeArrayPerFace<int>{}.isBuilt());
+    REQUIRE(not EdgeArrayPerCell<int>{}.isBuilt());
+
+    REQUIRE(not FaceArrayPerNode<int>{}.isBuilt());
+    REQUIRE(not FaceArrayPerEdge<int>{}.isBuilt());
+    REQUIRE(not FaceArrayPerCell<int>{}.isBuilt());
+
+    REQUIRE(not CellArrayPerNode<int>{}.isBuilt());
+    REQUIRE(not CellArrayPerEdge<int>{}.isBuilt());
+    REQUIRE(not CellArrayPerFace<int>{}.isBuilt());
+  }
+
+  SECTION("dimensions")
+  {
+    auto number_of_values = [](const auto& sub_item_array_per_item) -> size_t {
+      using SubItemArrayPerItemType = std::decay_t<decltype(sub_item_array_per_item)>;
+      using ItemId                  = typename SubItemArrayPerItemType::ItemId;
+
+      size_t number = 0;
+      for (ItemId item_id = 0; item_id < sub_item_array_per_item.numberOfItems(); ++item_id) {
+        for (size_t i = 0; i < sub_item_array_per_item.numberOfSubArrays(item_id); ++i) {
+          number += sub_item_array_per_item(item_id, i).size();
+        }
+      }
+      return number;
+    };
+
+    SECTION("1D")
+    {
+      const Mesh<Connectivity<1>>& mesh_1d = MeshDataBaseForTests::get().cartesianMesh<1>();
+      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+
+      SECTION("per cell")
+      {
+        NodeArrayPerCell<int> node_array_per_cell{connectivity, 3};
+        REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(node_array_per_cell.numberOfValues() == number_of_values(node_array_per_cell));
+        REQUIRE(node_array_per_cell.sizeOfArrays() == 3);
+
+        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+        {
+          bool is_correct = true;
+          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+            is_correct &=
+              (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id)) and
+              (node_array_per_cell.itemArrays(cell_id).size() == node_array_per_cell.numberOfSubArrays(cell_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        const NodeArrayPerCell<const int> const_node_array_per_cell = node_array_per_cell;
+        {
+          bool is_correct = true;
+          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+            is_correct &=
+              (const_node_array_per_cell.itemArrays(cell_id).size() == node_array_per_cell.numberOfSubArrays(cell_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
+        REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(edge_array_per_cell.numberOfValues() == number_of_values(edge_array_per_cell));
+        REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
+
+        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+        {
+          bool is_correct = true;
+          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        FaceArrayPerCell<int> face_array_per_cell{connectivity, 2};
+        REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(face_array_per_cell.numberOfValues() == number_of_values(face_array_per_cell));
+        REQUIRE(face_array_per_cell.sizeOfArrays() == 2);
+
+        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+        {
+          bool is_correct = true;
+          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+            is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
+          }
+          REQUIRE(is_correct);
+        }
+      }
+
+      SECTION("per face")
+      {
+        CellArrayPerFace<int> cell_array_per_face{connectivity, 2};
+        REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+        REQUIRE(cell_array_per_face.numberOfValues() == number_of_values(cell_array_per_face));
+        REQUIRE(cell_array_per_face.sizeOfArrays() == 2);
+
+        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+        {
+          bool is_correct = true;
+          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+            is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
+          }
+          REQUIRE(is_correct);
+        }
+      }
+
+      SECTION("per edge")
+      {
+        CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
+        REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+        REQUIRE(cell_array_per_edge.numberOfValues() == number_of_values(cell_array_per_edge));
+        REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
+
+        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+        {
+          bool is_correct = true;
+          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+          }
+          REQUIRE(is_correct);
+        }
+      }
+
+      SECTION("per node")
+      {
+        CellArrayPerNode<int> cell_array_per_node{connectivity, 4};
+        REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(cell_array_per_node.numberOfValues() == number_of_values(cell_array_per_node));
+        REQUIRE(cell_array_per_node.sizeOfArrays() == 4);
+
+        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+        {
+          bool is_correct = true;
+          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+            is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+          }
+          REQUIRE(is_correct);
+        }
+      }
+    }
+
+    SECTION("2D")
+    {
+      const Mesh<Connectivity<2>>& mesh_2d = MeshDataBaseForTests::get().cartesianMesh<2>();
+      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+
+      SECTION("per cell")
+      {
+        NodeArrayPerCell<int> node_array_per_cell{connectivity, 5};
+        REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(node_array_per_cell.numberOfValues() == number_of_values(node_array_per_cell));
+        REQUIRE(node_array_per_cell.sizeOfArrays() == 5);
+
+        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+        {
+          bool is_correct = true;
+          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+            is_correct &= (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
+        REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(edge_array_per_cell.numberOfValues() == number_of_values(edge_array_per_cell));
+        REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
+
+        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+        {
+          bool is_correct = true;
+          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        FaceArrayPerCell<int> face_array_per_cell{connectivity, 3};
+        REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(face_array_per_cell.numberOfValues() == number_of_values(face_array_per_cell));
+        REQUIRE(face_array_per_cell.sizeOfArrays() == 3);
+
+        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+        {
+          bool is_correct = true;
+          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+            is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
+          }
+          REQUIRE(is_correct);
+        }
+      }
+
+      SECTION("per face")
+      {
+        CellArrayPerFace<int> cell_array_per_face{connectivity, 3};
+        REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+        REQUIRE(cell_array_per_face.numberOfValues() == number_of_values(cell_array_per_face));
+        REQUIRE(cell_array_per_face.sizeOfArrays() == 3);
+
+        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+        {
+          bool is_correct = true;
+          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+            is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        NodeArrayPerFace<int> node_array_per_face{connectivity, 2};
+        REQUIRE(node_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+        REQUIRE(node_array_per_face.numberOfValues() == number_of_values(node_array_per_face));
+        REQUIRE(node_array_per_face.sizeOfArrays() == 2);
+
+        auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+        {
+          bool is_correct = true;
+          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+            is_correct &= (face_to_node_matrix[face_id].size() == node_array_per_face.numberOfSubArrays(face_id));
+          }
+          REQUIRE(is_correct);
+        }
+      }
+
+      SECTION("per edge")
+      {
+        CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
+        REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+        REQUIRE(cell_array_per_edge.numberOfValues() == number_of_values(cell_array_per_edge));
+        REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
+
+        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+        {
+          bool is_correct = true;
+          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        NodeArrayPerEdge<int> node_array_per_edge{connectivity, 5};
+        REQUIRE(node_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+        REQUIRE(node_array_per_edge.numberOfValues() == number_of_values(node_array_per_edge));
+        REQUIRE(node_array_per_edge.sizeOfArrays() == 5);
+
+        auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+        {
+          bool is_correct = true;
+          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+            is_correct &= (edge_to_node_matrix[edge_id].size() == node_array_per_edge.numberOfSubArrays(edge_id));
+          }
+          REQUIRE(is_correct);
+        }
+      }
+
+      SECTION("per node")
+      {
+        EdgeArrayPerNode<int> edge_array_per_node{connectivity, 4};
+        REQUIRE(edge_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(edge_array_per_node.numberOfValues() == number_of_values(edge_array_per_node));
+        REQUIRE(edge_array_per_node.sizeOfArrays() == 4);
+
+        auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
+        {
+          bool is_correct = true;
+          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+            is_correct &= (node_to_edge_matrix[node_id].size() == edge_array_per_node.numberOfSubArrays(node_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        FaceArrayPerNode<int> face_array_per_node{connectivity, 3};
+        REQUIRE(face_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(face_array_per_node.numberOfValues() == number_of_values(face_array_per_node));
+        REQUIRE(face_array_per_node.sizeOfArrays() == 3);
+
+        auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+        {
+          bool is_correct = true;
+          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+            is_correct &= (node_to_face_matrix[node_id].size() == face_array_per_node.numberOfSubArrays(node_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        CellArrayPerNode<int> cell_array_per_node{connectivity, 2};
+        REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(cell_array_per_node.numberOfValues() == number_of_values(cell_array_per_node));
+        REQUIRE(cell_array_per_node.sizeOfArrays() == 2);
+
+        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+        {
+          bool is_correct = true;
+          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+            is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+          }
+          REQUIRE(is_correct);
+        }
+      }
+    }
+    SECTION("3D")
+    {
+      const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
+      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+
+      SECTION("per cell")
+      {
+        NodeArrayPerCell<int> node_array_per_cell{connectivity, 3};
+        REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(node_array_per_cell.numberOfValues() == number_of_values(node_array_per_cell));
+        REQUIRE(node_array_per_cell.sizeOfArrays() == 3);
+
+        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+        {
+          bool is_correct = true;
+          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+            is_correct &= (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
+        REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(edge_array_per_cell.numberOfValues() == number_of_values(edge_array_per_cell));
+        REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
+
+        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+        {
+          bool is_correct = true;
+          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        FaceArrayPerCell<int> face_array_per_cell{connectivity, 3};
+        REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+        REQUIRE(face_array_per_cell.numberOfValues() == number_of_values(face_array_per_cell));
+        REQUIRE(face_array_per_cell.sizeOfArrays() == 3);
+
+        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+        {
+          bool is_correct = true;
+          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+            is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
+          }
+          REQUIRE(is_correct);
+        }
+      }
+
+      SECTION("per face")
+      {
+        CellArrayPerFace<int> cell_array_per_face{connectivity, 5};
+        REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+        REQUIRE(cell_array_per_face.numberOfValues() == number_of_values(cell_array_per_face));
+        REQUIRE(cell_array_per_face.sizeOfArrays() == 5);
+
+        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+        {
+          bool is_correct = true;
+          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+            is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        EdgeArrayPerFace<int> edge_array_per_face{connectivity, 3};
+        REQUIRE(edge_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+        REQUIRE(edge_array_per_face.numberOfValues() == number_of_values(edge_array_per_face));
+        REQUIRE(edge_array_per_face.sizeOfArrays() == 3);
+
+        auto face_to_edge_matrix = connectivity.faceToEdgeMatrix();
+        {
+          bool is_correct = true;
+          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+            is_correct &= (face_to_edge_matrix[face_id].size() == edge_array_per_face.numberOfSubArrays(face_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        NodeArrayPerFace<int> node_array_per_face{connectivity, 2};
+        REQUIRE(node_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+        REQUIRE(node_array_per_face.numberOfValues() == number_of_values(node_array_per_face));
+        REQUIRE(node_array_per_face.sizeOfArrays() == 2);
+
+        auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+        {
+          bool is_correct = true;
+          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+            is_correct &= (face_to_node_matrix[face_id].size() == node_array_per_face.numberOfSubArrays(face_id));
+          }
+          REQUIRE(is_correct);
+        }
+      }
+
+      SECTION("per edge")
+      {
+        CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
+        REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+        REQUIRE(cell_array_per_edge.numberOfValues() == number_of_values(cell_array_per_edge));
+        REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
+
+        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+        {
+          bool is_correct = true;
+          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        FaceArrayPerEdge<int> face_array_per_edge{connectivity, 5};
+        REQUIRE(face_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+        REQUIRE(face_array_per_edge.numberOfValues() == number_of_values(face_array_per_edge));
+        REQUIRE(face_array_per_edge.sizeOfArrays() == 5);
+
+        auto edge_to_face_matrix = connectivity.edgeToFaceMatrix();
+        {
+          bool is_correct = true;
+          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+            is_correct &= (edge_to_face_matrix[edge_id].size() == face_array_per_edge.numberOfSubArrays(edge_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        NodeArrayPerEdge<int> node_array_per_edge{connectivity, 3};
+        REQUIRE(node_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+        REQUIRE(node_array_per_edge.numberOfValues() == number_of_values(node_array_per_edge));
+        REQUIRE(node_array_per_edge.sizeOfArrays() == 3);
+
+        auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+        {
+          bool is_correct = true;
+          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+            is_correct &= (edge_to_node_matrix[edge_id].size() == node_array_per_edge.numberOfSubArrays(edge_id));
+          }
+          REQUIRE(is_correct);
+        }
+      }
+
+      SECTION("per node")
+      {
+        EdgeArrayPerNode<int> edge_array_per_node{connectivity, 3};
+        REQUIRE(edge_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(edge_array_per_node.numberOfValues() == number_of_values(edge_array_per_node));
+        REQUIRE(edge_array_per_node.sizeOfArrays() == 3);
+
+        auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
+        {
+          bool is_correct = true;
+          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+            is_correct &= (node_to_edge_matrix[node_id].size() == edge_array_per_node.numberOfSubArrays(node_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        FaceArrayPerNode<int> face_array_per_node{connectivity, 4};
+        REQUIRE(face_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(face_array_per_node.numberOfValues() == number_of_values(face_array_per_node));
+        REQUIRE(face_array_per_node.sizeOfArrays() == 4);
+
+        auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+        {
+          bool is_correct = true;
+          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+            is_correct &= (node_to_face_matrix[node_id].size() == face_array_per_node.numberOfSubArrays(node_id));
+          }
+          REQUIRE(is_correct);
+        }
+
+        CellArrayPerNode<int> cell_array_per_node{connectivity, 5};
+        REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+        REQUIRE(cell_array_per_node.numberOfValues() == number_of_values(cell_array_per_node));
+        REQUIRE(cell_array_per_node.sizeOfArrays() == 5);
+
+        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+        {
+          bool is_correct = true;
+          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+            is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+          }
+          REQUIRE(is_correct);
+        }
+      }
+    }
+  }
+
+  SECTION("array view")
+  {
+    SECTION("1D")
+    {
+      const Mesh<Connectivity<1>>& mesh_1d = MeshDataBaseForTests::get().cartesianMesh<1>();
+      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+
+      EdgeArrayPerCell<size_t> edge_arrays_per_cell{connectivity, 3};
+      {
+        size_t array = 0;
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          for (size_t i_edge = 0; i_edge < edge_arrays_per_cell.numberOfSubArrays(cell_id); ++i_edge) {
+            for (size_t i = 0; i < edge_arrays_per_cell(cell_id, i_edge).size(); ++i) {
+              edge_arrays_per_cell(cell_id, i_edge)[i] = array++;
+            }
+          }
+        }
+      }
+      {
+        bool is_same = true;
+        for (size_t i = 0; i < edge_arrays_per_cell.numberOfValues(); ++i) {
+          is_same &= (edge_arrays_per_cell[i] == i);
+        }
+        REQUIRE(is_same);
+      }
+
+      for (size_t i = 0; i < edge_arrays_per_cell.numberOfValues(); ++i) {
+        edge_arrays_per_cell[i] = i * i + 1;
+      }
+      {
+        bool is_same = true;
+        size_t i     = 0;
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          for (size_t i_edge = 0; i_edge < edge_arrays_per_cell.numberOfSubArrays(cell_id); ++i_edge) {
+            for (size_t l = 0; l < edge_arrays_per_cell(cell_id, i_edge).size(); ++l, ++i) {
+              is_same &= (edge_arrays_per_cell(cell_id, i_edge)[l] == i * i + 1);
+            }
+          }
+        }
+        REQUIRE(is_same);
+      }
+    }
+
+    SECTION("2D")
+    {
+      const Mesh<Connectivity<2>>& mesh_2d = MeshDataBaseForTests::get().cartesianMesh<2>();
+      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+
+      CellArrayPerFace<size_t> cell_arrays_per_face{connectivity, 3};
+      {
+        size_t array = 0;
+        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+          for (size_t i_cell = 0; i_cell < cell_arrays_per_face.numberOfSubArrays(face_id); ++i_cell) {
+            for (size_t i = 0; i < cell_arrays_per_face(face_id, i_cell).size(); ++i) {
+              cell_arrays_per_face(face_id, i_cell)[i] = array++;
+            }
+          }
+        }
+      }
+      {
+        bool is_same = true;
+        for (size_t i = 0; i < cell_arrays_per_face.numberOfValues(); ++i) {
+          is_same &= (cell_arrays_per_face[i] == i);
+        }
+        REQUIRE(is_same);
+      }
+      for (size_t i = 0; i < cell_arrays_per_face.numberOfValues(); ++i) {
+        cell_arrays_per_face[i] = 3 * i + 1;
+      }
+      {
+        bool is_same = true;
+        size_t i     = 0;
+        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+          for (size_t i_cell = 0; i_cell < cell_arrays_per_face.numberOfSubArrays(face_id); ++i_cell) {
+            for (size_t l = 0; l < cell_arrays_per_face(face_id, i_cell).size(); ++l, ++i) {
+              is_same &= (cell_arrays_per_face(face_id, i_cell)[l] == 3 * i + 1);
+            }
+          }
+        }
+        REQUIRE(is_same);
+      }
+    }
+
+    SECTION("3D")
+    {
+      const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
+      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+
+      FaceArrayPerNode<size_t> face_arrays_per_node{connectivity, 3};
+      {
+        size_t array = 0;
+        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+          for (size_t i_face = 0; i_face < face_arrays_per_node.numberOfSubArrays(node_id); ++i_face) {
+            for (size_t i = 0; i < face_arrays_per_node(node_id, i_face).size(); ++i)
+              face_arrays_per_node(node_id, i_face)[i] = array++;
+          }
+        }
+      }
+      {
+        bool is_same = true;
+        for (size_t i = 0; i < face_arrays_per_node.numberOfValues(); ++i) {
+          is_same &= (face_arrays_per_node[i] == i);
+        }
+        REQUIRE(is_same);
+      }
+
+      for (size_t i = 0; i < face_arrays_per_node.numberOfValues(); ++i) {
+        face_arrays_per_node[i] = 3 + i * i;
+      }
+      {
+        bool is_same = true;
+        size_t i     = 0;
+        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+          for (size_t i_face = 0; i_face < face_arrays_per_node.numberOfSubArrays(node_id); ++i_face) {
+            for (size_t l = 0; l < face_arrays_per_node(node_id, i_face).size(); ++l, ++i) {
+              is_same &= (face_arrays_per_node(node_id, i_face)[l] == 3 + i * i);
+            }
+          }
+        }
+        REQUIRE(is_same);
+      }
+    }
+  }
+
+  SECTION("copy")
+  {
+    const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
+    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+
+    SECTION("classic")
+    {
+      NodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3};
+      for (size_t i = 0; i < node_array_per_cell.numberOfValues(); ++i) {
+        node_array_per_cell[i] = i;
+      }
+
+      NodeArrayPerCell<size_t> copy_node_array_per_cell = copy(node_array_per_cell);
+      {
+        bool is_same = true;
+        for (size_t i = 0; i < copy_node_array_per_cell.numberOfValues(); ++i) {
+          is_same &= (copy_node_array_per_cell[i] == node_array_per_cell[i]);
+        }
+
+        REQUIRE(is_same);
+      }
+
+      {
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          auto node_array_list = node_array_per_cell.itemArrays(cell_id);
+          for (size_t i_node = 0; i_node < node_array_per_cell.numberOfSubArrays(cell_id); ++i_node) {
+            auto node_array = node_array_list[i_node];
+            for (size_t i = 0; i < node_array.size(); ++i) {
+              node_array[i] = cell_id + i_node + i;
+            }
+          }
+        }
+      }
+
+      {
+        bool is_same = true;
+        for (size_t i = 0; i < copy_node_array_per_cell.numberOfValues(); ++i) {
+          is_same &= (copy_node_array_per_cell[i] == node_array_per_cell[i]);
+        }
+
+        REQUIRE(not is_same);
+      }
+    }
+
+    SECTION("from weak")
+    {
+      WeakNodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3};
+      for (size_t i = 0; i < node_array_per_cell.numberOfValues(); ++i) {
+        node_array_per_cell[i] = i;
+      }
+
+      NodeArrayPerCell<size_t> copy_node_array_per_cell = copy(node_array_per_cell);
+      {
+        bool is_same = true;
+        for (size_t i = 0; i < copy_node_array_per_cell.numberOfValues(); ++i) {
+          is_same &= (copy_node_array_per_cell[i] == node_array_per_cell[i]);
+        }
+
+        REQUIRE(is_same);
+      }
+
+      {
+        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+          auto node_array_list = node_array_per_cell.itemArrays(cell_id);
+          for (size_t i_node = 0; i_node < node_array_per_cell.numberOfSubArrays(cell_id); ++i_node) {
+            auto node_array = node_array_list[i_node];
+            for (size_t i = 0; i < node_array.size(); ++i) {
+              node_array[i] = cell_id + i_node + i;
+            }
+          }
+        }
+      }
+
+      {
+        bool is_same = true;
+        for (size_t i = 0; i < copy_node_array_per_cell.numberOfValues(); ++i) {
+          is_same &= (copy_node_array_per_cell[i] == node_array_per_cell[i]);
+        }
+
+        REQUIRE(not is_same);
+      }
+    }
+  }
+
+  SECTION("WeakSubItemArrayPerItem")
+  {
+    const Mesh<Connectivity<2>>& mesh_2d = MeshDataBaseForTests::get().cartesianMesh<2>();
+    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+
+    WeakFaceArrayPerCell<int> weak_face_array_per_cell{connectivity, 3};
+
+    for (size_t i = 0; i < weak_face_array_per_cell.numberOfValues(); ++i) {
+      weak_face_array_per_cell[i] = i;
+    }
+
+    FaceArrayPerCell<const int> face_array_per_cell{weak_face_array_per_cell};
+
+    REQUIRE(face_array_per_cell.connectivity_ptr() == weak_face_array_per_cell.connectivity_ptr());
+    REQUIRE(face_array_per_cell.sizeOfArrays() == weak_face_array_per_cell.sizeOfArrays());
+
+    bool is_same = true;
+    for (size_t i = 0; i < weak_face_array_per_cell.numberOfValues(); ++i) {
+      is_same &= (face_array_per_cell[i] == weak_face_array_per_cell[i]);
+    }
+    REQUIRE(is_same);
+  }
+
+#ifndef NDEBUG
+  SECTION("error")
+  {
+    SECTION("checking for build SubItemArrayPerItem")
+    {
+      CellArrayPerNode<int> cell_array_per_node;
+      REQUIRE_THROWS_AS(cell_array_per_node[0], AssertError);
+      REQUIRE_THROWS_AS(cell_array_per_node.itemArrays(NodeId{0}), AssertError);
+      REQUIRE_THROWS_AS(cell_array_per_node(NodeId{0}, 0), AssertError);
+      REQUIRE_THROWS_AS(cell_array_per_node.sizeOfArrays(), AssertError);
+      REQUIRE_THROWS_AS(cell_array_per_node.numberOfValues(), AssertError);
+      REQUIRE_THROWS_AS(cell_array_per_node.numberOfItems(), AssertError);
+      REQUIRE_THROWS_AS(cell_array_per_node.numberOfSubArrays(NodeId{0}), AssertError);
+
+      FaceArrayPerCell<int> face_array_per_cell;
+      REQUIRE_THROWS_AS(face_array_per_cell[0], AssertError);
+      REQUIRE_THROWS_AS(face_array_per_cell.itemArrays(CellId{0}), AssertError);
+      REQUIRE_THROWS_AS(face_array_per_cell(CellId{0}, 0), AssertError);
+      REQUIRE_THROWS_AS(face_array_per_cell.sizeOfArrays(), AssertError);
+      REQUIRE_THROWS_AS(face_array_per_cell.numberOfValues(), AssertError);
+      REQUIRE_THROWS_AS(face_array_per_cell.numberOfItems(), AssertError);
+      REQUIRE_THROWS_AS(face_array_per_cell.numberOfSubArrays(CellId{0}), AssertError);
+
+      CellArrayPerEdge<int> cell_array_per_edge;
+      REQUIRE_THROWS_AS(cell_array_per_edge[0], AssertError);
+      REQUIRE_THROWS_AS(cell_array_per_edge.itemArrays(EdgeId{0}), AssertError);
+      REQUIRE_THROWS_AS(cell_array_per_edge(EdgeId{0}, 0), AssertError);
+      REQUIRE_THROWS_AS(cell_array_per_edge.sizeOfArrays(), AssertError);
+      REQUIRE_THROWS_AS(cell_array_per_edge.numberOfValues(), AssertError);
+      REQUIRE_THROWS_AS(cell_array_per_edge.numberOfItems(), AssertError);
+      REQUIRE_THROWS_AS(cell_array_per_edge.numberOfSubArrays(EdgeId{0}), AssertError);
+
+      NodeArrayPerFace<int> node_array_per_face;
+      REQUIRE_THROWS_AS(node_array_per_face[0], AssertError);
+      REQUIRE_THROWS_AS(node_array_per_face.itemArrays(FaceId{0}), AssertError);
+      REQUIRE_THROWS_AS(node_array_per_face(FaceId{0}, 0), AssertError);
+      REQUIRE_THROWS_AS(node_array_per_face.sizeOfArrays(), AssertError);
+      REQUIRE_THROWS_AS(node_array_per_face.numberOfValues(), AssertError);
+      REQUIRE_THROWS_AS(node_array_per_face.numberOfItems(), AssertError);
+      REQUIRE_THROWS_AS(node_array_per_face.numberOfSubArrays(FaceId{0}), AssertError);
+    }
+
+    SECTION("checking for bounds violation")
+    {
+      const Mesh<Connectivity<3>>& mesh_3d = MeshDataBaseForTests::get().cartesianMesh<3>();
+      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+
+      CellArrayPerFace<int> cell_array_per_face{connectivity, 3};
+      {
+        FaceId invalid_face_id = connectivity.numberOfFaces();
+        REQUIRE_THROWS_AS(cell_array_per_face(invalid_face_id, 0), AssertError);
+      }
+      if (connectivity.numberOfFaces() > 0) {
+        FaceId face_id          = 0;
+        const auto& cell_arrays = cell_array_per_face.itemArrays(face_id);
+        REQUIRE_THROWS_AS(cell_array_per_face(face_id, cell_arrays.size()), AssertError);
+        REQUIRE_THROWS_AS(cell_arrays[cell_arrays.size()], AssertError);
+        REQUIRE_THROWS_AS(cell_array_per_face.itemArrays(face_id)[cell_arrays.size()], AssertError);
+        REQUIRE_THROWS_AS(cell_array_per_face.itemArrays(face_id)[0][cell_array_per_face.sizeOfArrays()], AssertError);
+        REQUIRE_THROWS_AS(cell_array_per_face.itemArrays(face_id)[0][cell_array_per_face.sizeOfArrays()] = 2,
+                          AssertError);
+      }
+
+      FaceArrayPerNode<int> face_array_per_node{connectivity, 5};
+      {
+        NodeId invalid_node_id = connectivity.numberOfNodes();
+        REQUIRE_THROWS_AS(face_array_per_node(invalid_node_id, 0), AssertError);
+      }
+      if (connectivity.numberOfNodes() > 0) {
+        NodeId node_id          = 0;
+        const auto& face_arrays = face_array_per_node.itemArrays(node_id);
+        REQUIRE_THROWS_AS(face_array_per_node(node_id, face_arrays.size()), AssertError);
+        REQUIRE_THROWS_AS(face_arrays[face_arrays.size()], AssertError);
+        REQUIRE_THROWS_AS(face_array_per_node.itemArrays(node_id)[face_arrays.size()], AssertError);
+        REQUIRE_THROWS_AS(face_array_per_node.itemArrays(node_id)[0][face_array_per_node.sizeOfArrays()], AssertError);
+        REQUIRE_THROWS_AS(face_array_per_node.itemArrays(node_id)[0][face_array_per_node.sizeOfArrays()] = 2,
+                          AssertError);
+      }
+
+      EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 3};
+      {
+        CellId invalid_cell_id = connectivity.numberOfCells();
+        REQUIRE_THROWS_AS(edge_array_per_cell(invalid_cell_id, 0), AssertError);
+      }
+      if (connectivity.numberOfCells() > 0) {
+        CellId cell_id          = 0;
+        const auto& edge_arrays = edge_array_per_cell.itemArrays(cell_id);
+        REQUIRE_THROWS_AS(edge_array_per_cell(cell_id, edge_arrays.size()), AssertError);
+        REQUIRE_THROWS_AS(edge_arrays[edge_arrays.size()], AssertError);
+        REQUIRE_THROWS_AS(edge_array_per_cell.itemArrays(cell_id)[edge_arrays.size()], AssertError);
+        REQUIRE_THROWS_AS(edge_array_per_cell.itemArrays(cell_id)[0][edge_array_per_cell.sizeOfArrays()], AssertError);
+        REQUIRE_THROWS_AS(edge_array_per_cell.itemArrays(cell_id)[0][edge_array_per_cell.sizeOfArrays()] == 2,
+                          AssertError);
+      }
+
+      NodeArrayPerEdge<int> node_array_per_edge{connectivity, 3};
+      {
+        EdgeId invalid_edge_id = connectivity.numberOfEdges();
+        REQUIRE_THROWS_AS(node_array_per_edge(invalid_edge_id, 0), AssertError);
+      }
+      if (connectivity.numberOfEdges() > 0) {
+        EdgeId edge_id          = 0;
+        const auto& node_arrays = node_array_per_edge.itemArrays(edge_id);
+        REQUIRE_THROWS_AS(node_array_per_edge(edge_id, node_arrays.size()), AssertError);
+        REQUIRE_THROWS_AS(node_arrays[node_arrays.size()], AssertError);
+        REQUIRE_THROWS_AS(node_array_per_edge.itemArrays(edge_id)[node_arrays.size()], AssertError);
+        REQUIRE_THROWS_AS(node_array_per_edge.itemArrays(edge_id)[0][node_array_per_edge.sizeOfArrays()], AssertError);
+        REQUIRE_THROWS_AS(node_array_per_edge.itemArrays(edge_id)[0][node_array_per_edge.sizeOfArrays()] = 2,
+                          AssertError);
+      }
+    }
+  }
+#endif   // NDEBUG
+}