Ifpack2 Templated Preconditioning Package  Version 1.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Ifpack2_RILUK_def.hpp
1 /*@HEADER
2 // ***********************************************************************
3 //
4 // Ifpack2: Templated Object-Oriented Algebraic Preconditioner Package
5 // Copyright (2009) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
8 // license for use of this work by or on behalf of the U.S. Government.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // ***********************************************************************
38 //@HEADER
39 */
40 
41 #ifndef IFPACK2_CRSRILUK_DEF_HPP
42 #define IFPACK2_CRSRILUK_DEF_HPP
43 
44 #include "Ifpack2_LocalFilter.hpp"
45 #include "Tpetra_CrsMatrix.hpp"
46 #include "Ifpack2_LocalSparseTriangularSolver.hpp"
47 #include "Ifpack2_Details_getParamTryingTypes.hpp"
48 
49 namespace Ifpack2 {
50 
51 template<class MatrixType>
53  : A_ (Matrix_in),
54  LevelOfFill_ (0),
55  isAllocated_ (false),
56  isInitialized_ (false),
57  isComputed_ (false),
58  numInitialize_ (0),
59  numCompute_ (0),
60  numApply_ (0),
61  initializeTime_ (0.0),
62  computeTime_ (0.0),
63  applyTime_ (0.0),
64  RelaxValue_ (Teuchos::ScalarTraits<magnitude_type>::zero ()),
65  Athresh_ (Teuchos::ScalarTraits<magnitude_type>::zero ()),
66  Rthresh_ (Teuchos::ScalarTraits<magnitude_type>::one ())
67 {
68  allocateSolvers();
69 }
70 
71 
72 template<class MatrixType>
74  : A_ (Matrix_in),
75  LevelOfFill_ (0),
76  isAllocated_ (false),
77  isInitialized_ (false),
78  isComputed_ (false),
79  numInitialize_ (0),
80  numCompute_ (0),
81  numApply_ (0),
82  initializeTime_ (0.0),
83  computeTime_ (0.0),
84  applyTime_ (0.0),
85  RelaxValue_ (Teuchos::ScalarTraits<magnitude_type>::zero ()),
86  Athresh_ (Teuchos::ScalarTraits<magnitude_type>::zero ()),
87  Rthresh_ (Teuchos::ScalarTraits<magnitude_type>::one ())
88 {
89  allocateSolvers();
90 }
91 
92 
93 template<class MatrixType>
95 
96 template<class MatrixType>
98 {
101 }
102 
103 template<class MatrixType>
104 void
106 {
107  // It's legal for A to be null; in that case, you may not call
108  // initialize() until calling setMatrix() with a nonnull input.
109  // Regardless, setting the matrix invalidates any previous
110  // factorization.
111  if (A.getRawPtr () != A_.getRawPtr ()) {
112  isAllocated_ = false;
113  isInitialized_ = false;
114  isComputed_ = false;
115  A_local_ = Teuchos::null;
116  Graph_ = Teuchos::null;
117 
118  // The sparse triangular solvers get a triangular factor as their
119  // input matrix. The triangular factors L_ and U_ are getting
120  // reset, so we reset the solvers' matrices to null. Do that
121  // before setting L_ and U_ to null, so that latter step actually
122  // frees the factors.
123  if (! L_solver_.is_null ()) {
124  L_solver_->setMatrix (Teuchos::null);
125  }
126  if (! U_solver_.is_null ()) {
127  U_solver_->setMatrix (Teuchos::null);
128  }
129 
130  L_ = Teuchos::null;
131  U_ = Teuchos::null;
132  D_ = Teuchos::null;
133  A_ = A;
134  }
135 }
136 
137 
138 
139 template<class MatrixType>
142 {
144  L_.is_null (), std::runtime_error, "Ifpack2::RILUK::getL: The L factor "
145  "is null. Please call initialize() (and preferably also compute()) "
146  "before calling this method. If the input matrix has not yet been set, "
147  "you must first call setMatrix() with a nonnull input matrix before you "
148  "may call initialize() or compute().");
149  return *L_;
150 }
151 
152 
153 template<class MatrixType>
154 const Tpetra::Vector<typename RILUK<MatrixType>::scalar_type,
159 {
161  D_.is_null (), std::runtime_error, "Ifpack2::RILUK::getD: The D factor "
162  "(of diagonal entries) is null. Please call initialize() (and "
163  "preferably also compute()) before calling this method. If the input "
164  "matrix has not yet been set, you must first call setMatrix() with a "
165  "nonnull input matrix before you may call initialize() or compute().");
166  return *D_;
167 }
168 
169 
170 template<class MatrixType>
173 {
175  U_.is_null (), std::runtime_error, "Ifpack2::RILUK::getU: The U factor "
176  "is null. Please call initialize() (and preferably also compute()) "
177  "before calling this method. If the input matrix has not yet been set, "
178  "you must first call setMatrix() with a nonnull input matrix before you "
179  "may call initialize() or compute().");
180  return *U_;
181 }
182 
183 
184 template<class MatrixType>
187  A_.is_null (), std::runtime_error, "Ifpack2::RILUK::getNodeSmootherComplexity: "
188  "The input matrix A is null. Please call setMatrix() with a nonnull "
189  "input matrix, then call compute(), before calling this method.");
190  // RILUK methods cost roughly one apply + the nnz in the upper+lower triangles
191  if(!L_.is_null() && !U_.is_null())
192  return A_->getNodeNumEntries() + L_->getNodeNumEntries() + U_->getNodeNumEntries();
193  else
194  return 0;
195 }
196 
197 
198 
199 template<class MatrixType>
202  typename RILUK<MatrixType>::node_type> >
205  A_.is_null (), std::runtime_error, "Ifpack2::RILUK::getDomainMap: "
206  "The matrix is null. Please call setMatrix() with a nonnull input "
207  "before calling this method.");
208 
209  // FIXME (mfh 25 Jan 2014) Shouldn't this just come from A_?
211  Graph_.is_null (), std::runtime_error, "Ifpack2::RILUK::getDomainMap: "
212  "The computed graph is null. Please call initialize() before calling "
213  "this method.");
214  return Graph_->getL_Graph ()->getDomainMap ();
215 }
216 
217 
218 template<class MatrixType>
221  typename RILUK<MatrixType>::node_type> >
224  A_.is_null (), std::runtime_error, "Ifpack2::RILUK::getRangeMap: "
225  "The matrix is null. Please call setMatrix() with a nonnull input "
226  "before calling this method.");
227 
228  // FIXME (mfh 25 Jan 2014) Shouldn't this just come from A_?
230  Graph_.is_null (), std::runtime_error, "Ifpack2::RILUK::getRangeMap: "
231  "The computed graph is null. Please call initialize() before calling "
232  "this method.");
233  return Graph_->getL_Graph ()->getRangeMap ();
234 }
235 
236 
237 template<class MatrixType>
239 {
240  using Teuchos::null;
241  using Teuchos::rcp;
242 
243  if (! isAllocated_) {
244  // Deallocate any existing storage. This avoids storing 2x
245  // memory, since RCP op= does not deallocate until after the
246  // assignment.
247  L_ = null;
248  U_ = null;
249  D_ = null;
250 
251  // Allocate Matrix using ILUK graphs
252  L_ = rcp (new crs_matrix_type (Graph_->getL_Graph ()));
253  U_ = rcp (new crs_matrix_type (Graph_->getU_Graph ()));
254  L_->setAllToScalar (STS::zero ()); // Zero out L and U matrices
255  U_->setAllToScalar (STS::zero ());
256 
257  // FIXME (mfh 24 Jan 2014) This assumes domain == range Map for L and U.
258  L_->fillComplete ();
259  U_->fillComplete ();
260  D_ = rcp (new vec_type (Graph_->getL_Graph ()->getRowMap ()));
261  }
262  isAllocated_ = true;
263 }
264 
265 
266 template<class MatrixType>
267 void
270 {
271  using Details::getParamTryingTypes;
272  const char prefix[] = "Ifpack2::RILUK: ";
273 
274  // Default values of the various parameters.
275  int fillLevel = 0;
276  magnitude_type absThresh = STM::zero ();
277  magnitude_type relThresh = STM::one ();
278  magnitude_type relaxValue = STM::zero ();
279 
280  // "fact: iluk level-of-fill" parsing is more complicated, because
281  // we want to allow as many types as make sense. int is the native
282  // type, but we also want to accept double (for backwards
283  // compatibilty with ILUT). You can't cast arbitrary magnitude_type
284  // (e.g., Sacado::MP::Vector) to int, so we use float instead, to
285  // get coverage of the most common magnitude_type cases. Weirdly,
286  // there's an Ifpack2 test that sets the fill level as a
287  // global_ordinal_type.
288  {
289  const std::string paramName ("fact: iluk level-of-fill");
290  getParamTryingTypes<int, int, global_ordinal_type, double, float>
291  (fillLevel, params, paramName, prefix);
292  }
293  // For the other parameters, we prefer magnitude_type, but allow
294  // double for backwards compatibility.
295  {
296  const std::string paramName ("fact: absolute threshold");
297  getParamTryingTypes<magnitude_type, magnitude_type, double>
298  (absThresh, params, paramName, prefix);
299  }
300  {
301  const std::string paramName ("fact: relative threshold");
302  getParamTryingTypes<magnitude_type, magnitude_type, double>
303  (relThresh, params, paramName, prefix);
304  }
305  {
306  const std::string paramName ("fact: relax value");
307  getParamTryingTypes<magnitude_type, magnitude_type, double>
308  (relaxValue, params, paramName, prefix);
309  }
310 
311  // Forward to trisolvers.
312  L_solver_->setParameters(params);
313  U_solver_->setParameters(params);
314 
315  // "Commit" the values only after validating all of them. This
316  // ensures that there are no side effects if this routine throws an
317  // exception.
318 
319  LevelOfFill_ = fillLevel;
320 
321  // mfh 28 Nov 2012: The previous code would not assign Athresh_,
322  // Rthresh_, or RelaxValue_, if the read-in value was -1. I don't
323  // know if keeping this behavior is correct, but I'll keep it just
324  // so as not to change previous behavior.
325 
326  if (absThresh != -STM::one ()) {
327  Athresh_ = absThresh;
328  }
329  if (relThresh != -STM::one ()) {
330  Rthresh_ = relThresh;
331  }
332  if (relaxValue != -STM::one ()) {
333  RelaxValue_ = relaxValue;
334  }
335 }
336 
337 
338 template<class MatrixType>
341  return Teuchos::rcp_implicit_cast<const row_matrix_type> (A_);
342 }
343 
344 
345 template<class MatrixType>
348  return Teuchos::rcp_dynamic_cast<const crs_matrix_type> (A_, true);
349 }
350 
351 
352 template<class MatrixType>
355 {
356  using Teuchos::RCP;
357  using Teuchos::rcp;
358  using Teuchos::rcp_dynamic_cast;
359  using Teuchos::rcp_implicit_cast;
360 
361  // If A_'s communicator only has one process, or if its column and
362  // row Maps are the same, then it is already local, so use it
363  // directly.
364  if (A->getRowMap ()->getComm ()->getSize () == 1 ||
365  A->getRowMap ()->isSameAs (* (A->getColMap ()))) {
366  return A;
367  }
368 
369  // If A_ is already a LocalFilter, then use it directly. This
370  // should be the case if RILUK is being used through
371  // AdditiveSchwarz, for example.
372  RCP<const LocalFilter<row_matrix_type> > A_lf_r =
373  rcp_dynamic_cast<const LocalFilter<row_matrix_type> > (A);
374  if (! A_lf_r.is_null ()) {
375  return rcp_implicit_cast<const row_matrix_type> (A_lf_r);
376  }
377  else {
378  // A_'s communicator has more than one process, its row Map and
379  // its column Map differ, and A_ is not a LocalFilter. Thus, we
380  // have to wrap it in a LocalFilter.
381  return rcp (new LocalFilter<row_matrix_type> (A));
382  }
383 }
384 
385 
386 template<class MatrixType>
388 {
389  using Teuchos::RCP;
390  using Teuchos::rcp;
391  using Teuchos::rcp_const_cast;
392  using Teuchos::rcp_dynamic_cast;
393  using Teuchos::rcp_implicit_cast;
394  using Teuchos::Array;
395  using Teuchos::ArrayView;
396  typedef Tpetra::CrsGraph<local_ordinal_type,
398  node_type> crs_graph_type;
399  const char prefix[] = "Ifpack2::RILUK::initialize: ";
400 
402  (A_.is_null (), std::runtime_error, prefix << "The matrix is null. Please "
403  "call setMatrix() with a nonnull input before calling this method.");
405  (! A_->isFillComplete (), std::runtime_error, prefix << "The matrix is not "
406  "fill complete. You may not invoke initialize() or compute() with this "
407  "matrix until the matrix is fill complete. If your matrix is a "
408  "Tpetra::CrsMatrix, please call fillComplete on it (with the domain and "
409  "range Maps, if appropriate) before calling this method.");
410 
411  Teuchos::Time timer ("RILUK::initialize");
412  { // Start timing
413  Teuchos::TimeMonitor timeMon (timer);
414 
415  // Calling initialize() means that the user asserts that the graph
416  // of the sparse matrix may have changed. We must not just reuse
417  // the previous graph in that case.
418  //
419  // Regarding setting isAllocated_ to false: Eventually, we may want
420  // some kind of clever memory reuse strategy, but it's always
421  // correct just to blow everything away and start over.
422  isInitialized_ = false;
423  isAllocated_ = false;
424  isComputed_ = false;
425  Graph_ = Teuchos::null;
426 
427  A_local_ = makeLocalFilter (A_);
429  A_local_.is_null (), std::logic_error, "Ifpack2::RILUK::initialize: "
430  "makeLocalFilter returned null; it failed to compute A_local. "
431  "Please report this bug to the Ifpack2 developers.");
432 
433  // FIXME (mfh 24 Jan 2014, 26 Mar 2014) It would be more efficient
434  // to rewrite RILUK so that it works with any RowMatrix input, not
435  // just CrsMatrix. (That would require rewriting IlukGraph to
436  // handle a Tpetra::RowGraph.) However, to make it work for now,
437  // we just copy the input matrix if it's not a CrsMatrix.
438  {
439  RCP<const crs_matrix_type> A_local_crs =
440  rcp_dynamic_cast<const crs_matrix_type> (A_local_);
441  if (A_local_crs.is_null ()) {
442  local_ordinal_type numRows = A_local_->getNodeNumRows();
443  Array<size_t> entriesPerRow(numRows);
444  for(local_ordinal_type i = 0; i < numRows; i++)
445  {
446  entriesPerRow[i] = A_local_->getNumEntriesInLocalRow(i);
447  }
448  RCP<crs_matrix_type> A_local_crs_nc =
449  rcp (new crs_matrix_type (A_local_->getRowMap (),
450  A_local_->getColMap (),
451  entriesPerRow()));
452  // copy entries into A_local_crs
453  Teuchos::Array<local_ordinal_type> indices(A_local_->getNodeMaxNumRowEntries());
454  Teuchos::Array<scalar_type> values(A_local_->getNodeMaxNumRowEntries());
455  for(local_ordinal_type i = 0; i < numRows; i++)
456  {
457  size_t numEntries = 0;
458  A_local_->getLocalRowCopy(i, indices(), values(), numEntries);
459  ArrayView<const local_ordinal_type> indicesInsert(indices.data(), numEntries);
460  ArrayView<const scalar_type> valuesInsert(values.data(), numEntries);
461  A_local_crs_nc->insertLocalValues(i, indicesInsert, valuesInsert);
462  }
463  A_local_crs_nc->fillComplete (A_local_->getDomainMap (), A_local_->getRangeMap ());
464  A_local_crs = rcp_const_cast<const crs_matrix_type> (A_local_crs_nc);
465  }
466  Graph_ = rcp (new Ifpack2::IlukGraph<crs_graph_type> (A_local_crs->getCrsGraph (),
467  LevelOfFill_, 0));
468  }
469 
470  Graph_->initialize ();
471  allocate_L_and_U ();
472  checkOrderingConsistency (*A_local_);
473  L_solver_->setMatrix (L_);
474  L_solver_->initialize ();
475  U_solver_->setMatrix (U_);
476  U_solver_->initialize ();
477  // Do not call initAllValues. compute() always calls initAllValues to
478  // fill L and U with possibly new numbers. initialize() is concerned
479  // only with the nonzero pattern.
480 
481  } // Stop timing
482 
483  isInitialized_ = true;
484  ++numInitialize_;
485  initializeTime_ += timer.totalElapsedTime ();
486 }
487 
488 template<class MatrixType>
489 void
491 checkOrderingConsistency (const row_matrix_type& A)
492 {
493  // First check that the local row map ordering is the same as the local portion of the column map.
494  // The extraction of the strictly lower/upper parts of A, as well as the factorization,
495  // implicitly assume that this is the case.
496  Teuchos::ArrayView<const global_ordinal_type> rowGIDs = A.getRowMap()->getNodeElementList();
497  Teuchos::ArrayView<const global_ordinal_type> colGIDs = A.getColMap()->getNodeElementList();
498  bool gidsAreConsistentlyOrdered=true;
499  global_ordinal_type indexOfInconsistentGID=0;
500  for (global_ordinal_type i=0; i<rowGIDs.size(); ++i) {
501  if (rowGIDs[i] != colGIDs[i]) {
502  gidsAreConsistentlyOrdered=false;
503  indexOfInconsistentGID=i;
504  break;
505  }
506  }
507  TEUCHOS_TEST_FOR_EXCEPTION(gidsAreConsistentlyOrdered==false, std::runtime_error,
508  "The ordering of the local GIDs in the row and column maps is not the same"
509  << std::endl << "at index " << indexOfInconsistentGID
510  << ". Consistency is required, as all calculations are done with"
511  << std::endl << "local indexing.");
512 }
513 
514 template<class MatrixType>
515 void
516 RILUK<MatrixType>::
517 initAllValues (const row_matrix_type& A)
518 {
519  using Teuchos::ArrayRCP;
520  using Teuchos::Comm;
521  using Teuchos::ptr;
522  using Teuchos::RCP;
523  using Teuchos::REDUCE_SUM;
524  using Teuchos::reduceAll;
525  typedef Tpetra::Map<local_ordinal_type,global_ordinal_type,node_type> map_type;
526 
527  size_t NumIn = 0, NumL = 0, NumU = 0;
528  bool DiagFound = false;
529  size_t NumNonzeroDiags = 0;
530  size_t MaxNumEntries = A.getGlobalMaxNumRowEntries();
531 
532  // Allocate temporary space for extracting the strictly
533  // lower and upper parts of the matrix A.
534  Teuchos::Array<local_ordinal_type> InI(MaxNumEntries);
535  Teuchos::Array<local_ordinal_type> LI(MaxNumEntries);
536  Teuchos::Array<local_ordinal_type> UI(MaxNumEntries);
537  Teuchos::Array<scalar_type> InV(MaxNumEntries);
538  Teuchos::Array<scalar_type> LV(MaxNumEntries);
539  Teuchos::Array<scalar_type> UV(MaxNumEntries);
540 
541  // Check if values should be inserted or replaced
542  const bool ReplaceValues = L_->isStaticGraph () || L_->isLocallyIndexed ();
543 
544  L_->resumeFill ();
545  U_->resumeFill ();
546  if (ReplaceValues) {
547  L_->setAllToScalar (STS::zero ()); // Zero out L and U matrices
548  U_->setAllToScalar (STS::zero ());
549  }
550 
551  D_->putScalar (STS::zero ()); // Set diagonal values to zero
552  ArrayRCP<scalar_type> DV = D_->get1dViewNonConst (); // Get view of diagonal
553 
554  RCP<const map_type> rowMap = L_->getRowMap ();
555 
556  // First we copy the user's matrix into L and U, regardless of fill level.
557  // It is important to note that L and U are populated using local indices.
558  // This means that if the row map GIDs are not monotonically increasing
559  // (i.e., permuted or gappy), then the strictly lower (upper) part of the
560  // matrix is not the one that you would get if you based L (U) on GIDs.
561  // This is ok, as the *order* of the GIDs in the rowmap is a better
562  // expression of the user's intent than the GIDs themselves.
563 
564  Teuchos::ArrayView<const global_ordinal_type> nodeGIDs = rowMap->getNodeElementList();
565  for (size_t myRow=0; myRow<A.getNodeNumRows(); ++myRow) {
566  local_ordinal_type local_row = myRow;
567 
568  //TODO JJH 4April2014 An optimization is to use getLocalRowView. Not all matrices support this,
569  // we'd need to check via the Tpetra::RowMatrix method supportsRowViews().
570  A.getLocalRowCopy (local_row, InI(), InV(), NumIn); // Get Values and Indices
571 
572  // Split into L and U (we don't assume that indices are ordered).
573 
574  NumL = 0;
575  NumU = 0;
576  DiagFound = false;
577 
578  for (size_t j = 0; j < NumIn; ++j) {
579  const local_ordinal_type k = InI[j];
580 
581  if (k == local_row) {
582  DiagFound = true;
583  // Store perturbed diagonal in Tpetra::Vector D_
584  DV[local_row] += Rthresh_ * InV[j] + IFPACK2_SGN(InV[j]) * Athresh_;
585  }
586  else if (k < 0) { // Out of range
588  true, std::runtime_error, "Ifpack2::RILUK::initAllValues: current "
589  "GID k = " << k << " < 0. I'm not sure why this is an error; it is "
590  "probably an artifact of the undocumented assumptions of the "
591  "original implementation (likely copied and pasted from Ifpack). "
592  "Nevertheless, the code I found here insisted on this being an error "
593  "state, so I will throw an exception here.");
594  }
595  else if (k < local_row) {
596  LI[NumL] = k;
597  LV[NumL] = InV[j];
598  NumL++;
599  }
600  else if (Teuchos::as<size_t>(k) <= rowMap->getNodeNumElements()) {
601  UI[NumU] = k;
602  UV[NumU] = InV[j];
603  NumU++;
604  }
605  }
606 
607  // Check in things for this row of L and U
608 
609  if (DiagFound) {
610  ++NumNonzeroDiags;
611  } else {
612  DV[local_row] = Athresh_;
613  }
614 
615  if (NumL) {
616  if (ReplaceValues) {
617  L_->replaceLocalValues(local_row, LI(0, NumL), LV(0,NumL));
618  } else {
619  //FIXME JJH 24April2014 Is this correct? I believe this case is when there aren't already values
620  //FIXME in this row in the column locations corresponding to UI.
621  L_->insertLocalValues(local_row, LI(0,NumL), LV(0,NumL));
622  }
623  }
624 
625  if (NumU) {
626  if (ReplaceValues) {
627  U_->replaceLocalValues(local_row, UI(0,NumU), UV(0,NumU));
628  } else {
629  //FIXME JJH 24April2014 Is this correct? I believe this case is when there aren't already values
630  //FIXME in this row in the column locations corresponding to UI.
631  U_->insertLocalValues(local_row, UI(0,NumU), UV(0,NumU));
632  }
633  }
634  }
635 
636  // At this point L and U have the values of A in the structure of L
637  // and U, and diagonal vector D
638 
639  isInitialized_ = true;
640 }
641 
642 
643 template<class MatrixType>
645 {
646  using Teuchos::rcp;
647  const char prefix[] = "Ifpack2::RILUK::compute: ";
648 
649  // initialize() checks this too, but it's easier for users if the
650  // error shows them the name of the method that they actually
651  // called, rather than the name of some internally called method.
653  (A_.is_null (), std::runtime_error, prefix << "The matrix is null. Please "
654  "call setMatrix() with a nonnull input before calling this method.");
656  (! A_->isFillComplete (), std::runtime_error, prefix << "The matrix is not "
657  "fill complete. You may not invoke initialize() or compute() with this "
658  "matrix until the matrix is fill complete. If your matrix is a "
659  "Tpetra::CrsMatrix, please call fillComplete on it (with the domain and "
660  "range Maps, if appropriate) before calling this method.");
661 
662  if (! isInitialized ()) {
663  initialize (); // Don't count this in the compute() time
664  }
665 
666  Teuchos::Time timer ("RILUK::compute");
667 
668  // Start timing
669  Teuchos::TimeMonitor timeMon (timer);
670 
671  isComputed_ = false;
672 
673  // Fill L and U with numbers. This supports nonzero pattern reuse by calling
674  // initialize() once and then compute() multiple times.
675  initAllValues (*A_local_);
676 
677  // MinMachNum should be officially defined, for now pick something a little
678  // bigger than IEEE underflow value
679 
680  const scalar_type MinDiagonalValue = STS::rmin ();
681  const scalar_type MaxDiagonalValue = STS::one () / MinDiagonalValue;
682 
683  size_t NumIn, NumL, NumU;
684 
685  // Get Maximum Row length
686  const size_t MaxNumEntries =
687  L_->getNodeMaxNumRowEntries () + U_->getNodeMaxNumRowEntries () + 1;
688 
689  Teuchos::Array<local_ordinal_type> InI(MaxNumEntries); // Allocate temp space
690  Teuchos::Array<scalar_type> InV(MaxNumEntries);
691  size_t num_cols = U_->getColMap()->getNodeNumElements();
692  Teuchos::Array<int> colflag(num_cols);
693 
694  Teuchos::ArrayRCP<scalar_type> DV = D_->get1dViewNonConst(); // Get view of diagonal
695 
696  // Now start the factorization.
697 
698  // Need some integer workspace and pointers
699  size_t NumUU;
702  for (size_t j = 0; j < num_cols; ++j) {
703  colflag[j] = -1;
704  }
705 
706  for (size_t i = 0; i < L_->getNodeNumRows (); ++i) {
707  local_ordinal_type local_row = i;
708 
709  // Fill InV, InI with current row of L, D and U combined
710 
711  NumIn = MaxNumEntries;
712  L_->getLocalRowCopy (local_row, InI (), InV (), NumL);
713 
714  InV[NumL] = DV[i]; // Put in diagonal
715  InI[NumL] = local_row;
716 
717  U_->getLocalRowCopy (local_row, InI (NumL+1, MaxNumEntries-NumL-1),
718  InV (NumL+1, MaxNumEntries-NumL-1), NumU);
719  NumIn = NumL+NumU+1;
720 
721  // Set column flags
722  for (size_t j = 0; j < NumIn; ++j) {
723  colflag[InI[j]] = j;
724  }
725 
726  scalar_type diagmod = STS::zero (); // Off-diagonal accumulator
727 
728  for (size_t jj = 0; jj < NumL; ++jj) {
729  local_ordinal_type j = InI[jj];
730  scalar_type multiplier = InV[jj]; // current_mults++;
731 
732  InV[jj] *= DV[j];
733 
734  U_->getLocalRowView(j, UUI, UUV); // View of row above
735  NumUU = UUI.size();
736 
737  if (RelaxValue_ == STM::zero ()) {
738  for (size_t k = 0; k < NumUU; ++k) {
739  const int kk = colflag[UUI[k]];
740  // FIXME (mfh 23 Dec 2013) Wait a second, we just set
741  // colflag above using size_t (which is generally unsigned),
742  // but now we're querying it using int (which is signed).
743  if (kk > -1) {
744  InV[kk] -= multiplier * UUV[k];
745  }
746  }
747  }
748  else {
749  for (size_t k = 0; k < NumUU; ++k) {
750  // FIXME (mfh 23 Dec 2013) Wait a second, we just set
751  // colflag above using size_t (which is generally unsigned),
752  // but now we're querying it using int (which is signed).
753  const int kk = colflag[UUI[k]];
754  if (kk > -1) {
755  InV[kk] -= multiplier*UUV[k];
756  }
757  else {
758  diagmod -= multiplier*UUV[k];
759  }
760  }
761  }
762  }
763  if (NumL) {
764  // Replace current row of L
765  L_->replaceLocalValues (local_row, InI (0, NumL), InV (0, NumL));
766  }
767 
768  DV[i] = InV[NumL]; // Extract Diagonal value
769 
770  if (RelaxValue_ != STM::zero ()) {
771  DV[i] += RelaxValue_*diagmod; // Add off diagonal modifications
772  }
773 
774  if (STS::magnitude (DV[i]) > STS::magnitude (MaxDiagonalValue)) {
775  if (STS::real (DV[i]) < STM::zero ()) {
776  DV[i] = -MinDiagonalValue;
777  }
778  else {
779  DV[i] = MinDiagonalValue;
780  }
781  }
782  else {
783  DV[i] = STS::one () / DV[i]; // Invert diagonal value
784  }
785 
786  for (size_t j = 0; j < NumU; ++j) {
787  InV[NumL+1+j] *= DV[i]; // Scale U by inverse of diagonal
788  }
789 
790  if (NumU) {
791  // Replace current row of L and U
792  U_->replaceLocalValues (local_row, InI (NumL+1, NumU), InV (NumL+1, NumU));
793  }
794 
795  // Reset column flags
796  for (size_t j = 0; j < NumIn; ++j) {
797  colflag[InI[j]] = -1;
798  }
799  }
800 
801  // The domain of L and the range of U are exactly their own row maps
802  // (there is no communication). The domain of U and the range of L
803  // must be the same as those of the original matrix, However if the
804  // original matrix is a VbrMatrix, these two latter maps are
805  // translation from a block map to a point map.
806  // FIXME (mfh 23 Dec 2013) Do we know that the column Map of L_ is
807  // always one-to-one?
808  L_->fillComplete (L_->getColMap (), A_local_->getRangeMap ());
809  U_->fillComplete (A_local_->getDomainMap (), U_->getRowMap ());
810 
811  // If L_solver_ or U_solver store modified factors internally, we need to reset those
812  L_solver_->setMatrix (L_);
813  L_solver_->compute ();
814  U_solver_->setMatrix (U_);
815  U_solver_->compute ();
816 
817  isComputed_ = true;
818  ++numCompute_;
819  computeTime_ += timer.totalElapsedTime ();
820 }
821 
822 
823 template<class MatrixType>
824 void
826 apply (const Tpetra::MultiVector<scalar_type,local_ordinal_type,global_ordinal_type,node_type>& X,
827  Tpetra::MultiVector<scalar_type,local_ordinal_type,global_ordinal_type,node_type>& Y,
828  Teuchos::ETransp mode,
829  scalar_type alpha,
830  scalar_type beta) const
831 {
832  using Teuchos::RCP;
833  using Teuchos::rcpFromRef;
834 
836  A_.is_null (), std::runtime_error, "Ifpack2::RILUK::apply: The matrix is "
837  "null. Please call setMatrix() with a nonnull input, then initialize() "
838  "and compute(), before calling this method.");
840  ! isComputed (), std::runtime_error,
841  "Ifpack2::RILUK::apply: If you have not yet called compute(), "
842  "you must call compute() before calling this method.");
843  TEUCHOS_TEST_FOR_EXCEPTION(
844  X.getNumVectors () != Y.getNumVectors (), std::invalid_argument,
845  "Ifpack2::RILUK::apply: X and Y do not have the same number of columns. "
846  "X.getNumVectors() = " << X.getNumVectors ()
847  << " != Y.getNumVectors() = " << Y.getNumVectors () << ".");
848  TEUCHOS_TEST_FOR_EXCEPTION(
849  STS::isComplex && mode == Teuchos::CONJ_TRANS, std::logic_error,
850  "Ifpack2::RILUK::apply: mode = Teuchos::CONJ_TRANS is not implemented for "
851  "complex Scalar type. Please talk to the Ifpack2 developers to get this "
852  "fixed. There is a FIXME in this file about this very issue.");
853 #ifdef HAVE_IFPACK2_DEBUG
854  {
855  const magnitude_type D_nrm1 = D_->norm1 ();
856  TEUCHOS_TEST_FOR_EXCEPTION( STM::isnaninf (D_nrm1), std::runtime_error, "Ifpack2::RILUK::apply: The 1-norm of the stored diagonal is NaN or Inf.");
857  Teuchos::Array<magnitude_type> norms (X.getNumVectors ());
858  X.norm1 (norms ());
859  bool good = true;
860  for (size_t j = 0; j < X.getNumVectors (); ++j) {
861  if (STM::isnaninf (norms[j])) {
862  good = false;
863  break;
864  }
865  }
866  TEUCHOS_TEST_FOR_EXCEPTION( ! good, std::runtime_error, "Ifpack2::RILUK::apply: The 1-norm of the input X is NaN or Inf.");
867  }
868 #endif // HAVE_IFPACK2_DEBUG
869 
870  const scalar_type one = STS::one ();
871  const scalar_type zero = STS::zero ();
872 
873  Teuchos::Time timer ("RILUK::apply");
874  { // Start timing
875  Teuchos::TimeMonitor timeMon (timer);
876  if (alpha == one && beta == zero) {
877  if (mode == Teuchos::NO_TRANS) { // Solve L (D (U Y)) = X for Y.
878  // Start by solving L Y = X for Y.
879  L_solver_->apply (X, Y, mode);
880 
881  // Solve D Y = Y. The operation lets us do this in place in Y, so we can
882  // write "solve D Y = Y for Y."
883  Y.elementWiseMultiply (one, *D_, Y, zero);
884 
885  U_solver_->apply (Y, Y, mode); // Solve U Y = Y.
886  }
887  else { // Solve U^P (D^P (L^P Y)) = X for Y (where P is * or T).
888 
889  // Start by solving U^P Y = X for Y.
890  U_solver_->apply (X, Y, mode);
891 
892  // Solve D^P Y = Y.
893  //
894  // FIXME (mfh 24 Jan 2014) If mode = Teuchos::CONJ_TRANS, we
895  // need to do an elementwise multiply with the conjugate of
896  // D_, not just with D_ itself.
897  Y.elementWiseMultiply (one, *D_, Y, zero);
898 
899  L_solver_->apply (Y, Y, mode); // Solve L^P Y = Y.
900  }
901  }
902  else { // alpha != 1 or beta != 0
903  if (alpha == zero) {
904  // The special case for beta == 0 ensures that if Y contains Inf
905  // or NaN values, we replace them with 0 (following BLAS
906  // convention), rather than multiplying them by 0 to get NaN.
907  if (beta == zero) {
908  Y.putScalar (zero);
909  } else {
910  Y.scale (beta);
911  }
912  } else { // alpha != zero
913  MV Y_tmp (Y.getMap (), Y.getNumVectors ());
914  apply (X, Y_tmp, mode);
915  Y.update (alpha, Y_tmp, beta);
916  }
917  }
918  }//end timing
919 
920 #ifdef HAVE_IFPACK2_DEBUG
921  {
922  Teuchos::Array<magnitude_type> norms (Y.getNumVectors ());
923  Y.norm1 (norms ());
924  bool good = true;
925  for (size_t j = 0; j < Y.getNumVectors (); ++j) {
926  if (STM::isnaninf (norms[j])) {
927  good = false;
928  break;
929  }
930  }
931  TEUCHOS_TEST_FOR_EXCEPTION( ! good, std::runtime_error, "Ifpack2::RILUK::apply: The 1-norm of the output Y is NaN or Inf.");
932  }
933 #endif // HAVE_IFPACK2_DEBUG
934 
935  ++numApply_;
936  applyTime_ = timer.totalElapsedTime ();
937 }
938 
939 
940 template<class MatrixType>
942 multiply (const Tpetra::MultiVector<scalar_type,local_ordinal_type,global_ordinal_type,node_type>& X,
943  Tpetra::MultiVector<scalar_type,local_ordinal_type,global_ordinal_type,node_type>& Y,
944  const Teuchos::ETransp mode) const
945 {
946  const scalar_type zero = STS::zero ();
947  const scalar_type one = STS::one ();
948 
949  if (mode != Teuchos::NO_TRANS) {
950  U_->apply (X, Y, mode); //
951  Y.update (one, X, one); // Y = Y + X (account for implicit unit diagonal)
952 
953  // FIXME (mfh 24 Jan 2014) If mode = Teuchos::CONJ_TRANS, we need
954  // to do an elementwise multiply with the conjugate of D_, not
955  // just with D_ itself.
956  Y.elementWiseMultiply (one, *D_, Y, zero); // y = D*y (D_ has inverse of diagonal)
957 
958  MV Y_tmp (Y, Teuchos::Copy); // Need a temp copy of Y
959  L_->apply (Y_tmp, Y, mode);
960  Y.update (one, Y_tmp, one); // (account for implicit unit diagonal)
961  }
962  else {
963  L_->apply (X, Y, mode);
964  Y.update (one, X, one); // Y = Y + X (account for implicit unit diagonal)
965  Y.elementWiseMultiply (one, *D_, Y, zero); // y = D*y (D_ has inverse of diagonal)
966  MV Y_tmp (Y, Teuchos::Copy); // Need a temp copy of Y1
967  U_->apply (Y_tmp, Y, mode);
968  Y.update (one, Y_tmp, one); // (account for implicit unit diagonal)
969  }
970 }
971 
972 
973 template<class MatrixType>
974 std::string RILUK<MatrixType>::description () const
975 {
976  std::ostringstream os;
977 
978  // Output is a valid YAML dictionary in flow style. If you don't
979  // like everything on a single line, you should call describe()
980  // instead.
981  os << "\"Ifpack2::RILUK\": {";
982  os << "Initialized: " << (isInitialized () ? "true" : "false") << ", "
983  << "Computed: " << (isComputed () ? "true" : "false") << ", ";
984 
985  os << "Level-of-fill: " << getLevelOfFill() << ", ";
986 
987  if (A_.is_null ()) {
988  os << "Matrix: null";
989  }
990  else {
991  os << "Global matrix dimensions: ["
992  << A_->getGlobalNumRows () << ", " << A_->getGlobalNumCols () << "]"
993  << ", Global nnz: " << A_->getGlobalNumEntries();
994  }
995 
996  if (! L_solver_.is_null ()) os << ", " << L_solver_->description ();
997  if (! U_solver_.is_null ()) os << ", " << U_solver_->description ();
998 
999  os << "}";
1000  return os.str ();
1001 }
1002 
1003 } // namespace Ifpack2
1004 
1005 #define IFPACK2_RILUK_INSTANT(S,LO,GO,N) \
1006  template class Ifpack2::RILUK< Tpetra::RowMatrix<S, LO, GO, N> >;
1007 
1008 #endif
Teuchos::RCP< const Tpetra::Map< local_ordinal_type, global_ordinal_type, node_type > > getRangeMap() const
Returns the Tpetra::Map object associated with the range of this operator.
Definition: Ifpack2_RILUK_def.hpp:222
MatrixType::global_ordinal_type global_ordinal_type
The type of global indices in the input MatrixType.
Definition: Ifpack2_RILUK_decl.hpp:261
const Tpetra::Vector< scalar_type, local_ordinal_type, global_ordinal_type, node_type > & getD() const
Return the diagonal entries of the ILU factorization.
Definition: Ifpack2_RILUK_def.hpp:158
void initialize()
Initialize by computing the symbolic incomplete factorization.
Definition: Ifpack2_RILUK_def.hpp:387
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Tpetra::RowMatrix< scalar_type, local_ordinal_type, global_ordinal_type, node_type > row_matrix_type
Tpetra::RowMatrix specialization used by this class.
Definition: Ifpack2_RILUK_decl.hpp:273
size_type size() const
virtual void setMatrix(const Teuchos::RCP< const row_matrix_type > &A)
Change the matrix to be preconditioned.
Definition: Ifpack2_RILUK_def.hpp:105
std::string description() const
A one-line description of this object.
Definition: Ifpack2_RILUK_def.hpp:974
void setParameters(const Teuchos::ParameterList &params)
Definition: Ifpack2_RILUK_def.hpp:269
ILU(k) factorization of a given Tpetra::RowMatrix.
Definition: Ifpack2_RILUK_decl.hpp:243
size_t getNodeSmootherComplexity() const
Get a rough estimate of cost per iteration.
Definition: Ifpack2_RILUK_def.hpp:185
Teuchos::RCP< const crs_matrix_type > getCrsMatrix() const
Return the input matrix A as a Tpetra::CrsMatrix, if possible; else throws.
Definition: Ifpack2_RILUK_def.hpp:347
Tpetra::CrsMatrix< scalar_type, local_ordinal_type, global_ordinal_type, node_type > crs_matrix_type
Tpetra::CrsMatrix specialization used by this class for representing L and U.
Definition: Ifpack2_RILUK_decl.hpp:276
const crs_matrix_type & getU() const
Return the U factor of the ILU factorization.
Definition: Ifpack2_RILUK_def.hpp:172
MatrixType::node_type node_type
The Node type used by the input MatrixType.
Definition: Ifpack2_RILUK_decl.hpp:264
T * getRawPtr() const
Teuchos::RCP< const Tpetra::Map< local_ordinal_type, global_ordinal_type, node_type > > getDomainMap() const
Returns the Tpetra::Map object associated with the domain of this operator.
Definition: Ifpack2_RILUK_def.hpp:203
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
MatrixType::local_ordinal_type local_ordinal_type
The type of local indices in the input MatrixType.
Definition: Ifpack2_RILUK_decl.hpp:258
virtual ~RILUK()
Destructor (declared virtual for memory safety).
Definition: Ifpack2_RILUK_def.hpp:94
&quot;Preconditioner&quot; that solves local sparse triangular systems.
Definition: Ifpack2_LocalSparseTriangularSolver_decl.hpp:77
void compute()
Compute the (numeric) incomplete factorization.
Definition: Ifpack2_RILUK_def.hpp:644
TEUCHOS_DEPRECATED void reduceAll(const Comm< Ordinal > &comm, const EReductionType reductType, const Packet &send, Packet *globalReduct)
void apply(const Tpetra::MultiVector< scalar_type, local_ordinal_type, global_ordinal_type, node_type > &X, Tpetra::MultiVector< scalar_type, local_ordinal_type, global_ordinal_type, node_type > &Y, Teuchos::ETransp mode=Teuchos::NO_TRANS, scalar_type alpha=Teuchos::ScalarTraits< scalar_type >::one(), scalar_type beta=Teuchos::ScalarTraits< scalar_type >::zero()) const
Apply the (inverse of the) incomplete factorization to X, resulting in Y.
Definition: Ifpack2_RILUK_def.hpp:826
Teuchos::ScalarTraits< scalar_type >::magnitudeType magnitude_type
The type of the magnitude (absolute value) of a matrix entry.
Definition: Ifpack2_RILUK_decl.hpp:267
Construct a level filled graph for use in computing an ILU(k) incomplete factorization.
Definition: Ifpack2_IlukGraph.hpp:97
Teuchos::RCP< const row_matrix_type > getMatrix() const
Get the input matrix.
Definition: Ifpack2_RILUK_def.hpp:340
double totalElapsedTime(bool readCurrentTime=false) const
const crs_matrix_type & getL() const
Return the L factor of the ILU factorization.
Definition: Ifpack2_RILUK_def.hpp:141
MatrixType::scalar_type scalar_type
The type of the entries of the input MatrixType.
Definition: Ifpack2_RILUK_decl.hpp:255