From 6934ce2f8ad1ee25de4dae0338bb8948f21c755e Mon Sep 17 00:00:00 2001 From: Oliver Sander <sander@igpm.rwth-aachen.de> Date: Thu, 23 Oct 2014 01:15:13 +0000 Subject: [PATCH] A Euclidean trust region solver [[Imported from SVN: r9927]] --- dune/gfe/Makefile.am | 2 + dune/gfe/trustregionsolver.cc | 402 ++++++++++++++++++++++++++++++++++ dune/gfe/trustregionsolver.hh | 137 ++++++++++++ 3 files changed, 541 insertions(+) create mode 100644 dune/gfe/trustregionsolver.cc create mode 100644 dune/gfe/trustregionsolver.hh diff --git a/dune/gfe/Makefile.am b/dune/gfe/Makefile.am index cb75f032..58ee4494 100644 --- a/dune/gfe/Makefile.am +++ b/dune/gfe/Makefile.am @@ -41,6 +41,8 @@ srcinclude_HEADERS = adolcnamespaceinjections.hh \ targetspacertrsolver.hh \ tensorssd.hh \ tensor3.hh \ + trustregionsolver.hh \ + trustregionsolver.cc \ unitvector.hh \ vtkfile.hh diff --git a/dune/gfe/trustregionsolver.cc b/dune/gfe/trustregionsolver.cc new file mode 100644 index 00000000..b5894182 --- /dev/null +++ b/dune/gfe/trustregionsolver.cc @@ -0,0 +1,402 @@ +#include <dune/common/bitsetvector.hh> +#include <dune/common/timer.hh> + +#include <dune/istl/io.hh> + +#include <dune/fufem/functionspacebases/p1nodalbasis.hh> +#include <dune/fufem/assemblers/operatorassembler.hh> +#include <dune/fufem/assemblers/localassemblers/laplaceassembler.hh> +#include <dune/fufem/assemblers/localassemblers/massassembler.hh> + +// Using a monotone multigrid as the inner solver +#include <dune/solvers/iterationsteps/trustregiongsstep.hh> +#include <dune/solvers/iterationsteps/mmgstep.hh> +#include <dune/solvers/transferoperators/truncatedcompressedmgtransfer.hh> +#if defined THIRD_ORDER || defined SECOND_ORDER +#include <dune/gfe/pktop1mgtransfer.hh> +#endif +#include <dune/solvers/transferoperators/mandelobsrestrictor.hh> +#include <dune/solvers/solvers/iterativesolver.hh> +#include "maxnormtrustregion.hh" + +#include <dune/solvers/norms/twonorm.hh> +#include <dune/solvers/norms/h1seminorm.hh> + +template <class GridType, class VectorType> +void TrustRegionSolver<GridType,VectorType>:: +setup(const GridType& grid, + const FEAssembler<BasisType, VectorType>* assembler, + const SolutionType& x, + const Dune::BitSetVector<blocksize>& dirichletNodes, + double tolerance, + int maxTrustRegionSteps, + double initialTrustRegionRadius, + int multigridIterations, + double mgTolerance, + int mu, + int nu1, + int nu2, + int baseIterations, + double baseTolerance) +{ + grid_ = &grid; + assembler_ = assembler; + x_ = x; + this->tolerance_ = tolerance; + maxTrustRegionSteps_ = maxTrustRegionSteps; + initialTrustRegionRadius_ = initialTrustRegionRadius; + innerIterations_ = multigridIterations; + innerTolerance_ = mgTolerance; + ignoreNodes_ = &dirichletNodes; + + int numLevels = grid_->maxLevel()+1; + + // //////////////////////////////// + // Create a multigrid solver + // //////////////////////////////// + +#if 0//def HAVE_IPOPT + // First create an IPOpt base solver + QuadraticIPOptSolver<MatrixType, CorrectionType>* baseSolver = new QuadraticIPOptSolver<MatrixType,CorrectionType>; + baseSolver->verbosity_ = NumProc::QUIET; + baseSolver->tolerance_ = baseTolerance; +#else + // First create a Gauss-seidel base solver + TrustRegionGSStep<MatrixType, CorrectionType>* baseSolverStep = new TrustRegionGSStep<MatrixType, CorrectionType>; + + // Hack: the two-norm may not scale all that well, but it is fast! + TwoNorm<CorrectionType>* baseNorm = new TwoNorm<CorrectionType>; + + ::LoopSolver<CorrectionType>* baseSolver = new ::LoopSolver<CorrectionType>(baseSolverStep, + baseIterations, + baseTolerance, + baseNorm, + Solver::QUIET); +#endif + + // Make pre and postsmoothers + TrustRegionGSStep<MatrixType, CorrectionType>* presmoother = new TrustRegionGSStep<MatrixType, CorrectionType>; + TrustRegionGSStep<MatrixType, CorrectionType>* postsmoother = new TrustRegionGSStep<MatrixType, CorrectionType>; + + MonotoneMGStep<MatrixType, CorrectionType>* mmgStep = new MonotoneMGStep<MatrixType, CorrectionType>; + + mmgStep->setMGType(mu, nu1, nu2); + mmgStep->ignoreNodes_ = &dirichletNodes; + mmgStep->basesolver_ = baseSolver; + mmgStep->setSmoother(presmoother, postsmoother); + mmgStep->obstacleRestrictor_= new MandelObstacleRestrictor<CorrectionType>(); + mmgStep->verbosity_ = Solver::QUIET; + + // ////////////////////////////////////////////////////////////////////////////////////// + // Assemble a Laplace matrix to create a norm that's equivalent to the H1-norm + // ////////////////////////////////////////////////////////////////////////////////////// + + BasisType basis(grid.leafGridView()); + OperatorAssembler<BasisType,BasisType> operatorAssembler(basis, basis); + + LaplaceAssembler<GridType, typename BasisType::LocalFiniteElement, typename BasisType::LocalFiniteElement> laplaceStiffness; + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> > ScalarMatrixType; + ScalarMatrixType localA; + + operatorAssembler.assemble(laplaceStiffness, localA); + + if (h1SemiNorm_) + delete h1SemiNorm_; + + ScalarMatrixType* A = new ScalarMatrixType(localA); + + h1SemiNorm_ = new H1SemiNorm<CorrectionType>(*A); + + innerSolver_ = std::shared_ptr<LoopSolver<CorrectionType> >(new ::LoopSolver<CorrectionType>(mmgStep, + innerIterations_, + innerTolerance_, + h1SemiNorm_, + Solver::REDUCED)); + + // ////////////////////////////////////////////////////////////////////////////////////// + // Assemble a mass matrix to create a norm that's equivalent to the L2-norm + // This will be used to monitor the gradient + // ////////////////////////////////////////////////////////////////////////////////////// + + MassAssembler<GridType, typename BasisType::LocalFiniteElement, typename BasisType::LocalFiniteElement> massStiffness; + ScalarMatrixType localMassMatrix; + + operatorAssembler.assemble(massStiffness, localMassMatrix); + + ScalarMatrixType* massMatrix = new ScalarMatrixType(localMassMatrix); + l2Norm_ = std::make_shared<H1SemiNorm<CorrectionType> >(*massMatrix); + + // //////////////////////////////////////////////////////////// + // Create Hessian matrix and its occupation structure + // //////////////////////////////////////////////////////////// + + hessianMatrix_ = std::auto_ptr<MatrixType>(new MatrixType); + Dune::MatrixIndexSet indices(grid_->size(1), grid_->size(1)); + assembler_->getNeighborsPerVertex(indices); + indices.exportIdx(*hessianMatrix_); + + // //////////////////////////////////// + // Create the transfer operators + // //////////////////////////////////// + + for (size_t k=0; k<mmgStep->mgTransfer_.size(); k++) + delete(mmgStep->mgTransfer_[k]); + + mmgStep->mgTransfer_.resize(numLevels-1); + +#if defined THIRD_ORDER || defined SECOND_ORDER + if (numLevels>1) { + P1NodalBasis<typename GridType::LeafGridView,double> p1Basis(grid_->leafGridView()); + + PKtoP1MGTransfer<CorrectionType>* topTransferOp = new PKtoP1MGTransfer<CorrectionType>; + topTransferOp->setup(basis,p1Basis); + + mmgStep->mgTransfer_.back() = topTransferOp; + + for (int i=0; i<mmgStep->mgTransfer_.size()-1; i++){ + // Construct the local multigrid transfer matrix + TruncatedCompressedMGTransfer<CorrectionType>* newTransferOp = new TruncatedCompressedMGTransfer<CorrectionType>; + newTransferOp->setup(*grid_,i+1,i+2); + + mmgStep->mgTransfer_[i] = newTransferOp; + } + + } + +#else + for (size_t i=0; i<mmgStep->mgTransfer_.size(); i++){ + + // Construct the local multigrid transfer matrix + TruncatedCompressedMGTransfer<CorrectionType>* newTransferOp = new TruncatedCompressedMGTransfer<CorrectionType>; + newTransferOp->setup(*grid_,i,i+1); + + mmgStep->mgTransfer_[i] = newTransferOp;; + } +#endif + + // ////////////////////////////////////////////////////////// + // Create obstacles + // ////////////////////////////////////////////////////////// + + hasObstacle_.resize(basis.size(), true); + mmgStep->hasObstacle_ = &hasObstacle_; + +} + + +template <class GridType, class VectorType> +void TrustRegionSolver<GridType,VectorType>::solve() +{ + MonotoneMGStep<MatrixType,CorrectionType>* mgStep = NULL; + + // if the inner solver is a monotone multigrid set up a max-norm trust-region + if (dynamic_cast<LoopSolver<CorrectionType>*>(innerSolver_.get())) { + mgStep = dynamic_cast<MonotoneMGStep<MatrixType,CorrectionType>*>(dynamic_cast<LoopSolver<CorrectionType>*>(innerSolver_.get())->iterationStep_); + + } + + BasisType basis(grid_->leafGridView()); + MaxNormTrustRegion<blocksize> trustRegion(basis.size(), initialTrustRegionRadius_); + + std::vector<BoxConstraint<field_type,blocksize> > trustRegionObstacles; + + // ///////////////////////////////////////////////////// + // Trust-Region Solver + // ///////////////////////////////////////////////////// + + double oldEnergy = assembler_->computeEnergy(x_); + + bool recomputeGradientHessian = true; + CorrectionType rhs; + MatrixType stiffnessMatrix; + + for (int i=0; i<maxTrustRegionSteps_; i++) { + + Dune::Timer totalTimer; + if (this->verbosity_ == Solver::FULL) { + std::cout << "----------------------------------------------------" << std::endl; + std::cout << " Trust-Region Step Number: " << i + << ", radius: " << trustRegion.radius() + << ", energy: " << oldEnergy << std::endl; + std::cout << "----------------------------------------------------" << std::endl; + } + + Dune::Timer gradientTimer; + + if (recomputeGradientHessian) { + + assembler_->assembleGradientAndHessian(x_, + rhs, + *hessianMatrix_, + i==0 // assemble occupation pattern only for the first call + ); + + rhs *= -1; // The right hand side is the _negative_ gradient + + // Compute gradient norm to monitor convergence + CorrectionType gradient = rhs; + for (size_t j=0; j<gradient.size(); j++) + for (int k=0; k<gradient[j].size(); k++) + if ((*ignoreNodes_)[j][k]) + gradient[j][k] = 0; + + if (this->verbosity_ == Solver::FULL) + std::cout << "Gradient norm: " << l2Norm_->operator()(gradient) << std::endl; + + if (this->verbosity_ == Solver::FULL) + std::cout << "Assembly took " << gradientTimer.elapsed() << " sec." << std::endl; + + // Transfer matrix data + stiffnessMatrix = *hessianMatrix_; + + recomputeGradientHessian = false; + + } + + CorrectionType corr(rhs.size()); + corr = 0; + + mgStep->setProblem(stiffnessMatrix, corr, rhs); + + trustRegionObstacles = trustRegion.obstacles(); + mgStep->obstacles_ = &trustRegionObstacles; + + innerSolver_->preprocess(); + + /////////////////////////////// + // Solve ! + /////////////////////////////// + + std::cout << "Solve quadratic problem..." << std::endl; + + Dune::Timer solutionTimer; + innerSolver_->solve(); + std::cout << "Solving the quadratic problem took " << solutionTimer.elapsed() << " seconds." << std::endl; + + if (mgStep) + corr = mgStep->getSol(); + + //std::cout << "Correction: " << std::endl << corr_global << std::endl; + + // Output correction for debugging + Dune::VTKWriter<typename GridType::LeafGridView> vtkWriter(grid_->leafGridView()); + + Dune::BlockVector<Dune::FieldVector<double,3> > displacement(x_.size()); + for (size_t j=0; j<x_.size(); j++) + displacement[j] = x_[j] - identity_[j]; + + BasisType basis(grid_->leafGridView()); + Dune::shared_ptr<VTKBasisGridFunction<BasisType,Dune::BlockVector<Dune::FieldVector<double,3> > > > vtkDisplacement + = Dune::make_shared<VTKBasisGridFunction<BasisType,Dune::BlockVector<Dune::FieldVector<double,3> > > > + (basis, displacement, "Displacement"); + + Dune::shared_ptr<VTKBasisGridFunction<BasisType,Dune::BlockVector<Dune::FieldVector<double,3> > > > vtkCorrection + = Dune::make_shared<VTKBasisGridFunction<BasisType,Dune::BlockVector<Dune::FieldVector<double,3> > > > + (basis, corr, "Correction"); + + Dune::shared_ptr<VTKBasisGridFunction<BasisType,Dune::BlockVector<Dune::FieldVector<double,3> > > > vtkGradient + = Dune::make_shared<VTKBasisGridFunction<BasisType,Dune::BlockVector<Dune::FieldVector<double,3> > > > + (basis, rhs, "Gradient"); + + vtkWriter.addVertexData(vtkDisplacement); + vtkWriter.addVertexData(vtkCorrection); + vtkWriter.addVertexData(vtkGradient); + vtkWriter.write("hencky_correction_" + std::to_string(i+1)); + + if (this->verbosity_ == NumProc::FULL) + std::cout << "Infinity norm of the correction: " << corr.infinity_norm() << std::endl; + + if (corr.infinity_norm() < this->tolerance_) { + if (this->verbosity_ == NumProc::FULL) + std::cout << "CORRECTION IS SMALL ENOUGH" << std::endl; + + if (this->verbosity_ != NumProc::QUIET) + std::cout << i+1 << " trust-region steps were taken." << std::endl; + break; + } + + // //////////////////////////////////////////////////// + // Check whether trust-region step can be accepted + // //////////////////////////////////////////////////// + + SolutionType newIterate = x_; + for (size_t j=0; j<newIterate.size(); j++) + newIterate[j] += corr[j]; + + double energy = assembler_->computeEnergy(newIterate); + + // compute the model decrease + // It is $ m(x) - m(x+s) = -<g,s> - 0.5 <s, Hs> + // Note that rhs = -g + CorrectionType tmp(corr.size()); + tmp = 0; + hessianMatrix_->umv(corr, tmp); + double modelDecrease = (rhs*corr) - 0.5 * (corr*tmp); + + double relativeModelDecrease = modelDecrease / std::fabs(energy); + + if (this->verbosity_ == NumProc::FULL) { + std::cout << "Absolute model decrease: " << modelDecrease + << ", functional decrease: " << oldEnergy - energy << std::endl; + std::cout << "Relative model decrease: " << relativeModelDecrease + << ", functional decrease: " << (oldEnergy - energy)/energy << std::endl; + } + + assert(modelDecrease >= 0); + + if (energy >= oldEnergy) { + if (this->verbosity_ == NumProc::FULL) + printf("Richtung ist keine Abstiegsrichtung!\n"); + } + + if (energy >= oldEnergy && + (std::abs((oldEnergy-energy)/energy) < 1e-9 || relativeModelDecrease < 1e-9)) { + if (this->verbosity_ == NumProc::FULL) + std::cout << "Suspecting rounding problems" << std::endl; + + if (this->verbosity_ != NumProc::QUIET) + std::cout << i+1 << " trust-region steps were taken." << std::endl; + + x_ = newIterate; + break; + } + + // ////////////////////////////////////////////// + // Check for acceptance of the step + // ////////////////////////////////////////////// + if ( (oldEnergy-energy) / modelDecrease > 0.9) { + // very successful iteration + + x_ = newIterate; + trustRegion.scale(2); + + // current energy becomes 'oldEnergy' for the next iteration + oldEnergy = energy; + + recomputeGradientHessian = true; + + } else if ( (oldEnergy-energy) / modelDecrease > 0.01 + || std::abs(oldEnergy-energy) < 1e-12) { + // successful iteration + x_ = newIterate; + + // current energy becomes 'oldEnergy' for the next iteration + oldEnergy = energy; + + recomputeGradientHessian = true; + + } else { + + // unsuccessful iteration + + // Decrease the trust-region radius + trustRegion.scale(0.5); + + if (this->verbosity_ == NumProc::FULL) + std::cout << "Unsuccessful iteration!" << std::endl; + } + + std::cout << "iteration took " << totalTimer.elapsed() << " sec." << std::endl; + } + +} diff --git a/dune/gfe/trustregionsolver.hh b/dune/gfe/trustregionsolver.hh new file mode 100644 index 00000000..101d1ddf --- /dev/null +++ b/dune/gfe/trustregionsolver.hh @@ -0,0 +1,137 @@ +#ifndef DUNE_GFE_TRUST_REGION_SOLVER_HH +#define DUNE_GFE_TRUST_REGION_SOLVER_HH + +#include <vector> + +#include <dune/common/bitsetvector.hh> + +#include <dune/istl/bcrsmatrix.hh> +#include <dune/istl/bvector.hh> + +#include <dune/solvers/common/boxconstraint.hh> +#include <dune/solvers/norms/h1seminorm.hh> +#include <dune/solvers/solvers/iterativesolver.hh> +#include <dune/solvers/solvers/loopsolver.hh> + +#include <dune/fufem/functionspacebases/p1nodalbasis.hh> +#include <dune/fufem/functionspacebases/p2nodalbasis.hh> +#include <dune/fufem/functionspacebases/p3nodalbasis.hh> + +#include <dune/gfe/feassembler.hh> + +/** \brief Trust-region solver */ +template <class GridType, class VectorType> +class TrustRegionSolver + : public IterativeSolver<VectorType, + Dune::BitSetVector<VectorType::value_type::dimension> > +{ + const static int blocksize = VectorType::value_type::dimension; + + const static int gridDim = GridType::dimension; + + // Centralize the field type here + typedef double field_type; + + // Some types that I need + typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize, blocksize> > MatrixType; + typedef Dune::BlockVector<Dune::FieldVector<field_type, blocksize> > CorrectionType; + typedef VectorType SolutionType; + +#ifdef THIRD_ORDER + typedef P3NodalBasis<typename GridType::LeafGridView,double> BasisType; +#elif defined SECOND_ORDER + typedef P2NodalBasis<typename GridType::LeafGridView,double> BasisType; +#else + typedef P1NodalBasis<typename GridType::LeafGridView,double> BasisType; +#endif + +public: + + TrustRegionSolver() + : IterativeSolver<VectorType, Dune::BitSetVector<blocksize> >(0,100,NumProc::FULL), + hessianMatrix_(std::auto_ptr<MatrixType>(NULL)), h1SemiNorm_(NULL) + {} + + /** \brief Set up the solver using a monotone multigrid method as the inner solver */ + void setup(const GridType& grid, + const FEAssembler<BasisType,VectorType>* assembler, + const SolutionType& x, + const Dune::BitSetVector<blocksize>& dirichletNodes, + double tolerance, + int maxTrustRegionSteps, + double initialTrustRegionRadius, + int multigridIterations, + double mgTolerance, + int mu, + int nu1, + int nu2, + int baseIterations, + double baseTolerance); + + void setIgnoreNodes(const Dune::BitSetVector<blocksize>& ignoreNodes) + { + ignoreNodes_ = &ignoreNodes; + Dune::shared_ptr<LoopSolver<CorrectionType> > loopSolver = std::dynamic_pointer_cast<LoopSolver<CorrectionType> >(innerSolver_); + assert(loopSolver); + loopSolver->iterationStep_->ignoreNodes_ = ignoreNodes_; + } + + void solve(); + + void setInitialSolution(const SolutionType& x) DUNE_DEPRECATED { + x_ = x; + } + + void setInitialIterate(const SolutionType& x) { + x_ = x; + } + + SolutionType getSol() const {return x_;} + +protected: + + /** \brief The grid */ + const GridType* grid_; + + /** \brief The solution vector */ + SolutionType x_; + + /** \brief The initial trust-region radius in the maximum-norm */ + double initialTrustRegionRadius_; + + /** \brief Maximum number of trust-region steps */ + int maxTrustRegionSteps_; + + /** \brief Maximum number of multigrid iterations */ + int innerIterations_; + + /** \brief Error tolerance of the multigrid QP solver */ + double innerTolerance_; + + /** \brief Hessian matrix */ + std::auto_ptr<MatrixType> hessianMatrix_; + + /** \brief The assembler for the material law */ + const FEAssembler<BasisType, VectorType>* assembler_; + + /** \brief The solver for the quadratic inner problems */ + std::shared_ptr<Solver> innerSolver_; + + /** \brief Contains 'true' everywhere -- the trust-region is bounded */ + Dune::BitSetVector<blocksize> hasObstacle_; + + /** \brief The Dirichlet nodes */ + const Dune::BitSetVector<blocksize>* ignoreNodes_; + + /** \brief The norm used to measure multigrid convergence */ + H1SemiNorm<CorrectionType>* h1SemiNorm_; + + /** \brief An L2-norm, really. The H1SemiNorm class is badly named */ + std::shared_ptr<H1SemiNorm<CorrectionType> > l2Norm_; +public: + VectorType identity_; +}; + +#include "trustregionsolver.cc" + +#endif -- GitLab