dune-istl 2.12-git
Loading...
Searching...
No Matches
ilu.hh
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file LICENSE.md in module root
2// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
3// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4// vi: set et ts=4 sw=2 sts=2:
5#ifndef DUNE_ISTL_ILU_HH
6#define DUNE_ISTL_ILU_HH
7
8#include <sstream>
9
10#include <cmath>
11#include <complex>
12#include <map>
13#include <vector>
14
18
19#include "istlexception.hh"
20
25namespace Dune {
26
31 namespace ILU {
32
34 template<class M>
36 {
37 // iterator types
38 typedef typename M::RowIterator rowiterator;
39 typedef typename M::ColIterator coliterator;
40 typedef typename M::block_type block;
41
42 // implement left looking variant with stored inverse
43 rowiterator endi=A.end();
44 for (rowiterator i=A.begin(); i!=endi; ++i)
45 {
46 // coliterator is diagonal after the following loop
47 coliterator endij=(*i).end(); // end of row i
48 coliterator ij;
49
50 // eliminate entries left of diagonal; store L factor
51 for (ij=(*i).begin(); ij.index()<i.index(); ++ij)
52 {
53 // find A_jj which eliminates A_ij
54 coliterator jj = A[ij.index()].find(ij.index());
55
56 // compute L_ij = A_ij * A_jj^-1
57 Impl::asMatrix(*ij).rightmultiply(Impl::asMatrix(*jj));
58
59 // modify row
60 coliterator endjk=A[ij.index()].end(); // end of row j
61 coliterator jk=jj; ++jk;
62 coliterator ik=ij; ++ik;
63 while (ik!=endij && jk!=endjk)
64 if (ik.index()==jk.index())
65 {
66 block B(*jk);
67 Impl::asMatrix(B).leftmultiply(Impl::asMatrix(*ij));
68 *ik -= B;
69 ++ik; ++jk;
70 }
71 else
72 {
73 if (ik.index()<jk.index())
74 ++ik;
75 else
76 ++jk;
77 }
78 }
79
80 // invert pivot and store it in A
81 if (ij.index()!=i.index())
82 DUNE_THROW(ISTLError,"diagonal entry missing");
83 try {
84 Impl::asMatrix(*ij).invert(); // compute inverse of diagonal block
85 }
86 catch (Dune::FMatrixError & e) {
87 std::ostringstream sstream;
88 sstream << THROWSPEC(MatrixBlockError)
89 << THROWSPEC(MatrixBlockError)
90 << "ILU failed to invert matrix block A["
91 << i.index() << "][" << ij.index() << "]" << e.what();
93 ex.message(sstream.str());
94 ex.r = i.index();
95 ex.c = ij.index();
96 throw ex;
97 }
98 }
99 }
100
102 template<class M, class X, class Y>
103 void blockILUBacksolve (const M& A, X& v, const Y& d)
104 {
105 // iterator types
106 using rowiterator = typename M::ConstRowIterator;
107 using coliterator = typename M::ConstColIterator;
108 typedef typename Y::block_type dblock;
109 typedef typename X::block_type vblock;
110
111 // lower triangular solve
112 rowiterator endi=A.end();
113 for (rowiterator i=A.begin(); i!=endi; ++i)
114 {
115 // We need to be careful here: Directly using
116 // auto rhs = Impl::asVector(d[ i.index() ]);
117 // is not OK in case this is a proxy. Hence
118 // we first have to copy the value. Notice that
119 // this is still not OK, if the vector type itself returns
120 // proxy references.
121 dblock rhsValue(d[i.index()]);
122 auto&& rhs = Impl::asVector(rhsValue);
123 for (coliterator j=(*i).begin(); j.index()<i.index(); ++j)
124 Impl::asMatrix(*j).mmv(Impl::asVector(v[j.index()]),rhs);
125 Impl::asVector(v[i.index()]) = rhs; // Lii = I
126 }
127
130 auto rindex = [](auto it) { return std::prev(it.base()).index(); };
131 // upper triangular solve
132 rrowiterator rbegini{A.begin()};
133 for (rrowiterator i = rrowiterator{A.end()}; i!=rbegini; ++i)
134 {
135 // We need to be careful here: Directly using
136 // auto rhs = Impl::asVector(v[ i.index() ]);
137 // is not OK in case this is a proxy. Hence
138 // we first have to copy the value. Notice that
139 // this is still not OK, if the vector type itself returns
140 // proxy references.
141 auto row = rindex(i);
142 vblock rhsValue(v[row]);
143 auto&& rhs = Impl::asVector(rhsValue);
144 rcoliterator j;
145 for (j = rcoliterator{(*i).end()}; rindex(j)>row; ++j)
146 Impl::asMatrix(*j).mmv(Impl::asVector(v[rindex(j)]),rhs);
147 auto&& vi = Impl::asVector(v[row]);
148 Impl::asMatrix(*j).mv(rhs,vi); // diagonal stores inverse!
149 }
150 }
151
152 // recursive function template to access first entry of a matrix
153 template<class M>
154 typename M::field_type& firstMatrixElement (M& A,
155 [[maybe_unused]] typename std::enable_if_t<!Dune::IsNumber<M>::value>* sfinae = nullptr)
156 {
157 return firstMatrixElement(*(A.begin()->begin()));
158 }
159
160 template<class K>
162 [[maybe_unused]] typename std::enable_if_t<Dune::IsNumber<K>::value>* sfinae = nullptr)
163 {
164 return A;
165 }
166
167 template<class K, int n, int m>
169 {
170 return A[0][0];
171 }
172
179 template<class M>
180 void blockILUDecomposition (const M& A, int n, M& ILU)
181 {
182 // iterator types
183 typedef typename M::ColIterator coliterator;
184 typedef typename M::ConstRowIterator crowiterator;
185 typedef typename M::ConstColIterator ccoliterator;
186 typedef typename M::CreateIterator createiterator;
187 typedef typename M::field_type K;
188 typedef std::map<size_t, int> map;
189 typedef typename map::iterator mapiterator;
190
191 // symbolic factorization phase, store generation number in first matrix element
192 crowiterator endi=A.end();
193 createiterator ci=ILU.createbegin();
194 for (crowiterator i=A.begin(); i!=endi; ++i)
195 {
196 map rowpattern; // maps column index to generation
197
198 // initialize pattern with row of A
199 for (ccoliterator j=(*i).begin(); j!=(*i).end(); ++j)
200 rowpattern[j.index()] = 0;
201
202 // eliminate entries in row which are to the left of the diagonal
203 for (mapiterator ik=rowpattern.begin(); (*ik).first<i.index(); ++ik)
204 {
205 if ((*ik).second<n)
206 {
207 coliterator endk = ILU[(*ik).first].end(); // end of row k
208 coliterator kj = ILU[(*ik).first].find((*ik).first); // diagonal in k
209 for (++kj; kj!=endk; ++kj) // row k eliminates in row i
210 {
211 // we misuse the storage to store an int. If the field_type is std::complex, we have to access the real/abs part
212 // starting from C++11, we can use std::abs to always return a real value, even if it is double/float
213 using std::abs;
214 int generation = (int) Simd::lane(0, abs( firstMatrixElement(*kj) ));
215 if (generation<n)
216 {
217 mapiterator ij = rowpattern.find(kj.index());
218 if (ij==rowpattern.end())
219 {
220 rowpattern[kj.index()] = generation+1;
221 }
222 }
223 }
224 }
225 }
226
227 // create row
228 for (mapiterator ik=rowpattern.begin(); ik!=rowpattern.end(); ++ik)
229 ci.insert((*ik).first);
230 ++ci; // now row i exist
231
232 // write generation index into entries
233 coliterator endILUij = ILU[i.index()].end();;
234 for (coliterator ILUij=ILU[i.index()].begin(); ILUij!=endILUij; ++ILUij)
235 Simd::lane(0, firstMatrixElement(*ILUij)) = (Simd::Scalar<K>) rowpattern[ILUij.index()];
236 }
237
238 // copy entries of A
239 for (crowiterator i=A.begin(); i!=endi; ++i)
240 {
241 coliterator ILUij;
242 coliterator endILUij = ILU[i.index()].end();;
243 for (ILUij=ILU[i.index()].begin(); ILUij!=endILUij; ++ILUij)
244 (*ILUij) = 0; // clear row
245 ccoliterator Aij = (*i).begin();
246 ccoliterator endAij = (*i).end();
247 ILUij = ILU[i.index()].begin();
248 while (Aij!=endAij && ILUij!=endILUij)
249 {
250 if (Aij.index()==ILUij.index())
251 {
252 *ILUij = *Aij;
253 ++Aij; ++ILUij;
254 }
255 else
256 {
257 if (Aij.index()<ILUij.index())
258 ++Aij;
259 else
260 ++ILUij;
261 }
262 }
263 }
264
265 // call decomposition on pattern
267 }
268
270 template <class B, class Alloc = std::allocator<B>>
271 struct CRS
272 {
273 typedef B block_type;
274 typedef size_t size_type;
275
276 CRS() : nRows_( 0 ) {}
277
278 size_type rows() const { return nRows_; }
279
281 {
282 assert( rows_[ rows() ] != size_type(-1) );
283 return rows_[ rows() ];
284 }
285
286 void resize( const size_type nRows )
287 {
288 if( nRows_ != nRows )
289 {
290 nRows_ = nRows ;
291 rows_.resize( nRows_+1, size_type(-1) );
292 }
293 }
294
296 {
297 const size_type needed = values_.size() + nonZeros ;
298 if( values_.capacity() < needed )
299 {
300 const size_type estimate = needed * 1.1;
301 values_.reserve( estimate );
302 cols_.reserve( estimate );
303 }
304 }
305
306 void push_back( const block_type& value, const size_type index )
307 {
308 values_.push_back( value );
310 }
311
316 };
317
319 template<class M, class CRS, class InvVector>
320 void convertToCRS(const M& A, CRS& lower, CRS& upper, InvVector& inv )
321 {
322 typedef typename M :: size_type size_type;
323
324 lower.resize( A.N() );
325 upper.resize( A.N() );
326 inv.resize( A.N() );
327
328 // lower and upper triangular should store half of non zeros minus diagonal
329 const size_t memEstimate = (A.nonzeroes() - A.N())/2;
330
331 assert( A.nonzeroes() != 0 );
332 lower.reserveAdditional( memEstimate );
333 upper.reserveAdditional( memEstimate );
334
335 const auto endi = A.end();
336 size_type row = 0;
337 size_type colcount = 0;
338 lower.rows_[ 0 ] = colcount;
339 for (auto i=A.begin(); i!=endi; ++i, ++row)
340 {
341 const size_type iIndex = i.index();
342
343 // store entries left of diagonal
344 for (auto j=(*i).begin(); j.index() < iIndex; ++j )
345 {
346 lower.push_back( (*j), j.index() );
347 ++colcount;
348 }
349 lower.rows_[ iIndex+1 ] = colcount;
350 }
351
352 const auto rbegini = std::make_reverse_iterator(A.begin());
353 row = 0;
354 colcount = 0;
355 upper.rows_[ 0 ] = colcount ;
356 auto rindex = [](auto it) { return std::prev(it.base()).index(); };
357
358 // NOTE: upper and inv store entries in reverse row and col order,
359 // reverse here relative to ILU
360 for (auto i=std::make_reverse_iterator(A.end()); i!=rbegini; ++i, ++ row )
361 {
362 const auto rbeginij=std::make_reverse_iterator((*i).begin()); // end of row i
363
364 const size_type iIndex = rindex(i);
365
366 // store in reverse row order for faster access during backsolve
367 for (auto j=std::make_reverse_iterator((*i).end()); j != rbeginij; ++j )
368 {
369 const size_type jIndex = rindex(j);
370 if( rindex(j) == iIndex )
371 {
372 inv[ row ] = (*j);
373 break; // assuming consecutive ordering of A
374 }
375 else if ( rindex(j) >= rindex(i) )
376 {
377 upper.push_back( (*j), jIndex );
378 ++colcount ;
379 }
380 }
381 upper.rows_[ row+1 ] = colcount;
382 }
383 } // end convertToCRS
384
386 template<class CRS, class InvVector, class X, class Y>
387 void blockILUBacksolve (const CRS& lower,
388 const CRS& upper,
389 const InvVector& inv,
390 X& v, const Y& d)
391 {
392 // iterator types
393 typedef typename Y :: block_type dblock;
394 typedef typename X :: block_type vblock;
395 typedef typename X :: size_type size_type ;
396
397 const size_type iEnd = lower.rows();
398 const size_type lastRow = iEnd - 1;
399 if( iEnd != upper.rows() )
400 {
401 DUNE_THROW(ISTLError,"ILU::blockILUBacksolve: lower and upper rows must be the same");
402 }
403
404 // lower triangular solve
405 for( size_type i=0; i<iEnd; ++ i )
406 {
407 dblock rhsValue( d[ i ] );
408 auto&& rhs = Impl::asVector(rhsValue);
409 const size_type rowI = lower.rows_[ i ];
410 const size_type rowINext = lower.rows_[ i+1 ];
411
412 for( size_type col = rowI; col < rowINext; ++ col )
413 Impl::asMatrix(lower.values_[ col ]).mmv( Impl::asVector(v[ lower.cols_[ col ] ] ), rhs );
414
415 Impl::asVector(v[ i ]) = rhs; // Lii = I
416 }
417
418 // upper triangular solve
419 for( size_type i=0; i<iEnd; ++ i )
420 {
421 auto&& vBlock = Impl::asVector(v[ lastRow - i ]);
422 vblock rhsValue ( v[ lastRow - i ] );
423 auto&& rhs = Impl::asVector(rhsValue);
424 const size_type rowI = upper.rows_[ i ];
425 const size_type rowINext = upper.rows_[ i+1 ];
426
427 for( size_type col = rowI; col < rowINext; ++ col )
428 Impl::asMatrix(upper.values_[ col ]).mmv( Impl::asVector(v[ upper.cols_[ col ] ]), rhs );
429
430 // apply inverse and store result
431 Impl::asMatrix(inv[ i ]).mv(rhs, vBlock);
432 }
433 }
434
435 } // end namespace ILU
436
439} // end namespace
440
441#endif
Col col
Definition matrixmatrix.hh:351
void convertToCRS(const M &A, CRS &lower, CRS &upper, InvVector &inv)
convert ILU decomposition into CRS format for lower and upper triangular and inverse.
Definition ilu.hh:320
void blockILUBacksolve(const M &A, X &v, const Y &d)
LU backsolve with stored inverse.
Definition ilu.hh:103
M::field_type & firstMatrixElement(M &A, typename std::enable_if_t<!Dune::IsNumber< M >::value > *sfinae=nullptr)
Definition ilu.hh:154
void blockILU0Decomposition(M &A)
compute ILU decomposition of A. A is overwritten by its decomposition
Definition ilu.hh:35
void blockILUDecomposition(const M &A, int n, M &ILU)
Definition ilu.hh:180
static constexpr size_type M()
std::ptrdiff_t index() const
void message(const std::string &msg)
#define DUNE_THROW(E,...)
const char * what() const noexcept override
decltype(auto) lane(std::size_t l, V &&v)
typename Overloads::ScalarType< std::decay_t< V > >::type Scalar
a simple compressed row storage matrix class
Definition ilu.hh:272
std::vector< size_type > cols_
Definition ilu.hh:314
size_type nonZeros() const
Definition ilu.hh:280
void resize(const size_type nRows)
Definition ilu.hh:286
size_type rows() const
Definition ilu.hh:278
CRS()
Definition ilu.hh:276
void reserveAdditional(const size_type nonZeros)
Definition ilu.hh:295
B block_type
Definition ilu.hh:273
std::vector< block_type, Alloc > values_
Definition ilu.hh:313
size_type nRows_
Definition ilu.hh:315
size_t size_type
Definition ilu.hh:274
std::vector< size_type > rows_
Definition ilu.hh:312
void push_back(const block_type &value, const size_type index)
Definition ilu.hh:306
derive error class from the base class in common
Definition istlexception.hh:19
Error when performing an operation on a matrix block.
Definition istlexception.hh:52
int c
Definition istlexception.hh:54
int r
Definition istlexception.hh:54
T make_reverse_iterator(T... args)
T prev(T... args)
T push_back(T... args)
T reserve(T... args)
T resize(T... args)
T str(T... args)