//! virtual void CallBuild(Level& requestedLevel) const { int levelID = requestedLevel.GetLevelID(); #ifdef HAVE_MUELU_DEBUG // We cannot call Build method twice for the same level, but we can call it multiple times for different levels TEUCHOS_TEST_FOR_EXCEPTION((multipleCallCheck_ == ENABLED) && (multipleCallCheckGlobal_ == ENABLED) && (lastLevelID_ == levelID), Exceptions::RuntimeError, this->ShortClassName() << "::Build() called twice for the same level (levelID=" << levelID << "). This is likely due to a configuration error."); if (multipleCallCheck_ == FIRSTCALL) multipleCallCheck_ = ENABLED; lastLevelID_ = levelID; #endif TEUCHOS_TEST_FOR_EXCEPTION(requestedLevel.GetPreviousLevel() == Teuchos::null, Exceptions::RuntimeError, "LevelID = " << levelID); #ifdef HAVE_MUELU_TIMER_SYNCHRONIZATION RCP<const Teuchos::Comm<int> > comm = requestedLevel.GetComm(); if (comm.is_null()) { // Some factories are called before we constructed Ac, and therefore, // before we set the level communicator. For such factories we can get // the comm from the previous level, as all processes go there RCP<Level>& prevLevel = requestedLevel.GetPreviousLevel(); if (!prevLevel.is_null()) comm = prevLevel->GetComm(); } // Synchronization timer std::string syncTimer = this->ShortClassName() + ": Build sync (level=" + toString(requestedLevel.GetLevelID()) + ")"; if (!comm.is_null()) { TimeMonitor timer(*this, syncTimer); comm->barrier(); } #endif Build(*requestedLevel.GetPreviousLevel(), requestedLevel); #ifdef HAVE_MUELU_TIMER_SYNCHRONIZATION // Synchronization timer if (!comm.is_null()) { TimeMonitor timer(*this, syncTimer); comm->barrier(); } #endif GetOStream(Test) << *RemoveFactoriesFromList(GetParameterList()) << std::endl; }
void SmootherFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::BuildSmoother(Level& currentLevel, PreOrPost const preOrPost) const { // SmootherFactory is quite tricky because of the fact that one of the smoother prototypes may be zero. // The challenge is that we have no way of knowing how user uses this factory. For instance, lets say // user wants to use s1 prototype as a presmoother, and s2 as a postsmoother. He could do: // (a) create SmootherFactory(s1, s2), or // (b) create SmootherFactory(s1, null) and SmootherFactory(null, s2) // It may also happen that somewhere somebody set presmoother factory = postsmoother factory = (a) // How do you do DeclareInput in this case? It could easily introduce a bug if a user does not check // whether presmoother = postsmoother. A buggy code could look like that: // RCP<SmootherFactory> s = rcp(new SmootherFactory(s1,s2)); // level.Request("PreSmoother", s.get()); // level.Request("PostSmoother", s.get()); // Get<RCP<SmootherBase> > pre = Get<RCP<SmootherBase> >("PreSmoother", s.get()); // Get<RCP<SmootherBase> > post = Get<RCP<SmootherBase> >("PostSmoother", s.get()); // This code would call DeclareInput in request mode twice, but as the Build method generates both Pre and Post // smoothers, it would call DelcareInput in release mode only once, leaving requests. // This code has another problem if s2 = Teuchos::null. In that case, despite the request for PostSmoother, the factory // would not generate one, and second Get would throw. The real issue here is that given a Factory pointer // there is no way to be sure that this factory would generate any of "PreSmoother" or "PostSmoother", unless you are // able to cast it to SmootherFactory, do GetPrototypes and to check whether any of those is Teuchos::null. const Teuchos::ParameterList& pL = GetParameterList(); RCP<SmootherPrototype> preSmoother, postSmoother; ParameterList preSmootherParams, postSmootherParams; if ((preOrPost & PRE) && !preSmootherPrototype_.is_null()) { if (currentLevel.IsAvailable("PreSmoother data", this)) preSmoother = currentLevel.Get<RCP<SmootherPrototype> >("PreSmoother data", this); else preSmoother = preSmootherPrototype_->Copy(); int oldRank = -1; if (!currentLevel.GetComm().is_null()) oldRank = preSmoother->SetProcRankVerbose(currentLevel.GetComm()->getRank()); preSmoother->Setup(currentLevel); preSmootherParams = preSmoother->GetParameterList(); if (oldRank != -1) preSmoother->SetProcRankVerbose(oldRank); currentLevel.Set<RCP<SmootherBase> >("PreSmoother", preSmoother, this); if (pL.get<bool>("keep smoother data")) Set(currentLevel, "PreSmoother data", preSmoother); } if ((preOrPost & POST) && !postSmootherPrototype_.is_null()) { if (preOrPost == BOTH && preSmootherPrototype_ == postSmootherPrototype_) { // Simple reuse // Same prototypes for pre- and post-smoothers mean that we only need to call Setup only once postSmoother = preSmoother; // else if (preOrPost == BOTH && // preSmootherPrototype_ != Teuchos::null && // preSmootherPrototype_->GetType() == postSmootherPrototype_->GetType()) { // // More complex reuse case: need implementation of CopyParameters() and a smoothers smart enough to know when parameters affect the setup phase. // // YES: post-smoother == pre-smoother // // => copy the pre-smoother to avoid the setup phase of the post-smoother. // postSmoother = preSmoother->Copy(); // // If the post-smoother parameters are different from // // pre-smoother, the parameters stored in the post-smoother // // prototype are copied in the new post-smoother object. // postSmoother->CopyParameters(postSmootherPrototype_); // // If parameters don't influence the Setup phase (it is the case // // for Jacobi, Chebyshev...), PostSmoother is already setup. Nothing // // more to do. In the case of ILU, parameters of the smoother // // are in fact the parameters of the Setup phase. The call to // // CopyParameters resets the smoother (only if parameters are // // different) and we must call Setup() again. // postSmoother->Setup(currentLevel); // } // // TODO: if CopyParameters do not exist, do setup twice. } else { if (currentLevel.IsAvailable("PostSmoother data", this)) { postSmoother = currentLevel.Get<RCP<SmootherPrototype> >("PostSmoother data", this); } else { // No reuse: // - either we only do postsmoothing without any presmoothing // - or our postsmoother is different from presmoother postSmoother = postSmootherPrototype_->Copy(); } int oldRank = -1; if (!currentLevel.GetComm().is_null()) oldRank = postSmoother->SetProcRankVerbose(GetProcRankVerbose()); postSmoother->Setup(currentLevel); postSmootherParams = postSmoother->GetParameterList(); if (oldRank != -1) postSmoother->SetProcRankVerbose(oldRank); } currentLevel.Set<RCP<SmootherBase> >("PostSmoother", postSmoother, this); if (pL.get<bool>("keep smoother data")) Set(currentLevel, "PostSmoother data", preSmoother); } ParameterList& paramList = const_cast<ParameterList&>(this->GetParameterList()); if (postSmoother == preSmoother && !preSmoother.is_null()) { paramList.sublist("smoother", false) = preSmoother->GetParameterList(); } else { if (!preSmoother.is_null()) paramList.sublist("presmoother", false) = preSmootherParams; if (!postSmoother.is_null()) paramList.sublist("postsmoother", false) = postSmootherParams; } } // Build()