Skip to content
Snippets Groups Projects
Forked from agnumpde / dune-solvers
115 commits behind the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
mmgstep.cc 13.10 KiB
// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=8 sw=4 sts=4:
#include <dune/solvers/transferoperators/truncatedmgtransfer.hh>
#include <dune/solvers/iterationsteps/projectedblockgsstep.hh>

#include <dune/solvers/computeenergy.hh>


#if HAVE_IPOPT
#include <dune/solvers/solvers/quadraticipopt.hh>
#endif

template <class MatrixType, class VectorType>
void MonotoneMGStep<MatrixType, VectorType>::
preprocess()
{
    // Unset the recompute bitfields, so we compute the full stiffness matrix hierarchy at the beginning
    for (size_t i=0; i<this->mgTransfer_.size(); i++) {
        std::dynamic_pointer_cast<TruncatedMGTransfer<VectorType> >(this->mgTransfer_[i])->setRecomputeBitField(nullptr);
        std::dynamic_pointer_cast<TruncatedMGTransfer<VectorType> >(this->mgTransfer_[i])->setCriticalBitField(nullptr);
    }

    // Call preprocess of the base class
    MultigridStep<MatrixType,VectorType>::preprocess();

    // Then setup the obstacle flags which specify the subset of entries to be reassembled at each iteration
    hasObstacleHierarchy_.resize(this->numLevels());
    hasObstacleHierarchy_.back() = hasObstacle_;

    for (int i=this->mgTransfer_.size()-1; i>=0; i--) {
        hasObstacleHierarchy_[i] = std::make_shared<Dune::BitSetVector<dim> >();
        this->mgTransfer_[i]->restrict(*hasObstacleHierarchy_[i+1], *hasObstacleHierarchy_[i]);
        for (size_t j=0; j<hasObstacleHierarchy_[i]->size(); j++)
            if ((*this->ignoreNodesHierarchy_[i])[j].any())
                (*hasObstacleHierarchy_[i])[j][0]=false;
    }

    recompute_.resize(this->mgTransfer_.size());
    for (size_t i=0; i<this->mgTransfer_.size(); i++)
    {
        recompute_[i].resize(hasObstacleHierarchy_[i]->size());
        recompute_[i].unsetAll();
        std::dynamic_pointer_cast<TruncatedMGTransfer<VectorType> >(this->mgTransfer_[i])->setRecomputeBitField(&recompute_[i]);
    }

    oldCritical_.resize(this->numLevels());
    for (size_t i=0; i<this->numLevels(); i++)
    {
      oldCritical_[i].resize(hasObstacleHierarchy_[i]->size());
      oldCritical_[i].unsetAll();
    }

    // /////////////////////////////////////////////
    //   Set up base solver
    // /////////////////////////////////////////////

    obstacleHierarchy_.resize(this->numLevels());
    obstacleHierarchy_.back() = obstacles_;

    for (size_t i=0; i<obstacleHierarchy_.size()-1; i++)
        obstacleHierarchy_[i] = std::make_shared<ObstacleVectorType>();

    if (typeid(*this->basesolver_) == typeid(::LoopSolver<VectorType>)) {

        ::LoopSolver<VectorType>* loopBaseSolver = dynamic_cast< ::LoopSolver<VectorType>* > (this->basesolver_.get());

        typedef ProjectedBlockGSStep<MatrixType, VectorType> SmootherType;

        dynamic_cast<SmootherType*>(&loopBaseSolver->getIterationStep())->hasObstacle_ = hasObstacleHierarchy_[0].get();
        dynamic_cast<SmootherType*>(&loopBaseSolver->getIterationStep())->obstacles_   = obstacleHierarchy_[0].get();

#if HAVE_IPOPT
    } else if (typeid(*this->basesolver_) == typeid(QuadraticIPOptSolver<MatrixType,VectorType>)) {

        QuadraticIPOptSolver<MatrixType,VectorType>* ipoptBaseSolver = dynamic_cast<QuadraticIPOptSolver<MatrixType,VectorType>*> (this->basesolver_.get());

        ipoptBaseSolver->setObstacles(*obstacleHierarchy_[0]);
#endif
    } else {
        DUNE_THROW(SolverError, "You can't use " << typeid(*this->basesolver_).name()
                   << " as a base solver for obstacle problems!");
    }
}

template<class MatrixType, class VectorType>
void MonotoneMGStep<MatrixType, VectorType>::nestedIteration()
{
    for (int i=this->numLevels()-1; i>0; i--) {

        Dune::BitSetVector<dim> dummy(this->rhsHierarchy_[i].size(), false);

        // /////////////////////////////
        //   Restrict obstacles
        // //////////////////////////////
        obstacleRestrictor_->restrict(*obstacleHierarchy_[i], *obstacleHierarchy_[i-1],
                                    *hasObstacleHierarchy_[i], *hasObstacleHierarchy_[i-1],
                                    *this->mgTransfer_[i-1], dummy);

        // Restrict right hand side
        this->mgTransfer_[i-1]->restrict(this->rhsHierarchy_[i], this->rhsHierarchy_[i-1]);
    }

    for (this->level_ = 0; this->level_<(int) 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 (size_t i=0; i<obstacleHierarchy_[this->level_]->size(); i++) {
            BoxConstraint<field_type,dim>& cO = (*obstacleHierarchy_[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->xHierarchy_[this->level_] << std::endl;
        this->mgTransfer_[this->level_]->prolong(*this->xHierarchy_[this->level_], *this->xHierarchy_[this->level_+1]);

    }

}


template <class MatrixType, class VectorType>
void MonotoneMGStep<MatrixType, VectorType>::iterate()
{
    this->preprocessCalled = false;

    int& level = this->level_;

    // Define references just for ease of notation
    std::vector<std::shared_ptr<const MatrixType> >& mat = this->matrixHierarchy_;
    std::vector<std::shared_ptr<VectorType> >& x   = this->xHierarchy_;
    std::vector<VectorType>& rhs = this->rhsHierarchy_;
    auto& obstacles = obstacleHierarchy_;

    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 (size_t 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
        ProjectedBlockGSStep<MatrixType,VectorType>* presmoother = dynamic_cast<ProjectedBlockGSStep<MatrixType, VectorType>*>(this->presmoother_[level].get());
        assert(presmoother);
        presmoother->setProblem(*(mat[level]), *x[level], rhs[level]);
        presmoother->obstacles_ = obstacles[level].get();
        presmoother->hasObstacle_ = hasObstacleHierarchy_[level].get();
        presmoother->ignoreNodes_ = this->ignoreNodesHierarchy_[level];

        for (int i=0; i<this->nu1_; i++)
            presmoother->iterate();

        // First, a backup of the obstacles for postsmoothing
        std::vector<BoxConstraint<field_type,dim> > obstacleBackup = *obstacles[level];

        // Compute defect obstacles
        for (size_t i=0; i<obstacles[level]->size(); i++)
            (*obstacles[level])[i] -= (*x[level])[i];

        // ///////////////////////
        //    Truncation
        // ///////////////////////

        // Determine critical nodes
        const double eps = 1e-12;
        for (size_t 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;
                }

            }
        }

        ///////////////////////////////////////////////////////////////////////////////
        // Compute the part of the coarse grid matrix that needs to be recomputed.
        // There are two reasons why a matrix entry may need to be recomputed:
        // 1) A corresponding fine grid vertex switched from critical to non-critical or vice versa
        // 2) A corresponding fine grid matrix entry got recomputed
        ///////////////////////////////////////////////////////////////////////////////

        Dune::BitSetVector<dim> changed(critical.size());
        for (size_t i=0; i<changed.size(); i++)
          for (int j=0; j<dim; j++)
            changed[i][j] = (critical[i][j] != oldCritical_[level][i][j]);

        if (level < (int) this->numLevels()-1 )
          for (size_t i=0; i<changed.size(); i++)
            for (int j=0; j<dim; j++)
              changed[i][j] = (changed[i][j] || recompute_[level][i][j]);

        std::dynamic_pointer_cast<TruncatedMGTransfer<VectorType> >(this->mgTransfer_[level-1])->restrict(changed, recompute_[level-1]);

        oldCritical_[level] = critical;

        // Set bitfield of nodes that will be truncated
        std::dynamic_pointer_cast<TruncatedMGTransfer<VectorType> >(this->mgTransfer_[level-1])->setCriticalBitField(&critical);

        // Restrict stiffness matrix
        this->mgTransfer_[level-1]->galerkinRestrict(*(mat[level]), *(const_cast<MatrixType*>(mat[level-1].get())));

        // Restrict obstacles
        obstacleRestrictor_->restrict(*obstacles[level], *obstacles[level-1],
                                    *hasObstacleHierarchy_[level], *hasObstacleHierarchy_[level-1],
                                    *this->mgTransfer_[level-1],
                                    critical);

        // //////////////////////////////////////////////////////////////////////
        // Restriction:  fineResiduum = rhs[level] - mat[level] * x[level];
        // //////////////////////////////////////////////////////////////////////
        VectorType fineResidual = rhs[level];
        mat[level]->mmv(*x[level], fineResidual);

        // restrict residual
        this->mgTransfer_[level-1]->restrict(fineResidual, rhs[level-1]);

        // 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
        VectorType tmp;
        this->mgTransfer_[level-1]->prolong(*x[level-1], tmp);
        *x[level] += tmp;

        // Remove pointer to the temporary critical bitfield
        // this avoids a memory problem when the same mmg step is reused
        std::dynamic_pointer_cast<TruncatedMGTransfer<VectorType> >(this->mgTransfer_[level-1])->setCriticalBitField(nullptr);

        // restore the true (non-defect) obstacles
        *obstacles[level] = obstacleBackup;

#ifndef NDEBUG
        // Debug: is the current iterate really admissible?
        for (size_t i=0; i<obstacles[level]->size(); i++)
            for (int j=0; j<VectorType::block_type::dimension; j++)
                if (((*x[level])[i][j] - (*obstacles[level])[i].lower(j)<-1e-14
                    || (*x[level])[i][j] - (*obstacles[level])[i].upper(j) >1e-14 )
                    && (!(*this->ignoreNodesHierarchy_[level])[i][j])) {

                    std::cout << "Obstacle disregarded!\n";
                    std::cout << (*x[level])[i] << std::endl << (*obstacles[level])[i] << std::endl;
                    std::cout << "level: " << level << "   index: " << i << "   komponent: " << j << std::endl;
                    std::cout << "is " << ((critical[i][j]) ? "" : "not") << "critical" << std::endl;
                }
#endif

        // Postsmoothing
        ProjectedBlockGSStep<MatrixType,VectorType>* postsmoother = dynamic_cast<ProjectedBlockGSStep<MatrixType, VectorType>*>(this->postsmoother_[level].get());
        assert(postsmoother);
        postsmoother->setProblem(*(mat[level]), *x[level], rhs[level]);
        postsmoother->obstacles_ = obstacles[level].get();
        postsmoother->hasObstacle_ = hasObstacleHierarchy_[level].get();
        postsmoother->ignoreNodes_ = this->ignoreNodesHierarchy_[level];

        for (int i=0; i<this->nu2_; i++)
            postsmoother->iterate();

    }

    // ////////////////////////////////////////////////////////////////////
    //   Track the number of critical nodes found during this iteration
    // ////////////////////////////////////////////////////////////////////
    if (level==(int) 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==(int) this->numLevels()-1 && this->verbosity_==NumProc::FULL) {
        std::streamsize const oldPrecision = std::cout.precision();
        std::cout << "Total energy: "
                  << std::setprecision(10)
                  << computeEnergy(*mat[level], *x[level], rhs[level]) << std::endl
                  << std::setprecision(oldPrecision);
    }

}