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