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