diff --git a/Applications/ApplicationsLib/ProjectData.cpp b/Applications/ApplicationsLib/ProjectData.cpp
index 58c6ff0169443a8a9eceece6b25d7dee7db5017d..ad78dd94513b33841626e7f1ac5ee906af3dd2d2 100644
--- a/Applications/ApplicationsLib/ProjectData.cpp
+++ b/Applications/ApplicationsLib/ProjectData.cpp
@@ -330,8 +330,7 @@ void ProjectData::parseTimeStepping(BaseLib::ConfigTree const& timestepping_conf
 {
     DBUG("Reading time loop configuration.");
 
-    _time_loop = ApplicationsLib::createUncoupledProcessesTimeLoop<
-        GlobalMatrix, GlobalVector>(timestepping_config);
+    _time_loop = ApplicationsLib::createUncoupledProcessesTimeLoop(timestepping_config);
 
     if (!_time_loop)
     {
@@ -372,7 +371,7 @@ void ProjectData::parseNonlinearSolvers(BaseLib::ConfigTree const& config)
         auto const name = conf.getConfigParameter<std::string>("name");
         BaseLib::insertIfKeyUniqueElseError(_nonlinear_solvers,
             name,
-            NumLib::createNonlinearSolver<GlobalMatrix, GlobalVector>(
+            NumLib::createNonlinearSolver(
                 *linear_solver, conf).first,
             "The nonlinear solver name is not unique");
     }
diff --git a/Applications/ApplicationsLib/ProjectData.h b/Applications/ApplicationsLib/ProjectData.h
index 9d5abda1704d66154d445d199698f6fbe3ab79b9..9397f5a75e6d918fa4fad2eeb22af6e0006b35ce 100644
--- a/Applications/ApplicationsLib/ProjectData.h
+++ b/Applications/ApplicationsLib/ProjectData.h
@@ -44,7 +44,6 @@ class NonlinearSolverBase;
 
 namespace ApplicationsLib
 {
-template<typename Matrix, typename Vector>
 class UncoupledProcessesTimeLoop;
 }
 
@@ -57,8 +56,7 @@ class ProjectData final
 {
 public:
     /// The time loop type used to solve this project's processes.
-    using TimeLoop = ApplicationsLib::UncoupledProcessesTimeLoop<
-        GlobalMatrix, GlobalVector>;
+    using TimeLoop = ApplicationsLib::UncoupledProcessesTimeLoop;
 
     /// The empty constructor used in the gui, for example, when the project's
     /// configuration is not loaded yet.
diff --git a/Applications/ApplicationsLib/UncoupledProcessesTimeLoop.cpp b/Applications/ApplicationsLib/UncoupledProcessesTimeLoop.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ff891e6503b96805515dbeb94f5a5d6884f703fd
--- /dev/null
+++ b/Applications/ApplicationsLib/UncoupledProcessesTimeLoop.cpp
@@ -0,0 +1,229 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#include "UncoupledProcessesTimeLoop.h"
+
+namespace ApplicationsLib
+{
+
+std::unique_ptr<UncoupledProcessesTimeLoop>
+createUncoupledProcessesTimeLoop(BaseLib::ConfigTree const& conf)
+{
+    //! \ogs_file_param{prj__time_stepping__type}
+    auto const type = conf.peekConfigParameter<std::string>("type");
+
+    std::unique_ptr<NumLib::ITimeStepAlgorithm> timestepper;
+
+    if (type == "SingleStep") {
+        conf.ignoreConfigParameter("type");
+        timestepper.reset(new NumLib::FixedTimeStepping(0.0, 1.0, 1.0));
+    } else if (type == "FixedTimeStepping") {
+        timestepper = NumLib::FixedTimeStepping::newInstance(conf);
+    } else {
+            OGS_FATAL("Unknown timestepper type: `%s'.", type.c_str());
+    }
+
+    using TimeLoop = UncoupledProcessesTimeLoop;
+    return std::unique_ptr<TimeLoop>{new TimeLoop{std::move(timestepper)}};
+}
+
+
+std::vector<typename UncoupledProcessesTimeLoop::SingleProcessData>
+UncoupledProcessesTimeLoop::
+initInternalData(ProjectData& project)
+{
+    auto const num_processes = std::distance(project.processesBegin(),
+                                             project.processesEnd());
+
+    std::vector<SingleProcessData> per_process_data;
+    per_process_data.reserve(num_processes);
+
+    // create a time discretized ODE system for each process
+    for (auto p = project.processesBegin(); p != project.processesEnd(); ++p)
+    {
+        auto& pcs = **p;
+        auto& nonlinear_solver = pcs.getNonlinearSolver();
+        auto& time_disc = pcs.getTimeDiscretization();
+
+        per_process_data.emplace_back(
+                    makeSingleProcessData(nonlinear_solver, **p, time_disc));
+    }
+
+    return per_process_data;
+}
+
+
+void
+UncoupledProcessesTimeLoop::
+setInitialConditions(ProjectData& project,
+                     double const t0,
+                     std::vector<SingleProcessData>& per_process_data)
+{
+    auto const num_processes = std::distance(project.processesBegin(),
+                                             project.processesEnd());
+
+    _process_solutions.reserve(num_processes);
+
+    unsigned pcs_idx = 0;
+    for (auto p = project.processesBegin(); p != project.processesEnd();
+         ++p, ++pcs_idx)
+    {
+        auto& pcs         = **p;
+        auto& time_disc   =   pcs.getTimeDiscretization();
+
+        auto& ppd         =   per_process_data[pcs_idx];
+        auto& ode_sys     =  *ppd.tdisc_ode_sys;
+        auto const nl_tag =   ppd.nonlinear_solver_tag;
+
+        // append a solution vector of suitable size
+        _process_solutions.emplace_back(
+                    &MathLib::GlobalVectorProvider<GlobalVector>::provider.getVector(
+                        ode_sys.getMatrixSpecifications()));
+
+        auto& x0 = *_process_solutions[pcs_idx];
+        pcs.setInitialConditions(x0);
+        MathLib::BLAS::finalizeAssembly(x0);
+
+        time_disc.setInitialState(t0, x0); // push IC
+
+        if (time_disc.needsPreload())
+        {
+            auto& nonlinear_solver = ppd.nonlinear_solver;
+            auto& mat_strg         = ppd.mat_strg;
+
+            setEquationSystem(nonlinear_solver, ode_sys, nl_tag);
+            nonlinear_solver.assemble(x0);
+            time_disc.pushState(t0, x0, mat_strg); // TODO: that might do duplicate work
+        }
+    }
+}
+
+
+bool
+UncoupledProcessesTimeLoop::
+solveOneTimeStepOneProcess(
+        GlobalVector& x, double const t, double const delta_t,
+        SingleProcessData& process_data,
+        UncoupledProcessesTimeLoop::Process& process)
+{
+    auto& time_disc        =  process.getTimeDiscretization();
+    auto& ode_sys          = *process_data.tdisc_ode_sys;
+    auto& nonlinear_solver =  process_data.nonlinear_solver;
+    auto const nl_tag      =  process_data.nonlinear_solver_tag;
+
+    setEquationSystem(nonlinear_solver, ode_sys, nl_tag);
+
+    // Note: Order matters!
+    // First advance to the next timestep, then set known solutions at that
+    // time, afterwards pass the right solution vector and time to the
+    // preTimestep() hook.
+
+    time_disc.nextTimestep(t, delta_t);
+
+    applyKnownSolutions(ode_sys, nl_tag, x);
+
+    process.preTimestep(x, t, delta_t);
+
+    bool nonlinear_solver_succeeded = nonlinear_solver.solve(x);
+
+    auto& mat_strg = process_data.mat_strg;
+    time_disc.pushState(t, x, mat_strg);
+
+    process.postTimestep(x);
+
+    return nonlinear_solver_succeeded;
+}
+
+
+bool UncoupledProcessesTimeLoop::loop(ProjectData& project)
+{
+    auto per_process_data = initInternalData(project);
+
+    auto& out_ctrl = project.getOutputControl();
+    out_ctrl.initialize(project.processesBegin(), project.processesEnd());
+
+    auto const t0 = _timestepper->getTimeStep().current(); // time of the IC
+
+    // init solution storage
+    setInitialConditions(project, t0, per_process_data);
+
+    // output initial conditions
+    {
+        unsigned pcs_idx = 0;
+        for (auto p = project.processesBegin(); p != project.processesEnd();
+             ++p, ++pcs_idx)
+        {
+            auto const& x0 = *_process_solutions[pcs_idx];
+            out_ctrl.doOutput(**p, 0, t0, x0);
+        }
+    }
+
+    double t = t0;
+    std::size_t timestep = 1; // the first timestep really is number one
+    bool nonlinear_solver_succeeded = true;
+
+    while (_timestepper->next())
+    {
+        auto const ts = _timestepper->getTimeStep();
+        auto const delta_t = ts.dt();
+        t        = ts.current();
+        timestep = ts.steps();
+
+        INFO("=== timestep #%u (t=%gs, dt=%gs) ==============================",
+             timestep, t, delta_t);
+
+        // TODO use process name
+        unsigned pcs_idx = 0;
+        for (auto p = project.processesBegin(); p != project.processesEnd();
+             ++p, ++pcs_idx)
+        {
+            auto& x = *_process_solutions[pcs_idx];
+
+            nonlinear_solver_succeeded = solveOneTimeStepOneProcess(
+                        x, t, delta_t, per_process_data[pcs_idx], **p);
+
+            if (!nonlinear_solver_succeeded) {
+                ERR("The nonlinear solver failed in timestep #%u at t = %g s"
+                    " for process #%u.", timestep, t, pcs_idx);
+
+                // save unsuccessful solution
+                out_ctrl.doOutputAlways(**p, timestep, t, x);
+
+                break;
+            } else {
+                out_ctrl.doOutput(**p, timestep, t, x);
+            }
+        }
+
+        if (!nonlinear_solver_succeeded) break;
+    }
+
+    // output last timestep
+    if (nonlinear_solver_succeeded)
+    {
+        unsigned pcs_idx = 0;
+        for (auto p = project.processesBegin(); p != project.processesEnd();
+             ++p, ++pcs_idx)
+        {
+            auto const& x = *_process_solutions[pcs_idx];
+            out_ctrl.doOutputLastTimestep(**p, timestep, t, x);
+        }
+    }
+
+    return nonlinear_solver_succeeded;
+}
+
+
+UncoupledProcessesTimeLoop::~UncoupledProcessesTimeLoop()
+{
+    for (auto * x : _process_solutions)
+        MathLib::GlobalVectorProvider<GlobalVector>::provider.releaseVector(*x);
+}
+
+} // namespace ApplicationsLib
diff --git a/Applications/ApplicationsLib/UncoupledProcessesTimeLoop.h b/Applications/ApplicationsLib/UncoupledProcessesTimeLoop.h
index 2ce2b8a2c37dbc30ad6293408b8bf1ddd0fa5db3..a89d8d15826e6f1b28df181a68ef95cb01387660 100644
--- a/Applications/ApplicationsLib/UncoupledProcessesTimeLoop.h
+++ b/Applications/ApplicationsLib/UncoupledProcessesTimeLoop.h
@@ -26,7 +26,6 @@ namespace ApplicationsLib
 {
 
 //! Time loop capable of time-integrating several uncoupled processes at once.
-template<typename Matrix, typename Vector>
 class UncoupledProcessesTimeLoop
 {
 public:
@@ -41,28 +40,28 @@ public:
 
 private:
     //! An abstract nonlinear solver
-    using AbstractNLSolver = NumLib::NonlinearSolverBase<Matrix, Vector>;
+    using AbstractNLSolver = NumLib::NonlinearSolverBase<GlobalMatrix, GlobalVector>;
     //! An abstract equations system
-    using EquationSystem   = NumLib::EquationSystem<Vector>;
+    using EquationSystem   = NumLib::EquationSystem<GlobalVector>;
     //! An abstract process
     using Process          = ProcessLib::Process;
     //! An abstract time discretization
-    using TimeDisc         = NumLib::TimeDiscretization<Vector>;
+    using TimeDisc         = NumLib::TimeDiscretization<GlobalVector>;
 
-    std::vector<Vector*> _process_solutions;
+    std::vector<GlobalVector*> _process_solutions;
     std::unique_ptr<NumLib::ITimeStepAlgorithm> _timestepper;
 
     struct SingleProcessData
     {
         template<NumLib::ODESystemTag ODETag, NumLib::NonlinearSolverTag NLTag>
         SingleProcessData(
-                NumLib::NonlinearSolver<Matrix, Vector, NLTag>& nonlinear_solver,
+                NumLib::NonlinearSolver<GlobalMatrix, GlobalVector, NLTag>& nonlinear_solver,
                 TimeDisc& time_disc,
-                NumLib::ODESystem<Matrix, Vector, ODETag, NLTag>& ode_sys)
+                NumLib::ODESystem<GlobalMatrix, GlobalVector, ODETag, NLTag>& ode_sys)
             : nonlinear_solver_tag(NLTag)
             , nonlinear_solver(nonlinear_solver)
             , tdisc_ode_sys(
-                  new NumLib::TimeDiscretizedODESystem<Matrix, Vector, ODETag, NLTag>(
+                  new NumLib::TimeDiscretizedODESystem<GlobalMatrix, GlobalVector, ODETag, NLTag>(
                                 ode_sys, time_disc))
             , mat_strg(dynamic_cast<NumLib::InternalMatrixStorage&>(*tdisc_ode_sys))
         {}
@@ -94,7 +93,7 @@ private:
      *
      * \note Currently the \c ODETag is automatically inferred from the given
      *       \c ody_sys. This works as long as \c Process derives from
-     *       \c ODESystem<Matrix, Vector, ODETag>, i.e. as long we only deal with
+     *       \c ODESystem<GlobalMatrix, GlobalVector, ODETag>, i.e. as long we only deal with
      *       one type of ODE. When we introduce more types, this method will have
      *       to be extended slightly.
      */
@@ -102,17 +101,17 @@ private:
     SingleProcessData
     makeSingleProcessData(
             AbstractNLSolver& nonlinear_solver,
-            NumLib::ODESystem<Matrix, Vector, ODETag,
+            NumLib::ODESystem<GlobalMatrix, GlobalVector, ODETag,
                 NumLib::NonlinearSolverTag::Picard>& ode_sys,
             TimeDisc& time_disc)
     {
         using Tag = NumLib::NonlinearSolverTag;
         // A concrete Picard solver
         using NonlinearSolverPicard =
-            NumLib::NonlinearSolver<Matrix, Vector, Tag::Picard>;
+            NumLib::NonlinearSolver<GlobalMatrix, GlobalVector, Tag::Picard>;
         // A concrete Newton solver
         using NonlinearSolverNewton =
-            NumLib::NonlinearSolver<Matrix, Vector, Tag::Newton>;
+            NumLib::NonlinearSolver<GlobalMatrix, GlobalVector, Tag::Newton>;
 
         if (auto* nonlinear_solver_picard =
             dynamic_cast<NonlinearSolverPicard*>(&nonlinear_solver))
@@ -129,7 +128,7 @@ private:
         {
             // The Newton-Raphson method needs a Newton-ready ODE.
 
-            using ODENewton = NumLib::ODESystem<Matrix, Vector, ODETag, Tag::Newton>;
+            using ODENewton = NumLib::ODESystem<GlobalMatrix, GlobalVector, ODETag, Tag::Newton>;
             if (auto* ode_newton = dynamic_cast<ODENewton*>(&ode_sys))
             {
                 return SingleProcessData{
@@ -156,7 +155,7 @@ private:
 
     //! Solves one timestep for the given \c process.
     bool solveOneTimeStepOneProcess(
-            Vector& x, double const t, double const delta_t,
+            GlobalVector& x, double const t, double const delta_t,
             SingleProcessData& process_data,
             Process& process);
 
@@ -166,8 +165,8 @@ private:
     static void setEquationSystem(AbstractNLSolver& nonlinear_solver,
                                   EquationSystem& eq_sys)
     {
-        using Solver = NumLib::NonlinearSolver<Matrix, Vector, NLTag>;
-        using EqSys  = NumLib::NonlinearSystem<Matrix, Vector, NLTag>;
+        using Solver = NumLib::NonlinearSolver<GlobalMatrix, GlobalVector, NLTag>;
+        using EqSys  = NumLib::NonlinearSystem<GlobalMatrix, GlobalVector, NLTag>;
 
         assert(dynamic_cast<Solver*>(&nonlinear_solver) != nullptr);
         assert(dynamic_cast<EqSys*> (&eq_sys) != nullptr);
@@ -200,9 +199,9 @@ private:
     //! for equation systems linearized with either the Picard or Newton method.
     template<NumLib::NonlinearSolverTag NLTag>
     static void applyKnownSolutions(
-            EquationSystem const& eq_sys, Vector& x)
+            EquationSystem const& eq_sys, GlobalVector& x)
     {
-        using EqSys = NumLib::NonlinearSystem<Matrix, Vector, NLTag>;
+        using EqSys = NumLib::NonlinearSystem<GlobalMatrix, GlobalVector, NLTag>;
         assert(dynamic_cast<EqSys const*> (&eq_sys) != nullptr);
         auto& eq_sys_ = static_cast<EqSys const&> (eq_sys);
 
@@ -213,7 +212,7 @@ private:
     //! for equation systems linearized with either the Picard or Newton method.
     static void applyKnownSolutions(
             EquationSystem const& eq_sys,
-            NumLib::NonlinearSolverTag const nl_tag, Vector& x)
+            NumLib::NonlinearSolverTag const nl_tag, GlobalVector& x)
     {
         using Tag = NumLib::NonlinearSolverTag;
         switch (nl_tag)
@@ -225,228 +224,8 @@ private:
 };
 
 //! Builds an UncoupledProcessesTimeLoop from the given configuration.
-template<typename Matrix, typename Vector>
-std::unique_ptr<UncoupledProcessesTimeLoop<Matrix, Vector> >
-createUncoupledProcessesTimeLoop(BaseLib::ConfigTree const& conf)
-{
-    //! \ogs_file_param{prj__time_stepping__type}
-    auto const type = conf.peekConfigParameter<std::string>("type");
-
-    std::unique_ptr<NumLib::ITimeStepAlgorithm> timestepper;
-
-    if (type == "SingleStep") {
-        conf.ignoreConfigParameter("type");
-        timestepper.reset(new NumLib::FixedTimeStepping(0.0, 1.0, 1.0));
-    } else if (type == "FixedTimeStepping") {
-        timestepper = NumLib::FixedTimeStepping::newInstance(conf);
-    } else {
-            OGS_FATAL("Unknown timestepper type: `%s'.", type.c_str());
-    }
-
-    using TimeLoop = UncoupledProcessesTimeLoop<Matrix, Vector>;
-    return std::unique_ptr<TimeLoop>{new TimeLoop{std::move(timestepper)}};
-}
-
-
-template<typename Matrix, typename Vector>
-std::vector<typename UncoupledProcessesTimeLoop<Matrix, Vector>::SingleProcessData>
-UncoupledProcessesTimeLoop<Matrix, Vector>::
-initInternalData(ProjectData& project)
-{
-    auto const num_processes = std::distance(project.processesBegin(),
-                                             project.processesEnd());
-
-    std::vector<SingleProcessData> per_process_data;
-    per_process_data.reserve(num_processes);
-
-    // create a time discretized ODE system for each process
-    for (auto p = project.processesBegin(); p != project.processesEnd(); ++p)
-    {
-        auto& pcs = **p;
-        auto& nonlinear_solver = pcs.getNonlinearSolver();
-        auto& time_disc = pcs.getTimeDiscretization();
-
-        per_process_data.emplace_back(
-                    makeSingleProcessData(nonlinear_solver, **p, time_disc));
-    }
-
-    return per_process_data;
-}
-
-
-template<typename Matrix, typename Vector>
-void
-UncoupledProcessesTimeLoop<Matrix, Vector>::
-setInitialConditions(ProjectData& project,
-                     double const t0,
-                     std::vector<SingleProcessData>& per_process_data)
-{
-    auto const num_processes = std::distance(project.processesBegin(),
-                                             project.processesEnd());
-
-    _process_solutions.reserve(num_processes);
-
-    unsigned pcs_idx = 0;
-    for (auto p = project.processesBegin(); p != project.processesEnd();
-         ++p, ++pcs_idx)
-    {
-        auto& pcs         = **p;
-        auto& time_disc   =   pcs.getTimeDiscretization();
-
-        auto& ppd         =   per_process_data[pcs_idx];
-        auto& ode_sys     =  *ppd.tdisc_ode_sys;
-        auto const nl_tag =   ppd.nonlinear_solver_tag;
-
-        // append a solution vector of suitable size
-        _process_solutions.emplace_back(
-                    &MathLib::GlobalVectorProvider<Vector>::provider.getVector(
-                        ode_sys.getMatrixSpecifications()));
-
-        auto& x0 = *_process_solutions[pcs_idx];
-        pcs.setInitialConditions(x0);
-        MathLib::BLAS::finalizeAssembly(x0);
-
-        time_disc.setInitialState(t0, x0); // push IC
-
-        if (time_disc.needsPreload())
-        {
-            auto& nonlinear_solver = ppd.nonlinear_solver;
-            auto& mat_strg         = ppd.mat_strg;
-
-            setEquationSystem(nonlinear_solver, ode_sys, nl_tag);
-            nonlinear_solver.assemble(x0);
-            time_disc.pushState(t0, x0, mat_strg); // TODO: that might do duplicate work
-        }
-    }
-}
-
-
-template<typename Matrix, typename Vector>
-bool
-UncoupledProcessesTimeLoop<Matrix, Vector>::
-solveOneTimeStepOneProcess(
-        Vector& x, double const t, double const delta_t,
-        SingleProcessData& process_data,
-        typename UncoupledProcessesTimeLoop<Matrix, Vector>::Process& process)
-{
-    auto& time_disc        =  process.getTimeDiscretization();
-    auto& ode_sys          = *process_data.tdisc_ode_sys;
-    auto& nonlinear_solver =  process_data.nonlinear_solver;
-    auto const nl_tag      =  process_data.nonlinear_solver_tag;
-
-    setEquationSystem(nonlinear_solver, ode_sys, nl_tag);
-
-    // Note: Order matters!
-    // First advance to the next timestep, then set known solutions at that
-    // time, afterwards pass the right solution vector and time to the
-    // preTimestep() hook.
-
-    time_disc.nextTimestep(t, delta_t);
-
-    applyKnownSolutions(ode_sys, nl_tag, x);
-
-    process.preTimestep(x, t, delta_t);
-
-    bool nonlinear_solver_succeeded = nonlinear_solver.solve(x);
-
-    auto& mat_strg = process_data.mat_strg;
-    time_disc.pushState(t, x, mat_strg);
-
-    process.postTimestep(x);
-
-    return nonlinear_solver_succeeded;
-}
-
-
-template<typename Matrix, typename Vector>
-bool
-UncoupledProcessesTimeLoop<Matrix, Vector>::
-loop(ProjectData& project)
-{
-    auto per_process_data = initInternalData(project);
-
-    auto& out_ctrl = project.getOutputControl();
-    out_ctrl.initialize(project.processesBegin(), project.processesEnd());
-
-    auto const t0 = _timestepper->getTimeStep().current(); // time of the IC
-
-    // init solution storage
-    setInitialConditions(project, t0, per_process_data);
-
-    // output initial conditions
-    {
-        unsigned pcs_idx = 0;
-        for (auto p = project.processesBegin(); p != project.processesEnd();
-             ++p, ++pcs_idx)
-        {
-            auto const& x0 = *_process_solutions[pcs_idx];
-            out_ctrl.doOutput(**p, 0, t0, x0);
-        }
-    }
-
-    double t = t0;
-    std::size_t timestep = 1; // the first timestep really is number one
-    bool nonlinear_solver_succeeded = true;
-
-    while (_timestepper->next())
-    {
-        auto const ts = _timestepper->getTimeStep();
-        auto const delta_t = ts.dt();
-        t        = ts.current();
-        timestep = ts.steps();
-
-        INFO("=== timestep #%u (t=%gs, dt=%gs) ==============================",
-             timestep, t, delta_t);
-
-        // TODO use process name
-        unsigned pcs_idx = 0;
-        for (auto p = project.processesBegin(); p != project.processesEnd();
-             ++p, ++pcs_idx)
-        {
-            auto& x = *_process_solutions[pcs_idx];
-
-            nonlinear_solver_succeeded = solveOneTimeStepOneProcess(
-                        x, t, delta_t, per_process_data[pcs_idx], **p);
-
-            if (!nonlinear_solver_succeeded) {
-                ERR("The nonlinear solver failed in timestep #%u at t = %g s"
-                    " for process #%u.", timestep, t, pcs_idx);
-
-                // save unsuccessful solution
-                out_ctrl.doOutputAlways(**p, timestep, t, x);
-
-                break;
-            } else {
-                out_ctrl.doOutput(**p, timestep, t, x);
-            }
-        }
-
-        if (!nonlinear_solver_succeeded) break;
-    }
-
-    // output last timestep
-    if (nonlinear_solver_succeeded)
-    {
-        unsigned pcs_idx = 0;
-        for (auto p = project.processesBegin(); p != project.processesEnd();
-             ++p, ++pcs_idx)
-        {
-            auto const& x = *_process_solutions[pcs_idx];
-            out_ctrl.doOutputLastTimestep(**p, timestep, t, x);
-        }
-    }
-
-    return nonlinear_solver_succeeded;
-}
-
-
-template<typename Matrix, typename Vector>
-UncoupledProcessesTimeLoop<Matrix, Vector>::
-~UncoupledProcessesTimeLoop()
-{
-    for (auto * x : _process_solutions)
-        MathLib::GlobalVectorProvider<Vector>::provider.releaseVector(*x);
-}
+std::unique_ptr<UncoupledProcessesTimeLoop>
+createUncoupledProcessesTimeLoop(BaseLib::ConfigTree const& conf);
 
 } // namespace ApplicationsLib
 
diff --git a/NumLib/Assembler/VectorMatrixAssembler.h b/NumLib/Assembler/VectorMatrixAssembler.h
index 1a0433a8985f674a5250f6b0645844a97883453b..99e682e32c1b47f02cb0287c172b13a02fd839f0 100644
--- a/NumLib/Assembler/VectorMatrixAssembler.h
+++ b/NumLib/Assembler/VectorMatrixAssembler.h
@@ -37,8 +37,7 @@ getIndices(std::size_t const id,
     return indices;
 }
 
-template <typename GlobalVector>
-std::vector<double>
+inline std::vector<double>
 getLocalNodalDOFs(GlobalVector const& x,
                   std::vector<GlobalIndexType> const& dof_indices)
 {
@@ -64,15 +63,14 @@ getLocalNodalDOFs(GlobalVector const& x,
  * Each type of equation as flagged by the \c ODETag will have a different
  * VectorMatrixAssembler type.
  */
-template<typename GlobalMatrix, typename GlobalVector,
-         typename LocalAssembler,
+template<typename LocalAssembler,
          NumLib::ODESystemTag ODETag>
 class VectorMatrixAssembler;
 
 
 //! Specialization for first-order implicit quasi-linear systems.
-template<typename GlobalMatrix, typename GlobalVector, typename LocalAssembler>
-class VectorMatrixAssembler<GlobalMatrix, GlobalVector, LocalAssembler,
+template<typename LocalAssembler>
+class VectorMatrixAssembler<LocalAssembler,
         NumLib::ODESystemTag::FirstOrderImplicitQuasilinear> final
 {
 public:
@@ -167,8 +165,8 @@ private:
 
 
 //! Specialization used to add Neumann boundary conditions.
-template<typename GlobalMatrix, typename GlobalVector, typename LocalAssembler>
-class VectorMatrixAssembler<GlobalMatrix, GlobalVector, LocalAssembler,
+template<typename LocalAssembler>
+class VectorMatrixAssembler<LocalAssembler,
         NumLib::ODESystemTag::NeumannBC> final
 {
 public:
diff --git a/NumLib/Extrapolation/Extrapolator.h b/NumLib/Extrapolation/Extrapolator.h
index 797012aab204eaf33e53bc226da1eaf4d03dcd7e..0867cfa389a937efd852c24313d88d3516e5e823 100644
--- a/NumLib/Extrapolation/Extrapolator.h
+++ b/NumLib/Extrapolation/Extrapolator.h
@@ -12,6 +12,9 @@
 
 #include <vector>
 
+#include <Eigen/Eigen>
+#include "NumLib/NumericsConfig.h"
+
 namespace NumLib
 {
 
@@ -21,11 +24,10 @@ namespace NumLib
  * Local assemblers that want to have some integration point values extrapolated
  * using Extrapolator have to implement this interface.
  *
- * \tparam GlobalVector type of the global vector
  * \tparam PropertyTag  type of the property used to query a specific kind of
  *                      integration point value, usually an enum.
  */
-template <typename GlobalVector, typename PropertyTag>
+template <typename PropertyTag>
 class Extrapolatable
 {
 public:
@@ -55,13 +57,12 @@ public:
 /*! Interface for classes that extrapolate integration point values to nodal
  *  values.
  *
- * \tparam GlobalVector   type of the global vector
  * \tparam PropertyTag    type of the property used to query a specific kind of
  *                        integration point value, usually an enum.
  * \tparam LocalAssembler type of the local assembler being queried for
  *                        integration point values.
  */
-template<typename GlobalVector, typename PropertyTag, typename LocalAssembler>
+template<typename PropertyTag, typename LocalAssembler>
 class Extrapolator
 {
 public:
diff --git a/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator-impl.h b/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator-impl.h
index 9215984c16f18c4b77dea9e879bdc4287b16537b..b95f39cb29127f7ebb503293b209ea1ce25a664a 100644
--- a/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator-impl.h
+++ b/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator-impl.h
@@ -21,9 +21,9 @@
 namespace NumLib
 {
 
-template<typename GlobalVector, typename PropertyTag, typename LocalAssembler>
+template<typename PropertyTag, typename LocalAssembler>
 void
-LocalLinearLeastSquaresExtrapolator<GlobalVector, PropertyTag, LocalAssembler>::
+LocalLinearLeastSquaresExtrapolator<PropertyTag, LocalAssembler>::
 extrapolate(LocalAssemblers const& local_assemblers, PropertyTag const property)
 {
     _nodal_values.setZero();
@@ -35,7 +35,7 @@ extrapolate(LocalAssemblers const& local_assemblers, PropertyTag const property)
     counts->setZero(); // TODO BLAS?
 
     using Self = LocalLinearLeastSquaresExtrapolator<
-        GlobalVector, PropertyTag, LocalAssembler>;
+        PropertyTag, LocalAssembler>;
 
     NumLib::SerialExecutor::executeMemberDereferenced(
         *this, &Self::extrapolateElement, local_assemblers, property, *counts);
@@ -43,24 +43,24 @@ extrapolate(LocalAssemblers const& local_assemblers, PropertyTag const property)
     MathLib::BLAS::componentwiseDivide(_nodal_values, _nodal_values, *counts);
 }
 
-template<typename GlobalVector, typename PropertyTag, typename LocalAssembler>
+template<typename PropertyTag, typename LocalAssembler>
 void
-LocalLinearLeastSquaresExtrapolator<GlobalVector, PropertyTag, LocalAssembler>::
+LocalLinearLeastSquaresExtrapolator<PropertyTag, LocalAssembler>::
 calculateResiduals(LocalAssemblers const& local_assemblers,
                    PropertyTag const property)
 {
     assert(static_cast<std::size_t>(_residuals.size()) == local_assemblers.size());
 
     using Self = LocalLinearLeastSquaresExtrapolator<
-        GlobalVector, PropertyTag, LocalAssembler>;
+        PropertyTag, LocalAssembler>;
 
     NumLib::SerialExecutor::executeMemberDereferenced(
         *this, &Self::calculateResiudalElement, local_assemblers, property);
 }
 
-template<typename GlobalVector, typename PropertyTag, typename LocalAssembler>
+template<typename PropertyTag, typename LocalAssembler>
 void
-LocalLinearLeastSquaresExtrapolator<GlobalVector, PropertyTag, LocalAssembler>::
+LocalLinearLeastSquaresExtrapolator<PropertyTag, LocalAssembler>::
 extrapolateElement(std::size_t const element_index,
                    LocalAssembler const& loc_asm, PropertyTag const property,
                    GlobalVector& counts)
@@ -115,9 +115,9 @@ extrapolateElement(std::size_t const element_index,
     counts.add(global_indices, std::vector<double>(global_indices.size(), 1.0));
 }
 
-template<typename GlobalVector, typename PropertyTag, typename LocalAssembler>
+template<typename PropertyTag, typename LocalAssembler>
 void
-LocalLinearLeastSquaresExtrapolator<GlobalVector, PropertyTag, LocalAssembler>::
+LocalLinearLeastSquaresExtrapolator<PropertyTag, LocalAssembler>::
 calculateResiudalElement(std::size_t const element_index,
                          LocalAssembler const& loc_asm, PropertyTag const property)
 {
diff --git a/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.h b/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.h
index 6fd3066409dfd33c1cd7d435c95da7c9ae07c739..1336a25994f71c676e5ceaf3638ccefec46c30f0 100644
--- a/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.h
+++ b/NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.h
@@ -10,6 +10,7 @@
 #ifndef NUMLIB_LOCAL_LLSQ_EXTRAPOLATOR_H
 #define NUMLIB_LOCAL_LLSQ_EXTRAPOLATOR_H
 
+#include "NumLib/DOF/LocalToGlobalIndexMap.h"
 #include "NumLib/DOF/GlobalMatrixProviders.h"
 #include "Extrapolator.h"
 
@@ -34,13 +35,13 @@ namespace NumLib
  * use of the least squares which requires an exact or overdetermined equation system.
  * \endparblock
  */
-template<typename GlobalVector, typename PropertyTag, typename LocalAssembler>
+template<typename PropertyTag, typename LocalAssembler>
 class LocalLinearLeastSquaresExtrapolator
-        : public Extrapolator<GlobalVector, PropertyTag, LocalAssembler>
+        : public Extrapolator<PropertyTag, LocalAssembler>
 {
 public:
     using LocalAssemblers = typename Extrapolator<
-        GlobalVector, PropertyTag, LocalAssembler>::LocalAssemblers;
+        PropertyTag, LocalAssembler>::LocalAssemblers;
 
     /*! Constructs a new instance
      *
diff --git a/ProcessLib/GroundwaterFlow/GroundwaterFlowFEM.h b/ProcessLib/GroundwaterFlow/GroundwaterFlowFEM.h
index aa2d57f99cf2967f93304e4a91d65b7bc125b578..4f21fdc34310f2970b1c79d8c58258f09c26c35a 100644
--- a/ProcessLib/GroundwaterFlow/GroundwaterFlowFEM.h
+++ b/ProcessLib/GroundwaterFlow/GroundwaterFlowFEM.h
@@ -34,19 +34,16 @@ enum class IntegrationPointValue {
 
 const unsigned NUM_NODAL_DOF = 1;
 
-template <typename GlobalMatrix, typename GlobalVector>
 class GroundwaterFlowLocalAssemblerInterface
-        : public ProcessLib::LocalAssemblerInterface<GlobalMatrix, GlobalVector>
-        , public NumLib::Extrapolatable<GlobalVector, IntegrationPointValue>
+        : public ProcessLib::LocalAssemblerInterface
+        , public NumLib::Extrapolatable<IntegrationPointValue>
 {};
 
 template <typename ShapeFunction,
          typename IntegrationMethod,
-         typename GlobalMatrix,
-         typename GlobalVector,
          unsigned GlobalDim>
 class LocalAssemblerData
-        : public GroundwaterFlowLocalAssemblerInterface<GlobalMatrix, GlobalVector>
+        : public GroundwaterFlowLocalAssemblerInterface
 {
     using ShapeMatricesType = ShapeMatrixPolicyType<ShapeFunction, GlobalDim>;
     using ShapeMatrices = typename ShapeMatricesType::ShapeMatrices;
diff --git a/ProcessLib/GroundwaterFlow/GroundwaterFlowProcess.cpp b/ProcessLib/GroundwaterFlow/GroundwaterFlowProcess.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1c86cc3642a1c3e4e8bbdcfa8a0a5c89dbcd096f
--- /dev/null
+++ b/ProcessLib/GroundwaterFlow/GroundwaterFlowProcess.cpp
@@ -0,0 +1,164 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#include "GroundwaterFlowProcess.h"
+
+#include <cassert>
+
+#include "NumLib/Assembler/VectorMatrixAssembler.h"
+#include "ProcessLib/Utils/CreateLocalAssemblers.h"
+
+
+namespace ProcessLib
+{
+namespace GroundwaterFlow
+{
+
+GroundwaterFlowProcess::GroundwaterFlowProcess(
+    MeshLib::Mesh& mesh,
+    Base::NonlinearSolver& nonlinear_solver,
+    std::unique_ptr<Base::TimeDiscretization>&& time_discretization,
+    std::vector<std::reference_wrapper<ProcessVariable>>&& process_variables,
+    GroundwaterFlowProcessData&& process_data,
+    SecondaryVariableCollection&& secondary_variables,
+    ProcessOutput&& process_output
+    )
+    : Process(mesh, nonlinear_solver, std::move(time_discretization),
+                           std::move(process_variables),
+                           std::move(secondary_variables),
+                           std::move(process_output))
+    , _process_data(std::move(process_data))
+{
+    if (dynamic_cast<NumLib::ForwardEuler<GlobalVector>*>(
+                &Base::getTimeDiscretization()) != nullptr)
+    {
+        OGS_FATAL("GroundwaterFlowProcess can not be solved with the ForwardEuler"
+            " time discretization scheme. Aborting");
+        // Because the M matrix is not assembled. Thus, the linearized system
+        // would be singular. The same applies to CrankNicolson with theta = 0.0,
+        // but this case is not checked here.
+        // Anyway, the GroundwaterFlowProcess shall be transferred to a simpler
+        // ODESystemTag in the future.
+    }
+}
+
+void GroundwaterFlowProcess::initializeConcreteProcess(
+        NumLib::LocalToGlobalIndexMap const& dof_table,
+        MeshLib::Mesh const& mesh,
+        unsigned const integration_order)
+{
+    DBUG("Create global assembler.");
+    _global_assembler.reset(new GlobalAssembler(dof_table));
+
+    ProcessLib::createLocalAssemblers<LocalAssemblerData>(
+                mesh.getDimension(), mesh.getElements(),
+                dof_table, integration_order, _local_assemblers,
+                _process_data);
+
+    // TOOD Later on the DOF table can change during the simulation!
+    _extrapolator.reset(
+        new ExtrapolatorImplementation(Base::getMatrixSpecifications(),
+                                       *Base::_local_to_global_index_map));
+
+    Base::_secondary_variables.addSecondaryVariable(
+        "darcy_velocity_x", 1,
+        makeExtrapolator(IntegrationPointValue::DarcyVelocityX, *_extrapolator,
+                         _local_assemblers));
+
+    if (mesh.getDimension() > 1) {
+        Base::_secondary_variables.addSecondaryVariable(
+            "darcy_velocity_y", 1,
+            makeExtrapolator(IntegrationPointValue::DarcyVelocityY, *_extrapolator,
+                             _local_assemblers));
+    }
+    if (mesh.getDimension() > 2) {
+        Base::_secondary_variables.addSecondaryVariable(
+            "darcy_velocity_z", 1,
+            makeExtrapolator(IntegrationPointValue::DarcyVelocityZ, *_extrapolator,
+                             _local_assemblers));
+    }
+}
+
+void GroundwaterFlowProcess::assembleConcreteProcess(const double t, GlobalVector const& x,
+                             GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b)
+{
+    DBUG("Assemble GroundwaterFlowProcess.");
+
+    // Call global assembler for each local assembly item.
+    GlobalExecutor::executeMemberDereferenced(
+        *_global_assembler, &GlobalAssembler::assemble,
+        _local_assemblers, t, x, M, K, b);
+}
+
+std::unique_ptr<GroundwaterFlowProcess>
+createGroundwaterFlowProcess(
+    MeshLib::Mesh& mesh,
+    Process::NonlinearSolver& nonlinear_solver,
+    std::unique_ptr<Process::TimeDiscretization>&& time_discretization,
+    std::vector<ProcessVariable> const& variables,
+    std::vector<std::unique_ptr<ParameterBase>> const& parameters,
+    BaseLib::ConfigTree const& config)
+{
+    //! \ogs_file_param{process__type}
+    config.checkConfigParameter("type", "GROUNDWATER_FLOW");
+
+    DBUG("Create GroundwaterFlowProcess.");
+
+    // Process variable.
+    auto process_variables = findProcessVariables(variables, config, {
+        //! \ogs_file_param_special{process__GROUNDWATER_FLOW__process_variables__process_variable}
+        "process_variable"
+    });
+
+    // Hydraulic conductivity parameter.
+    auto& hydraulic_conductivity =
+        findParameter<double, MeshLib::Element const&>(
+            config,
+            //! \ogs_file_param_special{process__GROUNDWATER_FLOW__hydraulic_conductivity}
+            "hydraulic_conductivity",
+            parameters);
+
+    DBUG("Use \'%s\' as hydraulic conductivity parameter.",
+         hydraulic_conductivity.name.c_str());
+
+    GroundwaterFlowProcessData process_data {
+        hydraulic_conductivity
+    };
+
+    SecondaryVariableCollection secondary_variables {
+        //! \ogs_file_param{process__secondary_variables}
+        config.getConfigSubtreeOptional("secondary_variables"),
+        {
+            //! \ogs_file_param_special{process__GROUNDWATER_FLOW__secondary_variables__darcy_velocity_x}
+            "darcy_velocity_x",
+            //! \ogs_file_param_special{process__GROUNDWATER_FLOW__secondary_variables__darcy_velocity_y}
+            "darcy_velocity_y",
+            //! \ogs_file_param_special{process__GROUNDWATER_FLOW__secondary_variables__darcy_velocity_z}
+            "darcy_velocity_z"
+        }
+    };
+
+    ProcessOutput
+        //! \ogs_file_param{process__output}
+        process_output{config.getConfigSubtree("output"),
+                process_variables, secondary_variables};
+
+    return std::unique_ptr<GroundwaterFlowProcess>{
+        new GroundwaterFlowProcess{
+            mesh, nonlinear_solver,std::move(time_discretization),
+            std::move(process_variables),
+            std::move(process_data),
+            std::move(secondary_variables),
+            std::move(process_output)
+        }
+    };
+}
+
+}   // namespace GroundwaterFlow
+}   // namespace ProcessLib
diff --git a/ProcessLib/GroundwaterFlow/GroundwaterFlowProcess.h b/ProcessLib/GroundwaterFlow/GroundwaterFlowProcess.h
index d5d2610ba9c27fee1d990ba4478231ea5aa9c9a3..279e3a2c0525e52bcc8464f0af48f15dd4ba8156 100644
--- a/ProcessLib/GroundwaterFlow/GroundwaterFlowProcess.h
+++ b/ProcessLib/GroundwaterFlow/GroundwaterFlowProcess.h
@@ -10,13 +10,8 @@
 #ifndef PROCESS_LIB_GROUNDWATERFLOWPROCESS_H_
 #define PROCESS_LIB_GROUNDWATERFLOWPROCESS_H_
 
-#include <cassert>
-
-#include "NumLib/Assembler/VectorMatrixAssembler.h"
 #include "NumLib/Extrapolation/LocalLinearLeastSquaresExtrapolator.h"
 #include "ProcessLib/Process.h"
-#include "ProcessLib/Utils/CreateLocalAssemblers.h"
-
 #include "GroundwaterFlowFEM.h"
 #include "GroundwaterFlowProcessData.h"
 
@@ -38,27 +33,9 @@ public:
         std::unique_ptr<Base::TimeDiscretization>&& time_discretization,
         std::vector<std::reference_wrapper<ProcessVariable>>&& process_variables,
         GroundwaterFlowProcessData&& process_data,
-        SecondaryVariableCollection<GlobalVector>&& secondary_variables,
-        ProcessOutput<GlobalVector>&& process_output
-        )
-        : Process(mesh, nonlinear_solver, std::move(time_discretization),
-                               std::move(process_variables),
-                               std::move(secondary_variables),
-                               std::move(process_output))
-        , _process_data(std::move(process_data))
-    {
-        if (dynamic_cast<NumLib::ForwardEuler<GlobalVector>*>(
-                    &Base::getTimeDiscretization()) != nullptr)
-        {
-            OGS_FATAL("GroundwaterFlowProcess can not be solved with the ForwardEuler"
-                " time discretization scheme. Aborting");
-            // Because the M matrix is not assembled. Thus, the linearized system
-            // would be singular. The same applies to CrankNicolson with theta = 0.0,
-            // but this case is not checked here.
-            // Anyway, the GroundwaterFlowProcess shall be transferred to a simpler
-            // ODESystemTag in the future.
-        }
-    }
+        SecondaryVariableCollection&& secondary_variables,
+        ProcessOutput&& process_output
+        );
 
     //! \name ODESystem interface
     //! @{
@@ -71,71 +48,28 @@ public:
     //! @}
 
 private:
-    using LocalAssemblerInterface =
-        GroundwaterFlowLocalAssemblerInterface<GlobalMatrix, GlobalVector>;
-
     using GlobalAssembler = NumLib::VectorMatrixAssembler<
-            GlobalMatrix, GlobalVector, LocalAssemblerInterface,
+            GroundwaterFlowLocalAssemblerInterface,
             NumLib::ODESystemTag::FirstOrderImplicitQuasilinear>;
 
     using ExtrapolatorInterface = NumLib::Extrapolator<
-        GlobalVector, IntegrationPointValue, LocalAssemblerInterface>;
+        IntegrationPointValue, GroundwaterFlowLocalAssemblerInterface>;
     using ExtrapolatorImplementation = NumLib::LocalLinearLeastSquaresExtrapolator<
-        GlobalVector, IntegrationPointValue, LocalAssemblerInterface>;
+        IntegrationPointValue, GroundwaterFlowLocalAssemblerInterface>;
 
     void initializeConcreteProcess(
             NumLib::LocalToGlobalIndexMap const& dof_table,
             MeshLib::Mesh const& mesh,
-            unsigned const integration_order) override
-    {
-        DBUG("Create global assembler.");
-        _global_assembler.reset(new GlobalAssembler(dof_table));
-
-        ProcessLib::createLocalAssemblers<LocalAssemblerData>(
-                    mesh.getDimension(), mesh.getElements(),
-                    dof_table, integration_order, _local_assemblers,
-                    _process_data);
-
-        // TOOD Later on the DOF table can change during the simulation!
-        _extrapolator.reset(
-            new ExtrapolatorImplementation(Base::getMatrixSpecifications(),
-                                           *Base::_local_to_global_index_map));
-
-        Base::_secondary_variables.addSecondaryVariable(
-            "darcy_velocity_x", 1,
-            makeExtrapolator(IntegrationPointValue::DarcyVelocityX, *_extrapolator,
-                             _local_assemblers));
-
-        if (mesh.getDimension() > 1) {
-            Base::_secondary_variables.addSecondaryVariable(
-                "darcy_velocity_y", 1,
-                makeExtrapolator(IntegrationPointValue::DarcyVelocityY, *_extrapolator,
-                                 _local_assemblers));
-        }
-        if (mesh.getDimension() > 2) {
-            Base::_secondary_variables.addSecondaryVariable(
-                "darcy_velocity_z", 1,
-                makeExtrapolator(IntegrationPointValue::DarcyVelocityZ, *_extrapolator,
-                                 _local_assemblers));
-        }
-    }
+            unsigned const integration_order) override;
 
     void assembleConcreteProcess(const double t, GlobalVector const& x,
-                                 GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b) override
-    {
-        DBUG("Assemble GroundwaterFlowProcess.");
-
-        // Call global assembler for each local assembly item.
-        GlobalExecutor::executeMemberDereferenced(
-            *_global_assembler, &GlobalAssembler::assemble,
-            _local_assemblers, t, x, M, K, b);
-    }
+                                 GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b) override;
 
 
     GroundwaterFlowProcessData _process_data;
 
     std::unique_ptr<GlobalAssembler> _global_assembler;
-    std::vector<std::unique_ptr<LocalAssemblerInterface>> _local_assemblers;
+    std::vector<std::unique_ptr<GroundwaterFlowLocalAssemblerInterface>> _local_assemblers;
 
     std::unique_ptr<ExtrapolatorInterface> _extrapolator;
 };
@@ -147,62 +81,7 @@ createGroundwaterFlowProcess(
     std::unique_ptr<Process::TimeDiscretization>&& time_discretization,
     std::vector<ProcessVariable> const& variables,
     std::vector<std::unique_ptr<ParameterBase>> const& parameters,
-    BaseLib::ConfigTree const& config)
-{
-    //! \ogs_file_param{process__type}
-    config.checkConfigParameter("type", "GROUNDWATER_FLOW");
-
-    DBUG("Create GroundwaterFlowProcess.");
-
-    // Process variable.
-    auto process_variables = findProcessVariables(variables, config, {
-        //! \ogs_file_param_special{process__GROUNDWATER_FLOW__process_variables__process_variable}
-        "process_variable"
-    });
-
-    // Hydraulic conductivity parameter.
-    auto& hydraulic_conductivity =
-        findParameter<double, MeshLib::Element const&>(
-            config,
-            //! \ogs_file_param_special{process__GROUNDWATER_FLOW__hydraulic_conductivity}
-            "hydraulic_conductivity",
-            parameters);
-
-    DBUG("Use \'%s\' as hydraulic conductivity parameter.",
-         hydraulic_conductivity.name.c_str());
-
-    GroundwaterFlowProcessData process_data {
-        hydraulic_conductivity
-    };
-
-    SecondaryVariableCollection<GlobalVector> secondary_variables {
-        //! \ogs_file_param{process__secondary_variables}
-        config.getConfigSubtreeOptional("secondary_variables"),
-        {
-            //! \ogs_file_param_special{process__GROUNDWATER_FLOW__secondary_variables__darcy_velocity_x}
-            "darcy_velocity_x",
-            //! \ogs_file_param_special{process__GROUNDWATER_FLOW__secondary_variables__darcy_velocity_y}
-            "darcy_velocity_y",
-            //! \ogs_file_param_special{process__GROUNDWATER_FLOW__secondary_variables__darcy_velocity_z}
-            "darcy_velocity_z"
-        }
-    };
-
-    ProcessOutput<GlobalVector>
-        //! \ogs_file_param{process__output}
-        process_output{config.getConfigSubtree("output"),
-                process_variables, secondary_variables};
-
-    return std::unique_ptr<GroundwaterFlowProcess>{
-        new GroundwaterFlowProcess{
-            mesh, nonlinear_solver,std::move(time_discretization),
-            std::move(process_variables),
-            std::move(process_data),
-            std::move(secondary_variables),
-            std::move(process_output)
-        }
-    };
-}
+    BaseLib::ConfigTree const& config);
 
 }   // namespace GroundwaterFlow
 }   // namespace ProcessLib
diff --git a/ProcessLib/LocalAssemblerInterface.h b/ProcessLib/LocalAssemblerInterface.h
index 338faa9376d3a32c70a3d5a8bd299b05496c3c37..8c43b50380a754a2b0c2c9befe8ee103ae8494b0 100644
--- a/ProcessLib/LocalAssemblerInterface.h
+++ b/ProcessLib/LocalAssemblerInterface.h
@@ -18,7 +18,6 @@ namespace ProcessLib
  *
  * \todo Generalize to other NumLib::ODESystemTag's.
  */
-template <typename GlobalMatrix, typename GlobalVector>
 class LocalAssemblerInterface
 {
 public:
diff --git a/ProcessLib/NeumannBc.cpp b/ProcessLib/NeumannBc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7b14a1695aa372e8da85369fd7a4cf86f5ff89b3
--- /dev/null
+++ b/ProcessLib/NeumannBc.cpp
@@ -0,0 +1,92 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#include "NeumannBc.h"
+
+#include "NumLib/Fem/FiniteElement/TemplateIsoparametric.h"
+#include "NumLib/Fem/ShapeMatrixPolicy.h"
+
+#include "MeshLib/MeshSearch/NodeSearch.h"
+
+#include "Utils/CreateLocalAssemblers.h"
+
+namespace ProcessLib
+{
+
+NeumannBc::NeumannBc(
+    NeumannBcConfig const& bc,
+    unsigned const integration_order,
+    NumLib::LocalToGlobalIndexMap const& local_to_global_index_map,
+    int const variable_id,
+    int const component_id)
+    : _function(*bc.getFunction()),
+      _integration_order(integration_order)
+{
+    assert(component_id < local_to_global_index_map.getNumComponents());
+
+    // deep copy because the neumann bc config destroys the elements.
+    std::transform(bc.elementsBegin(), bc.elementsEnd(),
+            std::back_inserter(_elements),
+            std::mem_fn(&MeshLib::Element::clone));
+
+    std::vector<MeshLib::Node*> nodes = MeshLib::getUniqueNodes(_elements);
+
+    auto const& mesh_subsets =
+        local_to_global_index_map.getMeshSubsets(variable_id, component_id);
+
+    // TODO extend the node intersection to all parts of mesh_subsets, i.e.
+    // to each of the MeshSubset in the mesh_subsets.
+    _mesh_subset_all_nodes =
+        mesh_subsets.getMeshSubset(0).getIntersectionByNodes(nodes);
+    std::unique_ptr<MeshLib::MeshSubsets> all_mesh_subsets{
+        new MeshLib::MeshSubsets{_mesh_subset_all_nodes}};
+
+    // Create local DOF table from intersected mesh subsets for the given
+    // variable and component ids.
+    _local_to_global_index_map.reset(
+        local_to_global_index_map.deriveBoundaryConstrainedMap(
+            variable_id, component_id, std::move(all_mesh_subsets),
+            _elements));
+}
+
+NeumannBc::~NeumannBc()
+{
+    delete _mesh_subset_all_nodes;
+
+    for (auto e : _elements)
+        delete e;
+}
+
+void NeumannBc::integrate(const double t, GlobalVector& b)
+{
+    GlobalExecutor::executeMemberDereferenced(
+                *_global_assembler, &GlobalAssembler::assemble,
+                _local_assemblers, t, b);
+}
+
+void NeumannBc::initialize(unsigned global_dim)
+{
+    DBUG("Create global assembler.");
+    _global_assembler.reset(
+        new GlobalAssembler(*_local_to_global_index_map));
+
+    auto elementValueLookup = [this](MeshLib::Element const&)
+    {
+        return _function();
+    };
+
+    createLocalAssemblers<LocalNeumannBcAsmData>(
+        global_dim, _elements,
+        *_local_to_global_index_map, _integration_order,
+        _local_assemblers,
+        elementValueLookup
+        );
+}
+
+}   // namespace ProcessLib
diff --git a/ProcessLib/NeumannBc.h b/ProcessLib/NeumannBc.h
index c666ec4787b65e53d09b3a21da5afcf56290ff5f..0808f0e1f3ec2df13ccf7f5693ac313fac3aa71f 100644
--- a/ProcessLib/NeumannBc.h
+++ b/ProcessLib/NeumannBc.h
@@ -13,15 +13,9 @@
 #include <memory>
 #include <vector>
 
-#include "NumLib/Fem/FiniteElement/TemplateIsoparametric.h"
-#include "NumLib/Fem/ShapeMatrixPolicy.h"
-
-#include "NumLib/Assembler/VectorMatrixAssembler.h"
 #include "MeshLib/MeshSubset.h"
-#include "MeshLib/MeshSearch/NodeSearch.h"
-
-#include "Utils/CreateLocalAssemblers.h"
-
+#include "NumLib/NumericsConfig.h"
+#include "NumLib/Assembler/VectorMatrixAssembler.h"
 #include "NeumannBcConfig.h"
 #include "NeumannBcAssembler.h"
 
@@ -52,72 +46,15 @@ public:
         unsigned const integration_order,
         NumLib::LocalToGlobalIndexMap const& local_to_global_index_map,
         int const variable_id,
-        int const component_id)
-        : _function(*bc.getFunction()),
-          _integration_order(integration_order)
-    {
-        assert(component_id < local_to_global_index_map.getNumComponents());
-
-        // deep copy because the neumann bc config destroys the elements.
-        std::transform(bc.elementsBegin(), bc.elementsEnd(),
-                std::back_inserter(_elements),
-                std::mem_fn(&MeshLib::Element::clone));
-
-        std::vector<MeshLib::Node*> nodes = MeshLib::getUniqueNodes(_elements);
-
-        auto const& mesh_subsets =
-            local_to_global_index_map.getMeshSubsets(variable_id, component_id);
-
-        // TODO extend the node intersection to all parts of mesh_subsets, i.e.
-        // to each of the MeshSubset in the mesh_subsets.
-        _mesh_subset_all_nodes =
-            mesh_subsets.getMeshSubset(0).getIntersectionByNodes(nodes);
-        std::unique_ptr<MeshLib::MeshSubsets> all_mesh_subsets{
-            new MeshLib::MeshSubsets{_mesh_subset_all_nodes}};
-
-        // Create local DOF table from intersected mesh subsets for the given
-        // variable and component ids.
-        _local_to_global_index_map.reset(
-            local_to_global_index_map.deriveBoundaryConstrainedMap(
-                variable_id, component_id, std::move(all_mesh_subsets),
-                _elements));
-    }
-
-    ~NeumannBc()
-    {
-        delete _mesh_subset_all_nodes;
-
-        for (auto e : _elements)
-            delete e;
-    }
+        int const component_id);
+
+    ~NeumannBc();
 
     /// Calls local assemblers which calculate their contributions to the global
     /// matrix and the right-hand-side.
-    void integrate(const double t, GlobalVector& b)
-    {
-        GlobalExecutor::executeMemberDereferenced(
-                    *_global_assembler, &GlobalAssembler::assemble,
-                    _local_assemblers, t, b);
-    }
-
-    void initialize(unsigned global_dim)
-    {
-        DBUG("Create global assembler.");
-        _global_assembler.reset(
-            new GlobalAssembler(*_local_to_global_index_map));
-
-        auto elementValueLookup = [this](MeshLib::Element const&)
-        {
-            return _function();
-        };
-
-        createLocalAssemblers<LocalNeumannBcAsmData>(
-            global_dim, _elements,
-            *_local_to_global_index_map, _integration_order,
-            _local_assemblers,
-            elementValueLookup
-            );
-    }
+    void integrate(const double t, GlobalVector& b);
+
+    void initialize(unsigned global_dim);
 
 private:
     /// The right-hand-side function of the Neumann boundary condition given as
@@ -138,18 +75,15 @@ private:
     /// the #_function.
     unsigned const _integration_order;
 
-    using LocalAssembler = LocalNeumannBcAsmDataInterface<
-        GlobalMatrix, GlobalVector>;
-
     using GlobalAssembler =
         NumLib::VectorMatrixAssembler<
-            GlobalMatrix, GlobalVector, LocalAssembler,
+            LocalNeumannBcAsmDataInterface,
             NumLib::ODESystemTag::NeumannBC>;
 
     std::unique_ptr<GlobalAssembler> _global_assembler;
 
     /// Local assemblers for each element of #_elements.
-    std::vector<std::unique_ptr<LocalAssembler>> _local_assemblers;
+    std::vector<std::unique_ptr<LocalNeumannBcAsmDataInterface>> _local_assemblers;
 
 };
 
diff --git a/ProcessLib/NeumannBcAssembler.h b/ProcessLib/NeumannBcAssembler.h
index 9da05d9453cb0553ee3b87651ef6a405b20fb035..1e13a3996f075c5180bd3052f2637954620006f3 100644
--- a/ProcessLib/NeumannBcAssembler.h
+++ b/ProcessLib/NeumannBcAssembler.h
@@ -19,7 +19,6 @@
 namespace ProcessLib
 {
 
-template <typename GlobalMatrix, typename GlobalVector>
 class LocalNeumannBcAsmDataInterface
 {
 public:
@@ -33,10 +32,8 @@ public:
 
 template <typename ShapeFunction_,
          typename IntegrationMethod_,
-         typename GlobalMatrix,
-         typename GlobalVector,
          unsigned GlobalDim>
-class LocalNeumannBcAsmData : public LocalNeumannBcAsmDataInterface<GlobalMatrix, GlobalVector>
+class LocalNeumannBcAsmData : public LocalNeumannBcAsmDataInterface
 {
 public:
     using ShapeFunction = ShapeFunction_;
diff --git a/ProcessLib/Process.cpp b/ProcessLib/Process.cpp
index d2d245c06a8b425321caf57f4675fe650b238901..46fdb7d66324a0a04a80af3ae0e8975a047a9a00 100644
--- a/ProcessLib/Process.cpp
+++ b/ProcessLib/Process.cpp
@@ -9,8 +9,242 @@
 
 #include "Process.h"
 
+#include "NumLib/DOF/ComputeSparsityPattern.h"
+#include "DirichletBc.h"
+#include "NeumannBc.h"
+#include "NeumannBcAssembler.h"
+#include "ProcessVariable.h"
+#include "UniformDirichletBoundaryCondition.h"
+
 namespace ProcessLib
 {
+
+Process::Process(
+    MeshLib::Mesh& mesh,
+    NonlinearSolver& nonlinear_solver,
+    std::unique_ptr<TimeDiscretization>&& time_discretization,
+    std::vector<std::reference_wrapper<ProcessVariable>>&& process_variables,
+    SecondaryVariableCollection&& secondary_variables,
+    ProcessOutput&& process_output
+    )
+    : _mesh(mesh)
+    , _secondary_variables(std::move(secondary_variables))
+    , _process_output(std::move(process_output))
+    , _nonlinear_solver(nonlinear_solver)
+    , _time_discretization(std::move(time_discretization))
+    , _process_variables(std::move(process_variables))
+{}
+
+void Process::output(std::string const& file_name,
+            const unsigned /*timestep*/,
+            GlobalVector const& x) const
+{
+    doProcessOutput(file_name, x, _mesh, *_local_to_global_index_map,
+                    _process_variables, _secondary_variables, _process_output);
+}
+
+void Process::initialize()
+{
+    DBUG("Initialize process.");
+
+    DBUG("Construct dof mappings.");
+    constructDofTable();
+
+    DBUG("Compute sparsity pattern");
+    computeSparsityPattern();
+
+    initializeConcreteProcess(*_local_to_global_index_map, _mesh,
+                              _integration_order);
+
+    DBUG("Initialize boundary conditions.");
+    for (int variable_id = 0;
+         variable_id < static_cast<int>(_process_variables.size());
+         ++variable_id)
+    {
+        ProcessVariable& pv = _process_variables[variable_id];
+        for (int component_id = 0;
+             component_id < pv.getNumberOfComponents();
+             ++component_id)
+        {
+            createDirichletBcs(pv, variable_id, component_id);
+            createNeumannBcs(pv, variable_id, component_id);
+        }
+    }
+
+    for (auto& bc : _neumann_bcs)
+        bc->initialize(_mesh.getDimension());
+}
+
+void Process::setInitialConditions(GlobalVector& x)
+{
+    DBUG("Set initial conditions.");
+    for (int variable_id = 0;
+         variable_id < static_cast<int>(_process_variables.size());
+         ++variable_id)
+    {
+        ProcessVariable& pv = _process_variables[variable_id];
+        for (int component_id = 0;
+             component_id < pv.getNumberOfComponents();
+             ++component_id)
+        {
+            setInitialConditions(pv, variable_id, component_id, x);
+        }
+    }
+}
+
+MathLib::MatrixSpecifications Process::getMatrixSpecifications() const
+{
+    auto const& l = *_local_to_global_index_map;
+    return {l.dofSizeWithoutGhosts(), l.dofSizeWithoutGhosts(),
+            &l.getGhostIndices(), &_sparsity_pattern};
+}
+
+void Process::assemble(const double t, GlobalVector const& x,
+              GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b)
+{
+    assembleConcreteProcess(t, x, M, K, b);
+
+    // Call global assembler for each Neumann boundary local assembler.
+    for (auto const& bc : _neumann_bcs)
+        bc->integrate(t, b);
+}
+
+void Process::assembleJacobian(
+    const double t, GlobalVector const& x, GlobalVector const& xdot,
+    const double dxdot_dx, GlobalMatrix const& M,
+    const double dx_dx, GlobalMatrix const& K,
+    GlobalMatrix& Jac)
+{
+    assembleJacobianConcreteProcess(t, x, xdot, dxdot_dx, M, dx_dx, K, Jac);
+
+    // TODO In this method one could check if the user wants to use an
+    //      analytical or a numerical Jacobian. Then the right
+    //      assembleJacobianConcreteProcess() method will be chosen.
+    //      Additionally in the default implementation of said method one
+    //      could provide a fallback to a numerical Jacobian. However, that
+    //      would be in a sense implicit behaviour and it might be better to
+    //      just abort, as is currently the case.
+    //      In order to implement the Jacobian assembly entirely, in addition
+    //      to the operator() in VectorMatrixAssembler there has to be a method
+    //      that dispatches the Jacobian assembly.
+    //      Similarly, then the NeumannBC specialization of VectorMatrixAssembler
+    //      probably can be merged into the main class s.t. one has only one
+    //      type of VectorMatrixAssembler (for each equation type) with the
+    //      three methods assemble(), assembleJacobian() and assembleNeumannBC().
+    //      That list can be extended, e.g. by methods for the assembly of
+    //      source terms.
+    //      UPDATE: Probably it is better to keep a separate NeumannBC version of the
+    //      VectoMatrixAssembler since that will work for all kinds of processes.
+}
+
+void Process::assembleJacobianConcreteProcess(
+    const double /*t*/, GlobalVector const& /*x*/, GlobalVector const& /*xdot*/,
+    const double /*dxdot_dx*/, GlobalMatrix const& /*M*/,
+    const double /*dx_dx*/, GlobalMatrix const& /*K*/,
+    GlobalMatrix& /*Jac*/)
+{
+    OGS_FATAL("The concrete implementation of this Process did not override the"
+        " assembleJacobianConcreteProcess() method."
+        " Hence, no analytical Jacobian is provided for this process"
+        " and the Newton-Raphson method cannot be used to solve it.");
+}
+
+void Process::constructDofTable()
+{
+    // Create single component dof in every of the mesh's nodes.
+    _mesh_subset_all_nodes.reset(
+        new MeshLib::MeshSubset(_mesh, &_mesh.getNodes()));
+
+    // Collect the mesh subsets in a vector.
+    std::vector<std::unique_ptr<MeshLib::MeshSubsets>> all_mesh_subsets;
+    for (ProcessVariable const& pv : _process_variables)
+    {
+        std::generate_n(
+            std::back_inserter(all_mesh_subsets),
+            pv.getNumberOfComponents(),
+            [&]()
+            {
+                return std::unique_ptr<MeshLib::MeshSubsets>{
+                    new MeshLib::MeshSubsets{_mesh_subset_all_nodes.get()}};
+            });
+    }
+
+    _local_to_global_index_map.reset(
+        new NumLib::LocalToGlobalIndexMap(
+            std::move(all_mesh_subsets),
+            NumLib::ComponentOrder::BY_LOCATION));
+}
+
+void Process::setInitialConditions(ProcessVariable const& variable,
+                          int const variable_id,
+                          int const component_id,
+                          GlobalVector& x)
+{
+    std::size_t const n_nodes = _mesh.getNumberOfNodes();
+    for (std::size_t node_id = 0; node_id < n_nodes; ++node_id)
+    {
+        MeshLib::Location const l(_mesh.getID(),
+                                  MeshLib::MeshItemType::Node, node_id);
+        auto global_index =
+            std::abs(_local_to_global_index_map->getGlobalIndex(
+                l, variable_id, component_id));
+#ifdef USE_PETSC
+        // The global indices of the ghost entries of the global matrix or
+        // the global vectors need to be set as negative values for equation
+        // assembly, however the global indices start from zero.  Therefore,
+        // any ghost entry with zero index is assigned an negative value of
+        // the vector size or the matrix dimension.  To assign the initial
+        // value for the ghost entries, the negative indices of the ghost
+        // entries are restored to zero.
+        if (global_index == x.size())
+            global_index = 0;
+#endif
+        x.set(global_index,
+              variable.getInitialConditionValue(node_id, component_id));
+    }
+}
+
+void Process::createDirichletBcs(ProcessVariable& variable, int const variable_id,
+                        int const component_id)
+{
+    MeshGeoToolsLib::MeshNodeSearcher& mesh_node_searcher =
+        MeshGeoToolsLib::MeshNodeSearcher::getMeshNodeSearcher(
+            variable.getMesh());
+
+    variable.initializeDirichletBCs(std::back_inserter(_dirichlet_bcs),
+                                    mesh_node_searcher,
+                                    *_local_to_global_index_map,
+                                    variable_id,
+                                    component_id);
+}
+
+void Process::createNeumannBcs(ProcessVariable& variable, int const variable_id,
+                      int const component_id)
+{
+    // Find mesh nodes.
+    MeshGeoToolsLib::MeshNodeSearcher& mesh_node_searcher =
+        MeshGeoToolsLib::MeshNodeSearcher::getMeshNodeSearcher(
+            variable.getMesh());
+    MeshGeoToolsLib::BoundaryElementsSearcher mesh_element_searcher(
+        variable.getMesh(), mesh_node_searcher);
+
+    // Create a neumann BC for the process variable storing them in the
+    // _neumann_bcs vector.
+    variable.createNeumannBcs(
+                              std::back_inserter(_neumann_bcs),
+                              mesh_element_searcher,
+                              _integration_order,
+                              *_local_to_global_index_map,
+                              variable_id,
+                              component_id);
+}
+
+void Process::computeSparsityPattern()
+{
+    _sparsity_pattern = NumLib::computeSparsityPattern(
+        *_local_to_global_index_map, _mesh);
+}
+
 ProcessVariable& findProcessVariable(
     std::vector<ProcessVariable> const& variables,
     BaseLib::ConfigTree const& pv_config, std::string const& tag)
diff --git a/ProcessLib/Process.h b/ProcessLib/Process.h
index a8d8e065f2dd3681bea130919c2da134cb4e2792..f7fe21fc50fccf897a1e6ea42882ff1ede992b52 100644
--- a/ProcessLib/Process.h
+++ b/ProcessLib/Process.h
@@ -10,19 +10,13 @@
 #ifndef PROCESS_LIB_PROCESS_H_
 #define PROCESS_LIB_PROCESS_H_
 
-#include "MeshLib/IO/VtkIO/VtuInterface.h"
-#include "NumLib/DOF/ComputeSparsityPattern.h"
 #include "NumLib/ODESolver/ODESystem.h"
 #include "NumLib/ODESolver/TimeDiscretization.h"
 #include "NumLib/ODESolver/NonlinearSolver.h"
 
-#include "DirichletBc.h"
-#include "NeumannBc.h"
-#include "NeumannBcAssembler.h"
 #include "Parameter.h"
 #include "ProcessOutput.h"
-#include "ProcessVariable.h"
-#include "UniformDirichletBoundaryCondition.h"
+#include "SecondaryVariable.h"
 
 namespace MeshLib
 {
@@ -49,16 +43,9 @@ public:
         NonlinearSolver& nonlinear_solver,
         std::unique_ptr<TimeDiscretization>&& time_discretization,
         std::vector<std::reference_wrapper<ProcessVariable>>&& process_variables,
-        SecondaryVariableCollection<GlobalVector>&& secondary_variables,
-        ProcessOutput<GlobalVector>&& process_output
-        )
-        : _mesh(mesh)
-        , _secondary_variables(std::move(secondary_variables))
-        , _process_output(std::move(process_output))
-        , _nonlinear_solver(nonlinear_solver)
-        , _time_discretization(std::move(time_discretization))
-        , _process_variables(std::move(process_variables))
-    {}
+        SecondaryVariableCollection&& secondary_variables,
+        ProcessOutput&& process_output
+        );
 
     /// Preprocessing before starting assembly for new timestep.
     virtual void preTimestep(GlobalVector const& /*x*/,
@@ -71,105 +58,22 @@ public:
     /// The file_name is indicating the name of possible output file.
     void output(std::string const& file_name,
                 const unsigned /*timestep*/,
-                GlobalVector const& x) const
-    {
-        doProcessOutput(file_name, x, _mesh, *_local_to_global_index_map,
-                        _process_variables, _secondary_variables, _process_output);
-    }
+                GlobalVector const& x) const;
 
-    void initialize()
-    {
-        DBUG("Initialize process.");
-
-        DBUG("Construct dof mappings.");
-        constructDofTable();
-
-        DBUG("Compute sparsity pattern");
-        computeSparsityPattern();
-
-        initializeConcreteProcess(*_local_to_global_index_map, _mesh,
-                                  _integration_order);
-
-        DBUG("Initialize boundary conditions.");
-        for (int variable_id = 0;
-             variable_id < static_cast<int>(_process_variables.size());
-             ++variable_id)
-        {
-            ProcessVariable& pv = _process_variables[variable_id];
-            for (int component_id = 0;
-                 component_id < pv.getNumberOfComponents();
-                 ++component_id)
-            {
-                createDirichletBcs(pv, variable_id, component_id);
-                createNeumannBcs(pv, variable_id, component_id);
-            }
-        }
-
-        for (auto& bc : _neumann_bcs)
-            bc->initialize(_mesh.getDimension());
-    }
+    void initialize();
 
-    void setInitialConditions(GlobalVector& x)
-    {
-        DBUG("Set initial conditions.");
-        for (int variable_id = 0;
-             variable_id < static_cast<int>(_process_variables.size());
-             ++variable_id)
-        {
-            ProcessVariable& pv = _process_variables[variable_id];
-            for (int component_id = 0;
-                 component_id < pv.getNumberOfComponents();
-                 ++component_id)
-            {
-                setInitialConditions(pv, variable_id, component_id, x);
-            }
-        }
-    }
+    void setInitialConditions(GlobalVector& x);
 
-    MathLib::MatrixSpecifications getMatrixSpecifications() const override final
-    {
-        auto const& l = *_local_to_global_index_map;
-        return {l.dofSizeWithoutGhosts(), l.dofSizeWithoutGhosts(),
-                &l.getGhostIndices(), &_sparsity_pattern};
-    }
+    MathLib::MatrixSpecifications getMatrixSpecifications() const override final;
 
     void assemble(const double t, GlobalVector const& x,
-                  GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b) override final
-    {
-        assembleConcreteProcess(t, x, M, K, b);
-
-        // Call global assembler for each Neumann boundary local assembler.
-        for (auto const& bc : _neumann_bcs)
-            bc->integrate(t, b);
-    }
+                  GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b) override final;
 
     void assembleJacobian(
         const double t, GlobalVector const& x, GlobalVector const& xdot,
         const double dxdot_dx, GlobalMatrix const& M,
         const double dx_dx, GlobalMatrix const& K,
-        GlobalMatrix& Jac) override final
-    {
-        assembleJacobianConcreteProcess(t, x, xdot, dxdot_dx, M, dx_dx, K, Jac);
-
-        // TODO In this method one could check if the user wants to use an
-        //      analytical or a numerical Jacobian. Then the right
-        //      assembleJacobianConcreteProcess() method will be chosen.
-        //      Additionally in the default implementation of said method one
-        //      could provide a fallback to a numerical Jacobian. However, that
-        //      would be in a sense implicit behaviour and it might be better to
-        //      just abort, as is currently the case.
-        //      In order to implement the Jacobian assembly entirely, in addition
-        //      to the operator() in VectorMatrixAssembler there has to be a method
-        //      that dispatches the Jacobian assembly.
-        //      Similarly, then the NeumannBC specialization of VectorMatrixAssembler
-        //      probably can be merged into the main class s.t. one has only one
-        //      type of VectorMatrixAssembler (for each equation type) with the
-        //      three methods assemble(), assembleJacobian() and assembleNeumannBC().
-        //      That list can be extended, e.g. by methods for the assembly of
-        //      source terms.
-        //      UPDATE: Probably it is better to keep a separate NeumannBC version of the
-        //      VectoMatrixAssembler since that will work for all kinds of processes.
-    }
+        GlobalMatrix& Jac) override final;
 
     std::vector<DirichletBc<Index>> const* getKnownSolutions(
         double const /*t*/) const override final
@@ -202,113 +106,26 @@ private:
         const double /*t*/, GlobalVector const& /*x*/, GlobalVector const& /*xdot*/,
         const double /*dxdot_dx*/, GlobalMatrix const& /*M*/,
         const double /*dx_dx*/, GlobalMatrix const& /*K*/,
-        GlobalMatrix& /*Jac*/)
-    {
-        OGS_FATAL("The concrete implementation of this Process did not override the"
-            " assembleJacobianConcreteProcess() method."
-            " Hence, no analytical Jacobian is provided for this process"
-            " and the Newton-Raphson method cannot be used to solve it.");
-    }
+        GlobalMatrix& /*Jac*/);
 
-    void constructDofTable()
-    {
-        // Create single component dof in every of the mesh's nodes.
-        _mesh_subset_all_nodes.reset(
-            new MeshLib::MeshSubset(_mesh, &_mesh.getNodes()));
-
-        // Collect the mesh subsets in a vector.
-        std::vector<std::unique_ptr<MeshLib::MeshSubsets>> all_mesh_subsets;
-        for (ProcessVariable const& pv : _process_variables)
-        {
-            std::generate_n(
-                std::back_inserter(all_mesh_subsets),
-                pv.getNumberOfComponents(),
-                [&]()
-                {
-                    return std::unique_ptr<MeshLib::MeshSubsets>{
-                        new MeshLib::MeshSubsets{_mesh_subset_all_nodes.get()}};
-                });
-        }
-
-        _local_to_global_index_map.reset(
-            new NumLib::LocalToGlobalIndexMap(
-                std::move(all_mesh_subsets),
-                NumLib::ComponentOrder::BY_LOCATION));
-    }
+    void constructDofTable();
 
     /// Sets the initial condition values in the solution vector x for a given
     /// process variable and component.
     void setInitialConditions(ProcessVariable const& variable,
                               int const variable_id,
                               int const component_id,
-                              GlobalVector& x)
-    {
-        std::size_t const n_nodes = _mesh.getNumberOfNodes();
-        for (std::size_t node_id = 0; node_id < n_nodes; ++node_id)
-        {
-            MeshLib::Location const l(_mesh.getID(),
-                                      MeshLib::MeshItemType::Node, node_id);
-            auto global_index =
-                std::abs(_local_to_global_index_map->getGlobalIndex(
-                    l, variable_id, component_id));
-#ifdef USE_PETSC
-            // The global indices of the ghost entries of the global matrix or
-            // the global vectors need to be set as negative values for equation
-            // assembly, however the global indices start from zero.  Therefore,
-            // any ghost entry with zero index is assigned an negative value of
-            // the vector size or the matrix dimension.  To assign the initial
-            // value for the ghost entries, the negative indices of the ghost
-            // entries are restored to zero.
-            if (global_index == x.size())
-                global_index = 0;
-#endif
-            x.set(global_index,
-                  variable.getInitialConditionValue(node_id, component_id));
-        }
-    }
+                              GlobalVector& x);
 
     void createDirichletBcs(ProcessVariable& variable, int const variable_id,
-                            int const component_id)
-    {
-        MeshGeoToolsLib::MeshNodeSearcher& mesh_node_searcher =
-            MeshGeoToolsLib::MeshNodeSearcher::getMeshNodeSearcher(
-                variable.getMesh());
-
-        variable.initializeDirichletBCs(std::back_inserter(_dirichlet_bcs),
-                                        mesh_node_searcher,
-                                        *_local_to_global_index_map,
-                                        variable_id,
-                                        component_id);
-    }
+                            int const component_id);
 
     void createNeumannBcs(ProcessVariable& variable, int const variable_id,
-                          int const component_id)
-    {
-        // Find mesh nodes.
-        MeshGeoToolsLib::MeshNodeSearcher& mesh_node_searcher =
-            MeshGeoToolsLib::MeshNodeSearcher::getMeshNodeSearcher(
-                variable.getMesh());
-        MeshGeoToolsLib::BoundaryElementsSearcher mesh_element_searcher(
-            variable.getMesh(), mesh_node_searcher);
-
-        // Create a neumann BC for the process variable storing them in the
-        // _neumann_bcs vector.
-        variable.createNeumannBcs(
-                                  std::back_inserter(_neumann_bcs),
-                                  mesh_element_searcher,
-                                  _integration_order,
-                                  *_local_to_global_index_map,
-                                  variable_id,
-                                  component_id);
-    }
+                          int const component_id);
 
     /// Computes and stores global matrix' sparsity pattern from given
     /// DOF-table.
-    void computeSparsityPattern()
-    {
-        _sparsity_pattern =
-            NumLib::computeSparsityPattern(*_local_to_global_index_map, _mesh);
-    }
+    void computeSparsityPattern();
 
 protected:
     MeshLib::Mesh& _mesh;
@@ -317,8 +134,8 @@ protected:
     std::unique_ptr<NumLib::LocalToGlobalIndexMap>
         _local_to_global_index_map;
 
-    SecondaryVariableCollection<GlobalVector> _secondary_variables;
-    ProcessOutput<GlobalVector> _process_output;
+    SecondaryVariableCollection _secondary_variables;
+    ProcessOutput _process_output;
 
 private:
     unsigned const _integration_order = 2;
diff --git a/ProcessLib/ProcessOutput.cpp b/ProcessLib/ProcessOutput.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6579726fba3f6eac79cafcd1852b27950a23fdc9
--- /dev/null
+++ b/ProcessLib/ProcessOutput.cpp
@@ -0,0 +1,236 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#include "ProcessOutput.h"
+
+#include "MeshLib/IO/VtkIO/VtuInterface.h"
+
+namespace ProcessLib
+{
+
+ProcessOutput::ProcessOutput(BaseLib::ConfigTree const& output_config,
+              std::vector<std::reference_wrapper<ProcessVariable>> const&
+              process_variables,
+              SecondaryVariableCollection const& secondary_variables)
+{
+    //! \ogs_file_param{process__output__variables}
+    auto const out_vars = output_config.getConfigSubtree("variables");
+
+    //! \ogs_file_param{process__output__variables__variable}
+    for (auto out_var : out_vars.getConfigParameterList<std::string>("variable"))
+    {
+        if (output_variables.find(out_var) != output_variables.cend())
+        {
+            OGS_FATAL("output variable `%s' specified more than once.", out_var.c_str());
+        }
+
+        auto pred = [&out_var](ProcessVariable const& pv) {
+            return pv.getName() == out_var;
+        };
+
+        // check if out_var is a process variable
+        auto const& pcs_var = std::find_if(
+            process_variables.cbegin(), process_variables.cend(), pred);
+
+        if (pcs_var == process_variables.cend()
+            && !secondary_variables.variableExists(out_var))
+        {
+            OGS_FATAL("Output variable `%s' is neither a process variable nor a"
+                " secondary variable", out_var.c_str());
+        }
+
+        DBUG("adding output variable `%s'", out_var.c_str());
+        output_variables.insert(out_var);
+    }
+
+    if (auto out_resid = output_config.getConfigParameterOptional<bool>(
+            "output_extrapolation_residuals")) {
+        output_residuals = *out_resid;
+    }
+
+    // debug output
+    if (auto const param =
+        //! \ogs_file_param{process__output__output_iteration_results}
+        output_config.getConfigParameterOptional<bool>("output_iteration_results"))
+    {
+        DBUG("output_iteration_results: %s", (*param) ? "true" : "false");
+
+        output_iteration_results = *param;
+    }
+}
+
+
+void doProcessOutput(
+        std::string const& file_name,
+        GlobalVector const& x,
+        MeshLib::Mesh& mesh,
+        NumLib::LocalToGlobalIndexMap const& dof_table,
+        std::vector<std::reference_wrapper<ProcessVariable>> const&
+        process_variables,
+        SecondaryVariableCollection secondary_variables,
+        ProcessOutput const& process_output)
+{
+    DBUG("Process output.");
+
+    // Copy result
+#ifdef USE_PETSC
+    // TODO It is also possible directly to copy the data for single process
+    // variable to a mesh property. It needs a vector of global indices and
+    // some PETSc magic to do so.
+    std::vector<double> x_copy(x.getLocalSize() + x.getGhostSize());
+#else
+    std::vector<double> x_copy(x.size());
+#endif
+    x.copyValues(x_copy);
+
+    auto const& output_variables = process_output.output_variables;
+
+    std::size_t const n_nodes = mesh.getNumberOfNodes();
+    int global_component_offset = 0;
+    int global_component_offset_next = 0;
+
+    // primary variables
+    for (ProcessVariable& pv : process_variables)
+    {
+        int const n_components = pv.getNumberOfComponents();
+        global_component_offset = global_component_offset_next;
+        global_component_offset_next += n_components;
+
+        if (output_variables.find(pv.getName()) == output_variables.cend())
+            continue;
+
+        DBUG("  process variable %s", pv.getName().c_str());
+
+        auto& output_data = pv.getOrCreateMeshProperty();
+
+        for (std::size_t node_id = 0; node_id < n_nodes; ++node_id)
+        {
+            MeshLib::Location const l(mesh.getID(),
+                                      MeshLib::MeshItemType::Node, node_id);
+            // TODO extend component ids to multiple process variables.
+            for (int component_id = 0; component_id < n_components;
+                 ++component_id)
+            {
+                auto const global_component_id = global_component_offset + component_id;
+                auto const index =
+                        dof_table.getLocalIndex(
+                            l, global_component_id, x.getRangeBegin(),
+                            x.getRangeEnd());
+
+                output_data[node_id * n_components + component_id] =
+                        x_copy[index];
+            }
+        }
+    }
+
+#ifndef USE_PETSC
+    // the following section is for the output of secondary variables
+
+    auto count_mesh_items = [](
+        MeshLib::Mesh const& mesh, MeshLib::MeshItemType type) -> std::size_t
+    {
+        switch (type) {
+        case MeshLib::MeshItemType::Cell: return mesh.getNumberOfElements();
+        case MeshLib::MeshItemType::Node: return mesh.getNumberOfNodes();
+        default: break; // avoid compiler warning
+        }
+        return 0;
+    };
+
+    auto get_or_create_mesh_property = [&mesh, &count_mesh_items](
+        std::string const& property_name, MeshLib::MeshItemType type)
+    {
+        // Get or create a property vector for results.
+        boost::optional<MeshLib::PropertyVector<double>&> result;
+
+        auto const N = count_mesh_items(mesh, type);
+
+        if (mesh.getProperties().hasPropertyVector(property_name))
+        {
+            result = mesh.getProperties().template
+                     getPropertyVector<double>(property_name);
+        }
+        else
+        {
+            result = mesh.getProperties().template
+                     createNewPropertyVector<double>(property_name, type);
+            result->resize(N);
+        }
+        assert(result && result->size() == N);
+
+        return result;
+    };
+
+    auto add_secondary_var = [&](SecondaryVariable const& var,
+                             std::string const& output_name)
+    {
+        assert(var.n_components == 1); // TODO implement other cases
+
+        {
+            DBUG("  secondary variable %s", output_name.c_str());
+
+            auto result = get_or_create_mesh_property(
+                              output_name, MeshLib::MeshItemType::Node);
+            assert(result->size() == mesh.getNumberOfNodes());
+
+            std::unique_ptr<GlobalVector> result_cache;
+            auto const& nodal_values =
+                    var.fcts.eval_field(x, dof_table, result_cache);
+
+            // Copy result
+            for (std::size_t i = 0; i < mesh.getNumberOfNodes(); ++i)
+            {
+                assert(!std::isnan(nodal_values[i]));
+                (*result)[i] = nodal_values[i];
+            }
+        }
+
+        if (process_output.output_residuals && var.fcts.eval_residuals)
+        {
+            DBUG("  secondary variable %s residual", output_name.c_str());
+            auto const& property_name_res = output_name + "_residual";
+
+            auto result = get_or_create_mesh_property(
+                              property_name_res, MeshLib::MeshItemType::Cell);
+            assert(result->size() == mesh.getNumberOfElements());
+
+            std::unique_ptr<GlobalVector> result_cache;
+            auto const& residuals =
+                    var.fcts.eval_residuals(x, dof_table, result_cache);
+
+            // Copy result
+            for (std::size_t i = 0; i < mesh.getNumberOfElements(); ++i)
+            {
+                assert(!std::isnan(residuals[i]));
+                (*result)[i] = residuals[i];
+            }
+        }
+    };
+
+    for (auto const& elem : secondary_variables)
+    {
+        auto const& var_name = elem.first;
+        if (output_variables.find(var_name) != output_variables.cend())
+        {
+            add_secondary_var(elem.second, var_name);
+        }
+    }
+
+    // secondary variables output end
+#else
+    (void) secondary_variables;
+#endif // USE_PETSC
+
+    // Write output file
+    DBUG("Writing output to \'%s\'.", file_name.c_str());
+    MeshLib::IO::VtuInterface vtu_interface(&mesh, vtkXMLWriter::Binary, true);
+    vtu_interface.writeToFile(file_name);
+}
+
+} // ProcessLib
diff --git a/ProcessLib/ProcessOutput.h b/ProcessLib/ProcessOutput.h
index 96017e01b722c35149c564d9d52d6d87787fa5e6..d791b0ac17c5f561ceceb0d1b80da9e2835ac8cf 100644
--- a/ProcessLib/ProcessOutput.h
+++ b/ProcessLib/ProcessOutput.h
@@ -10,7 +10,6 @@
 #ifndef PROCESSLIB_PROCESSOUTPUT_H
 #define PROCESSLIB_PROCESSOUTPUT_H
 
-#include "MeshLib/IO/VtkIO/VtuInterface.h"
 #include "ProcessVariable.h"
 #include "SecondaryVariable.h"
 
@@ -18,60 +17,13 @@ namespace ProcessLib
 {
 
 //! Holds information about which variables to write to output files.
-template <typename GlobalVector>
 struct ProcessOutput final
 {
     //! Constructs a new instance.
     ProcessOutput(BaseLib::ConfigTree const& output_config,
                   std::vector<std::reference_wrapper<ProcessVariable>> const&
                   process_variables,
-                  SecondaryVariableCollection<GlobalVector> const& secondary_variables)
-    {
-        //! \ogs_file_param{process__output__variables}
-        auto const out_vars = output_config.getConfigSubtree("variables");
-
-        //! \ogs_file_param{process__output__variables__variable}
-        for (auto out_var : out_vars.getConfigParameterList<std::string>("variable"))
-        {
-            if (output_variables.find(out_var) != output_variables.cend())
-            {
-                OGS_FATAL("output variable `%s' specified more than once.", out_var.c_str());
-            }
-
-            auto pred = [&out_var](ProcessVariable const& pv) {
-                return pv.getName() == out_var;
-            };
-
-            // check if out_var is a process variable
-            auto const& pcs_var = std::find_if(
-                process_variables.cbegin(), process_variables.cend(), pred);
-
-            if (pcs_var == process_variables.cend()
-                && !secondary_variables.variableExists(out_var))
-            {
-                OGS_FATAL("Output variable `%s' is neither a process variable nor a"
-                    " secondary variable", out_var.c_str());
-            }
-
-            DBUG("adding output variable `%s'", out_var.c_str());
-            output_variables.insert(out_var);
-        }
-
-        if (auto out_resid = output_config.getConfigParameterOptional<bool>(
-                "output_extrapolation_residuals")) {
-            output_residuals = *out_resid;
-        }
-
-        // debug output
-        if (auto const param =
-            //! \ogs_file_param{process__output__output_iteration_results}
-            output_config.getConfigParameterOptional<bool>("output_iteration_results"))
-        {
-            DBUG("output_iteration_results: %s", (*param) ? "true" : "false");
-
-            output_iteration_results = *param;
-        }
-    }
+                  SecondaryVariableCollection const& secondary_variables);
 
     //! All variables that shall be output.
     std::set<std::string> output_variables;
@@ -84,7 +36,6 @@ struct ProcessOutput final
 
 
 //! Writes output to the given \c file_name using the VTU file format.
-template <typename GlobalVector>
 void doProcessOutput(
         std::string const& file_name,
         GlobalVector const& x,
@@ -92,165 +43,8 @@ void doProcessOutput(
         NumLib::LocalToGlobalIndexMap const& dof_table,
         std::vector<std::reference_wrapper<ProcessVariable>> const&
         process_variables,
-        SecondaryVariableCollection<GlobalVector> secondary_variables,
-        ProcessOutput<GlobalVector> const& process_output)
-{
-    DBUG("Process output.");
-
-    // Copy result
-#ifdef USE_PETSC
-    // TODO It is also possible directly to copy the data for single process
-    // variable to a mesh property. It needs a vector of global indices and
-    // some PETSc magic to do so.
-    std::vector<double> x_copy(x.getLocalSize() + x.getGhostSize());
-#else
-    std::vector<double> x_copy(x.size());
-#endif
-    x.copyValues(x_copy);
-
-    auto const& output_variables = process_output.output_variables;
-
-    std::size_t const n_nodes = mesh.getNumberOfNodes();
-    int global_component_offset = 0;
-    int global_component_offset_next = 0;
-
-    // primary variables
-    for (ProcessVariable& pv : process_variables)
-    {
-        int const n_components = pv.getNumberOfComponents();
-        global_component_offset = global_component_offset_next;
-        global_component_offset_next += n_components;
-
-        if (output_variables.find(pv.getName()) == output_variables.cend())
-            continue;
-
-        DBUG("  process variable %s", pv.getName().c_str());
-
-        auto& output_data = pv.getOrCreateMeshProperty();
-
-        for (std::size_t node_id = 0; node_id < n_nodes; ++node_id)
-        {
-            MeshLib::Location const l(mesh.getID(),
-                                      MeshLib::MeshItemType::Node, node_id);
-            // TODO extend component ids to multiple process variables.
-            for (int component_id = 0; component_id < n_components;
-                 ++component_id)
-            {
-                auto const global_component_id = global_component_offset + component_id;
-                auto const index =
-                        dof_table.getLocalIndex(
-                            l, global_component_id, x.getRangeBegin(),
-                            x.getRangeEnd());
-
-                output_data[node_id * n_components + component_id] =
-                        x_copy[index];
-            }
-        }
-    }
-
-#ifndef USE_PETSC
-    // the following section is for the output of secondary variables
-
-    auto count_mesh_items = [](
-        MeshLib::Mesh const& mesh, MeshLib::MeshItemType type) -> std::size_t
-    {
-        switch (type) {
-        case MeshLib::MeshItemType::Cell: return mesh.getNumberOfElements();
-        case MeshLib::MeshItemType::Node: return mesh.getNumberOfNodes();
-        default: break; // avoid compiler warning
-        }
-        return 0;
-    };
-
-    auto get_or_create_mesh_property = [&mesh, &count_mesh_items](
-        std::string const& property_name, MeshLib::MeshItemType type)
-    {
-        // Get or create a property vector for results.
-        boost::optional<MeshLib::PropertyVector<double>&> result;
-
-        auto const N = count_mesh_items(mesh, type);
-
-        if (mesh.getProperties().hasPropertyVector(property_name))
-        {
-            result = mesh.getProperties().template
-                     getPropertyVector<double>(property_name);
-        }
-        else
-        {
-            result = mesh.getProperties().template
-                     createNewPropertyVector<double>(property_name, type);
-            result->resize(N);
-        }
-        assert(result && result->size() == N);
-
-        return result;
-    };
-
-    auto add_secondary_var = [&](SecondaryVariable<GlobalVector> const& var,
-                             std::string const& output_name)
-    {
-        assert(var.n_components == 1); // TODO implement other cases
-
-        {
-            DBUG("  secondary variable %s", output_name.c_str());
-
-            auto result = get_or_create_mesh_property(
-                              output_name, MeshLib::MeshItemType::Node);
-            assert(result->size() == mesh.getNumberOfNodes());
-
-            std::unique_ptr<GlobalVector> result_cache;
-            auto const& nodal_values =
-                    var.fcts.eval_field(x, dof_table, result_cache);
-
-            // Copy result
-            for (std::size_t i = 0; i < mesh.getNumberOfNodes(); ++i)
-            {
-                assert(!std::isnan(nodal_values[i]));
-                (*result)[i] = nodal_values[i];
-            }
-        }
-
-        if (process_output.output_residuals && var.fcts.eval_residuals)
-        {
-            DBUG("  secondary variable %s residual", output_name.c_str());
-            auto const& property_name_res = output_name + "_residual";
-
-            auto result = get_or_create_mesh_property(
-                              property_name_res, MeshLib::MeshItemType::Cell);
-            assert(result->size() == mesh.getNumberOfElements());
-
-            std::unique_ptr<GlobalVector> result_cache;
-            auto const& residuals =
-                    var.fcts.eval_residuals(x, dof_table, result_cache);
-
-            // Copy result
-            for (std::size_t i = 0; i < mesh.getNumberOfElements(); ++i)
-            {
-                assert(!std::isnan(residuals[i]));
-                (*result)[i] = residuals[i];
-            }
-        }
-    };
-
-    for (auto const& elem : secondary_variables)
-    {
-        auto const& var_name = elem.first;
-        if (output_variables.find(var_name) != output_variables.cend())
-        {
-            add_secondary_var(elem.second, var_name);
-        }
-    }
-
-    // secondary variables output end
-#else
-    (void) secondary_variables;
-#endif // USE_PETSC
-
-    // Write output file
-    DBUG("Writing output to \'%s\'.", file_name.c_str());
-    MeshLib::IO::VtuInterface vtu_interface(&mesh, vtkXMLWriter::Binary, true);
-    vtu_interface.writeToFile(file_name);
-}
+        SecondaryVariableCollection secondary_variables,
+        ProcessOutput const& process_output);
 
 } // ProcessLib
 
diff --git a/ProcessLib/SecondaryVariable.cpp b/ProcessLib/SecondaryVariable.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3ceff2c9e083721bc4f45eb4a17585a772aa96d9
--- /dev/null
+++ b/ProcessLib/SecondaryVariable.cpp
@@ -0,0 +1,84 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#include "SecondaryVariable.h"
+
+namespace ProcessLib
+{
+
+SecondaryVariableCollection::SecondaryVariableCollection(
+        boost::optional<BaseLib::ConfigTree> const& config,
+        std::initializer_list<std::string> tag_names)
+{
+    if (!config) return;
+
+    // read which variables are defined in the config
+    for (auto const& tag_name : tag_names) {
+        if (!_all_secondary_variables.insert(tag_name).second) {
+            OGS_FATAL("Tag name <%s> has been specified twice as a secondary variable.");
+        }
+
+        //! \ogs_file_special
+        if (auto var_name = config->getConfigParameterOptional<std::string>(tag_name))
+        {
+            // TODO check primary vars, too
+            BaseLib::insertIfKeyValueUniqueElseError(
+                        _map_tagname_to_varname, tag_name, *var_name,
+                        "Secondary variable names must be unique.");
+        }
+    }
+}
+
+bool SecondaryVariableCollection::variableExists(std::string const& variable_name) const
+{
+    auto pred = [&variable_name](std::pair<std::string, std::string> const& p) {
+        return p.second == variable_name;
+    };
+
+    // check if out_var is a  secondary variable
+    auto const& var = std::find_if(
+        _map_tagname_to_varname.cbegin(), _map_tagname_to_varname.cend(), pred);
+
+    return var != _map_tagname_to_varname.cend();
+}
+
+void SecondaryVariableCollection::addSecondaryVariable(std::string const& tag_name,
+                          const unsigned num_components,
+                          SecondaryVariableFunctions&& fcts)
+{
+    auto it = _map_tagname_to_varname.find(tag_name);
+
+    // get user-supplied var_name for the given tag_name
+    if (it != _map_tagname_to_varname.end())
+    {
+        auto const& var_name = it->first;
+
+        if (!_configured_secondary_variables
+                 .emplace(std::make_pair(
+                     var_name,
+                     SecondaryVariable{
+                         var_name, num_components, std::move(fcts)}))
+                 .second)
+        {
+            OGS_FATAL("The secondary variable with name `%s' has already been "
+                      "set up.",
+                      var_name.c_str());
+        }
+    }
+    else if (_all_secondary_variables.find(tag_name) ==
+             _all_secondary_variables.end())
+    {
+        OGS_FATAL("The tag <%s> has not been registered to mark a secondary "
+                  "variable.",
+                  tag_name.c_str());
+    }
+}
+
+
+} // namespace ProcessLib
diff --git a/ProcessLib/SecondaryVariable.h b/ProcessLib/SecondaryVariable.h
index 480a9feedd2e780d6f7081e98ab279502daa989f..bf99e2e4c3fb4dfef1072c3be335b9caac102063 100644
--- a/ProcessLib/SecondaryVariable.h
+++ b/ProcessLib/SecondaryVariable.h
@@ -21,7 +21,6 @@ namespace ProcessLib
 
 //! Holder for function objects that compute secondary variables,
 //! and (optionally) also the residuals (e.g., in case of extrapolation)
-template<typename GlobalVector>
 struct SecondaryVariableFunctions final
 {
     /*! Type of functions used.
@@ -83,18 +82,16 @@ struct SecondaryVariableFunctions final
 };
 
 //! Stores information about a specific secondary variable
-template<typename GlobalVector>
 struct SecondaryVariable final
 {
     std::string const name;      //!< Name of the variable; used, e.g., for output.
     const unsigned n_components; //!< Number of components of the variable.
 
     //! Functions used for computing the secondary variable.
-    SecondaryVariableFunctions<GlobalVector> fcts;
+    SecondaryVariableFunctions fcts;
 };
 
 //! Handles configuration of several secondary variables from the project file.
-template<typename GlobalVector>
 class SecondaryVariableCollection final
 {
 public:
@@ -109,43 +106,13 @@ public:
      */
     SecondaryVariableCollection(
             boost::optional<BaseLib::ConfigTree> const& config,
-            std::initializer_list<std::string> tag_names)
-    {
-        if (!config) return;
-
-        // read which variables are defined in the config
-        for (auto const& tag_name : tag_names) {
-            if (!_all_secondary_variables.insert(tag_name).second) {
-                OGS_FATAL("Tag name <%s> has been specified twice as a secondary variable.");
-            }
-
-            //! \ogs_file_special
-            if (auto var_name = config->getConfigParameterOptional<std::string>(tag_name))
-            {
-                // TODO check primary vars, too
-                BaseLib::insertIfKeyValueUniqueElseError(
-                            _map_tagname_to_varname, tag_name, *var_name,
-                            "Secondary variable names must be unique.");
-            }
-        }
-    }
+            std::initializer_list<std::string> tag_names);
 
     /*! Tells if a secondary variable with the specified name has been set up.
      *
      * \note \c variable_name is not the tag name in the project file!
      */
-    bool variableExists(std::string const& variable_name) const
-    {
-        auto pred = [&variable_name](std::pair<std::string, std::string> const& p) {
-            return p.second == variable_name;
-        };
-
-        // check if out_var is a  secondary variable
-        auto const& var = std::find_if(
-            _map_tagname_to_varname.cbegin(), _map_tagname_to_varname.cend(), pred);
-
-        return var != _map_tagname_to_varname.cend();
-    }
+    bool variableExists(std::string const& variable_name) const;
 
     /*! Set up a secondary variable.
      *
@@ -161,47 +128,17 @@ public:
      */
     void addSecondaryVariable(std::string const& tag_name,
                               const unsigned num_components,
-                              SecondaryVariableFunctions<GlobalVector>&& fcts)
-    {
-        auto it = _map_tagname_to_varname.find(tag_name);
-
-        // get user-supplied var_name for the given tag_name
-        if (it != _map_tagname_to_varname.end())
-        {
-            auto const& var_name = it->first;
-
-            if (!_configured_secondary_variables
-                     .emplace(std::make_pair(
-                         var_name,
-                         SecondaryVariable<GlobalVector>{
-                             var_name, num_components, std::move(fcts)}))
-                     .second)
-            {
-                OGS_FATAL("The secondary variable with name `%s' has already been "
-                          "set up.",
-                          var_name.c_str());
-            }
-        }
-        else if (_all_secondary_variables.find(tag_name) ==
-                 _all_secondary_variables.end())
-        {
-            OGS_FATAL("The tag <%s> has not been registered to mark a secondary "
-                      "variable.",
-                      tag_name.c_str());
-        }
-    }
+                              SecondaryVariableFunctions&& fcts);
 
     //! Returns an iterator to the first secondary variable.
-    typename std::map<std::string,
-                      SecondaryVariable<GlobalVector>>::const_iterator
+    std::map<std::string, SecondaryVariable>::const_iterator
     begin() const
     {
         return _configured_secondary_variables.begin();
     }
 
     //! Returns an iterator past the last secondary variable.
-    typename std::map<std::string,
-                      SecondaryVariable<GlobalVector>>::const_iterator
+    std::map<std::string, SecondaryVariable>::const_iterator
     end() const
     {
         return _configured_secondary_variables.end();
@@ -213,7 +150,7 @@ private:
 
     //! Collection of all configured secondary variables.
     //! Maps the variable name to the corresponding SecondaryVariable.
-    std::map<std::string, SecondaryVariable<GlobalVector>> _configured_secondary_variables;
+    std::map<std::string, SecondaryVariable> _configured_secondary_variables;
 
     //! Set of all tags available as a secondary variable.
     std::set<std::string> _all_secondary_variables;
@@ -222,16 +159,16 @@ private:
 
 //! Creates an object that computes a secondary variable via extrapolation
 //! of integration point values.
-template<typename GlobalVector, typename PropertyEnum, typename LocalAssembler>
-SecondaryVariableFunctions<GlobalVector>
+template<typename PropertyEnum, typename LocalAssembler>
+SecondaryVariableFunctions
 makeExtrapolator(PropertyEnum const property,
-                 NumLib::Extrapolator<GlobalVector, PropertyEnum, LocalAssembler>&
+                 NumLib::Extrapolator<PropertyEnum, LocalAssembler>&
                  extrapolator,
-                 typename NumLib::Extrapolator<GlobalVector, PropertyEnum,
+                 typename NumLib::Extrapolator<PropertyEnum,
                      LocalAssembler>::LocalAssemblers const& local_assemblers)
 {
     static_assert(std::is_base_of<
-         NumLib::Extrapolatable<GlobalVector, PropertyEnum>, LocalAssembler>::value,
+         NumLib::Extrapolatable<PropertyEnum>, LocalAssembler>::value,
         "The passed local assembler type (i.e. the local assembler interface) must"
         " derive from NumLib::Extrapolatable<>.");
 
diff --git a/ProcessLib/TES/TESLocalAssembler-impl.h b/ProcessLib/TES/TESLocalAssembler-impl.h
index 4d4aa2ed9712869369ea2fdd6b1d656e6f2a24b4..524089b91208ad2f263fe0cf220e1bd4d5c7eac8 100644
--- a/ProcessLib/TES/TESLocalAssembler-impl.h
+++ b/ProcessLib/TES/TESLocalAssembler-impl.h
@@ -105,9 +105,9 @@ namespace ProcessLib
 namespace TES
 {
 template <typename ShapeFunction_, typename IntegrationMethod_,
-          typename GlobalMatrix, typename GlobalVector, unsigned GlobalDim>
+          unsigned GlobalDim>
 TESLocalAssembler<
-    ShapeFunction_, IntegrationMethod_, GlobalMatrix, GlobalVector,
+    ShapeFunction_, IntegrationMethod_,
     GlobalDim>::TESLocalAssembler(MeshLib::Element const& e,
                                   std::size_t const /*local_matrix_size*/,
                                   unsigned const integration_order,
@@ -131,9 +131,8 @@ TESLocalAssembler<
 }
 
 template <typename ShapeFunction_, typename IntegrationMethod_,
-          typename GlobalMatrix, typename GlobalVector, unsigned GlobalDim>
-void TESLocalAssembler<ShapeFunction_, IntegrationMethod_, GlobalMatrix,
-                       GlobalVector,
+          unsigned GlobalDim>
+void TESLocalAssembler<ShapeFunction_, IntegrationMethod_,
                        GlobalDim>::assemble(const double /*t*/,
                                             std::vector<double> const& local_x)
 {
@@ -186,9 +185,8 @@ void TESLocalAssembler<ShapeFunction_, IntegrationMethod_, GlobalMatrix,
 }
 
 template <typename ShapeFunction_, typename IntegrationMethod_,
-          typename GlobalMatrix, typename GlobalVector, unsigned GlobalDim>
-void TESLocalAssembler<ShapeFunction_, IntegrationMethod_, GlobalMatrix,
-                       GlobalVector, GlobalDim>::
+          unsigned GlobalDim>
+void TESLocalAssembler<ShapeFunction_, IntegrationMethod_, GlobalDim>::
     addToGlobal(
         NumLib::LocalToGlobalIndexMap::RowColumnIndices const& indices,
         GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b) const
@@ -199,9 +197,9 @@ void TESLocalAssembler<ShapeFunction_, IntegrationMethod_, GlobalMatrix,
 }
 
 template <typename ShapeFunction_, typename IntegrationMethod_,
-          typename GlobalMatrix, typename GlobalVector, unsigned GlobalDim>
+          unsigned GlobalDim>
 std::vector<double> const& TESLocalAssembler<
-    ShapeFunction_, IntegrationMethod_, GlobalMatrix, GlobalVector,
+    ShapeFunction_, IntegrationMethod_,
     GlobalDim>::getIntegrationPointValues(TESIntPtVariables const var,
                                           std::vector<double>& cache) const
 {
@@ -209,9 +207,9 @@ std::vector<double> const& TESLocalAssembler<
 }
 
 template <typename ShapeFunction_, typename IntegrationMethod_,
-          typename GlobalMatrix, typename GlobalVector, unsigned GlobalDim>
+          unsigned GlobalDim>
 bool TESLocalAssembler<
-    ShapeFunction_, IntegrationMethod_, GlobalMatrix, GlobalVector,
+    ShapeFunction_, IntegrationMethod_,
     GlobalDim>::checkBounds(std::vector<double> const& local_x,
                             std::vector<double> const& local_x_prev_ts)
 {
diff --git a/ProcessLib/TES/TESLocalAssembler.h b/ProcessLib/TES/TESLocalAssembler.h
index cc80fc33e7fce097d3e79eef1e7db13b97b2e54c..d3e12d37e7addf6d3c01486198206959891e1384 100644
--- a/ProcessLib/TES/TESLocalAssembler.h
+++ b/ProcessLib/TES/TESLocalAssembler.h
@@ -22,9 +22,8 @@ namespace ProcessLib
 {
 namespace TES
 {
-template <typename GlobalMatrix, typename GlobalVector>
 class TESLocalAssemblerInterface
-    : public NumLib::Extrapolatable<GlobalVector, TESIntPtVariables>
+    : public NumLib::Extrapolatable<TESIntPtVariables>
 {
 public:
     virtual ~TESLocalAssemblerInterface() = default;
@@ -41,9 +40,9 @@ public:
 };
 
 template <typename ShapeFunction_, typename IntegrationMethod_,
-          typename GlobalMatrix, typename GlobalVector, unsigned GlobalDim>
+          unsigned GlobalDim>
 class TESLocalAssembler final
-    : public TESLocalAssemblerInterface<GlobalMatrix, GlobalVector>
+    : public TESLocalAssemblerInterface
 {
 public:
     using ShapeFunction = ShapeFunction_;
diff --git a/ProcessLib/TES/TESProcess.cpp b/ProcessLib/TES/TESProcess.cpp
index 0d91e6a7c4db4dc2737080825e250fc59ac8b100..f5240ca0f4d97fe2e387188bc698a97f46907dd4 100644
--- a/ProcessLib/TES/TESProcess.cpp
+++ b/ProcessLib/TES/TESProcess.cpp
@@ -9,6 +9,8 @@
 
 #include "TESProcess.h"
 
+#include "Utils/CreateLocalAssemblers.h"
+
 // TODO Copied from VectorMatrixAssembler. Could be provided by the DOF table.
 inline NumLib::LocalToGlobalIndexMap::RowColumnIndices
 getRowColumnIndices_(std::size_t const id,
@@ -31,7 +33,6 @@ getRowColumnIndices_(std::size_t const id,
                                                                  indices);
 }
 
-template <typename GlobalVector>
 void getVectorValues(
     GlobalVector const& x,
     NumLib::LocalToGlobalIndexMap::RowColumnIndices const& r_c_indices,
@@ -48,7 +49,6 @@ void getVectorValues(
 }
 
 // TODO that essentially duplicates code which is also present in ProcessOutput.
-template <typename GlobalVector>
 double getNodalValue(GlobalVector const& x, MeshLib::Mesh const& mesh,
                      NumLib::LocalToGlobalIndexMap const& dof_table,
                      std::size_t const node_id,
@@ -73,8 +73,8 @@ TESProcess::TESProcess(
     std::unique_ptr<Process::TimeDiscretization>&&
         time_discretization,
     std::vector<std::reference_wrapper<ProcessVariable>>&& process_variables,
-    SecondaryVariableCollection<GlobalVector>&& secondary_variables,
-    ProcessOutput<GlobalVector>&& process_output,
+    SecondaryVariableCollection&& secondary_variables,
+    ProcessOutput&& process_output,
     const BaseLib::ConfigTree& config)
     : Process(
           mesh, nonlinear_solver, std::move(time_discretization),
@@ -202,12 +202,12 @@ void TESProcess::initializeConcreteProcess(
 
     // secondary variables
     auto add2nd = [&](std::string const& var_name, unsigned const n_components,
-                      SecondaryVariableFunctions<GlobalVector>&& fcts) {
+                      SecondaryVariableFunctions&& fcts) {
         this->_secondary_variables.addSecondaryVariable(var_name, n_components,
                                                         std::move(fcts));
     };
     auto makeEx =
-        [&](TESIntPtVariables var) -> SecondaryVariableFunctions<GlobalVector> {
+        [&](TESIntPtVariables var) -> SecondaryVariableFunctions {
         return ProcessLib::makeExtrapolator(var, *_extrapolator,
                                             _local_assemblers);
     };
@@ -304,7 +304,7 @@ NumLib::IterationResult TESProcess::postIteration(
         std::vector<double> local_x_prev_ts_cache;
 
         auto check_variable_bounds = [&](std::size_t id,
-                                         LocalAssembler& loc_asm) {
+                                         TESLocalAssemblerInterface& loc_asm) {
             auto const r_c_indices = getRowColumnIndices_(
                 id, *this->_local_to_global_index_map, indices_cache);
             getVectorValues(x, r_c_indices, local_x_cache);
diff --git a/ProcessLib/TES/TESProcess.h b/ProcessLib/TES/TESProcess.h
index 0570def18bf786b6f773f019fca58419284d845c..58a0e617c5d3cbdd1d3926627d12c2cb5b7db742 100644
--- a/ProcessLib/TES/TESProcess.h
+++ b/ProcessLib/TES/TESProcess.h
@@ -41,8 +41,8 @@ public:
             time_discretization,
         std::vector<std::reference_wrapper<ProcessVariable>>&&
             process_variables,
-        SecondaryVariableCollection<GlobalVector>&& secondary_variables,
-        ProcessOutput<GlobalVector>&& process_output,
+        SecondaryVariableCollection&& secondary_variables,
+        ProcessOutput&& process_output,
         BaseLib::ConfigTree const& config);
 
     void preTimestep(GlobalVector const& x, const double t,
@@ -52,18 +52,15 @@ public:
 
     bool isLinear() const override { return false; }
 private:
-    using LocalAssembler =
-        TESLocalAssemblerInterface<GlobalMatrix, GlobalVector>;
-
     using GlobalAssembler = NumLib::VectorMatrixAssembler<
-        GlobalMatrix, GlobalVector, LocalAssembler,
+        TESLocalAssemblerInterface,
         NumLib::ODESystemTag::FirstOrderImplicitQuasilinear>;
 
     using ExtrapolatorInterface =
-        NumLib::Extrapolator<GlobalVector, TESIntPtVariables, LocalAssembler>;
+        NumLib::Extrapolator<TESIntPtVariables, TESLocalAssemblerInterface>;
     using ExtrapolatorImplementation =
         NumLib::LocalLinearLeastSquaresExtrapolator<
-            GlobalVector, TESIntPtVariables, LocalAssembler>;
+            TESIntPtVariables, TESLocalAssemblerInterface>;
 
     void initializeConcreteProcess(
         NumLib::LocalToGlobalIndexMap const& dof_table,
@@ -89,7 +86,7 @@ private:
         std::unique_ptr<GlobalVector>& result_cache);
 
     std::unique_ptr<GlobalAssembler> _global_assembler;
-    std::vector<std::unique_ptr<LocalAssembler>> _local_assemblers;
+    std::vector<std::unique_ptr<TESLocalAssemblerInterface>> _local_assemblers;
 
     AssemblyParams _assembly_params;
 
@@ -119,7 +116,7 @@ inline std::unique_ptr<TESProcess> createTESProcess(
         variables, config,
         {"fluid_pressure", "temperature", "vapour_mass_fraction"});
 
-    SecondaryVariableCollection<GlobalVector>
+    SecondaryVariableCollection
         secondary_variables{
             config.getConfigSubtreeOptional("secondary_variables"),
             {"solid_density", "reaction_rate", "velocity_x", "velocity_y",
@@ -127,7 +124,7 @@ inline std::unique_ptr<TESProcess> createTESProcess(
              "vapour_partial_pressure", "relative_humidity",
              "equilibrium_loading"}};
 
-    ProcessOutput<GlobalVector> process_output{
+    ProcessOutput process_output{
         config.getConfigSubtree("output"), process_variables,
         secondary_variables};
 
diff --git a/ProcessLib/Utils/CreateLocalAssemblers.h b/ProcessLib/Utils/CreateLocalAssemblers.h
index f4fae98b4582ab010a9b8c62674b6bf626c96fa3..e14aae36025f633d0d71cc71af825c0a23d45408 100644
--- a/ProcessLib/Utils/CreateLocalAssemblers.h
+++ b/ProcessLib/Utils/CreateLocalAssemblers.h
@@ -24,7 +24,7 @@ namespace detail
 {
 
 template<unsigned GlobalDim,
-         template <typename, typename, typename, typename, unsigned> class
+         template <typename, typename, unsigned> class
          LocalAssemblerImplementation,
          typename LocalAssemblerInterface,
          typename... ExtraCtorArgs>
@@ -40,8 +40,6 @@ void createLocalAssemblers(
     using LocalDataInitializer = LocalDataInitializer<
         LocalAssemblerInterface,
         LocalAssemblerImplementation,
-        GlobalMatrix,
-        GlobalVector,
         GlobalDim,
         ExtraCtorArgs...>;
 
@@ -74,7 +72,7 @@ void createLocalAssemblers(
  * The first two template parameters cannot be deduced from the arguments.
  * Therefore they always have to be provided manually.
  */
-template<template <typename, typename, typename, typename, unsigned> class
+template<template <typename, typename, unsigned> class
          LocalAssemblerImplementation,
          typename LocalAssemblerInterface,
          typename... ExtraCtorArgs>
diff --git a/ProcessLib/Utils/LocalDataInitializer.h b/ProcessLib/Utils/LocalDataInitializer.h
index 3f644c31556085363cf65783cded9febd83377f9..5a2ecba8b801110c3728c579553e8bcd19d31bc1 100644
--- a/ProcessLib/Utils/LocalDataInitializer.h
+++ b/ProcessLib/Utils/LocalDataInitializer.h
@@ -122,9 +122,7 @@ namespace ProcessLib
 /// NumLib::ShapeLine2 is created.
 template <
     typename LocalAssemblerInterface,
-    template <typename, typename, typename, typename, unsigned> class LocalAssemblerData,
-    typename GlobalMatrix,
-    typename GlobalVector,
+    template <typename, typename, unsigned> class LocalAssemblerData,
     unsigned GlobalDim,
     typename... ConstructorArgs>
 class LocalDataInitializer final
@@ -285,7 +283,7 @@ private:
     using LAData = LocalAssemblerData<
             ShapeFunction,
             IntegrationMethod<ShapeFunction>,
-            GlobalMatrix, GlobalVector, GlobalDim>;
+            GlobalDim>;
 
     /// Generates a function that creates a new LocalAssembler of type LAData<SHAPE_FCT>
     template<typename ShapeFct>
diff --git a/Tests/NumLib/SteadyDiffusion2DExample1.h b/Tests/NumLib/SteadyDiffusion2DExample1.h
index 5e72dce9b35b50b7e7ca6ed638feb91127c9ba19..213aed5fac92b35d56953fc3305f164de1195281 100644
--- a/Tests/NumLib/SteadyDiffusion2DExample1.h
+++ b/Tests/NumLib/SteadyDiffusion2DExample1.h
@@ -29,7 +29,6 @@ template<typename IndexType>struct SteadyDiffusion2DExample1
         Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
     using LocalVectorType = Eigen::VectorXd;
 
-    template <typename GlobalMatrix, typename GlobalVector>
     class LocalAssemblerData
     {
     public:
@@ -72,14 +71,13 @@ template<typename IndexType>struct SteadyDiffusion2DExample1
         LocalVectorType const* _localRhs = nullptr;
     };
 
-    template <typename GlobalMatrix, typename GlobalVector>
     static
     void initializeLocalData(const MeshLib::Element& e,
-            LocalAssemblerData<GlobalMatrix, GlobalVector>*& data_ptr,
+            LocalAssemblerData*& data_ptr,
             std::size_t const local_matrix_size,
             SteadyDiffusion2DExample1 const& example)
     {
-        data_ptr = new LocalAssemblerData<GlobalMatrix, GlobalVector>;
+        data_ptr = new LocalAssemblerData;
         data_ptr->init(e, local_matrix_size, example._localA, example._localRhs);
     }
 
diff --git a/Tests/NumLib/TestExtrapolation.cpp b/Tests/NumLib/TestExtrapolation.cpp
index 9e509a690683388cc601bf3ca1e8a274897bdee0..afe1fb2667dea4e4f8507ab02a2315fcaa068e20 100644
--- a/Tests/NumLib/TestExtrapolation.cpp
+++ b/Tests/NumLib/TestExtrapolation.cpp
@@ -69,9 +69,8 @@ enum class IntegrationPointValue
     DerivedQuantity // a quantity computed for each integration point on-the-fly
 };
 
-template<typename GlobalMatrix, typename GlobalVector>
 class LocalAssemblerDataInterface
-        : public NumLib::Extrapolatable<GlobalVector, IntegrationPointValue>
+        : public NumLib::Extrapolatable<IntegrationPointValue>
 {
 public:
     virtual void interpolateNodalValuesToIntegrationPoints(
@@ -81,11 +80,9 @@ public:
 
 template<typename ShapeFunction,
          typename IntegrationMethod,
-         typename GlobalMatrix,
-         typename GlobalVector,
          unsigned GlobalDim>
 class LocalAssemblerData
-        : public LocalAssemblerDataInterface<GlobalMatrix, GlobalVector>
+        : public LocalAssemblerDataInterface
 {
     using ShapeMatricesType = ShapeMatrixPolicyType<ShapeFunction, GlobalDim>;
     using ShapeMatrices = typename ShapeMatricesType::ShapeMatrices;
@@ -150,17 +147,17 @@ private:
 class TestProcess
 {
 public:
-    using LocalAssembler = LocalAssemblerDataInterface<GlobalMatrix, GlobalVector>;
+    using LocalAssembler = LocalAssemblerDataInterface;
     using GlobalAssembler = NumLib::VectorMatrixAssembler<
-        GlobalMatrix, GlobalVector, LocalAssembler,
+        LocalAssembler,
         // The exact tag does not matter here.
         NumLib::ODESystemTag::FirstOrderImplicitQuasilinear>;
 
     using ExtrapolatorInterface =
-        NumLib::Extrapolator<GlobalVector, IntegrationPointValue, LocalAssembler>;
+        NumLib::Extrapolator<IntegrationPointValue, LocalAssembler>;
     using ExtrapolatorImplementation =
         NumLib::LocalLinearLeastSquaresExtrapolator<
-            GlobalVector, IntegrationPointValue, LocalAssembler>;
+            IntegrationPointValue, LocalAssembler>;
 
     TestProcess(MeshLib::Mesh const& mesh, unsigned const integration_order)
         : _integration_order(integration_order)
@@ -235,7 +232,7 @@ private:
     {
         using LocalDataInitializer = ProcessLib::LocalDataInitializer<
             LocalAssembler, LocalAssemblerData,
-            GlobalMatrix, GlobalVector, GlobalDim>;
+            GlobalDim>;
 
         _local_assemblers.resize(mesh.getNumberOfElements());
 
diff --git a/Tests/NumLib/TestSerialLinearSolver.cpp b/Tests/NumLib/TestSerialLinearSolver.cpp
index cc495b12b777ed9d9bf9140e6ae805529e3db696..14fac011b9f7f0800a952c516d8609fa443e9551 100644
--- a/Tests/NumLib/TestSerialLinearSolver.cpp
+++ b/Tests/NumLib/TestSerialLinearSolver.cpp
@@ -73,7 +73,7 @@ TEST(NumLibSerialLinearSolver, Steady2DdiffusionQuadElem)
     auto x = MathLib::MatrixVectorTraits<GlobalVector>::newInstance(ms);
     // TODO no setZero() for rhs, x?
 
-    using LocalAssembler = Example::LocalAssemblerData<GlobalMatrix, GlobalVector>;
+    using LocalAssembler = Example::LocalAssemblerData;
     // Initializer of the local assembler data.
     std::vector<LocalAssembler*> local_assembler_data;
     local_assembler_data.resize(ex1.msh->getNumberOfElements());
@@ -87,7 +87,7 @@ TEST(NumLibSerialLinearSolver, Steady2DdiffusionQuadElem)
 
         auto const num_local_dof = local_to_global_index_map.getNumElementDOF(id);
 
-        Example::initializeLocalData<GlobalMatrix, GlobalVector>(
+        Example::initializeLocalData(
                     item, item_data, num_local_dof, ex1);
     };
 
@@ -100,7 +100,7 @@ TEST(NumLibSerialLinearSolver, Steady2DdiffusionQuadElem)
     // TODO in the future use simpler NumLib::ODESystemTag
     // Local and global assemblers.
     typedef NumLib::VectorMatrixAssembler<
-            GlobalMatrix, GlobalVector, LocalAssembler,
+            LocalAssembler,
             NumLib::ODESystemTag::FirstOrderImplicitQuasilinear> GlobalAssembler;
 
     GlobalAssembler assembler(local_to_global_index_map);