Ifpack2 Templated Preconditioning Package Version 1.0
Loading...
Searching...
No Matches
Ifpack2_ILUT_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_ILUT_DEF_HPP
11#define IFPACK2_ILUT_DEF_HPP
12
13#include <type_traits>
14#include "Teuchos_TypeNameTraits.hpp"
15#include "Teuchos_StandardParameterEntryValidators.hpp"
16#include "Teuchos_Time.hpp"
17#include "Tpetra_CrsMatrix.hpp"
18#include "KokkosSparse_par_ilut.hpp"
19#include "Kokkos_Profiling_ScopedRegion.hpp"
20
21#include "Ifpack2_Heap.hpp"
22#include "Ifpack2_LocalFilter.hpp"
23#include "Ifpack2_LocalSparseTriangularSolver.hpp"
24#include "Ifpack2_Details_getCrsMatrix.hpp"
25#include "Ifpack2_Parameters.hpp"
26#include "Ifpack2_Details_getParamTryingTypes.hpp"
27#include "Ifpack2_Details_getCrsMatrix.hpp"
28
29namespace Ifpack2 {
30
31namespace {
32
33struct IlutImplType {
34 enum Enum {
35 Serial,
36 PAR_ILUT
37 };
38
39 static void loadPLTypeOption(Teuchos::Array<std::string>& type_strs, Teuchos::Array<Enum>& type_enums) {
40 type_strs.resize(2);
41 type_strs[0] = "serial";
42 type_strs[1] = "par_ilut";
43 type_enums.resize(2);
44 type_enums[0] = Serial;
45 type_enums[1] = PAR_ILUT;
46 }
47};
48
73template <class ScalarType>
74inline typename Teuchos::ScalarTraits<ScalarType>::magnitudeType
75ilutDefaultDropTolerance() {
76 typedef Teuchos::ScalarTraits<ScalarType> STS;
77 typedef typename STS::magnitudeType magnitude_type;
78 typedef Teuchos::ScalarTraits<magnitude_type> STM;
79
80 // 1/2. Hopefully this can be represented in magnitude_type.
81 const magnitude_type oneHalf = STM::one() / (STM::one() + STM::one());
82
83 // The min ensures that in case magnitude_type has very low
84 // precision, we'll at least get some value strictly less than
85 // one.
86 return std::min(static_cast<magnitude_type>(1000) * STS::magnitude(STS::eps()), oneHalf);
87}
88
89// Full specialization for ScalarType = double.
90// This specialization preserves ILUT's previous default behavior.
91template <>
92inline Teuchos::ScalarTraits<double>::magnitudeType
93ilutDefaultDropTolerance<double>() {
94 return 1e-12;
95}
96
97template <class MatrixType>
98static Teuchos::RCP<typename ILUT<MatrixType>::crs_matrix_type>
99buildLocalCrsFromRowMatrix(const Teuchos::RCP<const typename ILUT<MatrixType>::row_matrix_type>& A_local) {
100 using ilut_type = ILUT<MatrixType>;
101 using crs_matrix_type = typename ilut_type::crs_matrix_type;
102 using local_ordinal_type = typename ilut_type::local_ordinal_type;
103 using scalar_type = typename ilut_type::scalar_type;
104 using nonconst_local_inds_host_view_type = typename ilut_type::nonconst_local_inds_host_view_type;
105 using nonconst_values_host_view_type = typename ilut_type::nonconst_values_host_view_type;
106 using Teuchos::Array;
107 using Teuchos::RCP;
108 using Teuchos::rcp;
109
110 local_ordinal_type numRows = A_local->getLocalNumRows();
111 Array<size_t> entriesPerRow(numRows);
112 for (local_ordinal_type i = 0; i < numRows; i++) {
113 entriesPerRow[i] = A_local->getNumEntriesInLocalRow(i);
114 }
115
116 RCP<crs_matrix_type> A_local_crs_nc =
117 rcp(new crs_matrix_type(A_local->getRowMap(),
118 A_local->getColMap(),
119 entriesPerRow()));
120
121 nonconst_local_inds_host_view_type indices("indices", A_local->getLocalMaxNumRowEntries());
122 nonconst_values_host_view_type values("values", A_local->getLocalMaxNumRowEntries());
123
124 for (local_ordinal_type i = 0; i < numRows; i++) {
125 size_t numEntries = 0;
126 A_local->getLocalRowCopy(i, indices, values, numEntries);
127 A_local_crs_nc->insertLocalValues(i, static_cast<local_ordinal_type>(numEntries),
128 reinterpret_cast<scalar_type*>(values.data()),
129 indices.data());
130 }
131
132 A_local_crs_nc->fillComplete(A_local->getDomainMap(), A_local->getRangeMap());
133 return A_local_crs_nc;
134}
135
136template <class MatrixType>
137static void refreshCachedSourceCrsFromRowMatrix(
138 const Teuchos::RCP<const typename ILUT<MatrixType>::row_matrix_type>& A_local,
139 const Teuchos::RCP<typename ILUT<MatrixType>::crs_matrix_type>& source_crs_nc) {
140 using ilut_type = ILUT<MatrixType>;
141 using local_ordinal_type = typename ilut_type::local_ordinal_type;
142 using scalar_type = typename ilut_type::scalar_type;
143 using nonconst_local_inds_host_view_type = typename ilut_type::nonconst_local_inds_host_view_type;
144 using nonconst_values_host_view_type = typename ilut_type::nonconst_values_host_view_type;
145
146 source_crs_nc->resumeFill();
147
148 const local_ordinal_type numRows = A_local->getLocalNumRows();
149 nonconst_local_inds_host_view_type indices("indices", A_local->getLocalMaxNumRowEntries());
150 nonconst_values_host_view_type values("values", A_local->getLocalMaxNumRowEntries());
151
152 for (local_ordinal_type i = 0; i < numRows; i++) {
153 size_t numEntries = 0;
154 A_local->getLocalRowCopy(i, indices, values, numEntries);
155 source_crs_nc->replaceLocalValues(i, static_cast<local_ordinal_type>(numEntries),
156 reinterpret_cast<scalar_type*>(values.data()),
157 indices.data());
158 }
159
160 source_crs_nc->fillComplete(A_local->getDomainMap(), A_local->getRangeMap());
161}
162
163} // namespace
164
165template <class MatrixType>
166ILUT<MatrixType>::ILUT(const Teuchos::RCP<const row_matrix_type>& A)
167 : A_(A)
168 , Athresh_(Teuchos::ScalarTraits<magnitude_type>::zero())
169 , Rthresh_(Teuchos::ScalarTraits<magnitude_type>::one())
170 , RelaxValue_(Teuchos::ScalarTraits<magnitude_type>::zero())
171 , LevelOfFill_(1.0)
172 , DropTolerance_(ilutDefaultDropTolerance<scalar_type>())
173 , par_ilut_options_{1, 0., -1, -1, 0.75, false}
174 , InitializeTime_(0.0)
175 , ComputeTime_(0.0)
176 , ApplyTime_(0.0)
177 , NumInitialize_(0)
178 , NumCompute_(0)
179 , NumApply_(0)
180 , IsInitialized_(false)
181 , IsComputed_(false)
182 , useKokkosKernelsParILUT_(false)
183
184{
185 allocateSolvers();
186}
187
188template <class MatrixType>
190 L_solver_ = Teuchos::rcp(new LocalSparseTriangularSolver<row_matrix_type>());
191 L_solver_->setObjectLabel("lower");
192 U_solver_ = Teuchos::rcp(new LocalSparseTriangularSolver<row_matrix_type>());
193 U_solver_->setObjectLabel("upper");
194}
195
196template <class MatrixType>
197void ILUT<MatrixType>::setParameters(const Teuchos::ParameterList& params) {
198 using Ifpack2::Details::getParamTryingTypes;
199 const char prefix[] = "Ifpack2::ILUT: ";
200
201 // Don't actually change the instance variables until we've checked
202 // all parameters. This ensures that setParameters satisfies the
203 // strong exception guarantee (i.e., is transactional).
204
205 // Parsing implementation type
206 IlutImplType::Enum ilutimplType = IlutImplType::Serial;
207 do {
208 static const char typeName[] = "fact: type";
209
210 if (!params.isType<std::string>(typeName)) break;
211
212 // Map std::string <-> IlutImplType::Enum.
213 Teuchos::Array<std::string> ilutimplTypeStrs;
214 Teuchos::Array<IlutImplType::Enum> ilutimplTypeEnums;
215 IlutImplType::loadPLTypeOption(ilutimplTypeStrs, ilutimplTypeEnums);
216 Teuchos::StringToIntegralParameterEntryValidator<IlutImplType::Enum>
217 s2i(ilutimplTypeStrs(), ilutimplTypeEnums(), typeName, false);
218
219 ilutimplType = s2i.getIntegralValue(params.get<std::string>(typeName));
220 } while (0);
221
222 if (ilutimplType == IlutImplType::PAR_ILUT) {
223 this->useKokkosKernelsParILUT_ = true;
224 } else {
225 this->useKokkosKernelsParILUT_ = false;
226 }
227
228 // Fill level in ILUT is a double, not a magnitude_type, because it
229 // depends on LO and GO, not on Scalar. Also, you can't cast
230 // arbitrary magnitude_type (e.g., Sacado::MP::Vector) to double.
231 double fillLevel = LevelOfFill_;
232 {
233 const std::string paramName("fact: ilut level-of-fill");
234 TEUCHOS_TEST_FOR_EXCEPTION(
235 (params.isParameter(paramName) && this->useKokkosKernelsParILUT_), std::runtime_error,
236 "Ifpack2::ILUT: Parameter " << paramName << " is meaningless for algorithm par_ilut.");
237 getParamTryingTypes<double, double, float>(fillLevel, params, paramName, prefix);
238 TEUCHOS_TEST_FOR_EXCEPTION(fillLevel < 1.0, std::runtime_error,
239 "Ifpack2::ILUT: The \"" << paramName << "\" parameter must be >= "
240 "1.0, but you set it to "
241 << fillLevel << ". For ILUT, the fill level "
242 "means something different than it does for ILU(k). ILU(0) produces "
243 "factors with the same sparsity structure as the input matrix A. For "
244 "ILUT, level-of-fill = 1.0 will produce factors with nonzeros matching "
245 "the sparsity structure of A. level-of-fill > 1.0 allows for additional "
246 "fill-in.");
247 }
248
249 magnitude_type absThresh = Athresh_;
250 {
251 const std::string paramName("fact: absolute threshold");
252 getParamTryingTypes<magnitude_type, magnitude_type, double>(absThresh, params, paramName, prefix);
253 }
254
255 magnitude_type relThresh = Rthresh_;
256 {
257 const std::string paramName("fact: relative threshold");
258 getParamTryingTypes<magnitude_type, magnitude_type, double>(relThresh, params, paramName, prefix);
259 }
260
261 magnitude_type relaxValue = RelaxValue_;
262 {
263 const std::string paramName("fact: relax value");
264 getParamTryingTypes<magnitude_type, magnitude_type, double>(relaxValue, params, paramName, prefix);
265 }
266
267 magnitude_type dropTol = DropTolerance_;
268 {
269 const std::string paramName("fact: drop tolerance");
270 getParamTryingTypes<magnitude_type, magnitude_type, double>(dropTol, params, paramName, prefix);
271 }
272
273 int par_ilut_max_iter = 20;
274 magnitude_type par_ilut_residual_norm_delta_stop = 1e-2;
275 int par_ilut_team_size = 0;
276 int par_ilut_vector_size = 0;
277 float par_ilut_fill_in_limit = 0.75;
278 bool par_ilut_verbose = false;
279 if (this->useKokkosKernelsParILUT_) {
280 par_ilut_max_iter = par_ilut_options_.max_iter;
281 par_ilut_residual_norm_delta_stop = par_ilut_options_.residual_norm_delta_stop;
282 par_ilut_team_size = par_ilut_options_.team_size;
283 par_ilut_vector_size = par_ilut_options_.vector_size;
284 par_ilut_fill_in_limit = par_ilut_options_.fill_in_limit;
285 par_ilut_verbose = par_ilut_options_.verbose;
286
287 std::string par_ilut_plist_name("parallel ILUT options");
288 if (params.isSublist(par_ilut_plist_name)) {
289 Teuchos::ParameterList const& par_ilut_plist = params.sublist(par_ilut_plist_name);
290
291 std::string paramName("maximum iterations");
292 getParamTryingTypes<int, int>(par_ilut_max_iter, par_ilut_plist, paramName, prefix);
293
294 paramName = "residual norm delta stop";
295 getParamTryingTypes<magnitude_type, magnitude_type, double>(par_ilut_residual_norm_delta_stop, par_ilut_plist, paramName, prefix);
296
297 paramName = "team size";
298 getParamTryingTypes<int, int>(par_ilut_team_size, par_ilut_plist, paramName, prefix);
299
300 paramName = "vector size";
301 getParamTryingTypes<int, int>(par_ilut_vector_size, par_ilut_plist, paramName, prefix);
302
303 paramName = "fill in limit";
304 getParamTryingTypes<float, float, double>(par_ilut_fill_in_limit, par_ilut_plist, paramName, prefix);
305
306 paramName = "verbose";
307 getParamTryingTypes<bool, bool>(par_ilut_verbose, par_ilut_plist, paramName, prefix);
308
309 } // if (params.isSublist(par_ilut_plist_name))
310
311 par_ilut_options_.max_iter = par_ilut_max_iter;
312 par_ilut_options_.residual_norm_delta_stop = par_ilut_residual_norm_delta_stop;
313 par_ilut_options_.team_size = par_ilut_team_size;
314 par_ilut_options_.vector_size = par_ilut_vector_size;
315 par_ilut_options_.fill_in_limit = par_ilut_fill_in_limit;
316 par_ilut_options_.verbose = par_ilut_verbose;
317
318 } // if (this->useKokkosKernelsParILUT_)
319
320 // Forward to trisolvers.
321 L_solver_->setParameters(params);
322 U_solver_->setParameters(params);
323
324 LevelOfFill_ = fillLevel;
325 Athresh_ = absThresh;
326 Rthresh_ = relThresh;
327 RelaxValue_ = relaxValue;
328 DropTolerance_ = dropTol;
329}
330
331template <class MatrixType>
332Teuchos::RCP<const Teuchos::Comm<int> >
334 TEUCHOS_TEST_FOR_EXCEPTION(
335 A_.is_null(), std::runtime_error,
336 "Ifpack2::ILUT::getComm: "
337 "The matrix is null. Please call setMatrix() with a nonnull input "
338 "before calling this method.");
339 return A_->getComm();
340}
341
342template <class MatrixType>
343Teuchos::RCP<const typename ILUT<MatrixType>::row_matrix_type>
345 return A_;
346}
347
348template <class MatrixType>
349Teuchos::RCP<const typename ILUT<MatrixType>::map_type>
351 TEUCHOS_TEST_FOR_EXCEPTION(
352 A_.is_null(), std::runtime_error,
353 "Ifpack2::ILUT::getDomainMap: "
354 "The matrix is null. Please call setMatrix() with a nonnull input "
355 "before calling this method.");
356 return A_->getDomainMap();
357}
358
359template <class MatrixType>
360Teuchos::RCP<const typename ILUT<MatrixType>::map_type>
362 TEUCHOS_TEST_FOR_EXCEPTION(
363 A_.is_null(), std::runtime_error,
364 "Ifpack2::ILUT::getRangeMap: "
365 "The matrix is null. Please call setMatrix() with a nonnull input "
366 "before calling this method.");
367 return A_->getRangeMap();
368}
369
370template <class MatrixType>
372 return true;
373}
374
375template <class MatrixType>
377 return NumInitialize_;
378}
379
380template <class MatrixType>
382 return NumCompute_;
383}
384
385template <class MatrixType>
387 return NumApply_;
388}
389
390template <class MatrixType>
392 return InitializeTime_;
393}
394
395template <class MatrixType>
397 return ComputeTime_;
398}
399
400template <class MatrixType>
402 return ApplyTime_;
403}
404
405template <class MatrixType>
407 TEUCHOS_TEST_FOR_EXCEPTION(
408 A_.is_null(), std::runtime_error,
409 "Ifpack2::ILUT::getNodeSmootherComplexity: "
410 "The input matrix A is null. Please call setMatrix() with a nonnull "
411 "input matrix, then call compute(), before calling this method.");
412 // ILUT methods cost roughly one apply + the nnz in the upper+lower triangles
413 return A_->getLocalNumEntries() + getLocalNumEntries();
414}
415
416template <class MatrixType>
418 return L_->getGlobalNumEntries() + U_->getGlobalNumEntries();
419}
420
421template <class MatrixType>
423 return L_->getLocalNumEntries() + U_->getLocalNumEntries();
424}
425
426template <class MatrixType>
427void ILUT<MatrixType>::setMatrix(const Teuchos::RCP<const row_matrix_type>& A) {
428 if (A.getRawPtr() != A_.getRawPtr()) {
429 // Check in serial or one-process mode if the matrix is square.
430 TEUCHOS_TEST_FOR_EXCEPTION(
431 !A.is_null() && A->getComm()->getSize() == 1 &&
432 A->getLocalNumRows() != A->getLocalNumCols(),
433 std::runtime_error,
434 "Ifpack2::ILUT::setMatrix: If A's communicator only "
435 "contains one process, then A must be square. Instead, you provided a "
436 "matrix A with "
437 << A->getLocalNumRows() << " rows and "
438 << A->getLocalNumCols() << " columns.");
439
440 // It's legal for A to be null; in that case, you may not call
441 // initialize() until calling setMatrix() with a nonnull input.
442 // Regardless, setting the matrix invalidates any previous
443 // factorization.
444 IsInitialized_ = false;
445 IsComputed_ = false;
446 A_local_ = Teuchos::null;
447 A_local_crs_ = Teuchos::null;
448 A_local_crs_nc_ = Teuchos::null;
449
450 // The sparse triangular solvers get a triangular factor as their
451 // input matrix. The triangular factors L_ and U_ are getting
452 // reset, so we reset the solvers' matrices to null. Do that
453 // before setting L_ and U_ to null, so that latter step actually
454 // frees the factors.
455 if (!L_solver_.is_null()) {
456 L_solver_->setMatrix(Teuchos::null);
457 }
458 if (!U_solver_.is_null()) {
459 U_solver_->setMatrix(Teuchos::null);
460 }
461
462 L_ = Teuchos::null;
463 U_ = Teuchos::null;
464 A_ = A;
465 }
466}
467
468template <class MatrixType>
469Teuchos::RCP<const typename ILUT<MatrixType>::row_matrix_type>
470ILUT<MatrixType>::makeLocalFilter(const Teuchos::RCP<const row_matrix_type>& A) {
471 using Teuchos::RCP;
472 using Teuchos::rcp;
473 using Teuchos::rcp_dynamic_cast;
474 using Teuchos::rcp_implicit_cast;
475
476 // If A_'s communicator only has one process, or if its column and
477 // row Maps are the same, then it is already local, so use it
478 // directly.
479 if (A->getRowMap()->getComm()->getSize() == 1 ||
480 A->getRowMap()->isSameAs(*(A->getColMap()))) {
481 return A;
482 }
483
484 // If A_ is already a LocalFilter, then use it directly. This
485 // should be the case if RILUT is being used through
486 // AdditiveSchwarz, for example.
487 RCP<const LocalFilter<row_matrix_type> > A_lf_r =
488 rcp_dynamic_cast<const LocalFilter<row_matrix_type> >(A);
489 if (!A_lf_r.is_null()) {
490 return rcp_implicit_cast<const row_matrix_type>(A_lf_r);
491 } else {
492 // A_'s communicator has more than one process, its row Map and
493 // its column Map differ, and A_ is not a LocalFilter. Thus, we
494 // have to wrap it in a LocalFilter.
495 return rcp(new LocalFilter<row_matrix_type>(A));
496 }
497}
498
499template <class MatrixType>
501 using Teuchos::Array;
502 using Teuchos::RCP;
503 using Teuchos::rcp_const_cast;
504 Teuchos::Time timer("ILUT::initialize");
505 double startTime = timer.wallTime();
506 {
507 Teuchos::TimeMonitor timeMon(timer);
508
509 // Check that the matrix is nonnull.
510 TEUCHOS_TEST_FOR_EXCEPTION(
511 A_.is_null(), std::runtime_error,
512 "Ifpack2::ILUT::initialize: "
513 "The matrix to precondition is null. Please call setMatrix() with a "
514 "nonnull input before calling this method.");
515
516 // Clear any previous computations.
517 IsInitialized_ = false;
518 IsComputed_ = false;
519 A_local_ = Teuchos::null;
520 A_local_crs_ = Teuchos::null;
521 A_local_crs_nc_ = Teuchos::null;
522 L_ = Teuchos::null;
523 U_ = Teuchos::null;
524
525 A_local_ = makeLocalFilter(A_); // Compute the local filter.
526 TEUCHOS_TEST_FOR_EXCEPTION(
527 A_local_.is_null(), std::logic_error,
528 "Ifpack2::RILUT::initialize: "
529 "makeLocalFilter returned null; it failed to compute A_local. "
530 "Please report this bug to the Ifpack2 developers.");
531
532 if (this->useKokkosKernelsParILUT_) {
533 this->KernelHandle_ = Teuchos::rcp(new kk_handle_type());
534 KernelHandle_->create_par_ilut_handle();
535 auto par_ilut_handle = KernelHandle_->get_par_ilut_handle();
536 par_ilut_handle->set_residual_norm_delta_stop(par_ilut_options_.residual_norm_delta_stop);
537 par_ilut_handle->set_team_size(par_ilut_options_.team_size);
538 par_ilut_handle->set_vector_size(par_ilut_options_.vector_size);
539 par_ilut_handle->set_max_iter(par_ilut_options_.max_iter);
540 par_ilut_handle->set_fill_in_limit(par_ilut_options_.fill_in_limit);
541 par_ilut_handle->set_verbose(par_ilut_options_.verbose);
542 par_ilut_handle->set_async_update(false);
543
544 {
545 Kokkos::Profiling::ScopedRegion region("Ifpack2::ILUT::initialize::par_ilut::get_or_build_A_local_crs");
546 A_local_crs_ = Ifpack2::Details::getCrsMatrix(A_local_);
547 if (A_local_crs_.is_null()) {
548 A_local_crs_nc_ = buildLocalCrsFromRowMatrix<MatrixType>(A_local_);
549 A_local_crs_ = A_local_crs_nc_;
550 }
551 }
552 auto A_local_crs_device = A_local_crs_->getLocalMatrixDevice();
553
554 // KokkosKernels requires unsigned
555 typedef typename Kokkos::View<usize_type*, array_layout, device_type> ulno_row_view_t;
556 const int NumMyRows = A_local_crs_->getRowMap()->getLocalNumElements();
557 L_rowmap_ = ulno_row_view_t("L_row_map", NumMyRows + 1);
558 U_rowmap_ = ulno_row_view_t("U_row_map", NumMyRows + 1);
559 L_rowmap_orig_ = ulno_row_view_t("L_row_map_orig", NumMyRows + 1);
560 U_rowmap_orig_ = ulno_row_view_t("U_row_map_orig", NumMyRows + 1);
561
562 KokkosSparse::Experimental::par_ilut_symbolic(KernelHandle_.getRawPtr(),
563 A_local_crs_device.graph.row_map, A_local_crs_device.graph.entries,
564 L_rowmap_,
565 U_rowmap_);
566
567 Kokkos::deep_copy(L_rowmap_orig_, L_rowmap_);
568 Kokkos::deep_copy(U_rowmap_orig_, U_rowmap_);
569 }
570
571 IsInitialized_ = true;
572 ++NumInitialize_;
573 } // timer scope
574 InitializeTime_ += (timer.wallTime() - startTime);
575}
576
577template <typename ScalarType>
578typename Teuchos::ScalarTraits<ScalarType>::magnitudeType
579scalar_mag(const ScalarType& s) {
580 return Teuchos::ScalarTraits<ScalarType>::magnitude(s);
581}
582
583template <class MatrixType>
585 using Teuchos::Array;
586 using Teuchos::ArrayRCP;
587 using Teuchos::ArrayView;
588 using Teuchos::as;
589 using Teuchos::rcp;
590 using Teuchos::RCP;
591 using Teuchos::rcp_const_cast;
592 using Teuchos::reduceAll;
593
594 // Don't count initialization in the compute() time.
595 if (!isInitialized()) {
596 initialize();
597 }
598
599 Teuchos::Time timer("ILUT::compute");
600 double startTime = timer.wallTime();
601 { // Timer scope for timing compute()
602 Teuchos::TimeMonitor timeMon(timer, true);
603
604 if (!this->useKokkosKernelsParILUT_) {
605 //--------------------------------------------------------------------------
606 // Ifpack2::ILUT's serial version is a translation of the Aztec ILUT
607 // implementation. The Aztec ILUT implementation was written by Ray Tuminaro.
608 //
609 // This isn't an exact translation of the Aztec ILUT algorithm, for the
610 // following reasons:
611 // 1. Minor differences result from the fact that Aztec factors a MSR format
612 // matrix in place, while the code below factors an input CrsMatrix which
613 // remains untouched and stores the resulting factors in separate L and U
614 // CrsMatrix objects.
615 // Also, the Aztec code begins by shifting the matrix pointers back
616 // by one, and the pointer contents back by one, and then using 1-based
617 // Fortran-style indexing in the algorithm. This Ifpack2 code uses C-style
618 // 0-based indexing throughout.
619 // 2. Aztec stores the inverse of the diagonal of U. This Ifpack2 code
620 // stores the non-inverted diagonal in U.
621 // The triangular solves (in Ifpack2::ILUT::apply()) are performed by
622 // calling the Tpetra::CrsMatrix::solve method on the L and U objects, and
623 // this requires U to contain the non-inverted diagonal.
624 //
625 // ABW.
626 //--------------------------------------------------------------------------
627
628 const scalar_type zero = STS::zero();
629 const scalar_type one = STS::one();
630
631 const local_ordinal_type myNumRows = A_local_->getLocalNumRows();
632
633 // If this macro is defined, files containing the L and U factors
634 // will be written. DON'T CHECK IN THE CODE WITH THIS MACRO ENABLED!!!
635 // #define IFPACK2_WRITE_ILUT_FACTORS
636#ifdef IFPACK2_WRITE_ILUT_FACTORS
637 std::ofstream ofsL("L.ifpack2_ilut.mtx", std::ios::out);
638 std::ofstream ofsU("U.ifpack2_ilut.mtx", std::ios::out);
639#endif
640
641 // Calculate how much fill will be allowed in addition to the
642 // space that corresponds to the input matrix entries.
643 double local_nnz = static_cast<double>(A_local_->getLocalNumEntries());
644 double fill = ((getLevelOfFill() - 1.0) * local_nnz) / (2 * myNumRows);
645
646 // std::ceil gives the smallest integer larger than the argument.
647 // this may give a slightly different result than Aztec's fill value in
648 // some cases.
649 double fill_ceil = std::ceil(fill);
650
651 // Similarly to Aztec, we will allow the same amount of fill for each
652 // row, half in L and half in U.
653 size_type fillL = static_cast<size_type>(fill_ceil);
654 size_type fillU = static_cast<size_type>(fill_ceil);
655
656 Array<scalar_type> InvDiagU(myNumRows, zero);
657
658 Array<Array<local_ordinal_type> > L_tmp_idx(myNumRows);
659 Array<Array<scalar_type> > L_tmpv(myNumRows);
660 Array<Array<local_ordinal_type> > U_tmp_idx(myNumRows);
661 Array<Array<scalar_type> > U_tmpv(myNumRows);
662
663 enum { UNUSED,
664 ORIG,
665 FILL };
666 local_ordinal_type max_col = myNumRows;
667
668 Array<int> pattern(max_col, UNUSED);
669 Array<scalar_type> cur_row(max_col, zero);
670 Array<magnitude_type> unorm(max_col);
671 magnitude_type rownorm;
672 Array<local_ordinal_type> L_cols_heap;
673 Array<local_ordinal_type> U_cols;
674 Array<local_ordinal_type> L_vals_heap;
675 Array<local_ordinal_type> U_vals_heap;
676
677 // A comparison object which will be used to create 'heaps' of indices
678 // that are ordered according to the corresponding values in the
679 // 'cur_row' array.
680 greater_indirect<scalar_type, local_ordinal_type> vals_comp(cur_row);
681
682 // =================== //
683 // start factorization //
684 // =================== //
685 nonconst_local_inds_host_view_type ColIndicesARCP;
686 nonconst_values_host_view_type ColValuesARCP;
687 if (!A_local_->supportsRowViews()) {
688 const size_t maxnz = A_local_->getLocalMaxNumRowEntries();
689 Kokkos::resize(ColIndicesARCP, maxnz);
690 Kokkos::resize(ColValuesARCP, maxnz);
691 }
692
693 for (local_ordinal_type row_i = 0; row_i < myNumRows; ++row_i) {
694 local_inds_host_view_type ColIndicesA;
695 values_host_view_type ColValuesA;
696 size_t RowNnz;
697
698 if (A_local_->supportsRowViews()) {
699 A_local_->getLocalRowView(row_i, ColIndicesA, ColValuesA);
700 RowNnz = ColIndicesA.size();
701 } else {
702 A_local_->getLocalRowCopy(row_i, ColIndicesARCP, ColValuesARCP, RowNnz);
703 ColIndicesA = Kokkos::subview(ColIndicesARCP, std::make_pair((size_t)0, RowNnz));
704 ColValuesA = Kokkos::subview(ColValuesARCP, std::make_pair((size_t)0, RowNnz));
705 }
706
707 // Always include the diagonal in the U factor. The value should get
708 // set in the next loop below.
709 U_cols.push_back(row_i);
710 cur_row[row_i] = zero;
711 pattern[row_i] = ORIG;
712
713 size_type L_cols_heaplen = 0;
714 rownorm = STM::zero();
715 for (size_t i = 0; i < RowNnz; ++i) {
716 if (ColIndicesA[i] < myNumRows) {
717 if (ColIndicesA[i] < row_i) {
718 add_to_heap(ColIndicesA[i], L_cols_heap, L_cols_heaplen);
719 } else if (ColIndicesA[i] > row_i) {
720 U_cols.push_back(ColIndicesA[i]);
721 }
722
723 cur_row[ColIndicesA[i]] = ColValuesA[i];
724 pattern[ColIndicesA[i]] = ORIG;
725 rownorm += scalar_mag(ColValuesA[i]);
726 }
727 }
728
729 // Alter the diagonal according to the absolute-threshold and
730 // relative-threshold values. If not set, those values default
731 // to zero and one respectively.
732 const magnitude_type rthresh = getRelativeThreshold();
733 const scalar_type& v = cur_row[row_i];
734 cur_row[row_i] = as<scalar_type>(getAbsoluteThreshold() * IFPACK2_SGN(v)) + rthresh * v;
735
736 size_type orig_U_len = U_cols.size();
737 RowNnz = L_cols_heap.size() + orig_U_len;
738 rownorm = getDropTolerance() * rownorm / RowNnz;
739
740 // The following while loop corresponds to the 'L30' goto's in Aztec.
741 size_type L_vals_heaplen = 0;
742 while (L_cols_heaplen > 0) {
743 local_ordinal_type row_k = L_cols_heap.front();
744
745 scalar_type multiplier = cur_row[row_k] * InvDiagU[row_k];
746 cur_row[row_k] = multiplier;
747 magnitude_type mag_mult = scalar_mag(multiplier);
748 if (mag_mult * unorm[row_k] < rownorm) {
749 pattern[row_k] = UNUSED;
750 rm_heap_root(L_cols_heap, L_cols_heaplen);
751 continue;
752 }
753 if (pattern[row_k] != ORIG) {
754 if (L_vals_heaplen < fillL) {
755 add_to_heap(row_k, L_vals_heap, L_vals_heaplen, vals_comp);
756 } else if (L_vals_heaplen == 0 ||
757 mag_mult < scalar_mag(cur_row[L_vals_heap.front()])) {
758 pattern[row_k] = UNUSED;
759 rm_heap_root(L_cols_heap, L_cols_heaplen);
760 continue;
761 } else {
762 pattern[L_vals_heap.front()] = UNUSED;
763 rm_heap_root(L_vals_heap, L_vals_heaplen, vals_comp);
764 add_to_heap(row_k, L_vals_heap, L_vals_heaplen, vals_comp);
765 }
766 }
767
768 /* Reduce current row */
769
770 ArrayView<local_ordinal_type> ColIndicesU = U_tmp_idx[row_k]();
771 ArrayView<scalar_type> ColValuesU = U_tmpv[row_k]();
772 size_type ColNnzU = ColIndicesU.size();
773
774 for (size_type j = 0; j < ColNnzU; ++j) {
775 if (ColIndicesU[j] > row_k) {
776 scalar_type tmp = multiplier * ColValuesU[j];
777 local_ordinal_type col_j = ColIndicesU[j];
778 if (pattern[col_j] != UNUSED) {
779 cur_row[col_j] -= tmp;
780 } else if (scalar_mag(tmp) > rownorm) {
781 cur_row[col_j] = -tmp;
782 pattern[col_j] = FILL;
783 if (col_j > row_i) {
784 U_cols.push_back(col_j);
785 } else {
786 add_to_heap(col_j, L_cols_heap, L_cols_heaplen);
787 }
788 }
789 }
790 }
791
792 rm_heap_root(L_cols_heap, L_cols_heaplen);
793 } // end of while(L_cols_heaplen) loop
794
795 // Put indices and values for L into arrays and then into the L_ matrix.
796
797 // first, the original entries from the L section of A:
798 for (size_type i = 0; i < (size_type)ColIndicesA.size(); ++i) {
799 if (ColIndicesA[i] < row_i) {
800 L_tmp_idx[row_i].push_back(ColIndicesA[i]);
801 L_tmpv[row_i].push_back(cur_row[ColIndicesA[i]]);
802 pattern[ColIndicesA[i]] = UNUSED;
803 }
804 }
805
806 // next, the L entries resulting from fill:
807 for (size_type j = 0; j < L_vals_heaplen; ++j) {
808 L_tmp_idx[row_i].push_back(L_vals_heap[j]);
809 L_tmpv[row_i].push_back(cur_row[L_vals_heap[j]]);
810 pattern[L_vals_heap[j]] = UNUSED;
811 }
812
813 // L has a one on the diagonal, but we don't explicitly store
814 // it. If we don't store it, then the kernel which performs the
815 // triangular solve can assume a unit diagonal, take a short-cut
816 // and perform faster.
817
818#ifdef IFPACK2_WRITE_ILUT_FACTORS
819 for (size_type ii = 0; ii < L_tmp_idx[row_i].size(); ++ii) {
820 ofsL << row_i << " " << L_tmp_idx[row_i][ii] << " "
821 << L_tmpv[row_i][ii] << std::endl;
822 }
823#endif
824
825 // Pick out the diagonal element, store its reciprocal.
826 if (cur_row[row_i] == zero) {
827 std::cerr << "Ifpack2::ILUT::Compute: zero pivot encountered! "
828 << "Replacing with rownorm and continuing..."
829 << "(You may need to set the parameter "
830 << "'fact: absolute threshold'.)" << std::endl;
831 cur_row[row_i] = rownorm;
832 }
833 InvDiagU[row_i] = one / cur_row[row_i];
834
835 // Non-inverted diagonal is stored for U:
836 U_tmp_idx[row_i].push_back(row_i);
837 U_tmpv[row_i].push_back(cur_row[row_i]);
838 unorm[row_i] = scalar_mag(cur_row[row_i]);
839 pattern[row_i] = UNUSED;
840
841 // Now put indices and values for U into arrays and then into the U_ matrix.
842 // The first entry in U_cols is the diagonal, which we just handled, so we'll
843 // start our loop at j=1.
844
845 size_type U_vals_heaplen = 0;
846 for (size_type j = 1; j < U_cols.size(); ++j) {
847 local_ordinal_type col = U_cols[j];
848 if (pattern[col] != ORIG) {
849 if (U_vals_heaplen < fillU) {
850 add_to_heap(col, U_vals_heap, U_vals_heaplen, vals_comp);
851 } else if (U_vals_heaplen != 0 && scalar_mag(cur_row[col]) >
852 scalar_mag(cur_row[U_vals_heap.front()])) {
853 rm_heap_root(U_vals_heap, U_vals_heaplen, vals_comp);
854 add_to_heap(col, U_vals_heap, U_vals_heaplen, vals_comp);
855 }
856 } else {
857 U_tmp_idx[row_i].push_back(col);
858 U_tmpv[row_i].push_back(cur_row[col]);
859 unorm[row_i] += scalar_mag(cur_row[col]);
860 }
861 pattern[col] = UNUSED;
862 }
863
864 for (size_type j = 0; j < U_vals_heaplen; ++j) {
865 U_tmp_idx[row_i].push_back(U_vals_heap[j]);
866 U_tmpv[row_i].push_back(cur_row[U_vals_heap[j]]);
867 unorm[row_i] += scalar_mag(cur_row[U_vals_heap[j]]);
868 }
869
870 unorm[row_i] /= (orig_U_len + U_vals_heaplen);
871
872#ifdef IFPACK2_WRITE_ILUT_FACTORS
873 for (int ii = 0; ii < U_tmp_idx[row_i].size(); ++ii) {
874 ofsU << row_i << " " << U_tmp_idx[row_i][ii] << " "
875 << U_tmpv[row_i][ii] << std::endl;
876 }
877#endif
878
879 L_cols_heap.clear();
880 U_cols.clear();
881 L_vals_heap.clear();
882 U_vals_heap.clear();
883 } // end of for(row_i) loop
884
885 // Now allocate and fill the matrices
886 Array<size_t> nnzPerRow(myNumRows);
887
888 // Make sure to release the old memory for L & U prior to recomputing to
889 // avoid bloating the high-water mark.
890 L_ = Teuchos::null;
891 U_ = Teuchos::null;
892 L_solver_->setMatrix(Teuchos::null);
893 U_solver_->setMatrix(Teuchos::null);
894
895 for (local_ordinal_type row_i = 0; row_i < myNumRows; ++row_i) {
896 nnzPerRow[row_i] = L_tmp_idx[row_i].size();
897 }
898
899 L_ = rcp(new crs_matrix_type(A_local_->getRowMap(), A_local_->getColMap(),
900 nnzPerRow()));
901
902 for (local_ordinal_type row_i = 0; row_i < myNumRows; ++row_i) {
903 L_->insertLocalValues(row_i, L_tmp_idx[row_i](), L_tmpv[row_i]());
904 }
905
906 L_->fillComplete();
907
908 for (local_ordinal_type row_i = 0; row_i < myNumRows; ++row_i) {
909 nnzPerRow[row_i] = U_tmp_idx[row_i].size();
910 }
911
912 U_ = rcp(new crs_matrix_type(A_local_->getRowMap(), A_local_->getColMap(),
913 nnzPerRow()));
914
915 for (local_ordinal_type row_i = 0; row_i < myNumRows; ++row_i) {
916 U_->insertLocalValues(row_i, U_tmp_idx[row_i](), U_tmpv[row_i]());
917 }
918
919 U_->fillComplete();
920
921 L_solver_->setMatrix(L_);
922 L_solver_->initialize();
923 L_solver_->compute();
924
925 U_solver_->setMatrix(U_);
926 U_solver_->initialize();
927 U_solver_->compute();
928
929 } // if (!this->useKokkosKernelsParILUT_)
930 else {
931 Kokkos::Profiling::ScopedRegion region_total("Ifpack2::ILUT::compute::par_ilut");
932
933 // Set L, U rowmaps back to original state. Par_ilut can change them, which invalidates them
934 // if compute is called again.
935 if (this->isComputed()) {
936 Kokkos::resize(L_rowmap_, L_rowmap_orig_.size());
937 Kokkos::resize(U_rowmap_, U_rowmap_orig_.size());
938 Kokkos::deep_copy(L_rowmap_, L_rowmap_orig_);
939 Kokkos::deep_copy(U_rowmap_, U_rowmap_orig_);
940 }
941
942 TEUCHOS_TEST_FOR_EXCEPTION(A_local_crs_.is_null(), std::runtime_error,
943 "Ifpack2::ILUT::compute::par_ilut: A_local_crs_ is null after initialize().");
944
945 // If A_local_ was not originally a CrsMatrix, initialize() built and cached
946 // a mutable CRS copy in A_local_crs_nc_. Repeated compute() calls must refresh
947 // that cached CRS with the current values of A_local_ before invoking par_ilut.
948 if (A_local_crs_nc_ != Teuchos::null) {
949 Kokkos::Profiling::ScopedRegion region("Ifpack2::ILUT::compute::par_ilut::refresh_cached_source_crs_values");
950 refreshCachedSourceCrsFromRowMatrix<MatrixType>(A_local_, A_local_crs_nc_);
951 }
952
953 { // Make sure values in A is picked up even in case of pattern reuse
954 auto lclMtx = A_local_crs_->getLocalMatrixDevice();
955 A_local_rowmap_ = lclMtx.graph.row_map;
956 A_local_entries_ = lclMtx.graph.entries;
957 A_local_values_ = lclMtx.values;
958 }
959
960 // JHU TODO Should allocation of L & U's column (aka entry) and value arrays occur here or in init()?
961 auto par_ilut_handle = KernelHandle_->get_par_ilut_handle();
962 auto nnzL = par_ilut_handle->get_nnzL();
963 static_graph_entries_t L_entries_ = static_graph_entries_t("L_entries", nnzL);
964 local_matrix_values_t L_values_ = local_matrix_values_t("L_values", nnzL);
965
966 auto nnzU = par_ilut_handle->get_nnzU();
967 static_graph_entries_t U_entries_ = static_graph_entries_t("U_entries", nnzU);
968 local_matrix_values_t U_values_ = local_matrix_values_t("U_values", nnzU);
969
970 {
971 Kokkos::Profiling::ScopedRegion region("Ifpack2::ILUT::compute::par_ilut::numeric");
972 KokkosSparse::Experimental::par_ilut_numeric(KernelHandle_.getRawPtr(),
973 A_local_rowmap_, A_local_entries_, A_local_values_,
974 L_rowmap_, L_entries_, L_values_, U_rowmap_, U_entries_, U_values_);
975 }
976
977 {
978 Kokkos::Profiling::ScopedRegion region("Ifpack2::ILUT::compute::par_ilut::build_factors");
979 auto L_kokkosCrsGraph = local_graph_device_type(L_entries_, L_rowmap_);
980 auto U_kokkosCrsGraph = local_graph_device_type(U_entries_, U_rowmap_);
981
982 local_matrix_device_type L_localCrsMatrix_device;
983 L_localCrsMatrix_device = local_matrix_device_type("L_Factor_localmatrix",
984 A_local_->getLocalNumRows(),
985 L_values_,
986 L_kokkosCrsGraph);
987
988 L_ = rcp(new crs_matrix_type(L_localCrsMatrix_device,
989 A_local_crs_->getRowMap(),
990 A_local_crs_->getColMap(),
991 A_local_crs_->getDomainMap(),
992 A_local_crs_->getRangeMap(),
993 A_local_crs_->getGraph()->getImporter(),
994 A_local_crs_->getGraph()->getExporter()));
995
996 local_matrix_device_type U_localCrsMatrix_device;
997 U_localCrsMatrix_device = local_matrix_device_type("U_Factor_localmatrix",
998 A_local_->getLocalNumRows(),
999 U_values_,
1000 U_kokkosCrsGraph);
1001
1002 U_ = rcp(new crs_matrix_type(U_localCrsMatrix_device,
1003 A_local_crs_->getRowMap(),
1004 A_local_crs_->getColMap(),
1005 A_local_crs_->getDomainMap(),
1006 A_local_crs_->getRangeMap(),
1007 A_local_crs_->getGraph()->getImporter(),
1008 A_local_crs_->getGraph()->getExporter()));
1009 }
1010
1011 {
1012 Kokkos::Profiling::ScopedRegion region("Ifpack2::ILUT::compute::par_ilut::solver_setup");
1013 L_solver_->setMatrix(L_);
1014 L_solver_->compute(); // NOTE: Only do compute if the pointer changed. Otherwise, do nothing
1015 U_solver_->setMatrix(U_);
1016 U_solver_->compute(); // NOTE: Only do compute if the pointer changed. Otherwise, do nothing
1017 }
1018 } // if (!this->useKokkosKernelsParILUT_) ... else ...
1019
1020 } // Timer scope for timing compute()
1021 ComputeTime_ += (timer.wallTime() - startTime);
1022 IsComputed_ = true;
1023 ++NumCompute_;
1024} // compute()
1025
1026template <class MatrixType>
1028 apply(const Tpetra::MultiVector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>& X,
1029 Tpetra::MultiVector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>& Y,
1030 Teuchos::ETransp mode,
1031 scalar_type alpha,
1032 scalar_type beta) const {
1033 using Teuchos::RCP;
1034 using Teuchos::rcp;
1035 using Teuchos::rcpFromRef;
1036
1037 TEUCHOS_TEST_FOR_EXCEPTION(
1038 !isComputed(), std::runtime_error,
1039 "Ifpack2::ILUT::apply: You must call compute() to compute the incomplete "
1040 "factorization, before calling apply().");
1041
1042 TEUCHOS_TEST_FOR_EXCEPTION(
1043 X.getNumVectors() != Y.getNumVectors(), std::runtime_error,
1044 "Ifpack2::ILUT::apply: X and Y must have the same number of columns. "
1045 "X has "
1046 << X.getNumVectors() << " columns, but Y has "
1047 << Y.getNumVectors() << " columns.");
1048
1049 const scalar_type one = STS::one();
1050 const scalar_type zero = STS::zero();
1051
1052 Teuchos::Time timer("ILUT::apply");
1053 double startTime = timer.wallTime();
1054 { // Start timing
1055 Teuchos::TimeMonitor timeMon(timer, true);
1056
1057 if (alpha == one && beta == zero) {
1058 if (mode == Teuchos::NO_TRANS) { // Solve L (U Y) = X for Y.
1059 // Start by solving L Y = X for Y.
1060 L_solver_->apply(X, Y, mode);
1061
1062 // Solve U Y = Y.
1063 U_solver_->apply(Y, Y, mode);
1064 } else { // Solve U^P (L^P Y)) = X for Y (where P is * or T).
1065
1066 // Start by solving U^P Y = X for Y.
1067 U_solver_->apply(X, Y, mode);
1068
1069 // Solve L^P Y = Y.
1070 L_solver_->apply(Y, Y, mode);
1071 }
1072 } else { // alpha != 1 or beta != 0
1073 if (alpha == zero) {
1074 // The special case for beta == 0 ensures that if Y contains Inf
1075 // or NaN values, we replace them with 0 (following BLAS
1076 // convention), rather than multiplying them by 0 to get NaN.
1077 if (beta == zero) {
1078 Y.putScalar(zero);
1079 } else {
1080 Y.scale(beta);
1081 }
1082 } else { // alpha != zero
1083 MV Y_tmp(Y.getMap(), Y.getNumVectors());
1084 apply(X, Y_tmp, mode);
1085 Y.update(alpha, Y_tmp, beta);
1086 }
1087 }
1088 } // end timing
1089
1090 ++NumApply_;
1091 ApplyTime_ += (timer.wallTime() - startTime);
1092} // apply()
1093
1094template <class MatrixType>
1096 std::ostringstream os;
1097
1098 // Output is a valid YAML dictionary in flow style. If you don't
1099 // like everything on a single line, you should call describe()
1100 // instead.
1101 os << "\"Ifpack2::ILUT\": {";
1102 os << "Initialized: " << (isInitialized() ? "true" : "false") << ", "
1103 << "Computed: " << (isComputed() ? "true" : "false") << ", ";
1104
1105 os << "Level-of-fill: " << getLevelOfFill() << ", "
1106 << "absolute threshold: " << getAbsoluteThreshold() << ", "
1107 << "relative threshold: " << getRelativeThreshold() << ", "
1108 << "relaxation value: " << getRelaxValue() << ", ";
1109
1110 if (A_.is_null()) {
1111 os << "Matrix: null";
1112 } else {
1113 auto crsMat = Details::getCrsMatrix(A_);
1114 os << "Global matrix dimensions: ["
1115 << A_->getGlobalNumRows() << ", " << A_->getGlobalNumCols() << "]";
1116 if (!crsMat.is_null() && crsMat->haveGlobalConstants())
1117 os << ", Global nnz: " << A_->getGlobalNumEntries();
1118 }
1119
1120 os << "}";
1121 return os.str();
1122}
1123
1124template <class MatrixType>
1126 describe(Teuchos::FancyOStream& out,
1127 const Teuchos::EVerbosityLevel verbLevel) const {
1128 using std::endl;
1129 using Teuchos::Comm;
1130 using Teuchos::OSTab;
1131 using Teuchos::RCP;
1132 using Teuchos::TypeNameTraits;
1133 using Teuchos::VERB_DEFAULT;
1134 using Teuchos::VERB_EXTREME;
1135 using Teuchos::VERB_HIGH;
1136 using Teuchos::VERB_LOW;
1137 using Teuchos::VERB_MEDIUM;
1138 using Teuchos::VERB_NONE;
1139
1140 const Teuchos::EVerbosityLevel vl =
1141 (verbLevel == VERB_DEFAULT) ? VERB_LOW : verbLevel;
1142 OSTab tab0(out);
1143
1144 if (vl > VERB_NONE) {
1145 out << "\"Ifpack2::ILUT\":" << endl;
1146 OSTab tab1(out);
1147 out << "MatrixType: " << TypeNameTraits<MatrixType>::name() << endl;
1148 if (this->getObjectLabel() != "") {
1149 out << "Label: \"" << this->getObjectLabel() << "\"" << endl;
1150 }
1151 out << "Initialized: " << (isInitialized() ? "true" : "false")
1152 << endl
1153 << "Computed: " << (isComputed() ? "true" : "false")
1154 << endl
1155 << "Level of fill: " << getLevelOfFill() << endl
1156 << "Absolute threshold: " << getAbsoluteThreshold() << endl
1157 << "Relative threshold: " << getRelativeThreshold() << endl
1158 << "Relax value: " << getRelaxValue() << endl;
1159
1160 auto crsMat = Details::getCrsMatrix(A_);
1161 if (isComputed() && (!crsMat.is_null() && crsMat->haveGlobalConstants()) && vl >= VERB_HIGH) {
1162 const double fillFraction =
1163 (double)getGlobalNumEntries() / (double)A_->getGlobalNumEntries();
1164 const double nnzToRows =
1165 (double)getGlobalNumEntries() / (double)U_->getGlobalNumRows();
1166
1167 out << "Dimensions of L: [" << L_->getGlobalNumRows() << ", "
1168 << L_->getGlobalNumRows() << "]" << endl
1169 << "Dimensions of U: [" << U_->getGlobalNumRows() << ", "
1170 << U_->getGlobalNumRows() << "]" << endl
1171 << "Number of nonzeros in factors: " << getGlobalNumEntries() << endl
1172 << "Fill fraction of factors over A: " << fillFraction << endl
1173 << "Ratio of nonzeros to rows: " << nnzToRows << endl;
1174 }
1175
1176 out << "Number of initialize calls: " << getNumInitialize() << endl
1177 << "Number of compute calls: " << getNumCompute() << endl
1178 << "Number of apply calls: " << getNumApply() << endl
1179 << "Total time in seconds for initialize: " << getInitializeTime() << endl
1180 << "Total time in seconds for compute: " << getComputeTime() << endl
1181 << "Total time in seconds for apply: " << getApplyTime() << endl;
1182
1183 out << "Local matrix:" << endl;
1184 A_local_->describe(out, vl);
1185 }
1186}
1187
1188} // namespace Ifpack2
1189
1190// FIXME (mfh 16 Sep 2014) We should really only use RowMatrix here!
1191// There's no need to instantiate for CrsMatrix too. All Ifpack2
1192// preconditioners can and should do dynamic casts if they need a type
1193// more specific than RowMatrix.
1194
1195#define IFPACK2_ILUT_INSTANT(S, LO, GO, N) \
1196 template class Ifpack2::ILUT<Tpetra::RowMatrix<S, LO, GO, N> >;
1197
1198#endif /* IFPACK2_ILUT_DEF_HPP */
ILUT (incomplete LU factorization with threshold) of a Tpetra sparse matrix.
Definition Ifpack2_ILUT_decl.hpp:65
Teuchos::ScalarTraits< scalar_type >::magnitudeType magnitude_type
The type of the magnitude (absolute value) of a matrix entry.
Definition Ifpack2_ILUT_decl.hpp:83
Teuchos::RCP< const Teuchos::Comm< int > > getComm() const
Returns the input matrix's communicator.
Definition Ifpack2_ILUT_def.hpp:333
double getInitializeTime() const
Returns the time spent in Initialize().
Definition Ifpack2_ILUT_def.hpp:391
void compute()
Compute factors L and U using the specified diagonal perturbation thresholds and relaxation parameter...
Definition Ifpack2_ILUT_def.hpp:584
Teuchos::RCP< const row_matrix_type > getMatrix() const
Returns a reference to the matrix to be preconditioned.
Definition Ifpack2_ILUT_def.hpp:344
global_size_t getGlobalNumEntries() const
Returns the number of nonzero entries in the global graph.
Definition Ifpack2_ILUT_def.hpp:417
int getNumInitialize() const
Returns the number of calls to Initialize().
Definition Ifpack2_ILUT_def.hpp:376
MatrixType::local_ordinal_type local_ordinal_type
The type of local indices in the input MatrixType.
Definition Ifpack2_ILUT_decl.hpp:74
Teuchos::RCP< const map_type > getDomainMap() const
Tpetra::Map representing the domain of this operator.
Definition Ifpack2_ILUT_def.hpp:350
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 ILUT preconditioner to X, resulting in Y.
Definition Ifpack2_ILUT_def.hpp:1028
int getNumApply() const
Returns the number of calls to apply().
Definition Ifpack2_ILUT_def.hpp:386
size_t getNodeSmootherComplexity() const
Get a rough estimate of cost per iteration.
Definition Ifpack2_ILUT_def.hpp:406
MatrixType::scalar_type scalar_type
The type of the entries of the input MatrixType.
Definition Ifpack2_ILUT_decl.hpp:71
std::string description() const
Return a simple one-line description of this object.
Definition Ifpack2_ILUT_def.hpp:1095
bool hasTransposeApply() const
Whether this object's apply() method can apply the transpose (or conjugate transpose,...
Definition Ifpack2_ILUT_def.hpp:371
void describe(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel=Teuchos::Describable::verbLevel_default) const
Print the object with some verbosity level to an FancyOStream object.
Definition Ifpack2_ILUT_def.hpp:1126
ILUT(const Teuchos::RCP< const row_matrix_type > &A)
Constructor.
Definition Ifpack2_ILUT_def.hpp:166
virtual void setMatrix(const Teuchos::RCP< const row_matrix_type > &A)
Change the matrix to be preconditioned.
Definition Ifpack2_ILUT_def.hpp:427
Tpetra::CrsMatrix< scalar_type, local_ordinal_type, global_ordinal_type, node_type > crs_matrix_type
Type of the Tpetra::CrsMatrix specialization that this class uses for the L and U factors.
Definition Ifpack2_ILUT_decl.hpp:107
Teuchos::RCP< const map_type > getRangeMap() const
Tpetra::Map representing the range of this operator.
Definition Ifpack2_ILUT_def.hpp:361
size_t getLocalNumEntries() const
Returns the number of nonzero entries in the local graph.
Definition Ifpack2_ILUT_def.hpp:422
Tpetra::RowMatrix< scalar_type, local_ordinal_type, global_ordinal_type, node_type > row_matrix_type
Type of the Tpetra::RowMatrix specialization that this class uses.
Definition Ifpack2_ILUT_decl.hpp:90
void initialize()
Clear any previously computed factors, and potentially compute sparsity patterns of factors.
Definition Ifpack2_ILUT_def.hpp:500
int getNumCompute() const
Returns the number of calls to Compute().
Definition Ifpack2_ILUT_def.hpp:381
double getApplyTime() const
Returns the time spent in apply().
Definition Ifpack2_ILUT_def.hpp:401
void setParameters(const Teuchos::ParameterList &params)
Set preconditioner parameters.
Definition Ifpack2_ILUT_def.hpp:197
double getComputeTime() const
Returns the time spent in Compute().
Definition Ifpack2_ILUT_def.hpp:396
"Preconditioner" that solves local sparse triangular systems.
Definition Ifpack2_LocalSparseTriangularSolver_decl.hpp:54
Preconditioners and smoothers for Tpetra sparse matrices.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:40
void add_to_heap(const Ordinal &idx, Teuchos::Array< Ordinal > &heap, SizeType &heap_len)
Definition Ifpack2_Heap.hpp:35
void rm_heap_root(Teuchos::Array< Ordinal > &heap, SizeType &heap_len)
Definition Ifpack2_Heap.hpp:59