MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_UtilitiesBase_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_UTILITIESBASE_DEF_HPP
11#define MUELU_UTILITIESBASE_DEF_HPP
12
13#include "KokkosKernels_ArithTraits.hpp"
14#include "MueLu_ConfigDefs.hpp"
15
17
18#include "MueLu_PerfUtils.hpp"
19#include "MueLu_Monitor.hpp"
20#include <Xpetra_MatrixMatrix.hpp>
21#include <Xpetra_MatrixFactory.hpp>
22#include <Xpetra_MatrixUtils.hpp>
23#include <Xpetra_TripleMatrixMultiply.hpp>
24
25#include <Kokkos_Core.hpp>
26#include <KokkosSparse_CrsMatrix.hpp>
27#include <KokkosSparse_getDiagCopy.hpp>
28
29#include <Xpetra_BlockedVector.hpp>
30#include <Xpetra_BlockedMap.hpp>
31#include <Xpetra_BlockedMultiVector.hpp>
32#include <Xpetra_ExportFactory.hpp>
33
34#include <Xpetra_Import.hpp>
35#include <Xpetra_ImportFactory.hpp>
36#include <Xpetra_CrsGraph.hpp>
37#include <Xpetra_CrsGraphFactory.hpp>
38#include <Xpetra_CrsMatrixWrap.hpp>
39#include <Xpetra_StridedMap.hpp>
40
41#include "MueLu_Exceptions.hpp"
42#include "MueLu_Behavior.hpp"
43#include "Xpetra_CrsMatrixFactory.hpp"
44
45#include <KokkosKernels_Handle.hpp>
46#include <KokkosGraph_RCM.hpp>
47#include <MueLu_Level.hpp>
48#include <MueLu_FactoryManager.hpp>
49#include <MueLu_InverseApproximationFactory.hpp>
50
51namespace MueLu {
52
53template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
54RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
56 Crs2Op(RCP<Xpetra::CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>> Op) {
57 if (Op.is_null())
58 return Teuchos::null;
59 return rcp(new CrsMatrixWrap(Op));
60}
61
62template <class Scalar,
63 class LocalOrdinal,
64 class GlobalOrdinal,
65 class Node>
66Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
67removeSmallEntries(Teuchos::RCP<Xpetra::CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A,
68 const typename Teuchos::ScalarTraits<Scalar>::magnitudeType threshold,
69 const bool keepDiagonal) {
70 using ATS = KokkosKernels::ArithTraits<Scalar>;
71 using impl_SC = typename ATS::val_type;
72 using impl_ATS = KokkosKernels::ArithTraits<impl_SC>;
73
74 RCP<Xpetra::CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>> crsA;
75 if (keepDiagonal) {
76 crsA = applyFilter_GID(
77 A, KOKKOS_LAMBDA(GlobalOrdinal rgid,
78 GlobalOrdinal cgid,
79 impl_SC val) {
80 return ((impl_ATS::magnitude(val) > threshold) || (rgid == cgid));
81 });
82
83 } else {
84 crsA = applyFilter_vals(
85 A, KOKKOS_LAMBDA(impl_SC val) {
86 return (impl_ATS::magnitude(val) > threshold);
87 });
88 }
89 return rcp(new Xpetra::CrsMatrixWrap<Scalar, LocalOrdinal, GlobalOrdinal, Node>(crsA));
90}
91
92template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
93RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
95 GetThresholdedMatrix(const RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& Ain, const typename Teuchos::ScalarTraits<Scalar>::magnitudeType threshold, const bool keepDiagonal) {
96 RCP<Matrix> Aout;
97 {
98 using matrix_type = Xpetra::CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>;
99 using local_matrix_type = typename matrix_type::local_matrix_type;
100 using local_graph_type = typename matrix_type::local_graph_type;
101 using execution_space = typename Node::execution_space;
102 using rowmap_type = typename local_graph_type::row_map_type::non_const_type;
103 using entries_type = typename local_graph_type::entries_type::non_const_type;
104 using values_type = typename local_matrix_type::values_type::non_const_type;
105 using range_type = Kokkos::RangePolicy<execution_space, LocalOrdinal>;
106 using implATS = KokkosKernels::ArithTraits<typename matrix_type::impl_scalar_type>;
107 auto lclA = Ain->getLocalMatrixDevice();
108 auto lclRowmap = Ain->getRowMap()->getLocalMap();
109 auto lclColmap = Ain->getColMap()->getLocalMap();
110
111 local_matrix_type thresholdedLocalMatrix;
112 if (keepDiagonal) {
113 rowmap_type rowptr("MueLu::GetThresholdedMatrix::rowptr", lclA.numRows() + 1);
114 LocalOrdinal nnz = 0;
115 Kokkos::parallel_scan(
116 range_type(0, lclA.numRows()), KOKKOS_LAMBDA(const LocalOrdinal rlid, LocalOrdinal& my_nnz, const bool is_final) {
117 auto row = lclA.rowConst(rlid);
118 auto rclid = lclColmap.getLocalElement(lclRowmap.getGlobalElement(rlid));
119
120 for (LocalOrdinal offset = 0; offset < row.length; ++offset) {
121 auto clid = row.colidx(offset);
122 auto val = row.value(offset);
123 if ((rclid == clid) || implATS::magnitude(val) > threshold) {
124 ++my_nnz;
125 if (is_final && (rlid + 1 < lclA.numRows())) {
126 rowptr(rlid + 2) = my_nnz;
127 }
128 }
129 }
130 },
131 nnz);
132
133 entries_type entries(Kokkos::ViewAllocateWithoutInitializing("MueLu::GetThresholdedGraph::indices"), nnz);
134 values_type values(Kokkos::ViewAllocateWithoutInitializing("MueLu::GetThresholdedGraph::values"), nnz);
135 Kokkos::parallel_for(
136 range_type(0, lclA.numRows()), KOKKOS_LAMBDA(const LocalOrdinal rlid) {
137 auto row = lclA.rowConst(rlid);
138 auto rclid = lclColmap.getLocalElement(lclRowmap.getGlobalElement(rlid));
139
140 for (LocalOrdinal offset = 0; offset < row.length; ++offset) {
141 auto clid = row.colidx(offset);
142 auto val = row.value(offset);
143 if ((rclid == clid) || implATS::magnitude(val) > threshold) {
144 entries(rowptr(rlid + 1)) = clid;
145 values(rowptr(rlid + 1)) = val;
146 ++rowptr(rlid + 1);
147 }
149 });
150
151 thresholdedLocalMatrix = local_matrix_type("thresholdedLocalMatrix", lclA.numRows(), lclA.numCols(), nnz, values, rowptr, entries);
152 } else {
153 rowmap_type rowptr("MueLu::GetThresholdedMatrix::rowptr", lclA.numRows() + 1);
154 LocalOrdinal nnz = 0;
155 Kokkos::parallel_scan(
156 range_type(0, lclA.numRows()), KOKKOS_LAMBDA(const LocalOrdinal rlid, LocalOrdinal& my_nnz, const bool is_final) {
157 auto row = lclA.rowConst(rlid);
158
159 for (LocalOrdinal offset = 0; offset < row.length; ++offset) {
160 auto val = row.value(offset);
161 if (implATS::magnitude(val) > threshold) {
162 ++my_nnz;
163 if (is_final && (rlid + 1 < lclA.numRows())) {
164 rowptr(rlid + 2) = my_nnz;
165 }
167 }
168 },
169 nnz);
170
171 entries_type entries(Kokkos::ViewAllocateWithoutInitializing("MueLu::GetThresholdedGraph::indices"), nnz);
172 values_type values(Kokkos::ViewAllocateWithoutInitializing("MueLu::GetThresholdedGraph::values"), nnz);
173 Kokkos::parallel_for(
174 range_type(0, lclA.numRows()), KOKKOS_LAMBDA(const LocalOrdinal rlid) {
175 auto row = lclA.rowConst(rlid);
177 for (LocalOrdinal offset = 0; offset < row.length; ++offset) {
178 auto clid = row.colidx(offset);
179 auto val = row.value(offset);
180 if (implATS::magnitude(val) > threshold) {
181 entries(rowptr(rlid + 1)) = clid;
182 values(rowptr(rlid + 1)) = val;
183 ++rowptr(rlid + 1);
184 }
185 }
186 });
187
188 thresholdedLocalMatrix = local_matrix_type("thresholdedLocalMatrix", lclA.numRows(), lclA.numCols(), nnz, values, rowptr, entries);
189 }
190
191 Aout = MatrixFactory::Build(thresholdedLocalMatrix, Ain->getRowMap(), Ain->getColMap(), Ain->getDomainMap(), Ain->getRangeMap());
192 }
193
194 return Aout;
195}
197template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
198RCP<Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>>
200 GetThresholdedGraph(const RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A, const Magnitude threshold) {
201 RCP<CrsGraph> sparsityPattern;
202 {
203 using matrix_type = Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>;
204 using graph_type = Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>;
205 using local_graph_type = typename graph_type::local_graph_device_type;
206 using execution_space = typename Node::execution_space;
207 using rowmap_type = typename local_graph_type::row_map_type::non_const_type;
208 using entries_type = typename local_graph_type::entries_type::non_const_type;
209 using range_type = Kokkos::RangePolicy<execution_space, LocalOrdinal>;
210 using implATS = KokkosKernels::ArithTraits<typename matrix_type::impl_scalar_type>;
211 using magATS = KokkosKernels::ArithTraits<typename implATS::magnitudeType>;
212 auto lclA = A->getLocalMatrixDevice();
213 auto lclRowmap = A->getRowMap()->getLocalMap();
214 auto lclColmap = A->getColMap()->getLocalMap();
215
216 rowmap_type rowptr("MueLu::GetThresholdedGraph::rowptr", lclA.numRows() + 1);
217
218 LocalOrdinal nnz = 0;
219 Kokkos::parallel_scan(
220 range_type(0, lclA.numRows()), KOKKOS_LAMBDA(const LocalOrdinal rlid, LocalOrdinal& my_nnz, const bool is_final) {
221 auto row = lclA.rowConst(rlid);
222 auto rclid = lclColmap.getLocalElement(lclRowmap.getGlobalElement(rlid));
223
224 typename implATS::magnitudeType d = magATS::one();
225 for (LocalOrdinal offset = 0; offset < row.length; ++offset) {
226 auto clid = row.colidx(offset);
227 if (rclid == clid) {
228 auto val = implATS::magnitude(row.value(offset));
229 if (val > implATS::epsilon())
230 d = val;
232 }
233
234 for (LocalOrdinal offset = 0; offset < row.length; ++offset) {
235 auto clid = row.colidx(offset);
236 auto val = row.value(offset);
237 if ((rclid == clid) || implATS::magnitude(val) > d * threshold) {
238 ++my_nnz;
239 if (is_final && (rlid + 1 < lclA.numRows())) {
240 rowptr(rlid + 2) = my_nnz;
241 }
242 }
244 },
245 nnz);
246
247 entries_type entries(Kokkos::ViewAllocateWithoutInitializing("MueLu::GetThresholdedGraph::indices"), nnz);
248 Kokkos::parallel_for(
249 range_type(0, lclA.numRows()), KOKKOS_LAMBDA(const LocalOrdinal rlid) {
250 auto row = lclA.rowConst(rlid);
251 auto rclid = lclColmap.getLocalElement(lclRowmap.getGlobalElement(rlid));
252
253 typename implATS::magnitudeType d = magATS::one();
254 for (LocalOrdinal offset = 0; offset < row.length; ++offset) {
255 auto clid = row.colidx(offset);
256 if (rclid == clid) {
257 auto val = implATS::magnitude(row.value(offset));
258 if (val > implATS::epsilon())
259 d = val;
260 }
261 }
262
263 for (LocalOrdinal offset = 0; offset < row.length; ++offset) {
264 auto clid = row.colidx(offset);
265 auto val = row.value(offset);
266 if ((rclid == clid) || implATS::magnitude(val) > d * threshold) {
267 entries(rowptr(rlid + 1)) = clid;
268 ++rowptr(rlid + 1);
269 }
270 }
271 });
272
273 sparsityPattern = CrsGraphFactory::Build(A->getRowMap(), A->getColMap(), rowptr, entries);
274 sparsityPattern->fillComplete(A->getDomainMap(), A->getRangeMap());
275 }
276
277 return sparsityPattern;
278}
279
280template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
281RCP<Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>>
283 GetThresholdedLowerTriangularGraph(const RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A, const Magnitude threshold) {
284 RCP<CrsGraph> sparsityPattern;
285 {
286 using matrix_type = Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>;
287 using graph_type = Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>;
288 using local_graph_type = typename graph_type::local_graph_device_type;
289 using execution_space = typename Node::execution_space;
290 using rowmap_type = typename local_graph_type::row_map_type::non_const_type;
291 using entries_type = typename local_graph_type::entries_type::non_const_type;
292 using range_type = Kokkos::RangePolicy<execution_space, LocalOrdinal>;
293 using implATS = KokkosKernels::ArithTraits<typename matrix_type::impl_scalar_type>;
294 using magATS = KokkosKernels::ArithTraits<typename implATS::magnitudeType>;
295 auto lclA = A->getLocalMatrixDevice();
296 auto lclRowmap = A->getRowMap()->getLocalMap();
297 auto lclColmap = A->getColMap()->getLocalMap();
298
299 rowmap_type rowptr("MueLu::GetLowerTriangularGraph::rowptr", lclA.numRows() + 1);
300
301 LocalOrdinal nnz = 0;
302 Kokkos::parallel_scan(
303 range_type(0, lclA.numRows()), KOKKOS_LAMBDA(const LocalOrdinal rlid, LocalOrdinal& my_nnz, const bool is_final) {
304 auto row = lclA.rowConst(rlid);
305 auto row_gid = lclRowmap.getGlobalElement(rlid);
307 typename implATS::magnitudeType d = magATS::one();
308 for (LocalOrdinal offset = 0; offset < row.length; ++offset) {
309 auto clid = row.colidx(offset);
310 auto col_gid = lclColmap.getGlobalElement(clid);
311 if (row_gid == col_gid) {
312 auto val = implATS::magnitude(row.value(offset));
313 if (val > implATS::epsilon())
314 d = val;
315 }
316 }
318 for (LocalOrdinal offset = 0; offset < row.length; ++offset) {
319 auto clid = row.colidx(offset);
320 auto val = row.value(offset);
321 auto col_gid = lclColmap.getGlobalElement(clid);
322 if ((row_gid == col_gid) || ((row_gid > col_gid) && implATS::magnitude(val) > d * threshold)) {
323 ++my_nnz;
324 if (is_final && (rlid + 1 < lclA.numRows())) {
325 rowptr(rlid + 2) = my_nnz;
326 }
327 }
328 }
329 },
330 nnz);
331
332 entries_type entries(Kokkos::ViewAllocateWithoutInitializing("MueLu::GetThresholdedLowerTriangularGraph::indices"), nnz);
333 Kokkos::parallel_for(
334 range_type(0, lclA.numRows()), KOKKOS_LAMBDA(const LocalOrdinal rlid) {
335 auto row = lclA.rowConst(rlid);
336 auto row_gid = lclRowmap.getGlobalElement(rlid);
337
338 typename implATS::magnitudeType d = magATS::one();
339 for (LocalOrdinal offset = 0; offset < row.length; ++offset) {
340 auto clid = row.colidx(offset);
341 auto col_gid = lclColmap.getGlobalElement(clid);
342 if (row_gid == col_gid) {
343 auto val = implATS::magnitude(row.value(offset));
344 if (val > implATS::epsilon())
345 d = val;
346 }
347 }
348
349 for (LocalOrdinal offset = 0; offset < row.length; ++offset) {
350 auto clid = row.colidx(offset);
351 auto val = row.value(offset);
352 auto col_gid = lclColmap.getGlobalElement(clid);
353 if ((row_gid == col_gid) || ((row_gid > col_gid) && implATS::magnitude(val) > d * threshold)) {
354 entries(rowptr(rlid + 1)) = clid;
355 ++rowptr(rlid + 1);
356 }
357 }
358 });
359
360 sparsityPattern = CrsGraphFactory::Build(A->getRowMap(), A->getColMap(), rowptr, entries);
361 sparsityPattern->fillComplete(A->getDomainMap(), A->getRangeMap());
362 }
363
364 return sparsityPattern;
365}
366
367template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
368Teuchos::ArrayRCP<Scalar>
370 GetMatrixDiagonal_arcp(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A) {
371 size_t numRows = A.getRowMap()->getLocalNumElements();
372 Teuchos::ArrayRCP<Scalar> diag(numRows);
373 Teuchos::ArrayView<const LocalOrdinal> cols;
374 Teuchos::ArrayView<const Scalar> vals;
375 for (size_t i = 0; i < numRows; ++i) {
376 A.getLocalRowView(i, cols, vals);
378 for (; j < cols.size(); ++j) {
379 if (Teuchos::as<size_t>(cols[j]) == i) {
380 diag[i] = vals[j];
381 break;
382 }
383 }
384 if (j == cols.size()) {
385 // Diagonal entry is absent
386 diag[i] = Teuchos::ScalarTraits<Scalar>::zero();
387 }
388 }
389 return diag;
390}
391
392template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
393Teuchos::RCP<Xpetra::Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
395 GetMatrixDiagonal(const Matrix& A) {
396 const auto rowMap = A.getRowMap();
397 auto diag = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(rowMap, false);
398
399 const CrsMatrixWrap* crsOp = dynamic_cast<const CrsMatrixWrap*>(&A);
400 if ((crsOp != NULL) && (rowMap->lib() == Xpetra::UseTpetra)) {
401 using device_type = typename CrsGraph::device_type;
402 Kokkos::View<size_t*, device_type> offsets("offsets", rowMap->getLocalNumElements());
403 crsOp->getCrsGraph()->getLocalDiagOffsets(offsets);
404 crsOp->getCrsMatrix()->getLocalDiagCopy(*diag, offsets);
405 } else {
406 A.getLocalDiagCopy(*diag);
408
409 return diag;
410}
411
412// template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
413// RCP<Xpetra::Vector<Scalar,LocalOrdinal,GlobalOrdinal,Node> >
414// UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
415// GetMatrixDiagonalInverse(const Xpetra::Matrix<Scalar,LocalOrdinal,GlobalOrdinal,Node> & A, Magnitude tol, Scalar valReplacement) {
416// Teuchos::TimeMonitor MM = *Teuchos::TimeMonitor::getNewTimer("UtilitiesBase::GetMatrixDiagonalInverse");
418// RCP<const Map> rowMap = A.getRowMap();
419// RCP<Vector> diag = Xpetra::VectorFactory<Scalar,LocalOrdinal,GlobalOrdinal,Node>::Build(rowMap,true);
420
421// A.getLocalDiagCopy(*diag);
423// RCP<Vector> inv = MueLu::UtilitiesBase<Scalar,LocalOrdinal,GlobalOrdinal,Node>::GetInverse(diag, tol, valReplacement);
424
425// return inv;
426// }
427
428template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
429Teuchos::RCP<Xpetra::Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
431 GetMatrixDiagonalInverse(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
432 typename Teuchos::ScalarTraits<Scalar>::magnitudeType tol,
433 Scalar valReplacement,
434 const bool doLumped) {
435 Teuchos::TimeMonitor MM = *Teuchos::TimeMonitor::getNewTimer("Utilities::GetMatrixDiagonalInverse");
436
437 RCP<const BlockedCrsMatrix> bA = Teuchos::rcp_dynamic_cast<const BlockedCrsMatrix>(rcpFromRef(A));
438 if (!bA.is_null()) {
439 RCP<const Map> rowMap = A.getRowMap();
440 RCP<Vector> diag = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(rowMap, false);
441 A.getLocalDiagCopy(*diag);
442 RCP<Vector> inv = MueLu::UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::GetInverse(diag, tol, valReplacement);
443 return inv;
444 }
445
446 // Some useful type definitions
447 using local_matrix_type = typename Matrix::local_matrix_device_type;
448 // using local_graph_type = typename local_matrix_type::staticcrsgraph_type;
449 using value_type = typename local_matrix_type::value_type;
450 using values_type = typename local_matrix_type::values_type;
451 using scalar_type = typename values_type::non_const_value_type;
452 using ordinal_type = typename local_matrix_type::ordinal_type;
453 using execution_space = typename local_matrix_type::execution_space;
454 // using memory_space = typename local_matrix_type::memory_space;
455 // Be careful with this one, if using KokkosKernels::ArithTraits<Scalar>
456 // you are likely to run into errors when handling std::complex<>
457 // a good way to work around that is to use the following:
458 // using KAT = KokkosKernels::ArithTraits<KokkosKernels::ArithTraits<Scalar>::val_type> >
459 // here we have: value_type = KokkosKernels::ArithTraits<Scalar>::val_type
460 using KAT = KokkosKernels::ArithTraits<value_type>;
461
462 // Get/Create distributed objects
463 RCP<const Map> rowMap = A.getRowMap();
464 RCP<Vector> diag = VectorFactory::Build(rowMap, false);
465
466 // Now generate local objects
467 local_matrix_type localMatrix = A.getLocalMatrixDevice();
468 auto diagVals = diag->getLocalViewDevice(Tpetra::Access::ReadWrite);
469
470 ordinal_type numRows = localMatrix.graph.numRows();
471
472 scalar_type valReplacement_dev = valReplacement;
473
474 // Note: 2019-11-21, LBV
475 // This could be implemented with a TeamPolicy over the rows
476 // and a TeamVectorRange over the entries in a row if performance
477 // becomes more important here.
478 if (!doLumped)
479 Kokkos::parallel_for(
480 "Utilities::GetMatrixDiagonalInverse",
481 Kokkos::RangePolicy<ordinal_type, execution_space>(0, numRows),
482 KOKKOS_LAMBDA(const ordinal_type rowIdx) {
483 bool foundDiagEntry = false;
484 auto myRow = localMatrix.rowConst(rowIdx);
485 for (ordinal_type entryIdx = 0; entryIdx < myRow.length; ++entryIdx) {
486 if (myRow.colidx(entryIdx) == rowIdx) {
487 foundDiagEntry = true;
488 if (KAT::magnitude(myRow.value(entryIdx)) > KAT::magnitude(tol)) {
489 diagVals(rowIdx, 0) = KAT::one() / myRow.value(entryIdx);
490 } else {
491 diagVals(rowIdx, 0) = valReplacement_dev;
492 }
493 break;
494 }
495 }
496
497 if (!foundDiagEntry) {
498 diagVals(rowIdx, 0) = KAT::zero();
499 }
500 });
501 else
502 Kokkos::parallel_for(
503 "Utilities::GetMatrixDiagonalInverse",
504 Kokkos::RangePolicy<ordinal_type, execution_space>(0, numRows),
505 KOKKOS_LAMBDA(const ordinal_type rowIdx) {
506 auto myRow = localMatrix.rowConst(rowIdx);
507 for (ordinal_type entryIdx = 0; entryIdx < myRow.length; ++entryIdx) {
508 diagVals(rowIdx, 0) += KAT::magnitude(myRow.value(entryIdx));
509 }
510 if (KAT::magnitude(diagVals(rowIdx, 0)) > KAT::magnitude(tol))
511 diagVals(rowIdx, 0) = KAT::one() / diagVals(rowIdx, 0);
512 else
513 diagVals(rowIdx, 0) = valReplacement_dev;
514 });
515
516 return diag;
517}
518
519template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
520Teuchos::RCP<Xpetra::Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
522 GetLumpedMatrixDiagonal(Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node> const& A, const bool doReciprocal,
523 Magnitude tol,
524 Scalar valReplacement,
525 const bool replaceSingleEntryRowWithZero,
526 const bool useAverageAbsDiagVal) {
527 typedef Teuchos::ScalarTraits<Scalar> TST;
528
529 RCP<Vector> diag = Teuchos::null;
530 const Scalar zero = TST::zero();
531 const Scalar one = TST::one();
532 const Scalar two = one + one;
533
534 Teuchos::RCP<const Matrix> rcpA = Teuchos::rcpFromRef(A);
535
536 RCP<const Xpetra::BlockedCrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>> bA =
537 Teuchos::rcp_dynamic_cast<const Xpetra::BlockedCrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>(rcpA);
538 if (bA == Teuchos::null) {
539 RCP<const Map> rowMap = rcpA->getRowMap();
540 diag = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(rowMap, false);
541
542 if (rowMap->lib() == Xpetra::UnderlyingLib::UseTpetra) {
543 Teuchos::TimeMonitor MM = *Teuchos::TimeMonitor::getNewTimer("UtilitiesBase::GetLumpedMatrixDiagonal (Kokkos implementation)");
544 // Implement using Kokkos
545 using local_vector_type = typename Vector::dual_view_type::t_dev_um;
546 using local_matrix_type = typename Matrix::local_matrix_device_type;
547 using execution_space = typename local_vector_type::execution_space;
548 // using rowmap_type = typename local_matrix_type::row_map_type;
549 // using entries_type = typename local_matrix_type::index_type;
550 using values_type = typename local_matrix_type::values_type;
551 using scalar_type = typename values_type::non_const_value_type;
552 using mag_type = typename KokkosKernels::ArithTraits<scalar_type>::mag_type;
553 using KAT_S = typename KokkosKernels::ArithTraits<scalar_type>;
554 using KAT_M = typename KokkosKernels::ArithTraits<mag_type>;
555 using size_type = typename local_matrix_type::non_const_size_type;
556
557 local_vector_type diag_dev = diag->getLocalViewDevice(Tpetra::Access::OverwriteAll);
558 local_matrix_type local_mat_dev = rcpA->getLocalMatrixDevice();
559 Kokkos::RangePolicy<execution_space, int> my_policy(0, static_cast<int>(diag_dev.extent(0)));
560 scalar_type valReplacement_dev = valReplacement;
561
562 if (doReciprocal) {
563 Kokkos::View<int*, execution_space> nnzPerRow("nnz per rows", diag_dev.extent(0));
564 Kokkos::View<scalar_type*, execution_space> regSum("regSum", diag_dev.extent(0));
565 Kokkos::View<mag_type, execution_space> avgAbsDiagVal_dev("avgAbsDiagVal");
566 Kokkos::View<int, execution_space> numDiagsEqualToOne_dev("numDiagsEqualToOne");
567
568 {
569 Teuchos::TimeMonitor MMM = *Teuchos::TimeMonitor::getNewTimer("GetLumpedMatrixDiagonal: parallel_for (doReciprocal)");
570 Kokkos::parallel_for(
571 "GetLumpedMatrixDiagonal", my_policy,
572 KOKKOS_LAMBDA(const int rowIdx) {
573 diag_dev(rowIdx, 0) = KAT_S::zero();
574 for (size_type entryIdx = local_mat_dev.graph.row_map(rowIdx);
575 entryIdx < local_mat_dev.graph.row_map(rowIdx + 1);
576 ++entryIdx) {
577 regSum(rowIdx) += local_mat_dev.values(entryIdx);
578 if (KAT_M::zero() < KAT_S::abs(local_mat_dev.values(entryIdx))) {
579 ++nnzPerRow(rowIdx);
580 }
581 diag_dev(rowIdx, 0) += KAT_S::abs(local_mat_dev.values(entryIdx));
582 if (rowIdx == local_mat_dev.graph.entries(entryIdx)) {
583 Kokkos::atomic_add(&avgAbsDiagVal_dev(), KAT_S::abs(local_mat_dev.values(entryIdx)));
584 }
585 }
586
587 if (nnzPerRow(rowIdx) == 1 && KAT_S::magnitude(diag_dev(rowIdx, 0)) == KAT_M::one()) {
588 Kokkos::atomic_add(&numDiagsEqualToOne_dev(), 1);
589 }
590 });
591 }
592 if (useAverageAbsDiagVal) {
593 Teuchos::TimeMonitor MMM = *Teuchos::TimeMonitor::getNewTimer("GetLumpedMatrixDiagonal: useAverageAbsDiagVal");
594 typename Kokkos::View<mag_type, execution_space>::host_mirror_type avgAbsDiagVal = Kokkos::create_mirror_view(avgAbsDiagVal_dev);
595 Kokkos::deep_copy(avgAbsDiagVal, avgAbsDiagVal_dev);
596 int numDiagsEqualToOne;
597 Kokkos::deep_copy(numDiagsEqualToOne, numDiagsEqualToOne_dev);
598
599 tol = TST::magnitude(100 * Teuchos::ScalarTraits<Scalar>::eps()) * (avgAbsDiagVal() - numDiagsEqualToOne) / (rowMap->getLocalNumElements() - numDiagsEqualToOne);
600 }
601
602 {
603 Teuchos::TimeMonitor MMM = *Teuchos::TimeMonitor::getNewTimer("ComputeLumpedDiagonalInverse: parallel_for (doReciprocal)");
604 Kokkos::parallel_for(
605 "ComputeLumpedDiagonalInverse", my_policy,
606 KOKKOS_LAMBDA(const int rowIdx) {
607 if (replaceSingleEntryRowWithZero && nnzPerRow(rowIdx) <= 1) {
608 diag_dev(rowIdx, 0) = KAT_S::zero();
609 } else if ((diag_dev(rowIdx, 0) != KAT_S::zero()) && (KAT_S::magnitude(diag_dev(rowIdx, 0)) < KAT_S::magnitude(2 * regSum(rowIdx)))) {
610 diag_dev(rowIdx, 0) = KAT_S::one() / KAT_S::magnitude(2 * regSum(rowIdx));
611 } else {
612 if (KAT_S::magnitude(diag_dev(rowIdx, 0)) > tol) {
613 diag_dev(rowIdx, 0) = KAT_S::one() / diag_dev(rowIdx, 0);
614 } else {
615 diag_dev(rowIdx, 0) = valReplacement_dev;
616 }
617 }
618 });
619 }
620
621 } else {
622 Teuchos::TimeMonitor MMM = *Teuchos::TimeMonitor::getNewTimer("GetLumpedMatrixDiagonal: parallel_for");
623 Kokkos::parallel_for(
624 "GetLumpedMatrixDiagonal", my_policy,
625 KOKKOS_LAMBDA(const int rowIdx) {
626 diag_dev(rowIdx, 0) = KAT_S::zero();
627 for (size_type entryIdx = local_mat_dev.graph.row_map(rowIdx);
628 entryIdx < local_mat_dev.graph.row_map(rowIdx + 1);
629 ++entryIdx) {
630 diag_dev(rowIdx, 0) += KAT_S::magnitude(local_mat_dev.values(entryIdx));
631 }
632 });
633 }
634 } else {
635 // Implement using Teuchos
636 Teuchos::TimeMonitor MMM = *Teuchos::TimeMonitor::getNewTimer("UtilitiesBase: GetLumpedMatrixDiagonal: (Teuchos implementation)");
637 ArrayRCP<Scalar> diagVals = diag->getDataNonConst(0);
638 Teuchos::Array<Scalar> regSum(diag->getLocalLength());
639 Teuchos::ArrayView<const LocalOrdinal> cols;
640 Teuchos::ArrayView<const Scalar> vals;
641
642 std::vector<int> nnzPerRow(rowMap->getLocalNumElements());
643
644 // FIXME 2021-10-22 JHU If this is called with doReciprocal=false, what should the correct behavior be? Currently,
645 // FIXME 2021-10-22 JHU the diagonal entry is set to be the sum of the absolute values of the row entries.
646
647 const Magnitude zeroMagn = TST::magnitude(zero);
648 Magnitude avgAbsDiagVal = TST::magnitude(zero);
649 int numDiagsEqualToOne = 0;
650 for (size_t i = 0; i < rowMap->getLocalNumElements(); ++i) {
651 nnzPerRow[i] = 0;
652 rcpA->getLocalRowView(i, cols, vals);
653 diagVals[i] = zero;
654 for (LocalOrdinal j = 0; j < cols.size(); ++j) {
655 regSum[i] += vals[j];
656 const Magnitude rowEntryMagn = TST::magnitude(vals[j]);
657 if (rowEntryMagn > zeroMagn)
658 nnzPerRow[i]++;
659 diagVals[i] += rowEntryMagn;
660 if (static_cast<size_t>(cols[j]) == i)
661 avgAbsDiagVal += rowEntryMagn;
662 }
663 if (nnzPerRow[i] == 1 && TST::magnitude(diagVals[i]) == 1.)
664 numDiagsEqualToOne++;
665 }
666 if (useAverageAbsDiagVal)
667 tol = TST::magnitude(100 * Teuchos::ScalarTraits<Scalar>::eps()) * (avgAbsDiagVal - numDiagsEqualToOne) / (rowMap->getLocalNumElements() - numDiagsEqualToOne);
668 if (doReciprocal) {
669 for (size_t i = 0; i < rowMap->getLocalNumElements(); ++i) {
670 if (replaceSingleEntryRowWithZero && nnzPerRow[i] <= static_cast<int>(1))
671 diagVals[i] = zero;
672 else if ((diagVals[i] != zero) && (TST::magnitude(diagVals[i]) < TST::magnitude(two * regSum[i])))
673 diagVals[i] = one / TST::magnitude((two * regSum[i]));
674 else {
675 if (TST::magnitude(diagVals[i]) > tol)
676 diagVals[i] = one / diagVals[i];
677 else {
678 diagVals[i] = valReplacement;
679 }
680 }
681 }
682 }
683 }
684 } else {
685 TEUCHOS_TEST_FOR_EXCEPTION(doReciprocal, Xpetra::Exceptions::RuntimeError,
686 "UtilitiesBase::GetLumpedMatrixDiagonal(): extracting reciprocal of diagonal of a blocked matrix is not supported");
687 diag = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(bA->getRangeMapExtractor()->getFullMap(), true);
688
689 for (size_t row = 0; row < bA->Rows(); ++row) {
690 for (size_t col = 0; col < bA->Cols(); ++col) {
691 if (!bA->getMatrix(row, col).is_null()) {
692 // if we are in Thyra mode, but the block (row,row) is again a blocked operator, we have to use (pseudo) Xpetra-style GIDs with offset!
693 bool bThyraMode = bA->getRangeMapExtractor()->getThyraMode() && (Teuchos::rcp_dynamic_cast<Xpetra::BlockedCrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>(bA->getMatrix(row, col)) == Teuchos::null);
694 RCP<Vector> ddtemp = bA->getRangeMapExtractor()->ExtractVector(diag, row, bThyraMode);
695 RCP<const Vector> dd = GetLumpedMatrixDiagonal(*(bA->getMatrix(row, col)));
696 ddtemp->update(Teuchos::as<Scalar>(1.0), *dd, Teuchos::as<Scalar>(1.0));
697 bA->getRangeMapExtractor()->InsertVector(ddtemp, row, diag, bThyraMode);
698 }
699 }
700 }
701 }
702
703 return diag;
704}
705
706template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
707Teuchos::RCP<Xpetra::Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
709 GetMatrixMaxMinusOffDiagonal(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A) {
710 // Get/Create distributed objects
711 RCP<const Map> rowMap = A.getRowMap();
712 auto diag = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(rowMap, false);
713
714 // Implement using Kokkos
715 using local_vector_type = typename Vector::dual_view_type::t_dev_um;
716 using local_matrix_type = typename Matrix::local_matrix_device_type;
717 using execution_space = typename local_vector_type::execution_space;
718 using values_type = typename local_matrix_type::values_type;
719 using scalar_type = typename values_type::non_const_value_type;
720 using KAT_S = typename KokkosKernels::ArithTraits<scalar_type>;
721
722 auto diag_dev = diag->getLocalViewDevice(Tpetra::Access::OverwriteAll);
723 auto local_mat_dev = A.getLocalMatrixDevice();
724 Kokkos::RangePolicy<execution_space, int> my_policy(0, static_cast<int>(diag_dev.extent(0)));
725
726 Kokkos::parallel_for(
727 "GetMatrixMaxMinusOffDiagonal", my_policy,
728 KOKKOS_LAMBDA(const LocalOrdinal rowIdx) {
729 auto mymax = KAT_S::zero();
730 auto row = local_mat_dev.rowConst(rowIdx);
731 for (LocalOrdinal entryIdx = 0; entryIdx < row.length; ++entryIdx) {
732 if (rowIdx != row.colidx(entryIdx)) {
733 if (KAT_S::real(mymax) < -KAT_S::real(row.value(entryIdx)))
734 mymax = -KAT_S::real(row.value(entryIdx));
735 }
736 }
737 diag_dev(rowIdx, 0) = mymax;
738 });
739
740 return diag;
741}
742
743template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
744Teuchos::RCP<Xpetra::Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
746 GetMatrixMaxMinusOffDiagonal(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A, const Xpetra::Vector<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>& BlockNumber) {
747 TEUCHOS_TEST_FOR_EXCEPTION(!A.getColMap()->isSameAs(*BlockNumber.getMap()), std::runtime_error, "GetMatrixMaxMinusOffDiagonal: BlockNumber must match's A's column map.");
748
749 // Get/Create distributed objects
750 RCP<const Map> rowMap = A.getRowMap();
751 auto diag = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(rowMap, false);
752
753 // Implement using Kokkos
754 using local_vector_type = typename Vector::dual_view_type::t_dev_um;
755 using local_matrix_type = typename Matrix::local_matrix_device_type;
756 using execution_space = typename local_vector_type::execution_space;
757 using values_type = typename local_matrix_type::values_type;
758 using scalar_type = typename values_type::non_const_value_type;
759 using KAT_S = typename KokkosKernels::ArithTraits<scalar_type>;
760
761 auto diag_dev = diag->getLocalViewDevice(Tpetra::Access::OverwriteAll);
762 auto local_mat_dev = A.getLocalMatrixDevice();
763 auto local_block_dev = BlockNumber.getLocalViewDevice(Tpetra::Access::ReadOnly);
764 Kokkos::RangePolicy<execution_space, int> my_policy(0, static_cast<int>(diag_dev.extent(0)));
765
766 Kokkos::parallel_for(
767 "GetMatrixMaxMinusOffDiagonal", my_policy,
768 KOKKOS_LAMBDA(const LocalOrdinal rowIdx) {
769 auto mymax = KAT_S::zero();
770 auto row = local_mat_dev.row(rowIdx);
771 for (LocalOrdinal entryIdx = 0; entryIdx < row.length; ++entryIdx) {
772 if ((rowIdx != row.colidx(entryIdx)) && (local_block_dev(rowIdx, 0) == local_block_dev(row.colidx(entryIdx), 0))) {
773 if (KAT_S::real(mymax) < -KAT_S::real(row.value(entryIdx)))
774 mymax = -KAT_S::real(row.value(entryIdx));
775 }
776 }
777 diag_dev(rowIdx, 0) = mymax;
778 });
779
780 return diag;
781}
782
783template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
784Teuchos::RCP<Xpetra::Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
786 GetInverse(Teuchos::RCP<const Xpetra::Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>> v, typename Teuchos::ScalarTraits<Scalar>::magnitudeType tol, Scalar valReplacement) {
787 RCP<Vector> ret = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(v->getMap(), true);
788
789 // check whether input vector "v" is a BlockedVector
790 RCP<const BlockedVector> bv = Teuchos::rcp_dynamic_cast<const BlockedVector>(v);
791 if (bv.is_null() == false) {
792 RCP<BlockedVector> bret = Teuchos::rcp_dynamic_cast<BlockedVector>(ret);
793 TEUCHOS_TEST_FOR_EXCEPTION(bret.is_null() == true, MueLu::Exceptions::RuntimeError, "MueLu::UtilitiesBase::GetInverse: return vector should be of type BlockedVector");
794 RCP<const BlockedMap> bmap = bv->getBlockedMap();
795 for (size_t r = 0; r < bmap->getNumMaps(); ++r) {
796 RCP<const MultiVector> submvec = bv->getMultiVector(r, bmap->getThyraMode());
797 RCP<const Vector> subvec = submvec->getVector(0);
798 RCP<Vector> subvecinf = MueLu::UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::GetInverse(subvec, tol, valReplacement);
799 bret->setMultiVector(r, subvecinf, bmap->getThyraMode());
800 }
801 return ret;
802 }
803
804 // v is an {Epetra,Tpetra}Vector: work with the underlying raw data
805 ArrayRCP<Scalar> retVals = ret->getDataNonConst(0);
806 ArrayRCP<const Scalar> inputVals = v->getData(0);
807 for (size_t i = 0; i < v->getMap()->getLocalNumElements(); ++i) {
808 if (Teuchos::ScalarTraits<Scalar>::magnitude(inputVals[i]) > tol)
809 retVals[i] = Teuchos::ScalarTraits<Scalar>::one() / inputVals[i];
810 else
811 retVals[i] = valReplacement;
812 }
813 return ret;
814}
815
816// template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
817// RCP<Xpetra::Vector<Scalar,LocalOrdinal,GlobalOrdinal,Node> >
818// UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
819// GetMatrixOverlappedDiagonal(const Xpetra::Matrix<Scalar,LocalOrdinal,GlobalOrdinal,Node> & A) {
820// RCP<const Map> rowMap = A.getRowMap(), colMap = A.getColMap();
821
822// // Undo block map (if we have one)
823// RCP<const BlockedMap> browMap = Teuchos::rcp_dynamic_cast<const BlockedMap>(rowMap);
824// if(!browMap.is_null()) rowMap = browMap->getMap();
825
826// RCP<Vector> localDiag = Xpetra::VectorFactory<Scalar,LocalOrdinal,GlobalOrdinal,Node>::Build(rowMap);
827// try {
828// const CrsMatrixWrap* crsOp = dynamic_cast<const CrsMatrixWrap*>(&A);
829// if (crsOp == NULL) {
830// throw Exceptions::RuntimeError("cast to CrsMatrixWrap failed");
831// }
832// Teuchos::ArrayRCP<size_t> offsets;
833// crsOp->getLocalDiagOffsets(offsets);
834// crsOp->getLocalDiagCopy(*localDiag,offsets());
835// }
836// catch (...) {
837// ArrayRCP<Scalar> localDiagVals = localDiag->getDataNonConst(0);
838// Teuchos::ArrayRCP<Scalar> diagVals = GetMatrixDiagonal(A);
839// for (LocalOrdinal i = 0; i < localDiagVals.size(); i++)
840// localDiagVals[i] = diagVals[i];
841// localDiagVals = diagVals = null;
842// }
843
844// RCP<Vector> diagonal = Xpetra::VectorFactory<Scalar,LocalOrdinal,GlobalOrdinal,Node>::Build(colMap);
845// RCP< const Xpetra::Import<LocalOrdinal,GlobalOrdinal,Node> > importer;
846// importer = A.getCrsGraph()->getImporter();
847// if (importer == Teuchos::null) {
848// importer = Xpetra::ImportFactory<LocalOrdinal,GlobalOrdinal,Node>::Build(rowMap, colMap);
849// }
850// diagonal->doImport(*localDiag, *(importer), Xpetra::INSERT);
851// return diagonal;
852// }
853
854template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
855RCP<Xpetra::Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
857 GetMatrixOverlappedDiagonal(const Matrix& A) {
858 RCP<const Map> rowMap = A.getRowMap(), colMap = A.getColMap();
859 RCP<Vector> localDiag = GetMatrixDiagonal(A);
860 RCP<const Import> importer = A.getCrsGraph()->getImporter();
861 if (importer.is_null() && !rowMap->isSameAs(*colMap)) {
862 importer = ImportFactory::Build(rowMap, colMap);
863 }
864 if (!importer.is_null()) {
865 RCP<Vector> diagonal = VectorFactory::Build(colMap, false);
866 diagonal->doImport(*localDiag, *(importer), Xpetra::INSERT);
867 return diagonal;
868 } else
869 return localDiag;
870}
871
872template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
873RCP<Xpetra::Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
875 GetMatrixOverlappedDeletedRowsum(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A) {
876 using STS = typename Teuchos::ScalarTraits<SC>;
877
878 // Undo block map (if we have one)
879 RCP<const Map> rowMap = A.getRowMap(), colMap = A.getColMap();
880 RCP<const BlockedMap> browMap = Teuchos::rcp_dynamic_cast<const BlockedMap>(rowMap);
881 if (!browMap.is_null()) rowMap = browMap->getMap();
882
883 RCP<Vector> local = Xpetra::VectorFactory<SC, LO, GO, Node>::Build(rowMap);
884 RCP<Vector> ghosted = Xpetra::VectorFactory<SC, LO, GO, Node>::Build(colMap, true);
885 ArrayRCP<SC> localVals = local->getDataNonConst(0);
886
887 for (LO row = 0; row < static_cast<LO>(A.getRowMap()->getLocalNumElements()); ++row) {
888 size_t nnz = A.getNumEntriesInLocalRow(row);
889 ArrayView<const LO> indices;
890 ArrayView<const SC> vals;
891 A.getLocalRowView(row, indices, vals);
892
893 SC si = STS::zero();
894
895 for (LO colID = 0; colID < static_cast<LO>(nnz); colID++) {
896 if (indices[colID] != row) {
897 si += vals[colID];
898 }
899 }
900 localVals[row] = si;
901 }
902
903 RCP<const Xpetra::Import<LO, GO, Node>> importer;
904 importer = A.getCrsGraph()->getImporter();
905 if (importer == Teuchos::null) {
906 importer = Xpetra::ImportFactory<LO, GO, Node>::Build(rowMap, colMap);
907 }
908 ghosted->doImport(*local, *(importer), Xpetra::INSERT);
909 return ghosted;
910}
911
912template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
913RCP<Xpetra::Vector<typename Teuchos::ScalarTraits<Scalar>::magnitudeType, LocalOrdinal, GlobalOrdinal, Node>>
915 GetMatrixOverlappedAbsDeletedRowsum(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A) {
916 RCP<const Map> rowMap = A.getRowMap(), colMap = A.getColMap();
917 using STS = typename Teuchos::ScalarTraits<Scalar>;
918 using MTS = typename Teuchos::ScalarTraits<Magnitude>;
919 using MT = Magnitude;
920 using RealValuedVector = Xpetra::Vector<MT, LO, GO, Node>;
921
922 // Undo block map (if we have one)
923 RCP<const BlockedMap> browMap = Teuchos::rcp_dynamic_cast<const BlockedMap>(rowMap);
924 if (!browMap.is_null()) rowMap = browMap->getMap();
925
926 RCP<RealValuedVector> local = Xpetra::VectorFactory<MT, LO, GO, Node>::Build(rowMap);
927 RCP<RealValuedVector> ghosted = Xpetra::VectorFactory<MT, LO, GO, Node>::Build(colMap, true);
928 ArrayRCP<MT> localVals = local->getDataNonConst(0);
929
930 for (LO rowIdx = 0; rowIdx < static_cast<LO>(A.getRowMap()->getLocalNumElements()); ++rowIdx) {
931 size_t nnz = A.getNumEntriesInLocalRow(rowIdx);
932 ArrayView<const LO> indices;
933 ArrayView<const SC> vals;
934 A.getLocalRowView(rowIdx, indices, vals);
935
936 MT si = MTS::zero();
937
938 for (LO colID = 0; colID < static_cast<LO>(nnz); ++colID) {
939 if (indices[colID] != rowIdx) {
940 si += STS::magnitude(vals[colID]);
941 }
942 }
943 localVals[rowIdx] = si;
944 }
945
946 RCP<const Xpetra::Import<LO, GO, Node>> importer;
947 importer = A.getCrsGraph()->getImporter();
948 if (importer == Teuchos::null) {
949 importer = Xpetra::ImportFactory<LO, GO, Node>::Build(rowMap, colMap);
950 }
951 ghosted->doImport(*local, *(importer), Xpetra::INSERT);
952 return ghosted;
953}
954
955template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
958 CountNegativeDiagonalEntries(const Matrix& A) {
959 using local_matrix_type = typename Matrix::local_matrix_device_type;
960 using execution_space = typename local_matrix_type::execution_space;
961 using KAT_S = typename KokkosKernels::ArithTraits<typename local_matrix_type::value_type>;
962
963 auto local_mat_dev = A.getLocalMatrixDevice();
964 Kokkos::RangePolicy<execution_space, int> my_policy(0, static_cast<int>(local_mat_dev.numRows()));
965 GlobalOrdinal count_l = 0, count_g = 0;
966
967 Kokkos::parallel_reduce(
968 "CountNegativeDiagonalEntries", my_policy,
969 KOKKOS_LAMBDA(const LocalOrdinal rowIdx, GlobalOrdinal& sum) {
970 auto row = local_mat_dev.row(rowIdx);
971 for (LocalOrdinal entryIdx = 0; entryIdx < row.length; ++entryIdx) {
972 if (rowIdx == row.colidx(entryIdx) && KAT_S::real(row.value(entryIdx)) < 0)
973 sum++;
974 }
975 },
976 count_l);
977
978 MueLu_sumAll(A.getRowMap()->getComm(), count_l, count_g);
979 return count_g;
980}
981
982template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
983Teuchos::Array<typename Teuchos::ScalarTraits<Scalar>::magnitudeType>
985 ResidualNorm(const Xpetra::Operator<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Op, const MultiVector& X, const MultiVector& RHS) {
986 TEUCHOS_TEST_FOR_EXCEPTION(X.getNumVectors() != RHS.getNumVectors(), Exceptions::RuntimeError, "Number of solution vectors != number of right-hand sides")
987 const size_t numVecs = X.getNumVectors();
988 RCP<MultiVector> RES = Residual(Op, X, RHS);
989 Teuchos::Array<Magnitude> norms(numVecs);
990 RES->norm2(norms);
991 return norms;
992}
993
994template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
995Teuchos::Array<typename Teuchos::ScalarTraits<Scalar>::magnitudeType>
997 ResidualNorm(const Xpetra::Operator<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Op, const MultiVector& X, const MultiVector& RHS, MultiVector& Resid) {
998 TEUCHOS_TEST_FOR_EXCEPTION(X.getNumVectors() != RHS.getNumVectors(), Exceptions::RuntimeError, "Number of solution vectors != number of right-hand sides")
999 const size_t numVecs = X.getNumVectors();
1000 Residual(Op, X, RHS, Resid);
1001 Teuchos::Array<Magnitude> norms(numVecs);
1002 Resid.norm2(norms);
1003 return norms;
1004}
1005
1006template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1007RCP<Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
1009 Residual(const Xpetra::Operator<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Op, const MultiVector& X, const MultiVector& RHS) {
1010 TEUCHOS_TEST_FOR_EXCEPTION(X.getNumVectors() != RHS.getNumVectors(), Exceptions::RuntimeError, "Number of solution vectors != number of right-hand sides")
1011 const size_t numVecs = X.getNumVectors();
1012 // TODO Op.getRangeMap should return a BlockedMap if it is a BlockedCrsOperator
1013 RCP<MultiVector> RES = Xpetra::MultiVectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(RHS.getMap(), numVecs, false); // no need to initialize to zero
1014 Op.residual(X, RHS, *RES);
1015 return RES;
1016}
1017
1018template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1020 Residual(const Xpetra::Operator<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Op, const MultiVector& X, const MultiVector& RHS, MultiVector& Resid) {
1021 TEUCHOS_TEST_FOR_EXCEPTION(X.getNumVectors() != RHS.getNumVectors(), Exceptions::RuntimeError, "Number of solution vectors != number of right-hand sides");
1022 TEUCHOS_TEST_FOR_EXCEPTION(Resid.getNumVectors() != RHS.getNumVectors(), Exceptions::RuntimeError, "Number of residual vectors != number of right-hand sides");
1023 Op.residual(X, RHS, Resid);
1024}
1025
1026template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1027Scalar
1029 PowerMethod(const Matrix& A, bool scaleByDiag,
1030 LocalOrdinal niters, typename Teuchos::ScalarTraits<Scalar>::magnitudeType tolerance, typename Teuchos::ScalarTraits<Scalar>::magnitudeType diagonalReplacementTolerance, bool verbose, unsigned int seed) {
1031 TEUCHOS_TEST_FOR_EXCEPTION(!(A.getRangeMap()->isSameAs(*(A.getDomainMap()))), Exceptions::Incompatible,
1032 "Utils::PowerMethod: operator must have domain and range maps that are equivalent.");
1033
1034 // power iteration
1035 RCP<Vector> diagInvVec;
1036 if (scaleByDiag) {
1037 diagInvVec = GetMatrixDiagonalInverse(A, diagonalReplacementTolerance);
1038 }
1039
1040 Scalar lambda = PowerMethod(A, diagInvVec, niters, tolerance, verbose, seed);
1041 return lambda;
1042}
1043
1044template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1045Scalar
1046UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
1047 PowerMethod(const Matrix& A, const RCP<Vector>& diagInvVec,
1048 LocalOrdinal niters, typename Teuchos::ScalarTraits<Scalar>::magnitudeType tolerance, bool verbose, unsigned int seed) {
1049 TEUCHOS_TEST_FOR_EXCEPTION(!(A.getRangeMap()->isSameAs(*(A.getDomainMap()))), Exceptions::Incompatible,
1050 "Utils::PowerMethod: operator must have domain and range maps that are equivalent.");
1051
1052 // Create three vectors, fill z with random numbers
1053 RCP<Vector> q = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(A.getDomainMap(), true);
1054 RCP<Vector> r = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(A.getRangeMap(), true);
1055 RCP<Vector> z = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(A.getRangeMap(), false);
1056
1057 z->setSeed(seed); // seed random number generator
1058 z->randomize(true); // use Xpetra implementation: -> same results for Epetra and Tpetra
1059
1060 Teuchos::Array<Magnitude> norms(1);
1061
1062 typedef Teuchos::ScalarTraits<Scalar> STS;
1063
1064 const Scalar zero = STS::zero(), one = STS::one();
1065
1066 Scalar lambda = zero;
1067 Magnitude residual = STS::magnitude(zero);
1068
1069 // power iteration
1070 for (int iter = 0; iter < niters; ++iter) {
1071 z->norm2(norms); // Compute 2-norm of z
1072 q->update(one / norms[0], *z, zero); // Set q = z / normz
1073 A.apply(*q, *z); // Compute z = A*q
1074 if (diagInvVec != Teuchos::null)
1075 z->elementWiseMultiply(one, *diagInvVec, *z, zero);
1076 lambda = q->dot(*z); // Approximate maximum eigenvalue: lamba = dot(q,z)
1077
1078 if (iter % 100 == 0 || iter + 1 == niters) {
1079 r->update(1.0, *z, -lambda, *q, zero); // Compute A*q - lambda*q
1080 r->norm2(norms);
1081 residual = STS::magnitude(norms[0] / lambda);
1082 if (verbose) {
1083 std::cout << "Iter = " << iter
1084 << " Lambda = " << lambda
1085 << " Residual of A*q - lambda*q = " << residual
1086 << std::endl;
1087 }
1088 }
1089 if (residual < tolerance)
1090 break;
1091 }
1092 return lambda;
1093}
1094
1095template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1096RCP<Teuchos::FancyOStream>
1098 MakeFancy(std::ostream& os) {
1099 RCP<Teuchos::FancyOStream> fancy = Teuchos::fancyOStream(Teuchos::rcpFromRef(os));
1100 return fancy;
1101}
1102
1103template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1104typename Teuchos::ScalarTraits<Scalar>::magnitudeType
1106 Distance2(const Teuchos::Array<Teuchos::ArrayRCP<const Scalar>>& v, LocalOrdinal i0, LocalOrdinal i1) {
1107 const size_t numVectors = v.size();
1108
1109 Scalar d = Teuchos::ScalarTraits<Scalar>::zero();
1110 for (size_t j = 0; j < numVectors; j++) {
1111 d += (v[j][i0] - v[j][i1]) * (v[j][i0] - v[j][i1]);
1112 }
1113 return Teuchos::ScalarTraits<Scalar>::magnitude(d);
1114}
1115
1116template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1117typename Teuchos::ScalarTraits<Scalar>::magnitudeType
1119 Distance2(const Teuchos::ArrayView<double>& weight, const Teuchos::Array<Teuchos::ArrayRCP<const Scalar>>& v, LocalOrdinal i0, LocalOrdinal i1) {
1120 const size_t numVectors = v.size();
1121 using MT = typename Teuchos::ScalarTraits<Scalar>::magnitudeType;
1122
1123 Scalar d = Teuchos::ScalarTraits<Scalar>::zero();
1124 for (size_t j = 0; j < numVectors; j++) {
1125 d += Teuchos::as<MT>(weight[j]) * (v[j][i0] - v[j][i1]) * (v[j][i0] - v[j][i1]);
1126 }
1127 return Teuchos::ScalarTraits<Scalar>::magnitude(d);
1128}
1129
1130template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1131Teuchos::ArrayRCP<const bool>
1133 DetectDirichletRows(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A, const typename Teuchos::ScalarTraits<Scalar>::magnitudeType& tol, bool count_twos_as_dirichlet) {
1134 LocalOrdinal numRows = A.getLocalNumRows();
1135 typedef Teuchos::ScalarTraits<Scalar> STS;
1136 ArrayRCP<bool> boundaryNodes(numRows, true);
1137 if (count_twos_as_dirichlet) {
1138 for (LocalOrdinal row = 0; row < numRows; row++) {
1139 ArrayView<const LocalOrdinal> indices;
1140 ArrayView<const Scalar> vals;
1141 A.getLocalRowView(row, indices, vals);
1142 size_t nnz = A.getNumEntriesInLocalRow(row);
1143 if (nnz > 2) {
1144 size_t col;
1145 for (col = 0; col < nnz; col++)
1146 if ((indices[col] != row) && STS::magnitude(vals[col]) > tol) {
1147 if (!boundaryNodes[row])
1148 break;
1149 boundaryNodes[row] = false;
1150 }
1151 if (col == nnz)
1152 boundaryNodes[row] = true;
1153 }
1154 }
1155 } else {
1156 for (LocalOrdinal row = 0; row < numRows; row++) {
1157 ArrayView<const LocalOrdinal> indices;
1158 ArrayView<const Scalar> vals;
1159 A.getLocalRowView(row, indices, vals);
1160 size_t nnz = A.getNumEntriesInLocalRow(row);
1161 if (nnz > 1)
1162 for (size_t col = 0; col < nnz; col++)
1163 if ((indices[col] != row) && STS::magnitude(vals[col]) > tol) {
1164 boundaryNodes[row] = false;
1165 break;
1166 }
1167 }
1168 }
1169 return boundaryNodes;
1170}
1171
1172template <class CrsMatrix>
1173KOKKOS_FORCEINLINE_FUNCTION bool isDirichletRow(typename CrsMatrix::ordinal_type rowId,
1174 KokkosSparse::SparseRowViewConst<CrsMatrix>& row,
1175 const typename KokkosKernels::ArithTraits<typename CrsMatrix::value_type>::magnitudeType& tol,
1176 const bool count_twos_as_dirichlet) {
1177 using ATS = KokkosKernels::ArithTraits<typename CrsMatrix::value_type>;
1178
1179 auto length = row.length;
1180 bool boundaryNode = true;
1181
1182 if (count_twos_as_dirichlet) {
1183 if (length > 2) {
1184 decltype(length) colID = 0;
1185 for (; colID < length; colID++)
1186 if ((row.colidx(colID) != rowId) &&
1187 (ATS::magnitude(row.value(colID)) > tol)) {
1188 if (!boundaryNode)
1189 break;
1190 boundaryNode = false;
1191 }
1192 if (colID == length)
1193 boundaryNode = true;
1194 }
1195 } else {
1196 for (decltype(length) colID = 0; colID < length; colID++)
1197 if ((row.colidx(colID) != rowId) &&
1198 (ATS::magnitude(row.value(colID)) > tol)) {
1199 boundaryNode = false;
1200 break;
1201 }
1202 }
1203 return boundaryNode;
1204}
1205
1206template <class SC, class LO, class GO, class NO, class memory_space>
1207Kokkos::View<bool*, memory_space>
1208DetectDirichletRows_kokkos(const Xpetra::Matrix<SC, LO, GO, NO>& A,
1209 const typename Teuchos::ScalarTraits<SC>::magnitudeType& tol,
1210 const bool count_twos_as_dirichlet) {
1211 using impl_scalar_type = typename KokkosKernels::ArithTraits<SC>::val_type;
1212 using ATS = KokkosKernels::ArithTraits<impl_scalar_type>;
1213 using range_type = Kokkos::RangePolicy<LO, typename NO::execution_space>;
1214 using helpers = Xpetra::Helpers<SC, LO, GO, NO>;
1215
1216 Kokkos::View<bool*, typename NO::device_type::memory_space> boundaryNodes;
1217
1218 if (helpers::isTpetraBlockCrs(A)) {
1219 const Tpetra::BlockCrsMatrix<SC, LO, GO, NO>& Am = toTpetraBlock(A);
1220 auto b_graph = Am.getCrsGraph().getLocalGraphDevice();
1221 auto b_rowptr = Am.getCrsGraph().getLocalRowPtrsDevice();
1222 auto values = Am.getValuesDevice();
1223 LO numBlockRows = Am.getLocalNumRows();
1224 const LO stride = Am.getBlockSize() * Am.getBlockSize();
1225
1226 boundaryNodes = Kokkos::View<bool*, typename NO::device_type::memory_space>(Kokkos::ViewAllocateWithoutInitializing("boundaryNodes"), numBlockRows);
1227
1228 if (count_twos_as_dirichlet)
1229 throw Exceptions::RuntimeError("BlockCrs does not support counting twos as Dirichlet");
1230
1231 Kokkos::parallel_for(
1232 "MueLu:Utils::DetectDirichletRowsBlockCrs", range_type(0, numBlockRows),
1233 KOKKOS_LAMBDA(const LO row) {
1234 auto rowView = b_graph.rowConst(row);
1235 auto length = rowView.length;
1236 LO valstart = b_rowptr[row] * stride;
1237
1238 boundaryNodes(row) = true;
1239 decltype(length) colID = 0;
1240 for (; colID < length; colID++) {
1241 if (rowView.colidx(colID) != row) {
1242 LO current = valstart + colID * stride;
1243 for (LO k = 0; k < stride; k++) {
1244 if (ATS::magnitude(values[current + k]) > tol) {
1245 boundaryNodes(row) = false;
1246 break;
1247 }
1248 }
1249 }
1250 if (boundaryNodes(row) == false)
1251 break;
1252 }
1253 });
1254 } else {
1255 auto localMatrix = A.getLocalMatrixDevice();
1256 LO numRows = A.getLocalNumRows();
1257 boundaryNodes = Kokkos::View<bool*, typename NO::device_type::memory_space>(Kokkos::ViewAllocateWithoutInitializing("boundaryNodes"), numRows);
1258
1259 Kokkos::parallel_for(
1260 "MueLu:Utils::DetectDirichletRows", range_type(0, numRows),
1261 KOKKOS_LAMBDA(const LO row) {
1262 auto rowView = localMatrix.rowConst(row);
1263 boundaryNodes(row) = isDirichletRow(row, rowView, tol, count_twos_as_dirichlet);
1264 });
1265 }
1266 if constexpr (std::is_same<memory_space, typename NO::device_type::memory_space>::value)
1267 return boundaryNodes;
1268 else {
1269 Kokkos::View<bool*, memory_space> boundaryNodes2(Kokkos::ViewAllocateWithoutInitializing("boundaryNodes"), boundaryNodes.extent(0));
1270 Kokkos::deep_copy(boundaryNodes2, boundaryNodes);
1271 return boundaryNodes2;
1272 }
1273 // CAG: No idea why this is needed to avoid "warning: missing return statement at end of non-void function"
1274 Kokkos::View<bool*, memory_space> dummy("dummy", 0);
1275 return dummy;
1276}
1277
1278template <class SC, class LO, class GO, class NO>
1279Kokkos::View<bool*, typename NO::device_type::memory_space>
1281 DetectDirichletRows_kokkos(const Xpetra::Matrix<SC, LO, GO, NO>& A,
1282 const typename Teuchos::ScalarTraits<SC>::magnitudeType& tol,
1283 const bool count_twos_as_dirichlet) {
1284 return MueLu::DetectDirichletRows_kokkos<SC, LO, GO, NO, typename NO::device_type::memory_space>(A, tol, count_twos_as_dirichlet);
1285}
1286
1287template <class SC, class LO, class GO, class NO>
1288Kokkos::View<bool*, typename Kokkos::HostSpace>
1290 DetectDirichletRows_kokkos_host(const Xpetra::Matrix<SC, LO, GO, NO>& A,
1291 const typename Teuchos::ScalarTraits<SC>::magnitudeType& tol,
1292 const bool count_twos_as_dirichlet) {
1293 return MueLu::DetectDirichletRows_kokkos<SC, LO, GO, NO, typename Kokkos::HostSpace>(A, tol, count_twos_as_dirichlet);
1294}
1295
1296template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1297Teuchos::ArrayRCP<const bool>
1299 DetectDirichletRowsExt(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A, bool& bHasZeroDiagonal, const typename Teuchos::ScalarTraits<Scalar>::magnitudeType& tol) {
1300 // assume that there is no zero diagonal in matrix
1301 bHasZeroDiagonal = false;
1302
1303 Teuchos::RCP<Vector> diagVec = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(A.getRowMap());
1304 A.getLocalDiagCopy(*diagVec);
1305 Teuchos::ArrayRCP<const Scalar> diagVecData = diagVec->getData(0);
1306
1307 LocalOrdinal numRows = A.getLocalNumRows();
1308 typedef Teuchos::ScalarTraits<Scalar> STS;
1309 ArrayRCP<bool> boundaryNodes(numRows, false);
1310 for (LocalOrdinal row = 0; row < numRows; row++) {
1311 ArrayView<const LocalOrdinal> indices;
1312 ArrayView<const Scalar> vals;
1313 A.getLocalRowView(row, indices, vals);
1314 size_t nnz = 0; // collect nonzeros in row (excluding the diagonal)
1315 bool bHasDiag = false;
1316 for (decltype(indices.size()) col = 0; col < indices.size(); col++) {
1317 if (indices[col] != row) {
1318 if (STS::magnitude(vals[col] / STS::magnitude(sqrt(STS::magnitude(diagVecData[row]) * STS::magnitude(diagVecData[col])))) > tol) {
1319 nnz++;
1320 }
1321 } else
1322 bHasDiag = true; // found a diagonal entry
1323 }
1324 if (bHasDiag == false)
1325 bHasZeroDiagonal = true; // we found at least one row without a diagonal
1326 else if (nnz == 0)
1327 boundaryNodes[row] = true;
1328 }
1329 return boundaryNodes;
1330}
1331
1332template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1334 EnforceInitialCondition(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
1335 const Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>& RHS,
1336 Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>& InitialGuess,
1337 const typename Teuchos::ScalarTraits<SC>::magnitudeType& tol,
1338 const bool count_twos_as_dirichlet) {
1339 using range_type = Kokkos::RangePolicy<LO, typename Node::execution_space>;
1340
1341 LocalOrdinal numRows = A.getLocalNumRows();
1342 LocalOrdinal numVectors = RHS.getNumVectors();
1343 TEUCHOS_ASSERT_EQUALITY(numVectors, Teuchos::as<LocalOrdinal>(InitialGuess.getNumVectors()));
1344 if (Behavior::debug())
1345 TEUCHOS_ASSERT(RHS.getMap()->isCompatible(*InitialGuess.getMap()));
1346
1347 auto lclRHS = RHS.getLocalViewDevice(Tpetra::Access::ReadOnly);
1348 auto lclInitialGuess = InitialGuess.getLocalViewDevice(Tpetra::Access::ReadWrite);
1349 auto lclA = A.getLocalMatrixDevice();
1350
1351 Kokkos::parallel_for(
1352 "MueLu:Utils::EnforceInitialCondition", range_type(0, numRows),
1353 KOKKOS_LAMBDA(const LO i) {
1354 auto row = lclA.rowConst(i);
1355 if (isDirichletRow(i, row, tol, count_twos_as_dirichlet)) {
1356 for (LocalOrdinal j = 0; j < numVectors; ++j)
1357 lclInitialGuess(i, j) = lclRHS(i, j);
1358 }
1359 });
1360}
1361
1362template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1364 FindNonZeros(const Teuchos::ArrayRCP<const Scalar> vals,
1365 Teuchos::ArrayRCP<bool> nonzeros) {
1366 TEUCHOS_ASSERT(vals.size() == nonzeros.size());
1367 typedef typename Teuchos::ScalarTraits<Scalar>::magnitudeType magnitudeType;
1368 const magnitudeType eps = 2.0 * Teuchos::ScalarTraits<magnitudeType>::eps();
1369 for (size_t i = 0; i < static_cast<size_t>(vals.size()); i++) {
1370 nonzeros[i] = (Teuchos::ScalarTraits<Scalar>::magnitude(vals[i]) > eps);
1371 }
1372}
1373
1374// Find Nonzeros in a device view
1375template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1377 FindNonZeros(const typename Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>::dual_view_type::t_dev_const_um vals,
1378 Kokkos::View<bool*, typename Node::device_type> nonzeros) {
1379 using ATS = KokkosKernels::ArithTraits<Scalar>;
1380 using impl_ATS = KokkosKernels::ArithTraits<typename ATS::val_type>;
1381 using range_type = Kokkos::RangePolicy<LocalOrdinal, typename Node::execution_space>;
1382 TEUCHOS_ASSERT(vals.extent(0) == nonzeros.extent(0));
1383 const typename ATS::magnitudeType eps = 2.0 * impl_ATS::eps();
1384
1385 Kokkos::parallel_for(
1386 "MueLu:Maxwell1::FindNonZeros", range_type(0, vals.extent(0)),
1387 KOKKOS_LAMBDA(const size_t i) {
1388 nonzeros(i) = (impl_ATS::magnitude(vals(i, 0)) > eps);
1389 });
1390}
1391
1392template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1394 DetectDirichletColsAndDomains(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
1395 const Teuchos::ArrayRCP<bool>& dirichletRows,
1396 Teuchos::ArrayRCP<bool> dirichletCols,
1397 Teuchos::ArrayRCP<bool> dirichletDomain) {
1398 const Scalar one = Teuchos::ScalarTraits<Scalar>::one();
1399 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> domMap = A.getDomainMap();
1400 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> rowMap = A.getRowMap();
1401 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> colMap = A.getColMap();
1402 TEUCHOS_ASSERT(static_cast<size_t>(dirichletRows.size()) == rowMap->getLocalNumElements());
1403 TEUCHOS_ASSERT(static_cast<size_t>(dirichletCols.size()) == colMap->getLocalNumElements());
1404 TEUCHOS_ASSERT(static_cast<size_t>(dirichletDomain.size()) == domMap->getLocalNumElements());
1405 RCP<Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>> myColsToZero = Xpetra::MultiVectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(colMap, 1, /*zeroOut=*/true);
1406 // Find all local column indices that are in Dirichlet rows, record in myColsToZero as 1.0
1407 for (size_t i = 0; i < (size_t)dirichletRows.size(); i++) {
1408 if (dirichletRows[i]) {
1409 ArrayView<const LocalOrdinal> indices;
1410 ArrayView<const Scalar> values;
1411 A.getLocalRowView(i, indices, values);
1412 for (size_t j = 0; j < static_cast<size_t>(indices.size()); j++)
1413 myColsToZero->replaceLocalValue(indices[j], 0, one);
1414 }
1415 }
1416
1417 RCP<Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>> globalColsToZero;
1418 RCP<const Xpetra::Import<LocalOrdinal, GlobalOrdinal, Node>> importer = A.getCrsGraph()->getImporter();
1419 if (!importer.is_null()) {
1420 globalColsToZero = Xpetra::MultiVectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(domMap, 1, /*zeroOut=*/true);
1421 // export to domain map
1422 globalColsToZero->doExport(*myColsToZero, *importer, Xpetra::ADD);
1423 // import to column map
1424 myColsToZero->doImport(*globalColsToZero, *importer, Xpetra::INSERT);
1425 } else
1426 globalColsToZero = myColsToZero;
1427
1428 FindNonZeros(globalColsToZero->getData(0), dirichletDomain);
1429 FindNonZeros(myColsToZero->getData(0), dirichletCols);
1430}
1431
1432template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1434 DetectDirichletColsAndDomains(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
1435 const Kokkos::View<bool*, typename Node::device_type>& dirichletRows,
1436 Kokkos::View<bool*, typename Node::device_type> dirichletCols,
1437 Kokkos::View<bool*, typename Node::device_type> dirichletDomain) {
1438 using ATS = KokkosKernels::ArithTraits<Scalar>;
1439 using impl_ATS = KokkosKernels::ArithTraits<typename ATS::val_type>;
1440 using range_type = Kokkos::RangePolicy<LocalOrdinal, typename Node::execution_space>;
1441 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> domMap = A.getDomainMap();
1442 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> rowMap = A.getRowMap();
1443 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> colMap = A.getColMap();
1444 TEUCHOS_ASSERT(dirichletRows.extent(0) == rowMap->getLocalNumElements());
1445 TEUCHOS_ASSERT(dirichletCols.extent(0) == colMap->getLocalNumElements());
1446 TEUCHOS_ASSERT(dirichletDomain.extent(0) == domMap->getLocalNumElements());
1447 RCP<Xpetra::Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>> myColsToZero = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(colMap, /*zeroOut=*/true);
1448 // Find all local column indices that are in Dirichlet rows, record in myColsToZero as 1.0
1449 auto myColsToZeroView = myColsToZero->getLocalViewDevice(Tpetra::Access::ReadWrite);
1450 auto localMatrix = A.getLocalMatrixDevice();
1451 Kokkos::parallel_for(
1452 "MueLu:Maxwell1::DetectDirichletCols", range_type(0, rowMap->getLocalNumElements()),
1453 KOKKOS_LAMBDA(const LocalOrdinal row) {
1454 if (dirichletRows(row)) {
1455 auto rowView = localMatrix.row(row);
1456 auto length = rowView.length;
1457
1458 for (decltype(length) colID = 0; colID < length; colID++)
1459 myColsToZeroView(rowView.colidx(colID), 0) = impl_ATS::one();
1460 }
1461 });
1462
1463 RCP<Xpetra::Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>> globalColsToZero;
1464 RCP<const Xpetra::Import<LocalOrdinal, GlobalOrdinal, Node>> importer = A.getCrsGraph()->getImporter();
1465 if (!importer.is_null()) {
1466 globalColsToZero = Xpetra::VectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(domMap, /*zeroOut=*/true);
1467 // export to domain map
1468 globalColsToZero->doExport(*myColsToZero, *importer, Xpetra::ADD);
1469 // import to column map
1470 myColsToZero->doImport(*globalColsToZero, *importer, Xpetra::INSERT);
1471 } else
1472 globalColsToZero = myColsToZero;
1473 UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::FindNonZeros(globalColsToZero->getLocalViewDevice(Tpetra::Access::ReadOnly), dirichletDomain);
1474 UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::FindNonZeros(myColsToZero->getLocalViewDevice(Tpetra::Access::ReadOnly), dirichletCols);
1475}
1476
1477template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1479 ApplyRowSumCriterion(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A, const typename Teuchos::ScalarTraits<Scalar>::magnitudeType rowSumTol, Teuchos::ArrayRCP<bool>& dirichletRows) {
1480 typedef Teuchos::ScalarTraits<Scalar> STS;
1481 typedef typename Teuchos::ScalarTraits<Scalar>::magnitudeType MT;
1482 typedef Teuchos::ScalarTraits<MT> MTS;
1483 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> rowmap = A.getRowMap();
1484 for (LocalOrdinal row = 0; row < Teuchos::as<LocalOrdinal>(rowmap->getLocalNumElements()); ++row) {
1485 size_t nnz = A.getNumEntriesInLocalRow(row);
1486 ArrayView<const LocalOrdinal> indices;
1487 ArrayView<const Scalar> vals;
1488 A.getLocalRowView(row, indices, vals);
1489
1490 Scalar rowsum = STS::zero();
1491 Scalar diagval = STS::zero();
1492
1493 for (LocalOrdinal colID = 0; colID < Teuchos::as<LocalOrdinal>(nnz); colID++) {
1494 LocalOrdinal col = indices[colID];
1495 if (row == col)
1496 diagval = vals[colID];
1497 rowsum += vals[colID];
1498 }
1499 // printf("A(%d,:) row_sum(point) = %6.4e\n",row,rowsum);
1500 if (rowSumTol < MTS::one() && STS::magnitude(rowsum) > STS::magnitude(diagval) * rowSumTol) {
1501 // printf("Row %d triggers rowsum\n",(int)row);
1502 dirichletRows[row] = true;
1503 }
1504 }
1505}
1506
1507template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1508void UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
1509 ApplyRowSumCriterion(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A, const Xpetra::Vector<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>& BlockNumber, const typename Teuchos::ScalarTraits<Scalar>::magnitudeType rowSumTol, Teuchos::ArrayRCP<bool>& dirichletRows) {
1510 typedef Teuchos::ScalarTraits<Scalar> STS;
1511 typedef typename Teuchos::ScalarTraits<Scalar>::magnitudeType MT;
1512 typedef Teuchos::ScalarTraits<MT> MTS;
1513 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> rowmap = A.getRowMap();
1514
1515 TEUCHOS_TEST_FOR_EXCEPTION(!A.getColMap()->isSameAs(*BlockNumber.getMap()), std::runtime_error, "ApplyRowSumCriterion: BlockNumber must match's A's column map.");
1516
1517 Teuchos::ArrayRCP<const LocalOrdinal> block_id = BlockNumber.getData(0);
1518 for (LocalOrdinal row = 0; row < Teuchos::as<LocalOrdinal>(rowmap->getLocalNumElements()); ++row) {
1519 size_t nnz = A.getNumEntriesInLocalRow(row);
1520 ArrayView<const LocalOrdinal> indices;
1521 ArrayView<const Scalar> vals;
1522 A.getLocalRowView(row, indices, vals);
1523
1524 Scalar rowsum = STS::zero();
1525 Scalar diagval = STS::zero();
1526 for (LocalOrdinal colID = 0; colID < Teuchos::as<LocalOrdinal>(nnz); colID++) {
1527 LocalOrdinal col = indices[colID];
1528 if (row == col)
1529 diagval = vals[colID];
1530 if (block_id[row] == block_id[col])
1531 rowsum += vals[colID];
1532 }
1533
1534 // printf("A(%d,:) row_sum(block) = %6.4e\n",row,rowsum);
1535 if (rowSumTol < MTS::one() && STS::magnitude(rowsum) > STS::magnitude(diagval) * rowSumTol) {
1536 // printf("Row %d triggers rowsum\n",(int)row);
1537 dirichletRows[row] = true;
1538 }
1539 }
1540}
1541
1542// Applies rowsum criterion
1543template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node, class memory_space>
1544void ApplyRowSumCriterion(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
1545 const typename Teuchos::ScalarTraits<Scalar>::magnitudeType rowSumTol,
1546 Kokkos::View<bool*, memory_space>& dirichletRows) {
1547 typedef Teuchos::ScalarTraits<Scalar> STS;
1548 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> rowmap = A.getRowMap();
1549
1550 auto dirichletRowsHost = Kokkos::create_mirror_view(dirichletRows);
1551 Kokkos::deep_copy(dirichletRowsHost, dirichletRows);
1552
1553 for (LocalOrdinal row = 0; row < Teuchos::as<LocalOrdinal>(rowmap->getLocalNumElements()); ++row) {
1554 size_t nnz = A.getNumEntriesInLocalRow(row);
1555 ArrayView<const LocalOrdinal> indices;
1556 ArrayView<const Scalar> vals;
1557 A.getLocalRowView(row, indices, vals);
1558
1559 Scalar rowsum = STS::zero();
1560 Scalar diagval = STS::zero();
1561 for (LocalOrdinal colID = 0; colID < Teuchos::as<LocalOrdinal>(nnz); colID++) {
1562 LocalOrdinal col = indices[colID];
1563 if (row == col)
1564 diagval = vals[colID];
1565 rowsum += vals[colID];
1566 }
1567 if (STS::real(rowsum) > STS::magnitude(diagval) * rowSumTol)
1568 dirichletRowsHost(row) = true;
1569 }
1570
1571 Kokkos::deep_copy(dirichletRows, dirichletRowsHost);
1572}
1573
1574template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1575void UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
1576 ApplyRowSumCriterion(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
1577 const typename Teuchos::ScalarTraits<Scalar>::magnitudeType rowSumTol,
1578 Kokkos::View<bool*, typename Node::device_type::memory_space>& dirichletRows) {
1579 MueLu::ApplyRowSumCriterion<Scalar, LocalOrdinal, GlobalOrdinal, Node, typename Node::device_type::memory_space>(A, rowSumTol, dirichletRows);
1580}
1581
1582template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1583void UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
1584 ApplyRowSumCriterionHost(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
1585 const typename Teuchos::ScalarTraits<Scalar>::magnitudeType rowSumTol,
1586 Kokkos::View<bool*, Kokkos::HostSpace>& dirichletRows) {
1587 MueLu::ApplyRowSumCriterion<Scalar, LocalOrdinal, GlobalOrdinal, Node, Kokkos::HostSpace>(A, rowSumTol, dirichletRows);
1588}
1589
1590// Applies rowsum criterion
1591template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node, class memory_space>
1592void ApplyRowSumCriterion(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
1593 const Xpetra::Vector<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>& BlockNumber,
1594 const typename Teuchos::ScalarTraits<Scalar>::magnitudeType rowSumTol,
1595 Kokkos::View<bool*, memory_space>& dirichletRows) {
1596 typedef Teuchos::ScalarTraits<Scalar> STS;
1597 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> rowmap = A.getRowMap();
1598
1599 TEUCHOS_TEST_FOR_EXCEPTION(!A.getColMap()->isSameAs(*BlockNumber.getMap()), std::runtime_error, "ApplyRowSumCriterion: BlockNumber must match's A's column map.");
1600
1601 auto dirichletRowsHost = Kokkos::create_mirror_view(dirichletRows);
1602 Kokkos::deep_copy(dirichletRowsHost, dirichletRows);
1603
1604 Teuchos::ArrayRCP<const LocalOrdinal> block_id = BlockNumber.getData(0);
1605 for (LocalOrdinal row = 0; row < Teuchos::as<LocalOrdinal>(rowmap->getLocalNumElements()); ++row) {
1606 size_t nnz = A.getNumEntriesInLocalRow(row);
1607 ArrayView<const LocalOrdinal> indices;
1608 ArrayView<const Scalar> vals;
1609 A.getLocalRowView(row, indices, vals);
1610
1611 Scalar rowsum = STS::zero();
1612 Scalar diagval = STS::zero();
1613 for (LocalOrdinal colID = 0; colID < Teuchos::as<LocalOrdinal>(nnz); colID++) {
1614 LocalOrdinal col = indices[colID];
1615 if (row == col)
1616 diagval = vals[colID];
1617 if (block_id[row] == block_id[col])
1618 rowsum += vals[colID];
1619 }
1620 if (STS::real(rowsum) > STS::magnitude(diagval) * rowSumTol)
1621 dirichletRowsHost(row) = true;
1622 }
1623
1624 Kokkos::deep_copy(dirichletRows, dirichletRowsHost);
1625}
1626
1627template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1628void UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
1629 ApplyRowSumCriterion(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
1630 const Xpetra::Vector<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>& BlockNumber,
1631 const typename Teuchos::ScalarTraits<Scalar>::magnitudeType rowSumTol,
1632 Kokkos::View<bool*, typename Node::device_type::memory_space>& dirichletRows) {
1633 MueLu::ApplyRowSumCriterion<Scalar, LocalOrdinal, GlobalOrdinal, Node, typename Node::device_type::memory_space>(A, BlockNumber, rowSumTol, dirichletRows);
1634}
1635
1636template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1637void UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
1638 ApplyRowSumCriterionHost(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
1639 const Xpetra::Vector<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>& BlockNumber,
1640 const typename Teuchos::ScalarTraits<Scalar>::magnitudeType rowSumTol,
1641 Kokkos::View<bool*, Kokkos::HostSpace>& dirichletRows) {
1642 MueLu::ApplyRowSumCriterion<Scalar, LocalOrdinal, GlobalOrdinal, Node, Kokkos::HostSpace>(A, BlockNumber, rowSumTol, dirichletRows);
1643}
1644
1645template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1646Teuchos::ArrayRCP<const bool>
1648 DetectDirichletCols(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
1649 const Teuchos::ArrayRCP<const bool>& dirichletRows) {
1650 Scalar zero = Teuchos::ScalarTraits<Scalar>::zero();
1651 Scalar one = Teuchos::ScalarTraits<Scalar>::one();
1652 Teuchos::RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> domMap = A.getDomainMap();
1653 Teuchos::RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> colMap = A.getColMap();
1654 Teuchos::RCP<Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>> myColsToZero = Xpetra::MultiVectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(colMap, 1);
1655 myColsToZero->putScalar(zero);
1656 // Find all local column indices that are in Dirichlet rows, record in myColsToZero as 1.0
1657 for (size_t i = 0; i < (size_t)dirichletRows.size(); i++) {
1658 if (dirichletRows[i]) {
1659 Teuchos::ArrayView<const LocalOrdinal> indices;
1660 Teuchos::ArrayView<const Scalar> values;
1661 A.getLocalRowView(i, indices, values);
1662 for (size_t j = 0; j < static_cast<size_t>(indices.size()); j++)
1663 myColsToZero->replaceLocalValue(indices[j], 0, one);
1664 }
1665 }
1666
1667 Teuchos::RCP<Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>> globalColsToZero = Xpetra::MultiVectorFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(domMap, 1);
1668 globalColsToZero->putScalar(zero);
1669 Teuchos::RCP<Xpetra::Export<LocalOrdinal, GlobalOrdinal, Node>> exporter = Xpetra::ExportFactory<LocalOrdinal, GlobalOrdinal, Node>::Build(colMap, domMap);
1670 // export to domain map
1671 globalColsToZero->doExport(*myColsToZero, *exporter, Xpetra::ADD);
1672 // import to column map
1673 myColsToZero->doImport(*globalColsToZero, *exporter, Xpetra::INSERT);
1674 Teuchos::ArrayRCP<const Scalar> myCols = myColsToZero->getData(0);
1675 Teuchos::ArrayRCP<bool> dirichletCols(colMap->getLocalNumElements(), true);
1676 Magnitude eps = Teuchos::ScalarTraits<Magnitude>::eps();
1677 for (size_t i = 0; i < colMap->getLocalNumElements(); i++) {
1678 dirichletCols[i] = Teuchos::ScalarTraits<Scalar>::magnitude(myCols[i]) > 2.0 * eps;
1679 }
1680 return dirichletCols;
1681}
1682
1683template <class SC, class LO, class GO, class NO>
1684Kokkos::View<bool*, typename NO::device_type>
1686 DetectDirichletCols(const Xpetra::Matrix<SC, LO, GO, NO>& A,
1687 const Kokkos::View<const bool*, typename NO::device_type>& dirichletRows) {
1688 using ATS = KokkosKernels::ArithTraits<SC>;
1689 using impl_ATS = KokkosKernels::ArithTraits<typename ATS::val_type>;
1690 using range_type = Kokkos::RangePolicy<LO, typename NO::execution_space>;
1691 using scalar_type = typename KokkosKernels::ArithTraits<SC>::val_type;
1692 using mag_type = typename KokkosKernels::ArithTraits<scalar_type>::mag_type;
1693 using KAT_M = typename KokkosKernels::ArithTraits<mag_type>;
1694 using KAT_S = typename KokkosKernels::ArithTraits<scalar_type>;
1695
1696 SC zero = ATS::zero();
1697
1698 auto localMatrix = A.getLocalMatrixDevice();
1699 LO numRows = A.getLocalNumRows();
1700
1701 Teuchos::RCP<const Xpetra::Map<LO, GO, NO>> domMap = A.getDomainMap();
1702 Teuchos::RCP<const Xpetra::Map<LO, GO, NO>> colMap = A.getColMap();
1703 Teuchos::RCP<Xpetra::MultiVector<SC, LO, GO, NO>> myColsToZero = Xpetra::MultiVectorFactory<SC, LO, GO, NO>::Build(colMap, 1);
1704 myColsToZero->putScalar(zero);
1705 auto myColsToZeroView = myColsToZero->getLocalViewDevice(Tpetra::Access::ReadWrite);
1706 // Find all local column indices that are in Dirichlet rows, record in myColsToZero as 1.0
1707 Kokkos::parallel_for(
1708 "MueLu:Utils::DetectDirichletCols1", range_type(0, numRows),
1709 KOKKOS_LAMBDA(const LO row) {
1710 if (dirichletRows(row)) {
1711 auto rowView = localMatrix.row(row);
1712 auto length = rowView.length;
1713
1714 for (decltype(length) colID = 0; colID < length; colID++) {
1715 if (KAT_S::abs(rowView.value(colID)) > KAT_M::zero())
1716 myColsToZeroView(rowView.colidx(colID), 0) = impl_ATS::one();
1717 }
1718 }
1719 });
1720
1721 Teuchos::RCP<Xpetra::MultiVector<SC, LO, GO, NO>> globalColsToZero = Xpetra::MultiVectorFactory<SC, LO, GO, NO>::Build(domMap, 1);
1722 globalColsToZero->putScalar(zero);
1723 Teuchos::RCP<Xpetra::Export<LO, GO, NO>> exporter = Xpetra::ExportFactory<LO, GO, NO>::Build(colMap, domMap);
1724 // export to domain map
1725 globalColsToZero->doExport(*myColsToZero, *exporter, Xpetra::ADD);
1726 // import to column map
1727 myColsToZero->doImport(*globalColsToZero, *exporter, Xpetra::INSERT);
1728
1729 auto myCols = myColsToZero->getLocalViewDevice(Tpetra::Access::ReadOnly);
1730 size_t numColEntries = colMap->getLocalNumElements();
1731 Kokkos::View<bool*, typename NO::device_type> dirichletCols(Kokkos::ViewAllocateWithoutInitializing("dirichletCols"), numColEntries);
1732 const typename ATS::magnitudeType eps = 2.0 * ATS::eps();
1733
1734 Kokkos::parallel_for(
1735 "MueLu:Utils::DetectDirichletCols2", range_type(0, numColEntries),
1736 KOKKOS_LAMBDA(const size_t i) {
1737 dirichletCols(i) = impl_ATS::magnitude(myCols(i, 0)) > eps;
1738 });
1739 return dirichletCols;
1740}
1741
1742template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1743Scalar
1745 Frobenius(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A, const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& B) {
1746 // We check only row maps. Column may be different. One would hope that they are the same, as we typically
1747 // calculate frobenius norm of the specified sparsity pattern with an updated matrix from the previous step,
1748 // but matrix addition, even when one is submatrix of the other, changes column map (though change may be as
1749 // simple as couple of elements swapped)
1750 TEUCHOS_TEST_FOR_EXCEPTION(!A.getRowMap()->isSameAs(*B.getRowMap()), Exceptions::Incompatible, "MueLu::CGSolver::Frobenius: row maps are incompatible");
1751 TEUCHOS_TEST_FOR_EXCEPTION(!A.isFillComplete() || !B.isFillComplete(), Exceptions::RuntimeError, "Matrices must be fill completed");
1752
1753 const Map& AColMap = *A.getColMap();
1754 const Map& BColMap = *B.getColMap();
1755
1756 Teuchos::ArrayView<const LocalOrdinal> indA, indB;
1757 Teuchos::ArrayView<const Scalar> valA, valB;
1758 size_t nnzA = 0, nnzB = 0;
1759
1760 // We use a simple algorithm
1761 // for each row we fill valBAll array with the values in the corresponding row of B
1762 // as such, it serves as both sorted array and as storage, so we don't need to do a
1763 // tricky problem: "find a value in the row of B corresponding to the specific GID"
1764 // Once we do that, we translate LID of entries of row of A to LID of B, and multiply
1765 // corresponding entries.
1766 // The algorithm should be reasonably cheap, as it does not sort anything, provided
1767 // that getLocalElement and getGlobalElement functions are reasonably effective. It
1768 // *is* possible that the costs are hidden in those functions, but if maps are close
1769 // to linear maps, we should be fine
1770 Teuchos::Array<Scalar> valBAll(BColMap.getLocalNumElements());
1771
1772 LocalOrdinal invalid = Teuchos::OrdinalTraits<LocalOrdinal>::invalid();
1773 Scalar zero = Teuchos::ScalarTraits<Scalar>::zero(), f = zero, gf;
1774 size_t numRows = A.getLocalNumRows();
1775 for (size_t i = 0; i < numRows; i++) {
1776 A.getLocalRowView(i, indA, valA);
1777 B.getLocalRowView(i, indB, valB);
1778 nnzA = indA.size();
1779 nnzB = indB.size();
1780
1781 // Set up array values
1782 for (size_t j = 0; j < nnzB; j++)
1783 valBAll[indB[j]] = valB[j];
1784
1785 for (size_t j = 0; j < nnzA; j++) {
1786 // The cost of the whole Frobenius dot product function depends on the
1787 // cost of the getLocalElement and getGlobalElement functions here.
1788 LocalOrdinal ind = BColMap.getLocalElement(AColMap.getGlobalElement(indA[j]));
1789 if (ind != invalid)
1790 f += valBAll[ind] * valA[j];
1791 }
1792
1793 // Clean up array values
1794 for (size_t j = 0; j < nnzB; j++)
1795 valBAll[indB[j]] = zero;
1796 }
1797
1798 MueLu_sumAll(AColMap.getComm(), f, gf);
1799
1800 return gf;
1801}
1802
1803template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1806 // Distribute the seeds evenly in [1,maxint-1]. This guarantees nothing
1807 // about where in random number stream we are, but avoids overflow situations
1808 // in parallel when multiplying by a PID. It would be better to use
1809 // a good parallel random number generator.
1810 double one = 1.0;
1811 int maxint = INT_MAX; //= 2^31-1 = 2147483647 for 32-bit integers
1812 int mySeed = Teuchos::as<int>((maxint - 1) * (one - (comm.getRank() + 1) / (comm.getSize() + one)));
1813 if (mySeed < 1 || mySeed == maxint) {
1814 std::ostringstream errStr;
1815 errStr << "Error detected with random seed = " << mySeed << ". It should be in the interval [1,2^31-2].";
1816 throw Exceptions::RuntimeError(errStr.str());
1817 }
1818 std::srand(mySeed);
1819 // For Tpetra, we could use Kokkos' random number generator here.
1820 Teuchos::ScalarTraits<Scalar>::seedrandom(mySeed);
1821}
1822
1823template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1825 FindDirichletRows(Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A,
1826 std::vector<LocalOrdinal>& dirichletRows, bool count_twos_as_dirichlet) {
1827 typedef typename Teuchos::ScalarTraits<Scalar>::magnitudeType MT;
1828 dirichletRows.resize(0);
1829 for (size_t i = 0; i < A->getLocalNumRows(); i++) {
1830 Teuchos::ArrayView<const LocalOrdinal> indices;
1831 Teuchos::ArrayView<const Scalar> values;
1832 A->getLocalRowView(i, indices, values);
1833 int nnz = 0;
1834 for (size_t j = 0; j < (size_t)indices.size(); j++) {
1835 if (Teuchos::ScalarTraits<Scalar>::magnitude(values[j]) > Teuchos::ScalarTraits<MT>::eps()) {
1836 nnz++;
1837 }
1838 }
1839 if (nnz == 1 || (count_twos_as_dirichlet && nnz == 2)) {
1840 dirichletRows.push_back(i);
1841 }
1842 }
1843}
1844
1845template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1847 ApplyOAZToMatrixRows(Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A,
1848 const std::vector<LocalOrdinal>& dirichletRows) {
1849 RCP<const Map> Rmap = A->getRowMap();
1850 RCP<const Map> Cmap = A->getColMap();
1851 Scalar one = Teuchos::ScalarTraits<Scalar>::one();
1852 Scalar zero = Teuchos::ScalarTraits<Scalar>::zero();
1853
1854 for (size_t i = 0; i < dirichletRows.size(); i++) {
1855 GlobalOrdinal row_gid = Rmap->getGlobalElement(dirichletRows[i]);
1856
1857 Teuchos::ArrayView<const LocalOrdinal> indices;
1858 Teuchos::ArrayView<const Scalar> values;
1859 A->getLocalRowView(dirichletRows[i], indices, values);
1860 // NOTE: This won't work with fancy node types.
1861 Scalar* valuesNC = const_cast<Scalar*>(values.getRawPtr());
1862 for (size_t j = 0; j < (size_t)indices.size(); j++) {
1863 if (Cmap->getGlobalElement(indices[j]) == row_gid)
1864 valuesNC[j] = one;
1865 else
1866 valuesNC[j] = zero;
1867 }
1868 }
1869}
1870
1871template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1873 ApplyOAZToMatrixRows(Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A,
1874 const Teuchos::ArrayRCP<const bool>& dirichletRows) {
1875 TEUCHOS_ASSERT(A->isFillComplete());
1876 RCP<const Map> domMap = A->getDomainMap();
1877 RCP<const Map> ranMap = A->getRangeMap();
1878 RCP<const Map> Rmap = A->getRowMap();
1879 RCP<const Map> Cmap = A->getColMap();
1880 TEUCHOS_ASSERT(static_cast<size_t>(dirichletRows.size()) == Rmap->getLocalNumElements());
1881 const Scalar one = Teuchos::ScalarTraits<Scalar>::one();
1882 const Scalar zero = Teuchos::ScalarTraits<Scalar>::zero();
1883 A->resumeFill();
1884 for (size_t i = 0; i < (size_t)dirichletRows.size(); i++) {
1885 if (dirichletRows[i]) {
1886 GlobalOrdinal row_gid = Rmap->getGlobalElement(i);
1887
1888 Teuchos::ArrayView<const LocalOrdinal> indices;
1889 Teuchos::ArrayView<const Scalar> values;
1890 A->getLocalRowView(i, indices, values);
1891
1892 Teuchos::ArrayRCP<Scalar> valuesNC(values.size());
1893 for (size_t j = 0; j < (size_t)indices.size(); j++) {
1894 if (Cmap->getGlobalElement(indices[j]) == row_gid)
1895 valuesNC[j] = one;
1896 else
1897 valuesNC[j] = zero;
1898 }
1899 A->replaceLocalValues(i, indices, valuesNC());
1900 }
1901 }
1902 A->fillComplete(domMap, ranMap);
1903}
1904
1905template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1907 ApplyOAZToMatrixRows(Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A,
1908 const Kokkos::View<const bool*, typename Node::device_type>& dirichletRows) {
1909 TEUCHOS_ASSERT(A->isFillComplete());
1910 using ATS = KokkosKernels::ArithTraits<Scalar>;
1911 using impl_ATS = KokkosKernels::ArithTraits<typename ATS::val_type>;
1912 using range_type = Kokkos::RangePolicy<LocalOrdinal, typename Node::execution_space>;
1913
1914 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> domMap = A->getDomainMap();
1915 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> ranMap = A->getRangeMap();
1916 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> Rmap = A->getRowMap();
1917 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> Cmap = A->getColMap();
1918
1919 TEUCHOS_ASSERT(static_cast<size_t>(dirichletRows.size()) == Rmap->getLocalNumElements());
1920
1921 auto localMatrix = A->getLocalMatrixDevice();
1922 auto localRmap = Rmap->getLocalMap();
1923 auto localCmap = Cmap->getLocalMap();
1924
1925 Kokkos::parallel_for(
1926 "MueLu::Utils::ApplyOAZ", range_type(0, dirichletRows.extent(0)),
1927 KOKKOS_LAMBDA(const LocalOrdinal row) {
1928 if (dirichletRows(row)) {
1929 auto rowView = localMatrix.row(row);
1930 auto length = rowView.length;
1931 auto row_gid = localRmap.getGlobalElement(row);
1932 auto row_lid = localCmap.getLocalElement(row_gid);
1933
1934 for (decltype(length) colID = 0; colID < length; colID++)
1935 if (rowView.colidx(colID) == row_lid)
1936 rowView.value(colID) = impl_ATS::one();
1937 else
1938 rowView.value(colID) = impl_ATS::zero();
1939 }
1940 });
1941}
1942
1943template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1945 ZeroDirichletRows(Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A,
1946 const std::vector<LocalOrdinal>& dirichletRows,
1947 Scalar replaceWith) {
1948 for (size_t i = 0; i < dirichletRows.size(); i++) {
1949 Teuchos::ArrayView<const LocalOrdinal> indices;
1950 Teuchos::ArrayView<const Scalar> values;
1951 A->getLocalRowView(dirichletRows[i], indices, values);
1952 // NOTE: This won't work with fancy node types.
1953 Scalar* valuesNC = const_cast<Scalar*>(values.getRawPtr());
1954 for (size_t j = 0; j < (size_t)indices.size(); j++)
1955 valuesNC[j] = replaceWith;
1956 }
1957}
1958
1959template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1961 ZeroDirichletRows(Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A,
1962 const Teuchos::ArrayRCP<const bool>& dirichletRows,
1963 Scalar replaceWith) {
1964 TEUCHOS_ASSERT(static_cast<size_t>(dirichletRows.size()) == A->getRowMap()->getLocalNumElements());
1965 for (size_t i = 0; i < (size_t)dirichletRows.size(); i++) {
1966 if (dirichletRows[i]) {
1967 Teuchos::ArrayView<const LocalOrdinal> indices;
1968 Teuchos::ArrayView<const Scalar> values;
1969 A->getLocalRowView(i, indices, values);
1970 // NOTE: This won't work with fancy node types.
1971 Scalar* valuesNC = const_cast<Scalar*>(values.getRawPtr());
1972 for (size_t j = 0; j < (size_t)indices.size(); j++)
1973 valuesNC[j] = replaceWith;
1974 }
1975 }
1976}
1977
1978template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1980 ZeroDirichletRows(Teuchos::RCP<Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& X,
1981 const Teuchos::ArrayRCP<const bool>& dirichletRows,
1982 Scalar replaceWith) {
1983 TEUCHOS_ASSERT(static_cast<size_t>(dirichletRows.size()) == X->getMap()->getLocalNumElements());
1984 for (size_t i = 0; i < (size_t)dirichletRows.size(); i++) {
1985 if (dirichletRows[i]) {
1986 for (size_t j = 0; j < X->getNumVectors(); j++)
1987 X->replaceLocalValue(i, j, replaceWith);
1988 }
1989 }
1990}
1991
1992template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1994 ZeroDirichletRows(RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A,
1995 const Kokkos::View<const bool*, typename Node::device_type>& dirichletRows,
1996 Scalar replaceWith) {
1997 using ATS = KokkosKernels::ArithTraits<Scalar>;
1998 using range_type = Kokkos::RangePolicy<LocalOrdinal, typename Node::execution_space>;
1999
2000 typename ATS::val_type impl_replaceWith = replaceWith;
2001
2002 auto localMatrix = A->getLocalMatrixDevice();
2003 LocalOrdinal numRows = A->getLocalNumRows();
2004
2005 Kokkos::parallel_for(
2006 "MueLu:Utils::ZeroDirichletRows", range_type(0, numRows),
2007 KOKKOS_LAMBDA(const LocalOrdinal row) {
2008 if (dirichletRows(row)) {
2009 auto rowView = localMatrix.row(row);
2010 auto length = rowView.length;
2011 for (decltype(length) colID = 0; colID < length; colID++)
2012 rowView.value(colID) = impl_replaceWith;
2013 }
2014 });
2015}
2016
2017template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2018void UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
2019 ZeroDirichletRows(RCP<Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& X,
2020 const Kokkos::View<const bool*, typename Node::device_type>& dirichletRows,
2021 Scalar replaceWith) {
2022 using ATS = KokkosKernels::ArithTraits<Scalar>;
2023 using range_type = Kokkos::RangePolicy<LocalOrdinal, typename Node::execution_space>;
2024
2025 typename ATS::val_type impl_replaceWith = replaceWith;
2026
2027 auto myCols = X->getLocalViewDevice(Tpetra::Access::ReadWrite);
2028 size_t numVecs = X->getNumVectors();
2029 Kokkos::parallel_for(
2030 "MueLu:Utils::ZeroDirichletRows_MV", range_type(0, dirichletRows.size()),
2031 KOKKOS_LAMBDA(const size_t i) {
2032 if (dirichletRows(i)) {
2033 for (size_t j = 0; j < numVecs; j++)
2034 myCols(i, j) = impl_replaceWith;
2035 }
2036 });
2037}
2038
2039template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2041 ZeroDirichletCols(Teuchos::RCP<Matrix>& A,
2042 const Teuchos::ArrayRCP<const bool>& dirichletCols,
2043 Scalar replaceWith) {
2044 TEUCHOS_ASSERT(static_cast<size_t>(dirichletCols.size()) == A->getColMap()->getLocalNumElements());
2045 for (size_t i = 0; i < A->getLocalNumRows(); i++) {
2046 Teuchos::ArrayView<const LocalOrdinal> indices;
2047 Teuchos::ArrayView<const Scalar> values;
2048 A->getLocalRowView(i, indices, values);
2049 // NOTE: This won't work with fancy node types.
2050 Scalar* valuesNC = const_cast<Scalar*>(values.getRawPtr());
2051 for (size_t j = 0; j < static_cast<size_t>(indices.size()); j++)
2052 if (dirichletCols[indices[j]])
2053 valuesNC[j] = replaceWith;
2054 }
2055}
2056
2057template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2059 ZeroDirichletCols(RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A,
2060 const Kokkos::View<const bool*, typename Node::device_type>& dirichletCols,
2061 Scalar replaceWith, const bool DontZeroDiagEntries) {
2062 using ATS = KokkosKernels::ArithTraits<Scalar>;
2063 using range_type = Kokkos::RangePolicy<LocalOrdinal, typename Node::execution_space>;
2064
2065 typename ATS::val_type impl_replaceWith = replaceWith;
2066
2067 auto localMatrix = A->getLocalMatrixDevice();
2068 LocalOrdinal numRows = A->getLocalNumRows();
2069 auto lclRowmap = A->getRowMap()->getLocalMap();
2070 auto lclCowmap = A->getColMap()->getLocalMap();
2071
2072 Kokkos::parallel_for(
2073 "MueLu:Utils::ZeroDirichletCols", range_type(0, numRows),
2074 KOKKOS_LAMBDA(const LocalOrdinal row) {
2075 auto rowView = localMatrix.row(row);
2076 auto length = rowView.length;
2077
2078 if (DontZeroDiagEntries) {
2079 auto rgid = lclRowmap.getGlobalElement(row);
2080 for (decltype(length) colID = 0; colID < length; colID++) {
2081 if (dirichletCols(rowView.colidx(colID))) {
2082 auto cgid = lclCowmap.getGlobalElement(rowView.colidx(colID));
2083 if (rgid != cgid) rowView.value(colID) = impl_replaceWith;
2084 }
2085 }
2086 } else {
2087 for (decltype(length) colID = 0; colID < length; colID++)
2088 if (dirichletCols(rowView.colidx(colID))) {
2089 rowView.value(colID) = impl_replaceWith;
2090 }
2091 }
2092 });
2093}
2094
2095template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2097 FindDirichletRowsAndPropagateToCols(Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A,
2098 Teuchos::RCP<Xpetra::Vector<int, LocalOrdinal, GlobalOrdinal, Node>>& isDirichletRow,
2099 Teuchos::RCP<Xpetra::Vector<int, LocalOrdinal, GlobalOrdinal, Node>>& isDirichletCol) {
2100 // Make sure A's RowMap == DomainMap
2101 if (!A->getRowMap()->isSameAs(*A->getDomainMap())) {
2102 throw std::runtime_error("UtilitiesBase::FindDirichletRowsAndPropagateToCols row and domain maps must match.");
2103 }
2104 RCP<const Xpetra::Import<LocalOrdinal, GlobalOrdinal, Node>> importer = A->getCrsGraph()->getImporter();
2105 bool has_import = !importer.is_null();
2106
2107 // Find the Dirichlet rows
2108 std::vector<LocalOrdinal> dirichletRows;
2109 FindDirichletRows(A, dirichletRows);
2110
2111#if 0
2112 printf("[%d] DirichletRow Ids = ",A->getRowMap()->getComm()->getRank());
2113 for(size_t i=0; i<(size_t) dirichletRows.size(); i++)
2114 printf("%d ",dirichletRows[i]);
2115 printf("\n");
2116 fflush(stdout);
2117#endif
2118 // Allocate all as non-Dirichlet
2119 isDirichletRow = Xpetra::VectorFactory<int, LocalOrdinal, GlobalOrdinal, Node>::Build(A->getRowMap(), true);
2120 isDirichletCol = Xpetra::VectorFactory<int, LocalOrdinal, GlobalOrdinal, Node>::Build(A->getColMap(), true);
2121
2122 {
2123 Teuchos::ArrayRCP<int> dr_rcp = isDirichletRow->getDataNonConst(0);
2124 Teuchos::ArrayView<int> dr = dr_rcp();
2125 Teuchos::ArrayRCP<int> dc_rcp = isDirichletCol->getDataNonConst(0);
2126 Teuchos::ArrayView<int> dc = dc_rcp();
2127 for (size_t i = 0; i < (size_t)dirichletRows.size(); i++) {
2128 dr[dirichletRows[i]] = 1;
2129 if (!has_import) dc[dirichletRows[i]] = 1;
2130 }
2131 }
2132
2133 if (has_import)
2134 isDirichletCol->doImport(*isDirichletRow, *importer, Xpetra::CombineMode::ADD);
2135}
2136
2137template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2138RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
2140 ReplaceNonZerosWithOnes(const RCP<Matrix>& original) {
2141 using ISC = typename KokkosKernels::ArithTraits<Scalar>::val_type;
2142 using range_type = Kokkos::RangePolicy<LocalOrdinal, typename Node::execution_space>;
2143 using local_matrix_type = typename CrsMatrix::local_matrix_device_type;
2144 using values_type = typename local_matrix_type::values_type;
2145
2146 const ISC ONE = KokkosKernels::ArithTraits<ISC>::one();
2147 const ISC ZERO = KokkosKernels::ArithTraits<ISC>::zero();
2148
2149 // Copy the values array of the old matrix to a new array, replacing all the non-zeros with one
2150 auto localMatrix = original->getLocalMatrixDevice();
2151 TEUCHOS_TEST_FOR_EXCEPTION(!original->hasCrsGraph(), Exceptions::RuntimeError, "ReplaceNonZerosWithOnes: Cannot get CrsGraph");
2152 values_type new_values("values", localMatrix.nnz());
2153
2154 Kokkos::parallel_for(
2155 "ReplaceNonZerosWithOnes", range_type(0, localMatrix.nnz()), KOKKOS_LAMBDA(const size_t i) {
2156 if (localMatrix.values(i) != ZERO)
2157 new_values(i) = ONE;
2158 else
2159 new_values(i) = ZERO;
2160 });
2161
2162 // Build the new matrix
2163 RCP<Matrix> NewMatrix = Xpetra::MatrixFactory<SC, LO, GO, NO>::Build(original->getCrsGraph(), new_values);
2164 TEUCHOS_TEST_FOR_EXCEPTION(NewMatrix.is_null(), Exceptions::RuntimeError, "ReplaceNonZerosWithOnes: MatrixFactory::Build() did not return matrix");
2165 NewMatrix->fillComplete(original->getDomainMap(), original->getRangeMap());
2166 return NewMatrix;
2167}
2168
2169template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2170RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
2171UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::SPAI(const RCP<Matrix>& M, const std::string& MinvScheme) {
2172 // Create Minv via sparse apprximate inverse
2173
2174 Level miniLevel;
2175 Teuchos::RCP<MueLu::FactoryManager<SC, LO, GO, NO>> factoryHandler = Teuchos::rcp(new MueLu::FactoryManager<SC, LO, GO, NO>());
2176 miniLevel.SetFactoryManager(factoryHandler);
2177 miniLevel.SetLevelID(0);
2178#ifdef HAVE_MUELU_TIMER_SYNCHRONIZATION
2179 miniLevel.SetComm(M->getRowMap()->getComm());
2180#endif
2181 miniLevel.Set("A", M);
2182
2183 auto invapproxFact = rcp(new InverseApproximationFactory());
2184 invapproxFact->SetFactory("A", MueLu::NoFactory::getRCP());
2185 if (MinvScheme == "fsai")
2186 invapproxFact->SetParameter("inverse: approximation type", Teuchos::ParameterEntry(std::string("factoredsparseapproxinverse")));
2187 else
2188 invapproxFact->SetParameter("inverse: approximation type", Teuchos::ParameterEntry(std::string("sparseapproxinverse")));
2189
2190 miniLevel.Request("Ainv", invapproxFact.get());
2191 invapproxFact->Build(miniLevel);
2192 RCP<Matrix> NewMatrix = miniLevel.Get<RCP<Matrix>>("Ainv", invapproxFact.get());
2193 TEUCHOS_TEST_FOR_EXCEPTION(NewMatrix.is_null(), Exceptions::RuntimeError, "SPAI: MatrixFactory::Build() did not return matrix");
2194 return NewMatrix;
2195}
2196
2197template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2198RCP<const Xpetra::BlockedMap<LocalOrdinal, GlobalOrdinal, Node>>
2200 GeneratedBlockedTargetMap(const Xpetra::BlockedMap<LocalOrdinal, GlobalOrdinal, Node>& sourceBlockedMap,
2201 const Xpetra::Import<LocalOrdinal, GlobalOrdinal, Node>& Importer) {
2202 typedef Xpetra::Vector<int, LocalOrdinal, GlobalOrdinal, Node> IntVector;
2203 Xpetra::UnderlyingLib lib = sourceBlockedMap.lib();
2204
2205 // De-stride the map if we have to (might regret this later)
2206 RCP<const Map> fullMap = sourceBlockedMap.getMap();
2207 RCP<const Map> stridedMap = Teuchos::rcp_dynamic_cast<const Xpetra::StridedMap<LocalOrdinal, GlobalOrdinal, Node>>(fullMap);
2208 if (!stridedMap.is_null()) fullMap = stridedMap->getMap();
2209
2210 // Initial sanity checking for map compatibil
2211 const size_t numSubMaps = sourceBlockedMap.getNumMaps();
2212 if (!Importer.getSourceMap()->isCompatible(*fullMap))
2213 throw std::runtime_error("GenerateBlockedTargetMap(): Map compatibility error");
2214
2215 // Build an indicator vector
2216 RCP<IntVector> block_ids = Xpetra::VectorFactory<int, LocalOrdinal, GlobalOrdinal, Node>::Build(fullMap);
2217
2218 for (size_t i = 0; i < numSubMaps; i++) {
2219 RCP<const Map> map = sourceBlockedMap.getMap(i);
2220
2221 for (size_t j = 0; j < map->getLocalNumElements(); j++) {
2222 LocalOrdinal jj = fullMap->getLocalElement(map->getGlobalElement(j));
2223 block_ids->replaceLocalValue(jj, (int)i);
2224 }
2225 }
2226
2227 // Get the block ids for the new map
2228 RCP<const Map> targetMap = Importer.getTargetMap();
2229 RCP<IntVector> new_block_ids = Xpetra::VectorFactory<int, LocalOrdinal, GlobalOrdinal, Node>::Build(targetMap);
2230 new_block_ids->doImport(*block_ids, Importer, Xpetra::CombineMode::ADD);
2231 Teuchos::ArrayRCP<const int> dataRCP = new_block_ids->getData(0);
2232 Teuchos::ArrayView<const int> data = dataRCP();
2233
2234 // Get the GIDs for each subblock
2235 Teuchos::Array<Teuchos::Array<GlobalOrdinal>> elementsInSubMap(numSubMaps);
2236 for (size_t i = 0; i < targetMap->getLocalNumElements(); i++) {
2237 elementsInSubMap[data[i]].push_back(targetMap->getGlobalElement(i));
2238 }
2239
2240 // Generate the new submaps
2241 std::vector<RCP<const Map>> subMaps(numSubMaps);
2242 for (size_t i = 0; i < numSubMaps; i++) {
2243 subMaps[i] = Xpetra::MapFactory<LocalOrdinal, GlobalOrdinal, Node>::Build(lib, Teuchos::OrdinalTraits<GlobalOrdinal>::invalid(), elementsInSubMap[i](), targetMap->getIndexBase(), targetMap->getComm());
2244 }
2245
2246 // Build the BlockedMap
2247 return rcp(new BlockedMap(targetMap, subMaps));
2248}
2249
2250template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2252 MapsAreNested(const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>& rowMap, const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>& colMap) {
2253 if ((rowMap.lib() == Xpetra::UseTpetra) && (colMap.lib() == Xpetra::UseTpetra)) {
2254 auto tpRowMap = toTpetra(rowMap);
2255 auto tpColMap = toTpetra(colMap);
2256 return tpColMap.isLocallyFitted(tpRowMap);
2257 }
2258
2259 ArrayView<const GlobalOrdinal> rowElements = rowMap.getLocalElementList();
2260 ArrayView<const GlobalOrdinal> colElements = colMap.getLocalElementList();
2261
2262 const size_t numElements = rowElements.size();
2263
2264 if (size_t(colElements.size()) < numElements)
2265 return false;
2266
2267 bool goodMap = true;
2268 for (size_t i = 0; i < numElements; i++)
2269 if (rowElements[i] != colElements[i]) {
2270 goodMap = false;
2271 break;
2272 }
2273
2274 return goodMap;
2275}
2276
2277template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2278Teuchos::RCP<Xpetra::Vector<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>>
2280 ReverseCuthillMcKee(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Op) {
2281 using local_matrix_type = typename Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_matrix_device_type;
2282 using local_graph_type = typename local_matrix_type::staticcrsgraph_type;
2283 using lno_nnz_view_t = typename local_graph_type::entries_type::non_const_type;
2284 using device = typename local_graph_type::device_type;
2285 using execution_space = typename local_matrix_type::execution_space;
2286 using ordinal_type = typename local_matrix_type::ordinal_type;
2287
2288 local_graph_type localGraph = Op.getLocalMatrixDevice().graph;
2289
2290 lno_nnz_view_t rcmOrder = KokkosGraph::Experimental::graph_rcm<device, typename local_graph_type::row_map_type, typename local_graph_type::entries_type, lno_nnz_view_t>(localGraph.row_map, localGraph.entries);
2291
2292 RCP<Xpetra::Vector<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>> retval =
2293 Xpetra::VectorFactory<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>::Build(Op.getRowMap());
2294
2295 // Copy out and reorder data
2296 auto view1D = Kokkos::subview(retval->getLocalViewDevice(Tpetra::Access::ReadWrite), Kokkos::ALL(), 0);
2297 Kokkos::parallel_for(
2298 "Utilities::ReverseCuthillMcKee",
2299 Kokkos::RangePolicy<ordinal_type, execution_space>(0, localGraph.numRows()),
2300 KOKKOS_LAMBDA(const ordinal_type rowIdx) {
2301 view1D(rcmOrder(rowIdx)) = rowIdx;
2302 });
2303 return retval;
2304}
2305
2306template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2307Teuchos::RCP<Xpetra::Vector<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>>
2309 CuthillMcKee(const Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Op) {
2310 using local_matrix_type = typename Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_matrix_device_type;
2311 using local_graph_type = typename local_matrix_type::staticcrsgraph_type;
2312 using lno_nnz_view_t = typename local_graph_type::entries_type::non_const_type;
2313 using device = typename local_graph_type::device_type;
2314 using execution_space = typename local_matrix_type::execution_space;
2315 using ordinal_type = typename local_matrix_type::ordinal_type;
2316
2317 local_graph_type localGraph = Op.getLocalMatrixDevice().graph;
2318 LocalOrdinal numRows = localGraph.numRows();
2319
2320 lno_nnz_view_t rcmOrder = KokkosGraph::Experimental::graph_rcm<device, typename local_graph_type::row_map_type, typename local_graph_type::entries_type, lno_nnz_view_t>(localGraph.row_map, localGraph.entries);
2321
2322 RCP<Xpetra::Vector<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>> retval =
2323 Xpetra::VectorFactory<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>::Build(Op.getRowMap());
2324
2325 // Copy out data
2326 auto view1D = Kokkos::subview(retval->getLocalViewDevice(Tpetra::Access::ReadWrite), Kokkos::ALL(), 0);
2327 // Since KokkosKernels produced RCM, also reverse the order of the view to get CM
2328 Kokkos::parallel_for(
2329 "Utilities::ReverseCuthillMcKee",
2330 Kokkos::RangePolicy<ordinal_type, execution_space>(0, numRows),
2331 KOKKOS_LAMBDA(const ordinal_type rowIdx) {
2332 view1D(rcmOrder(numRows - 1 - rowIdx)) = rowIdx;
2333 });
2334 return retval;
2335}
2336
2337template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2339 TripleMatrixProduct(const Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& R,
2340 const Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& A,
2341 const Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& P,
2342 Teuchos::RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& Ac,
2343 const Teuchos::ParameterList& pL,
2344 const MueLu::BaseClass& verbObj,
2345 Teuchos::RCP<Teuchos::ParameterList>& APparams,
2346 Teuchos::RCP<Teuchos::ParameterList>& RAPparams,
2347 Level* coarseLevel) {
2348 using Matrix = Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>;
2349 using MatrixMatrix = Xpetra::MatrixMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>;
2350 using MatrixFactory = Xpetra::MatrixFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>;
2352
2353 const bool doTranspose = true;
2354 const bool doFillComplete = true;
2355 const bool doOptimizeStorage = true;
2356
2357 const bool useImplicit = pL.get<bool>("transpose: use implicit");
2358
2359 const std::string matrixName = (pL.isType<std::string>("Matrix name")) ? pL.get<std::string>("Matrix name") : "A";
2360 const std::string prolongatorName = (pL.isType<std::string>("Prolongator name")) ? pL.get<std::string>("Prolongator name") : "P";
2361 const std::string restrictorName = (pL.isType<std::string>("Restrictor name")) ? pL.get<std::string>("Restrictor name") : "R";
2362 std::string coarseMatrixName;
2363 if (pL.isType<std::string>("coarseMatrixName"))
2364 coarseMatrixName = pL.get<std::string>("coarseMatrixName");
2365 else {
2366 if (matrixName.size() == 1)
2367 coarseMatrixName = matrixName + "c";
2368 else
2369 coarseMatrixName = matrixName + "_coarse";
2370 }
2371
2372 std::string levelstr, labelstr;
2373 if (coarseLevel != nullptr) {
2374 std::ostringstream levelss;
2375 levelss << coarseLevel->GetLevelID();
2376 levelstr = levelss.str();
2377 labelstr = FormattingHelper::getColonLabel(coarseLevel->getObjectLabel());
2378 }
2379
2380 bool isGPU = Node::is_gpu;
2381
2382 // Reuse coarse matrix memory if available (multiple solve)
2383 if (RAPparams.is_null())
2384 RAPparams = rcp(new ParameterList);
2385 if (pL.isSublist("matrixmatrix: kernel params"))
2386 RAPparams->setParameters(pL.sublist("matrixmatrix: kernel params"));
2387
2388 if (RAPparams->isParameter("graph")) {
2389 Ac = RAPparams->get<RCP<Matrix>>("graph");
2390
2391 // Some eigenvalue may have been cached with the matrix in the previous run.
2392 // As the matrix values will be updated, we need to reset the eigenvalue.
2393 Ac->SetMaxEigenvalueEstimate(-Teuchos::ScalarTraits<Scalar>::one());
2394 }
2395
2396 // We compute global constants by default for the RAP, but not for the temps
2397 RAPparams->set("compute global constants: temporaries", RAPparams->get("compute global constants: temporaries", false));
2398 RAPparams->set("compute global constants", RAPparams->get("compute global constants", true));
2399
2400 if (pL.get<bool>("rap: triple product") == false || isGPU) {
2401 if (pL.get<bool>("rap: triple product") && isGPU)
2402 verbObj.GetOStream(Warnings1) << "Switching from triple product to R x (A x P) since triple product has not been implemented for "
2403 << Node::execution_space::name() << std::endl;
2404
2405 RCP<Matrix> AP;
2406
2407 // Reuse pattern if available (multiple solve)
2408 if (APparams.is_null())
2409 APparams = rcp(new ParameterList);
2410 if (pL.isSublist("matrixmatrix: kernel params"))
2411 APparams = rcp(new ParameterList(pL.sublist("matrixmatrix: kernel params")));
2412
2413 // By default, we don't need global constants for A*P
2414 APparams->set("compute global constants: temporaries", APparams->get("compute global constants: temporaries", false));
2415 APparams->set("compute global constants", APparams->get("compute global constants", false));
2416
2417 if (APparams->isParameter("graph"))
2418 AP = APparams->get<RCP<Matrix>>("graph");
2419
2420 std::string monitorstrAP = "MxM: " + matrixName + " x " + prolongatorName;
2421 std::string timerstrAP = "MueLu::" + matrixName + "*" + prolongatorName;
2422 if (!labelstr.empty())
2423 timerstrAP = labelstr + timerstrAP;
2424 if (!levelstr.empty())
2425 timerstrAP = timerstrAP + "-" + levelstr;
2426
2427 {
2428 SubFactoryMonitor subM(verbObj, monitorstrAP, *coarseLevel);
2429
2430 AP = MatrixMatrix::Multiply(*A, !doTranspose, *P, !doTranspose, AP, verbObj.GetOStream(Statistics2),
2431 doFillComplete, doOptimizeStorage, timerstrAP, APparams);
2432 }
2433
2434 // Allow optimization of storage.
2435 // This is necessary for new faster Epetra MM kernels.
2436 // Seems to work with matrix modifications to repair diagonal entries.
2437
2438 std::string timerstrRAP, monitorstrRAP;
2439 if (useImplicit) {
2440 monitorstrRAP = "MxM: " + prolongatorName + "' x (" + matrixName + prolongatorName + ") (implicit)";
2441 timerstrRAP = "MueLu::" + restrictorName + "*(" + matrixName + "*" + prolongatorName + ")-implicit";
2442 } else {
2443 monitorstrRAP = "MxM: " + restrictorName + " x (" + matrixName + prolongatorName + ") (explicit)";
2444 timerstrRAP = "MueLu::" + restrictorName + "*(" + matrixName + "*" + prolongatorName + ")-explicit";
2445 }
2446 if (!labelstr.empty())
2447 timerstrRAP = labelstr + timerstrRAP;
2448 if (!levelstr.empty())
2449 timerstrRAP = timerstrRAP + "-" + levelstr;
2450
2451 if (useImplicit) {
2452 SubFactoryMonitor m2(verbObj, monitorstrRAP, *coarseLevel);
2453
2454 Ac = MatrixMatrix::Multiply(*P, doTranspose, *AP, !doTranspose, Ac, verbObj.GetOStream(Statistics2),
2455 doFillComplete, doOptimizeStorage, timerstrRAP, RAPparams);
2456
2457 } else {
2458 SubFactoryMonitor m2(verbObj, monitorstrRAP, *coarseLevel);
2459
2460 Ac = MatrixMatrix::Multiply(*R, !doTranspose, *AP, !doTranspose, Ac, verbObj.GetOStream(Statistics2),
2461 doFillComplete, doOptimizeStorage, timerstrRAP, RAPparams);
2462 }
2463
2464 if (!isGPU) {
2465 APparams->set("graph", AP);
2466 }
2467
2468 } else {
2469 std::string monitorstrRAP;
2470 std::string timerstrRAP;
2471 if (useImplicit) {
2472 monitorstrRAP = "MxMxM: " + restrictorName + " x " + matrixName + " x " + prolongatorName + " (implicit)";
2473 timerstrRAP = "MueLu::" + restrictorName + "*" + matrixName + "*" + prolongatorName + "-implicit";
2474 } else {
2475 monitorstrRAP = "MxMxM: " + restrictorName + " x " + matrixName + " x " + prolongatorName + " (explicit)";
2476 timerstrRAP = "MueLu::" + restrictorName + "*" + matrixName + "*" + prolongatorName + "-explicit";
2477 }
2478 if (!labelstr.empty())
2479 timerstrRAP = labelstr + timerstrRAP;
2480 if (!levelstr.empty())
2481 timerstrRAP = timerstrRAP + "-" + levelstr;
2482
2483 if (useImplicit) {
2484 Ac = MatrixFactory::Build(P->getDomainMap(), Teuchos::as<LocalOrdinal>(0));
2485
2486 SubFactoryMonitor m2(verbObj, monitorstrRAP, *coarseLevel);
2487
2488 Xpetra::TripleMatrixMultiply<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
2489 MultiplyRAP(*P, doTranspose, *A, !doTranspose, *P, !doTranspose, *Ac, doFillComplete,
2490 doOptimizeStorage, timerstrRAP,
2491 RAPparams);
2492 } else {
2493 Ac = MatrixFactory::Build(R->getRowMap(), Teuchos::as<LocalOrdinal>(0));
2494
2495 SubFactoryMonitor m2(verbObj, monitorstrRAP, *coarseLevel);
2496
2497 Xpetra::TripleMatrixMultiply<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
2498 MultiplyRAP(*R, !doTranspose, *A, !doTranspose, *P, !doTranspose, *Ac, doFillComplete,
2499 doOptimizeStorage, timerstrRAP,
2500 RAPparams);
2501 }
2502 }
2503
2504 Teuchos::ArrayView<const double> relativeFloor = pL.get<Teuchos::Array<double>>("rap: relative diagonal floor")();
2505 if (relativeFloor.size() > 0) {
2506 Xpetra::MatrixUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::RelativeDiagonalBoost(Ac, relativeFloor, verbObj.GetOStream(Statistics2));
2507 }
2508
2509 bool repairZeroDiagonals = pL.get<bool>("RepairMainDiagonal") || pL.get<bool>("rap: fix zero diagonals");
2510 bool checkAc = pL.get<bool>("CheckMainDiagonal") || pL.get<bool>("rap: fix zero diagonals");
2511
2512 if (checkAc || repairZeroDiagonals) {
2513 using magnitudeType = typename Teuchos::ScalarTraits<Scalar>::magnitudeType;
2514 magnitudeType threshold;
2515 if (pL.isType<magnitudeType>("rap: fix zero diagonals threshold"))
2516 threshold = pL.get<magnitudeType>("rap: fix zero diagonals threshold");
2517 else
2518 threshold = Teuchos::as<magnitudeType>(pL.get<double>("rap: fix zero diagonals threshold"));
2519 Scalar replacement = Teuchos::as<Scalar>(pL.get<double>("rap: fix zero diagonals replacement"));
2520 Xpetra::MatrixUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::CheckRepairMainDiagonal(Ac, repairZeroDiagonals, verbObj.GetOStream(Warnings1), threshold, replacement);
2521 }
2522
2523 if (verbObj.IsPrint(Statistics2)) {
2524 RCP<ParameterList> params = rcp(new ParameterList());
2525 params->set("printLoadBalancingInfo", true);
2526 params->set("printCommInfo", true);
2527 verbObj.GetOStream(Statistics2) << PerfUtils::PrintMatrixInfo(*Ac, coarseMatrixName, params);
2528 }
2529
2530 if (!isGPU) {
2531 RAPparams->set("graph", Ac);
2532 }
2533
2534 if (Behavior::debug())
2535 MatrixUtils::checkLocalRowMapMatchesColMap(*Ac);
2536}
2537
2538} // namespace MueLu
2539
2540#define MUELU_UTILITIESBASE_SHORT
2541#endif // MUELU_UTILITIESBASE_DEF_HPP
2542
2543// LocalWords: LocalOrdinal
#define MueLu_sumAll(rcpComm, in, out)
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultScalar Scalar
MueLu::DefaultGlobalOrdinal GlobalOrdinal
MueLu::DefaultNode Node
Base class for MueLu classes.
Exception throws to report incompatible objects (like maps).
Exception throws to report errors in the internal logical of the program.
This class specifies the default factory that should generate some data on a Level if the data does n...
Factory for building the approximate inverse of a matrix.
Class that holds all level-specific information.
void SetComm(RCP< const Teuchos::Comm< int > > const &comm)
void SetLevelID(int levelID)
Set level number.
int GetLevelID() const
Return level number.
T & Get(const std::string &ename, const FactoryBase *factory=NoFactory::get())
Get data without decrementing associated storage counter (i.e., read-only access)....
void Set(const std::string &ename, const T &entry, const FactoryBase *factory=NoFactory::get())
void Request(const FactoryBase &factory)
Increment the storage counter for all the inputs of a factory.
void SetFactoryManager(const RCP< const FactoryManagerBase > &factoryManager)
Set default factories (used internally by Hierarchy::SetLevel()).
static const RCP< const NoFactory > getRCP()
Static Get() functions.
Timer to be used in factories. Similar to SubMonitor but adds a timer level by level.
static Teuchos::RCP< Vector > GetInverse(Teuchos::RCP< const Vector > v, Magnitude tol=Teuchos::ScalarTraits< Scalar >::eps() *100, Scalar valReplacement=Teuchos::ScalarTraits< Scalar >::zero())
Return vector containing inverse of input vector.
static RCP< Vector > GetMatrixDiagonalInverse(const Matrix &A, Magnitude tol=Teuchos::ScalarTraits< Scalar >::eps() *100, Scalar valReplacement=Teuchos::ScalarTraits< Scalar >::zero(), const bool doLumped=false)
Extract Matrix Diagonal.
static RCP< Xpetra::CrsGraph< LocalOrdinal, GlobalOrdinal, Node > > GetThresholdedLowerTriangularGraph(const RCP< Matrix > &A, const Magnitude threshold)
Threshold a graph.
static RCP< Xpetra::CrsGraph< LocalOrdinal, GlobalOrdinal, Node > > GetThresholdedGraph(const RCP< Matrix > &A, const Magnitude threshold)
Threshold a graph.
static Teuchos::ArrayRCP< Scalar > GetMatrixDiagonal_arcp(const Matrix &A)
Extract Matrix Diagonal.
static RCP< Matrix > GetThresholdedMatrix(const RCP< Matrix > &Ain, const Magnitude threshold, const bool keepDiagonal=true)
Threshold a matrix.
static RCP< Matrix > Crs2Op(RCP< CrsMatrix > Op)
static RCP< Vector > GetMatrixDiagonal(const Matrix &A)
Extract Matrix Diagonal.
Teuchos::FancyOStream & GetOStream(MsgType type, int thisProcRankOnly=0) const
Get an output stream for outputting the input message type.
bool IsPrint(MsgType type, int thisProcRankOnly=-1) const
Find out whether we need to print out information for a specific message type.
Namespace for MueLu classes and methods.
Kokkos::View< bool *, memory_space > DetectDirichletRows_kokkos(const Xpetra::Matrix< SC, LO, GO, NO > &A, const typename Teuchos::ScalarTraits< SC >::magnitudeType &tol, const bool count_twos_as_dirichlet)
@ Statistics2
Print even more statistics.
@ Warnings1
Additional warnings.
void ApplyRowSumCriterion(const Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &A, const typename Teuchos::ScalarTraits< Scalar >::magnitudeType rowSumTol, Kokkos::View< bool *, memory_space > &dirichletRows)
Teuchos::RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > removeSmallEntries(Teuchos::RCP< Xpetra::CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > &A, const typename Teuchos::ScalarTraits< Scalar >::magnitudeType threshold, const bool keepDiagonal)
KOKKOS_FORCEINLINE_FUNCTION bool isDirichletRow(typename CrsMatrix::ordinal_type rowId, KokkosSparse::SparseRowViewConst< CrsMatrix > &row, const typename KokkosKernels::ArithTraits< typename CrsMatrix::value_type >::magnitudeType &tol, const bool count_twos_as_dirichlet)