MueLu  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MueLu_StratimikosSmoother_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_STRATIMIKOSSMOOTHER_DEF_HPP
11 #define MUELU_STRATIMIKOSSMOOTHER_DEF_HPP
12 
13 #include "MueLu_ConfigDefs.hpp"
14 
15 #if defined(HAVE_MUELU_STRATIMIKOS) && defined(HAVE_MUELU_THYRA)
16 
18 
19 #include <Xpetra_CrsMatrix.hpp>
20 #include <Xpetra_CrsMatrixWrap.hpp>
21 #include <Xpetra_Matrix.hpp>
22 
24 #include "MueLu_Level.hpp"
25 #include "MueLu_Utilities.hpp"
26 #include "MueLu_Monitor.hpp"
27 #ifdef MUELU_RECURMG
29 #endif
30 
31 #include <Stratimikos_DefaultLinearSolverBuilder.hpp>
32 #include "Teuchos_AbstractFactoryStd.hpp"
34 #include <unordered_map>
35 
36 namespace MueLu {
37 
38 template <class LocalOrdinal, class GlobalOrdinal, class Node>
39 StratimikosSmoother<double, LocalOrdinal, GlobalOrdinal, Node>::StratimikosSmoother(const std::string type, const Teuchos::ParameterList& paramList)
40  : type_(type) {
41  std::transform(type_.begin(), type_.end(), type_.begin(), ::toupper);
42  ParameterList& pList = const_cast<ParameterList&>(paramList);
43 
44  if (pList.isParameter("smoother: recurMgOnFilteredA")) {
45  recurMgOnFilteredA_ = true;
46  pList.remove("smoother: recurMgOnFilteredA");
47  }
48  bool isSupported = type_ == "STRATIMIKOS";
49  this->declareConstructionOutcome(!isSupported, "Stratimikos does not provide the smoother '" + type_ + "'.");
50  if (isSupported)
51  SetParameterList(paramList);
52 }
53 
54 template <class LocalOrdinal, class GlobalOrdinal, class Node>
55 void StratimikosSmoother<double, LocalOrdinal, GlobalOrdinal, Node>::SetParameterList(const Teuchos::ParameterList& paramList) {
56  Factory::SetParameterList(paramList);
57 }
58 
59 template <class LocalOrdinal, class GlobalOrdinal, class Node>
60 void StratimikosSmoother<double, LocalOrdinal, GlobalOrdinal, Node>::DeclareInput(Level& currentLevel) const {
61  this->Input(currentLevel, "A");
62 }
63 
64 template <class LocalOrdinal, class GlobalOrdinal, class Node>
65 void StratimikosSmoother<double, LocalOrdinal, GlobalOrdinal, Node>::Setup(Level& currentLevel) {
66  FactoryMonitor m(*this, "Setup Smoother", currentLevel);
67 
68  A_ = Factory::Get<RCP<Matrix> >(currentLevel, "A");
69  SetupStratimikos(currentLevel);
70  SmootherPrototype::IsSetup(true);
71  this->GetOStream(Statistics1) << description() << std::endl;
72 }
73 
74 template <class LocalOrdinal, class GlobalOrdinal, class Node>
75 void StratimikosSmoother<double, LocalOrdinal, GlobalOrdinal, Node>::SetupStratimikos(Level& currentLevel) {
76  RCP<const Thyra::LinearOpBase<Scalar> > thyraA;
77  if (recurMgOnFilteredA_) {
78  RCP<Matrix> filteredA;
79  ExperimentalDropVertConnections(filteredA, currentLevel);
80  thyraA = Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::toThyra(Teuchos::rcp_dynamic_cast<CrsMatrixWrap>(filteredA)->getCrsMatrix());
81  } else
82  thyraA = Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::toThyra(Teuchos::rcp_dynamic_cast<CrsMatrixWrap>(A_)->getCrsMatrix());
83 
84  // Build Stratimikos solver
85  Stratimikos::DefaultLinearSolverBuilder linearSolverBuilder;
86  if (recurMgOnFilteredA_) {
87 #ifdef MUELU_RECURMG
88  Stratimikos::enableMueLu<LocalOrdinal, GlobalOrdinal, Node>(linearSolverBuilder);
89 #else
90  TEUCHOS_TEST_FOR_EXCEPTION(true, Exceptions::RuntimeError, "MueLu::StratimikosSmoother:: must compile with MUELU_RECURMG defined. Unfortunately, cmake does not always produce a proper link.txt file (which sometimes requires libmuelu.a before and after libmuelu-interface.a). After configuring, run script muelu/utils/misc/patchLinkForRecurMG to change link.txt files manually. If you want to create test example, add -DMUELU_RECURMG=ON to cmake arguments.");
91 #endif
92  }
93 
94  linearSolverBuilder.setParameterList(rcpFromRef(const_cast<ParameterList&>(this->GetParameterList())));
95 
96  // Build a new "solver factory" according to the previously specified parameter list.
97  RCP<Thyra::LinearOpWithSolveFactoryBase<Scalar> > solverFactory = Thyra::createLinearSolveStrategy(linearSolverBuilder);
98  solver_ = Thyra::linearOpWithSolve(*solverFactory, thyraA);
99 #ifdef dumpOutRecurMGDebug
100  char mystring[120];
101  sprintf(mystring, "for i in A_[0123456789].m P_[0123456789].m; do T=Xecho $i | sed Xs/.m$/%d.m/XX; mv $i $T; done", (int)currentLevel.GetLevelID());
102  fflush(stdout);
103  mystring[50] = '`';
104  mystring[65] = '"';
105  mystring[76] = '"';
106  mystring[77] = '`';
107  system(mystring);
108 #endif
109 }
110 
111 template <class LocalOrdinal, class GlobalOrdinal, class Node>
112 void StratimikosSmoother<double, LocalOrdinal, GlobalOrdinal, Node>::ExperimentalDropVertConnections(RCP<Matrix>& filteredA, Level& currentLevel) {
113  // strip out the veritcal connections.
114  //
115  // There is some code, which is currently turned off (via sumDropped). That
116  // makes things more complicated. I want to keep it for now, and so it is
117  // explained here. The basic idea is to try and maintain the character
118  // of the horizontal stencil by summing dropped entries appropriately.
119  // However, this does not correspond to plane relexation, and so I am
120  // not sure it is really justified. Anyway, the picture below
121  // gives a situation with a 15-pt stencil
122  //
123  // a
124  // /
125  // /
126  // b ----c----- d
127  // /
128  // /
129  // e
130  // f dropped a & l summed into f
131  // / dropped b & m summed into g
132  // / dropped c & n summed into i
133  // g ----i----- j dropped d & o summed into j
134  // / dropped e & p summed into k
135  // /
136  // k
137  // l
138  // /
139  // /
140  // m ----n----- o
141  // /
142  // /
143  // p
144  // To do this, we use umap to record locations within the middle layer associated
145  // with each line ID (e.g. g corresponds to the 13th line). Then, when dropping (in a 2nd pass) we
146  // use lineId and umap to find where dropped entries should be summed (e.g., b corresponds to the
147  // 13th line and umap[13] points at location for g).
148  // using TST = typename Teuchos::ScalarTraits<SC>;
149 
150  bool sumDropped = false;
151 
152  LO dofsPerNode = A_->GetFixedBlockSize();
153 
154  RCP<ParameterList> fillCompleteParams(new ParameterList); // This code taken from Build method
155  fillCompleteParams->set("No Nonlocal Changes", true); // within MueLu_FilteredAFactory_def
156  filteredA = MatrixFactory::Build(A_->getCrsGraph());
157  filteredA->resumeFill();
158 
159  ArrayView<const LocalOrdinal> inds;
160  ArrayView<const Scalar> valsA;
161  ArrayView<Scalar> vals;
162  Teuchos::ArrayRCP<LocalOrdinal> TVertLineId = Factory::Get<Teuchos::ArrayRCP<LocalOrdinal> >(currentLevel, "LineDetection_VertLineIds");
163  Teuchos::ArrayRCP<LocalOrdinal> TLayerId = Factory::Get<Teuchos::ArrayRCP<LocalOrdinal> >(currentLevel, "LineDetection_Layers");
164  LocalOrdinal* VertLineId = TVertLineId.getRawPtr();
165  LocalOrdinal* LayerId = TLayerId.getRawPtr();
166  TEUCHOS_TEST_FOR_EXCEPTION((LayerId == NULL) || (VertLineId == NULL), Exceptions::RuntimeError, "MueLu::StratimikosSmoother:: no line information found on this level. Cannot use recurMgOnFilteredA on this level.");
167 
169  for (size_t i = 0; i < A_->getRowMap()->getLocalNumElements(); i++) {
170  A_->getLocalRowView(i, inds, valsA);
171  size_t nnz = inds.size();
172  ArrayView<const Scalar> vals1;
173  filteredA->getLocalRowView(i, inds, vals1);
174  vals = ArrayView<Scalar>(const_cast<Scalar*>(vals1.getRawPtr()), nnz);
175  memcpy(vals.getRawPtr(), valsA.getRawPtr(), nnz * sizeof(Scalar));
176  size_t inode, jdof, jnode, jdof_offset;
177  inode = i / dofsPerNode;
178 
179  std::unordered_map<LocalOrdinal, LocalOrdinal> umap; // umap[k] indicates where the dropped entry
180  // corresponding to kth line should be added
181  // within the row. See comments above.
182  if (sumDropped) {
183  for (size_t j = 0; j < nnz; j++) {
184  jdof = inds[j];
185  jnode = jdof / dofsPerNode;
186  jdof_offset = jdof - jnode * dofsPerNode;
187  if (LayerId[jnode] == LayerId[inode]) umap[dofsPerNode * VertLineId[jnode] + jdof_offset] = j;
188  }
189  }
190 
191  // drop non-middle layer entries. When sumDropped=true, sum dropped entries to corresponding mid-layer entry
192  for (size_t j = 0; j < nnz; j++) {
193  jdof = inds[j];
194  jnode = jdof / dofsPerNode;
195  jdof_offset = jdof - jnode * dofsPerNode;
196  if (LayerId[jnode] != LayerId[inode]) {
197  if (sumDropped) {
198  if (umap.find(dofsPerNode * VertLineId[jnode + jdof_offset]) != umap.end())
199  vals[umap[dofsPerNode * VertLineId[jnode + jdof_offset]]] += vals[j];
200  }
201  vals[j] = ZERO;
202  }
203  }
204  }
205  filteredA->fillComplete(fillCompleteParams);
206 }
207 
208 template <class LocalOrdinal, class GlobalOrdinal, class Node>
209 void StratimikosSmoother<double, LocalOrdinal, GlobalOrdinal, Node>::Apply(MultiVector& X, const MultiVector& B, bool InitialGuessIsZero) const {
210  TEUCHOS_TEST_FOR_EXCEPTION(SmootherPrototype::IsSetup() == false, Exceptions::RuntimeError, "MueLu::StratimikosSmoother::Apply(): Setup() has not been called");
211 
212  // Apply
213  if (InitialGuessIsZero) {
214  X.putScalar(0.0);
215  RCP<Thyra::MultiVectorBase<Scalar> > thyraX = Teuchos::rcp_const_cast<Thyra::MultiVectorBase<Scalar> >(Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::toThyraMultiVector(rcpFromRef(X)));
216  RCP<const Thyra::MultiVectorBase<Scalar> > thyraB = Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::toThyraMultiVector(rcpFromRef(B));
217  Thyra::SolveStatus<Scalar> status = Thyra::solve<Scalar>(*solver_, Thyra::NOTRANS, *thyraB, thyraX.ptr());
218  RCP<MultiVector> thyXpX = Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::toXpetra(thyraX, X.getMap()->getComm());
219  X = *thyXpX;
220 
221  } else {
222  typedef Teuchos::ScalarTraits<Scalar> TST;
223  RCP<MultiVector> Residual = Utilities::Residual(*A_, X, B);
224 
225  RCP<MultiVector> Cor = Xpetra::MultiVectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(X.getMap(), X.getNumVectors(), true);
226  RCP<Thyra::MultiVectorBase<Scalar> > thyraCor = Teuchos::rcp_const_cast<Thyra::MultiVectorBase<Scalar> >(Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::toThyraMultiVector(Cor));
227  RCP<const Thyra::MultiVectorBase<Scalar> > thyraRes = Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::toThyraMultiVector(Residual);
228  Thyra::SolveStatus<Scalar> status = Thyra::solve<Scalar>(*solver_, Thyra::NOTRANS, *thyraRes, thyraCor.ptr());
229  RCP<MultiVector> thyXpCor = Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::toXpetra(thyraCor, X.getMap()->getComm());
230  X.update(TST::one(), *thyXpCor, TST::one());
231  }
232 }
233 
234 template <class LocalOrdinal, class GlobalOrdinal, class Node>
235 RCP<MueLu::SmootherPrototype<double, LocalOrdinal, GlobalOrdinal, Node> > StratimikosSmoother<double, LocalOrdinal, GlobalOrdinal, Node>::Copy() const {
236  RCP<StratimikosSmoother> smoother = rcp(new StratimikosSmoother(*this));
237  smoother->SetParameterList(this->GetParameterList());
238  return smoother;
239 }
240 
241 template <class LocalOrdinal, class GlobalOrdinal, class Node>
242 std::string StratimikosSmoother<double, LocalOrdinal, GlobalOrdinal, Node>::description() const {
243  std::ostringstream out;
244  if (SmootherPrototype::IsSetup()) {
245  out << solver_->description();
246  } else {
247  out << "STRATIMIKOS {type = " << type_ << "}";
248  }
249  return out.str();
250 }
251 
252 template <class LocalOrdinal, class GlobalOrdinal, class Node>
253 void StratimikosSmoother<double, LocalOrdinal, GlobalOrdinal, Node>::print(Teuchos::FancyOStream& out, const VerbLevel verbLevel) const {
255 
256  if (verbLevel & Parameters1) {
257  out0 << "Parameter list: " << std::endl;
258  Teuchos::OSTab tab2(out);
259  out << this->GetParameterList();
260  }
261 
262  if (verbLevel & External)
263  if (solver_ != Teuchos::null) {
264  Teuchos::OSTab tab2(out);
265  out << *solver_ << std::endl;
266  }
267 
268  if (verbLevel & Debug) {
269  out0 << "IsSetup: " << Teuchos::toString(SmootherPrototype::IsSetup()) << std::endl
270  << "-" << std::endl
271  << "RCP<solver_>: " << solver_ << std::endl;
272  }
273 }
274 
275 template <class LocalOrdinal, class GlobalOrdinal, class Node>
276 size_t StratimikosSmoother<double, LocalOrdinal, GlobalOrdinal, Node>::getNodeSmootherComplexity() const {
278 }
279 
280 } // namespace MueLu
281 
282 #endif // HAVE_MUELU_STRATIMIKOS
283 #endif // MUELU_STRATIMIKOSSMOOTHER_DEF_HPP
MueLu::DefaultLocalOrdinal LocalOrdinal
static Teuchos::RCP< MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Build(const Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node >> &map, size_t NumVectors, bool zeroOut=true)
Print external lib objects.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Print more statistics.
T * getRawPtr() const
Print additional debugging information.
LocalOrdinal LO
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
MueLu::DefaultScalar Scalar
#define MUELU_DESCRIBE
Helper macro for implementing Describable::describe() for BaseClass objects.
Print class parameters (more parameters, more verbose)
std::string toString(const T &t)