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