diff --git a/src/spatial-solving/fixedpointiterator.hh b/src/spatial-solving/fixedpointiterator.hh
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ba31c81ec07ac10ae00c20e5813f39cf40bea106 100644
--- a/src/spatial-solving/fixedpointiterator.hh
+++ b/src/spatial-solving/fixedpointiterator.hh
@@ -0,0 +1,53 @@
+#ifndef SRC_SPATIAL_SOLVING_FIXEDPOINTITERATOR_HH
+#define SRC_SPATIAL_SOLVING_FIXEDPOINTITERATOR_HH
+
+#include <memory>
+
+#include <dune/common/parametertree.hh>
+
+#include <dune/solvers/norms/norm.hh>
+#include <dune/solvers/solvers/solver.hh>
+
+struct FixedPointIterationCounter {
+  size_t iterations = 0;
+  size_t multigridIterations = 0;
+
+  void operator+=(FixedPointIterationCounter const &other);
+};
+
+std::ostream &operator<<(std::ostream &stream,
+                         FixedPointIterationCounter const &fpic);
+
+template <class Factory, class Updaters, class ErrorNorm>
+class FixedPointIterator {
+  using ScalarVector = typename Updaters::StateUpdater::ScalarVector;
+  using Vector = typename Factory::Vector;
+  using Matrix = typename Factory::Matrix;
+  using ConvexProblem = typename Factory::ConvexProblem;
+  using BlockProblem = typename Factory::BlockProblem;
+  using Nonlinearity = typename ConvexProblem::NonlinearityType;
+
+public:
+  FixedPointIterator(Factory &factory, Dune::ParameterTree const &parset,
+                     std::shared_ptr<Nonlinearity> globalFriction,
+                     ErrorNorm const &errorNorm_);
+
+  FixedPointIterationCounter run(Updaters updaters,
+                                 Matrix const &velocityMatrix,
+                                 Vector const &velocityRHS,
+                                 Vector &velocityIterate);
+
+private:
+  std::shared_ptr<typename Factory::Step> step_;
+  Dune::ParameterTree const &parset_;
+  std::shared_ptr<Nonlinearity> globalFriction_;
+
+  size_t fixedPointMaxIterations_;
+  double fixedPointTolerance_;
+  double lambda_;
+  size_t velocityMaxIterations_;
+  double velocityTolerance_;
+  Solver::VerbosityMode verbosity_;
+  ErrorNorm const &errorNorm_;
+};
+#endif
diff --git a/src/spatial-solving/solverfactory.cc b/src/spatial-solving/solverfactory.cc
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3242724ae9a55a360b091f103ae30cd9b8704f76 100644
--- a/src/spatial-solving/solverfactory.cc
+++ b/src/spatial-solving/solverfactory.cc
@@ -0,0 +1,59 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_IPOPT
+#undef HAVE_IPOPT
+#endif
+
+#include <dune/fufem/assemblers/transferoperatorassembler.hh>
+#include <dune/solvers/solvers/solver.hh>
+
+#include "solverfactory.hh"
+
+template <size_t dim, class BlockProblem, class Grid>
+SolverFactory<dim, BlockProblem, Grid>::SolverFactory(
+    Dune::ParameterTree const &parset, Grid const &grid,
+    Dune::BitSetVector<dim> const &ignoreNodes)
+    : baseEnergyNorm(linearBaseSolverStep),
+      linearBaseSolver(&linearBaseSolverStep,
+                       parset.get<size_t>("linear.maxiumumIterations"),
+                       parset.get<double>("linear.tolerance"), &baseEnergyNorm,
+                       Solver::QUIET),
+      transferOperators(grid.maxLevel()),
+      multigridStep(
+          std::make_shared<Step>(linearIterationStep, nonlinearSmoother)) {
+  // linear iteration step
+  linearIterationStep.setMGType(parset.get<int>("linear.cycle"),
+                                parset.get<int>("linear.pre"),
+                                parset.get<int>("linear.post"));
+  linearIterationStep.basesolver_ = &linearBaseSolver;
+  linearIterationStep.setSmoother(&linearPresmoother, &linearPostsmoother);
+
+  // transfer operators
+  for (auto &&x : transferOperators)
+    x = new CompressedMultigridTransfer<Vector>;
+  TransferOperatorAssembler<Grid>(grid)
+      .assembleOperatorPointerHierarchy(transferOperators);
+  linearIterationStep.setTransferOperators(transferOperators);
+
+  // tnnmg iteration step
+  multigridStep->setSmoothingSteps(parset.get<int>("main.pre"),
+                                   parset.get<int>("main.multi"),
+                                   parset.get<int>("main.post"));
+  multigridStep->ignoreNodes_ = &ignoreNodes;
+}
+
+template <size_t dim, class BlockProblem, class Grid>
+SolverFactory<dim, BlockProblem, Grid>::~SolverFactory() {
+  for (auto &&x : transferOperators)
+    delete x;
+}
+
+template <size_t dim, class BlockProblem, class Grid>
+auto SolverFactory<dim, BlockProblem, Grid>::getStep()
+    -> std::shared_ptr<Step> {
+  return multigridStep;
+}
+
+#include "solverfactory_tmpl.cc"
diff --git a/src/spatial-solving/solverfactory.hh b/src/spatial-solving/solverfactory.hh
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8f05a5727ba9602a09b62c3df48f4bb793413bfa 100644
--- a/src/spatial-solving/solverfactory.hh
+++ b/src/spatial-solving/solverfactory.hh
@@ -0,0 +1,48 @@
+#ifndef SRC_SPATIAL_SOLVING_SOLVERFACTORY_HH
+#define SRC_SPATIAL_SOLVING_SOLVERFACTORY_HH
+
+#include <dune/common/bitsetvector.hh>
+#include <dune/common/parametertree.hh>
+
+#include <dune/solvers/iterationsteps/multigridstep.hh>
+#include <dune/solvers/iterationsteps/truncatedblockgsstep.hh>
+#include <dune/solvers/norms/energynorm.hh>
+#include <dune/solvers/solvers/loopsolver.hh>
+#include <dune/solvers/transferoperators/compressedmultigridtransfer.hh>
+#include <dune/tnnmg/iterationsteps/genericnonlineargs.hh>
+#include <dune/tnnmg/iterationsteps/tnnmgstep.hh>
+
+template <size_t dim, class BlockProblemTEMPLATE, class Grid>
+class SolverFactory {
+public:
+  using BlockProblem = BlockProblemTEMPLATE;
+  using ConvexProblem = typename BlockProblem::ConvexProblemType;
+  using Vector = typename BlockProblem::VectorType;
+  using Matrix = typename BlockProblem::MatrixType;
+
+private:
+  using NonlinearSmoother = GenericNonlinearGS<BlockProblem>;
+
+public:
+  using Step =
+      TruncatedNonsmoothNewtonMultigrid<BlockProblem, NonlinearSmoother>;
+
+  SolverFactory(Dune::ParameterTree const &parset, Grid const &grid,
+                Dune::BitSetVector<dim> const &ignoreNodes);
+
+  ~SolverFactory();
+
+  std::shared_ptr<Step> getStep();
+
+private:
+  TruncatedBlockGSStep<Matrix, Vector> linearBaseSolverStep;
+  EnergyNorm<Matrix, Vector> baseEnergyNorm;
+  LoopSolver<Vector> linearBaseSolver;
+  TruncatedBlockGSStep<Matrix, Vector> linearPresmoother;
+  TruncatedBlockGSStep<Matrix, Vector> linearPostsmoother;
+  MultigridStep<Matrix, Vector> linearIterationStep;
+  std::vector<CompressedMultigridTransfer<Vector> *> transferOperators;
+  NonlinearSmoother nonlinearSmoother;
+  std::shared_ptr<Step> multigridStep;
+};
+#endif
diff --git a/src/spatial-solving/solverfactory_tmpl.cc b/src/spatial-solving/solverfactory_tmpl.cc
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..506d9042a6814dab7d4a4ad43647f81262b7091a 100644
--- a/src/spatial-solving/solverfactory_tmpl.cc
+++ b/src/spatial-solving/solverfactory_tmpl.cc
@@ -0,0 +1,22 @@
+#ifndef MY_DIM
+#error MY_DIM unset
+#endif
+
+#include "../explicitgrid.hh"
+#include "../explicitvectors.hh"
+
+#include <dune/tnnmg/nonlinearities/zerononlinearity.hh>
+#include <dune/tnnmg/problem-classes/blocknonlineartnnmgproblem.hh>
+#include <dune/tnnmg/problem-classes/convexproblem.hh>
+
+#include <dune/tectonic/globalfriction.hh>
+#include <dune/tectonic/myblockproblem.hh>
+
+template class SolverFactory<
+    MY_DIM,
+    MyBlockProblem<ConvexProblem<GlobalFriction<Matrix, Vector>, Matrix>>,
+    Grid>;
+template class SolverFactory<
+    MY_DIM, BlockNonlinearTNNMGProblem<ConvexProblem<
+                ZeroNonlinearity<LocalVector, LocalMatrix>, Matrix>>,
+    Grid>;