diff --git a/NumLib/Extrapolation/ExtrapolatableElementCollection.h b/NumLib/Extrapolation/ExtrapolatableElementCollection.h
index c07d719a79a71a8cfd4e3c7d1bb53f0312c9f582..ab47613cad507e716182f93d212450cd5a85341c 100644
--- a/NumLib/Extrapolation/ExtrapolatableElementCollection.h
+++ b/NumLib/Extrapolation/ExtrapolatableElementCollection.h
@@ -9,11 +9,17 @@
 
 #pragma once
 
+#include <functional>
 #include <vector>
+
+#include "MathLib/LinAlg/GlobalMatrixVectorTypes.h"
+
 #include "ExtrapolatableElement.h"
 
 namespace NumLib
 {
+class LocalToGlobalIndexMap;
+
 /*! Adaptor to get information needed by an Extrapolator from an "arbitrary"
  * collection of elements (e.g., local assemblers).
  *
@@ -50,7 +56,10 @@ public:
      * \endparblock
      */
     virtual std::vector<double> const& getIntegrationPointValues(
-        std::size_t const id, std::vector<double>& cache) const = 0;
+        std::size_t const id, const double t,
+        GlobalVector const& current_solution,
+        LocalToGlobalIndexMap const& dof_table,
+        std::vector<double>& cache) const = 0;
 
     //! Returns the number of elements whose properties shall be extrapolated.
     virtual std::size_t size() const = 0;
@@ -64,9 +73,9 @@ class ExtrapolatableLocalAssemblerCollection
 {
 public:
     //! LocalAssemblerCollection contains many LocalAssembler's.
-    using LocalAssembler = typename std::decay<decltype(
-        *std::declval<LocalAssemblerCollection>()[static_cast<std::size_t>(
-            0)])>::type;
+    using LocalAssembler =
+        typename std::decay<decltype(*std::declval<LocalAssemblerCollection>()
+                                         [static_cast<std::size_t>(0)])>::type;
 
     static_assert(std::is_base_of<ExtrapolatableElement, LocalAssembler>::value,
                   "Local assemblers used for extrapolation must be derived "
@@ -80,8 +89,12 @@ public:
      * For further information about the \c cache parameter see
      * ExtrapolatableElementCollection::getIntegrationPointValues().
      */
-    using IntegrationPointValuesMethod = std::vector<double> const& (
-        LocalAssembler::*)(std::vector<double>& cache) const;
+    using IntegrationPointValuesMethod =
+        std::function<std::vector<double> const&(
+            LocalAssembler const& loc_asm, const double t,
+            GlobalVector const& current_solution,
+            NumLib::LocalToGlobalIndexMap const& dof_table,
+            std::vector<double>& cache)>;
 
     /*! Constructs a new instance.
      *
@@ -92,9 +105,9 @@ public:
      */
     ExtrapolatableLocalAssemblerCollection(
         LocalAssemblerCollection const& local_assemblers,
-        IntegrationPointValuesMethod integration_point_values_method)
-        : _local_assemblers(local_assemblers)
-        , _integration_point_values_method(integration_point_values_method)
+        IntegrationPointValuesMethod const& integration_point_values_method)
+        : _local_assemblers(local_assemblers),
+          _integration_point_values_method{integration_point_values_method}
     {
     }
 
@@ -106,10 +119,14 @@ public:
     }
 
     std::vector<double> const& getIntegrationPointValues(
-        std::size_t const id, std::vector<double>& cache) const override
+        std::size_t const id, const double t,
+        GlobalVector const& current_solution,
+        LocalToGlobalIndexMap const& dof_table,
+        std::vector<double>& cache) const override
     {
         auto const& loc_asm = *_local_assemblers[id];
-        return (loc_asm.*_integration_point_values_method)(cache);
+        return _integration_point_values_method(loc_asm, t, current_solution,
+                                                dof_table, cache);
     }
 
     std::size_t size() const override { return _local_assemblers.size(); }
diff --git a/NumLib/Extrapolation/Extrapolator.h b/NumLib/Extrapolation/Extrapolator.h
index fa13cace48383ba255688c0780dc9673a7c84bf8..c658057c146b5e9edaac52d37edde3fe7153ea60 100644
--- a/NumLib/Extrapolation/Extrapolator.h
+++ b/NumLib/Extrapolation/Extrapolator.h
@@ -17,6 +17,7 @@
 
 namespace NumLib
 {
+class LocalToGlobalIndexMap;
 
 //! Interface for classes that extrapolate integration point values to nodal
 //! values.
@@ -25,7 +26,11 @@ class Extrapolator
 public:
     //! Extrapolates the given \c property from the given local assemblers.
     virtual void extrapolate(
-            ExtrapolatableElementCollection const& extrapolatables) = 0;
+        const unsigned num_components,
+        ExtrapolatableElementCollection const& extrapolatables,
+        const double t,
+        GlobalVector const& current_solution,
+        LocalToGlobalIndexMap const& dof_table) = 0;
 
     /*! Computes residuals from the extrapolation of the given \c property.
      *
@@ -34,7 +39,11 @@ public:
      * \pre extrapolate() must have been called before with the same arguments.
      */
     virtual void calculateResiduals(
-            ExtrapolatableElementCollection const& extrapolatables) = 0;
+        const unsigned num_components,
+        ExtrapolatableElementCollection const& extrapolatables,
+        const double t,
+        GlobalVector const& current_solution,
+        LocalToGlobalIndexMap const& dof_table) = 0;
 
     //! Returns the extrapolated nodal values.
     //! \todo Maybe write directly to a MeshProperty.
diff --git a/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.cpp b/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.cpp
index 29c973ae10a4dc8ba548f89b990a0bb07a63d44d..6a59ef02d0e0b108afba924986dfc83dc7249475 100644
--- a/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.cpp
+++ b/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.cpp
@@ -23,18 +23,7 @@ namespace NumLib
 {
 LocalLinearLeastSquaresExtrapolator::LocalLinearLeastSquaresExtrapolator(
     NumLib::LocalToGlobalIndexMap const& dof_table)
-    : _nodal_values(NumLib::GlobalVectorProvider::provider.getVector(
-          MathLib::MatrixSpecifications(dof_table.dofSizeWithoutGhosts(),
-                                        dof_table.dofSizeWithoutGhosts(),
-                                        &dof_table.getGhostIndices(),
-                                        nullptr))),
-#ifndef USE_PETSC
-      _residuals(dof_table.size()),
-#else
-      _residuals(dof_table.dofSizeWithoutGhosts(),
-                 dof_table.getGhostIndices(), false),
-#endif
-      _local_to_global(dof_table)
+    : _dof_table_single_component(dof_table)
 {
     /* Note in case the following assertion fails:
      * If you copied the extrapolation code, for your processes from
@@ -43,58 +32,112 @@ LocalLinearLeastSquaresExtrapolator::LocalLinearLeastSquaresExtrapolator(
      * likely too simplistic. You better adapt the extrapolation code from
      * some more advanced process, like the TES process.
      */
-    assert(dof_table.getNumberOfComponents() == 1 &&
-           "The d.o.f. table passed must be for one variable that has "
-           "only one component!");
+    if (dof_table.getNumberOfComponents() != 1)
+        OGS_FATAL(
+            "The d.o.f. table passed must be for one variable that has "
+            "only one component!");
 }
 
 void LocalLinearLeastSquaresExtrapolator::extrapolate(
-    ExtrapolatableElementCollection const& extrapolatables)
+    const unsigned num_components,
+    ExtrapolatableElementCollection const& extrapolatables,
+    const double t,
+    GlobalVector const& current_solution,
+    LocalToGlobalIndexMap const& dof_table)
 {
-    _nodal_values.setZero();
+    auto const num_nodal_dof_result =
+        _dof_table_single_component.dofSizeWithoutGhosts() * num_components;
+    if (!_nodal_values ||
+        static_cast<std::size_t>(_nodal_values->size()) != num_nodal_dof_result)
+    {
+        _nodal_values = MathLib::MatrixVectorTraits<GlobalVector>::newInstance(
+            MathLib::MatrixSpecifications{
+                num_nodal_dof_result, num_nodal_dof_result, nullptr, nullptr});
+    }
+    _nodal_values->setZero();
 
     // counts the writes to each nodal value, i.e., the summands in order to
     // compute the average afterwards
     auto counts =
-        MathLib::MatrixVectorTraits<GlobalVector>::newInstance(_nodal_values);
+        MathLib::MatrixVectorTraits<GlobalVector>::newInstance(*_nodal_values);
     counts->setZero();  // TODO BLAS?
 
     auto const size = extrapolatables.size();
-    for (std::size_t i=0; i<size; ++i) {
-        extrapolateElement(i, extrapolatables, *counts);
+    for (std::size_t i = 0; i < size; ++i)
+    {
+        extrapolateElement(i, num_components, extrapolatables, t,
+                           current_solution, dof_table, *counts);
     }
 
-    MathLib::LinAlg::componentwiseDivide(_nodal_values, _nodal_values, *counts);
+    MathLib::LinAlg::componentwiseDivide(*_nodal_values, *_nodal_values,
+                                         *counts);
 }
 
 void LocalLinearLeastSquaresExtrapolator::calculateResiduals(
-    ExtrapolatableElementCollection const& extrapolatables)
+    const unsigned num_components,
+    ExtrapolatableElementCollection const& extrapolatables,
+    const double t,
+    GlobalVector const& current_solution,
+    LocalToGlobalIndexMap const& dof_table)
 {
-    assert(static_cast<std::size_t>(_residuals.size()) ==
-           extrapolatables.size());
+    auto const num_element_dof_result = static_cast<GlobalIndexType>(
+        _dof_table_single_component.size() * num_components);
+
+    if (!_residuals || _residuals->size() != num_element_dof_result)
+    {
+#ifndef USE_PETSC
+        _residuals.reset(new GlobalVector{num_element_dof_result});
+#else
+        _residuals.reset(new GlobalVector{num_element_dof_result, false});
+#endif
+    }
+
+    if (static_cast<std::size_t>(num_element_dof_result) !=
+        extrapolatables.size() * num_components)
+    {
+        OGS_FATAL("mismatch in number of D.o.F.");
+    }
 
     auto const size = extrapolatables.size();
-    for (std::size_t i=0; i<size; ++i) {
-        calculateResidualElement(i, extrapolatables);
+    for (std::size_t i = 0; i < size; ++i)
+    {
+        calculateResidualElement(i, num_components, extrapolatables, t,
+                                 current_solution, dof_table);
     }
 }
 
 void LocalLinearLeastSquaresExtrapolator::extrapolateElement(
     std::size_t const element_index,
+    const unsigned num_components,
     ExtrapolatableElementCollection const& extrapolatables,
+    const double t,
+    GlobalVector const& current_solution,
+    LocalToGlobalIndexMap const& dof_table,
     GlobalVector& counts)
 {
     auto const& integration_point_values =
         extrapolatables.getIntegrationPointValues(
-            element_index, _integration_point_values_cache);
+            element_index, t, current_solution, dof_table,
+            _integration_point_values_cache);
 
     auto const& N_0 = extrapolatables.getShapeMatrix(element_index, 0);
-    const auto num_nodes = static_cast<unsigned>(N_0.cols());
-    const unsigned num_int_pts = integration_point_values.size();
+    const unsigned num_nodes = N_0.cols();
+    const unsigned num_values = integration_point_values.size();
+
+    if (num_values % num_components != 0)
+        OGS_FATAL(
+            "The number of computed integration point values is not divisable "
+            "by the number of num_components. Maybe the computed property is "
+            "not a %d-component vector for each integration point.",
+            num_components);
 
-    assert(num_int_pts >= num_nodes &&
-           "Least squares is not possible if there are more nodes than"
-           "integration points.");
+    // number of integration points in the element
+    const auto num_int_pts = num_values / num_components;
+
+    if (num_int_pts < static_cast<decltype(num_int_pts)>(num_nodes))
+        OGS_FATAL(
+            "Least squares is not possible if there are more nodes than"
+            "integration points.");
 
     auto const pair_it_inserted = _qr_decomposition_cache.emplace(
         std::make_pair(num_nodes, num_int_pts), CachedData{});
@@ -146,51 +189,108 @@ void LocalLinearLeastSquaresExtrapolator::extrapolateElement(
         OGS_FATAL("The cached and the passed shapematrices differ.");
     }
 
-    // TODO make gp_vals an Eigen::VectorXd const& ?
-    auto const integration_point_values_vec =
-        MathLib::toVector(integration_point_values);
-
-    // Apply the pre-computed pseudo-inverse.
-    Eigen::VectorXd const nodal_values =
-        cached_data.A_pinv * integration_point_values_vec;
-
     // TODO: for now always zeroth component is used. This has to be extended if
     // multi-component properties shall be extrapolated
-    auto const& global_indices = _local_to_global(element_index, 0).rows;
+    auto const& global_indices =
+        _dof_table_single_component(element_index, 0).rows;
+
+    if (num_components == 1)
+    {
+        // TODO make gp_vals an Eigen::VectorXd const& ?
+        auto const integration_point_values_vec =
+            MathLib::toVector(integration_point_values);
+
+        // Apply the pre-computed pseudo-inverse.
+        Eigen::VectorXd const nodal_values =
+            cached_data.A_pinv * integration_point_values_vec;
+
+        // TODO does that give rise to PETSc problems?
+        _nodal_values->add(global_indices, nodal_values);
+        counts.add(global_indices,
+                   std::vector<double>(global_indices.size(), 1.0));
+    }
+    else
+    {
+        // TODO make gp_vals an Eigen::VectorXd const& ?
+        auto const integration_point_values_mat = MathLib::toMatrix(
+            integration_point_values, num_components, num_int_pts);
+
+        // Apply the pre-computed pseudo-inverse.
+        Eigen::MatrixXd const nodal_values =
+            cached_data.A_pinv * integration_point_values_mat.transpose();
+
+        std::vector<GlobalIndexType> indices;
+        indices.reserve(num_components * global_indices.size());
+
+        // component-wise in nodal_values_flat, location-wise ordering in
+        // _nodal_values
+        for (unsigned comp = 0; comp < num_components; ++comp)
+        {
+            for (auto i : global_indices)
+            {
+                indices.push_back(num_components * i + comp);
+            }
+        }
 
-    // TODO does that give rise to PETSc problems?
-    _nodal_values.add(global_indices, nodal_values);
-    counts.add(global_indices, std::vector<double>(global_indices.size(), 1.0));
+        // Nodal_values are passed as a raw pointer, because PETScVector and
+        // EigenVector implementations differ slightly.
+        _nodal_values->add(indices, nodal_values.data());
+        counts.add(indices, std::vector<double>(indices.size(), 1.0));
+    }
 }
 
 void LocalLinearLeastSquaresExtrapolator::calculateResidualElement(
     std::size_t const element_index,
-    ExtrapolatableElementCollection const& extrapolatables)
+    const unsigned num_components,
+    ExtrapolatableElementCollection const& extrapolatables,
+    const double t,
+    GlobalVector const& current_solution,
+    LocalToGlobalIndexMap const& dof_table)
 {
     auto const& int_pt_vals = extrapolatables.getIntegrationPointValues(
-        element_index, _integration_point_values_cache);
+        element_index, t, current_solution, dof_table,
+        _integration_point_values_cache);
 
-    // TODO: for now always zeroth component is used
-    const auto& global_indices = _local_to_global(element_index, 0).rows;
+    const unsigned num_values = int_pt_vals.size();
+    if (num_values % num_components != 0)
+        OGS_FATAL(
+            "The number of computed integration point values is not divisable "
+            "by the number of num_components. Maybe the computed property is "
+            "not a %d-component vector for each integration point.",
+            num_components);
 
-    const unsigned num_int_pts = int_pt_vals.size();
-    const unsigned num_nodes = global_indices.size();
+    // number of integration points in the element
+    const auto num_int_pts = num_values / num_components;
 
-    // filter nodal values of the current element
-    Eigen::VectorXd nodal_vals_element(num_nodes);
-    for (unsigned i = 0; i < num_nodes; ++i) {
-        // TODO PETSc negative indices?
-        nodal_vals_element[i] = _nodal_values[global_indices[i]];
-    }
+    // TODO: for now always zeroth component is used
+    const auto& global_indices =
+        _dof_table_single_component(element_index, 0).rows;
+    const unsigned num_nodes = global_indices.size();
 
     auto const& interpolation_matrix =
         _qr_decomposition_cache.find({num_nodes, num_int_pts})->second.A;
 
-    double const residual = (interpolation_matrix * nodal_vals_element -
-                             MathLib::toVector(int_pt_vals))
-                                .squaredNorm();
+    Eigen::VectorXd nodal_vals_element(num_nodes);
+    auto const int_pt_vals_mat =
+        MathLib::toMatrix(int_pt_vals, num_components, num_int_pts);
+
+    for (unsigned comp = 0; comp < num_components; ++comp)
+    {
+        // filter nodal values of the current element
+        for (unsigned i = 0; i < num_nodes; ++i)
+        {
+            // TODO PETSc negative indices?
+            auto const idx = num_components * global_indices[i] + comp;
+            nodal_vals_element[i] = (*_nodal_values)[idx];
+        }
 
-    _residuals.set(element_index, std::sqrt(residual / num_int_pts));
+        double const residual = (interpolation_matrix * nodal_vals_element -
+                                 int_pt_vals_mat.row(comp).transpose())
+                                    .squaredNorm();
+
+        auto const eidx = num_components * element_index + comp;
+        _residuals->set(eidx, std::sqrt(residual / num_int_pts));
+    }
 }
 
 }  // namespace NumLib
diff --git a/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.h b/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.h
index aa8929267d47411a80203c52aec28ee973a1013e..f1cbb79f266c7ef8ee355496cc4459ff62430be1 100644
--- a/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.h
+++ b/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.h
@@ -48,8 +48,11 @@ public:
     explicit LocalLinearLeastSquaresExtrapolator(
         NumLib::LocalToGlobalIndexMap const& dof_table);
 
-    void extrapolate(
-            ExtrapolatableElementCollection const& extrapolatables) override;
+    void extrapolate(const unsigned num_components,
+                     ExtrapolatableElementCollection const& extrapolatables,
+                     const double t,
+                     GlobalVector const& current_solution,
+                     LocalToGlobalIndexMap const& dof_table) override;
 
     /*! \copydoc Extrapolator::calculateResiduals()
      *
@@ -59,41 +62,44 @@ public:
      * again.
      */
     void calculateResiduals(
-            ExtrapolatableElementCollection const& extrapolatables) override;
+        const unsigned num_components,
+        ExtrapolatableElementCollection const& extrapolatables,
+        const double t,
+        GlobalVector const& current_solution,
+        LocalToGlobalIndexMap const& dof_table) override;
 
     GlobalVector const& getNodalValues() const override
     {
-        return _nodal_values;
+        return *_nodal_values;
     }
 
     GlobalVector const& getElementResiduals() const override
     {
-        return _residuals;
-    }
-
-    ~LocalLinearLeastSquaresExtrapolator() override
-    {
-        NumLib::GlobalVectorProvider::provider.releaseVector(
-            _nodal_values);
+        return *_residuals;
     }
 
 private:
     //! Extrapolate one element.
     void extrapolateElement(
-        std::size_t const element_index,
-        ExtrapolatableElementCollection const& extrapolatables,
-        GlobalVector& counts);
+        std::size_t const element_index, const unsigned num_components,
+        ExtrapolatableElementCollection const& extrapolatables, const double t,
+        GlobalVector const& current_solution,
+        LocalToGlobalIndexMap const& dof_table, GlobalVector& counts);
 
     //! Compute the residuals for one element
     void calculateResidualElement(
         std::size_t const element_index,
-        ExtrapolatableElementCollection const& extrapolatables);
+        const unsigned num_components,
+        ExtrapolatableElementCollection const& extrapolatables,
+        const double t,
+        GlobalVector const& current_solution,
+        LocalToGlobalIndexMap const& dof_table);
 
-    GlobalVector& _nodal_values;  //!< extrapolated nodal values
-    GlobalVector _residuals;      //!< extrapolation residuals
+    std::unique_ptr<GlobalVector> _nodal_values;  //!< extrapolated nodal values
+    std::unique_ptr<GlobalVector> _residuals;     //!< extrapolation residuals
 
     //! DOF table used for writing to global vectors.
-    NumLib::LocalToGlobalIndexMap const& _local_to_global;
+    NumLib::LocalToGlobalIndexMap const& _dof_table_single_component;
 
     //! Avoids frequent reallocations.
     std::vector<double> _integration_point_values_cache;
@@ -108,12 +114,12 @@ private:
         Eigen::MatrixXd A_pinv;
     };
 
-    /*! Maps (\#nodes, \#int_pts) to (N_0, QR decomposition), where N_0 is the
-     * shape matrix of the first integration point.
+    /*! Maps (#nodes, #int_pts) to (N_0, QR decomposition),
+     * where N_0 is the shape matrix of the first integration point.
      *
-     * \note It is assumed that the pair (\#nodes, \#int_pts) uniquely
-     * identifies the set of all shape matrices N for a mesh element (i.e., only
-     * N, not dN/dx etc.).
+     * \note It is assumed that the pair (#nodes, #int_pts) uniquely identifies
+     * the set of all shape matrices N for a mesh element (i.e., only N, not
+     * dN/dx etc.).
      *
      * \todo Add the element dimension as identifying criterion, or change to
      * typeid.