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