diff --git a/src/algebra/DenseMatrixWrapper.hpp b/src/algebra/DenseMatrixWrapper.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..dee57f27eff3d4c906cb256754d86ca6eeafbdfa
--- /dev/null
+++ b/src/algebra/DenseMatrixWrapper.hpp
@@ -0,0 +1,59 @@
+#ifndef DENSE_MATRIX_WRAPPER_HPP
+#define DENSE_MATRIX_WRAPPER_HPP
+
+#include <algebra/SmallMatrix.hpp>
+#include <algebra/TinyMatrix.hpp>
+
+template <typename DataType>
+class DenseMatrixWrapper
+{
+ private:
+  const size_t m_number_of_rows;
+  const size_t m_number_of_columns;
+  const DataType* const m_matrix_ptr;
+
+ public:
+  PUGS_INLINE
+  bool
+  isSquare() const
+  {
+    return (m_number_of_rows == m_number_of_columns);
+  }
+
+  PUGS_INLINE
+  size_t
+  numberOfRows() const
+  {
+    return m_number_of_rows;
+  }
+
+  PUGS_INLINE
+  size_t
+  numberOfColumns() const
+  {
+    return m_number_of_columns;
+  }
+
+  PUGS_INLINE
+  const DataType* const&
+  ptr() const
+  {
+    return m_matrix_ptr;
+  }
+
+  DenseMatrixWrapper(const SmallMatrix<DataType>& A)
+    : m_number_of_rows{A.numberOfRows()},
+      m_number_of_columns{A.numberOfColumns()},
+      m_matrix_ptr{(m_number_of_columns * m_number_of_rows > 0) ? (&A(0, 0)) : nullptr}
+  {}
+
+  template <size_t M, size_t N>
+  DenseMatrixWrapper(const TinyMatrix<M, N, DataType>& A)
+    : m_number_of_rows{M}, m_number_of_columns{N}, m_matrix_ptr{(M * N > 0) ? (&A(0, 0)) : nullptr}
+  {}
+
+  DenseMatrixWrapper(const DenseMatrixWrapper&) = delete;
+  DenseMatrixWrapper(DenseMatrixWrapper&&)      = delete;
+};
+
+#endif   // DENSE_MATRIX_WRAPPER_HPP
diff --git a/src/algebra/Eigen3Utils.hpp b/src/algebra/Eigen3Utils.hpp
index 5224a4b0bb10dc252b0f299cc9cbbbfe9fa87712..d9d61e02746c77e22002fb1c1ecfb93db6c1a9bb 100644
--- a/src/algebra/Eigen3Utils.hpp
+++ b/src/algebra/Eigen3Utils.hpp
@@ -6,8 +6,7 @@
 #ifdef PUGS_HAS_EIGEN3
 
 #include <algebra/CRSMatrix.hpp>
-#include <algebra/SmallMatrix.hpp>
-#include <algebra/TinyMatrix.hpp>
+#include <algebra/DenseMatrixWrapper.hpp>
 
 #include <eigen3/Eigen/Eigen>
 
@@ -25,6 +24,13 @@ class Eigen3DenseMatrixEmbedder
   {}
 
  public:
+  PUGS_INLINE
+  size_t
+  isSquare() const
+  {
+    return (m_eigen_matrix.rows() == m_eigen_matrix.cols());
+  }
+
   PUGS_INLINE
   size_t
   numberOfRows() const
@@ -53,12 +59,8 @@ class Eigen3DenseMatrixEmbedder
     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(const DenseMatrixWrapper<double>& A)
+    : Eigen3DenseMatrixEmbedder{A.numberOfRows(), A.numberOfColumns(), A.ptr()}
   {}
 
   ~Eigen3DenseMatrixEmbedder() = default;
@@ -88,6 +90,13 @@ class Eigen3SparseMatrixEmbedder
     return m_eigen_matrix.cols();
   }
 
+  PUGS_INLINE
+  size_t
+  isSquare() const
+  {
+    return (m_eigen_matrix.rows() == m_eigen_matrix.cols());
+  }
+
   PUGS_INLINE
   Eigen3MapMatrixType&
   matrix()
@@ -113,6 +122,24 @@ class Eigen3SparseMatrixEmbedder
   ~Eigen3SparseMatrixEmbedder() = default;
 };
 
+#else   // PUGS_HAS_EIGEN3
+
+class Eigen3DenseMatrixEmbedder
+{
+ public:
+  Eigen3DenseMatrixEmbedder(const DenseMatrixWrapper<double>&) {}
+
+  ~Eigen3DenseMatrixEmbedder() = default;
+};
+
+class Eigen3SparseMatrixEmbedder
+{
+ public:
+  Eigen3SparseMatrixEmbedder(const CRSMatrix<double>&) {}
+
+  ~Eigen3SparseMatrixEmbedder() = default;
+};
+
 #endif   // PUGS_HAS_EIGEN3
 
 #endif   // EIGEN3_UTILS_HPP
diff --git a/src/algebra/LinearSolver.cpp b/src/algebra/LinearSolver.cpp
index 1ebb59663405883aac2405979ff60bdb7ccb3e7e..52bf8fc3969d2001685c5548f31c30ce7af20dd0 100644
--- a/src/algebra/LinearSolver.cpp
+++ b/src/algebra/LinearSolver.cpp
@@ -187,10 +187,10 @@ struct LinearSolver::Internals
 
   template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType>
   static void
-  builtinSolveLocalSystem(const MatrixType& A,
-                          SolutionVectorType& x,
-                          const RHSVectorType& b,
-                          const LinearSolverOptions& options)
+  _builtinSolveLocalSystem(const MatrixType& A,
+                           SolutionVectorType& x,
+                           const RHSVectorType& b,
+                           const LinearSolverOptions& options)
   {
     if (options.precond() != LSPrecond::none) {
       // LCOV_EXCL_START
@@ -215,18 +215,15 @@ struct LinearSolver::Internals
   }
 
 #ifdef PUGS_HAS_EIGEN3
-  template <typename PreconditionerType,
-            typename Eigen3MatrixEmbedderType,
-            typename SolutionVectorType,
-            typename RHSVectorType>
+  template <typename PreconditionerType, typename Eigen3MatrixEmbedderType>
   static void
   _preconditionedEigen3SolveLocalSystem(const Eigen3MatrixEmbedderType& eigen3_A,
-                                        SolutionVectorType& x,
-                                        const RHSVectorType& b,
+                                        VectorWrapper<double>& x,
+                                        const VectorWrapper<const double>& b,
                                         const LinearSolverOptions& options)
   {
-    Eigen::Map<Eigen::VectorX<double>> eigen3_x{(x.size() > 0) ? (&x[0]) : nullptr, static_cast<int>(x.size())};
-    Eigen::Map<const Eigen::VectorX<double>> eigen3_b{(b.size() > 0) ? (&b[0]) : nullptr, static_cast<int>(b.size())};
+    Eigen::Map<Eigen::VectorX<double>> eigen3_x{x.ptr(), static_cast<int>(x.size())};
+    Eigen::Map<const Eigen::VectorX<double>> eigen3_b{b.ptr(), static_cast<int>(b.size())};
 
     using Eigen3MatrixType = typename Eigen3MatrixEmbedderType::Eigen3MatrixType;
 
@@ -287,28 +284,29 @@ struct LinearSolver::Internals
     }
   }
 
-  template <typename Eigen3MatrixEmbedderType, typename SolutionVectorType, typename RHSVectorType>
+  template <typename Eigen3MatrixEmbedderType>
   static void
   _eigen3SolveLocalSystem(const Eigen3MatrixEmbedderType& eigen3_A,
-                          SolutionVectorType& x,
-                          const RHSVectorType& b,
+                          VectorWrapper<double>& x,
+                          const VectorWrapper<const double>& b,
                           const LinearSolverOptions& options)
   {
     switch (options.precond()) {
     case LSPrecond::none: {
-      _preconditionedEigen3SolveLocalSystem<Eigen::IdentityPreconditioner, Eigen3MatrixEmbedderType, SolutionVectorType,
-                                            RHSVectorType>(eigen3_A, x, b, options);
+      _preconditionedEigen3SolveLocalSystem<Eigen::IdentityPreconditioner, Eigen3MatrixEmbedderType>(eigen3_A, x, b,
+                                                                                                     options);
       break;
     }
     case LSPrecond::diagonal: {
-      _preconditionedEigen3SolveLocalSystem<Eigen::DiagonalPreconditioner<double>, Eigen3MatrixEmbedderType,
-                                            SolutionVectorType, RHSVectorType>(eigen3_A, x, b, options);
+      _preconditionedEigen3SolveLocalSystem<Eigen::DiagonalPreconditioner<double>, Eigen3MatrixEmbedderType>(eigen3_A,
+                                                                                                             x, b,
+                                                                                                             options);
       break;
     }
     case LSPrecond::incomplete_cholesky: {
       if constexpr (std::is_same_v<Eigen3SparseMatrixEmbedder, Eigen3MatrixEmbedderType>) {
-        _preconditionedEigen3SolveLocalSystem<Eigen::IncompleteCholesky<double>, Eigen3MatrixEmbedderType,
-                                              SolutionVectorType, RHSVectorType>(eigen3_A, x, b, options);
+        _preconditionedEigen3SolveLocalSystem<Eigen::IncompleteCholesky<double>, Eigen3MatrixEmbedderType>(eigen3_A, x,
+                                                                                                           b, options);
       } else {
         throw NormalError("incomplete cholesky is not available for dense matrices in Eigen3");
       }
@@ -316,8 +314,8 @@ struct LinearSolver::Internals
     }
     case LSPrecond::incomplete_LU: {
       if constexpr (std::is_same_v<Eigen3SparseMatrixEmbedder, Eigen3MatrixEmbedderType>) {
-        _preconditionedEigen3SolveLocalSystem<Eigen::IncompleteLUT<double>, Eigen3MatrixEmbedderType,
-                                              SolutionVectorType, RHSVectorType>(eigen3_A, x, b, options);
+        _preconditionedEigen3SolveLocalSystem<Eigen::IncompleteLUT<double>, Eigen3MatrixEmbedderType>(eigen3_A, x, b,
+                                                                                                      options);
       } else {
         throw NormalError("incomplete LU is not available for dense matrices in Eigen3");
       }
@@ -330,39 +328,12 @@ struct LinearSolver::Internals
       // LCOV_EXCL_STOP
     }
   }
-
-  template <typename DataType, typename SolutionVectorType, typename RHSVectorType>
-  static void
-  eigen3SolveLocalSystem(const CRSMatrix<DataType>& A,
-                         SolutionVectorType& x,
-                         const RHSVectorType& b,
-                         const LinearSolverOptions& options)
-  {
-    Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare());
-
-    Eigen3SparseMatrixEmbedder eigen3_A{A};
-    _eigen3SolveLocalSystem(eigen3_A, x, b, options);
-  }
-
-  template <typename DataType, typename SolutionVectorType, typename RHSVectorType>
-  static void
-  eigen3SolveLocalSystem(const SmallMatrix<DataType>& A,
-                         SolutionVectorType& x,
-                         const RHSVectorType& b,
-                         const LinearSolverOptions& options)
-  {
-    Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare());
-
-    Eigen3DenseMatrixEmbedder eigen3_A{A};
-    _eigen3SolveLocalSystem(eigen3_A, x, b, options);
-  }
-
 #else   // PUGS_HAS_EIGEN3
 
   // LCOV_EXCL_START
   template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType>
   static void
-  eigen3SolveLocalSystem(const MatrixType&, SolutionVectorType&, const RHSVectorType&, const LinearSolverOptions&)
+  _eigen3SolveLocalSystem(const MatrixType&, SolutionVectorType&, const RHSVectorType&, const LinearSolverOptions&)
   {
     checkHasLibrary(LSLibrary::eigen3);
     throw UnexpectedError("unexpected situation should not reach this point!");
@@ -379,19 +350,17 @@ struct LinearSolver::Internals
     return 0;
   }
 
-  template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType>
+  template <typename MatrixType>
   static void
-  petscSolveLocalSystem(const MatrixType& A,
-                        SolutionVectorType& x,
-                        const RHSVectorType& b,
-                        const LinearSolverOptions& options)
+  _petscSolveLocalSystem(const MatrixType& A,
+                         VectorWrapper<double>& x,
+                         const VectorWrapper<const double>& b,
+                         const LinearSolverOptions& options)
   {
-    Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare());
-
     Vec petscB;
-    VecCreateMPIWithArray(PETSC_COMM_SELF, 1, b.size(), b.size(), &b[0], &petscB);
+    VecCreateMPIWithArray(PETSC_COMM_SELF, 1, b.size(), b.size(), b.ptr(), &petscB);
     Vec petscX;
-    VecCreateMPIWithArray(PETSC_COMM_SELF, 1, x.size(), x.size(), &x[0], &petscX);
+    VecCreateMPIWithArray(PETSC_COMM_SELF, 1, x.size(), x.size(), x.ptr(), &petscX);
 
     PETScAijMatrixEmbedder petscA(A);
 
@@ -491,7 +460,7 @@ struct LinearSolver::Internals
   // LCOV_EXCL_START
   template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType>
   static void
-  petscSolveLocalSystem(const MatrixType&, SolutionVectorType&, const RHSVectorType&, const LinearSolverOptions&)
+  _petscSolveLocalSystem(const MatrixType&, SolutionVectorType&, const RHSVectorType&, const LinearSolverOptions&)
   {
     checkHasLibrary(LSLibrary::petsc);
     throw UnexpectedError("unexpected situation should not reach this point!");
@@ -499,40 +468,6 @@ struct LinearSolver::Internals
   // LCOV_EXCL_STOP
 
 #endif   // PUGS_HAS_PETSC
-
-  template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType>
-  static void
-  solveLocalSystem(const MatrixType& A,
-                   SolutionVectorType& x,
-                   const RHSVectorType& b,
-                   const LinearSolverOptions& options)
-  {
-    switch (options.library()) {
-    case LSLibrary::builtin: {
-      builtinSolveLocalSystem(A, x, b, options);
-      break;
-    }
-      // LCOV_EXCL_START
-    case LSLibrary::eigen3: {
-      // not covered since if Eigen3 is not linked this point is
-      // unreachable: LinearSolver throws an exception at construction
-      // in this case.
-      eigen3SolveLocalSystem(A, x, b, options);
-      break;
-    }
-    case LSLibrary::petsc: {
-      // not covered since if PETSc is not linked this point is
-      // unreachable: LinearSolver throws an exception at construction
-      // in this case.
-      petscSolveLocalSystem(A, x, b, options);
-      break;
-    }
-    default: {
-      throw UnexpectedError(::name(options.library()) + " cannot solve local systems");
-    }
-      // LCOV_EXCL_STOP
-    }
-  }
 };
 
 bool
@@ -548,15 +483,52 @@ LinearSolver::checkOptions(const LinearSolverOptions& options) const
 }
 
 void
-LinearSolver::solveLocalSystem(const CRSMatrix<double, int>& A, Vector<double>& x, const Vector<const double>& b)
+LinearSolver::_builtinSolveLocalSystem(const CRSMatrix<double, int>& A,
+                                       Vector<double>& x,
+                                       const Vector<const double>& b)
+{
+  Internals::_builtinSolveLocalSystem(A, x, b, m_options);
+}
+
+void
+LinearSolver::_builtinSolveLocalSystem(const SmallMatrix<double>& A,
+                                       SmallVector<double>& x,
+                                       const SmallVector<const double>& b)
+{
+  Internals::_builtinSolveLocalSystem(A, x, b, m_options);
+}
+
+void
+LinearSolver::_eigen3SolveLocalSystem(const Eigen3SparseMatrixEmbedder& A,
+                                      VectorWrapper<double> x,
+                                      const VectorWrapper<const double>& b)
+{
+  Internals::_eigen3SolveLocalSystem(A, x, b, m_options);
+}
+
+void
+LinearSolver::_eigen3SolveLocalSystem(const DenseMatrixWrapper<double>& A,
+                                      VectorWrapper<double> x,
+                                      const VectorWrapper<const double>& b)
+{
+  Eigen3DenseMatrixEmbedder A_wrapper{A};
+  Internals::_eigen3SolveLocalSystem(A_wrapper, x, b, m_options);
+}
+
+void
+LinearSolver::_petscSolveLocalSystem(const CRSMatrix<double, int>& A,
+                                     VectorWrapper<double> x,
+                                     const VectorWrapper<const double>& b)
 {
-  Internals::solveLocalSystem(A, x, b, m_options);
+  Internals::_petscSolveLocalSystem(A, x, b, m_options);
 }
 
 void
-LinearSolver::solveLocalSystem(const SmallMatrix<double>& A, SmallVector<double>& x, const SmallVector<const double>& b)
+LinearSolver::_petscSolveLocalSystem(const DenseMatrixWrapper<double>& A,
+                                     VectorWrapper<double> x,
+                                     const VectorWrapper<const double>& b)
 {
-  Internals::solveLocalSystem(A, x, b, m_options);
+  Internals::_petscSolveLocalSystem(A, x, b, m_options);
 }
 
 LinearSolver::LinearSolver(const LinearSolverOptions& options) : m_options{options}
diff --git a/src/algebra/LinearSolver.hpp b/src/algebra/LinearSolver.hpp
index e90402c8ddc78c9426f97300ab6b10730b4c503b..2d03a9cf5e7cced7f99b6707c042395f48054199 100644
--- a/src/algebra/LinearSolver.hpp
+++ b/src/algebra/LinearSolver.hpp
@@ -2,11 +2,14 @@
 #define LINEAR_SOLVER_HPP
 
 #include <algebra/CRSMatrix.hpp>
+#include <algebra/DenseMatrixWrapper.hpp>
+#include <algebra/Eigen3Utils.hpp>
 #include <algebra/LinearSolverOptions.hpp>
 #include <algebra/SmallMatrix.hpp>
 #include <algebra/TinyMatrix.hpp>
 #include <algebra/TinyVector.hpp>
 #include <algebra/Vector.hpp>
+#include <algebra/VectorWrapper.hpp>
 #include <utils/Exceptions.hpp>
 
 class LinearSolver
@@ -16,29 +19,139 @@ class LinearSolver
 
   const LinearSolverOptions m_options;
 
+  void _builtinSolveLocalSystem(const CRSMatrix<double, int>& A, Vector<double>& x, const Vector<const double>& b);
+
+  void _builtinSolveLocalSystem(const SmallMatrix<double>& A,
+                                SmallVector<double>& x,
+                                const SmallVector<const double>& b);
+
+  void _eigen3SolveLocalSystem(const Eigen3SparseMatrixEmbedder& A,
+                               VectorWrapper<double> x,
+                               const VectorWrapper<const double>& b);
+
+  void _eigen3SolveLocalSystem(const DenseMatrixWrapper<double>& A,
+                               VectorWrapper<double> x,
+                               const VectorWrapper<const double>& b);
+
+  void _petscSolveLocalSystem(const CRSMatrix<double, int>& A,
+                              VectorWrapper<double> x,
+                              const VectorWrapper<const double>& b);
+
+  void _petscSolveLocalSystem(const DenseMatrixWrapper<double>& A,
+                              VectorWrapper<double> x,
+                              const VectorWrapper<const double>& b);
+
  public:
   bool hasLibrary(LSLibrary library) const;
   void checkOptions(const LinearSolverOptions& options) const;
 
+  void
+  solveLocalSystem(const CRSMatrix<double, int>& A, Vector<double>& x, const Vector<const double>& b)
+  {
+    Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare());
+    switch (m_options.library()) {
+    case LSLibrary::builtin: {
+      _builtinSolveLocalSystem(A, x, b);
+      break;
+    }
+      // LCOV_EXCL_START
+    case LSLibrary::eigen3: {
+      // not covered since if Eigen3 is not linked this point is
+      // unreachable: LinearSolver throws an exception at construction
+      // in this case.
+      _eigen3SolveLocalSystem(A, x, b);
+      break;
+    }
+    case LSLibrary::petsc: {
+      // not covered since if PETSc is not linked this point is
+      // unreachable: LinearSolver throws an exception at construction
+      // in this case.
+      _petscSolveLocalSystem(A, x, b);
+      break;
+    }
+    default: {
+      throw UnexpectedError(::name(m_options.library()) + " cannot solve local systems");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  void
+  solveLocalSystem(const SmallMatrix<double>& A, SmallVector<double>& x, const SmallVector<const double>& b)
+  {
+    Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare());
+    switch (m_options.library()) {
+    case LSLibrary::builtin: {
+      _builtinSolveLocalSystem(A, x, b);
+      break;
+    }
+      // LCOV_EXCL_START
+    case LSLibrary::eigen3: {
+      // not covered since if Eigen3 is not linked this point is
+      // unreachable: LinearSolver throws an exception at construction
+      // in this case.
+      _eigen3SolveLocalSystem(A, x, b);
+      break;
+    }
+    case LSLibrary::petsc: {
+      // not covered since if PETSc is not linked this point is
+      // unreachable: LinearSolver throws an exception at construction
+      // in this case.
+      _petscSolveLocalSystem(A, x, b);
+      break;
+    }
+    default: {
+      throw UnexpectedError(::name(m_options.library()) + " cannot solve local systems");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
   template <size_t N>
   void
   solveLocalSystem(const TinyMatrix<N>& A, TinyVector<N>& x, const TinyVector<N>& b)
   {
-    SmallMatrix A_dense{A};
+    Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare());
+    switch (m_options.library()) {
+    case LSLibrary::builtin: {
+      // Probably expensive but builtin is not the preferred way to
+      // solve linear systems
+      SmallMatrix A_dense{A};
+
+      SmallVector x_vector{x};
+      SmallVector<const double> b_vector{b};
 
-    SmallVector x_vector{x};
-    SmallVector b_vector{b};
+      this->solveLocalSystem(A_dense, x_vector, b_vector);
 
-    this->solveLocalSystem(A_dense, x_vector, b_vector);
+      for (size_t i = 0; i < N; ++i) {
+        x[i] = x_vector[i];
+      }
 
-    for (size_t i = 0; i < N; ++i) {
-      x[i] = x_vector[i];
+      _builtinSolveLocalSystem(A, x, b);
+      break;
+    }
+      // LCOV_EXCL_START
+    case LSLibrary::eigen3: {
+      // not covered since if Eigen3 is not linked this point is
+      // unreachable: LinearSolver throws an exception at construction
+      // in this case.
+      _eigen3SolveLocalSystem(A, x, b);
+      break;
+    }
+    case LSLibrary::petsc: {
+      // not covered since if PETSc is not linked this point is
+      // unreachable: LinearSolver throws an exception at construction
+      // in this case.
+      _petscSolveLocalSystem(A, x, b);
+      break;
+    }
+    default: {
+      throw UnexpectedError(::name(m_options.library()) + " cannot solve local systems");
+    }
+      // LCOV_EXCL_STOP
     }
   }
 
-  void solveLocalSystem(const CRSMatrix<double, int>& A, Vector<double>& x, const Vector<const double>& b);
-  void solveLocalSystem(const SmallMatrix<double>& A, SmallVector<double>& x, const SmallVector<const double>& b);
-
   LinearSolver();
   LinearSolver(const LinearSolverOptions& options);
 };
diff --git a/src/algebra/PETScUtils.hpp b/src/algebra/PETScUtils.hpp
index c4db9ee5910ef8e58333dbfb6dc20c8a1987bb46..c6d2e8be88c6098e2f187cda9177c120facc6f23 100644
--- a/src/algebra/PETScUtils.hpp
+++ b/src/algebra/PETScUtils.hpp
@@ -6,6 +6,7 @@
 #ifdef PUGS_HAS_PETSC
 
 #include <algebra/CRSMatrix.hpp>
+#include <algebra/DenseMatrixWrapper.hpp>
 #include <algebra/SmallMatrix.hpp>
 #include <algebra/TinyMatrix.hpp>
 
@@ -52,12 +53,8 @@ class PETScAijMatrixEmbedder
     return m_petscMat;
   }
 
-  template <size_t N>
-  PETScAijMatrixEmbedder(const TinyMatrix<N>& A) : PETScAijMatrixEmbedder{N, N, &A(0, 0)}
-  {}
-
-  PETScAijMatrixEmbedder(const SmallMatrix<double>& A)
-    : PETScAijMatrixEmbedder{A.numberOfRows(), A.numberOfColumns(), &A(0, 0)}
+  PETScAijMatrixEmbedder(const DenseMatrixWrapper<double>& A)
+    : PETScAijMatrixEmbedder{A.numberOfRows(), A.numberOfColumns(), A.ptr()}
   {}
 
   PETScAijMatrixEmbedder(const CRSMatrix<double, int>& A);
diff --git a/src/algebra/VectorWrapper.hpp b/src/algebra/VectorWrapper.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0bdeefbfd3f10407358613e7f1d2487bba22b26d
--- /dev/null
+++ b/src/algebra/VectorWrapper.hpp
@@ -0,0 +1,47 @@
+#ifndef VECTOR_WRAPPER_HPP
+#define VECTOR_WRAPPER_HPP
+
+#include <algebra/SmallVector.hpp>
+#include <algebra/TinyVector.hpp>
+#include <algebra/Vector.hpp>
+
+template <typename DataType>
+class VectorWrapper
+{
+ private:
+  const size_t m_size;
+  DataType* const m_vector_ptr;
+
+ public:
+  PUGS_INLINE
+  size_t
+  size() const
+  {
+    return m_size;
+  }
+
+  PUGS_INLINE
+  DataType* const&
+  ptr() const
+  {
+    return m_vector_ptr;
+  }
+
+  VectorWrapper(const Vector<DataType>& x) : m_size{x.size()}, m_vector_ptr{(m_size > 0) ? (&x[0]) : nullptr} {}
+  VectorWrapper(Vector<DataType>& x) : m_size{x.size()}, m_vector_ptr{(m_size > 0) ? (&x[0]) : nullptr} {}
+
+  VectorWrapper(const SmallVector<DataType>& x) : m_size{x.size()}, m_vector_ptr{(m_size > 0) ? (&x[0]) : nullptr} {}
+
+  template <size_t N>
+  VectorWrapper(const TinyVector<N, DataType>& x) : m_size{N}, m_vector_ptr{(N > 0) ? (&x[0]) : nullptr}
+  {}
+
+  template <size_t N>
+  VectorWrapper(TinyVector<N, DataType>& x) : m_size{N}, m_vector_ptr{(N > 0) ? (&x[0]) : nullptr}
+  {}
+
+  VectorWrapper(const VectorWrapper&) = delete;
+  VectorWrapper(VectorWrapper&&)      = delete;
+};
+
+#endif   // VECTOR_WRAPPER_HPP
diff --git a/src/utils/PugsTraits.hpp b/src/utils/PugsTraits.hpp
index 2e1d409578c9295a1ad032466842c2f336ba272f..3c7a4e40a217d957dea388223d99574e6b1ecf74 100644
--- a/src/utils/PugsTraits.hpp
+++ b/src/utils/PugsTraits.hpp
@@ -15,6 +15,12 @@ class TinyVector;
 template <size_t M, size_t N, typename T>
 class TinyMatrix;
 
+template <typename DataType>
+class SmallMatrix;
+
+template <typename DataType, typename IndexType>
+class CRSMatrix;
+
 template <typename DataType, ItemType item_type, typename ConnectivityPtr>
 class ItemValue;
 template <typename DataType, ItemType item_type, typename ConnectivityPtr>
@@ -114,6 +120,22 @@ inline constexpr bool is_tiny_matrix_v = false;
 template <size_t M, size_t N, typename T>
 inline constexpr bool is_tiny_matrix_v<TinyMatrix<M, N, T>> = true;
 
+// Traits is_small_matrix
+
+template <typename T>
+inline constexpr bool is_small_matrix_v = false;
+
+template <typename DataType>
+inline constexpr bool is_small_matrix_v<SmallMatrix<DataType>> = true;
+
+// Traits is_crs_matrix
+
+template <typename T>
+inline constexpr bool is_crs_matrix_v = false;
+
+template <typename DataType, typename IndexType>
+inline constexpr bool is_crs_matrix_v<CRSMatrix<DataType, IndexType>> = true;
+
 // Trais is ItemValue
 
 template <typename T>
diff --git a/tests/test_LinearSolver.cpp b/tests/test_LinearSolver.cpp
index 4d01df2f0605e2bd141c1de3d95f28776f3178a0..76ef6aa2bb75c3ea94fcf392f59ddb74350dc50a 100644
--- a/tests/test_LinearSolver.cpp
+++ b/tests/test_LinearSolver.cpp
@@ -752,46 +752,10 @@ TEST_CASE("LinearSolver", "[algebra]")
             }
           }
 
-          SECTION("BICGStab2")
-          {
-            LinearSolverOptions options;
-            options.library() = LSLibrary::petsc;
-            options.method()  = LSMethod::bicgstab2;
-            options.precond() = LSPrecond::none;
-
-            SECTION("BICGStab2 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)));
-            }
-
-            SECTION("BICGStab2 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("GMRES")
           {
             LinearSolverOptions options;
-            options.library() = LSLibrary::petsc;
+            options.library() = LSLibrary::eigen3;
             options.method()  = LSMethod::gmres;
             options.precond() = LSPrecond::none;
 
@@ -841,7 +805,7 @@ TEST_CASE("LinearSolver", "[algebra]")
           SECTION("LU")
           {
             LinearSolverOptions options;
-            options.library() = LSLibrary::petsc;
+            options.library() = LSLibrary::eigen3;
             options.method()  = LSMethod::lu;
             options.precond() = LSPrecond::none;
 
@@ -859,7 +823,7 @@ TEST_CASE("LinearSolver", "[algebra]")
           SECTION("Eigen3 not linked")
           {
             LinearSolverOptions options;
-            options.library() = LSLibrary::petsc;
+            options.library() = LSLibrary::eigen3;
             options.method()  = LSMethod::cg;
             options.precond() = LSPrecond::none;