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->getHostLocalView(Xpetra::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->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
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
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 = toCrsMatrix(newA);
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
290 RCP<const Tpetra_CrsMatrix> tA = toTpetra(factorA);
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
308template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
309void 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
312 RCP<Tpetra_MultiVector> tX, tB;
313 if (!useTransformation_) {
314 tX = toTpetra(Teuchos::rcpFromRef(X));
315 tB = toTpetra(Teuchos::rcpFromRef(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
321 TEUCHOS_TEST_FOR_EXCEPTION(numVectors > 1, Exceptions::RuntimeError,
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
331 tX = toTpetra(X_);
332 tB = toTpetra(B_);
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
362template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
363RCP<MueLu::SmootherPrototype<Scalar, LocalOrdinal, GlobalOrdinal, Node> >
368
369template <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
383template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
384void Amesos2Smoother<Scalar, LocalOrdinal, GlobalOrdinal, Node>::print(Teuchos::FancyOStream& out, const VerbLevel verbLevel) const {
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
407template <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
#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.
Timer to be used in factories. Similar to Monitor but with additional timers.
void Input(Level &level, const std::string &varName) const
T Get(Level &level, const std::string &varName) const
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).