Ifpack2 Templated Preconditioning Package  Version 1.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Ifpack2_MDF_def.hpp
1 // @HEADER
2 // *****************************************************************************
3 // Ifpack2: Templated Object-Oriented Algebraic Preconditioner Package
4 //
5 // Copyright 2009 NTESS and the Ifpack2 contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #ifndef IFPACK2_MDF_DEF_HPP
11 #define IFPACK2_MDF_DEF_HPP
12 
13 #include "Ifpack2_LocalFilter.hpp"
14 #include "Ifpack2_ScalingType.hpp"
15 #include "Tpetra_CrsMatrix.hpp"
16 #include "Teuchos_StandardParameterEntryValidators.hpp"
17 #include "Ifpack2_LocalSparseTriangularSolver.hpp"
18 #include "Ifpack2_Details_getParamTryingTypes.hpp"
19 #include "Kokkos_Core.hpp"
20 #include "Kokkos_Sort.hpp"
21 #include "KokkosKernels_Sorting.hpp"
22 #include <exception>
23 #include <type_traits>
24 
25 namespace Ifpack2 {
26 
27 namespace Details {
28 
29 namespace MDFImpl {
30 
31 template <class dev_view_t>
32 auto copy_view(const dev_view_t& vals) {
33  using Kokkos::view_alloc;
34  using Kokkos::WithoutInitializing;
35  typename dev_view_t::non_const_type newvals(view_alloc(vals.label(), WithoutInitializing), vals.extent(0));
36  Kokkos::deep_copy(newvals, vals);
37  return newvals;
38 }
39 
40 template <class array_t, class dev_view_t>
41 void copy_dev_view_to_host_array(array_t& array, const dev_view_t& dev_view) {
42  using host_view_t = typename dev_view_t::HostMirror;
43 
44  // Clear out existing and allocate
45  const auto ext = dev_view.extent(0);
46 
48  ext != size_t(array.size()), std::logic_error,
49  "Ifpack2::MDF::copy_dev_view_to_host_array: "
50  "Size of permuations on host and device do not match. "
51  "Please report this bug to the Ifpack2 developers.");
52 
53  // Wrap array data in view and copy
54  Kokkos::deep_copy(host_view_t(array.get(), ext), dev_view);
55 }
56 
57 template <class scalar_type, class local_ordinal_type, class global_ordinal_type, class node_type>
58 void applyReorderingPermutations(
59  const Tpetra::MultiVector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>& X,
60  Tpetra::MultiVector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>& Y,
62  TEUCHOS_TEST_FOR_EXCEPTION(X.getNumVectors() != Y.getNumVectors(), std::runtime_error,
63  "Ifpack2::MDF::applyReorderingPermuations ERROR: X.getNumVectors() != Y.getNumVectors().");
64 
66  Teuchos::ArrayRCP<Teuchos::ArrayRCP<scalar_type>> y_ptr = Y.get2dViewNonConst();
67 
68  for (size_t k = 0; k < X.getNumVectors(); k++)
69  for (local_ordinal_type i = 0; (size_t)i < X.getLocalLength(); i++)
70  y_ptr[k][perm[i]] = x_ptr[k][i];
71 }
72 
73 template <class scalar_type, class local_ordinal_type, class global_ordinal_type, class node_type>
74 auto get_local_crs_row_matrix(
75  Teuchos::RCP<const Tpetra::RowMatrix<scalar_type, local_ordinal_type, global_ordinal_type, node_type>> A_local) {
76  using Teuchos::Array;
77  using Teuchos::RCP;
78  using Teuchos::rcp;
79  using Teuchos::rcp_const_cast;
80  using Teuchos::rcp_dynamic_cast;
81 
82  using crs_matrix_type = Tpetra::CrsMatrix<scalar_type, local_ordinal_type, global_ordinal_type, node_type>;
83 
84  using nonconst_local_inds_host_view_type = typename crs_matrix_type::nonconst_local_inds_host_view_type;
85  using nonconst_values_host_view_type = typename crs_matrix_type::nonconst_values_host_view_type;
86 
87  RCP<const crs_matrix_type> A_local_crs = rcp_dynamic_cast<const crs_matrix_type>(A_local);
88  if (A_local_crs.is_null()) {
89  local_ordinal_type numRows = A_local->getLocalNumRows();
90  Array<size_t> entriesPerRow(numRows);
91  for (local_ordinal_type i = 0; i < numRows; i++) {
92  entriesPerRow[i] = A_local->getNumEntriesInLocalRow(i);
93  }
94  RCP<crs_matrix_type> A_local_crs_nc =
95  rcp(new crs_matrix_type(A_local->getRowMap(),
96  A_local->getColMap(),
97  entriesPerRow()));
98  // copy entries into A_local_crs
99  nonconst_local_inds_host_view_type indices("indices", A_local->getLocalMaxNumRowEntries());
100  nonconst_values_host_view_type values("values", A_local->getLocalMaxNumRowEntries());
101  for (local_ordinal_type i = 0; i < numRows; i++) {
102  size_t numEntries = 0;
103  A_local->getLocalRowCopy(i, indices, values, numEntries);
104  A_local_crs_nc->insertLocalValues(i, numEntries, reinterpret_cast<scalar_type*>(values.data()), indices.data());
105  }
106  A_local_crs_nc->fillComplete(A_local->getDomainMap(), A_local->getRangeMap());
107  A_local_crs = rcp_const_cast<const crs_matrix_type>(A_local_crs_nc);
108  }
109 
110  return A_local_crs;
111 }
112 
113 } // namespace MDFImpl
114 
115 } // namespace Details
116 
117 template <class MatrixType>
119  : A_(Matrix_in)
120  , Verbosity_(0)
121  , LevelOfFill_(0)
122  , Overalloc_(2.)
123  , isAllocated_(false)
124  , isInitialized_(false)
125  , isComputed_(false)
126  , numInitialize_(0)
127  , numCompute_(0)
128  , numApply_(0)
129  , initializeTime_(0.0)
130  , computeTime_(0.0)
131  , applyTime_(0.0) {
132  allocateSolvers();
133  allocatePermutations();
134 }
135 
136 template <class MatrixType>
138  : A_(Matrix_in)
139  , Verbosity_(0)
140  , LevelOfFill_(0)
141  , Overalloc_(2.)
142  , isAllocated_(false)
143  , isInitialized_(false)
144  , isComputed_(false)
145  , numInitialize_(0)
146  , numCompute_(0)
147  , numApply_(0)
148  , initializeTime_(0.0)
149  , computeTime_(0.0)
150  , applyTime_(0.0) {
151  allocateSolvers();
152  allocatePermutations();
153 }
154 
155 template <class MatrixType>
156 void MDF<MatrixType>::allocatePermutations(bool force) {
157  if (A_.is_null()) return;
158 
159  // Allocate arrays as soon as size as known so their pointer is availabe
160  if (force || permutations_.is_null() || A_->getLocalNumRows() != size_t(permutations_.size())) {
161  permutations_ = Teuchos::null;
162  reversePermutations_ = Teuchos::null;
163  permutations_ = permutations_type(A_->getLocalNumRows());
164  reversePermutations_ = permutations_type(A_->getLocalNumRows());
165  }
166 }
167 
168 template <class MatrixType>
169 void MDF<MatrixType>::allocateSolvers() {
170  L_solver_ = Teuchos::null;
171  U_solver_ = Teuchos::null;
172  L_solver_ = Teuchos::rcp(new LocalSparseTriangularSolver<row_matrix_type>());
173  L_solver_->setObjectLabel("lower");
174  U_solver_ = Teuchos::rcp(new LocalSparseTriangularSolver<row_matrix_type>());
175  U_solver_->setObjectLabel("upper");
176 }
177 
178 template <class MatrixType>
180  // It's legal for A to be null; in that case, you may not call
181  // initialize() until calling setMatrix() with a nonnull input.
182  // Regardless, setting the matrix invalidates any previous
183  // factorization.
184  if (A.getRawPtr() != A_.getRawPtr()) {
185  isAllocated_ = false;
186  isInitialized_ = false;
187  isComputed_ = false;
188  A_local_ = Teuchos::null;
189  MDF_handle_ = Teuchos::null;
190 
191  // The sparse triangular solvers get a triangular factor as their
192  // input matrix. The triangular factors L_ and U_ are getting
193  // reset, so we reset the solvers' matrices to null. Do that
194  // before setting L_ and U_ to null, so that latter step actually
195  // frees the factors.
196  if (!L_solver_.is_null()) {
197  L_solver_->setMatrix(Teuchos::null);
198  }
199  if (!U_solver_.is_null()) {
200  U_solver_->setMatrix(Teuchos::null);
201  }
202 
203  L_ = Teuchos::null;
204  U_ = Teuchos::null;
205  A_ = A;
206 
207  allocatePermutations(true);
208  }
209 }
210 
211 template <class MatrixType>
212 const typename MDF<MatrixType>::crs_matrix_type&
215  L_.is_null(), std::runtime_error,
216  "Ifpack2::MDF::getL: The L factor "
217  "is null. Please call initialize() and compute() "
218  "before calling this method. If the input matrix has not yet been set, "
219  "you must first call setMatrix() with a nonnull input matrix before you "
220  "may call initialize() or compute().");
221  return *L_;
222 }
223 
224 template <class MatrixType>
228  permutations_.is_null(), std::runtime_error,
229  "Ifpack2::MDF::getPermutations: "
230  "The permulations are null. Please call initialize() and compute() "
231  "before calling this method. If the input matrix has not yet been set, "
232  "you must first call setMatrix() with a nonnull input matrix before you "
233  "may call initialize() or compute().");
234  return const_cast<permutations_type&>(permutations_);
235 }
236 template <class MatrixType>
240  reversePermutations_.is_null(), std::runtime_error,
241  "Ifpack2::MDF::getReversePermutations: "
242  "The permulations are null. Please call initialize() and compute() "
243  "before calling this method. If the input matrix has not yet been set, "
244  "you must first call setMatrix() with a nonnull input matrix before you "
245  "may call initialize() or compute().");
246  return const_cast<permutations_type&>(reversePermutations_);
247 }
248 
249 template <class MatrixType>
250 const typename MDF<MatrixType>::crs_matrix_type&
253  U_.is_null(), std::runtime_error,
254  "Ifpack2::MDF::getU: The U factor "
255  "is null. Please call initialize() and compute() "
256  "before calling this method. If the input matrix has not yet been set, "
257  "you must first call setMatrix() with a nonnull input matrix before you "
258  "may call initialize() or compute().");
259  return *U_;
260 }
261 
262 template <class MatrixType>
265  A_.is_null(), std::runtime_error,
266  "Ifpack2::MDF::getNodeSmootherComplexity: "
267  "The input matrix A is null. Please call setMatrix() with a nonnull "
268  "input matrix, then call compute(), before calling this method.");
269  // MDF methods cost roughly one apply + the nnz in the upper+lower triangles
270  if (!L_.is_null() && !U_.is_null())
271  return A_->getLocalNumEntries() + L_->getLocalNumEntries() + U_->getLocalNumEntries();
272  else
273  return 0;
274 }
275 
276 template <class MatrixType>
279  typename MDF<MatrixType>::node_type>>
282  A_.is_null(), std::runtime_error,
283  "Ifpack2::MDF::getDomainMap: "
284  "The matrix is null. Please call setMatrix() with a nonnull input "
285  "before calling this method.");
286 
287  // FIXME (mfh 25 Jan 2014) Shouldn't this just come from A_?
289  L_.is_null(), std::runtime_error,
290  "Ifpack2::MDF::getDomainMap: "
291  "The computed graph is null. Please call initialize() and compute() before calling "
292  "this method.");
293  return L_->getDomainMap();
294 }
295 
296 template <class MatrixType>
299  typename MDF<MatrixType>::node_type>>
302  A_.is_null(), std::runtime_error,
303  "Ifpack2::MDF::getRangeMap: "
304  "The matrix is null. Please call setMatrix() with a nonnull input "
305  "before calling this method.");
306 
307  // FIXME (mfh 25 Jan 2014) Shouldn't this just come from A_?
309  L_.is_null(), std::runtime_error,
310  "Ifpack2::MDF::getRangeMap: "
311  "The computed graph is null. Please call initialize() abd compute() before calling "
312  "this method.");
313  return L_->getRangeMap();
314 }
315 
316 template <class MatrixType>
319  using Details::getParamTryingTypes;
320  using Teuchos::Array;
322  using Teuchos::RCP;
323  const char prefix[] = "Ifpack2::MDF: ";
324 
325  // Default values of the various parameters.
326  int fillLevel = 0;
327  double overalloc = 2.;
328  int verbosity = 0;
329 
330  // "fact: mdf level-of-fill" parsing is more complicated, because
331  // we want to allow as many types as make sense. int is the native
332  // type, but we also want to accept double (for backwards
333  // compatibilty with ILUT). You can't cast arbitrary magnitude_type
334  // (e.g., Sacado::MP::Vector) to int, so we use float instead, to
335  // get coverage of the most common magnitude_type cases. Weirdly,
336  // there's an Ifpack2 test that sets the fill level as a
337  // global_ordinal_type.
338  {
339  const std::string paramName("fact: mdf level-of-fill");
340  getParamTryingTypes<int, int, global_ordinal_type, double, float>(fillLevel, params, paramName, prefix);
341 
342  TEUCHOS_TEST_FOR_EXCEPTION(fillLevel != 0, std::runtime_error, prefix << "MDF with level of fill != 0 is not yet implemented.");
343  }
344  {
345  const std::string paramName("Verbosity");
346  getParamTryingTypes<int, int, global_ordinal_type, double, float>(verbosity, params, paramName, prefix);
347  }
348  {
349  const std::string paramName("fact: mdf overalloc");
350  getParamTryingTypes<double, double>(overalloc, params, paramName, prefix);
351  }
352 
353  // Forward to trisolvers.
354  L_solver_->setParameters(params);
355  U_solver_->setParameters(params);
356 
357  // "Commit" the values only after validating all of them. This
358  // ensures that there are no side effects if this routine throws an
359  // exception.
360 
361  LevelOfFill_ = fillLevel;
362  Overalloc_ = overalloc;
363  Verbosity_ = verbosity;
364 }
365 
366 template <class MatrixType>
369  return Teuchos::rcp_implicit_cast<const row_matrix_type>(A_);
370 }
371 
372 template <class MatrixType>
375  return Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A_, true);
376 }
377 
378 template <class MatrixType>
381  using Teuchos::RCP;
382  using Teuchos::rcp;
383  using Teuchos::rcp_dynamic_cast;
384  using Teuchos::rcp_implicit_cast;
385 
386  // If A_'s communicator only has one process, or if its column and
387  // row Maps are the same, then it is already local, so use it
388  // directly.
389  if (A->getRowMap()->getComm()->getSize() == 1 ||
390  A->getRowMap()->isSameAs(*(A->getColMap()))) {
391  return A;
392  }
393 
394  // If A_ is already a LocalFilter, then use it directly. This
395  // should be the case if MDF is being used through
396  // AdditiveSchwarz, for example.
397  RCP<const LocalFilter<row_matrix_type>> A_lf_r =
398  rcp_dynamic_cast<const LocalFilter<row_matrix_type>>(A);
399  if (!A_lf_r.is_null()) {
400  return rcp_implicit_cast<const row_matrix_type>(A_lf_r);
401  } else {
402  // A_'s communicator has more than one process, its row Map and
403  // its column Map differ, and A_ is not a LocalFilter. Thus, we
404  // have to wrap it in a LocalFilter.
405  return rcp(new LocalFilter<row_matrix_type>(A));
406  }
407 }
408 
409 template <class MatrixType>
411  using Teuchos::Array;
412  using Teuchos::ArrayView;
413  using Teuchos::RCP;
414  using Teuchos::rcp;
415  using Teuchos::rcp_const_cast;
416  using Teuchos::rcp_dynamic_cast;
417  using Teuchos::rcp_implicit_cast;
418  const char prefix[] = "Ifpack2::MDF::initialize: ";
419 
420  TEUCHOS_TEST_FOR_EXCEPTION(A_.is_null(), std::runtime_error, prefix << "The matrix is null. Please "
421  "call setMatrix() with a nonnull input before calling this method.");
422  TEUCHOS_TEST_FOR_EXCEPTION(!A_->isFillComplete(), std::runtime_error, prefix << "The matrix is not "
423  "fill complete. You may not invoke initialize() or compute() with this "
424  "matrix until the matrix is fill complete. If your matrix is a "
425  "Tpetra::CrsMatrix, please call fillComplete on it (with the domain and "
426  "range Maps, if appropriate) before calling this method.");
427 
428  Teuchos::Time timer("MDF::initialize");
429  double startTime = timer.wallTime();
430  { // Start timing
431  Teuchos::TimeMonitor timeMon(timer);
432 
433  // Calling initialize() means that the user asserts that the graph
434  // of the sparse matrix may have changed. We must not just reuse
435  // the previous graph in that case.
436  //
437  // Regarding setting isAllocated_ to false: Eventually, we may want
438  // some kind of clever memory reuse strategy, but it's always
439  // correct just to blow everything away and start over.
440  isInitialized_ = false;
441  isAllocated_ = false;
442  isComputed_ = false;
443  MDF_handle_ = Teuchos::null;
444 
445  A_local_ = makeLocalFilter(A_);
447  A_local_.is_null(), std::logic_error,
448  "Ifpack2::MDF::initialize: "
449  "makeLocalFilter returned null; it failed to compute A_local. "
450  "Please report this bug to the Ifpack2 developers.");
451 
452  // FIXME (mfh 24 Jan 2014, 26 Mar 2014) It would be more efficient
453  // to rewrite MDF so that it works with any RowMatrix input, not
454  // just CrsMatrix. (That would require rewriting mdfGraph to
455  // handle a Tpetra::RowGraph.) However, to make it work for now,
456  // we just copy the input matrix if it's not a CrsMatrix.
457  {
458  RCP<const crs_matrix_type> A_local_crs = Details::MDFImpl::get_local_crs_row_matrix(A_local_);
459 
460  auto A_local_device = A_local_crs->getLocalMatrixDevice();
461  MDF_handle_ = rcp(new MDF_handle_device_type(A_local_device));
462  MDF_handle_->set_verbosity(Verbosity_);
463 
464  KokkosSparse::Experimental::mdf_symbolic(A_local_device, *MDF_handle_);
465 
466  isAllocated_ = true;
467  }
468 
469  checkOrderingConsistency(*A_local_);
470  } // Stop timing
471 
472  isInitialized_ = true;
473  ++numInitialize_;
474  initializeTime_ += (timer.wallTime() - startTime);
475 }
476 
477 template <class MatrixType>
479  checkOrderingConsistency(const row_matrix_type& A) {
480  // First check that the local row map ordering is the same as the local portion of the column map.
481  // The extraction of the strictly lower/upper parts of A, as well as the factorization,
482  // implicitly assume that this is the case.
483  Teuchos::ArrayView<const global_ordinal_type> rowGIDs = A.getRowMap()->getLocalElementList();
484  Teuchos::ArrayView<const global_ordinal_type> colGIDs = A.getColMap()->getLocalElementList();
485  bool gidsAreConsistentlyOrdered = true;
486  global_ordinal_type indexOfInconsistentGID = 0;
487  for (global_ordinal_type i = 0; i < rowGIDs.size(); ++i) {
488  if (rowGIDs[i] != colGIDs[i]) {
489  gidsAreConsistentlyOrdered = false;
490  indexOfInconsistentGID = i;
491  break;
492  }
493  }
494  TEUCHOS_TEST_FOR_EXCEPTION(gidsAreConsistentlyOrdered == false, std::runtime_error,
495  "The ordering of the local GIDs in the row and column maps is not the same"
496  << std::endl
497  << "at index " << indexOfInconsistentGID
498  << ". Consistency is required, as all calculations are done with"
499  << std::endl
500  << "local indexing.");
501 }
502 
503 template <class MatrixType>
505  using Teuchos::Array;
506  using Teuchos::ArrayView;
507  using Teuchos::RCP;
508  using Teuchos::rcp;
509  using Teuchos::rcp_const_cast;
510  using Teuchos::rcp_dynamic_cast;
511  const char prefix[] = "Ifpack2::MDF::compute: ";
512 
513  // initialize() checks this too, but it's easier for users if the
514  // error shows them the name of the method that they actually
515  // called, rather than the name of some internally called method.
516  TEUCHOS_TEST_FOR_EXCEPTION(A_.is_null(), std::runtime_error, prefix << "The matrix is null. Please "
517  "call setMatrix() with a nonnull input before calling this method.");
518  TEUCHOS_TEST_FOR_EXCEPTION(!A_->isFillComplete(), std::runtime_error, prefix << "The matrix is not "
519  "fill complete. You may not invoke initialize() or compute() with this "
520  "matrix until the matrix is fill complete. If your matrix is a "
521  "Tpetra::CrsMatrix, please call fillComplete on it (with the domain and "
522  "range Maps, if appropriate) before calling this method.");
523 
524  if (!isInitialized()) {
525  initialize(); // Don't count this in the compute() time
526  }
527 
528  Teuchos::Time timer("MDF::compute");
529 
530  // Start timing
531  Teuchos::TimeMonitor timeMon(timer);
532  double startTime = timer.wallTime();
533 
534  isComputed_ = false;
535 
536  { // Make sure values in A is picked up even in case of pattern reuse
537  RCP<const crs_matrix_type> A_local_crs = Details::MDFImpl::get_local_crs_row_matrix(A_local_);
538 
539  // Compute the ordering and factorize
540  auto A_local_device = A_local_crs->getLocalMatrixDevice();
541 
542  KokkosSparse::Experimental::mdf_numeric(A_local_device, *MDF_handle_);
543  }
544 
545  // Ordering convention for MDF impl and here are reversed. Do reverse here to avoid confusion
546  Details::MDFImpl::copy_dev_view_to_host_array(reversePermutations_, MDF_handle_->permutation);
547  Details::MDFImpl::copy_dev_view_to_host_array(permutations_, MDF_handle_->permutation_inv);
548 
549  // TMR: Need to COPY the values held by the MDF handle because the CRS matrix needs to
550  // exclusively own them and the MDF_handles use_count contribution throws that off
551  {
552  auto L_mdf = MDF_handle_->getL();
553  L_ = rcp(new crs_matrix_type(
554  A_local_->getRowMap(),
555  A_local_->getColMap(),
556  Details::MDFImpl::copy_view(L_mdf.graph.row_map),
557  Details::MDFImpl::copy_view(L_mdf.graph.entries),
558  Details::MDFImpl::copy_view(L_mdf.values)));
559  }
560  {
561  auto U_mdf = MDF_handle_->getU();
562  U_ = rcp(new crs_matrix_type(
563  A_local_->getRowMap(),
564  A_local_->getColMap(),
565  Details::MDFImpl::copy_view(U_mdf.graph.row_map),
566  Details::MDFImpl::copy_view(U_mdf.graph.entries),
567  Details::MDFImpl::copy_view(U_mdf.values)));
568  }
569  L_->fillComplete();
570  U_->fillComplete();
571  L_solver_->setMatrix(L_);
572  L_solver_->initialize();
573  L_solver_->compute();
574  U_solver_->setMatrix(U_);
575  U_solver_->initialize();
576  U_solver_->compute();
577 
578  isComputed_ = true;
579  ++numCompute_;
580  computeTime_ += (timer.wallTime() - startTime);
581 }
582 
583 template <class MatrixType>
585  apply_impl(const Tpetra::MultiVector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>& X,
586  Tpetra::MultiVector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>& Y,
587  Teuchos::ETransp mode,
588  scalar_type alpha,
589  scalar_type beta) const {
590  const scalar_type one = STS::one();
591  const scalar_type zero = STS::zero();
592 
593  if (alpha == one && beta == zero) {
594  MV tmp(Y.getMap(), Y.getNumVectors());
595  Details::MDFImpl::applyReorderingPermutations(X, tmp, permutations_);
596  if (mode == Teuchos::NO_TRANS) { // Solve L (D (U Y)) = X for Y.
597  // Start by solving L Y = X for Y.
598  L_solver_->apply(tmp, Y, mode);
599  U_solver_->apply(Y, tmp, mode); // Solve U Y = Y.
600  } else { // Solve U^P (D^P (L^P Y)) = X for Y (where P is * or T).
601  // Start by solving U^P Y = X for Y.
602  U_solver_->apply(tmp, Y, mode);
603  L_solver_->apply(Y, tmp, mode); // Solve L^P Y = Y.
604  }
605  Details::MDFImpl::applyReorderingPermutations(tmp, Y, reversePermutations_);
606  } else { // alpha != 1 or beta != 0
607  if (alpha == zero) {
608  // The special case for beta == 0 ensures that if Y contains Inf
609  // or NaN values, we replace them with 0 (following BLAS
610  // convention), rather than multiplying them by 0 to get NaN.
611  if (beta == zero) {
612  Y.putScalar(zero);
613  } else {
614  Y.scale(beta);
615  }
616  } else { // alpha != zero
617  MV Y_tmp(Y.getMap(), Y.getNumVectors());
618  apply_impl(X, Y_tmp, mode);
619  Y.update(alpha, Y_tmp, beta);
620  }
621  }
622 }
623 
624 template <class MatrixType>
626  apply(const Tpetra::MultiVector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>& X,
627  Tpetra::MultiVector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>& Y,
628  Teuchos::ETransp mode,
629  scalar_type alpha,
630  scalar_type beta) const {
631  using Teuchos::RCP;
632  using Teuchos::rcpFromRef;
633 
635  A_.is_null(), std::runtime_error,
636  "Ifpack2::MDF::apply: The matrix is "
637  "null. Please call setMatrix() with a nonnull input, then initialize() "
638  "and compute(), before calling this method.");
640  !isComputed(), std::runtime_error,
641  "Ifpack2::MDF::apply: If you have not yet called compute(), "
642  "you must call compute() before calling this method.");
643  TEUCHOS_TEST_FOR_EXCEPTION(
644  X.getNumVectors() != Y.getNumVectors(), std::invalid_argument,
645  "Ifpack2::MDF::apply: X and Y do not have the same number of columns. "
646  "X.getNumVectors() = "
647  << X.getNumVectors()
648  << " != Y.getNumVectors() = " << Y.getNumVectors() << ".");
649  TEUCHOS_TEST_FOR_EXCEPTION(
650  STS::isComplex && mode == Teuchos::CONJ_TRANS, std::logic_error,
651  "Ifpack2::MDF::apply: mode = Teuchos::CONJ_TRANS is not implemented for "
652  "complex Scalar type. Please talk to the Ifpack2 developers to get this "
653  "fixed. There is a FIXME in this file about this very issue.");
654 #ifdef HAVE_IFPACK2_DEBUG
655  {
656  Teuchos::Array<magnitude_type> norms(X.getNumVectors());
657  X.norm1(norms());
658  bool good = true;
659  for (size_t j = 0; j < X.getNumVectors(); ++j) {
660  if (STM::isnaninf(norms[j])) {
661  good = false;
662  break;
663  }
664  }
665  TEUCHOS_TEST_FOR_EXCEPTION(!good, std::runtime_error, "Ifpack2::MDF::apply: The 1-norm of the input X is NaN or Inf.");
666  }
667 #endif // HAVE_IFPACK2_DEBUG
668 
669  Teuchos::Time timer("MDF::apply");
670  double startTime = timer.wallTime();
671  { // Start timing
672  Teuchos::TimeMonitor timeMon(timer);
673  apply_impl(X, Y, mode, alpha, beta);
674  } // end timing
675 
676 #ifdef HAVE_IFPACK2_DEBUG
677  {
678  Teuchos::Array<magnitude_type> norms(Y.getNumVectors());
679  Y.norm1(norms());
680  bool good = true;
681  for (size_t j = 0; j < Y.getNumVectors(); ++j) {
682  if (STM::isnaninf(norms[j])) {
683  good = false;
684  break;
685  }
686  }
687  TEUCHOS_TEST_FOR_EXCEPTION(!good, std::runtime_error, "Ifpack2::MDF::apply: The 1-norm of the output Y is NaN or Inf.");
688  }
689 #endif // HAVE_IFPACK2_DEBUG
690 
691  ++numApply_;
692  applyTime_ += (timer.wallTime() - startTime);
693 }
694 
695 template <class MatrixType>
696 std::string MDF<MatrixType>::description() const {
697  std::ostringstream os;
698 
699  // Output is a valid YAML dictionary in flow style. If you don't
700  // like everything on a single line, you should call describe()
701  // instead.
702  os << "\"Ifpack2::MDF\": {";
703  os << "Initialized: " << (isInitialized() ? "true" : "false") << ", "
704  << "Computed: " << (isComputed() ? "true" : "false") << ", ";
705 
706  os << "Level-of-fill: " << getLevelOfFill() << ", ";
707 
708  if (A_.is_null()) {
709  os << "Matrix: null";
710  } else {
711  os << "Global matrix dimensions: ["
712  << A_->getGlobalNumRows() << ", " << A_->getGlobalNumCols() << "]"
713  << ", Global nnz: " << A_->getGlobalNumEntries();
714  }
715 
716  if (!L_solver_.is_null()) os << ", " << L_solver_->description();
717  if (!U_solver_.is_null()) os << ", " << U_solver_->description();
718 
719  os << "}";
720  return os.str();
721 }
722 
723 } // namespace Ifpack2
724 
725 #define IFPACK2_MDF_INSTANT(S, LO, GO, N) \
726  template class Ifpack2::MDF<Tpetra::RowMatrix<S, LO, GO, N>>;
727 
728 #endif /* IFPACK2_MDF_DEF_HPP */
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_MDF_decl.hpp:87
Ifpack2::ScalingType enumerable type.
void compute()
Compute the (numeric) incomplete factorization.
Definition: Ifpack2_MDF_def.hpp:504
MDF (incomplete LU factorization with minimum discarded fill reordering) of a Tpetra sparse matrix...
Definition: Ifpack2_MDF_decl.hpp:50
permutations_type & getReversePermutations() const
Return the reverse permutations of the MDF factorization.
Definition: Ifpack2_MDF_def.hpp:238
MatrixType::node_type node_type
The Node type used by the input MatrixType.
Definition: Ifpack2_MDF_decl.hpp:69
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
std::string description() const
A one-line description of this object.
Definition: Ifpack2_MDF_def.hpp:696
size_type size() const
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_MDF_def.hpp:300
MatrixType::global_ordinal_type global_ordinal_type
The type of global indices in the input MatrixType.
Definition: Ifpack2_MDF_decl.hpp:66
Teuchos::RCP< const crs_matrix_type > getCrsMatrix() const
Return the input matrix A as a Tpetra::CrsMatrix, if possible; else throws.
Definition: Ifpack2_MDF_def.hpp:374
size_t getNodeSmootherComplexity() const
Get a rough estimate of cost per iteration.
Definition: Ifpack2_MDF_def.hpp:263
T * getRawPtr() const
permutations_type & getPermutations() const
Return the permutations of the MDF factorization.
Definition: Ifpack2_MDF_def.hpp:226
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
virtual void setMatrix(const Teuchos::RCP< const row_matrix_type > &A)
Change the matrix to be preconditioned.
Definition: Ifpack2_MDF_def.hpp:179
void setParameters(const Teuchos::ParameterList &params)
Definition: Ifpack2_MDF_def.hpp:318
const crs_matrix_type & getL() const
Return the L factor of the MDF factorization.
Definition: Ifpack2_MDF_def.hpp:213
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_MDF_def.hpp:280
Tpetra::RowMatrix< scalar_type, local_ordinal_type, global_ordinal_type, node_type > row_matrix_type
Tpetra::RowMatrix specialization used by this class.
Definition: Ifpack2_MDF_decl.hpp:85
MatrixType::scalar_type scalar_type
The type of the entries of the input MatrixType.
Definition: Ifpack2_MDF_decl.hpp:60
Teuchos::RCP< const row_matrix_type > getMatrix() const
Get the input matrix.
Definition: Ifpack2_MDF_def.hpp:368
static double wallTime()
const crs_matrix_type & getU() const
Return the U factor of the MDF factorization.
Definition: Ifpack2_MDF_def.hpp:251
void initialize()
Initialize by computing the symbolic incomplete factorization.
Definition: Ifpack2_MDF_def.hpp:410
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_MDF_def.hpp:626
bool is_null() const