Dune Core Modules (2.9.0)

ilu.hh
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (C) 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 <cmath>
9 #include <complex>
10 #include <map>
11 #include <vector>
12 
13 #include <dune/common/fmatrix.hh>
16 
17 #include "istlexception.hh"
18 
23 namespace Dune {
24 
29  namespace ILU {
30 
32  template<class M>
34  {
35  // iterator types
36  typedef typename M::RowIterator rowiterator;
37  typedef typename M::ColIterator coliterator;
38  typedef typename M::block_type block;
39 
40  // implement left looking variant with stored inverse
41  rowiterator endi=A.end();
42  for (rowiterator i=A.begin(); i!=endi; ++i)
43  {
44  // coliterator is diagonal after the following loop
45  coliterator endij=(*i).end(); // end of row i
46  coliterator ij;
47 
48  // eliminate entries left of diagonal; store L factor
49  for (ij=(*i).begin(); ij.index()<i.index(); ++ij)
50  {
51  // find A_jj which eliminates A_ij
52  coliterator jj = A[ij.index()].find(ij.index());
53 
54  // compute L_ij = A_jj^-1 * A_ij
55  Impl::asMatrix(*ij).rightmultiply(Impl::asMatrix(*jj));
56 
57  // modify row
58  coliterator endjk=A[ij.index()].end(); // end of row j
59  coliterator jk=jj; ++jk;
60  coliterator ik=ij; ++ik;
61  while (ik!=endij && jk!=endjk)
62  if (ik.index()==jk.index())
63  {
64  block B(*jk);
65  Impl::asMatrix(B).leftmultiply(Impl::asMatrix(*ij));
66  *ik -= B;
67  ++ik; ++jk;
68  }
69  else
70  {
71  if (ik.index()<jk.index())
72  ++ik;
73  else
74  ++jk;
75  }
76  }
77 
78  // invert pivot and store it in A
79  if (ij.index()!=i.index())
80  DUNE_THROW(ISTLError,"diagonal entry missing");
81  try {
82  Impl::asMatrix(*ij).invert(); // compute inverse of diagonal block
83  }
84  catch (Dune::FMatrixError & e) {
85  DUNE_THROW(MatrixBlockError, "ILU failed to invert matrix block A["
86  << i.index() << "][" << ij.index() << "]" << e.what();
87  th__ex.r=i.index(); th__ex.c=ij.index(););
88  }
89  }
90  }
91 
93  template<class M, class X, class Y>
94  void blockILUBacksolve (const M& A, X& v, const Y& d)
95  {
96  // iterator types
97  typedef typename M::ConstRowIterator rowiterator;
98  typedef typename M::ConstColIterator coliterator;
99  typedef typename Y::block_type dblock;
100  typedef typename X::block_type vblock;
101 
102  // lower triangular solve
103  rowiterator endi=A.end();
104  for (rowiterator i=A.begin(); i!=endi; ++i)
105  {
106  // We need to be careful here: Directly using
107  // auto rhs = Impl::asVector(d[ i.index() ]);
108  // is not OK in case this is a proxy. Hence
109  // we first have to copy the value. Notice that
110  // this is still not OK, if the vector type itself returns
111  // proxy references.
112  dblock rhsValue(d[i.index()]);
113  auto&& rhs = Impl::asVector(rhsValue);
114  for (coliterator j=(*i).begin(); j.index()<i.index(); ++j)
115  Impl::asMatrix(*j).mmv(Impl::asVector(v[j.index()]),rhs);
116  Impl::asVector(v[i.index()]) = rhs; // Lii = I
117  }
118 
119  // upper triangular solve
120  rowiterator rendi=A.beforeBegin();
121  for (rowiterator i=A.beforeEnd(); i!=rendi; --i)
122  {
123  // We need to be careful here: Directly using
124  // auto rhs = Impl::asVector(v[ i.index() ]);
125  // is not OK in case this is a proxy. Hence
126  // we first have to copy the value. Notice that
127  // this is still not OK, if the vector type itself returns
128  // proxy references.
129  vblock rhsValue(v[i.index()]);
130  auto&& rhs = Impl::asVector(rhsValue);
131  coliterator j;
132  for (j=(*i).beforeEnd(); j.index()>i.index(); --j)
133  Impl::asMatrix(*j).mmv(Impl::asVector(v[j.index()]),rhs);
134  auto&& vi = Impl::asVector(v[i.index()]);
135  Impl::asMatrix(*j).mv(rhs,vi); // diagonal stores inverse!
136  }
137  }
138 
139  // recursive function template to access first entry of a matrix
140  template<class M>
141  typename M::field_type& firstMatrixElement (M& A,
142  [[maybe_unused]] typename std::enable_if_t<!Dune::IsNumber<M>::value>* sfinae = nullptr)
143  {
144  return firstMatrixElement(*(A.begin()->begin()));
145  }
146 
147  template<class K>
148  K& firstMatrixElement (K& A,
149  [[maybe_unused]] typename std::enable_if_t<Dune::IsNumber<K>::value>* sfinae = nullptr)
150  {
151  return A;
152  }
153 
154  template<class K, int n, int m>
155  K& firstMatrixElement (FieldMatrix<K,n,m>& A)
156  {
157  return A[0][0];
158  }
159 
166  template<class M>
167  void blockILUDecomposition (const M& A, int n, M& ILU)
168  {
169  // iterator types
170  typedef typename M::ColIterator coliterator;
171  typedef typename M::ConstRowIterator crowiterator;
172  typedef typename M::ConstColIterator ccoliterator;
173  typedef typename M::CreateIterator createiterator;
174  typedef typename M::field_type K;
175  typedef std::map<size_t, int> map;
176  typedef typename map::iterator mapiterator;
177 
178  // symbolic factorization phase, store generation number in first matrix element
179  crowiterator endi=A.end();
180  createiterator ci=ILU.createbegin();
181  for (crowiterator i=A.begin(); i!=endi; ++i)
182  {
183  map rowpattern; // maps column index to generation
184 
185  // initialize pattern with row of A
186  for (ccoliterator j=(*i).begin(); j!=(*i).end(); ++j)
187  rowpattern[j.index()] = 0;
188 
189  // eliminate entries in row which are to the left of the diagonal
190  for (mapiterator ik=rowpattern.begin(); (*ik).first<i.index(); ++ik)
191  {
192  if ((*ik).second<n)
193  {
194  coliterator endk = ILU[(*ik).first].end(); // end of row k
195  coliterator kj = ILU[(*ik).first].find((*ik).first); // diagonal in k
196  for (++kj; kj!=endk; ++kj) // row k eliminates in row i
197  {
198  // we misuse the storage to store an int. If the field_type is std::complex, we have to access the real/abs part
199  // starting from C++11, we can use std::abs to always return a real value, even if it is double/float
200  using std::abs;
201  int generation = (int) Simd::lane(0, abs( firstMatrixElement(*kj) ));
202  if (generation<n)
203  {
204  mapiterator ij = rowpattern.find(kj.index());
205  if (ij==rowpattern.end())
206  {
207  rowpattern[kj.index()] = generation+1;
208  }
209  }
210  }
211  }
212  }
213 
214  // create row
215  for (mapiterator ik=rowpattern.begin(); ik!=rowpattern.end(); ++ik)
216  ci.insert((*ik).first);
217  ++ci; // now row i exist
218 
219  // write generation index into entries
220  coliterator endILUij = ILU[i.index()].end();;
221  for (coliterator ILUij=ILU[i.index()].begin(); ILUij!=endILUij; ++ILUij)
222  Simd::lane(0, firstMatrixElement(*ILUij)) = (Simd::Scalar<K>) rowpattern[ILUij.index()];
223  }
224 
225  // copy entries of A
226  for (crowiterator i=A.begin(); i!=endi; ++i)
227  {
228  coliterator ILUij;
229  coliterator endILUij = ILU[i.index()].end();;
230  for (ILUij=ILU[i.index()].begin(); ILUij!=endILUij; ++ILUij)
231  (*ILUij) = 0; // clear row
232  ccoliterator Aij = (*i).begin();
233  ccoliterator endAij = (*i).end();
234  ILUij = ILU[i.index()].begin();
235  while (Aij!=endAij && ILUij!=endILUij)
236  {
237  if (Aij.index()==ILUij.index())
238  {
239  *ILUij = *Aij;
240  ++Aij; ++ILUij;
241  }
242  else
243  {
244  if (Aij.index()<ILUij.index())
245  ++Aij;
246  else
247  ++ILUij;
248  }
249  }
250  }
251 
252  // call decomposition on pattern
254  }
255 
257  template <class B, class Alloc = std::allocator<B>>
258  struct CRS
259  {
260  typedef B block_type;
261  typedef size_t size_type;
262 
263  CRS() : nRows_( 0 ) {}
264 
265  size_type rows() const { return nRows_; }
266 
267  size_type nonZeros() const
268  {
269  assert( rows_[ rows() ] != size_type(-1) );
270  return rows_[ rows() ];
271  }
272 
273  void resize( const size_type nRows )
274  {
275  if( nRows_ != nRows )
276  {
277  nRows_ = nRows ;
278  rows_.resize( nRows_+1, size_type(-1) );
279  }
280  }
281 
282  void reserveAdditional( const size_type nonZeros )
283  {
284  const size_type needed = values_.size() + nonZeros ;
285  if( values_.capacity() < needed )
286  {
287  const size_type estimate = needed * 1.1;
288  values_.reserve( estimate );
289  cols_.reserve( estimate );
290  }
291  }
292 
293  void push_back( const block_type& value, const size_type index )
294  {
295  values_.push_back( value );
296  cols_.push_back( index );
297  }
298 
299  std::vector< size_type > rows_;
300  std::vector< block_type, Alloc> values_;
301  std::vector< size_type > cols_;
302  size_type nRows_;
303  };
304 
306  template<class M, class CRS, class InvVector>
307  void convertToCRS(const M& A, CRS& lower, CRS& upper, InvVector& inv )
308  {
309  typedef typename M :: size_type size_type;
310 
311  lower.resize( A.N() );
312  upper.resize( A.N() );
313  inv.resize( A.N() );
314 
315  // lower and upper triangular should store half of non zeros minus diagonal
316  const size_t memEstimate = (A.nonzeroes() - A.N())/2;
317 
318  assert( A.nonzeroes() != 0 );
319  lower.reserveAdditional( memEstimate );
320  upper.reserveAdditional( memEstimate );
321 
322  const auto endi = A.end();
323  size_type row = 0;
324  size_type colcount = 0;
325  lower.rows_[ 0 ] = colcount;
326  for (auto i=A.begin(); i!=endi; ++i, ++row)
327  {
328  const size_type iIndex = i.index();
329 
330  // store entries left of diagonal
331  for (auto j=(*i).begin(); j.index() < iIndex; ++j )
332  {
333  lower.push_back( (*j), j.index() );
334  ++colcount;
335  }
336  lower.rows_[ iIndex+1 ] = colcount;
337  }
338 
339  const auto rendi = A.beforeBegin();
340  row = 0;
341  colcount = 0;
342  upper.rows_[ 0 ] = colcount ;
343 
344  // NOTE: upper and inv store entries in reverse row and col order,
345  // reverse here relative to ILU
346  for (auto i=A.beforeEnd(); i!=rendi; --i, ++ row )
347  {
348  const auto endij=(*i).beforeBegin(); // end of row i
349 
350  const size_type iIndex = i.index();
351 
352  // store in reverse row order for faster access during backsolve
353  for (auto j=(*i).beforeEnd(); j != endij; --j )
354  {
355  const size_type jIndex = j.index();
356  if( j.index() == iIndex )
357  {
358  inv[ row ] = (*j);
359  break; // assuming consecutive ordering of A
360  }
361  else if ( j.index() >= i.index() )
362  {
363  upper.push_back( (*j), jIndex );
364  ++colcount ;
365  }
366  }
367  upper.rows_[ row+1 ] = colcount;
368  }
369  } // end convertToCRS
370 
372  template<class CRS, class InvVector, class X, class Y>
373  void blockILUBacksolve (const CRS& lower,
374  const CRS& upper,
375  const InvVector& inv,
376  X& v, const Y& d)
377  {
378  // iterator types
379  typedef typename Y :: block_type dblock;
380  typedef typename X :: block_type vblock;
381  typedef typename X :: size_type size_type ;
382 
383  const size_type iEnd = lower.rows();
384  const size_type lastRow = iEnd - 1;
385  if( iEnd != upper.rows() )
386  {
387  DUNE_THROW(ISTLError,"ILU::blockILUBacksolve: lower and upper rows must be the same");
388  }
389 
390  // lower triangular solve
391  for( size_type i=0; i<iEnd; ++ i )
392  {
393  dblock rhsValue( d[ i ] );
394  auto&& rhs = Impl::asVector(rhsValue);
395  const size_type rowI = lower.rows_[ i ];
396  const size_type rowINext = lower.rows_[ i+1 ];
397 
398  for( size_type col = rowI; col < rowINext; ++ col )
399  Impl::asMatrix(lower.values_[ col ]).mmv( Impl::asVector(v[ lower.cols_[ col ] ] ), rhs );
400 
401  Impl::asVector(v[ i ]) = rhs; // Lii = I
402  }
403 
404  // upper triangular solve
405  for( size_type i=0; i<iEnd; ++ i )
406  {
407  auto&& vBlock = Impl::asVector(v[ lastRow - i ]);
408  vblock rhsValue ( v[ lastRow - i ] );
409  auto&& rhs = Impl::asVector(rhsValue);
410  const size_type rowI = upper.rows_[ i ];
411  const size_type rowINext = upper.rows_[ i+1 ];
412 
413  for( size_type col = rowI; col < rowINext; ++ col )
414  Impl::asMatrix(upper.values_[ col ]).mmv( Impl::asVector(v[ upper.cols_[ col ] ]), rhs );
415 
416  // apply inverse and store result
417  Impl::asMatrix(inv[ i ]).mv(rhs, vBlock);
418  }
419  }
420 
421  } // end namespace ILU
422 
425 } // end namespace
426 
427 #endif
Error thrown if operations of a FieldMatrix fail.
Definition: densematrix.hh:126
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
Implements a matrix constructed from a given type representing a field and compile-time given number ...
const char * what() const noexcept override
output internal message buffer
Definition: exceptions.cc:37
#define DUNE_THROW(E, m)
Definition: exceptions.hh:218
decltype(auto) lane(std::size_t l, V &&v)
Extract an element of a SIMD type.
Definition: interface.hh:324
typename Overloads::ScalarType< std::decay_t< V > >::type Scalar
Element type of some SIMD type.
Definition: interface.hh:235
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:307
void blockILUBacksolve(const M &A, X &v, const Y &d)
LU backsolve with stored inverse.
Definition: ilu.hh:94
void blockILU0Decomposition(M &A)
compute ILU decomposition of A. A is overwritten by its decomposition
Definition: ilu.hh:33
void blockILUDecomposition(const M &A, int n, M &ILU)
Definition: ilu.hh:167
Dune namespace.
Definition: alignedallocator.hh:13
Implements a scalar matrix view wrapper around an existing scalar.
Implements a scalar vector view wrapper around an existing scalar.
a simple compressed row storage matrix class
Definition: ilu.hh:259
Whether this type acts as a scalar in the context of (hierarchically blocked) containers.
Definition: typetraits.hh:194
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.80.0 (May 3, 22:32, 2024)