diff --git a/dune/contact/solvers/primaldualsemismoothnewtonsolver.cc b/dune/contact/solvers/primaldualsemismoothnewtonsolver.cc
new file mode 100644
index 0000000000000000000000000000000000000000..01436e76b2dc3b3d5c09a7079e2f5b87a39ca821
--- /dev/null
+++ b/dune/contact/solvers/primaldualsemismoothnewtonsolver.cc
@@ -0,0 +1,198 @@
+// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// vi: set ts=4 sw=2 et sts=2:
+
+#include <limits>
+#include <dune/common/timer.hh>
+#include <dune/istl/io.hh>
+
+#include <dune/solvers/solvers/quadraticipopt.hh>
+#include <dune/solvers/common/boxconstraint.hh>
+#include <dune/istl/preconditioners.hh>
+#include <dune/istl/umfpack.hh>
+#include <dune/istl/solvers.hh>
+
+namespace Dune {
+namespace Contact {
+
+template<class ProblemType>
+void PrimalDualSemiSmoothNewtonSolver<ProblemType>::solve()
+{
+  auto& iterates = *iterates_;
+
+  size_t totalSize(0);
+  for (size_t i=0; i<iterates.size(); i++)
+    totalSize += iterates[i].size();
+
+  // If no Dirichlet conditions are prescribed we have to setup the initial correction
+  if (correction_.size() != dim*totalSize) {
+    correction_.resize(dim*totalSize);
+    correction_ = 0;
+  }
+
+  problem_->contactAssembler()->getContactCouplings()[0]->path_ = debugPath_;
+  problem_->contactAssembler()->getContactCouplings()[0]->it_ = -1;
+
+  // compute energy infeasibility of initial iterate and build the contact mapping
+  energy_ = problem_->energy(iterates);
+  infeasibility_ = problem_->infeasibility(iterates);
+
+  // if there are non-homogeneous Dirichlet conditions,
+  // then we have to adjust the initial infeasibility
+  infeasibility_ = std::max(infeasibility_, correction_.infinity_norm());
+
+  if (this->verbosity_ == Solver::REDUCED or this->verbosity_== Solver::FULL)
+    std::cout<< " Initial energy: " << energy_
+             << " and infeasibility: " << infeasibility_ << std::endl;
+
+  // initialise the iteration counter and error
+  int it(0);
+  field_type error = std::numeric_limits<field_type>::max();
+
+  // assemble linearisations of initial deformation
+  problem_->assembleLinearisations(iterates);
+  // initialise Lagrange multiplier with zero
+  lagrangeMultiplier_.resize(problem_->getConstraints().size());
+ // lagrangeMultiplier_ = -1000;
+  lagrangeMultiplier_ = problem_->leastSquaresLagrangeMultiplier();
+
+  problem_->initialiseEmptyActiveSet();
+
+  // create IpOpt solver
+  QuadraticIPOptSolver<ScalarMatrix, ScalarVector> ipoptSolver(localTol_, localMaxIterations_,
+                                                            Solver::QUIET, linearSolverType_);
+
+  bool activeSetChanged(true);
+  // Loop until desired tolerance or maximum number of iterations is reached
+  for (; activeSetChanged or (it<this->maxIterations_ and (error>this->tolerance_ or std::isnan(error))); it++)
+  {
+
+    if (this->verbosity_ == Solver::REDUCED or this->verbosity_== Solver::FULL)
+      std::cout << "************************\n"
+                << "*  PriDual iteration " << it << "\n"
+                << "************************\n";
+
+    problem_->contactAssembler()->getContactCouplings()[0]->path_ = debugPath_;
+    problem_->contactAssembler()->getContactCouplings()[0]->it_ = it;
+
+    // Measure norm of the old iterate
+    field_type oldNorm(0);
+    for (size_t i=0; i<iterates_->size(); i++)
+      oldNorm += (*errorNorms_[i])(iterates[i]);
+
+    // assemble the local quadratic problem
+    problem_->assemblePrimalDualProblem(lagrangeMultiplier_);
+
+    ScalarVector priDualIterate = combineVector(correction_, ScalarVector(lagrangeMultiplier_.size(),0));
+
+    field_type newEnergy;
+    field_type newInfeasibility;
+    std::vector<BlockVector> newIterates;
+
+    // setup the linearised problem
+    problem_->addHardDirichlet(correction_, dirichletNodes_);
+    ipoptSolver.setProblem(problem_->priDualMatrix(), priDualIterate, problem_->priDualRhs());
+
+    Timer timer;
+
+#if HAVE_SUITESPARSE_UMFPACK
+    UMFPack<ScalarMatrix> umfPack(problem_->priDualMatrix(), 0);
+    std::cout<<"took "<<timer.elapsed()<<" to decompose matrix\n";
+
+    // solve
+    InverseOperatorResult res;
+    auto copyRhs = problem_->priDualRhs();
+    umfPack.apply(priDualIterate, copyRhs, res);
+    if (!res.converged)
+      std::cout<<"Warning: Not converged! Reduction "<<res.reduction<<" iterations "<<res.iterations<<std::endl;
+#endif
+
+    auto residual = problem_->priDualRhs();
+    problem_->priDualMatrix().mmv(priDualIterate, residual);
+    std::cout<<"Residual " << residual.infinity_norm()<<std::endl;
+
+
+
+    if (this->verbosity_ == Solver::FULL)
+      std::cout << "Solved local problem in " << timer.stop() << " secs\n";
+
+    std::tie(correction_, lagrangeMultiplier_) = decomposeVector(priDualIterate);
+
+    // new iterate
+    int counter(0);
+    newIterates = iterates;
+    for (size_t i=0; i<newIterates.size(); i++)
+      for (size_t j=0; j<newIterates[i].size(); j++)
+        for (int k = 0; k < dim; k++)
+          newIterates[i][j][k] += correction_[counter++][0];
+
+    std::cout<<"iterates inf "<<newIterates[0].infinity_norm() << " and " << newIterates[1].infinity_norm()<<std::endl;
+    // compute new energy and infeasibility
+    newEnergy = problem_->energy(newIterates);
+    newInfeasibility = problem_->infeasibility(newIterates);
+
+    // update the active set, needs to be called after the deformation is updated
+    // which is done in "infeasibility"
+    problem_->updateActiveSetAndLagrangeMultiplier(lagrangeMultiplier_);
+    activeSetChanged = (problem_->changedActiveNodes() > 0);
+
+    if (this->verbosity_ == Solver::FULL) {
+      std::cout << "Active nodes changed " << problem_->changedActiveNodes()
+                << " active now " << problem_->getActiveSet().count() << " \n";
+      std::cout << "New energy of potential iterate " << newEnergy
+                << " and infeasibility " << newInfeasibility << std::endl;
+    }
+
+    // update the Newton problem
+    problem_->assembleLinearisations(newIterates);
+
+    // if the correction is very small stop the loop
+    field_type infNorm = correction_.infinity_norm();
+    if(this->verbosity_==Solver::FULL)
+      std::cout << "Correction infinite norm: " << infNorm << std::endl;
+
+    // compute error estimate
+    std::vector<BlockVector> corrections(iterates.size());
+    counter = 0;
+    for (size_t i=0; i<iterates.size();i++) {
+      corrections[i].resize(iterates[i].size());
+      for (size_t j=0; j<corrections[i].size(); j++)
+        for (int k = 0; k < dim; k++)
+          corrections[i][j][k] = correction_[counter++][0];
+    }
+
+    auto residuals = problem_->residuals(lagrangeMultiplier_);
+    for (int k=0; k<2; k++)
+      for (size_t j=0; j<residuals[k].size();j++)
+        if (dN_[k][j].any())
+          residuals[k][j] = 0;
+
+    field_type absError = 0;
+    for (size_t i=0; i < corrections.size(); ++i)
+      absError += (*errorNorms_[i])(residuals[i]);
+    absError += newInfeasibility;
+
+    error = absError;
+    if (this->verbosity_ == Solver::REDUCED or this->verbosity_== Solver::FULL)
+      std::cout << "In filter iteration "<< it
+                << ", absolut norm of correction : " << absError
+                << " relative error " << error
+                << "\nEnergy " << newEnergy
+                << " and infeasibility " << newInfeasibility << std::endl;
+
+    // update iterates, energy and infeasibility
+    iterates = newIterates;
+    energy_ = newEnergy;
+    infeasibility_ = newInfeasibility;
+
+    // set correction to zero to remove any Dirichlet values
+    correction_  = 0;
+
+  } // end of filter loop
+
+  if (this->verbosity_ == Solver::REDUCED or this->verbosity_== Solver::FULL)
+    std::cout << "Solved problem in " << it << " iterations with final energy " << energy_
+              << " and infeasibility " << infeasibility_ << std::endl;
+}
+
+} /* namespace Contact */
+} /* namespace Dune */
diff --git a/dune/contact/solvers/primaldualsemismoothnewtonsolver.hh b/dune/contact/solvers/primaldualsemismoothnewtonsolver.hh
new file mode 100644
index 0000000000000000000000000000000000000000..741e6020f3a0b693c6ff9242780b887ba7313e32
--- /dev/null
+++ b/dune/contact/solvers/primaldualsemismoothnewtonsolver.hh
@@ -0,0 +1,168 @@
+// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// vi: set ts=4 sw=2 et sts=2:
+#ifndef DUNE_CONTACT_SOLVERS_PRIMAL_DUAL_SEMI_SMOOTH_NEWTON_SOLVER_HH
+#define DUNE_CONTACT_SOLVERS_PRIMAL_DUAL_SEMI_SMOOTH_NEWTON_SOLVER_HH
+
+#include <dune/common/bitsetvector.hh>
+#include <dune/common/parametertree.hh>
+
+#include <dune/solvers/norms/norm.hh>
+#include <dune/solvers/solvers/iterativesolver.hh>
+
+namespace Dune {
+namespace Contact {
+
+/** \brief Primal-Dual Semi-Smooth Newton method
+ *
+ *  See Popp, Wall et al. 2011
+*/
+template <class Problem>
+class PrimalDualSemiSmoothNewtonSolver
+    : public Dune::Solvers::IterativeSolver<typename Problem::BlockVector> {
+
+  using BlockVector = typename Problem::BlockVector;
+  using field_type = typename Dune::FieldTraits<BlockVector>::field_type;
+  using ScalarVector = typename Problem::ScalarVector;
+  using ScalarMatrix = typename Problem::ScalarMatrix;
+  using Matrix = typename Problem::MatrixType;
+  using JacobianType = typename Problem::JacobianType;
+
+  static constexpr int dim = BlockVector::block_type::dimension;
+  using Base = Dune::Solvers::IterativeSolver<BlockVector>;
+
+public:
+  PrimalDualSemiSmoothNewtonSolver(
+      const Dune::ParameterTree& config, std::vector<BlockVector>& x,
+      Problem& problem,
+      const std::vector<std::shared_ptr<Norm<BlockVector> > >& errorNorms)
+      : Base(config.get<field_type>("tolerance"),
+             config.get<int>("maxIterations"),
+             config.get("verbosity", NumProc::FULL)) {
+    setupParameter(config);
+    setProblem(x, problem);
+    setErrorNorm(errorNorms);
+
+    size_t totalSize(0);
+    for (size_t i = 0; i < x.size(); i++)
+      totalSize += x[i].size();
+
+    dirichletNodes_.resize(totalSize*dim, false);
+  }
+
+  //! Setup the trust region parameter from a config file
+  void setupParameter(const Dune::ParameterTree& config) {
+    localTol_ = config.get<field_type>("localTolerance");
+    localMaxIterations_ = config.get<int>("localMaxIterations");
+    linearSolverType_ = config.get("linearSolverType", "");
+  }
+
+  /** \brief Set the Dirichlet values.
+     *
+     * The Dirichlet conditions are enforced during the solution
+     * of the first linearised problem rather than modifying the
+     * initial iterate of the filter scheme. This avoids possible problems
+     *in the energy evaluation due to inverted elements.
+     * Hence Dirichlet values for the correction must be handed over!
+     */
+  void setDirichletValues(
+      const std::vector<BlockVector>& dirichletValues,
+      const std::vector<Dune::BitSetVector<dim> >& dirichletNodes) {
+    // only adjust correction Dirichlet conditions are prescribed
+    correction_.resize(dirichletNodes_.size());
+    correction_ = 0;
+    int counter(0);
+    for (size_t i = 0; i < dirichletNodes.size(); i++)
+      for (size_t j = 0; j < dirichletNodes[i].size(); counter++, j++)
+        if (dirichletNodes[i][j].any())
+          for (int k = 0; k < dim; k++) {
+            dirichletNodes_[dim*counter + k] = dirichletNodes[i][j][k];
+            correction_[dim*counter + k][0] = dirichletValues[i][j][k];
+          }
+
+    dN_ = dirichletNodes;
+  }
+
+  //! Set initial iterate and the problem class
+  void setProblem(std::vector<BlockVector>& x, Problem& problem) {
+    iterates_ = Dune::stackobject_to_shared_ptr<std::vector<BlockVector> >(x);
+    problem_ = Dune::stackobject_to_shared_ptr<Problem>(problem);
+  }
+
+  //! Set the error norms to be used within the filter method
+  void
+  setErrorNorm(const std::vector<std::shared_ptr<Norm<BlockVector> > >& errorNorms) {
+    errorNorms_ = errorNorms;
+  }
+
+  //! Solve the problem
+  void solve();
+
+  //! Return solution object
+  const std::vector<BlockVector>& getSol() const { return *iterates_; }
+
+  std::string debugPath_;
+  std::vector<Dune::BitSetVector<dim> > dN_;
+
+private:
+  ScalarVector combineVector(const ScalarVector& primal, const ScalarVector& dual) const {
+    size_t index(0);
+    ScalarVector combinedVector(primal.size() + dual.size());
+    for (size_t i = 0; i < primal.size(); i++)
+      combinedVector[index++] = primal[i];
+
+    for (size_t i = 0; i < dual.size(); i++)
+      combinedVector[index++] = dual[i];
+    return combinedVector;
+  }
+
+  std::tuple<ScalarVector, ScalarVector> decomposeVector(const ScalarVector& primalDual) const {
+    size_t index(0);
+    ScalarVector primal(correction_.size());
+    for (size_t i = 0; i < primal.size(); i++)
+        primal[i] = primalDual[index++];
+
+    ScalarVector dual(lagrangeMultiplier_.size());
+    for (size_t i = 0; i < dual.size(); i++)
+      dual[i] = primalDual[index++];
+    return std::tie(primal, dual);
+  }
+
+  //! The iterates of the global scheme
+  std::shared_ptr<std::vector<BlockVector> > iterates_;
+
+  ScalarVector lagrangeMultiplier_;
+
+  //! Dirichlet degrees of freedom
+  Dune::BitSetVector<1> dirichletNodes_;
+
+  //! The iterate for the local problems, containing possible Dirichlet values
+  ScalarVector correction_;
+
+  //! The problem type, providing methods to assemble the nonlinear and
+  //! linearised
+  //! energy and constraints
+  std::shared_ptr<Problem> problem_;
+
+  //! The norms to estimate the errors
+  std::vector<std::shared_ptr<Norm<BlockVector> > > errorNorms_;
+
+  //! Tolerance for the solution of the local problem
+  field_type localTol_;
+  //! Max iterations for the solution of the local problem
+  int localMaxIterations_;
+
+  //! Store the old energy
+  field_type energy_;
+  //! Store the infeasibility of the old iterate
+  field_type infeasibility_;
+
+  //! The linear solver to be used within IpOpt
+  std::string linearSolverType_;
+};
+
+} /* namespace Contact */
+} /* namespace Dune */
+
+#include "primaldualsemismoothnewtonsolver.cc"
+
+#endif