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