diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2e0b299005e12999c0dcdcffe495f2eb69bf3662..52579983f4dc7deaa42079eae59532695cdcf9ae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -245,6 +245,26 @@ else()
   endif()
 endif()
 
+# -----------------------------------------------------
+# Check for Eigen3
+# defaults use Eigen3
+set(PUGS_ENABLE_EIGEN3 AUTO CACHE STRING
+  "Choose one of: AUTO ON OFF")
+
+if (PUGS_ENABLE_EIGEN3 MATCHES "^(AUTO|ON)$")
+  find_package (Eigen3 NO_MODULE)
+
+  if (TARGET Eigen3::Eigen)
+    set(EIGEN3_TARGET Eigen3::Eigen)
+    set(EIGEN3_FOUND TRUE)
+  else()
+    set(EIGEN3_FOUND FALSE)
+  endif (TARGET Eigen3::Eigen)
+  set(PUGS_HAS_EIGEN3 ${EIGEN3_FOUND})
+else()
+  unset(PUGS_HAS_EIGEN3)
+endif()
+
 # -----------------------------------------------------
 
 if (${MPI_FOUND})
@@ -829,6 +849,16 @@ else()
   endif()
 endif()
 
+if (EIGEN3_FOUND)
+  message(" Eigen3: ${Eigen3_VERSION}")
+else()
+  if (PUGS_ENABLE_EIGEN3 MATCHES "^(AUTO|ON)$")
+    message(" Eigen3: not found!")
+  else()
+      message(" Eigen3: explicitly deactivated!")
+  endif()
+endif()
+
 if (HDF5_FOUND)
   message(" HDF5: ${HDF5_VERSION} parallel: ${HDF5_IS_PARALLEL}")
 else()
diff --git a/src/algebra/Eigen3Utils.hpp b/src/algebra/Eigen3Utils.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..14434fe940878366171fc7b264e1aa50419124ab
--- /dev/null
+++ b/src/algebra/Eigen3Utils.hpp
@@ -0,0 +1,116 @@
+#ifndef EIGEN3_UTILS_HPP
+#define EIGEN3_UTILS_HPP
+
+#include <utils/pugs_config.hpp>
+
+#ifdef PUGS_HAS_EIGEN3
+
+#include <algebra/CRSMatrix.hpp>
+#include <algebra/SmallMatrix.hpp>
+#include <algebra/TinyMatrix.hpp>
+
+#include <eigen3/Eigen/Eigen>
+
+class Eigen3DenseMatrixEmbedder
+{
+ public:
+  using MatrixType = Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>;
+
+ private:
+  MatrixType m_eigen_matrix;
+
+  Eigen3DenseMatrixEmbedder(const size_t nb_rows, const size_t nb_columns, const double* A)
+    : m_eigen_matrix{A, int(nb_rows), int(nb_columns)}
+  {}
+
+ public:
+  PUGS_INLINE
+  size_t
+  numberOfRows() const
+  {
+    return m_eigen_matrix.rows();
+  }
+
+  PUGS_INLINE
+  size_t
+  numberOfColumns() const
+  {
+    return m_eigen_matrix.cols();
+  }
+
+  PUGS_INLINE
+  MatrixType&
+  matrix()
+  {
+    return m_eigen_matrix;
+  }
+
+  PUGS_INLINE
+  const MatrixType&
+  matrix() const
+  {
+    return m_eigen_matrix;
+  }
+
+  template <size_t N>
+  Eigen3DenseMatrixEmbedder(const TinyMatrix<N>& A) : Eigen3DenseMatrixEmbedder{N, N, &A(0, 0)}
+  {}
+
+  Eigen3DenseMatrixEmbedder(const SmallMatrix<double>& A)
+    : Eigen3DenseMatrixEmbedder{A.numberOfRows(), A.numberOfColumns(), &A(0, 0)}
+  {}
+
+  ~Eigen3DenseMatrixEmbedder() = default;
+};
+
+class Eigen3SparseMatrixEmbedder
+{
+ public:
+  using MatrixType = Eigen::Map<const Eigen::SparseMatrix<double, Eigen::RowMajor>>;
+
+ private:
+  MatrixType m_eigen_matrix;
+
+ public:
+  PUGS_INLINE
+  size_t
+  numberOfRows() const
+  {
+    return m_eigen_matrix.rows();
+  }
+
+  PUGS_INLINE
+  size_t
+  numberOfColumns() const
+  {
+    return m_eigen_matrix.cols();
+  }
+
+  PUGS_INLINE
+  MatrixType&
+  matrix()
+  {
+    return m_eigen_matrix;
+  }
+
+  PUGS_INLINE
+  const MatrixType&
+  matrix() const
+  {
+    return m_eigen_matrix;
+  }
+
+  Eigen3SparseMatrixEmbedder(const CRSMatrix<double>& A)
+    : m_eigen_matrix{A.numberOfRows(),
+                     A.numberOfColumns(),
+                     static_cast<int>(A.values().size()),                                  //
+                     (A.rowMap().size() > 0) ? &(A.rowMap()[0]) : nullptr,                 //
+                     (A.columnIndices().size() > 0) ? &(A.columnIndices()[0]) : nullptr,   //
+                     (A.values().size() > 0) ? &(A.values()[0]) : nullptr}
+  {}
+  ~Eigen3SparseMatrixEmbedder() = default;
+};
+
+#endif   // PUGS_HAS_EIGEN3
+
+#endif   // EIGEN3_UTILS_HPP
diff --git a/src/algebra/LinearSolver.cpp b/src/algebra/LinearSolver.cpp
index 99bff927c744c9ba216e575dcc914a2bdba1d4da..7504ced5f7390e3d69c4587c0d2dbf1f1b153517 100644
--- a/src/algebra/LinearSolver.cpp
+++ b/src/algebra/LinearSolver.cpp
@@ -18,6 +18,13 @@ struct LinearSolver::Internals
     case LSLibrary::builtin: {
       return true;
     }
+    case LSLibrary::eigen3: {
+#ifdef PUGS_HAS_EIGEN3
+      return true;
+#else
+      return false;
+#endif
+    }
     case LSLibrary::petsc: {
 #ifdef PUGS_HAS_PETSC
       return true;
@@ -57,6 +64,28 @@ struct LinearSolver::Internals
     }
   }
 
+  static void
+  checkEigen3Method(const LSMethod method)
+  {
+    switch (method) {
+    case LSMethod::cg:
+#warning clean-up
+      // case LSMethod::bicgstab:
+      // case LSMethod::bicgstab2:
+      // case LSMethod::gmres:
+      // case LSMethod::lu:
+      // case LSMethod::cholesky:
+      {
+        break;
+      }
+      // LCOV_EXCL_START
+    default: {
+      throw NormalError(name(method) + " is not an Eigen3 linear solver!");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
   static void
   checkPETScMethod(const LSMethod method)
   {
@@ -90,6 +119,27 @@ struct LinearSolver::Internals
     }
   }
 
+  static void
+  checkEigen3Precond(const LSPrecond precond)
+  {
+    switch (precond) {
+    case LSPrecond::none:
+#warning clean-up
+      // case LSPrecond::amg:
+      // case LSPrecond::diagonal:
+      // case LSPrecond::incomplete_cholesky:
+      // case LSPrecond::incomplete_LU:
+      {
+        break;
+      }
+      // LCOV_EXCL_START
+    default: {
+      throw NormalError(name(precond) + " is not an Eigen3 preconditioner!");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
   static void
   checkPETScPrecond(const LSPrecond precond)
   {
@@ -118,6 +168,11 @@ struct LinearSolver::Internals
       checkBuiltinPrecond(options.precond());
       break;
     }
+    case LSLibrary::eigen3: {
+      checkEigen3Method(options.method());
+      checkEigen3Precond(options.precond());
+      break;
+    }
     case LSLibrary::petsc: {
       checkPETScMethod(options.method());
       checkPETScPrecond(options.precond());
diff --git a/src/algebra/LinearSolverOptions.hpp b/src/algebra/LinearSolverOptions.hpp
index 940ce1ed63a2dbbf046022aee838c3f9438c1906..97a1748780f19d28e98f299fd7679d7ab15466f0 100644
--- a/src/algebra/LinearSolverOptions.hpp
+++ b/src/algebra/LinearSolverOptions.hpp
@@ -10,6 +10,7 @@ enum class LSLibrary : int8_t
   LS__begin = 0,
   //
   builtin = LS__begin,
+  eigen3,
   petsc,
   //
   LS__end
@@ -49,6 +50,9 @@ name(const LSLibrary library)
   case LSLibrary::builtin: {
     return "builtin";
   }
+  case LSLibrary::eigen3: {
+    return "Eigen3";
+  }
   case LSLibrary::petsc: {
     return "PETSc";
   }
diff --git a/src/utils/BuildInfo.cpp b/src/utils/BuildInfo.cpp
index 81d7567750eaef8513b580daf11eac6d3f916642..2413bdbaeb5712e3b7eaf96fd9d09df5e8f0a6e0 100644
--- a/src/utils/BuildInfo.cpp
+++ b/src/utils/BuildInfo.cpp
@@ -17,6 +17,10 @@
 #include <slepc.h>
 #endif   // PUGS_HAS_PETSC
 
+#ifdef PUGS_HAS_EIGEN3
+#include <eigen3/Eigen/Eigen>
+#endif   // PUGS_HAS_EIGEN3
+
 #ifdef PUGS_HAS_HDF5
 #include <highfive/highfive.hpp>
 #endif   // PUGS_HAS_HDF5
@@ -82,6 +86,16 @@ BuildInfo::slepcLibrary()
 #endif   // PUGS_HAS_SLEPC
 }
 
+std::string
+BuildInfo::eigen3Library()
+{
+#ifdef PUGS_HAS_EIGEN3
+  return stringify(EIGEN_WORLD_VERSION) + "." + stringify(EIGEN_MAJOR_VERSION) + "." + stringify(EIGEN_MINOR_VERSION);
+#else    // PUGS_HAS_EIGEN3
+  return "none";
+#endif   // PUGS_HAS_EIGEN3
+}
+
 std::string
 BuildInfo::hdf5Library()
 {
diff --git a/src/utils/BuildInfo.hpp b/src/utils/BuildInfo.hpp
index df4a32d93a284db7fc8d3abbea9ac2e43ea5d36e..86f7c04f035827a936a00c9b1f42e304e33e7b26 100644
--- a/src/utils/BuildInfo.hpp
+++ b/src/utils/BuildInfo.hpp
@@ -11,6 +11,7 @@ struct BuildInfo
   static std::string mpiLibrary();
   static std::string petscLibrary();
   static std::string slepcLibrary();
+  static std::string eigen3Library();
   static std::string hdf5Library();
   static std::string slurmLibrary();
 };
diff --git a/src/utils/PugsUtils.cpp b/src/utils/PugsUtils.cpp
index 1ac38b32696417d887e91205715b9fc80d9f81af..ac64a50cfe0b026fe0185d79e30d4f6d648317ac 100644
--- a/src/utils/PugsUtils.cpp
+++ b/src/utils/PugsUtils.cpp
@@ -64,6 +64,7 @@ pugsBuildInfo()
   os << "MPI:      " << rang::style::bold << BuildInfo::mpiLibrary() << rang::style::reset << '\n';
   os << "PETSc:    " << rang::style::bold << BuildInfo::petscLibrary() << rang::style::reset << '\n';
   os << "SLEPc:    " << rang::style::bold << BuildInfo::slepcLibrary() << rang::style::reset << '\n';
+  os << "Eigen3:   " << rang::style::bold << BuildInfo::eigen3Library() << rang::style::reset << '\n';
   os << "HDF5:     " << rang::style::bold << BuildInfo::hdf5Library() << rang::style::reset << '\n';
   os << "SLURM:    " << rang::style::bold << BuildInfo::slurmLibrary() << rang::style::reset << '\n';
   os << "-------------------------------------------------------";
diff --git a/src/utils/pugs_config.hpp.in b/src/utils/pugs_config.hpp.in
index 7402a526d5f34505dbda8524226c971e4701d4b6..a07d8c314da95f8db595886e3df5b29cdc45907a 100644
--- a/src/utils/pugs_config.hpp.in
+++ b/src/utils/pugs_config.hpp.in
@@ -5,6 +5,7 @@
 #cmakedefine PUGS_HAS_MPI
 #cmakedefine PUGS_HAS_PETSC
 #cmakedefine PUGS_HAS_SLEPC
+#cmakedefine PUGS_HAS_EIGEN3
 #cmakedefine PUGS_HAS_HDF5
 #cmakedefine PUGS_HAS_SLURM
 
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 23a4ac3094424515b6d488a577781cfe6bbcce5e..1dd13e6e3ce1f23c31e046db7597a1ed2ffb947d 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -104,6 +104,7 @@ add_executable (unit_tests
   test_DualMeshManager.cpp
   test_DualMeshType.cpp
   test_EdgeIntegrator.cpp
+  test_Eigen3Utils.cpp
   test_EigenvalueSolver.cpp
   test_EmbeddedData.cpp
   test_EmbeddedDiscreteFunctionUtils.cpp
diff --git a/tests/test_Eigen3Utils.cpp b/tests/test_Eigen3Utils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0c51aad3c03ee90695f837c4a9cbff0566d566f3
--- /dev/null
+++ b/tests/test_Eigen3Utils.cpp
@@ -0,0 +1,107 @@
+#include <utils/pugs_config.hpp>
+
+#ifdef PUGS_HAS_EIGEN3
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <algebra/Eigen3Utils.hpp>
+
+#include <algebra/CRSMatrixDescriptor.hpp>
+
+#include <eigen3/Eigen/Core>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("Eigen3Utils", "[algebra]")
+{
+  SECTION("Eigen3DenseMatrixEmbedder")
+  {
+    SECTION("from TinyMatrix")
+    {
+      TinyMatrix<3> A{1, 2, 3, 4, 5, 6, 7, 8, 9};
+      Eigen3DenseMatrixEmbedder eigen3_A{A};
+
+      for (size_t i = 0; i < 3; ++i) {
+        for (size_t j = 0; j < 3; ++j) {
+          REQUIRE(eigen3_A.matrix().coeff(i, j) == 1 + i * 3 + j);
+        }
+      }
+    }
+
+    SECTION("from SmallMatrix")
+    {
+      SmallMatrix<double> A(3, 3);
+      for (size_t i = 0; i < 3; ++i) {
+        for (size_t j = 0; j < 3; ++j) {
+          A(i, j) = 1 + i * 3 + j;
+        }
+      }
+
+      Eigen3DenseMatrixEmbedder eigen3_A{A};
+
+      REQUIRE(eigen3_A.numberOfRows() == 3);
+      REQUIRE(eigen3_A.numberOfColumns() == 3);
+
+      for (size_t i = 0; i < 3; ++i) {
+        for (size_t j = 0; j < 3; ++j) {
+          REQUIRE(eigen3_A.matrix().coeff(i, j) == 1 + i * 3 + j);
+        }
+      }
+    }
+
+    SECTION("from SmallMatrix [non-square]")
+    {
+      SmallMatrix<double> A(4, 3);
+      for (size_t i = 0; i < 4; ++i) {
+        for (size_t j = 0; j < 3; ++j) {
+          A(i, j) = 1 + i * 3 + j;
+        }
+      }
+
+      Eigen3DenseMatrixEmbedder eigen3_A{A};
+
+      REQUIRE(eigen3_A.numberOfRows() == 4);
+      REQUIRE(eigen3_A.numberOfColumns() == 3);
+
+      for (size_t i = 0; i < 4; ++i) {
+        for (size_t j = 0; j < 3; ++j) {
+          REQUIRE(eigen3_A.matrix().coeff(i, j) == 1 + i * 3 + j);
+        }
+      }
+    }
+  }
+
+  SECTION("from CRSMatrix")
+  {
+    Array<int> non_zeros(4);
+    non_zeros[0] = 1;
+    non_zeros[1] = 2;
+    non_zeros[2] = 3;
+    non_zeros[3] = 2;
+
+    CRSMatrixDescriptor<double, int> A(4, 3, non_zeros);
+
+    A(0, 0) = 1;
+    A(1, 0) = 2;
+    A(1, 2) = 3;
+    A(2, 0) = 4;
+    A(2, 1) = 5;
+    A(2, 2) = 6;
+    A(3, 1) = 7;
+    A(3, 2) = 8;
+
+    Eigen3SparseMatrixEmbedder eigen3_A{A.getCRSMatrix()};
+
+    REQUIRE(eigen3_A.matrix().coeff(0, 0) == 1);
+    REQUIRE(eigen3_A.matrix().coeff(1, 0) == 2);
+    REQUIRE(eigen3_A.matrix().coeff(1, 2) == 3);
+    REQUIRE(eigen3_A.matrix().coeff(2, 0) == 4);
+    REQUIRE(eigen3_A.matrix().coeff(2, 1) == 5);
+    REQUIRE(eigen3_A.matrix().coeff(2, 2) == 6);
+    REQUIRE(eigen3_A.matrix().coeff(3, 1) == 7);
+    REQUIRE(eigen3_A.matrix().coeff(3, 2) == 8);
+  }
+}
+
+#endif   // PUGS_HAS_EIGEN3
diff --git a/tests/test_LinearSolver.cpp b/tests/test_LinearSolver.cpp
index 11195faab3dad2f94e82dba78b9afc368d0edcd0..01c71cbea82041747ee137ca76d15a17de89c170 100644
--- a/tests/test_LinearSolver.cpp
+++ b/tests/test_LinearSolver.cpp
@@ -21,6 +21,12 @@ TEST_CASE("LinearSolver", "[algebra]")
 #else    // PUGS_HAS_PETSC
     REQUIRE(linear_solver.hasLibrary(LSLibrary::petsc) == false);
 #endif   // PUGS_HAS_PETSC
+
+#ifdef PUGS_HAS_EIGEN3
+    REQUIRE(linear_solver.hasLibrary(LSLibrary::eigen3) == true);
+#else    // PUGS_HAS_PETSC
+    REQUIRE(linear_solver.hasLibrary(LSLibrary::eigen3) == false);
+#endif   // PUGS_HAS_PETSC
   }
 
   SECTION("check linear solver building")
@@ -108,7 +114,7 @@ TEST_CASE("LinearSolver", "[algebra]")
         REQUIRE_NOTHROW(linear_solver.checkOptions(options));
       }
 
-      SECTION("builtin precond")
+      SECTION("PETSc precond")
       {
         options.library() = LSLibrary::petsc;
         options.method()  = LSMethod::cg;
@@ -140,6 +146,72 @@ TEST_CASE("LinearSolver", "[algebra]")
       REQUIRE_THROWS_WITH(LinearSolver{options}, "error: PETSc is not linked to pugs. Cannot use it!");
     }
 #endif   // PUGS_HAS_PETSC
+
+    SECTION("Eigen3")
+    {
+      LinearSolverOptions always_valid;
+      always_valid.library() = LSLibrary::builtin;
+      always_valid.method()  = LSMethod::cg;
+      always_valid.precond() = LSPrecond::none;
+
+      LinearSolver linear_solver{always_valid};
+
+      SECTION("Eigen3 methods")
+      {
+        options.library() = LSLibrary::eigen3;
+        options.precond() = LSPrecond::none;
+
+        options.method() = LSMethod::cg;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.method() = LSMethod::bicgstab;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.method() = LSMethod::bicgstab2;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.method() = LSMethod::lu;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.method() = LSMethod::cholesky;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.method() = LSMethod::gmres;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+      }
+
+      SECTION("Eigen3 precond")
+      {
+        options.library() = LSLibrary::eigen3;
+        options.method()  = LSMethod::cg;
+
+        options.precond() = LSPrecond::none;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.precond() = LSPrecond::diagonal;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.precond() = LSPrecond::incomplete_LU;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.precond() = LSPrecond::incomplete_cholesky;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+
+        options.precond() = LSPrecond::amg;
+        REQUIRE_NOTHROW(linear_solver.checkOptions(options));
+      }
+    }
+
+#ifndef PUGS_HAS_EIGEN3
+    SECTION("not linked Eigen3")
+    {
+      options.library() = LSLibrary::eigen3;
+      options.method()  = LSMethod::cg;
+      options.precond() = LSPrecond::none;
+
+      REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!");
+    }
+#endif   // PUGS_HAS_EIGEN3
   }
 
   SECTION("Sparse Matrices")
@@ -205,6 +277,105 @@ TEST_CASE("LinearSolver", "[algebra]")
           }
         }
 
+        SECTION("Eigen3")
+        {
+#ifdef PUGS_HAS_EIGEN3
+
+          SECTION("CG")
+          {
+            LinearSolverOptions options;
+            options.library() = LSLibrary::eigen3;
+            options.method()  = LSMethod::cg;
+            options.precond() = LSPrecond::none;
+            options.verbose() = true;
+
+            SECTION("CG no preconditioner")
+            {
+              options.precond() = LSPrecond::none;
+
+              Vector<double> x{5};
+              x = zero;
+
+              LinearSolver solver{options};
+
+              solver.solveLocalSystem(A, x, b);
+              Vector error = x - x_exact;
+              REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+            }
+#warning test all options
+            // SECTION("CG Diagonal")
+            // {
+            //   options.precond() = LSPrecond::diagonal;
+
+            //   Vector<double> x{5};
+            //   x = zero;
+
+            //   LinearSolver solver{options};
+
+            //   solver.solveLocalSystem(A, x, b);
+            //   Vector error = x - x_exact;
+            //   REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+            // }
+
+            // SECTION("CG ICholesky")
+            // {
+            //   options.precond() = LSPrecond::incomplete_cholesky;
+
+            //   Vector<double> x{5};
+            //   x = zero;
+
+            //   LinearSolver solver{options};
+
+            //   solver.solveLocalSystem(A, x, b);
+            //   Vector error = x - x_exact;
+            //   REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+            // }
+
+            // SECTION("CG AMG")
+            // {
+            //   options.precond() = LSPrecond::amg;
+
+            //   Vector<double> x{5};
+            //   x = zero;
+
+            //   LinearSolver solver{options};
+
+            //   solver.solveLocalSystem(A, x, b);
+            //   Vector error = x - x_exact;
+            //   REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+            // }
+          }
+
+          SECTION("Cholesky")
+          {
+            LinearSolverOptions options;
+            options.library() = LSLibrary::eigen3;
+            options.method()  = LSMethod::cholesky;
+            options.precond() = LSPrecond::none;
+
+            Vector<double> x{5};
+            x = zero;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            Vector error = x - x_exact;
+            REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+          }
+
+#else    // PUGS_HAS_EIGEN3
+          SECTION("Eigen3 not linked")
+          {
+            LinearSolverOptions options;
+            options.library() = LSLibrary::eigen3;
+            options.method()  = LSMethod::cg;
+            options.precond() = LSPrecond::none;
+
+            REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!");
+          }
+#endif   // PUGS_HAS_EIGEN3
+        }
+
         SECTION("PETSc")
         {
 #ifdef PUGS_HAS_PETSC
@@ -753,6 +924,177 @@ TEST_CASE("LinearSolver", "[algebra]")
           }
         }
 
+        SECTION("Eigen3")
+        {
+#ifdef PUGS_HAS_EIGEN3
+
+          SECTION("BICGStab")
+          {
+            LinearSolverOptions options;
+            options.library() = LSLibrary::eigen3;
+            options.method()  = LSMethod::bicgstab;
+            options.precond() = LSPrecond::none;
+            options.verbose() = true;
+
+            SECTION("BICGStab no preconditioner")
+            {
+              options.precond() = LSPrecond::none;
+
+              SmallVector<double> x{5};
+              x = zero;
+
+              LinearSolver solver{options};
+
+              solver.solveLocalSystem(A, x, b);
+              SmallVector error = x - x_exact;
+              REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+            }
+
+            SECTION("BICGStab Diagonal")
+            {
+              options.precond() = LSPrecond::diagonal;
+
+              SmallVector<double> x{5};
+              x = zero;
+
+              LinearSolver solver{options};
+
+              solver.solveLocalSystem(A, x, b);
+              SmallVector error = x - x_exact;
+              REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+            }
+
+            SECTION("BICGStab ILU")
+            {
+              options.precond() = LSPrecond::incomplete_LU;
+
+              SmallVector<double> x{5};
+              x = zero;
+
+              LinearSolver solver{options};
+
+              solver.solveLocalSystem(A, x, b);
+              SmallVector error = x - x_exact;
+              REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+            }
+          }
+
+          SECTION("BICGStab2")
+          {
+            LinearSolverOptions options;
+            options.library() = LSLibrary::petsc;
+            options.method()  = LSMethod::bicgstab2;
+            options.precond() = LSPrecond::none;
+
+            SECTION("BICGStab2 no preconditioner")
+            {
+              options.precond() = LSPrecond::none;
+
+              SmallVector<double> x{5};
+              x = zero;
+
+              LinearSolver solver{options};
+
+              solver.solveLocalSystem(A, x, b);
+              SmallVector error = x - x_exact;
+              REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+            }
+
+            SECTION("BICGStab2 Diagonal")
+            {
+              options.precond() = LSPrecond::diagonal;
+
+              SmallVector<double> x{5};
+              x = zero;
+
+              LinearSolver solver{options};
+
+              solver.solveLocalSystem(A, x, b);
+              SmallVector error = x - x_exact;
+              REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+            }
+          }
+
+          SECTION("GMRES")
+          {
+            LinearSolverOptions options;
+            options.library() = LSLibrary::petsc;
+            options.method()  = LSMethod::gmres;
+            options.precond() = LSPrecond::none;
+
+            SECTION("GMRES no preconditioner")
+            {
+              options.precond() = LSPrecond::none;
+
+              SmallVector<double> x{5};
+              x = zero;
+
+              LinearSolver solver{options};
+
+              solver.solveLocalSystem(A, x, b);
+              SmallVector error = x - x_exact;
+              REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+            }
+
+            SECTION("GMRES Diagonal")
+            {
+              options.precond() = LSPrecond::diagonal;
+
+              SmallVector<double> x{5};
+              x = zero;
+
+              LinearSolver solver{options};
+
+              solver.solveLocalSystem(A, x, b);
+              SmallVector error = x - x_exact;
+              REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+            }
+
+            SECTION("GMRES ILU")
+            {
+              options.precond() = LSPrecond::incomplete_LU;
+
+              SmallVector<double> x{5};
+              x = zero;
+
+              LinearSolver solver{options};
+
+              solver.solveLocalSystem(A, x, b);
+              SmallVector error = x - x_exact;
+              REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+            }
+          }
+
+          SECTION("LU")
+          {
+            LinearSolverOptions options;
+            options.library() = LSLibrary::petsc;
+            options.method()  = LSMethod::lu;
+            options.precond() = LSPrecond::none;
+
+            SmallVector<double> x{5};
+            x = zero;
+
+            LinearSolver solver{options};
+
+            solver.solveLocalSystem(A, x, b);
+            SmallVector error = x - x_exact;
+            REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact)));
+          }
+
+#else    // PUGS_HAS_EIGEN3
+          SECTION("Eigen3 not linked")
+          {
+            LinearSolverOptions options;
+            options.library() = LSLibrary::petsc;
+            options.method()  = LSMethod::cg;
+            options.precond() = LSPrecond::none;
+
+            REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!");
+          }
+#endif   // PUGS_HAS_EIGEN3
+        }
+
         SECTION("PETSc")
         {
 #ifdef PUGS_HAS_PETSC
diff --git a/tests/test_LinearSolverOptions.cpp b/tests/test_LinearSolverOptions.cpp
index 90519ddf9797f0d6fa745cbc34e2f80443eed33d..6209597147cb03fed823935d8a2a69110018d76a 100644
--- a/tests/test_LinearSolverOptions.cpp
+++ b/tests/test_LinearSolverOptions.cpp
@@ -135,6 +135,7 @@ TEST_CASE("LinearSolverOptions", "[algebra]")
 
     const std::string library_list = R"(
   - builtin
+  - Eigen3
   - PETSc
 )";
 
diff --git a/tests/test_PugsUtils.cpp b/tests/test_PugsUtils.cpp
index fb86486527e3f1d91ce5c2f1e7d2653a1dea3bde..5ba8817fcaa34ddf31f0955ed9fd118f841bd87d 100644
--- a/tests/test_PugsUtils.cpp
+++ b/tests/test_PugsUtils.cpp
@@ -51,6 +51,7 @@ TEST_CASE("PugsUtils", "[utils]")
       os << "MPI:      " << rang::style::bold << BuildInfo::mpiLibrary() << rang::style::reset << '\n';
       os << "PETSc:    " << rang::style::bold << BuildInfo::petscLibrary() << rang::style::reset << '\n';
       os << "SLEPc:    " << rang::style::bold << BuildInfo::slepcLibrary() << rang::style::reset << '\n';
+      os << "Eigen3:   " << rang::style::bold << BuildInfo::eigen3Library() << rang::style::reset << '\n';
       os << "HDF5:     " << rang::style::bold << BuildInfo::hdf5Library() << rang::style::reset << '\n';
       os << "SLURM:    " << rang::style::bold << BuildInfo::slurmLibrary() << rang::style::reset << '\n';
       os << "-------------------------------------------------------";