diff --git a/dune-solvers/iterationsteps/mmgstep.cc b/dune-solvers/iterationsteps/mmgstep.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c8d38738a1546e29a9179bcc7663bd9eaf122e7a
--- /dev/null
+++ b/dune-solvers/iterationsteps/mmgstep.cc
@@ -0,0 +1,256 @@
+#include <dune/ag-common/transferoperators/truncatedmgtransfer.hh>
+#include <dune/ag-common/projectedblockgsstep.hh>
+#include <dune/ag-common/computeenergy.hh>
+
+
+#ifdef HAVE_IPOPT
+#include <dune/ag-common/quadraticipopt.hh>
+#endif
+
+template <class OperatorType, class DiscFuncType>
+void MonotoneMGStep<OperatorType, DiscFuncType>::
+preprocess()
+{
+    // Call preprocess of the base class
+    MultigridStep<OperatorType,DiscFuncType>::preprocess();
+
+    // Then specify the subset of entries to be reassembled at each iteration
+    recompute_.resize(this->numLevels_);
+    recompute_[recompute_.size()-1] = (*hasObstacle_)[recompute_.size()-1];
+    for (int i=this->mgTransfer_.size()-1; i>=0; i--)
+        this->mgTransfer_[i]->restrictScalarBitField(recompute_[i+1], recompute_[i]);
+        
+
+    for (size_t i=0; i<this->mgTransfer_.size(); i++)
+        dynamic_cast<TruncatedMGTransfer<DiscFuncType>*>(this->mgTransfer_[i])->recompute_ = &recompute_[i];
+    
+    // /////////////////////////////////////////////
+    //   Set up base solver
+    // /////////////////////////////////////////////
+    if (typeid(*this->basesolver_) == typeid(LoopSolver<DiscFuncType>)) {
+
+        LoopSolver<DiscFuncType>* loopBaseSolver = dynamic_cast<LoopSolver<DiscFuncType>* > (this->basesolver_);
+     
+        typedef ProjectedBlockGSStep<OperatorType, DiscFuncType> SmootherType;
+        
+        dynamic_cast<SmootherType*>(loopBaseSolver->iterationStep_)->hasObstacle_ = &(*hasObstacle_)[0];
+        dynamic_cast<SmootherType*>(loopBaseSolver->iterationStep_)->obstacles_   = &(*obstacles_)[0];
+        
+#ifdef HAVE_IPOPT
+    } else if (typeid(*this->basesolver_) == typeid(QuadraticIPOptSolver<OperatorType,DiscFuncType>)) {
+
+        QuadraticIPOptSolver<OperatorType,DiscFuncType>* ipoptBaseSolver = dynamic_cast<QuadraticIPOptSolver<OperatorType,DiscFuncType>*> (this->basesolver_);
+     
+        ipoptBaseSolver->obstacles_   = (this->numLevels_ == 1) ? &(*obstacles_)[0]   : NULL;
+#endif
+    } else {
+        DUNE_THROW(SolverError, "You can't use " << typeid(*this->basesolver_).name()
+                   << " as a base solver for contact problems!");
+    }
+}
+
+template<class OperatorType, class DiscFuncType>
+void MonotoneMGStep<OperatorType, DiscFuncType>::nestedIteration()
+{
+    for (int i=this->numLevels_-1; i>0; i--) {
+
+        Dune::BitSetVector<dim> dummy(this->rhs_[i].size(), false);
+
+        // /////////////////////////////
+        //   Restrict obstacles
+        // //////////////////////////////
+        obstacleRestrictor_->restrict((*obstacles_)[i], 
+                                    (*obstacles_)[i-1],
+                                    (*hasObstacle_)[i], 
+                                    (*hasObstacle_)[i-1],
+                                    *this->mgTransfer_[i-1],
+                                    dummy);
+
+        // Restrict right hand side
+        this->mgTransfer_[i-1]->restrict(this->rhs_[i], this->rhs_[i-1]);
+    }
+
+    for (this->level_ = 0; this->level_<this->numLevels_-1; this->level_++) {
+
+        // If we start from an infeasible configuration, the restricted
+        // obstacles may be inconsistent.  We do an ad hoc correction here.
+        // The true obstacles on the maxlevel are not touched.
+        for (int i=0; i<(*obstacles_)[this->level_].size(); i++) {
+            BoxConstraint<field_type,dim>& cO = (*obstacles_)[this->level_][i];
+            for (int j=0; j<dim; j++)
+                if (cO.lower(j) > cO.upper(j))
+                    cO.lower(j) = cO.upper(j) = (cO.lower(j) + cO.upper(j)) / 2;
+        }
+        
+        std::cout << "Nested iteration on level " << this->level_ << std::endl;
+        iterate();
+        iterate();
+        //std::cout << this->x_[this->level_] << std::endl;
+        this->mgTransfer_[this->level_]->prolong(this->x_[this->level_], this->x_[this->level_+1]);
+        
+    }
+     
+}
+
+
+template <class OperatorType, class DiscFuncType>
+void MonotoneMGStep<OperatorType, DiscFuncType>::iterate()
+{
+    this->preprocessCalled = false;
+
+    int& level = this->level_;
+
+    // Define references just for ease of notation
+    std::vector<const OperatorType*>& mat = this->mat_;
+    std::vector<DiscFuncType>& x   = this->x_;
+    std::vector<DiscFuncType>& rhs = this->rhs_;
+    std::vector<std::vector<BoxConstraint<field_type,dim> > >& obstacles = *this->obstacles_;
+
+    Dune::BitSetVector<dim> critical(x[level].size(), false);
+
+    // Solve directly if we're looking at the coarse problem
+    if (level == 0) {
+
+        this->basesolver_->solve();
+
+        // Determine critical nodes  (only for debugging)
+        const double eps = 1e-12;
+        for (int i=0; i<obstacles[level].size(); i++) {
+            
+            for (int j=0; j<dim; j++) {
+                
+                if (obstacles[level][i].lower(j) >= x[level][i][j] - eps
+                    || obstacles[level][i].upper(j) <= x[level][i][j] + eps) {
+                    critical[i][j] = true;
+                }
+                
+            }
+        }
+
+    } else {
+    
+        // Presmoothing
+        this->presmoother_->setProblem(*(this->mat_[level]), x[level], rhs[level]);
+        dynamic_cast<ProjectedBlockGSStep<OperatorType, DiscFuncType>*>(this->presmoother_)->obstacles_ = &(obstacles[level]);
+        dynamic_cast<ProjectedBlockGSStep<OperatorType, DiscFuncType>*>(this->presmoother_)->hasObstacle_ = &((*hasObstacle_)[level]);
+        this->presmoother_->ignoreNodes_ = this->ignoreNodesHierarchy_[level];
+        
+        for (int i=0; i<this->nu1_; i++)
+            this->presmoother_->iterate();
+        
+        // First, a backup of the obstacles for postsmoothing
+        std::vector<BoxConstraint<field_type,dim> > obstacleBackup = obstacles[level];
+        
+        // Compute defect obstacles
+        for (int i=0; i<(*obstacles_)[level].size(); i++)
+            obstacles[level][i] -= x[level][i];
+        
+        // ///////////////////////
+        //    Truncation
+        // ///////////////////////
+        
+        // Determine critical nodes
+        const double eps = 1e-12;
+        for (int i=0; i<obstacles[level].size(); i++) {
+            
+            for (int j=0; j<dim; j++) {
+                
+                if (obstacles[level][i].lower(j) >= -eps
+                    || obstacles[level][i].upper(j) <= eps) {
+                    critical[i][j] = true;
+                }
+                
+            }
+        }
+        
+     
+        // Restrict stiffness matrix
+        dynamic_cast<TruncatedMGTransfer<DiscFuncType>*>(this->mgTransfer_[level-1])
+            ->galerkinRestrict(*(mat[level]), *(const_cast<OperatorType*>(mat[level-1])), critical);
+        
+        // Restrict obstacles
+        obstacleRestrictor_->restrict(obstacles[level], obstacles[level-1],
+                                    (*hasObstacle_)[level], (*hasObstacle_)[level-1],
+                                    *this->mgTransfer_[level-1],
+                                    critical);
+        
+        // //////////////////////////////////////////////////////////////////////
+        // Restriction:  fineResiduum = rhs[level] - mat[level] * x[level];
+        // //////////////////////////////////////////////////////////////////////
+        DiscFuncType fineResidual = rhs[level];
+        mat[level]->mmv(x[level], fineResidual);
+
+        // restrict residual
+        dynamic_cast<TruncatedMGTransfer<DiscFuncType>*>(this->mgTransfer_[level-1])->restrict(fineResidual, rhs[level-1], critical);
+        
+        // Choose all zeros as the initial correction
+        x[level-1] = 0;
+        
+        // ///////////////////////////////////////
+        // Recursively solve the coarser system
+        // ///////////////////////////////////////
+        level--;
+        for (int i=0; i<this->mu_; i++)
+            iterate();
+        level++;
+        
+        // ////////////////////////////////////////
+        // Prolong
+        // ////////////////////////////////////////
+        
+        // add correction to the presmoothed solution
+        DiscFuncType tmp;
+        dynamic_cast<TruncatedMGTransfer<DiscFuncType>*>(this->mgTransfer_[level-1])->prolong(x[level-1], tmp, critical);
+        x[level] += tmp;
+        
+        // restore the true (non-defect) obstacles
+        obstacles[level] = obstacleBackup;
+        
+#ifndef NDEBUG
+        // Debug: is the current iterate really admissible?
+        for (int i=0; i<obstacles[level].size(); i++)
+            for (int j=0; j<DiscFuncType::block_type::size; j++)
+                if (x[level][i][j] < obstacles[level][i].lower(j)
+                    || x[level][i][j] > obstacles[level][i].upper(j)) {
+                    
+                    std::cout << "Obstacle disregarded!\n";
+                    std::cout << x[level][i] << std::endl << obstacles[level][i] << std::endl;
+                    printf("level: %d   index: %d   komponent: %d\n", level, i, j);
+                    printf("is %s critical\n", (critical[i][j]) ? "" : "not");
+                }
+#endif
+        
+        // Postsmoothing
+        this->postsmoother_->setProblem(*(mat[level]), x[level], rhs[level]);
+        dynamic_cast<ProjectedBlockGSStep<OperatorType, DiscFuncType>*>(this->postsmoother_)->obstacles_ = &obstacles[level];
+        dynamic_cast<ProjectedBlockGSStep<OperatorType, DiscFuncType>*>(this->postsmoother_)->hasObstacle_ = &((*hasObstacle_)[level]);
+        this->postsmoother_->ignoreNodes_ = this->ignoreNodesHierarchy_[level];
+        
+        for (int i=0; i<this->nu2_; i++)
+            this->postsmoother_->iterate();
+        
+    }
+    
+    // ////////////////////////////////////////////////////////////////////
+    //   Track the number of critical nodes found during this iteration
+    // ////////////////////////////////////////////////////////////////////
+    if (level==this->numLevels_-1 && this->verbosity_==NumProc::FULL) {
+            
+        std::cout << critical.count() << " critical nodes found on level " << level;
+            
+        int changes = 0;
+        for (unsigned int i=0; i<oldCritical.size(); i++)
+            for (int j=0; j<dim; j++)
+                if (oldCritical[i][j] !=critical[i][j])
+                    changes++;
+        
+        std::cout << ", and " << changes << " changes." << std::endl;
+        oldCritical = critical;
+    }
+
+    // Debug: output energy
+    if (level==this->numLevels_-1 && this->verbosity_==NumProc::FULL)
+        std::cout << "Total energy: " 
+                  << std::setprecision(10) << computeEnergy(*mat[level], x[level], rhs[level]) << std::endl;
+
+}
diff --git a/dune-solvers/iterationsteps/mmgstep.hh b/dune-solvers/iterationsteps/mmgstep.hh
new file mode 100644
index 0000000000000000000000000000000000000000..f704fe9a7ee7424cf1895227153977594eac5fe9
--- /dev/null
+++ b/dune-solvers/iterationsteps/mmgstep.hh
@@ -0,0 +1,78 @@
+#ifndef DUNE_MONOTONE_MULTIGRID_STEP_HH
+#define DUNE_MONOTONE_MULTIGRID_STEP_HH
+
+#include <dune/ag-common/multigridstep.hh>
+#include <dune/ag-common/transferoperators/multigridtransfer.hh>
+#include <dune/ag-common/projectedblockgsstep.hh>
+#include <dune/ag-common/boxconstraint.hh>
+#include <dune/ag-common/obstaclerestrictor.hh>
+
+/** \brief The general monotone multigrid solver
+*/
+template<class OperatorType, class DiscFuncType>
+class MonotoneMGStep : public MultigridStep<OperatorType, DiscFuncType>
+{
+    
+    static const int dim = DiscFuncType::block_type::dimension;
+    
+    typedef typename DiscFuncType::field_type field_type;
+    
+public:
+    
+    MonotoneMGStep() {} 
+    
+    MonotoneMGStep(int numLevels) {
+        this->numLevels_ = numLevels;
+        this->mat_.resize(numLevels);
+        this->x_.resize(numLevels);
+        this->rhs_.resize(numLevels);
+    }
+    
+    
+    MonotoneMGStep(const OperatorType& mat, 
+                   DiscFuncType& x, 
+                   DiscFuncType& rhs, int numLevels)
+        : MultigridStep<OperatorType, DiscFuncType>(mat, x, rhs, numLevels)
+    {
+        oldCritical.resize(x.size(), false);
+    }
+    
+    virtual ~MonotoneMGStep() {
+        // Delete the coarse grid matrices we created
+        for (int i=0; i<this->numLevels_-1; i++) {
+            delete(this->mat_[i]);
+            this->mat_[i] = NULL;
+        }
+    }
+    
+    virtual void setProblem(const OperatorType& mat, 
+                            DiscFuncType& x, 
+                            DiscFuncType& rhs,
+                            int numLevels) 
+    {
+        MultigridStep<OperatorType, DiscFuncType>::setProblem(mat,x,rhs, numLevels);
+        oldCritical.resize(x.size()*dim, false);
+    }
+    
+    virtual void iterate();
+    
+    virtual void preprocess();
+    
+    virtual void nestedIteration();
+    
+    ObstacleRestrictor<DiscFuncType>* obstacleRestrictor_;
+    
+    std::vector<Dune::BitSetVector<1> >* hasObstacle_;
+    
+    std::vector<Dune::BitSetVector<1> > recompute_;
+    
+    std::vector<std::vector<BoxConstraint<field_type,dim> > >* obstacles_;
+    
+    // Needed to track changes in the set of critical bits, and allows
+    // to check which dofs where critical after the last iteration
+    Dune::BitSetVector<dim> oldCritical;
+};
+
+#include "mmgstep.cc"
+
+#endif