diff --git a/src/main.cpp b/src/main.cpp
index f277ce412860ccff5b88c256a2b552a8443e3e73..b1a3decd13b36a4b738c49c9815a0c4bfb327d5f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -11,9 +11,7 @@
 // #include <AcousticSolverClass.hpp>
 // #include <AcousticSolverTest.hpp>
 
-#include <Connectivity1D.hpp>
-#include <Connectivity2D.hpp>
-#include <Connectivity3D.hpp>
+#include <Connectivity.hpp>
 
 #include <Mesh.hpp>
 #include <BoundaryCondition.hpp>
@@ -197,7 +195,6 @@ int main(int argc, char *argv[])
           typedef TinyVector<MeshType::dimension> Rd;
 
           const Kokkos::View<const double*> Vj = mesh_data.Vj();
-          const Kokkos::View<const Rd**> Cjr = mesh_data.Cjr();
 
           const double tmax=0.2;
           double t=0;
@@ -304,10 +301,7 @@ int main(int argc, char *argv[])
 
           AcousticSolver<MeshDataType> acoustic_solver(mesh_data, unknowns, bc_list);
 
-          typedef TinyVector<MeshType::dimension> Rd;
-
           const Kokkos::View<const double*> Vj = mesh_data.Vj();
-          const Kokkos::View<const Rd**> Cjr = mesh_data.Cjr();
 
           const double tmax=0.2;
           double t=0;
@@ -403,10 +397,7 @@ int main(int argc, char *argv[])
 
           AcousticSolver<MeshDataType> acoustic_solver(mesh_data, unknowns, bc_list);
 
-          typedef TinyVector<MeshType::dimension> Rd;
-
           const Kokkos::View<const double*> Vj = mesh_data.Vj();
-          const Kokkos::View<const Rd**> Cjr = mesh_data.Cjr();
 
           const double tmax=0.2;
           double t=0;
@@ -431,7 +422,6 @@ int main(int argc, char *argv[])
               dt=tmax-t;
             }
             acoustic_solver.computeNextStep(t,dt, unknowns);
-
             block_eos.updatePandCFromRhoE();
 
             t += dt;
@@ -450,90 +440,8 @@ int main(int argc, char *argv[])
       std::cout << "* "  << rang::fgB::red << "Could not be uglier!" << rang::fg::reset << " (" << __FILE__ << ':' << __LINE__ << ")\n";
 
     } else {
-      // class for acoustic solver test
-      Kokkos::Timer timer;
-      timer.reset();
-      std::shared_ptr<Connectivity1D>connectivity( new Connectivity1D(number));
-      typedef Mesh<Connectivity1D> MeshType;
-      typedef MeshData<MeshType> MeshDataType;
-      typedef FiniteVolumesEulerUnknowns<MeshDataType> UnknownsType;
-
-      MeshType mesh(connectivity);
-      MeshDataType mesh_data(mesh);
-
-      std::vector<BoundaryConditionHandler> bc_list;
-      { // quite dirty!
-        // SymmetryBoundaryCondition<MeshType::dimension>* sym_bc0
-        //     = new SymmetryBoundaryCondition<MeshType::dimension>(std::vector<unsigned int>({0u}),
-        //                                                          TinyVector<1>(-1));
-        // std::shared_ptr<SymmetryBoundaryCondition<1>> bc0(sym_bc0);
-        // bc_list.push_back(BoundaryConditionHandler(bc0));
-        PressureBoundaryCondition* pres_bc0
-            = new PressureBoundaryCondition(1,
-                                            std::vector<unsigned int>({static_cast<unsigned int>(0u)}));
-        std::shared_ptr<PressureBoundaryCondition> bc0(pres_bc0);
-        bc_list.push_back(BoundaryConditionHandler(bc0));
-
-        PressureBoundaryCondition* pres_bc1
-            = new PressureBoundaryCondition(0.1,
-                                            std::vector<unsigned int>({static_cast<unsigned int>(mesh.numberOfCells())}));
-        std::shared_ptr<PressureBoundaryCondition> bc1(pres_bc1);
-        bc_list.push_back(BoundaryConditionHandler(bc1));
-      }
-
-      UnknownsType unknowns(mesh_data);
-
-      unknowns.initializeSod();
-
-      AcousticSolver<MeshDataType> acoustic_solver(mesh_data, unknowns, bc_list);
-
-      typedef TinyVector<MeshType::dimension> Rd;
-
-      const Kokkos::View<const double*> Vj = mesh_data.Vj();
-      const Kokkos::View<const Rd**> Cjr = mesh_data.Cjr();
-
-      const double tmax=0.2;
-      double t=0;
-
-      int itermax=std::numeric_limits<int>::max();
-      int iteration=0;
-
-      Kokkos::View<double*> rhoj = unknowns.rhoj();
-      Kokkos::View<double*> ej = unknowns.ej();
-      Kokkos::View<double*> pj = unknowns.pj();
-      Kokkos::View<double*> gammaj = unknowns.gammaj();
-      Kokkos::View<double*> cj = unknowns.cj();
-
-      BlockPerfectGas block_eos(rhoj, ej, pj, gammaj, cj);
-
-      while((t<tmax) and (iteration<itermax)) {
-        double dt = 0.4*acoustic_solver.acoustic_dt(Vj, cj);
-        if (t+dt>tmax) {
-          dt=tmax-t;
-        }
-
-        acoustic_solver.computeNextStep(t,dt, unknowns);
-
-        block_eos.updatePandCFromRhoE();
-
-        t += dt;
-        ++iteration;
-      }
-
-      std::cout << "* " << rang::style::underline << "Final time" << rang::style::reset
-                << ":  " << rang::fgB::green << t << rang::fg::reset << " (" << iteration << " iterations)\n";
-
-      method_cost_map["AcousticSolverWithMesh"] = timer.seconds();
-
-      { // gnuplot output for density
-        const Kokkos::View<const Rd*> xj   = mesh_data.xj();
-        const Kokkos::View<const double*> rhoj = unknowns.rhoj();
-        std::ofstream fout("rho");
-        for (size_t j=0; j<mesh.numberOfCells(); ++j) {
-          fout << xj[j][0] << ' ' << rhoj[j] << '\n';
-        }
-      }
-
+      std::cerr << "Connectivity1D defined by number of nodes no more implemented\n";
+      std::exit(0);
     }
   }
   catch (const AssertError& error) {
diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt
index f07bc30ba6e13173157a7ddc0b0664ea0920bf60..c28b0643fd8bbf1b2677512423d2185d9a7ff970 100644
--- a/src/mesh/CMakeLists.txt
+++ b/src/mesh/CMakeLists.txt
@@ -5,6 +5,8 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
 
 add_library(
   PastisMesh
+  Connectivity.cpp
+  ConnectivityComputer.cpp
   GmshReader.cpp)
 
 #include_directories(${PASTIS_SOURCE_DIR}/utils)
diff --git a/src/mesh/CellType.hpp b/src/mesh/CellType.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1f444323b5f5073b9aa09d020cc1fc962b1182a5
--- /dev/null
+++ b/src/mesh/CellType.hpp
@@ -0,0 +1,17 @@
+#ifndef CELL_TYPE_HPP
+#define  CELL_TYPE_HPP
+
+enum class CellType : unsigned short
+{
+  Line,
+
+  Triangle,
+  Quadrangle,
+
+  Tetrahedron,
+  Pyramid,
+  Prism,
+  Hexahedron
+};
+
+#endif // CELL_TYPE_HPP
diff --git a/src/mesh/Connectivity.cpp b/src/mesh/Connectivity.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eb5fb87b36947a5e4778f2e38161e6f35cb3f92d
--- /dev/null
+++ b/src/mesh/Connectivity.cpp
@@ -0,0 +1,261 @@
+#include <Connectivity.hpp>
+#include <map>
+
+template<>
+void Connectivity<3>::_computeCellFaceAndFaceNodeConnectivities()
+{
+  typedef std::tuple<unsigned int, unsigned short, bool> CellFaceInfo;
+
+  const auto& cell_to_node_matrix
+      = this->getMatrix(ItemType::cell, ItemType::node);
+
+  Kokkos::View<unsigned short*> cell_nb_faces("cell_nb_faces", this->numberOfCells());
+  std::map<Face, std::vector<CellFaceInfo>> face_cells_map;
+  for (unsigned int j=0; j<this->numberOfCells(); ++j) {
+    const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+
+    switch (m_cell_type[j]) {
+      case CellType::Tetrahedron: {
+        cell_nb_faces[j] = 4;
+        // face 0
+        Face f0({cell_nodes(1),
+                 cell_nodes(2),
+                 cell_nodes(3)});
+        face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed()));
+
+        // face 1
+        Face f1({cell_nodes(0),
+                 cell_nodes(3),
+                 cell_nodes(2)});
+        face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed()));
+
+        // face 2
+        Face f2({cell_nodes(0),
+                 cell_nodes(1),
+                 cell_nodes(3)});
+        face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed()));
+
+        // face 3
+        Face f3({cell_nodes(0),
+                 cell_nodes(2),
+                 cell_nodes(1)});
+        face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed()));
+        break;
+      }
+      case CellType::Hexahedron: {
+        // face 0
+        Face f0({cell_nodes(3),
+                 cell_nodes(2),
+                 cell_nodes(1),
+                 cell_nodes(0)});
+        face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed()));
+
+        // face 1
+        Face f1({cell_nodes(4),
+                 cell_nodes(5),
+                 cell_nodes(6),
+                 cell_nodes(7)});
+        face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed()));
+
+        // face 2
+        Face f2({cell_nodes(0),
+                 cell_nodes(4),
+                 cell_nodes(7),
+                 cell_nodes(3)});
+        face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed()));
+
+        // face 3
+        Face f3({cell_nodes(1),
+                 cell_nodes(2),
+                 cell_nodes(6),
+                 cell_nodes(5)});
+        face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed()));
+
+        // face 4
+        Face f4({cell_nodes(0),
+                 cell_nodes(1),
+                 cell_nodes(5),
+                 cell_nodes(4)});
+        face_cells_map[f4].emplace_back(std::make_tuple(j, 4, f4.reversed()));
+
+        // face 5
+        Face f5({cell_nodes(3),
+                 cell_nodes(7),
+                 cell_nodes(6),
+                 cell_nodes(2)});
+        face_cells_map[f5].emplace_back(std::make_tuple(j, 5, f5.reversed()));
+
+        cell_nb_faces[j] = 6;
+        break;
+      }
+      default: {
+        std::cerr << "unexpected cell type!\n";
+        std::exit(0);
+      }
+    }
+  }
+
+  {
+    auto& cell_to_face_matrix
+        = m_item_to_item_matrix[itemId(ItemType::cell)][itemId(ItemType::face)];
+    std::vector<std::vector<unsigned int>> cell_to_face_vector(this->numberOfCells());
+    for (size_t j=0; j<cell_to_face_vector.size(); ++j) {
+      cell_to_face_vector[j].resize(cell_nb_faces[j]);
+    }
+    int l=0;
+    for (const auto& face_cells_vector : face_cells_map) {
+      const auto& cells_vector = face_cells_vector.second;
+      for (unsigned short lj=0; lj<cells_vector.size(); ++lj) {
+        const auto& [cell_number, cell_local_face, reversed] = cells_vector[lj];
+        cell_to_face_vector[cell_number][cell_local_face] = l;
+      }
+      ++l;
+    }
+    cell_to_face_matrix = cell_to_face_vector;
+  }
+
+  FaceValuePerCell<bool> cell_face_is_reversed(*this);
+  {
+    for (const auto& face_cells_vector : face_cells_map) {
+      const auto& cells_vector = face_cells_vector.second;
+      for (unsigned short lj=0; lj<cells_vector.size(); ++lj) {
+        const auto& [cell_number, cell_local_face, reversed] = cells_vector[lj];
+        cell_face_is_reversed(cell_number, cell_local_face) = reversed;
+      }
+    }
+
+    m_cell_face_is_reversed = cell_face_is_reversed;
+  }
+
+  {
+    auto& face_to_node_matrix
+        = m_item_to_item_matrix[itemId(ItemType::face)][itemId(ItemType::node)];
+
+    std::vector<std::vector<unsigned int>> face_to_node_vector(face_cells_map.size());
+    int l=0;
+    for (const auto& face_info : face_cells_map) {
+      const Face& face = face_info.first;
+      face_to_node_vector[l] = face.nodeIdList();
+      ++l;
+    }
+    face_to_node_matrix = face_to_node_vector;
+  }
+
+  {
+    int l=0;
+    for (const auto& face_cells_vector : face_cells_map) {
+      const Face& face = face_cells_vector.first;
+      m_face_number_map[face] = l;
+      ++l;
+    }
+  }
+
+#warning check that the number of cell per faces is <=2
+}
+
+template<>
+void Connectivity<2>::_computeCellFaceAndFaceNodeConnectivities()
+{
+  const auto& cell_to_node_matrix
+      = this->getMatrix(ItemType::cell, ItemType::node);
+
+  // In 2D faces are simply define
+  typedef std::pair<unsigned int, unsigned short> CellFaceId;
+  std::map<Face, std::vector<CellFaceId>> face_cells_map;
+  for (unsigned int j=0; j<this->numberOfCells(); ++j) {
+    const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+    for (unsigned short r=0; r<cell_nodes.length; ++r) {
+      unsigned int node0_id = cell_nodes(r);
+      unsigned int node1_id = cell_nodes((r+1)%cell_nodes.length);
+      if (node1_id<node0_id) {
+        std::swap(node0_id, node1_id);
+      }
+      face_cells_map[Face({node0_id, node1_id})].push_back(std::make_pair(j, r));
+    }
+  }
+
+  {
+    int l=0;
+    for (const auto& face_cells_vector : face_cells_map) {
+      const Face& face = face_cells_vector.first;
+      m_face_number_map[face] = l;
+      ++l;
+    }
+  }
+
+  {
+    std::vector<std::vector<unsigned int>> face_to_node_vector(face_cells_map.size());
+    int l=0;
+    for (const auto& face_info : face_cells_map) {
+      const Face& face = face_info.first;
+      face_to_node_vector[l] = {face.m_node0_id, face.m_node1_id};
+      ++l;
+    }
+    auto& face_to_node_matrix
+        = m_item_to_item_matrix[itemId(ItemType::face)][itemId(ItemType::node)];
+    face_to_node_matrix = face_to_node_vector;
+  }
+
+  {
+    std::vector<std::vector<unsigned int>> face_to_cell_vector(face_cells_map.size());
+    int l=0;
+    for (const auto& face_cells_vector : face_cells_map) {
+      const auto& [face, cell_info_vector] = face_cells_vector;
+      for (const auto& cell_info : cell_info_vector) {
+        face_to_cell_vector[l].push_back(cell_info.second);
+      }
+      ++l;
+    }
+    auto& face_to_cell_matrix
+        = m_item_to_item_matrix[itemId(ItemType::face)][itemId(ItemType::cell)];
+    face_to_cell_matrix = face_to_cell_vector;
+  }
+}
+
+
+template<size_t Dimension>
+Connectivity<Dimension>::
+Connectivity(const std::vector<std::vector<unsigned int>>& cell_by_node_vector,
+             const std::vector<CellType>& cell_type_vector)
+{
+  Assert(cell_by_node_vector.size() == cell_type_vector.size());
+
+  auto& cell_to_node_matrix
+      = m_item_to_item_matrix[itemId(ItemType::cell)][itemId(ItemType::node)];
+  cell_to_node_matrix = cell_by_node_vector;
+
+  Assert(this->numberOfCells()>0);
+
+  {
+    Kokkos::View<CellType*> cell_type("cell_type", this->numberOfCells());
+    Kokkos::parallel_for(this->numberOfCells(), KOKKOS_LAMBDA(const int& j){
+        cell_type[j] = cell_type_vector[j];
+      });
+    m_cell_type = cell_type;
+  }
+  {
+    Kokkos::View<double*> inv_cell_nb_nodes("inv_cell_nb_nodes", this->numberOfCells());
+    Kokkos::parallel_for(this->numberOfCells(), KOKKOS_LAMBDA(const int& j){
+        const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+        inv_cell_nb_nodes[j] = 1./cell_nodes.length;
+      });
+    m_inv_cell_nb_nodes = inv_cell_nb_nodes;
+  }
+
+  if constexpr (Dimension>1) {
+    this->_computeCellFaceAndFaceNodeConnectivities();
+  }
+}
+
+
+template Connectivity1D::
+Connectivity(const std::vector<std::vector<unsigned int>>& cell_by_node_vector,
+             const std::vector<CellType>& cell_type_vector);
+
+template Connectivity2D::
+Connectivity(const std::vector<std::vector<unsigned int>>& cell_by_node_vector,
+             const std::vector<CellType>& cell_type_vector);
+
+template Connectivity3D::
+Connectivity(const std::vector<std::vector<unsigned int>>& cell_by_node_vector,
+             const std::vector<CellType>& cell_type_vector);
diff --git a/src/mesh/Connectivity.hpp b/src/mesh/Connectivity.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0994fc9c84e4105c3f9de66b55231f3fd61ff8fe
--- /dev/null
+++ b/src/mesh/Connectivity.hpp
@@ -0,0 +1,498 @@
+#ifndef CONNECTIVITY_HPP
+#define CONNECTIVITY_HPP
+
+#include <PastisAssert.hpp>
+#include <TinyVector.hpp>
+
+#include <Kokkos_Core.hpp>
+
+#include <IConnectivity.hpp>
+
+#include <ConnectivityMatrix.hpp>
+#include <SubItemValuePerItem.hpp>
+#include <ConnectivityComputer.hpp>
+
+#include <vector>
+#include <unordered_map>
+#include <algorithm>
+
+#include <CellType.hpp>
+
+#include <RefId.hpp>
+#include <ItemType.hpp>
+#include <RefNodeList.hpp>
+#include <RefFaceList.hpp>
+
+#include <tuple>
+#include <algorithm>
+
+template <size_t Dimension>
+class Connectivity;
+
+template <size_t Dimension>
+class ConnectivityFace;
+
+template<>
+class ConnectivityFace<1>
+{
+ public:
+  friend struct Hash;
+  struct Hash
+  {
+    size_t operator()(const ConnectivityFace& f) const;
+  };
+};
+
+template<>
+class ConnectivityFace<2>
+{
+ public:
+  friend struct Hash;
+  struct Hash
+  {
+    size_t operator()(const ConnectivityFace& f) const {
+      size_t hash = 0;
+      hash ^= std::hash<unsigned int>()(f.m_node0_id);
+      hash ^= std::hash<unsigned int>()(f.m_node1_id) >> 1;
+      return hash;
+    }
+  };
+
+  unsigned int m_node0_id;
+  unsigned int m_node1_id;
+
+  friend std::ostream& operator<<(std::ostream& os, const ConnectivityFace& f)
+  {
+    os << f.m_node0_id << ' ' << f.m_node1_id << ' ';
+    return os;
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  bool operator==(const ConnectivityFace& f) const
+  {
+    return ((m_node0_id == f.m_node0_id) and
+            (m_node1_id == f.m_node1_id));
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  bool operator<(const ConnectivityFace& f) const
+  {
+    return ((m_node0_id<f.m_node0_id) or
+            ((m_node0_id == f.m_node0_id) and
+             (m_node1_id<f.m_node1_id)));
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  ConnectivityFace& operator=(const ConnectivityFace&) = default;
+
+  KOKKOS_INLINE_FUNCTION
+  ConnectivityFace& operator=(ConnectivityFace&&) = default;
+
+  KOKKOS_INLINE_FUNCTION
+  ConnectivityFace(const std::vector<unsigned int>& given_node_id_list)
+  {
+    Assert(given_node_id_list.size()==2);
+#warning rework this dirty constructor
+    const auto& [min, max] = std::minmax(given_node_id_list[0], given_node_id_list[1]);
+    m_node0_id = min;
+    m_node1_id = max;
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  ConnectivityFace(const ConnectivityFace&) = default;
+
+  KOKKOS_INLINE_FUNCTION
+  ConnectivityFace(ConnectivityFace&&) = default;
+
+  KOKKOS_INLINE_FUNCTION
+  ~ConnectivityFace() = default;
+};
+
+template <>
+class ConnectivityFace<3>
+{
+ private:
+  friend class Connectivity<3>;
+  friend struct Hash;
+  struct Hash
+  {
+    size_t operator()(const ConnectivityFace& f) const {
+      size_t hash = 0;
+      for (size_t i=0; i<f.m_node_id_list.size(); ++i) {
+        hash ^= std::hash<unsigned int>()(f.m_node_id_list[i]) >> i;
+      }
+      return hash;
+    }
+  };
+
+  bool m_reversed;
+  std::vector<unsigned int> m_node_id_list;
+
+  friend std::ostream& operator<<(std::ostream& os, const ConnectivityFace& f)
+  {
+    for (auto id : f.m_node_id_list) {
+      std::cout << id << ' ';
+    }
+    return os;
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const bool& reversed() const
+  {
+    return m_reversed;
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const std::vector<unsigned int>& nodeIdList() const
+  {
+    return m_node_id_list;
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  std::vector<unsigned int> _sort(const std::vector<unsigned int>& node_list)
+  {
+    const auto min_id = std::min_element(node_list.begin(), node_list.end());
+    const int shift = std::distance(node_list.begin(), min_id);
+
+    std::vector<unsigned int> rotated_node_list(node_list.size());
+    if (node_list[(shift+1)%node_list.size()] > node_list[(shift+node_list.size()-1)%node_list.size()]) {
+      for (size_t i=0; i<node_list.size(); ++i) {
+        rotated_node_list[i] = node_list[(shift+node_list.size()-i)%node_list.size()];
+        m_reversed = true;
+      }
+    } else {
+      for (size_t i=0; i<node_list.size(); ++i) {
+        rotated_node_list[i] = node_list[(shift+i)%node_list.size()];
+      }
+    }
+
+    return rotated_node_list;
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  ConnectivityFace(const std::vector<unsigned int>& given_node_id_list)
+      : m_reversed(false),
+        m_node_id_list(_sort(given_node_id_list))
+  {
+    ;
+  }
+
+ public:
+  bool operator==(const ConnectivityFace& f) const
+  {
+    if (m_node_id_list.size() == f.nodeIdList().size()) {
+      for (size_t j=0; j<m_node_id_list.size(); ++j) {
+        if (m_node_id_list[j] != f.nodeIdList()[j]) {
+          return false;
+        }
+      }
+      return true;
+    }
+    return false;
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  bool operator<(const ConnectivityFace& f) const
+  {
+    const size_t min_nb_nodes = std::min(f.m_node_id_list.size(), m_node_id_list.size());
+    for (size_t i=0; i<min_nb_nodes; ++i) {
+      if (m_node_id_list[i] <  f.m_node_id_list[i]) return true;
+      if (m_node_id_list[i] != f.m_node_id_list[i]) return false;
+    }
+    return m_node_id_list.size() < f.m_node_id_list.size();
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  ConnectivityFace& operator=(const ConnectivityFace&) = default;
+
+  KOKKOS_INLINE_FUNCTION
+  ConnectivityFace& operator=(ConnectivityFace&&) = default;
+
+  KOKKOS_INLINE_FUNCTION
+  ConnectivityFace(const ConnectivityFace&) = default;
+
+  KOKKOS_INLINE_FUNCTION
+  ConnectivityFace(ConnectivityFace&&) = default;
+
+
+  KOKKOS_INLINE_FUNCTION
+  ConnectivityFace() = delete;
+
+  KOKKOS_INLINE_FUNCTION
+  ~ConnectivityFace() = default;
+};
+
+template <size_t Dimension>
+class Connectivity final
+    : public IConnectivity
+{
+ private:
+  constexpr static auto& itemId = ItemId<Dimension>::itemId;
+
+ public:
+  static constexpr size_t dimension = Dimension;
+
+ private:
+  ConnectivityMatrix m_item_to_item_matrix[Dimension+1][Dimension+1];
+  Kokkos::View<const CellType*> m_cell_type;
+
+  FaceValuePerCell<const bool> m_cell_face_is_reversed;
+
+  NodeValuePerCell<const unsigned short> m_cell_local_numbers_in_their_nodes;
+  EdgeValuePerCell<const unsigned short> m_cell_local_numbers_in_their_edges;
+  FaceValuePerCell<const unsigned short> m_cell_local_numbers_in_their_faces;
+
+  CellValuePerFace<const unsigned short> m_face_local_numbers_in_their_cells;
+  EdgeValuePerFace<const unsigned short> m_face_local_numbers_in_their_edges;
+  NodeValuePerFace<const unsigned short> m_face_local_numbers_in_their_nodes;
+
+  CellValuePerEdge<const unsigned short> m_edge_local_numbers_in_their_cells;
+  FaceValuePerEdge<const unsigned short> m_edge_local_numbers_in_their_faces;
+  NodeValuePerEdge<const unsigned short> m_edge_local_numbers_in_their_nodes;
+
+  CellValuePerNode<const unsigned short> m_node_local_numbers_in_their_cells;
+  EdgeValuePerNode<const unsigned short> m_node_local_numbers_in_their_edges;
+  FaceValuePerNode<const unsigned short> m_node_local_numbers_in_their_faces;
+
+  ConnectivityComputer m_connectivity_computer;
+
+  std::vector<RefFaceList> m_ref_face_list;
+  std::vector<RefNodeList> m_ref_node_list;
+
+  Kokkos::View<double*> m_inv_cell_nb_nodes;
+
+  using Face = ConnectivityFace<Dimension>;
+
+  std::unordered_map<Face, unsigned int, typename Face::Hash> m_face_number_map;
+
+  void _computeCellFaceAndFaceNodeConnectivities();
+
+  template <typename SubItemValuePerItemType>
+  KOKKOS_INLINE_FUNCTION
+  const SubItemValuePerItemType&
+  _lazzyBuildItemNumberInTheirChild(const SubItemValuePerItemType& sub_item_value_per_item) const
+  {
+    if (not sub_item_value_per_item.isBuilt()) {
+      const_cast<SubItemValuePerItemType&>(sub_item_value_per_item)
+          = m_connectivity_computer
+          . computeLocalItemNumberInChildItem<SubItemValuePerItemType::item_t,
+                                              SubItemValuePerItemType::sub_item_t>(*this);
+    }
+    return sub_item_value_per_item;
+  }
+
+ public:
+
+  KOKKOS_INLINE_FUNCTION
+  const bool& isConnectivityMatrixBuilt(const ItemType& item_type_0,
+                                        const ItemType& item_type_1) const
+  {
+    const ConnectivityMatrix& connectivity_matrix
+        = m_item_to_item_matrix[itemId(item_type_0)][itemId(item_type_1)];
+    return connectivity_matrix.isBuilt();
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const ConnectivityMatrix& getMatrix(const ItemType& item_type_0,
+                                      const ItemType& item_type_1) const
+  {
+    const ConnectivityMatrix& connectivity_matrix
+        = m_item_to_item_matrix[itemId(item_type_0)][itemId(item_type_1)];
+    if (not connectivity_matrix.isBuilt()) {
+      const_cast<ConnectivityMatrix&>(connectivity_matrix)
+          = m_connectivity_computer
+          . computeConnectivityMatrix(*this, item_type_0, item_type_1);
+
+    }
+    return connectivity_matrix;
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& cellFaceIsReversed() const
+  {
+    static_assert(dimension>1, "reversed faces makes no sense in dimension 1");
+    return m_cell_face_is_reversed;
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& cellLocalNumbersInTheirNodes() const
+  {
+    return _lazzyBuildItemNumberInTheirChild(m_cell_local_numbers_in_their_nodes);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& cellLocalNumbersInTheirEdges() const
+  {
+    if constexpr (dimension>2) {
+      return _lazzyBuildItemNumberInTheirChild(m_cell_local_numbers_in_their_edges);
+    } else {
+      return cellLocalNumbersInTheirFaces();
+    }
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& cellLocalNumbersInTheirFaces() const
+  {
+    if constexpr (dimension>1) {
+      return _lazzyBuildItemNumberInTheirChild(m_cell_local_numbers_in_their_faces);
+    } else {
+      return cellLocalNumbersInTheirNodes();
+    }
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& faceLocalNumbersInTheirCells() const
+  {
+    if constexpr(dimension>1) {
+      return _lazzyBuildItemNumberInTheirChild(m_face_local_numbers_in_their_cells);
+    } else {
+      return nodeLocalNumbersInTheirCells();
+    }
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& faceLocalNumbersInTheirEdges() const
+  {
+    static_assert(dimension>2,"this function has no meaning in 1d or 2d");
+    return _lazzyBuildItemNumberInTheirChild(m_face_local_numbers_in_their_edges);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& faceLocalNumbersInTheirNodes() const
+  {
+    static_assert(dimension>1,"this function has no meaning in 1d");
+    return _lazzyBuildItemNumberInTheirChild(m_face_local_numbers_in_their_nodes);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& edgeLocalNumbersInTheirCells() const
+  {
+    if constexpr (dimension>2) {
+      return _lazzyBuildItemNumberInTheirChild(m_edge_local_numbers_in_their_cells);
+    } else {
+      return faceLocalNumbersInTheirCells();
+    }
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& edgeLocalNumbersInTheirFaces() const
+  {
+    static_assert(dimension>2, "this function has no meaning in 1d or 2d");
+    return _lazzyBuildItemNumberInTheirChild(m_edge_local_numbers_in_their_faces);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& edgeLocalNumbersInTheirNodes() const
+  {
+    static_assert(dimension>2, "this function has no meaning in 1d or 2d");
+    return _lazzyBuildItemNumberInTheirChild(m_edge_local_numbers_in_their_nodes);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& nodeLocalNumbersInTheirCells() const
+  {
+    return _lazzyBuildItemNumberInTheirChild(m_node_local_numbers_in_their_cells);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& nodeLocalNumbersInTheirEdges() const
+  {
+    static_assert(dimension>2, "this function has no meaning in 1d or 2d");
+    return _lazzyBuildItemNumberInTheirChild(m_node_local_numbers_in_their_edges);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto& nodeLocalNumbersInTheirFaces() const
+  {
+    static_assert(dimension>1,"this function has no meaning in 1d");
+    return _lazzyBuildItemNumberInTheirChild(m_node_local_numbers_in_their_faces);
+  }
+
+  void addRefFaceList(const RefFaceList& ref_face_list)
+  {
+    m_ref_face_list.push_back(ref_face_list);
+  }
+
+  size_t numberOfRefFaceList() const
+  {
+    return m_ref_face_list.size();
+  }
+
+  const RefFaceList& refFaceList(const size_t& i) const
+  {
+    return m_ref_face_list[i];
+  }
+
+  void addRefNodeList(const RefNodeList& ref_node_list)
+  {
+    m_ref_node_list.push_back(ref_node_list);
+  }
+
+  size_t numberOfRefNodeList() const
+  {
+    return m_ref_node_list.size();
+  }
+
+  const RefNodeList& refNodeList(const size_t& i) const
+  {
+    return m_ref_node_list[i];
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  size_t numberOfNodes() const
+  {
+    const auto& node_to_cell_matrix
+        = this->getMatrix(ItemType::node,ItemType::cell);
+    return node_to_cell_matrix.numRows();
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  size_t numberOfFaces() const
+  {
+    const auto& face_to_node_matrix
+        = this->getMatrix(ItemType::face,ItemType::cell);
+    return face_to_node_matrix.numRows();
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  size_t numberOfCells() const
+  {
+    const auto& cell_to_node_matrix
+        = this->getMatrix(ItemType::cell,ItemType::node);
+    return cell_to_node_matrix.numRows();
+  }
+
+  const Kokkos::View<const double*> invCellNbNodes() const
+  {
+#warning add calculation on demand when variables will be defined
+    return m_inv_cell_nb_nodes;
+  }
+
+  unsigned int getFaceNumber(const std::vector<unsigned int>& face_nodes) const
+  {
+    const Face face(face_nodes);
+    auto i_face = m_face_number_map.find(face);
+    if (i_face == m_face_number_map.end()) {
+      std::cerr << "Face " << face << " not found!\n";
+      throw std::exception();
+      std::exit(0);
+    }
+    return i_face->second;
+  }
+
+  Connectivity(const Connectivity&) = delete;
+
+  Connectivity(const std::vector<std::vector<unsigned int>>& cell_by_node_vector,
+               const std::vector<CellType>& cell_type_vector);
+
+  ~Connectivity()
+  {
+    ;
+  }
+};
+
+using Connectivity3D = Connectivity<3>;
+using Connectivity2D = Connectivity<2>;
+using Connectivity1D = Connectivity<1>;
+
+#endif // CONNECTIVITY_HPP
diff --git a/src/mesh/Connectivity1D.hpp b/src/mesh/Connectivity1D.hpp
deleted file mode 100644
index d0422f7b25a7b1df91e444f9bea0b9e8ab04a602..0000000000000000000000000000000000000000
--- a/src/mesh/Connectivity1D.hpp
+++ /dev/null
@@ -1,211 +0,0 @@
-#ifndef CONNECTIVITY_1D_HPP
-#define CONNECTIVITY_1D_HPP
-
-#include <Kokkos_Core.hpp>
-#include <PastisAssert.hpp>
-
-#include <TinyVector.hpp>
-#include <ConnectivityUtils.hpp>
-
-#include <RefId.hpp>
-#include <RefNodeList.hpp>
-
-class Connectivity1D
-{
-public:
-  static constexpr size_t dimension = 1;
-
-private:
-  std::vector<RefNodeList> m_ref_node_list;
-
-  size_t  m_number_of_nodes;
-  const size_t& m_number_of_faces = m_number_of_nodes;
-  const size_t  m_number_of_cells;
-
-  const Kokkos::View<const unsigned int**> m_cell_nodes;
-  const Kokkos::View<const unsigned int**>& m_cell_faces = m_cell_nodes;
-
-  const Kokkos::View<const unsigned short*> m_cell_nb_nodes;
-  Kokkos::View<double*> m_inv_cell_nb_nodes;
-  const Kokkos::View<const unsigned short*>& m_cell_nb_faces = m_cell_nb_nodes;
-
-  Kokkos::View<const unsigned short*> m_node_nb_cells;
-  const Kokkos::View<const unsigned short*>& m_face_nb_cells = m_node_nb_cells;
-
-  Kokkos::View<const unsigned int**> m_node_cells;
-  const Kokkos::View<const unsigned int**>& m_face_cells = m_node_cells;
-
-  Kokkos::View<const unsigned short**> m_node_cell_local_node;
-  const Kokkos::View<const unsigned short**>& m_face_cell_local_face = m_node_cell_local_node;
-
-  size_t  m_max_nb_node_per_cell;
-
-  const Kokkos::View<const unsigned int**>
-  _buildCellNodes(const size_t& number_of_cells)
-  {
-    Kokkos::View<unsigned int*[2]> cell_nodes("cell_nodes", number_of_cells);
-
-    Kokkos::parallel_for(m_number_of_cells, KOKKOS_LAMBDA(const size_t& j) {
-        cell_nodes(j,0) = j;
-        cell_nodes(j,1) = j+1;
-      });
-
-    return cell_nodes;
-  }
-
-
-  const Kokkos::View<const unsigned short*>
-  _buildCellNbNodes(const size_t& number_of_cells)
-  {
-    Kokkos::View<unsigned short*>  cell_nb_nodes("cell_nb_nodes", number_of_cells);
-    Kokkos::parallel_for(m_number_of_cells, KOKKOS_LAMBDA(const size_t& j) {
-        cell_nb_nodes[j] = 2;
-      });
-
-    return cell_nb_nodes;
-  }
-
-public:
-  void addRefNodeList(const RefNodeList& ref_node_list)
-  {
-    m_ref_node_list.push_back(ref_node_list);
-  }
-
-  size_t numberOfRefNodeList() const
-  {
-    return m_ref_node_list.size();
-  }
-
-  const RefNodeList& refNodeList(const size_t& i) const
-  {
-    return m_ref_node_list[i];
-  }
-
-  const size_t& numberOfNodes() const
-  {
-    return m_number_of_nodes;
-  }
-
-  const size_t& numberOfFaces() const
-  {
-    return m_number_of_faces;
-  }
-
-  const size_t& numberOfCells() const
-  {
-    return m_number_of_cells;
-  }
-
-  const size_t& maxNbNodePerCell() const
-  {
-    return m_max_nb_node_per_cell;
-  }
-
-  const Kokkos::View<const unsigned int**> cellNodes() const
-  {
-    return m_cell_nodes;
-  }
-
-  const Kokkos::View<const unsigned int**> cellFaces() const
-  {
-    return m_cell_faces;
-  }
-
-  const Kokkos::View<const unsigned short*> nodeNbCells() const
-  {
-    return m_node_nb_cells;
-  }
-
-  const Kokkos::View<const unsigned short*> cellNbNodes() const
-  {
-    return m_cell_nb_nodes;
-  }
-
-  const Kokkos::View<const double*> invCellNbNodes() const
-  {
-    return m_inv_cell_nb_nodes;
-  }
-
-  const Kokkos::View<const unsigned short*> cellNbFaces() const
-  {
-    return m_cell_nb_faces;
-  }
-
-  const Kokkos::View<const unsigned short*> faceNbCells() const
-  {
-    return m_face_nb_cells;
-  }
-
-  const Kokkos::View<const unsigned int**> nodeCells() const
-  {
-    return m_node_cells;
-  }
-
-  const Kokkos::View<const unsigned int**> faceCells() const
-  {
-    return m_face_cells;
-  }
-
-  const Kokkos::View<const unsigned short**> nodeCellLocalNode() const
-  {
-    return m_node_cell_local_node;
-  }
-
-  const Kokkos::View<const unsigned short**> faceCellLocalFace() const
-  {
-    return m_face_cell_local_face;
-  }
-
-  Connectivity1D(const Connectivity1D&) = delete;
-
-  Connectivity1D(const size_t& number_of_cells)
-    : m_number_of_cells (number_of_cells),
-      m_cell_nodes (_buildCellNodes(number_of_cells)),
-      m_cell_nb_nodes (_buildCellNbNodes(number_of_cells)),
-      m_inv_cell_nb_nodes ("inv_cell_nb_nodes", m_number_of_cells)
-  {
-    Assert(number_of_cells>0);
-    Kokkos::parallel_for(m_number_of_cells, KOKKOS_LAMBDA(const size_t& j) {
-        m_inv_cell_nb_nodes[j] = 1./m_cell_nb_nodes[j];
-      });
-
-    ConnectivityUtils utils;
-    utils.computeNodeCellConnectivity(m_cell_nodes,
-                                      m_cell_nb_nodes,
-                                      m_number_of_cells,
-                                      m_max_nb_node_per_cell,
-                                      m_number_of_nodes,
-                                      m_node_nb_cells,
-                                      m_node_cells,
-                                      m_node_cell_local_node);
-  }
-
-  Connectivity1D(const Kokkos::View<const unsigned short*> cell_nb_nodes,
-                 const Kokkos::View<const unsigned int**> cell_nodes)
-    : m_number_of_cells (cell_nb_nodes.extent(0)),
-      m_cell_nodes (cell_nodes),
-      m_cell_nb_nodes (cell_nb_nodes),
-      m_inv_cell_nb_nodes ("inv_cell_nb_nodes", m_number_of_cells)
-  {
-    Kokkos::parallel_for(m_number_of_cells, KOKKOS_LAMBDA(const size_t& j) {
-        m_inv_cell_nb_nodes[j] = 1./m_cell_nb_nodes[j];
-      });
-
-    ConnectivityUtils utils;
-    utils.computeNodeCellConnectivity(m_cell_nodes,
-                                      m_cell_nb_nodes,
-                                      m_number_of_cells,
-                                      m_max_nb_node_per_cell,
-                                      m_number_of_nodes,
-                                      m_node_nb_cells,
-                                      m_node_cells,
-                                      m_node_cell_local_node);
-  }
-
-  ~Connectivity1D()
-  {
-    ;
-  }
-};
-
-#endif // CONNECTIVITY_1D_HPP
diff --git a/src/mesh/Connectivity2D.hpp b/src/mesh/Connectivity2D.hpp
deleted file mode 100644
index bf87b5b9ab2f69306ec4dbc68958927f7df6df6f..0000000000000000000000000000000000000000
--- a/src/mesh/Connectivity2D.hpp
+++ /dev/null
@@ -1,352 +0,0 @@
-#ifndef CONNECTIVITY_2D_HPP
-#define CONNECTIVITY_2D_HPP
-
-#include <Kokkos_Core.hpp>
-#include <PastisAssert.hpp>
-#include <TinyVector.hpp>
-
-#include <ConnectivityUtils.hpp>
-#include <vector>
-#include <map>
-#include <algorithm>
-
-#include <RefId.hpp>
-#include <RefNodeList.hpp>
-#include <RefFaceList.hpp>
-
-class Connectivity2D
-{
- public:
-  static constexpr size_t dimension = 2;
-
- private:
-  std::vector<RefFaceList> m_ref_face_list;
-  std::vector<RefNodeList> m_ref_node_list;
-
-  const size_t  m_number_of_cells;
-  size_t m_number_of_faces;
-  size_t m_number_of_nodes;
-
-  const Kokkos::View<const unsigned short*> m_cell_nb_nodes;
-  const Kokkos::View<const unsigned int**> m_cell_nodes;
-  Kokkos::View<double*> m_inv_cell_nb_nodes;
-
-  Kokkos::View<const unsigned short*> m_cell_nb_faces;
-  Kokkos::View<unsigned int**> m_cell_faces;
-
-  Kokkos::View<const unsigned short*> m_node_nb_cells;
-  Kokkos::View<const unsigned int**> m_node_cells;
-  Kokkos::View<const unsigned short**> m_node_cell_local_node;
-
-  Kokkos::View<unsigned short*> m_face_nb_cells;
-  Kokkos::View<unsigned int**> m_face_cells;
-  Kokkos::View<unsigned short**> m_face_cell_local_face;
-
-  Kokkos::View<unsigned short*> m_face_nb_nodes;
-  Kokkos::View<unsigned int**> m_face_nodes;
-  Kokkos::View<unsigned short**> m_face_node_local_face;
-
-  size_t  m_max_nb_node_per_cell;
-
-  struct Face
-  {
-    const unsigned int m_node0_id;
-    const unsigned int m_node1_id;
-
-    KOKKOS_INLINE_FUNCTION
-    bool operator<(const Face& f) const
-    {
-      return ((m_node0_id<f.m_node0_id) or
-              ((m_node0_id == f.m_node0_id) and
-               (m_node1_id<f.m_node1_id)));
-    }
-
-    KOKKOS_INLINE_FUNCTION
-    Face& operator=(const Face&) = default;
-
-    KOKKOS_INLINE_FUNCTION
-    Face& operator=(Face&&) = default;
-
-    KOKKOS_INLINE_FUNCTION
-    Face(const Face&) = default;
-
-    KOKKOS_INLINE_FUNCTION
-    Face(Face&&) = default;
-
-    KOKKOS_INLINE_FUNCTION
-    Face(unsigned int node0_id,
-         unsigned int node1_id)
-        : m_node0_id(node0_id),
-          m_node1_id(node1_id)
-    {
-      ;
-    }
-
-    KOKKOS_INLINE_FUNCTION
-    ~Face() = default;
-  };
-
-  void _computeFaceCellConnectivities()
-  {
-    // In 2D faces are simply define
-    typedef std::pair<unsigned int, unsigned short> CellFaceId;
-    std::map<Face, std::vector<CellFaceId>> face_cells_map;
-    for (unsigned int j=0; j<m_number_of_cells; ++j) {
-      const unsigned short cell_nb_nodes = m_cell_nb_nodes[j];
-      for (unsigned short r=0; r<cell_nb_nodes; ++r) {
-        unsigned int node0_id = m_cell_nodes(j,r);
-        unsigned int node1_id = m_cell_nodes(j,(r+1)%cell_nb_nodes);
-        if (node1_id<node0_id) {
-          std::swap(node0_id, node1_id);
-        }
-        face_cells_map[Face(node0_id, node1_id)].push_back(std::make_pair(j, r));
-      }
-    }
-
-    m_number_of_faces = face_cells_map.size();
-    Kokkos::View<unsigned short*> face_nb_nodes("face_nb_nodes", m_number_of_faces);
-    Kokkos::parallel_for(m_number_of_faces, KOKKOS_LAMBDA(const unsigned int& l) {
-        face_nb_nodes[l] = 2;
-      });
-    m_face_nb_nodes = face_nb_nodes;
-
-    Kokkos::View<unsigned int**> face_nodes("face_nodes", m_number_of_faces, 2);
-    {
-      int l=0;
-      for (const auto& face_cells_vector : face_cells_map) {
-        const Face& face = face_cells_vector.first;
-        face_nodes(l,0) = face.m_node0_id;
-        face_nodes(l,1) = face.m_node1_id;
-        ++l;
-      }
-    }
-    m_face_nodes = face_nodes;
-
-    Kokkos::View<unsigned short*> face_nb_cells("face_nb_cells", m_number_of_faces);
-    {
-      int l=0;
-      for (const auto& face_cells_vector : face_cells_map) {
-        const auto& cells_vector = face_cells_vector.second;
-        face_nb_cells[l] = cells_vector.size();
-        ++l;
-      }
-    }
-    m_face_nb_cells = face_nb_cells;
-
-    Kokkos::View<unsigned int**> face_cells("face_cells", face_cells_map.size(), 2);
-    {
-      int l=0;
-      for (const auto& face_cells_vector : face_cells_map) {
-        const auto& cells_vector = face_cells_vector.second;
-        for (unsigned short lj=0; lj<cells_vector.size(); ++lj) {
-          unsigned int cell_number = cells_vector[lj].first;
-          face_cells(l,lj) = cell_number;
-        }
-        ++l;
-      }
-    }
-    m_face_cells = face_cells;
-
-    // In 2d cell_nb_faces = cell_nb_node
-    m_cell_nb_faces = m_cell_nb_nodes;
-    Kokkos::View<unsigned int**> cell_faces("cell_faces", m_number_of_faces, m_max_nb_node_per_cell);
-    {
-      int l=0;
-      for (const auto& face_cells_vector : face_cells_map) {
-        const auto& cells_vector = face_cells_vector.second;
-        for (unsigned short lj=0; lj<cells_vector.size(); ++lj) {
-          unsigned int cell_number = cells_vector[lj].first;
-          unsigned short cell_local_face = cells_vector[lj].second;
-          cell_faces(cell_number,cell_local_face) = l;
-        }
-        ++l;
-      }
-    }
-    m_cell_faces = cell_faces;
-
-    Kokkos::View<unsigned short**> face_cell_local_face("face_cell_local_face",
-                                                        m_number_of_faces, m_max_nb_node_per_cell);
-    {
-      int l=0;
-      for (const auto& face_cells_vector : face_cells_map) {
-        const auto& cells_vector = face_cells_vector.second;
-        for (unsigned short lj=0; lj<cells_vector.size(); ++lj) {
-          unsigned short cell_local_face = cells_vector[lj].second;
-          face_cell_local_face(l,lj) = cell_local_face;
-        }
-        ++l;
-      }
-    }
-    m_face_cell_local_face = face_cell_local_face;
-  }
-
- public:
-  void addRefFaceList(const RefFaceList& ref_face_list)
-  {
-    m_ref_face_list.push_back(ref_face_list);
-  }
-
-  size_t numberOfRefFaceList() const
-  {
-    return m_ref_face_list.size();
-  }
-
-  const RefFaceList& refFaceList(const size_t& i) const
-  {
-    return m_ref_face_list[i];
-  }
-
-  void addRefNodeList(const RefNodeList& ref_node_list)
-  {
-    m_ref_node_list.push_back(ref_node_list);
-  }
-
-  size_t numberOfRefNodeList() const
-  {
-    return m_ref_node_list.size();
-  }
-
-  const RefNodeList& refNodeList(const size_t& i) const
-  {
-    return m_ref_node_list[i];
-  }
-
-  const size_t& numberOfNodes() const
-  {
-    return m_number_of_nodes;
-  }
-
-  const size_t& numberOfFaces() const
-  {
-    return m_number_of_faces;
-  }
-
-  const size_t& numberOfCells() const
-  {
-    return m_number_of_cells;
-  }
-
-  const size_t& maxNbNodePerCell() const
-  {
-    return m_max_nb_node_per_cell;
-  }
-
-  const Kokkos::View<const unsigned int**> cellNodes() const
-  {
-    return m_cell_nodes;
-  }
-
-  const Kokkos::View<const unsigned int**> cellFaces() const
-  {
-    return m_cell_faces;
-  }
-
-  const Kokkos::View<const unsigned short*> nodeNbCells() const
-  {
-    return m_node_nb_cells;
-  }
-
-  const Kokkos::View<const unsigned short*> cellNbNodes() const
-  {
-    return m_cell_nb_nodes;
-  }
-
-  const Kokkos::View<const double*> invCellNbNodes() const
-  {
-    return m_inv_cell_nb_nodes;
-  }
-
-  const Kokkos::View<const unsigned short*> cellNbFaces() const
-  {
-    return m_cell_nb_faces;
-  }
-
-  const Kokkos::View<const unsigned short*> faceNbCells() const
-  {
-    return m_face_nb_cells;
-  }
-
-  const Kokkos::View<const unsigned short*> faceNbNodes() const
-  {
-    return m_face_nb_nodes;
-  }
-
-  const Kokkos::View<const unsigned int**> nodeCells() const
-  {
-    return m_node_cells;
-  }
-
-  const Kokkos::View<const unsigned int**> faceCells() const
-  {
-    return m_face_cells;
-  }
-
-  const Kokkos::View<const unsigned int**> faceNodes() const
-  {
-    return m_face_nodes;
-  }
-
-  const Kokkos::View<const unsigned short**> nodeCellLocalNode() const
-  {
-    return m_node_cell_local_node;
-  }
-
-  const Kokkos::View<const unsigned short**> faceCellLocalFace() const
-  {
-    return m_face_cell_local_face;
-  }
-
-  unsigned int getFaceNumber(const unsigned int node0_id,
-                             const unsigned int node1_id) const
-  {
-#warning rewrite
-    const unsigned int n0_id = std::min(node0_id, node1_id);
-    const unsigned int n1_id = std::max(node0_id, node1_id);
-    // worst code ever
-    for (unsigned int l=0; l<m_number_of_faces; ++l) {
-      if ((m_face_nodes(l,0) == n0_id) and (m_face_nodes(l,1) == n1_id)) {
-        return l;
-      }
-    }
-    std::cerr << "Face not found!\n";
-    std::exit(0);
-    return 0;
-  }
-
-  Connectivity2D(const Connectivity2D&) = delete;
-
-  Connectivity2D(const Kokkos::View<const unsigned short*> cell_nb_nodes,
-                 const Kokkos::View<const unsigned int**> cell_nodes)
-      : m_number_of_cells (cell_nodes.extent(0)),
-        m_cell_nb_nodes(cell_nb_nodes),
-        m_cell_nodes (cell_nodes)
-  {
-    {
-      Kokkos::View<double*> inv_cell_nb_nodes("inv_cell_nb_nodes", m_number_of_cells);
-      Kokkos::parallel_for(m_number_of_cells, KOKKOS_LAMBDA(const int&j){
-          inv_cell_nb_nodes[j] = 1./m_cell_nb_nodes[j];
-        });
-      m_inv_cell_nb_nodes = inv_cell_nb_nodes;
-    }
-    Assert(m_number_of_cells>0);
-
-    ConnectivityUtils utils;
-    utils.computeNodeCellConnectivity(m_cell_nodes,
-                                      m_cell_nb_nodes,
-                                      m_number_of_cells,
-                                      m_max_nb_node_per_cell,
-                                      m_number_of_nodes,
-                                      m_node_nb_cells,
-                                      m_node_cells,
-                                      m_node_cell_local_node);
-
-    this->_computeFaceCellConnectivities();
-  }
-
-  ~Connectivity2D()
-  {
-    ;
-  }
-};
-
-#endif // CONNECTIVITY_2D_HPP
diff --git a/src/mesh/Connectivity3D.hpp b/src/mesh/Connectivity3D.hpp
deleted file mode 100644
index ce982d2765dd82736c3211238017529ce997d92f..0000000000000000000000000000000000000000
--- a/src/mesh/Connectivity3D.hpp
+++ /dev/null
@@ -1,566 +0,0 @@
-#ifndef CONNECTIVITY_3D_HPP
-#define CONNECTIVITY_3D_HPP
-
-#include <Kokkos_Core.hpp>
-#include <PastisAssert.hpp>
-#include <TinyVector.hpp>
-
-#include <ConnectivityUtils.hpp>
-#include <vector>
-#include <map>
-#include <unordered_map>
-#include <algorithm>
-
-#include <RefId.hpp>
-#include <RefNodeList.hpp>
-#include <RefFaceList.hpp>
-
-#include <tuple>
-
-class Connectivity3D
-{
-public:
-  static constexpr size_t dimension = 3;
-
-private:
-  std::vector<RefFaceList> m_ref_face_list;
-  std::vector<RefNodeList> m_ref_node_list;
-
-  const size_t  m_number_of_cells;
-  size_t m_number_of_faces;
-  size_t m_number_of_nodes;
-
-  const Kokkos::View<const unsigned short*> m_cell_nb_nodes;
-  const Kokkos::View<const unsigned int**> m_cell_nodes;
-  Kokkos::View<double*> m_inv_cell_nb_nodes;
-
-  Kokkos::View<const unsigned short*> m_cell_nb_faces;
-  Kokkos::View<const unsigned int**> m_cell_faces;
-  Kokkos::View<const bool**> m_cell_faces_is_reversed;
-
-  Kokkos::View<const unsigned short*> m_node_nb_cells;
-  Kokkos::View<const unsigned int**> m_node_cells;
-  Kokkos::View<const unsigned short**> m_node_cell_local_node;
-
-  Kokkos::View<const unsigned short*> m_face_nb_cells;
-  Kokkos::View<const unsigned int**> m_face_cells;
-  Kokkos::View<const unsigned short**> m_face_cell_local_face;
-
-  Kokkos::View<const unsigned short*> m_face_nb_nodes;
-  Kokkos::View<const unsigned int**> m_face_nodes;
-  //  Kokkos::View<unsigned short**> m_face_node_local_face;
-
-  Kokkos::View<const unsigned short*> m_node_nb_faces;
-  Kokkos::View<const unsigned int**> m_node_faces;
-  //  Kokkos::View<unsigned short**> m_node_face_local_node;
-
-  size_t m_max_nb_node_per_cell;
-  size_t m_max_nb_face_per_cell;
-  size_t m_max_nb_node_per_face;
-
-  class Face
-  {
-   public:
-    friend struct Hash;
-    struct Hash {
-      size_t operator()(const Face& f) const
-      {
-        size_t hash = 0;
-        for (size_t i=0; i<f.m_node_id_list.size(); ++i) {
-          hash ^= std::hash<unsigned int>()(f.m_node_id_list[i]) >> i;
-        }
-        return hash;
-      }
-    };
-   private:
-    bool m_reversed;
-    std::vector<unsigned int> m_node_id_list;
-
-   public:
-    friend std::ostream& operator<<(std::ostream& os, const Face& f)
-    {
-      for (auto id : f.m_node_id_list) {
-        std::cout << id << ' ';
-      }
-      return os;
-    }
-
-    KOKKOS_INLINE_FUNCTION
-    const bool& reversed() const
-    {
-      return m_reversed;
-    }
-
-    KOKKOS_INLINE_FUNCTION
-    const std::vector<unsigned int>& nodeIdList() const
-    {
-      return m_node_id_list;
-    }
-
-    KOKKOS_INLINE_FUNCTION
-    std::vector<unsigned int> _sort(const std::vector<unsigned int>& node_list)
-    {
-      const auto min_id = std::min_element(node_list.begin(), node_list.end());
-      const int shift = std::distance(node_list.begin(), min_id);
-
-      std::vector<unsigned int> rotated_node_list(node_list.size());
-      if (node_list[(shift+1)%node_list.size()] > node_list[(shift+node_list.size()-1)%node_list.size()]) {
-        for (size_t i=0; i<node_list.size(); ++i) {
-          rotated_node_list[i] = node_list[(shift+node_list.size()-i)%node_list.size()];
-          m_reversed = true;
-        }
-      } else {
-        for (size_t i=0; i<node_list.size(); ++i) {
-          rotated_node_list[i] = node_list[(shift+i)%node_list.size()];
-        }
-      }
-
-      return rotated_node_list;
-    }
-
-    bool operator==(const Face& f) const
-    {
-      if (m_node_id_list.size() == f.nodeIdList().size()) {
-        for (size_t j=0; j<m_node_id_list.size(); ++j) {
-          if (m_node_id_list[j] != f.nodeIdList()[j]) {
-            return false;
-          }
-        }
-        return true;
-      }
-      return false;
-    }
-
-    KOKKOS_INLINE_FUNCTION
-    bool operator<(const Face& f) const
-    {
-      const size_t min_nb_nodes = std::min(f.m_node_id_list.size(), m_node_id_list.size());
-      for (size_t i=0; i<min_nb_nodes; ++i) {
-        if (m_node_id_list[i] <  f.m_node_id_list[i]) return true;
-        if (m_node_id_list[i] != f.m_node_id_list[i]) return false;
-      }
-      return m_node_id_list.size() < f.m_node_id_list.size();
-    }
-
-    KOKKOS_INLINE_FUNCTION
-    Face& operator=(const Face&) = default;
-
-    KOKKOS_INLINE_FUNCTION
-    Face& operator=(Face&&) = default;
-
-    KOKKOS_INLINE_FUNCTION
-    Face(const Face&) = default;
-
-    KOKKOS_INLINE_FUNCTION
-    Face(Face&&) = default;
-
-    KOKKOS_INLINE_FUNCTION
-    Face(const std::vector<unsigned int>& given_node_id_list)
-        : m_reversed(false),
-          m_node_id_list(_sort(given_node_id_list))
-    {
-      ;
-    }
-
-    KOKKOS_INLINE_FUNCTION
-    Face() = delete;
-
-    KOKKOS_INLINE_FUNCTION
-    ~Face() = default;
-  };
-
-  std::unordered_map<Face,unsigned int, Face::Hash> m_face_number_map;
-
-  void _computeFaceCellConnectivities()
-  {
-    Kokkos::View<unsigned short*> cell_nb_faces("cell_nb_faces", m_number_of_cells);
-
-    typedef std::tuple<unsigned int, unsigned short, bool> CellFaceInfo;
-    std::map<Face, std::vector<CellFaceInfo>> face_cells_map;
-    for (unsigned int j=0; j<m_number_of_cells; ++j) {
-      const unsigned short cell_nb_nodes = m_cell_nb_nodes[j];
-      switch (cell_nb_nodes) {
-        case 4: { // tetrahedron
-          cell_nb_faces[j] = 4;
-          // face 0
-          Face f0({m_cell_nodes(j,1),
-                   m_cell_nodes(j,2),
-                   m_cell_nodes(j,3)});
-          face_cells_map[f0].push_back(std::make_tuple(j, 0, f0.reversed()));
-
-          // face 1
-          Face f1({m_cell_nodes(j,0),
-                   m_cell_nodes(j,3),
-                   m_cell_nodes(j,2)});
-          face_cells_map[f1].push_back(std::make_tuple(j, 1, f1.reversed()));
-
-          // face 2
-          Face f2({m_cell_nodes(j,0),
-                   m_cell_nodes(j,1),
-                   m_cell_nodes(j,3)});
-          face_cells_map[f2].push_back(std::make_tuple(j, 2, f2.reversed()));
-
-          // face 3
-          Face f3({m_cell_nodes(j,0),
-                   m_cell_nodes(j,2),
-                   m_cell_nodes(j,1)});
-          face_cells_map[f3].push_back(std::make_tuple(j, 3, f3.reversed()));
-          break;
-        }
-        case 8: { // hexahedron
-          // face 0
-          Face f0({m_cell_nodes(j,3),
-                   m_cell_nodes(j,2),
-                   m_cell_nodes(j,1),
-                   m_cell_nodes(j,0)});
-          face_cells_map[f0].push_back(std::make_tuple(j, 0, f0.reversed()));
-
-          // face 1
-          Face f1({m_cell_nodes(j,4),
-                   m_cell_nodes(j,5),
-                   m_cell_nodes(j,6),
-                   m_cell_nodes(j,7)});
-          face_cells_map[f1].push_back(std::make_tuple(j, 1, f1.reversed()));
-
-          // face 2
-          Face f2({m_cell_nodes(j,0),
-                   m_cell_nodes(j,4),
-                   m_cell_nodes(j,7),
-                   m_cell_nodes(j,3)});
-          face_cells_map[f2].push_back(std::make_tuple(j, 2, f2.reversed()));
-
-          // face 3
-          Face f3({m_cell_nodes(j,1),
-                   m_cell_nodes(j,2),
-                   m_cell_nodes(j,6),
-                   m_cell_nodes(j,5)});
-          face_cells_map[f3].push_back(std::make_tuple(j, 3, f3.reversed()));
-
-          // face 4
-          Face f4({m_cell_nodes(j,0),
-                   m_cell_nodes(j,1),
-                   m_cell_nodes(j,5),
-                   m_cell_nodes(j,4)});
-          face_cells_map[f4].push_back(std::make_tuple(j, 4, f4.reversed()));
-
-          // face 5
-          Face f5({m_cell_nodes(j,3),
-                   m_cell_nodes(j,7),
-                   m_cell_nodes(j,6),
-                   m_cell_nodes(j,2)});
-          face_cells_map[f5].push_back(std::make_tuple(j, 5, f5.reversed()));
-
-          cell_nb_faces[j] = 6;
-          break;
-        }
-        default: {
-          std::cerr << "unexpected cell type!\n";
-          std::exit(0);
-        }
-      }
-    }
-    std::cerr << __FILE__ << ':' << __LINE__ << ':'
-              << rang::fg::red << " only works for hexa" << rang::fg::reset << '\n';
-
-    m_cell_nb_faces = cell_nb_faces;
-
-    m_number_of_faces = face_cells_map.size();
-
-    std::cerr << __FILE__ << ':' << __LINE__ << ':'
-              << rang::fg::red << " m_max_nb_node_per_face forced to 4" << rang::fg::reset << '\n';
-    Kokkos::View<unsigned short*> face_nb_nodes("face_nb_nodes", m_number_of_faces);
-    m_max_nb_node_per_face = 4;
-    Kokkos::View<unsigned int**> face_nodes("face_nodes", m_number_of_faces, m_max_nb_node_per_face);
-    {
-      int l=0;
-      for (const auto& face_cells_vector : face_cells_map) {
-        const Face& face = face_cells_vector.first;
-        for(size_t r=0; r<face.nodeIdList().size(); ++r) {
-          face_nodes(l,r) = face.nodeIdList()[r];
-        }
-        face_nb_nodes[l] = face.nodeIdList().size();
-        m_face_number_map[face] = l;
-        ++l;
-      }
-    }
-    m_face_nodes = face_nodes;
-    m_face_nb_nodes = face_nb_nodes;
-
-    Kokkos::View<unsigned short*> face_nb_cells("face_nb_cells", m_number_of_faces);
-    {
-      int l=0;
-      for (const auto& face_cells_vector : face_cells_map) {
-        const auto& cells_vector = face_cells_vector.second;
-        face_nb_cells[l] = cells_vector.size();
-        ++l;
-      }
-    }
-    m_face_nb_cells = face_nb_cells;
-
-#warning check that the number of cell per faces is <=2
-
-    Kokkos::View<unsigned int**> face_cells("face_cells", face_cells_map.size(), 2);
-    {
-      int l=0;
-      for (const auto& face_cells_vector : face_cells_map) {
-        const auto& cells_vector = face_cells_vector.second;
-        for (unsigned short lj=0; lj<cells_vector.size(); ++lj) {
-          const unsigned int cell_number = std::get<0>(cells_vector[lj]);
-          face_cells(l,lj) = cell_number;
-        }
-        ++l;
-      }
-    }
-    m_face_cells = face_cells;
-
-    std::cerr << __FILE__ << ':' << __LINE__ << ':'
-              << rang::fg::red << " m_max_nb_face_per_cell forced to 6" << rang::fg::reset << '\n';
-    m_max_nb_face_per_cell = 6;
-    Kokkos::View<unsigned int**> cell_faces("cell_faces", m_number_of_cells, m_max_nb_face_per_cell);
-    {
-      int l=0;
-      for (const auto& face_cells_vector : face_cells_map) {
-        const auto& cells_vector = face_cells_vector.second;
-        for (unsigned short lj=0; lj<cells_vector.size(); ++lj) {
-          const auto& [cell_number, cell_local_face, reversed] = cells_vector[lj];
-          cell_faces(cell_number,cell_local_face) = l;
-        }
-        ++l;
-      }
-    }
-    m_cell_faces = cell_faces;
-
-    Kokkos::View<bool**> cell_faces_is_reversed("cell_faces_is_reversed", m_number_of_cells, m_max_nb_face_per_cell);
-    {
-      int l=0;
-      for (const auto& face_cells_vector : face_cells_map) {
-        const auto& cells_vector = face_cells_vector.second;
-        for (unsigned short lj=0; lj<cells_vector.size(); ++lj) {
-          const auto& [cell_number, cell_local_face, reversed] = cells_vector[lj];
-          cell_faces_is_reversed(cell_number,cell_local_face) = reversed;
-        }
-        ++l;
-      }
-    }
-    m_cell_faces_is_reversed = cell_faces_is_reversed;
-
-    Kokkos::View<unsigned short**> face_cell_local_face("face_cell_local_face",
-                                                        m_number_of_faces, m_max_nb_node_per_cell);
-    {
-      int l=0;
-      for (const auto& face_cells_vector : face_cells_map) {
-        const auto& cells_vector = face_cells_vector.second;
-        for (unsigned short lj=0; lj<cells_vector.size(); ++lj) {
-          unsigned short cell_local_face = std::get<1>(cells_vector[lj]);
-          face_cell_local_face(l,lj) = cell_local_face;
-        }
-        ++l;
-      }
-    }
-    m_face_cell_local_face = face_cell_local_face;
-
-    std::unordered_map<unsigned int, std::vector<unsigned int>> node_faces_map;
-    for (size_t l=0; l<m_number_of_faces; ++l) {
-      for (size_t lr=0; lr<face_nb_nodes(l); ++lr) {
-        const unsigned int r = face_nodes(l, lr);
-        node_faces_map[r].push_back(l);
-      }
-    }
-    Kokkos::View<unsigned short*> node_nb_faces("node_nb_faces", m_number_of_nodes);
-    size_t max_nb_face_per_node = 0;
-    for (auto node_faces : node_faces_map) {
-      max_nb_face_per_node = std::max(node_faces.second.size(), max_nb_face_per_node);
-      node_nb_faces[node_faces.first] = node_faces.second.size();
-    }
-    m_node_nb_faces = node_nb_faces;
-
-    Kokkos::View<unsigned int**> node_faces("node_faces", m_number_of_nodes, max_nb_face_per_node);
-    for (auto node_faces_vector : node_faces_map) {
-      const unsigned int r = node_faces_vector.first;
-      const std::vector<unsigned int>&  faces_vector = node_faces_vector.second;
-      for (size_t l=0; l < faces_vector.size(); ++l) {
-        node_faces(r, l) = faces_vector[l];
-      }
-    }
-    m_node_faces = node_faces;
-  }
-
- public:
-  void addRefFaceList(const RefFaceList& ref_face_list)
-  {
-    m_ref_face_list.push_back(ref_face_list);
-  }
-
-  size_t numberOfRefFaceList() const
-  {
-    return m_ref_face_list.size();
-  }
-
-  const RefFaceList& refFaceList(const size_t& i) const
-  {
-    return m_ref_face_list[i];
-  }
-
-  void addRefNodeList(const RefNodeList& ref_node_list)
-  {
-    m_ref_node_list.push_back(ref_node_list);
-  }
-
-  size_t numberOfRefNodeList() const
-  {
-    return m_ref_node_list.size();
-  }
-
-  const RefNodeList& refNodeList(const size_t& i) const
-  {
-    return m_ref_node_list[i];
-  }
-
-  const size_t& numberOfNodes() const
-  {
-    return m_number_of_nodes;
-  }
-
-  const size_t& numberOfFaces() const
-  {
-    return m_number_of_faces;
-  }
-
-  const size_t& numberOfCells() const
-  {
-    return m_number_of_cells;
-  }
-
-  const size_t& maxNbFacePerCell() const
-  {
-    return m_max_nb_face_per_cell;
-  }
-
-  const size_t& maxNbNodePerCell() const
-  {
-    return m_max_nb_node_per_cell;
-  }
-
-  const size_t& maxNbNodePerFace() const
-  {
-    return m_max_nb_node_per_face;
-  }
-
-  const Kokkos::View<const unsigned int**> cellNodes() const
-  {
-    return m_cell_nodes;
-  }
-
-  const Kokkos::View<const unsigned int**> cellFaces() const
-  {
-    return m_cell_faces;
-  }
-
-  const Kokkos::View<const bool**> cellFacesIsReversed() const
-  {
-    return m_cell_faces_is_reversed;
-  }
-
-  const Kokkos::View<const unsigned short*> nodeNbCells() const
-  {
-    return m_node_nb_cells;
-  }
-
-  const Kokkos::View<const unsigned short*> cellNbNodes() const
-  {
-    return m_cell_nb_nodes;
-  }
-
-  const Kokkos::View<const double*> invCellNbNodes() const
-  {
-    return m_inv_cell_nb_nodes;
-  }
-
-  const Kokkos::View<const unsigned short*> cellNbFaces() const
-  {
-    return m_cell_nb_faces;
-  }
-
-  const Kokkos::View<const unsigned short*> faceNbCells() const
-  {
-    return m_face_nb_cells;
-  }
-
-  const Kokkos::View<const unsigned short*> faceNbNodes() const
-  {
-    return m_face_nb_nodes;
-  }
-
-  const Kokkos::View<const unsigned int**> nodeCells() const
-  {
-    return m_node_cells;
-  }
-
-  const Kokkos::View<const unsigned int**> faceCells() const
-  {
-    return m_face_cells;
-  }
-
-  const Kokkos::View<const unsigned int**> faceNodes() const
-  {
-    return m_face_nodes;
-  }
-
-  const Kokkos::View<const unsigned short**> nodeCellLocalNode() const
-  {
-    return m_node_cell_local_node;
-  }
-
-  const Kokkos::View<const unsigned short**> faceCellLocalFace() const
-  {
-    return m_face_cell_local_face;
-  }
-
-  unsigned int getFaceNumber(const std::vector<unsigned int>& face_nodes) const
-  {
-    const Face face(face_nodes);
-    auto i_face = m_face_number_map.find(face);
-    if (i_face == m_face_number_map.end()) {
-      std::cerr << "Face " << face << " not found!\n";
-      std::exit(0);
-    }
-    return i_face->second;
-  }
-
-  Connectivity3D(const Connectivity3D&) = delete;
-
-  Connectivity3D(const Kokkos::View<const unsigned short*> cell_nb_nodes,
-                 const Kokkos::View<const unsigned int**> cell_nodes)
-    : m_number_of_cells (cell_nodes.extent(0)),
-      m_cell_nb_nodes(cell_nb_nodes),
-      m_cell_nodes (cell_nodes)
-  {
-    {
-      Kokkos::View<double*> inv_cell_nb_nodes("inv_cell_nb_nodes", m_number_of_cells);
-      Kokkos::parallel_for(m_number_of_cells, KOKKOS_LAMBDA(const int&j){
-          inv_cell_nb_nodes[j] = 1./m_cell_nb_nodes[j];
-        });
-      m_inv_cell_nb_nodes = inv_cell_nb_nodes;
-    }
-    Assert(m_number_of_cells>0);
-
-    ConnectivityUtils utils;
-    utils.computeNodeCellConnectivity(m_cell_nodes,
-                                      m_cell_nb_nodes,
-                                      m_number_of_cells,
-                                      m_max_nb_node_per_cell,
-                                      m_number_of_nodes,
-                                      m_node_nb_cells,
-                                      m_node_cells,
-                                      m_node_cell_local_node);
-
-    this->_computeFaceCellConnectivities();
-  }
-
-  ~Connectivity3D()
-  {
-    ;
-  }
-};
-
-#endif // CONNECTIVITY_3D_HPP
diff --git a/src/mesh/ConnectivityComputer.cpp b/src/mesh/ConnectivityComputer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0c87ccb6d64f840cd450eb5ded67c6a337a5cf25
--- /dev/null
+++ b/src/mesh/ConnectivityComputer.cpp
@@ -0,0 +1,158 @@
+#include <Connectivity.hpp>
+
+#include <ConnectivityComputer.hpp>
+#include <map>
+
+
+template <typename ConnectivityType>
+KOKKOS_INLINE_FUNCTION
+ConnectivityMatrix ConnectivityComputer::
+computeConnectivityMatrix(const ConnectivityType& connectivity,
+                          const ItemType& item_type,
+                          const ItemType& child_item_type) const
+{
+  ConnectivityMatrix item_to_child_item_matrix;
+  if (connectivity.isConnectivityMatrixBuilt(child_item_type, item_type)) {
+    const ConnectivityMatrix& child_to_item_matrix
+        = connectivity.getMatrix(child_item_type, item_type);
+
+    std::cout << "computing connectivity "
+              << itemName(item_type) << " -> " << itemName(child_item_type) << '\n';
+
+    item_to_child_item_matrix
+        = this->_computeInverse(child_to_item_matrix);
+  } else {
+    std::cerr << "unable to compute connectivity "
+              << itemName(item_type) << " -> " << itemName(child_item_type) << '\n';
+    std::exit(0);
+  }
+
+  return item_to_child_item_matrix;
+}
+
+template
+ConnectivityMatrix ConnectivityComputer::
+computeConnectivityMatrix(const Connectivity1D&, const ItemType&, const ItemType&) const;
+
+template
+ConnectivityMatrix ConnectivityComputer::
+computeConnectivityMatrix(const Connectivity2D&, const ItemType&, const ItemType&) const;
+
+
+template
+ConnectivityMatrix ConnectivityComputer::
+computeConnectivityMatrix(const Connectivity3D&, const ItemType&, const ItemType&) const;
+
+ConnectivityMatrix
+ConnectivityComputer::
+_computeInverse(const ConnectivityMatrix& item_to_child_matrix) const
+{
+  std::map<unsigned int, std::vector<unsigned int>> child_to_item_vector_map;
+  const size_t& number_of_items = item_to_child_matrix.numRows();
+
+  for (unsigned int j=0; j<number_of_items; ++j) {
+    const auto& item_to_child = item_to_child_matrix.rowConst(j);
+    for (unsigned int r=0; r<item_to_child.length; ++r) {
+      child_to_item_vector_map[item_to_child(r)].push_back(j);
+    }
+  }
+
+  {
+    size_t i=0;
+    for (const auto& [child_item_id, item_vector] : child_to_item_vector_map) {
+      if (child_item_id != i) {
+        std::cerr << "sparse item numerotation NIY\n";
+        std::exit(0);
+      }
+      ++i;
+    }
+  }
+
+  std::vector<std::vector<unsigned int>> child_to_items_vector(child_to_item_vector_map.size());
+  for (const auto& [child_item_id, item_vector] : child_to_item_vector_map) {
+    child_to_items_vector[child_item_id] = item_vector;
+  }
+
+  return ConnectivityMatrix(child_to_items_vector);
+}
+
+template <ItemType item_type,
+          ItemType child_item_type,
+          typename ConnectivityType>
+SubItemValuePerItem<const unsigned short, child_item_type, item_type>
+ConnectivityComputer::computeLocalItemNumberInChildItem(const ConnectivityType& connectivity) const
+{
+  const ConnectivityMatrix& child_item_to_items_matrix
+      = connectivity.getMatrix(child_item_type, item_type);
+  const ConnectivityMatrix& item_to_child_items_matrix
+      = connectivity.getMatrix(item_type, child_item_type);
+
+  SubItemValuePerItem<unsigned short, child_item_type, item_type> item_number_in_child_item(connectivity);
+  for (unsigned int r=0; r<item_to_child_items_matrix.numRows(); ++r) {
+    const auto& item_to_child_items = item_to_child_items_matrix.rowConst(r);
+    for (unsigned short J=0; J<item_to_child_items.length; ++J) {
+      const unsigned int j = item_to_child_items(J);
+      const auto& child_item_to_items = child_item_to_items_matrix.rowConst(j);
+
+      for (unsigned int R=0; R<child_item_to_items.length; ++R) {
+        if (child_item_to_items(R) == r) {
+          item_number_in_child_item(r,J) = R;
+          break;
+        }
+      }
+    }
+  }
+
+  return item_number_in_child_item;
+}
+
+// 1D
+
+template SubItemValuePerItem<const unsigned short, ItemType::cell, ItemType::node>
+ConnectivityComputer::
+computeLocalItemNumberInChildItem<ItemType::node,
+                                  ItemType::cell>(const Connectivity1D&) const;
+
+template SubItemValuePerItem<const unsigned short, ItemType::cell, ItemType::face>
+ConnectivityComputer::
+computeLocalItemNumberInChildItem<ItemType::face,
+                                  ItemType::cell>(const Connectivity1D&) const;
+
+template SubItemValuePerItem<const unsigned short, ItemType::node, ItemType::cell>
+ConnectivityComputer::
+computeLocalItemNumberInChildItem<ItemType::cell,
+                                  ItemType::node>(const Connectivity1D&) const;
+
+// 2D
+
+template SubItemValuePerItem<const unsigned short, ItemType::cell, ItemType::node>
+ConnectivityComputer::
+computeLocalItemNumberInChildItem<ItemType::node,
+                                  ItemType::cell>(const Connectivity2D&) const;
+
+template SubItemValuePerItem<const unsigned short, ItemType::cell, ItemType::face>
+ConnectivityComputer::
+computeLocalItemNumberInChildItem<ItemType::face,
+                                  ItemType::cell>(const Connectivity2D&) const;
+
+template SubItemValuePerItem<const unsigned short, ItemType::node, ItemType::cell>
+ConnectivityComputer::
+computeLocalItemNumberInChildItem<ItemType::cell,
+                                  ItemType::node>(const Connectivity2D&) const;
+
+// 3D
+
+template SubItemValuePerItem<const unsigned short, ItemType::cell, ItemType::node>
+ConnectivityComputer::
+computeLocalItemNumberInChildItem<ItemType::node,
+                                  ItemType::cell>(const Connectivity3D&) const;
+
+template SubItemValuePerItem<const unsigned short, ItemType::cell, ItemType::face>
+ConnectivityComputer::
+computeLocalItemNumberInChildItem<ItemType::face,
+                                  ItemType::cell>(const Connectivity3D&) const;
+
+template SubItemValuePerItem<const unsigned short, ItemType::node, ItemType::cell>
+ConnectivityComputer::
+computeLocalItemNumberInChildItem<ItemType::cell,
+                                  ItemType::node>(const Connectivity3D&) const;
diff --git a/src/mesh/ConnectivityComputer.hpp b/src/mesh/ConnectivityComputer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8b4fdfc3ab20bf2d6290d234a13b833dc6451cb6
--- /dev/null
+++ b/src/mesh/ConnectivityComputer.hpp
@@ -0,0 +1,32 @@
+#ifndef CONNECTIVITY_COMPUTER_HPP
+#define CONNECTIVITY_COMPUTER_HPP
+
+#include <ConnectivityMatrix.hpp>
+#include <SubItemValuePerItem.hpp>
+
+class ConnectivityComputer
+{
+ private:
+  ConnectivityMatrix
+  _computeInverse(const ConnectivityMatrix& item_to_child_matrix) const;
+
+ public:
+  template <typename ConnectivityType>
+  ConnectivityMatrix
+  computeConnectivityMatrix(const ConnectivityType& connectivity,
+                            const ItemType& item_type,
+                            const ItemType& child_item_type) const;
+
+
+  template <ItemType item_type,
+            ItemType child_item_type,
+            typename ConnectivityType>
+  SubItemValuePerItem<const unsigned short, child_item_type, item_type>
+  computeLocalItemNumberInChildItem(const ConnectivityType& connectivity) const;
+
+  ConnectivityComputer(const ConnectivityComputer&) = default;
+  ConnectivityComputer() = default;
+  ~ConnectivityComputer() = default;
+};
+
+#endif // CONNECTIVITY_COMPUTER_HPP
diff --git a/src/mesh/ConnectivityMatrix.hpp b/src/mesh/ConnectivityMatrix.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..356ee6da56d474836180cc867318c2d4f912549a
--- /dev/null
+++ b/src/mesh/ConnectivityMatrix.hpp
@@ -0,0 +1,74 @@
+#ifndef CONNECTIVITY_MATRIX_HPP
+#define CONNECTIVITY_MATRIX_HPP
+
+#include <Kokkos_Core.hpp>
+#include <Kokkos_StaticCrsGraph.hpp>
+
+class ConnectivityMatrix
+{
+ private:
+  typedef Kokkos::StaticCrsGraph<unsigned int, Kokkos::HostSpace> HostMatrix;
+  HostMatrix m_host_matrix;
+
+  bool m_is_built{false};
+
+ public:
+  typedef HostMatrix::row_map_type HostRowType;
+
+  const bool& isBuilt() const
+  {
+    return m_is_built;
+  }
+
+  const auto& entries() const
+  {
+    return m_host_matrix.entries;
+  }
+
+  const auto& rowsMap() const
+  {
+    return m_host_matrix.row_map;
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto numEntries() const
+  {
+    return m_host_matrix.entries.extent(0);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto numRows() const
+  {
+    return m_host_matrix.numRows();
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto rowConst(const size_t& j) const
+  {
+    return m_host_matrix.rowConst(j);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  const auto rowMap(const size_t& j) const
+  {
+    return m_host_matrix.row_map[j];
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  ConnectivityMatrix(const std::vector<std::vector<unsigned int>>& initializer)
+      : m_host_matrix{Kokkos::create_staticcrsgraph<HostMatrix>("connectivity_matrix", initializer)},
+        m_is_built{true}
+  {
+    ;
+  }
+
+  ConnectivityMatrix& operator=(const ConnectivityMatrix&) = default;
+  ConnectivityMatrix& operator=(ConnectivityMatrix&&) = default;
+
+  ConnectivityMatrix() = default;
+  ConnectivityMatrix(const ConnectivityMatrix&) = default;
+  ConnectivityMatrix(ConnectivityMatrix&&) = default;
+  ~ConnectivityMatrix() = default;
+};
+
+#endif // CONNECTIVITY_MATRIX_HPP
diff --git a/src/mesh/ConnectivityUtils.hpp b/src/mesh/ConnectivityUtils.hpp
deleted file mode 100644
index 3fe9e6f6a11032e752ce5bdcd9d76e7a9c46e1b0..0000000000000000000000000000000000000000
--- a/src/mesh/ConnectivityUtils.hpp
+++ /dev/null
@@ -1,84 +0,0 @@
-#ifndef CONNECTIVITY_UTILS_HPP
-#define CONNECTIVITY_UTILS_HPP
-
-#include <map>
-#include <Kokkos_Core.hpp>
-
-class ConnectivityUtils
-{
- public:
-  void computeNodeCellConnectivity(const Kokkos::View<const unsigned int**> cell_nodes,
-                                   const Kokkos::View<const unsigned short*> cell_nb_nodes,
-                                   const size_t& number_of_cells,
-                                   size_t& max_nb_node_per_cell,
-                                   size_t& number_of_nodes,
-                                   Kokkos::View<const unsigned short*>& node_nb_cells,
-                                   Kokkos::View<const unsigned int**>& node_cells,
-                                   Kokkos::View<const unsigned short**>& node_cell_local_node)
-  {
-    std::map<unsigned int, std::vector<unsigned int>> node_cells_map;
-    using namespace Kokkos::Experimental;
-    Kokkos::parallel_reduce(number_of_cells, KOKKOS_LAMBDA(const int& j, size_t& nb_max) {
-        const size_t n = cell_nb_nodes[j];
-        if (n > nb_max) nb_max = n;
-      }, Kokkos::Max<size_t>(max_nb_node_per_cell));
-
-    for (unsigned int j=0; j<number_of_cells; ++j) {
-      for (unsigned int r=0; r<cell_nb_nodes[j]; ++r) {
-        node_cells_map[cell_nodes(j,r)].push_back(j);
-      }
-    }
-
-    {
-      size_t i=0;
-      for (const auto& i_cell_vector : node_cells_map) {
-        const auto& [node_id, cell_vector] = i_cell_vector;
-        if (node_id != i) {
-          std::cerr << "sparse node numerotation NIY\n";
-          std::exit(0);
-        }
-        ++i;
-      }
-    }
-
-    number_of_nodes = node_cells_map.size();
-
-    Kokkos::View<unsigned short*> built_node_nb_cells("node_nb_cells", node_cells_map.size());
-    size_t max_node_cells = 0;
-    for (const auto& i_cell_vector : node_cells_map) {
-      const auto& [i, cells_vector] = i_cell_vector;
-      const size_t nb_cells = cells_vector.size();
-      built_node_nb_cells[i] = nb_cells;
-      if (nb_cells > max_node_cells) {
-        max_node_cells = nb_cells;
-      }
-    }
-    node_nb_cells = built_node_nb_cells;
-
-    Kokkos::View<unsigned int**> built_node_cells("node_cells", node_cells_map.size(), max_node_cells);
-    for (const auto& i_cell_vector : node_cells_map) {
-      const auto& [i, cells_vector] = i_cell_vector;
-      for (size_t j=0; j<cells_vector.size(); ++j) {
-        built_node_cells(i,j) = cells_vector[j];
-      }
-    }
-    node_cells = built_node_cells;
-
-    Kokkos::View<unsigned short**> built_node_cell_local_node("node_cell_local_node",
-                                                              node_cells_map.size(), max_node_cells);
-    Kokkos::parallel_for(number_of_nodes, KOKKOS_LAMBDA(const unsigned int& r){
-        for (unsigned short J=0; J<node_nb_cells[r]; ++J) {
-          const unsigned int j = node_cells(r,J);
-          for (unsigned int R=0; R<cell_nb_nodes[j]; ++R) {
-            if (cell_nodes(j,R) == r) {
-              built_node_cell_local_node(r,J)=R;
-              break;
-            }
-          }
-        }
-      });
-    node_cell_local_node = built_node_cell_local_node;
-  }
-};
-
-#endif // CONNECTIVITY_UTILS_HPP
diff --git a/src/mesh/GmshReader.cpp b/src/mesh/GmshReader.cpp
index c8c2e09e7a28bf484cac0840b41d964f87fc994a..f16ab2fa02e06aad3715eeb7c84028476c7565ae 100644
--- a/src/mesh/GmshReader.cpp
+++ b/src/mesh/GmshReader.cpp
@@ -5,9 +5,8 @@
 #include <set>
 #include <rang.hpp>
 
-#include <Connectivity1D.hpp>
-#include <Connectivity2D.hpp>
-#include <Connectivity3D.hpp>
+#include <CellType.hpp>
+#include <Connectivity.hpp>
 
 #include <Mesh.hpp>
 
@@ -52,7 +51,7 @@ class ErrorHandler
    * Prints the error message
    *
    */
-  virtual void writeErrorMessage();
+  virtual void writeErrorMessage() const;
 
   /**
    * The copy constructor
@@ -90,7 +89,7 @@ class ErrorHandler
     ;
   }
 };
-void ErrorHandler::writeErrorMessage()
+void ErrorHandler::writeErrorMessage() const
 {
   switch(__type) {
     case asked: {
@@ -293,7 +292,7 @@ GmshReader::GmshReader(const std::string& filename)
     this->__proceedData();
     // this->__createMesh();
   }
-  catch(ErrorHandler e) {
+  catch(const ErrorHandler& e) {
     e.writeErrorMessage();
     std::exit(0);
   }
@@ -789,39 +788,36 @@ GmshReader::__proceedData()
   dimension3_mask[12]=1;
   dimension3_mask[13]=1;
 
-  std::cout << "- dimension 0 entities : " << (dimension0_mask, elementNumber) << '\n';
-  std::cout << "- dimension 1 entities : " << (dimension1_mask, elementNumber) << '\n';
-  std::cout << "- dimension 2 entities : " << (dimension2_mask, elementNumber) << '\n';
-  std::cout << "- dimension 3 entities : " << (dimension3_mask, elementNumber) << '\n';
+  std::cout << "- dimension 0 entities: " << (dimension0_mask, elementNumber) << '\n';
+  std::cout << "- dimension 1 entities: " << (dimension1_mask, elementNumber) << '\n';
+  std::cout << "- dimension 2 entities: " << (dimension2_mask, elementNumber) << '\n';
+  std::cout << "- dimension 3 entities: " << (dimension3_mask, elementNumber) << '\n';
   if ((dimension3_mask, elementNumber)>0) {
     const size_t nb_cells = (dimension3_mask, elementNumber);
-    size_t max_nb_node_per_cell=4;
-    if (elementNumber[4] > 0) {
-      max_nb_node_per_cell = 8;
-    }
-    const Kokkos::View<unsigned int**> cell_nodes("cell_nodes", nb_cells, max_nb_node_per_cell);
+
+    std::vector<CellType> cell_type_vector(nb_cells);
+
+    std::vector<std::vector<unsigned int>> cell_by_node_vector(nb_cells);
     const size_t nb_tetrahedra = __tetrahedra.extent(0);
     for (size_t j=0; j<nb_tetrahedra; ++j) {
+      cell_by_node_vector[j].resize(4);
       for (int r=0; r<4; ++r) {
-        cell_nodes(j,r) = __tetrahedra[j][r];
+        cell_by_node_vector[j][r] = __tetrahedra[j][r];
       }
+      cell_type_vector[j] = CellType::Tetrahedron;
     }
     const size_t nb_hexahedra = __hexahedra.extent(0);
     for (size_t j=0; j<nb_hexahedra; ++j) {
-      const size_t jh = j+nb_tetrahedra;
+      const size_t jh = nb_tetrahedra+j;
+      cell_by_node_vector[jh].resize(8);
       for (int r=0; r<8; ++r) {
-        cell_nodes(jh,r) = __hexahedra[j][r];
+        cell_by_node_vector[jh][r] = __hexahedra[j][r];
       }
-    }
-    const Kokkos::View<unsigned short*> cell_nb_nodes("cell_nb_nodes", nb_cells);
-    for (size_t j=0; j<nb_tetrahedra; ++j) {
-      cell_nb_nodes[j] = 4;
-    }
-    for (size_t j=nb_tetrahedra; j<nb_tetrahedra+nb_hexahedra; ++j) {
-      cell_nb_nodes[j] = 8;
+      cell_type_vector[jh] = CellType::Hexahedron;
     }
 
-    std::shared_ptr<Connectivity3D> p_connectivity(new Connectivity3D(cell_nb_nodes, cell_nodes));
+    std::shared_ptr<Connectivity3D> p_connectivity(new Connectivity3D(cell_by_node_vector,
+                                                                      cell_type_vector));
     Connectivity3D& connectivity = *p_connectivity;
 
     std::map<unsigned int, std::vector<unsigned int>> ref_faces_map;
@@ -847,16 +843,6 @@ GmshReader::__proceedData()
       connectivity.addRefFaceList(RefFaceList(physical_ref_id.refId(), face_list));
     }
 
-    // for (size_t j=0; j<nb_cells; ++j) {
-    //   std::cout << std::setw(5) << j << ": ";
-    //   for (size_t r=0; r<cell_nb_nodes[j]; ++r) {
-    //     std::cout << ' ' << cell_nodes(j,r);
-    //   }
-    //   std::cout << '\n';
-    // }
-
-    // std::cerr << "*** using a 3d mesh (NIY)\n";
-    // std::exit(0);
     typedef Mesh<Connectivity3D> MeshType;
     typedef TinyVector<3, double> Rd;
 
@@ -870,39 +856,36 @@ GmshReader::__proceedData()
 
   } else if ((dimension2_mask, elementNumber)>0) {
     const size_t nb_cells = (dimension2_mask, elementNumber);
-    size_t max_nb_node_per_cell=3;
-    if (elementNumber[2] > 0) {
-      max_nb_node_per_cell = 4;
-    }
-    const Kokkos::View<unsigned int**> cell_nodes("cell_nodes", nb_cells, max_nb_node_per_cell);
+
+    std::vector<CellType> cell_type_vector(nb_cells);
+
+    std::vector<std::vector<unsigned int>> cell_by_node_vector(nb_cells);
     const size_t nb_triangles = __triangles.extent(0);
     for (size_t j=0; j<nb_triangles; ++j) {
-      cell_nodes(j,0) = __triangles[j][0];
-      cell_nodes(j,1) = __triangles[j][1];
-      cell_nodes(j,2) = __triangles[j][2];
+      cell_by_node_vector[j].resize(3);
+      for (int r=0; r<3; ++r) {
+        cell_by_node_vector[j][r] = __triangles[j][r];
+      }
+      cell_type_vector[j] = CellType::Triangle;
     }
 
     const size_t nb_quadrangles = __quadrangles.extent(0);
     for (size_t j=0; j<nb_quadrangles; ++j) {
       const size_t jq = j+nb_triangles;
-      cell_nodes(jq,0) = __quadrangles[j][0];
-      cell_nodes(jq,1) = __quadrangles[j][1];
-      cell_nodes(jq,2) = __quadrangles[j][2];
-      cell_nodes(jq,3) = __quadrangles[j][3];
-    }
-    const Kokkos::View<unsigned short*> cell_nb_nodes("cell_nb_nodes", nb_cells);
-    for (size_t j=0; j<nb_triangles; ++j) {
-      cell_nb_nodes[j] = 3;
-    }
-    for (size_t j=nb_triangles; j<nb_triangles+nb_quadrangles; ++j) {
-      cell_nb_nodes[j] = 4;
+      cell_by_node_vector[jq].resize(4);
+      for (int r=0; r<4; ++r) {
+        cell_by_node_vector[jq][r] = __quadrangles[j][r];
+      }
+      cell_type_vector[jq] = CellType::Quadrangle;
     }
-    std::shared_ptr<Connectivity2D> p_connectivity(new Connectivity2D(cell_nb_nodes, cell_nodes));
+
+    std::shared_ptr<Connectivity2D> p_connectivity(new Connectivity2D(cell_by_node_vector,
+                                                                      cell_type_vector));
     Connectivity2D& connectivity = *p_connectivity;
 
     std::map<unsigned int, std::vector<unsigned int>> ref_faces_map;
     for (unsigned int e=0; e<__edges.extent(0); ++e) {
-      const unsigned int edge_number = connectivity.getFaceNumber(__edges[e][0], __edges[e][1]);
+      const unsigned int edge_number = connectivity.getFaceNumber({__edges[e][0], __edges[e][1]});
       const unsigned int& ref = __edges_ref[e];
       ref_faces_map[ref].push_back(edge_number);
     }
@@ -943,22 +926,21 @@ GmshReader::__proceedData()
     m_mesh = std::shared_ptr<IMesh>(new MeshType(p_connectivity, xr));
 
   } else if ((dimension1_mask, elementNumber)>0) {
-
     const size_t nb_cells = (dimension1_mask, elementNumber);
-    const size_t max_nb_node_per_cell=2;
 
-    const Kokkos::View<unsigned int**> cell_nodes("cell_nodes", nb_cells, max_nb_node_per_cell);
-    for (size_t j=0; j<nb_cells; ++j) {
-      cell_nodes(j,0) = __edges[j][0];
-      cell_nodes(j,1) = __edges[j][1];
-    }
+    std::vector<CellType> cell_type_vector(nb_cells);
 
-    const Kokkos::View<unsigned short*> cell_nb_nodes("cell_nb_nodes", nb_cells);
+    std::vector<std::vector<unsigned int>> cell_by_node_vector(nb_cells);
     for (size_t j=0; j<nb_cells; ++j) {
-      cell_nb_nodes[j] = 2;
+      cell_by_node_vector[j].resize(2);
+      for (int r=0; r<2; ++r) {
+        cell_by_node_vector[j][r] = __edges[j][r];
+      }
+      cell_type_vector[j] = CellType::Line;
     }
 
-    std::shared_ptr<Connectivity1D> p_connectivity(new Connectivity1D(cell_nb_nodes, cell_nodes));
+    std::shared_ptr<Connectivity1D> p_connectivity(new Connectivity1D(cell_by_node_vector,
+                                                                      cell_type_vector));
     Connectivity1D& connectivity = *p_connectivity;
 
     std::map<unsigned int, std::vector<unsigned int>> ref_points_map;
diff --git a/src/mesh/GmshReader.hpp b/src/mesh/GmshReader.hpp
index f02cfa16f116d2df744cc73bacc073b8964526de..8825a67df3d6cf1593ff54bb6af8b7a96b079b5b 100644
--- a/src/mesh/GmshReader.hpp
+++ b/src/mesh/GmshReader.hpp
@@ -4,7 +4,6 @@
 #include <Kokkos_Core.hpp>
 #include <TinyVector.hpp>
 
-#include <Connectivity2D.hpp>
 #include <Mesh.hpp>
 
 #include <RefId.hpp>
diff --git a/src/mesh/IConnectivity.hpp b/src/mesh/IConnectivity.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..49432fdea276d770c993960a2ffb48da2f7407b0
--- /dev/null
+++ b/src/mesh/IConnectivity.hpp
@@ -0,0 +1,18 @@
+#ifndef ICONNECTIVITY_HPP
+#define ICONNECTIVITY_HPP
+
+#include <ItemType.hpp>
+#include <ConnectivityMatrix.hpp>
+
+struct IConnectivity
+{
+  virtual const ConnectivityMatrix&
+  getMatrix(const ItemType& item_type_0,
+            const ItemType& item_type_1) const = 0;
+
+  IConnectivity() = default;
+  IConnectivity(const IConnectivity&) = delete;
+  ~IConnectivity() = default;
+};
+
+#endif // ICONNECTIVITY_HPP
diff --git a/src/mesh/ItemType.hpp b/src/mesh/ItemType.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d37c02fae15120b872c008f939c5d4907debb085
--- /dev/null
+++ b/src/mesh/ItemType.hpp
@@ -0,0 +1,118 @@
+#ifndef ITEM_TYPE_HPP
+#define ITEM_TYPE_HPP
+
+#include <utility>
+#include <limits>
+#include <string>
+
+enum class ItemType
+{
+  node = 0,
+  edge = 1,
+  face = 2,
+  cell = 3
+};
+
+inline constexpr
+std::string_view itemName(const ItemType& item_type)
+{
+  std::string_view name;
+  switch(item_type){
+    case ItemType::node: {
+      name = "node";
+      break;
+    }
+    case ItemType::edge: {
+      name = "edge";
+      break;
+    }
+    case ItemType::face: {
+      name = "face";
+      break;
+    }
+    case ItemType::cell: {
+      name = "cell";
+      break;
+    }
+  }
+  return name;
+}
+
+template <size_t Dimension>
+struct ItemId {};
+
+template <>
+struct ItemId<1>
+{
+  inline static constexpr size_t itemId(const ItemType& item_type) {
+    size_t i = std::numeric_limits<size_t>::max();
+    switch(item_type) {
+      case ItemType::cell: {
+        i=0;
+        break;
+      }
+      case ItemType::edge:
+      case ItemType::face:
+      case ItemType::node: {
+        // in 1d, faces, edges and nodes are the same
+        i=1;
+        break;
+      }
+    }
+    return i;
+  }
+};
+
+template <>
+struct ItemId<2>
+{
+  inline static constexpr size_t itemId(const ItemType& item_type) {
+    size_t i = std::numeric_limits<size_t>::max();
+    switch(item_type) {
+      case ItemType::cell: {
+        i=0;
+        break;
+      }
+      case ItemType::edge:
+      case ItemType::face: {
+        // in 2d, faces and edges are the same
+        i=1;
+        break;
+      }
+      case ItemType::node: {
+        i=2;
+        break;
+      }
+    }
+    return i;
+  }
+};
+
+template <>
+struct ItemId<3>
+{
+  inline static constexpr size_t itemId(const ItemType& item_type) {
+    size_t i = std::numeric_limits<size_t>::max();
+    switch(item_type) {
+      case ItemType::cell: {
+        i=0;
+        break;
+      }
+      case ItemType::edge: {
+        i=1;
+        break;
+      }
+      case ItemType::face: {
+        i=2;
+        break;
+      }
+      case ItemType::node: {
+        i=3;
+        break;
+      }
+    }
+    return i;
+  }
+};
+
+#endif // ITEM_TYPE_HPP
diff --git a/src/mesh/Mesh.hpp b/src/mesh/Mesh.hpp
index 2b05731f54a466635d43996319314968d1da5bb1..3aee2c287d5cdd06c4a754115caf930973c4f2cb 100644
--- a/src/mesh/Mesh.hpp
+++ b/src/mesh/Mesh.hpp
@@ -36,17 +36,20 @@ public:
     return *m_connectivity;
   }
 
-  const size_t& numberOfNodes() const
+  KOKKOS_INLINE_FUNCTION
+  size_t numberOfNodes() const
   {
     return m_connectivity->numberOfNodes();
   }
 
-  const size_t& numberOfFaces() const
+  KOKKOS_INLINE_FUNCTION
+  size_t numberOfFaces() const
   {
     return m_connectivity->numberOfFaces();
   }
 
-  const size_t& numberOfCells() const
+  KOKKOS_INLINE_FUNCTION
+  size_t numberOfCells() const
   {
     return m_connectivity->numberOfCells();
   }
diff --git a/src/mesh/MeshData.hpp b/src/mesh/MeshData.hpp
index 59411b7f984f318e5af1e35745651208a046abd0..0fe98256bb87d8d02b29899906e475737f4e835d 100644
--- a/src/mesh/MeshData.hpp
+++ b/src/mesh/MeshData.hpp
@@ -4,12 +4,14 @@
 #include <Kokkos_Core.hpp>
 #include <TinyVector.hpp>
 
+#include <SubItemValuePerItem.hpp>
+
 #include <map>
 
 template <typename M>
 class MeshData
 {
-public:
+ public:
   typedef M MeshType;
 
   static constexpr size_t dimension = MeshType::dimension;
@@ -19,56 +21,62 @@ public:
 
   static constexpr double inv_dimension = 1./dimension;
 
-private:
+ private:
   const MeshType& m_mesh;
-  Kokkos::View<Rd**> m_Cjr;
-  Kokkos::View<double**>  m_ljr;
-  Kokkos::View<Rd**> m_njr;
+  NodeValuePerCell<Rd> m_Cjr;
+  NodeValuePerCell<double> m_ljr;
+  NodeValuePerCell<Rd> m_njr;
   Kokkos::View<Rd*>  m_xj;
   Kokkos::View<double*>   m_Vj;
 
   KOKKOS_INLINE_FUNCTION
   void _updateCenter()
   { // Computes vertices isobarycenter
-    if(dimension == 1) {
+    if constexpr (dimension == 1) {
       const Kokkos::View<const Rd*> xr = m_mesh.xr();
-      const Kokkos::View<const unsigned int**>& cell_nodes
-	= m_mesh.connectivity().cellNodes();
+
+      const auto& cell_to_node_matrix
+          = m_mesh.connectivity().getMatrix(ItemType::cell,
+                                            ItemType::node);
+
       Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j){
-	  m_xj[j] = 0.5*(xr[cell_nodes(j,0)]+xr[cell_nodes(j,1)]);
-	});
+          const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+          m_xj[j] = 0.5*(xr[cell_nodes(0)]+xr[cell_nodes(1)]);
+        });
     } else {
       const Kokkos::View<const Rd*> xr = m_mesh.xr();
-      const Kokkos::View<const unsigned int**>& cell_nodes
-	= m_mesh.connectivity().cellNodes();
-      const Kokkos::View<const unsigned short*>& cell_nb_nodes
-	= m_mesh.connectivity().cellNbNodes();
       const Kokkos::View<const double*>& inv_cell_nb_nodes
-	= m_mesh.connectivity().invCellNbNodes();
+          = m_mesh.connectivity().invCellNbNodes();
+
+      const auto& cell_to_node_matrix
+          = m_mesh.connectivity().getMatrix(ItemType::cell,
+                                            ItemType::node);
+
       Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j){
-	  Rd X = zero;
-	  for (int R=0; R<cell_nb_nodes[j]; ++R) {
-	   X += xr[cell_nodes(j,R)];
-	  }
-	  m_xj[j] = inv_cell_nb_nodes[j]*X;
-	});
+          Rd X = zero;
+          const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+          for (size_t R=0; R<cell_nodes.length; ++R) {
+            X += xr[cell_nodes(R)];
+          }
+          m_xj[j] = inv_cell_nb_nodes[j]*X;
+        });
     }
   }
 
   KOKKOS_INLINE_FUNCTION
   void _updateVolume()
   {
-    const Kokkos::View<const unsigned int**>& cell_nodes
-      = m_mesh.connectivity().cellNodes();
-    const Kokkos::View<const unsigned short*> cell_nb_nodes
-      = m_mesh.connectivity().cellNbNodes();
-
     const Kokkos::View<const Rd*> xr = m_mesh.xr();
+    const auto& cell_to_node_matrix
+        = m_mesh.connectivity().getMatrix(ItemType::cell,
+                                          ItemType::node);
 
     Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j){
         double sum_cjr_xr = 0;
-        for (int R=0; R<cell_nb_nodes[j]; ++R) {
-          sum_cjr_xr += (xr[cell_nodes(j,R)], m_Cjr(j,R));
+        const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+
+        for (size_t R=0; R<cell_nodes.length; ++R) {
+          sum_cjr_xr += (xr[cell_nodes(R)], m_Cjr(j,R));
         }
         m_Vj[j] = inv_dimension * sum_cjr_xr;
       });
@@ -78,136 +86,140 @@ private:
   void _updateCjr() {
     if constexpr (dimension == 1) {
       // Cjr/njr/ljr are constant overtime
-      }
+    }
     else if constexpr (dimension == 2) {
-      const Kokkos::View<const unsigned int**>& cell_nodes
-          = m_mesh.connectivity().cellNodes();
-      const Kokkos::View<const unsigned short*> cell_nb_nodes
-          = m_mesh.connectivity().cellNbNodes();
-
       const Kokkos::View<const Rd*> xr = m_mesh.xr();
+      const auto& cell_to_node_matrix
+          = m_mesh.connectivity().getMatrix(ItemType::cell,
+                                            ItemType::node);
 
       Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j){
-          for (int R=0; R<cell_nb_nodes[j]; ++R) {
-            int Rp1 = (R+1)%cell_nb_nodes[j];
-            int Rm1 = (R+cell_nb_nodes[j]-1)%cell_nb_nodes[j];
-            Rd half_xrp_xrm = 0.5*(xr(cell_nodes(j,Rp1))-xr(cell_nodes(j,Rm1)));
+          const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+          for (size_t R=0; R<cell_nodes.length; ++R) {
+            int Rp1 = (R+1)%cell_nodes.length;
+            int Rm1 = (R+cell_nodes.length-1)%cell_nodes.length;
+            Rd half_xrp_xrm = 0.5*(xr(cell_nodes(Rp1))-xr(cell_nodes(Rm1)));
             m_Cjr(j,R) = Rd{-half_xrp_xrm[1], half_xrp_xrm[0]};
           }
         });
 
-      Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j){
-          for (int R=0; R<cell_nb_nodes[j]; ++R) {
-            const Rd& Cjr = m_Cjr(j,R);
-            m_ljr(j,R) = l2Norm(Cjr);
-          }
+      const NodeValuePerCell<Rd>& Cjr = m_Cjr;
+      Kokkos::parallel_for(m_Cjr.numberOfValues(), KOKKOS_LAMBDA(const int& j){
+          m_ljr[j] = l2Norm(Cjr[j]);
         });
 
-      Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j){
-          for (int R=0; R<cell_nb_nodes[j]; ++R) {
-            const Rd& Cjr = m_Cjr(j,R);
-            const double inv_ljr = 1./m_ljr(j,R);
-            m_njr(j,R) = inv_ljr*Cjr;
-          }
+      const NodeValuePerCell<double>& ljr = m_ljr;
+      Kokkos::parallel_for(m_Cjr.numberOfValues(), KOKKOS_LAMBDA(const int& j){
+          m_njr[j] = (1./ljr[j])*Cjr[j];
         });
-    } else if (dimension ==3) {
-      const Kokkos::View<const unsigned int**>& cell_nodes
-          = m_mesh.connectivity().cellNodes();
-      const Kokkos::View<const unsigned short*> cell_nb_nodes
-          = m_mesh.connectivity().cellNbNodes();
 
+    } else if (dimension ==3) {
       const Kokkos::View<const Rd*> xr = m_mesh.xr();
 
-      Kokkos::View<Rd**> Nlr("Nlr", m_mesh.connectivity().numberOfFaces(), m_mesh.connectivity().maxNbNodePerFace());
+      NodeValuePerFace<Rd> Nlr(m_mesh.connectivity());
+      const auto& face_to_node_matrix
+          = m_mesh.connectivity().getMatrix(ItemType::face,
+                                            ItemType::node);
 
-      const Kokkos::View<const unsigned int**>& face_nodes
-          = m_mesh.connectivity().faceNodes();
-      const Kokkos::View<const unsigned short*>& face_nb_nodes
-          = m_mesh.connectivity().faceNbNodes();
       Kokkos::parallel_for(m_mesh.numberOfFaces(), KOKKOS_LAMBDA(const int& l) {
-          const size_t nb_nodes = face_nb_nodes[l];
+          const auto& face_nodes = face_to_node_matrix.rowConst(l);
+          const size_t nb_nodes = face_nodes.length;
           std::vector<Rd> dxr(nb_nodes);
           for (size_t r=0; r<nb_nodes; ++r) {
-            dxr[r] = xr[face_nodes(l,(r+1)%nb_nodes)] - xr[face_nodes(l,(r+nb_nodes-1)%nb_nodes)];
+            dxr[r] = xr[face_nodes((r+1)%nb_nodes)] - xr[face_nodes((r+nb_nodes-1)%nb_nodes)];
           }
           const double inv_12_nb_nodes = 1./(12.*nb_nodes);
           for (size_t r=0; r<nb_nodes; ++r) {
             Rd Nr = zero;
             const Rd two_dxr = 2*dxr[r];
             for (size_t s=0; s<nb_nodes; ++s) {
-              Nr += crossProduct((two_dxr - dxr[s]), xr[face_nodes(l,s)]);
+              Nr += crossProduct((two_dxr - dxr[s]), xr[face_nodes(s)]);
             }
             Nr *= inv_12_nb_nodes;
-            Nr -= (1./6.)*crossProduct(dxr[r], xr[face_nodes(l,r)]);
+            Nr -= (1./6.)*crossProduct(dxr[r], xr[face_nodes(r)]);
             Nlr(l,r) = Nr;
           }
         });
 
-      const Kokkos::View<const unsigned int**> cell_faces
-          = m_mesh.connectivity().cellFaces();
-      const Kokkos::View<const bool**> cell_faces_is_reversed
-          = m_mesh.connectivity().cellFacesIsReversed();
+      Kokkos::parallel_for(m_Cjr.numberOfValues(), KOKKOS_LAMBDA(const int& jr){
+          m_Cjr[jr] = zero;
+        });
+
+      const auto& cell_to_node_matrix
+          = m_mesh.connectivity().getMatrix(ItemType::cell,
+                                            ItemType::node);
 
-      const Kokkos::View<const unsigned short*> cell_nb_faces
-          = m_mesh.connectivity().cellNbFaces();
+      const auto& cell_to_face_matrix
+          = m_mesh.connectivity().getMatrix(ItemType::cell,
+                                            ItemType::face);
 
+      const auto& cell_face_is_reversed = m_mesh.connectivity().cellFaceIsReversed();
       Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j) {
-          for (int R=0; R<cell_nb_nodes[j]; ++R) {
-            m_Cjr(j,R) = zero;
-          }
-          std::map<unsigned int, unsigned short> node_id_to_local;
-          for (size_t R=0; R<cell_nb_nodes[j]; ++R) {
-            node_id_to_local[cell_nodes(j,R)] = R;
-          }
-          for (size_t L=0; L<cell_nb_faces[j]; ++L) {
-            const size_t l = cell_faces(j, L);
-            if (cell_faces_is_reversed(j, L)) {
-              for (size_t rl = 0; rl<face_nb_nodes[l]; ++rl) {
-                m_Cjr(j, node_id_to_local[face_nodes(l,rl)]) -= Nlr(l, rl);
+          const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+
+          const auto& cell_faces = cell_to_face_matrix.rowConst(j);
+          const auto& face_is_reversed = cell_face_is_reversed.itemValues(j);
+
+          for (size_t L=0; L<cell_faces.length; ++L) {
+            const size_t l = cell_faces(L);
+            const auto& face_nodes = face_to_node_matrix.rowConst(l);
+
+#warning should this lambda be replaced by a precomputed correspondance?
+            std::function local_node_number_in_cell
+                = [&](const size_t& node_number) {
+                    for (size_t i_node=0; i_node<cell_nodes.length; ++i_node) {
+                      if (node_number == cell_nodes(i_node)) {
+                        return i_node;
+                        break;
+                      }
+                    }
+                    return std::numeric_limits<size_t>::max();
+                  };
+
+            if (face_is_reversed[L]) {
+              for (size_t rl = 0; rl<face_nodes.length; ++rl) {
+                const size_t R = local_node_number_in_cell(face_nodes(rl));
+                m_Cjr(j, R) -= Nlr(l,rl);
               }
             } else {
-              for (size_t rl = 0; rl<face_nb_nodes[l]; ++rl) {
-                m_Cjr(j, node_id_to_local[face_nodes(l,rl)]) += Nlr(l, rl);
+              for (size_t rl = 0; rl<face_nodes.length; ++rl) {
+                const size_t R = local_node_number_in_cell(face_nodes(rl));
+                m_Cjr(j, R) += Nlr(l,rl);
               }
             }
           }
         });
 
-      Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j) {
-          for (int R=0; R<cell_nb_nodes[j]; ++R) {
-            const Rd& Cjr = m_Cjr(j,R);
-            m_ljr(j,R) = l2Norm(Cjr);
-          }
+      const NodeValuePerCell<Rd>& Cjr = m_Cjr;
+      Kokkos::parallel_for(m_Cjr.numberOfValues(), KOKKOS_LAMBDA(const int& jr){
+          m_ljr[jr] = l2Norm(Cjr[jr]);
         });
 
-      Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j) {
-          for (int R=0; R<cell_nb_nodes[j]; ++R) {
-            const Rd& Cjr = m_Cjr(j,R);
-            const double inv_ljr = 1./m_ljr(j,R);
-            m_njr(j,R) = inv_ljr*Cjr;
-          }
+      const NodeValuePerCell<double>& ljr = m_ljr;
+      Kokkos::parallel_for(m_Cjr.numberOfValues(), KOKKOS_LAMBDA(const int& jr){
+          m_njr[jr] = (1./ljr[jr])*Cjr[jr];
         });
     }
     static_assert((dimension<=3), "only 1d, 2d and 3d are implemented");
   }
 
-public:
+ public:
   const MeshType& mesh() const
   {
     return m_mesh;
   }
 
-  const Kokkos::View<const Rd**> Cjr() const
+  const NodeValuePerCell<Rd>& Cjr() const
   {
     return m_Cjr;
   }
 
-  const Kokkos::View<const double**> ljr() const
+  const NodeValuePerCell<double>& ljr() const
   {
     return m_ljr;
   }
 
-  const Kokkos::View<const Rd**> njr() const
+  const NodeValuePerCell<Rd>& njr() const
   {
     return m_njr;
   }
@@ -230,25 +242,25 @@ public:
   }
 
   MeshData(const MeshType& mesh)
-    : m_mesh(mesh),
-      m_Cjr("Cjr", mesh.numberOfCells(), mesh.connectivity().maxNbNodePerCell()),
-      m_ljr("ljr", mesh.numberOfCells(), mesh.connectivity().maxNbNodePerCell()),
-      m_njr("njr", mesh.numberOfCells(), mesh.connectivity().maxNbNodePerCell()),
-      m_xj("xj", mesh.numberOfCells()),
-      m_Vj("Vj", mesh.numberOfCells())
+      : m_mesh(mesh),
+        m_Cjr(mesh.connectivity()),
+        m_ljr(mesh.connectivity()),
+        m_njr(mesh.connectivity()),
+        m_xj("xj", mesh.numberOfCells()),
+        m_Vj("Vj", mesh.numberOfCells())
   {
     if constexpr (dimension==1) {
       // in 1d Cjr are computed once for all
       Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j) {
-	  m_Cjr(j,0)=-1;
-	  m_Cjr(j,1)= 1;
-	});
-      // in 1d njr=Cjr
+          m_Cjr(j,0)=-1;
+          m_Cjr(j,1)= 1;
+        });
+      // in 1d njr=Cjr (here performs a shallow copy)
       m_njr=m_Cjr;
       Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j) {
-	  m_ljr(j,0)= 1;
-	  m_ljr(j,1)= 1;
-	});
+          m_ljr(j,0)= 1;
+          m_ljr(j,1)= 1;
+        });
     }
     this->updateAllData();
   }
diff --git a/src/mesh/MeshNodeBoundary.hpp b/src/mesh/MeshNodeBoundary.hpp
index 8361c2f29cab7253f10c76c639016cad54d2fd73..87c537439bf1a27e8443865677cd6b58c132378c 100644
--- a/src/mesh/MeshNodeBoundary.hpp
+++ b/src/mesh/MeshNodeBoundary.hpp
@@ -2,11 +2,14 @@
 #define MESH_NODE_BOUNDARY_HPP
 
 #include <Kokkos_Core.hpp>
+#include <Kokkos_Vector.hpp>
 #include <TinyVector.hpp>
 
 #include <RefNodeList.hpp>
 #include <RefFaceList.hpp>
 
+#include <ConnectivityMatrix.hpp>
+#include <IConnectivity.hpp>
 #include <iostream>
 
 template <size_t dimension>
@@ -28,26 +31,31 @@ class MeshNodeBoundary
                    const RefFaceList& ref_face_list)
   {
     static_assert(dimension == MeshType::dimension);
-    const Kokkos::View<const unsigned short*> face_nb_cells = mesh.connectivity().faceNbCells();
+    const auto& face_to_cell_matrix
+        = mesh.connectivity().getMatrix(ItemType::face, ItemType::cell);
 
     const Kokkos::View<const unsigned int*>& face_list = ref_face_list.faceList();
     Kokkos::parallel_for(face_list.extent(0), KOKKOS_LAMBDA(const int& l){
-        if (face_nb_cells[face_list[l]]>1) {
+        const auto& face_cells = face_to_cell_matrix.rowConst(face_list[l]);
+        if (face_cells.length>1) {
           std::cerr << "internal faces cannot be used to define mesh boundaries\n";
           std::exit(1);
         }
       });
 
-    const Kokkos::View<const unsigned short*> face_nb_nodes = mesh.connectivity().faceNbNodes();
-    const Kokkos::View<const unsigned int**> face_nodes = mesh.connectivity().faceNodes();
-
-    std::vector<unsigned int> node_ids;
+    Kokkos::vector<unsigned int> node_ids;
     // not enough but should reduce significantly the number of resizing
     node_ids.reserve(dimension*face_list.extent(0));
+    const auto& face_to_node_matrix
+        = mesh.connectivity().getMatrix(ItemType::face,
+                                        ItemType::node);
+
     for (size_t l=0; l<face_list.extent(0); ++l) {
       const size_t face_number = face_list[l];
-      for (size_t r=0; r<face_nb_nodes[face_number]; ++r) {
-        node_ids.push_back(face_nodes(face_number,r));
+      const auto& face_nodes = face_to_node_matrix.rowConst(face_number);
+
+      for (size_t r=0; r<face_nodes.length; ++r) {
+        node_ids.push_back(face_nodes(r));
       }
     }
     std::sort(node_ids.begin(), node_ids.end());
@@ -307,17 +315,22 @@ _getOutgoingNormal(const MeshType& mesh)
   const R normal = this->_getNormal(mesh);
 
   const Kokkos::View<const R*>& xr = mesh.xr();
-  const Kokkos::View<const unsigned int**>& node_cells = mesh.connectivity().nodeCells();
-  const Kokkos::View<const unsigned int**>& cell_nodes = mesh.connectivity().cellNodes();
-  const Kokkos::View<const unsigned short*>& cell_nb_nodes = mesh.connectivity().cellNbNodes();
+  const auto& cell_to_node_matrix
+      = mesh.connectivity().getMatrix(ItemType::cell,
+                                      ItemType::node);
+
+  const auto& node_to_cell_matrix
+      = mesh.connectivity().getMatrix(ItemType::node,
+                                      ItemType::cell);
 
   const size_t r0 = m_node_list[0];
-  const size_t j0 = node_cells(r0,0);
+  const size_t j0 = node_to_cell_matrix.rowConst(r0)(0);
+  const auto& j0_nodes = cell_to_node_matrix.rowConst(j0);
   double max_height = 0;
-  for (int r=0; r<cell_nb_nodes(j0); ++r) {
-    const double height = (xr(cell_nodes(j0, r))-xr(r0), normal);
+  for (size_t r=0; r<j0_nodes.length; ++r) {
+    const double height = (xr(j0_nodes(r))-xr(r0), normal);
     if (std::abs(height) > std::abs(max_height)) {
-      max_height =  height;
+      max_height = height;
     }
   }
   if (max_height > 0) {
@@ -339,17 +352,22 @@ _getOutgoingNormal(const MeshType& mesh)
   const R2 normal = this->_getNormal(mesh);
 
   const Kokkos::View<const R2*>& xr = mesh.xr();
-  const Kokkos::View<const unsigned int**>& node_cells = mesh.connectivity().nodeCells();
-  const Kokkos::View<const unsigned int**>& cell_nodes = mesh.connectivity().cellNodes();
-  const Kokkos::View<const unsigned short*>& cell_nb_nodes = mesh.connectivity().cellNbNodes();
+  const auto& cell_to_node_matrix
+      = mesh.connectivity().getMatrix(ItemType::cell,
+                                      ItemType::node);
+
+  const auto& node_to_cell_matrix
+      = mesh.connectivity().getMatrix(ItemType::node,
+                                      ItemType::cell);
 
   const size_t r0 = m_node_list[0];
-  const size_t j0 = node_cells(r0,0);
+  const size_t j0 = node_to_cell_matrix.rowConst(r0)(0);
+  const auto& j0_nodes = cell_to_node_matrix.rowConst(j0);
   double max_height = 0;
-  for (int r=0; r<cell_nb_nodes(j0); ++r) {
-    const double height = (xr(cell_nodes(j0, r))-xr(r0), normal);
+  for (size_t r=0; r<j0_nodes.length; ++r) {
+    const double height = (xr(j0_nodes(r))-xr(r0), normal);
     if (std::abs(height) > std::abs(max_height)) {
-      max_height =  height;
+      max_height = height;
     }
   }
   if (max_height > 0) {
@@ -371,17 +389,22 @@ _getOutgoingNormal(const MeshType& mesh)
   const R3 normal = this->_getNormal(mesh);
 
   const Kokkos::View<const R3*>& xr = mesh.xr();
-  const Kokkos::View<const unsigned int**>& node_cells = mesh.connectivity().nodeCells();
-  const Kokkos::View<const unsigned int**>& cell_nodes = mesh.connectivity().cellNodes();
-  const Kokkos::View<const unsigned short*>& cell_nb_nodes = mesh.connectivity().cellNbNodes();
+  const auto& cell_to_node_matrix
+      = mesh.connectivity().getMatrix(ItemType::cell,
+                                      ItemType::node);
+
+  const auto& node_to_cell_matrix
+      = mesh.connectivity().getMatrix(ItemType::node,
+                                      ItemType::cell);
 
   const size_t r0 = m_node_list[0];
-  const size_t j0 = node_cells(r0,0);
+  const size_t j0 = node_to_cell_matrix.rowConst(r0)(0);
+  const auto& j0_nodes = cell_to_node_matrix.rowConst(j0);
   double max_height = 0;
-  for (int r=0; r<cell_nb_nodes(j0); ++r) {
-    const double height = (xr(cell_nodes(j0, r))-xr(r0), normal);
+  for (size_t r=0; r<j0_nodes.length; ++r) {
+    const double height = (xr(j0_nodes(r))-xr(r0), normal);
     if (std::abs(height) > std::abs(max_height)) {
-      max_height =  height;
+      max_height = height;
     }
   }
   if (max_height > 0) {
diff --git a/src/mesh/SubItemValuePerItem.hpp b/src/mesh/SubItemValuePerItem.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5778fc73989ca547130288d1737bf9b1d767c2ef
--- /dev/null
+++ b/src/mesh/SubItemValuePerItem.hpp
@@ -0,0 +1,249 @@
+#ifndef SUBITEM_VALUE_PER_ITEM_HPP
+#define SUBITEM_VALUE_PER_ITEM_HPP
+
+#include <Kokkos_StaticCrsGraph.hpp>
+#include <ItemType.hpp>
+#include <PastisAssert.hpp>
+
+#include <ConnectivityMatrix.hpp>
+#include <IConnectivity.hpp>
+
+template <typename DataType,
+          ItemType sub_item_type,
+          ItemType item_type,
+          typename Allowed=void>
+class SubItemValuePerItem;
+
+template <typename DataType,
+          ItemType sub_item_type,
+          ItemType item_type>
+class SubItemValuePerItem<DataType,
+                          sub_item_type,
+                          item_type,
+                          std::enable_if_t<sub_item_type != item_type>>
+{
+ public:
+  static const ItemType item_t{item_type};
+  static const ItemType sub_item_t{sub_item_type};
+ private:
+  bool m_is_built{false};
+
+  ConnectivityMatrix::HostRowType m_host_row_map;
+  Kokkos::View<DataType*> m_values;
+
+  // Allows const version to access our data
+  friend SubItemValuePerItem<std::add_const_t<DataType>,
+                             sub_item_type,
+                             item_type>;
+
+ public:
+  class SubView
+  {
+   private:
+    KOKKOS_RESTRICT DataType* const m_sub_values;
+    const size_t m_size;
+   public:
+    KOKKOS_INLINE_FUNCTION
+    const DataType& operator[](const size_t& i) const
+    {
+      Assert(i<m_size);
+      return m_sub_values[i];
+    }
+
+    KOKKOS_FORCEINLINE_FUNCTION
+    DataType& operator[](const size_t& i)
+    {
+      Assert(i<m_size);
+      return m_sub_values[i];
+    }
+
+    KOKKOS_INLINE_FUNCTION
+    const size_t& size() const
+    {
+      return m_size;
+    }
+
+    SubView(const SubView&) = delete;
+
+    KOKKOS_INLINE_FUNCTION
+    SubView(SubView&&) = default;
+
+    KOKKOS_INLINE_FUNCTION
+    SubView(const Kokkos::View<DataType*>& values,
+            const size_t& begin,
+            const size_t& end)
+        : m_sub_values(&(values[begin])),
+          m_size(end-begin)
+    {
+      Assert(begin<=end);
+      Assert(end<=values.extent(0));
+    }
+  };
+
+  KOKKOS_FORCEINLINE_FUNCTION
+  const bool& isBuilt() const
+  {
+    return m_is_built;
+  }
+
+  KOKKOS_FORCEINLINE_FUNCTION
+  DataType& operator()(const size_t& j, const size_t& r)
+  {
+    Assert(m_is_built);
+    return m_values[m_host_row_map(j)+r];
+  }
+
+  // Following Kokkos logic, these classes are view and const view does allow
+  // changes in data
+  KOKKOS_FORCEINLINE_FUNCTION
+  DataType& operator()(const size_t& j, const size_t& r) const
+  {
+    Assert(m_is_built);
+    return m_values[m_host_row_map(j)+r];
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  size_t numberOfValues() const
+  {
+    Assert(m_is_built);
+    return m_values.extent(0);
+  }
+
+  KOKKOS_FORCEINLINE_FUNCTION
+  DataType& operator[](const size_t& i)
+  {
+    Assert(m_is_built);
+    return m_values[i];
+  }
+
+  // Following Kokkos logic, these classes are view and const view does allow
+  // changes in data
+  KOKKOS_FORCEINLINE_FUNCTION
+  DataType& operator[](const size_t & i) const
+  {
+    Assert(m_is_built);
+    return m_values[i];
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  size_t numberOfItems() const
+  {
+    Assert(m_is_built);
+    Assert(m_host_row_map.extent(0) != 0);
+    return m_host_row_map.extent(0);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  size_t numberOfSubValues(const size_t& i_cell) const
+  {
+    Assert(m_is_built);
+    return m_host_row_map(i_cell+1)-m_host_row_map(i_cell);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  SubView itemValues(const size_t& i_cell)
+  {
+    Assert(m_is_built);
+    const auto& cell_begin = m_host_row_map(i_cell);
+    const auto& cell_end = m_host_row_map(i_cell+1);
+    return SubView(m_values, cell_begin, cell_end);
+  }
+
+  // Following Kokkos logic, these classes are view and const view does allow
+  // changes in data
+  KOKKOS_INLINE_FUNCTION
+  SubView itemValues(const size_t& i_cell) const
+  {
+    Assert(m_is_built);
+    const auto& cell_begin = m_host_row_map(i_cell);
+    const auto& cell_end = m_host_row_map(i_cell+1);
+    return SubView(m_values, cell_begin, cell_end);
+  }
+
+  template <typename DataType2>
+  KOKKOS_INLINE_FUNCTION
+  SubItemValuePerItem&
+  operator=(const SubItemValuePerItem<DataType2, sub_item_type, item_type>& sub_item_value_per_item)
+  {
+    // ensures that DataType is the same as source DataType2
+    static_assert(std::is_same<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>(),
+                  "Cannot assign SubItemValuePerItem of different type");
+    // ensures that const is not lost through copy
+    static_assert(((std::is_const<DataType2>() and std::is_const<DataType>())
+                   or not std::is_const<DataType2>()),
+                  "Cannot assign const  SubItemValuePerItem to a non const SubItemValuePerItem");
+    m_host_row_map = sub_item_value_per_item.m_host_row_map;
+    m_values = sub_item_value_per_item.m_values;
+
+    m_is_built = sub_item_value_per_item.m_is_built;
+
+    return *this;
+  }
+
+  template <typename DataType2>
+  KOKKOS_INLINE_FUNCTION
+  SubItemValuePerItem(const SubItemValuePerItem<DataType2, sub_item_type, item_type>& sub_item_value_per_item)
+  {
+    this->operator=(sub_item_value_per_item);
+  }
+
+  SubItemValuePerItem() = default;
+
+  SubItemValuePerItem(const IConnectivity& connectivity)
+      : m_is_built{true}
+  {
+    ConnectivityMatrix connectivity_matrix
+        = connectivity.getMatrix(item_type, sub_item_type);
+
+    m_host_row_map = connectivity_matrix.rowsMap();
+    m_values = Kokkos::View<DataType*>("values", connectivity_matrix.numEntries());
+  }
+
+  ~SubItemValuePerItem() = default;
+};
+
+// Item values at nodes
+
+template <typename DataType>
+using NodeValuePerEdge = SubItemValuePerItem<DataType, ItemType::node, ItemType::edge>;
+
+template <typename DataType>
+using NodeValuePerFace = SubItemValuePerItem<DataType, ItemType::node, ItemType::face>;
+
+template <typename DataType>
+using NodeValuePerCell = SubItemValuePerItem<DataType, ItemType::node, ItemType::cell>;
+
+// Item values at edges
+
+template <typename DataType>
+using EdgeValuePerNode = SubItemValuePerItem<DataType, ItemType::edge, ItemType::node>;
+
+template <typename DataType>
+using EdgeValuePerFace = SubItemValuePerItem<DataType, ItemType::edge, ItemType::face>;
+
+template <typename DataType>
+using EdgeValuePerCell = SubItemValuePerItem<DataType, ItemType::edge, ItemType::cell>;
+
+// Item values at faces
+
+template <typename DataType>
+using FaceValuePerNode = SubItemValuePerItem<DataType, ItemType::face, ItemType::node>;
+
+template <typename DataType>
+using FaceValuePerEdge = SubItemValuePerItem<DataType, ItemType::face, ItemType::edge>;
+
+template <typename DataType>
+using FaceValuePerCell = SubItemValuePerItem<DataType, ItemType::face, ItemType::cell>;
+
+// Item values at cells
+
+template <typename DataType>
+using CellValuePerNode = SubItemValuePerItem<DataType, ItemType::cell, ItemType::node>;
+
+template <typename DataType>
+using CellValuePerEdge = SubItemValuePerItem<DataType, ItemType::cell, ItemType::edge>;
+
+template <typename DataType>
+using CellValuePerFace = SubItemValuePerItem<DataType, ItemType::cell, ItemType::face>;
+
+#endif // SUBITEM_VALUE_PER_ITEM_HPP
diff --git a/src/output/VTKWriter.hpp b/src/output/VTKWriter.hpp
index 4e34f666cc0829eef509f4140aaa3aab6d9f9727..a33fef0bfbe2abf2c73b34e7c3b564fb2cd81461 100644
--- a/src/output/VTKWriter.hpp
+++ b/src/output/VTKWriter.hpp
@@ -6,6 +6,7 @@
 #include <iomanip>
 #include <sstream>
 #include <TinyVector.hpp>
+#include <IConnectivity.hpp>
 
 class VTKWriter
 {
@@ -67,13 +68,17 @@ class VTKWriter
     fout << "</Points>\n";
 
     fout << "<Cells>\n";
-    const Kokkos::View<const unsigned int**> cell_nodes = mesh.connectivity().cellNodes();
-    const Kokkos::View<const unsigned short*> cell_nb_nodes = mesh.connectivity().cellNbNodes();
 
     fout << "<DataArray type=\"Int32\" Name=\"connectivity\" NumberOfComponents=\"1\" format=\"ascii\">\n";
+
+    const auto& cell_to_node_matrix
+        = mesh.connectivity().getMatrix(ItemType::cell,
+                                        ItemType::node);
+
     for (unsigned int j=0; j<mesh.numberOfCells(); ++j) {
-      for (unsigned short r=0; r<cell_nb_nodes[j]; ++r) {
-        fout << cell_nodes(j,r) << ' ';
+      const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+      for (unsigned short r=0; r<cell_nodes.length; ++r) {
+        fout << cell_nodes(r) << ' ';
       }
     }
     fout << '\n';
@@ -83,7 +88,8 @@ class VTKWriter
     {
       unsigned int offset=0;
       for (unsigned int j=0; j<mesh.numberOfCells(); ++j) {
-        offset += cell_nb_nodes[j];
+        const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+        offset += cell_nodes.length;
         fout << offset << ' ';
       }
     }
@@ -92,7 +98,8 @@ class VTKWriter
 
     fout << "<DataArray type=\"Int8\" Name=\"types\" NumberOfComponents=\"1\" format=\"ascii\">\n";
     for (unsigned int j=0; j<mesh.numberOfCells(); ++j) {
-      switch (cell_nb_nodes[j]) {
+      const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+      switch (cell_nodes.length) {
         case 2: {
           fout << "3 ";
           break;
diff --git a/src/scheme/AcousticSolver.hpp b/src/scheme/AcousticSolver.hpp
index 3e2d108d7eef1386aa4ec1c2679ecd26c80ec243..0cc6ec0df7a97b91999376873ed7b35a0c5acf6c 100644
--- a/src/scheme/AcousticSolver.hpp
+++ b/src/scheme/AcousticSolver.hpp
@@ -14,6 +14,8 @@
 #include <FiniteVolumesEulerUnknowns.hpp>
 #include <BoundaryCondition.hpp>
 
+#include <SubItemValuePerItem.hpp>
+
 template<typename MeshData>
 class AcousticSolver
 {
@@ -30,13 +32,13 @@ class AcousticSolver
   typedef TinyVector<dimension> Rd;
   typedef TinyMatrix<dimension> Rdd;
 
-private:
+ private:
   struct ReduceMin
   {
-  private:
+   private:
     const Kokkos::View<const double*> x_;
 
-  public:
+   public:
     typedef Kokkos::View<const double*>::non_const_value_type value_type;
 
     ReduceMin(const Kokkos::View<const double*>& x) : x_ (x) {}
@@ -47,16 +49,16 @@ private:
     void operator() (const size_type i, value_type& update) const
     {
       if (x_(i) < update) {
-	update = x_(i);
+        update = x_(i);
       }
     }
 
     KOKKOS_INLINE_FUNCTION
     void join (volatile value_type& dst,
-	       const volatile value_type& src) const
+               const volatile value_type& src) const
     {
       if (src < dst) {
-	dst = src;
+        dst = src;
       }
     }
 
@@ -67,50 +69,53 @@ private:
     }
   };
 
-
   KOKKOS_INLINE_FUNCTION
   const Kokkos::View<const double*>
   computeRhoCj(const Kokkos::View<const double*>& rhoj,
-	       const Kokkos::View<const double*>& cj)
+               const Kokkos::View<const double*>& cj)
   {
     Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j) {
-	m_rhocj[j] = rhoj[j]*cj[j];
+        m_rhocj[j] = rhoj[j]*cj[j];
       });
     return m_rhocj;
   }
 
   KOKKOS_INLINE_FUNCTION
-  const Kokkos::View<const Rdd**>
-  computeAjr(const Kokkos::View<const double*>& rhocj,
-	     const Kokkos::View<const double**>& ljr,
-	     const Kokkos::View<const Rd**>& njr) {
-    const Kokkos::View<const unsigned short*> cell_nb_nodes
-      = m_connectivity.cellNbNodes();
-
+  void computeAjr(const Kokkos::View<const double*>& rhocj,
+                  const NodeValuePerCell<Rd>& Cjr,
+                  const NodeValuePerCell<double>& ljr,
+                  const NodeValuePerCell<Rd>& njr)
+  {
     Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j) {
-	for (int r=0; r<cell_nb_nodes[j]; ++r) {
-	  m_Ajr(j,r) = tensorProduct((rhocj(j)*ljr(j,r))*njr(j,r), njr(j,r));
-	}
+        const size_t& nb_nodes =m_Ajr.numberOfSubValues(j);
+        const double& rho_c = rhocj(j);
+        for (size_t r=0; r<nb_nodes; ++r) {
+          m_Ajr(j,r) = tensorProduct(rho_c*Cjr(j,r), njr(j,r));
+        }
       });
-
-    return m_Ajr;
   }
 
   KOKKOS_INLINE_FUNCTION
   const Kokkos::View<const Rdd*>
-  computeAr(const Kokkos::View<const Rdd**>& Ajr) {
-    const Kokkos::View<const unsigned int**> node_cells = m_connectivity.nodeCells();
-    const Kokkos::View<const unsigned short**> node_cell_local_node = m_connectivity.nodeCellLocalNode();
-    const Kokkos::View<const unsigned short*> node_nb_cells = m_connectivity.nodeNbCells();
+  computeAr(const NodeValuePerCell<Rdd>& Ajr) {
+    const auto& node_to_cell_matrix
+        = m_connectivity.getMatrix(ItemType::node,
+                                   ItemType::cell);
+    const auto& node_local_numbers_in_their_cells
+        = m_connectivity.nodeLocalNumbersInTheirCells();
 
     Kokkos::parallel_for(m_mesh.numberOfNodes(), KOKKOS_LAMBDA(const int& r) {
-	Rdd sum = zero;
-	for (int j=0; j<node_nb_cells(r); ++j) {
-	  const int J = node_cells(r,j);
-	  const int R = node_cell_local_node(r,j);
-	  sum += Ajr(J,R);
-	}
-	m_Ar(r) = sum;
+        Rdd sum = zero;
+        const auto& node_to_cell = node_to_cell_matrix.rowConst(r);
+        const auto& node_local_number_in_its_cells
+            = node_local_numbers_in_their_cells.itemValues(r);
+
+        for (size_t j=0; j<node_to_cell.length; ++j) {
+          const unsigned int J = node_to_cell(j);
+          const unsigned int R = node_local_number_in_its_cells[j];
+          sum += Ajr(J,R);
+        }
+        m_Ar(r) = sum;
       });
 
     return m_Ar;
@@ -118,22 +123,27 @@ private:
 
   KOKKOS_INLINE_FUNCTION
   const Kokkos::View<const Rd*>
-  computeBr(const Kokkos::View<const Rdd**>& Ajr,
-	    const Kokkos::View<const Rd**>& Cjr,
-	    const Kokkos::View<const Rd*>& uj,
-	    const Kokkos::View<const double*>& pj) {
-    const Kokkos::View<const unsigned int**>& node_cells = m_connectivity.nodeCells();
-    const Kokkos::View<const unsigned short**>& node_cell_local_node = m_connectivity.nodeCellLocalNode();
-    const Kokkos::View<const unsigned short*>& node_nb_cells = m_connectivity.nodeNbCells();
+  computeBr(const NodeValuePerCell<Rdd>& Ajr,
+            const NodeValuePerCell<Rd>& Cjr,
+            const Kokkos::View<const Rd*>& uj,
+            const Kokkos::View<const double*>& pj) {
+    const auto& node_to_cell_matrix
+        = m_connectivity.getMatrix(ItemType::node,
+                                   ItemType::cell);
+    const auto& node_local_numbers_in_their_cells
+        = m_connectivity.nodeLocalNumbersInTheirCells();
 
     Kokkos::parallel_for(m_mesh.numberOfNodes(), KOKKOS_LAMBDA(const int& r) {
-	Rd& br = m_br(r);
-	br = zero;
-	for (int j=0; j<node_nb_cells(r); ++j) {
-	  const int J = node_cells(r,j);
-	  const int R = node_cell_local_node(r,j);
-	  br += Ajr(J,R)*uj(J) + pj(J)*Cjr(J,R);
-	}
+        Rd& br = m_br(r);
+        br = zero;
+        const auto& node_to_cell = node_to_cell_matrix.rowConst(r);
+        const auto& node_local_number_in_its_cells
+            = node_local_numbers_in_their_cells.itemValues(r);
+        for (size_t j=0; j<node_to_cell.length; ++j) {
+          const unsigned int J = node_to_cell(j);
+          const unsigned int R = node_local_number_in_its_cells[j];
+          br += Ajr(J,R)*uj(J) + pj(J)*Cjr(J,R);
+        }
       });
 
     return m_br;
@@ -143,171 +153,155 @@ private:
   {
     for (const auto& handler : m_boundary_condition_list) {
       switch (handler.boundaryCondition().type()) {
-      case BoundaryCondition::normal_velocity: {
-	std::cerr << "normal_velocity NIY\n";
-	std::exit(0);
-	break;
-      }
-      case BoundaryCondition::velocity: {
-	std::cerr << "velocity NIY\n";
-	std::exit(0);
-	break;
-      }
-      case BoundaryCondition::pressure: {
-	const PressureBoundaryCondition& pressure_bc
-	  = dynamic_cast<const PressureBoundaryCondition&>(handler.boundaryCondition());
-	const Kokkos::View<const unsigned int**> face_cells
-	  = m_connectivity.faceCells();
-	const Kokkos::View<const unsigned short**> face_cell_local_face
-	  = m_connectivity.faceCellLocalFace();
-	const Kokkos::View<const Rd**> Cjr
-	  = m_mesh_data.Cjr();
-
-	Kokkos::parallel_for(pressure_bc.numberOfFaces(), KOKKOS_LAMBDA(const int& l_number) {
-	    // quite ugly: node/faces are melt in a bad way... at least works in 1d...
-	    const int l = pressure_bc.faceList()[l_number];
-	    Assert(m_connectivity.faceNbCells()[l] == 1);
-	    const unsigned int j = face_cells(l,0);
-	    const unsigned int L = face_cell_local_face(l,0);
-
-	    m_br(l) -= pressure_bc.value()*Cjr(j,L);
-	  });
-	break;
-      }
-      case BoundaryCondition::symmetry: {
-	const SymmetryBoundaryCondition<dimension>& symmetry_bc
-	  = dynamic_cast<const SymmetryBoundaryCondition<dimension>&>(handler.boundaryCondition());
-	const Rd& n = symmetry_bc.outgoingNormal();
-
-	const Rdd I = identity;
-	const Rdd nxn = tensorProduct(n,n);
-	const Rdd P = I-nxn;
-
-	Kokkos::parallel_for(symmetry_bc.numberOfNodes(), KOKKOS_LAMBDA(const int& r_number) {
-	    const int r = symmetry_bc.nodeList()[r_number];
-	    // assert(m_connectivity.nodeNbCells()[r] == 1);
-
-	    m_Ar(r) = P*m_Ar(r)*P + nxn;
-	    m_br(r) = P*m_br(r);
-	  });
-	break;
-      }
+        case BoundaryCondition::normal_velocity: {
+          std::cerr << __FILE__ << ':' << __LINE__  << ": normal_velocity BC NIY\n";
+          std::exit(0);
+          break;
+        }
+        case BoundaryCondition::velocity: {
+          std::cerr << __FILE__ << ':' << __LINE__  << ": velocity BC NIY\n";
+          std::exit(0);
+          break;
+        }
+        case BoundaryCondition::pressure: {
+          // const PressureBoundaryCondition& pressure_bc
+          //   = dynamic_cast<const PressureBoundaryCondition&>(handler.boundaryCondition());
+          std::cerr << __FILE__ << ':' << __LINE__ << ": pressure BC NIY\n";
+          std::exit(0);
+          break;
+        }
+        case BoundaryCondition::symmetry: {
+          const SymmetryBoundaryCondition<dimension>& symmetry_bc
+              = dynamic_cast<const SymmetryBoundaryCondition<dimension>&>(handler.boundaryCondition());
+          const Rd& n = symmetry_bc.outgoingNormal();
+
+          const Rdd I = identity;
+          const Rdd nxn = tensorProduct(n,n);
+          const Rdd P = I-nxn;
+
+          Kokkos::parallel_for(symmetry_bc.numberOfNodes(), KOKKOS_LAMBDA(const int& r_number) {
+              const int r = symmetry_bc.nodeList()[r_number];
+
+              m_Ar(r) = P*m_Ar(r)*P + nxn;
+              m_br(r) = P*m_br(r);
+            });
+          break;
+        }
       }
     }
   }
 
   Kokkos::View<Rd*>
   computeUr(const Kokkos::View<const Rdd*>& Ar,
-	    const Kokkos::View<const Rd*>& br) {
+            const Kokkos::View<const Rd*>& br)
+  {
     inverse(Ar, m_inv_Ar);
     const Kokkos::View<const Rdd*> invAr = m_inv_Ar;
     Kokkos::parallel_for(m_mesh.numberOfNodes(), KOKKOS_LAMBDA(const int& r) {
-	m_ur[r]=invAr(r)*br(r);
+        m_ur[r]=invAr(r)*br(r);
       });
 
     return m_ur;
   }
 
-  Kokkos::View<Rd**>
-  computeFjr(const Kokkos::View<const Rdd**>& Ajr,
-	     const Kokkos::View<const Rd*>& ur,
-	     const Kokkos::View<const Rd**>& Cjr,
-	     const Kokkos::View<const Rd*>& uj,
-	     const Kokkos::View<const double*>& pj) {
-    const Kokkos::View<const unsigned int**>& cell_nodes = m_connectivity.cellNodes();
-    const Kokkos::View<const unsigned short*> cell_nb_nodes
-      = m_connectivity.cellNbNodes();
+  void
+  computeFjr(const NodeValuePerCell<Rdd>& Ajr,
+             const Kokkos::View<const Rd*>& ur,
+             const NodeValuePerCell<Rd>& Cjr,
+             const Kokkos::View<const Rd*>& uj,
+             const Kokkos::View<const double*>& pj)
+  {
+    const auto& cell_to_node_matrix
+        = m_mesh.connectivity().getMatrix(ItemType::cell,
+                                          ItemType::node);
 
     Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j) {
-	for (int r=0; r<cell_nb_nodes[j]; ++r) {
-	  m_Fjr(j,r) = Ajr(j,r)*(uj(j)-ur(cell_nodes(j,r)))+pj(j)*Cjr(j,r);
-	}
-      });
+        const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
 
-    return m_Fjr;
+        for (size_t r=0; r<cell_nodes.length; ++r) {
+          m_Fjr(j,r) = Ajr(j,r)*(uj(j)-ur(cell_nodes(r)))+pj(j)*Cjr(j,r);
+        }
+      });
   }
 
   void inverse(const Kokkos::View<const Rdd*>& A,
-               Kokkos::View<Rdd*>& inv_A) const {
+               Kokkos::View<Rdd*>& inv_A) const
+  {
     Kokkos::parallel_for(m_mesh.numberOfNodes(), KOKKOS_LAMBDA(const int& r) {
         inv_A(r) = ::inverse(A(r));
       });
   }
 
-  void inverse(const Kokkos::View<const double*>& x,
-	       Kokkos::View<double*>& inv_x) const {
-    Kokkos::parallel_for(x.size(), KOKKOS_LAMBDA(const int& r) {
-	inv_x(r) = 1./x(r);
-      });
-  }
-
   KOKKOS_INLINE_FUNCTION
   void computeExplicitFluxes(const Kokkos::View<const Rd*>& xr,
-			     const Kokkos::View<const Rd*>& xj,
-			     const Kokkos::View<const double*>& rhoj,
-			     const Kokkos::View<const Rd*>& uj,
-			     const Kokkos::View<const double*>& pj,
-			     const Kokkos::View<const double*>& cj,
-			     const Kokkos::View<const double*>& Vj,
-			     const Kokkos::View<const Rd**>& Cjr,
-			     const Kokkos::View<const double**>& ljr,
-			     const Kokkos::View<const Rd**>& njr) {
+                             const Kokkos::View<const Rd*>& xj,
+                             const Kokkos::View<const double*>& rhoj,
+                             const Kokkos::View<const Rd*>& uj,
+                             const Kokkos::View<const double*>& pj,
+                             const Kokkos::View<const double*>& cj,
+                             const Kokkos::View<const double*>& Vj,
+                             const NodeValuePerCell<Rd>& Cjr,
+                             const NodeValuePerCell<double>& ljr,
+                             const NodeValuePerCell<Rd>& njr) {
     const Kokkos::View<const double*> rhocj  = computeRhoCj(rhoj, cj);
-    const Kokkos::View<const Rdd**> Ajr = computeAjr(rhocj, ljr, njr);
+    computeAjr(rhocj, Cjr, ljr, njr);
 
-    const Kokkos::View<const Rdd*> Ar = computeAr(Ajr);
-    const Kokkos::View<const Rd*> br = computeBr(Ajr, Cjr, uj, pj);
+    const Kokkos::View<const Rdd*> Ar = computeAr(m_Ajr);
+    const Kokkos::View<const Rd*> br = computeBr(m_Ajr, Cjr, uj, pj);
 
     this->applyBoundaryConditions();
 
     Kokkos::View<Rd*> ur = m_ur;
-    Kokkos::View<Rd**> Fjr = m_Fjr;
     ur  = computeUr(Ar, br);
-    Fjr = computeFjr(Ajr, ur, Cjr, uj, pj);
+    computeFjr(m_Ajr, ur, Cjr, uj, pj);
   }
 
   Kokkos::View<Rd*> m_br;
-  Kokkos::View<Rdd**> m_Ajr;
+  NodeValuePerCell<Rdd> m_Ajr;
   Kokkos::View<Rdd*> m_Ar;
   Kokkos::View<Rdd*> m_inv_Ar;
-  Kokkos::View<Rd**> m_Fjr;
+  NodeValuePerCell<Rd> m_Fjr;
   Kokkos::View<Rd*> m_ur;
   Kokkos::View<double*> m_rhocj;
   Kokkos::View<double*> m_Vj_over_cj;
 
-public:
+ public:
   AcousticSolver(MeshData& mesh_data,
-		 UnknownsType& unknowns,
-		 const std::vector<BoundaryConditionHandler>& bc_list)
-    : m_mesh_data(mesh_data),
-      m_mesh(mesh_data.mesh()),
-      m_connectivity(m_mesh.connectivity()),
-      m_boundary_condition_list(bc_list),
-      m_br("br", m_mesh.numberOfNodes()),
-      m_Ajr("Ajr", m_mesh.numberOfCells(), m_connectivity.maxNbNodePerCell()),
-      m_Ar("Ar", m_mesh.numberOfNodes()),
-      m_inv_Ar("inv_Ar", m_mesh.numberOfNodes()),
-      m_Fjr("Fjr", m_mesh.numberOfCells(), m_connectivity.maxNbNodePerCell()),
-      m_ur("ur", m_mesh.numberOfNodes()),
-      m_rhocj("rho_c", m_mesh.numberOfCells()),
-      m_Vj_over_cj("Vj_over_cj", m_mesh.numberOfCells())
+                 UnknownsType& unknowns,
+                 const std::vector<BoundaryConditionHandler>& bc_list)
+      : m_mesh_data(mesh_data),
+        m_mesh(mesh_data.mesh()),
+        m_connectivity(m_mesh.connectivity()),
+        m_boundary_condition_list(bc_list),
+        m_br("br", m_mesh.numberOfNodes()),
+        m_Ajr(m_connectivity),
+        m_Ar("Ar", m_mesh.numberOfNodes()),
+        m_inv_Ar("inv_Ar", m_mesh.numberOfNodes()),
+        m_Fjr(m_connectivity),
+        m_ur("ur", m_mesh.numberOfNodes()),
+        m_rhocj("rho_c", m_mesh.numberOfCells()),
+        m_Vj_over_cj("Vj_over_cj", m_mesh.numberOfCells())
   {
     ;
   }
 
   KOKKOS_INLINE_FUNCTION
   double acoustic_dt(const Kokkos::View<const double*>& Vj,
-		     const Kokkos::View<const double*>& cj) const {
-    const Kokkos::View<const double**> ljr = m_mesh_data.ljr();
-    const Kokkos::View<const unsigned short*>& cell_nb_nodes
-      = m_connectivity.cellNbNodes();
+                     const Kokkos::View<const double*>& cj) const
+  {
+    const NodeValuePerCell<double>& ljr = m_mesh_data.ljr();
+    const auto& cell_to_node_matrix
+        = m_mesh.connectivity().getMatrix(ItemType::cell,
+                                          ItemType::node);
 
     Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j){
-	double S = 0;
-	for (int r=0; r<cell_nb_nodes(j); ++r) {
-	  S += ljr(j,r);
-	}
-	m_Vj_over_cj[j] = 2*Vj[j]/(S*cj[j]);
+        const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+
+        double S = 0;
+        for (size_t r=0; r<cell_nodes.length; ++r) {
+          S += ljr(j,r);
+        }
+        m_Vj_over_cj[j] = 2*Vj[j]/(S*cj[j]);
       });
 
     double dt = std::numeric_limits<double>::max();
@@ -318,7 +312,7 @@ public:
 
 
   void computeNextStep(const double& t, const double& dt,
-		       UnknownsType& unknowns)
+                       UnknownsType& unknowns)
   {
     Kokkos::View<double*> rhoj = unknowns.rhoj();
     Kokkos::View<Rd*> uj = unknowns.uj();
@@ -331,46 +325,47 @@ public:
 
     const Kokkos::View<const Rd*> xj = m_mesh_data.xj();
     const Kokkos::View<const double*> Vj = m_mesh_data.Vj();
-    const Kokkos::View<const Rd**> Cjr = m_mesh_data.Cjr();
-    const Kokkos::View<const double**> ljr = m_mesh_data.ljr();
-    const Kokkos::View<const Rd**> njr = m_mesh_data.njr();
+    const NodeValuePerCell<Rd>& Cjr = m_mesh_data.Cjr();
+    const NodeValuePerCell<double>& ljr = m_mesh_data.ljr();
+    const NodeValuePerCell<Rd>& njr = m_mesh_data.njr();
     Kokkos::View<Rd*> xr = m_mesh.xr();
 
     computeExplicitFluxes(xr, xj, rhoj, uj, pj, cj, Vj, Cjr, ljr, njr);
 
-    const Kokkos::View<const Rd**> Fjr = m_Fjr;
+    const NodeValuePerCell<Rd>& Fjr = m_Fjr;
     const Kokkos::View<const Rd*> ur = m_ur;
-    const Kokkos::View<const unsigned short*> cell_nb_nodes
-      = m_connectivity.cellNbNodes();
-    const Kokkos::View<const unsigned int**>& cell_nodes
-      = m_connectivity.cellNodes();
+    const auto& cell_to_node_matrix
+        = m_mesh.connectivity().getMatrix(ItemType::cell,
+                                          ItemType::node);
 
     const Kokkos::View<const double*> inv_mj = unknowns.invMj();
     Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j) {
-	Rd momentum_fluxes = zero;
-	double energy_fluxes = 0;
-	for (int R=0; R<cell_nb_nodes[j]; ++R) {
-	  const int r=cell_nodes(j,R);
-	  momentum_fluxes +=  Fjr(j,R);
-	  energy_fluxes   += (Fjr(j,R), ur[r]);
-	}
-	uj[j] -= (dt*inv_mj[j]) * momentum_fluxes;
-	Ej[j] -= (dt*inv_mj[j]) * energy_fluxes;
+        const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
+
+        Rd momentum_fluxes = zero;
+        double energy_fluxes = 0;
+        for (size_t R=0; R<cell_nodes.length; ++R) {
+          const unsigned int r=cell_nodes(R);
+          momentum_fluxes +=  Fjr(j,R);
+          energy_fluxes   += (Fjr(j,R), ur[r]);
+        }
+        uj[j] -= (dt*inv_mj[j]) * momentum_fluxes;
+        Ej[j] -= (dt*inv_mj[j]) * energy_fluxes;
       });
 
     Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j) {
-	ej[j] = Ej[j] - 0.5 * (uj[j],uj[j]);
+        ej[j] = Ej[j] - 0.5 * (uj[j],uj[j]);
       });
 
     Kokkos::parallel_for(m_mesh.numberOfNodes(), KOKKOS_LAMBDA(const int& r){
-	xr[r] += dt*ur[r];
+        xr[r] += dt*ur[r];
       });
 
     m_mesh_data.updateAllData();
 
     const Kokkos::View<const double*> mj = unknowns.mj();
     Kokkos::parallel_for(m_mesh.numberOfCells(), KOKKOS_LAMBDA(const int& j){
-	rhoj[j] = mj[j]/Vj[j];
+        rhoj[j] = mj[j]/Vj[j];
       });
   }
 };
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 2c1ac56dc2e5ba1886c5efc79709ea6f01deecd4..3b4823f1eaa5d56c7bb2a7e7201d623e8308d60a 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -4,6 +4,7 @@ set(EXECUTABLE_OUTPUT_PATH ${PASTIS_BINARY_DIR})
 
 add_executable (unit_tests
   test_main.cpp
+  test_ItemType.cpp
   test_PastisAssert.cpp
   test_RevisionInfo.cpp
   test_TinyMatrix.cpp
diff --git a/tests/test_ItemType.cpp b/tests/test_ItemType.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7c39963613dd3bae8d60e5fb4c9a4b0915ce6343
--- /dev/null
+++ b/tests/test_ItemType.cpp
@@ -0,0 +1,48 @@
+#include <catch.hpp>
+
+#include <ItemType.hpp>
+
+TEST_CASE("ItemType", "[connectivity]") {
+
+  ItemType node_type = ItemType::node;
+  ItemType edge_type = ItemType::edge;
+  ItemType face_type = ItemType::face;
+  ItemType cell_type = ItemType::cell;
+
+  SECTION("checking for item type differences") {
+    REQUIRE(((node_type != edge_type) and
+             (node_type != face_type) and
+             (node_type != cell_type) and
+             (edge_type != face_type) and
+             (edge_type != cell_type) and
+             (face_type != cell_type)));
+  }
+
+  SECTION("checking for item type names") {
+    REQUIRE(itemName(node_type)=="node");
+    REQUIRE(itemName(edge_type)=="edge");
+    REQUIRE(itemName(face_type)=="face");
+    REQUIRE(itemName(cell_type)=="cell");
+  }
+
+  SECTION("checking for item ids in 1d") {
+    REQUIRE(ItemId<1>::itemId(cell_type)==0);
+    REQUIRE(ItemId<1>::itemId(edge_type)==1);
+    REQUIRE(ItemId<1>::itemId(face_type)==1);
+    REQUIRE(ItemId<1>::itemId(node_type)==1);
+  }
+
+  SECTION("checking for item ids in 2d") {
+    REQUIRE(ItemId<2>::itemId(cell_type)==0);
+    REQUIRE(ItemId<2>::itemId(edge_type)==1);
+    REQUIRE(ItemId<2>::itemId(face_type)==1);
+    REQUIRE(ItemId<2>::itemId(node_type)==2);
+  }
+
+  SECTION("checking for item ids in 3d") {
+    REQUIRE(ItemId<3>::itemId(cell_type)==0);
+    REQUIRE(ItemId<3>::itemId(edge_type)==1);
+    REQUIRE(ItemId<3>::itemId(face_type)==2);
+    REQUIRE(ItemId<3>::itemId(node_type)==3);
+  }
+}
diff --git a/tests/test_PastisAssert.cpp b/tests/test_PastisAssert.cpp
index 8d07321a113be496a220cbfe62934248c3c8907b..d55ad8d826d75ac10e81b5b110495e74d131e921 100644
--- a/tests/test_PastisAssert.cpp
+++ b/tests/test_PastisAssert.cpp
@@ -3,7 +3,7 @@
 #include <PastisAssert.hpp>
 #include <string>
 
-TEST_CASE("PastisAssert", "[assert]") {
+TEST_CASE("PastisAssert", "[utils]") {
   SECTION("checking for assert error") {
     const std::string filename = "filename";
     const int line = 10;