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->getHostLocalView(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->getHostLocalView(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_SUPERLU)
114  type_ = "Superlu";
115 #elif defined(HAVE_AMESOS2_KLU2)
116  type_ = "Klu";
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  using ATS = Kokkos::ArithTraits<SC>;
207  using impl_Scalar = typename ATS::val_type;
208  using impl_ATS = Kokkos::ArithTraits<impl_Scalar>;
209  using range_type = Kokkos::RangePolicy<LO, typename NO::execution_space>;
210 
211  typedef typename Matrix::local_matrix_type KCRS;
212  typedef typename KCRS::StaticCrsGraphType graph_t;
213  typedef typename graph_t::row_map_type::non_const_type lno_view_t;
214  typedef typename graph_t::entries_type::non_const_type lno_nnz_view_t;
215  typedef typename KCRS::values_type::non_const_type scalar_view_t;
216 
217  const impl_Scalar impl_SC_ZERO = impl_ATS::zero();
218 
219  size_t lclNumRows = rowMap->getLocalNumElements();
220  LocalOrdinal lclNumCols = Teuchos::as<LocalOrdinal>(gblNumCols);
221  lno_view_t newRowPointers("newRowPointers", lclNumRows + 1);
222  lno_nnz_view_t newColIndices("newColIndices", lclNumRows * gblNumCols);
223  scalar_view_t newValues("newValues", lclNumRows * gblNumCols);
224 
225  impl_Scalar shift;
226  {
227  RCP<Vector> diag = VectorFactory::Build(A->getRowMap());
228  A->getLocalDiagCopy(*diag);
229  shift = diag->normInf();
230  }
231 
232  // form normalization * nullspace * nullspace^T
233  {
234  auto lclNullspace = Nullspace->getDeviceLocalView(Xpetra::Access::ReadOnly);
235  auto lclGhostedNullspace = ghostedNullspace->getDeviceLocalView(Xpetra::Access::ReadOnly);
236  Kokkos::parallel_for(
237  "MueLu:Amesos2Smoother::fixNullspace_1", range_type(0, lclNumRows + 1),
238  KOKKOS_LAMBDA(const size_t i) {
239  if (i < lclNumRows) {
240  newRowPointers(i) = i * gblNumCols;
241  for (LocalOrdinal j = 0; j < lclNumCols; j++) {
242  newColIndices(i * gblNumCols + j) = j;
243  newValues(i * gblNumCols + j) = impl_SC_ZERO;
244  for (size_t I = 0; I < lclNullspace.extent(1); I++)
245  for (size_t J = 0; J < lclGhostedNullspace.extent(1); J++)
246  newValues(i * gblNumCols + j) += shift * lclNullspace(i, I) * impl_ATS::conjugate(lclGhostedNullspace(j, J));
247  }
248  } else
249  newRowPointers(lclNumRows) = lclNumRows * gblNumCols;
250  });
251  }
252 
253  // add A
254  if (colMap->lib() == Xpetra::UseTpetra) {
255  auto lclA = A->getLocalMatrixDevice();
256  auto lclColMapA = A->getColMap()->getLocalMap();
257  auto lclColMapANew = colMap->getLocalMap();
258  Kokkos::parallel_for(
259  "MueLu:Amesos2Smoother::fixNullspace_2", range_type(0, lclNumRows),
260  KOKKOS_LAMBDA(const size_t i) {
261  for (size_t jj = lclA.graph.row_map(i); jj < lclA.graph.row_map(i + 1); jj++) {
262  LO j = lclColMapANew.getLocalElement(lclColMapA.getGlobalElement(lclA.graph.entries(jj)));
263  impl_Scalar v = lclA.values(jj);
264  newValues(i * gblNumCols + j) += v;
265  }
266  });
267  } else {
268  auto lclA = A->getLocalMatrixHost();
269  for (size_t i = 0; i < lclNumRows; i++) {
270  for (size_t jj = lclA.graph.row_map(i); jj < lclA.graph.row_map(i + 1); jj++) {
271  LO j = colMap->getLocalElement(A->getColMap()->getGlobalElement(lclA.graph.entries(jj)));
272  SC v = lclA.values(jj);
273  newValues(i * gblNumCols + j) += v;
274  }
275  }
276  }
277 
278  RCP<Matrix> newA = rcp(new CrsMatrixWrap(rowMap, colMap, 0));
279  RCP<CrsMatrix> newAcrs = rcp_dynamic_cast<CrsMatrixWrap>(newA)->getCrsMatrix();
280  newAcrs->setAllValues(newRowPointers, newColIndices, newValues);
281  newAcrs->expertStaticFillComplete(A->getDomainMap(), A->getRangeMap(),
282  importer, A->getCrsGraph()->getExporter());
283 
284  factorA = newA;
285  rowMap = factorA->getRowMap();
286  } else {
287  factorA = A;
288  }
289 
291 
292  prec_ = Amesos2::create<Tpetra_CrsMatrix, Tpetra_MultiVector>(type_, tA);
293  TEUCHOS_TEST_FOR_EXCEPTION(prec_ == Teuchos::null, Exceptions::RuntimeError, "Amesos2::create returns Teuchos::null");
294  RCP<Teuchos::ParameterList> amesos2_params = Teuchos::rcpFromRef(pL.sublist("Amesos2"));
295  amesos2_params->setName("Amesos2");
296  if ((rowMap->getGlobalNumElements() != as<size_t>((rowMap->getMaxAllGlobalIndex() - rowMap->getMinAllGlobalIndex()) + 1)) ||
297  (!rowMap->isContiguous() && (rowMap->getComm()->getSize() == 1))) {
298  if (((type_ != "Cusolver") && (type_ != "Tacho")) && !(amesos2_params->sublist(prec_->name()).template isType<bool>("IsContiguous")))
299  amesos2_params->sublist(prec_->name()).set("IsContiguous", false, "Are GIDs Contiguous");
300  }
301  prec_->setParameters(amesos2_params);
302 
303  prec_->numericFactorization();
304 
306 }
307 
308 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
309 void Amesos2Smoother<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Apply(MultiVector& X, const MultiVector& B, bool /* InitialGuessIsZero */) const {
310  TEUCHOS_TEST_FOR_EXCEPTION(SmootherPrototype::IsSetup() == false, Exceptions::RuntimeError, "MueLu::Amesos2Smoother::Apply(): Setup() has not been called");
311 
313  if (!useTransformation_) {
315  tB = Utilities::MV2NonConstTpetraMV2(const_cast<MultiVector&>(B));
316  } else {
317  // Copy data of the original vectors into the transformed ones
318  size_t numVectors = X.getNumVectors();
319  size_t length = X.getLocalLength();
320 
322  "MueLu::Amesos2Smoother::Apply: Fixing coarse matrix for Amesos2 for multivectors has not been implemented yet.");
323  ArrayRCP<const SC> Xdata = X.getData(0), Bdata = B.getData(0);
324  ArrayRCP<SC> X_data = X_->getDataNonConst(0), B_data = B_->getDataNonConst(0);
325 
326  for (size_t i = 0; i < length; i++) {
327  X_data[i] = Xdata[i];
328  B_data[i] = Bdata[i];
329  }
330 
333  }
334 
335  prec_->setX(tX);
336  prec_->setB(tB);
337 
338  prec_->solve();
339 
340  prec_->setX(Teuchos::null);
341  prec_->setB(Teuchos::null);
342 
343  if (useTransformation_) {
344  // Copy data from the transformed vectors into the original ones
345  size_t length = X.getLocalLength();
346 
347  ArrayRCP<SC> Xdata = X.getDataNonConst(0);
348  ArrayRCP<const SC> X_data = X_->getData(0);
349 
350  for (size_t i = 0; i < length; i++)
351  Xdata[i] = X_data[i];
352  }
353 
354  {
355  Teuchos::ParameterList pL = this->GetParameterList();
356  if (pL.get<bool>("fix nullspace")) {
357  projection_->projectOut(X);
358  }
359  }
360 }
361 
362 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
365  Copy() const {
366  return rcp(new Amesos2Smoother(*this));
367 }
368 
369 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
371  std::ostringstream out;
372 
373  if (SmootherPrototype::IsSetup() == true) {
374  out << prec_->description();
375 
376  } else {
378  out << "{type = " << type_ << "}";
379  }
380  return out.str();
381 }
382 
383 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
386 
387  if (verbLevel & Parameters0)
388  out0 << "Prec. type: " << type_ << std::endl;
389 
390  if (verbLevel & Parameters1) {
391  out0 << "Parameter list: " << std::endl;
392  Teuchos::OSTab tab2(out);
393  out << this->GetParameterList();
394  }
395 
396  if ((verbLevel & External) && prec_ != Teuchos::null) {
397  Teuchos::OSTab tab2(out);
398  out << *prec_ << std::endl;
399  }
400 
401  if (verbLevel & Debug)
402  out0 << "IsSetup: " << Teuchos::toString(SmootherPrototype::IsSetup()) << std::endl
403  << "-" << std::endl
404  << "RCP<prec_>: " << prec_ << std::endl;
405 }
406 
407 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
409  if (!prec_.is_null())
410  return prec_->getStatus().getNnzLU();
411  else
412  return 0.0;
413 }
414 } // namespace MueLu
415 
416 #endif // HAVE_MUELU_AMESOS2
417 #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
static RCP< Tpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > MV2NonConstTpetraMV2(Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &vec)
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)
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.
static RCP< Tpetra::CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Op2NonConstTpetraCrs(RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node >> Op)
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)