Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
multigridtest.cc 6.12 KiB
// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=8 sw=4 sts=4:

#include <config.h>

#include <cmath>
#include <iostream>
#include <sstream>

// dune-common includes
#include <dune/common/bitsetvector.hh>

// dune-istl includes
#include <dune/istl/bcrsmatrix.hh>

// dune-grid includes

// dune-solver includes
#include <dune/solvers/transferoperators/compressedmultigridtransfer.hh>
#include <dune/solvers/norms/energynorm.hh>
#include <dune/solvers/solvers/loopsolver.hh>
#include <dune/solvers/iterationsteps/multigridstep.hh>
#include <dune/solvers/iterationsteps/truncatedblockgsstep.hh>


#include "common.hh"

/**  \brief test for the MultigridStep class
  *
  *  This test tests if the Dirichlet problem for a laplace operator is solved correctly for a "random" rhs by a LoopSolver employing the MultigridStep.
  *  It furthermore tests the functionality to solve correctly for a different rhs with otherwise unaltered data without calling preprocess().
  *  Setting and using different smoothers in different levels is NOT a tested feature.
  */
template <size_t blocksize>
struct MultigridTestSuite
{
    template <class GridType>
    bool check(const GridType& grid)
    {
        double tol = 1e-12;
        int maxIterations = 200;

        bool passed = true;

        typedef typename Dune::FieldMatrix<double, blocksize, blocksize> MatrixBlock;
        typedef typename Dune::FieldVector<double, blocksize> VectorBlock;
        typedef typename Dune::BCRSMatrix<MatrixBlock> Matrix;
        typedef typename Dune::BlockVector<VectorBlock> Vector;
        typedef typename Dune::BitSetVector<blocksize> BitVector;

        typedef typename GridType::LevelGridView GridView;


        const GridView gridView = grid.levelGridView(grid.maxLevel());

        Matrix A;
        constructPQ1Pattern(gridView, A);
        A=0.0;
        assemblePQ1Stiffness(gridView, A);

        Vector u(A.N()), u_ex(A.N());
        u = 0;
        for (size_t i=0; i<u.size(); ++i)
            for (size_t j=0; j<blocksize; ++j)
            {
                u_ex[i][j] = (1.0*rand())/RAND_MAX;
//                A[i][i][j][j] += 0.5*std::abs(A[0][0][0][0]); // in case of no dirichlet values you can make the matrix pd hereby
            }

        Vector rhs(A.N());
        A.mv(u_ex, rhs);

        BitVector ignore(A.N());
        ignore.unsetAll();
        markBoundaryDOFs(gridView, ignore);

        // set dirichlet values
        for (size_t i=0; i<u.size(); ++i)
            for (size_t j=0; j<blocksize; ++j)
                if (ignore[i][j])
                    u[i][j] = u_ex[i][j];

        typedef EnergyNorm<Matrix, Vector> Norm;
        typedef ::LoopSolver<Vector> Solver;
        typedef MultigridStep<Matrix, Vector, BitVector> MGStep;
        typedef MultigridTransfer<Vector, BitVector, Matrix> Transfer;
        typedef CompressedMultigridTransfer<Vector, BitVector, Matrix> TransferImplementation;
        typedef TruncatedBlockGSStep<Matrix, Vector> Smoother;

        // we need a vector of pointers to the transfer operator base class
        std::vector<Transfer*> transfer(grid.maxLevel());
        for (int i = 0; i < transfer.size(); ++i)
        {
            // create transfer operator from level i to i+1 (note that this will only work for either uniformly refined grids or adaptive grids with RefinementType=COPY)
            TransferImplementation* t = new TransferImplementation;
            t->setup(grid, i, i+1);
            transfer[i] = t;
        }

        // we want to control errors with respect to the energy norm induced by the matrix
        Norm energynorm(A);

        // set up smoothers and basesolver
        Smoother smoother, basesolver_step;

        Norm basenorm(basesolver_step);

        Solver basesolver(&basesolver_step, 1, 1e-10, &basenorm, Solver::QUIET);

        // create iteration step and set all kinds of data
        MGStep mgStep;
        mgStep.setTransferOperators(transfer);
        mgStep.setProblem(A,u,rhs);
        mgStep.setSmoother(&smoother);
        mgStep.setMGType(1,3,3);
        mgStep.ignoreNodes_ = &ignore;
        mgStep.basesolver_  = &basesolver;

        // create loop solver
        Solver solver(&mgStep, maxIterations, tol, &energynorm, Solver::FULL);

        // solve problem
        solver.preprocess();
        solver.solve();

        u = mgStep.getSol();

        u -= u_ex;
        if (energynorm(u)>tol*10)
        {
            std::cout << "The MultigridStep did not produce a satisfactory result. ||u-u_ex||=" << energynorm(u) << std::endl;
            std::cout << "||u_ex||=" << energynorm(u_ex) << std::endl;
            passed = false;
        }


        // now solve with the same operator but different rhs (and dirichlet values)

        // create new exact solution
        for (size_t i=0; i<u.size(); ++i)
            for (size_t j=0; j<blocksize; ++j)
                u_ex[i][j] = (1.0*rand())/RAND_MAX;

        // ... and its corresponding rhs
        A.mv(u_ex, rhs);

        // set initial iterate (remember that the iterationStep member points to u)
        u = 0.0;
        // set dirichlet values
        for (size_t i=0; i<u.size(); ++i)
            for (size_t j=0; j<blocksize; ++j)
                if (ignore[i][j])
                    u[i][j] = u_ex[i][j];

        // reset rhs in mgStep without calling preprocess subsequently
        mgStep.setRhs(rhs);

        solver.solve();

        u = mgStep.getSol();

        u -= u_ex;
        if (energynorm(u)>tol*10)
        {
            std::cout << "The MultigridStep did not produce a satisfactory result after calling setRhs(). ||u-u_ex||=" << energynorm(u) << std::endl;
            std::cout << "||u_ex||=" << energynorm(u_ex) << std::endl;
            passed = false;
        }

        return passed;
    }
};



int main(int argc, char** argv) try
{
    bool passed(true);

    MultigridTestSuite<1> testsuite1;
//    MultigridTestSuite<2> testsuite2;
//    MultigridTestSuite<8> testsuite8;
    passed = checkWithStandardGrids(testsuite1);
//    passed = checkWithStandardGrids(testsuite2);
//    passed = checkWithStandardGrids(testsuite8);

    return passed ? 0 : 1;
}
catch (Dune::Exception e) {

    std::cout << e << std::endl;

}