diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 96bc4057ca80d1ef413ea396522dd8179a46ccc9..a200fa2df84bcfeedef912f5b40e5f4e60b37468 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,7 +1,7 @@
 # gitlab-ci file for waves
 
 default:
-    image: rboman/waves
+    image: rboman/waves:2020.0
     before_script:
         - source /opt/intel/mkl/bin/mklvars.sh intel64
         - source /opt/intel/tbb/bin/tbbvars.sh intel64
@@ -35,7 +35,7 @@ build-py2-no-tlnos:
         - make -j 4
 
 build-py3:
-    image: rboman/waves-py3
+    image: rboman/waves-py3:2020.0
     stage: build
     script:
         - printenv | sort
@@ -71,7 +71,7 @@ ctest-py2:
         - build-py2
 
 ctest-py3:
-    image: rboman/waves-py3
+    image: rboman/waves-py3:2020.0
     stage: test
     script:
         - cd build
diff --git a/flow/src/wAdjoint.cpp b/flow/src/wAdjoint.cpp
index caf2b63a8663cda55c089db85f51a9ec08fea9bb..cbf88d7618f37d4383ca514bdf5681049a6281c5 100644
--- a/flow/src/wAdjoint.cpp
+++ b/flow/src/wAdjoint.cpp
@@ -32,11 +32,9 @@
 #include "wMem.h"
 #include "wResults.h"
 #include "wMshExport.h"
+#include "wMumpsInterface.h"
 
-#include "gmm_gmm.h"
-#include <gmm/gmm_MUMPS_interface.h>
-#include "gmm_extra.h"
-#include "std_extra.h"
+#include <Eigen/Sparse>
 
 #include <tbb/task_scheduler_init.h>
 #include <tbb/tbb.h>
@@ -82,26 +80,27 @@ void Adjoint::run()
     std::cout << "--- Adjoint solver ---\n"
               << "Number of threads: " << nthreads << "\n"
               << std::endl;
+    // Map solution vectors
+    Eigen::Map<Eigen::VectorXd> lambdaL_(lambdaL.data(), lambdaL.size()), lambdaD_(lambdaD.data(), lambdaD.size());
     // build SoE
-    gmm::csr_matrix<double> dR;
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dR(sol->pbl->msh->nodes.size(), sol->pbl->msh->nodes.size());
     buildDR(dR);
-    std::vector<double> dL(sol->pbl->msh->nodes.size()), dD(sol->pbl->msh->nodes.size());
-    buildDI(dL, dD);
-
-    // Solve linear SoE dR {lambdaL, lambdaD} = {dL, dD}
-    bool solve_success = gmm::MUMPS_solve(dR, lambdaL, dL);
-    if (!solve_success)
-        throw std::runtime_error("Problem occured in gmm::MUMPS_solve while solving for lift sensitivities!");
-    solve_success = gmm::MUMPS_solve(dR, lambdaD, dD);
-    if (!solve_success)
-        throw std::runtime_error("Problem occured in gmm::MUMPS_solve while solving for drag sensitivities!");
+    std::vector<double> dummyL(sol->pbl->msh->nodes.size()), dummyD(sol->pbl->msh->nodes.size()); // dummy bs vector
+    Eigen::Map<Eigen::VectorXd> dL_(dummyL.data(), dummyL.size()), dD_(dummyD.data(), dummyD.size());
+    buildDI(dL_, dD_);
+
+    // Setup linear solver and solve linear SoE dR {lambdaL, lambdaD} = {dL, dD}
+    std::map<std::string, int> mumpsOpt;
+    MumpsInterface mumps(mumpsOpt);
+    mumps.setSoE(dR, dL_);
+    mumps.compute(lambdaL_);
+    mumps.setSoE(dR, dD_);
+    mumps.compute(lambdaD_);
+    mumps.finalize();
 
     // Check residual
-    std::vector<double> rLambdaL(sol->pbl->msh->nodes.size()), rLambdaD(sol->pbl->msh->nodes.size()), dR_lambdaL(sol->pbl->msh->nodes.size()), dR_lambdaD(sol->pbl->msh->nodes.size());
-    gmm::mult(dR, lambdaL, dR_lambdaL);
-    gmm::mult(dR, lambdaD, dR_lambdaD);
-    gmm::add(dR_lambdaL, gmm::scaled(dL, -1.0), rLambdaL);
-    gmm::add(dR_lambdaD, gmm::scaled(dD, -1.0), rLambdaD);
+    Eigen::VectorXd rLambdaL = dR * lambdaL_ - dL_;
+    Eigen::VectorXd rLambdaD = dR * lambdaD_ - dD_;
 
     std::cout << std::setw(8) << "L_Iter"
               << std::setw(15) << "Sens[lambdaL]"
@@ -110,10 +109,10 @@ void Adjoint::run()
               << std::setw(15) << "Res[lambdaD]" << std::endl;
     std::cout << std::fixed << std::setprecision(5);
     std::cout << std::setw(8) << 0
-              << std::setw(15) << gmm::vect_norm2(lambdaL)
-              << std::setw(15) << gmm::vect_norm2(lambdaD)
-              << std::setw(15) << log10(gmm::vect_norm2(rLambdaL))
-              << std::setw(15) << log10(gmm::vect_norm2(rLambdaD)) << std::endl;
+              << std::setw(15) << lambdaL_.norm()
+              << std::setw(15) << lambdaD_.norm()
+              << std::setw(15) << log10(rLambdaL.norm())
+              << std::setw(15) << log10(rLambdaD.norm()) << std::endl;
 
     std::cout << ANSI_COLOR_YELLOW << "Warning: the adjoint solver is experimental and has not been validated!\n"
               << ANSI_COLOR_RESET << std::endl;
@@ -124,13 +123,14 @@ void Adjoint::run()
  * @authors Adrien Crovato
  * @todo Refactor, this is heavily copy-pasted from Newton::buildJ
  */
-void Adjoint::buildDR(gmm::csr_matrix<double> &dR)
+void Adjoint::buildDR(Eigen::SparseMatrix<double, Eigen::RowMajor> &dR)
 {
     // Multithread
     tbb::spin_mutex mutex;
 
-    // Temporary Jacobian matrix
-    gmm::row_matrix<gmm::wsvector<double>> A(sol->pbl->msh->nodes.size(), sol->pbl->msh->nodes.size());
+    // List of triplets to build Jacobian matrix
+    std::vector<Eigen::Triplet<double>> T;
+    T.reserve(sol->pbl->msh->nodes.size());
 
     // Full Potential Equation with upwind bias: analytical derivatives
     auto fluid = sol->pbl->medium;
@@ -160,40 +160,28 @@ void Adjoint::buildDR(gmm::csr_matrix<double> &dR)
 
             // 3rd term (mu*drhoU*grad_phi*grad_psi)
             Eigen::VectorXd bei;
-            std::vector<double> bejU(eU->nodes.size());
+            Eigen::RowVectorXd bejU(eU->nodes.size());
             e->buildDs(bei, sol->phi, Fct0C(1.));
             std::vector<double> gradU(sol->pbl->nDim);
             eU->computeGrad(sol->phi, gradU, 0);
-            std::vector<double> dNU(sol->pbl->nDim);
+            Eigen::VectorXd dNU(sol->pbl->nDim);
             Cache &cacheU = eU->getVCache();
-
-            /* TEMPO EIGEN/GMM */
-            Eigen::MatrixXd JJinvU = eU->getVMem().getJinv(0);
-            gmm::dense_matrix<double> JinvU(JJinvU.rows(), JJinvU.cols());
-            for (size_t i = 0; i < JinvU.nrows(); ++i)
-                for (size_t j = 0; j < JinvU.ncols(); ++j)
-                    JinvU(i, j) = JJinvU(i, j);
-
             for (size_t j = 0; j < eU->nodes.size(); ++j)
             {
-                gmm::mult(JinvU, gmm::col_vector(cacheU.dff[0][j]), gmm::col_vector(dNU));
-                for (size_t i = 0; i < sol->pbl->nDim; ++i)
-                    bejU[j] += gradU[i] * dNU[i];
+                dNU = eU->getVMem().getJinv(0) * Eigen::Map<Eigen::VectorXd>(cacheU.dff[0][j].data(), sol->pbl->nDim);
+                bejU(j) = Eigen::Map<Eigen::VectorXd>(gradU.data(), sol->pbl->nDim).dot(dNU);
             }
-            gmm::dense_matrix<double> Ae3(e->nodes.size(), eU->nodes.size());
-            for (size_t i = 0; i < e->nodes.size(); ++i)
-                for (size_t j = 0; j < eU->nodes.size(); ++j)
-                    Ae3(i, j) = mu * fluid->rho.evalD(*eU, sol->phi, 0) * bei[i] * bejU[j];
+            Eigen::MatrixXd Ae3(e->nodes.size(), eU->nodes.size());
+            Ae3 = mu * fluid->rho.evalD(*eU, sol->phi, 0) * bei * bejU;
 
             // 4th term (mu*rhoU*grad_phi*grad_psi)
             Eigen::MatrixXd Ae4;
             e->buildK(Ae4, sol->phi, Fct0C(1.));
             Ae4 *= mu * fluid->rho.eval(*eU, sol->phi, 0);
             // 5th term dmu*(rhoU-rho)*grad_phi*grad_psi
-            double deltaRho = fluid->rho.eval(*eU, sol->phi, 0) - fluid->rho.eval(*e, sol->phi, 0);
             Eigen::MatrixXd Ae5;
             e->buildDK(Ae5, sol->phi, fluid->mach);
-            Ae5 *= deltaRho * dmu;
+            Ae5 *= (fluid->rho.eval(*eU, sol->phi, 0) - fluid->rho.eval(*e, sol->phi, 0)) * dmu;
 
             // Assembly (supersonic)
             tbb::spin_mutex::scoped_lock lock(mutex);
@@ -203,12 +191,12 @@ void Adjoint::buildDR(gmm::csr_matrix<double> &dR)
                 for (size_t jj = 0; jj < e->nodes.size(); ++jj)
                 {
                     Node *nodj = e->nodes[jj];
-                    A(nodi->row, nodj->row) += Ae4(ii, jj) + Ae5(ii, jj);
+                    T.push_back(Eigen::Triplet<double>(nodi->row, nodj->row, Ae4(ii, jj) + Ae5(ii, jj)));
                 }
                 for (size_t jj = 0; jj < eU->nodes.size(); ++jj)
                 {
                     Node *nodj = eU->nodes[jj];
-                    A(nodi->row, nodj->row) += Ae3(ii, jj);
+                    T.push_back(Eigen::Triplet<double>(nodi->row, nodj->row, Ae3(ii, jj)));
                 }
             }
         }
@@ -220,10 +208,12 @@ void Adjoint::buildDR(gmm::csr_matrix<double> &dR)
             for (size_t jj = 0; jj < e->nodes.size(); ++jj)
             {
                 Node *nodj = e->nodes[jj];
-                A(nodi->row, nodj->row) += Ae1(ii, jj) + Ae2(ii, jj);
+                T.push_back(Eigen::Triplet<double>(nodi->row, nodj->row, Ae1(ii, jj) + Ae2(ii, jj)));
             }
         }
     });
+    // Build Jacobian matrix without BCs
+    dR.setFromTriplets(T.begin(), T.end());
     // Apply wake BCs for adjoint problem
     for (auto wake : sol->pbl->wBCs)
     {
@@ -241,9 +231,9 @@ void Adjoint::buildDR(gmm::csr_matrix<double> &dR)
                 for (size_t jj = 0; jj < we->nRow; ++jj)
                 {
                     Node *nodj = we->surUpE->nodes[jj];
-                    A(nodi->row, nodj->row) += 2 * Kupup(ii, jj);
+                    dR.coeffRef(nodi->row, nodj->row) += 2 * Kupup(ii, jj);
                     nodj = we->surLwE->nodes[jj];
-                    A(nodi->row, nodj->row) += Kuplw(ii, jj);
+                    dR.coeffRef(nodi->row, nodj->row) += Kuplw(ii, jj);
                 }
             }
             for (size_t ii = 0; ii < we->nColLw; ++ii)
@@ -252,9 +242,9 @@ void Adjoint::buildDR(gmm::csr_matrix<double> &dR)
                 for (size_t jj = 0; jj < we->nRow; ++jj)
                 {
                     Node *nodj = we->surUpE->nodes[jj];
-                    A(nodi->row, nodj->row) -= 2 * Klwup(ii, jj);
+                    dR.coeffRef(nodi->row, nodj->row) -= 2 * Klwup(ii, jj);
                     nodj = we->surLwE->nodes[jj];
-                    A(nodi->row, nodj->row) -= Klwlw(ii, jj);
+                    dR.coeffRef(nodi->row, nodj->row) -= Klwlw(ii, jj);
                 }
             }
         });
@@ -264,8 +254,13 @@ void Adjoint::buildDR(gmm::csr_matrix<double> &dR)
     {
         for (auto nod : dBC->nodes)
         {
-            A.row(nod->row) = gmm::wsvector<double>(sol->pbl->msh->nodes.size());
-            A(nod->row, nod->row) = 1.0;
+            for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(dR, nod->row); it; ++it)
+            {
+                if (it.row() == it.col())
+                    it.valueRef() = 1.;
+                else
+                    it.valueRef() = 0.;
+            }
         }
     }
     for (auto fBC : sol->pbl->fBCs)
@@ -274,17 +269,24 @@ void Adjoint::buildDR(gmm::csr_matrix<double> &dR)
         {
             for (auto nod : e->nodes)
             {
-                A.row(nod->row) = gmm::wsvector<double>(sol->pbl->msh->nodes.size());
-                A(nod->row, nod->row) = 1.0;
+                for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(dR, nod->row); it; ++it)
+                {
+                    if (it.row() == it.col())
+                        it.valueRef() = 1.;
+                    else
+                        it.valueRef() = 0.;
+                }
             }
         }
     }
     // Transpose
-    gmm::transposed(A);
-    gmm::copy(A, dR);
+    dR.transpose();
+    // Clean matrix and turn to compressed row format
+    dR.prune(0.);
+    dR.makeCompressed();
 
     if (verbose)
-        std::cout << "dR (" << gmm::mat_nrows(dR) << "," << gmm::mat_ncols(dR) << ") nnz=" << gmm::nnz(dR) << "\n";
+        std::cout << "J (" << dR.rows() << "," << dR.cols() << ") nnz=" << dR.nonZeros() << "\n";
 }
 
 /**
@@ -292,7 +294,7 @@ void Adjoint::buildDR(gmm::csr_matrix<double> &dR)
  * @authors Adrien Crovato
  * @todo Refactor, to be checked
  */
-void Adjoint::buildDI(std::vector<double> &dL, std::vector<double> &dD)
+void Adjoint::buildDI(Eigen::Map<Eigen::VectorXd> &dL, Eigen::Map<Eigen::VectorXd> &dD)
 {
     // Multithread
     tbb::spin_mutex mutex;
@@ -319,8 +321,8 @@ void Adjoint::buildDI(std::vector<double> &dL, std::vector<double> &dD)
             for (size_t ii = 0; ii < eV->nodes.size(); ++ii)
             {
                 Node *nodi = eV->nodes[ii];
-                dL[nodi->row] += factorL * be(ii);
-                dD[nodi->row] += factorD * be(ii);
+                dL(nodi->row) += factorL * be(ii);
+                dD(nodi->row) += factorD * be(ii);
             }
         });
     }
@@ -329,8 +331,8 @@ void Adjoint::buildDI(std::vector<double> &dL, std::vector<double> &dD)
     {
         for (auto nod : dBC->nodes)
         {
-            dL[nod->row] = 0.;
-            dD[nod->row] = 0.;
+            dL(nod->row) = 0.;
+            dD(nod->row) = 0.;
         }
     }
     for (auto fBC : sol->pbl->fBCs)
@@ -339,16 +341,16 @@ void Adjoint::buildDI(std::vector<double> &dL, std::vector<double> &dD)
         {
             for (auto nod : e->nodes)
             {
-                dL[nod->row] = 0.;
-                dD[nod->row] = 0.;
+                dL(nod->row) = 0.;
+                dD(nod->row) = 0.;
             }
         }
     }
 
     if (verbose)
     {
-        std::cout << "dL (" << gmm::vect_size(dL) << ")\n";
-        std::cout << "dD (" << gmm::vect_size(dD) << ")\n";
+        std::cout << "dL (" << dL.size() << ")\n";
+        std::cout << "dD (" << dD.size() << ")\n";
     }
 }
 
diff --git a/flow/src/wAdjoint.h b/flow/src/wAdjoint.h
index cf752783fd039dd1ad44c2037b4d2cfc390982fd..8cecd60761c272b615e24e9fd9e0f49531c0bf14 100644
--- a/flow/src/wAdjoint.h
+++ b/flow/src/wAdjoint.h
@@ -20,12 +20,10 @@
 #include "flow.h"
 #include "wObject.h"
 
-#include "gmm_gmm.h"
-
 #include <iostream>
 #include <vector>
 #include <memory>
-#include <Eigen/Dense>
+#include <Eigen/Sparse>
 
 namespace flow
 {
@@ -58,8 +56,8 @@ public:
 #endif
 
 private:
-    void buildDR(gmm::csr_matrix<double> &dR);
-    void buildDI(std::vector<double> &dL, std::vector<double> &dD);
+    void buildDR(Eigen::SparseMatrix<double, Eigen::RowMajor> &dR);
+    void buildDI(Eigen::Map<Eigen::VectorXd> &dL, Eigen::Map<Eigen::VectorXd> &dD);
     void buildWake(WakeElement *&we, Eigen::MatrixXd &Kupup, Eigen::MatrixXd &Kuplw, Eigen::MatrixXd &Klwup, Eigen::MatrixXd &Klwlw);
     void buildBoundary(tbox::Element *&e, tbox::Element *&eV, Eigen::RowVectorXd &be);
 };
diff --git a/flow/src/wAssign.h b/flow/src/wAssign.h
index 4ce9492e7b7c0a774d8c7e04e77b3259cfe3488d..81b8ab0440d96b0627dd8465ff010974000256cd 100644
--- a/flow/src/wAssign.h
+++ b/flow/src/wAssign.h
@@ -22,6 +22,7 @@
 #include "wF0Ps.h"
 #include <vector>
 #include <string>
+#include <Eigen/Dense>
 
 namespace flow
 {
diff --git a/flow/src/wFpeLSFunction.cpp b/flow/src/wFpeLSFunction.cpp
index b13cf9e4774bdb9fc79dbdd82f0dcb06706512d7..061ba4c016b814293e19263354ee63126cfc0776 100644
--- a/flow/src/wFpeLSFunction.cpp
+++ b/flow/src/wFpeLSFunction.cpp
@@ -19,9 +19,8 @@ using namespace tbox;
 using namespace flow;
 
 FpeLSFunction::FpeLSFunction(flow::Newton &_solver, std::vector<double> &_u,
-                             std::vector<double> &_u0, std::vector<double> &_du,
-                             std::vector<double> &_fRes) : LSFunction(),
-                                                           solver(_solver), u(_u), u0(_u0), du(_du), fRes(_fRes)
+                             std::vector<double> &_u0, Eigen::Map<Eigen::VectorXd> &_du,
+                             Eigen::Map<Eigen::VectorXd> &_fRes) : LSFunction(), solver(_solver), u(_u), u0(_u0), du(_du), fRes(_fRes)
 {
 }
 
@@ -29,7 +28,7 @@ FpeLSFunction::FpeLSFunction(flow::Newton &_solver, std::vector<double> &_u,
  * @brief Update initial and change in solution 
  * @authors Adrien Crovato
  */
-void FpeLSFunction::update(std::vector<double> &_u0, std::vector<double> &_du)
+void FpeLSFunction::update(std::vector<double> &_u0, Eigen::Map<Eigen::VectorXd> &_du)
 {
     u0 = _u0;
     du = _du;
@@ -41,12 +40,10 @@ void FpeLSFunction::update(std::vector<double> &_u0, std::vector<double> &_du)
  */
 double FpeLSFunction::eval(double alpha)
 {
-    double resNorm = 0.;
-    gmm::add(u0, gmm::scaled(du, alpha), u);
-    gmm::clear(fRes);
+    for (size_t i = 0; i < u.size(); ++i)
+        u[i] = u0[i] + alpha * du(i);
     solver.buildRes(fRes);
-    resNorm = gmm::vect_norm2(fRes);
-    return resNorm;
+    return fRes.norm();
 }
 
 void FpeLSFunction::write(std::ostream &out) const
diff --git a/flow/src/wFpeLSFunction.h b/flow/src/wFpeLSFunction.h
index 4261bd348409c84db9d606980a8c5306e9c06175..4b947e79788da12eeda76c6aa81cdbc1a49ec22e 100644
--- a/flow/src/wFpeLSFunction.h
+++ b/flow/src/wFpeLSFunction.h
@@ -34,15 +34,15 @@ namespace flow
 class FLOW_API FpeLSFunction : public tbox::LSFunction
 {
 private:
-    flow::Newton &solver;      ///< solver object (must be of Newton type)
-    std::vector<double> &u;    ///< solution
-    std::vector<double> u0;    ///< initial solution
-    std::vector<double> du;    ///< change in solution
-    std::vector<double> &fRes; ///< residual vector
+    flow::Newton &solver;              ///< solver object (must be of Newton type)
+    std::vector<double> &u;            ///< solution
+    std::vector<double> u0;            ///< initial solution
+    Eigen::Map<Eigen::VectorXd> du;    ///< change in solution
+    Eigen::Map<Eigen::VectorXd> &fRes; ///< residual vector
 
 public:
-    FpeLSFunction(flow::Newton &_solver, std::vector<double> &_u, std::vector<double> &_u0, std::vector<double> &_du, std::vector<double> &_fRes);
-    void update(std::vector<double> &_u0, std::vector<double> &_du);
+    FpeLSFunction(flow::Newton &_solver, std::vector<double> &_u, std::vector<double> &_u0, Eigen::Map<Eigen::VectorXd> &_du, Eigen::Map<Eigen::VectorXd> &_fRes);
+    void update(std::vector<double> &_u0, Eigen::Map<Eigen::VectorXd> &_du);
     virtual double eval(double alpha) override;
 
 #ifndef SWIG
diff --git a/flow/src/wNewton.cpp b/flow/src/wNewton.cpp
index fef2dcb1d832cba156fa6df1dd87d6c0fb3c1a4a..4a77d5516ddaf8d92f648beb449f36fc4944d325 100644
--- a/flow/src/wNewton.cpp
+++ b/flow/src/wNewton.cpp
@@ -31,11 +31,9 @@
 #include "wLinesearch.h"
 #include "wMem.h"
 #include "wCache.h"
+#include "wMumpsInterface.h"
 
-#include "gmm_gmm.h"
-#include <gmm/gmm_MUMPS_interface.h>
-#include "gmm_extra.h"
-#include "std_extra.h"
+#include <Eigen/Sparse>
 
 #include <tbb/task_scheduler_init.h>
 #include <tbb/tbb.h>
@@ -101,19 +99,24 @@ bool Newton::run()
     int avC = 0;       // adaptive viscosity counter
     bool rUpwd = true; // update best upwind element
 
+    // Setup linear (inner) solver MUMPS
+    std::map<std::string, int> mumpsOpt;
+    MumpsInterface mumps(mumpsOpt);
+
+    // Residual and change in solution
+    std::vector<double> dPhi(phi.size()); // dummy dPhi
+    Eigen::Map<Eigen::VectorXd> rPhi_(rPhi.data(), rPhi.size()), dPhi_(dPhi.data(), dPhi.size());
+
     // Find initial best upwind element
     findUpwd();
     // Compute initial residual
-    buildRes(rPhi);
-    double resInit = gmm::vect_norm2(rPhi);
-    double absRes = gmm::vect_norm2(rPhi);
+    buildRes(rPhi_);
+    double resInit = rPhi_.norm();
+    double absRes = rPhi_.norm();
     double relRes = absRes / resInit;
 
-    // Change in solution
-    std::vector<double> deltaPhi(phi.size(), 0.);
-
     // Linesearch
-    FpeLSFunction fpe(*this, phi, phi, deltaPhi, rPhi);
+    FpeLSFunction fpe(*this, phi, phi, dPhi_, rPhi_);
     BankRoseLS ls(fpe);
     //FleuryLS ls(fpe, 1., 1e-2, false);
     ls.set(maxLsIt, lsTol, verbose);
@@ -166,17 +169,16 @@ bool Newton::run()
         }
 
         // Build Jacobian matrix
-        gmm::csr_matrix<double> J;
+        Eigen::SparseMatrix<double, Eigen::RowMajor> J(pbl->msh->nodes.size(), pbl->msh->nodes.size());
         buildJac(J);
 
         // Solve linear SoE J \DeltaPhi = R
-        bool solve_success = gmm::MUMPS_solve(J, deltaPhi, rPhi);
-        if (!solve_success)
-            throw std::runtime_error("Problem occured in gmm::MUMPS_solve!");
+        mumps.setSoE(J, rPhi_);
+        mumps.compute(dPhi_);
         itC++;
 
         // Compute new solution and residual with linesearch
-        fpe.update(phi, deltaPhi);
+        fpe.update(phi, dPhi_);
         try
         {
             ls.run();
@@ -184,7 +186,7 @@ bool Newton::run()
         catch (...)
         {
         }
-        absRes = gmm::vect_norm2(rPhi);
+        absRes = rPhi_.norm();
         relRes = absRes / resInit;
         // Re-find best upwind element
         if (rUpwd)
@@ -215,6 +217,9 @@ bool Newton::run()
             continue;
     } while (itC < maxIt);
 
+    // Terminate MUMPS
+    mumps.finalize();
+
     // Compute field variables
     Solver::computeFlow();
 
@@ -243,13 +248,14 @@ bool Newton::run()
  * @brief Build the Jacobian (tangent) matrix
  * @authors Adrien Crovato
  */
-void Newton::buildJac(gmm::csr_matrix<double> &J)
+void Newton::buildJac(Eigen::SparseMatrix<double, Eigen::RowMajor> &J)
 {
     // Multithread
     tbb::spin_mutex mutex;
 
-    // Temporary Jacobian matrix
-    gmm::row_matrix<gmm::wsvector<double>> A(pbl->msh->nodes.size(), pbl->msh->nodes.size());
+    // List of triplets to build Jacobian matrix
+    std::vector<Eigen::Triplet<double>> T;
+    T.reserve(pbl->msh->nodes.size());
 
     // Full Potential Equation with upwind bias: analytical derivatives
     auto fluid = pbl->medium;
@@ -278,30 +284,19 @@ void Newton::buildJac(gmm::csr_matrix<double> &J)
 
             // 3rd term (mu*drhoU*grad_phi*grad_psi)
             Eigen::VectorXd bei;
-            std::vector<double> bejU(eU->nodes.size());
+            Eigen::RowVectorXd bejU(eU->nodes.size());
             e->buildDs(bei, phi, Fct0C(1.));
             std::vector<double> gradU(pbl->nDim);
             eU->computeGrad(phi, gradU, 0);
-            std::vector<double> dNU(pbl->nDim);
+            Eigen::VectorXd dNU(pbl->nDim);
             Cache &cacheU = eU->getVCache();
-
-            /* TEMPO EIGEN/GMM */
-            Eigen::MatrixXd JJinvU = eU->getVMem().getJinv(0);
-            gmm::dense_matrix<double> JinvU(JJinvU.rows(), JJinvU.cols());
-            for (size_t i = 0; i < JinvU.nrows(); ++i)
-                for (size_t j = 0; j < JinvU.ncols(); ++j)
-                    JinvU(i, j) = JJinvU(i, j);
-
             for (size_t j = 0; j < eU->nodes.size(); ++j)
             {
-                gmm::mult(JinvU, gmm::col_vector(cacheU.dff[0][j]), gmm::col_vector(dNU));
-                for (size_t i = 0; i < pbl->nDim; ++i)
-                    bejU[j] += gradU[i] * dNU[i];
+                dNU = eU->getVMem().getJinv(0) * Eigen::Map<Eigen::VectorXd>(cacheU.dff[0][j].data(), pbl->nDim);
+                bejU(j) = Eigen::Map<Eigen::VectorXd>(gradU.data(), pbl->nDim).dot(dNU);
             }
-            gmm::dense_matrix<double> Ae3(e->nodes.size(), eU->nodes.size());
-            for (size_t i = 0; i < e->nodes.size(); ++i)
-                for (size_t j = 0; j < eU->nodes.size(); ++j)
-                    Ae3(i, j) = mu * fluid->rho.evalD(*eU, phi, 0) * bei[i] * bejU[j];
+            Eigen::MatrixXd Ae3(e->nodes.size(), eU->nodes.size());
+            Ae3 = mu * fluid->rho.evalD(*eU, phi, 0) * bei * bejU;
 
             // 4th term (mu*rhoU*grad_phi*grad_psi)
             Eigen::MatrixXd Ae4;
@@ -309,10 +304,9 @@ void Newton::buildJac(gmm::csr_matrix<double> &J)
             Ae4 *= mu * fluid->rho.eval(*eU, phi, 0);
 
             // 5th term dmu*(rhoU-rho)*grad_phi*grad_psi
-            double deltaRho = fluid->rho.eval(*eU, phi, 0) - fluid->rho.eval(*e, phi, 0);
             Eigen::MatrixXd Ae5;
             e->buildDK(Ae5, phi, fluid->mach);
-            Ae5 *= deltaRho * dmu;
+            Ae5 *= (fluid->rho.eval(*eU, phi, 0) - fluid->rho.eval(*e, phi, 0)) * dmu;
 
             // Assembly (supersonic)
             tbb::spin_mutex::scoped_lock lock(mutex);
@@ -322,12 +316,12 @@ void Newton::buildJac(gmm::csr_matrix<double> &J)
                 for (size_t jj = 0; jj < e->nodes.size(); ++jj)
                 {
                     Node *nodj = e->nodes[jj];
-                    A(nodi->row, nodj->row) += Ae4(ii, jj) + Ae5(ii, jj);
+                    T.push_back(Eigen::Triplet<double>(nodi->row, nodj->row, Ae4(ii, jj) + Ae5(ii, jj)));
                 }
                 for (size_t jj = 0; jj < eU->nodes.size(); ++jj)
                 {
                     Node *nodj = eU->nodes[jj];
-                    A(nodi->row, nodj->row) += Ae3(ii, jj);
+                    T.push_back(Eigen::Triplet<double>(nodi->row, nodj->row, Ae3(ii, jj)));
                 }
             }
         }
@@ -339,10 +333,12 @@ void Newton::buildJac(gmm::csr_matrix<double> &J)
             for (size_t jj = 0; jj < e->nodes.size(); ++jj)
             {
                 Node *nodj = e->nodes[jj];
-                A(nodi->row, nodj->row) += Ae1(ii, jj) + Ae2(ii, jj);
+                T.push_back(Eigen::Triplet<double>(nodi->row, nodj->row, Ae1(ii, jj) + Ae2(ii, jj)));
             }
         }
     });
+    // Build Jacobian matrix without BCs
+    J.setFromTriplets(T.begin(), T.end());
 
     // Kutta condition (3 steps): analytical derivatives
     // Egality of normal flux through the wake
@@ -353,11 +349,17 @@ void Newton::buildJac(gmm::csr_matrix<double> &J)
         tbb::parallel_do(wake->nodMap.begin(), wake->nodMap.end(), [&](std::pair<Node *, Node *> p) {
             int idxUp = p.first->row;
             int idxLw = p.second->row;
-            for (auto node : pbl->msh->nodes)
+            // first grab upper row values...
+            std::vector<Eigen::Triplet<double>> tUp;
+            tUp.reserve(pbl->msh->nodes.size() / 10);
+            for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(J, idxUp); it; ++it)
             {
-                A(idxLw, node->row) = A(idxLw, node->row) + A(idxUp, node->row);
-                A(idxUp, node->row) = 0.;
+                tUp.push_back(Eigen::Triplet<double>(it.row(), it.col(), it.value()));
+                it.valueRef() = 0.;
             }
+            // ...then add them to lower row values
+            for (auto t : tUp)
+                J.coeffRef(idxLw, t.col()) += t.value();
         });
     }
     // Egality of velocity norm through the wake
@@ -377,12 +379,12 @@ void Newton::buildJac(gmm::csr_matrix<double> &J)
                 for (size_t jj = 0; jj < we->nColUp; ++jj)
                 {
                     Node *nodj = we->volUpE->nodes[jj];
-                    A(nodi->row, nodj->row) += Aue(ii, jj);
+                    J.coeffRef(nodi->row, nodj->row) += Aue(ii, jj);
                 }
                 for (size_t jj = 0; jj < we->nColLw; ++jj)
                 {
                     Node *nodj = we->volLwE->nodes[jj];
-                    A(nodi->row, nodj->row) -= Ale(ii, jj);
+                    J.coeffRef(nodi->row, nodj->row) -= Ale(ii, jj);
                 }
             }
         });
@@ -404,7 +406,7 @@ void Newton::buildJac(gmm::csr_matrix<double> &J)
                 for (size_t jj = 0; jj < ke->nCol; ++jj)
                 {
                     Node *nodj = ke->volE->nodes[jj];
-                    A(nodi->row, nodj->row) += Ae(ii, jj);
+                    J.coeffRef(nodi->row, nodj->row) += Ae(ii, jj);
                 }
             }
         });
@@ -415,25 +417,36 @@ void Newton::buildJac(gmm::csr_matrix<double> &J)
     {
         for (auto nod : dBC->nodes)
         {
-            A.row(nod->row) = gmm::wsvector<double>(pbl->msh->nodes.size());
-            A(nod->row, nod->row) = 1.0;
+            for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(J, nod->row); it; ++it)
+            {
+                if (it.row() == it.col())
+                    it.valueRef() = 1.;
+                else
+                    it.valueRef() = 0.;
+            }
         }
     }
 
-    gmm::copy(A, J);
+    // Clean matrix and turn to compressed row format
+    J.prune(0.);
+    J.makeCompressed();
+
     if (verbose)
-        std::cout << "J (" << gmm::mat_nrows(J) << "," << gmm::mat_ncols(J) << ") nnz=" << gmm::nnz(J) << "\n";
+        std::cout << "J (" << J.rows() << "," << J.cols() << ") nnz=" << J.nonZeros() << "\n";
 }
 
 /**
  * @brief Build the residual vector
  * @authors Adrien Crovato
  */
-void Newton::buildRes(std::vector<double> &R)
+void Newton::buildRes(Eigen::Map<Eigen::VectorXd> &R)
 {
     // Multithread
     tbb::spin_mutex mutex;
 
+    // Reset residual
+    R.setZero();
+
     // Full Potential Equation with upwind bias
     auto fluid = pbl->medium;
     tbb::parallel_do(fluid->adjMap.begin(), fluid->adjMap.end(), [&](std::pair<Element *, std::vector<Element *>> p) {
@@ -462,7 +475,7 @@ void Newton::buildRes(std::vector<double> &R)
             for (size_t ii = 0; ii < e->nodes.size(); ++ii)
             {
                 Node *nodi = e->nodes[ii];
-                R[nodi->row] -= beU(ii);
+                R(nodi->row) -= beU(ii);
             }
         }
         // Assembly (subsonic)
@@ -470,7 +483,7 @@ void Newton::buildRes(std::vector<double> &R)
         for (size_t ii = 0; ii < e->nodes.size(); ++ii)
         {
             Node *nodi = e->nodes[ii];
-            R[nodi->row] -= be(ii);
+            R(nodi->row) -= be(ii);
         }
     });
     // Apply freestream (Neumann) BCs
@@ -486,7 +499,7 @@ void Newton::buildRes(std::vector<double> &R)
             for (size_t ii = 0; ii < e->nodes.size(); ++ii)
             {
                 Node *nodi = e->nodes[ii];
-                R[nodi->row] += be(ii);
+                R(nodi->row) += be(ii);
             }
         });
     }
@@ -503,7 +516,7 @@ void Newton::buildRes(std::vector<double> &R)
             for (size_t ii = 0; ii < p.first->nodes.size(); ++ii)
             {
                 Node *nodi = p.first->nodes[ii];
-                R[nodi->row] += be(ii);
+                R(nodi->row) += be(ii);
             }
         });
     }
@@ -518,8 +531,8 @@ void Newton::buildRes(std::vector<double> &R)
         tbb::parallel_do(wake->nodMap.begin(), wake->nodMap.end(), [&](std::pair<Node *, Node *> p) {
             int idxUp = p.first->row;
             int idxLw = p.second->row;
-            R[idxLw] = R[idxLw] + R[idxUp];
-            R[idxUp] = 0.;
+            R(idxLw) += R[idxUp];
+            R(idxUp) = 0.;
         });
     }
     // Egality of velocity norm through the wake
@@ -536,8 +549,8 @@ void Newton::buildRes(std::vector<double> &R)
             for (size_t ii = 0; ii < we->nRow; ++ii)
             {
                 Node *nodi = we->wkN[ii].second;
-                R[nodi->row] -= bue(ii);
-                R[nodi->row] += ble(ii);
+                R(nodi->row) -= bue(ii);
+                R(nodi->row) += ble(ii);
             }
         });
     }
@@ -556,7 +569,7 @@ void Newton::buildRes(std::vector<double> &R)
             for (size_t ii = 0; ii < ke->nRow; ++ii)
             {
                 Node *nodi = ke->teN[ii].second;
-                R[nodi->row] -= be(ii);
+                R(nodi->row) -= be(ii);
             }
         });
     }
@@ -565,11 +578,11 @@ void Newton::buildRes(std::vector<double> &R)
     for (auto dBC : pbl->dBCs)
     {
         for (auto nod : dBC->nodes)
-            R[nod->row] = 0.;
+            R(nod->row) = 0.;
     }
 
     if (verbose)
-        std::cout << "R (" << gmm::vect_size(R) << ")\n";
+        std::cout << "R (" << R.size() << ")\n";
 }
 
 /**
diff --git a/flow/src/wNewton.h b/flow/src/wNewton.h
index 86f287f11cd9d541b995561db206abcf22cfd9ba..bfa19aa6cdeac258230610d2d26cbe567f655c18 100644
--- a/flow/src/wNewton.h
+++ b/flow/src/wNewton.h
@@ -23,7 +23,7 @@
 #include <iostream>
 #include <vector>
 #include <memory>
-#include "gmm_gmm.h"
+#include <Eigen/Sparse>
 
 namespace flow
 {
@@ -56,8 +56,8 @@ public:
 #endif
 
 private:
-    void buildJac(gmm::csr_matrix<double> &J);
-    void buildRes(std::vector<double> &R);
+    void buildJac(Eigen::SparseMatrix<double, Eigen::RowMajor> &J);
+    void buildRes(Eigen::Map<Eigen::VectorXd> &R);
     void findUpwd();
 };
 
diff --git a/flow/src/wPicard.cpp b/flow/src/wPicard.cpp
index f472a7df272f1181d9c312d8a4d552e8051eed93..bbee1333e0b0ed894cff1d9c70ecdf320e9a5840 100644
--- a/flow/src/wPicard.cpp
+++ b/flow/src/wPicard.cpp
@@ -27,12 +27,10 @@
 #include "wTag.h"
 #include "wNode.h"
 #include "wElement.h"
-
-#include "gmm_gmm.h"
-#include "gmm_extra.h"
-#include "std_extra.h"
 #include "wMumpsInterface.h"
 
+#include <Eigen/Sparse>
+
 #include <tbb/task_scheduler_init.h>
 #include <tbb/tbb.h>
 #include <tbb/parallel_do.h>
@@ -92,7 +90,9 @@ bool Picard::run()
     int it = 0;
     double resInit = 1.;
     double absRes = 1., relRes = 1.;
-    std::vector<double> phiOld(phi.size()); // old solution (for relaxation)
+    // Solution, residual and old solution
+    Eigen::Map<Eigen::VectorXd> phi_(phi.data(), phi.size()), rPhi_(rPhi.data(), rPhi.size());
+    Eigen::VectorXd phiOld(phi.size());
 
     // Setup linear (inner) solver MUMPS
     std::map<std::string, int> mumpsOpt;
@@ -107,19 +107,18 @@ bool Picard::run()
     do
     {
         // Reinitialize SoE
-        gmm::csr_matrix<double> A;
-        std::vector<double> b(pbl->msh->nodes.size());
+        Eigen::SparseMatrix<double, Eigen::RowMajor> A(pbl->msh->nodes.size(), pbl->msh->nodes.size());
+        std::vector<double> b(pbl->msh->nodes.size()); // dummy b vector
+        Eigen::Map<Eigen::VectorXd> b_ = Eigen::Map<Eigen::VectorXd>(b.data(), b.size());
 
         // Build system of equations A \phi = b
         build(A, b);
 
         // Compute residual
-        std::vector<double> A_phi(pbl->msh->nodes.size());
-        gmm::mult(A, phi, A_phi);
-        gmm::add(A_phi, gmm::scaled(b, -1.0), rPhi);
+        rPhi_ = A * phi_ - b_;
         if (it == 0)
-            resInit = gmm::vect_norm2(rPhi);
-        absRes = gmm::vect_norm2(rPhi);
+            resInit = rPhi_.norm();
+        absRes = rPhi_.norm();
         relRes = absRes / resInit;
 
         // Compute aerodynamic coefficient on boundaries
@@ -141,17 +140,14 @@ bool Picard::run()
         it++;
 
         // Transfer old solution
-        phiOld = phi;
+        phiOld = phi_;
 
         // Solve linear SoE
-        mumps.setSoE(A, b);
-        mumps.analyze(); // could be done once at first iteration if structure does not change
-        mumps.factorize();
-        mumps.solve(phi);
+        mumps.setSoE(A, b_);
+        mumps.compute(phi_);
 
         // Relaxation
-        for (size_t i = 0; i < phi.size(); ++i)
-            phi[i] = (1 - relax) * phiOld[i] + relax * phi[i];
+        phi_ = (1 - relax) * phiOld + relax * phi_;
     } while (it < maxIt);
 
     // Terminate MUMPS
@@ -185,13 +181,14 @@ bool Picard::run()
  * @brief Build LHS matrix and RHS vector for Picard iteration
  * @authors Adrien Crovato
  */
-void Picard::build(gmm::csr_matrix<double> &A, std::vector<double> &b)
+void Picard::build(Eigen::SparseMatrix<double, Eigen::RowMajor> &A, std::vector<double> &b)
 {
     // Multithread
     tbb::spin_mutex mutex;
 
-    // Matrix to be built
-    gmm::row_matrix<gmm::wsvector<double>> localA(pbl->msh->nodes.size(), pbl->msh->nodes.size());
+    // List of triplets to build matrix
+    std::vector<Eigen::Triplet<double>> T;
+    T.reserve(pbl->msh->nodes.size());
 
     auto fluid = pbl->medium;
     tbb::parallel_do(fluid->adjMap.begin(), fluid->adjMap.end(), [&](std::pair<Element *, std::vector<Element *>> p) {
@@ -210,18 +207,25 @@ void Picard::build(gmm::csr_matrix<double> &A, std::vector<double> &b)
             for (size_t jj = 0; jj < e->nodes.size(); ++jj)
             {
                 Node *nodj = e->nodes[jj];
-                localA(nodi->row, nodj->row) += Ae(ii, jj);
+                T.push_back(Eigen::Triplet<double>(nodi->row, nodj->row, Ae(ii, jj)));
             }
         }
     });
+    // Build matrix without BCs
+    A.setFromTriplets(T.begin(), T.end());
 
     // Apply Dirichlet BCs to A matrix
     for (auto dBC : pbl->dBCs)
     {
         for (auto nod : dBC->nodes)
         {
-            localA.row(nod->row) = gmm::wsvector<double>(pbl->msh->nodes.size());
-            localA(nod->row, nod->row) = 1.0;
+            for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(A, nod->row); it; ++it)
+            {
+                if (it.row() == it.col())
+                    it.valueRef() = 1.;
+                else
+                    it.valueRef() = 0.;
+            }
         }
     }
 
@@ -273,12 +277,19 @@ void Picard::build(gmm::csr_matrix<double> &A, std::vector<double> &b)
         tbb::parallel_do(wake->nodMap.begin(), wake->nodMap.end(), [&](std::pair<Node *, Node *> p) {
             int idxUp = p.first->row;
             int idxLw = p.second->row;
-            for (auto node : pbl->msh->nodes)
+            // first grab upper row values...
+            std::vector<Eigen::Triplet<double>> tUp;
+            tUp.reserve(pbl->msh->nodes.size() / 10);
+            for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(A, idxUp); it; ++it)
             {
-                localA(idxLw, node->row) = localA(idxLw, node->row) + localA(idxUp, node->row);
-                localA(idxUp, node->row) = 0.;
+                tUp.push_back(Eigen::Triplet<double>(it.row(), it.col(), it.value()));
+                it.valueRef() = 0.;
             }
-            b[idxLw] = b[idxLw] + b[idxUp];
+            // ...then add them to lower row values
+            for (auto t : tUp)
+                A.coeffRef(idxLw, t.col()) += t.value();
+            // same for rhs
+            b[idxLw] += b[idxUp];
             b[idxUp] = 0.;
         });
     }
@@ -299,12 +310,12 @@ void Picard::build(gmm::csr_matrix<double> &A, std::vector<double> &b)
                 for (size_t jj = 0; jj < we->volUpE->nodes.size(); ++jj)
                 {
                     Node *nodj = we->volUpE->nodes[jj];
-                    localA(nodi->row, nodj->row) += Aue(ii, jj);
+                    A.coeffRef(nodi->row, nodj->row) += Aue(ii, jj);
                 }
                 for (size_t jj = 0; jj < we->volLwE->nodes.size(); ++jj)
                 {
                     Node *nodj = we->volLwE->nodes[jj];
-                    localA(nodi->row, nodj->row) -= Ale(ii, jj);
+                    A.coeffRef(nodi->row, nodj->row) -= Ale(ii, jj);
                 }
             }
         });
@@ -326,17 +337,20 @@ void Picard::build(gmm::csr_matrix<double> &A, std::vector<double> &b)
                 for (size_t jj = 0; jj < ke->nCol; ++jj)
                 {
                     Node *nodj = ke->volE->nodes[jj];
-                    localA(nodi->row, nodj->row) += Ae(ii, jj);
+                    A.coeffRef(nodi->row, nodj->row) += Ae(ii, jj);
                 }
             }
         });
     }
 
-    gmm::copy(localA, A);
+    // Clean matrix and turn to compressed row format
+    A.prune(0.);
+    A.makeCompressed();
+
     if (verbose)
     {
-        std::cout << "A (" << gmm::mat_nrows(A) << "," << gmm::mat_ncols(A) << ") nnz=" << gmm::nnz(A) << "\n";
-        std::cout << "b (" << gmm::vect_size(b) << ")\n";
+        std::cout << "A (" << A.rows() << "," << A.cols() << ") nnz=" << A.nonZeros() << "\n";
+        std::cout << "b (" << b.size() << ")\n";
     }
 }
 
diff --git a/flow/src/wPicard.h b/flow/src/wPicard.h
index 525d236596f0221149a84da0d34a21852ea79b0c..4237f8509aa70c2431ffb70baec1782029b5940d 100644
--- a/flow/src/wPicard.h
+++ b/flow/src/wPicard.h
@@ -23,7 +23,7 @@
 #include <iostream>
 #include <vector>
 #include <memory>
-#include "gmm_gmm.h"
+#include <Eigen/Sparse>
 
 namespace flow
 {
@@ -48,7 +48,7 @@ public:
 #endif
 
 private:
-    void build(gmm::csr_matrix<double> &A, std::vector<double> &b);
+    void build(Eigen::SparseMatrix<double, Eigen::RowMajor> &A, std::vector<double> &b);
 };
 
 } // namespace flow
diff --git a/heat/src/wSolver.cpp b/heat/src/wSolver.cpp
index 88e802ada22f80e6c0828e8b411069942525e1b5..7fb301cf62d2fd40fafae92f21134c97caca13ed 100644
--- a/heat/src/wSolver.cpp
+++ b/heat/src/wSolver.cpp
@@ -31,10 +31,9 @@
 #include "wPeriodic.h"
 #include "wResults.h"
 #include "wMshExport.h"
-#include "gmm_gmm.h"
-#include <gmm/gmm_MUMPS_interface.h>
-#include "gmm_extra.h"
-#include "std_extra.h"
+#include "wMumpsInterface.h"
+
+#include <Eigen/Sparse>
 #include <typeinfo>
 #include <algorithm>
 #include <iomanip>
@@ -95,8 +94,8 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
         n->row = i++;
 
     // setup results
-    T1.resize(msh->nodes.size());
-    gmm::clear(T1); // in case of restart
+    T1.resize(msh->nodes.size(), 0.); // in case of restart
+    Eigen::Map<Eigen::VectorXd> T1_(T1.data(), T1.size());
 
     Results results;
     results.scalars_at_nodes["T"] = &T1;
@@ -138,6 +137,10 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
     }
     tms["prepro"].stop(); // ----- end of preprocessing phase
 
+    // Setup linear (inner) solver MUMPS
+    std::map<std::string, int> mumpsOpt;
+    MumpsInterface mumps(mumpsOpt);
+
     // --------------------------------------------------------------------
     // Iteration LOOP
     // --------------------------------------------------------------------
@@ -145,12 +148,13 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
     int it = 0;
     while (it <= itmax)
     {
-        std::vector<double> rhs(msh->nodes.size()); // RHS
+        std::vector<double> rhs(msh->nodes.size());                                             // dummy RHS
+        Eigen::Map<Eigen::VectorXd> rhs_ = Eigen::Map<Eigen::VectorXd>(rhs.data(), rhs.size()); // actual RHS
 
         tms["Kass"].start();
         // K matrix assembly
-        gmm::csr_matrix<double> K;
-        buildK(K, rhs);
+        Eigen::SparseMatrix<double, Eigen::RowMajor> K(msh->nodes.size(), msh->nodes.size());
+        buildK(K, rhs_);
         //tomatlab("K.txt", K);
         //throw std::runtime_error("stop!");
         tms["Kass"].stop();
@@ -159,8 +163,8 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
 
         if (!pbl->pdic)
         {
-            builds(rhs); // sources
-            buildq(rhs); // prescribed fluxes (=sources over lines)
+            builds(rhs_); // sources
+            buildq(rhs_); // prescribed fluxes (=sources over lines)
 
             // BCs
             for (auto dBC : pbl->dBCs) // apply Dirichlet BCs to RHS
@@ -173,18 +177,14 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
         tms["residual"].start();
 
         // method #1
-
-        std::vector<double> qint(msh->nodes.size());
-        gmm::mult(K, T1, qint); // calcul de q_int = K T1:
+        Eigen::VectorXd qint = K * T1_; // calcul de q_int = K T1:
 
         // method #2
         if (0)
         {
-            std::vector<double> qint2(msh->nodes.size());
+            Eigen::VectorXd qint2;
             buildqint(qint2); // calcul de q_int par integration du flux sur l'elem
-            std::vector<double> diff(msh->nodes.size());
-            gmm::add(qint, gmm::scaled(qint2, -1.0), diff);
-            double normdiff = gmm::vect_norm2(diff);
+            double normdiff = (qint - qint2).norm();
             //if(verbose>1)
             std::cout << "norm diff=" << normdiff << '\n';
         }
@@ -194,10 +194,9 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
             std::cout << i << ":" << qint[i] << "," << qint2[i] << "\n";
         */
 
-        std::vector<double> res(msh->nodes.size()); // residual
-        gmm::add(qint, gmm::scaled(rhs, -1.0), res);
-        double rnorm2 = gmm::vect_norm2(res);
-        double rnorminf = gmm::vect_norminf(res);
+        Eigen::VectorXd res = qint - rhs_; // residual
+        double rnorm2 = res.norm();
+        double rnorminf = res.lpNorm<Eigen::Infinity>();
         tms["residual"].stop();
 
         if (verbose > 0)
@@ -216,9 +215,13 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
         tms["solve"].start();
         if (verbose > 1)
             std::cout << "solving...\n";
-        bool ok = gmm::MUMPS_solve(K, T1, rhs);
-        if (!ok)
-            throw std::runtime_error("problem while solving system with MUMPS");
+        mumps.setSoE(K, rhs_);
+        if (it == 1)
+        {
+            mumps.analyze();
+        }
+        mumps.factorize();
+        mumps.solve(T1_);
         tms["solve"].stop();
     }
     if (it > itmax)
@@ -227,6 +230,9 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
         std::cout << "           in less than " << itmax << " iterations!\n\n"; // TODO: generer une erreur!
     }
 
+    // Terminate MUMPS
+    mumps.finalize();
+
     // --------------------------------------------------------------------
     // Postprocessing
     // --------------------------------------------------------------------
@@ -260,7 +266,7 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
     results.tensors_at_elems["k [tensor]"] = &kmoy;
 
     double Vtot = 0.0;
-    std::vector<double> qM(2);
+    Eigen::Vector2d qM = Eigen::Vector2d::Zero();
     for (auto mat : pbl->media)
     {
         tbb::parallel_do(mat->tag->elems.begin(), mat->tag->elems.end(),
@@ -270,11 +276,8 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
                              //std::cout << "processing element #" << e->no << "\n";
 
                              // (flux moyen . volume) sur l'element
-                             Eigen::VectorXd qqV;
-                             e->computeqV(T1, qqV, mat->k);
-                             std::vector<double> qV(qqV.rows());
-                             for (size_t ii = 0; ii < qqV.rows(); ++ii)
-                                qV[ii] = qqV(ii);
+                             Eigen::VectorXd qV;
+                             e->computeqV(T1, qV, mat->k);
 
                              double V = e->computeV();
 
@@ -289,33 +292,31 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
                                  gradT[i] = Pt(grad[0], grad[1]);
 
                                  // mean q over the element
-                                 kgradT[i] = Pt(qV[0] / V, qV[1] / V);
+                                 kgradT[i] = Pt(qV(0) / V, qV(1) / V);
 
                                  // mean k over the element
-                                 Eigen::MatrixXd kkmean;
-                                 e->computeV(T1, kkmean, mat->k);
-                                 gmm::dense_matrix<double> kmean(kkmean.rows(), kkmean.cols());
-                                 for (size_t ii = 0; ii < kkmean.rows(); ++ii)
-                                     for (size_t jj = 0; jj < kkmean.cols(); ++jj)
-                                         kmean(ii, jj) = kkmean(ii, jj);
-                                 gmm::scale(kmean, 1. / V);
+                                 Eigen::MatrixXd kmean;
+                                 e->computeV(T1, kmean, mat->k);
+                                 kmean /= V;
                                  kmoy11[i] = kmean(0, 0);
                                  kmoy22[i] = kmean(1, 1);
                                  kmoy12[i] = kmean(0, 1);
                                  kmoy21[i] = kmean(1, 0);
 
                                  // tensor
-                                 gmm::resize(kmean, 3, 3);
-                                 gmm::copy(kmean, kmoy[i]);
+                                 kmean.conservativeResize(3, 3);
+                                 for (size_t ii = 0; ii < kmean.rows(); ++ii)
+                                     for (size_t jj = 0; jj < kmean.cols(); ++jj)
+                                         kmoy[i](ii, jj) = kmean(ii, jj);
                                  //std::cout << kmean << '\n';
                              }
 
                              tbb::spin_mutex::scoped_lock lock(mutex);
                              Vtot += V;
-                             gmm::add(qV, qM); // qM = qM + qV
+                             qM += qV; // qM = qM + qV
                          });
     }
-    gmm::scale(qM, 1. / Vtot);
+    qM /= Vtot;
 
     if (verbose > 1)
     {
@@ -326,11 +327,11 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
     // periodic BCs
     if (pbl->pdic)
     {
-        pbl->pdic->qM = Pt(qM[0], qM[1]);
+        pbl->pdic->qM = Pt(qM(0), qM(1));
         if (verbose > 1)
         {
-            std::cout << "=> k_x = " << ((pbl->pdic->dTxM == 0.0) ? 0 : qM[0] / pbl->pdic->dTxM) << '\n';
-            std::cout << "=> k_y = " << ((pbl->pdic->dTyM == 0.0) ? 0 : qM[1] / pbl->pdic->dTyM) << '\n';
+            std::cout << "=> k_x = " << ((pbl->pdic->dTxM == 0.0) ? 0 : qM(0) / pbl->pdic->dTxM) << '\n';
+            std::cout << "=> k_y = " << ((pbl->pdic->dTyM == 0.0) ? 0 : qM(1) / pbl->pdic->dTyM) << '\n';
         }
     }
 
@@ -352,12 +353,15 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
     }
 }
 
-void Solver::buildK(gmm::csr_matrix<double> &K2, std::vector<double> &rhs)
+void Solver::buildK(Eigen::SparseMatrix<double, Eigen::RowMajor> &K2, Eigen::Map<Eigen::VectorXd> &rhs)
 {
     tbb::spin_mutex mutex;
 
     std::shared_ptr<MshData> msh = pbl->msh;
-    gmm::row_matrix<gmm::wsvector<double>> K(msh->nodes.size(), msh->nodes.size());
+
+    // List of triplets to build matrix
+    std::vector<Eigen::Triplet<double>> T;
+    T.reserve(msh->nodes.size());
 
     // periodic BCs
     // (on va sommer les lignes des noeuds "d" sur celles des noeuds "n")
@@ -392,12 +396,8 @@ void Solver::buildK(gmm::csr_matrix<double> &K2, std::vector<double> &rhs)
                                      return;
                                  //std::cout << "processing element #" << e->no << "\n";
 
-                                 Eigen::MatrixXd KKe;
-                                 e->buildK(KKe, T1, mat->k, false);
-                                 gmm::dense_matrix<double> Ke(KKe.rows(), KKe.cols());
-                                 for (size_t ii = 0; ii < KKe.rows(); ++ii)
-                                     for (size_t jj = 0; jj < KKe.cols(); ++jj)
-                                         Ke(ii, jj) = KKe(ii, jj);
+                                 Eigen::MatrixXd Ke;
+                                 e->buildK(Ke, T1, mat->k, false);
 
                                  // assembly
                                  tbb::spin_mutex::scoped_lock lock(mutex);
@@ -407,7 +407,7 @@ void Solver::buildK(gmm::csr_matrix<double> &K2, std::vector<double> &rhs)
                                      for (size_t jj = 0; jj < e->nodes.size(); ++jj)
                                      {
                                          Node *nodj = e->nodes[jj];
-                                         K(rows[nodi->row], nodj->row) += Ke(ii, jj);
+                                         T.push_back(Eigen::Triplet<double>(rows[nodi->row], nodj->row, Ke(ii, jj)));
                                      }
                                  }
                              });
@@ -426,16 +426,12 @@ void Solver::buildK(gmm::csr_matrix<double> &K2, std::vector<double> &rhs)
         {
             if (verbose > 1)
                 std::cout << "\tprocessing " << *mat << '\n';
-            Eigen::MatrixXd KKe; // bidon
+            Eigen::MatrixXd Ke; // bidon
             std::for_each(mat->tag->elems.begin(), mat->tag->elems.end(),
                           [&](Element *e) {
                               if (e->type() != ELTYPE::TRI3)
                                   return;
-                              e->buildK(KKe, T1, mat->k, true);
-                              gmm::dense_matrix<double> Ke(KKe.rows(), KKe.cols());
-                                 for (size_t ii = 0; ii < KKe.rows(); ++ii)
-                                     for (size_t jj = 0; jj < KKe.cols(); ++jj)
-                                         Ke(ii, jj) = KKe(ii, jj);
+                              e->buildK(Ke, T1, mat->k, true);
                           });
         }
 
@@ -460,12 +456,8 @@ void Solver::buildK(gmm::csr_matrix<double> &K2, std::vector<double> &rhs)
                           [&](Element *e) {
                               if (e->type() != ELTYPE::TRI3)
                                   return;
-                              Eigen::MatrixXd KKe;
-                              e->buildK(KKe, T1, mat->k, false);
-                              gmm::dense_matrix<double> Ke(KKe.rows(), KKe.cols());
-                                 for (size_t ii = 0; ii < KKe.rows(); ++ii)
-                                     for (size_t jj = 0; jj < KKe.cols(); ++jj)
-                                         Ke(ii, jj) = KKe(ii, jj);
+                              Eigen::MatrixXd Ke;
+                              e->buildK(Ke, T1, mat->k, false);
 
                               // assembly
                               //tbb::spin_mutex::scoped_lock lock(mutex);
@@ -475,7 +467,7 @@ void Solver::buildK(gmm::csr_matrix<double> &K2, std::vector<double> &rhs)
                                   for (size_t jj = 0; jj < e->nodes.size(); ++jj)
                                   {
                                       Node *nodj = e->nodes[jj];
-                                      K(rows[nodi->row], nodj->row) += Ke(ii, jj);
+                                      T.push_back(Eigen::Triplet<double>(rows[nodi->row], nodj->row, Ke(ii, jj)));
                                   }
                               }
                           });
@@ -483,6 +475,8 @@ void Solver::buildK(gmm::csr_matrix<double> &K2, std::vector<double> &rhs)
         if (verbose > 1)
             std::cout << "assembly done.\n";
     }
+    // Build matrix without BCs
+    K2.setFromTriplets(T.begin(), T.end());
 
     // periodic BCs
     if (pbl->pdic)
@@ -500,20 +494,24 @@ void Solver::buildK(gmm::csr_matrix<double> &K2, std::vector<double> &rhs)
 
         for (auto np : pbl->pdic->LR)
         {
-            K.row(np.d->row) = gmm::wsvector<double>(msh->nodes.size()); // clears related row
+            // clears related row
+            for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(K2, np.d->row); it; ++it)
+                it.valueRef() = 0.;
 
-            K(np.d->row, np.d->row) = 1.0;
-            K(np.d->row, np.n->row) = -1.0;
-            rhs[np.d->row] = lx * dTxM;
+            K2.coeffRef(np.d->row, np.d->row) = 1.0;
+            K2.coeffRef(np.d->row, np.n->row) = -1.0;
+            rhs(np.d->row) = lx * dTxM;
         }
 
         for (auto np : pbl->pdic->BT)
         {
-            K.row(np.d->row) = gmm::wsvector<double>(msh->nodes.size()); // clears related row
+            // clears related row
+            for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(K2, np.d->row); it; ++it)
+                it.valueRef() = 0.;
 
-            K(np.d->row, np.d->row) = 1.0;
-            K(np.d->row, np.n->row) = -1.0;
-            rhs[np.d->row] = ly * dTyM;
+            K2.coeffRef(np.d->row, np.d->row) = 1.0;
+            K2.coeffRef(np.d->row, np.n->row) = -1.0;
+            rhs(np.d->row) = ly * dTyM;
         }
 
         // corners \theta_2,3,4 fct \theta_1
@@ -521,22 +519,24 @@ void Solver::buildK(gmm::csr_matrix<double> &K2, std::vector<double> &rhs)
         for (auto i = 0; i < 4; ++i)
         {
             Node *nod = pbl->pdic->corners[i];
-            K.row(nod->row) = gmm::wsvector<double>(msh->nodes.size()); // clears related row
+            // clears related row
+            for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(K2, nod->row); it; ++it)
+                it.valueRef() = 0.;
         }
 
         Node *n1 = pbl->pdic->corners[0];
         for (auto i = 1; i < 4; ++i)
         {
             Node *ni = pbl->pdic->corners[i];
-            K(ni->row, ni->row) = 1.0;
-            K(ni->row, n1->row) = -1.0;
+            K2.coeffRef(ni->row, ni->row) = 1.0;
+            K2.coeffRef(ni->row, n1->row) = -1.0;
         }
         Node *n2 = pbl->pdic->corners[1];
         Node *n3 = pbl->pdic->corners[2];
         Node *n4 = pbl->pdic->corners[3];
-        rhs[n2->row] = lx * dTxM;
-        rhs[n3->row] = lx * dTxM + ly * dTyM;
-        rhs[n4->row] = ly * dTyM;
+        rhs(n2->row) = lx * dTxM;
+        rhs(n3->row) = lx * dTxM + ly * dTyM;
+        rhs(n4->row) = ly * dTyM;
 
         // thermal energy consistency condition (n1->row)
 
@@ -551,29 +551,25 @@ void Solver::buildK(gmm::csr_matrix<double> &K2, std::vector<double> &rhs)
                                      return;
                                  //std::cout << "processing element #" << e->no << "\n";
 
-                                 Eigen::VectorXd CCe;
+                                 Eigen::VectorXd Ce;
 
                                  // ** Ce matrix => K matrix
                                  Fct0C f(1.0);
-                                 e->builds(CCe, T1, f);
-                                 std::vector<double> Ce(CCe.rows());
-                                 for (size_t ii = 0; ii < CCe.rows(); ++ii)
-                                     Ce[ii] = CCe(ii);
-                                 
+                                 e->builds(Ce, T1, f);
 
                                  // assembly
                                  tbb::spin_mutex::scoped_lock lock(mutex);
                                  for (size_t jj = 0; jj < e->nodes.size(); ++jj)
                                  {
                                      Node *nodj = e->nodes[jj];
-                                     K(n1->row, nodj->row) += (mat->rhoc) * Ce[jj];
+                                     K2.coeffRef(n1->row, nodj->row) += (mat->rhoc) * Ce(jj);
 
-                                     Vtot += Ce[jj];
-                                     rhocM_Vtot += (mat->rhoc) * Ce[jj];
+                                     Vtot += Ce(jj);
+                                     rhocM_Vtot += (mat->rhoc) * Ce(jj);
                                  }
                              });
         }
-        rhs[n1->row] = rhocM_Vtot * TM;
+        rhs(n1->row) = rhocM_Vtot * TM;
         if (verbose > 1)
             std::cout << "Vtot=" << Vtot << "\n";
         if (verbose > 1)
@@ -591,28 +587,35 @@ void Solver::buildK(gmm::csr_matrix<double> &K2, std::vector<double> &rhs)
             for (auto nod : dBC->nodes)
             {
                 //std::cout << "processing " << *nod << "\n";
-                K.row(nod->row) = gmm::wsvector<double>(msh->nodes.size()); // clears related row
-                K(nod->row, nod->row) = 1.0;
+                for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(K2, nod->row); it; ++it)
+                {
+                    if (it.row() == it.col())
+                        it.valueRef() = 1.;
+                    else
+                        it.valueRef() = 0.;
+                }
             }
         }
         if (verbose > 1)
             std::cout << "done." << std::endl;
     }
 
-    // copy in a read-only CSR
-    gmm::copy(K, K2);
+    // Clean matrix and turn to compressed row format
+    K2.prune(0.);
+    K2.makeCompressed();
 
     if (verbose > 1)
     {
-        std::cout << "K (" << gmm::mat_nrows(K2) << "," << gmm::mat_ncols(K2) << ") nnz=" << gmm::nnz(K2) << "\n";
+        std::cout << "K (" << K2.rows() << "," << K2.cols() << ") nnz=" << K2.nonZeros() << "\n";
     }
-    //gmm::cout << K2 << '\n';
 }
 
-void Solver::buildqint(std::vector<double> &qint)
+void Solver::buildqint(Eigen::VectorXd &qint)
 {
     tbb::spin_mutex mutex;
 
+    qint = Eigen::VectorXd::Zero(pbl->msh->nodes.size());
+
     for (auto mat : pbl->media)
     {
         if (verbose > 1)
@@ -623,18 +626,15 @@ void Solver::buildqint(std::vector<double> &qint)
                                  return;
                              //std::cout << "processing element #" << e->no << "\n";
 
-                             Eigen::VectorXd qqe;
-                             e->computeqint(T1, qqe, mat->k);
-                             std::vector<double> qe(qqe.rows());
-                             for (size_t ii = 0; ii < qqe.rows(); ++ii)
-                                qe[ii] = qqe(ii);
+                             Eigen::VectorXd qe;
+                             e->computeqint(T1, qe, mat->k);
 
                              // assembly
                              tbb::spin_mutex::scoped_lock lock(mutex);
                              for (size_t ii = 0; ii < e->nodes.size(); ++ii)
                              {
                                  Node *nodi = e->nodes[ii];
-                                 qint[nodi->row] += qe[ii];
+                                 qint(nodi->row) += qe(ii);
                              }
                          });
     }
@@ -649,7 +649,7 @@ void Solver::buildqint(std::vector<double> &qint)
         {
             for (auto nod : dBC->nodes)
             {
-                qint[nod->row] = T1[nod->row]; // pour comparer avec K.T1
+                qint(nod->row) = T1[nod->row]; // pour comparer avec K.T1
             }
         }
         if (verbose > 1)
@@ -661,7 +661,7 @@ void Solver::buildqint(std::vector<double> &qint)
  * @brief volume sources
  */
 
-void Solver::builds(std::vector<double> &s)
+void Solver::builds(Eigen::Map<Eigen::VectorXd> &s)
 {
     tbb::spin_mutex mutex;
 
@@ -677,13 +677,10 @@ void Solver::builds(std::vector<double> &s)
                                  return;
                              //std::cout << "processing element #" << e->no << "\n";
 
-                             Eigen::VectorXd sse;
+                             Eigen::VectorXd se;
 
                              // ** se vector => s vector
-                             e->builds(sse, T1, src->f);
-                             std::vector<double> se(sse.rows());
-                             for (size_t ii = 0; ii < sse.rows(); ++ii)
-                                se[ii] = sse(ii);
+                             e->builds(se, T1, src->f);
 
                              // assembly
                              tbb::spin_mutex::scoped_lock lock(mutex);
@@ -691,14 +688,14 @@ void Solver::builds(std::vector<double> &s)
                              for (size_t ii = 0; ii < e->nodes.size(); ++ii)
                              {
                                  Node *nodi = e->nodes[ii];
-                                 s[nodi->row] += se[ii];
+                                 s(nodi->row) += se(ii);
                              }
                          });
     }
 
     if (verbose > 1)
     {
-        std::cout << "s (" << gmm::vect_size(s) << ")\n";
+        std::cout << "s (" << s.size() << ")\n";
     }
 }
 
@@ -706,7 +703,7 @@ void Solver::builds(std::vector<double> &s)
  * @brief terme BC Neumann (pourrait etre mergé avec "builds"... c'est idem)
  * @todo merge this routine with builds
  */
-void Solver::buildq(std::vector<double> &q)
+void Solver::buildq(Eigen::Map<Eigen::VectorXd> &q)
 {
     tbb::spin_mutex mutex;
 
@@ -722,13 +719,10 @@ void Solver::buildq(std::vector<double> &q)
                                  return;
                              //std::cout << "processing element #" << e->no << "\n";
 
-                             Eigen::VectorXd qqe;
+                             Eigen::VectorXd qe;
 
                              // ** qe vector => q vector
-                             e->builds(qqe, T1, bnd->f);
-                             std::vector<double> qe(qqe.rows());
-                             for (size_t ii = 0; ii < qqe.rows(); ++ii)
-                                qe[ii] = qqe(ii);
+                             e->builds(qe, T1, bnd->f);
 
                              // assembly
                              tbb::spin_mutex::scoped_lock lock(mutex);
@@ -736,14 +730,14 @@ void Solver::buildq(std::vector<double> &q)
                              for (size_t ii = 0; ii < e->nodes.size(); ++ii)
                              {
                                  Node *nodi = e->nodes[ii];
-                                 q[nodi->row] += qe[ii];
+                                 q(nodi->row) += qe(ii);
                              }
                          });
     }
 
     if (verbose > 1)
     {
-        std::cout << "q (" << gmm::vect_size(q) << ")\n";
+        std::cout << "q (" << q.size() << ")\n";
     }
 }
 
diff --git a/heat/src/wSolver.h b/heat/src/wSolver.h
index d8cf7b8eb902b93bbdac23ec59086a9f0243be3d..d91f0f36813b0eb09138ecd4e54474d46183efa7 100644
--- a/heat/src/wSolver.h
+++ b/heat/src/wSolver.h
@@ -25,7 +25,7 @@
 #include <iostream>
 #include <vector>
 #include <memory>
-#include "gmm_gmm.h"
+#include <Eigen/Sparse>
 
 using namespace tbox;
 
@@ -67,10 +67,10 @@ public:
 #endif
 
 private:
-    void buildK(gmm::csr_matrix<double> &K2, std::vector<double> &rhs);
-    void builds(std::vector<double> &s);
-    void buildq(std::vector<double> &s);
-    void buildqint(std::vector<double> &qint);
+    void buildK(Eigen::SparseMatrix<double, Eigen::RowMajor> &K2, Eigen::Map<Eigen::VectorXd> &rhs);
+    void builds(Eigen::Map<Eigen::VectorXd> &s);
+    void buildq(Eigen::Map<Eigen::VectorXd> &s);
+    void buildqint(Eigen::VectorXd &qint);
 };
 
 } // namespace heat
diff --git a/heat/tests/fe2/Lmpi.py b/heat/tests/fe2/Lmpi.py
index fdb8c21aaf542cf39d6327d2d84046f40cad9127..baf2439b92cfffceb02638f74771c78414fbc7b7 100644
--- a/heat/tests/fe2/Lmpi.py
+++ b/heat/tests/fe2/Lmpi.py
@@ -57,7 +57,7 @@ class Macro(object):
 
         self.solver = h.Solver(self.pbl)
         self.solver.nthreads = 1
-        self.solver.restol = 1e-11 
+        self.solver.restol = 1e-10 
         self.solver.itmax = 1000 
 
 if __name__ == "__main__":
diff --git a/heat/tests/periodic/honeycomb.py b/heat/tests/periodic/honeycomb.py
index c0836368d4d9439a0acc4c03b5e325949ae136f1..1464d321263bdc7ca3e100126fec3c6ef209d32b 100755
--- a/heat/tests/periodic/honeycomb.py
+++ b/heat/tests/periodic/honeycomb.py
@@ -36,6 +36,7 @@ class MyMaterial(tbox.Fct2U):
         k2 = 100.
         k.resize(2, 2)
         k[0,0] = k[1,1] = k1+(k2-k1)/(t2-t1)*(u-t1)
+        k[0,1] = k[1,0] = 0.
         #k[0,0] = k[1,1] = k1 # simple case
         #print "returning k=%f" %k
         return k
diff --git a/heat/tests/periodic/lincomb.py b/heat/tests/periodic/lincomb.py
index a833b73fbb345cc5276f20786cfca70276506738..246dfcf65dc2cfde0a23d5bce4f6971c56d87916 100755
--- a/heat/tests/periodic/lincomb.py
+++ b/heat/tests/periodic/lincomb.py
@@ -45,7 +45,8 @@ class MyMaterial(tbox.Fct2XYZ):
         import math
         v = old_div((self.k1+self.k2),2)+ abs(self.k2-self.k1)/2*math.sin(2*math.pi/L*4*(x+old_div(y,2)))
         k.resize(2, 2)
-        k[0,0] = k[1,1] = v;
+        k[0,0] = k[1,1] = v
+        k[0,1] = k[1,0] = 0.
         #k[0,0] = self.k1  # constant
         #k[1,1] = self.k2
 
diff --git a/mirrors/src/wSolver.cpp b/mirrors/src/wSolver.cpp
index 69f0274516939b826dc969cc7d3ee6aad54471bf..2ab298b05f55f718267d2da65fd65c1e78c057fe 100644
--- a/mirrors/src/wSolver.cpp
+++ b/mirrors/src/wSolver.cpp
@@ -30,9 +30,6 @@
 #include "wNode.h"
 #include "wPt.h"
 #include "wANSYSSolution.h"
-#include "std_extra.h"
-#include "gmm_gmm.h"
-#include <gmm/gmm_MUMPS_interface.h>
 #include <map>
 #include "wTag.h"
 #include <math.h>
@@ -43,6 +40,9 @@
 #include "wGaussHex8.h"
 #include "wResults.h"
 #include "wMshExport.h"
+#include "wMumpsInterface.h"
+
+#include <Eigen/Sparse>
 
 #include <math.h>
 
@@ -142,10 +142,12 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
     std::cout << "\tAssembly...\n";
 
     // memory allocation
-    gmm::row_matrix<gmm::wsvector<double>> Tmp_1(pbl_size, pbl_size);
-    gmm::csc_matrix<double> K(pbl_size, pbl_size);
+    std::vector<Eigen::Triplet<double>> Trip; // list of triplets to build stiffness matrix
+    Trip.reserve(pbl_size);
+    Eigen::SparseMatrix<double, Eigen::RowMajor> K(pbl_size, pbl_size);
     std::vector<double> rhs(pbl_size);
     std::vector<double> u(pbl_size);
+    Eigen::Map<Eigen::VectorXd> u_(u.data(), u.size()), rhs_(rhs.data(), rhs.size());
 
     // loop on the media
     for (auto m : pbl.media)
@@ -185,16 +187,12 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
                              {
                                  //      Element *e = current_tag.elems[index];
 
-                                 Eigen::MatrixXd KK_e;
+                                 Eigen::MatrixXd K_e;
 
                                  if (e->type() != ELTYPE::HEX8 && e->type() != ELTYPE::TETRA4)
                                      return; //if the element is not a tetrahedron or a hexahedron, it is not treated
 
-                                 e->buildK_mechanic(KK_e, H);
-                                 gmm::dense_matrix<double> K_e(KK_e.rows(), KK_e.cols());
-                                 for (size_t ii = 0; ii < KK_e.rows(); ++ii)
-                                     for (size_t jj = 0; jj < KK_e.cols(); ++jj)
-                                         K_e(ii, jj) = KK_e(ii, jj);
+                                 e->buildK_mechanic(K_e, H);
 
                                  for (size_t ii = 0; ii < e->nodes.size(); ++ii)
                                  {
@@ -208,7 +206,7 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
                                          for (auto iii = 0; iii < 3; ++iii)
                                              for (auto jjj = 0; jjj < 3; ++jjj)
                                                  if (K_e(3 * ii + iii, 3 * jj + jjj) != 0)
-                                                     Tmp_1(3 * (nodi->row) + iii, 3 * (nodj->row) + jjj) += K_e(3 * ii + iii, 3 * jj + jjj);
+                                                     Trip.push_back(Eigen::Triplet<double>(3 * (nodi->row) + iii, 3 * (nodj->row) + jjj, K_e(3 * ii + iii, 3 * jj + jjj)));
                                      }
                                  }
                              });
@@ -226,16 +224,12 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
             // L matrix
             //=============================tbb==================================
             tbb::parallel_do(current_tag.elems.begin(), current_tag.elems.end(), [&](Element *e) {
-                Eigen::MatrixXd LL_e;
+                Eigen::MatrixXd L_e;
 
                 if (e->type() != ELTYPE::HEX8 && e->type() != ELTYPE::TETRA4)
                     return; //if the element is not a tetrahedron or a hexahedron, it is not treated
 
-                e->buildL_thermic(LL_e, K_conductivity);
-                gmm::dense_matrix<double> L_e(LL_e.rows(), LL_e.cols());
-                for (size_t ii = 0; ii < LL_e.rows(); ++ii)
-                    for (size_t jj = 0; jj < LL_e.cols(); ++jj)
-                        L_e(ii, jj) = LL_e(ii, jj);
+                e->buildL_thermic(L_e, K_conductivity);
 
                 for (size_t ii = 0; ii < e->nodes.size(); ++ii)
                 {
@@ -247,7 +241,7 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
                         tbb::spin_mutex::scoped_lock lock(mutex);
                         //=====================================//
                         if (L_e(ii, jj) != 0)
-                            Tmp_1(T_first_index + (nodi->row), T_first_index + (nodj->row)) += L_e(ii, jj);
+                            Trip.push_back(Eigen::Triplet<double>(T_first_index + (nodi->row), T_first_index + (nodj->row), L_e(ii, jj)));
                     }
                 }
             });
@@ -272,16 +266,12 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
             // S matrix
             //=============================tbb==================================
             tbb::parallel_do(current_tag.elems.begin(), current_tag.elems.end(), [&](Element *e) {
-                Eigen::MatrixXd SS_e;
+                Eigen::MatrixXd S_e;
 
                 if (e->type() != ELTYPE::HEX8 && e->type() != ELTYPE::TETRA4)
                     return; //if the element is not a tetrahedron or a hexahedron, it is not treated
 
-                e->buildS_thermomechanic(SS_e, D);
-                gmm::dense_matrix<double> S_e(SS_e.rows(), SS_e.cols());
-                for (size_t ii = 0; ii < SS_e.rows(); ++ii)
-                    for (size_t jj = 0; jj < SS_e.cols(); ++jj)
-                        S_e(ii, jj) = SS_e(ii, jj);
+                e->buildS_thermomechanic(S_e, D);
 
                 for (size_t ii = 0; ii < e->nodes.size(); ++ii)
                 {
@@ -294,13 +284,15 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
                         //=====================================//
                         for (size_t iii = 0; iii < 3; ++iii)
                             if (S_e(3 * ii + iii, jj) != 0)
-                                Tmp_1(3 * (nodi->row) + iii, T_first_index + (nodj->row)) += S_e(3 * ii + iii, jj);
+                                Trip.push_back(Eigen::Triplet<double>(3 * (nodi->row) + iii, T_first_index + (nodj->row), S_e(3 * ii + iii, jj)));
                     }
                 }
             });
             //=============================tbb==================================
         }
     }
+    // Build matrix without BCs
+    K.setFromTriplets(Trip.begin(), Trip.end());
     tms["Kass"].stop();
 
     // ------------------------------------------
@@ -321,16 +313,12 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
             //loop on the elements
             for (auto e : current_tag.elems)
             {
-                Eigen::MatrixXd NN_e;
+                Eigen::MatrixXd N_e;
 
                 if (e->type() != ELTYPE::HEX8 && e->type() != ELTYPE::TETRA4)
                     continue; //if the element is not a tetrahedron or a hexahedron, it is not treated
 
-                e->buildM(NN_e);
-                gmm::dense_matrix<double> N_e(NN_e.rows(), NN_e.cols());
-                for (size_t ii = 0; ii < NN_e.rows(); ++ii)
-                    for (size_t jj = 0; jj < NN_e.cols(); ++jj)
-                        N_e(ii, jj) = NN_e(ii, jj);
+                e->buildM(N_e);
 
                 for (size_t ii = 0; ii < e->nodes.size(); ++ii)
                 {
@@ -370,16 +358,12 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
             //loop on the elements
             for (auto e : current_tag.elems)
             {
-                Eigen::MatrixXd NN_e;
+                Eigen::MatrixXd N_e;
 
                 if (e->type() != ELTYPE::HEX8 && e->type() != ELTYPE::TETRA4)
                     continue; //if the element is not a triangle or a quadrangle, it is not treated
 
-                e->buildS(NN_e);
-                gmm::dense_matrix<double> N_e(NN_e.rows(), NN_e.cols());
-                for (size_t ii = 0; ii < NN_e.rows(); ++ii)
-                    for (size_t jj = 0; jj < NN_e.cols(); ++jj)
-                        N_e(ii, jj) = NN_e(ii, jj);
+                e->buildS(N_e);
 
                 for (size_t ii = 0; ii < e->nodes.size(); ++ii)
                 {
@@ -403,16 +387,12 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
             //loop on the elements
             for (auto e : current_tag.elems)
             {
-                Eigen::MatrixXd NN_e;
+                Eigen::MatrixXd N_e;
 
                 if (e->type() != ELTYPE::HEX8 && e->type() != ELTYPE::TETRA4)
                     continue; //if the element is not a triangle or a quadrangle, it is not treated
 
-                e->buildS(NN_e);
-                gmm::dense_matrix<double> N_e(NN_e.rows(), NN_e.cols());
-                for (size_t ii = 0; ii < NN_e.rows(); ++ii)
-                    for (size_t jj = 0; jj < NN_e.cols(); ++jj)
-                        N_e(ii, jj) = NN_e(ii, jj);
+                e->buildS(N_e);
 
                 for (size_t ii = 0; ii < e->nodes.size(); ++ii)
                 {
@@ -460,8 +440,13 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
                         if (b->which_dof.x[iii] == 1)
                         {
                             Node *nodi = e->nodes[ii];
-                            Tmp_1.row(3 * (nodi->row) + iii) = gmm::wsvector<double>(pbl_size); // clears related row
-                            Tmp_1(3 * (nodi->row) + iii, 3 * (nodi->row) + iii) = 1.0;
+                            for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(K, 3 * nodi->row + iii); it; ++it)
+                            {
+                                if (it.row() == it.col())
+                                    it.valueRef() = 1.;
+                                else
+                                    it.valueRef() = 0.;
+                            }
                             rhs[3 * (nodi->row) + iii] = b->values.x[iii];
                         }
             }
@@ -487,9 +472,14 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
                 for (size_t ii = 0; ii < e->nodes.size(); ++ii)
                 {
                     Node *nodi = e->nodes[ii];
-                    Tmp_1.row(T_first_index + (nodi->row)) = gmm::wsvector<double>(pbl_size); // clears related row
-                    Tmp_1(T_first_index + (nodi->row), T_first_index + (nodi->row)) = 1.0;
-                    rhs[T_first_index + (nodi->row)] = b->values - T_ref;
+                    for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(K, T_first_index + nodi->row); it; ++it)
+                    {
+                        if (it.row() == it.col())
+                            it.valueRef() = 1.;
+                        else
+                            it.valueRef() = 0.;
+                    }
+                    rhs[T_first_index + nodi->row] = b->values - T_ref;
                 }
             }
         }
@@ -503,9 +493,9 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
     // ------------------------------------------
     tms["Kclean"].start();
     std::cout << "\tCleaning matrices...\n";
-    // clean K
-    gmm::clean(Tmp_1, 1E-12);
-    gmm::copy(Tmp_1, K);
+    // Clean matrix and turn to compressed row format
+    K.prune(0.);
+    K.makeCompressed();
     tms["Kclean"].stop();
 
     // ------------------------------------------
@@ -515,9 +505,10 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
     // ------------------------------------------
     tms["solve"].start();
     std::cout << "\tSolving linear system...\n";
-    bool ok = gmm::MUMPS_solve(K, u, rhs);
-    if (!ok)
-        throw std::runtime_error("problem while solving system with MUMPS");
+    std::map<std::string, int> mumpsOpt;
+    MumpsInterface mumps(mumpsOpt);
+    mumps.setSoE(K, rhs_);
+    mumps.compute(u_);
     tms["solve"].stop();
 
     // ------------------------------------------
@@ -665,16 +656,12 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
 
             for (auto e : current_tag.elems)
             {
-                Eigen::MatrixXd NN_e;
+                Eigen::MatrixXd N_e;
 
                 if (e->type() != ELTYPE::HEX8 && e->type() != ELTYPE::TETRA4)
                     continue; //if the element is not a triangle or a quadrangle, it is not treated
 
-                e->buildS(NN_e);
-                gmm::dense_matrix<double> N_e(NN_e.rows(), NN_e.cols());
-                for (size_t ii = 0; ii < NN_e.rows(); ++ii)
-                    for (size_t jj = 0; jj < NN_e.cols(); ++jj)
-                        N_e(ii, jj) = NN_e(ii, jj);
+                e->buildS(N_e);
 
                 for (size_t ii = 0; ii < e->nodes.size(); ++ii)
                 {
@@ -722,12 +709,10 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
                 Z_10[ii] = sqrt(8) * pow(rho[ii], 3.) * cos(3 * theta[ii]);
             }
 
-            gmm::row_matrix<gmm::wsvector<double>> W(msh->nodes.size(), msh->nodes.size());
-            gmm::dense_matrix<double> Z(msh->nodes.size(), 10);
-            gmm::row_matrix<gmm::wsvector<double>> TMP_Z(10, msh->nodes.size());
-            gmm::dense_matrix<double> A(10, 10);
-            std::vector<double> rhs_Z(10); // RHS
-            std::vector<double> c_Z(10);
+            Eigen::MatrixXd W = Eigen::MatrixXd::Zero(msh->nodes.size(), msh->nodes.size());
+            Eigen::MatrixXd Z(msh->nodes.size(), 10);
+            Eigen::MatrixXd A(10, 10);
+            Eigen::VectorXd rhs_Z(10); // RHS
 
             for (size_t ii = 0; ii < msh->nodes.size(); ++ii)
             {
@@ -745,11 +730,9 @@ void Solver::start(std::shared_ptr<MshExport> mshWriter)
                 Z(ii, 9) = Z_10[ii];
             }
 
-            gmm::mult(gmm::transposed(Z), W, TMP_Z);
-            gmm::mult(TMP_Z, gmm::col_vector(n_displacement), gmm::col_vector(rhs_Z));
-            gmm::mult(TMP_Z, Z, A);
-
-            gmm::lu_solve(A, c_Z, rhs_Z);
+            A = Z.transpose() * W * Z;
+            rhs_Z = Z.transpose() * W * Eigen::Map<Eigen::VectorXd>(n_displacement.data(), msh->nodes.size());
+            Eigen::VectorXd c_Z = A.partialPivLu().solve(rhs_Z);
 
             std::cout << c_Z;
 
diff --git a/mrstlnos/src/Results_def.hpp b/mrstlnos/src/Results_def.hpp
index c4b34a8d2f870054c2190cd44cf8ee827639791e..3179124aae80da02c74cc18128838db7cb4c67ab 100644
--- a/mrstlnos/src/Results_def.hpp
+++ b/mrstlnos/src/Results_def.hpp
@@ -155,9 +155,6 @@ void Write_Results(
     for (auto &k : strain_at_nodes)
         gmm::resize(k, 3, 3);
 
-    gmm::dense_matrix<double> zero_3x3(3, 3);
-    gmm::clear(zero_3x3);
-
     // Fill the result vectors
 
     for (size_t ell = 0; ell < ensemble_size; ++ell)
@@ -208,8 +205,8 @@ void Write_Results(
             displacement_y[i] = dy;
             displacement_z[i] = dz;
             displacement_u[i] = sqrt(dx * dx + dy * dy + dz * dz);
-            gmm::copy(zero_3x3, strain_at_nodes[i]);
-            gmm::copy(zero_3x3, stress_at_nodes[i]);
+            gmm::clear(strain_at_nodes[i]);
+            gmm::clear(stress_at_nodes[i]);
             shear_modulus_per_node[i] = ET::coeff(random_field->operator()(pbl.msh->nodes[nodesWO_LO_to_GO[i]]->pos.x[0], pbl.msh->nodes[nodesWO_LO_to_GO[i]]->pos.x[1], pbl.msh->nodes[nodesWO_LO_to_GO[i]]->pos.x[2]), ell);
             displacement[i] = Pt(dx, dy, dz);
             if (numPrimalDPN == 4)
@@ -259,8 +256,8 @@ void Write_Results(
                     }
                     G = ET::coeff(random_field->operator()(x, y, z), ell);
                 }
-                gmm::dense_matrix<double> H(9, 9);
-                gmm::dense_matrix<double> D(3, 3);
+                Eigen::MatrixXd H = Eigen::MatrixXd::Zero(9, 9);
+                Eigen::MatrixXd D = Eigen::MatrixXd::Zero(3, 3);
 
                 for (auto i = 0; i < 3; ++i)
                     for (auto j = 0; j < 3; ++j)
@@ -283,17 +280,17 @@ void Write_Results(
                         D(i, i) = -3. * kappa * beta;
                 }
                 std::vector<double> x_tmp(8), y_tmp(8), z_tmp(8), T_tmp(8);
-                gmm::dense_matrix<double> Strain(3, 3);
-                gmm::dense_matrix<double> Sigma(3, 3);
+                Eigen::MatrixXd Strain(3, 3);
+                Eigen::MatrixXd Sigma(3, 3);
 
                 auto e = current_tag->elems[i_elem];
-                std::vector<gmm::dense_matrix<double>> Strain_at_node(e->nodes.size());
-                std::vector<gmm::dense_matrix<double>> Sigma_at_node(e->nodes.size());
+                std::vector<Eigen::MatrixXd> Strain_at_node(e->nodes.size());
+                std::vector<Eigen::MatrixXd> Sigma_at_node(e->nodes.size());
 
                 for (auto &k : Strain_at_node)
-                    gmm::resize(k, 3, 3);
+                    k = Eigen::MatrixXd::Zero(3, 3);
                 for (auto &k : Sigma_at_node)
-                    gmm::resize(k, 3, 3);
+                    k = Eigen::MatrixXd::Zero(3, 3);
 
                 if (e->type() == ELTYPE::HEX8 || e->type() == ELTYPE::TETRA4)
                 {
@@ -307,17 +304,12 @@ void Write_Results(
                             T_tmp[i] = T[node_i];
                         else
                             T_tmp[i] = 0.;
-
-                        gmm::dense_matrix<double> Strain_tmp(3, 3);
-                        gmm::clear(Strain_tmp);
-                        gmm::copy(Strain_tmp, Strain_at_node[i]);
-                        gmm::copy(Strain_tmp, Sigma_at_node[i]);
                     }
 
                     strain_stress_x_y_z(e, Strain, Sigma, H, D, x_tmp, y_tmp, z_tmp, T_tmp, numPrimalDPN == 4);
 
-                    gmm::copy(Strain, strain[elems_LO_to_GO[e->no - 1]]);
-                    gmm::copy(Sigma, stress[elems_LO_to_GO[e->no - 1]]);
+                    strain[elems_LO_to_GO[e->no - 1]] = Strain;
+                    stress[elems_LO_to_GO[e->no - 1]] = Sigma;
 
                     G_vector[elems_LO_to_GO[e->no - 1]] = G;
 
@@ -330,8 +322,13 @@ void Write_Results(
                         LocalOrdinal node_i = nodesWO_GO_to_LO[e->nodes[i]->row];
                         if (ell == 0)
                             ++adjacent_elems[node_i]; // Increases the number of adjacent elements by one only for the first sample
-                        gmm::add(Strain_at_node[i], strain_at_nodes[node_i]);
-                        gmm::add(Sigma_at_node[i], stress_at_nodes[node_i]);
+
+                        for (size_t iii = 0; iii < 3; ++iii)
+                            for (size_t jjj = 0; jjj < 3; ++jjj)
+                            {
+                                strain_at_nodes[node_i](iii, jjj) += Strain_at_node[i](iii, jjj);
+                                stress_at_nodes[node_i](iii, jjj) += Sigma_at_node[i](iii, jjj);
+                            }
                     }
                 }
             }
diff --git a/mrstlnos/src/Stress_computation.h b/mrstlnos/src/Stress_computation.h
index 463ab1fd4e2aa4f44e9d5304f0d8d5a95113d1d2..906c927f4842acd80afbd77eb0889e9c278759c0 100644
--- a/mrstlnos/src/Stress_computation.h
+++ b/mrstlnos/src/Stress_computation.h
@@ -12,19 +12,15 @@
 
 /**
  * @brief Those functions compute the stress and strain at nodes and in average per element.
- * They are currently implemented using gmm data structure but should be refactored using Kokkos:view
+ * They are currently implemented using Eigen data structure but should be refactored using Kokkos:view
  * such that it will be possible to use parallel for in the future.
  * @authors Kim Liegeois
  */
 
-void strain_at_gp(size_t n_n, Mem &mem, Cache &cache, size_t a, gmm::dense_matrix<double> u_e, gmm::dense_matrix<double> &Strain)
+void strain_at_gp(size_t n_n, Mem &mem, Cache &cache, size_t a, const Eigen::MatrixXd &u_e, Eigen::MatrixXd &Strain)
 {
-    Eigen::MatrixXd const &JJ = mem.getJinv(a);
-
-    gmm::dense_matrix<double> du_d_Phi(3, 3);
-    gmm::dense_matrix<double> Tmp(3, 3);
-    gmm::dense_matrix<double> Tmp2(3, 3);
-
+    Eigen::MatrixXd const &J = mem.getJinv(a);
+    Eigen::Matrix3d du_d_Phi = Eigen::Matrix3d::Zero();
     for (size_t i = 0; i < n_n; ++i)
     {
         std::vector<double> &dffi = cache.dff[a][i];
@@ -32,23 +28,12 @@ void strain_at_gp(size_t n_n, Mem &mem, Cache &cache, size_t a, gmm::dense_matri
             for (auto j = 0; j < 3; ++j)
                 du_d_Phi(j, k) += dffi[j] * u_e(k, i);
     }
-
-    /* TEMPO EIGEN/GMM */
-    gmm::dense_matrix<double> J(JJ.rows(), JJ.cols());
-    for (size_t i = 0; i < J.nrows(); ++i)
-        for (size_t j = 0; j < J.ncols(); ++j)
-            J(i, j) = JJ(i, j);
-
-    gmm::mult(J, du_d_Phi, Tmp);
-    gmm::add(gmm::transposed(Tmp), Tmp, Tmp2);
-    gmm::copy(gmm::scaled(Tmp2, 0.5), Tmp);
-    gmm::add(Tmp, Strain);
+    Strain += (J * du_d_Phi).transpose() * (J * du_d_Phi) * 0.5;
 }
 
-void compute_stress(size_t n_n, gmm::dense_matrix<double> Strain, gmm::dense_matrix<double> &Stress, const gmm::dense_matrix<double> &H, const gmm::dense_matrix<double> &D, const std::vector<double> &T, bool thermal)
+void compute_stress(size_t n_n, const Eigen::MatrixXd &Strain, Eigen::MatrixXd &Stress, const Eigen::MatrixXd &H, const Eigen::MatrixXd &D, const std::vector<double> &T, bool thermal)
 {
-    gmm::clear(Stress);
-
+    Stress = Eigen::Matrix3d::Zero();
     for (size_t i = 0; i < 3; ++i)
         for (size_t j = 0; j < 3; ++j)
             for (size_t k = 0; k < 3; ++k)
@@ -66,7 +51,7 @@ void compute_stress(size_t n_n, gmm::dense_matrix<double> Strain, gmm::dense_mat
             }
 }
 
-void strain_stress_x_y_z(Element *elem, gmm::dense_matrix<double> &Strain, gmm::dense_matrix<double> &Stress, const gmm::dense_matrix<double> &H, const gmm::dense_matrix<double> &D, const std::vector<double> &x, const std::vector<double> &y, const std::vector<double> &z, const std::vector<double> &T, bool thermal = false)
+void strain_stress_x_y_z(Element *elem, Eigen::MatrixXd &Strain, Eigen::MatrixXd &Stress, const Eigen::MatrixXd &H, const Eigen::MatrixXd &D, const std::vector<double> &x, const std::vector<double> &y, const std::vector<double> &z, const std::vector<double> &T, bool thermal = false)
 {
     Cache &cache = elem->getVCache();
     Mem &mem = elem->getVMem();
@@ -74,10 +59,7 @@ void strain_stress_x_y_z(Element *elem, gmm::dense_matrix<double> &Strain, gmm::
     size_t n_gp = cache.dff.size();
     size_t n_n = elem->nodes.size();
 
-    gmm::dense_matrix<double> u_e(3, n_n);
-    gmm::dense_matrix<double> Tmp(3, 3);
-
-    gmm::clear(Strain);
+    Eigen::MatrixXd u_e(3, n_n);
 
     for (size_t i = 0; i < n_n; ++i)
     {
@@ -86,18 +68,17 @@ void strain_stress_x_y_z(Element *elem, gmm::dense_matrix<double> &Strain, gmm::
         u_e(2, i) = z[i];
     }
 
+    Strain = Eigen::Matrix3d::Zero();
     for (size_t a = 0; a < n_gp; ++a)
     {
         strain_at_gp(n_n, mem, cache, a, u_e, Strain);
     }
-
-    gmm::copy(gmm::scaled(Strain, 1. / n_n), Tmp);
-    gmm::copy(Tmp, Strain); // Prevent Level 2 warning: a conflict is possible in copy
+    Strain /= n_n;
 
     compute_stress(n_n, Strain, Stress, H, D, T, thermal);
 }
 
-void strain_stress_x_y_z_at_node(Element *elem, std::vector<gmm::dense_matrix<double>> &Strain_at_node, std::vector<gmm::dense_matrix<double>> &Stress_at_node, const gmm::dense_matrix<double> &H, const gmm::dense_matrix<double> &D, const std::vector<double> &x, const std::vector<double> &y, const std::vector<double> &z, const std::vector<double> &T, bool thermal = false)
+void strain_stress_x_y_z_at_node(Element *elem, std::vector<Eigen::MatrixXd> &Strain_at_node, std::vector<Eigen::MatrixXd> &Stress_at_node, const Eigen::MatrixXd &H, const Eigen::MatrixXd &D, const std::vector<double> &x, const std::vector<double> &y, const std::vector<double> &z, const std::vector<double> &T, bool thermal = false)
 {
     Cache &cache = elem->getVCache();
     Mem &mem = elem->getVMem();
@@ -105,15 +86,15 @@ void strain_stress_x_y_z_at_node(Element *elem, std::vector<gmm::dense_matrix<do
     size_t n_gp = cache.dff.size();
     size_t n_n = elem->nodes.size();
 
-    std::vector<gmm::dense_matrix<double>> Strain_at_gp(n_gp);
-    std::vector<gmm::dense_matrix<double>> Stress_at_gp(n_gp);
-    gmm::dense_matrix<double> u_e(3, n_n);
-    gmm::dense_matrix<double> Strain(3, 3);
+    std::vector<Eigen::MatrixXd> Strain_at_gp(n_gp);
+    std::vector<Eigen::MatrixXd> Stress_at_gp(n_gp);
+    Eigen::MatrixXd u_e(3, n_n);
+    Eigen::MatrixXd Strain(3, 3);
 
     for (auto &k : Strain_at_gp)
-        gmm::resize(k, 3, 3);
+        k.resize(3, 3);
     for (auto &k : Stress_at_gp)
-        gmm::resize(k, 3, 3);
+        k.resize(3, 3);
 
     for (size_t i = 0; i < n_n; ++i)
     {
@@ -124,9 +105,9 @@ void strain_stress_x_y_z_at_node(Element *elem, std::vector<gmm::dense_matrix<do
 
     for (size_t a = 0; a < n_gp; ++a)
     {
-        gmm::clear(Strain);
+        Strain = Eigen::Matrix3d::Zero();
         strain_at_gp(n_n, mem, cache, a, u_e, Strain);
-        gmm::copy(Strain, Strain_at_gp[a]);
+        Strain_at_gp[a] = Strain;
         compute_stress(n_n, Strain, Stress_at_gp[a], H, D, T, thermal);
     }
 
@@ -145,16 +126,10 @@ void strain_stress_x_y_z_at_node(Element *elem, std::vector<gmm::dense_matrix<do
     elem->gp2node(values_at_gp, values_at_node);
 
     for (size_t k = 0; k < n_n; ++k)
-    {
-        gmm::dense_matrix<double> Strain_tmp(3, 3);
-        gmm::dense_matrix<double> Stress_tmp(3, 3);
         for (size_t i = 0; i < 3; ++i)
             for (size_t j = 0; j < 3; ++j)
             {
-                Strain_tmp(i, j) = values_at_node(k, i * 3 + j);
-                Stress_tmp(i, j) = values_at_node(k, 9 + i * 3 + j);
+                Strain_at_node[k](i, j) = values_at_node(k, i * 3 + j);
+                Stress_at_node[k](i, j) = values_at_node(k, 9 + i * 3 + j);
             }
-        gmm::copy(Strain_tmp, Strain_at_node[k]);
-        gmm::copy(Stress_tmp, Stress_at_node[k]);
-    }
 }
diff --git a/tbox/_src/tboxw.h b/tbox/_src/tboxw.h
index ce854492278e43ec67e41a4acaaefd42a8bb379f..0aed2c43f7871adc58e5d45631ecc42f51bc16ec 100644
--- a/tbox/_src/tboxw.h
+++ b/tbox/_src/tboxw.h
@@ -15,7 +15,7 @@
  */
 
 #include "tbox.h"
-#include "gmm_extra.h"
+#include "eigen_extra.h"
 #include "std_extra.h"
 
 #include "wCache.h"
diff --git a/tbox/src/CMakeLists.txt b/tbox/src/CMakeLists.txt
index bc7e64d036c4c56e83f6bcd74c0914d37b82b955..682a8a677d081de3910001ce544a796f4e0d1cf1 100644
--- a/tbox/src/CMakeLists.txt
+++ b/tbox/src/CMakeLists.txt
@@ -68,12 +68,22 @@ ENDIF()
 # -- Eigen --
 FIND_PACKAGE(EIGEN REQUIRED)
 TARGET_INCLUDE_DIRECTORIES(tbox PUBLIC ${EIGEN_INCLUDE_DIRS})
+TARGET_COMPILE_DEFINITIONS(tbox PUBLIC EIGEN_DONT_PARALLELIZE)
+IF(MKL_INCLUDE_DIRS AND MKL_LIBRARIES)
+    MESSAGE(STATUS "Linking Eigen with MKL")
+    TARGET_COMPILE_DEFINITIONS(tbox PUBLIC EIGEN_USE_MKL_ALL)
+ELSE()
+    MESSAGE(STATUS "Linking Eigen with ${BLA_VENDOR}")
+    TARGET_COMPILE_DEFINITIONS(tbox PUBLIC EIGEN_USE_BLAS)
+    #TARGET_COMPILE_DEFINITIONS(tbox PUBLIC EIGEN_USE_LAPACKE) # why this line fails?
+ENDIF()
+TARGET_LINK_LIBRARIES(tbox ${LAPACK_LIBRARIES})
 
 # -- GMM --
 FIND_PACKAGE(GMM REQUIRED)
 TARGET_INCLUDE_DIRECTORIES(tbox PUBLIC ${GMM_INCLUDE_DIRS})
-TARGET_COMPILE_DEFINITIONS(tbox PUBLIC GMM_USES_LAPACK ${GMM_DEFINITIONS})
-TARGET_LINK_LIBRARIES(tbox ${LAPACK_LIBRARIES})
+#TARGET_COMPILE_DEFINITIONS(tbox PUBLIC GMM_USES_LAPACK ${GMM_DEFINITIONS})
+#TARGET_LINK_LIBRARIES(tbox ${LAPACK_LIBRARIES})
 
 # -- MUMPS --
 FIND_PACKAGE(MUMPS REQUIRED)
diff --git a/tbox/src/gmm_extra.cpp b/tbox/src/eigen_extra.cpp
similarity index 71%
rename from tbox/src/gmm_extra.cpp
rename to tbox/src/eigen_extra.cpp
index c16d1fc2cdf9b1d8103b4c822a3ee2e9f4a5fc6a..980a4fa419e1249587bba31db8efed99ea220af6 100644
--- a/tbox/src/gmm_extra.cpp
+++ b/tbox/src/eigen_extra.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "gmm_extra.h"
+#include "eigen_extra.h"
 #include <fstream>
 
 using namespace tbox;
@@ -26,17 +26,13 @@ using namespace tbox;
  */
 
 TBOX_API void
-tomatlab(std::string const &fname, gmm::csr_matrix<double> const &A)
+tomatlab(std::string const &fname, Eigen::SparseMatrix<double> const &A)
 {
     std::ofstream f(fname.c_str());
 
-    for (size_t i = 0; i < mat_nrows(A); ++i)
-        for (size_t j = 0; j < mat_ncols(A); ++j)
-        {
-            double v = A(i, j);
-            if (v != 0.0)
-                f << i + 1 << ' ' << j + 1 << ' ' << v << '\n';
-        }
+    for (size_t k = 0; k < A.outerSize(); ++k)
+        for (Eigen::SparseMatrix<double>::InnerIterator it(A, k); it; ++it)
+            f << it.row() + 1 << ' ' << it.col() + 1 << ' ' << it.value() << '\n';
 
     f.close();
 }
diff --git a/tbox/src/gmm_extra.h b/tbox/src/eigen_extra.h
similarity index 80%
rename from tbox/src/gmm_extra.h
rename to tbox/src/eigen_extra.h
index 9796bf7616daa52375098837b776098b99d1fc4c..09559a7352d5decefff4b6f9904e977d9d5cc0ad 100644
--- a/tbox/src/gmm_extra.h
+++ b/tbox/src/eigen_extra.h
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef GMM_EXTRA_H
-#define GMM_EXTRA_H
+#ifndef EIGEN_EXTRA_H
+#define EIGEN_EXTRA_H
 
 #include "tbox.h"
-#include "gmm_gmm.h"
 #include <string>
+#include <Eigen/Sparse>
 
 namespace tbox
 {
 
 TBOX_API void
-tomatlab(std::string const &fname, gmm::csr_matrix<double> const &A);
+tomatlab(std::string const &fname, Eigen::SparseMatrix<double> const &A);
 }
 
-#endif //GMM_EXTRA_H
+#endif //EIGEN_EXTRA_H
diff --git a/tbox/src/wMshDeform.cpp b/tbox/src/wMshDeform.cpp
index a54c9fa6ab87a4317ee9e2be0fdd76db05c68bf0..2b57c30a6efb31632acbcfa42441480c3a66e074 100644
--- a/tbox/src/wMshDeform.cpp
+++ b/tbox/src/wMshDeform.cpp
@@ -23,9 +23,10 @@
 #include "wElement.h"
 #include "wPt.h"
 #include "wMem.h"
+#include "wMumpsInterface.h"
+
+#include <Eigen/Sparse>
 
-#include "gmm_gmm.h"
-#include "gmm_extra.h"
 //#include <tbb/task_scheduler_init.h>
 //#include <tbb/tbb.h>
 //#include <tbb/parallel_do.h>
@@ -236,11 +237,12 @@ void MshDeform::deform()
     //    tbb::spin_mutex mutex;
 
     // Stiffness matrix
-    gmm::csr_matrix<double> K;
-    // Temporary matrix
-    gmm::row_matrix<gmm::wsvector<double>> Kt(nDim * mshSize, nDim * mshSize);
+    Eigen::SparseMatrix<double, Eigen::RowMajor> K(nDim * mshSize, nDim * mshSize);
+    // List of triplets to build stifness matrix
+    std::vector<Eigen::Triplet<double>> T;
+    T.reserve(nDim * mshSize);
     // force (RHS) and displacement (UKN) vectors
-    std::vector<double> f(nDim * mshSize, 0.), q(nDim * mshSize, 0.);
+    Eigen::VectorXd f = Eigen::VectorXd::Zero(nDim * mshSize), q= Eigen::VectorXd::Zero(nDim * mshSize);
 
     // Build stiffness matrix
     std::cout << "--- Deforming mesh using linear elasticiy equations ---\n"
@@ -253,7 +255,7 @@ void MshDeform::deform()
         Eigen::MatrixXd H;
         if (nDim == 2)
         {
-            H =  Eigen::Matrix3d::Zero();
+            H = Eigen::Matrix3d::Zero();
             double nu = 0.;               // Poisson ratio [-1, 0.5]
             double E = 1 / e->computeV(); // Young modulus
             H(0, 0) = 1 - nu;
@@ -265,7 +267,7 @@ void MshDeform::deform()
         }
         else
         {
-            H =  Eigen::MatrixXd::Zero(6, 6);
+            H = Eigen::MatrixXd::Zero(6, 6);
             double nu = 0.;               // Poisson ratio [-1, 0.5]
             double E = 1 / e->computeV(); // Young modulus
             H(0, 0) = 1 - nu;
@@ -284,13 +286,8 @@ void MshDeform::deform()
         }
 
         // Elementary stiffness matrix
-        Eigen::MatrixXd KKe;
-        e->buildK_linElastic(KKe, H);
-        gmm::dense_matrix<double> Ke(KKe.rows(), KKe.cols());
-        for (size_t ii = 0; ii < KKe.rows(); ++ii)
-            for (size_t jj = 0; jj < KKe.cols(); ++jj)
-                Ke(ii, jj) = KKe(ii, jj);
-
+        Eigen::MatrixXd Ke;
+        e->buildK_linElastic(Ke, H);
 
         // Assembly
         //        tbb::spin_mutex::scoped_lock lock(mutex);
@@ -304,13 +301,15 @@ void MshDeform::deform()
                     for (int n = 0; n < nDim; ++n)
                     {
                         size_t rowj = nDim * (e->nodes[j]->row) + n;
-                        Kt(rowi, rowj) += Ke(nDim * i + m, nDim * j + n);
+                        T.push_back(Eigen::Triplet<double>(rowi, rowj, Ke(nDim * i + m, nDim * j + n)));
                     }
                 }
             }
         }
     }
     //    });
+    // Build matrix without BCs
+    K.setFromTriplets(T.begin(), T.end());
     // Apply "periodic condition" on internal boundaries
     // -> for each node pair (a,b)
     for (auto p : intBndPair)
@@ -319,17 +318,20 @@ void MshDeform::deform()
         {
             size_t row0 = nDim * (p.first->row) + m;
             size_t row1 = nDim * (p.second->row) + m;
-            // -> Add the lines a and b on b, and reset a line
-            for (size_t j = 0; j < Kt.ncols(); j++)
+            // first grab "a" row values...
+            std::vector<Eigen::Triplet<double>> tUp;
+            tUp.reserve(nDim * mshSize / 10);
+            for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(K, row0); it; ++it)
             {
-                Kt(row1, j) = Kt(row1, j) + Kt(row0, j);
-                Kt(row0, j) = 0.;
+                tUp.push_back(Eigen::Triplet<double>(it.row(), it.col(), it.value()));
+                it.valueRef() = 0.;
             }
-            f[row1] += f[row0];
-            // -> Write the equation on a line (posA = posB)
-            Kt(row0, row0) = 1.0;
-            Kt(row0, row1) = -1.0;
-            f[row0] = 0.;
+            // ...then add them to "b" row values
+            for (auto t : tUp)
+                K.coeffRef(row1, t.col()) += t.value();
+            // write equation on "a" line (pos"a" = pos"b")
+            K.coeffRef(row0, row0) = 1.0;
+            K.coeffRef(row0, row1) = -1.0;
         }
     }
     // Apply Dirichlet condition on fixed boundaries
@@ -338,18 +340,26 @@ void MshDeform::deform()
         for (int m = 0; m < nDim; ++m)
         {
             size_t rowi = nDim * (fxdBndNodes[i]->row) + m;
-            Kt.row(rowi) = gmm::wsvector<double>(nDim * mshSize); // clear row
-            Kt(rowi, rowi) = 1.;
-            f[rowi] = 0.;
+            for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(K, rowi); it; ++it)
+            {
+                if (it.row() == it.col())
+                    it.valueRef() = 1.;
+                else
+                    it.valueRef() = 0.;
+            }
         }
     }
     // Apply Dirichlet condition on symmetry boundaries
     for (size_t i = 0; i < symBndNodes.size(); i++)
     {
         size_t rowi = nDim * (symBndNodes[i]->row) + blck;
-        Kt.row(rowi) = gmm::wsvector<double>(nDim * mshSize); // clear row
-        Kt(rowi, rowi) = 1.;
-        f[rowi] = 0.;
+        for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(K, rowi); it; ++it)
+        {
+            if (it.row() == it.col())
+                it.valueRef() = 1.;
+            else
+                it.valueRef() = 0.;
+        }
     }
     // Apply Dirichlet condition on moving boundaries
     for (size_t i = 0; i < movBndNodes.size(); i++)
@@ -357,20 +367,25 @@ void MshDeform::deform()
         for (int m = 0; m < nDim; ++m)
         {
             size_t rowi = nDim * (movBndNodes[i]->row) + m;
-            Kt.row(rowi) = gmm::wsvector<double>(nDim * mshSize); // clear row
-            Kt(rowi, rowi) = 1.;
-            f[rowi] = movBndNodes[i]->pos.x[m] - initPos[nDim * i + m];
+            for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(K, rowi); it; ++it)
+            {
+                if (it.row() == it.col())
+                    it.valueRef() = 1.;
+                else
+                    it.valueRef() = 0.;
+            }
+            f(rowi) = movBndNodes[i]->pos.x[m] - initPos[nDim * i + m];
         }
     }
+    // Clean matrix and turn to compressed row format
+    K.prune(0.);
+    K.makeCompressed();
     std::cout << "done" << std::endl;
-
     // Solve the SoE
     std ::cout << "Solving equations... " << std::flush;
-    gmm::copy(Kt, K);
-    gmm::iteration iter(1e-8);
-    int restart = 50;
-    gmm::ilu_precond<gmm::csr_matrix<double>> PR(K);
-    gmm::gmres(K, q, f, PR, restart, iter);
+    Eigen::BiCGSTAB<Eigen::SparseMatrix<double, Eigen::RowMajor>, Eigen::IncompleteLUT<double>> solver; // something more efficient might exist
+    solver.compute(K);
+    q = solver.solve(f);
     std::cout << "done" << std::endl;
 
     // Update position
@@ -378,13 +393,13 @@ void MshDeform::deform()
     for (auto n : msh->nodes)
     {
         for (int m = 0; m < nDim; m++)
-            n->pos.x[m] += q[nDim * (n->row) + m];
+            n->pos.x[m] += q(nDim * (n->row) + m);
     }
     // Reset moving boundary position
     for (auto n : movBndNodes)
     {
         for (int m = 0; m < nDim; m++)
-            n->pos.x[m] -= q[nDim * (n->row) + m];
+            n->pos.x[m] -= q(nDim * (n->row) + m);
     }
     // Update element memory
     for (auto e : fldElems)
diff --git a/tbox/src/wMumpsInterface.cpp b/tbox/src/wMumpsInterface.cpp
index e2488a7576b022aa75369d98e1ed1aac2673a667..74c1b45a25412c1e12fa4f3280c5c9425fd2b9f3 100644
--- a/tbox/src/wMumpsInterface.cpp
+++ b/tbox/src/wMumpsInterface.cpp
@@ -34,45 +34,38 @@ MumpsInterface::MumpsInterface(std::map<std::string, int> const &opts)
 
 /**
  * @brief Setup the system of equations
- * 
- * Works only for row-major matrices
  * @authors Adrien Crovato
  */
-void MumpsInterface::setSoE(gmm::csr_matrix<double> const &A, std::vector<double> const &b)
+void MumpsInterface::setSoE(Eigen::SparseMatrix<double> const &A, Eigen::Map<Eigen::VectorXd> const &b)
 {
     // Check that matrix A is square
-    if (gmm::mat_nrows(A) != gmm::mat_ncols(A))
+    if (A.rows() != A.cols())
         throw std::runtime_error("tbox::MumpsInterface::setMat: Matrix A is not square!\n");
     // Build data structure
-    // [AC] the matrix size should not change at each iteration, but can the number of nonzero element change?
-    //      if so, we need to clear and resize at each iteration, otherwise we can split this method
-    size_t nz = gmm::nnz(A);
-    size_t nr = gmm::mat_nrows(A);
-    size_t nc = gmm::mat_ncols(A);
+    // the matrix size, structure and number of nonzero entries can change at each iteration!
+    size_t nz = A.nonZeros();
     rows.clear();
     cols.clear();
     matx.clear();
-    vect.clear();
+    vect.setZero();
     rows.reserve(nz);
     cols.reserve(nz);
     matx.reserve(nz);
-    vect.resize(b.size());
-    gmm::copy(b, vect);
-    // assume order of A to be row-major
-    for (size_t i = 0; i < nr; ++i)
-        for (size_t j = 0; j < nc; ++j)
-            if (A(i, j) != 0.)
-            {
-                rows.push_back(static_cast<MUMPS_INT>(i + 1));
-                cols.push_back(static_cast<MUMPS_INT>(j + 1));
-                matx.push_back(A(i, j));
-            }
+    vect = b;
+    // Get the nonzero indices and values
+    for (size_t k = 0; k < A.outerSize(); ++k)
+        for (Eigen::SparseMatrix<double>::InnerIterator it(A, k); it; ++it)
+        {
+            rows.push_back(static_cast<MUMPS_INT>(it.row() + 1));
+            cols.push_back(static_cast<MUMPS_INT>(it.col() + 1));
+            matx.push_back(it.value());
+        }
     // Set sizes
-    id.n = int(nr);
+    id.n = int(A.rows());
     id.nz = int(nz);
     // Set matrices
     id.a = &matx[0];
-    id.rhs = &vect[0];
+    id.rhs = vect.data();
     // Set id
     id.irn = &rows[0];
     id.jcn = &cols[0];
@@ -100,12 +93,23 @@ void MumpsInterface::factorize()
  * @brief Solve the SoE
  * @authors Adrien Crovato, Romain Boman
  */
-void MumpsInterface::solve(std::vector<double> &x)
+void MumpsInterface::solve(Eigen::Map<Eigen::VectorXd> &x)
 {
     id.job = 3;
     dmumps_c(&id);
     check();
-    gmm::copy(vect, x);
+    x = vect;
+}
+/**
+ * @brief Analyze the structure of the matrix, factorize it and solve the SoE
+ * @authors Adrien Crovato, Romain Boman
+ */
+void MumpsInterface::compute(Eigen::Map<Eigen::VectorXd> &x)
+{
+    id.job = 6;
+    dmumps_c(&id);
+    check();
+    x = vect;
 }
 
 /**
diff --git a/tbox/src/wMumpsInterface.h b/tbox/src/wMumpsInterface.h
index 494fb1ff9e06eba2fbc813138455687bf692be1d..9035bdd49b3d5786b3330f9fc2af493fb6455fd4 100644
--- a/tbox/src/wMumpsInterface.h
+++ b/tbox/src/wMumpsInterface.h
@@ -20,7 +20,7 @@
 #include "tbox.h"
 #include "wObject.h"
 #include <dmumps_c.h>
-#include "gmm_gmm.h"
+#include <Eigen/Sparse>
 
 #ifndef SWIG
 
@@ -34,10 +34,10 @@ namespace tbox
  */
 
 /**
- * @brief MUMPS interface for tbox (gmm)
+ * @brief MUMPS interface for Eigen
  * 
  * Reference: https://github.com/rboman/math0471/blob/master/mumps/main.cpp
- * Reference gmm_MUMPS_interface.h
+ * Reference gmm_MUMPS_interface.h (old)
  * List of supported options (refer to setOptions() for description):
  * - verbosity
  * - ordering
@@ -52,7 +52,7 @@ private:
     std::vector<MUMPS_INT> rows; ///< rows id
     std::vector<MUMPS_INT> cols; ///< columns id
     std::vector<double> matx;    ///< matrix nonzero entries
-    std::vector<double> vect;    ///< rhs vector
+    Eigen::VectorXd vect;        ///< rhs vector
 
     void setOptions(std::map<std::string, int> const &opts);
     void check();
@@ -60,10 +60,11 @@ private:
 public:
     MumpsInterface(std::map<std::string, int> const &opts);
 
-    void setSoE(gmm::csr_matrix<double> const &A, std::vector<double> const &b);
+    void setSoE(Eigen::SparseMatrix<double> const &A, Eigen::Map<Eigen::VectorXd> const &b);
     void analyze();
     void factorize();
-    void solve(std::vector<double> &x);
+    void solve(Eigen::Map<Eigen::VectorXd> &x);
+    void compute(Eigen::Map<Eigen::VectorXd> &x);
     void finalize();
 
     virtual void write(std::ostream &out) const override;
diff --git a/waves/src/wForwardEuler.cpp b/waves/src/wForwardEuler.cpp
index 77b9ac252648eb447fd86a1139db2b5a2126113c..db4527259962c58112960aa55486177d6ebf33a0 100644
--- a/waves/src/wForwardEuler.cpp
+++ b/waves/src/wForwardEuler.cpp
@@ -25,7 +25,6 @@
 #include "wSource.h"
 #include "wResults.h"
 #include "wMshExport.h"
-#include "gmm_gmm.h"
 using namespace tbox;
 using namespace waves;
 
@@ -41,6 +40,7 @@ void ForwardEuler::start(std::shared_ptr<MshExport> mshWriter)
     auto msh = pbl->msh;
     u1.resize(msh->nodes.size());
     v1.resize(msh->nodes.size());
+    Eigen::Map<Eigen::VectorXd> u1_(u1.data(), u1.size()), v1_(v1.data(), v1.size());
 
     // assign a row for each node
     int i = 0;
@@ -49,11 +49,12 @@ void ForwardEuler::start(std::shared_ptr<MshExport> mshWriter)
     //std::for_each(msh->nodes.begin(), msh->nodes.end(), [&](Node *n) { n->row = i++; });
 
     // matrix assembly
-    gmm::csr_matrix<double> K;
+    Eigen::SparseMatrix<double, Eigen::RowMajor> K(msh->nodes.size(), msh->nodes.size());
     std::vector<double> Md(msh->nodes.size());
+    Eigen::Map<Eigen::VectorXd> Md_(Md.data(), Md.size());
     buildKM(K, Md, u1);
 
-    gmm::csr_matrix<double> S;
+    Eigen::SparseMatrix<double, Eigen::RowMajor> S(msh->nodes.size(), msh->nodes.size());
     buildS(S);
 
     // setup results
@@ -66,7 +67,6 @@ void ForwardEuler::start(std::shared_ptr<MshExport> mshWriter)
     std::cout << "\n\nTime integration...\n";
     std::cout << *this << '\n';
 
-    std::vector<double> tmp1(msh->nodes.size()), tmp2(msh->nodes.size());
     double t = 0.0;
     int nt = 1;
     results.nt = nt;
@@ -86,16 +86,13 @@ void ForwardEuler::start(std::shared_ptr<MshExport> mshWriter)
         nt++;
 
         // equation #1
-        gmm::add(u1, gmm::scaled(v1, dt), u1); // u1 = u0 + dt*v0
+        u1_ += v1_ * dt; // u1 = u0 + dt*v0
 
         for (auto s : pbl->srcs)
             s->apply(t, u1);
 
         // equation #2
-        gmm::mult(K, u1, tmp1);
-        gmm::mult(S, v1, tmp2);
-        for (size_t i = 0; i < gmm::vect_size(Md); ++i)
-            v1[i] = v1[i] - dt / Md[i] * (tmp1[i] + tmp2[i]);
+        v1_.array() -= dt * (K * u1_ + S * v1_).array() / Md_.array();
 
         if (nt % savefreq == 0)
         {
diff --git a/waves/src/wNewmark.cpp b/waves/src/wNewmark.cpp
index f09f1d2d8c4ac1ee21af2c1e0c9fb0303b710112..0ae1088f6afd6f80540f9afdae636a95846db392 100644
--- a/waves/src/wNewmark.cpp
+++ b/waves/src/wNewmark.cpp
@@ -25,7 +25,6 @@
 #include "wSource.h"
 #include "wResults.h"
 #include "wMshExport.h"
-#include "gmm_gmm.h"
 using namespace tbox;
 using namespace waves;
 
@@ -43,6 +42,7 @@ void Newmark::start(std::shared_ptr<MshExport> mshWriter)
     auto msh = pbl->msh;
     u1.resize(msh->nodes.size());
     v1.resize(msh->nodes.size());
+    Eigen::Map<Eigen::VectorXd> u1_(u1.data(), u1.size()), v1_(v1.data(), v1.size());
 
     // assign a row for each node
     int i = 0;
@@ -50,11 +50,12 @@ void Newmark::start(std::shared_ptr<MshExport> mshWriter)
         n->row = i++;
 
     // matrix assembly
-    gmm::csr_matrix<double> K;
+    Eigen::SparseMatrix<double, Eigen::RowMajor> K(msh->nodes.size(), msh->nodes.size());
     std::vector<double> Md(msh->nodes.size());
+    Eigen::Map<Eigen::VectorXd> Md_(Md.data(), Md.size());
     buildKM(K, Md, u1);
 
-    gmm::csr_matrix<double> S;
+    Eigen::SparseMatrix<double, Eigen::RowMajor> S(msh->nodes.size(), msh->nodes.size());
     buildS(S);
 
     // setup results
@@ -67,7 +68,6 @@ void Newmark::start(std::shared_ptr<MshExport> mshWriter)
     std::cout << "\n\nTime integration...\n";
     std::cout << *this << '\n';
 
-    std::vector<double> tmp1(msh->nodes.size()), tmp2(msh->nodes.size());
     double t = 0.0;
     int nt = 1;
     results.nt = nt;
@@ -85,16 +85,13 @@ void Newmark::start(std::shared_ptr<MshExport> mshWriter)
         nt++;
 
         // equation #1
-        gmm::add(u1, gmm::scaled(v1, dt), u1); // u1 = u0 + dt*v0
+        u1_ += v1_ * dt; // u1 = u0 + dt*v0
 
         for (auto s : pbl->srcs)
             s->apply(t, u1);
 
         // equation #2
-        gmm::mult(K, u1, tmp1);
-        gmm::mult(S, v1, tmp2);
-        for (size_t i = 0; i < gmm::vect_size(Md); ++i)
-            v1[i] = v1[i] - dt / Md[i] * (tmp1[i] + tmp2[i]);
+        v1_.array() -= dt * (K * u1_ + S * v1_).array() / Md_.array();
 
         if (nt % savefreq == 0)
         {
diff --git a/waves/src/wRungeKutta.cpp b/waves/src/wRungeKutta.cpp
index 015c0f95eb72f39a1d8a2b3532abbb8867acee7c..30b2a4adfd2d44cf7d065e0fdd41429c62fe0aad 100644
--- a/waves/src/wRungeKutta.cpp
+++ b/waves/src/wRungeKutta.cpp
@@ -25,7 +25,6 @@
 #include "wSource.h"
 #include "wResults.h"
 #include "wMshExport.h"
-#include "gmm_gmm.h"
 using namespace tbox;
 using namespace waves;
 
@@ -48,11 +47,11 @@ void RungeKutta::start(std::shared_ptr<MshExport> mshWriter)
         n->row = i++;
 
     // matrix assembly
-    gmm::csr_matrix<double> K;
+    Eigen::SparseMatrix<double, Eigen::RowMajor> K(msh->nodes.size(), msh->nodes.size());
     std::vector<double> Md(msh->nodes.size());
     buildKM(K, Md, u1);
 
-    gmm::csr_matrix<double> S;
+    Eigen::SparseMatrix<double, Eigen::RowMajor> S(msh->nodes.size(), msh->nodes.size());
     buildS(S);
 
     // setup results
@@ -94,14 +93,14 @@ void RungeKutta::start(std::shared_ptr<MshExport> mshWriter)
         t += dt;
         nt++;
 
-        predictor(up1, vp1, u1, v1, u1, v1, tmp1, tmp2, dt, Md, K, S, 1.0, t);
-        predictor(up2, vp2, up1, vp1, u1, v1, tmp1, tmp2, dt, Md, K, S, 1.0, t);
-        predictor(up3, vp3, up2, vp2, u1, v1, tmp1, tmp2, dt, Md, K, S, 1.0, t);
+        predictor(up1, vp1, u1, v1, u1, v1, dt, Md, K, S, 1.0, t);
+        predictor(up2, vp2, up1, vp1, u1, v1, dt, Md, K, S, 1.0, t);
+        predictor(up3, vp3, up2, vp2, u1, v1, dt, Md, K, S, 1.0, t);
 
-        predictor(u2, v2, u1, v1, u1, v1, tmp1, tmp2, dt, Md, K, S, 1. / 2., t);
-        predictor(u1, v1, up1, vp1, u2, v2, tmp1, tmp2, dt, Md, K, S, 1. / 3., t);
-        predictor(u2, v2, up2, vp2, u1, v1, tmp1, tmp2, dt, Md, K, S, 1. / 8., t);
-        predictor(u1, v1, up3, vp3, u2, v2, tmp1, tmp2, dt, Md, K, S, 1. / 24., t);
+        predictor(u2, v2, u1, v1, u1, v1, dt, Md, K, S, 1. / 2., t);
+        predictor(u1, v1, up1, vp1, u2, v2, dt, Md, K, S, 1. / 3., t);
+        predictor(u2, v2, up2, vp2, u1, v1, dt, Md, K, S, 1. / 8., t);
+        predictor(u1, v1, up3, vp3, u2, v2, dt, Md, K, S, 1. / 24., t);
 
         //u1[snod->row] = sin(2*M_PI*20*t); //BC bis
         //if(source) source->apply(t, u1);
@@ -128,13 +127,15 @@ void RungeKutta::start(std::shared_ptr<MshExport> mshWriter)
 void RungeKutta::predictor(std::vector<double> &up2, std::vector<double> &vp2, // premier membre
                            std::vector<double> &up1, std::vector<double> &vp1, // predicteur
                            std::vector<double> &u0, std::vector<double> &v0,   // sol au temps precedent
-                           std::vector<double> &tmp1, std::vector<double> &tmp2, double dt,
+                           double dt,
                            std::vector<double> &Md,
-                           gmm::csr_matrix<double> &K,
-                           gmm::csr_matrix<double> &S, double ak, double t)
+                           Eigen::SparseMatrix<double, Eigen::RowMajor> &K,
+                           Eigen::SparseMatrix<double, Eigen::RowMajor> &S, double ak, double t)
 {
+    // Define maps
+    Eigen::Map<Eigen::VectorXd> u0_(u0.data(), u0.size()), v0_(v0.data(), v0.size()), up1_(up1.data(), up1.size()), vp1_(vp1.data(), vp1.size()), up2_(up2.data(), up2.size()), vp2_(vp2.data(), vp2.size()), Md_(Md.data(), Md.size());
     // equation #1
-    gmm::add(u0, gmm::scaled(vp1, ak * dt), up2);
+    up2_ = u0_ + vp1_ * ak * dt;
 
     //up2[snod->row] = sin(2*M_PI*20*t); //BC bis
     //if(source) source->apply(t, up2);
@@ -142,8 +143,5 @@ void RungeKutta::predictor(std::vector<double> &up2, std::vector<double> &vp2, /
     std::for_each(pbl->srcs.begin(), pbl->srcs.end(), [&](std::shared_ptr<Source> s) { s->apply(t, up2); });
 
     // equation #2
-    gmm::mult(K, up1, tmp1);
-    gmm::mult(S, vp1, tmp2);
-    for (size_t i = 0; i < gmm::vect_size(Md); ++i)
-        vp2[i] = v0[i] - ak * dt / Md[i] * (tmp1[i] + tmp2[i]);
+    vp2_.array() = v0_.array() - ak * dt * (K * up1_ + S * vp1_).array() / Md_.array();
 }
diff --git a/waves/src/wRungeKutta.h b/waves/src/wRungeKutta.h
index bdc622cdd67ff7eae3d46cf29ae224bfcd0222f9..f8ee1eeda3ec7242e227c5702d8d314628fd88d7 100644
--- a/waves/src/wRungeKutta.h
+++ b/waves/src/wRungeKutta.h
@@ -19,7 +19,7 @@
 
 #include "waves.h"
 #include "wTimeIntegration.h"
-#include "gmm_gmm.h"
+#include <Eigen/Sparse>
 #include <vector>
 #include <iostream>
 
@@ -43,10 +43,10 @@ private:
     void predictor(std::vector<double> &up2, std::vector<double> &vp2, // premier membre
                    std::vector<double> &up1, std::vector<double> &vp1, // predicteur
                    std::vector<double> &u0, std::vector<double> &v0,   // sol au temps precedent
-                   std::vector<double> &tmp1, std::vector<double> &tmp2, double dt,
+                   double dt,
                    std::vector<double> &Md,
-                   gmm::csr_matrix<double> &K,
-                   gmm::csr_matrix<double> &S, double ak, double t);
+                   Eigen::SparseMatrix<double, Eigen::RowMajor> &K,
+                   Eigen::SparseMatrix<double, Eigen::RowMajor> &S, double ak, double t);
 };
 
 } // namespace waves
diff --git a/waves/src/wTimeIntegration.cpp b/waves/src/wTimeIntegration.cpp
index 91d62a73880f8e1844ddfae763cbc8a04e8890fd..8985c38c7f8280255f62926c5227161fe9508f13 100644
--- a/waves/src/wTimeIntegration.cpp
+++ b/waves/src/wTimeIntegration.cpp
@@ -26,7 +26,6 @@
 #include "wBoundary.h"
 #include "wFct0.h"
 #include "wMshExport.h"
-#include "gmm_gmm.h"
 #include <typeinfo>
 
 #include <tbb/task_scheduler_init.h>
@@ -45,7 +44,7 @@ TimeIntegration::TimeIntegration(std::shared_ptr<Problem> _pbl) : pbl(_pbl)
     ttot = 10.0;
     dt = 1.0e-1 / 5;
     savefreq = 5;
-    nthreads = 4;
+    nthreads = 1;
     wgmsh = Wgmsh::WG_NONE;
 
     verbose = false;
@@ -88,13 +87,14 @@ void TimeIntegration::dummyIC()
     }
 }
 
-void TimeIntegration::buildS(gmm::csr_matrix<double> &S2)
+void TimeIntegration::buildS(Eigen::SparseMatrix<double, Eigen::RowMajor> &S2)
 {
     tbb::spin_mutex mutex;
     tbb::task_scheduler_init init(nthreads);
 
     auto msh = pbl->msh;
-    gmm::row_matrix<gmm::wsvector<double>> S(msh->nodes.size(), msh->nodes.size());
+    std::vector<Eigen::Triplet<double>> T; // list of triplets to build S matrix
+    T.reserve(msh->nodes.size());
 
     std::cout << "building S (TBB/lambda) using " << nthreads << " threads...\n";
 
@@ -108,13 +108,9 @@ void TimeIntegration::buildS(gmm::csr_matrix<double> &S2)
                                  return;
                              //std::cout << "processing element #" << e->no << "\n";
 
-                             Eigen::MatrixXd SSe;
+                             Eigen::MatrixXd Se;
                              // ** Se matrix => S vector
-                             e->buildS(SSe);
-                             gmm::dense_matrix<double> Se(SSe.rows(), SSe.cols());
-                                 for (size_t ii = 0; ii < SSe.rows(); ++ii)
-                                     for (size_t jj = 0; jj < SSe.cols(); ++jj)
-                                         Se(ii, jj) = SSe(ii, jj);
+                             e->buildS(Se);
 
                              // assembly
                              tbb::spin_mutex::scoped_lock lock(mutex);
@@ -125,21 +121,22 @@ void TimeIntegration::buildS(gmm::csr_matrix<double> &S2)
                                  for (size_t jj = 0; jj < e->nodes.size(); ++jj)
                                  {
                                      Node *nodj = e->nodes[jj];
-                                     S(nodi->row, nodj->row) += bnd->c * Se(ii, jj);
+                                     T.push_back(Eigen::Triplet<double>(nodi->row, nodj->row, bnd->c * Se(ii, jj)));
                                  }
                              }
                          });
     }
+    // Build, clean and turn to compressed row format
+    S2.setFromTriplets(T.begin(), T.end());
+    S2.prune(0.);
+    S2.makeCompressed();
 
-    // copy in a read-only CSR
-    gmm::copy(S, S2);
-
-    std::cout << "S (" << gmm::mat_nrows(S2) << "," << gmm::mat_ncols(S2) << ") nnz=" << gmm::nnz(S2) << "\n";
+    std::cout << "S (" << S2.rows() << "," << S2.cols() << ") nnz=" << S2.nonZeros() << "\n";
     chrono1.read();
     std::cout << "[cpu] " << chrono1 << '\n';
 }
 
-void TimeIntegration::buildKM(gmm::csr_matrix<double> &K2,
+void TimeIntegration::buildKM(Eigen::SparseMatrix<double, Eigen::RowMajor> &K2,
                               std::vector<double> &Md, std::vector<double> const &u)
 {
     Timer chrono;
@@ -149,14 +146,15 @@ void TimeIntegration::buildKM(gmm::csr_matrix<double> &K2,
     std::cout << "[cpu:buildKM] " << chrono << '\n';
 }
 
-void TimeIntegration::buildKM_tbb_lambda(gmm::csr_matrix<double> &K2,
+void TimeIntegration::buildKM_tbb_lambda(Eigen::SparseMatrix<double, Eigen::RowMajor> &K2,
                                          std::vector<double> &Md, std::vector<double> const &u)
 {
     tbb::spin_mutex mutex;
     tbb::task_scheduler_init init(nthreads);
 
     auto msh = pbl->msh;
-    gmm::row_matrix<gmm::wsvector<double>> K(msh->nodes.size(), msh->nodes.size());
+    std::vector<Eigen::Triplet<double>> T; // list of triplets to build K matrix
+    T.reserve(msh->nodes.size());
 
     std::cout << "building K/M (TBB/lambda) using " << nthreads << " threads...\n";
 
@@ -169,22 +167,14 @@ void TimeIntegration::buildKM_tbb_lambda(gmm::csr_matrix<double> &K2,
                                  return;
                              //std::cout << "processing element #" << e->no << "\n";
 
-                             Eigen::MatrixXd MMe;
-                             Eigen::MatrixXd KKe;
+                             Eigen::MatrixXd Me;
+                             Eigen::MatrixXd Ke;
 
                              // ** Me matrix => Md vector
-                             e->buildM(MMe);
-                             gmm::dense_matrix<double> Me(MMe.rows(), MMe.cols());
-                                 for (size_t ii = 0; ii < MMe.rows(); ++ii)
-                                     for (size_t jj = 0; jj < MMe.cols(); ++jj)
-                                         Me(ii, jj) = MMe(ii, jj);
+                             e->buildM(Me);
 
                              // ** Ke matrix => K matrix
-                             e->buildK(KKe, u, Fct0C(1.0));
-                             gmm::dense_matrix<double> Ke(KKe.rows(), KKe.cols());
-                                 for (size_t ii = 0; ii < KKe.rows(); ++ii)
-                                     for (size_t jj = 0; jj < KKe.cols(); ++jj)
-                                         Ke(ii, jj) = KKe(ii, jj);
+                             e->buildK(Ke, u, Fct0C(1.0));
 
                              // assembly
                              tbb::spin_mutex::scoped_lock lock(mutex);
@@ -195,28 +185,30 @@ void TimeIntegration::buildKM_tbb_lambda(gmm::csr_matrix<double> &K2,
                                  for (size_t jj = 0; jj < e->nodes.size(); ++jj)
                                  {
                                      Node *nodj = e->nodes[jj];
-                                     K(nodi->row, nodj->row) += (mat->c * mat->c) * Ke(ii, jj);
+                                     T.push_back(Eigen::Triplet<double>(nodi->row, nodj->row, (mat->c * mat->c) * Ke(ii, jj)));
                                      Md[nodi->row] += Me(ii, jj);
                                  }
                              }
                          });
     }
+    // Build, clean and turn to compressed row format
+    K2.setFromTriplets(T.begin(), T.end());
+    K2.prune(0.);
+    K2.makeCompressed();
 
-    // copy in a read-only CSR
-    gmm::copy(K, K2);
-
-    std::cout << "K (" << gmm::mat_nrows(K2) << "," << gmm::mat_ncols(K2) << ") nnz=" << gmm::nnz(K2) << "\n";
+    std::cout << "S (" << K2.rows() << "," << K2.cols() << ") nnz=" << K2.nonZeros() << "\n";
     chrono1.read();
     std::cout << "[cpu] " << chrono1 << '\n';
 }
 
-void TimeIntegration::build(MATTYPE type, gmm::csr_matrix<double> &A2)
+void TimeIntegration::build(MATTYPE type, Eigen::SparseMatrix<double, Eigen::RowMajor> &A2)
 {
     tbb::spin_mutex mutex;
     tbb::task_scheduler_init init(nthreads); // TODO mettre ça ailleurs...
 
     auto msh = pbl->msh;
-    gmm::row_matrix<gmm::wsvector<double>> A(msh->nodes.size(), msh->nodes.size());
+    std::vector<Eigen::Triplet<double>> T; // list of triplets to build K matrix
+    T.reserve(msh->nodes.size());
 
     std::cout << "building " << type << " using " << nthreads << " threads...\n";
 
@@ -227,12 +219,8 @@ void TimeIntegration::build(MATTYPE type, gmm::csr_matrix<double> &A2)
                          [&](Element *e) {
                              if (e->type() != ELTYPE::HEX8)
                                  return;
-                             Eigen::MatrixXd AAe;
-                             e->build(type, AAe);
-                             gmm::dense_matrix<double> Ae(AAe.rows(), AAe.cols());
-                                 for (size_t ii = 0; ii < AAe.rows(); ++ii)
-                                     for (size_t jj = 0; jj < AAe.cols(); ++jj)
-                                         Ae(ii, jj) = AAe(ii, jj);
+                             Eigen::MatrixXd Ae;
+                             e->build(type, Ae);
 
                              // assembly
                              tbb::spin_mutex::scoped_lock lock(mutex);
@@ -242,16 +230,17 @@ void TimeIntegration::build(MATTYPE type, gmm::csr_matrix<double> &A2)
                                  for (size_t jj = 0; jj < e->nodes.size(); ++jj)
                                  {
                                      Node *nodj = e->nodes[jj];
-                                     A(nodi->row, nodj->row) += Ae(ii, jj);
+                                     T.push_back(Eigen::Triplet<double>(nodi->row, nodj->row, Ae(ii, jj)));
                                  }
                              }
                          });
     }
+    // Build, clean and turn to compressed row format
+    A2.setFromTriplets(T.begin(), T.end());
+    A2.prune(0.);
+    A2.makeCompressed();
 
-    // copy in a read-only CSR
-    gmm::copy(A, A2);
-
-    std::cout << type << " (" << gmm::mat_nrows(A2) << "," << gmm::mat_ncols(A2) << ") nnz=" << gmm::nnz(A2) << "\n";
+    std::cout << "S (" << A2.rows() << "," << A2.cols() << ") nnz=" << A2.nonZeros() << "\n";
     chrono1.read();
     std::cout << "[cpu] " << chrono1 << '\n';
 }
diff --git a/waves/src/wTimeIntegration.h b/waves/src/wTimeIntegration.h
index 65ac1593ec0a7c864c797b46b9a3135281ca634b..fd9c4b4ea2b5c1689f832d51b7aa909b23b97d6e 100644
--- a/waves/src/wTimeIntegration.h
+++ b/waves/src/wTimeIntegration.h
@@ -24,7 +24,7 @@
 #include <iostream>
 #include <vector>
 #include <memory>
-#include "gmm_gmm.h"
+#include <Eigen/Sparse>
 
 namespace waves
 {
@@ -71,14 +71,14 @@ public:
 
     void dummyIC();
 
-    void buildS(gmm::csr_matrix<double> &S2);
+    void buildS(Eigen::SparseMatrix<double, Eigen::RowMajor> &S2);
 
-    void buildKM(gmm::csr_matrix<double> &K2, std::vector<double> &Md, std::vector<double> const &u);
-    void buildKM_tbb_lambda(gmm::csr_matrix<double> &K2, std::vector<double> &Md, std::vector<double> const &u);
+    void buildKM(Eigen::SparseMatrix<double, Eigen::RowMajor> &K2, std::vector<double> &Md, std::vector<double> const &u);
+    void buildKM_tbb_lambda(Eigen::SparseMatrix<double, Eigen::RowMajor> &K2, std::vector<double> &Md, std::vector<double> const &u);
 
     void setGUI(DisplayHook &hook) { dhook = &hook; }
 
-    void build(tbox::MATTYPE type, gmm::csr_matrix<double> &A2);
+    void build(tbox::MATTYPE type, Eigen::SparseMatrix<double, Eigen::RowMajor> &A2);
 
     void stop() { stopit = true; }