From f31ff8893856c6087397941154c11a7f0ee5ab9d Mon Sep 17 00:00:00 2001 From: Oliver Sander <oliver.sander@tu-dresden.de> Date: Tue, 11 Dec 2018 14:44:08 +0100 Subject: [PATCH] Implement MatrixMarket support for BCRSMatrix<double> etc --- dune/istl/matrixmarket.hh | 196 +++++++++++++++++++++-------- dune/istl/test/matrixmarkettest.cc | 107 +++++++++------- 2 files changed, 206 insertions(+), 97 deletions(-) diff --git a/dune/istl/matrixmarket.hh b/dune/istl/matrixmarket.hh index d9ba94229..a8f4faaaf 100644 --- a/dune/istl/matrixmarket.hh +++ b/dune/istl/matrixmarket.hh @@ -169,13 +169,13 @@ namespace Dune template<class M> struct mm_header_printer; - template<typename T, typename A, int i, int j> - struct mm_header_printer<BCRSMatrix<FieldMatrix<T,i,j>,A> > + template<typename T, typename A> + struct mm_header_printer<BCRSMatrix<T,A> > { static void print(std::ostream& os) { os<<"%%MatrixMarket matrix coordinate "; - os<<mm_numeric_type<T>::str()<<" general"<<std::endl; + os<<mm_numeric_type<typename Imp::BlockTraits<T>::field_type>::str()<<" general"<<std::endl; } }; @@ -185,7 +185,7 @@ namespace Dune static void print(std::ostream& os) { os<<"%%MatrixMarket matrix array "; - os<<mm_numeric_type<typename B::field_type>::str()<<" general"<<std::endl; + os<<mm_numeric_type<typename Imp::BlockTraits<B>::field_type>::str()<<" general"<<std::endl; } }; @@ -220,6 +220,19 @@ namespace Dune template<class M> struct mm_block_structure_header; + template<typename T, typename A> + struct mm_block_structure_header<BlockVector<T,A> > + { + typedef BlockVector<T,A> M; + static_assert(IsNumber<T>::value, "Only scalar entries are expected here!"); + + static void print(std::ostream& os, const M&) + { + os<<"% ISTL_STRUCT blocked "; + os<<"1 1"<<std::endl; + } + }; + template<typename T, typename A, int i> struct mm_block_structure_header<BlockVector<FieldVector<T,i>,A> > { @@ -232,6 +245,19 @@ namespace Dune } }; + template<typename T, typename A> + struct mm_block_structure_header<BCRSMatrix<T,A> > + { + typedef BCRSMatrix<T,A> M; + static_assert(IsNumber<T>::value, "Only scalar entries are expected here!"); + + static void print(std::ostream& os, const M&) + { + os<<"% ISTL_STRUCT blocked "; + os<<"1 1"<<std::endl; + } + }; + template<typename T, typename A, int i, int j> struct mm_block_structure_header<BCRSMatrix<FieldMatrix<T,i,j>,A> > { @@ -651,19 +677,35 @@ namespace Dune * @param row The row data as read from file. * @param matrix The matrix whose data we set. */ - template<typename M> + template<typename T> void operator()(const std::vector<std::set<IndexData<D> > >& rows, - M& matrix) + BCRSMatrix<T>& matrix) { - for(typename M::RowIterator iter=matrix.begin(); - iter!= matrix.end(); ++iter) + static_assert(IsNumber<T>::value && brows==1 && bcols==1, "Only scalar entries are expected here!"); + for (auto iter=matrix.begin(); iter!= matrix.end(); ++iter) { - for(typename M::size_type brow=iter.index()*brows, + auto brow=iter.index(); + for (auto siter=rows[brow].begin(); siter != rows[brow].end(); ++siter) + (*iter)[siter->index] = siter->number; + } + } + + /** + * @brief Sets the matrix values. + * @param row The row data as read from file. + * @param matrix The matrix whose data we set. + */ + template<typename T> + void operator()(const std::vector<std::set<IndexData<D> > >& rows, + BCRSMatrix<FieldMatrix<T,brows,bcols> >& matrix) + { + for (auto iter=matrix.begin(); iter!= matrix.end(); ++iter) + { + for (auto brow=iter.index()*brows, browend=iter.index()*brows+brows; brow<browend; ++brow) { - typedef typename std::set<IndexData<D> >::const_iterator Siter; - for(Siter siter=rows[brow].begin(), send=rows[brow].end(); + for (auto siter=rows[brow].begin(), send=rows[brow].end(); siter != send; ++siter) (*iter)[siter->index/bcols][brow%brows][siter->index%bcols]=siter->number; } @@ -694,12 +736,39 @@ namespace Dune return std::conj(r); } - template<typename T, typename A, int brows, int bcols, typename D> - void readSparseEntries(Dune::BCRSMatrix<Dune::FieldMatrix<T,brows,bcols>,A>& matrix, + template<typename M> + struct mm_multipliers + {}; + + template<typename B, typename A> + struct mm_multipliers<BCRSMatrix<B,A> > + { + enum { + rows = 1, + cols = 1 + }; + }; + + template<typename B, int i, int j, typename A> + struct mm_multipliers<BCRSMatrix<FieldMatrix<B,i,j>,A> > + { + enum { + rows = i, + cols = j + }; + }; + + template<typename T, typename A, typename D> + void readSparseEntries(Dune::BCRSMatrix<T,A>& matrix, std::istream& file, std::size_t entries, const MMHeader& mmHeader, const D&) { - typedef Dune::BCRSMatrix<Dune::FieldMatrix<T,brows,bcols>,A> Matrix; + typedef Dune::BCRSMatrix<T,A> Matrix; + + // Number of rows and columns of T, if it is a matrix (1x1 otherwise) + constexpr int brows = mm_multipliers<Matrix>::rows; + constexpr int bcols = mm_multipliers<Matrix>::cols; + // First path // store entries together with column index in a separate // data structure @@ -810,6 +879,15 @@ namespace Dune istr >> cols; } + template<typename T, typename A> + void mm_read_vector_entries(Dune::BlockVector<T,A>& vector, + std::size_t size, + std::istream& istr) + { + for (int i=0; size>0; ++i, --size) + istr>>vector[i]; + } + template<typename T, typename A, int entries> void mm_read_vector_entries(Dune::BlockVector<Dune::FieldVector<T,entries>,A>& vector, std::size_t size, @@ -829,8 +907,8 @@ namespace Dune * @param istr The input stream to read the data from. * @warning Not all formats are supported! */ - template<typename T, typename A, int entries> - void readMatrixMarket(Dune::BlockVector<Dune::FieldVector<T,entries>,A>& vector, + template<typename T, typename A> + void readMatrixMarket(Dune::BlockVector<T,A>& vector, std::istream& istr) { using namespace MatrixMarketImpl; @@ -844,29 +922,37 @@ namespace Dune if(header.type!=array_type) DUNE_THROW(MatrixMarketFormatError, "Vectors have to be stored in array format!"); - std::size_t size=rows/entries; - if(size*entries!=rows) - DUNE_THROW(MatrixMarketFormatError, "Block size of vector is not correct!"); - vector.resize(size); + Dune::Hybrid::ifElse(Dune::IsNumber<T>(), + [&](auto id){ + vector.resize(rows); + }, + [&](auto id){ // Assume that T is a FieldVector + T dummy; + auto blocksize = id(dummy).size(); + std::size_t size=rows/blocksize; + if(size*blocksize!=rows) + DUNE_THROW(MatrixMarketFormatError, "Block size of vector is not correct!"); - istr.ignore(std::numeric_limits<std::streamsize>::max(),'\n'); + vector.resize(size); + }); + istr.ignore(std::numeric_limits<std::streamsize>::max(),'\n'); mm_read_vector_entries(vector, rows, istr); } - /** * @brief Reads a sparse matrix from a matrix market file. * @param matrix The matrix to store the data in. * @param istr The input stream to read the data from. * @warning Not all formats are supported! */ - template<typename T, typename A, int brows, int bcols> - void readMatrixMarket(Dune::BCRSMatrix<Dune::FieldMatrix<T,brows,bcols>,A>& matrix, + template<typename T, typename A> + void readMatrixMarket(Dune::BCRSMatrix<T,A>& matrix, std::istream& istr) { using namespace MatrixMarketImpl; + using Matrix = Dune::BCRSMatrix<T,A>; MMHeader header; if(!readMatrixMarketBanner(istr, header)) { @@ -896,46 +982,42 @@ namespace Dune std::size_t nnz, blockrows, blockcols; + // Number of rows and columns of T, if it is a matrix (1x1 otherwise) + constexpr int brows = mm_multipliers<Matrix>::rows; + constexpr int bcols = mm_multipliers<Matrix>::cols; + std::tie(blockrows, blockcols, nnz) = calculateNNZ<brows, bcols>(rows, cols, entries, header); istr.ignore(std::numeric_limits<std::streamsize>::max(),'\n'); matrix.setSize(blockrows, blockcols); - matrix.setBuildMode(Dune::BCRSMatrix<Dune::FieldMatrix<T,brows,bcols>,A>::row_wise); + matrix.setBuildMode(Dune::BCRSMatrix<T,A>::row_wise); if(header.type==array_type) DUNE_THROW(Dune::NotImplemented, "Array format currently not supported for matrices!"); - readSparseEntries(matrix, istr, entries, header, NumericWrapper<T>()); + readSparseEntries(matrix, istr, entries, header, NumericWrapper<typename Matrix::field_type>()); } - template<typename M> - struct mm_multipliers - {}; - - template<typename B, int i, int j, typename A> - struct mm_multipliers<BCRSMatrix<FieldMatrix<B,i,j>,A> > - { - enum { - rows = i, - cols = j - }; - }; - - template<typename B, int i, int j> - void mm_print_entry(const FieldMatrix<B,i,j>& entry, - typename FieldMatrix<B,i,j>::size_type rowidx, - typename FieldMatrix<B,i,j>::size_type colidx, + // Print a scalar entry + template<typename B> + void mm_print_entry(const B& entry, + std::size_t rowidx, + std::size_t colidx, std::ostream& ostr) { - typedef typename FieldMatrix<B,i,j>::const_iterator riterator; - typedef typename FieldMatrix<B,i,j>::ConstColIterator citerator; - for(riterator row=entry.begin(); row != entry.end(); ++row, ++rowidx) { - int coli=colidx; - for(citerator col = row->begin(); col != row->end(); ++col, ++coli) - ostr<< rowidx<<" "<<coli<<" "<<*col<<std::endl; - } + Hybrid::ifElse(IsNumber<B>(), + [&](auto id) { + ostr << rowidx << " " << colidx << " " << entry << std::endl; + }, + [&](auto id) { + for (auto row=id(entry).begin(); row != id(entry).end(); ++row, ++rowidx) { + int coli=colidx; + for (auto col = row->begin(); col != row->end(); ++col, ++coli) + ostr<< rowidx<<" "<<coli<<" "<<*col<<std::endl; + } + }); } // Write a vector entry @@ -963,6 +1045,12 @@ namespace Dune std::integral_constant<int,isnumeric>()); } + template<typename T, typename A> + std::size_t countEntries(const BlockVector<T,A>& vector) + { + return vector.size(); + } + template<typename T, typename A, int i> std::size_t countEntries(const BlockVector<FieldVector<T,i>,A>& vector) { @@ -987,8 +1075,8 @@ namespace Dune std::ostream& ostr, const std::integral_constant<int,1>&) { - ostr<<matrix.N()*mm_multipliers<M>::rows<<" " - <<matrix.M()*mm_multipliers<M>::cols<<" " + ostr<<matrix.N()*MatrixMarketImpl::mm_multipliers<M>::rows<<" " + <<matrix.M()*MatrixMarketImpl::mm_multipliers<M>::cols<<" " <<countNonZeros(matrix)<<std::endl; typedef typename M::const_iterator riterator; @@ -996,8 +1084,8 @@ namespace Dune for(riterator row=matrix.begin(); row != matrix.end(); ++row) for(citerator col = row->begin(); col != row->end(); ++col) // Matrix Market indexing start with 1! - mm_print_entry(*col, row.index()*mm_multipliers<M>::rows+1, - col.index()*mm_multipliers<M>::cols+1, ostr); + mm_print_entry(*col, row.index()*MatrixMarketImpl::mm_multipliers<M>::rows+1, + col.index()*MatrixMarketImpl::mm_multipliers<M>::cols+1, ostr); } diff --git a/dune/istl/test/matrixmarkettest.cc b/dune/istl/test/matrixmarkettest.cc index 7d9b20864..a2483cd0a 100644 --- a/dune/istl/test/matrixmarkettest.cc +++ b/dune/istl/test/matrixmarkettest.cc @@ -21,54 +21,40 @@ #include "laplacian.hh" #endif -int main(int argc, char** argv) +template <class Matrix, class Vector> +int testMatrixMarket(int N) { -#if HAVE_MPI - MPI_Init(&argc, &argv); - int size; - MPI_Comm_size(MPI_COMM_WORLD, &size); -#endif - const int BS=1; - int N=100; - - if(argc>1) - N = atoi(argv[1]); - std::cout<<"testing for N="<<N<<" BS="<<1<<std::endl; - - - typedef Dune::FieldMatrix<double,BS,BS> MatrixBlock; - typedef Dune::BCRSMatrix<MatrixBlock> BCRSMat; - typedef Dune::FieldVector<double,BS> VectorBlock; - typedef Dune::BlockVector<VectorBlock> BVector; - #if HAVE_MPI typedef int GlobalId; typedef Dune::OwnerOverlapCopyCommunication<GlobalId> Communication; Communication comm(MPI_COMM_WORLD); - std::cout<<comm.communicator().rank()<<" "<<comm.communicator().size()<< - " "<<size<<std::endl; + std::cout << comm.communicator().rank() << " " << comm.communicator().size() << std::endl; int n; - BCRSMat mat = setupAnisotropic2d<BCRSMat>(N, comm.indexSet(), comm.communicator(), &n, .011); + Matrix mat = setupAnisotropic2d<typename Matrix::block_type>(N, comm.indexSet(), comm.communicator(), &n, .011); #else - BCRSMat mat; + Matrix mat; setupLaplacian(mat, N); #endif - BVector bv(mat.N()), cv(mat.N()); - typedef BVector::iterator VIter; + Vector bv(mat.N()), cv(mat.N()); int i=0; - for(VIter entry=bv.begin(); bv.end() != entry; ++entry) { - typedef BVector::block_type::iterator SIter; - for(SIter sentry=entry->begin(); sentry != entry->end(); ++sentry,++i) - *sentry=i; - } + Dune::Hybrid::ifElse(Dune::IsNumber<typename Vector::block_type>(), + [&](auto id) { + for(auto entry=bv.begin(); bv.end() != entry; ++entry, ++i) + *entry=i; + }, + [&](auto id) { + for(auto entry=bv.begin(); bv.end() != entry; ++entry) + for (auto sentry=id(entry)->begin(); sentry != id(entry)->end(); ++sentry,++i) + *sentry=i; + }); #if HAVE_MPI comm.remoteIndices().rebuild<false>(); comm.copyOwnerToAll(bv,bv); - Dune::OverlappingSchwarzOperator<BCRSMat,BVector,BVector,Communication> op(mat, comm); + Dune::OverlappingSchwarzOperator<Matrix,Vector,Vector,Communication> op(mat, comm); op.apply(bv, cv); storeMatrixMarket(mat, std::string("testmat"), comm); storeMatrixMarket(bv, std::string("testvec"), comm, false); @@ -81,8 +67,8 @@ int main(int argc, char** argv) storeMatrixMarket(bv, std::string("testvec")); #endif - BCRSMat mat1; - BVector bv1,cv1; + Matrix mat1; + Vector bv1,cv1; #if HAVE_MPI Communication comm1(MPI_COMM_WORLD); @@ -100,11 +86,9 @@ int main(int argc, char** argv) ++ret; std::cerr<<"matrix sizes do not match"<<std::endl; } - typedef BCRSMat::const_iterator RowIterator; - typedef BCRSMat::ConstColIterator ColIterator; - for(RowIterator row=mat.begin(), row1=mat1.begin(); row!=mat.end(); ++row, ++row1) - for(ColIterator col=row->begin(), col1=row1->begin(); col!= row->end(); ++col, ++col1) + for (auto row=mat.begin(), row1=mat1.begin(); row!=mat.end(); ++row, ++row1) + for (auto col=row->begin(), col1=row1->begin(); col!= row->end(); ++col, ++col1) { if(col.index()!=col1.index()) { std::cerr <<"Column indices do not match"<<std::endl; @@ -116,8 +100,8 @@ int main(int argc, char** argv) } } - for(VIter entry=bv.begin(), entry1=bv1.begin(); bv.end() != entry; ++entry, ++entry1) - if(*entry!=*entry1) + for (auto entry=bv.begin(), entry1=bv1.begin(); bv.end() != entry; ++entry, ++entry1) + if (*entry!=*entry1) { std::cerr<<"written and read vector do not match"<<std::endl; ++ret; @@ -126,7 +110,7 @@ int main(int argc, char** argv) cv1.resize(mat1.M()); #if HAVE_MPI - Dune::OverlappingSchwarzOperator<BCRSMat,BVector,BVector,Communication> op1(mat1, comm1); + Dune::OverlappingSchwarzOperator<Matrix,Vector,Vector,Communication> op1(mat1, comm1); op1.apply(bv1, cv1); if(comm1.indexSet()!=comm.indexSet()) @@ -135,21 +119,58 @@ int main(int argc, char** argv) ++ret; } #else - typedef Dune::MatrixAdapter<BCRSMat,BVector,BVector> Operator; + typedef Dune::MatrixAdapter<Matrix,Vector,Vector> Operator; Operator op1(mat1); op1.apply(bv1, cv1); #endif - for(VIter entry=cv.begin(), entry1=cv1.begin(); cv.end() != entry; ++entry, ++entry1) - if(*entry!=*entry1) + for (auto entry=cv.begin(), entry1=cv1.begin(); cv.end() != entry; ++entry, ++entry1) + if (*entry!=*entry1) { std::cerr<<"computed vectors do not match"<<std::endl; ++ret; } + return ret; +} + +int main(int argc, char** argv) +{ +#if HAVE_MPI + MPI_Init(&argc, &argv); + int size; + MPI_Comm_size(MPI_COMM_WORLD, &size); +#endif + const int BS=1; + int N=2; + + if(argc>1) + N = atoi(argv[1]); + std::cout<<"testing for N="<<N<<" BS="<<1<<std::endl; + + // Test scalar matrices and vectors + int ret = testMatrixMarket<Dune::BCRSMatrix<double>, Dune::BlockVector<double> >(N); + #if HAVE_MPI if(ret!=0) MPI_Abort(MPI_COMM_WORLD, ret); +#endif + + // Test block matrices and vectors with trivial blocks + typedef Dune::FieldMatrix<double,BS,BS> MatrixBlock; + typedef Dune::BCRSMatrix<MatrixBlock> BCRSMat; + typedef Dune::FieldVector<double,BS> VectorBlock; + typedef Dune::BlockVector<VectorBlock> BVector; + + ret = testMatrixMarket<BCRSMat, BVector>(N); + +#if HAVE_MPI + if(ret!=0) + MPI_Abort(MPI_COMM_WORLD, ret); +#endif + + +#if HAVE_MPI MPI_Finalize(); #endif } -- GitLab