Ifpack2 Templated Preconditioning Package Version 1.0
Loading...
Searching...
No Matches
Ifpack2_AdditiveSchwarz_def.hpp
Go to the documentation of this file.
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
21
22#ifndef IFPACK2_ADDITIVESCHWARZ_DEF_HPP
23#define IFPACK2_ADDITIVESCHWARZ_DEF_HPP
24
26#include "Trilinos_Details_LinearSolverFactory.hpp"
27// We need Ifpack2's implementation of LinearSolver, because we use it
28// to wrap the user-provided Ifpack2::Preconditioner in
29// Ifpack2::AdditiveSchwarz::setInnerPreconditioner.
30#include "Ifpack2_Details_LinearSolver.hpp"
31#include "Ifpack2_Details_getParamTryingTypes.hpp"
32
33#if defined(HAVE_IFPACK2_XPETRA) && defined(HAVE_IFPACK2_ZOLTAN2)
34#include "Zoltan2_TpetraRowGraphAdapter.hpp"
35#include "Zoltan2_OrderingProblem.hpp"
36#include "Zoltan2_OrderingSolution.hpp"
37#endif
38
40#include "Ifpack2_Parameters.hpp"
41#include "Ifpack2_LocalFilter.hpp"
42#include "Ifpack2_ReorderFilter.hpp"
43#include "Ifpack2_SingletonFilter.hpp"
44#include "Ifpack2_Details_AdditiveSchwarzFilter.hpp"
45
46#ifdef HAVE_MPI
47#include "Teuchos_DefaultMpiComm.hpp"
48#endif
49
50#include "Teuchos_StandardParameterEntryValidators.hpp"
51#include <locale> // std::toupper
52
53#include <Tpetra_BlockMultiVector.hpp>
54
55// FIXME (mfh 25 Aug 2015) Work-around for Bug 6392. This doesn't
56// need to be a weak symbol because it only refers to a function in
57// the Ifpack2 package.
58namespace Ifpack2 {
59namespace Details {
60extern void registerLinearSolverFactory();
61} // namespace Details
62} // namespace Ifpack2
63
64#ifdef HAVE_IFPACK2_DEBUG
65
66namespace { // (anonymous)
67
68template <class MV>
69bool anyBad(const MV& X) {
70 using STS = Teuchos::ScalarTraits<typename MV::scalar_type>;
71 using magnitude_type = typename STS::magnitudeType;
72 using STM = Teuchos::ScalarTraits<magnitude_type>;
73
74 Teuchos::Array<magnitude_type> norms(X.getNumVectors());
75 X.norm2(norms());
76 bool good = true;
77 for (size_t j = 0; j < X.getNumVectors(); ++j) {
78 if (STM::isnaninf(norms[j])) {
79 good = false;
80 break;
81 }
82 }
83 return !good;
84}
85
86} // namespace
87
88#endif // HAVE_IFPACK2_DEBUG
89
90namespace Ifpack2 {
91
92template <class MatrixType, class LocalInverseType>
93bool AdditiveSchwarz<MatrixType, LocalInverseType>::hasInnerPrecName() const {
94 const char* options[4] = {
95 "inner preconditioner name",
96 "subdomain solver name",
97 "schwarz: inner preconditioner name",
98 "schwarz: subdomain solver name"};
99 const int numOptions = 4;
100 bool match = false;
101 for (int k = 0; k < numOptions && !match; ++k) {
102 if (List_.isParameter(options[k])) {
103 return true;
104 }
105 }
106 return false;
107}
108
109template <class MatrixType, class LocalInverseType>
110void AdditiveSchwarz<MatrixType, LocalInverseType>::removeInnerPrecName() {
111 const char* options[4] = {
112 "inner preconditioner name",
113 "subdomain solver name",
114 "schwarz: inner preconditioner name",
115 "schwarz: subdomain solver name"};
116 const int numOptions = 4;
117 for (int k = 0; k < numOptions; ++k) {
118 List_.remove(options[k], false);
119 }
120}
121
122template <class MatrixType, class LocalInverseType>
123std::string
124AdditiveSchwarz<MatrixType, LocalInverseType>::innerPrecName() const {
125 const char* options[4] = {
126 "inner preconditioner name",
127 "subdomain solver name",
128 "schwarz: inner preconditioner name",
129 "schwarz: subdomain solver name"};
130 const int numOptions = 4;
131 std::string newName;
132 bool match = false;
133
134 // As soon as one parameter option matches, ignore all others.
135 for (int k = 0; k < numOptions && !match; ++k) {
136 const Teuchos::ParameterEntry* paramEnt =
137 List_.getEntryPtr(options[k]);
138 if (paramEnt != nullptr && paramEnt->isType<std::string>()) {
139 newName = Teuchos::getValue<std::string>(*paramEnt);
140 match = true;
141 }
142 }
143 return match ? newName : defaultInnerPrecName();
144}
145
146template <class MatrixType, class LocalInverseType>
147void AdditiveSchwarz<MatrixType, LocalInverseType>::removeInnerPrecParams() {
148 const char* options[4] = {
149 "inner preconditioner parameters",
150 "subdomain solver parameters",
151 "schwarz: inner preconditioner parameters",
152 "schwarz: subdomain solver parameters"};
153 const int numOptions = 4;
154
155 // As soon as one parameter option matches, ignore all others.
156 for (int k = 0; k < numOptions; ++k) {
157 List_.remove(options[k], false);
158 }
159}
160
161template <class MatrixType, class LocalInverseType>
162std::pair<Teuchos::ParameterList, bool>
163AdditiveSchwarz<MatrixType, LocalInverseType>::innerPrecParams() const {
164 const char* options[4] = {
165 "inner preconditioner parameters",
166 "subdomain solver parameters",
167 "schwarz: inner preconditioner parameters",
168 "schwarz: subdomain solver parameters"};
169 const int numOptions = 4;
170 Teuchos::ParameterList params;
171
172 // As soon as one parameter option matches, ignore all others.
173 bool match = false;
174 for (int k = 0; k < numOptions && !match; ++k) {
175 if (List_.isSublist(options[k])) {
176 params = List_.sublist(options[k]);
177 match = true;
178 }
179 }
180 // Default is an empty list of parameters.
181 return std::make_pair(params, match);
182}
183
184template <class MatrixType, class LocalInverseType>
185std::string
186AdditiveSchwarz<MatrixType, LocalInverseType>::defaultInnerPrecName() {
187 // The default inner preconditioner is "ILUT", for backwards
188 // compatibility with the original AdditiveSchwarz implementation.
189 return "ILUT";
190}
191
192template <class MatrixType, class LocalInverseType>
194 AdditiveSchwarz(const Teuchos::RCP<const row_matrix_type>& A)
195 : Matrix_(A) {}
196
197template <class MatrixType, class LocalInverseType>
199 AdditiveSchwarz(const Teuchos::RCP<const row_matrix_type>& A,
200 const Teuchos::RCP<const coord_type>& coordinates)
201 : Matrix_(A)
202 , Coordinates_(coordinates) {}
203
204template <class MatrixType, class LocalInverseType>
206 AdditiveSchwarz(const Teuchos::RCP<const row_matrix_type>& A,
207 const int overlapLevel)
208 : Matrix_(A)
209 , OverlapLevel_(overlapLevel) {}
210
211template <class MatrixType, class LocalInverseType>
212Teuchos::RCP<const Tpetra::Map<typename MatrixType::local_ordinal_type, typename MatrixType::global_ordinal_type, typename MatrixType::node_type>>
214 getDomainMap() const {
215 TEUCHOS_TEST_FOR_EXCEPTION(
216 Matrix_.is_null(), std::runtime_error,
217 "Ifpack2::AdditiveSchwarz::"
218 "getDomainMap: The matrix to precondition is null. You must either pass "
219 "a nonnull matrix to the constructor, or call setMatrix() with a nonnull "
220 "input, before you may call this method.");
221 return Matrix_->getDomainMap();
222}
223
224template <class MatrixType, class LocalInverseType>
225Teuchos::RCP<const Tpetra::Map<typename MatrixType::local_ordinal_type, typename MatrixType::global_ordinal_type, typename MatrixType::node_type>>
227 TEUCHOS_TEST_FOR_EXCEPTION(
228 Matrix_.is_null(), std::runtime_error,
229 "Ifpack2::AdditiveSchwarz::"
230 "getRangeMap: The matrix to precondition is null. You must either pass "
231 "a nonnull matrix to the constructor, or call setMatrix() with a nonnull "
232 "input, before you may call this method.");
233 return Matrix_->getRangeMap();
234}
235
236template <class MatrixType, class LocalInverseType>
237Teuchos::RCP<const Tpetra::RowMatrix<typename MatrixType::scalar_type, typename MatrixType::local_ordinal_type, typename MatrixType::global_ordinal_type, typename MatrixType::node_type>> AdditiveSchwarz<MatrixType, LocalInverseType>::getMatrix() const {
238 return Matrix_;
239}
240
241template <class MatrixType, class LocalInverseType>
242Teuchos::RCP<const Tpetra::MultiVector<typename Teuchos::ScalarTraits<typename MatrixType::scalar_type>::magnitudeType, typename MatrixType::local_ordinal_type, typename MatrixType::global_ordinal_type, typename MatrixType::node_type>> AdditiveSchwarz<MatrixType, LocalInverseType>::getCoord() const {
243 return Coordinates_;
244}
245
246namespace {
247
248template <class MatrixType, class map_type>
249Teuchos::RCP<const map_type>
250pointMapFromMeshMap(const Teuchos::RCP<const map_type>& meshMap, const typename MatrixType::local_ordinal_type blockSize) {
251 using BMV = Tpetra::BlockMultiVector<
252 typename MatrixType::scalar_type,
253 typename MatrixType::local_ordinal_type,
254 typename MatrixType::global_ordinal_type,
255 typename MatrixType::node_type>;
256
257 if (blockSize == 1) return meshMap;
258
259 return Teuchos::RCP<const map_type>(new map_type(BMV::makePointMap(*meshMap, blockSize)));
260}
261
262template <typename MV, typename Map>
263void resetMultiVecIfNeeded(std::unique_ptr<MV>& mv_ptr, const Map& map, const size_t numVectors, bool initialize) {
264 if (!mv_ptr || mv_ptr->getNumVectors() != numVectors) {
265 mv_ptr.reset(new MV(map, numVectors, initialize));
266 }
267}
268
269} // namespace
270
271template <class MatrixType, class LocalInverseType>
273 apply(const Tpetra::MultiVector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>& B,
274 Tpetra::MultiVector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>& Y,
275 Teuchos::ETransp mode,
276 scalar_type alpha,
277 scalar_type beta) const {
278 using Teuchos::RCP;
279 using Teuchos::rcp;
280 using Teuchos::rcp_dynamic_cast;
281 using Teuchos::Time;
282 using Teuchos::TimeMonitor;
283 typedef Teuchos::ScalarTraits<scalar_type> STS;
284 const char prefix[] = "Ifpack2::AdditiveSchwarz::apply: ";
285
286 TEUCHOS_TEST_FOR_EXCEPTION(!IsComputed_, std::runtime_error,
287 prefix << "isComputed() must be true before you may call apply().");
288 TEUCHOS_TEST_FOR_EXCEPTION(Matrix_.is_null(), std::logic_error, prefix << "The input matrix A is null, but the preconditioner says that it has "
289 "been computed (isComputed() is true). This should never happen, since "
290 "setMatrix() should always mark the preconditioner as not computed if "
291 "its argument is null. "
292 "Please report this bug to the Ifpack2 developers.");
293 TEUCHOS_TEST_FOR_EXCEPTION(Inverse_.is_null(), std::runtime_error,
294 prefix << "The subdomain solver is null. "
295 "This can only happen if you called setInnerPreconditioner() with a null "
296 "input, after calling initialize() or compute(). If you choose to call "
297 "setInnerPreconditioner() with a null input, you must then call it with "
298 "a nonnull input before you may call initialize() or compute().");
299 TEUCHOS_TEST_FOR_EXCEPTION(B.getNumVectors() != Y.getNumVectors(), std::invalid_argument,
300 prefix << "B and Y must have the same number of columns. B has " << B.getNumVectors() << " columns, but Y has " << Y.getNumVectors() << ".");
301 TEUCHOS_TEST_FOR_EXCEPTION(IsOverlapping_ && OverlappingMatrix_.is_null(), std::logic_error,
302 prefix << "The overlapping matrix is null. "
303 "This should never happen if IsOverlapping_ is true. "
304 "Please report this bug to the Ifpack2 developers.");
305 TEUCHOS_TEST_FOR_EXCEPTION(!IsOverlapping_ && localMap_.is_null(), std::logic_error,
306 prefix << "localMap_ is null. "
307 "This should never happen if IsOverlapping_ is false. "
308 "Please report this bug to the Ifpack2 developers.");
309 TEUCHOS_TEST_FOR_EXCEPTION(alpha != STS::one(), std::logic_error,
310 prefix << "Not implemented for alpha != 1.");
311 TEUCHOS_TEST_FOR_EXCEPTION(beta != STS::zero(), std::logic_error,
312 prefix << "Not implemented for beta != 0.");
313
314#ifdef HAVE_IFPACK2_DEBUG
315 {
316 const bool bad = anyBad(B);
317 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
318 "Ifpack2::AdditiveSchwarz::apply: "
319 "The 2-norm of the input B is NaN or Inf.");
320 }
321#endif // HAVE_IFPACK2_DEBUG
322
323#ifdef HAVE_IFPACK2_DEBUG
324 if (!ZeroStartingSolution_) {
325 const bool bad = anyBad(Y);
326 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
327 "Ifpack2::AdditiveSchwarz::apply: "
328 "On input, the initial guess Y has 2-norm NaN or Inf "
329 "(ZeroStartingSolution_ is false).");
330 }
331#endif // HAVE_IFPACK2_DEBUG
332
333 const std::string timerName("Ifpack2::AdditiveSchwarz::apply");
334 RCP<Time> timer = TimeMonitor::lookupCounter(timerName);
335 if (timer.is_null()) {
336 timer = TimeMonitor::getNewCounter(timerName);
337 }
338 double startTime = timer->wallTime();
339
340 { // Start timing here.
341 TimeMonitor timeMon(*timer);
342
343 const scalar_type ZERO = Teuchos::ScalarTraits<scalar_type>::zero();
344 const size_t numVectors = B.getNumVectors();
345
346 // mfh 25 Apr 2015: Fix for currently failing
347 // Ifpack2_AdditiveSchwarz_RILUK test.
348 if (ZeroStartingSolution_) {
349 Y.putScalar(ZERO);
350 }
351
352 // set up for overlap communication
353 MV* OverlappingB = nullptr;
354 MV* OverlappingY = nullptr;
355 {
356 RCP<const map_type> B_and_Y_map = pointMapFromMeshMap<MatrixType>(IsOverlapping_ ? OverlappingMatrix_->getRowMap() : localMap_, Matrix_->getBlockSize());
357 resetMultiVecIfNeeded(overlapping_B_, B_and_Y_map, numVectors, false);
358 resetMultiVecIfNeeded(overlapping_Y_, B_and_Y_map, numVectors, false);
359 OverlappingB = overlapping_B_.get();
360 OverlappingY = overlapping_Y_.get();
361 // FIXME (mfh 25 Jun 2019) It's not clear whether we really need
362 // to fill with zeros here, but that's what was happening before.
363 OverlappingB->putScalar(ZERO);
364 OverlappingY->putScalar(ZERO);
365 }
366
367 RCP<MV> globalOverlappingB;
368 if (!IsOverlapping_) {
369 auto matrixPointRowMap = pointMapFromMeshMap<MatrixType>(Matrix_->getRowMap(), Matrix_->getBlockSize());
370
371 globalOverlappingB =
372 OverlappingB->offsetViewNonConst(matrixPointRowMap, 0);
373
374 // Create Import object on demand, if necessary.
375 if (DistributedImporter_.is_null()) {
376 // FIXME (mfh 15 Apr 2014) Why can't we just ask the Matrix
377 // for its Import object? Of course a general RowMatrix might
378 // not necessarily have one.
379 DistributedImporter_ =
380 rcp(new import_type(matrixPointRowMap,
381 Matrix_->getDomainMap()));
382 }
383 }
384
385 resetMultiVecIfNeeded(R_, B.getMap(), numVectors, false);
386 resetMultiVecIfNeeded(C_, Y.getMap(), numVectors, false);
387 // If taking averages in overlap region, we need to compute
388 // the number of procs who have a copy of each overlap dof
389 Teuchos::ArrayRCP<scalar_type> dataNumOverlapCopies;
390 if (IsOverlapping_ && AvgOverlap_) {
391 if (num_overlap_copies_.get() == nullptr) {
392 num_overlap_copies_.reset(new MV(Y.getMap(), 1, false));
393 RCP<MV> onesVec(new MV(OverlappingMatrix_->getRowMap(), 1, false));
394 onesVec->putScalar(Teuchos::ScalarTraits<scalar_type>::one());
395 rcp_dynamic_cast<OverlappingRowMatrix<row_matrix_type>>(OverlappingMatrix_)->exportMultiVector(*onesVec, *(num_overlap_copies_.get()), CombineMode_);
396 }
397 dataNumOverlapCopies = num_overlap_copies_.get()->getDataNonConst(0);
398 }
399
400 MV* R = R_.get();
401 MV* C = C_.get();
402
403 // FIXME (mfh 25 Jun 2019) It was never clear whether C had to be
404 // initialized to zero. R definitely should not need this.
405 C->putScalar(ZERO);
406
407 for (int ni = 0; ni < NumIterations_; ++ni) {
408#ifdef HAVE_IFPACK2_DEBUG
409 {
410 const bool bad = anyBad(Y);
411 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
412 "Ifpack2::AdditiveSchwarz::apply: "
413 "At top of iteration "
414 << ni << ", the 2-norm of Y is NaN or Inf.");
415 }
416#endif // HAVE_IFPACK2_DEBUG
417
418 Tpetra::deep_copy(*R, B);
419
420 // if (ZeroStartingSolution_ && ni == 0) {
421 // Y.putScalar (STS::zero ());
422 // }
423 if (!ZeroStartingSolution_ || ni > 0) {
424 // calculate residual
425 Matrix_->apply(Y, *R, mode, -STS::one(), STS::one());
426
427#ifdef HAVE_IFPACK2_DEBUG
428 {
429 const bool bad = anyBad(*R);
430 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
431 "Ifpack2::AdditiveSchwarz::apply: "
432 "At iteration "
433 << ni << ", the 2-norm of R (result of computing "
434 "residual with Y) is NaN or Inf.");
435 }
436#endif // HAVE_IFPACK2_DEBUG
437 }
438
439 // do communication if necessary
440 if (IsOverlapping_) {
441 TEUCHOS_TEST_FOR_EXCEPTION(OverlappingMatrix_.is_null(), std::logic_error, prefix << "IsOverlapping_ is true, but OverlappingMatrix_, while nonnull, is "
442 "not an OverlappingRowMatrix<row_matrix_type>. Please report this "
443 "bug to the Ifpack2 developers.");
444 OverlappingMatrix_->importMultiVector(*R, *OverlappingB, Tpetra::INSERT);
445
446 // JJH We don't need to import the solution Y we are always solving AY=R with initial guess zero
447 // if (ZeroStartingSolution_ == false)
448 // overlapMatrix->importMultiVector (Y, *OverlappingY, Tpetra::INSERT);
449 /*
450 FIXME from Ifpack1: Will not work with non-zero starting solutions.
451 TODO JJH 3/20/15 I don't know whether this comment is still valid.
452
453 Here is the log for the associated commit 720b2fa4 to Ifpack1:
454
455 "Added a note to recall that the nonzero starting solution will not
456 work properly if reordering, filtering or wider overlaps are used. This only
457 applied to methods like Jacobi, Gauss-Seidel, and SGS (in both point and block
458 version), and not to ILU-type preconditioners."
459 */
460
461#ifdef HAVE_IFPACK2_DEBUG
462 {
463 const bool bad = anyBad(*OverlappingB);
464 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
465 "Ifpack2::AdditiveSchwarz::apply: "
466 "At iteration "
467 << ni << ", result of importMultiVector from R "
468 "to OverlappingB, has 2-norm NaN or Inf.");
469 }
470#endif // HAVE_IFPACK2_DEBUG
471 } else {
472 globalOverlappingB->doImport(*R, *DistributedImporter_, Tpetra::INSERT);
473
474#ifdef HAVE_IFPACK2_DEBUG
475 {
476 const bool bad = anyBad(*globalOverlappingB);
477 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
478 "Ifpack2::AdditiveSchwarz::apply: "
479 "At iteration "
480 << ni << ", result of doImport from R, has 2-norm "
481 "NaN or Inf.");
482 }
483#endif // HAVE_IFPACK2_DEBUG
484 }
485
486#ifdef HAVE_IFPACK2_DEBUG
487 {
488 const bool bad = anyBad(*OverlappingB);
489 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
490 "Ifpack2::AdditiveSchwarz::apply: "
491 "At iteration "
492 << ni << ", right before localApply, the 2-norm of "
493 "OverlappingB is NaN or Inf.");
494 }
495#endif // HAVE_IFPACK2_DEBUG
496
497 // local solve
498 localApply(*OverlappingB, *OverlappingY);
499
500#ifdef HAVE_IFPACK2_DEBUG
501 {
502 const bool bad = anyBad(*OverlappingY);
503 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
504 "Ifpack2::AdditiveSchwarz::apply: "
505 "At iteration "
506 << ni << ", after localApply and before export / "
507 "copy, the 2-norm of OverlappingY is NaN or Inf.");
508 }
509#endif // HAVE_IFPACK2_DEBUG
510
511#ifdef HAVE_IFPACK2_DEBUG
512 {
513 const bool bad = anyBad(*C);
514 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
515 "Ifpack2::AdditiveSchwarz::apply: "
516 "At iteration "
517 << ni << ", before export / copy, the 2-norm of C "
518 "is NaN or Inf.");
519 }
520#endif // HAVE_IFPACK2_DEBUG
521
522 // do communication if necessary
523 if (IsOverlapping_) {
524 TEUCHOS_TEST_FOR_EXCEPTION(OverlappingMatrix_.is_null(), std::logic_error, prefix << "OverlappingMatrix_ is null when it shouldn't be. "
525 "Please report this bug to the Ifpack2 developers.");
526 OverlappingMatrix_->exportMultiVector(*OverlappingY, *C, CombineMode_);
527
528 // average solution in overlap regions if requested via "schwarz: combine mode" "AVG"
529 if (AvgOverlap_) {
530 Teuchos::ArrayRCP<scalar_type> dataC = C->getDataNonConst(0);
531 for (int i = 0; i < (int)C->getMap()->getLocalNumElements(); i++) {
532 dataC[i] = dataC[i] / dataNumOverlapCopies[i];
533 }
534 }
535 } else {
536 // mfh 16 Apr 2014: Make a view of Y with the same Map as
537 // OverlappingY, so that we can copy OverlappingY into Y. This
538 // replaces code that iterates over all entries of OverlappingY,
539 // copying them one at a time into Y. That code assumed that
540 // the rows of Y and the rows of OverlappingY have the same
541 // global indices in the same order; see Bug 5992.
542 RCP<MV> C_view = C->offsetViewNonConst(OverlappingY->getMap(), 0);
543 Tpetra::deep_copy(*C_view, *OverlappingY);
544 }
545
546#ifdef HAVE_IFPACK2_DEBUG
547 {
548 const bool bad = anyBad(*C);
549 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
550 "Ifpack2::AdditiveSchwarz::apply: "
551 "At iteration "
552 << ni << ", before Y := C + Y, the 2-norm of C "
553 "is NaN or Inf.");
554 }
555#endif // HAVE_IFPACK2_DEBUG
556
557#ifdef HAVE_IFPACK2_DEBUG
558 {
559 const bool bad = anyBad(Y);
560 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
561 "Ifpack2::AdditiveSchwarz::apply: "
562 "Before Y := C + Y, at iteration "
563 << ni << ", the 2-norm of Y "
564 "is NaN or Inf.");
565 }
566#endif // HAVE_IFPACK2_DEBUG
567
568 Y.update(UpdateDamping_, *C, STS::one());
569
570#ifdef HAVE_IFPACK2_DEBUG
571 {
572 const bool bad = anyBad(Y);
573 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
574 "Ifpack2::AdditiveSchwarz::apply: "
575 "At iteration "
576 << ni << ", after Y := C + Y, the 2-norm of Y "
577 "is NaN or Inf.");
578 }
579#endif // HAVE_IFPACK2_DEBUG
580 } // for each iteration
581
582 } // Stop timing here
583
584#ifdef HAVE_IFPACK2_DEBUG
585 {
586 const bool bad = anyBad(Y);
587 TEUCHOS_TEST_FOR_EXCEPTION(bad, std::runtime_error,
588 "Ifpack2::AdditiveSchwarz::apply: "
589 "The 2-norm of the output Y is NaN or Inf.");
590 }
591#endif // HAVE_IFPACK2_DEBUG
592
593 ++NumApply_;
594
595 ApplyTime_ += (timer->wallTime() - startTime);
596}
597
598template <class MatrixType, class LocalInverseType>
600 localApply(MV& OverlappingB, MV& OverlappingY) const {
601 using Teuchos::RCP;
602 using Teuchos::rcp_dynamic_cast;
603
604 const size_t numVectors = OverlappingB.getNumVectors();
605
606 auto additiveSchwarzFilter = rcp_dynamic_cast<Details::AdditiveSchwarzFilter<MatrixType>>(innerMatrix_);
607 if (additiveSchwarzFilter) {
608 // Create the reduced system innerMatrix_ * ReducedY = ReducedB.
609 // This effectively fuses 3 tasks:
610 // -SingletonFilter::SolveSingletons (solve entries of OverlappingY corresponding to singletons)
611 // -SingletonFilter::CreateReducedRHS (fill ReducedReorderedB from OverlappingB, with entries in singleton columns eliminated)
612 // -ReorderFilter::permuteOriginalToReordered (apply permutation to ReducedReorderedB)
613 resetMultiVecIfNeeded(reduced_reordered_B_, additiveSchwarzFilter->getRowMap(), numVectors, true);
614 resetMultiVecIfNeeded(reduced_reordered_Y_, additiveSchwarzFilter->getRowMap(), numVectors, true);
615 additiveSchwarzFilter->CreateReducedProblem(OverlappingB, OverlappingY, *reduced_reordered_B_);
616 // Apply inner solver
617 Inverse_->solve(*reduced_reordered_Y_, *reduced_reordered_B_);
618 // Scatter ReducedY back to non-singleton rows of OverlappingY, according to the reordering.
619 additiveSchwarzFilter->UpdateLHS(*reduced_reordered_Y_, OverlappingY);
620 } else {
621 if (FilterSingletons_) {
622 // process singleton filter
623 resetMultiVecIfNeeded(reduced_B_, SingletonMatrix_->getRowMap(), numVectors, true);
624 resetMultiVecIfNeeded(reduced_Y_, SingletonMatrix_->getRowMap(), numVectors, true);
625
626 RCP<SingletonFilter<row_matrix_type>> singletonFilter =
627 rcp_dynamic_cast<SingletonFilter<row_matrix_type>>(SingletonMatrix_);
628 TEUCHOS_TEST_FOR_EXCEPTION(!SingletonMatrix_.is_null() && singletonFilter.is_null(),
629 std::logic_error,
630 "Ifpack2::AdditiveSchwarz::localApply: "
631 "SingletonFilter_ is nonnull but is not a SingletonFilter"
632 "<row_matrix_type>. This should never happen. Please report this bug "
633 "to the Ifpack2 developers.");
634 singletonFilter->SolveSingletons(OverlappingB, OverlappingY);
635 singletonFilter->CreateReducedRHS(OverlappingY, OverlappingB, *reduced_B_);
636
637 // process reordering
638 if (!UseReordering_) {
639 Inverse_->solve(*reduced_Y_, *reduced_B_);
640 } else {
641 RCP<ReorderFilter<row_matrix_type>> rf =
642 rcp_dynamic_cast<ReorderFilter<row_matrix_type>>(ReorderedLocalizedMatrix_);
643 TEUCHOS_TEST_FOR_EXCEPTION(!ReorderedLocalizedMatrix_.is_null() && rf.is_null(), std::logic_error,
644 "Ifpack2::AdditiveSchwarz::localApply: ReorderedLocalizedMatrix_ is "
645 "nonnull but is not a ReorderFilter<row_matrix_type>. This should "
646 "never happen. Please report this bug to the Ifpack2 developers.");
647 resetMultiVecIfNeeded(reordered_B_, reduced_B_->getMap(), numVectors, false);
648 resetMultiVecIfNeeded(reordered_Y_, reduced_Y_->getMap(), numVectors, false);
649 rf->permuteOriginalToReordered(*reduced_B_, *reordered_B_);
650 Inverse_->solve(*reordered_Y_, *reordered_B_);
651 rf->permuteReorderedToOriginal(*reordered_Y_, *reduced_Y_);
652 }
653
654 // finish up with singletons
655 singletonFilter->UpdateLHS(*reduced_Y_, OverlappingY);
656 } else {
657 // process reordering
658 if (!UseReordering_) {
659 Inverse_->solve(OverlappingY, OverlappingB);
660 } else {
661 resetMultiVecIfNeeded(reordered_B_, OverlappingB.getMap(), numVectors, false);
662 resetMultiVecIfNeeded(reordered_Y_, OverlappingY.getMap(), numVectors, false);
663
664 RCP<ReorderFilter<row_matrix_type>> rf =
665 rcp_dynamic_cast<ReorderFilter<row_matrix_type>>(ReorderedLocalizedMatrix_);
666 TEUCHOS_TEST_FOR_EXCEPTION(!ReorderedLocalizedMatrix_.is_null() && rf.is_null(), std::logic_error,
667 "Ifpack2::AdditiveSchwarz::localApply: ReorderedLocalizedMatrix_ is "
668 "nonnull but is not a ReorderFilter<row_matrix_type>. This should "
669 "never happen. Please report this bug to the Ifpack2 developers.");
670 rf->permuteOriginalToReordered(OverlappingB, *reordered_B_);
671 Inverse_->solve(*reordered_Y_, *reordered_B_);
672 rf->permuteReorderedToOriginal(*reordered_Y_, OverlappingY);
673 }
674 }
675 }
676}
677
678template <class MatrixType, class LocalInverseType>
680 setParameters(const Teuchos::ParameterList& plist) {
681 // mfh 18 Nov 2013: Ifpack2's setParameters() method passes in the
682 // input list as const. This means that we have to copy it before
683 // validation or passing into setParameterList().
684 List_ = plist;
685 this->setParameterList(Teuchos::rcpFromRef(List_));
686}
687
688template <class MatrixType, class LocalInverseType>
690 setParameterList(const Teuchos::RCP<Teuchos::ParameterList>& plist) {
691 using Details::getParamTryingTypes;
692 using Teuchos::ParameterEntry;
693 using Teuchos::ParameterEntryValidator;
694 using Teuchos::ParameterList;
695 using Teuchos::RCP;
696 using Teuchos::rcp;
697 using Teuchos::rcp_dynamic_cast;
698 using Teuchos::StringToIntegralParameterEntryValidator;
699 using Tpetra::CombineMode;
700 const char prefix[] = "Ifpack2::AdditiveSchwarz: ";
701
702 if (plist.is_null()) {
703 // Assume that the user meant to set default parameters by passing
704 // in an empty list.
705 this->setParameterList(rcp(new ParameterList()));
706 }
707 // FIXME (mfh 26 Aug 2015) It's not necessarily true that plist is
708 // nonnull at this point.
709
710 // At this point, plist should be nonnull.
711 TEUCHOS_TEST_FOR_EXCEPTION(
712 plist.is_null(), std::logic_error,
713 "Ifpack2::AdditiveSchwarz::"
714 "setParameterList: plist is null. This should never happen, since the "
715 "method should have replaced a null input list with a nonnull empty list "
716 "by this point. Please report this bug to the Ifpack2 developers.");
717
718 // TODO JJH 24March2015 The list needs to be validated. Not sure why this is commented out.
719 // try {
720 // List_.validateParameters (* getValidParameters ());
721 // }
722 // catch (std::exception& e) {
723 // std::cerr << "Ifpack2::AdditiveSchwarz::setParameterList: Validation failed with the following error message: " << e.what () << std::endl;
724 // throw e;
725 // }
726
727 // mfh 18 Nov 2013: Supplying the current value as the default value
728 // when calling ParameterList::get() ensures "delta" behavior when
729 // users pass in new parameters: any unspecified parameters in the
730 // new list retain their values in the old list. This preserves
731 // backwards compatiblity with this class' previous behavior. Note
732 // that validateParametersAndSetDefaults() would have different
733 // behavior: any parameters not in the new list would get default
734 // values, which could be different than their values in the
735 // original list.
736
737 const std::string cmParamName("schwarz: combine mode");
738 const ParameterEntry* cmEnt = plist->getEntryPtr(cmParamName);
739 if (cmEnt != nullptr) {
740 if (cmEnt->isType<CombineMode>()) {
741 CombineMode_ = Teuchos::getValue<CombineMode>(*cmEnt);
742 } else if (cmEnt->isType<int>()) {
743 const int cm = Teuchos::getValue<int>(*cmEnt);
744 CombineMode_ = static_cast<CombineMode>(cm);
745 } else if (cmEnt->isType<std::string>()) {
746 // Try to get the combine mode as a string. If this works, use
747 // the validator to convert to int. This is painful, but
748 // necessary in order to do validation, since the input list may
749 // not necessarily come with a validator.
750 const ParameterEntry& validEntry =
751 getValidParameters()->getEntry(cmParamName);
752 RCP<const ParameterEntryValidator> v = validEntry.validator();
753 using vs2e_type = StringToIntegralParameterEntryValidator<CombineMode>;
754 RCP<const vs2e_type> vs2e = rcp_dynamic_cast<const vs2e_type>(v, true);
755
756 ParameterEntry& inputEntry = plist->getEntry(cmParamName);
757 // As AVG is only a Schwarz option and does not exist in Tpetra's
758 // version of CombineMode, we use a separate boolean local to
759 // Schwarz in conjunction with CombineMode_ == ADD to handle
760 // averaging. Here, we change input entry to ADD and set the boolean.
761 if (strncmp(Teuchos::getValue<std::string>(inputEntry).c_str(), "AVG", 3) == 0) {
762 inputEntry.template setValue<std::string>("ADD");
763 AvgOverlap_ = true;
764 }
765 CombineMode_ = vs2e->getIntegralValue(inputEntry, cmParamName);
766 }
767 }
768 // If doing user partitioning with Block Jacobi relaxation and overlapping blocks, we might
769 // later need to know whether or not the overlapping Schwarz scheme is "ADD" or "ZERO" (which
770 // is really RAS Schwarz. If it is "ADD", communication will be necessary when computing the
771 // proper weights needed to combine solution values in overlap regions
772 if (plist->isParameter("subdomain solver name")) {
773 if (plist->get<std::string>("subdomain solver name") == "BLOCK_RELAXATION") {
774 if (plist->isSublist("subdomain solver parameters")) {
775 if (plist->sublist("subdomain solver parameters").isParameter("relaxation: type")) {
776 if (plist->sublist("subdomain solver parameters").get<std::string>("relaxation: type") == "Jacobi") {
777 if (plist->sublist("subdomain solver parameters").isParameter("partitioner: type")) {
778 if (plist->sublist("subdomain solver parameters").get<std::string>("partitioner: type") == "user") {
779 if (CombineMode_ == Tpetra::ADD) plist->sublist("subdomain solver parameters").set("partitioner: combine mode", "ADD");
780 if (CombineMode_ == Tpetra::ZERO) plist->sublist("subdomain solver parameters").set("partitioner: combine mode", "ZERO");
781 AvgOverlap_ = false; // averaging already taken care of by the partitioner: nonsymmetric overlap combine option
782 }
783 }
784 }
785 }
786 }
787 }
788 }
789
790 OverlapLevel_ = plist->get("schwarz: overlap level", OverlapLevel_);
791
792 // We set IsOverlapping_ in initialize(), once we know that Matrix_ is nonnull.
793
794 // Will we be doing reordering? Unlike Ifpack, we'll use a
795 // "schwarz: reordering list" to give to Zoltan2.
796 UseReordering_ = plist->get("schwarz: use reordering", UseReordering_);
797
798#if !defined(HAVE_IFPACK2_XPETRA) || !defined(HAVE_IFPACK2_ZOLTAN2)
799 TEUCHOS_TEST_FOR_EXCEPTION(
800 UseReordering_, std::invalid_argument,
801 "Ifpack2::AdditiveSchwarz::"
802 "setParameters: You specified \"schwarz: use reordering\" = true. "
803 "This is only valid when Trilinos was built with Ifpack2, Xpetra, and "
804 "Zoltan2 enabled. Either Xpetra or Zoltan2 was not enabled in your build "
805 "of Trilinos.");
806#endif
807
808 // FIXME (mfh 18 Nov 2013) Now would be a good time to validate the
809 // "schwarz: reordering list" parameter list. Currently, that list
810 // gets extracted in setup().
811
812 // if true, filter singletons. NOTE: the filtered matrix can still have
813 // singletons! A simple example: upper triangular matrix, if I remove
814 // the lower node, I still get a matrix with a singleton! However, filter
815 // singletons should help for PDE problems with Dirichlet BCs.
816 FilterSingletons_ = plist->get("schwarz: filter singletons", FilterSingletons_);
817
818 // Allow for damped Schwarz updates
819 getParamTryingTypes<scalar_type, scalar_type, double>(UpdateDamping_, *plist, "schwarz: update damping", prefix);
820
821 // If the inner solver doesn't exist yet, don't create it.
822 // initialize() creates it.
823 //
824 // If the inner solver _does_ exist, there are three cases,
825 // depending on what the user put in the input ParameterList.
826 //
827 // 1. The user did /not/ provide a parameter specifying the inner
828 // solver's type, nor did the user specify a sublist of
829 // parameters for the inner solver
830 // 2. The user did /not/ provide a parameter specifying the inner
831 // solver's type, but /did/ specify a sublist of parameters for
832 // the inner solver
833 // 3. The user provided a parameter specifying the inner solver's
834 // type (it does not matter in this case whether the user gave
835 // a sublist of parameters for the inner solver)
836 //
837 // AdditiveSchwarz has "delta" (relative) semantics for setting
838 // parameters. This means that if the user did not specify the
839 // inner solver's type, we presume that the type has not changed.
840 // Thus, if the inner solver exists, we don't need to recreate it.
841 //
842 // In Case 3, if the user bothered to specify the inner solver's
843 // type, then we must assume it may differ than the current inner
844 // solver's type. Thus, we have to recreate the inner solver. We
845 // achieve this here by assigning null to Inverse_; initialize()
846 // will recreate the solver when it is needed. Our assumption here
847 // is necessary because Ifpack2::Preconditioner does not have a
848 // method for querying a preconditioner's "type" (i.e., name) as a
849 // string. Remember that the user may have previously set an
850 // arbitrary inner solver by calling setInnerPreconditioner().
851 //
852 // See note at the end of setInnerPreconditioner().
853
854 if (!Inverse_.is_null()) {
855 // "CUSTOM" explicitly indicates that the user called or plans to
856 // call setInnerPreconditioner.
857 if (hasInnerPrecName() && innerPrecName() != "CUSTOM") {
858 // Wipe out the current inner solver. initialize() will
859 // recreate it with the correct type.
860 Inverse_ = Teuchos::null;
861 } else {
862 // Extract and apply the sublist of parameters to give to the
863 // inner solver, if there is such a sublist of parameters.
864 std::pair<ParameterList, bool> result = innerPrecParams();
865 if (result.second) {
866 // FIXME (mfh 26 Aug 2015) Rewrite innerPrecParams() so this
867 // isn't another deep copy.
868 Inverse_->setParameters(rcp(new ParameterList(result.first)));
869 }
870 }
871 }
872
873 NumIterations_ = plist->get("schwarz: num iterations", NumIterations_);
874 ZeroStartingSolution_ =
875 plist->get("schwarz: zero starting solution", ZeroStartingSolution_);
876}
877
878template <class MatrixType, class LocalInverseType>
879Teuchos::RCP<const Teuchos::ParameterList>
881 getValidParameters() const {
882 using Teuchos::ParameterList;
883 using Teuchos::parameterList;
884 using Teuchos::RCP;
885 using Teuchos::rcp_const_cast;
886
887 if (validParams_.is_null()) {
888 const int overlapLevel = 0;
889 const bool useReordering = false;
890 const bool filterSingletons = false;
891 const int numIterations = 1;
892 const bool zeroStartingSolution = true;
893 const scalar_type updateDamping = Teuchos::ScalarTraits<scalar_type>::one();
894 ParameterList reorderingSublist;
895 reorderingSublist.set("order_method", std::string("rcm"));
896
897 RCP<ParameterList> plist = parameterList("Ifpack2::AdditiveSchwarz");
898
899 Tpetra::setCombineModeParameter(*plist, "schwarz: combine mode");
900 plist->set("schwarz: overlap level", overlapLevel);
901 plist->set("schwarz: use reordering", useReordering);
902 plist->set("schwarz: reordering list", reorderingSublist);
903 // mfh 24 Mar 2015: We accept this for backwards compatibility
904 // ONLY. It is IGNORED.
905 plist->set("schwarz: compute condest", false);
906 plist->set("schwarz: filter singletons", filterSingletons);
907 plist->set("schwarz: num iterations", numIterations);
908 plist->set("schwarz: zero starting solution", zeroStartingSolution);
909 plist->set("schwarz: update damping", updateDamping);
910
911 // FIXME (mfh 18 Nov 2013) Get valid parameters from inner solver.
912 // JJH The inner solver should handle its own validation.
913 //
914 // FIXME (mfh 18 Nov 2013) Get valid parameters from Zoltan2, if
915 // Zoltan2 was enabled in the build.
916 // JJH Zoltan2 should handle its own validation.
917 //
918
919 validParams_ = rcp_const_cast<const ParameterList>(plist);
920 }
921 return validParams_;
922}
923
924template <class MatrixType, class LocalInverseType>
926 using Teuchos::RCP;
927 using Teuchos::rcp;
928 using Teuchos::SerialComm;
929 using Teuchos::Time;
930 using Teuchos::TimeMonitor;
931 using Tpetra::global_size_t;
932
933 const std::string timerName("Ifpack2::AdditiveSchwarz::initialize");
934 RCP<Time> timer = TimeMonitor::lookupCounter(timerName);
935 if (timer.is_null()) {
936 timer = TimeMonitor::getNewCounter(timerName);
937 }
938 double startTime = timer->wallTime();
939
940 { // Start timing here.
941 TimeMonitor timeMon(*timer);
942
943 TEUCHOS_TEST_FOR_EXCEPTION(
944 Matrix_.is_null(), std::runtime_error,
945 "Ifpack2::AdditiveSchwarz::"
946 "initialize: The matrix to precondition is null. You must either pass "
947 "a nonnull matrix to the constructor, or call setMatrix() with a nonnull "
948 "input, before you may call this method.");
949
950 IsInitialized_ = false;
951 IsComputed_ = false;
952 overlapping_B_.reset(nullptr);
953 overlapping_Y_.reset(nullptr);
954 R_.reset(nullptr);
955 C_.reset(nullptr);
956 reduced_reordered_B_.reset(nullptr);
957 reduced_reordered_Y_.reset(nullptr);
958 reduced_B_.reset(nullptr);
959 reduced_Y_.reset(nullptr);
960 reordered_B_.reset(nullptr);
961 reordered_Y_.reset(nullptr);
962
963 RCP<const Teuchos::Comm<int>> comm = Matrix_->getComm();
964 RCP<const map_type> rowMap = Matrix_->getRowMap();
965 const global_size_t INVALID =
966 Teuchos::OrdinalTraits<global_size_t>::invalid();
967
968 // If there's only one process in the matrix's communicator,
969 // then there's no need to compute overlap.
970 if (comm->getSize() == 1) {
971 OverlapLevel_ = 0;
972 IsOverlapping_ = false;
973 } else if (OverlapLevel_ != 0) {
974 IsOverlapping_ = true;
975 }
976
977 if (OverlapLevel_ == 0) {
978 const global_ordinal_type indexBase = rowMap->getIndexBase();
979 RCP<const SerialComm<int>> localComm(new SerialComm<int>());
980 // FIXME (mfh 15 Apr 2014) What if indexBase isn't the least
981 // global index in the list of GIDs on this process?
982 localMap_ =
983 rcp(new map_type(INVALID, rowMap->getLocalNumElements(),
984 indexBase, localComm));
985 }
986
987 // compute the overlapping matrix if necessary
988 if (IsOverlapping_) {
989 Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("OverlappingRowMatrix construction"));
990 OverlappingMatrix_ = rcp(new OverlappingRowMatrix<row_matrix_type>(Matrix_, OverlapLevel_));
991 }
992
993 setup(); // This does a lot of the initialization work.
994
995 [&]() {
996 if (Inverse_.is_null()) return;
997 const std::string innerName = innerPrecName();
998 if (innerName.compare("RILUK") != 0) return;
999 if (Coordinates_ == Teuchos::null) return;
1000
1001 // a user may provide coordinates that do not match the row map
1002 if (rowMap->getGlobalNumElements() != Coordinates_->getMap()->getGlobalNumElements()) return;
1003
1004 auto ifpack2_Inverse = Teuchos::rcp_dynamic_cast<Ifpack2::Details::LinearSolver<scalar_type, local_ordinal_type, global_ordinal_type, node_type>>(Inverse_);
1005 if (!IsOverlapping_ && !UseReordering_) {
1006 ifpack2_Inverse->setCoord(Coordinates_);
1007 return;
1008 }
1009
1010 RCP<coord_type> tmp_Coordinates_;
1011 if (IsOverlapping_) {
1012 tmp_Coordinates_ = rcp(new coord_type(OverlappingMatrix_->getRowMap(), Coordinates_->getNumVectors(), false));
1013 Tpetra::Import<local_ordinal_type, global_ordinal_type, node_type> importer(Coordinates_->getMap(), tmp_Coordinates_->getMap());
1014 tmp_Coordinates_->doImport(*Coordinates_, importer, Tpetra::INSERT);
1015 } else {
1016 tmp_Coordinates_ = rcp(new coord_type(*Coordinates_, Teuchos::Copy));
1017 }
1018 if (UseReordering_) {
1019 auto coorDevice = tmp_Coordinates_->getLocalViewDevice(Tpetra::Access::ReadWrite);
1020 auto permDevice = perm_coors.view_device();
1021 Kokkos::View<magnitude_type**, Kokkos::LayoutLeft> tmp_coor(Kokkos::view_alloc(Kokkos::WithoutInitializing, "tmp_coor"), coorDevice.extent(0), coorDevice.extent(1));
1022 Kokkos::parallel_for(
1023 Kokkos::RangePolicy<typename crs_matrix_type::execution_space>(0, static_cast<int>(coorDevice.extent(0))), KOKKOS_LAMBDA(const int& i) {
1024 for (int j = 0; j < static_cast<int>(coorDevice.extent(1)); j++) {
1025 tmp_coor(permDevice(i), j) = coorDevice(i, j);
1026 }
1027 });
1028 Kokkos::deep_copy(coorDevice, tmp_coor);
1029 }
1030 ifpack2_Inverse->setCoord(tmp_Coordinates_);
1031 }();
1032
1033 if (!Inverse_.is_null()) {
1034 Inverse_->symbolic(); // Initialize subdomain solver.
1035 }
1036
1037 } // Stop timing here.
1038
1039 IsInitialized_ = true;
1040 ++NumInitialize_;
1041
1042 InitializeTime_ += (timer->wallTime() - startTime);
1043}
1044
1045template <class MatrixType, class LocalInverseType>
1047 return IsInitialized_;
1048}
1049
1050template <class MatrixType, class LocalInverseType>
1052 using Teuchos::RCP;
1053 using Teuchos::Time;
1054 using Teuchos::TimeMonitor;
1055
1056 if (!IsInitialized_) {
1057 initialize();
1058 }
1059
1060 TEUCHOS_TEST_FOR_EXCEPTION(
1061 !isInitialized(), std::logic_error,
1062 "Ifpack2::AdditiveSchwarz::compute: "
1063 "The preconditioner is not yet initialized, "
1064 "even though initialize() supposedly has been called. "
1065 "This should never happen. "
1066 "Please report this bug to the Ifpack2 developers.");
1067
1068 TEUCHOS_TEST_FOR_EXCEPTION(
1069 Inverse_.is_null(), std::runtime_error,
1070 "Ifpack2::AdditiveSchwarz::compute: The subdomain solver is null. "
1071 "This can only happen if you called setInnerPreconditioner() with a null "
1072 "input, after calling initialize() or compute(). If you choose to call "
1073 "setInnerPreconditioner() with a null input, you must then call it with a "
1074 "nonnull input before you may call initialize() or compute().");
1075
1076 const std::string timerName("Ifpack2::AdditiveSchwarz::compute");
1077 RCP<Time> timer = TimeMonitor::lookupCounter(timerName);
1078 if (timer.is_null()) {
1079 timer = TimeMonitor::getNewCounter(timerName);
1080 }
1081 TimeMonitor timeMon(*timer);
1082 double startTime = timer->wallTime();
1083
1084 // compute () assumes that the values of Matrix_ (aka A) have changed.
1085 // If this has overlap, do an import from the input matrix to the halo.
1086 if (IsOverlapping_) {
1087 Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("Halo Import"));
1088 OverlappingMatrix_->doExtImport();
1089 }
1090 // At this point, either Matrix_ or OverlappingMatrix_ (depending on whether this is overlapping)
1091 // has new values and unchanged structure. If we are using AdditiveSchwarzFilter, update the local matrix.
1092 //
1093 if (auto asf = Teuchos::rcp_dynamic_cast<Details::AdditiveSchwarzFilter<MatrixType>>(innerMatrix_)) {
1094 Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("Fill Local Matrix"));
1095 // NOTE: if this compute() call comes right after the initialize() with no intervening matrix changes, this call is redundant.
1096 // initialize() already filled the local matrix. However, we have no way to tell if this is the case.
1097 asf->updateMatrixValues();
1098 }
1099 // Now, whether the Inverse_'s matrix is the AdditiveSchwarzFilter's local matrix or simply Matrix_/OverlappingMatrix_,
1100 // it will be able to see the new values and update itself accordingly.
1101
1102 { // Start timing here.
1103
1104 IsComputed_ = false;
1105 Inverse_->numeric();
1106 } // Stop timing here.
1107
1108 IsComputed_ = true;
1109 ++NumCompute_;
1110
1111 ComputeTime_ += (timer->wallTime() - startTime);
1112}
1113
1114//==============================================================================
1115// Returns true if the preconditioner has been successfully computed, false otherwise.
1116template <class MatrixType, class LocalInverseType>
1118 return IsComputed_;
1119}
1120
1121template <class MatrixType, class LocalInverseType>
1123 return NumInitialize_;
1124}
1125
1126template <class MatrixType, class LocalInverseType>
1128 return NumCompute_;
1129}
1130
1131template <class MatrixType, class LocalInverseType>
1133 return NumApply_;
1134}
1135
1136template <class MatrixType, class LocalInverseType>
1138 return InitializeTime_;
1139}
1140
1141template <class MatrixType, class LocalInverseType>
1143 return ComputeTime_;
1144}
1145
1146template <class MatrixType, class LocalInverseType>
1148 return ApplyTime_;
1149}
1150
1151template <class MatrixType, class LocalInverseType>
1153 std::ostringstream out;
1154
1155 out << "\"Ifpack2::AdditiveSchwarz\": {";
1156 if (this->getObjectLabel() != "") {
1157 out << "Label: \"" << this->getObjectLabel() << "\", ";
1158 }
1159 out << "Initialized: " << (isInitialized() ? "true" : "false")
1160 << ", Computed: " << (isComputed() ? "true" : "false")
1161 << ", Iterations: " << NumIterations_
1162 << ", Overlap level: " << OverlapLevel_
1163 << ", Subdomain reordering: \"" << ReorderingAlgorithm_ << "\"";
1164 out << ", Combine mode: \"";
1165 if (CombineMode_ == Tpetra::INSERT) {
1166 out << "INSERT";
1167 } else if (CombineMode_ == Tpetra::ADD) {
1168 out << "ADD";
1169 } else if (CombineMode_ == Tpetra::REPLACE) {
1170 out << "REPLACE";
1171 } else if (CombineMode_ == Tpetra::ABSMAX) {
1172 out << "ABSMAX";
1173 } else if (CombineMode_ == Tpetra::ZERO) {
1174 out << "ZERO";
1175 }
1176 out << "\"";
1177 if (Matrix_.is_null()) {
1178 out << ", Matrix: null";
1179 } else {
1180 out << ", Global matrix dimensions: ["
1181 << Matrix_->getGlobalNumRows() << ", "
1182 << Matrix_->getGlobalNumCols() << "]";
1183 }
1184 out << ", Inner solver: ";
1185 if (!Inverse_.is_null()) {
1186 Teuchos::RCP<Teuchos::Describable> inv =
1187 Teuchos::rcp_dynamic_cast<Teuchos::Describable>(Inverse_);
1188 if (!inv.is_null()) {
1189 out << "{" << inv->description() << "}";
1190 } else {
1191 out << "{"
1192 << "Some inner solver"
1193 << "}";
1194 }
1195 } else {
1196 out << "null";
1197 }
1198
1199 out << "}";
1200 return out.str();
1201}
1202
1203template <class MatrixType, class LocalInverseType>
1205 describe(Teuchos::FancyOStream& out,
1206 const Teuchos::EVerbosityLevel verbLevel) const {
1207 using std::endl;
1208 using Teuchos::OSTab;
1209 using Teuchos::TypeNameTraits;
1210
1211 const int myRank = Matrix_->getComm()->getRank();
1212 const int numProcs = Matrix_->getComm()->getSize();
1213 const Teuchos::EVerbosityLevel vl =
1214 (verbLevel == Teuchos::VERB_DEFAULT) ? Teuchos::VERB_LOW : verbLevel;
1215
1216 if (vl > Teuchos::VERB_NONE) {
1217 // describe() starts with a tab, by convention.
1218 OSTab tab0(out);
1219 if (myRank == 0) {
1220 out << "\"Ifpack2::AdditiveSchwarz\":";
1221 }
1222 OSTab tab1(out);
1223 if (myRank == 0) {
1224 out << "MatrixType: " << TypeNameTraits<MatrixType>::name() << endl;
1225 out << "LocalInverseType: " << TypeNameTraits<LocalInverseType>::name() << endl;
1226 if (this->getObjectLabel() != "") {
1227 out << "Label: \"" << this->getObjectLabel() << "\"" << endl;
1228 }
1229
1230 out << "Overlap level: " << OverlapLevel_ << endl
1231 << "Combine mode: \"";
1232 if (CombineMode_ == Tpetra::INSERT) {
1233 out << "INSERT";
1234 } else if (CombineMode_ == Tpetra::ADD) {
1235 out << "ADD";
1236 } else if (CombineMode_ == Tpetra::REPLACE) {
1237 out << "REPLACE";
1238 } else if (CombineMode_ == Tpetra::ABSMAX) {
1239 out << "ABSMAX";
1240 } else if (CombineMode_ == Tpetra::ZERO) {
1241 out << "ZERO";
1242 }
1243 out << "\"" << endl
1244 << "Subdomain reordering: \"" << ReorderingAlgorithm_ << "\"" << endl;
1245 }
1246
1247 if (Matrix_.is_null()) {
1248 if (myRank == 0) {
1249 out << "Matrix: null" << endl;
1250 }
1251 } else {
1252 if (myRank == 0) {
1253 out << "Matrix:" << endl;
1254 std::flush(out);
1255 }
1256 Matrix_->getComm()->barrier(); // wait for output to finish
1257 Matrix_->describe(out, Teuchos::VERB_LOW);
1258 }
1259
1260 if (myRank == 0) {
1261 out << "Number of initialize calls: " << getNumInitialize() << endl
1262 << "Number of compute calls: " << getNumCompute() << endl
1263 << "Number of apply calls: " << getNumApply() << endl
1264 << "Total time in seconds for initialize: " << getInitializeTime() << endl
1265 << "Total time in seconds for compute: " << getComputeTime() << endl
1266 << "Total time in seconds for apply: " << getApplyTime() << endl;
1267 }
1268
1269 if (Inverse_.is_null()) {
1270 if (myRank == 0) {
1271 out << "Subdomain solver: null" << endl;
1272 }
1273 } else {
1274 if (vl < Teuchos::VERB_EXTREME) {
1275 if (myRank == 0) {
1276 auto ifpack2_inverse = Teuchos::rcp_dynamic_cast<Ifpack2::Details::LinearSolver<scalar_type, local_ordinal_type, global_ordinal_type, node_type>>(Inverse_);
1277 if (ifpack2_inverse.is_null())
1278 out << "Subdomain solver: not null" << endl;
1279 else {
1280 out << "Subdomain solver: ";
1281 ifpack2_inverse->describe(out, Teuchos::VERB_LOW);
1282 }
1283 }
1284 } else { // vl >= Teuchos::VERB_EXTREME
1285 for (int p = 0; p < numProcs; ++p) {
1286 if (p == myRank) {
1287 out << "Subdomain solver on Process " << myRank << ":";
1288 if (Inverse_.is_null()) {
1289 out << "null" << endl;
1290 } else {
1291 Teuchos::RCP<Teuchos::Describable> inv =
1292 Teuchos::rcp_dynamic_cast<Teuchos::Describable>(Inverse_);
1293 if (!inv.is_null()) {
1294 out << endl;
1295 inv->describe(out, vl);
1296 } else {
1297 out << "null" << endl;
1298 }
1299 }
1300 }
1301 Matrix_->getComm()->barrier();
1302 Matrix_->getComm()->barrier();
1303 Matrix_->getComm()->barrier(); // wait for output to finish
1304 }
1305 }
1306 }
1307
1308 Matrix_->getComm()->barrier(); // wait for output to finish
1309 }
1310}
1311
1312template <class MatrixType, class LocalInverseType>
1313std::ostream& AdditiveSchwarz<MatrixType, LocalInverseType>::print(std::ostream& os) const {
1314 Teuchos::FancyOStream fos(Teuchos::rcp(&os, false));
1315 fos.setOutputToRootOnly(0);
1316 describe(fos);
1317 return (os);
1318}
1319
1320template <class MatrixType, class LocalInverseType>
1322 return OverlapLevel_;
1323}
1324
1325template <class MatrixType, class LocalInverseType>
1327#ifdef HAVE_MPI
1328 using Teuchos::MpiComm;
1329#endif // HAVE_MPI
1330 using Teuchos::ArrayRCP;
1331 using Teuchos::ParameterList;
1332 using Teuchos::RCP;
1333 using Teuchos::rcp;
1334 using Teuchos::rcp_dynamic_cast;
1335 using Teuchos::rcpFromRef;
1336
1337 TEUCHOS_TEST_FOR_EXCEPTION(
1338 Matrix_.is_null(), std::runtime_error,
1339 "Ifpack2::AdditiveSchwarz::"
1340 "initialize: The matrix to precondition is null. You must either pass "
1341 "a nonnull matrix to the constructor, or call setMatrix() with a nonnull "
1342 "input, before you may call this method.");
1343
1344 // If the matrix is a CrsMatrix or OverlappingRowMatrix, use the high-performance
1345 // AdditiveSchwarzFilter. Otherwise, use composition of Reordered/Singleton/LocalFilter.
1346 auto matrixCrs = rcp_dynamic_cast<const crs_matrix_type>(Matrix_);
1347 if (!OverlappingMatrix_.is_null() || !matrixCrs.is_null()) {
1348 ArrayRCP<local_ordinal_type> perm;
1349 ArrayRCP<local_ordinal_type> revperm;
1350 if (UseReordering_) {
1351 Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("Reordering"));
1352#if defined(HAVE_IFPACK2_XPETRA) && defined(HAVE_IFPACK2_ZOLTAN2)
1353 // Unlike Ifpack, Zoltan2 does all the dirty work here.
1354 Teuchos::ParameterList zlist = List_.sublist("schwarz: reordering list");
1355 ReorderingAlgorithm_ = zlist.get<std::string>("order_method", "rcm");
1356
1357 if (ReorderingAlgorithm_ == "user") {
1358 // User-provided reordering
1359 perm = zlist.get<Teuchos::ArrayRCP<local_ordinal_type>>("user ordering");
1360 revperm = zlist.get<Teuchos::ArrayRCP<local_ordinal_type>>("user reverse ordering");
1361 } else {
1362 // Zoltan2 reordering
1363 typedef Tpetra::RowGraph<local_ordinal_type, global_ordinal_type, node_type> row_graph_type;
1364 typedef Zoltan2::TpetraRowGraphAdapter<row_graph_type> z2_adapter_type;
1365 auto constActiveGraph = Teuchos::rcp_const_cast<const row_graph_type>(
1366 IsOverlapping_ ? OverlappingMatrix_->getGraph() : Matrix_->getGraph());
1367 z2_adapter_type Zoltan2Graph(constActiveGraph);
1368
1369 typedef Zoltan2::OrderingProblem<z2_adapter_type> ordering_problem_type;
1370#ifdef HAVE_MPI
1371 // Grab the MPI Communicator and build the ordering problem with that
1372 MPI_Comm myRawComm;
1373
1374 RCP<const MpiComm<int>> mpicomm =
1375 rcp_dynamic_cast<const MpiComm<int>>(Matrix_->getComm());
1376 if (mpicomm == Teuchos::null) {
1377 myRawComm = MPI_COMM_SELF;
1378 } else {
1379 myRawComm = *(mpicomm->getRawMpiComm());
1380 }
1381 ordering_problem_type MyOrderingProblem(&Zoltan2Graph, &zlist, myRawComm);
1382#else
1383 ordering_problem_type MyOrderingProblem(&Zoltan2Graph, &zlist);
1384#endif
1385 MyOrderingProblem.solve();
1386
1387 {
1388 typedef Zoltan2::LocalOrderingSolution<local_ordinal_type>
1389 ordering_solution_type;
1390
1391 ordering_solution_type sol(*MyOrderingProblem.getLocalOrderingSolution());
1392
1393 // perm[i] gives the where OLD index i shows up in the NEW
1394 // ordering. revperm[i] gives the where NEW index i shows
1395 // up in the OLD ordering. Note that perm is actually the
1396 // "inverse permutation," in Zoltan2 terms.
1397 perm = sol.getPermutationRCPConst(true);
1398 revperm = sol.getPermutationRCPConst();
1399 }
1400 }
1401#else
1402 // This is a logic_error, not a runtime_error, because
1403 // setParameters() should have excluded this case already.
1404 TEUCHOS_TEST_FOR_EXCEPTION(
1405 true, std::logic_error,
1406 "Ifpack2::AdditiveSchwarz::setup: "
1407 "The Zoltan2 and Xpetra packages must be enabled in order "
1408 "to support reordering.");
1409#endif
1410 } else {
1411 local_ordinal_type numLocalRows = OverlappingMatrix_.is_null() ? matrixCrs->getLocalNumRows() : OverlappingMatrix_->getLocalNumRows();
1412 // Use an identity ordering.
1413 // TODO: create a non-permuted code path in AdditiveSchwarzFilter, in the case that neither
1414 // reordering nor singleton filtering are enabled. In this situation it's like LocalFilter.
1415 perm = ArrayRCP<local_ordinal_type>(numLocalRows);
1416 revperm = ArrayRCP<local_ordinal_type>(numLocalRows);
1417 for (local_ordinal_type i = 0; i < numLocalRows; i++) {
1418 perm[i] = i;
1419 revperm[i] = i;
1420 }
1421 }
1422
1423 // Now, construct the filter
1424 {
1425 Teuchos::TimeMonitor t(*Teuchos::TimeMonitor::getNewTimer("Filter construction"));
1426 RCP<Details::AdditiveSchwarzFilter<MatrixType>> asf;
1427 if (OverlappingMatrix_.is_null())
1428 asf = rcp(new Details::AdditiveSchwarzFilter<MatrixType>(matrixCrs, perm, revperm, FilterSingletons_));
1429 else
1430 asf = rcp(new Details::AdditiveSchwarzFilter<MatrixType>(OverlappingMatrix_, perm, revperm, FilterSingletons_));
1431 innerMatrix_ = asf;
1432 }
1433
1434 if (UseReordering_ && (Coordinates_ != Teuchos::null)) {
1435 perm_coors = perm_dualview_type(Kokkos::view_alloc(Kokkos::WithoutInitializing, "perm_coors"), perm.size());
1436 perm_coors.modify_host();
1437 auto permHost = perm_coors.view_host();
1438 for (local_ordinal_type i = 0; i < static_cast<local_ordinal_type>(perm.size()); i++) {
1439 permHost(i) = perm[i];
1440 }
1441 perm_coors.sync_device();
1442 }
1443 } else {
1444 // Localized version of Matrix_ or OverlappingMatrix_.
1445 RCP<row_matrix_type> LocalizedMatrix;
1446
1447 // The "most current local matrix." At the end of this method, this
1448 // will be handed off to the inner solver.
1449 RCP<row_matrix_type> ActiveMatrix;
1450
1451 // Create localized matrix.
1452 if (!OverlappingMatrix_.is_null()) {
1453 LocalizedMatrix = rcp(new LocalFilter<row_matrix_type>(OverlappingMatrix_));
1454 } else {
1455 LocalizedMatrix = rcp(new LocalFilter<row_matrix_type>(Matrix_));
1456 }
1457
1458 // Sanity check; I don't trust the logic above to have created LocalizedMatrix.
1459 TEUCHOS_TEST_FOR_EXCEPTION(
1460 LocalizedMatrix.is_null(), std::logic_error,
1461 "Ifpack2::AdditiveSchwarz::setup: LocalizedMatrix is null, after the code "
1462 "that claimed to have created it. This should never be the case. Please "
1463 "report this bug to the Ifpack2 developers.");
1464
1465 // Mark localized matrix as active
1466 ActiveMatrix = LocalizedMatrix;
1467
1468 // Singleton Filtering
1469 if (FilterSingletons_) {
1470 SingletonMatrix_ = rcp(new SingletonFilter<row_matrix_type>(LocalizedMatrix));
1471 ActiveMatrix = SingletonMatrix_;
1472 }
1473
1474 // Do reordering
1475 if (UseReordering_) {
1476#if defined(HAVE_IFPACK2_XPETRA) && defined(HAVE_IFPACK2_ZOLTAN2)
1477 // Unlike Ifpack, Zoltan2 does all the dirty work here.
1478 typedef ReorderFilter<row_matrix_type> reorder_filter_type;
1479 Teuchos::ParameterList zlist = List_.sublist("schwarz: reordering list");
1480 ReorderingAlgorithm_ = zlist.get<std::string>("order_method", "rcm");
1481
1482 ArrayRCP<local_ordinal_type> perm;
1483 ArrayRCP<local_ordinal_type> revperm;
1484
1485 if (ReorderingAlgorithm_ == "user") {
1486 // User-provided reordering
1487 perm = zlist.get<Teuchos::ArrayRCP<local_ordinal_type>>("user ordering");
1488 revperm = zlist.get<Teuchos::ArrayRCP<local_ordinal_type>>("user reverse ordering");
1489 } else {
1490 // Zoltan2 reordering
1491 typedef Tpetra::RowGraph<local_ordinal_type, global_ordinal_type, node_type> row_graph_type;
1492 typedef Zoltan2::TpetraRowGraphAdapter<row_graph_type> z2_adapter_type;
1493 RCP<const row_graph_type> constActiveGraph =
1494 Teuchos::rcp_const_cast<const row_graph_type>(ActiveMatrix->getGraph());
1495 z2_adapter_type Zoltan2Graph(constActiveGraph);
1496
1497 typedef Zoltan2::OrderingProblem<z2_adapter_type> ordering_problem_type;
1498#ifdef HAVE_MPI
1499 // Grab the MPI Communicator and build the ordering problem with that
1500 MPI_Comm myRawComm;
1501
1502 RCP<const MpiComm<int>> mpicomm =
1503 rcp_dynamic_cast<const MpiComm<int>>(ActiveMatrix->getComm());
1504 if (mpicomm == Teuchos::null) {
1505 myRawComm = MPI_COMM_SELF;
1506 } else {
1507 myRawComm = *(mpicomm->getRawMpiComm());
1508 }
1509 ordering_problem_type MyOrderingProblem(&Zoltan2Graph, &zlist, myRawComm);
1510#else
1511 ordering_problem_type MyOrderingProblem(&Zoltan2Graph, &zlist);
1512#endif
1513 MyOrderingProblem.solve();
1514
1515 {
1516 typedef Zoltan2::LocalOrderingSolution<local_ordinal_type>
1517 ordering_solution_type;
1518
1519 ordering_solution_type sol(*MyOrderingProblem.getLocalOrderingSolution());
1520
1521 // perm[i] gives the where OLD index i shows up in the NEW
1522 // ordering. revperm[i] gives the where NEW index i shows
1523 // up in the OLD ordering. Note that perm is actually the
1524 // "inverse permutation," in Zoltan2 terms.
1525 perm = sol.getPermutationRCPConst(true);
1526 revperm = sol.getPermutationRCPConst();
1527 }
1528 }
1529 // All reorderings here...
1530 ReorderedLocalizedMatrix_ = rcp(new reorder_filter_type(ActiveMatrix, perm, revperm));
1531
1532 ActiveMatrix = ReorderedLocalizedMatrix_;
1533#else
1534 // This is a logic_error, not a runtime_error, because
1535 // setParameters() should have excluded this case already.
1536 TEUCHOS_TEST_FOR_EXCEPTION(
1537 true, std::logic_error,
1538 "Ifpack2::AdditiveSchwarz::setup: "
1539 "The Zoltan2 and Xpetra packages must be enabled in order "
1540 "to support reordering.");
1541#endif
1542 }
1543 innerMatrix_ = ActiveMatrix;
1544 }
1545
1546 TEUCHOS_TEST_FOR_EXCEPTION(
1547 innerMatrix_.is_null(), std::logic_error,
1548 "Ifpack2::AdditiveSchwarz::"
1549 "setup: Inner matrix is null right before constructing inner solver. "
1550 "Please report this bug to the Ifpack2 developers.");
1551
1552 // Construct the inner solver if necessary.
1553 if (Inverse_.is_null()) {
1554 const std::string innerName = innerPrecName();
1555 TEUCHOS_TEST_FOR_EXCEPTION(
1556 innerName == "INVALID", std::logic_error,
1557 "Ifpack2::AdditiveSchwarz::initialize: AdditiveSchwarz doesn't "
1558 "know how to create an instance of your LocalInverseType \""
1559 << Teuchos::TypeNameTraits<LocalInverseType>::name() << "\". "
1560 "Please talk to the Ifpack2 developers for details.");
1561
1562 TEUCHOS_TEST_FOR_EXCEPTION(
1563 innerName == "CUSTOM", std::runtime_error,
1564 "Ifpack2::AdditiveSchwarz::"
1565 "initialize: If the \"inner preconditioner name\" parameter (or any "
1566 "alias thereof) has the value \"CUSTOM\", then you must first call "
1567 "setInnerPreconditioner with a nonnull inner preconditioner input before "
1568 "you may call initialize().");
1569
1570 // FIXME (mfh 26 Aug 2015) Once we fix Bug 6392, the following
1571 // three lines of code can and SHOULD go away.
1572 if (!Trilinos::Details::Impl::registeredSomeLinearSolverFactory("Ifpack2")) {
1574 }
1575
1576 // FIXME (mfh 26 Aug 2015) Provide the capability to get inner
1577 // solvers from packages other than Ifpack2.
1578 typedef typename MV::mag_type MT;
1579 RCP<inner_solver_type> innerPrec =
1580 Trilinos::Details::getLinearSolver<MV, OP, MT>("Ifpack2", innerName);
1581 TEUCHOS_TEST_FOR_EXCEPTION(
1582 innerPrec.is_null(), std::logic_error,
1583 "Ifpack2::AdditiveSchwarz::setup: Failed to create inner preconditioner "
1584 "with name \""
1585 << innerName << "\".");
1586 innerPrec->setMatrix(innerMatrix_);
1587
1588 // Extract and apply the sublist of parameters to give to the
1589 // inner solver, if there is such a sublist of parameters.
1590 std::pair<Teuchos::ParameterList, bool> result = innerPrecParams();
1591 if (result.second) {
1592 // FIXME (mfh 26 Aug 2015) We don't really want to use yet
1593 // another deep copy of the ParameterList here.
1594 innerPrec->setParameters(rcp(new ParameterList(result.first)));
1595 }
1596 Inverse_ = innerPrec; // "Commit" the inner solver.
1597 } else if (Inverse_->getMatrix().getRawPtr() != innerMatrix_.getRawPtr()) {
1598 // The new inner matrix is different from the inner
1599 // preconditioner's current matrix, so give the inner
1600 // preconditioner the new inner matrix.
1601 Inverse_->setMatrix(innerMatrix_);
1602 }
1603 TEUCHOS_TEST_FOR_EXCEPTION(
1604 Inverse_.is_null(), std::logic_error,
1605 "Ifpack2::AdditiveSchwarz::"
1606 "setup: Inverse_ is null right after we were supposed to have created it."
1607 " Please report this bug to the Ifpack2 developers.");
1608
1609 // We don't have to call setInnerPreconditioner() here, because we
1610 // had the inner matrix (innerMatrix_) before creation of the inner
1611 // preconditioner. Calling setInnerPreconditioner here would be
1612 // legal, but it would require an unnecessary reset of the inner
1613 // preconditioner (i.e., calling initialize() and compute() again).
1614}
1615
1616template <class MatrixType, class LocalInverseType>
1621 node_type>>& innerPrec) {
1622 if (!innerPrec.is_null()) {
1623 // Make sure that the new inner solver knows how to have its matrix changed.
1624 typedef Details::CanChangeMatrix<row_matrix_type> can_change_type;
1625 can_change_type* innerSolver = dynamic_cast<can_change_type*>(&*innerPrec);
1626 TEUCHOS_TEST_FOR_EXCEPTION(
1627 innerSolver == NULL, std::invalid_argument,
1628 "Ifpack2::AdditiveSchwarz::"
1629 "setInnerPreconditioner: The input preconditioner does not implement the "
1630 "setMatrix() feature. Only input preconditioners that inherit from "
1631 "Ifpack2::Details::CanChangeMatrix implement this feature.");
1632
1633 // If users provide an inner solver, we assume that
1634 // AdditiveSchwarz's current inner solver parameters no longer
1635 // apply. (In fact, we will remove those parameters from
1636 // AdditiveSchwarz's current list below.) Thus, we do /not/ apply
1637 // the current sublist of inner solver parameters to the input
1638 // inner solver.
1639
1640 // mfh 03 Jan 2014: Thanks to Paul Tsuji for pointing out that
1641 // it's perfectly legal for innerMatrix_ to be null here. This
1642 // can happen if initialize() has not been called yet. For
1643 // example, when Ifpack2::Factory creates an AdditiveSchwarz
1644 // instance, it calls setInnerPreconditioner() without first
1645 // calling initialize().
1646
1647 // Give the local matrix to the new inner solver.
1648 if (auto asf = Teuchos::rcp_dynamic_cast<Details::AdditiveSchwarzFilter<MatrixType>>(innerMatrix_))
1649 innerSolver->setMatrix(asf->getFilteredMatrix());
1650 else
1651 innerSolver->setMatrix(innerMatrix_);
1652
1653 // If the user previously specified a parameter for the inner
1654 // preconditioner's type, then clear out that parameter and its
1655 // associated sublist. Replace the inner preconditioner's type with
1656 // "CUSTOM", to make it obvious that AdditiveSchwarz's ParameterList
1657 // does not necessarily describe the current inner preconditioner.
1658 // We have to remove all allowed aliases of "inner preconditioner
1659 // name" before we may set it to "CUSTOM". Users may also set this
1660 // parameter to "CUSTOM" themselves, but this is not required.
1661 removeInnerPrecName();
1662 removeInnerPrecParams();
1663 List_.set("inner preconditioner name", "CUSTOM");
1664
1665 // Bring the new inner solver's current status (initialized or
1666 // computed) in line with AdditiveSchwarz's current status.
1667 if (isInitialized()) {
1668 innerPrec->initialize();
1669 }
1670 if (isComputed()) {
1671 innerPrec->compute();
1672 }
1673 }
1674
1675 // If the new inner solver is null, we don't change the initialized
1676 // or computed status of AdditiveSchwarz. That way, AdditiveSchwarz
1677 // won't have to recompute innerMatrix_ if the inner solver changes.
1678 // This does introduce a new error condition in compute() and
1679 // apply(), but that's OK.
1680
1681 // Set the new inner solver.
1684 inner_solver_impl_type;
1685 Inverse_ = Teuchos::rcp(new inner_solver_impl_type(innerPrec, "CUSTOM"));
1686}
1687
1688template <class MatrixType, class LocalInverseType>
1690 setMatrix(const Teuchos::RCP<const row_matrix_type>& A) {
1691 // Don't set the matrix unless it is different from the current one.
1692 if (A.getRawPtr() != Matrix_.getRawPtr()) {
1693 IsInitialized_ = false;
1694 IsComputed_ = false;
1695
1696 // Reset all the state computed in initialize() and compute().
1697 OverlappingMatrix_ = Teuchos::null;
1698 ReorderedLocalizedMatrix_ = Teuchos::null;
1699 innerMatrix_ = Teuchos::null;
1700 SingletonMatrix_ = Teuchos::null;
1701 localMap_ = Teuchos::null;
1702 overlapping_B_.reset(nullptr);
1703 overlapping_Y_.reset(nullptr);
1704 R_.reset(nullptr);
1705 C_.reset(nullptr);
1706 DistributedImporter_ = Teuchos::null;
1707
1708 Matrix_ = A;
1709 }
1710}
1711
1712template <class MatrixType, class LocalInverseType>
1714 setCoord(const Teuchos::RCP<const coord_type>& Coordinates) {
1715 // Don't set unless it is different from the current one.
1716 if (Coordinates.getRawPtr() != Coordinates_.getRawPtr()) {
1717 Coordinates_ = Coordinates;
1718 }
1719}
1720
1721} // namespace Ifpack2
1722
1723// NOTE (mfh 26 Aug 2015) There's no need to instantiate for CrsMatrix
1724// too. All Ifpack2 preconditioners can and should do dynamic casts
1725// internally, if they need a type more specific than RowMatrix.
1726#define IFPACK2_ADDITIVESCHWARZ_INSTANT(S, LO, GO, N) \
1727 template class Ifpack2::AdditiveSchwarz<Tpetra::RowMatrix<S, LO, GO, N>>;
1728
1729#endif // IFPACK2_ADDITIVESCHWARZ_DECL_HPP
Declaration of Ifpack2::AdditiveSchwarz, which implements additive Schwarz preconditioning with an ar...
void registerLinearSolverFactory()
Register Ifpack2's LinearSolverFactory with the central repository, for all enabled combinations of t...
Definition Ifpack2_Details_registerLinearSolverFactory.cpp:33
Declaration of interface for preconditioners that can change their matrix after construction.
Additive Schwarz domain decomposition for Tpetra sparse matrices.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:260
typename MatrixType::local_ordinal_type local_ordinal_type
The type of local indices in the input MatrixType.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:283
std::string description() const
Return a simple one-line description of this object.
Definition Ifpack2_AdditiveSchwarz_def.hpp:1152
virtual bool isInitialized() const
Returns true if the preconditioner has been successfully initialized, false otherwise.
Definition Ifpack2_AdditiveSchwarz_def.hpp:1046
void setCoord(const Teuchos::RCP< const coord_type > &Coordinates)
Set the matrix rows' coordinates.
Definition Ifpack2_AdditiveSchwarz_def.hpp:1714
virtual int getOverlapLevel() const
Returns the level of overlap.
Definition Ifpack2_AdditiveSchwarz_def.hpp:1321
Teuchos::RCP< const Teuchos::ParameterList > getValidParameters() const
Get a list of the preconditioner's default parameters.
Definition Ifpack2_AdditiveSchwarz_def.hpp:881
typename MatrixType::global_ordinal_type global_ordinal_type
The type of global indices in the input MatrixType.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:286
virtual int getNumCompute() const
Returns the number of calls to compute().
Definition Ifpack2_AdditiveSchwarz_def.hpp:1127
Teuchos::RCP< const coord_type > getCoord() const
Get the coordinates associated with the input matrix's rows.
Definition Ifpack2_AdditiveSchwarz_def.hpp:242
virtual int getNumApply() const
Returns the number of calls to apply().
Definition Ifpack2_AdditiveSchwarz_def.hpp:1132
virtual double getComputeTime() const
Returns the time spent in compute().
Definition Ifpack2_AdditiveSchwarz_def.hpp:1142
virtual double getApplyTime() const
Returns the time spent in apply().
Definition Ifpack2_AdditiveSchwarz_def.hpp:1147
virtual void setMatrix(const Teuchos::RCP< const row_matrix_type > &A)
Change the matrix to be preconditioned.
Definition Ifpack2_AdditiveSchwarz_def.hpp:1690
typename MatrixType::node_type node_type
The Node type used by the input MatrixType.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:289
virtual void initialize()
Computes all (graph-related) data necessary to initialize the preconditioner.
Definition Ifpack2_AdditiveSchwarz_def.hpp:925
void setParameterList(const Teuchos::RCP< Teuchos::ParameterList > &plist)
Set the preconditioner's parameters.
Definition Ifpack2_AdditiveSchwarz_def.hpp:690
virtual void setInnerPreconditioner(const Teuchos::RCP< Preconditioner< scalar_type, local_ordinal_type, global_ordinal_type, node_type > > &innerPrec)
Set the inner preconditioner.
Definition Ifpack2_AdditiveSchwarz_def.hpp:1618
virtual double getInitializeTime() const
Returns the time spent in initialize().
Definition Ifpack2_AdditiveSchwarz_def.hpp:1137
virtual Teuchos::RCP< const Tpetra::Map< local_ordinal_type, global_ordinal_type, node_type > > getRangeMap() const
The range Map of this operator.
Definition Ifpack2_AdditiveSchwarz_def.hpp:226
virtual Teuchos::RCP< const Tpetra::Map< local_ordinal_type, global_ordinal_type, node_type > > getDomainMap() const
The domain Map of this operator.
Definition Ifpack2_AdditiveSchwarz_def.hpp:214
virtual void compute()
Computes all (coefficient) data necessary to apply the preconditioner.
Definition Ifpack2_AdditiveSchwarz_def.hpp:1051
Tpetra::MultiVector< magnitude_type, local_ordinal_type, global_ordinal_type, node_type > coord_type
The Tpetra::MultiVector specialization used for containing coordinates.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:308
virtual std::ostream & print(std::ostream &os) const
Prints basic information on iostream. This function is used by operator<<.
Definition Ifpack2_AdditiveSchwarz_def.hpp:1313
typename MatrixType::scalar_type scalar_type
The type of the entries of the input MatrixType.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:280
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_AdditiveSchwarz_def.hpp:1205
virtual void setParameters(const Teuchos::ParameterList &plist)
Set the preconditioner's parameters.
Definition Ifpack2_AdditiveSchwarz_def.hpp:680
virtual Teuchos::RCP< const row_matrix_type > getMatrix() const
The input matrix.
Definition Ifpack2_AdditiveSchwarz_def.hpp:237
virtual 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 preconditioner to X, putting the result in Y.
Definition Ifpack2_AdditiveSchwarz_def.hpp:273
virtual bool isComputed() const
Returns true if the preconditioner has been successfully computed, false otherwise.
Definition Ifpack2_AdditiveSchwarz_def.hpp:1117
virtual int getNumInitialize() const
Returns the number of calls to initialize().
Definition Ifpack2_AdditiveSchwarz_def.hpp:1122
AdditiveSchwarz(const Teuchos::RCP< const row_matrix_type > &A)
Constructor that takes a matrix.
Definition Ifpack2_AdditiveSchwarz_def.hpp:194
Ifpack2's implementation of Trilinos::Details::LinearSolver interface.
Definition Ifpack2_Details_LinearSolver_decl.hpp:75
Sparse matrix (Tpetra::RowMatrix subclass) with ghost rows.
Definition Ifpack2_OverlappingRowMatrix_decl.hpp:25
Interface for all Ifpack2 preconditioners.
Definition Ifpack2_Preconditioner.hpp:74
void registerLinearSolverFactory()
Ifpack2 implementation details.
Preconditioners and smoothers for Tpetra sparse matrices.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:40
void getValidParameters(Teuchos::ParameterList &params)
Fills a list which contains all the parameters possibly used by Ifpack2.
Definition Ifpack2_Parameters.cpp:18