MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_Hierarchy_def.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// MueLu: A package for multigrid based preconditioning
4//
5// Copyright 2012 NTESS and the MueLu contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef MUELU_HIERARCHY_DEF_HPP
11#define MUELU_HIERARCHY_DEF_HPP
12
13#include <time.h>
14
15#include <algorithm>
16#include <sstream>
17#include <list>
18
19#include <Xpetra_Matrix.hpp>
20#include <Xpetra_MultiVectorFactory.hpp>
21#include <Xpetra_Operator.hpp>
22#include <Xpetra_IO.hpp>
23
25
26#include "MueLu_FactoryManager.hpp"
27#include "MueLu_HierarchyUtils.hpp"
28#include "MueLu_TopRAPFactory.hpp"
29#include "MueLu_TopSmootherFactory.hpp"
30#include "MueLu_Level.hpp"
31#include "MueLu_Monitor.hpp"
32#include "MueLu_PerfUtils.hpp"
33#include "MueLu_PFactory.hpp"
34#include "MueLu_SmootherFactory.hpp"
36#include "MueLu_Behavior.hpp"
37
38#include "Teuchos_TimeMonitor.hpp"
39
40namespace MueLu {
41
42template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
44 : maxCoarseSize_(GetDefaultMaxCoarseSize())
45 , implicitTranspose_(GetDefaultImplicitTranspose())
46 , fuseProlongationAndUpdate_(GetDefaultFuseProlongationAndUpdate())
47 , doPRrebalance_(GetDefaultPRrebalance())
48 , doPRViaCopyrebalance_(false)
49 , isPreconditioner_(true)
50 , Cycle_(GetDefaultCycle())
51 , WCycleStartLevel_(0)
52 , scalingFactor_(Teuchos::ScalarTraits<double>::one())
53 , lib_(Xpetra::UseTpetra)
54 , isDumpingEnabled_(false)
55 , dumpLevel_(-2)
56 , rate_(-1)
57 , sizeOfAllocatedLevelMultiVectors_(0) {
58 AddLevel(rcp(new Level));
59}
60
61template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
63 : Hierarchy() {
64 SetLabel(label);
65 setObjectLabel(label);
66 Levels_[0]->setObjectLabel(label);
67}
68
69template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
71 : maxCoarseSize_(GetDefaultMaxCoarseSize())
72 , implicitTranspose_(GetDefaultImplicitTranspose())
73 , fuseProlongationAndUpdate_(GetDefaultFuseProlongationAndUpdate())
74 , doPRrebalance_(GetDefaultPRrebalance())
75 , doPRViaCopyrebalance_(false)
76 , isPreconditioner_(true)
77 , Cycle_(GetDefaultCycle())
78 , WCycleStartLevel_(0)
79 , scalingFactor_(Teuchos::ScalarTraits<double>::one())
80 , isDumpingEnabled_(false)
81 , dumpLevel_(-2)
82 , rate_(-1)
83 , sizeOfAllocatedLevelMultiVectors_(0) {
84 lib_ = A->getDomainMap()->lib();
85
86 RCP<Level> Finest = rcp(new Level);
87 AddLevel(Finest);
88
89 Finest->Set("A", A);
90}
91
92template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
93Hierarchy<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Hierarchy(const RCP<Matrix>& A, const std::string& label)
94 : Hierarchy(A) {
95 setObjectLabel(label);
96 Levels_[0]->setObjectLabel(label);
97}
98
99template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
101
102template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
104 int levelID = LastLevelID() + 1; // ID of the inserted level
105
106 if (level->GetLevelID() != -1 && (level->GetLevelID() != levelID))
107 GetOStream(Warnings1) << "Hierarchy::AddLevel(): Level with ID=" << level->GetLevelID() << " have been added at the end of the hierarchy\n but its ID have been redefined"
108 << " because last level ID of the hierarchy was " << LastLevelID() << "." << std::endl;
109
110 Levels_.push_back(level);
111 level->SetLevelID(levelID);
112 level->setlib(lib_);
113
114 level->SetPreviousLevel((levelID == 0) ? Teuchos::null : Levels_[LastLevelID() - 1]);
115 level->setObjectLabel(this->getObjectLabel());
116}
117
118template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
120 RCP<Level> newLevel = Levels_[LastLevelID()]->Build(); // new coarse level, using copy constructor
121 newLevel->setlib(lib_);
122 this->AddLevel(newLevel); // add to hierarchy
123}
124
125template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
127 TEUCHOS_TEST_FOR_EXCEPTION(levelID < 0 || levelID > LastLevelID(), Exceptions::RuntimeError,
128 "MueLu::Hierarchy::GetLevel(): invalid input parameter value: LevelID = " << levelID);
129 return Levels_[levelID];
130}
131
132template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
134 return Levels_.size();
135}
136
137template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
139 RCP<Operator> A = Levels_[0]->template Get<RCP<Operator>>("A");
140 RCP<const Teuchos::Comm<int>> comm = A->getDomainMap()->getComm();
141
142 int numLevels = GetNumLevels();
143 int numGlobalLevels;
144 Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, numLevels, Teuchos::ptr(&numGlobalLevels));
145
146 return numGlobalLevels;
147}
148
149template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
151 double totalNnz = 0, lev0Nnz = 1;
152 for (int i = 0; i < GetNumLevels(); ++i) {
153 TEUCHOS_TEST_FOR_EXCEPTION(!(Levels_[i]->IsAvailable("A")), Exceptions::RuntimeError,
154 "Operator complexity cannot be calculated because A is unavailable on level " << i);
155 RCP<Operator> A = Levels_[i]->template Get<RCP<Operator>>("A");
156 if (A.is_null())
157 break;
158
159 RCP<Matrix> Am = rcp_dynamic_cast<Matrix>(A);
160 if (Am.is_null()) {
161 GetOStream(Warnings0) << "Some level operators are not matrices, operator complexity calculation aborted" << std::endl;
162 return 0.0;
163 }
164
165 if (!Am->haveGlobalConstants()) {
166 GetOStream(Warnings0) << "Some level operators are do not have global constants computed, operator complexity calculation aborted" << std::endl;
167 return 0.0;
168 }
169
170 totalNnz += as<double>(Am->getGlobalNumEntries());
171 if (i == 0)
172 lev0Nnz = totalNnz;
173 }
174 return totalNnz / lev0Nnz;
175}
176
177template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
179 double node_sc = 0, global_sc = 0;
180 double a0_nnz = 0;
181 const size_t INVALID = Teuchos::OrdinalTraits<size_t>::invalid();
182 // Get cost of fine matvec
183 if (GetNumLevels() <= 0) return -1.0;
184 if (!Levels_[0]->IsAvailable("A")) return -1.0;
185
186 RCP<Operator> A = Levels_[0]->template Get<RCP<Operator>>("A");
187 if (A.is_null()) return -1.0;
188 RCP<Matrix> Am = rcp_dynamic_cast<Matrix>(A);
189 if (Am.is_null()) return -1.0;
190 if (!Am->haveGlobalConstants()) return -1.0;
191 a0_nnz = as<double>(Am->getGlobalNumEntries());
192
193 // Get smoother complexity at each level
194 for (int i = 0; i < GetNumLevels(); ++i) {
195 size_t level_sc = 0;
196 if (!Levels_[i]->IsAvailable("PreSmoother")) continue;
197 RCP<SmootherBase> S = Levels_[i]->template Get<RCP<SmootherBase>>("PreSmoother");
198 if (S.is_null()) continue;
199 level_sc = S->getNodeSmootherComplexity();
200 if (level_sc == INVALID) {
201 global_sc = -1.0;
202 break;
203 }
204
205 node_sc += as<double>(level_sc);
206 }
207
208 double min_sc = 0.0;
209 RCP<const Teuchos::Comm<int>> comm = A->getDomainMap()->getComm();
210 Teuchos::reduceAll(*comm, Teuchos::REDUCE_SUM, node_sc, Teuchos::ptr(&global_sc));
211 Teuchos::reduceAll(*comm, Teuchos::REDUCE_MIN, node_sc, Teuchos::ptr(&min_sc));
212
213 if (min_sc < 0.0)
214 return -1.0;
215 else
216 return global_sc / a0_nnz;
217}
218
219// Coherence checks todo in Setup() (using an helper function):
220template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
222 TEUCHOS_TEST_FOR_EXCEPTION(level.lib() != lib_, Exceptions::RuntimeError,
223 "MueLu::Hierarchy::CheckLevel(): wrong underlying linear algebra library.");
224 TEUCHOS_TEST_FOR_EXCEPTION(level.GetLevelID() != levelID, Exceptions::RuntimeError,
225 "MueLu::Hierarchy::CheckLevel(): wrong level ID");
226 TEUCHOS_TEST_FOR_EXCEPTION(levelID != 0 && level.GetPreviousLevel() != Levels_[levelID - 1], Exceptions::RuntimeError,
227 "MueLu::Hierarchy::Setup(): wrong level parent");
228}
229
230template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
232 for (int i = 0; i < GetNumLevels(); ++i) {
233 RCP<Level> level = Levels_[i];
234 if (level->IsAvailable("A")) {
235 RCP<Operator> Aop = level->Get<RCP<Operator>>("A");
236 RCP<Matrix> A = rcp_dynamic_cast<Matrix>(Aop);
237 if (!A.is_null()) {
238 RCP<const Import> xpImporter = A->getCrsGraph()->getImporter();
239 if (!xpImporter.is_null())
240 xpImporter->setDistributorParameters(matvecParams);
241 RCP<const Export> xpExporter = A->getCrsGraph()->getExporter();
242 if (!xpExporter.is_null())
243 xpExporter->setDistributorParameters(matvecParams);
244 }
245 }
246 const std::list<std::string> matrices = {"P", "R", "D0", "NodeMatrix"};
247 for (auto it = matrices.begin(); it != matrices.end(); ++it) {
248 if (level->IsAvailable(*it)) {
249 RCP<Matrix> mat = level->Get<RCP<Matrix>>(*it);
250 if (!mat.is_null()) {
251 RCP<const Import> xpImporter = mat->getCrsGraph()->getImporter();
252 if (!xpImporter.is_null()) {
253 xpImporter->setDistributorParameters(matvecParams);
254 }
255 RCP<const Export> xpExporter = mat->getCrsGraph()->getExporter();
256 if (!xpExporter.is_null())
257 xpExporter->setDistributorParameters(matvecParams);
258 }
259 }
260 }
261 if (level->IsAvailable("Importer")) {
262 RCP<const Import> xpImporter = level->Get<RCP<const Import>>("Importer");
263 if (!xpImporter.is_null())
264 xpImporter->setDistributorParameters(matvecParams);
265 }
266 }
267}
268
269// The function uses three managers: fine, coarse and next coarse
270// We construct the data for the coarse level, and do requests for the next coarse
271template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
273 const RCP<const FactoryManagerBase> fineLevelManager,
274 const RCP<const FactoryManagerBase> coarseLevelManager,
275 const RCP<const FactoryManagerBase> nextLevelManager) {
276 // Use PrintMonitor/TimerMonitor instead of just a FactoryMonitor to print "Level 0" instead of Hierarchy(0)
277 // Print is done after the requests for next coarse level
278
279 TEUCHOS_TEST_FOR_EXCEPTION(LastLevelID() < coarseLevelID, Exceptions::RuntimeError,
280 "MueLu::Hierarchy:Setup(): level " << coarseLevelID << " (specified by coarseLevelID argument) "
281 "must be built before calling this function.");
282
283 Level& level = *Levels_[coarseLevelID];
284
285 bool useStackedTimer = !Teuchos::TimeMonitor::stackedTimerNameIsDefault();
286
287 std::string label = FormattingHelper::getColonLabel(level.getObjectLabel());
288 RCP<TimeMonitor> m1;
289 if (!useStackedTimer)
290 m1 = rcp(new TimeMonitor(*this, label + this->ShortClassName() + ": " + "Setup (total)"));
291 TimeMonitor m2(*this, label + this->ShortClassName() + ": " + "Setup" + " (total, level=" + Teuchos::toString(coarseLevelID) + ")");
292
293 // TODO: pass coarseLevelManager by reference
294 TEUCHOS_TEST_FOR_EXCEPTION(coarseLevelManager == Teuchos::null, Exceptions::RuntimeError,
295 "MueLu::Hierarchy::Setup(): argument coarseLevelManager cannot be null");
296
299
300 if (levelManagers_.size() < coarseLevelID + 1)
301 levelManagers_.resize(coarseLevelID + 1);
302 levelManagers_[coarseLevelID] = coarseLevelManager;
303
304 bool isFinestLevel = (fineLevelManager.is_null());
305 bool isLastLevel = (nextLevelManager.is_null());
306
307 int oldRank = -1;
308 if (isFinestLevel) {
309 RCP<Operator> A = level.Get<RCP<Operator>>("A");
310 RCP<const Map> domainMap = A->getDomainMap();
311 RCP<const Teuchos::Comm<int>> comm = domainMap->getComm();
312
313 // Initialize random seed for reproducibility
315
316 // Record the communicator on the level (used for timers sync)
317 level.SetComm(comm);
318 oldRank = SetProcRankVerbose(comm->getRank());
319
320 // Set the Hierarchy library to match that of the finest level matrix,
321 // even if it was already set
322 lib_ = domainMap->lib();
323 level.setlib(lib_);
324
325 } else {
326 // Permeate library to a coarser level
327 level.setlib(lib_);
328
329 Level& prevLevel = *Levels_[coarseLevelID - 1];
330 oldRank = SetProcRankVerbose(prevLevel.GetComm()->getRank());
331 }
332
333 CheckLevel(level, coarseLevelID);
334
335 // Attach FactoryManager to the fine level
336 RCP<SetFactoryManager> SFMFine;
337 if (!isFinestLevel)
338 SFMFine = rcp(new SetFactoryManager(Levels_[coarseLevelID - 1], fineLevelManager));
339
340 if (isFinestLevel && Levels_[coarseLevelID]->IsAvailable("Coordinates"))
341 ReplaceCoordinateMap(*Levels_[coarseLevelID]);
342
343 // Attach FactoryManager to the coarse level
344 SetFactoryManager SFMCoarse(Levels_[coarseLevelID], coarseLevelManager);
345
346 if (isDumpingEnabled_ && (dumpLevel_ == 0 || dumpLevel_ == -1) && coarseLevelID == 1)
347 DumpCurrentGraph(0);
348
349 RCP<TopSmootherFactory> coarseFact;
350 RCP<TopSmootherFactory> smootherFact = rcp(new TopSmootherFactory(coarseLevelManager, "Smoother"));
351
352 int nextLevelID = coarseLevelID + 1;
353
354 RCP<SetFactoryManager> SFMNext;
355 if (isLastLevel == false) {
356 // We are not at the coarsest level, so there is going to be another level ("next coarse") after this one ("coarse")
357 if (nextLevelID > LastLevelID())
358 AddNewLevel();
359 CheckLevel(*Levels_[nextLevelID], nextLevelID);
360
361 // Attach FactoryManager to the next level (level after coarse)
362 SFMNext = rcp(new SetFactoryManager(Levels_[nextLevelID], nextLevelManager));
363 Levels_[nextLevelID]->Request(TopRAPFactory(coarseLevelManager, nextLevelManager));
364
365 // Do smoother requests here. We don't know whether this is going to be
366 // the coarsest level or not, but we need to DeclareInput before we call
367 // coarseRAPFactory.Build(), otherwise some stuff may be erased after
368 // level releases
369 level.Request(*smootherFact);
370
371 } else {
372 // Similar to smoother above, do the coarse solver request here. We don't
373 // know whether this is going to be the coarsest level or not, but we
374 // need to DeclareInput before we call coarseRAPFactory.Build(),
375 // otherwise some stuff may be erased after level releases. This is
376 // actually evident on ProjectorSmoother. It requires both "A" and
377 // "Nullspace". However, "Nullspace" is erased after all releases, so if
378 // we call the coarse factory request after RAP build we would not have
379 // any data, and cannot get it as we don't have previous managers. The
380 // typical trace looks like this:
381 //
382 // MueLu::Level(0)::GetFactory(Aggregates, 0): No FactoryManager
383 // during request for data " Aggregates" on level 0 by factory TentativePFactory
384 // during request for data " P" on level 1 by factory EminPFactory
385 // during request for data " P" on level 1 by factory TransPFactory
386 // during request for data " R" on level 1 by factory RAPFactory
387 // during request for data " A" on level 1 by factory TentativePFactory
388 // during request for data " Nullspace" on level 2 by factory NullspaceFactory
389 // during request for data " Nullspace" on level 2 by factory NullspacePresmoothFactory
390 // during request for data " Nullspace" on level 2 by factory ProjectorSmoother
391 // during request for data " PreSmoother" on level 2 by factory NoFactory
392 if (coarseFact.is_null())
393 coarseFact = rcp(new TopSmootherFactory(coarseLevelManager, "CoarseSolver"));
394 level.Request(*coarseFact);
395 }
396
397 GetOStream(Runtime0) << std::endl;
398 PrintMonitor m0(*this, "Level " + Teuchos::toString(coarseLevelID), static_cast<MsgType>(Runtime0 | Test));
399
400 // Build coarse level hierarchy
401 RCP<Operator> Ac = Teuchos::null;
402 TopRAPFactory coarseRAPFactory(fineLevelManager, coarseLevelManager);
403
404 if (level.IsAvailable("A")) {
405 Ac = level.Get<RCP<Operator>>("A");
406 } else if (!isFinestLevel) {
407 // We only build here, the release is done later
408 coarseRAPFactory.Build(*level.GetPreviousLevel(), level);
409 }
410
411 bool setLastLevelviaMaxCoarseSize = false;
412 if (level.IsAvailable("A"))
413 Ac = level.Get<RCP<Operator>>("A");
414 RCP<Matrix> Acm = rcp_dynamic_cast<Matrix>(Ac);
415
416 // Record the communicator on the level
417 if (!Ac.is_null())
418 level.SetComm(Ac->getDomainMap()->getComm());
419
420 // Test if we reach the end of the hierarchy
421 bool isOrigLastLevel = isLastLevel;
422 if (isLastLevel) {
423 // Last level as we have achieved the max limit
424 isLastLevel = true;
425
426 } else if (Ac.is_null()) {
427 // Last level for this processor, as it does not belong to the next
428 // subcommunicator. Other processors may continue working on the
429 // hierarchy
430 isLastLevel = true;
431
432 } else {
433 if (!Acm.is_null() && Acm->getGlobalNumRows() <= maxCoarseSize_) {
434 // Last level as the size of the coarse matrix became too small
435 GetOStream(Runtime0) << "Max coarse size (<= " << maxCoarseSize_ << ") achieved" << std::endl;
436 isLastLevel = true;
437 if (Acm->getGlobalNumRows() != 0) setLastLevelviaMaxCoarseSize = true;
438 }
439 }
440
441 if (!Ac.is_null() && !isFinestLevel) {
442 RCP<Operator> A = Levels_[coarseLevelID - 1]->template Get<RCP<Operator>>("A");
443 RCP<Matrix> Am = rcp_dynamic_cast<Matrix>(A);
444
445 const double maxCoarse2FineRatio = 0.8;
446 if (!Acm.is_null() && !Am.is_null() && Acm->getGlobalNumRows() > maxCoarse2FineRatio * Am->getGlobalNumRows()) {
447 // We could abort here, but for now we simply notify user.
448 // Couple of additional points:
449 // - if repartitioning is delayed until level K, but the aggregation
450 // procedure stagnates between levels K-1 and K. In this case,
451 // repartitioning could enable faster coarsening once again, but the
452 // hierarchy construction will abort due to the stagnation check.
453 // - if the matrix is small enough, we could move it to one processor.
454 GetOStream(Warnings0) << "Aggregation stagnated. Please check your matrix and/or adjust your configuration file."
455 << "Possible fixes:\n"
456 << " - reduce the maximum number of levels\n"
457 << " - enable repartitioning\n"
458 << " - increase the minimum coarse size." << std::endl;
459 }
460 }
461
462 if (isLastLevel) {
463 if (!isOrigLastLevel) {
464 // We did not expect to finish this early so we did request a smoother.
465 // We need a coarse solver instead. Do the magic.
466 level.Release(*smootherFact);
467 if (coarseFact.is_null())
468 coarseFact = rcp(new TopSmootherFactory(coarseLevelManager, "CoarseSolver"));
469 level.Request(*coarseFact);
470 }
471
472 // Do the actual build, if we have any data.
473 // NOTE: this is not a great check, we may want to call Build() regardless.
474 if (!Ac.is_null())
475 coarseFact->Build(level);
476
477 // Once the dirty deed is done, release stuff. The smoother has already
478 // been released.
479 level.Release(*coarseFact);
480
481 } else {
482 // isLastLevel = false => isOrigLastLevel = false, meaning that we have
483 // requested the smoother. Now we need to build it and to release it.
484 // We don't need to worry about the coarse solver, as we didn't request it.
485 if (!Ac.is_null())
486 smootherFact->Build(level);
487
488 level.Release(*smootherFact);
489 }
490
491 if (isLastLevel == true) {
492 int actualNumLevels = nextLevelID;
493 if (isOrigLastLevel == false) {
494 // Earlier in the function, we constructed the next coarse level, and requested data for the that level,
495 // assuming that we are not at the coarsest level. Now, we changed our mind, so we have to release those.
496 Levels_[nextLevelID]->Release(TopRAPFactory(coarseLevelManager, nextLevelManager));
497
498 // We truncate/resize the hierarchy and possibly remove the last created level if there is
499 // something wrong with it as indicated by its P not being valid. This might happen
500 // if the global number of aggregates turns out to be zero
501
502 if (!setLastLevelviaMaxCoarseSize) {
503 if (Levels_[nextLevelID - 1]->IsAvailable("P")) {
504 if (Levels_[nextLevelID - 1]->template Get<RCP<Matrix>>("P") == Teuchos::null) actualNumLevels = nextLevelID - 1;
505 } else
506 actualNumLevels = nextLevelID - 1;
507 }
508 }
509 if (actualNumLevels == nextLevelID - 1) {
510 // Didn't expect to finish early so we requested smoother but need coarse solver instead.
511 Levels_[nextLevelID - 2]->Release(*smootherFact);
512
513 if (Levels_[nextLevelID - 2]->IsAvailable("PreSmoother")) Levels_[nextLevelID - 2]->RemoveKeepFlag("PreSmoother", NoFactory::get());
514 if (Levels_[nextLevelID - 2]->IsAvailable("PostSmoother")) Levels_[nextLevelID - 2]->RemoveKeepFlag("PostSmoother", NoFactory::get());
515 if (coarseFact.is_null())
516 coarseFact = rcp(new TopSmootherFactory(coarseLevelManager, "CoarseSolver"));
517 Levels_[nextLevelID - 2]->Request(*coarseFact);
518 if (!(Levels_[nextLevelID - 2]->template Get<RCP<Matrix>>("A").is_null()))
519 coarseFact->Build(*(Levels_[nextLevelID - 2]));
520 Levels_[nextLevelID - 2]->Release(*coarseFact);
521 }
522 Levels_.resize(actualNumLevels);
523 levelManagers_.resize(actualNumLevels);
524 }
525
526 // I think this is the proper place for graph so that it shows every dependence
527 if (isDumpingEnabled_ && ((dumpLevel_ > 0 && coarseLevelID == dumpLevel_) || dumpLevel_ == -1))
528 DumpCurrentGraph(coarseLevelID);
529
530 if (!isFinestLevel) {
531 // Release the hierarchy data
532 // We release so late to help blocked solvers, as the smoothers for them need A blocks
533 // which we construct in RAPFactory
534 level.Release(coarseRAPFactory);
535 }
536
537 if (oldRank != -1)
538 SetProcRankVerbose(oldRank);
539
540 return isLastLevel;
541}
542
543template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
545 int numLevels = Levels_.size();
546 TEUCHOS_TEST_FOR_EXCEPTION(levelManagers_.size() != numLevels, Exceptions::RuntimeError,
547 "Hierarchy::SetupRe: " << Levels_.size() << " levels, but " << levelManagers_.size() << " level factory managers");
548
549 const int startLevel = 0;
550 Clear(startLevel);
551
552#ifdef HAVE_MUELU_DEBUG
553 // Reset factories' data used for debugging
554 for (int i = 0; i < numLevels; i++)
555 levelManagers_[i]->ResetDebugData();
556
557#endif
558
559 int levelID;
560 for (levelID = startLevel; levelID < numLevels;) {
561 bool r = Setup(levelID,
562 (levelID != 0 ? levelManagers_[levelID - 1] : Teuchos::null),
563 levelManagers_[levelID],
564 (levelID + 1 != numLevels ? levelManagers_[levelID + 1] : Teuchos::null));
565 levelID++;
566 if (r) break;
567 }
568 // We may construct fewer levels for some reason, make sure we continue
569 // doing that in the future
570 Levels_.resize(levelID);
571 levelManagers_.resize(levelID);
572
573 int sizeOfVecs = sizeOfAllocatedLevelMultiVectors_;
574
575 AllocateLevelMultiVectors(sizeOfVecs, true);
576
577 // since the # of levels, etc. may have changed, force re-determination of description during next call to description()
578 ResetDescription();
579
580 describe(GetOStream(Statistics0), GetVerbLevel());
581
582 CheckForEmptySmoothersAndCoarseSolve();
583}
584
585template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
586void Hierarchy<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Setup(const FactoryManagerBase& manager, int startLevel, int numDesiredLevels) {
587 // Use MueLu::BaseClass::description() to avoid printing "{numLevels = 1}" (numLevels is increasing...)
588 PrintMonitor m0(*this, "Setup (" + this->MueLu::BaseClass::description() + ")", Runtime0);
589
590 Clear(startLevel);
591
592 // Check Levels_[startLevel] exists.
593 TEUCHOS_TEST_FOR_EXCEPTION(Levels_.size() <= startLevel, Exceptions::RuntimeError,
594 "MueLu::Hierarchy::Setup(): fine level (" << startLevel << ") does not exist");
595
596 TEUCHOS_TEST_FOR_EXCEPTION(numDesiredLevels <= 0, Exceptions::RuntimeError,
597 "Constructing non-positive (" << numDesiredLevels << ") number of levels does not make sense.");
598
599 // Check for fine level matrix A
600 TEUCHOS_TEST_FOR_EXCEPTION(!Levels_[startLevel]->IsAvailable("A"), Exceptions::RuntimeError,
601 "MueLu::Hierarchy::Setup(): fine level (" << startLevel << ") has no matrix A! "
602 "Set fine level matrix A using Level.Set()");
603
604 RCP<Operator> A = Levels_[startLevel]->template Get<RCP<Operator>>("A");
605 lib_ = A->getDomainMap()->lib();
606
607 if (IsPrint(Statistics2)) {
608 RCP<Matrix> Amat = rcp_dynamic_cast<Matrix>(A);
609
610 if (!Amat.is_null()) {
611 RCP<ParameterList> params = rcp(new ParameterList());
612 params->set("printLoadBalancingInfo", true);
613 params->set("printCommInfo", true);
614
615 GetOStream(Statistics2) << PerfUtils::PrintMatrixInfo(*Amat, "A0", params);
616 } else {
617 GetOStream(Warnings1) << "Fine level operator is not a matrix, statistics are not available" << std::endl;
618 }
619 }
620
621 RCP<const FactoryManagerBase> rcpmanager = rcpFromRef(manager);
622
623 const int lastLevel = startLevel + numDesiredLevels - 1;
624 GetOStream(Runtime0) << "Setup loop: startLevel = " << startLevel << ", lastLevel = " << lastLevel
625 << " (stop if numLevels = " << numDesiredLevels << " or Ac.size() < " << maxCoarseSize_ << ")" << std::endl;
626
627 // Setup multigrid levels
628 int iLevel = 0;
629 if (numDesiredLevels == 1) {
630 iLevel = 0;
631 Setup(startLevel, Teuchos::null, rcpmanager, Teuchos::null); // setup finest==coarsest level (first and last managers are Teuchos::null)
632
633 } else {
634 bool bIsLastLevel = Setup(startLevel, Teuchos::null, rcpmanager, rcpmanager); // setup finest level (level 0) (first manager is Teuchos::null)
635 if (bIsLastLevel == false) {
636 for (iLevel = startLevel + 1; iLevel < lastLevel; iLevel++) {
637 bIsLastLevel = Setup(iLevel, rcpmanager, rcpmanager, rcpmanager); // setup intermediate levels
638 if (bIsLastLevel == true)
639 break;
640 }
641 if (bIsLastLevel == false)
642 Setup(lastLevel, rcpmanager, rcpmanager, Teuchos::null); // setup coarsest level (last manager is Teuchos::null)
643 }
644 }
645
646 // TODO: some check like this should be done at the beginning of the routine
647 TEUCHOS_TEST_FOR_EXCEPTION(iLevel != Levels_.size() - 1, Exceptions::RuntimeError,
648 "MueLu::Hierarchy::Setup(): number of level");
649
650 // TODO: this is not exception safe: manager will still hold default
651 // factories if you exit this function with an exception
652 manager.Clean();
653
654 describe(GetOStream(Statistics0), GetVerbLevel());
655
656 CheckForEmptySmoothersAndCoarseSolve();
657}
658
659template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
661 for (LO levelNo = 0; levelNo < GetNumLevels(); ++levelNo) {
662 auto level = Levels_[levelNo];
663 if ((level->IsAvailable("A") && !level->template Get<RCP<Operator>>("A").is_null()) && (!level->IsAvailable("PreSmoother")) && (!level->IsAvailable("PostSmoother"))) {
664 GetOStream(Warnings1) << "No " << (levelNo == as<LO>(Levels_.size()) - 1 ? "coarse grid solver" : "smoother") << " on level " << level->GetLevelID() << std::endl;
665 }
666 }
667}
668
669template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
671 if (startLevel < GetNumLevels())
672 GetOStream(Runtime0) << "Clearing old data (if any)" << std::endl;
673
674 for (int iLevel = startLevel; iLevel < GetNumLevels(); iLevel++)
675 Levels_[iLevel]->Clear();
676}
677
678template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
680 GetOStream(Runtime0) << "Clearing old data (expert)" << std::endl;
681 for (int iLevel = 0; iLevel < GetNumLevels(); iLevel++)
682 Levels_[iLevel]->ExpertClear();
683}
684
685#if defined(HAVE_MUELU_EXPERIMENTAL) && defined(HAVE_MUELU_ADDITIVE_VARIANT)
686template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
687ConvergenceStatus Hierarchy<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Iterate(const MultiVector& B, MultiVector& X, ConvData conv,
688 bool InitialGuessIsZero, LO startLevel) {
689 LO nIts = conv.maxIts_;
690 MagnitudeType tol = conv.tol_;
691
692 std::string prefix = this->ShortClassName() + ": ";
693 std::string levelSuffix = " (level=" + toString(startLevel) + ")";
694 std::string levelSuffix1 = " (level=" + toString(startLevel + 1) + ")";
695
696 using namespace Teuchos;
697 RCP<Time> CompTime = Teuchos::TimeMonitor::getNewCounter(prefix + "Computational Time (total)");
698 RCP<Time> Concurrent = Teuchos::TimeMonitor::getNewCounter(prefix + "Concurrent portion");
699 RCP<Time> ApplyR = Teuchos::TimeMonitor::getNewCounter(prefix + "R: Computational Time");
700 RCP<Time> ApplyPbar = Teuchos::TimeMonitor::getNewCounter(prefix + "Pbar: Computational Time");
701 RCP<Time> CompFine = Teuchos::TimeMonitor::getNewCounter(prefix + "Fine: Computational Time");
702 RCP<Time> CompCoarse = Teuchos::TimeMonitor::getNewCounter(prefix + "Coarse: Computational Time");
703 RCP<Time> ApplySum = Teuchos::TimeMonitor::getNewCounter(prefix + "Sum: Computational Time");
704 RCP<Time> Synchronize_beginning = Teuchos::TimeMonitor::getNewCounter(prefix + "Synchronize_beginning");
705 RCP<Time> Synchronize_center = Teuchos::TimeMonitor::getNewCounter(prefix + "Synchronize_center");
706 RCP<Time> Synchronize_end = Teuchos::TimeMonitor::getNewCounter(prefix + "Synchronize_end");
707
708 RCP<Level> Fine = Levels_[0];
709 RCP<Level> Coarse;
710
711 RCP<Operator> A = Fine->Get<RCP<Operator>>("A");
712 Teuchos::RCP<const Teuchos::Comm<int>> communicator = A->getDomainMap()->getComm();
713
714 // Synchronize_beginning->start();
715 // communicator->barrier();
716 // Synchronize_beginning->stop();
717
718 CompTime->start();
719
720 SC one = STS::one(), zero = STS::zero();
721
722 bool zeroGuess = InitialGuessIsZero;
723
724 // ======= UPFRONT DEFINITION OF COARSE VARIABLES ===========
725
726 // RCP<const Map> origMap;
727 RCP<Operator> P;
728 RCP<Operator> Pbar;
729 RCP<Operator> R;
730 RCP<MultiVector> coarseRhs, coarseX;
731 RCP<Operator> Ac;
732 RCP<SmootherBase> preSmoo_coarse, postSmoo_coarse;
733 bool emptyCoarseSolve = true;
734 RCP<MultiVector> coarseX_prolonged = MultiVectorFactory::Build(X.getMap(), X.getNumVectors(), true);
735
736 RCP<const Import> importer;
737
738 if (Levels_.size() > 1) {
739 Coarse = Levels_[1];
740 if (Coarse->IsAvailable("Importer"))
741 importer = Coarse->Get<RCP<const Import>>("Importer");
742
743 R = Coarse->Get<RCP<Operator>>("R");
744 P = Coarse->Get<RCP<Operator>>("P");
745
746 // if(Coarse->IsAvailable("Pbar"))
747 Pbar = Coarse->Get<RCP<Operator>>("Pbar");
748
749 coarseRhs = MultiVectorFactory::Build(R->getRangeMap(), B.getNumVectors(), true);
750
751 Ac = Coarse->Get<RCP<Operator>>("A");
752
753 ApplyR->start();
754 R->apply(B, *coarseRhs, Teuchos::NO_TRANS, one, zero);
755 // P->apply(B, *coarseRhs, Teuchos::TRANS, one, zero);
756 ApplyR->stop();
757
758 if (doPRrebalance_ || importer.is_null()) {
759 coarseX = MultiVectorFactory::Build(coarseRhs->getMap(), X.getNumVectors(), true);
760
761 } else {
762 RCP<TimeMonitor> ITime = rcp(new TimeMonitor(*this, prefix + "Solve : import (total)", Timings0));
763 RCP<TimeMonitor> ILevelTime = rcp(new TimeMonitor(*this, prefix + "Solve : import" + levelSuffix1, Timings0));
764
765 // Import: range map of R --> domain map of rebalanced Ac (before subcomm replacement)
766 RCP<MultiVector> coarseTmp = MultiVectorFactory::Build(importer->getTargetMap(), coarseRhs->getNumVectors());
767 coarseTmp->doImport(*coarseRhs, *importer, Xpetra::INSERT);
768 coarseRhs.swap(coarseTmp);
769
770 coarseX = MultiVectorFactory::Build(importer->getTargetMap(), X.getNumVectors(), true);
771 }
772
773 if (Coarse->IsAvailable("PreSmoother"))
774 preSmoo_coarse = Coarse->Get<RCP<SmootherBase>>("PreSmoother");
775 if (Coarse->IsAvailable("PostSmoother"))
776 postSmoo_coarse = Coarse->Get<RCP<SmootherBase>>("PostSmoother");
777 }
778
779 // ==========================================================
780
781 MagnitudeType prevNorm = STS::magnitude(STS::one()), curNorm = STS::magnitude(STS::one());
782 rate_ = 1.0;
783
784 if (Behavior::debug()) {
785 if (A->getDomainMap()->isCompatible(*(X.getMap())) == false) {
786 std::ostringstream ss;
787 ss << "Level " << startLevel << ": level A's domain map is not compatible with X";
788 throw Exceptions::Incompatible(ss.str());
789 }
790
791 if (A->getRangeMap()->isCompatible(*(B.getMap())) == false) {
792 std::ostringstream ss;
793 ss << "Level " << startLevel << ": level A's range map is not compatible with B";
794 throw Exceptions::Incompatible(ss.str());
795 }
796 }
797
798 bool emptyFineSolve = true;
799
800 RCP<MultiVector> fineX;
801 fineX = MultiVectorFactory::Build(X.getMap(), X.getNumVectors(), true);
802
803 // Synchronize_center->start();
804 // communicator->barrier();
805 // Synchronize_center->stop();
806
807 Concurrent->start();
808
809 // NOTE: we need to check using IsAvailable before Get here to avoid building default smoother
810 if (Fine->IsAvailable("PreSmoother")) {
811 RCP<SmootherBase> preSmoo = Fine->Get<RCP<SmootherBase>>("PreSmoother");
812 CompFine->start();
813 preSmoo->Apply(*fineX, B, zeroGuess);
814 CompFine->stop();
815 emptyFineSolve = false;
816 }
817 if (Fine->IsAvailable("PostSmoother")) {
818 RCP<SmootherBase> postSmoo = Fine->Get<RCP<SmootherBase>>("PostSmoother");
819 CompFine->start();
820 postSmoo->Apply(*fineX, B, zeroGuess);
821 CompFine->stop();
822
823 emptyFineSolve = false;
824 }
825 if (emptyFineSolve == true) {
826 // Fine grid smoother is identity
827 fineX->update(one, B, zero);
828 }
829
830 if (Levels_.size() > 1) {
831 // NOTE: we need to check using IsAvailable before Get here to avoid building default smoother
832 if (Coarse->IsAvailable("PreSmoother")) {
833 CompCoarse->start();
834 preSmoo_coarse->Apply(*coarseX, *coarseRhs, zeroGuess);
835 CompCoarse->stop();
836 emptyCoarseSolve = false;
837 }
838 if (Coarse->IsAvailable("PostSmoother")) {
839 CompCoarse->start();
840 postSmoo_coarse->Apply(*coarseX, *coarseRhs, zeroGuess);
841 CompCoarse->stop();
842 emptyCoarseSolve = false;
843 }
844 if (emptyCoarseSolve == true) {
845 // Coarse operator is identity
846 coarseX->update(one, *coarseRhs, zero);
847 }
848 Concurrent->stop();
849 // Synchronize_end->start();
850 // communicator->barrier();
851 // Synchronize_end->stop();
852
853 if (!doPRrebalance_ && !importer.is_null()) {
854 RCP<TimeMonitor> ITime = rcp(new TimeMonitor(*this, prefix + "Solve : export (total)", Timings0));
855 RCP<TimeMonitor> ILevelTime = rcp(new TimeMonitor(*this, prefix + "Solve : export" + levelSuffix1, Timings0));
856
857 // Import: range map of rebalanced Ac (before subcomm replacement) --> domain map of P
858 RCP<MultiVector> coarseTmp = MultiVectorFactory::Build(importer->getSourceMap(), coarseX->getNumVectors());
859 coarseTmp->doExport(*coarseX, *importer, Xpetra::INSERT);
860 coarseX.swap(coarseTmp);
861 }
862
863 ApplyPbar->start();
864 Pbar->apply(*coarseX, *coarseX_prolonged, Teuchos::NO_TRANS, one, zero);
865 ApplyPbar->stop();
866 }
867
868 ApplySum->start();
869 X.update(1.0, *fineX, 1.0, *coarseX_prolonged, 0.0);
870 ApplySum->stop();
871
872 CompTime->stop();
873
874 // communicator->barrier();
875
877}
878#else
879// ---------------------------------------- Iterate -------------------------------------------------------
880template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
882 bool InitialGuessIsZero, LO startLevel) {
883 LO nIts = conv.maxIts_;
884 MagnitudeType tol = conv.tol_;
885
886 // These timers work as follows. "iterateTime" records total time spent in
887 // iterate. "levelTime" records time on a per level basis. The label is
888 // crafted to mimic the per-level messages used in Monitors. Note that a
889 // given level is timed with a TimeMonitor instead of a Monitor or
890 // SubMonitor. This is mainly because I want to time each level
891 // separately, and Monitors/SubMonitors print "(total) xx yy zz" ,
892 // "(sub,total) xx yy zz", respectively, which is subject to
893 // misinterpretation. The per-level TimeMonitors are stopped/started
894 // manually before/after a recursive call to Iterate. A side artifact to
895 // this approach is that the counts for intermediate level timers are twice
896 // the counts for the finest and coarsest levels.
897
898 RCP<Level> Fine = Levels_[startLevel];
899
900 std::string label = FormattingHelper::getColonLabel(Fine->getObjectLabel());
901 std::string prefix = label + this->ShortClassName() + ": ";
902 std::string levelSuffix = " (level=" + toString(startLevel) + ")";
903 std::string levelSuffix1 = " (level=" + toString(startLevel + 1) + ")";
904
905 bool useStackedTimer = !Teuchos::TimeMonitor::stackedTimerNameIsDefault();
906
907 RCP<Monitor> iterateTime;
908 RCP<TimeMonitor> iterateTime1;
909 if (startLevel == 0)
910 iterateTime = rcp(new Monitor(*this, "Solve", label, (nIts == 1) ? None : Runtime0, Timings0));
911 else if (!useStackedTimer)
912 iterateTime1 = rcp(new TimeMonitor(*this, prefix + "Solve (total, level=" + toString(startLevel) + ")", Timings0));
913
914 std::string iterateLevelTimeLabel = prefix + "Solve" + levelSuffix;
915 RCP<TimeMonitor> iterateLevelTime = rcp(new TimeMonitor(*this, iterateLevelTimeLabel, Timings0));
916
917 bool zeroGuess = InitialGuessIsZero;
918
919 RCP<Operator> A = Fine->Get<RCP<Operator>>("A");
920 using namespace Teuchos;
921 RCP<Time> CompCoarse = Teuchos::TimeMonitor::getNewCounter(prefix + "Coarse: Computational Time");
922
923 if (A.is_null()) {
924 // This processor does not have any data for this process on coarser
925 // levels. This can only happen when there are multiple processors and
926 // we use repartitioning.
928 }
929
930 // If we switched the number of vectors, we'd need to reallocate here.
931 // If the number of vectors is unchanged, this is a noop.
932 // NOTE: We need to check against B because the tests in AllocateLevelMultiVectors
933 // will fail on Stokhos Scalar types (due to the so-called 'hidden dimension')
934 const BlockedMultiVector* Bblocked = dynamic_cast<const BlockedMultiVector*>(&B);
935 if (residual_.size() > startLevel &&
936 ((Bblocked && !Bblocked->isSameSize(*residual_[startLevel])) ||
937 (!Bblocked && !residual_[startLevel]->isSameSize(B))))
938 DeleteLevelMultiVectors();
939 AllocateLevelMultiVectors(X.getNumVectors());
940
941 // Print residual information before iterating
942 typedef Teuchos::ScalarTraits<typename STS::magnitudeType> STM;
943 MagnitudeType prevNorm = STM::one();
944 rate_ = 1.0;
945 if (IsCalculationOfResidualRequired(startLevel, conv)) {
946 ConvergenceStatus convergenceStatus = ComputeResidualAndPrintHistory(*A, X, B, Teuchos::ScalarTraits<LO>::zero(), startLevel, conv, prevNorm);
947 if (convergenceStatus == MueLu::ConvergenceStatus::Converged)
948 return convergenceStatus;
949 }
950
951 SC one = STS::one(), zero = STS::zero();
952 for (LO iteration = 1; iteration <= nIts; iteration++) {
953#ifdef HAVE_MUELU_DEBUG
954#if 0 // TODO fix me
955 if (A->getDomainMap()->isCompatible(*(X.getMap())) == false) {
956 std::ostringstream ss;
957 ss << "Level " << startLevel << ": level A's domain map is not compatible with X";
958 throw Exceptions::Incompatible(ss.str());
959 }
960
961 if (A->getRangeMap()->isCompatible(*(B.getMap())) == false) {
962 std::ostringstream ss;
963 ss << "Level " << startLevel << ": level A's range map is not compatible with B";
964 throw Exceptions::Incompatible(ss.str());
965 }
966#endif
967#endif
968
969 if (startLevel == as<LO>(Levels_.size()) - 1) {
970 // On the coarsest level, we do either smoothing (if defined) or a direct solve.
971 RCP<TimeMonitor> CLevelTime = rcp(new TimeMonitor(*this, prefix + "Solve : coarse" + levelSuffix, Timings0));
972
973 bool emptySolve = true;
974
975 // NOTE: we need to check using IsAvailable before Get here to avoid building default smoother
976 if (Fine->IsAvailable("PreSmoother")) {
977 RCP<SmootherBase> preSmoo = Fine->Get<RCP<SmootherBase>>("PreSmoother");
978 CompCoarse->start();
979 preSmoo->Apply(X, B, zeroGuess);
980 CompCoarse->stop();
981 zeroGuess = false;
982 emptySolve = false;
983 }
984 if (Fine->IsAvailable("PostSmoother")) {
985 RCP<SmootherBase> postSmoo = Fine->Get<RCP<SmootherBase>>("PostSmoother");
986 CompCoarse->start();
987 postSmoo->Apply(X, B, zeroGuess);
988 CompCoarse->stop();
989 emptySolve = false;
990 zeroGuess = false;
991 }
992 if (emptySolve == true) {
993 // Coarse operator is identity
994 X.update(one, B, zero);
995 }
996
997 } else {
998 // On intermediate levels, we do cycles
999 RCP<Level> Coarse = Levels_[startLevel + 1];
1000 {
1001 // ============== PRESMOOTHING ==============
1002 RCP<TimeMonitor> STime;
1003 if (!useStackedTimer)
1004 STime = rcp(new TimeMonitor(*this, prefix + "Solve : smoothing (total)", Timings0));
1005 RCP<TimeMonitor> SLevelTime = rcp(new TimeMonitor(*this, prefix + "Solve : smoothing" + levelSuffix, Timings0));
1006
1007 if (Fine->IsAvailable("PreSmoother")) {
1008 RCP<SmootherBase> preSmoo = Fine->Get<RCP<SmootherBase>>("PreSmoother");
1009 preSmoo->Apply(X, B, zeroGuess);
1010 zeroGuess = false;
1011 }
1012 }
1013
1014 RCP<MultiVector> residual;
1015 {
1016 RCP<TimeMonitor> ATime;
1017 if (!useStackedTimer)
1018 ATime = rcp(new TimeMonitor(*this, prefix + "Solve : residual calculation (total)", Timings0));
1019 RCP<TimeMonitor> ALevelTime = rcp(new TimeMonitor(*this, prefix + "Solve : residual calculation" + levelSuffix, Timings0));
1020 if (zeroGuess) {
1021 // If there's a pre-smoother, then zeroGuess is false. If there isn't and people still have zeroGuess set,
1022 // then then X still has who knows what, so we need to zero it before we go to the coarse grid.
1023 X.putScalar(zero);
1024 }
1025
1026 Utilities::Residual(*A, X, B, *residual_[startLevel]);
1027 residual = residual_[startLevel];
1028 }
1029
1030 RCP<Operator> P = Coarse->Get<RCP<Operator>>("P");
1031 if (Coarse->IsAvailable("Pbar"))
1032 P = Coarse->Get<RCP<Operator>>("Pbar");
1033
1034 RCP<MultiVector> coarseRhs, coarseX;
1035 // const bool initializeWithZeros = true;
1036 {
1037 // ============== RESTRICTION ==============
1038 RCP<TimeMonitor> RTime;
1039 if (!useStackedTimer)
1040 RTime = rcp(new TimeMonitor(*this, prefix + "Solve : restriction (total)", Timings0));
1041 RCP<TimeMonitor> RLevelTime = rcp(new TimeMonitor(*this, prefix + "Solve : restriction" + levelSuffix, Timings0));
1042 coarseRhs = coarseRhs_[startLevel];
1043
1044 if (implicitTranspose_) {
1045 P->apply(*residual, *coarseRhs, Teuchos::TRANS, one, zero);
1046
1047 } else {
1048 RCP<Operator> R = Coarse->Get<RCP<Operator>>("R");
1049 R->apply(*residual, *coarseRhs, Teuchos::NO_TRANS, one, zero);
1050 }
1051 }
1052
1053 RCP<const Import> importer;
1054 if (Coarse->IsAvailable("Importer"))
1055 importer = Coarse->Get<RCP<const Import>>("Importer");
1056
1057 coarseX = coarseX_[startLevel];
1058 if (!doPRrebalance_ && !importer.is_null()) {
1059 RCP<TimeMonitor> ITime;
1060 if (!useStackedTimer)
1061 ITime = rcp(new TimeMonitor(*this, prefix + "Solve : import (total)", Timings0));
1062 RCP<TimeMonitor> ILevelTime = rcp(new TimeMonitor(*this, prefix + "Solve : import" + levelSuffix1, Timings0));
1063
1064 // Import: range map of R --> domain map of rebalanced Ac (before subcomm replacement)
1065 RCP<MultiVector> coarseTmp = coarseImport_[startLevel];
1066 coarseTmp->doImport(*coarseRhs, *importer, Xpetra::INSERT);
1067 coarseRhs.swap(coarseTmp);
1068 }
1069
1070 RCP<Operator> Ac = Coarse->Get<RCP<Operator>>("A");
1071 if (!Ac.is_null()) {
1072 RCP<const Map> origXMap = coarseX->getMap();
1073 RCP<const Map> origRhsMap = coarseRhs->getMap();
1074
1075 // Replace maps with maps with a subcommunicator
1076 coarseRhs->replaceMap(Ac->getRangeMap());
1077 coarseX->replaceMap(Ac->getDomainMap());
1078
1079 {
1080 iterateLevelTime = Teuchos::null; // stop timing this level
1081
1082 Iterate(*coarseRhs, *coarseX, 1, true, startLevel + 1);
1083 // ^^ zero initial guess
1084 if (Cycle_ == WCYCLE && WCycleStartLevel_ <= startLevel)
1085 Iterate(*coarseRhs, *coarseX, 1, false, startLevel + 1);
1086 // ^^ nonzero initial guess
1087
1088 iterateLevelTime = rcp(new TimeMonitor(*this, iterateLevelTimeLabel)); // restart timing this level
1089 }
1090 coarseX->replaceMap(origXMap);
1091 coarseRhs->replaceMap(origRhsMap);
1092 }
1093
1094 if (!doPRrebalance_ && !importer.is_null()) {
1095 RCP<TimeMonitor> ITime;
1096 if (!useStackedTimer)
1097 ITime = rcp(new TimeMonitor(*this, prefix + "Solve : export (total)", Timings0));
1098 RCP<TimeMonitor> ILevelTime = rcp(new TimeMonitor(*this, prefix + "Solve : export" + levelSuffix1, Timings0));
1099
1100 // Import: range map of rebalanced Ac (before subcomm replacement) --> domain map of P
1101 RCP<MultiVector> coarseTmp = coarseExport_[startLevel];
1102 coarseTmp->doExport(*coarseX, *importer, Xpetra::INSERT);
1103 coarseX.swap(coarseTmp);
1104 }
1105
1106 {
1107 // ============== PROLONGATION ==============
1108 RCP<TimeMonitor> PTime;
1109 if (!useStackedTimer)
1110 PTime = rcp(new TimeMonitor(*this, prefix + "Solve : prolongation (total)", Timings0));
1111 RCP<TimeMonitor> PLevelTime = rcp(new TimeMonitor(*this, prefix + "Solve : prolongation" + levelSuffix, Timings0));
1112 // Update X += P * coarseX
1113 // Note that due to what may be round-off error accumulation, use of the fused kernel
1114 // P->apply(*coarseX, X, Teuchos::NO_TRANS, one, one);
1115 // can in some cases result in slightly higher iteration counts.
1116 if (fuseProlongationAndUpdate_) {
1117 P->apply(*coarseX, X, Teuchos::NO_TRANS, scalingFactor_, one);
1118 } else {
1119 RCP<MultiVector> correction = correction_[startLevel];
1120 P->apply(*coarseX, *correction, Teuchos::NO_TRANS, one, zero);
1121 X.update(scalingFactor_, *correction, one);
1122 }
1123 }
1124
1125 {
1126 // ============== POSTSMOOTHING ==============
1127 RCP<TimeMonitor> STime;
1128 if (!useStackedTimer)
1129 STime = rcp(new TimeMonitor(*this, prefix + "Solve : smoothing (total)", Timings0));
1130 RCP<TimeMonitor> SLevelTime = rcp(new TimeMonitor(*this, prefix + "Solve : smoothing" + levelSuffix, Timings0));
1131
1132 if (Fine->IsAvailable("PostSmoother")) {
1133 RCP<SmootherBase> postSmoo = Fine->Get<RCP<SmootherBase>>("PostSmoother");
1134 postSmoo->Apply(X, B, false);
1135 }
1136 }
1137 }
1138 zeroGuess = false;
1139
1140 if (IsCalculationOfResidualRequired(startLevel, conv)) {
1141 ConvergenceStatus convergenceStatus = ComputeResidualAndPrintHistory(*A, X, B, iteration, startLevel, conv, prevNorm);
1142 if (convergenceStatus == MueLu::ConvergenceStatus::Converged)
1143 return convergenceStatus;
1144 }
1145 }
1147}
1148#endif
1149
1150template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1151void Hierarchy<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Write(const LO& start, const LO& end, const std::string& suffix) {
1152 LO startLevel = (start != -1 ? start : 0);
1153 LO endLevel = (end != -1 ? end : Levels_.size() - 1);
1154
1155 TEUCHOS_TEST_FOR_EXCEPTION(startLevel > endLevel, Exceptions::RuntimeError,
1156 "MueLu::Hierarchy::Write : startLevel must be <= endLevel");
1157
1158 TEUCHOS_TEST_FOR_EXCEPTION(startLevel < 0 || endLevel >= Levels_.size(), Exceptions::RuntimeError,
1159 "MueLu::Hierarchy::Write bad start or end level");
1160
1161 for (LO i = startLevel; i < endLevel + 1; i++) {
1162 RCP<Matrix> A = rcp_dynamic_cast<Matrix>(Levels_[i]->template Get<RCP<Operator>>("A")), P, R;
1163 if (i > 0) {
1164 P = rcp_dynamic_cast<Matrix>(Levels_[i]->template Get<RCP<Operator>>("P"));
1165 if (!implicitTranspose_)
1166 R = rcp_dynamic_cast<Matrix>(Levels_[i]->template Get<RCP<Operator>>("R"));
1167 }
1168
1169 if (!A.is_null()) Xpetra::IO<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Write("A_" + toString(i) + suffix + ".m", *A);
1170 if (!P.is_null()) {
1171 Xpetra::IO<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Write("P_" + toString(i) + suffix + ".m", *P);
1172 }
1173 if (!R.is_null()) {
1174 Xpetra::IO<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Write("R_" + toString(i) + suffix + ".m", *R);
1175 }
1176 }
1177}
1178
1179template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1180void Hierarchy<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Keep(const std::string& ename, const FactoryBase* factory) {
1181 for (Array<RCP<Level>>::iterator it = Levels_.begin(); it != Levels_.end(); ++it)
1182 (*it)->Keep(ename, factory);
1183}
1184
1185template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1187 for (Array<RCP<Level>>::iterator it = Levels_.begin(); it != Levels_.end(); ++it)
1188 (*it)->Delete(ename, factory);
1189}
1190
1191template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1193 for (Array<RCP<Level>>::iterator it = Levels_.begin(); it != Levels_.end(); ++it)
1194 (*it)->AddKeepFlag(ename, factory, keep);
1195}
1196
1197template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1199 for (Array<RCP<Level>>::iterator it = Levels_.begin(); it != Levels_.end(); ++it)
1200 (*it)->RemoveKeepFlag(ename, factory, keep);
1201}
1202
1203template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1205 if (description_.empty()) {
1206 std::ostringstream out;
1207 out << BaseClass::description();
1208 out << "{#levels = " << GetGlobalNumLevels() << ", complexity = " << GetOperatorComplexity() << "}";
1209 description_ = out.str();
1210 }
1211 return description_;
1212}
1213
1214template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1215void Hierarchy<Scalar, LocalOrdinal, GlobalOrdinal, Node>::describe(Teuchos::FancyOStream& out, const Teuchos::EVerbosityLevel tVerbLevel) const {
1216 describe(out, toMueLuVerbLevel(tVerbLevel));
1217}
1218
1219template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1220void Hierarchy<Scalar, LocalOrdinal, GlobalOrdinal, Node>::describe(Teuchos::FancyOStream& out, const VerbLevel verbLevel) const {
1221 RCP<Operator> A0 = Levels_[0]->template Get<RCP<Operator>>("A");
1222 RCP<const Teuchos::Comm<int>> comm = A0->getDomainMap()->getComm();
1223
1224 int numLevels = GetNumLevels();
1225 RCP<Operator> Ac = Levels_[numLevels - 1]->template Get<RCP<Operator>>("A");
1226 if (Ac.is_null()) {
1227 // It may happen that we do repartition on the last level, but the matrix
1228 // is small enough to satisfy "max coarse size" requirement. Then, even
1229 // though we have the level, the matrix would be null on all but one processors
1230 numLevels--;
1231 }
1232 int root = comm->getRank();
1233
1234#ifdef HAVE_MPI
1235 int smartData = numLevels * comm->getSize() + comm->getRank(), maxSmartData;
1236 reduceAll(*comm, Teuchos::REDUCE_MAX, smartData, Teuchos::ptr(&maxSmartData));
1237 root = maxSmartData % comm->getSize();
1238#endif
1239
1240 // Compute smoother complexity, if needed
1241 double smoother_comp = -1.0;
1242 if (verbLevel & (Statistics0 | Test))
1243 smoother_comp = GetSmootherComplexity();
1244
1245 std::string outstr;
1246 if (comm->getRank() == root && verbLevel & (Statistics0 | Test)) {
1247 std::vector<Xpetra::global_size_t> nnzPerLevel;
1248 std::vector<Xpetra::global_size_t> rowsPerLevel;
1249 std::vector<int> numProcsPerLevel;
1250 bool someOpsNotMatrices = false;
1251 const Xpetra::global_size_t OPERATOR = Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid();
1252 const Xpetra::global_size_t UNAVAILABLE = Teuchos::OrdinalTraits<Xpetra::global_size_t>::max();
1253 for (int i = 0; i < numLevels; i++) {
1254 TEUCHOS_TEST_FOR_EXCEPTION(!(Levels_[i]->IsAvailable("A")), Exceptions::RuntimeError,
1255 "Operator A is not available on level " << i);
1256
1257 RCP<Operator> A = Levels_[i]->template Get<RCP<Operator>>("A");
1258 TEUCHOS_TEST_FOR_EXCEPTION(A.is_null(), Exceptions::RuntimeError,
1259 "Operator A on level " << i << " is null.");
1260
1261 RCP<Matrix> Am = rcp_dynamic_cast<Matrix>(A);
1262 if (Am.is_null()) {
1263 someOpsNotMatrices = true;
1264 nnzPerLevel.push_back(OPERATOR);
1265 rowsPerLevel.push_back(A->getDomainMap()->getGlobalNumElements());
1266 numProcsPerLevel.push_back(A->getDomainMap()->getComm()->getSize());
1267 } else {
1268 LO storageblocksize = Am->GetStorageBlockSize();
1269 if (Am->haveGlobalConstants()) {
1270 Xpetra::global_size_t nnz = Am->getGlobalNumEntries() * storageblocksize * storageblocksize;
1271 nnzPerLevel.push_back(nnz);
1272 } else
1273 nnzPerLevel.push_back(UNAVAILABLE);
1274 rowsPerLevel.push_back(Am->getGlobalNumRows() * storageblocksize);
1275 numProcsPerLevel.push_back(Am->getRowMap()->getComm()->getSize());
1276 }
1277 }
1278 if (someOpsNotMatrices)
1279 GetOStream(Warnings0) << "Some level operators are not matrices, statistics calculation are incomplete" << std::endl;
1280
1281 {
1282 std::string label = Levels_[0]->getObjectLabel();
1283 std::ostringstream oss;
1284 oss << std::setfill(' ');
1285 oss << "\n--------------------------------------------------------------------------------\n";
1286 oss << "--- Multigrid Summary " << std::setw(32) << "---\n";
1287 oss << "--------------------------------------------------------------------------------" << std::endl;
1288 if (hierarchyLabel_ != "") oss << "Label = " << hierarchyLabel_ << std::endl;
1289 if (verbLevel & Parameters1)
1290 oss << "Scalar = " << Teuchos::ScalarTraits<Scalar>::name() << std::endl;
1291 oss << "Number of levels = " << numLevels << std::endl;
1292 oss << "Operator complexity = " << std::setprecision(2) << std::setiosflags(std::ios::fixed);
1293 if (!someOpsNotMatrices)
1294 oss << GetOperatorComplexity() << std::endl;
1295 else
1296 oss << "not available (Some operators in hierarchy are not matrices.)" << std::endl;
1297
1298 if (smoother_comp != -1.0) {
1299 oss << "Smoother complexity = " << std::setprecision(2) << std::setiosflags(std::ios::fixed)
1300 << smoother_comp << std::endl;
1301 }
1302
1303 switch (Cycle_) {
1304 case VCYCLE:
1305 oss << "Cycle type = V" << std::endl;
1306 break;
1307 case WCYCLE:
1308 oss << "Cycle type = W" << std::endl;
1309 if (WCycleStartLevel_ > 0)
1310 oss << "Cycle start level = " << WCycleStartLevel_ << std::endl;
1311 break;
1312 default:
1313 break;
1314 };
1315 oss << std::endl;
1316
1317 Xpetra::global_size_t tt = rowsPerLevel[0];
1318 int rowspacer = 2;
1319 while (tt != 0) {
1320 tt /= 10;
1321 rowspacer++;
1322 }
1323 for (size_t i = 0; i < nnzPerLevel.size(); ++i) {
1324 tt = nnzPerLevel[i];
1325 if ((tt != OPERATOR) && (tt != UNAVAILABLE))
1326 break;
1327 tt = 100; // This will get used if all levels are operators.
1328 }
1329 int nnzspacer = 2;
1330 while (tt != 0) {
1331 tt /= 10;
1332 nnzspacer++;
1333 }
1334 tt = numProcsPerLevel[0];
1335 int npspacer = 2;
1336 while (tt != 0) {
1337 tt /= 10;
1338 npspacer++;
1339 }
1340 oss << "level " << std::setw(rowspacer) << " rows " << std::setw(nnzspacer) << " nnz "
1341 << " nnz/row" << std::setw(npspacer) << " c ratio"
1342 << " procs" << std::endl;
1343 for (size_t i = 0; i < nnzPerLevel.size(); ++i) {
1344 oss << " " << i << " ";
1345 oss << std::setw(rowspacer) << rowsPerLevel[i];
1346 if ((nnzPerLevel[i] != OPERATOR) && (nnzPerLevel[i] != UNAVAILABLE)) {
1347 oss << std::setw(nnzspacer) << nnzPerLevel[i];
1348 oss << std::setprecision(2) << std::setiosflags(std::ios::fixed);
1349 oss << std::setw(9) << as<double>(nnzPerLevel[i]) / rowsPerLevel[i];
1350 } else {
1351 if (nnzPerLevel[i] == OPERATOR)
1352 oss << std::setw(nnzspacer) << "Operator";
1353 else
1354 oss << std::setw(nnzspacer) << "N/A";
1355 oss << std::setprecision(2) << std::setiosflags(std::ios::fixed);
1356 oss << std::setw(9) << " ";
1357 }
1358 if (i)
1359 oss << std::setw(9) << as<double>(rowsPerLevel[i - 1]) / rowsPerLevel[i];
1360 else
1361 oss << std::setw(9) << " ";
1362 oss << " " << std::setw(npspacer) << numProcsPerLevel[i] << std::endl;
1363 }
1364 oss << std::endl;
1365 for (int i = 0; i < GetNumLevels(); ++i) {
1366 RCP<SmootherBase> preSmoo, postSmoo;
1367 if (Levels_[i]->IsAvailable("PreSmoother"))
1368 preSmoo = Levels_[i]->template Get<RCP<SmootherBase>>("PreSmoother");
1369 if (Levels_[i]->IsAvailable("PostSmoother"))
1370 postSmoo = Levels_[i]->template Get<RCP<SmootherBase>>("PostSmoother");
1371
1372 if (preSmoo != null && preSmoo == postSmoo)
1373 oss << "Smoother (level " << i << ") both : " << preSmoo->description() << std::endl;
1374 else {
1375 oss << "Smoother (level " << i << ") pre : "
1376 << (preSmoo != null ? preSmoo->description() : "no smoother") << std::endl;
1377 oss << "Smoother (level " << i << ") post : "
1378 << (postSmoo != null ? postSmoo->description() : "no smoother") << std::endl;
1379 }
1380
1381 oss << std::endl;
1382 }
1383
1384 outstr = oss.str();
1385 }
1386 }
1387
1388#ifdef HAVE_MPI
1389 RCP<const Teuchos::MpiComm<int>> mpiComm = rcp_dynamic_cast<const Teuchos::MpiComm<int>>(comm);
1390 MPI_Comm rawComm = (*mpiComm->getRawMpiComm())();
1391
1392 int strLength = outstr.size();
1393 MPI_Bcast(&strLength, 1, MPI_INT, root, rawComm);
1394 if (comm->getRank() != root)
1395 outstr.resize(strLength);
1396 MPI_Bcast(&outstr[0], strLength, MPI_CHAR, root, rawComm);
1397#endif
1398
1399 out << outstr;
1400}
1401
1402// NOTE: at some point this should be replaced by a friend operator <<
1403template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1404void Hierarchy<Scalar, LocalOrdinal, GlobalOrdinal, Node>::print(std::ostream& out, const VerbLevel verbLevel) const {
1405 Teuchos::OSTab tab2(out);
1406 for (int i = 0; i < GetNumLevels(); ++i)
1407 Levels_[i]->print(out, verbLevel);
1408}
1409
1410template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1412 isPreconditioner_ = flag;
1413}
1414
1415template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1417 if (GetProcRankVerbose() != 0)
1418 return;
1419#if defined(HAVE_MUELU_BOOST) && defined(HAVE_MUELU_BOOST_FOR_REAL) && defined(BOOST_VERSION) && (BOOST_VERSION >= 104400)
1420
1421 BoostGraph graph;
1422
1423 BoostProperties dp;
1424 dp.property("label", boost::get(boost::vertex_name, graph));
1425 dp.property("id", boost::get(boost::vertex_index, graph));
1426 dp.property("label", boost::get(boost::edge_name, graph));
1427 dp.property("color", boost::get(boost::edge_color, graph));
1428
1429 // create local maps
1430 std::map<const FactoryBase*, BoostVertex> vindices;
1431 typedef std::map<std::pair<BoostVertex, BoostVertex>, std::string> emap;
1432 emap edges;
1433
1434 static int call_id = 0;
1435
1436 RCP<Operator> A = Levels_[0]->template Get<RCP<Operator>>("A");
1437 int rank = A->getDomainMap()->getComm()->getRank();
1438
1439 // printf("[%d] CMS: ----------------------\n",rank);
1440 for (int i = currLevel; i <= currLevel + 1 && i < GetNumLevels(); i++) {
1441 edges.clear();
1442 Levels_[i]->UpdateGraph(vindices, edges, dp, graph);
1443
1444 for (emap::const_iterator eit = edges.begin(); eit != edges.end(); eit++) {
1445 std::pair<BoostEdge, bool> boost_edge = boost::add_edge(eit->first.first, eit->first.second, graph);
1446 // printf("[%d] CMS: Hierarchy, adding edge (%d->%d) %d\n",rank,(int)eit->first.first,(int)eit->first.second,(int)boost_edge.second);
1447 // Because xdot.py views 'Graph' as a keyword
1448 if (eit->second == std::string("Graph"))
1449 boost::put("label", dp, boost_edge.first, std::string("Graph_"));
1450 else
1451 boost::put("label", dp, boost_edge.first, eit->second);
1452 if (i == currLevel)
1453 boost::put("color", dp, boost_edge.first, std::string("red"));
1454 else
1455 boost::put("color", dp, boost_edge.first, std::string("blue"));
1456 }
1457 }
1458
1459 std::ofstream out(dumpFile_.c_str() + std::string("_") + std::to_string(currLevel) + std::string("_") + std::to_string(call_id) + std::string("_") + std::to_string(rank) + std::string(".dot"));
1460 boost::write_graphviz_dp(out, graph, dp, std::string("id"));
1461 out.close();
1462 call_id++;
1463#else
1464 GetOStream(Errors) << "Dependency graph output requires boost and MueLu_ENABLE_Boost_for_real" << std::endl;
1465#endif
1466}
1467
1468// Enforce that coordinate vector's map is consistent with that of A
1469template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1471 RCP<Operator> Ao = level.Get<RCP<Operator>>("A");
1472 RCP<Matrix> A = rcp_dynamic_cast<Matrix>(Ao);
1473 if (A.is_null()) {
1474 GetOStream(Runtime1) << "Hierarchy::ReplaceCoordinateMap: operator is not a matrix, skipping..." << std::endl;
1475 return;
1476 }
1477 if (Teuchos::rcp_dynamic_cast<BlockedCrsMatrix>(A) != Teuchos::null) {
1478 GetOStream(Runtime1) << "Hierarchy::ReplaceCoordinateMap: operator is a BlockedCrsMatrix, skipping..." << std::endl;
1479 return;
1480 }
1481
1482 typedef Xpetra::MultiVector<typename Teuchos::ScalarTraits<Scalar>::coordinateType, LO, GO, NO> xdMV;
1483
1484 RCP<xdMV> coords = level.Get<RCP<xdMV>>("Coordinates");
1485
1486 if (A->getRowMap()->isSameAs(*(coords->getMap()))) {
1487 GetOStream(Runtime1) << "Hierarchy::ReplaceCoordinateMap: matrix and coordinates maps are same, skipping..." << std::endl;
1488 return;
1489 }
1490
1491 if (A->IsView("stridedMaps") && rcp_dynamic_cast<const StridedMap>(A->getRowMap("stridedMaps")) != Teuchos::null) {
1492 RCP<const StridedMap> stridedRowMap = rcp_dynamic_cast<const StridedMap>(A->getRowMap("stridedMaps"));
1493
1494 // It is better to through an exceptions if maps may be inconsistent, than to ignore it and experience unfathomable breakdowns
1495 TEUCHOS_TEST_FOR_EXCEPTION(stridedRowMap->getStridedBlockId() != -1 || stridedRowMap->getOffset() != 0,
1496 Exceptions::RuntimeError, "Hierarchy::ReplaceCoordinateMap: nontrivial maps (block id = " << stridedRowMap->getStridedBlockId() << ", offset = " << stridedRowMap->getOffset() << ")");
1497 }
1498
1499 GetOStream(Runtime1) << "Replacing coordinate map" << std::endl;
1500 TEUCHOS_TEST_FOR_EXCEPTION(A->GetFixedBlockSize() % A->GetStorageBlockSize() != 0, Exceptions::RuntimeError, "Hierarchy::ReplaceCoordinateMap: Storage block size does not evenly divide fixed block size");
1501
1502 size_t blkSize = A->GetFixedBlockSize() / A->GetStorageBlockSize();
1503
1504 RCP<const Map> nodeMap = A->getRowMap();
1505 if (blkSize > 1) {
1506 // Create a nodal map, as coordinates have not been expanded to a DOF map yet.
1507 RCP<const Map> dofMap = A->getRowMap();
1508 GO indexBase = dofMap->getIndexBase();
1509 size_t numLocalDOFs = dofMap->getLocalNumElements();
1510 TEUCHOS_TEST_FOR_EXCEPTION(numLocalDOFs % blkSize, Exceptions::RuntimeError,
1511 "Hierarchy::ReplaceCoordinateMap: block size (" << blkSize << ") is incompatible with the number of local dofs in a row map (" << numLocalDOFs);
1512 ArrayView<const GO> GIDs = dofMap->getLocalElementList();
1513
1514 Array<GO> nodeGIDs(numLocalDOFs / blkSize);
1515 for (size_t i = 0; i < numLocalDOFs; i += blkSize)
1516 nodeGIDs[i / blkSize] = (GIDs[i] - indexBase) / blkSize + indexBase;
1517
1518 Xpetra::global_size_t INVALID = Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid();
1519 nodeMap = MapFactory::Build(dofMap->lib(), INVALID, nodeGIDs(), indexBase, dofMap->getComm());
1520 } else {
1521 // blkSize == 1
1522 // Check whether the length of vectors fits to the size of A
1523 // If yes, make sure that the maps are matching
1524 // If no, throw a warning but do not touch the Coordinates
1525 if (coords->getLocalLength() != A->getRowMap()->getLocalNumElements()) {
1526 GetOStream(Warnings) << "Coordinate vector does not match row map of matrix A!" << std::endl;
1527 return;
1528 }
1529 }
1530
1531 Array<ArrayView<const typename Teuchos::ScalarTraits<Scalar>::coordinateType>> coordDataView;
1532 std::vector<ArrayRCP<const typename Teuchos::ScalarTraits<Scalar>::coordinateType>> coordData;
1533 for (size_t i = 0; i < coords->getNumVectors(); i++) {
1534 coordData.push_back(coords->getData(i));
1535 coordDataView.push_back(coordData[i]());
1536 }
1537
1538 RCP<xdMV> newCoords = Xpetra::MultiVectorFactory<typename Teuchos::ScalarTraits<Scalar>::coordinateType, LO, GO, NO>::Build(nodeMap, coordDataView(), coords->getNumVectors());
1539 level.Set("Coordinates", newCoords);
1540}
1541
1542template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1544 int N = Levels_.size();
1545 if (((sizeOfAllocatedLevelMultiVectors_ == numvecs && residual_.size() == N) || numvecs <= 0) && !forceMapCheck) return;
1546
1547 // If, somehow, we changed the number of levels, delete everything first
1548 if (residual_.size() != N) {
1549 DeleteLevelMultiVectors();
1550
1551 residual_.resize(N);
1552 coarseRhs_.resize(N);
1553 coarseX_.resize(N);
1554 coarseImport_.resize(N);
1555 coarseExport_.resize(N);
1556 correction_.resize(N);
1557 }
1558
1559 for (int i = 0; i < N; i++) {
1560 RCP<Operator> A = Levels_[i]->template Get<RCP<Operator>>("A");
1561 if (!A.is_null()) {
1562 // This dance is because we allow A to have a BlockedMap and X/B to have (compatible) non-blocked map
1563 RCP<const BlockedCrsMatrix> A_as_blocked = Teuchos::rcp_dynamic_cast<const BlockedCrsMatrix>(A);
1564 RCP<const Map> Arm = A->getRangeMap();
1565 RCP<const Map> Adm = A->getDomainMap();
1566 if (!A_as_blocked.is_null()) {
1567 Adm = A_as_blocked->getFullDomainMap();
1568 }
1569
1570 if (residual_[i].is_null() || !residual_[i]->getMap()->isSameAs(*Arm))
1571 // This is zero'd by default since it is filled via an operator apply
1572 residual_[i] = MultiVectorFactory::Build(Arm, numvecs, true);
1573 if (correction_[i].is_null() || !correction_[i]->getMap()->isSameAs(*Adm))
1574 correction_[i] = MultiVectorFactory::Build(Adm, numvecs, false);
1575 }
1576
1577 if (i + 1 < N) {
1578 // This is zero'd by default since it is filled via an operator apply
1579 if (implicitTranspose_) {
1580 RCP<Operator> P = Levels_[i + 1]->template Get<RCP<Operator>>("P");
1581 if (!P.is_null()) {
1582 RCP<const Map> map = P->getDomainMap();
1583 if (coarseRhs_[i].is_null() || !coarseRhs_[i]->getMap()->isSameAs(*map))
1584 coarseRhs_[i] = MultiVectorFactory::Build(map, numvecs, true);
1585 }
1586 } else {
1587 RCP<Operator> R = Levels_[i + 1]->template Get<RCP<Operator>>("R");
1588 if (!R.is_null()) {
1589 RCP<const Map> map = R->getRangeMap();
1590 if (coarseRhs_[i].is_null() || !coarseRhs_[i]->getMap()->isSameAs(*map))
1591 coarseRhs_[i] = MultiVectorFactory::Build(map, numvecs, true);
1592 }
1593 }
1594
1595 RCP<const Import> importer;
1596 if (Levels_[i + 1]->IsAvailable("Importer"))
1597 importer = Levels_[i + 1]->template Get<RCP<const Import>>("Importer");
1598 if (doPRrebalance_ || importer.is_null()) {
1599 RCP<const Map> map = coarseRhs_[i]->getMap();
1600 if (coarseX_[i].is_null() || !coarseX_[i]->getMap()->isSameAs(*map))
1601 coarseX_[i] = MultiVectorFactory::Build(map, numvecs, true);
1602 } else {
1603 RCP<const Map> map;
1604 map = importer->getTargetMap();
1605 if (coarseImport_[i].is_null() || !coarseImport_[i]->getMap()->isSameAs(*map)) {
1606 coarseImport_[i] = MultiVectorFactory::Build(map, numvecs, false);
1607 coarseX_[i] = MultiVectorFactory::Build(map, numvecs, false);
1608 }
1609 map = importer->getSourceMap();
1610 if (coarseExport_[i].is_null() || !coarseExport_[i]->getMap()->isSameAs(*map))
1611 coarseExport_[i] = MultiVectorFactory::Build(map, numvecs, false);
1612 }
1613 }
1614 }
1615 sizeOfAllocatedLevelMultiVectors_ = numvecs;
1616}
1617
1618template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1620 if (sizeOfAllocatedLevelMultiVectors_ == 0) return;
1621 residual_.resize(0);
1622 coarseRhs_.resize(0);
1623 coarseX_.resize(0);
1624 coarseImport_.resize(0);
1625 coarseExport_.resize(0);
1626 correction_.resize(0);
1627 sizeOfAllocatedLevelMultiVectors_ = 0;
1628}
1629
1630template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1632 const LO startLevel, const ConvData& conv) const {
1633 return (startLevel == 0 && !isPreconditioner_ && (IsPrint(Statistics1) || conv.tol_ > 0));
1634}
1635
1636template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1638 const Teuchos::Array<MagnitudeType>& residualNorm, const MagnitudeType convergenceTolerance) const {
1640
1641 if (convergenceTolerance > Teuchos::ScalarTraits<MagnitudeType>::zero()) {
1642 bool passed = true;
1643 for (LO k = 0; k < residualNorm.size(); k++)
1644 if (residualNorm[k] >= convergenceTolerance)
1645 passed = false;
1646
1647 if (passed)
1648 convergenceStatus = ConvergenceStatus::Converged;
1649 else
1650 convergenceStatus = ConvergenceStatus::Unconverged;
1651 }
1652
1653 return convergenceStatus;
1654}
1655
1656template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1658 const LO iteration, const Teuchos::Array<MagnitudeType>& residualNorm) const {
1659 GetOStream(Statistics1) << "iter: "
1660 << std::setiosflags(std::ios::left)
1661 << std::setprecision(3) << std::setw(4) << iteration
1662 << " residual = "
1663 << std::setprecision(10) << residualNorm
1664 << std::endl;
1665}
1666
1667template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1669 const Operator& A, const MultiVector& X, const MultiVector& B, const LO iteration,
1670 const LO startLevel, const ConvData& conv, MagnitudeType& previousResidualNorm) {
1671 Teuchos::Array<MagnitudeType> residualNorm;
1672 residualNorm = Utilities::ResidualNorm(A, X, B, *residual_[startLevel]);
1673
1674 const MagnitudeType currentResidualNorm = residualNorm[0];
1675 rate_ = currentResidualNorm / previousResidualNorm;
1676 previousResidualNorm = currentResidualNorm;
1677
1678 if (IsPrint(Statistics1))
1679 PrintResidualHistory(iteration, residualNorm);
1680
1681 return IsConverged(residualNorm, conv.tol_);
1682}
1683
1684} // namespace MueLu
1685
1686#endif // MUELU_HIERARCHY_DEF_HPP
static bool debug()
Whether MueLu is in debug mode.
virtual std::string description() const
Return a simple one-line description of this object.
Exception throws to report incompatible objects (like maps).
Exception throws to report errors in the internal logical of the program.
Base class for factories (e.g., R, P, and A_coarse).
Class that provides default factories within Needs class.
Provides methods to build a multigrid hierarchy and apply multigrid cycles.
void AddLevel(const RCP< Level > &level)
Add a level at the end of the hierarchy.
double GetSmootherComplexity() const
void Write(const LO &start=-1, const LO &end=-1, const std::string &suffix="")
Print matrices in the multigrid hierarchy to file.
RCP< Level > & GetLevel(const int levelID=0)
Retrieve a certain level from hierarchy.
virtual ~Hierarchy()
Destructor.
void CheckLevel(Level &level, int levelID)
Helper function.
std::string description() const
Return a simple one-line description of this object.
void CheckForEmptySmoothersAndCoarseSolve()
void IsPreconditioner(const bool flag)
Array< RCP< Level > > Levels_
Container for Level objects.
bool Setup(int coarseLevelID, const RCP< const FactoryManagerBase > fineLevelManager, const RCP< const FactoryManagerBase > coarseLevelManager, const RCP< const FactoryManagerBase > nextLevelManager=Teuchos::null)
Multi-level setup phase: build a new level of the hierarchy.
STS::magnitudeType MagnitudeType
void describe(Teuchos::FancyOStream &out, const VerbLevel verbLevel=Default) const
Print the Hierarchy with some verbosity level to a FancyOStream object.
ConvergenceStatus IsConverged(const Teuchos::Array< MagnitudeType > &residualNorm, const MagnitudeType convergenceTolerance) const
Decide if the multigrid iteration is converged.
ConvergenceStatus Iterate(const MultiVector &B, MultiVector &X, ConvData conv=ConvData(), bool InitialGuessIsZero=false, LO startLevel=0)
Apply the multigrid preconditioner.
void DumpCurrentGraph(int level) const
void SetMatvecParams(RCP< ParameterList > matvecParams)
Xpetra::UnderlyingLib lib_
Epetra/Tpetra mode.
void Clear(int startLevel=0)
Clear impermanent data from previous setup.
bool IsCalculationOfResidualRequired(const LO startLevel, const ConvData &conv) const
Decide if the residual needs to be computed.
ConvergenceStatus ComputeResidualAndPrintHistory(const Operator &A, const MultiVector &X, const MultiVector &B, const LO iteration, const LO startLevel, const ConvData &conv, MagnitudeType &previousResidualNorm)
Compute the residual norm and print it depending on the verbosity level.
double GetOperatorComplexity() const
void PrintResidualHistory(const LO iteration, const Teuchos::Array< MagnitudeType > &residualNorm) const
Print residualNorm for this iteration to the screen.
void AllocateLevelMultiVectors(int numvecs, bool forceMapCheck=false)
void print(std::ostream &out=std::cout, const VerbLevel verbLevel=(MueLu::Parameters|MueLu::Statistics0)) const
Hierarchy::print is local hierarchy function, thus the statistics can be different from global ones.
void Delete(const std::string &ename, const FactoryBase *factory=NoFactory::get())
Call Level::Delete(ename, factory) for each level of the Hierarchy.
void Keep(const std::string &ename, const FactoryBase *factory=NoFactory::get())
Call Level::Keep(ename, factory) for each level of the Hierarchy.
void SetLabel(const std::string &hierarchyLabel)
void AddKeepFlag(const std::string &ename, const FactoryBase *factory=NoFactory::get(), KeepType keep=MueLu::Keep)
Call Level::AddKeepFlag for each level of the Hierarchy.
void AddNewLevel()
Add a new level at the end of the hierarchy.
void RemoveKeepFlag(const std::string &ename, const FactoryBase *factory, KeepType keep=MueLu::All)
Call Level::RemoveKeepFlag for each level of the Hierarchy.
void ReplaceCoordinateMap(Level &level)
Class that holds all level-specific information.
bool IsAvailable(const std::string &ename, const FactoryBase *factory=NoFactory::get()) const
Test whether a need's value has been saved.
void SetComm(RCP< const Teuchos::Comm< int > > const &comm)
void setlib(Xpetra::UnderlyingLib lib2)
RCP< Level > & GetPreviousLevel()
Previous level.
void Release(const FactoryBase &factory)
Decrement the storage counter for all the inputs of a factory.
RCP< const Teuchos::Comm< int > > GetComm() const
int GetLevelID() const
Return level number.
T & Get(const std::string &ename, const FactoryBase *factory=NoFactory::get())
Get data without decrementing associated storage counter (i.e., read-only access)....
void Set(const std::string &ename, const T &entry, const FactoryBase *factory=NoFactory::get())
void Request(const FactoryBase &factory)
Increment the storage counter for all the inputs of a factory.
Xpetra::UnderlyingLib lib()
Timer to be used in non-factories.
static const NoFactory * get()
static std::string PrintMatrixInfo(const Matrix &A, const std::string &msgTag, RCP< const Teuchos::ParameterList > params=Teuchos::null)
An exception safe way to call the method 'Level::SetFactoryManager()'.
Integrates Teuchos::TimeMonitor with MueLu verbosity system.
void Build(Level &fineLevel, Level &coarseLevel) const
Build an object with this factory.
static Teuchos::Array< Magnitude > ResidualNorm(const Xpetra::Operator< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Op, const MultiVector &X, const MultiVector &RHS)
static void SetRandomSeed(const Teuchos::Comm< int > &comm)
Set seed for random number generator.
static RCP< MultiVector > Residual(const Xpetra::Operator< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Op, const MultiVector &X, const MultiVector &RHS)
Namespace for MueLu classes and methods.
@ Warnings0
Important warning messages (one line)
@ Statistics2
Print even more statistics.
@ Warnings
Print all warning messages.
@ Statistics1
Print more statistics.
@ Timings0
High level timing information (use Teuchos::TimeMonitor::summarize() to print)
@ Runtime0
One-liner description of what is happening.
@ Runtime1
Description of what is happening (more verbose)
@ Warnings1
Additional warnings.
@ Statistics0
Print statistics that do not involve significant additional computation.
@ Parameters1
Print class parameters (more parameters, more verbose)
short KeepType
std::string toString(const T &what)
Little helper function to convert non-string types to strings.
VerbLevel toMueLuVerbLevel(const Teuchos::EVerbosityLevel verbLevel)
Translate Teuchos verbosity level to MueLu verbosity level.
static std::string getColonLabel(const std::string &label)
Helper function for object label.
Data struct for defining stopping criteria of multigrid iteration.