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#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
28namespace MueLu {
29
30template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
32 Projection(RCP<Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node> >& Nullspace) {
33 localMap_ = Xpetra::MapFactory<LocalOrdinal, GlobalOrdinal, Node>::Build(Nullspace->getMap()->lib(),
34 Nullspace->getNumVectors(),
35 Nullspace->getMap()->getIndexBase(),
36 Nullspace->getMap()->getComm(),
37 Xpetra::LocallyReplicated);
38
39 Teuchos::RCP<Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node> > tempMV = Xpetra::MultiVectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(localMap_, Nullspace->getNumVectors());
40 const Scalar ONE = Teuchos::ScalarTraits<Scalar>::one();
41 const Scalar ZERO = Teuchos::ScalarTraits<Scalar>::zero();
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(Tpetra::Access::ReadOnly);
48 Kokkos::deep_copy(Q, dots);
49 LDQ = Q.stride(1);
50 }
51
52 Teuchos::LAPACK<LocalOrdinal, Scalar> lapack;
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
68template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
70 projectOut(Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>& X) {
71 const Scalar ONE = Teuchos::ScalarTraits<Scalar>::one();
72 const Scalar ZERO = Teuchos::ScalarTraits<Scalar>::zero();
73
74 // Project X onto orthonormal nullspace
75 // Nullspace_ ^T * X
76 Teuchos::RCP<Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node> > tempMV = Xpetra::MultiVectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(localMap_, X.getNumVectors());
77 tempMV->multiply(Teuchos::CONJ_TRANS, Teuchos::NO_TRANS, ONE, *Nullspace_, X, ZERO);
78 auto dots = tempMV->getLocalViewHost(Tpetra::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
94template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
95Amesos2Smoother<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Amesos2Smoother(const std::string& type, const Teuchos::ParameterList& paramList)
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
138template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
140
141template <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
153template <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
162template <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_device_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(Tpetra::Access::ReadOnly);
243 auto lclGhostedNullspace = ghostedNullspace->getLocalViewDevice(Tpetra::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
298 RCP<const Tpetra_CrsMatrix> tA = toTpetra(factorA);
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
316template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
317void 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
320 RCP<Tpetra_MultiVector> tX, tB;
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
329 TEUCHOS_TEST_FOR_EXCEPTION(numVectors > 1, Exceptions::RuntimeError,
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
370template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
371RCP<MueLu::SmootherPrototype<Scalar, LocalOrdinal, GlobalOrdinal, Node> >
376
377template <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
391template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
392void Amesos2Smoother<Scalar, LocalOrdinal, GlobalOrdinal, Node>::print(Teuchos::FancyOStream& out, const VerbLevel verbLevel) const {
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
415template <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
#define MUELU_DESCRIBE
Helper macro for implementing Describable::describe() for BaseClass objects.
#define I(i, j)
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultScalar Scalar
Class that encapsulates Amesos2 direct solvers.
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< 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.
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.
Projection(RCP< Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > &Nullspace)
void projectOut(Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &X)
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)