ParallelOverlappingILU0_impl.hpp
Go to the documentation of this file.
1/*
2 Copyright 2015, 2022 Dr. Blatt - HPC-Simulation-Software & Services
3 Copyright 2015 Statoil AS
4
5 This file is part of the Open Porous Media project (OPM).
6
7 OPM is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 OPM is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with OPM. If not, see <http://www.gnu.org/licenses/>.
19*/
20
22
23#include <dune/common/version.hh>
24
25#include <dune/istl/ilu.hh>
26#include <dune/istl/owneroverlapcopy.hh>
27
28#include <opm/common/ErrorMacros.hpp>
29#include <opm/common/TimingMacros.hpp>
30
33
34namespace Opm
35{
36namespace detail
37{
38
40template<class M>
41void ghost_last_bilu0_decomposition (M& A, std::size_t interiorSize)
42{
43 // iterator types
44 using rowiterator = typename M::RowIterator;
45 using coliterator = typename M::ColIterator;
46 using block = typename M::block_type;
47
48 // implement left looking variant with stored inverse
49 for (rowiterator i = A.begin(); i.index() < interiorSize; ++i)
50 {
51 // coliterator is diagonal after the following loop
52 coliterator endij=(*i).end(); // end of row i
53 coliterator ij;
54
55 // eliminate entries left of diagonal; store L factor
56 for (ij=(*i).begin(); ij.index()<i.index(); ++ij)
57 {
58 // find A_jj which eliminates A_ij
59 coliterator jj = A[ij.index()].find(ij.index());
60
61 // compute L_ij = A_jj^-1 * A_ij
62 (*ij).rightmultiply(*jj);
63
64 // modify row
65 coliterator endjk=A[ij.index()].end(); // end of row j
66 coliterator jk=jj; ++jk;
67 coliterator ik=ij; ++ik;
68 while (ik!=endij && jk!=endjk)
69 if (ik.index()==jk.index())
70 {
71 block B(*jk);
72 B.leftmultiply(*ij);
73 *ik -= B;
74 ++ik; ++jk;
75 }
76 else
77 {
78 if (ik.index()<jk.index())
79 ++ik;
80 else
81 ++jk;
82 }
83 }
84
85 // invert pivot and store it in A
86 if (ij.index()!=i.index())
87 DUNE_THROW(Dune::ISTLError,"diagonal entry missing");
88 try {
89 (*ij).invert(); // compute inverse of diagonal block
90 }
91 catch (Dune::FMatrixError & e) {
92 DUNE_THROW(Dune::ISTLError,"ILU failed to invert matrix block");
93 }
94 }
95}
96
98template<class M, class CRS, class InvVector>
99void convertToCRS(const M& A, CRS& lower, CRS& upper, InvVector& inv)
100{
101 OPM_TIMEBLOCK(convertToCRS);
102 // No need to do anything for 0 rows. Return to prevent indexing a
103 // a zero sized array.
104 if ( A.N() == 0 )
105 {
106 return;
107 }
108
109 using size_type = typename M :: size_type;
110
111 lower.clear();
112 upper.clear();
113 inv.clear();
114 lower.resize( A.N() );
115 upper.resize( A.N() );
116 inv.resize( A.N() );
117
118 // Count the lower and upper matrix entries.
119 size_type numLower = 0;
120 size_type numUpper = 0;
121 const auto endi = A.end();
122 for (auto i = A.begin(); i != endi; ++i) {
123 const size_type iIndex = i.index();
124 size_type numLowerRow = 0;
125 for (auto j = (*i).begin(); j.index() < iIndex; ++j) {
126 ++numLowerRow;
127 }
128 numLower += numLowerRow;
129 numUpper += (*i).size() - numLowerRow - 1;
130 }
131 assert(numLower + numUpper + A.N() == A.nonzeroes());
132
133 lower.reserveAdditional( numLower );
134
135 // implement left looking variant with stored inverse
136 size_type row = 0;
137 size_type colcount = 0;
138 lower.rows_[ 0 ] = colcount;
139 for (auto i=A.begin(); i!=endi; ++i, ++row)
140 {
141 const size_type iIndex = i.index();
142
143 // eliminate entries left of diagonal; store L factor
144 for (auto j=(*i).begin(); j.index() < iIndex; ++j )
145 {
146 lower.push_back( (*j), j.index() );
147 ++colcount;
148 }
149 lower.rows_[ iIndex+1 ] = colcount;
150 }
151
152 assert(colcount == numLower);
153
154 const auto rendi = A.beforeBegin();
155 row = 0;
156 colcount = 0;
157 upper.rows_[ 0 ] = colcount ;
158
159 upper.reserveAdditional( numUpper );
160
161 // NOTE: upper and inv store entries in reverse order, reverse here
162 // relative to ILU
163 for (auto i=A.beforeEnd(); i!=rendi; --i, ++ row )
164 {
165 const size_type iIndex = i.index();
166
167 // store in reverse row order
168 // eliminate entries left of diagonal; store L factor
169 for (auto j=(*i).beforeEnd(); j.index()>=iIndex; --j )
170 {
171 const size_type jIndex = j.index();
172 if( j.index() == iIndex )
173 {
174 inv[ row ] = (*j);
175 break;
176 }
177 else if ( j.index() >= i.index() )
178 {
179 upper.push_back( (*j), jIndex );
180 ++colcount ;
181 }
182 }
183 upper.rows_[ row+1 ] = colcount;
184 }
185 assert(colcount == numUpper);
186}
187
188template <class PI>
189size_t set_interiorSize( [[maybe_unused]] size_t N, size_t interiorSize, [[maybe_unused]] const PI& comm)
190{
191 return interiorSize;
192}
193
194#if HAVE_MPI
195template<>
196size_t set_interiorSize(size_t N, size_t interiorSize, const Dune::OwnerOverlapCopyCommunication<int,int>& comm)
197{
198 if (interiorSize<N)
199 return interiorSize;
200 auto indexSet = comm.indexSet();
201
202 size_t new_is = 0;
203 for (auto idx = indexSet.begin(); idx!=indexSet.end(); ++idx)
204 {
205 if (idx->local().attribute()==1)
206 {
207 auto loc = idx->local().local();
208 if (loc > new_is) {
209 new_is = loc;
210 }
211 }
212 }
213 return new_is + 1;
214}
215#endif
216
217} // end namespace detail
218
219
220template<class Matrix, class Domain, class Range, class ParallelInfoT>
221Dune::SolverCategory::Category
223{
224 return std::is_same_v<ParallelInfoT, Dune::Amg::SequentialInformation> ?
225 Dune::SolverCategory::sequential : Dune::SolverCategory::overlapping;
226}
227
228template<class Matrix, class Domain, class Range, class ParallelInfoT>
230ParallelOverlappingILU0(const Matrix& A,
231 const int n, const field_type w,
232 MILU_VARIANT milu, bool redblack,
233 bool reorder_sphere)
234 : lower_(),
235 upper_(),
236 inv_(),
237 comm_(nullptr), w_(w),
238 relaxation_( std::abs( w - 1.0 ) > 1e-15 ),
239 A_(&reinterpret_cast<const Matrix&>(A)), iluIteration_(n),
240 milu_(milu), redBlack_(redblack), reorderSphere_(reorder_sphere)
241{
242 interiorSize_ = A.N();
243 // BlockMatrix is a Subclass of FieldMatrix that just adds
244 // methods. Therefore this cast should be safe.
245 update();
246}
247
248template<class Matrix, class Domain, class Range, class ParallelInfoT>
250ParallelOverlappingILU0(const Matrix& A,
251 const ParallelInfo& comm, const int n, const field_type w,
252 MILU_VARIANT milu, bool redblack,
253 bool reorder_sphere)
254 : lower_(),
255 upper_(),
256 inv_(),
257 comm_(&comm), w_(w),
258 relaxation_( std::abs( w - 1.0 ) > 1e-15 ),
259 A_(&reinterpret_cast<const Matrix&>(A)), iluIteration_(n),
260 milu_(milu), redBlack_(redblack), reorderSphere_(reorder_sphere)
261{
262 interiorSize_ = A.N();
263 // BlockMatrix is a Subclass of FieldMatrix that just adds
264 // methods. Therefore this cast should be safe.
265 update();
266}
267
268template<class Matrix, class Domain, class Range, class ParallelInfoT>
270ParallelOverlappingILU0(const Matrix& A,
271 const field_type w, MILU_VARIANT milu, bool redblack,
272 bool reorder_sphere)
273 : ParallelOverlappingILU0( A, 0, w, milu, redblack, reorder_sphere )
274{}
275
276template<class Matrix, class Domain, class Range, class ParallelInfoT>
278ParallelOverlappingILU0(const Matrix& A,
279 const ParallelInfo& comm, const field_type w,
280 MILU_VARIANT milu, bool redblack,
281 bool reorder_sphere)
282 : lower_(),
283 upper_(),
284 inv_(),
285 comm_(&comm), w_(w),
286 relaxation_( std::abs( w - 1.0 ) > 1e-15 ),
287 A_(&reinterpret_cast<const Matrix&>(A)), iluIteration_(0),
288 milu_(milu), redBlack_(redblack), reorderSphere_(reorder_sphere)
289{
290 interiorSize_ = A.N();
291 // BlockMatrix is a Subclass of FieldMatrix that just adds
292 // methods. Therefore this cast should be safe.
293 update();
294}
295
296template<class Matrix, class Domain, class Range, class ParallelInfoT>
298ParallelOverlappingILU0(const Matrix& A,
299 const ParallelInfo& comm,
300 const field_type w, MILU_VARIANT milu,
301 size_type interiorSize, bool redblack,
302 bool reorder_sphere)
303 : lower_(),
304 upper_(),
305 inv_(),
306 comm_(&comm), w_(w),
307 relaxation_( std::abs( w - 1.0 ) > 1e-15 ),
308 interiorSize_(interiorSize),
309 A_(&reinterpret_cast<const Matrix&>(A)), iluIteration_(0),
310 milu_(milu), redBlack_(redblack), reorderSphere_(reorder_sphere)
311{
312 // BlockMatrix is a Subclass of FieldMatrix that just adds
313 // methods. Therefore this cast should be safe.
314 update( );
315}
316
317template<class Matrix, class Domain, class Range, class ParallelInfoT>
319apply (Domain& v, const Range& d)
320{
321 OPM_TIMEBLOCK(apply);
322 Range& md = reorderD(d);
323 Domain& mv = reorderV(v);
324
325 // iterator types
326 using dblock = typename Range ::block_type;
327 using vblock = typename Domain::block_type;
328
329 const size_type iEnd = lower_.rows();
330 const size_type lastRow = iEnd - 1;
331 size_type upperLoopStart = iEnd - interiorSize_;
332 size_type lowerLoopEnd = interiorSize_;
333 if (iEnd != upper_.rows())
334 {
335 OPM_THROW(std::logic_error,"ILU: number of lower and upper rows must be the same");
336 }
337
338 // lower triangular solve
339 for (size_type i = 0; i < lowerLoopEnd; ++i)
340 {
341 dblock rhs( md[ i ] );
342 const size_type rowI = lower_.rows_[ i ];
343 const size_type rowINext = lower_.rows_[ i+1 ];
344
345 for (size_type col = rowI; col < rowINext; ++col)
346 {
347 lower_.values_[ col ].mmv( mv[ lower_.cols_[ col ] ], rhs );
348 }
349
350 mv[ i ] = rhs; // Lii = I
351 }
352
353 for (size_type i = upperLoopStart; i < iEnd; ++i)
354 {
355 vblock& vBlock = mv[ lastRow - i ];
356 vblock rhs ( vBlock );
357 const size_type rowI = upper_.rows_[ i ];
358 const size_type rowINext = upper_.rows_[ i+1 ];
359
360 for (size_type col = rowI; col < rowINext; ++col)
361 {
362 upper_.values_[ col ].mmv( mv[ upper_.cols_[ col ] ], rhs );
363 }
364
365 // apply inverse and store result
366 inv_[ i ].mv( rhs, vBlock);
367 }
368
369 copyOwnerToAll( mv );
370
371 if( relaxation_ ) {
372 mv *= w_;
373 }
374 reorderBack(mv, v);
375}
376
377template<class Matrix, class Domain, class Range, class ParallelInfoT>
378template<class V>
380copyOwnerToAll(V& v) const
381{
382 if( comm_ ) {
383 comm_->copyOwnerToAll(v, v);
384 }
385}
386
387template<class Matrix, class Domain, class Range, class ParallelInfoT>
389update()
390{
391 OPM_TIMEBLOCK(update);
392 // (For older DUNE versions the communicator might be
393 // invalid if redistribution in AMG happened on the coarset level.
394 // Therefore we check for nonzero size
395 if (comm_ && comm_->communicator().size() <= 0)
396 {
397 if (A_->N() > 0)
398 {
399 OPM_THROW(std::logic_error, "Expected a matrix with zero rows for an invalid communicator.");
400 }
401 else
402 {
403 // simply set the communicator to null
404 comm_ = nullptr;
405 }
406 }
407
408 int ilu_setup_successful = 1;
409 std::string message;
410 const int rank = comm_ ? comm_->communicator().rank() : 0;
411
412 if (redBlack_)
413 {
414 using Graph = Dune::Amg::MatrixGraph<const Matrix>;
415 Graph graph(*A_);
416 auto colorsTuple = colorVerticesWelshPowell(graph);
417 const auto& colors = std::get<0>(colorsTuple);
418 const auto& verticesPerColor = std::get<2>(colorsTuple);
419 auto noColors = std::get<1>(colorsTuple);
420 if ( reorderSphere_ )
421 {
422 ordering_ = reorderVerticesSpheres(colors, noColors, verticesPerColor,
423 graph, 0);
424 }
425 else
426 {
427 ordering_ = reorderVerticesPreserving(colors, noColors, verticesPerColor,
428 graph);
429 }
430 }
431
432 std::vector<std::size_t> inverseOrdering(ordering_.size());
433 std::size_t index = 0;
434 for (const auto newIndex : ordering_)
435 {
436 inverseOrdering[newIndex] = index++;
437 }
438
439 try
440 {
441 OPM_TIMEBLOCK(iluDecomposition);
442 if (iluIteration_ == 0) {
443
444 if (comm_) {
445 interiorSize_ = detail::set_interiorSize(A_->N(), interiorSize_, *comm_);
446 }
447
448 // create ILU-0 decomposition
449 if (ordering_.empty())
450 {
451 if (ILU_) {
452 OPM_TIMEBLOCK(iluDecompositionMakeMatrix);
453 // The ILU_ matrix is already a copy with the same
454 // sparse structure as A_, but the values of A_ may
455 // have changed, so we must copy all elements.
456 for (std::size_t row = 0; row < A_->N(); ++row) {
457 const auto& Arow = (*A_)[row];
458 auto& ILUrow = (*ILU_)[row];
459 auto Ait = Arow.begin();
460 auto Iit = ILUrow.begin();
461 for (; Ait != Arow.end(); ++Ait, ++Iit) {
462 *Iit = *Ait;
463 }
464 }
465 } else {
466 // First call, must duplicate matrix.
467 ILU_ = std::make_unique<Matrix>(*A_);
468 }
469 }
470 else
471 {
472 ILU_ = std::make_unique<Matrix>(A_->N(), A_->M(),
473 A_->nonzeroes(), Matrix::row_wise);
474 auto& newA = *ILU_;
475 // Create sparsity pattern
476 for (auto iter = newA.createbegin(), iend = newA.createend(); iter != iend; ++iter)
477 {
478 const auto& row = (*A_)[inverseOrdering[iter.index()]];
479 for (auto col = row.begin(), cend = row.end(); col != cend; ++col)
480 {
481 iter.insert(ordering_[col.index()]);
482 }
483 }
484 // Copy values
485 for (auto iter = A_->begin(), iend = A_->end(); iter != iend; ++iter)
486 {
487 auto& newRow = newA[ordering_[iter.index()]];
488 for (auto col = iter->begin(), cend = iter->end(); col != cend; ++col)
489 {
490 newRow[ordering_[col.index()]] = *col;
491 }
492 }
493 }
494
495 switch (milu_)
496 {
499 break;
501 detail::milu0_decomposition ( *ILU_, detail::identityFunctor<typename Matrix::field_type>,
502 detail::signFunctor<typename Matrix::field_type> );
503 break;
505 detail::milu0_decomposition ( *ILU_, detail::absFunctor<typename Matrix::field_type>,
506 detail::signFunctor<typename Matrix::field_type> );
507 break;
509 detail::milu0_decomposition ( *ILU_, detail::identityFunctor<typename Matrix::field_type>,
510 detail::isPositiveFunctor<typename Matrix::field_type> );
511 break;
512 default:
513 if (interiorSize_ == A_->N())
514#if DUNE_VERSION_LT(DUNE_GRID, 2, 8)
515 bilu0_decomposition( *ILU_ );
516#else
517 Dune::ILU::blockILU0Decomposition( *ILU_ );
518#endif
519 else
520 detail::ghost_last_bilu0_decomposition(*ILU_, interiorSize_);
521 break;
522 }
523 }
524 else {
525 // create ILU-n decomposition
526 ILU_ = std::make_unique<Matrix>(A_->N(), A_->M(), Matrix::row_wise);
527 std::unique_ptr<detail::Reorderer> reorderer, inverseReorderer;
528 if (ordering_.empty())
529 {
530 reorderer.reset(new detail::NoReorderer());
531 inverseReorderer.reset(new detail::NoReorderer());
532 }
533 else
534 {
535 reorderer.reset(new detail::RealReorderer(ordering_));
536 inverseReorderer.reset(new detail::RealReorderer(inverseOrdering));
537 }
538
539 milun_decomposition( *A_, iluIteration_, milu_, *ILU_, *reorderer, *inverseReorderer );
540 }
541 }
542 catch (const Dune::MatrixBlockError& error)
543 {
544 message = error.what();
545 std::cerr << "Exception occurred on process " << rank << " during " <<
546 "setup of ILU0 preconditioner with message: "
547 << message<<std::endl;
548 ilu_setup_successful = 0;
549 }
550
551 // Check whether there was a problem on some process
552 const bool parallel_failure = comm_ && comm_->communicator().min(ilu_setup_successful) == 0;
553 const bool local_failure = ilu_setup_successful == 0;
554 if (local_failure || parallel_failure)
555 {
556 throw Dune::MatrixBlockError();
557 }
558
559 // store ILU in simple CRS format
560 detail::convertToCRS(*ILU_, lower_, upper_, inv_);
561}
562
563template<class Matrix, class Domain, class Range, class ParallelInfoT>
565reorderD(const Range& d)
566{
567 if (ordering_.empty())
568 {
569 // As d is non-const in the apply method of the
570 // solver casting away constness in this particular
571 // setting is not undefined. It is ugly though but due
572 // to the preconditioner interface of dune-istl.
573 return const_cast<Range&>(d);
574 }
575 else
576 {
577 reorderedD_.resize(d.size());
578 std::size_t i = 0;
579 for (const auto index : ordering_)
580 {
581 reorderedD_[index] = d[i++];
582 }
583 return reorderedD_;
584 }
585}
586
587template<class Matrix, class Domain, class Range, class ParallelInfoT>
589reorderV(Domain& v)
590{
591 if (ordering_.empty())
592 {
593 return v;
594 }
595 else
596 {
597 reorderedV_.resize(v.size());
598 std::size_t i = 0;
599 for (const auto index : ordering_)
600 {
601 reorderedV_[index] = v[i++];
602 }
603 return reorderedV_;
604 }
605}
606
607template<class Matrix, class Domain, class Range, class ParallelInfoT>
609reorderBack(const Range& reorderedV, Range& v)
610{
611 if (!ordering_.empty())
612 {
613 std::size_t i = 0;
614 for (const auto index : ordering_)
615 {
616 v[i++] = reorderedV[index];
617 }
618 }
619}
620
621} // end namespace Opm
A two-step version of an overlapping Schwarz preconditioner using one step ILU0 as.
Definition: ParallelOverlappingILU0.hpp:131
ParallelOverlappingILU0(const Matrix &A, const int n, const field_type w, MILU_VARIANT milu, bool redblack=false, bool reorder_sphere=true)
Constructor.
Definition: ParallelOverlappingILU0_impl.hpp:230
Domain & reorderV(Domain &v)
Reorder V if needed and return a reference to it.
Definition: ParallelOverlappingILU0_impl.hpp:589
size_type interiorSize_
Definition: ParallelOverlappingILU0.hpp:350
void reorderBack(const Range &reorderedV, Range &v)
Definition: ParallelOverlappingILU0_impl.hpp:609
Range & reorderD(const Range &d)
Reorder D if needed and return a reference to it.
Definition: ParallelOverlappingILU0_impl.hpp:565
void copyOwnerToAll(V &v) const
Definition: ParallelOverlappingILU0_impl.hpp:380
Dune::SolverCategory::Category category() const override
Definition: ParallelOverlappingILU0_impl.hpp:222
void update() override
Definition: ParallelOverlappingILU0_impl.hpp:389
void apply(Domain &v, const Range &d) override
Apply the preconditoner.
Definition: ParallelOverlappingILU0_impl.hpp:319
typename matrix_type::size_type size_type
Definition: ParallelOverlappingILU0.hpp:145
typename Domain::field_type field_type
The field type of the preconditioner.
Definition: ParallelOverlappingILU0.hpp:142
void milu0_decomposition(M &A, FieldFunct< M > absFunctor=signFunctor< typename M::field_type >, FieldFunct< M > signFunctor=oneFunctor< typename M::field_type >, std::vector< typename M::block_type > *diagonal=nullptr)
void convertToCRS(const M &A, CRS &lower, CRS &upper, InvVector &inv)
compute ILU decomposition of A. A is overwritten by its decomposition
Definition: ParallelOverlappingILU0_impl.hpp:99
void milun_decomposition(const M &A, int n, MILU_VARIANT milu, M &ILU, Reorderer &ordering, Reorderer &inverseOrdering)
size_t set_interiorSize(size_t N, size_t interiorSize, const PI &comm)
Definition: ParallelOverlappingILU0_impl.hpp:189
void ghost_last_bilu0_decomposition(M &A, std::size_t interiorSize)
Compute Blocked ILU0 decomposition, when we know junk ghost rows are located at the end of A.
Definition: ParallelOverlappingILU0_impl.hpp:41
Definition: blackoilboundaryratevector.hh:37
MILU_VARIANT
Definition: MILU.hpp:34
@ MILU_1
sum(dropped entries)
@ MILU_2
sum(dropped entries)
@ MILU_3
sum(|dropped entries|)
@ MILU_4
sum(dropped entries)
std::vector< std::size_t > reorderVerticesPreserving(const std::vector< int > &colors, int noColors, const std::vector< std::size_t > &verticesPerColor, const Graph &graph)
! Reorder colored graph preserving order of vertices with the same color.
Definition: GraphColoring.hpp:169
std::vector< std::size_t > reorderVerticesSpheres(const std::vector< int > &colors, int noColors, const std::vector< std::size_t > &verticesPerColor, const Graph &graph, typename Graph::VertexDescriptor root)
! Reorder Vetrices in spheres
Definition: GraphColoring.hpp:189
std::tuple< std::vector< int >, int, std::vector< std::size_t > > colorVerticesWelshPowell(const Graph &graph)
Color the vertices of graph.
Definition: GraphColoring.hpp:113
Definition: MILU.hpp:76
Definition: MILU.hpp:84