diff --git a/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp b/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp
index 1c4b2b9f01b66deff2c33140e412fb08c2a7c53c..bac8b5c544879688f081d8da0e09e08503d4f5ff 100644
--- a/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp
+++ b/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp
@@ -18,7 +18,7 @@ ItemArrayVariantFunctionInterpoler::_interpolate() const
 {
   std::shared_ptr p_mesh     = m_mesh_v->get<MeshType>();
   constexpr size_t Dimension = MeshType::Dimension;
-  using MeshDataType         = MeshData<Dimension>;
+  using MeshDataType         = MeshData<MeshType>;
 
   switch (m_item_type) {
   case ItemType::cell: {
diff --git a/src/language/utils/ItemValueVariantFunctionInterpoler.cpp b/src/language/utils/ItemValueVariantFunctionInterpoler.cpp
index 74d07b577f2b168e1f3fe50930aa24ea45dd4d1d..be073b907e1412c0de2445f2fa098af4cc83eb41 100644
--- a/src/language/utils/ItemValueVariantFunctionInterpoler.cpp
+++ b/src/language/utils/ItemValueVariantFunctionInterpoler.cpp
@@ -18,7 +18,7 @@ ItemValueVariantFunctionInterpoler::_interpolate() const
 {
   std::shared_ptr p_mesh     = m_mesh_v->get<MeshType>();
   constexpr size_t Dimension = MeshType::Dimension;
-  using MeshDataType         = MeshData<Dimension>;
+  using MeshDataType         = MeshData<MeshType>;
 
   switch (m_item_type) {
   case ItemType::cell: {
diff --git a/src/mesh/DiamondDualMeshBuilder.cpp b/src/mesh/DiamondDualMeshBuilder.cpp
index f6ecba946595e352a9a962fe1039aa8121aab88b..43f5528a294f6f7b6912df989a9d0c064b128dcc 100644
--- a/src/mesh/DiamondDualMeshBuilder.cpp
+++ b/src/mesh/DiamondDualMeshBuilder.cpp
@@ -29,7 +29,7 @@ DiamondDualMeshBuilder::_buildDualDiamondMeshFrom(const MeshType& primal_mesh)
 
   const NodeValue<const TinyVector<Dimension>> primal_xr = primal_mesh.xr();
 
-  MeshData<Dimension>& primal_mesh_data                  = MeshDataManager::instance().getMeshData(primal_mesh);
+  MeshData<MeshType>& primal_mesh_data                   = MeshDataManager::instance().getMeshData(primal_mesh);
   const CellValue<const TinyVector<Dimension>> primal_xj = primal_mesh_data.xj();
 
   std::shared_ptr primal_to_diamond_dual_connectivity_data_mapper =
diff --git a/src/mesh/Dual1DMeshBuilder.cpp b/src/mesh/Dual1DMeshBuilder.cpp
index c861a8bf95db581c6e061eb3b613a4a8d4a6df7b..b39cd000cd62c71817bdc1236dec441115504d65 100644
--- a/src/mesh/Dual1DMeshBuilder.cpp
+++ b/src/mesh/Dual1DMeshBuilder.cpp
@@ -29,7 +29,7 @@ Dual1DMeshBuilder::_buildDual1DMeshFrom(const MeshType& primal_mesh)
 
   const NodeValue<const TinyVector<1>> primal_xr = primal_mesh.xr();
 
-  MeshData<1>& primal_mesh_data                  = MeshDataManager::instance().getMeshData(primal_mesh);
+  MeshData<MeshType>& primal_mesh_data           = MeshDataManager::instance().getMeshData(primal_mesh);
   const CellValue<const TinyVector<1>> primal_xj = primal_mesh_data.xj();
 
   std::shared_ptr primal_to_dual_1d_connectivity_data_mapper =
diff --git a/src/mesh/MedianDualMeshBuilder.cpp b/src/mesh/MedianDualMeshBuilder.cpp
index 5a22cf0b2bb95c295834f2a8e88a5ee60457f82a..411005ec94aff3b1c2f19f56054671ab03e070b2 100644
--- a/src/mesh/MedianDualMeshBuilder.cpp
+++ b/src/mesh/MedianDualMeshBuilder.cpp
@@ -29,7 +29,7 @@ MedianDualMeshBuilder::_buildMedianDualMeshFrom(const MeshType& primal_mesh)
 
   const NodeValue<const TinyVector<Dimension>> primal_xr = primal_mesh.xr();
 
-  MeshData<Dimension>& primal_mesh_data                  = MeshDataManager::instance().getMeshData(primal_mesh);
+  MeshData<MeshType>& primal_mesh_data                   = MeshDataManager::instance().getMeshData(primal_mesh);
   const CellValue<const TinyVector<Dimension>> primal_xj = primal_mesh_data.xj();
   const FaceValue<const TinyVector<Dimension>> primal_xl = primal_mesh_data.xl();
 
diff --git a/src/mesh/MeshData.cpp b/src/mesh/MeshData.cpp
index 30ef6a0156e9a4f98cc77d491da63c7100408cab..40e3878624b1ae84d1923063df6bc6b707926104 100644
--- a/src/mesh/MeshData.cpp
+++ b/src/mesh/MeshData.cpp
@@ -10,11 +10,11 @@
 
 template <size_t Dimension>
 void
-MeshData<Dimension>::_storeBadMesh()
+MeshData<Mesh<Dimension>>::_storeBadMesh()
 {
   VTKWriter writer("bad_mesh");
   writer.writeOnMesh(std::make_shared<MeshVariant>(
-                       std::make_shared<const MeshType>(m_mesh.shared_connectivity(), m_mesh.xr())),
+                       std::make_shared<const Mesh<Dimension>>(m_mesh.shared_connectivity(), m_mesh.xr())),
                      {std::make_shared<NamedItemValueVariant>(std::make_shared<ItemValueVariant>(m_Vj), "volume")});
   std::ostringstream error_msg;
   error_msg << "mesh contains cells of non-positive volume (see " << rang::fgB::yellow << "bad_mesh.pvd"
@@ -22,6 +22,6 @@ MeshData<Dimension>::_storeBadMesh()
   throw NormalError(error_msg.str());
 }
 
-template void MeshData<1>::_storeBadMesh();
-template void MeshData<2>::_storeBadMesh();
-template void MeshData<3>::_storeBadMesh();
+template void MeshData<Mesh<1>>::_storeBadMesh();
+template void MeshData<Mesh<2>>::_storeBadMesh();
+template void MeshData<Mesh<3>>::_storeBadMesh();
diff --git a/src/mesh/MeshData.hpp b/src/mesh/MeshData.hpp
index a198aab4526215b1e88f25311a4d77de15513d1d..d5eba5f5df0e29f388b3781bc8c40a84729ae46d 100644
--- a/src/mesh/MeshData.hpp
+++ b/src/mesh/MeshData.hpp
@@ -4,6 +4,7 @@
 #include <algebra/TinyVector.hpp>
 #include <mesh/IMeshData.hpp>
 #include <mesh/ItemValue.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <mesh/SubItemValuePerItem.hpp>
 #include <utils/Messenger.hpp>
 #include <utils/PugsUtils.hpp>
@@ -11,15 +12,18 @@
 template <size_t Dimension>
 class Mesh;
 
+template <MeshConcept MeshType>
+class MeshData;
+
 template <size_t Dimension>
-class MeshData : public IMeshData
+class MeshData<Mesh<Dimension>> : public IMeshData
 {
  public:
+  using MeshType = Mesh<Dimension>;
+
   static_assert(Dimension > 0, "dimension must be strictly positive");
   static_assert((Dimension <= 3), "only 1d, 2d and 3d are implemented");
 
-  using MeshType = Mesh<Dimension>;
-
   using Rd = TinyVector<Dimension>;
 
   static constexpr double inv_Dimension = 1. / Dimension;
diff --git a/src/mesh/MeshDataManager.cpp b/src/mesh/MeshDataManager.cpp
index 3607fcad6b476f7b48c922891722b11a639d84a7..4eb8f5677c6fae70fb3df70ade91e52a538691c1 100644
--- a/src/mesh/MeshDataManager.cpp
+++ b/src/mesh/MeshDataManager.cpp
@@ -40,21 +40,21 @@ MeshDataManager::deleteMeshData(const size_t mesh_id)
   m_mesh_id_mesh_data_map.erase(mesh_id);
 }
 
-template <size_t Dimension>
-MeshData<Dimension>&
-MeshDataManager::getMeshData(const Mesh<Dimension>& mesh)
+template <MeshConcept MeshType>
+MeshData<MeshType>&
+MeshDataManager::getMeshData(const MeshType& mesh)
 {
   if (auto i_mesh_data = m_mesh_id_mesh_data_map.find(mesh.id()); i_mesh_data != m_mesh_id_mesh_data_map.end()) {
-    return dynamic_cast<MeshData<Dimension>&>(*i_mesh_data->second);
+    return dynamic_cast<MeshData<MeshType>&>(*i_mesh_data->second);
   } else {
     // **cannot** use make_shared since MeshData constructor is **private**
-    std::shared_ptr<MeshData<Dimension>> mesh_data{new MeshData<Dimension>(mesh)};
+    std::shared_ptr<MeshData<MeshType>> mesh_data{new MeshData<MeshType>(mesh)};
 
     m_mesh_id_mesh_data_map[mesh.id()] = mesh_data;
     return *mesh_data;
   }
 }
 
-template MeshData<1>& MeshDataManager::getMeshData(const Mesh<1>&);
-template MeshData<2>& MeshDataManager::getMeshData(const Mesh<2>&);
-template MeshData<3>& MeshDataManager::getMeshData(const Mesh<3>&);
+template MeshData<Mesh<1>>& MeshDataManager::getMeshData(const Mesh<1>&);
+template MeshData<Mesh<2>>& MeshDataManager::getMeshData(const Mesh<2>&);
+template MeshData<Mesh<3>>& MeshDataManager::getMeshData(const Mesh<3>&);
diff --git a/src/mesh/MeshDataManager.hpp b/src/mesh/MeshDataManager.hpp
index 0d2bf964b02daf341bbcd6e1b30c9cfe8bfee64e..b47756c01afe4a4ead97fba7e9f8543018989b31 100644
--- a/src/mesh/MeshDataManager.hpp
+++ b/src/mesh/MeshDataManager.hpp
@@ -2,6 +2,7 @@
 #define MESH_DATA_MANAGER_HPP
 
 #include <mesh/IMeshData.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <utils/PugsAssert.hpp>
 #include <utils/PugsMacros.hpp>
 
@@ -11,7 +12,7 @@
 template <size_t Dimension>
 class Mesh;
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 class MeshData;
 
 class MeshDataManager
@@ -41,8 +42,8 @@ class MeshDataManager
 
   void deleteMeshData(const size_t mesh_id);
 
-  template <size_t Dimension>
-  MeshData<Dimension>& getMeshData(const Mesh<Dimension>&);
+  template <MeshConcept MeshType>
+  MeshData<MeshType>& getMeshData(const MeshType&);
 };
 
 #endif   // MESH_DATA_MANAGER_HPP
diff --git a/src/scheme/AcousticSolver.cpp b/src/scheme/AcousticSolver.cpp
index 9f00bf12b15fcbee0caa6c225aa204738a4a755b..43476df571d4ac14e7fcfb8e6fd21593d6049896 100644
--- a/src/scheme/AcousticSolver.cpp
+++ b/src/scheme/AcousticSolver.cpp
@@ -57,7 +57,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
   using Rdxd = TinyMatrix<Dimension>;
   using Rd   = TinyVector<Dimension>;
 
-  using MeshDataType = MeshData<Dimension>;
+  using MeshDataType = MeshData<MeshType>;
 
   using DiscreteScalarFunction = DiscreteFunctionP0<const double>;
   using DiscreteVectorFunction = DiscreteFunctionP0<const Rd>;
@@ -525,7 +525,7 @@ AcousticSolverHandler::AcousticSolver<MeshType>::_applyPressureBC(const Boundary
       [&](auto&& bc) {
         using T = std::decay_t<decltype(bc)>;
         if constexpr (std::is_same_v<PressureBoundaryCondition, T>) {
-          MeshData<Dimension>& mesh_data = MeshDataManager::instance().getMeshData(mesh);
+          MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(mesh);
           if constexpr (Dimension == 1) {
             const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr();
 
diff --git a/src/scheme/DiscreteFunctionInterpoler.cpp b/src/scheme/DiscreteFunctionInterpoler.cpp
index 6c5228a91b9b1d9aeaf7a4eb2ec3e7d91bcf2ea1..f8e5a58ca5dd4c736fff870e9d58468535ef4325 100644
--- a/src/scheme/DiscreteFunctionInterpoler.cpp
+++ b/src/scheme/DiscreteFunctionInterpoler.cpp
@@ -17,9 +17,8 @@ DiscreteFunctionInterpoler::_interpolateOnZoneList() const
 
   constexpr size_t Dimension = MeshType::Dimension;
 
-  std::shared_ptr p_mesh  = m_mesh->get<MeshType>();
-  using MeshDataType      = MeshData<Dimension>;
-  MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
+  std::shared_ptr p_mesh        = m_mesh->get<MeshType>();
+  MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
 
   CellValue<bool> is_in_zone{p_mesh->connectivity()};
   is_in_zone.fill(false);
@@ -76,9 +75,8 @@ DiscreteFunctionInterpoler::_interpolateGlobally() const
   Assert(m_zone_list.size() == 0, "invalid call when zones are defined");
   constexpr size_t Dimension = MeshType::Dimension;
 
-  std::shared_ptr p_mesh  = m_mesh->get<MeshType>();
-  using MeshDataType      = MeshData<Dimension>;
-  MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
+  std::shared_ptr p_mesh        = m_mesh->get<MeshType>();
+  MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
 
   if constexpr (std::is_same_v<DataType, ValueType>) {
     return DiscreteFunctionP0<ValueType>(m_mesh,
diff --git a/src/scheme/DiscreteFunctionVectorInterpoler.cpp b/src/scheme/DiscreteFunctionVectorInterpoler.cpp
index 547709c4561be569d63377956cfce75578c56930..fffead074b14f08acff58d83a29d8a356ffb8801 100644
--- a/src/scheme/DiscreteFunctionVectorInterpoler.cpp
+++ b/src/scheme/DiscreteFunctionVectorInterpoler.cpp
@@ -14,9 +14,8 @@ DiscreteFunctionVectorInterpoler::_interpolateOnZoneList() const
   Assert(m_zone_list.size() > 0, "no zone list provided");
   constexpr size_t Dimension = MeshType::Dimension;
 
-  std::shared_ptr p_mesh  = m_mesh->get<MeshType>();
-  using MeshDataType      = MeshData<Dimension>;
-  MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
+  std::shared_ptr p_mesh        = m_mesh->get<MeshType>();
+  MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
 
   CellValue<bool> is_in_zone{p_mesh->connectivity()};
   is_in_zone.fill(false);
@@ -75,8 +74,7 @@ DiscreteFunctionVectorInterpoler::_interpolateGlobally() const
 
   std::shared_ptr p_mesh = m_mesh->get<MeshType>();
 
-  using MeshDataType      = MeshData<Dimension>;
-  MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
+  MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
 
   return DiscreteFunctionP0Vector<DataType>(m_mesh,
                                             InterpolateItemArray<DataType(TinyVector<Dimension>)>::template interpolate<
diff --git a/src/scheme/FluxingAdvectionSolver.cpp b/src/scheme/FluxingAdvectionSolver.cpp
index e1bf11a8be958ab9ae30c284615011450541c53e..509156b1ecbb8eadc3de8c21d69b1d1faa5a9402 100644
--- a/src/scheme/FluxingAdvectionSolver.cpp
+++ b/src/scheme/FluxingAdvectionSolver.cpp
@@ -33,8 +33,7 @@ class FluxingAdvectionSolver
  private:
   static constexpr size_t Dimension = MeshType::Dimension;
 
-  using Rd           = TinyVector<Dimension>;
-  using MeshDataType = MeshData<Dimension>;
+  using Rd = TinyVector<Dimension>;
 
   const std::shared_ptr<const MeshType> m_old_mesh;
   const std::shared_ptr<const MeshType> m_new_mesh;
@@ -421,7 +420,7 @@ FluxingAdvectionSolver<MeshType>::_computeCycleNumber(FaceValue<double> fluxing_
       }
     });
 
-  MeshData<Dimension>& mesh_data   = MeshDataManager::instance().getMeshData(*m_old_mesh);
+  MeshData<MeshType>& mesh_data    = MeshDataManager::instance().getMeshData(*m_old_mesh);
   const CellValue<const double> Vj = mesh_data.Vj();
   CellValue<size_t> ratio(m_old_mesh->connectivity());
 
@@ -641,7 +640,7 @@ FluxingAdvectionSolver<MeshType>::_remapAllQuantities()
   const auto cell_to_face_matrix              = m_new_mesh->connectivity().cellToFaceMatrix();
   const auto face_local_number_in_their_cells = m_new_mesh->connectivity().faceLocalNumbersInTheirCells();
 
-  MeshData<Dimension>& old_mesh_data = MeshDataManager::instance().getMeshData(*m_old_mesh);
+  MeshData<MeshType>& old_mesh_data = MeshDataManager::instance().getMeshData(*m_old_mesh);
 
   const CellValue<const double> old_Vj = old_mesh_data.Vj();
   const CellValue<double> step_Vj      = copy(old_Vj);
diff --git a/src/scheme/HyperelasticSolver.cpp b/src/scheme/HyperelasticSolver.cpp
index 4fc3918850b19b2ab404d20b222fc3132dc1d6df..5309385ecb210c20dec2995c3b6a603a21ca40a7 100644
--- a/src/scheme/HyperelasticSolver.cpp
+++ b/src/scheme/HyperelasticSolver.cpp
@@ -57,7 +57,7 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
   using Rdxd = TinyMatrix<Dimension>;
   using Rd   = TinyVector<Dimension>;
 
-  using MeshDataType = MeshData<Dimension>;
+  using MeshDataType = MeshData<MeshType>;
 
   using DiscreteScalarFunction = DiscreteFunctionP0<const double>;
   using DiscreteVectorFunction = DiscreteFunctionP0<const Rd>;
@@ -560,7 +560,7 @@ HyperelasticSolverHandler::HyperelasticSolver<MeshType>::_applyPressureBC(const
       [&](auto&& bc) {
         using T = std::decay_t<decltype(bc)>;
         if constexpr (std::is_same_v<PressureBoundaryCondition, T>) {
-          MeshData<Dimension>& mesh_data = MeshDataManager::instance().getMeshData(mesh);
+          MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh);
           if constexpr (Dimension == 1) {
             const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr();
 
@@ -625,7 +625,7 @@ HyperelasticSolverHandler::HyperelasticSolver<MeshType>::_applyNormalStressBC(co
       [&](auto&& bc) {
         using T = std::decay_t<decltype(bc)>;
         if constexpr (std::is_same_v<NormalStressBoundaryCondition, T>) {
-          MeshData<Dimension>& mesh_data = MeshDataManager::instance().getMeshData(mesh);
+          MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh);
           if constexpr (Dimension == 1) {
             const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr();