MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_IfpackSmoother.cpp
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#include "MueLu_ConfigDefs.hpp"
11
12#if defined(HAVE_MUELU_EPETRA) && defined(HAVE_MUELU_IFPACK)
13#include <Ifpack.h>
14#include <Ifpack_Chebyshev.h>
15#include "Xpetra_MultiVectorFactory.hpp"
16
18
19#include "MueLu_Level.hpp"
20#include "MueLu_Utilities.hpp"
21#include "MueLu_Monitor.hpp"
22#include "MueLu_Aggregates.hpp"
23
24namespace MueLu {
25
26template <class Node>
27IfpackSmoother<Node>::IfpackSmoother(std::string const& type, Teuchos::ParameterList const& paramList, LO const& overlap)
28 : type_(type)
29 , overlap_(overlap) {
30 this->declareConstructionOutcome(false, "");
31 SetParameterList(paramList);
32}
33
34template <class Node>
35void IfpackSmoother<Node>::SetParameterList(const Teuchos::ParameterList& paramList) {
37
39 // It might be invalid to change parameters after the setup, but it depends entirely on Ifpack implementation.
40 // TODO: I don't know if Ifpack returns an error code or exception or ignore parameters modification in this case...
41 prec_->SetParameters(const_cast<ParameterList&>(this->GetParameterList()));
42 }
43}
44
45template <class Node>
46void IfpackSmoother<Node>::SetPrecParameters(const Teuchos::ParameterList& list) const {
47 ParameterList& paramList = const_cast<ParameterList&>(this->GetParameterList());
48 paramList.setParameters(list);
49
50 RCP<ParameterList> precList = this->RemoveFactoriesFromList(this->GetParameterList());
51
52 prec_->SetParameters(*precList);
53
54 // We would like to have the following line here:
55 // paramList.setParameters(*precList);
56 // For instance, if Ifpack sets somem parameters internally, we would like to have
57 // them listed when we call this->GetParameterList()
58 // But because of the way Ifpack handles the list, we cannot do that.
59 // The bad scenario goes like this:
60 // * SmootherFactory calls Setup
61 // * Setup calls SetPrecParameters
62 // * We call prec_->SetParameters(*precList)
63 // This actually updates the internal parameter list with default prec_ parameters
64 // This means that we get a parameter ("chebyshev: max eigenvalue", -1) in the list
65 // * Setup calls prec_->Compute()
66 // Here we may compute the max eigenvalue, but we get no indication of this. If we
67 // do compute it, our parameter list becomes outdated
68 // * SmootherFactory calls Apply
69 // * Apply constructs a list with a list with an entry "chebyshev: zero starting solution"
70 // * We call prec_->SetParameters(*precList)
71 // The last call is the problem. At this point, we have a list with an outdated entry
72 // "chebyshev: max eigenvalue", but prec_ uses this entry and replaces the computed max
73 // eigenvalue with the one from the list, resulting in -1.0 eigenvalue.
74 //
75 // Ifpack2 does not have this problem, as it does not populate the list with new entries
76}
77
78template <class Node>
79void IfpackSmoother<Node>::DeclareInput(Level& currentLevel) const {
80 this->Input(currentLevel, "A");
81
82 if (type_ == "LINESMOOTHING_BANDED_RELAXATION" ||
83 type_ == "LINESMOOTHING_BANDED RELAXATION" ||
84 type_ == "LINESMOOTHING_BANDEDRELAXATION" ||
85 type_ == "LINESMOOTHING_BLOCK_RELAXATION" ||
86 type_ == "LINESMOOTHING_BLOCK RELAXATION" ||
87 type_ == "LINESMOOTHING_BLOCKRELAXATION") {
88 this->Input(currentLevel, "CoarseNumZLayers"); // necessary for fallback criterion
89 this->Input(currentLevel, "LineDetection_VertLineIds"); // necessary to feed block smoother
90 } // if (type_ == "LINESMOOTHING_BANDEDRELAXATION")
91 else if (type_ == "AGGREGATE") {
92 // Aggregate smoothing needs aggregates
93 this->Input(currentLevel, "Aggregates");
94 }
95}
96
97template <class Node>
99 FactoryMonitor m(*this, "Setup Smoother", currentLevel);
100 if (SmootherPrototype::IsSetup() == true)
101 this->GetOStream(Warnings0) << "MueLu::IfpackSmoother::Setup(): Setup() has already been called" << std::endl;
102
103 A_ = Factory::Get<RCP<Matrix> >(currentLevel, "A");
104
105 double lambdaMax = -1.0;
106 if (type_ == "Chebyshev") {
107 std::string maxEigString = "chebyshev: max eigenvalue";
108 std::string eigRatioString = "chebyshev: ratio eigenvalue";
109
110 try {
111 lambdaMax = Teuchos::getValue<Scalar>(this->GetParameter(maxEigString));
112 this->GetOStream(Statistics1) << maxEigString << " (cached with smoother parameter list) = " << lambdaMax << std::endl;
113
114 } catch (Teuchos::Exceptions::InvalidParameterName&) {
115 lambdaMax = A_->GetMaxEigenvalueEstimate();
116
117 if (lambdaMax != -1.0) {
118 this->GetOStream(Statistics1) << maxEigString << " (cached with matrix) = " << lambdaMax << std::endl;
119 this->SetParameter(maxEigString, ParameterEntry(lambdaMax));
120 }
121 }
122
123 // Calculate the eigenvalue ratio
124 const Scalar defaultEigRatio = 20;
125
126 Scalar ratio = defaultEigRatio;
127 try {
128 ratio = Teuchos::getValue<Scalar>(this->GetParameter(eigRatioString));
129
130 } catch (Teuchos::Exceptions::InvalidParameterName&) {
131 this->SetParameter(eigRatioString, ParameterEntry(ratio));
132 }
133
134 if (currentLevel.GetLevelID()) {
135 // Update ratio to be
136 // ratio = max(number of fine DOFs / number of coarse DOFs, defaultValue)
137 //
138 // NOTE: We don't need to request previous level matrix as we know for sure it was constructed
139 RCP<const Matrix> fineA = currentLevel.GetPreviousLevel()->Get<RCP<Matrix> >("A");
140 size_t nRowsFine = fineA->getGlobalNumRows();
141 size_t nRowsCoarse = A_->getGlobalNumRows();
142
143 ratio = std::max(ratio, as<Scalar>(nRowsFine) / nRowsCoarse);
144
145 this->GetOStream(Statistics1) << eigRatioString << " (computed) = " << ratio << std::endl;
146 this->SetParameter(eigRatioString, ParameterEntry(ratio));
147 }
148 } // if (type_ == "Chebyshev")
149
150 if (type_ == "LINESMOOTHING_BANDED_RELAXATION" ||
151 type_ == "LINESMOOTHING_BANDED RELAXATION" ||
152 type_ == "LINESMOOTHING_BANDEDRELAXATION" ||
153 type_ == "LINESMOOTHING_BLOCK_RELAXATION" ||
154 type_ == "LINESMOOTHING_BLOCK RELAXATION" ||
155 type_ == "LINESMOOTHING_BLOCKRELAXATION") {
156 ParameterList& myparamList = const_cast<ParameterList&>(this->GetParameterList());
157
158 LO CoarseNumZLayers = currentLevel.Get<LO>("CoarseNumZLayers", Factory::GetFactory("CoarseNumZLayers").get());
159 if (CoarseNumZLayers > 0) {
160 Teuchos::ArrayRCP<LO> TVertLineIdSmoo = currentLevel.Get<Teuchos::ArrayRCP<LO> >("LineDetection_VertLineIds", Factory::GetFactory("LineDetection_VertLineIds").get());
161
162 // determine number of local parts
163 LO maxPart = 0;
164 for (size_t k = 0; k < Teuchos::as<size_t>(TVertLineIdSmoo.size()); k++) {
165 if (maxPart < TVertLineIdSmoo[k]) maxPart = TVertLineIdSmoo[k];
166 }
167
168 size_t numLocalRows = A_->getLocalNumRows();
169 TEUCHOS_TEST_FOR_EXCEPTION(numLocalRows % TVertLineIdSmoo.size() != 0, Exceptions::RuntimeError, "MueLu::Ifpack2Smoother::Setup(): the number of local nodes is incompatible with the TVertLineIdsSmoo.");
170
171 if (numLocalRows == Teuchos::as<size_t>(TVertLineIdSmoo.size())) {
172 myparamList.set("partitioner: type", "user");
173 myparamList.set("partitioner: map", &(TVertLineIdSmoo[0]));
174 myparamList.set("partitioner: local parts", maxPart + 1);
175 } else {
176 // we assume a constant number of DOFs per node
177 size_t numDofsPerNode = numLocalRows / TVertLineIdSmoo.size();
178
179 // Create a new Teuchos::ArrayRCP<LO> of size numLocalRows and fill it with the corresponding information
180 Teuchos::ArrayRCP<LO> partitionerMap(numLocalRows, Teuchos::OrdinalTraits<LocalOrdinal>::invalid());
181 for (size_t blockRow = 0; blockRow < Teuchos::as<size_t>(TVertLineIdSmoo.size()); ++blockRow)
182 for (size_t dof = 0; dof < numDofsPerNode; dof++)
183 partitionerMap[blockRow * numDofsPerNode + dof] = TVertLineIdSmoo[blockRow];
184 myparamList.set("partitioner: type", "user");
185 myparamList.set("partitioner: map", &(partitionerMap[0]));
186 myparamList.set("partitioner: local parts", maxPart + 1);
187 }
188
189 if (type_ == "LINESMOOTHING_BANDED_RELAXATION" ||
190 type_ == "LINESMOOTHING_BANDED RELAXATION" ||
191 type_ == "LINESMOOTHING_BANDEDRELAXATION")
192 type_ = "block relaxation";
193 else
194 type_ = "block relaxation";
195 } else {
196 // line detection failed -> fallback to point-wise relaxation
197 this->GetOStream(Runtime0) << "Line detection failed: fall back to point-wise relaxation" << std::endl;
198 myparamList.remove("partitioner: type", false);
199 myparamList.remove("partitioner: map", false);
200 myparamList.remove("partitioner: local parts", false);
201 type_ = "point relaxation stand-alone";
202 }
203
204 } // if (type_ == "LINESMOOTHING_BANDEDRELAXATION")
205
206 if (type_ == "AGGREGATE") {
207 SetupAggregate(currentLevel);
208 }
209
210 else {
211 // If we're using a linear partitioner and haven't set the # local parts, set it to match the operator's block size
212 ParameterList& precList = const_cast<ParameterList&>(this->GetParameterList());
213 if (precList.isParameter("partitioner: type") && precList.get<std::string>("partitioner: type") == "linear" &&
214 !precList.isParameter("partitioner: local parts")) {
215 precList.set("partitioner: local parts", (int)A_->getLocalNumRows() / A_->GetFixedBlockSize());
216 }
217
218 RCP<Epetra_CrsMatrix> epA = Utilities::Op2NonConstEpetraCrs(A_);
219
220 Ifpack factory;
221 prec_ = rcp(factory.Create(type_, &(*epA), overlap_));
222 TEUCHOS_TEST_FOR_EXCEPTION(prec_.is_null(), Exceptions::RuntimeError, "Could not create an Ifpack preconditioner with type = \"" << type_ << "\"");
223 SetPrecParameters();
224 prec_->Compute();
225 }
226
228
229 if (type_ == "Chebyshev" && lambdaMax == -1.0) {
230 Teuchos::RCP<Ifpack_Chebyshev> chebyPrec = rcp_dynamic_cast<Ifpack_Chebyshev>(prec_);
231 if (chebyPrec != Teuchos::null) {
232 lambdaMax = chebyPrec->GetLambdaMax();
233 A_->SetMaxEigenvalueEstimate(lambdaMax);
234 this->GetOStream(Statistics1) << "chebyshev: max eigenvalue (calculated by Ifpack)"
235 << " = " << lambdaMax << std::endl;
236 }
237 TEUCHOS_TEST_FOR_EXCEPTION(lambdaMax == -1.0, Exceptions::RuntimeError, "MueLu::IfpackSmoother::Setup(): no maximum eigenvalue estimate");
238 }
239
240 this->GetOStream(Statistics1) << description() << std::endl;
241}
242
243template <class Node>
245 ParameterList& paramList = const_cast<ParameterList&>(this->GetParameterList());
246
247 if (this->IsSetup() == true) {
248 this->GetOStream(Warnings0) << "MueLu::Ifpack2moother::SetupAggregate(): Setup() has already been called" << std::endl;
249 this->GetOStream(Warnings0) << "MueLu::IfpackSmoother::SetupAggregate(): reuse of this type is not available, reverting to full construction" << std::endl;
250 }
251
252 this->GetOStream(Statistics0) << "IfpackSmoother: Using Aggregate Smoothing" << std::endl;
253
254 RCP<Aggregates> aggregates = Factory::Get<RCP<Aggregates> >(currentLevel, "Aggregates");
255 RCP<const LOMultiVector> vertex2AggId = aggregates->GetVertex2AggId();
256 ArrayRCP<LO> aggregate_ids = rcp_const_cast<LOMultiVector>(vertex2AggId)->getDataNonConst(0);
257 ArrayRCP<LO> dof_ids;
258
259 // We need to unamalgamate, if the FixedBlockSize > 1
260 if (A_->GetFixedBlockSize() > 1) {
261 // NOTE: We're basically going to have to leave a deallocated pointer hanging out
262 // in the paramList object (and inside the partitioner). This never gets
263 // use again after Compute() gets called, so this is OK, but I'm still leaving
264 // this note here in case it bites us again later.
265 LO blocksize = (LO)A_->GetFixedBlockSize();
266 dof_ids.resize(aggregate_ids.size() * blocksize);
267 for (LO i = 0; i < (LO)aggregate_ids.size(); i++) {
268 for (LO j = 0; j < (LO)blocksize; j++)
269 dof_ids[i * blocksize + j] = aggregate_ids[i];
270 }
271 } else {
272 dof_ids = aggregate_ids;
273 }
274
275 paramList.set("partitioner: map", dof_ids.getRawPtr());
276 paramList.set("partitioner: type", "user");
277 paramList.set("partitioner: overlap", 0);
278 paramList.set("partitioner: local parts", (int)aggregates->GetNumAggregates());
279 // In case of Dirichlet nodes
280 paramList.set("partitioner: keep singletons", true);
281
282 RCP<Epetra_CrsMatrix> A = Utilities::Op2NonConstEpetraCrs(A_);
283 type_ = "block relaxation stand-alone";
284
285 Ifpack factory;
286 prec_ = rcp(factory.Create(type_, &(*A), overlap_));
287 TEUCHOS_TEST_FOR_EXCEPTION(prec_.is_null(), Exceptions::RuntimeError, "Could not create an Ifpack preconditioner with type = \"" << type_ << "\"");
288 SetPrecParameters();
289
290 int rv = prec_->Compute();
291 TEUCHOS_TEST_FOR_EXCEPTION(rv, Exceptions::RuntimeError, "Ifpack preconditioner with type = \"" << type_ << "\" Compute() call failed.");
292}
293
294template <class Node>
295void IfpackSmoother<Node>::Apply(MultiVector& X, const MultiVector& B, bool InitialGuessIsZero) const {
296 TEUCHOS_TEST_FOR_EXCEPTION(SmootherPrototype::IsSetup() == false, Exceptions::RuntimeError, "MueLu::IfpackSmoother::Apply(): Setup() has not been called");
297
298 // Forward the InitialGuessIsZero option to Ifpack
299 Teuchos::ParameterList paramList;
300 bool supportInitialGuess = false;
301 if (type_ == "Chebyshev") {
302 paramList.set("chebyshev: zero starting solution", InitialGuessIsZero);
303 supportInitialGuess = true;
304
305 } else if (type_ == "point relaxation stand-alone") {
306 paramList.set("relaxation: zero starting solution", InitialGuessIsZero);
307 supportInitialGuess = true;
308 }
309
310 SetPrecParameters(paramList);
311
312 // Apply
313 if (InitialGuessIsZero || supportInitialGuess) {
316
317 prec_->ApplyInverse(epB, epX);
318
319 } else {
320 RCP<MultiVector> Residual = Utilities::Residual(*A_, X, B);
321 RCP<MultiVector> Correction = MultiVectorFactory::Build(A_->getDomainMap(), X.getNumVectors());
322
324 const Epetra_MultiVector& epB = Utilities::MV2EpetraMV(*Residual);
325
326 prec_->ApplyInverse(epB, epX);
327
328 X.update(1.0, *Correction, 1.0);
329 }
330}
331
332template <class Node>
333RCP<MueLu::SmootherPrototype<double, int, int, Node> > IfpackSmoother<Node>::Copy() const {
334 RCP<IfpackSmoother<Node> > smoother = rcp(new IfpackSmoother<Node>(*this));
335 smoother->SetParameterList(this->GetParameterList());
336 return Teuchos::rcp_dynamic_cast<MueLu::SmootherPrototype<double, int, int, Node> >(smoother);
337}
338
339template <class Node>
341 std::ostringstream out;
342 // The check "GetVerbLevel() == Test" is to avoid
343 // failures in the EasyInterface test.
344 if (prec_ == Teuchos::null || this->GetVerbLevel() == InterfaceTest) {
346 out << "{type = " << type_ << "}";
347 } else {
348 out << prec_->Label();
349 }
350 return out.str();
351}
352
353template <class Node>
354void IfpackSmoother<Node>::print(Teuchos::FancyOStream& out, const VerbLevel verbLevel) const {
356
357 if (verbLevel & Parameters0)
358 out0 << "Prec. type: " << type_ << std::endl;
359
360 if (verbLevel & Parameters1) {
361 out0 << "Parameter list: " << std::endl;
362 Teuchos::OSTab tab2(out);
363 out << this->GetParameterList();
364 out0 << "Overlap: " << overlap_ << std::endl;
365 }
366
367 if (verbLevel & External)
368 if (prec_ != Teuchos::null) {
369 Teuchos::OSTab tab2(out);
370 out << *prec_ << std::endl;
371 }
372
373 if (verbLevel & Debug) {
374 out0 << "IsSetup: " << Teuchos::toString(SmootherPrototype::IsSetup()) << std::endl
375 << "-" << std::endl
376 << "RCP<A_>: " << A_ << std::endl
377 << "RCP<prec_>: " << prec_ << std::endl;
378 }
379}
380
381template <class Node>
383 // FIXME: This is a placeholder
384 return Teuchos::OrdinalTraits<size_t>::invalid();
385}
386
387} // namespace MueLu
388
389// The IfpackSmoother is only templated on the Node, since it is an Epetra only object
390// Therefore we do not need the full ETI instantiations as we do for the other MueLu
391// objects which are instantiated on all template parameters.
392#if defined(HAVE_MUELU_EPETRA)
394#endif
395
396#endif
#define MUELU_DESCRIBE
Helper macro for implementing Describable::describe() for BaseClass objects.
Ifpack_Preconditioner * Create(const std::string PrecType, Epetra_RowMatrix *Matrix, const int overlap=0, bool overrideSerialDefault=false)
virtual std::string description() const
Return a simple one-line description of this object.
Exception throws to report errors in the internal logical of the program.
Timer to be used in factories. Similar to Monitor but with additional timers.
const RCP< const FactoryBase > GetFactory(const std::string &varName) const
Default implementation of FactoryAcceptor::GetFactory()
Class that encapsulates Ifpack smoothers.
void Apply(MultiVector &X, const MultiVector &B, bool InitialGuessIsZero=false) const
Apply the preconditioner.
void SetPrecParameters(const Teuchos::ParameterList &list=Teuchos::ParameterList()) const
IfpackSmoother(std::string const &type, Teuchos::ParameterList const &paramList=Teuchos::ParameterList(), LO const &overlap=0)
Constructor.
void print(Teuchos::FancyOStream &out, const VerbLevel verbLevel=Default) const
Print the object with some verbosity level to an FancyOStream object.
std::string description() const
Return a simple one-line description of this object.
void Setup(Level &currentLevel)
Set up the smoother.
RCP< SmootherPrototype > Copy() const
void DeclareInput(Level &currentLevel) const
Input.
size_t getNodeSmootherComplexity() const
Get a rough estimate of cost per iteration.
void SetupAggregate(Level &currentLevel)
void SetParameterList(const Teuchos::ParameterList &paramList)
Set parameters from a parameter list and return with default values.
Class that holds all level-specific information.
RCP< Level > & GetPreviousLevel()
Previous level.
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)....
virtual void SetParameterList(const Teuchos::ParameterList &paramList)=0
Set parameters from a parameter list and return with default values.
void declareConstructionOutcome(bool fail, std::string msg)
bool IsSetup() const
Get the state of a smoother prototype.
static RCP< MultiVector > Residual(const Xpetra::Operator< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Op, const MultiVector &X, const MultiVector &RHS)
static RCP< Epetra_MultiVector > MV2NonConstEpetraMV(RCP< Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > vec)
static RCP< Epetra_CrsMatrix > Op2NonConstEpetraCrs(RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Op)
static RCP< const Epetra_MultiVector > MV2EpetraMV(RCP< Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > const vec)
Helper utility to pull out the underlying Epetra objects from an Xpetra object.
Namespace for MueLu classes and methods.
@ Warnings0
Important warning messages (one line)
@ Debug
Print additional debugging information.
@ Statistics1
Print more statistics.
@ External
Print external lib objects.
@ Runtime0
One-liner description of what is happening.
@ Parameters0
Print class parameters.
@ Statistics0
Print statistics that do not involve significant additional computation.
@ Parameters1
Print class parameters (more parameters, more verbose)