From be38b8de9b9af39c01fde9b3c89f647a498c2037 Mon Sep 17 00:00:00 2001 From: Robert K <robertk@posteo.org> Date: Tue, 30 May 2017 16:13:20 +0200 Subject: [PATCH] [feature][SeqILU] faster implementation of ILU preconditioners using a simple Compressed Row Storage for the lower and upper triangular matrices. Also, SeqILU unifies both, SeqILU0 and SeqILUn. --- dune/istl/ilu.hh | 176 ++++++++++++++++++++++++++++++++- dune/istl/paamg/smoother.hh | 48 ++++++++- dune/istl/preconditioners.hh | 129 ++++++++++++++++++++++++ dune/istl/test/multirhstest.cc | 5 + 4 files changed, 354 insertions(+), 4 deletions(-) diff --git a/dune/istl/ilu.hh b/dune/istl/ilu.hh index 4b3f19aff..7f9b1003b 100644 --- a/dune/istl/ilu.hh +++ b/dune/istl/ilu.hh @@ -10,6 +10,7 @@ #include <string> #include <set> #include <map> +#include <vector> #include <dune/common/fmatrix.hh> #include "istlexception.hh" @@ -169,8 +170,8 @@ namespace Dune { createiterator ci=ILU.createbegin(); for (crowiterator i=A.begin(); i!=endi; ++i) { - // std::cout << "in row " << i.index() << std::endl; - map rowpattern; // maps column index to generation + // std::cout << "in row " << i.index() << std::endl; + map rowpattern; // maps column index to generation // initialize pattern with row of A for (ccoliterator j=(*i).begin(); j!=(*i).end(); ++j) @@ -218,7 +219,7 @@ namespace Dune { firstmatrixelement(*ILUij) = (K) rowpattern[ILUij.index()]; } - // printmatrix(std::cout,ILU,"ilu pattern","row",10,2); + // printmatrix(std::cout,ILU,"ilu pattern","row",10,2); // copy entries of A for (crowiterator i=A.begin(); i!=endi; ++i) @@ -251,6 +252,175 @@ namespace Dune { bilu0_decomposition(ILU); } + namespace ILU { + + template <class B> + struct CRS + { + typedef B block_type; + typedef size_t size_type; + + CRS() : nRows_( 0 ) {} + + size_type rows() const { return nRows_; } + + size_type nonZeros() const + { + assert( rows_[ rows() ] != size_type(-1) ); + return rows_[ rows() ]; + } + + void resize( const size_type nRows ) + { + if( nRows_ != nRows ) + { + nRows_ = nRows ; + rows_.resize( nRows_+1, size_type(-1) ); + } + } + + void reserveAdditional( const size_type nonZeros ) + { + const size_type needed = values_.size() + nonZeros ; + if( values_.capacity() < needed ) + { + const size_type estimate = needed * 1.1; + values_.reserve( estimate ); + cols_.reserve( estimate ); + } + } + + void push_back( const block_type& value, const size_type index ) + { + values_.push_back( value ); + cols_.push_back( index ); + } + + std::vector< size_type > rows_; + std::vector< block_type > values_; + std::vector< size_type > cols_; + size_type nRows_; + }; + + //! convert ILU decomposition into CRS format for lower and upper triangular and inverse. + template<class M, class CRS, class InvVector> + void convertToCRS(const M& A, CRS& lower, CRS& upper, InvVector& inv ) + { + typedef typename M :: size_type size_type; + + lower.resize( A.N() ); + upper.resize( A.N() ); + inv.resize( A.N() ); + + lower.reserveAdditional( 2*A.N() ); + + const auto endi = A.end(); + size_type row = 0; + size_type colcount = 0; + lower.rows_[ 0 ] = colcount; + for (auto i=A.begin(); i!=endi; ++i, ++row) + { + const size_type iIndex = i.index(); + lower.reserveAdditional( (*i).size() ); + + // store entries left of diagonal + for (auto j=(*i).begin(); j.index() < iIndex; ++j ) + { + lower.push_back( (*j), j.index() ); + ++colcount; + } + lower.rows_[ iIndex+1 ] = colcount; + } + + const auto rendi = A.beforeBegin(); + row = 0; + colcount = 0; + upper.rows_[ 0 ] = colcount ; + + upper.reserveAdditional( lower.nonZeros() + A.N() ); + + // NOTE: upper and inv store entries in reverse order, reverse here + // relative to ILU + for (auto i=A.beforeEnd(); i!=rendi; --i, ++ row ) + { + const auto endij=(*i).end(); // end of row i + + const size_type iIndex = i.index(); + upper.reserveAdditional( (*i).size() ); + + // store in reverse row order for faster access during backsolve + for (auto j=(*i).begin(); j != endij; ++j ) + { + const size_type jIndex = j.index(); + if( j.index() == iIndex ) + { + inv[ row ] = (*j); + } + else if ( j.index() >= i.index() ) + { + upper.push_back( (*j), jIndex ); + ++colcount ; + } + } + upper.rows_[ row+1 ] = colcount; + } + } // end convertToCRS + + + //! LU backsolve with stored inverse in CRS format for lower and upper triangular + template<class CRS, class mblock, class X, class Y> + void bilu_backsolve (const CRS& lower, + const CRS& upper, + const std::vector< mblock >& inv, + X& v, const Y& d) + { + // iterator types + typedef typename Y :: block_type dblock; + typedef typename X :: block_type vblock; + typedef typename X :: size_type size_type ; + + const size_type iEnd = lower.rows(); + const size_type lastRow = iEnd - 1; + if( iEnd != upper.rows() ) + { + DUNE_THROW(ISTLError,"ILU::bilu_backsolve: lower and upper rows must be the same"); + } + + // lower triangular solve + for( size_type i=0; i<iEnd; ++ i ) + { + dblock rhs( d[ i ] ); + const size_type rowI = lower.rows_[ i ]; + const size_type rowINext = lower.rows_[ i+1 ]; + + for( size_type col = rowI; col < rowINext; ++ col ) + { + lower.values_[ col ].mmv( v[ lower.cols_[ col ] ], rhs ); + } + + v[ i ] = rhs; // Lii = I + } + + // upper triangular solve + for( size_type i=0; i<iEnd; ++ i ) + { + vblock& vBlock = v[ lastRow - i ]; + vblock rhs ( vBlock ); + const size_type rowI = upper.rows_[ i ]; + const size_type rowINext = upper.rows_[ i+1 ]; + + for( size_type col = rowI; col < rowINext; ++ col ) + { + upper.values_[ col ].mmv( v[ upper.cols_[ col ] ], rhs ); + } + + // apply inverse and store result + inv[ i ].mv( rhs, vBlock); + } + } + + } // end namespace ILU + /** @} end documentation */ diff --git a/dune/istl/paamg/smoother.hh b/dune/istl/paamg/smoother.hh index 1f689ed64..9debb1dea 100644 --- a/dune/istl/paamg/smoother.hh +++ b/dune/istl/paamg/smoother.hh @@ -279,7 +279,7 @@ namespace Dune /** - * @brief Policy for the construction of the SeqJac smoother + * @brief Policy for the construction of the SeqILUn smoother */ template<class M, class X, class Y> struct ConstructionTraits<SeqILUn<M,X,Y> > @@ -299,6 +299,52 @@ namespace Dune }; + + template<class M, class X, class Y> + class ConstructionArgs<SeqILU<M,X,Y> > + : public DefaultConstructionArgs<SeqILU<M,X,Y> > + { + public: + ConstructionArgs(int n=0) + : n_(n) + {} + + void setN(int n) + { + n_ = n; + } + + int getN() + { + return n_; + } + + private: + int n_; + }; + + + /** + * @brief Policy for the construction of the SeqILU smoother + */ + template<class M, class X, class Y> + struct ConstructionTraits<SeqILU<M,X,Y> > + { + typedef ConstructionArgs<SeqILU<M,X,Y> > Arguments; + + static inline SeqILU<M,X,Y>* construct(Arguments& args) + { + return new SeqILU<M,X,Y>(args.getMatrix(), args.getN(), + args.getArgs().relaxationFactor); + } + + static void deconstruct(SeqILU<M,X,Y>* ilu) + { + delete ilu; + } + + }; + /** * @brief Policy for the construction of the ParSSOR smoother */ diff --git a/dune/istl/preconditioners.hh b/dune/istl/preconditioners.hh index 84acf024c..6e883c9ba 100644 --- a/dune/istl/preconditioners.hh +++ b/dune/istl/preconditioners.hh @@ -7,6 +7,7 @@ #include <complex> #include <iostream> #include <iomanip> +#include <memory> #include <string> #include <dune/common/unused.hh> @@ -487,6 +488,134 @@ namespace Dune { + /*! + \brief Sequential ILU preconditioner. + + Wraps the naked ISTL generic ILU preconditioner into the solver framework. + + \tparam M The matrix type to operate on + \tparam X Type of the update + \tparam Y Type of the defect + \tparam l Ignored. Just there to have the same number of template arguments + as other preconditioners. + */ + template<class M, class X, class Y, int l=1> + class SeqILU : public Preconditioner<X,Y> { + public: + //! \brief The matrix type the preconditioner is for. + typedef typename std::remove_const<M>::type matrix_type; + //! block type of matrix + typedef typename matrix_type :: block_type block_type; + //! \brief The domain type of the preconditioner. + typedef X domain_type; + //! \brief The range type of the preconditioner. + typedef Y range_type; + //! \brief The field type of the preconditioner. + typedef typename X::field_type field_type; + + //! \brief type of ILU storage + typedef typename ILU::CRS< block_type > CRS; + + /*! \brief Constructor. + + Constructor invoking ILU(0) gets all parameters to operate the prec. + \param A The matrix to operate on. + \param w The relaxation factor. + */ + SeqILU (const M& A, field_type w) + : SeqILU( A, 0, w ) // construct ILU(0) + { + } + + /*! \brief Constructor. + + Constructor invoking ILU(n). + \param A The matrix to operate on. + \param n The number of iterations to perform. + \param w The relaxation factor. + */ + SeqILU (const M& A, int n, field_type w) + : lower_(), + upper_(), + inv_(), + w_(w), + wNotIdentity_( std::abs( w_ - field_type(1) ) > 1e-15 ) + { + std::unique_ptr< matrix_type > ILUA; + + if( n == 0 ) + { + // copy A + ILUA.reset( new matrix_type( A ) ); + // create ILU(0) decomposition + bilu0_decomposition( *ILUA ); + } + else + { + // create matrix in build mode + ILUA.reset( new matrix_type( A.N(), A.M(), matrix_type::row_wise) ); + // create ILU(n) decomposition + bilu_decomposition( A, n, *ILUA ); + } + + // store ILU in simple CRS format + ILU::convertToCRS( *ILUA, lower_, upper_, inv_ ); + } + + /*! + \brief Prepare the preconditioner. + + \copydoc Preconditioner::pre(X&,Y&) + */ + virtual void pre (X& x, Y& b) + { + DUNE_UNUSED_PARAMETER(x); + DUNE_UNUSED_PARAMETER(b); + } + + /*! + \brief Apply the preconditoner. + + \copydoc Preconditioner::apply(X&,const Y&) + */ + virtual void apply (X& v, const Y& d) + { + ILU::bilu_backsolve(lower_, upper_, inv_, v, d); + if( wNotIdentity_ ) + { + v *= w_; + } + } + + /*! + \brief Clean up. + + \copydoc Preconditioner::post(X&) + */ + virtual void post (X& x) + { + DUNE_UNUSED_PARAMETER(x); + } + + //! Category of the preconditioner (see SolverCategory::Category) + virtual SolverCategory::Category category() const + { + return SolverCategory::sequential; + } + + protected: + //! \brief The ILU(n) decomposition of the matrix. + CRS lower_; + CRS upper_; + std::vector< block_type > inv_; + + //! \brief The relaxation factor to use. + const field_type w_; + //! \brief true if w != 1.0 + const bool wNotIdentity_; + }; + + /*! \brief Sequential ILU0 preconditioner. diff --git a/dune/istl/test/multirhstest.cc b/dune/istl/test/multirhstest.cc index da3a66670..3aca3f274 100644 --- a/dune/istl/test/multirhstest.cc +++ b/dune/istl/test/multirhstest.cc @@ -142,6 +142,9 @@ void test_all(unsigned int Runs = 1) Dune::SeqILU0<Matrix,Vector,Vector> ilu0(A,0.1); // preconditioner object Dune::SeqILUn<Matrix,Vector,Vector> ilu1(A,1,0.1); // preconditioner object + Dune::SeqILU<Matrix,Vector,Vector> ilu_0(A,0.1); // preconditioner object + Dune::SeqILU<Matrix,Vector,Vector> ilu_1(A,1,0.1); // preconditioner object + // AMG typedef Dune::Amg::RowSum Norm; typedef Dune::Amg::CoarsenCriterion<Dune::Amg::UnSymmetricCriterion<Matrix,Norm> > @@ -173,6 +176,8 @@ void test_all(unsigned int Runs = 1) test_all_solvers("SSOR", op,ssor,N,Runs); test_all_solvers("ILU0", op,ilu0,N,Runs); test_all_solvers("ILU1", op,ilu1,N,Runs); + test_all_solvers("ILU(0)", op,ilu_0,N,Runs); + test_all_solvers("ILU(1)", op,ilu_1,N,Runs); test_all_solvers("AMG", op,amg,N,Runs); } -- GitLab