From af488bd097d543ae2a37327847544fe1bdf2e4a9 Mon Sep 17 00:00:00 2001
From: Clovis <clovis.schoeck@etudiant.univ-rennes.fr>
Date: Fri, 20 Sep 2024 16:52:44 +0200
Subject: [PATCH] Navier-Stokes GKS solver programmed

---
 src/language/modules/SchemeModule.cpp |   6 +-
 src/scheme/GKS2.cpp                   |  76 ++-
 src/scheme/GKSNavier.cpp              | 697 +++++++++++++++++---------
 src/scheme/GKSNavier.hpp              |  11 +-
 4 files changed, 499 insertions(+), 291 deletions(-)

diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index 342a440db..1361721b1 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -459,11 +459,11 @@ SchemeModule::SchemeModule()
 
       [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
          const std::shared_ptr<const DiscreteFunctionVariant>& rhoU,
-         const std::shared_ptr<const DiscreteFunctionVariant>& rhoE,
-         const std::shared_ptr<const DiscreteFunctionVariant>& tau, const double& delta, const double& dt)
+         const std::shared_ptr<const DiscreteFunctionVariant>& rhoE, const double& viscosity, const double& delta,
+         const double& dt)
         -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>, std::shared_ptr<const DiscreteFunctionVariant>,
                       std::shared_ptr<const DiscreteFunctionVariant>> {
-        return GKSHandler{getCommonMesh({rho, rhoU, rhoE})}.solver().gksNavier(rho, rhoU, rhoE, tau, delta, dt);
+        return GKSHandler{getCommonMesh({rho, rhoU, rhoE})}.solver().gksNavier(rho, rhoU, rhoE, viscosity, delta, dt);
       }
 
       ));
diff --git a/src/scheme/GKS2.cpp b/src/scheme/GKS2.cpp
index 6ba5583e0..cd5a89ff5 100644
--- a/src/scheme/GKS2.cpp
+++ b/src/scheme/GKS2.cpp
@@ -33,7 +33,7 @@ class GKS2
       if (tau_n[cell_id] == 0)
         eta[cell_id] = 0;
       else
-        eta[cell_id] = tau_n[cell_id];   //(tau_n[cell_id] / dt) * (1 - std::exp(-dt / tau_n[cell_id]));
+        eta[cell_id] = (tau_n[cell_id] / dt) * (1 - std::exp(-dt / tau_n[cell_id]));
     }
     // std::cout << "eta = " << eta << std::endl;
 
@@ -83,6 +83,10 @@ class GKS2
     CellValue<Rd> U{p_mesh->connectivity()};
     // U.fill(Rd(0));
 
+    CellValue<double> erf_term_pos(mesh.connectivity());
+    CellValue<double> erf_term_neg(mesh.connectivity());
+    CellValue<double> exp_term(mesh.connectivity());
+
     parallel_for(
       mesh.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
         U[cell_id][0]   = rho_U_n[cell_id][0] / rho_n[cell_id];
@@ -90,6 +94,14 @@ class GKS2
         lambda[cell_id] = 0.5 * (1. + delta) * rho_n[cell_id] / (2 * rho_E_n[cell_id] - rho_U_2);
       });
 
+    parallel_for(
+      mesh.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+        double U_2            = U[cell_id][0] * U[cell_id][0];
+        erf_term_pos[cell_id] = 1. + std::erf(std::sqrt(lambda[cell_id]) * U[cell_id][0]);
+        erf_term_neg[cell_id] = 1. - std::erf(std::sqrt(lambda[cell_id]) * U[cell_id][0]);
+        exp_term[cell_id]     = std::exp(-lambda[cell_id] * U_2) / std::sqrt(pi * lambda[cell_id]);
+      });
+
     parallel_for(
       mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
         const auto& node_cells = node_to_cell_matrix[node_id];
@@ -106,35 +118,22 @@ class GKS2
             continue;
 
           if (l == 0) {
-            double U_2_left = U[node_cells[l]][0] * U[node_cells[l]][0];
-
-            rho_cell_left =
-              rho_n[node_cells[l]] * (1 + std::erf(std::sqrt(lambda[node_cells[l]]) * U[node_cells[l]][0]));
+            rho_cell_left = rho_n[node_cells[l]] * erf_term_pos[node_cells[l]];
 
             rho_U_cell_left[0] =
-              rho_U_n[node_cells[l]][0] * (1. + std::erf(std::sqrt(lambda[node_cells[l]]) * U[node_cells[l]][0])) +
-              rho_n[node_cells[l]] * std::exp(-lambda[node_cells[l]] * U_2_left) /
-                std::sqrt(pi * lambda[node_cells[l]]);
-
-            rho_E_cell_left =
-              rho_E_n[node_cells[l]] * (1. + std::erf(std::sqrt(lambda[node_cells[l]]) * U[node_cells[l]][0])) +
-              0.5 * rho_U_n[node_cells[l]][0] * std::exp(-lambda[node_cells[l]] * U_2_left) /
-                std::sqrt(pi * lambda[node_cells[l]]);
-          } else {
-            double U_2_right = U[node_cells[l]][0] * U[node_cells[l]][0];
+              rho_U_n[node_cells[l]][0] * erf_term_pos[node_cells[l]] + rho_n[node_cells[l]] * exp_term[node_cells[l]];
 
+            rho_E_cell_left = rho_E_n[node_cells[l]] * erf_term_pos[node_cells[l]] +
+                              0.5 * rho_U_n[node_cells[l]][0] * exp_term[node_cells[l]];
+          } else {
             rho_cell_right =
-              rho_n[node_cells[l]] * (1 - std::erf(std::sqrt(lambda[node_cells[l]]) * U[node_cells[l]][0]));
+              rho_n[node_cells[l]] * (1. - std::erf(std::sqrt(lambda[node_cells[l]]) * U[node_cells[l]][0]));
 
             rho_U_cell_right[0] =
-              rho_U_n[node_cells[l]][0] * (1. - std::erf(std::sqrt(lambda[node_cells[l]]) * U[node_cells[l]][0])) -
-              rho_n[node_cells[l]] * std::exp(-lambda[node_cells[l]] * U_2_right) /
-                std::sqrt(pi * lambda[node_cells[l]]);
-
-            rho_E_cell_right =
-              rho_E_n[node_cells[l]] * (1. - std::erf(std::sqrt(lambda[node_cells[l]]) * U[node_cells[l]][0])) -
-              0.5 * rho_U_n[node_cells[l]][0] * std::exp(-lambda[node_cells[l]] * U_2_right) /
-                std::sqrt(pi * lambda[node_cells[l]]);
+              rho_U_n[node_cells[l]][0] * erf_term_neg[node_cells[l]] - rho_n[node_cells[l]] * exp_term[node_cells[l]];
+
+            rho_E_cell_right = rho_E_n[node_cells[l]] * erf_term_neg[node_cells[l]] -
+                               0.5 * rho_U_n[node_cells[l]][0] * exp_term[node_cells[l]];
           }
         }
         rho_node[node_id]   = 0.5 * (rho_cell_left + rho_cell_right);
@@ -159,28 +158,26 @@ class GKS2
             double U_2_left     = U[node_cells[l]][0] * U[node_cells[l]][0];
             double rho_U_2_left = rho_U_n[node_cells[l]][0] * U[node_cells[l]][0];
 
-            F2_fn_left[0] = (rho_U_2_left + 0.5 * rho_n[node_cells[l]] / lambda[node_cells[l]]) *
-                              (1. + std::erf(std::sqrt(lambda[node_cells[l]]) * U[node_cells[l]][0])) +
-                            rho_U_n[node_cells[l]][0] * std::exp(-lambda[node_cells[l]] * U_2_left) /
-                              std::sqrt(pi * lambda[node_cells[l]]);
+            F2_fn_left[0] =
+              (rho_U_2_left + 0.5 * rho_n[node_cells[l]] / lambda[node_cells[l]]) * erf_term_pos[node_cells[l]] +
+              rho_U_n[node_cells[l]][0] * exp_term[node_cells[l]];
 
             F3_fn_left[0] = 0.5 * rho_U_n[node_cells[l]][0] * (U_2_left + 0.5 * (delta + 3) / lambda[node_cells[l]]) *
-                              (1. + std::erf(std::sqrt(lambda[node_cells[l]]) * U[node_cells[l]][0])) +
+                              erf_term_pos[node_cells[l]] +
                             0.5 * rho_n[node_cells[l]] * (U_2_left + 0.5 * (delta + 2) / lambda[node_cells[l]]) *
-                              std::exp(-lambda[node_cells[l]] * U_2_left) / std::sqrt(pi * lambda[node_cells[l]]);
+                              exp_term[node_cells[l]];
           } else {
             double U_2_right     = U[node_cells[l]][0] * U[node_cells[l]][0];
             double rho_U_2_right = rho_U_n[node_cells[l]][0] * U[node_cells[l]][0];
 
-            F2_fn_right[0] = (rho_U_2_right + 0.5 * rho_n[node_cells[l]] / lambda[node_cells[l]]) *
-                               (1. - std::erf(std::sqrt(lambda[node_cells[l]]) * U[node_cells[l]][0])) -
-                             rho_U_n[node_cells[l]][0] * std::exp(-lambda[node_cells[l]] * U_2_right) /
-                               std::sqrt(pi * lambda[node_cells[l]]);
+            F2_fn_right[0] =
+              (rho_U_2_right + 0.5 * rho_n[node_cells[l]] / lambda[node_cells[l]]) * erf_term_neg[node_cells[l]] -
+              rho_U_n[node_cells[l]][0] * exp_term[node_cells[l]];
 
             F3_fn_right[0] = 0.5 * rho_U_n[node_cells[l]][0] * (U_2_right + 0.5 * (delta + 3) / lambda[node_cells[l]]) *
-                               (1. - std::erf(std::sqrt(lambda[node_cells[l]]) * U[node_cells[l]][0])) -
+                               erf_term_neg[node_cells[l]] -
                              0.5 * rho_n[node_cells[l]] * (U_2_right + 0.5 * (delta + 2) / lambda[node_cells[l]]) *
-                               std::exp(-lambda[node_cells[l]] * U_2_right) / std::sqrt(pi * lambda[node_cells[l]]);
+                               exp_term[node_cells[l]];
           }
         }
 
@@ -215,13 +212,6 @@ class GKS2
       const size_t& nb_nodes = rho_flux_G.numberOfSubValues(cell_id);
       for (size_t r = 0; r < nb_nodes; r++) {
         rho[cell_id] -= dt / Vj[cell_id] * rho_flux_G(cell_id, r)[0];
-        // rho_U[cell_id] -= dt / Vj[cell_id] *
-        //                   ((1 - eta[cell_id]) * rho_U_flux_G(cell_id, r) + eta[cell_id] *
-        // (rho_U_flux_Ffn(cell_id,
-        //                   r)));
-        // rho_E[cell_id] -=
-        //   dt / Vj[cell_id] *
-        //   ((1 - eta[cell_id]) * rho_E_flux_G(cell_id, r)[0] + eta[cell_id] * (rho_E_flux_Ffn(cell_id, r)[0]));
         rho_U[cell_id] -=
           dt / Vj[cell_id] *
           (rho_U_flux_G(cell_id, r) + eta[cell_id] * (rho_U_flux_Ffn(cell_id, r) - rho_U_flux_G(cell_id, r)));
diff --git a/src/scheme/GKSNavier.cpp b/src/scheme/GKSNavier.cpp
index 29c4d8cf0..f85ae84ce 100644
--- a/src/scheme/GKSNavier.cpp
+++ b/src/scheme/GKSNavier.cpp
@@ -31,8 +31,10 @@ gks_inv_dt(const std::shared_ptr<const DiscreteFunctionVariant>& c_v,
 
         CellValue<double> local_inv_dt{mesh.connectivity()};
         parallel_for(
-          mesh.numberOfCells(),
-          PUGS_LAMBDA(CellId cell_id) { local_inv_dt[cell_id] = (c[cell_id] + abs(U[cell_id])) / Vj[cell_id]; });
+          mesh.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+            // local_inv_dt[cell_id] = (c[cell_id] + abs(U[cell_id])) / (Vj[cell_id]);
+            local_inv_dt[cell_id] = (c[cell_id] + abs(U[cell_id])) / (Vj[cell_id] * Vj[cell_id]);
+          });
 
         return max(local_inv_dt);
       } else {
@@ -46,7 +48,8 @@ template <MeshConcept MeshType>
 class GKSHandler::GKSNAVIER final : public GKSHandler::IGKSNAVIER
 {
  private:
-  static constexpr size_t Dimension = MeshType::Dimension;
+  static constexpr size_t Dimension   = MeshType::Dimension;
+  static constexpr size_t SIZEproblem = 2 + Dimension;
 
   using Rd   = TinyVector<Dimension>;
   using Rdxd = TinyMatrix<Dimension>;
@@ -59,67 +62,78 @@ class GKSHandler::GKSNAVIER final : public GKSHandler::IGKSNAVIER
   const double pi    = std::acos(-1);
   const double sqrt2 = std::sqrt(2);
 
-  TinyMatrix<2 + Dimension>
-  _computeInvMatrixM(const double& rho, const double& U, const double& lambda, const double& delta) const
+  TinyMatrix<SIZEproblem>
+  _computeInvM_bis(const double& rho,
+                   const Rd& rhoU,
+                   const double& rhoE,
+                   const Rd& U,
+                   const double& lambda,
+                   const double& delta) const
   {
-    const double sqrt_rho    = std::sqrt(rho);
-    const double sqrt_lambda = std::sqrt(lambda);
-    const double U_2         = U * U;
+    TinyMatrix<SIZEproblem> M;
+    double U_2 = U[0] * U[0];
+    double p   = rho / (2 * lambda);
 
-    TinyMatrix<2 + Dimension> invMatrixM(zero);
-    TinyMatrix<2 + Dimension> MatrixP(zero);
-    TinyMatrix<2 + Dimension> transposeMatrixP(zero);
-    TinyMatrix<2 + Dimension> invMatrixMtilde(zero);
+    M(0, 0) = rho;
+    M(1, 1) = rhoU[0] * U[0] + p;
+    M(2, 2) =
+      0.25 * rho * U_2 * U_2 + (delta + 4) * 0.5 * U_2 * p + 0.125 * (delta * delta + 6 * delta + 8) * p / lambda;
 
-    invMatrixMtilde(1, 1) = 1;
-    invMatrixMtilde(2, 2) = 1;
-    invMatrixMtilde(3, 3) = 2 * sqrt2 * lambda / (std::sqrt(delta + 1) * sqrt_rho);
+    M(1, 0) = rhoU[0];
+    M(0, 1) = M(1, 0);
 
-    MatrixP(1, 1) = 1 / sqrt_rho;
-    MatrixP(2, 2) = sqrt2 * sqrt_lambda / sqrt_rho;
-    MatrixP(3, 3) = 1;
-    MatrixP(2, 1) = -sqrt2 * sqrt_lambda * U / sqrt_rho;
-    MatrixP(3, 1) = 0.5 * U_2 - 0.25 * (delta + 1) / lambda;
-    MatrixP(3, 2) = -U;
+    M(2, 0) = rhoE;
+    M(0, 2) = M(2, 0);
 
-    invMatrixM = MatrixP * invMatrixMtilde * transpose(MatrixP);
+    M(2, 1) = 0.5 * rhoU[0] * U_2 + (delta + 4) * 0.5 * U[0] * p;
+    M(1, 2) = M(2, 1);
 
-    return invMatrixM;
+    const TinyMatrix<SIZEproblem> M_bis    = M;
+    const TinyMatrix<SIZEproblem> inverseM = inverse(M_bis);
+
+    return inverseM;
   }
 
-  TinyVector<2 + Dimension>
-  _compute_al(const TinyMatrix<2 + Dimension>& invMatrixM,
-              const DiscreteScalarFunction& rho,
-              const DiscreteVectorFunction& rhoU,
-              const DiscreteScalarFunction& rhoE)
-  {}
+  TinyMatrix<SIZEproblem>
+  _computeInvMatrixM(const double& rho, const Rd& U, const double& lambda, const double& delta) const
+  {
+    const double sqrt_rho = std::sqrt(rho);
+
+    const double sqrt_lambda = std::sqrt(lambda);
+    const double U_2         = U[0] * U[0];
+
+    TinyMatrix<SIZEproblem> invMatrixM(zero);
+    TinyMatrix<SIZEproblem> MatrixP(zero);
+    TinyMatrix<SIZEproblem> invMatrixP(zero);
+    TinyMatrix<SIZEproblem> invMatrixMtilde(zero);
 
-  TinyVector<2 + Dimension>
-  _compute_ar(const TinyMatrix<2 + Dimension>& invMatrixM)
-  {}
+    invMatrixMtilde(0, 0) = 1;
+    invMatrixMtilde(1, 1) = 1;
+    invMatrixMtilde(2, 2) = 2 * sqrt2 * lambda / (std::sqrt(delta + 1) * sqrt_rho);
 
-  TinyVector<2 + Dimension>
-  _compute_Al(const TinyMatrix<2 + Dimension>& invMatrixM)
-  {}
+    MatrixP(0, 0) = 1 / sqrt_rho;
+    MatrixP(1, 1) = sqrt2 * sqrt_lambda / sqrt_rho;
+    MatrixP(2, 2) = 1;
+    MatrixP(1, 0) = -sqrt2 * sqrt_lambda * U[0] / sqrt_rho;
+    MatrixP(2, 0) = 0.5 * U_2 - 0.25 * (delta + 1) / lambda;
+    MatrixP(2, 1) = -U[0];
 
-  TinyVector<2 + Dimension>
-  _compute_Ar(const TinyMatrix<2 + Dimension>& invMatrixM)
-  {}
+    invMatrixP(0, 0) = sqrt_rho;
+    invMatrixP(1, 1) = sqrt_rho / (sqrt2 * sqrt_lambda);
+    invMatrixP(2, 2) = 1;
+    invMatrixP(1, 0) = sqrt_rho * U[0];
+    invMatrixP(2, 0) = sqrt_rho * (0.5 * U_2 + 0.25 * (delta + 1) / lambda);
+    invMatrixP(2, 1) = U[0] * sqrt_rho / (sqrt2 * sqrt_lambda);
 
-  TinyVector<2 + Dimension>
-  _compute_bl(const TinyMatrix<2 + Dimension>& invMatrixM)
-  {}
+    invMatrixM = MatrixP * invMatrixMtilde * invMatrixP;
 
-  TinyVector<2 + Dimension>
-  _compute_br(const TinyMatrix<2 + Dimension>& invMatrixM)
-  {}
+    // std::cout << std ::endl << invMatrixP << std ::endl << std ::endl;
 
-  TinyVector<2 + Dimension>
-  _compute_B(const TinyMatrix<2 + Dimension>& invMatrixM)
-  {}
+    return invMatrixM;
+  }
 
   TinyVector<7>
-  _compute_u_moments(const double& rho, const Rd& U, const double& lambda)
+  _compute_u_moments(const double& rho, const Rd& U, const double& lambda) const
   {
     const double U_2      = U[0] * U[0];
     const double U_4      = U_2 * U_2;
@@ -133,11 +147,11 @@ class GKSHandler::GKSNAVIER final : public GKSHandler::IGKSNAVIER
     u_moments[4] = rho * (U_4 + 3 * U_2 / lambda + 0.75 / lambda_2);
     u_moments[5] = rho * U[0] * (U_4 + 5 * U_2 / lambda + 3.75 / lambda_2);
     u_moments[6] = rho * (U_4 * U_2 + 7.5 * U_4 / lambda + 11.25 * U_2 / lambda_2 + 1.875 / (lambda_2 * lambda));
-
     return u_moments;
   }
+
   TinyVector<2>
-  _compute_xi2_moments(const double& lambda, const double& delta)
+  _compute_xi2_moments(const double& lambda, const double& delta) const
   {
     TinyVector<2> xi2_moments;
     xi2_moments[0] = delta * 0.5 / lambda;
@@ -146,126 +160,222 @@ class GKSHandler::GKSNAVIER final : public GKSHandler::IGKSNAVIER
     return xi2_moments;
   }
 
-  TinyMatrix<2 + Dimension>
-  _computeMatrixC1(const double& rho,
-                   const Rd& U,
-                   const double& lambda,
-                   TinyVector<7> u_moments,
-                   TinyVector<2> xi2_moments)
+  std::tuple<CellValue<const TinyVector<SIZEproblem>>, CellValue<const TinyVector<SIZEproblem>>>
+  _computeGradConservVariables_for_a(const MeshType& mesh,
+                                     const DiscreteScalarFunction& rho,
+                                     const DiscreteVectorFunction& rhoU,
+                                     const DiscreteScalarFunction& rhoE) const
   {
-    const double U_2      = U[0] * U[0];
-    const double U_4      = U_2 * U_2;
-    const double lambda_2 = lambda * lambda;
+    auto& mesh_data = MeshDataManager::instance().getMeshData(mesh);
 
-    const double erf_term = (1 + std::erf(std::sqrt(lambda) * U[0]));
-    const double exp_term = std::exp(-lambda * U_2) / std::sqrt(pi * lambda);
+    auto Vj = mesh_data.Vj();
 
-    TinyMatrix<2 + Dimension> MatrixC1(zero);
+    auto node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix();
 
-    MatrixC1(1, 1) = u_moments[2] * erf_term + rho * U * exp_term;
-    MatrixC1(2, 2) = u_moments[4] * erf_term + rho * U * (U_2 + 2.5 / lambda) * exp_term;
-    MatrixC1(3, 3) =
-      (u_moments[6] + 2 * xi2_moments[0] * u_moments[4] + xi2_moments[1] * u_moments[2]) * erf_term +
-      rho * (U * (U_4 + 7 * U_2 / lambda + 8.25 / lambda_2) + 2 * xi2_moments[0] * U * (U_2 + 2.5 / lambda)) * exp_term;
-    MatrixC1(3, 3) *= 0.25;
+    CellValue<TinyVector<SIZEproblem>> Gradj{mesh.connectivity()};
+    Gradj.fill(zero);
+    CellValue<TinyVector<SIZEproblem>> Gradjpu{mesh.connectivity()};
+    Gradjpu.fill(zero);
 
-    MatrixC1(1, 1) *= 0.25;
-    MatrixC1(2, 2) *= 0.25;
-    MatrixC1(3, 3) *= 0.25;
+    NodeValue<double> rho_node_mean{mesh.connectivity()};
+    NodeValue<Rd> rhoU_node_mean{mesh.connectivity()};
+    NodeValue<double> rhoE_node_mean{mesh.connectivity()};
+    rho_node_mean.fill(0);
+    rhoU_node_mean.fill(zero);
+    rhoE_node_mean.fill(0);
 
-    MatrixC1(2, 1) = u_moments[3] + rho * (U_2 + 1 / lambda) * exp_term;
-    MatrixC1(2, 1) *= 0.5;
-    MatrixC1(3, 1) = 0.5 * (u_moments[4] + u_moments[2] * xi2_moments[1]) * erf_term +
-                     rho * U[0] * ((U_2 + 2.5 / lambda) + xi2_moments[0]) * exp_term;
-    MatrixC1(3, 1) *= 0.5;
-    MatrixC1(3, 2) = (u_moments[5] + u_moments[3] * xi2_moments[0]) * erf_term +
-                     rho * (U_4 + 4.5 * U_2 / lambda + 2 / lambda_2 + (U_2 + 1 / lambda) * xi2_moments[0]) * exp_term;
-    MatrixC1(3, 2) *= 0.5;
-
-    return (MatrixC1 + transpose(MatrixC1));
+    parallel_for(
+      mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+        const auto& node_cells = node_to_cell_matrix[node_id];
+
+        for (size_t l = 0; l < node_cells.size(); l++) {
+          if (node_cells.size() == 1)
+            continue;
+
+          rho_node_mean[node_id] += rho[node_cells[l]];
+          rhoU_node_mean[node_id] += rhoU[node_cells[l]];
+          rhoE_node_mean[node_id] += rhoE[node_cells[l]];
+        }
+
+        rho_node_mean[node_id] *= 0.5;
+        rhoU_node_mean[node_id] *= 0.5;
+        rhoE_node_mean[node_id] *= 0.5;
+
+        for (size_t l = 0; l < node_cells.size(); l++) {
+          if (node_cells.size() == 1) {
+            continue;
+          }
+          if (l == 0) {
+            Gradj[node_cells[l]][0] = (rho_node_mean[node_id] - rho[node_cells[l]]) / Vj[node_cells[l]];
+            Gradj[node_cells[l]][1] = (rhoU_node_mean[node_id][0] - rhoU[node_cells[l]][0]) / Vj[node_cells[l]];
+            Gradj[node_cells[l]][2] = (rhoE_node_mean[node_id] - rhoE[node_cells[l]]) / Vj[node_cells[l]];
+          } else {
+            Gradjpu[node_cells[l]][0] = (rho[node_cells[l]] - rho_node_mean[node_id]) / Vj[node_cells[l]];
+            Gradjpu[node_cells[l]][1] = (rhoU[node_cells[l]][0] - rhoU_node_mean[node_id][0]) / Vj[node_cells[l]];
+            Gradjpu[node_cells[l]][2] = (rhoE[node_cells[l]] - rhoE_node_mean[node_id]) / Vj[node_cells[l]];
+            ;
+          }
+        }
+      });
+    return {Gradj, Gradjpu};
   }
 
-  TinyMatrix<2 + Dimension>
-  _computeMatrixC2(const double& rho,
-                   const Rd& U,
-                   const double& lambda,
-                   TinyVector<7> u_moments,
-                   TinyVector<2> xi2_moments)
+  TinyVector<SIZEproblem>
+  _compute_a_and_b(const TinyMatrix<SIZEproblem>& invMatrixM, const TinyVector<SIZEproblem>& Grad) const
+  {
+    TinyVector<SIZEproblem> a = 2 * invMatrixM * Grad;
+    return a;
+  }
+
+  TinyVector<SIZEproblem>
+  _compute_A(const TinyMatrix<SIZEproblem>& invMatrixM,
+             const TinyMatrix<SIZEproblem>& C1_full,
+             const TinyVector<SIZEproblem>& a) const
+  {
+    TinyVector<SIZEproblem> A = (invMatrixM * C1_full) * a;
+    return A;
+  }
+
+  TinyVector<SIZEproblem>
+  _compute_B(const TinyMatrix<SIZEproblem>& invMatrixM_interface,
+             const TinyMatrix<SIZEproblem>& C0_semi_pos,
+             const TinyMatrix<SIZEproblem>& C0_semi_neg,
+             const TinyVector<SIZEproblem>& al,
+             const TinyVector<SIZEproblem>& ar) const
+  {
+    TinyVector<SIZEproblem> B = invMatrixM_interface * (C0_semi_pos * al + C0_semi_neg * ar);
+    return B;
+  }
+
+  std::tuple<CellValue<TinyVector<SIZEproblem>>, CellValue<TinyVector<SIZEproblem>>>
+  _computeGradConservVariables_for_b(const MeshType& mesh,
+                                     const DiscreteScalarFunction& rho,
+                                     const DiscreteVectorFunction& rhoU,
+                                     const DiscreteScalarFunction& rhoE,
+                                     const NodeValue<double>& rho_node,
+                                     const NodeValue<Rd>& rhoU_node,
+                                     const NodeValue<double>& rhoE_node) const
+  {
+    auto& mesh_data = MeshDataManager::instance().getMeshData(mesh);
+
+    auto Vj = mesh_data.Vj();
+
+    auto node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix();
+
+    CellValue<TinyVector<SIZEproblem>> Gradj{mesh.connectivity()};
+    Gradj.fill(zero);
+    CellValue<TinyVector<SIZEproblem>> Gradjpu{mesh.connectivity()};
+    Gradjpu.fill(zero);
+
+    parallel_for(
+      mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+        const auto& node_cells = node_to_cell_matrix[node_id];
+
+        for (size_t l = 0; l < node_cells.size(); l++) {
+          if (node_cells.size() == 1) {
+            continue;
+          }
+          if (l == 0) {
+            Gradj[node_cells[l]][0] = (rho_node[node_id] - rho[node_cells[l]]) / Vj[node_cells[l]];
+            Gradj[node_cells[l]][1] = (rhoU_node[node_id][0] - rhoU[node_cells[l]][0]) / Vj[node_cells[l]];
+            Gradj[node_cells[l]][2] = (rhoE_node[node_id] - rhoE[node_cells[l]]) / Vj[node_cells[l]];
+          } else {
+            Gradjpu[node_cells[l]][0] = (rho[node_cells[l]] - rho_node[node_id]) / Vj[node_cells[l]];
+            Gradjpu[node_cells[l]][1] = (rhoU[node_cells[l]][0] - rhoU_node[node_id][0]) / Vj[node_cells[l]];
+            Gradjpu[node_cells[l]][2] = (rhoE[node_cells[l]] - rhoE_node[node_id]) / Vj[node_cells[l]];
+            ;
+          }
+        }
+      });
+
+    return {Gradj, Gradjpu};
+  }
+
+  std::tuple<TinyMatrix<SIZEproblem>, TinyMatrix<SIZEproblem>, TinyMatrix<SIZEproblem>>
+  _computeMatricesC0C1C2_semi_moments(const double& rho,
+                                      const Rd& U,
+                                      const double& lambda,
+                                      const TinyVector<7>& u_moments,
+                                      const TinyVector<2>& xi2_moments,
+                                      const int& sign_u) const
   {
     const double U_2      = U[0] * U[0];
     const double U_4      = U_2 * U_2;
     const double lambda_2 = lambda * lambda;
 
-    const double erf_term = (1 - std::erf(std::sqrt(lambda) * U[0]));
-    const double exp_term = std::exp(-lambda * U_2) / std::sqrt(pi * lambda);
-
-    TinyMatrix<2 + Dimension> MatrixC2(zero);
+    const double erf_term = (1 + sign_u * std::erf(std::sqrt(lambda) * U[0]));
+    const double exp_term = rho * sign_u * std::exp(-lambda * U_2) / std::sqrt(pi * lambda);
+
+    TinyMatrix<SIZEproblem> MatrixC0(zero);
+    MatrixC0(0, 0) = 0.5 * u_moments[0] * erf_term;
+    MatrixC0(1, 1) = 0.5 * (u_moments[2] * erf_term + U[0] * exp_term);
+    MatrixC0(2, 2) = 0.5 * (u_moments[4] * erf_term + U[0] * (U_2 + 2.5 / lambda) * exp_term) +
+                     MatrixC0(0, 0) * xi2_moments[1] + 2 * MatrixC0(1, 1) * xi2_moments[0];
+    MatrixC0(2, 2) *= 0.25;
+
+    MatrixC0(1, 0) = 0.5 * (u_moments[1] * erf_term + exp_term);
+    MatrixC0(0, 1) = MatrixC0(1, 0);
+    MatrixC0(2, 0) = MatrixC0(1, 1) + MatrixC0(0, 0) * xi2_moments[0];
+    MatrixC0(2, 0) *= 0.5;
+    MatrixC0(0, 2) = MatrixC0(2, 0);
+    MatrixC0(2, 1) = 0.5 * (u_moments[3] * erf_term + (U_2 + 1 / lambda) * exp_term) + MatrixC0(1, 0) * xi2_moments[0];
+    MatrixC0(2, 1) *= 0.5;
+    MatrixC0(1, 2) = MatrixC0(2, 1);
+
+    TinyMatrix<SIZEproblem> MatrixC1(zero);
+    MatrixC1(0, 0) = MatrixC0(1, 0);
+    MatrixC1(1, 1) = 0.5 * (u_moments[3] * erf_term + (U_2 + 1 / lambda) * exp_term);
+    MatrixC1(2, 2) = 0.5 * (u_moments[5] * erf_term + (U_4 + 4.5 * U_2 / lambda + 2 / lambda_2) * exp_term) +
+                     MatrixC1(0, 0) * xi2_moments[1] + 2 * MatrixC1(1, 1) * xi2_moments[0];
+    MatrixC1(2, 2) *= 0.25;
 
-    MatrixC2(1, 1) = u_moments[2] * erf_term - rho * U * exp_term;
-    MatrixC2(2, 2) = u_moments[4] * erf_term - rho * U * (U_2 + 2.5 / lambda) * exp_term;
-    MatrixC2(3, 3) =
-      (u_moments[6] + 2 * xi2_moments[0] * u_moments[4] + xi2_moments[1] * u_moments[2]) * erf_term -
-      rho * (U * (U_4 + 7 * U_2 / lambda + 8.25 / lambda_2) + 2 * xi2_moments[0] * U * (U_2 + 2.5 / lambda)) * exp_term;
-    MatrixC2(3, 3) *= 0.25;
+    MatrixC1(1, 0) = MatrixC0(1, 1);
+    MatrixC1(0, 1) = MatrixC1(1, 0);
+    MatrixC1(2, 0) = MatrixC1(1, 1) + MatrixC1(0, 0) * xi2_moments[0];
+    MatrixC1(2, 0) *= 0.5;
+    MatrixC1(0, 2) = MatrixC1(2, 0);
+    MatrixC1(2, 1) =
+      0.5 * (u_moments[4] * erf_term + U[0] * (U_2 + 2.5 / lambda) * exp_term) + MatrixC1(1, 0) * xi2_moments[0];
+    MatrixC1(2, 1) *= 0.5;
+    MatrixC1(1, 2) = MatrixC1(2, 1);
 
-    MatrixC2(1, 1) *= 0.25;
+    TinyMatrix<SIZEproblem> MatrixC2(zero);
+    MatrixC2(0, 0) = MatrixC1(1, 0);
+    MatrixC2(1, 1) = 0.5 * (u_moments[4] * erf_term + U[0] * (U_2 + 2.5 / lambda) * exp_term);
+    MatrixC2(2, 2) = 0.5 * (u_moments[6] * erf_term + U[0] * (U_4 + 7 * U_2 / lambda + 8.25 / lambda_2) * exp_term) +
+                     MatrixC2(0, 0) * xi2_moments[1] + 2 * MatrixC2(1, 1) * xi2_moments[0];
     MatrixC2(2, 2) *= 0.25;
-    MatrixC2(3, 3) *= 0.25;
 
-    MatrixC2(2, 1) = u_moments[3] * erf_term - rho * (U_2 + 1 / lambda) * exp_term;
+    MatrixC2(1, 0) = MatrixC1(1, 1);
+    MatrixC2(0, 1) = MatrixC2(1, 0);
+    MatrixC2(2, 0) = MatrixC2(1, 1) + MatrixC2(0, 0) * xi2_moments[0];
+    MatrixC2(2, 0) *= 0.5;
+    MatrixC2(0, 2) = MatrixC2(2, 0);
+    MatrixC2(2, 1) = 0.5 * (u_moments[5] * erf_term + (U_4 + 4.5 * U_2 / lambda + 2 / lambda_2) * exp_term) +
+                     MatrixC2(1, 0) * xi2_moments[0];
     MatrixC2(2, 1) *= 0.5;
-    MatrixC2(3, 1) = 0.5 * (u_moments[4] + u_moments[2] * xi2_moments[1]) * erf_term -
-                     rho * U[0] * ((U_2 + 2.5 / lambda) + xi2_moments[0]) * exp_term;
-    MatrixC2(3, 1) *= 0.5;
-    MatrixC2(3, 2) = (u_moments[5] + u_moments[3] * xi2_moments[0]) * erf_term -
-                     rho * (U_4 + 4.5 * U_2 / lambda + 2 / lambda_2 + (U_2 + 1 / lambda) * xi2_moments[0]) * exp_term;
-    MatrixC2(3, 2) *= 0.5;
-
-    return (MatrixC2 + transpose(MatrixC2));
+    MatrixC2(1, 2) = MatrixC2(2, 1);
+
+    return {MatrixC0, MatrixC1, MatrixC2};
   }
 
-  TinyMatrix<2 + Dimension>
-  _computeMatrixC3(const double& rho,
-                   const Rd& U,
-                   const double& lambda,
-                   TinyVector<7> u_moments,
-                   TinyVector<2> xi2_moments)
+  TinyMatrix<SIZEproblem>
+  _computeMatricesC1_full_moments(const TinyVector<7>& u_moments, const TinyVector<2>& xi2_moments) const
   {
-    const double U_2      = U[0] * U[0];
-    const double U_4      = U_2 * U_2;
-    const double lambda_2 = lambda * lambda;
+    TinyMatrix<SIZEproblem> MatrixC1(zero);
+
+    MatrixC1(0, 0) = u_moments[1];
+    MatrixC1(1, 1) = u_moments[3];
+    MatrixC1(2, 2) = 0.25 * (u_moments[5] + 2 * xi2_moments[0] * u_moments[3] + xi2_moments[1] * u_moments[1]);
+
+    MatrixC1(1, 0) = u_moments[2];
+    MatrixC1(0, 1) = MatrixC1(1, 0);
+    MatrixC1(2, 0) = 0.5 * (u_moments[3] + u_moments[1] * xi2_moments[0]);
+    MatrixC1(0, 2) = MatrixC1(2, 0);
+    MatrixC1(2, 1) = (u_moments[4] + u_moments[2] * xi2_moments[0]);
+    MatrixC1(1, 2) = MatrixC1(2, 1);
 
-    const double erf_term = (1 - std::erf(std::sqrt(lambda) * U[0]));
-    const double exp_term = std::exp(-lambda * U_2) / std::sqrt(pi * lambda);
-
-    TinyMatrix<2 + Dimension> MatrixC3(zero);
-
-    MatrixC3(1, 1) = u_moments[2] * erf_term - rho * U * exp_term;
-    MatrixC3(2, 2) = u_moments[4] * erf_term - rho * U * (U_2 + 2.5 / lambda) * exp_term;
-    MatrixC3(3, 3) =
-      (u_moments[6] + 2 * xi2_moments[0] * u_moments[4] + xi2_moments[1] * u_moments[2]) * erf_term -
-      rho * (U * (U_4 + 7 * U_2 / lambda + 8.25 / lambda_2) + 2 * xi2_moments[0] * U * (U_2 + 2.5 / lambda)) * exp_term;
-    MatrixC3(3, 3) *= 0.25;
-
-    MatrixC3(1, 1) *= 0.5;
-    MatrixC3(2, 2) *= 0.5;
-    MatrixC3(3, 3) *= 0.5;
-
-    MatrixC3(2, 1) = u_moments[3] * erf_term - rho * (U_2 + 1 / lambda) * exp_term;
-    MatrixC3(2, 1) *= 0.5;
-    MatrixC3(3, 1) = 0.5 * (u_moments[4] + u_moments[2] * xi2_moments[1]) * erf_term -
-                     rho * U[0] * ((U_2 + 2.5 / lambda) + xi2_moments[0]) * exp_term;
-    MatrixC3(3, 1) *= 0.5;
-    MatrixC3(3, 2) = (u_moments[5] + u_moments[3] * xi2_moments[0]) * erf_term -
-                     rho * (U_4 + 4.5 * U_2 / lambda + 2 / lambda_2 + (U_2 + 1 / lambda) * xi2_moments[0]) * exp_term;
-    MatrixC3(3, 2) *= 0.5;
-
-    // Divide by 2 the diagonal because there are double terms on it after the addition of the matrix and its transpose
-    MatrixC3(1, 1) *= 0.5;
-    MatrixC3(2, 2) *= 0.5;
-    MatrixC3(3, 3) *= 0.5;
-
-    return (MatrixC3 + transpose(MatrixC3));
+    return {MatrixC1};
   }
 
  public:
@@ -275,51 +385,35 @@ class GKSHandler::GKSNAVIER final : public GKSHandler::IGKSNAVIER
   compute_fluxes(const std::shared_ptr<const DiscreteFunctionVariant>& rho_v,
                  const std::shared_ptr<const DiscreteFunctionVariant>& rhoU_v,
                  const std::shared_ptr<const DiscreteFunctionVariant>& rhoE_v,
-                 const std::shared_ptr<const DiscreteFunctionVariant>& tau_v,
+                 const double& viscosity,
                  const double& delta,
                  const double& dt) const
   {
     auto mesh_v          = getCommonMesh({rho_v, rhoU_v, rhoE_v});
     const MeshType& mesh = *mesh_v->get<MeshType>();
 
-    DiscreteFunctionP0<const double> tau_n = tau_v->get<DiscreteScalarFunction>();
-    CellValue<double> eta(mesh.connectivity());
-    for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-      if (tau_n[cell_id] == 0)
-        eta[cell_id] = 0;
-      else
-        eta[cell_id] = (tau_n[cell_id] / dt) * (1 - std::exp(-dt / tau_n[cell_id]));
-    }
-
     DiscreteScalarFunction rho_n  = rho_v->get<DiscreteScalarFunction>();
     DiscreteVectorFunction rhoU_n = rhoU_v->get<DiscreteVectorFunction>();
     DiscreteScalarFunction rhoE_n = rhoE_v->get<DiscreteScalarFunction>();
 
-    DiscreteFunctionP0<double> rho  = copy(rho_n);
-    DiscreteFunctionP0<Rd> rhoU     = copy(rhoU_n);
-    DiscreteFunctionP0<double> rhoE = copy(rhoE_n);
+    DiscreteScalarFunction rho  = copy(rho_n);
+    DiscreteVectorFunction rhoU = copy(rhoU_n);
+    DiscreteScalarFunction rhoE = copy(rhoE_n);
+
+    CellValue<double> lambda{mesh.connectivity()};
+    lambda.fill(1);
+    CellValue<Rd> U{mesh.connectivity()};
+    U.fill(zero);
 
     auto& mesh_data = MeshDataManager::instance().getMeshData(mesh);
 
+    auto Vj = mesh_data.Vj();
+
     auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix();
     auto node_to_cell_matrix = mesh.connectivity().nodeToCellMatrix();
 
     const NodeValuePerCell<const Rd> njr = mesh_data.njr();
 
-    NodeValuePerCell<Rd> rho_flux_G(mesh.connectivity());
-    NodeValuePerCell<Rd> rhoU_flux_G(mesh.connectivity());
-    NodeValuePerCell<Rd> rhoE_flux_G(mesh.connectivity());
-    rho_flux_G.fill(zero);
-    rhoU_flux_G.fill(zero);
-    rhoE_flux_G.fill(zero);
-
-    NodeValuePerCell<Rd> rho_flux_Ffn(mesh.connectivity());
-    NodeValuePerCell<Rd> rhoU_flux_Ffn(mesh.connectivity());
-    NodeValuePerCell<Rd> rhoE_flux_Ffn(mesh.connectivity());
-    rho_flux_Ffn.fill(zero);
-    rhoU_flux_Ffn.fill(zero);
-    rhoE_flux_Ffn.fill(zero);
-
     CellValue<double> rho_fluxes(mesh.connectivity());
     CellValue<Rd> rhoU_fluxes(mesh.connectivity());
     CellValue<double> rhoE_fluxes(mesh.connectivity());
@@ -327,21 +421,16 @@ class GKSHandler::GKSNAVIER final : public GKSHandler::IGKSNAVIER
     rhoU_fluxes.fill(zero);
     rhoE_fluxes.fill(0);
 
-    NodeValue<Rd> F2fn(mesh.connectivity());
-    NodeValue<Rd> F3fn(mesh.connectivity());
-    F2fn.fill(zero);
-    F3fn.fill(zero);
-
     NodeValue<double> rho_node(mesh.connectivity());
     NodeValue<Rd> rhoU_node(mesh.connectivity());
     NodeValue<double> rhoE_node(mesh.connectivity());
-    rho_node.fill(0);
+    NodeValue<Rd> U_node(mesh.connectivity());
+    NodeValue<double> lambda_node(mesh.connectivity());
+    rho_node.fill(1);
     rhoU_node.fill(zero);
-    rhoE_node.fill(0);
-    CellValue<double> lambda{mesh.connectivity()};
-    lambda.fill(1);
-    CellValue<Rd> U{mesh.connectivity()};
-    U.fill(zero);
+    rhoE_node.fill(1);
+    U_node.fill(zero);
+    lambda_node.fill(1);
 
     CellValue<double> err_function_term(mesh.connectivity());
     CellValue<double> exp_term(mesh.connectivity());
@@ -360,6 +449,36 @@ class GKSHandler::GKSNAVIER final : public GKSHandler::IGKSNAVIER
         exp_term[cell_id]          = std::exp(-lambda[cell_id] * U_2) / std::sqrt(pi * lambda[cell_id]);
       });
 
+    // NodeValue<double> rho_node_mean{mesh.connectivity()};
+    // NodeValue<Rd> rhoU_node_mean{mesh.connectivity()};
+    // NodeValue<double> rhoE_node_mean{mesh.connectivity()};
+    // NodeValue<Rd> U_node_mean{mesh.connectivity()};
+    // NodeValue<double> lambda_node_mean{mesh.connectivity()};
+
+    // rho_node_mean.fill(1);
+    // rhoU_node_mean.fill(zero);
+    // rhoE_node_mean.fill(1);
+    // U_node_mean.fill(zero);
+    // lambda_node_mean.fill(1);
+
+    // parallel_for(
+    //   mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+    //     const auto& node_cells = node_to_cell_matrix[node_id];
+
+    //     for (size_t l = 0; l < node_cells.size(); l++) {
+    //       if (node_cells.size() == 1)
+    //         continue;
+
+    //       rho_node_mean[node_id] += rho[node_cells[l]];
+    //       rhoU_node_mean[node_id] += rhoU[node_cells[l]];
+    //       rhoE_node_mean[node_id] += rhoE[node_cells[l]];
+    //     }
+
+    //     rho_node_mean[node_id] *= 0.5;
+    //     rhoU_node_mean[node_id] *= 0.5;
+    //     rhoE_node_mean[node_id] *= 0.5;
+    //   });
+
     parallel_for(
       mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
         const auto& node_cells = node_to_cell_matrix[node_id];
@@ -398,77 +517,179 @@ class GKSHandler::GKSNAVIER final : public GKSHandler::IGKSNAVIER
         rhoE_node[node_id]    = 0.5 * (rhoE_cell_left + rhoE_cell_right);
       });
 
+    parallel_for(
+      mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+        U_node[node_id][0]   = rhoU_node[node_id][0] / rho_node[node_id];
+        double rhoU2_node    = rhoU_node[node_id][0] * rhoU_node[node_id][0] / rho_node[node_id];
+        lambda_node[node_id] = (1 + delta) * rho_node[node_id] / (2 * (2 * rhoE_node[node_id] - rhoU2_node));
+      });
+
+    NodeValue<double> tau{mesh.connectivity()};
+    tau.fill(1);
+    NodeValue<double> eta{mesh.connectivity()};
+    eta.fill(1);
+    NodeValue<double> zeta{mesh.connectivity()};
+    zeta.fill(1);
+    for (NodeId node_id = 0; node_id < mesh.numberOfNodes(); ++node_id) {
+      tau[node_id] = viscosity * rho_node[node_id] / (2 * lambda_node[node_id]);
+      if (viscosity == 0) {
+        eta[node_id]  = 0;
+        zeta[node_id] = 0;
+      } else {
+        zeta[node_id] = std::exp(-dt / tau[node_id]);
+        eta[node_id]  = (tau[node_id] / dt) * (1 - zeta[node_id]);
+      }
+    }
+
+    auto [Gradj_for_a, Gradjpu_for_a] = this->_computeGradConservVariables_for_a(mesh, rho, rhoU, rhoE);
+    auto [Gradj_for_b, Gradjpu_for_b] =
+      this->_computeGradConservVariables_for_b(mesh, rho, rhoU, rhoE, rho_node, rhoU_node, rhoE_node);
+
+    NodeValue<TinyVector<SIZEproblem>> Fluxes_term{mesh.connectivity()};
+    Fluxes_term.fill(zero);
+
     parallel_for(
       mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
         const auto& node_cells = node_to_cell_matrix[node_id];
 
-        double F2_fn_left  = 0;
-        double F3_fn_left  = 0;
-        double F2_fn_right = 0;
-        double F3_fn_right = 0;
+        const double rho_left_node = rho[node_cells[0]] + 0.5 * Vj[node_cells[0]] * Gradj_for_a[node_cells[0]][0];
+        Rd rhoU_left_node;
+        rhoU_left_node[0]           = rhoU[node_cells[0]][0] + 0.5 * Vj[node_cells[0]] * Gradj_for_a[node_cells[0]][1];
+        const double rhoE_left_node = rhoE[node_cells[0]] + 0.5 * Vj[node_cells[0]] * Gradj_for_a[node_cells[0]][2];
 
-        for (size_t l = 0; l < node_cells.size(); l++) {
-          if (node_cells.size() == 1)
-            continue;
+        Rd U_left_node;
+        U_left_node[0] = rhoU_left_node[0] / rho_left_node;
+        const double lambda_left_node =
+          (1 + delta) * rho_left_node / (2 * (2 * rhoE_left_node - rhoU_left_node[0] * U_left_node[0]));
 
-          if (l == 0) {
-            double U_2_left    = U[node_cells[l]][0] * U[node_cells[l]][0];
-            double rhoU_2_left = rhoU_n[node_cells[l]][0] * U[node_cells[l]][0];
+        const double rho_right_node = rho[node_cells[1]] - 0.5 * Vj[node_cells[1]] * Gradj_for_a[node_cells[1]][0];
+        Rd rhoU_right_node;
+        rhoU_right_node[0]           = rhoU[node_cells[1]][0] - 0.5 * Vj[node_cells[1]] * Gradj_for_a[node_cells[1]][1];
+        const double rhoE_right_node = rhoE[node_cells[1]] - 0.5 * Vj[node_cells[1]] * Gradj_for_a[node_cells[1]][2];
 
-            F2_fn_left = (rhoU_2_left + 0.5 * rho_n[node_cells[l]] / lambda[node_cells[l]]) *
-                           (1. + err_function_term[node_cells[l]]) +
-                         rhoU_n[node_cells[l]][0] * exp_term[node_cells[l]];
+        // peut etre des + 0.5* ici (au dessus)
 
-            F3_fn_left = 0.5 * rhoU_n[node_cells[l]][0] * (U_2_left + 0.5 * (delta + 3) / lambda[node_cells[l]]) *
-                           (1. + err_function_term[node_cells[l]]) +
-                         0.5 * rho_n[node_cells[l]] * (U_2_left + 0.5 * (delta + 2) / lambda[node_cells[l]]) *
-                           exp_term[node_cells[l]];
-          } else {
-            double U_2_right    = U[node_cells[l]][0] * U[node_cells[l]][0];
-            double rhoU_2_right = rhoU_n[node_cells[l]][0] * U[node_cells[l]][0];
+        Rd U_right_node;
+        U_right_node[0] = rhoU_right_node[0] / rho_right_node;
+        const double lambda_right_node =
+          (1 + delta) * rho_right_node / (2 * (2 * rhoE_right_node - rhoU_right_node[0] * U_right_node[0]));
+
+        TinyVector<7> u_moments_left   = this->_compute_u_moments(rho_left_node, U_left_node, lambda_left_node);
+        TinyVector<2> xi2_moments_left = this->_compute_xi2_moments(lambda_left_node, delta);
+
+        TinyVector<7> u_moments_right   = this->_compute_u_moments(rho_right_node, U_right_node, lambda_right_node);
+        TinyVector<2> xi2_moments_right = this->_compute_xi2_moments(lambda_right_node, delta);
+
+        // TinyMatrix<SIZEproblem> InvM_left =
+        //   this->_computeInvMatrixM(rho_left_node, U_left_node, lambda_left_node, delta);
+        // TinyMatrix<SIZEproblem> InvM_right =
+        //   this->_computeInvMatrixM(rho_right_node, U_right_node, lambda_right_node, delta);
+        // TinyMatrix<SIZEproblem> InvMInterface =
+        //   this->_computeInvMatrixM(rho_node[node_id], U_node[node_id], lambda_node[node_id], delta);
 
-            F2_fn_right = (rhoU_2_right + 0.5 * rho_n[node_cells[l]] / lambda[node_cells[l]]) *
-                            (1. - err_function_term[node_cells[l]]) -
-                          rhoU_n[node_cells[l]][0] * exp_term[node_cells[l]];
+        TinyMatrix<SIZEproblem> InvM_left =
+          this->_computeInvM_bis(rho_left_node, rhoU_left_node, rhoE_left_node, U_left_node, lambda_left_node, delta);
+        TinyMatrix<SIZEproblem> InvM_right = this->_computeInvM_bis(rho_right_node, rhoU_right_node, rhoE_right_node,
+                                                                    U_right_node, lambda_right_node, delta);
+        TinyMatrix<SIZEproblem> InvMInterface =
+          this->_computeInvM_bis(rho_node[node_id], rhoU_node[node_id], rhoE_node[node_id], U_node[node_id],
+                                 lambda_node[node_id], delta);
 
-            F3_fn_right = 0.5 * rhoU_n[node_cells[l]][0] * (U_2_right + 0.5 * (delta + 3) / lambda[node_cells[l]]) *
-                            (1. - err_function_term[node_cells[l]]) -
-                          0.5 * rho_n[node_cells[l]] * (U_2_right + 0.5 * (delta + 2) / lambda[node_cells[l]]) *
-                            exp_term[node_cells[l]];
+        // std::cout << std::endl << "InvM : " << InvM << "\t and \t InvM_bis" << InvM_bis << std::endl;
+
+        TinyVector<SIZEproblem> al;
+        TinyVector<SIZEproblem> ar;
+        TinyVector<SIZEproblem> bl;
+        TinyVector<SIZEproblem> br;
+
+        for (size_t l = 0; l < node_cells.size(); l++) {
+          if (node_cells.size() == 1) {
+            continue;
+          }
+          if (l == 0) {
+            al = this->_compute_a_and_b(InvM_left, Gradj_for_a[node_cells[l]]);
+            bl = this->_compute_a_and_b(InvMInterface, Gradj_for_b[node_cells[l]]);
+          } else {
+            ar = this->_compute_a_and_b(InvM_right, Gradjpu_for_a[node_cells[l]]);
+            br = this->_compute_a_and_b(InvMInterface, Gradjpu_for_b[node_cells[l]]);
           }
         }
 
-        F2fn[node_id][0] = 0.5 * (F2_fn_left + F2_fn_right);
-        F3fn[node_id][0] = 0.5 * (F3_fn_left + F3_fn_right);
+        // TinyMatrix<SIZEproblem> Matrix_for_A_left =
+        //   this->_computeMatricesC1_full_moments(u_moments_left, xi2_moments_left);
+        // TinyMatrix<SIZEproblem> Matrix_for_A_right =
+        //   this->_computeMatricesC1_full_moments(u_moments_right, xi2_moments_right);
+
+        auto [C0_semi_pos_left, C1_semi_pos_left, C2_semi_pos_left] =
+          this->_computeMatricesC0C1C2_semi_moments(rho_left_node, U_left_node, lambda_left_node, u_moments_left,
+                                                    xi2_moments_left, 1);
+
+        auto [C0_semi_neg_right, C1_semi_neg_right, C2_semi_neg_right] =
+          this->_computeMatricesC0C1C2_semi_moments(rho_right_node, U_right_node, lambda_right_node, u_moments_right,
+                                                    xi2_moments_left, -1);
+
+        TinyVector<SIZEproblem> Al = this->_compute_A(InvM_left, C1_semi_pos_left, -al);
+        TinyVector<SIZEproblem> Ar = this->_compute_A(InvM_right, C1_semi_neg_right, -ar);
+
+        TinyVector<SIZEproblem> B = this->_compute_B(InvMInterface, C0_semi_pos_left, C0_semi_neg_right, al, ar);
+
+        TinyVector<SIZEproblem> Fluxes_from_a = (tau[node_id] * zeta[node_id] - tau[node_id] * eta[node_id]) *
+                                                (C2_semi_pos_left * al + C2_semi_neg_right * ar);
+        TinyVector<SIZEproblem> Fluxes_for_Navier =
+          -tau[node_id] * eta[node_id] *
+          ((C1_semi_pos_left * Al + C1_semi_neg_right * Ar) + (C2_semi_pos_left * al + C2_semi_neg_right * ar));
+
+        TinyVector<7> u_moments_interface =
+          this->_compute_u_moments(rho_node[node_id], U_node[node_id], lambda_node[node_id]);
+        TinyVector<2> xi2_moments_interface = this->_compute_xi2_moments(lambda_node[node_id], delta);
+
+        auto [C0_semi_pos_interface, C1_semi_pos_interface, C2_semi_pos_interface] =
+          this->_computeMatricesC0C1C2_semi_moments(rho_node[node_id], U_node[node_id], lambda_node[node_id],
+                                                    u_moments_interface, xi2_moments_interface, 1);
+        auto [C0_semi_neg_interface, C1_semi_neg_interface, C2_semi_neg_interface] =
+          this->_computeMatricesC0C1C2_semi_moments(rho_node[node_id], U_node[node_id], lambda_node[node_id],
+                                                    u_moments_interface, xi2_moments_interface, -1);
+
+        TinyMatrix<SIZEproblem> Matrix_for_B =
+          this->_computeMatricesC1_full_moments(u_moments_interface, xi2_moments_interface);
+
+        TinyVector<SIZEproblem> Fluxes_from_b = (2 * tau[node_id] * eta[node_id] - tau[node_id] * (1 + zeta[node_id])) *
+                                                (C2_semi_pos_interface * bl + C2_semi_neg_interface * br);
+        TinyVector<SIZEproblem> Fluxes_from_B =
+          (dt / 2 - tau[node_id] + tau[node_id] * eta[node_id]) * Matrix_for_B * B;
+
+        TinyVector<SIZEproblem> G;
+        G[0] = u_moments_interface[1];
+        G[1] = u_moments_interface[2];
+        G[2] = 0.5 * (u_moments_interface[3] + u_moments_interface[1] * xi2_moments_interface[0]);
+
+        TinyVector<SIZEproblem> F_fn;
+        F_fn[0] = C1_semi_pos_left(0, 0) + C1_semi_neg_right(0, 0);
+        F_fn[1] = C1_semi_pos_left(1, 0) + C1_semi_neg_right(1, 0);
+        F_fn[2] = C1_semi_pos_left(2, 0) + C1_semi_neg_right(2, 0);
+
+        Fluxes_term[node_id] = (1 - eta[node_id]) * G + eta[node_id] * F_fn + Fluxes_from_a + Fluxes_from_b +
+                               Fluxes_from_B + Fluxes_for_Navier;
       });
 
+    NodeValuePerCell<TinyVector<SIZEproblem>> Fluxes_term_apply{mesh.connectivity()};
+    Fluxes_term_apply.fill(zero);
+
     parallel_for(
       mesh.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
         const auto& cell_nodes = cell_to_node_matrix[cell_id];
 
         for (size_t r = 0; r < cell_nodes.size(); r++) {
-          double rhoU_2_node = rhoU_node[cell_nodes[r]][0] * rhoU_node[cell_nodes[r]][0] / rho_node[cell_nodes[r]];
-
-          rho_flux_G(cell_id, r) = rhoU_node[cell_nodes[r]][0] * njr(cell_id, r);
-          rhoU_flux_G(cell_id, r) =
-            (delta * rhoU_2_node / (1. + delta) + 2 * rhoE_node[cell_nodes[r]] / (1. + delta)) * njr(cell_id, r);
-          rhoE_flux_G(cell_id, r) =
-            rhoU_node[cell_nodes[r]][0] / rho_node[cell_nodes[r]] *
-            ((3. + delta) * rhoE_node[cell_nodes[r]] / (1. + delta) - rhoU_2_node / (1. + delta)) * njr(cell_id, r);
-
-          rhoU_flux_Ffn(cell_id, r) = F2fn[cell_nodes[r]][0] * njr(cell_id, r);
-          rhoE_flux_Ffn(cell_id, r) = F3fn[cell_nodes[r]][0] * njr(cell_id, r);
+          Fluxes_term_apply(cell_id, r) = njr(cell_id, r)[0] * Fluxes_term[cell_nodes[r]];
         }
       });
 
-    for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-      const size_t& nb_nodes = rho_flux_G.numberOfSubValues(cell_id);
+    for (CellId cell_id = 2; cell_id < mesh.numberOfCells() - 2; ++cell_id) {
+      const size_t& nb_nodes = Fluxes_term_apply.numberOfSubValues(cell_id);
       for (size_t r = 0; r < nb_nodes; r++) {
-        rho_fluxes[cell_id] += rho_flux_G(cell_id, r)[0];
-        rhoU_fluxes[cell_id] +=
-          ((1 - eta[cell_id]) * rhoU_flux_G(cell_id, r) + eta[cell_id] * (rhoU_flux_Ffn(cell_id, r)));
-        rhoE_fluxes[cell_id] +=
-          ((1 - eta[cell_id]) * rhoE_flux_G(cell_id, r)[0] + eta[cell_id] * (rhoE_flux_Ffn(cell_id, r)[0]));
+        rho_fluxes[cell_id] += Fluxes_term_apply(cell_id, r)[0];
+        rhoU_fluxes[cell_id][0] += Fluxes_term_apply(cell_id, r)[1];
+        rhoE_fluxes[cell_id] += Fluxes_term_apply(cell_id, r)[2];
       }
     }
 
@@ -547,13 +768,13 @@ class GKSHandler::GKSNAVIER final : public GKSHandler::IGKSNAVIER
   gksNavier(const std::shared_ptr<const DiscreteFunctionVariant>& rho_v,
             const std::shared_ptr<const DiscreteFunctionVariant>& rhoU_v,
             const std::shared_ptr<const DiscreteFunctionVariant>& rhoE_v,
-            const std::shared_ptr<const DiscreteFunctionVariant>& tau_v,
+            const double& viscosity,
             const double& delta,
             const double& dt) const
   {
     std::shared_ptr mesh_v = getCommonMesh({rho_v, rhoU_v, rhoE_v});
 
-    auto [rho_fluxes, rhoU_fluxes, rhoE_fluxes] = compute_fluxes(rho_v, rhoU_v, rhoE_v, tau_v, delta, dt);
+    auto [rho_fluxes, rhoU_fluxes, rhoE_fluxes] = compute_fluxes(rho_v, rhoU_v, rhoE_v, viscosity, delta, dt);
     return apply_fluxes(rho_v, rhoU_v, rhoE_v, rho_fluxes, rhoU_fluxes, rhoE_fluxes, dt);
   }
 
@@ -572,7 +793,11 @@ GKSHandler::GKSHandler(const std::shared_ptr<const MeshVariant>& mesh_v)
     [&](auto&& mesh) {
       using MeshType = mesh_type_t<decltype(mesh)>;
       if constexpr (is_polygonal_mesh_v<MeshType>) {
-        m_gks_navier = std::make_unique<GKSNAVIER<MeshType>>();
+        if constexpr (MeshType::Dimension == 1) {
+          m_gks_navier = std::make_unique<GKSNAVIER<MeshType>>();
+        } else {
+          throw NormalError("unexpected mesh type");
+        }
       } else {
         throw NormalError("unexpected mesh type");
       }
diff --git a/src/scheme/GKSNavier.hpp b/src/scheme/GKSNavier.hpp
index 571f87d83..2410d32d3 100644
--- a/src/scheme/GKSNavier.hpp
+++ b/src/scheme/GKSNavier.hpp
@@ -19,13 +19,6 @@ double gks_inv_dt(const std::shared_ptr<const DiscreteFunctionVariant>& c,
 
 class GKSHandler
 {
- public:
-  enum class SolverType
-  {
-    Glace,
-    Eucclhyd
-  };
-
  private:
   struct IGKSNAVIER
   {
@@ -35,7 +28,7 @@ class GKSHandler
     compute_fluxes(const std::shared_ptr<const DiscreteFunctionVariant>& rho_v,
                    const std::shared_ptr<const DiscreteFunctionVariant>& rhoU_v,
                    const std::shared_ptr<const DiscreteFunctionVariant>& rhoE_v,
-                   const std::shared_ptr<const DiscreteFunctionVariant>& tau_v,
+                   const double& viscosity,
                    const double& delta,
                    const double& dt) const = 0;
 
@@ -56,7 +49,7 @@ class GKSHandler
     gksNavier(const std::shared_ptr<const DiscreteFunctionVariant>& rho_v,
               const std::shared_ptr<const DiscreteFunctionVariant>& rhoU_v,
               const std::shared_ptr<const DiscreteFunctionVariant>& rhoE_v,
-              const std::shared_ptr<const DiscreteFunctionVariant>& tau_v,
+              const double& viscosity,
               const double& delta,
               const double& dt) const = 0;
 
-- 
GitLab