MueLu  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 
17 #include "MueLu_IfpackSmoother.hpp"
18 
19 #include "MueLu_Level.hpp"
20 #include "MueLu_Utilities.hpp"
21 #include "MueLu_Monitor.hpp"
22 #include "MueLu_Aggregates.hpp"
23 
24 namespace MueLu {
25 
26 template <class Node>
27 IfpackSmoother<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 
34 template <class Node>
36  Factory::SetParameterList(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 
45 template <class Node>
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 
78 template <class Node>
79 void 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 
97 template <class Node>
98 void IfpackSmoother<Node>::Setup(Level& currentLevel) {
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 
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 
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
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 = 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 
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 
243 template <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 
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 
294 template <class Node>
295 void 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 
332 template <class Node>
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 
339 template <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 
353 template <class Node>
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 
381 template <class Node>
383  // FIXME: This is a placeholder
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
void Setup(Level &currentLevel)
Set up the smoother.
Important warning messages (one line)
RCP< SmootherPrototype > Copy() const
void SetPrecParameters(const Teuchos::ParameterList &list=Teuchos::ParameterList()) const
KOKKOS_INLINE_FUNCTION LO GetNumAggregates() const
Print external lib objects.
T & get(const std::string &name, T def_value)
static RCP< Epetra_CrsMatrix > Op2NonConstEpetraCrs(RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node >> Op)
Timer to be used in factories. Similar to Monitor but with additional timers.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Print more statistics.
T * getRawPtr() const
Print additional debugging information.
LocalOrdinal LO
One-liner description of what is happening.
ParameterList & set(std::string const &name, T &&value, std::string const &docString="", RCP< const ParameterEntryValidator > const &validator=null)
size_type size() const
Ifpack_Preconditioner * Create(const std::string PrecType, Epetra_RowMatrix *Matrix, const int overlap=0, bool overrideSerialDefault=false)
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.
void resize(const size_type n, const T &val=T())
bool isParameter(const std::string &name) const
Print statistics that do not involve significant additional computation.
bool remove(std::string const &name, bool throwIfNotExists=true)
static RCP< MultiVector > Residual(const Xpetra::Operator< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Op, const MultiVector &X, const MultiVector &RHS)
virtual void SetParameterList(const Teuchos::ParameterList &paramList)
Set parameters from a parameter list and return with default values.
void DeclareInput(Level &currentLevel) const
Input.
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
static RCP< Epetra_MultiVector > MV2NonConstEpetraMV(RCP< Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node >> vec)
void declareConstructionOutcome(bool fail, std::string msg)
Class that holds all level-specific information.
Definition: MueLu_Level.hpp:63
bool IsSetup() const
Get the state of a smoother prototype.
size_t getNodeSmootherComplexity() const
Get a rough estimate of cost per iteration.
const RCP< LOMultiVector > & GetVertex2AggId() const
Returns constant vector that maps local node IDs to local aggregates IDs.
virtual double GetLambdaMax()
#define MUELU_DESCRIBE
Helper macro for implementing Describable::describe() for BaseClass objects.
ParameterList & setParameters(const ParameterList &source)
void SetParameterList(const Teuchos::ParameterList &paramList)
Set parameters from a parameter list and return with default values.
Print class parameters.
std::string description() const
Return a simple one-line description of this object.
void SetupAggregate(Level &currentLevel)
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.
Print class parameters (more parameters, more verbose)
Exception throws to report errors in the internal logical of the program.
void Apply(MultiVector &X, const MultiVector &B, bool InitialGuessIsZero=false) const
Apply the preconditioner.
virtual std::string description() const
Return a simple one-line description of this object.
const RCP< const FactoryBase > GetFactory(const std::string &varName) const
Default implementation of FactoryAcceptor::GetFactory()
Class that encapsulates Ifpack smoothers.
std::string toString(const T &t)