diff --git a/dune/gfe/Makefile.am b/dune/gfe/Makefile.am index 58ee44949a1078e9bb0bccab8ab14c69981ebce5..2d9e4802931cd7b96a6b7c3f063793c4b810e318 100644 --- a/dune/gfe/Makefile.am +++ b/dune/gfe/Makefile.am @@ -41,6 +41,7 @@ srcinclude_HEADERS = adolcnamespaceinjections.hh \ targetspacertrsolver.hh \ tensorssd.hh \ tensor3.hh \ + trustregionmmgbasesolver.hh \ trustregionsolver.hh \ trustregionsolver.cc \ unitvector.hh \ diff --git a/dune/gfe/trustregionmmgbasesolver.hh b/dune/gfe/trustregionmmgbasesolver.hh new file mode 100644 index 0000000000000000000000000000000000000000..86776e0fed5ead24733c839b2fcdf04e4e0e7b0a --- /dev/null +++ b/dune/gfe/trustregionmmgbasesolver.hh @@ -0,0 +1,187 @@ +// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=8 sw=4 sts=4: +#ifndef DUNE_GFE_TRUSTREGIONMMGBASESOLVER_HH +#define DUNE_GFE_TRUSTREGIONMMGBASESOLVER_HH + +#include <dune/istl/umfpack.hh> + +#include <dune/solvers/solvers/iterativesolver.hh> +#include <dune/solvers/solvers/quadraticipopt.hh> + +/** \brief Base solver for a monotone multigrid solver when used as the inner solver in a trust region method + * + * Monotone multigrid methods solve constrained problems even on the coarsest level. Therefore, the choice + * of possible coarse grid solvers is limited. I have tried both Gauss-Seidel and IPOpt, and both of them + * have not satisfied my. Gauss-Seidel is slow when the coarse grid is not coarse enough. The results computed + * by IPOpt appear to be imprecise, and I didn't get good trust-region convergence. I don't really understand + * this -- it may be a bug in our IPOpt wrapper code. + * Ideally, one would use a direct solver, in particular close to the solution. However, a direct solver may + * not produce admissible corrections. Also, it may produce energy increase. Therefore, the TrustRegionMMGBaseSolver + * does a pragmatic approach: a solution is first computed using UMFPack, disregarding the obstacles. + * If the result is admissible and decreases the energy we keep it. Otherwise we fall back to IPOpt. + */ +template <class MatrixType, class VectorType> +class TrustRegionMMGBaseSolver +: public IterativeSolver<VectorType>, + public CanIgnore<Dune::BitSetVector<VectorType::block_type::dimension> > +{ + typedef typename VectorType::field_type field_type; + + // For complex-valued data + typedef typename Dune::FieldTraits<field_type>::real_type real_type; + + enum {blocksize = VectorType::value_type::dimension}; + typedef Dune::BitSetVector<blocksize> BitVectorType; + +public: + + /** \brief Constructor taking all relevant data */ + TrustRegionMMGBaseSolver(Solver::VerbosityMode verbosity) + : IterativeSolver<VectorType, BitVectorType>(100, // maxIterations + 1e-8, // tolerance + nullptr, + verbosity, + false) // false = use absolute error + {} + + void setProblem(const MatrixType& matrix, + VectorType& x, + const VectorType& rhs) { + matrix_ = &matrix; + x_ = &x; + rhs_ = &rhs; + } + + /** \brief Checks whether all relevant member variables are set + * \exception SolverError if the iteration step is not set up properly + */ + virtual void check() const; + + virtual void preprocess(); + + /** \brief Loop, call the iteration procedure + * and monitor convergence + */ + virtual void solve(); + + //! The quadratic term in the quadratic energy, is assumed to be symmetric + const MatrixType* matrix_; + + //! Vector to store the solution + VectorType* x_; + + //! The linear term in the quadratic energy + const VectorType* rhs_; + + std::vector<BoxConstraint<field_type,blocksize> >* obstacles_; + +}; + + +template <class MatrixType, class VectorType> +void TrustRegionMMGBaseSolver<MatrixType, VectorType>::check() const +{ + // check base class + IterativeSolver<VectorType,BitVectorType>::check(); +} + +template <class MatrixType, class VectorType> +void TrustRegionMMGBaseSolver<MatrixType, VectorType>::preprocess() +{ + //this->iterationStep_->preprocess(); +} + +template <class MatrixType, class VectorType> +void TrustRegionMMGBaseSolver<MatrixType, VectorType>::solve() +{ + MatrixType modifiedStiffness = *matrix_; + VectorType modifiedRhs = *rhs_; + + /////////////////////////////////////////// + // Modify Dirichlet rows + /////////////////////////////////////////// + + for (size_t j=0; j<modifiedStiffness.N(); j++) + { + auto cIt = modifiedStiffness[j].begin(); + auto cEndIt = modifiedStiffness[j].end(); + + for (; cIt!=cEndIt; ++cIt) + { + for (int k=0; k<blocksize; k++) + if ((*this->ignoreNodes_)[j][k]) + { + (*cIt)[k] = 0; + if (cIt.index()==j) + (*cIt)[k][k] = 1.0; + } + + } + + for (int k=0; k<blocksize; k++) + if ((*this->ignoreNodes_)[j][k]) + modifiedRhs[j][k] = 0.0; + } + + ///////////////////////////////////////////////////////////////// + // Solve the system + ///////////////////////////////////////////////////////////////// + Dune::InverseOperatorResult statistics; + Dune::UMFPack<MatrixType> solver(modifiedStiffness); + solver.apply(*x_, modifiedRhs, statistics); + + // Model increase? Then the matrix is not positive definite -- fall back to ipopt solver + // The model decrease is $ m(x) - m(x+s) = -<g,s> - 0.5 <s, Hs> + // Note that rhs = -g + VectorType tmp(x_->size()); + tmp = 0; + matrix_->umv(*x_, tmp); + double modelDecrease = (modifiedRhs*(*x_)) - 0.5 * ((*x_)*tmp); + + if (modelDecrease < 0) + { + std::cout << "Model increase: " << -modelDecrease << ", falling back to slower solver" << std::endl; + + QuadraticIPOptSolver<MatrixType, VectorType> baseSolver; + baseSolver.verbosity_ = NumProc::QUIET; + baseSolver.tolerance_ = 1e-8; + + baseSolver.setProblem(*matrix_, *x_, *rhs_); + baseSolver.obstacles_ = obstacles_; + + baseSolver.solve(); + } + + // Check whether the solution is admissible wrt the obstacles + bool admissible = true; + assert (obstacles_->size() == x_->size()); + for (size_t i=0; i<obstacles_->size(); i++) + { + for (int j=0; j<blocksize; j++) + if ( (*x_)[i][j] > (*obstacles_)[i].upper(j) or (*x_)[i][j] < (*obstacles_)[i].lower(j) ) + { + admissible = false; + break; + } + if (not admissible) + break; + } + + if (not admissible) + { + std::cout << "Inadmissible step, falling back to slower solver" << std::endl; + + QuadraticIPOptSolver<MatrixType, VectorType> baseSolver; + baseSolver.verbosity_ = NumProc::QUIET; + baseSolver.tolerance_ = 1e-8; + + baseSolver.setProblem(*matrix_, *x_, *rhs_); + baseSolver.obstacles_ = obstacles_; + + baseSolver.solve(); + } + +} + + +#endif