diff --git a/dumux/linear/istlsolvers.hh b/dumux/linear/istlsolvers.hh
index 6d29313ed955623e3c2a5a847b2e81dfcc063f34..4dba72b3bb2bd4cfe92f680187220b08b453f18f 100644
--- a/dumux/linear/istlsolvers.hh
+++ b/dumux/linear/istlsolvers.hh
@@ -25,8 +25,10 @@
 #define DUMUX_LINEAR_ISTL_SOLVERS_HH
 
 #include <memory>
+#include <variant>
 
 #include <dune/common/exceptions.hh>
+#include <dune/common/shared_ptr.hh>
 #include <dune/common/parallel/indexset.hh>
 #include <dune/common/parallel/mpicommunication.hh>
 #include <dune/grid/common/capabilities.hh>
@@ -93,10 +95,67 @@ class IstlDefaultPreconditionerFactory
 
 using IstlAmgPreconditionerFactory = Dune::AMGCreator;
 
+template<class M, bool convert = false>
+struct MatrixForSolver { using type = M; };
+
+template<class M>
+struct MatrixForSolver<M, true>
+{ using type = std::decay_t<decltype(MatrixConverter<M>::multiTypeToBCRSMatrix(std::declval<M>()))>; };
+
+template<class V, bool convert = false>
+struct VectorForSolver { using type = V; };
+
+template<class V>
+struct VectorForSolver<V, true>
+{ using type = std::decay_t<decltype(VectorConverter<V>::multiTypeToBlockVector(std::declval<V>()))>; };
+
+template<class LSTraits, class LATraits, bool convert, bool parallel = LSTraits::canCommunicate>
+struct MatrixOperator;
+
+template<class LSTraits, class LATraits, bool convert>
+struct MatrixOperator<LSTraits, LATraits, convert, true>
+{
+    using M = typename MatrixForSolver<typename LATraits::Matrix, convert>::type;
+    using V = typename VectorForSolver<typename LATraits::Vector, convert>::type;
+#if HAVE_MPI
+    using type = std::variant<
+        std::shared_ptr<typename LSTraits::template Sequential<M, V>::LinearOperator>,
+        std::shared_ptr<typename LSTraits::template ParallelOverlapping<M, V>::LinearOperator>,
+        std::shared_ptr<typename LSTraits::template ParallelNonoverlapping<M, V>::LinearOperator>
+    >;
+#else
+    using type = std::variant<
+        std::shared_ptr<typename LSTraits::template Sequential<M, V>::LinearOperator>
+    >;
+#endif
+};
+
+template<class LSTraits, class LATraits, bool convert>
+struct MatrixOperator<LSTraits, LATraits, convert, false>
+{
+    using M = typename MatrixForSolver<typename LATraits::Matrix, convert>::type;
+    using V = typename VectorForSolver<typename LATraits::Vector, convert>::type;
+    using type = std::variant<
+        std::shared_ptr<typename LSTraits::template Sequential<M, V>::LinearOperator>
+    >;
+};
+
 } // end namespace Dumux::Detail::IstlSolvers
 
 namespace Dumux::Detail {
 
+struct IstlSolverResult : public Dune::InverseOperatorResult
+{
+    IstlSolverResult() = default;
+    IstlSolverResult(const IstlSolverResult&) = default;
+    IstlSolverResult(IstlSolverResult&&) = default;
+
+    IstlSolverResult(const Dune::InverseOperatorResult& o) : InverseOperatorResult(o) {}
+    IstlSolverResult(Dune::InverseOperatorResult&& o) : InverseOperatorResult(std::move(o)) {}
+
+    operator bool() const { return this->converged; }
+};
+
 /*!
  * \ingroup Linear
  * \brief Standard dune-istl iterative linear solvers
@@ -110,25 +169,36 @@ class IstlIterativeLinearSolver
     using XVector = typename LinearAlgebraTraits::Vector;
     using BVector = typename LinearAlgebraTraits::Vector;
     using Scalar = typename InverseOperator::real_type;
+
     using ScalarProduct = Dune::ScalarProduct<typename InverseOperator::domain_type>;
+
     static constexpr bool convertMultiTypeVectorAndMatrix
         = convertMultiTypeLATypes && isMultiTypeBlockVector<XVector>::value;
+    using MatrixForSolver = typename Detail::IstlSolvers::MatrixForSolver<Matrix, convertMultiTypeVectorAndMatrix>::type;
+    using BVectorForSolver = typename Detail::IstlSolvers::VectorForSolver<BVector, convertMultiTypeVectorAndMatrix>::type;
+    using XVectorForSolver = typename Detail::IstlSolvers::VectorForSolver<XVector, convertMultiTypeVectorAndMatrix>::type;
+    // a variant type that can hold sequential, overlapping, and non-overlapping operators
+    using MatrixOperatorHolder = typename Detail::IstlSolvers::MatrixOperator<
+        LinearSolverTraits, LinearAlgebraTraits, convertMultiTypeVectorAndMatrix
+    >::type;
+
 #if HAVE_MPI
     using Comm = Dune::OwnerOverlapCopyCommunication<Dune::bigunsignedint<96>, int>;
     using ParallelHelper = ParallelISTLHelper<LinearSolverTraits>;
 #endif
 
+    using ParameterInitializer = std::variant<std::string, Dune::ParameterTree>;
 public:
 
     /*!
      * \brief Constructor for sequential solvers
      */
-    IstlIterativeLinearSolver(const std::string& paramGroup = "")
+    IstlIterativeLinearSolver(const ParameterInitializer& params = "")
     {
         if (Dune::MPIHelper::getCommunication().size() > 1)
             DUNE_THROW(Dune::InvalidStateException, "Using sequential constructor for parallel run. Use signature with gridView and dofMapper!");
 
-        initializeParameters_(paramGroup);
+        initializeParameters_(params);
         solverCategory_ = Dune::SolverCategory::sequential;
         scalarProduct_ = std::make_shared<ScalarProduct>();
     }
@@ -139,9 +209,9 @@ public:
     template <class GridView, class DofMapper>
     IstlIterativeLinearSolver(const GridView& gridView,
                               const DofMapper& dofMapper,
-                              const std::string& paramGroup = "")
+                              const ParameterInitializer& params = "")
     {
-        initializeParameters_(paramGroup);
+        initializeParameters_(params);
 #if HAVE_MPI
         solverCategory_ = Detail::solverCategory<LinearSolverTraits>(gridView);
         if constexpr (LinearSolverTraits::canCommunicate)
@@ -149,7 +219,7 @@ public:
 
             if (solverCategory_ != Dune::SolverCategory::sequential)
             {
-                parallelHelper_ = std::make_unique<ParallelISTLHelper<LinearSolverTraits>>(gridView, dofMapper);
+                parallelHelper_ = std::make_shared<ParallelISTLHelper<LinearSolverTraits>>(gridView, dofMapper);
                 communication_ = std::make_shared<Comm>(gridView.comm(), solverCategory_);
                 scalarProduct_ = Dune::createScalarProduct<XVector>(*communication_, solverCategory_);
                 parallelHelper_->createParallelIndexSet(*communication_);
@@ -174,9 +244,9 @@ public:
                               std::shared_ptr<ScalarProduct> scalarProduct,
                               const GridView& gridView,
                               const DofMapper& dofMapper,
-                              const std::string& paramGroup = "")
+                              const ParameterInitializer& params = "")
     {
-        initializeParameters_(paramGroup);
+        initializeParameters_(params);
         solverCategory_ = Detail::solverCategory(gridView);
         scalarProduct_ = scalarProduct;
         communication_ = communication;
@@ -184,22 +254,49 @@ public:
         {
             if (solverCategory_ != Dune::SolverCategory::sequential)
             {
-                parallelHelper_ = std::make_unique<ParallelISTLHelper<LinearSolverTraits>>(gridView, dofMapper);
+                parallelHelper_ = std::make_shared<ParallelISTLHelper<LinearSolverTraits>>(gridView, dofMapper);
                 parallelHelper_->createParallelIndexSet(communication);
             }
         }
     }
 #endif
 
-    bool solve(Matrix& A, XVector& x, BVector& b)
+    /*!
+     * \brief Solve the linear system Ax = b
+     */
+    IstlSolverResult solve(Matrix& A, XVector& x, BVector& b)
+    { return solveSequentialOrParallel_(A, x, b); }
+
+    /*!
+     * \brief Set the matrix A of the linear system Ax = b for reuse
+     */
+    void setMatrix(std::shared_ptr<Matrix> A)
     {
-#if HAVE_MPI
-        return solveSequentialOrParallel_(A, x, b);
-#else
-        return solveSequential_(A, x, b);
-#endif
+        linearOperator_ = makeParallelOrSequentialLinearOperator_(std::move(A));
+        solver_ = constructPreconditionedSolver_(linearOperator_);
     }
 
+    /*!
+     * \brief Set the matrix A of the linear system Ax = b for reuse
+     * \note The client has to take care of the lifetime management of A
+     */
+    void setMatrix(Matrix& A)
+    { setMatrix(Dune::stackobject_to_shared_ptr(A)); }
+
+    /*!
+     * \brief Solve the linear system Ax = b where A has been set with \ref setMatrix
+     */
+    IstlSolverResult solve(XVector& x, BVector& b) const
+    {
+        if (!solver_)
+            DUNE_THROW(Dune::InvalidStateException, "Called solve(x, b) but no linear operator has been set");
+
+        return solveSequentialOrParallel_(x, b, *solver_);
+    }
+
+    /*!
+     * \brief Compute the 2-norm of vector x
+     */
     Scalar norm(const XVector& x) const
     {
 #if HAVE_MPI
@@ -225,133 +322,244 @@ public:
             return scalarProduct_->norm(x);
     }
 
-    const Dune::InverseOperatorResult& result() const
-    {
-        return result_;
-    }
-
+    /*!
+     * \brief The name of the linear solver
+     */
     const std::string& name() const
     {
         return name_;
     }
 
+    /*!
+     * \brief Set the residual reduction tolerance
+     */
     void setResidualReduction(double residReduction)
-    { params_["reduction"] = std::to_string(residReduction); }
+    {
+        params_["reduction"] = std::to_string(residReduction);
+
+        // reconstruct the solver with new parameters
+        if (solver_)
+            solver_ = constructPreconditionedSolver_(linearOperator_);
+    }
+
+    /*!
+     * \brief Set the maximum number of linear solver iterations
+     */
+    void setMaxIter(std::size_t maxIter)
+    {
+        params_["maxit"] = std::to_string(maxIter);
+
+        // reconstruct the solver with new parameters
+        if (solver_)
+            solver_ = constructPreconditionedSolver_(linearOperator_);
+    }
+
+    /*!
+     * \brief Set the linear solver parameters
+     * \param params Either a std::string giving a parameter group (parameters are read from input file) or a Dune::ParameterTree
+     * \note In case of a Dune::ParameterTree, the parameters are passed trait to the linear solver and preconditioner
+     */
+    void setParams(const ParameterInitializer& params)
+    {
+        initializeParameters_(params);
+
+        // reconstruct the solver with new parameters
+        if (solver_)
+            solver_ = constructPreconditionedSolver_(linearOperator_);
+    }
 
 private:
 
-    void initializeParameters_(const std::string& paramGroup)
+    void initializeParameters_(const ParameterInitializer& params)
     {
-        params_ = Dumux::LinearSolverParameters<LinearSolverTraits>::createParameterTree(paramGroup);
+        if (std::holds_alternative<std::string>(params))
+            params_ = Dumux::LinearSolverParameters<LinearSolverTraits>::createParameterTree(std::get<std::string>(params));
+        else
+            params_ = std::get<Dune::ParameterTree>(params);
     }
 
-    bool solveSequential_(Matrix& A, XVector& x, BVector& b)
+    MatrixOperatorHolder makeSequentialLinearOperator_(std::shared_ptr<Matrix> A)
     {
+        using SequentialTraits = typename LinearSolverTraits::template Sequential<MatrixForSolver, XVectorForSolver>;
         if constexpr (convertMultiTypeVectorAndMatrix)
         {
             // create the BCRS matrix the IterativeSolver backend can handle
-            auto M = MatrixConverter<Matrix>::multiTypeToBCRSMatrix(A);
+            auto M = std::make_shared<MatrixForSolver>(MatrixConverter<Matrix>::multiTypeToBCRSMatrix(*A));
+            return std::make_shared<typename SequentialTraits::LinearOperator>(M);
+        }
+        else
+        {
+            return std::make_shared<typename SequentialTraits::LinearOperator>(A);
+        }
+    }
 
-            // get the new matrix sizes
-            const std::size_t numRows = M.N();
-            assert(numRows == M.M());
+    template<class ParallelTraits>
+    MatrixOperatorHolder makeParallelLinearOperator_(std::shared_ptr<Matrix> A, ParallelTraits = {})
+    {
+#if HAVE_MPI
+        // make matrix consistent
+        prepareMatrixParallel<LinearSolverTraits, ParallelTraits>(*A, *parallelHelper_);
+        return std::make_shared<typename ParallelTraits::LinearOperator>(std::move(A), *communication_);
+#else
+        DUNE_THROW(Dune::InvalidStateException, "Calling makeParallelLinearOperator for sequential run");
+#endif
+    }
 
+    MatrixOperatorHolder makeParallelOrSequentialLinearOperator_(std::shared_ptr<Matrix> A)
+    {
+        return executeSequentialOrParallel_(
+            [&]{ return makeSequentialLinearOperator_(std::move(A)); },
+            [&](auto traits){ return makeParallelLinearOperator_(std::move(A), traits); }
+        );
+    }
+
+    MatrixOperatorHolder makeSequentialLinearOperator_(Matrix& A)
+    { return makeSequentialLinearOperator_(Dune::stackobject_to_shared_ptr<Matrix>(A)); }
+
+    MatrixOperatorHolder makeParallelOrSequentialLinearOperator_(Matrix& A)
+    { return makeParallelOrSequentialLinearOperator_(Dune::stackobject_to_shared_ptr<Matrix>(A)); }
+
+    template<class ParallelTraits>
+    MatrixOperatorHolder makeParallelLinearOperator_(Matrix& A, ParallelTraits = {})
+    { return makeParallelLinearOperator_<ParallelTraits>(Dune::stackobject_to_shared_ptr<Matrix>(A)); }
+
+    IstlSolverResult solveSequential_(Matrix& A, XVector& x, BVector& b)
+    {
+        // construct solver from linear operator
+        auto linearOperatorHolder = makeSequentialLinearOperator_(A);
+        auto solver = constructPreconditionedSolver_(linearOperatorHolder);
+
+        return solveSequential_(x, b, *solver);
+    }
+
+    IstlSolverResult solveSequential_(XVector& x, BVector& b, InverseOperator& solver) const
+    {
+        Dune::InverseOperatorResult result;
+        if constexpr (convertMultiTypeVectorAndMatrix)
+        {
             // create the vector the IterativeSolver backend can handle
-            auto bTmp = VectorConverter<BVector>::multiTypeToBlockVector(b);
-            assert(bTmp.size() == numRows);
+            BVectorForSolver bTmp = VectorConverter<BVector>::multiTypeToBlockVector(b);
 
             // create a block vector to which the linear solver writes the solution
-            using VectorBlock = typename Dune::FieldVector<Scalar, 1>;
-            using BlockVector = typename Dune::BlockVector<VectorBlock>;
-            BlockVector y(numRows);
-
-            auto linearOperator = std::make_shared<Dune::MatrixAdapter<decltype(M), decltype(y), decltype(bTmp)>>(M);
-            auto solver = constructPreconditionedSolver_(linearOperator);
+            XVectorForSolver y(bTmp.size());
 
             // solve linear system
-            solver.apply(y, bTmp, result_);
+            solver.apply(y, bTmp, result);
 
             // copy back the result y into x
-            if(result_.converged)
+            if (result.converged)
                 VectorConverter<XVector>::retrieveValues(x, y);
         }
         else
         {
-            // construct solver from linear operator
-            using SequentialTraits = typename LinearSolverTraits::template Sequential<Matrix, XVector>;
-            auto linearOperator = std::make_shared<typename SequentialTraits::LinearOperator>(A);
-            auto solver = constructPreconditionedSolver_(linearOperator);
-
             // solve linear system
-            solver.apply(x, b, result_);
+            solver.apply(x, b, result);
         }
 
-        return result_.converged;
+        return result;
+    }
+
+    IstlSolverResult solveSequentialOrParallel_(Matrix& A, XVector& x, BVector& b)
+    {
+        return executeSequentialOrParallel_(
+            [&]{ return solveSequential_(A, x, b); },
+            [&](auto traits){ return solveParallel_(A, x, b, traits); }
+        );
+    }
+
+    IstlSolverResult solveSequentialOrParallel_(XVector& x, BVector& b, InverseOperator& solver) const
+    {
+        return executeSequentialOrParallel_(
+            [&]{ return solveSequential_(x, b, solver); },
+            [&](auto traits){ return solveParallel_(x, b, solver, traits); }
+        );
+    }
+
+    template<class ParallelTraits>
+    IstlSolverResult solveParallel_(Matrix& A, XVector& x, BVector& b, ParallelTraits = {})
+    {
+        // construct solver from linear operator
+        auto linearOperatorHolder = makeParallelLinearOperator_<ParallelTraits>(A);
+        auto solver = constructPreconditionedSolver_(linearOperatorHolder);
+        return solveParallel_<ParallelTraits>(x, b, *solver);
+    }
+
+    template<class ParallelTraits>
+    IstlSolverResult solveParallel_(XVector& x, BVector& b, InverseOperator& solver, ParallelTraits = {}) const
+    {
+#if HAVE_MPI
+        // make right hand side consistent
+        prepareVectorParallel<LinearSolverTraits, ParallelTraits>(b, *parallelHelper_);
+
+        // solve linear system
+        Dune::InverseOperatorResult result;
+        solver.apply(x, b, result);
+        return result;
+#else
+        DUNE_THROW(Dune::InvalidStateException, "Calling makeParallelLinearOperator for sequential run");
+#endif
     }
 
+
+    std::shared_ptr<InverseOperator> constructPreconditionedSolver_(MatrixOperatorHolder& ops)
+    {
+        return std::visit([&](auto&& op)
+        {
+            using LinearOperator = typename std::decay_t<decltype(op)>::element_type;
+            const auto& params = params_.sub("preconditioner");
+            using Prec = Dune::Preconditioner<typename LinearOperator::domain_type, typename LinearOperator::range_type>;
+            using TL = Dune::TypeList<typename LinearOperator::matrix_type, typename LinearOperator::domain_type, typename LinearOperator::range_type>;
+            std::shared_ptr<Prec> prec = PreconditionerFactory{}(TL{}, op, params);
+
 #if HAVE_MPI
-    bool solveSequentialOrParallel_(Matrix& A, XVector& x, BVector& b)
+            if (prec->category() != op->category() && prec->category() == Dune::SolverCategory::sequential)
+                prec = Dune::wrapPreconditioner4Parallel(prec, op);
+#endif
+            return std::make_shared<InverseOperator>(op, scalarProduct_, prec, params_);
+        }, ops);
+    }
+
+    template<class Seq, class Par>
+    decltype(auto) executeSequentialOrParallel_(Seq&& sequentialAction, Par&& parallelAction) const
     {
+#if HAVE_MPI
         // For Dune::MultiTypeBlockMatrix there is currently no generic way
         // of handling parallelism, we therefore can only solve these types of systems sequentially
         if constexpr (isMultiTypeBlockMatrix<Matrix>::value || !LinearSolverTraits::canCommunicate)
-            return solveSequential_(A, x, b);
+            return sequentialAction();
         else
         {
             switch (solverCategory_)
             {
                 case Dune::SolverCategory::sequential:
-                    return solveSequential_(A, x, b);
+                    return sequentialAction();
                 case Dune::SolverCategory::nonoverlapping:
                     using NOTraits = typename LinearSolverTraits::template ParallelNonoverlapping<Matrix, XVector>;
-                    return solveParallel_<NOTraits>(A, x, b);
+                    return parallelAction(NOTraits{});
                 case Dune::SolverCategory::overlapping:
                     using OTraits = typename LinearSolverTraits::template ParallelOverlapping<Matrix, XVector>;
-                    return solveParallel_<OTraits>(A, x, b);
+                    return parallelAction(OTraits{});
                 default: DUNE_THROW(Dune::InvalidStateException, "Unknown solver category");
             }
         }
-    }
-
-    template<class ParallelTraits>
-    bool solveParallel_(Matrix& A, XVector& x, BVector& b)
-    {
-        // make linear algebra consistent
-        prepareLinearAlgebraParallel<LinearSolverTraits, ParallelTraits>(A, b, *parallelHelper_);
-
-        // construct solver from linear operator
-        auto linearOperator = std::make_shared<typename ParallelTraits::LinearOperator>(A, *communication_);
-        auto solver = constructPreconditionedSolver_(linearOperator);
-
-        // solve linear system
-        solver.apply(x, b, result_);
-        return result_.converged;
-    }
-#endif // HAVE_MPI
-
-    template<class LinearOperator>
-    InverseOperator constructPreconditionedSolver_(std::shared_ptr<LinearOperator>& op)
-    {
-        const auto& params = params_.sub("preconditioner");
-        using Prec = Dune::Preconditioner<typename LinearOperator::domain_type, typename LinearOperator::range_type>;
-        using TL = Dune::TypeList<typename LinearOperator::matrix_type, typename LinearOperator::domain_type, typename LinearOperator::range_type>;
-        std::shared_ptr<Prec> prec = PreconditionerFactory{}(TL{}, op, params);
-
-#if HAVE_MPI
-        if (prec->category() != op->category() && prec->category() == Dune::SolverCategory::sequential)
-            prec = Dune::wrapPreconditioner4Parallel(prec, op);
+#else
+        return sequentialAction();
 #endif
-        return {op, scalarProduct_, prec, params_};
     }
 
 #if HAVE_MPI
-    std::unique_ptr<ParallelHelper> parallelHelper_;
+    std::shared_ptr<const ParallelHelper> parallelHelper_;
     std::shared_ptr<Comm> communication_;
 #endif
+
     Dune::SolverCategory::Category solverCategory_;
     std::shared_ptr<ScalarProduct> scalarProduct_;
 
-    Dune::InverseOperatorResult result_;
+    // for stored solvers (reuse matrix)
+    MatrixOperatorHolder linearOperator_;
+    // for stored solvers (reuse matrix)
+    std::shared_ptr<InverseOperator> solver_;
+
     Dune::ParameterTree params_;
     std::string name_;
 };
@@ -507,90 +715,129 @@ namespace Dumux::Detail {
 template<class LSTraits, class LATraits, template<class M> class Solver>
 class DirectIstlSolver : public LinearSolver
 {
+    using Matrix = typename LATraits::Matrix;
+    using XVector = typename LATraits::Vector;
+    using BVector = typename LATraits::Vector;
+
+    static constexpr bool convertMultiTypeVectorAndMatrix = isMultiTypeBlockVector<XVector>::value;
+    using MatrixForSolver = typename Detail::IstlSolvers::MatrixForSolver<Matrix, convertMultiTypeVectorAndMatrix>::type;
+    using BVectorForSolver = typename Detail::IstlSolvers::VectorForSolver<BVector, convertMultiTypeVectorAndMatrix>::type;
+    using XVectorForSolver = typename Detail::IstlSolvers::VectorForSolver<XVector, convertMultiTypeVectorAndMatrix>::type;
+    using InverseOperator = Dune::InverseOperator<XVectorForSolver, BVectorForSolver>;
 public:
     using LinearSolver::LinearSolver;
 
-    template<class Matrix, class Vector>
-    bool solve(const Matrix& A, Vector& x, const Vector& b)
+    /*!
+     * \brief Solve the linear system Ax = b
+     */
+    IstlSolverResult solve(const Matrix& A, XVector& x, const BVector& b)
     {
-        // support dune-istl multi-type block vector/matrix
-        if constexpr (isMultiTypeBlockVector<Vector>())
-        {
-            auto [AA, xx, bb] = convertIstlMultiTypeToBCRSSystem_(A, x, b);
-            bool converged = solve_(AA, xx, bb);
-            if (converged)
-                VectorConverter<Vector>::retrieveValues(x, xx);
-            return converged;
-        }
+        return solve_(A, x, b);
+    }
+
+    /*!
+     * \brief Solve the linear system Ax = b using the matrix set with \ref setMatrix
+     */
+    IstlSolverResult solve(XVector& x, const BVector& b)
+    {
+        if (!solver_)
+            DUNE_THROW(Dune::InvalidStateException, "Called solve(x, b) but no linear operator has been set");
 
+        return solve_(x, b, *solver_);
+    }
+
+    /*!
+     * \brief Set the matrix A of the linear system Ax = b for reuse
+     */
+    void setMatrix(std::shared_ptr<Matrix> A)
+    {
+        if constexpr (convertMultiTypeVectorAndMatrix)
+            matrix_ = std::make_shared<MatrixForSolver>(MatrixConverter<Matrix>::multiTypeToBCRSMatrix(A));
         else
-            return solve_(A, x, b);
+            matrix_ = A;
+
+        solver_ = std::make_shared<Solver<MatrixForSolver>>(*matrix_);
     }
 
+    /*!
+     * \brief Set the matrix A of the linear system Ax = b for reuse
+     * \note The client has to take care of the lifetime management of A
+     */
+    void setMatrix(Matrix& A)
+    { setMatrix(Dune::stackobject_to_shared_ptr(A)); }
+
+    /*!
+     * \brief name of the linear solver
+     */
     std::string name() const
     {
         return "Direct solver";
     }
 
-    const Dune::InverseOperatorResult& result() const
+private:
+    IstlSolverResult solve_(const Matrix& A, XVector& x, const BVector& b)
     {
-        return result_;
+        // support dune-istl multi-type block vector/matrix by copying
+        if constexpr (isMultiTypeBlockVector<BVector>())
+        {
+            const auto AA = MatrixConverter<Matrix>::multiTypeToBCRSMatrix(A);
+            Solver<MatrixForSolver> solver(AA, this->verbosity() > 0);
+            return solve_(x, b, solver);
+        }
+        else
+        {
+            Solver<MatrixForSolver> solver(A, this->verbosity() > 0);
+            return solve_(x, b, solver);
+        }
     }
 
-private:
-    Dune::InverseOperatorResult result_;
-
-    template<class Matrix, class Vector>
-    bool solve_(const Matrix& A, Vector& x, const Vector& b)
+    IstlSolverResult solve_(XVector& x, const BVector& b, InverseOperator& solver) const
     {
-        static_assert(isBCRSMatrix<Matrix>::value, "Direct solver only works with BCRS matrices!");
-        using BlockType = typename Matrix::block_type;
-        static_assert(BlockType::rows == BlockType::cols, "Matrix block must be quadratic!");
-        constexpr auto blockSize = BlockType::rows;
-
-        Solver<Matrix> solver(A, this->verbosity() > 0);
+        static_assert(isBCRSMatrix<MatrixForSolver>::value, "Direct solver only works with BCRS matrices!");
+        static_assert(MatrixForSolver::block_type::rows == MatrixForSolver::block_type::cols, "Matrix block must be quadratic!");
 
-        Vector bTmp(b);
-        solver.apply(x, bTmp, result_);
+        Dune::InverseOperatorResult result;
+        if constexpr (isMultiTypeBlockVector<BVector>())
+        {
+            auto bb = VectorConverter<BVector>::multiTypeToBlockVector(b);
+            XVectorForSolver xx(bb.size());
+            solver.apply(xx, bb, result);
+            checkResult_(xx, result);
+            if (result.converged)
+                VectorConverter<XVector>::retrieveValues(x, xx);
+            return result;
+        }
+        else
+        {
+            BVectorForSolver bTmp(b);
+            solver.apply(x, bTmp, result);
+            checkResult_(x, result);
+            return result;
+        }
+    }
 
+    void checkResult_(const XVectorForSolver& x, Dune::InverseOperatorResult& result) const
+    {
         int size = x.size();
         for (int i = 0; i < size; i++)
         {
-            for (int j = 0; j < blockSize; j++)
+            for (int j = 0; j < x[i].size(); j++)
             {
                 using std::isnan;
                 using std::isinf;
                 if (isnan(x[i][j]) || isinf(x[i][j]))
                 {
-                    result_.converged = false;
+                    result.converged = false;
                     break;
                 }
             }
         }
-
-        return result_.converged;
     }
 
-    template<class Matrix, class Vector>
-    auto convertIstlMultiTypeToBCRSSystem_(const Matrix& A, Vector& x, const Vector& b)
-    {
-        const auto AA = MatrixConverter<Matrix>::multiTypeToBCRSMatrix(A);
-
-        // get the new matrix sizes
-        const std::size_t numRows = AA.N();
-        assert(numRows == AA.M());
-
-        // create the vector the IterativeSolver backend can handle
-        const auto bb = VectorConverter<Vector>::multiTypeToBlockVector(b);
-        assert(bb.size() == numRows);
-
-        // create a blockvector to which the linear solver writes the solution
-        using VectorBlock = typename Dune::FieldVector<Scalar, 1>;
-        using BlockVector = typename Dune::BlockVector<VectorBlock>;
-        BlockVector xx(numRows);
-
-        return std::make_tuple(std::move(AA), std::move(xx), std::move(bb));
-    }
+    //! matrix when using the setMatrix interface for matrix reuse
+    std::shared_ptr<MatrixForSolver> matrix_;
+    //! solver when using the setMatrix interface for matrix reuse
+    std::shared_ptr<InverseOperator> solver_;
 };
 
 } // end namespace Dumux::Detail
diff --git a/test/linear/test_linearsolver.cc b/test/linear/test_linearsolver.cc
index 4128648940c8b857952ae384d750241a2321a966..c268d399d777f6a1fc5d8eae53e3d9aa209de719 100644
--- a/test/linear/test_linearsolver.cc
+++ b/test/linear/test_linearsolver.cc
@@ -83,9 +83,12 @@ int main(int argc, char* argv[])
         LinearSolver solver(testSolverName);
 
         std::cout << "Solving Laplace problem with " << solver.name() << "\n";
-        solver.solve(A, x, b);
-        if (!solver.result().converged)
+        auto result = solver.solve(A, x, b);
+        if (!result.converged)
             DUNE_THROW(Dune::Exception, testSolverName << " did not converge!");
+
+        if (!result)
+            DUNE_THROW(Dune::Exception, "Solver result cannot be implicitly converted to bool");
     }
 
     // IstlSolverFactoryBackend