Ifpack2 Templated Preconditioning Package Version 1.0
Loading...
Searching...
No Matches
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"
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
25namespace Ifpack2 {
26
27namespace Details {
28
29namespace MDFImpl {
30
31template <class dev_view_t>
32auto 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
40template <class array_t, class dev_view_t>
41void copy_dev_view_to_host_array(array_t& array, const dev_view_t& dev_view) {
42 using host_view_t = typename dev_view_t::host_mirror_type;
43
44 // Clear out existing and allocate
45 const auto ext = dev_view.extent(0);
46
47 TEUCHOS_TEST_FOR_EXCEPTION(
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
57template <class scalar_type, class local_ordinal_type, class global_ordinal_type, class node_type>
58void 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,
61 const Teuchos::ArrayRCP<local_ordinal_type>& perm) {
62 TEUCHOS_TEST_FOR_EXCEPTION(X.getNumVectors() != Y.getNumVectors(), std::runtime_error,
63 "Ifpack2::MDF::applyReorderingPermuations ERROR: X.getNumVectors() != Y.getNumVectors().");
64
65 Teuchos::ArrayRCP<Teuchos::ArrayRCP<const scalar_type>> x_ptr = X.get2dView();
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
73template <class scalar_type, class local_ordinal_type, class global_ordinal_type, class node_type>
74auto 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
117template <class MatrixType>
118MDF<MatrixType>::MDF(const Teuchos::RCP<const row_matrix_type>& Matrix_in)
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
136template <class MatrixType>
137MDF<MatrixType>::MDF(const Teuchos::RCP<const crs_matrix_type>& Matrix_in)
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
155template <class MatrixType>
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
168template <class MatrixType>
169void 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
178template <class MatrixType>
179void MDF<MatrixType>::setMatrix(const Teuchos::RCP<const row_matrix_type>& A) {
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
211template <class MatrixType>
214 TEUCHOS_TEST_FOR_EXCEPTION(
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
224template <class MatrixType>
225typename MDF<MatrixType>::permutations_type&
227 TEUCHOS_TEST_FOR_EXCEPTION(
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}
236template <class MatrixType>
237typename MDF<MatrixType>::permutations_type&
239 TEUCHOS_TEST_FOR_EXCEPTION(
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
249template <class MatrixType>
252 TEUCHOS_TEST_FOR_EXCEPTION(
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
262template <class MatrixType>
264 TEUCHOS_TEST_FOR_EXCEPTION(
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
276template <class MatrixType>
277Teuchos::RCP<const Tpetra::Map<typename MDF<MatrixType>::local_ordinal_type,
281 TEUCHOS_TEST_FOR_EXCEPTION(
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_?
288 TEUCHOS_TEST_FOR_EXCEPTION(
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
296template <class MatrixType>
297Teuchos::RCP<const Tpetra::Map<typename MDF<MatrixType>::local_ordinal_type,
301 TEUCHOS_TEST_FOR_EXCEPTION(
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_?
308 TEUCHOS_TEST_FOR_EXCEPTION(
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
316template <class MatrixType>
318 setParameters(const Teuchos::ParameterList& params) {
319 using Details::getParamTryingTypes;
320 using Teuchos::Array;
321 using Teuchos::ParameterList;
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
366template <class MatrixType>
367Teuchos::RCP<const typename MDF<MatrixType>::row_matrix_type>
369 return Teuchos::rcp_implicit_cast<const row_matrix_type>(A_);
370}
371
372template <class MatrixType>
373Teuchos::RCP<const typename MDF<MatrixType>::crs_matrix_type>
375 return Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A_, true);
376}
377
378template <class MatrixType>
379Teuchos::RCP<const typename MDF<MatrixType>::row_matrix_type>
380MDF<MatrixType>::makeLocalFilter(const Teuchos::RCP<const row_matrix_type>& A) {
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
409template <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_);
446 TEUCHOS_TEST_FOR_EXCEPTION(
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
477template <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
503template <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
583template <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
624template <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
634 TEUCHOS_TEST_FOR_EXCEPTION(
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.");
639 TEUCHOS_TEST_FOR_EXCEPTION(
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
695template <class MatrixType>
696std::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 */
Ifpack2::ScalingType enumerable type.
MDF (incomplete LU factorization with minimum discarded fill reordering) of a Tpetra sparse matrix.
Definition Ifpack2_MDF_decl.hpp:57
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
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
permutations_type & getReversePermutations() const
Return the reverse permutations of the MDF factorization.
Definition Ifpack2_MDF_def.hpp:238
const crs_matrix_type & getU() const
Return the U factor of the MDF factorization.
Definition Ifpack2_MDF_def.hpp:251
void compute()
Compute the (numeric) incomplete factorization.
Definition Ifpack2_MDF_def.hpp:504
void initialize()
Initialize by computing the symbolic incomplete factorization.
Definition Ifpack2_MDF_def.hpp:410
std::string description() const
A one-line description of this object.
Definition Ifpack2_MDF_def.hpp:696
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
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:94
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
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
MatrixType::node_type node_type
The Node type used by the input MatrixType.
Definition Ifpack2_MDF_decl.hpp:69
MatrixType::global_ordinal_type global_ordinal_type
The type of global indices in the input MatrixType.
Definition Ifpack2_MDF_decl.hpp:66
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
virtual void setMatrix(const Teuchos::RCP< const row_matrix_type > &A)
Change the matrix to be preconditioned.
Definition Ifpack2_MDF_def.hpp:179
size_t getNodeSmootherComplexity() const
Get a rough estimate of cost per iteration.
Definition Ifpack2_MDF_def.hpp:263
permutations_type & getPermutations() const
Return the permutations of the MDF factorization.
Definition Ifpack2_MDF_def.hpp:226
Ifpack2 implementation details.
Preconditioners and smoothers for Tpetra sparse matrices.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:40