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