MueLu  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MueLu_Amesos2Smoother_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_AMESOS2SMOOTHER_DEF_HPP
11 #define MUELU_AMESOS2SMOOTHER_DEF_HPP
12 
13 #include <algorithm>
14 
15 #include "MueLu_ConfigDefs.hpp"
16 #if defined(HAVE_MUELU_AMESOS2)
17 #include <Xpetra_Matrix.hpp>
18 #include <Xpetra_IO.hpp>
19 
20 #include <Amesos2_config.h>
21 #include <Amesos2.hpp>
22 
24 #include "MueLu_Level.hpp"
25 #include "MueLu_Utilities.hpp"
26 #include "MueLu_Monitor.hpp"
27 
28 namespace MueLu {
29 
30 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
33  localMap_ = Xpetra::MapFactory<LocalOrdinal, GlobalOrdinal, Node>::Build(Nullspace->getMap()->lib(),
34  Nullspace->getNumVectors(),
35  Nullspace->getMap()->getIndexBase(),
36  Nullspace->getMap()->getComm(),
38 
42  tempMV->multiply(Teuchos::CONJ_TRANS, Teuchos::NO_TRANS, ONE, *Nullspace, *Nullspace, ZERO);
43 
44  Kokkos::View<Scalar**, Kokkos::LayoutLeft, Kokkos::HostSpace> Q("Q", Nullspace->getNumVectors(), Nullspace->getNumVectors());
45  int LDQ;
46  {
47  auto dots = tempMV->getLocalViewHost(Xpetra::Access::ReadOnly);
48  Kokkos::deep_copy(Q, dots);
49  LDQ = Q.stride(1);
50  }
51 
53  int info = 0;
54  lapack.POTRF('L', Nullspace->getNumVectors(), Q.data(), LDQ, &info);
55  TEUCHOS_ASSERT(info == 0);
56  lapack.TRTRI('L', 'N', Nullspace->getNumVectors(), Q.data(), LDQ, &info);
57  TEUCHOS_ASSERT(info == 0);
58 
59  Nullspace_ = Xpetra::MultiVectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(Nullspace->getMap(), Nullspace->getNumVectors());
60 
61  for (size_t i = 0; i < Nullspace->getNumVectors(); i++) {
62  for (size_t j = 0; j <= i; j++) {
63  Nullspace_->getVectorNonConst(i)->update(Q(i, j), *Nullspace->getVector(j), ONE);
64  }
65  }
66 }
67 
68 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
73 
74  // Project X onto orthonormal nullspace
75  // Nullspace_ ^T * X
77  tempMV->multiply(Teuchos::CONJ_TRANS, Teuchos::NO_TRANS, ONE, *Nullspace_, X, ZERO);
78  auto dots = tempMV->getLocalViewHost(Xpetra::Access::ReadOnly);
79  bool doProject = true;
80  for (size_t i = 0; i < X.getNumVectors(); i++) {
81  for (size_t j = 0; j < Nullspace_->getNumVectors(); j++) {
82  doProject = doProject || (Teuchos::ScalarTraits<Scalar>::magnitude(dots(j, i)) > 100 * Teuchos::ScalarTraits<Scalar>::eps());
83  }
84  }
85  if (doProject) {
86  for (size_t i = 0; i < X.getNumVectors(); i++) {
87  for (size_t j = 0; j < Nullspace_->getNumVectors(); j++) {
88  X.getVectorNonConst(i)->update(-dots(j, i), *Nullspace_->getVector(j), ONE);
89  }
90  }
91  }
92 }
93 
94 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
96  : type_(type)
97  , useTransformation_(false) {
98  this->SetParameterList(paramList);
99 
100  if (!type_.empty()) {
101  // Transform string to "Abcde" notation
102  std::transform(type_.begin(), type_.end(), type_.begin(), ::tolower);
103  std::transform(type_.begin(), ++type_.begin(), type_.begin(), ::toupper);
104  }
105  if (type_ == "Superlu_dist")
106  type_ = "Superludist";
107 
108  // Try to come up with something availble
109  // Order corresponds to our preference
110  // TODO: It would be great is Amesos2 provides directly this kind of logic for us
111  if (type_ == "" || Amesos2::query(type_) == false) {
112  std::string oldtype = type_;
113 #if defined(HAVE_AMESOS2_KLU2)
114  type_ = "Klu";
115 #elif defined(HAVE_AMESOS2_SUPERLU)
116  type_ = "Superlu";
117 #elif defined(HAVE_AMESOS2_SUPERLUDIST)
118  type_ = "Superludist";
119 #elif defined(HAVE_AMESOS2_BASKER)
120  type_ = "Basker";
121 #else
122  this->declareConstructionOutcome(true, std::string("Amesos2 has been compiled without SuperLU_DIST, SuperLU, Klu, or Basker. By default, MueLu tries") +
123  "to use one of these libraries. Amesos2 must be compiled with one of these solvers, " +
124  "or a valid Amesos2 solver has to be specified explicitly.");
125  return;
126 #endif
127  if (oldtype != "")
128  this->GetOStream(Warnings0) << "MueLu::Amesos2Smoother: \"" << oldtype << "\" is not available. Using \"" << type_ << "\" instead" << std::endl;
129  else
130  this->GetOStream(Runtime1) << "MueLu::Amesos2Smoother: using \"" << type_ << "\"" << std::endl;
131  }
132 
133  // Check the validity of the solver type parameter
134  this->declareConstructionOutcome(Amesos2::query(type_) == false, "The Amesos2 library reported that the solver '" + type_ + "' is not available. " +
135  "Amesos2 has been compiled without the support of this solver, or the solver name is misspelled.");
136 }
137 
138 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
140 
141 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
143  RCP<ParameterList> validParamList = rcp(new ParameterList());
144  validParamList->set<RCP<const FactoryBase> >("A", null, "Factory of the coarse matrix");
145  validParamList->set<RCP<const FactoryBase> >("Nullspace", null, "Factory of the nullspace");
146  validParamList->set<bool>("fix nullspace", false, "Remove zero eigenvalue by adding rank one correction.");
147  ParameterList norecurse;
148  norecurse.disableRecursiveValidation();
149  validParamList->set<ParameterList>("Amesos2", norecurse, "Parameters that are passed to Amesos2");
150  return validParamList;
151 }
152 
153 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
155  ParameterList pL = this->GetParameterList();
156 
157  this->Input(currentLevel, "A");
158  if (pL.get<bool>("fix nullspace"))
159  this->Input(currentLevel, "Nullspace");
160 }
161 
162 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
164  FactoryMonitor m(*this, "Setup Smoother", currentLevel);
165 
166  if (SmootherPrototype::IsSetup() == true)
167  this->GetOStream(Warnings0) << "MueLu::Amesos2Smoother::Setup(): Setup() has already been called" << std::endl;
168 
169  RCP<Matrix> A = Factory::Get<RCP<Matrix> >(currentLevel, "A");
170 
171  // Do a quick check if we need to modify the matrix
172  RCP<const Map> rowMap = A->getRowMap();
173  RCP<Matrix> factorA;
174  Teuchos::ParameterList pL = this->GetParameterList();
175 
176  if (pL.get<bool>("fix nullspace")) {
177  this->GetOStream(Runtime1) << "MueLu::Amesos2Smoother::Setup(): fixing nullspace" << std::endl;
178 
179  rowMap = A->getRowMap();
180  size_t gblNumCols = rowMap->getGlobalNumElements();
181 
182  RCP<MultiVector> NullspaceOrig = Factory::Get<RCP<MultiVector> >(currentLevel, "Nullspace");
183 
184  projection_ = rcp(new Projection<Scalar, LocalOrdinal, GlobalOrdinal, Node>(NullspaceOrig));
185  RCP<MultiVector> Nullspace = projection_->Nullspace_;
186 
187  RCP<MultiVector> ghostedNullspace;
188  RCP<const Map> colMap;
189  RCP<const Import> importer;
190  if (rowMap->getComm()->getSize() > 1) {
191  this->GetOStream(Warnings0) << "MueLu::Amesos2Smoother::Setup(): Applying nullspace fix on distributed matrix. Try rebalancing to single rank!" << std::endl;
192  ArrayRCP<GO> elements_RCP;
193  elements_RCP.resize(gblNumCols);
194  ArrayView<GO> elements = elements_RCP();
195  for (size_t k = 0; k < gblNumCols; k++)
196  elements[k] = Teuchos::as<GO>(k);
197  colMap = MapFactory::Build(rowMap->lib(), gblNumCols * rowMap->getComm()->getSize(), elements, Teuchos::ScalarTraits<GO>::zero(), rowMap->getComm());
198  importer = ImportFactory::Build(rowMap, colMap);
199  ghostedNullspace = MultiVectorFactory::Build(colMap, Nullspace->getNumVectors());
200  ghostedNullspace->doImport(*Nullspace, *importer, Xpetra::INSERT);
201  } else {
202  ghostedNullspace = Nullspace;
203  colMap = rowMap;
204  }
205 
206 #if KOKKOS_VERSION >= 40799
207  using ATS = KokkosKernels::ArithTraits<SC>;
208 #else
209  using ATS = Kokkos::ArithTraits<SC>;
210 #endif
211  using impl_Scalar = typename ATS::val_type;
212 #if KOKKOS_VERSION >= 40799
213  using impl_ATS = KokkosKernels::ArithTraits<impl_Scalar>;
214 #else
215  using impl_ATS = Kokkos::ArithTraits<impl_Scalar>;
216 #endif
217  using range_type = Kokkos::RangePolicy<LO, typename NO::execution_space>;
218 
219  typedef typename Matrix::local_matrix_type KCRS;
220  typedef typename KCRS::StaticCrsGraphType graph_t;
221  typedef typename graph_t::row_map_type::non_const_type lno_view_t;
222  typedef typename graph_t::entries_type::non_const_type lno_nnz_view_t;
223  typedef typename KCRS::values_type::non_const_type scalar_view_t;
224 
225  const impl_Scalar impl_SC_ZERO = impl_ATS::zero();
226 
227  size_t lclNumRows = rowMap->getLocalNumElements();
228  LocalOrdinal lclNumCols = Teuchos::as<LocalOrdinal>(gblNumCols);
229  lno_view_t newRowPointers("newRowPointers", lclNumRows + 1);
230  lno_nnz_view_t newColIndices("newColIndices", lclNumRows * gblNumCols);
231  scalar_view_t newValues("newValues", lclNumRows * gblNumCols);
232 
233  impl_Scalar shift;
234  {
235  RCP<Vector> diag = VectorFactory::Build(A->getRowMap());
236  A->getLocalDiagCopy(*diag);
237  shift = diag->normInf();
238  }
239 
240  // form normalization * nullspace * nullspace^T
241  {
242  auto lclNullspace = Nullspace->getLocalViewDevice(Xpetra::Access::ReadOnly);
243  auto lclGhostedNullspace = ghostedNullspace->getLocalViewDevice(Xpetra::Access::ReadOnly);
244  Kokkos::parallel_for(
245  "MueLu:Amesos2Smoother::fixNullspace_1", range_type(0, lclNumRows + 1),
246  KOKKOS_LAMBDA(const size_t i) {
247  if (i < lclNumRows) {
248  newRowPointers(i) = i * gblNumCols;
249  for (LocalOrdinal j = 0; j < lclNumCols; j++) {
250  newColIndices(i * gblNumCols + j) = j;
251  newValues(i * gblNumCols + j) = impl_SC_ZERO;
252  for (size_t I = 0; I < lclNullspace.extent(1); I++)
253  for (size_t J = 0; J < lclGhostedNullspace.extent(1); J++)
254  newValues(i * gblNumCols + j) += shift * lclNullspace(i, I) * impl_ATS::conjugate(lclGhostedNullspace(j, J));
255  }
256  } else
257  newRowPointers(lclNumRows) = lclNumRows * gblNumCols;
258  });
259  }
260 
261  // add A
262  if (colMap->lib() == Xpetra::UseTpetra) {
263  auto lclA = A->getLocalMatrixDevice();
264  auto lclColMapA = A->getColMap()->getLocalMap();
265  auto lclColMapANew = colMap->getLocalMap();
266  Kokkos::parallel_for(
267  "MueLu:Amesos2Smoother::fixNullspace_2", range_type(0, lclNumRows),
268  KOKKOS_LAMBDA(const size_t i) {
269  for (size_t jj = lclA.graph.row_map(i); jj < lclA.graph.row_map(i + 1); jj++) {
270  LO j = lclColMapANew.getLocalElement(lclColMapA.getGlobalElement(lclA.graph.entries(jj)));
271  impl_Scalar v = lclA.values(jj);
272  newValues(i * gblNumCols + j) += v;
273  }
274  });
275  } else {
276  auto lclA = A->getLocalMatrixHost();
277  for (size_t i = 0; i < lclNumRows; i++) {
278  for (size_t jj = lclA.graph.row_map(i); jj < lclA.graph.row_map(i + 1); jj++) {
279  LO j = colMap->getLocalElement(A->getColMap()->getGlobalElement(lclA.graph.entries(jj)));
280  SC v = lclA.values(jj);
281  newValues(i * gblNumCols + j) += v;
282  }
283  }
284  }
285 
286  RCP<Matrix> newA = rcp(new CrsMatrixWrap(rowMap, colMap, 0));
287  RCP<CrsMatrix> newAcrs = toCrsMatrix(newA);
288  newAcrs->setAllValues(newRowPointers, newColIndices, newValues);
289  newAcrs->expertStaticFillComplete(A->getDomainMap(), A->getRangeMap(),
290  importer, A->getCrsGraph()->getExporter());
291 
292  factorA = newA;
293  rowMap = factorA->getRowMap();
294  } else {
295  factorA = A;
296  }
297 
299 
300  prec_ = Amesos2::create<Tpetra_CrsMatrix, Tpetra_MultiVector>(type_, tA);
301  TEUCHOS_TEST_FOR_EXCEPTION(prec_ == Teuchos::null, Exceptions::RuntimeError, "Amesos2::create returns Teuchos::null");
302  RCP<Teuchos::ParameterList> amesos2_params = Teuchos::rcpFromRef(pL.sublist("Amesos2"));
303  amesos2_params->setName("Amesos2");
304  if ((rowMap->getGlobalNumElements() != as<size_t>((rowMap->getMaxAllGlobalIndex() - rowMap->getMinAllGlobalIndex()) + 1)) ||
305  (!rowMap->isContiguous() && (rowMap->getComm()->getSize() == 1))) {
306  if (((type_ != "Cusolver") && (type_ != "Tacho")) && !(amesos2_params->sublist(prec_->name()).template isType<bool>("IsContiguous")))
307  amesos2_params->sublist(prec_->name()).set("IsContiguous", false, "Are GIDs Contiguous");
308  }
309  prec_->setParameters(amesos2_params);
310 
311  prec_->numericFactorization();
312 
314 }
315 
316 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
317 void Amesos2Smoother<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Apply(MultiVector& X, const MultiVector& B, bool /* InitialGuessIsZero */) const {
318  TEUCHOS_TEST_FOR_EXCEPTION(SmootherPrototype::IsSetup() == false, Exceptions::RuntimeError, "MueLu::Amesos2Smoother::Apply(): Setup() has not been called");
319 
321  if (!useTransformation_) {
322  tX = toTpetra(Teuchos::rcpFromRef(X));
323  tB = toTpetra(Teuchos::rcpFromRef(const_cast<MultiVector&>(B)));
324  } else {
325  // Copy data of the original vectors into the transformed ones
326  size_t numVectors = X.getNumVectors();
327  size_t length = X.getLocalLength();
328 
330  "MueLu::Amesos2Smoother::Apply: Fixing coarse matrix for Amesos2 for multivectors has not been implemented yet.");
331  ArrayRCP<const SC> Xdata = X.getData(0), Bdata = B.getData(0);
332  ArrayRCP<SC> X_data = X_->getDataNonConst(0), B_data = B_->getDataNonConst(0);
333 
334  for (size_t i = 0; i < length; i++) {
335  X_data[i] = Xdata[i];
336  B_data[i] = Bdata[i];
337  }
338 
339  tX = toTpetra(X_);
340  tB = toTpetra(B_);
341  }
342 
343  prec_->setX(tX);
344  prec_->setB(tB);
345 
346  prec_->solve();
347 
348  prec_->setX(Teuchos::null);
349  prec_->setB(Teuchos::null);
350 
351  if (useTransformation_) {
352  // Copy data from the transformed vectors into the original ones
353  size_t length = X.getLocalLength();
354 
355  ArrayRCP<SC> Xdata = X.getDataNonConst(0);
356  ArrayRCP<const SC> X_data = X_->getData(0);
357 
358  for (size_t i = 0; i < length; i++)
359  Xdata[i] = X_data[i];
360  }
361 
362  {
363  Teuchos::ParameterList pL = this->GetParameterList();
364  if (pL.get<bool>("fix nullspace")) {
365  projection_->projectOut(X);
366  }
367  }
368 }
369 
370 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
373  Copy() const {
374  return rcp(new Amesos2Smoother(*this));
375 }
376 
377 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
379  std::ostringstream out;
380 
381  if (SmootherPrototype::IsSetup() == true) {
382  out << prec_->description();
383 
384  } else {
386  out << "{type = " << type_ << "}";
387  }
388  return out.str();
389 }
390 
391 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
394 
395  if (verbLevel & Parameters0)
396  out0 << "Prec. type: " << type_ << std::endl;
397 
398  if (verbLevel & Parameters1) {
399  out0 << "Parameter list: " << std::endl;
400  Teuchos::OSTab tab2(out);
401  out << this->GetParameterList();
402  }
403 
404  if ((verbLevel & External) && prec_ != Teuchos::null) {
405  Teuchos::OSTab tab2(out);
406  out << *prec_ << std::endl;
407  }
408 
409  if (verbLevel & Debug)
410  out0 << "IsSetup: " << Teuchos::toString(SmootherPrototype::IsSetup()) << std::endl
411  << "-" << std::endl
412  << "RCP<prec_>: " << prec_ << std::endl;
413 }
414 
415 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
417  if (!prec_.is_null())
418  return prec_->getStatus().getNnzLU();
419  else
420  return 0.0;
421 }
422 } // namespace MueLu
423 
424 #endif // HAVE_MUELU_AMESOS2
425 #endif // MUELU_AMESOS2SMOOTHER_DEF_HPP
Important warning messages (one line)
MueLu::DefaultLocalOrdinal LocalOrdinal
Teuchos::FancyOStream & GetOStream(MsgType type, int thisProcRankOnly=0) const
Get an output stream for outputting the input message type.
RCP< const ParameterList > GetValidParameterList() const
Return a const parameter list of valid parameters that setParameterList() will accept.
static Teuchos::RCP< MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Build(const Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node >> &map, size_t NumVectors, bool zeroOut=true)
ParameterList & disableRecursiveValidation()
static magnitudeType eps()
Print external lib objects.
T & get(const std::string &name, T def_value)
std::string type_
amesos2-specific key phrase that denote smoother type
void Apply(MultiVector &X, const MultiVector &B, bool InitialGuessIsZero=false) const
Apply the direct solver. Solves the linear system AX=B using the constructed solver.
Timer to be used in factories. Similar to Monitor but with additional timers.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Print additional debugging information.
LocalOrdinal LO
std::string tolower(const std::string &str)
ParameterList & set(std::string const &name, T &&value, std::string const &docString="", RCP< const ParameterEntryValidator > const &validator=null)
Amesos2Smoother(const std::string &type="", const Teuchos::ParameterList &paramList=Teuchos::ParameterList())
Constructor Creates a MueLu interface to the direct solvers in the Amesos2 package. If you are using type==&quot;&quot;, then either SuperLU or KLU2 are used by default.
std::string description() const
Return a simple one-line description of this object.
void resize(const size_type n, const T &val=T())
RCP< SmootherPrototype > Copy() const
virtual void SetParameterList(const Teuchos::ParameterList &paramList)
Set parameters from a parameter list and return with default values.
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
MueLu::DefaultScalar Scalar
void declareConstructionOutcome(bool fail, std::string msg)
Class that holds all level-specific information.
Definition: MueLu_Level.hpp:63
static Teuchos::RCP< Map< LocalOrdinal, GlobalOrdinal, Node > > Build(UnderlyingLib lib, global_size_t numGlobalElements, GlobalOrdinal indexBase, const Teuchos::RCP< const Teuchos::Comm< int >> &comm, LocalGlobal lg=Xpetra::GloballyDistributed)
#define I(i, j)
Class that encapsulates Amesos2 direct solvers.
void POTRF(const char &UPLO, const OrdinalType &n, ScalarType *A, const OrdinalType &lda, OrdinalType *info) const
bool IsSetup() const
Get the state of a smoother prototype.
void DeclareInput(Level &currentLevel) const
Input.
#define MUELU_DESCRIBE
Helper macro for implementing Describable::describe() for BaseClass objects.
ParameterList & setParameters(const ParameterList &source)
Print class parameters.
static magnitudeType magnitude(T a)
RCP< const Tpetra::CrsGraph< LocalOrdinal, GlobalOrdinal, Node > > toTpetra(const RCP< const CrsGraph< LocalOrdinal, GlobalOrdinal, Node > > &graph)
virtual ~Amesos2Smoother()
Destructor.
void Setup(Level &currentLevel)
Set up the direct solver. This creates the underlying Amesos2 solver object according to the paramete...
Scalar SC
ParameterList & sublist(const std::string &name, bool mustAlreadyExist=false, const std::string &docString="")
ParameterList & setName(const std::string &name)
Print class parameters (more parameters, more verbose)
virtual Teuchos::RCP< Vector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > getVectorNonConst(size_t j)=0
Exception throws to report errors in the internal logical of the program.
#define TEUCHOS_ASSERT(assertion_test)
Description of what is happening (more verbose)
virtual size_t getNumVectors() const =0
void TRTRI(const char &UPLO, const char &DIAG, const OrdinalType &n, ScalarType *A, const OrdinalType &lda, OrdinalType *info) const
size_t getNodeSmootherComplexity() const
Get a rough estimate of cost per iteration.
void print(Teuchos::FancyOStream &out, const VerbLevel verbLevel=Default) const
Print the object with some verbosity level to an FancyOStream object.
virtual std::string description() const
Return a simple one-line description of this object.
Projection(RCP< Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node >> &Nullspace)
std::string toString(const T &t)
void projectOut(Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &X)