diff --git a/src/language/modules/CMakeLists.txt b/src/language/modules/CMakeLists.txt
index 1ecbe1db54c85a4282ed1a55a043115f09bc7525..b94d4249c0bc2d013d62384d7329e9914feccf84 100644
--- a/src/language/modules/CMakeLists.txt
+++ b/src/language/modules/CMakeLists.txt
@@ -20,6 +20,7 @@ add_library(
 target_link_libraries(
   PugsLanguageModules
   ${HIGHFIVE_TARGET}
+  PugsScheme
 )
 
 add_dependencies(
@@ -27,5 +28,6 @@ add_dependencies(
   PugsLanguageModules
   PugsLanguageAlgorithms
   PugsUtils
+  PugsScheme
   PugsMesh
 )
diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index d9c6f0d141d0d3ed5b259e673c70dbad56f4061e..ef3e5dbc9dc6e501e185aface5817480b94f4ac5 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -36,7 +36,6 @@
 #include <scheme/DiscreteFunctionVectorIntegrator.hpp>
 #include <scheme/DiscreteFunctionVectorInterpoler.hpp>
 #include <scheme/EulerKineticSolver.hpp>
-#include <scheme/EulerKineticSolver2D.hpp>
 #include <scheme/EulerKineticSolverAcoustic2LagrangeFVOrder2.hpp>
 #include <scheme/EulerKineticSolverAcousticLagrangeFV.hpp>
 #include <scheme/EulerKineticSolverFirstOrderFV.hpp>
@@ -44,6 +43,7 @@
 #include <scheme/EulerKineticSolverMeanFluxMood.hpp>
 #include <scheme/EulerKineticSolverMoodFD.hpp>
 #include <scheme/EulerKineticSolverMoodFV.hpp>
+#include <scheme/EulerKineticSolverMultiD.hpp>
 #include <scheme/EulerKineticSolverOneFluxMood.hpp>
 #include <scheme/EulerKineticSolverThirdOrderFD.hpp>
 #include <scheme/EulerKineticSolverThirdOrderFV.hpp>
@@ -956,47 +956,62 @@ SchemeModule::SchemeModule()
 
                                           ));
 
-  this->_addBuiltinFunction("euler_kinetic_2D",
+  this->_addBuiltinFunction("euler_kinetic_MultiD",
                             std::function(
 
                               [](const double& dt, const double& gamma, const std::vector<TinyVector<2>>& lambda_vector,
                                  const double& eps, const size_t& SpaceOrder,
                                  const std::shared_ptr<const DiscreteFunctionVariant>& F_rho,
-                                 const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_u1,
-                                 const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_u2,
+                                 const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_u,
                                  const std::shared_ptr<const DiscreteFunctionVariant>& F_E,
                                  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                    bc_descriptor_list) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                      std::shared_ptr<const DiscreteFunctionVariant>,
+                                                                     std::shared_ptr<const DiscreteFunctionVariant>> {
+                                return eulerKineticSolverMultiD(dt, gamma, lambda_vector, eps, SpaceOrder, F_rho,
+                                                                F_rho_u, F_E, bc_descriptor_list);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("euler_kinetic_MultiD",
+                            std::function(
+
+                              [](const double& dt, const double& gamma, const std::vector<TinyVector<3>>& lambda_vector,
+                                 const double& eps, const size_t& SpaceOrder,
+                                 const std::shared_ptr<const DiscreteFunctionVariant>& F_rho,
+                                 const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_u,
+                                 const std::shared_ptr<const DiscreteFunctionVariant>& F_E,
+                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
+                                   bc_descriptor_list) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                      std::shared_ptr<const DiscreteFunctionVariant>,
                                                                      std::shared_ptr<const DiscreteFunctionVariant>> {
-                                return eulerKineticSolver2D(dt, gamma, lambda_vector, eps, SpaceOrder, F_rho, F_rho_u1,
-                                                            F_rho_u2, F_E, bc_descriptor_list);
+                                return eulerKineticSolverMultiD(dt, gamma, lambda_vector, eps, SpaceOrder, F_rho,
+                                                                F_rho_u, F_E, bc_descriptor_list);
                               }
 
                               ));
 
-  this->_addBuiltinFunction("get_lambda_vector",
+  this->_addBuiltinFunction("get_lambda_vector_2d",
                             std::function(
 
                               [](const std::shared_ptr<const MeshVariant>& mesh, const double& lambda, const size_t& L,
                                  const size_t& M) -> std::vector<TinyVector<2>> {
-                                return getLambdaVector(mesh, lambda, L, M);
+                                return getLambdaVector2D(mesh, lambda, L, M);
                               }
 
                               ));
 
-  this->_addBuiltinFunction("get_velocity", std::function(
+  this->_addBuiltinFunction("get_lambda_vector_3d", std::function(
 
-                                              [](const std::shared_ptr<const DiscreteFunctionVariant>& Fn_rho_u1,
-                                                 const std::shared_ptr<const DiscreteFunctionVariant>& Fn_rho_u2)
-                                                -> std::shared_ptr<const DiscreteFunctionVariant> {
-                                                return getVelocity(Fn_rho_u1, Fn_rho_u2);
-                                              }
+                                                      [](const std::shared_ptr<const MeshVariant>& mesh,
+                                                         const double& lambda) -> std::vector<TinyVector<3>> {
+                                                        return getLambdaVector3D(mesh, lambda);
+                                                      }
 
-                                              ));
+                                                      ));
 
-  this->_addBuiltinFunction("get_euler_kinetic_waves_2D",
+  this->_addBuiltinFunction("get_euler_kinetic_waves_MultiD",
                             std::function(
 
                               [](const std::vector<TinyVector<2>>& lambda_vector,
@@ -1005,9 +1020,23 @@ SchemeModule::SchemeModule()
                                  const std::shared_ptr<const DiscreteFunctionVariant>& rho_E,
                                  const double& gamma) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                     std::shared_ptr<const DiscreteFunctionVariant>,
+                                                                    std::shared_ptr<const DiscreteFunctionVariant>> {
+                                return getEulerKineticWavesMultiD(lambda_vector, rho, rho_u, rho_E, gamma);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("get_euler_kinetic_waves_MultiD",
+                            std::function(
+
+                              [](const std::vector<TinyVector<3>>& lambda_vector,
+                                 const std::shared_ptr<const DiscreteFunctionVariant>& rho,
+                                 const std::shared_ptr<const DiscreteFunctionVariant>& rho_u,
+                                 const std::shared_ptr<const DiscreteFunctionVariant>& rho_E,
+                                 const double& gamma) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                     std::shared_ptr<const DiscreteFunctionVariant>,
                                                                     std::shared_ptr<const DiscreteFunctionVariant>> {
-                                return getEulerKineticWaves2D(lambda_vector, rho, rho_u, rho_E, gamma);
+                                return getEulerKineticWavesMultiD(lambda_vector, rho, rho_u, rho_E, gamma);
                               }
 
                               ));
diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt
index e26e157ec3c145d4d53397859a4ff6f4de1e0c3f..e243b52496e81225803fe7ae28bd320ff44748be 100644
--- a/src/scheme/CMakeLists.txt
+++ b/src/scheme/CMakeLists.txt
@@ -21,7 +21,7 @@ add_library(
   EulerKineticSolverOneFluxMood.cpp
   EulerKineticSolverMoodFD.cpp
   EulerKineticSolverMoodFV.cpp
-  EulerKineticSolver2D.cpp
+  EulerKineticSolverMultiD.cpp
   EulerKineticSolverThirdOrderMoodFV.cpp
   EulerKineticSolverThirdOrderMoodFD.cpp
   EulerKineticSolverThirdOrderFV.cpp
diff --git a/src/scheme/EulerKineticSolver2D.hpp b/src/scheme/EulerKineticSolver2D.hpp
deleted file mode 100644
index a7937ee7ae067059b671d10b05674e109b7d9b88..0000000000000000000000000000000000000000
--- a/src/scheme/EulerKineticSolver2D.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef EULER_KINETIC_SOLVER_2D_HPP
-#define EULER_KINETIC_SOLVER_2D_HPP
-
-#include <language/utils/FunctionSymbolId.hpp>
-#include <scheme/DiscreteFunctionVariant.hpp>
-
-class IBoundaryConditionDescriptor;
-
-std::vector<TinyVector<2>> getLambdaVector(const std::shared_ptr<const MeshVariant>& mesh,
-                                           const double& lambda,
-                                           const size_t& L,
-                                           const size_t& M);
-
-std::shared_ptr<const DiscreteFunctionVariant> getVelocity(
-  const std::shared_ptr<const DiscreteFunctionVariant>& Fn_rho_u1,
-  const std::shared_ptr<const DiscreteFunctionVariant>& Fn_rho_u2);
-
-std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
-           std::shared_ptr<const DiscreteFunctionVariant>,
-           std::shared_ptr<const DiscreteFunctionVariant>,
-           std::shared_ptr<const DiscreteFunctionVariant>>
-getEulerKineticWaves2D(const std::vector<TinyVector<2>>& lambda_vector,
-                       const std::shared_ptr<const DiscreteFunctionVariant>& rho,
-                       const std::shared_ptr<const DiscreteFunctionVariant>& rho_u,
-                       const std::shared_ptr<const DiscreteFunctionVariant>& rho_E,
-                       const double& gamma);
-
-std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
-           std::shared_ptr<const DiscreteFunctionVariant>,
-           std::shared_ptr<const DiscreteFunctionVariant>,
-           std::shared_ptr<const DiscreteFunctionVariant>>
-eulerKineticSolver2D(const double& dt,
-                     const double& gamma,
-                     const std::vector<TinyVector<2>>& lambda_vector,
-                     const double& eps,
-                     const size_t& SpaceOrder,
-                     const std::shared_ptr<const DiscreteFunctionVariant>& F_rho,
-                     const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_u1,
-                     const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_u2,
-                     const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_E,
-                     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list);
-
-#endif   // EULER_KINETIC_SOLVER_2D_HPP
diff --git a/src/scheme/EulerKineticSolver2D.cpp b/src/scheme/EulerKineticSolverMultiD.cpp
similarity index 69%
rename from src/scheme/EulerKineticSolver2D.cpp
rename to src/scheme/EulerKineticSolverMultiD.cpp
index 8f7f0af61e0d92441db063036041bcecfb9810e8..3ea60272a74641a139f9acfb34174554e770c4bd 100644
--- a/src/scheme/EulerKineticSolver2D.cpp
+++ b/src/scheme/EulerKineticSolverMultiD.cpp
@@ -1,4 +1,4 @@
-#include <scheme/EulerKineticSolver2D.hpp>
+#include <scheme/EulerKineticSolverMultiD.hpp>
 
 #include <language/utils/EvaluateAtPoints.hpp>
 #include <language/utils/InterpolateItemArray.hpp>
@@ -29,7 +29,10 @@
 #include <tuple>
 
 std::vector<TinyVector<2>>
-getLambdaVector(const std::shared_ptr<const MeshVariant>& mesh, const double& lambda, const size_t& L, const size_t& M)
+getLambdaVector2D(const std::shared_ptr<const MeshVariant>& mesh,
+                  const double& lambda,
+                  const size_t& L,
+                  const size_t& M)
 {
   return std::visit(
     [&](auto&& p_mesh) -> std::vector<TinyVector<2>> {
@@ -51,59 +54,46 @@ getLambdaVector(const std::shared_ptr<const MeshVariant>& mesh, const double& la
 
         return lambda_vector;
       } else {
-        throw NotImplementedError("Invalid mesh type for 2D Euler Kinetic solver");
+        throw NotImplementedError("Invalid mesh type for MultiD Euler Kinetic solver");
       }
     },
     mesh->variant());
 }
 
-std::shared_ptr<const DiscreteFunctionVariant>
-getVelocity(const std::shared_ptr<const DiscreteFunctionVariant>& Fn_rho_u1,
-            const std::shared_ptr<const DiscreteFunctionVariant>& Fn_rho_u2)
+std::vector<TinyVector<3>>
+getLambdaVector3D(const std::shared_ptr<const MeshVariant>& mesh, const double& lambda)
 {
-  const std::shared_ptr<const MeshVariant> mesh_v = getCommonMesh({Fn_rho_u1, Fn_rho_u2});
-  if (mesh_v.use_count() == 0) {
-    throw NormalError("Fn_rho_u1 and Fn_rho_u2 have to be defined on the same mesh");
-  }
   return std::visit(
-    [&](auto&& p_mesh) -> std::shared_ptr<const DiscreteFunctionVariant> {
+    [&](auto&& p_mesh) -> std::vector<TinyVector<3>> {
       using MeshType = mesh_type_t<decltype(p_mesh)>;
-      if constexpr (is_polygonal_mesh_v<MeshType> and (MeshType::Dimension == 2)) {
-        auto F_rho_u1 = Fn_rho_u1->get<DiscreteFunctionP0Vector<const double>>();
-        auto F_rho_u2 = Fn_rho_u2->get<DiscreteFunctionP0Vector<const double>>();
-
-        DiscreteFunctionP0<TinyVector<MeshType::Dimension>> rho_u{p_mesh};
-
-        parallel_for(
-          p_mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            rho_u[cell_id][0] = 0;
-            rho_u[cell_id][1] = 0;
-            size_t nb_waves   = F_rho_u1[cell_id].size();
-            for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
-              rho_u[cell_id][0] += F_rho_u1[cell_id][i_wave];
-              rho_u[cell_id][1] += F_rho_u2[cell_id][i_wave];
-            }
-          });
+      if constexpr (is_polygonal_mesh_v<MeshType> and (MeshType::Dimension == 3)) {
+        std::vector<TinyVector<3>> lambda_vector{6};
 
-        return std::make_shared<DiscreteFunctionVariant>(rho_u);
+        lambda_vector[0] = TinyVector<3>{lambda, 0., 0.};
+        lambda_vector[1] = TinyVector<3>{-lambda, 0., 0.};
+        lambda_vector[2] = TinyVector<3>{0., lambda, 0.};
+        lambda_vector[3] = TinyVector<3>{0., -lambda, 0.};
+        lambda_vector[4] = TinyVector<3>{0., 0., lambda};
+        lambda_vector[5] = TinyVector<3>{0., 0., -lambda};
+
+        return lambda_vector;
       } else {
-        throw NotImplementedError("Invalid mesh type for 2D Euler Kinetic solver");
+        throw NotImplementedError("Invalid mesh type for MultiD Euler Kinetic solver");
       }
     },
-    mesh_v->variant());
+    mesh->variant());
 }
 
 template <MeshConcept MeshType>
 std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
-           std::shared_ptr<const DiscreteFunctionVariant>,
            std::shared_ptr<const DiscreteFunctionVariant>,
            std::shared_ptr<const DiscreteFunctionVariant>>
-getEulerKineticWaves2D(const std::shared_ptr<const MeshType> p_mesh,
-                       const std::vector<TinyVector<MeshType::Dimension>> lambda_vector,
-                       DiscreteFunctionP0<const double> rho,
-                       DiscreteFunctionP0<const TinyVector<MeshType::Dimension>> rho_u,
-                       DiscreteFunctionP0<const double> rho_E,
-                       const double& gamma)
+GetEulerKineticWavesMultiD(const std::shared_ptr<const MeshType> p_mesh,
+                           const std::vector<TinyVector<MeshType::Dimension>> lambda_vector,
+                           DiscreteFunctionP0<const double> rho,
+                           DiscreteFunctionP0<const TinyVector<MeshType::Dimension>> rho_u,
+                           DiscreteFunctionP0<const double> rho_E,
+                           const double& gamma)
 {
   if (lambda_vector.size() < 3) {
     throw NormalError("lambda vector must be of dimension greater than 3");
@@ -115,71 +105,67 @@ getEulerKineticWaves2D(const std::shared_ptr<const MeshType> p_mesh,
   const CellValue<const double> m_Vj                          = mesh_data.Vj();
 
   DiscreteFunctionP0Vector<double> Fn_rho(p_mesh, lambda_vector.size());
-  DiscreteFunctionP0Vector<double> Fn_rho_u1(p_mesh, lambda_vector.size());
-  DiscreteFunctionP0Vector<double> Fn_rho_u2(p_mesh, lambda_vector.size());
+  DiscreteFunctionP0Vector<TinyVector<MeshType::Dimension>> Fn_rho_u(p_mesh, lambda_vector.size());
   DiscreteFunctionP0Vector<double> Fn_rho_E(p_mesh, lambda_vector.size());
 
   // const auto& cell_to_node_matrix = p_mesh->connectivity().cellToNodeMatrix();
-  CellArray<TinyVector<MeshType::Dimension>> vec_A{p_mesh->connectivity(), 4};
+  CellValue<TinyVector<MeshType::Dimension>> A_rho{p_mesh->connectivity()};
+  CellValue<TinyMatrix<MeshType::Dimension>> A_rho_u{p_mesh->connectivity()};
+  CellValue<TinyVector<MeshType::Dimension>> A_rho_E{p_mesh->connectivity()};
+
+  const TinyMatrix<MeshType::Dimension> I = identity;
 
   parallel_for(
     p_mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-      double rho_e         = rho_E[cell_id] - 0.5 * (1. / rho[cell_id]) * dot(rho_u[cell_id], rho_u[cell_id]);
-      double p             = (gamma - 1) * rho_e;
-      vec_A[cell_id][0][0] = rho_u[cell_id][0];
-      vec_A[cell_id][1][0] = rho_u[cell_id][0] * rho_u[cell_id][0] / rho[cell_id] + p;
-      vec_A[cell_id][2][0] = rho_u[cell_id][0] * rho_u[cell_id][1] / rho[cell_id];
-      vec_A[cell_id][3][0] = (rho_E[cell_id] + p) * rho_u[cell_id][0] / rho[cell_id];
-
-      vec_A[cell_id][0][1] = rho_u[cell_id][1];
-      vec_A[cell_id][1][1] = rho_u[cell_id][0] * rho_u[cell_id][1] / rho[cell_id];
-      vec_A[cell_id][2][1] = rho_u[cell_id][1] * rho_u[cell_id][1] / rho[cell_id] + p;
-      vec_A[cell_id][3][1] = (rho_E[cell_id] + p) * rho_u[cell_id][1] / rho[cell_id];
+      double rho_e = rho_E[cell_id] - 0.5 * (1. / rho[cell_id]) * dot(rho_u[cell_id], rho_u[cell_id]);
+      double p     = (gamma - 1) * rho_e;
+
+      A_rho[cell_id]   = rho_u[cell_id];
+      A_rho_u[cell_id] = 1. / rho[cell_id] * tensorProduct(rho_u[cell_id], rho_u[cell_id]) + p * I;
+      A_rho_E[cell_id] = (rho_E[cell_id] + p) / rho[cell_id] * rho_u[cell_id];
     });
 
   const size_t nb_waves                 = lambda_vector.size();
   TinyVector<MeshType::Dimension> inv_S = zero;
-  for (size_t i = 0; i < nb_waves; ++i) {
-    for (size_t d = 0; d < MeshType::Dimension; ++d) {
-      inv_S[d] += std::pow(lambda_vector[i][d], 2);
-    }
-  }
+
   for (size_t d = 0; d < MeshType::Dimension; ++d) {
+    for (size_t i = 0; i < nb_waves; ++i) {
+      inv_S[d] += lambda_vector[i][d] * lambda_vector[i][d];
+    }
     inv_S[d] = 1. / inv_S[d];
   }
 
   parallel_for(
     p_mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
       for (size_t i = 0; i < nb_waves; ++i) {
-        Fn_rho[cell_id][i] = (1. / nb_waves) * rho[cell_id] + inv_S[0] * vec_A[cell_id][0][0] * lambda_vector[i][0] +
-                             inv_S[1] * vec_A[cell_id][0][1] * lambda_vector[i][1];
-        Fn_rho_u1[cell_id][i] = (1. / nb_waves) * rho_u[cell_id][0] +
-                                inv_S[0] * vec_A[cell_id][1][0] * lambda_vector[i][0] +
-                                inv_S[1] * vec_A[cell_id][1][1] * lambda_vector[i][1];
-        Fn_rho_u2[cell_id][i] = (1. / nb_waves) * rho_u[cell_id][1] +
-                                inv_S[0] * vec_A[cell_id][2][0] * lambda_vector[i][0] +
-                                inv_S[1] * vec_A[cell_id][2][1] * lambda_vector[i][1];
-        Fn_rho_E[cell_id][i] = (1. / nb_waves) * rho_E[cell_id] +
-                               inv_S[0] * vec_A[cell_id][3][0] * lambda_vector[i][0] +
-                               inv_S[1] * vec_A[cell_id][3][1] * lambda_vector[i][1];
+        Fn_rho[cell_id][i]   = (1. / nb_waves) * rho[cell_id];
+        Fn_rho_u[cell_id][i] = (1. / nb_waves) * rho_u[cell_id];
+        Fn_rho_E[cell_id][i] = (1. / nb_waves) * rho_E[cell_id];
+
+        for (size_t d1 = 0; d1 < MeshType::Dimension; ++d1) {
+          Fn_rho[cell_id][i] += inv_S[d1] * lambda_vector[i][d1] * A_rho[cell_id][d1];
+          for (size_t d2 = 0; d2 < MeshType::Dimension; ++d2) {
+            Fn_rho_u[cell_id][i][d1] += inv_S[d2] * lambda_vector[i][d2] * A_rho_u[cell_id](d2, d1);
+          }
+          Fn_rho_E[cell_id][i] += inv_S[d1] * lambda_vector[i][d1] * A_rho_E[cell_id][d1];
+        }
       }
     });
 
   return std::make_tuple(std::make_shared<DiscreteFunctionVariant>(Fn_rho),
-                         std::make_shared<DiscreteFunctionVariant>(Fn_rho_u1),
-                         std::make_shared<DiscreteFunctionVariant>(Fn_rho_u2),
+                         std::make_shared<DiscreteFunctionVariant>(Fn_rho_u),
                          std::make_shared<DiscreteFunctionVariant>(Fn_rho_E));
 }
 
+template <size_t Dimension>
 std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
-           std::shared_ptr<const DiscreteFunctionVariant>,
            std::shared_ptr<const DiscreteFunctionVariant>,
            std::shared_ptr<const DiscreteFunctionVariant>>
-getEulerKineticWaves2D(const std::vector<TinyVector<2>>& lambda_vector,
-                       const std::shared_ptr<const DiscreteFunctionVariant>& rho,
-                       const std::shared_ptr<const DiscreteFunctionVariant>& rho_u,
-                       const std::shared_ptr<const DiscreteFunctionVariant>& rho_E,
-                       const double& gamma)
+getEulerKineticWavesMultiD(const std::vector<TinyVector<Dimension>>& lambda_vector,
+                           const std::shared_ptr<const DiscreteFunctionVariant>& rho,
+                           const std::shared_ptr<const DiscreteFunctionVariant>& rho_u,
+                           const std::shared_ptr<const DiscreteFunctionVariant>& rho_E,
+                           const double& gamma)
 {
   const std::shared_ptr<const MeshVariant> mesh_v = getCommonMesh({rho, rho_u, rho_E});
   if (mesh_v.use_count() == 0) {
@@ -189,23 +175,41 @@ getEulerKineticWaves2D(const std::vector<TinyVector<2>>& lambda_vector,
   return std::visit(
     [&](auto&& p_mesh)
       -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>,
-                    std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>> {
+                    std::shared_ptr<const DiscreteFunctionVariant>> {
       using MeshType = mesh_type_t<decltype(p_mesh)>;
-      if constexpr (is_polygonal_mesh_v<MeshType> and (MeshType::Dimension == 2)) {
+      if constexpr (is_polygonal_mesh_v<MeshType> and (MeshType::Dimension == Dimension)) {
         auto rho_h   = rho->get<DiscreteFunctionP0<const double>>();
         auto rho_u_h = rho_u->get<DiscreteFunctionP0<const TinyVector<MeshType::Dimension>>>();
         auto rho_E_h = rho_E->get<DiscreteFunctionP0<const double>>();
 
-        return getEulerKineticWaves2D(p_mesh, lambda_vector, rho_h, rho_u_h, rho_E_h, gamma);
+        return GetEulerKineticWavesMultiD(p_mesh, lambda_vector, rho_h, rho_u_h, rho_E_h, gamma);
       } else {
-        throw NotImplementedError("Invalid mesh type for 2D Euler Kinetic solver");
+        throw NotImplementedError("Invalid mesh type for MultiD Euler Kinetic solver");
       }
     },
     mesh_v->variant());
 }
 
+template std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
+                    std::shared_ptr<const DiscreteFunctionVariant>,
+                    std::shared_ptr<const DiscreteFunctionVariant>>
+getEulerKineticWavesMultiD(const std::vector<TinyVector<2>>& lambda_vector,
+                           const std::shared_ptr<const DiscreteFunctionVariant>& rho,
+                           const std::shared_ptr<const DiscreteFunctionVariant>& rho_u,
+                           const std::shared_ptr<const DiscreteFunctionVariant>& rho_E,
+                           const double& gamma);
+
+template std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
+                    std::shared_ptr<const DiscreteFunctionVariant>,
+                    std::shared_ptr<const DiscreteFunctionVariant>>
+getEulerKineticWavesMultiD(const std::vector<TinyVector<3>>& lambda_vector,
+                           const std::shared_ptr<const DiscreteFunctionVariant>& rho,
+                           const std::shared_ptr<const DiscreteFunctionVariant>& rho_u,
+                           const std::shared_ptr<const DiscreteFunctionVariant>& rho_E,
+                           const double& gamma);
+
 template <MeshConcept MeshType>
-class EulerKineticSolver2D
+class EulerKineticSolverMultiD
 {
  private:
   constexpr static size_t Dimension = MeshType::Dimension;
@@ -225,18 +229,14 @@ class EulerKineticSolver2D
   const std::vector<TinyVector<Dimension>>& m_lambda_vector;
   const size_t m_SpaceOrder;
   const CellArray<const double> m_Fn_rho;
-  const CellArray<TinyVector<Dimension>> m_Fn_rho_u;
+  const CellArray<const TinyVector<Dimension>> m_Fn_rho_u;
   const CellArray<const double> m_Fn_rho_E;
   const double m_gamma;
   const std::shared_ptr<const MeshType> p_mesh;
   const MeshType& m_mesh;
-  const CellValue<const double> m_Vj                      = MeshDataManager::instance().getMeshData(m_mesh).Vj();
-  const NodeValuePerCell<const TinyVector<Dimension>> Cjr = MeshDataManager::instance().getMeshData(m_mesh).Cjr();
-  const NodeValuePerCell<const TinyVector<Dimension>> njr = MeshDataManager::instance().getMeshData(m_mesh).njr();
-  const NodeValuePerFace<const TinyVector<Dimension>> Nlr = MeshDataManager::instance().getMeshData(m_mesh).Nlr();
-  const NodeValuePerFace<const TinyVector<Dimension>> nlr = MeshDataManager::instance().getMeshData(m_mesh).nlr();
-  const FaceValue<const TinyVector<Dimension>> nl         = MeshDataManager::instance().getMeshData(m_mesh).nl();
-  const FaceValue<const TinyVector<Dimension>> Nl         = MeshDataManager::instance().getMeshData(m_mesh).Nl();
+  const CellValue<const double> m_Vj = MeshDataManager::instance().getMeshData(m_mesh).Vj();
+
+  const FaceValue<const TinyVector<Dimension>> nl = MeshDataManager::instance().getMeshData(m_mesh).nl();
 
   CellValue<const double> m_inv_Vj;
   const CellValue<const TinyVector<Dimension>> m_xj = MeshDataManager::instance().getMeshData(m_mesh).xj();
@@ -508,97 +508,107 @@ class EulerKineticSolver2D
   NodeArray<T>
   compute_Flux1(CellArray<T>& Fn)
   {
-    const size_t nb_waves = m_lambda_vector.size();
-    NodeArray<T> Fr(m_mesh.connectivity(), nb_waves);
-    const auto& node_local_numbers_in_their_cells = p_mesh->connectivity().nodeLocalNumbersInTheirCells();
-    const auto& node_to_cell_matrix               = p_mesh->connectivity().nodeToCellMatrix();
-
-    if constexpr (is_tiny_vector_v<T>) {
-      Fr.fill(zero);
-    } else {
-      Fr.fill(0.);
-    }
+    if constexpr (Dimension > 1) {
+      const NodeValuePerCell<const TinyVector<Dimension>> njr = MeshDataManager::instance().getMeshData(m_mesh).njr();
+      const size_t nb_waves                                   = m_lambda_vector.size();
+      NodeArray<T> Fr(m_mesh.connectivity(), nb_waves);
+      const auto& node_local_numbers_in_their_cells = p_mesh->connectivity().nodeLocalNumbersInTheirCells();
+      const auto& node_to_cell_matrix               = p_mesh->connectivity().nodeToCellMatrix();
+
+      if constexpr (is_tiny_vector_v<T>) {
+        Fr.fill(zero);
+      } else {
+        Fr.fill(0.);
+      }
 
-    parallel_for(
-      p_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
-        const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id);
-        const auto& node_to_cell                  = node_to_cell_matrix[node_id];
-        for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
-          double sum_li_njr = 0;
+      parallel_for(
+        p_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id);
+          const auto& node_to_cell                  = node_to_cell_matrix[node_id];
+          for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
+            double sum_li_njr = 0;
 
-          for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
-            const CellId cell_id      = node_to_cell[i_cell];
-            const unsigned int i_node = node_local_number_in_its_cell[i_cell];
+            for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+              const CellId cell_id      = node_to_cell[i_cell];
+              const unsigned int i_node = node_local_number_in_its_cell[i_cell];
 
-            double li_njr = dot(m_lambda_vector[i_wave], njr(cell_id, i_node));
-            if (li_njr > 0) {
-              Fr[node_id][i_wave] += Fn[cell_id][i_wave] * li_njr;
-              sum_li_njr += li_njr;
+              double li_njr = dot(m_lambda_vector[i_wave], njr(cell_id, i_node));
+              if (li_njr > 0) {
+                Fr[node_id][i_wave] += Fn[cell_id][i_wave] * li_njr;
+                sum_li_njr += li_njr;
+              }
+            }
+            if (sum_li_njr != 0) {
+              Fr[node_id][i_wave] /= sum_li_njr;
             }
           }
-          if (sum_li_njr != 0) {
-            Fr[node_id][i_wave] /= sum_li_njr;
-          }
-        }
-      });
+        });
 
-    return Fr;
+      return Fr;
+    } else {
+      throw NormalError("Glace not defined in 1d");
+    }
   }
 
   template <typename T>
   FaceArrayPerNode<T>
   compute_Flux1_eucchlyd(CellArray<T> Fn)
   {
-    const size_t nb_waves = m_lambda_vector.size();
-    FaceArrayPerNode<T> Flr(m_mesh.connectivity(), nb_waves);
-    const auto& node_local_numbers_in_their_faces = p_mesh->connectivity().nodeLocalNumbersInTheirFaces();
-    const auto& face_local_numbers_in_their_cells = p_mesh->connectivity().faceLocalNumbersInTheirCells();
-    const auto& node_to_face_matrix               = p_mesh->connectivity().nodeToFaceMatrix();
-    const auto& face_to_cell_matrix               = p_mesh->connectivity().faceToCellMatrix();
-    const auto& face_cell_is_reversed             = p_mesh->connectivity().cellFaceIsReversed();
-
-    if constexpr (is_tiny_vector_v<T>) {
-      Flr.fill(zero);
-    } else {
-      Flr.fill(0.);
-    }
+    if constexpr (Dimension > 1) {
+      const NodeValuePerFace<const TinyVector<Dimension>> nlr = MeshDataManager::instance().getMeshData(m_mesh).nlr();
+      const size_t nb_waves                                   = m_lambda_vector.size();
+      FaceArrayPerNode<T> Flr(m_mesh.connectivity(), nb_waves);
+      const auto& node_local_numbers_in_their_faces = p_mesh->connectivity().nodeLocalNumbersInTheirFaces();
+      const auto& face_local_numbers_in_their_cells = p_mesh->connectivity().faceLocalNumbersInTheirCells();
+      const auto& node_to_face_matrix               = p_mesh->connectivity().nodeToFaceMatrix();
+      const auto& face_to_cell_matrix               = p_mesh->connectivity().faceToCellMatrix();
+      const auto& face_cell_is_reversed             = p_mesh->connectivity().cellFaceIsReversed();
+
+      if constexpr (is_tiny_vector_v<T>) {
+        Flr.fill(zero);
+      } else {
+        Flr.fill(0.);
+      }
 
-    parallel_for(
-      p_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
-        const auto& node_local_number_in_its_face = node_local_numbers_in_their_faces.itemArray(node_id);
-        const auto& node_to_face                  = node_to_face_matrix[node_id];
+      parallel_for(
+        p_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          const auto& node_local_number_in_its_face = node_local_numbers_in_their_faces.itemArray(node_id);
+          const auto& node_to_face                  = node_to_face_matrix[node_id];
 
-        for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
-          for (size_t i_face = 0; i_face < node_to_face.size(); ++i_face) {
-            const FaceId face_id                      = node_to_face[i_face];
-            const auto& face_local_number_in_its_cell = face_local_numbers_in_their_cells.itemArray(face_id);
-            const auto& face_to_cell                  = face_to_cell_matrix[face_id];
-            const size_t i_node_face                  = node_local_number_in_its_face[i_face];
+          for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
+            for (size_t i_face = 0; i_face < node_to_face.size(); ++i_face) {
+              const FaceId face_id                      = node_to_face[i_face];
+              const auto& face_local_number_in_its_cell = face_local_numbers_in_their_cells.itemArray(face_id);
+              const auto& face_to_cell                  = face_to_cell_matrix[face_id];
+              const size_t i_node_face                  = node_local_number_in_its_face[i_face];
 
-            double sum = 0;
+              double sum = 0;
 
-            for (size_t i_cell = 0; i_cell < face_to_cell.size(); ++i_cell) {
-              const CellId cell_id     = face_to_cell[i_cell];
-              const size_t i_face_cell = face_local_number_in_its_cell[i_cell];
+              for (size_t i_cell = 0; i_cell < face_to_cell.size(); ++i_cell) {
+                const CellId cell_id     = face_to_cell[i_cell];
+                const size_t i_face_cell = face_local_number_in_its_cell[i_cell];
 
-              TinyVector<Dimension> n_face = nlr(face_id, i_node_face);
-              const double sign            = face_cell_is_reversed(cell_id, i_face_cell) ? -1 : 1;
+                TinyVector<Dimension> n_face = nlr(face_id, i_node_face);
+                const double sign            = face_cell_is_reversed(cell_id, i_face_cell) ? -1 : 1;
 
-              double li_nlr = sign * dot(m_lambda_vector[i_wave], n_face);
+                double li_nlr = sign * dot(m_lambda_vector[i_wave], n_face);
 
-              if (li_nlr > 0) {
-                Flr[node_id][i_face][i_wave] += Fn[cell_id][i_wave] * li_nlr;
-                sum += li_nlr;
+                if (li_nlr > 0) {
+                  Flr[node_id][i_face][i_wave] += Fn[cell_id][i_wave] * li_nlr;
+                  sum += li_nlr;
+                }
+              }
+              if (sum != 0) {
+                Flr[node_id][i_face][i_wave] /= sum;
               }
-            }
-            if (sum != 0) {
-              Flr[node_id][i_face][i_wave] /= sum;
             }
           }
-        }
-      });
+        });
 
-    return Flr;
+      return Flr;
+    } else {
+      throw NormalError("Eucclhyd not defined in 1d");
+    }
   }
 
   template <typename T>
@@ -815,110 +825,126 @@ class EulerKineticSolver2D
   DiscreteFunctionP0Vector<double>
   compute_deltaLFn(NodeArray<double> Fr)
   {
-    const size_t nb_waves = m_lambda_vector.size();
-    DiscreteFunctionP0Vector<double> deltaLFn(p_mesh, nb_waves);
+    if constexpr (Dimension > 1) {
+      const NodeValuePerCell<const TinyVector<Dimension>> Cjr = MeshDataManager::instance().getMeshData(m_mesh).Cjr();
+      const size_t nb_waves                                   = m_lambda_vector.size();
+      DiscreteFunctionP0Vector<double> deltaLFn(p_mesh, nb_waves);
 
-    const auto& cell_to_node_matrix = p_mesh->connectivity().cellToNodeMatrix();
+      const auto& cell_to_node_matrix = p_mesh->connectivity().cellToNodeMatrix();
 
-    parallel_for(
-      p_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-        const auto& cell_to_node = cell_to_node_matrix[cell_id];
+      parallel_for(
+        p_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+          const auto& cell_to_node = cell_to_node_matrix[cell_id];
 
-        for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
-          deltaLFn[cell_id][i_wave] = 0;
-          for (size_t i_node = 0; i_node < cell_to_node.size(); ++i_node) {
-            const NodeId node_id = cell_to_node[i_node];
+          for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
+            deltaLFn[cell_id][i_wave] = 0;
+            for (size_t i_node = 0; i_node < cell_to_node.size(); ++i_node) {
+              const NodeId node_id = cell_to_node[i_node];
 
-            double li_Cjr = dot(m_lambda_vector[i_wave], Cjr(cell_id, i_node));
-            deltaLFn[cell_id][i_wave] += Fr[node_id][i_wave] * li_Cjr;
+              double li_Cjr = dot(m_lambda_vector[i_wave], Cjr(cell_id, i_node));
+              deltaLFn[cell_id][i_wave] += Fr[node_id][i_wave] * li_Cjr;
+            }
           }
-        }
-      });
-    return deltaLFn;
+        });
+      return deltaLFn;
+    } else {
+      throw NormalError("Glace not defined in 1d");
+    }
   }
 
   DiscreteFunctionP0Vector<double>
   compute_deltaLFn_eucclhyd(FaceArrayPerNode<double> Fr)
   {
-    const size_t nb_waves = m_lambda_vector.size();
-    DiscreteFunctionP0Vector<double> deltaLFn(p_mesh, nb_waves);
+    if constexpr (Dimension > 1) {
+      const size_t nb_waves = m_lambda_vector.size();
+      DiscreteFunctionP0Vector<double> deltaLFn(p_mesh, nb_waves);
 
-    const auto& cell_to_face_matrix   = p_mesh->connectivity().cellToFaceMatrix();
-    const auto& face_to_node_matrix   = p_mesh->connectivity().faceToNodeMatrix();
-    const auto& node_to_face_matrix   = p_mesh->connectivity().nodeToFaceMatrix();
-    const auto& face_cell_is_reversed = p_mesh->connectivity().cellFaceIsReversed();
+      const NodeValuePerFace<const TinyVector<Dimension>> Nlr = MeshDataManager::instance().getMeshData(m_mesh).Nlr();
 
-    parallel_for(
-      p_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-        const auto& cell_to_face = cell_to_face_matrix[cell_id];
+      const auto& cell_to_face_matrix   = p_mesh->connectivity().cellToFaceMatrix();
+      const auto& face_to_node_matrix   = p_mesh->connectivity().faceToNodeMatrix();
+      const auto& node_to_face_matrix   = p_mesh->connectivity().nodeToFaceMatrix();
+      const auto& face_cell_is_reversed = p_mesh->connectivity().cellFaceIsReversed();
 
-        for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
-          deltaLFn[cell_id][i_wave] = 0;
+      parallel_for(
+        p_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+          const auto& cell_to_face = cell_to_face_matrix[cell_id];
+
+          for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
+            deltaLFn[cell_id][i_wave] = 0;
 
-          for (size_t i_face_cell = 0; i_face_cell < cell_to_face.size(); ++i_face_cell) {
-            const FaceId face_id     = cell_to_face[i_face_cell];
-            const auto& face_to_node = face_to_node_matrix[face_id];
+            for (size_t i_face_cell = 0; i_face_cell < cell_to_face.size(); ++i_face_cell) {
+              const FaceId face_id     = cell_to_face[i_face_cell];
+              const auto& face_to_node = face_to_node_matrix[face_id];
 
-            for (size_t i_node_face = 0; i_node_face < face_to_node.size(); ++i_node_face) {
-              const NodeId node_id     = face_to_node[i_node_face];
-              const auto& node_to_face = node_to_face_matrix[node_id];
+              for (size_t i_node_face = 0; i_node_face < face_to_node.size(); ++i_node_face) {
+                const NodeId node_id     = face_to_node[i_node_face];
+                const auto& node_to_face = node_to_face_matrix[node_id];
 
-              auto local_face_number_in_node = [&](FaceId face_number) {
-                for (size_t i_face = 0; i_face < node_to_face.size(); ++i_face) {
-                  if (face_number == node_to_face[i_face]) {
-                    return i_face;
+                auto local_face_number_in_node = [&](FaceId face_number) {
+                  for (size_t i_face = 0; i_face < node_to_face.size(); ++i_face) {
+                    if (face_number == node_to_face[i_face]) {
+                      return i_face;
+                    }
                   }
-                }
-                return std::numeric_limits<size_t>::max();
-              };
+                  return std::numeric_limits<size_t>::max();
+                };
 
-              const size_t i_face_node = local_face_number_in_node(face_id);
+                const size_t i_face_node = local_face_number_in_node(face_id);
 
-              const double sign = face_cell_is_reversed(cell_id, i_face_cell) ? -1 : 1;
+                const double sign = face_cell_is_reversed(cell_id, i_face_cell) ? -1 : 1;
 
-              const double li_Nlr = sign * dot(m_lambda_vector[i_wave], Nlr(face_id, i_node_face));
+                const double li_Nlr = sign * dot(m_lambda_vector[i_wave], Nlr(face_id, i_node_face));
 
-              deltaLFn[cell_id][i_wave] += Fr[node_id][i_face_node][i_wave] * li_Nlr;
+                deltaLFn[cell_id][i_wave] += Fr[node_id][i_face_node][i_wave] * li_Nlr;
+              }
             }
           }
-        }
-      });
-    return deltaLFn;
+        });
+      return deltaLFn;
+    } else {
+      throw NormalError("Eucclhyd not defined in 1d");
+    }
   }
 
   template <typename T>
   CellArray<T>
   compute_deltaLFn_face(FaceArray<T> Fl)
   {
-    const size_t nb_waves = m_lambda_vector.size();
-    CellArray<T> deltaLFn(p_mesh->connectivity(), nb_waves);
+    if constexpr (Dimension > 1) {
+      const FaceValue<const TinyVector<Dimension>> Nl = MeshDataManager::instance().getMeshData(m_mesh).Nl();
+      const size_t nb_waves                           = m_lambda_vector.size();
+      CellArray<T> deltaLFn(p_mesh->connectivity(), nb_waves);
 
-    const auto& cell_to_face_matrix   = p_mesh->connectivity().cellToFaceMatrix();
-    const auto& face_cell_is_reversed = p_mesh->connectivity().cellFaceIsReversed();
+      const auto& cell_to_face_matrix   = p_mesh->connectivity().cellToFaceMatrix();
+      const auto& face_cell_is_reversed = p_mesh->connectivity().cellFaceIsReversed();
 
-    if constexpr (is_tiny_vector_v<T>) {
-      deltaLFn.fill(zero);
-    } else {
-      deltaLFn.fill(0.);
-    }
+      if constexpr (is_tiny_vector_v<T>) {
+        deltaLFn.fill(zero);
+      } else {
+        deltaLFn.fill(0.);
+      }
 
-    parallel_for(
-      p_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-        const auto& cell_to_face = cell_to_face_matrix[cell_id];
+      parallel_for(
+        p_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+          const auto& cell_to_face = cell_to_face_matrix[cell_id];
 
-        for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
-          for (size_t i_face_cell = 0; i_face_cell < cell_to_face.size(); ++i_face_cell) {
-            const FaceId face_id = cell_to_face[i_face_cell];
+          for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
+            for (size_t i_face_cell = 0; i_face_cell < cell_to_face.size(); ++i_face_cell) {
+              const FaceId face_id = cell_to_face[i_face_cell];
 
-            const double sign = face_cell_is_reversed(cell_id, i_face_cell) ? -1 : 1;
+              const double sign = face_cell_is_reversed(cell_id, i_face_cell) ? -1 : 1;
 
-            const double li_Nl = sign * dot(m_lambda_vector[i_wave], Nl[face_id]);
+              const double li_Nl = sign * dot(m_lambda_vector[i_wave], Nl[face_id]);
 
-            deltaLFn[cell_id][i_wave] += li_Nl * Fl[face_id][i_wave];
+              deltaLFn[cell_id][i_wave] += li_Nl * Fl[face_id][i_wave];
+            }
           }
-        }
-      });
-    return deltaLFn;
+        });
+      return deltaLFn;
+    } else {
+      throw NormalError("Nl in meaningless in 1d");
+    }
   }
 
   CellValue<bool>
@@ -947,7 +973,6 @@ class EulerKineticSolver2D
   }
 
   std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
-             std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>>
   apply(const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list)
@@ -998,8 +1023,7 @@ class EulerKineticSolver2D
     const CellArray<const double> Fn_rho_E                = copy(m_Fn_rho_E);
 
     DiscreteFunctionP0Vector<double> Fnp1_rho(p_mesh, nb_waves);
-    DiscreteFunctionP0Vector<double> Fnp1_rho_u1(p_mesh, nb_waves);
-    DiscreteFunctionP0Vector<double> Fnp1_rho_u2(p_mesh, nb_waves);
+    DiscreteFunctionP0Vector<TinyVector<Dimension>> Fnp1_rho_u(p_mesh, nb_waves);
     DiscreteFunctionP0Vector<double> Fnp1_rho_E(p_mesh, nb_waves);
 
     // Compute first order F
@@ -1091,41 +1115,36 @@ class EulerKineticSolver2D
           for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
             Fnp1_rho[cell_id][i_wave] = c1 * (m_eps * Fn_rho[cell_id][i_wave] - c2 * deltaLFn_rho[cell_id][i_wave] +
                                               m_dt * M_rho_np1[cell_id][i_wave]);
-            Fnp1_rho_u1[cell_id][i_wave] =
-              c1 * (m_eps * Fn_rho_u[cell_id][i_wave][0] - c2 * deltaLFn_rho_u[cell_id][i_wave][0] +
-                    m_dt * M_rho_u_np1[cell_id][i_wave][0]);
-            Fnp1_rho_u2[cell_id][i_wave] =
-              c1 * (m_eps * Fn_rho_u[cell_id][i_wave][1] - c2 * deltaLFn_rho_u[cell_id][i_wave][1] +
-                    m_dt * M_rho_u_np1[cell_id][i_wave][1]);
+            Fnp1_rho_u[cell_id][i_wave] =
+              c1 * (m_eps * Fn_rho_u[cell_id][i_wave] - c2 * deltaLFn_rho_u[cell_id][i_wave] +
+                    m_dt * M_rho_u_np1[cell_id][i_wave]);
             Fnp1_rho_E[cell_id][i_wave] =
               c1 * (m_eps * Fn_rho_E[cell_id][i_wave] - c2 * deltaLFn_rho_E[cell_id][i_wave] +
                     m_dt * M_rho_E_np1[cell_id][i_wave]);
           }
         } else {
           for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
-            Fnp1_rho[cell_id][i_wave]    = Fn_rho_ex[cell_id][i_wave];
-            Fnp1_rho_u1[cell_id][i_wave] = Fn_rho_u_ex[cell_id][i_wave][0];
-            Fnp1_rho_u2[cell_id][i_wave] = Fn_rho_u_ex[cell_id][i_wave][1];
-            Fnp1_rho_E[cell_id][i_wave]  = Fn_rho_E_ex[cell_id][i_wave];
+            Fnp1_rho[cell_id][i_wave]   = Fn_rho_ex[cell_id][i_wave];
+            Fnp1_rho_u[cell_id][i_wave] = Fn_rho_u_ex[cell_id][i_wave];
+            Fnp1_rho_E[cell_id][i_wave] = Fn_rho_E_ex[cell_id][i_wave];
           }
         }
       });
 
     return std::make_tuple(std::make_shared<DiscreteFunctionVariant>(Fnp1_rho),
-                           std::make_shared<DiscreteFunctionVariant>(Fnp1_rho_u1),
-                           std::make_shared<DiscreteFunctionVariant>(Fnp1_rho_u2),
+                           std::make_shared<DiscreteFunctionVariant>(Fnp1_rho_u),
                            std::make_shared<DiscreteFunctionVariant>(Fnp1_rho_E));
   }
 
-  EulerKineticSolver2D(std::shared_ptr<const MeshType> mesh,
-                       const double& dt,
-                       const double& eps,
-                       const double& gamma,
-                       const std::vector<TinyVector<2>>& lambda_vector,
-                       const size_t& SpaceOrder,
-                       const CellArray<const double>& Fn_rho,
-                       const CellArray<TinyVector<2>>& Fn_rho_u,
-                       const CellArray<const double>& Fn_rho_E)
+  EulerKineticSolverMultiD(std::shared_ptr<const MeshType> mesh,
+                           const double& dt,
+                           const double& eps,
+                           const double& gamma,
+                           const std::vector<TinyVector<MeshType::Dimension>>& lambda_vector,
+                           const size_t& SpaceOrder,
+                           const CellArray<const double>& Fn_rho,
+                           const CellArray<const TinyVector<MeshType::Dimension>>& Fn_rho_u,
+                           const CellArray<const double>& Fn_rho_E)
     : m_dt{dt},
       m_eps{eps},
       m_lambda_vector{lambda_vector},
@@ -1151,11 +1170,11 @@ class EulerKineticSolver2D
     }();
   }
 
-  ~EulerKineticSolver2D() = default;
+  ~EulerKineticSolverMultiD() = default;
 };
 
 template <MeshConcept MeshType>
-class EulerKineticSolver2D<MeshType>::SymmetryBoundaryCondition
+class EulerKineticSolverMultiD<MeshType>::SymmetryBoundaryCondition
 {
  public:
   static constexpr const size_t Dimension = MeshType::Dimension;
@@ -1208,7 +1227,7 @@ class EulerKineticSolver2D<MeshType>::SymmetryBoundaryCondition
 };
 
 template <MeshConcept MeshType>
-class EulerKineticSolver2D<MeshType>::WallBoundaryCondition
+class EulerKineticSolverMultiD<MeshType>::WallBoundaryCondition
 {
  public:
   static constexpr const size_t Dimension = MeshType::Dimension;
@@ -1254,7 +1273,7 @@ class EulerKineticSolver2D<MeshType>::WallBoundaryCondition
 };
 
 template <MeshConcept MeshType>
-class EulerKineticSolver2D<MeshType>::InflowListBoundaryCondition
+class EulerKineticSolverMultiD<MeshType>::InflowListBoundaryCondition
 {
  public:
   static constexpr const size_t Dimension = MeshType::Dimension;
@@ -1294,7 +1313,7 @@ class EulerKineticSolver2D<MeshType>::InflowListBoundaryCondition
 };
 
 template <MeshConcept MeshType>
-class EulerKineticSolver2D<MeshType>::OutflowBoundaryCondition
+class EulerKineticSolverMultiD<MeshType>::OutflowBoundaryCondition
 {
  public:
   static constexpr const size_t Dimension = MeshType::Dimension;
@@ -1339,60 +1358,71 @@ class EulerKineticSolver2D<MeshType>::OutflowBoundaryCondition
   ~OutflowBoundaryCondition() = default;
 };
 
+template <size_t Dimension>
 std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
-           std::shared_ptr<const DiscreteFunctionVariant>,
            std::shared_ptr<const DiscreteFunctionVariant>,
            std::shared_ptr<const DiscreteFunctionVariant>>
-eulerKineticSolver2D(const double& dt,
-                     const double& gamma,
-                     const std::vector<TinyVector<2>>& lambda_vector,
-                     const double& eps,
-                     const size_t& SpaceOrder,
-                     const std::shared_ptr<const DiscreteFunctionVariant>& F_rho,
-                     const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_u1,
-                     const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_u2,
-                     const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_E,
-                     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list)
+eulerKineticSolverMultiD(const double& dt,
+                         const double& gamma,
+                         const std::vector<TinyVector<Dimension>>& lambda_vector,
+                         const double& eps,
+                         const size_t& SpaceOrder,
+                         const std::shared_ptr<const DiscreteFunctionVariant>& F_rho,
+                         const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_u,
+                         const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_E,
+                         const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list)
 {
-  const std::shared_ptr<const MeshVariant> mesh_v = getCommonMesh({F_rho, F_rho_u1, F_rho_u2, F_rho_E});
+  const std::shared_ptr<const MeshVariant> mesh_v = getCommonMesh({F_rho, F_rho_u, F_rho_E});
   if (mesh_v.use_count() == 0) {
     throw NormalError("F_rho, F_rho_u1, F_rho_u2 and F_rho_E have to be defined on the same mesh");
   }
   if (SpaceOrder > 1) {
-    throw NotImplementedError("Euler kinetic solver in 2D not implemented at order > 1");
+    throw NotImplementedError("Euler kinetic solver in Multi D not implemented at order > 1");
   }
 
   return std::visit(
     [&](auto&& p_mesh)
       -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>,
-                    std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>> {
+                    std::shared_ptr<const DiscreteFunctionVariant>> {
       using MeshType = mesh_type_t<decltype(p_mesh)>;
-      if constexpr (is_polygonal_mesh_v<MeshType> and (MeshType::Dimension == 2)) {
-        auto Fn_rho    = F_rho->get<DiscreteFunctionP0Vector<const double>>();
-        auto Fn_rho_u1 = F_rho_u1->get<DiscreteFunctionP0Vector<const double>>();
-        auto Fn_rho_u2 = F_rho_u2->get<DiscreteFunctionP0Vector<const double>>();
-        auto Fn_rho_E  = F_rho_E->get<DiscreteFunctionP0Vector<const double>>();
-
-        const double nb_waves                = lambda_vector.size();
-        CellArray<const double> Fn_rho_array = Fn_rho.cellArrays();
-        CellArray<TinyVector<MeshType::Dimension>> Fn_rho_u_array(p_mesh->connectivity(), nb_waves);
-        CellArray<const double> Fn_rho_E_array = Fn_rho_E.cellArrays();
-
-        parallel_for(
-          p_mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            for (size_t i_wave = 0; i_wave < nb_waves; ++i_wave) {
-              Fn_rho_u_array[cell_id][i_wave][0] = Fn_rho_u1[cell_id][i_wave];
-              Fn_rho_u_array[cell_id][i_wave][1] = Fn_rho_u2[cell_id][i_wave];
-            }
-          });
+      if constexpr (is_polygonal_mesh_v<MeshType> and (MeshType::Dimension == Dimension)) {
+        auto Fn_rho   = F_rho->get<DiscreteFunctionP0Vector<const double>>();
+        auto Fn_rho_u = F_rho_u->get<DiscreteFunctionP0Vector<const TinyVector<MeshType::Dimension>>>();
+        auto Fn_rho_E = F_rho_E->get<DiscreteFunctionP0Vector<const double>>();
 
-        EulerKineticSolver2D solver(p_mesh, dt, eps, gamma, lambda_vector, SpaceOrder, Fn_rho_array, Fn_rho_u_array,
-                                    Fn_rho_E_array);
+        EulerKineticSolverMultiD solver(p_mesh, dt, eps, gamma, lambda_vector, SpaceOrder, Fn_rho.cellArrays(),
+                                        Fn_rho_u.cellArrays(), Fn_rho_E.cellArrays());
 
         return solver.apply(bc_descriptor_list);
       } else {
-        throw NotImplementedError("Invalid mesh type for 2D Euler Kinetic solver");
+        throw NotImplementedError("Invalid mesh type for Multi D Euler Kinetic solver");
       }
     },
     mesh_v->variant());
 }
+
+template std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
+                    std::shared_ptr<const DiscreteFunctionVariant>,
+                    std::shared_ptr<const DiscreteFunctionVariant>>
+eulerKineticSolverMultiD(const double& dt,
+                         const double& gamma,
+                         const std::vector<TinyVector<2>>& lambda_vector,
+                         const double& eps,
+                         const size_t& SpaceOrder,
+                         const std::shared_ptr<const DiscreteFunctionVariant>& F_rho,
+                         const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_u,
+                         const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_E,
+                         const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list);
+
+template std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
+                    std::shared_ptr<const DiscreteFunctionVariant>,
+                    std::shared_ptr<const DiscreteFunctionVariant>>
+eulerKineticSolverMultiD(const double& dt,
+                         const double& gamma,
+                         const std::vector<TinyVector<3>>& lambda_vector,
+                         const double& eps,
+                         const size_t& SpaceOrder,
+                         const std::shared_ptr<const DiscreteFunctionVariant>& F_rho,
+                         const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_u,
+                         const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_E,
+                         const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list);
diff --git a/src/scheme/EulerKineticSolverMultiD.hpp b/src/scheme/EulerKineticSolverMultiD.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..987f10f8a97189ca0db782cf0545e92b0d5bb933
--- /dev/null
+++ b/src/scheme/EulerKineticSolverMultiD.hpp
@@ -0,0 +1,40 @@
+#ifndef EULER_KINETIC_SOLVER_MULTID_HPP
+#define EULER_KINETIC_SOLVER_MULTID_HPP
+
+#include <language/utils/FunctionSymbolId.hpp>
+#include <scheme/DiscreteFunctionVariant.hpp>
+
+class IBoundaryConditionDescriptor;
+
+std::vector<TinyVector<2>> getLambdaVector2D(const std::shared_ptr<const MeshVariant>& mesh,
+                                             const double& lambda,
+                                             const size_t& L,
+                                             const size_t& M);
+
+std::vector<TinyVector<3>> getLambdaVector3D(const std::shared_ptr<const MeshVariant>& mesh, const double& lambda);
+
+template <size_t Dimension>
+std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
+           std::shared_ptr<const DiscreteFunctionVariant>,
+           std::shared_ptr<const DiscreteFunctionVariant>>
+getEulerKineticWavesMultiD(const std::vector<TinyVector<Dimension>>& lambda_vector,
+                           const std::shared_ptr<const DiscreteFunctionVariant>& rho,
+                           const std::shared_ptr<const DiscreteFunctionVariant>& rho_u,
+                           const std::shared_ptr<const DiscreteFunctionVariant>& rho_E,
+                           const double& gamma);
+
+template <size_t Dimension>
+std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
+           std::shared_ptr<const DiscreteFunctionVariant>,
+           std::shared_ptr<const DiscreteFunctionVariant>>
+eulerKineticSolverMultiD(const double& dt,
+                         const double& gamma,
+                         const std::vector<TinyVector<Dimension>>& lambda_vector,
+                         const double& eps,
+                         const size_t& SpaceOrder,
+                         const std::shared_ptr<const DiscreteFunctionVariant>& F_rho,
+                         const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_u,
+                         const std::shared_ptr<const DiscreteFunctionVariant>& F_rho_E,
+                         const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list);
+
+#endif   // EULER_KINETIC_SOLVER_MULTID_HPP