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