MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_CoalesceDropFactory_kokkos_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_COALESCEDROPFACTORY_KOKKOS_DEF_HPP
11#define MUELU_COALESCEDROPFACTORY_KOKKOS_DEF_HPP
12
13#include <Kokkos_Core.hpp>
14#include <KokkosSparse_CrsMatrix.hpp>
15#include <sstream>
16#include <string>
17#include <tuple>
18
19#include "Xpetra_Matrix.hpp"
20
22
23#include "MueLu_AmalgamationInfo.hpp"
24#include "MueLu_Exceptions.hpp"
25#include "MueLu_Level.hpp"
26#include "MueLu_LWGraph_kokkos.hpp"
27#include "MueLu_MasterList.hpp"
28#include "MueLu_Monitor.hpp"
29#include "MueLu_Utilities.hpp"
30
34
35// The different dropping algorithms are split up over several translation units. This speeds up compilation and also avoids launch latencies on GPU.
37#include "MueLu_ScalarDroppingClassical.hpp"
38#include "MueLu_ScalarDroppingDistanceLaplacian.hpp"
40#include "MueLu_VectorDroppingClassical.hpp"
41#include "MueLu_VectorDroppingDistanceLaplacian.hpp"
42
43namespace MueLu {
44
45template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
47 RCP<ParameterList> validParamList = rcp(new ParameterList());
48
49#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
50 SET_VALID_ENTRY("aggregation: use blocking");
51 SET_VALID_ENTRY("aggregation: drop tol");
52 SET_VALID_ENTRY("aggregation: use ml scaling of drop tol");
53 SET_VALID_ENTRY("aggregation: Dirichlet threshold");
54 SET_VALID_ENTRY("aggregation: greedy Dirichlet");
55 SET_VALID_ENTRY("aggregation: row sum drop tol");
56 SET_VALID_ENTRY("aggregation: strength-of-connection: matrix");
57 SET_VALID_ENTRY("aggregation: strength-of-connection: measure");
58 SET_VALID_ENTRY("aggregation: drop scheme");
59 SET_VALID_ENTRY("aggregation: block diagonal: interleaved blocksize");
60 SET_VALID_ENTRY("aggregation: distance laplacian metric");
61 SET_VALID_ENTRY("aggregation: Minv scheme");
62 SET_VALID_ENTRY("aggregation: distance laplacian directional weights");
63 SET_VALID_ENTRY("aggregation: dropping may create Dirichlet");
64#ifdef HAVE_MUELU_COALESCEDROP_ALLOW_OLD_PARAMETERS
65 SET_VALID_ENTRY("aggregation: distance laplacian algo");
66 SET_VALID_ENTRY("aggregation: classical algo");
67#endif
68 SET_VALID_ENTRY("aggregation: symmetrize graph after dropping");
69 SET_VALID_ENTRY("aggregation: symmetrize color graph");
70 SET_VALID_ENTRY("aggregation: coloring: use color graph");
71 SET_VALID_ENTRY("aggregation: coloring: localize color graph");
72
73 SET_VALID_ENTRY("filtered matrix: use lumping");
74 SET_VALID_ENTRY("filtered matrix: reuse graph");
75 SET_VALID_ENTRY("filtered matrix: reuse eigenvalue");
76
77 SET_VALID_ENTRY("filtered matrix: use root stencil");
78 SET_VALID_ENTRY("filtered matrix: lumping choice");
79 SET_VALID_ENTRY("filtered matrix: use spread lumping");
80 SET_VALID_ENTRY("filtered matrix: spread lumping diag dom growth factor");
81 SET_VALID_ENTRY("filtered matrix: spread lumping diag dom cap");
82 SET_VALID_ENTRY("filtered matrix: Dirichlet threshold");
83 SET_VALID_ENTRY("filtered matrix: count negative diagonals");
84
85#undef SET_VALID_ENTRY
86 validParamList->set<bool>("lightweight wrap", true, "Experimental option for lightweight graph access");
87#ifndef HAVE_MUELU_COALESCEDROP_ALLOW_OLD_PARAMETERS
88 validParamList->getEntry("aggregation: drop scheme").setValidator(rcp(new Teuchos::StringValidator(Teuchos::tuple<std::string>("point-wise", "cut-drop"))));
89#else
90 validParamList->getEntry("aggregation: drop scheme").setValidator(rcp(new Teuchos::StringValidator(Teuchos::tuple<std::string>("point-wise", "cut-drop", "signed classical sa", "classical", "distance laplacian", "signed classical", "block diagonal", "block diagonal classical", "block diagonal distance laplacian", "block diagonal signed classical", "block diagonal colored signed classical", "signed classical distance laplacian", "signed classical sa distance laplacian"))));
91 validParamList->getEntry("aggregation: classical algo").setValidator(rcp(new Teuchos::StringValidator(Teuchos::tuple<std::string>("default", "unscaled cut", "scaled cut", "scaled cut symmetric"))));
92 validParamList->getEntry("aggregation: distance laplacian algo").setValidator(rcp(new Teuchos::StringValidator(Teuchos::tuple<std::string>("default", "unscaled cut", "scaled cut", "scaled cut symmetric"))));
93#endif
94 validParamList->getEntry("aggregation: strength-of-connection: matrix").setValidator(rcp(new Teuchos::StringValidator(Teuchos::tuple<std::string>("A", "distance laplacian", "MinvA"))));
95 validParamList->getEntry("aggregation: strength-of-connection: measure").setValidator(rcp(new Teuchos::StringValidator(Teuchos::tuple<std::string>("smoothed aggregation", "signed smoothed aggregation", "signed ruge-stueben", "unscaled"))));
96 validParamList->getEntry("aggregation: distance laplacian metric").setValidator(rcp(new Teuchos::StringValidator(Teuchos::tuple<std::string>("unweighted", "material"))));
97 validParamList->getEntry("aggregation: Minv scheme").setValidator(rcp(new Teuchos::StringValidator(Teuchos::tuple<std::string>("spai", "fsai"))));
98 validParamList->getEntry("aggregation: symmetrize graph after dropping").setValidator(rcp(new Teuchos::StringValidator(Teuchos::tuple<std::string>("no symmetrization", "weak wins", "strong wins"))));
99 validParamList->getEntry("aggregation: symmetrize color graph").setValidator(rcp(new Teuchos::StringValidator(Teuchos::tuple<std::string>("weak wins", "strong wins"))));
100
101 validParamList->set<RCP<const FactoryBase>>("A", Teuchos::null, "Generating factory of the matrix A");
102 validParamList->set<RCP<const FactoryBase>>("UnAmalgamationInfo", Teuchos::null, "Generating factory for UnAmalgamationInfo");
103 validParamList->set<RCP<const FactoryBase>>("Coordinates", Teuchos::null, "Generating factory for Coordinates");
104 validParamList->set<RCP<const FactoryBase>>("BlockNumber", Teuchos::null, "Generating factory for BlockNumber");
105 validParamList->set<RCP<const FactoryBase>>("Material", Teuchos::null, "Generating factory for Material");
106 validParamList->set<RCP<const FactoryBase>>("M", Teuchos::null, "Generating factory for M");
107 validParamList->set<RCP<const FactoryBase>>("Minv", Teuchos::null, "Generating factory for Minv");
108 validParamList->set<RCP<const FactoryBase>>("MinvA", Teuchos::null, "Generating factory for MinvA");
109
110 // Make sure we don't recursively validate options for project auxiliary matrices
111 ParameterList norecurse;
112 norecurse.disableRecursiveValidation();
113 validParamList->set<ParameterList>("project auxiliary matrices", norecurse, "matrices that will be projected on coarse levels");
114
115 return validParamList;
116}
117
118template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
120 Input(currentLevel, "A");
121 Input(currentLevel, "UnAmalgamationInfo");
122
123 const ParameterList& pL = GetParameterList();
124
125 std::string socUsesMatrix = pL.get<std::string>("aggregation: strength-of-connection: matrix");
126 bool needCoords = (socUsesMatrix == "distance laplacian");
127 const bool needM = (socUsesMatrix == "MinvA");
128#ifdef HAVE_MUELU_COALESCEDROP_ALLOW_OLD_PARAMETERS
129 std::string droppingMethod = pL.get<std::string>("aggregation: drop scheme");
130 needCoords |= (droppingMethod.find("distance laplacian") != std::string::npos);
131#endif
132 if (needCoords) {
133 Input(currentLevel, "Coordinates");
134 std::string distLaplMetric = pL.get<std::string>("aggregation: distance laplacian metric");
135 if (distLaplMetric == "material")
136 Input(currentLevel, "Material");
137 }
138 if (needM && (currentLevel.GetLevelID() != 0)) {
139 if (pL.isSublist("project auxiliary matrices")) {
140 auto projectList = pL.sublist("project auxiliary matrices");
141 if (projectList.isParameter("MinvA"))
142 Input(currentLevel, "MinvA");
143 else if (projectList.isParameter("Minv"))
144 Input(currentLevel, "Minv");
145 else if (projectList.isParameter("M"))
146 Input(currentLevel, "M");
147 }
148 }
149
150 bool useBlocking = pL.get<bool>("aggregation: use blocking");
151#ifdef HAVE_MUELU_COALESCEDROP_ALLOW_OLD_PARAMETERS
152 useBlocking |= (droppingMethod.find("block diagonal") != std::string::npos);
153#endif
154 if (useBlocking) {
155 Input(currentLevel, "BlockNumber");
156 }
157}
158
159template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
161 Build(Level& currentLevel) const {
162 auto A = Get<RCP<Matrix>>(currentLevel, "A");
163 TEUCHOS_TEST_FOR_EXCEPTION(A->GetFixedBlockSize() % A->GetStorageBlockSize() != 0, Exceptions::RuntimeError, "A->GetFixedBlockSize() needs to be a multiple of A->GetStorageBlockSize()");
164 LO blkSize = A->GetFixedBlockSize() / A->GetStorageBlockSize();
165
166 std::tuple<GlobalOrdinal, GlobalOrdinal, boundary_nodes_type> results;
167 if (blkSize == 1)
168 results = BuildScalar(currentLevel);
169 else
170 results = BuildVector(currentLevel);
171
172 if (GetVerbLevel() & Statistics1) {
173 GlobalOrdinal numDropped = std::get<0>(results);
174 GlobalOrdinal numKept = std::get<1>(results);
175 auto boundaryNodes = std::get<2>(results);
176
177 GO numLocalBoundaryNodes = 0;
178
179 Kokkos::parallel_reduce(
180 "MueLu:CoalesceDropF:Build:bnd", range_type(0, boundaryNodes.extent(0)),
181 KOKKOS_LAMBDA(const LO i, GO& n) {
182 if (boundaryNodes(i))
183 n++;
184 },
185 numLocalBoundaryNodes);
186
187 if (IsPrint(Statistics1)) {
188 auto comm = A->getRowMap()->getComm();
189
190 std::vector<GlobalOrdinal> localStats = {numLocalBoundaryNodes, numDropped, numKept};
191 std::vector<GlobalOrdinal> globalStats(3);
192 Teuchos::reduceAll(*comm, Teuchos::REDUCE_SUM, 3, localStats.data(), globalStats.data());
193
194 GO numGlobalBoundaryNodes = globalStats[0];
195 GO numGlobalDropped = globalStats[1];
196 GO numGlobalKept = globalStats[2];
197 GO numGlobalTotal = numGlobalKept + numGlobalDropped;
198
199 GetOStream(Statistics1) << "Detected " << numGlobalBoundaryNodes << " Dirichlet nodes" << std::endl;
200 if (numGlobalTotal != 0) {
201 GetOStream(Statistics1) << "Number of dropped entries: "
202 << numGlobalDropped << "/" << numGlobalTotal
203 << " (" << 100 * Teuchos::as<double>(numGlobalDropped) / Teuchos::as<double>(numGlobalTotal) << "%)" << std::endl;
204 }
205 }
206 }
207}
208
209template <class local_matrix_type, class boundary_nodes_view, class... Functors>
210void runBoundaryFunctors(local_matrix_type& lclA, boundary_nodes_view& boundaryNodes, Functors&... functors) {
211 using local_ordinal_type = typename local_matrix_type::ordinal_type;
212 using execution_space = typename local_matrix_type::execution_space;
213 using range_type = Kokkos::RangePolicy<local_ordinal_type, execution_space>;
214 auto range = range_type(0, boundaryNodes.extent(0));
215 auto boundaries = BoundaryDetection::BoundaryFunctor(lclA, functors...);
216 Kokkos::parallel_for("CoalesceDrop::BoundaryDetection", range, boundaries);
217}
218
219template <class magnitudeType>
220void translateOldAlgoParam(const Teuchos::ParameterList& pL, std::string& droppingMethod, bool& useBlocking, std::string& socUsesMatrix, std::string& socUsesMeasure, std::string& symmetrizeDroppedGraph, bool& generateColoringGraph, magnitudeType& threshold, MueLu::MatrixConstruction::lumpingType& lumpingChoice) {
221 std::set<std::string> validDroppingMethods = {"piece-wise", "cut-drop"};
222
223 if (!pL.get<bool>("filtered matrix: use lumping")) lumpingChoice = MueLu::MatrixConstruction::no_lumping;
224
225 if (validDroppingMethods.find(droppingMethod) == validDroppingMethods.end()) {
226 std::string algo = droppingMethod;
227 std::string classicalAlgoStr = pL.get<std::string>("aggregation: classical algo");
228 std::string distanceLaplacianAlgoStr = pL.get<std::string>("aggregation: distance laplacian algo");
229
230 // Remove prefix "block diagonal" from algo
231 if (algo.find("block diagonal") == 0) {
232 useBlocking = true;
233 algo = algo.substr(14);
234 if (algo != "") {
235 algo = algo.substr(1);
236 }
237 }
238
239 if ((algo == "classical") || (algo == "signed classical sa") || (algo == "signed classical") || (algo == "colored signed classical")) {
240 socUsesMatrix = "A";
241
242 if (algo == "classical") {
243 socUsesMeasure = "smoothed aggregation";
244 } else if (algo == "signed classical sa") {
245 socUsesMeasure = "signed smoothed aggregation";
246 } else if (algo == "signed classical") {
247 socUsesMeasure = "signed ruge-stueben";
248 } else if (algo == "colored signed classical") {
249 socUsesMeasure = "signed ruge-stueben";
250 generateColoringGraph = true;
251 }
252
253 if (classicalAlgoStr == "default")
254 droppingMethod = "point-wise";
255 else if (classicalAlgoStr == "unscaled cut") {
256 socUsesMeasure = "unscaled";
257 droppingMethod = "cut-drop";
258 } else if (classicalAlgoStr == "scaled cut") {
259 droppingMethod = "cut-drop";
260 } else if (classicalAlgoStr == "scaled cut symmetric") {
261 droppingMethod = "cut-drop";
262 symmetrizeDroppedGraph = "strong wins";
263 }
264 } else if ((algo == "distance laplacian") || (algo == "signed classical sa distance laplacian") || (algo == "signed classical distance laplacian")) {
265 socUsesMatrix = "distance laplacian";
266
267 if (algo == "distance laplacian") {
268 socUsesMeasure = "smoothed aggregation";
269 } else if (algo == "signed classical sa distance laplacian") {
270 socUsesMeasure = "signed smoothed aggregation";
271 } else if (algo == "signed classical distance laplacian") {
272 socUsesMeasure = "signed ruge-stueben";
273 }
274
275 if (distanceLaplacianAlgoStr == "default")
276 droppingMethod = "point-wise";
277 else if (distanceLaplacianAlgoStr == "unscaled cut") {
278 socUsesMeasure = "unscaled";
279 droppingMethod = "cut-drop";
280 } else if (distanceLaplacianAlgoStr == "scaled cut") {
281 droppingMethod = "cut-drop";
282 } else if (distanceLaplacianAlgoStr == "scaled cut symmetric") {
283 droppingMethod = "cut-drop";
284 symmetrizeDroppedGraph = "strong wins";
285 }
286 } else if (algo == "") {
287 // algo was "block diagonal", but we process and remove the "block diagonal" part
288 socUsesMatrix = "A";
289 threshold = Teuchos::ScalarTraits<magnitudeType>::zero();
290 }
291 }
292}
293
294template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
295std::tuple<GlobalOrdinal, GlobalOrdinal, typename MueLu::LWGraph_kokkos<LocalOrdinal, GlobalOrdinal, Node>::boundary_nodes_type> CoalesceDropFactory_kokkos<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
296 BuildScalar(Level& currentLevel) const {
297 FactoryMonitor m(*this, "BuildScalar", currentLevel);
298
299 using MatrixType = Xpetra::CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>;
300 using GraphType = Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>;
301 using local_matrix_type = typename MatrixType::local_matrix_device_type;
302 using local_graph_type = typename GraphType::local_graph_device_type;
303 using rowptr_type = typename local_graph_type::row_map_type::non_const_type;
304 using entries_type = typename local_graph_type::entries_type::non_const_type;
305 using values_type = typename local_matrix_type::values_type::non_const_type;
306 using device_type = typename Node::device_type;
307 using memory_space = typename device_type::memory_space;
308 using results_view_type = Kokkos::View<DecisionType*, memory_space>;
309 using magnitudeType = typename Teuchos::ScalarTraits<Scalar>::magnitudeType;
310 using doubleMultiVector = Xpetra::MultiVector<magnitudeType, LO, GO, NO>;
311
312 typedef Teuchos::ScalarTraits<Scalar> STS;
313 const magnitudeType zero = Teuchos::ScalarTraits<magnitudeType>::zero();
314
315 auto A = Get<RCP<Matrix>>(currentLevel, "A");
316
317 const bool needToBuildFilteredA = currentLevel.IsRequested("A", this);
318
320 // Process parameterlist
321 const ParameterList& pL = GetParameterList();
322
323 // Boundary detection
324 const magnitudeType dirichletThreshold = STS::magnitude(as<SC>(pL.get<double>("aggregation: Dirichlet threshold")));
325 const magnitudeType rowSumTol = as<magnitudeType>(pL.get<double>("aggregation: row sum drop tol"));
326 const LocalOrdinal dirichletNonzeroThreshold = 1;
327
328 // Dropping
329 bool useBlocking = pL.get<bool>("aggregation: use blocking");
330 std::string droppingMethod = pL.get<std::string>("aggregation: drop scheme");
331 std::string socUsesMatrix = pL.get<std::string>("aggregation: strength-of-connection: matrix");
332 std::string socUsesMeasure = pL.get<std::string>("aggregation: strength-of-connection: measure");
333 std::string distanceLaplacianMetric = pL.get<std::string>("aggregation: distance laplacian metric");
334 std::string MinvScheme = pL.get<std::string>("aggregation: Minv scheme");
335 std::string symmetrizeDroppedGraph = pL.get<std::string>("aggregation: symmetrize graph after dropping");
336 std::string symmetrizeColoringGraph = pL.get<std::string>("aggregation: symmetrize color graph");
337 magnitudeType threshold;
338 // If we're doing the ML-style halving of the drop tol at each level, we do that here.
339 if (pL.get<bool>("aggregation: use ml scaling of drop tol"))
340 threshold = pL.get<double>("aggregation: drop tol") / pow(2.0, currentLevel.GetLevelID());
341 else
342 threshold = as<magnitudeType>(pL.get<double>("aggregation: drop tol"));
343 bool aggregationMayCreateDirichlet = pL.get<bool>("aggregation: dropping may create Dirichlet");
344
345 // Fill
346 const bool reuseGraph = pL.get<bool>("filtered matrix: reuse graph") && needToBuildFilteredA;
347 const bool reuseEigenvalue = pL.get<bool>("filtered matrix: reuse eigenvalue");
348
349 const bool useRootStencil = pL.get<bool>("filtered matrix: use root stencil");
350 const bool useSpreadLumping = pL.get<bool>("filtered matrix: use spread lumping");
351 const std::string lumpingChoiceString = pL.get<std::string>("filtered matrix: lumping choice");
353 if (needToBuildFilteredA) {
354 if (lumpingChoiceString == "diag lumping")
356 else if (lumpingChoiceString == "distributed lumping")
358 }
359
360 const magnitudeType filteringDirichletThreshold = as<magnitudeType>(pL.get<double>("filtered matrix: Dirichlet threshold"));
361
362 // coloring graph
363 bool generateColoringGraph = pL.get<bool>("aggregation: coloring: use color graph");
364 const bool localizeColoringGraph = pL.get<bool>("aggregation: coloring: localize color graph");
365
366#ifdef HAVE_MUELU_COALESCEDROP_ALLOW_OLD_PARAMETERS
367 translateOldAlgoParam(pL, droppingMethod, useBlocking, socUsesMatrix, socUsesMeasure, symmetrizeDroppedGraph, generateColoringGraph, threshold, lumpingChoice);
368#endif
369
370 {
371 std::stringstream ss;
372 ss << "dropping scheme = \"" << droppingMethod << "\", strength-of-connection measure = \"" << socUsesMeasure << "\", strength-of-connection matrix = \"" << socUsesMatrix << "\", ";
373 if (socUsesMatrix == "distance laplacian")
374 ss << "distance laplacian metric = \"" << distanceLaplacianMetric << "\", ";
375 ss << "threshold = " << threshold << ", blocksize = " << A->GetFixedBlockSize() << ", useBlocking = " << useBlocking;
376 ss << ", symmetrizeDroppedGraph = " << symmetrizeDroppedGraph << std::endl;
377
378 GetOStream(Runtime0) << ss.str();
379 }
380
381 TEUCHOS_ASSERT(!useRootStencil);
382 TEUCHOS_ASSERT(!useSpreadLumping);
383 TEUCHOS_ASSERT((lumpingChoice != MueLu::MatrixConstruction::distributed_lumping) || !reuseGraph);
384 if (droppingMethod == "cut-drop")
385 TEUCHOS_TEST_FOR_EXCEPTION(threshold > 1.0, Exceptions::RuntimeError, "For cut-drop algorithms, \"aggregation: drop tol\" = " << threshold << ", needs to be <= 1.0");
386
388 // We perform four sweeps over the rows of A:
389 // Pass 1: detection of boundary nodes
390 // Pass 2: diagonal extraction
391 // Pass 3: drop decision for each entry and construction of the rowptr of the filtered matrix
392 // Pass 4: fill of the filtered matrix
393 //
394 // Pass 1 and 3 apply a sequence of criteria to each row of the matrix.
395
396 // TODO: We could merge pass 1 and 2.
397
398 // Compute matrix that is used for dropping
399 RCP<Matrix> A_drop;
400 if (threshold != zero) {
401 if ((socUsesMatrix == "A") || (socUsesMatrix == "MinvA")) {
402 // Get matrix used for dropping
403 if (socUsesMatrix == "A")
404 A_drop = A;
405 else if (socUsesMatrix == "MinvA") {
406 bool storeMinvOnLevel = false, storeMinvAOnLevel = false;
407 if (pL.isSublist("project auxiliary matrices")) {
408 auto projectList = pL.sublist("project auxiliary matrices");
409 if (projectList.isParameter("MinvA"))
410 storeMinvAOnLevel = true;
411 else {
412 if (projectList.isParameter("Minv"))
413 storeMinvOnLevel = true;
414 else
415 storeMinvAOnLevel = true; // nothing found in project list, so default to storing MinvA
416 }
417 } else
418 storeMinvAOnLevel = true; // no project list given, so default to storing MinvA
419
420 if (IsAvailable(currentLevel, "MinvA")) {
421 A_drop = Get<RCP<Matrix>>(currentLevel, "MinvA");
422 } else {
423 RCP<Matrix> Minv;
424 bool MueLu_Minv = IsAvailable(currentLevel, "Minv");
425 bool User_Minv = currentLevel.IsAvailable("Minv", NoFactory::get());
426 if (MueLu_Minv || User_Minv) {
427 if (MueLu_Minv)
428 Minv = Get<RCP<Matrix>>(currentLevel, "Minv");
429 else
430 Minv = currentLevel.Get<RCP<Matrix>>("Minv", NoFactory::get());
431 } else { // get M and create Minv
432
433 RCP<Matrix> M;
434 if (currentLevel.GetLevelID() == 0) {
435 M = currentLevel.Get<RCP<Matrix>>("M", NoFactory::get());
436 } else {
437 M = Get<RCP<Matrix>>(currentLevel, "M");
438 }
439 // Find Dirichlets in A to stick corresponding Dirichlets in M
440#ifdef moreExperimentsToSeeIfHelpful
441 auto boundaryNodes = MueLu::Utilities<SC, LO, GO, NO>::DetectDirichletRows_kokkos(*A, 8.0 * Teuchos::ScalarTraits<magnitudeType>::eps());
442 if (GetVerbLevel() & Statistics1) {
443 int nAbc = 0;
444 for (size_t iii = 0; iii < A->getLocalNumRows(); iii++)
445 if (boundaryNodes[iii]) nAbc++;
446 auto MbcBefore = MueLu::Utilities<SC, LO, GO, NO>::DetectDirichletRows_kokkos(*M, 8.0 * Teuchos::ScalarTraits<magnitudeType>::eps());
447 int nMbcBefore = 0;
448 for (size_t iii = 0; iii < M->getLocalNumRows(); iii++)
449 if (MbcBefore[iii]) nMbcBefore++;
450 GetOStream(Statistics1) << "MueLu_CoalesceDropFactory_kokkos_def: # A bcs = " << nAbc << ", # M bcs before MueLu enforcement = " << nMbcBefore;
451 }
453 if (GetVerbLevel() & Statistics1) {
454 auto MbcAfter = MueLu::Utilities<SC, LO, GO, NO>::DetectDirichletRows_kokkos(*M, 8.0 * Teuchos::ScalarTraits<magnitudeType>::eps());
455 int nMbcAfter = 0;
456 for (size_t iii = 0; iii < M->getLocalNumRows(); iii++)
457 if (MbcAfter[iii]) nMbcAfter++;
458 GetOStream(Statistics1) << ", # M bcs after MueLu enforcement = " << nMbcAfter << std::endl;
459 }
460#endif
461
462 // Create Minv via sparse approximate inverse
463
464 Minv = Utilities::SPAI(M, MinvScheme);
465
466 if (storeMinvOnLevel) currentLevel.Set("Minv", Minv);
467 } // finished if/else (currentLevel.IsAvailable("Minv", *mtf)
468
469 // build MinvA matrix with same sparsity pattern as A.
470 A_drop = Xpetra::MatrixFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::BuildCopy(A);
471 auto params = Teuchos::rcp(new Teuchos::ParameterList());
472 params->set("MM Throw For Non-Existent Entries", false);
473 A_drop = Xpetra::MatrixMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Multiply(*Minv, false, *A, false, A_drop, GetOStream(Statistics2), true, true, std::string("MinvA"), params);
474
475 // Enforce Dirichlet BCs by zeroing out off-diagonal rows and columns and putting a 1 on diag of MinvA
476 auto boundaryNodes = MueLu::Utilities<SC, LO, GO, NO>::DetectDirichletRows_kokkos(*A, 8.0 * Teuchos::ScalarTraits<magnitudeType>::eps());
477 auto dirichletCols = MueLu::Utilities<SC, LO, GO, NO>::DetectDirichletCols(*A, boundaryNodes);
479 MueLu::Utilities<SC, LO, GO, NO>::ZeroDirichletCols(A_drop, dirichletCols, Teuchos::ScalarTraits<SC>::zero(), true);
480
481 if (storeMinvAOnLevel) currentLevel.Set("MinvA", A_drop);
482 } // finished if/else (currentLevel.IsAvailable("MinvA", NoFactory::get()))
483 } // else if (socUsesMatrix == "MinvA") {
484 }
485 }
486
487 auto crsA = toCrsMatrix(A);
488 auto lclA = crsA->getLocalMatrixDevice();
489 auto range = range_type(0, lclA.numRows());
490
492 // Pass 1: Detect boundary nodes
493 //
494 // The following criteria are available:
495 // - BoundaryDetection::PointDirichletFunctor
496 // Marks rows as Dirichlet based on value threshold and number of off-diagonal entries
497 // - BoundaryDetection::RowSumFunctor
498 // Marks rows as Dirichlet bases on row-sum criterion
499
500 // Dirichlet nodes
501 auto boundaryNodes = boundary_nodes_type("boundaryNodes", lclA.numRows()); // initialized to false
502 {
503 SubFactoryMonitor mBoundary(*this, "Boundary detection", currentLevel);
504
505 // macro that applies boundary detection functors
506 auto dirichlet_detection = BoundaryDetection::PointDirichletFunctor(lclA, boundaryNodes, dirichletThreshold, dirichletNonzeroThreshold);
507
508 if (rowSumTol <= 0.) {
509 runBoundaryFunctors(lclA, boundaryNodes, dirichlet_detection);
510 } else {
511 auto apply_rowsum = BoundaryDetection::RowSumFunctor(lclA, boundaryNodes, rowSumTol);
512 runBoundaryFunctors(lclA, boundaryNodes, dirichlet_detection, apply_rowsum);
513 }
514 }
515 // In what follows, boundaryNodes can still still get modified if aggregationMayCreateDirichlet == true.
516 // Otherwise we're now done with it now.
517
519 // Pass 2 & 3: Diagonal extraction and determine dropping and construct
520 // rowptr of filtered matrix
521 //
522 // The following criteria are available:
523 // - Misc::PointwiseDropBoundaryFunctor
524 // Drop all rows that have been marked as Dirichlet
525 // - Misc::DropOffRankFunctor
526 // Drop all entries that are off-rank
527 // - ClassicalDropping::DropFunctor
528 // Classical dropping
529 // - DistanceLaplacian::DropFunctor
530 // Distance Laplacian dropping
531 // - Misc::KeepDiagonalFunctor
532 // Mark diagonal as KEEP
533 // - Misc::MarkSingletonFunctor
534 // Mark singletons after dropping as Dirichlet
535 // - Misc::BlockDiagonalizeFunctor
536 // Drop coupling between blocks
537 //
538 // For the block diagonal variants we first block diagonalized and then apply "blocksize = 1" algorithms.
539
540 // rowptr of filtered A
541 auto filtered_rowptr = rowptr_type("filtered_rowptr", lclA.numRows() + 1);
542 // Number of nonzeros of filtered A
543 LocalOrdinal nnz_filtered = 0;
544 // dropping decisions for each entry
545 auto results = results_view_type("results", lclA.nnz()); // initialized to UNDECIDED
546 {
547 SubFactoryMonitor mDropping(*this, "Dropping decisions", currentLevel);
548
549 if (threshold != zero) {
550 if ((socUsesMatrix == "A") || (socUsesMatrix == "MinvA")) {
551 if (socUsesMeasure == "unscaled") {
552 ScalarDroppingClassical<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::UnscaledMeasure>::runDroppingFunctors_on_A(*A_drop, results, filtered_rowptr, nnz_filtered, boundaryNodes, droppingMethod, threshold,
553 aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, currentLevel, *this);
554 } else if (socUsesMeasure == "smoothed aggregation") {
555 ScalarDroppingClassical<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::SmoothedAggregationMeasure>::runDroppingFunctors_on_A(*A_drop, results, filtered_rowptr, nnz_filtered, boundaryNodes, droppingMethod, threshold,
556 aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, currentLevel, *this);
557 } else if (socUsesMeasure == "signed ruge-stueben") {
558 ScalarDroppingClassical<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::SignedRugeStuebenMeasure>::runDroppingFunctors_on_A(*A_drop, results, filtered_rowptr, nnz_filtered, boundaryNodes, droppingMethod, threshold,
559 aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, currentLevel, *this);
560 } else if (socUsesMeasure == "signed smoothed aggregation") {
561 ScalarDroppingClassical<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::SignedSmoothedAggregationMeasure>::runDroppingFunctors_on_A(*A_drop, results, filtered_rowptr, nnz_filtered, boundaryNodes, droppingMethod, threshold,
562 aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, currentLevel, *this);
563 }
564 } else if (socUsesMatrix == "distance laplacian") {
565 auto coords = Get<RCP<doubleMultiVector>>(currentLevel, "Coordinates");
566 if (socUsesMeasure == "unscaled") {
567 ScalarDroppingDistanceLaplacian<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::UnscaledMeasure>::runDroppingFunctors_on_dlap(*A, results, filtered_rowptr, nnz_filtered, boundaryNodes, droppingMethod, threshold, aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, distanceLaplacianMetric, currentLevel, *this);
568 } else if (socUsesMeasure == "smoothed aggregation") {
569 ScalarDroppingDistanceLaplacian<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::SmoothedAggregationMeasure>::runDroppingFunctors_on_dlap(*A, results, filtered_rowptr, nnz_filtered, boundaryNodes, droppingMethod, threshold, aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, distanceLaplacianMetric, currentLevel, *this);
570 } else if (socUsesMeasure == "signed ruge-stueben") {
571 ScalarDroppingDistanceLaplacian<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::SignedRugeStuebenMeasure>::runDroppingFunctors_on_dlap(*A, results, filtered_rowptr, nnz_filtered, boundaryNodes, droppingMethod, threshold, aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, distanceLaplacianMetric, currentLevel, *this);
572 } else if (socUsesMeasure == "signed smoothed aggregation") {
573 ScalarDroppingDistanceLaplacian<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::SignedSmoothedAggregationMeasure>::runDroppingFunctors_on_dlap(*A, results, filtered_rowptr, nnz_filtered, boundaryNodes, droppingMethod, threshold, aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, distanceLaplacianMetric, currentLevel, *this);
574 }
575 }
576 } else {
577 Kokkos::deep_copy(results, KEEP);
578
579 if (symmetrizeDroppedGraph != "no symmetrization") {
580 auto drop_boundaries = Misc::PointwiseSymmetricDropBoundaryFunctor(*A, boundaryNodes, results);
581 ScalarDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, results, filtered_rowptr, nnz_filtered, useBlocking, currentLevel, *this, drop_boundaries);
582 } else {
583 auto no_op = Misc::NoOpFunctor<LocalOrdinal>();
584 ScalarDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, results, filtered_rowptr, nnz_filtered, useBlocking, currentLevel, *this, no_op);
585 }
586 }
587
588 // 3 symmetrize possibilities:
589 //
590 // - strong wins changes both (i,j) and (j,i) to strong if previously either (i,j) or (j,i) was strong
591 // - weak wins+mayCreateDir changes both (i,j) and (j,i) to weak previously either of them were weak
592 // - weak wins+!mayCreateDir same as weak wins+mayCreateDir except if the change would create a row with
593 // no off-diagonals (when previously we had off-diagonals). In this case, we ensure that we
594 // retain the entry in that row that was previously strong and largest in magnitude.
595
596 if (symmetrizeDroppedGraph == "strong wins") {
597 auto symmetrize = Misc::SymmetrizeFunctor<local_matrix_type, false, false>(lclA, results);
598 ScalarDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, results, filtered_rowptr, nnz_filtered, useBlocking, currentLevel, *this, symmetrize);
599 } else if ((symmetrizeDroppedGraph == "weak wins") && (!pL.get<bool>("aggregation: dropping may create Dirichlet"))) {
600 auto symmetrize = Misc::SymmetrizeFunctor<local_matrix_type, true, false>(lclA, results);
601 ScalarDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, results, filtered_rowptr, nnz_filtered, useBlocking, currentLevel, *this, symmetrize);
602 } else if (symmetrizeDroppedGraph == "weak wins") { // pL.get<bool>("aggregation: dropping may create Dirichlet") must be true
603 auto symmetrize = Misc::SymmetrizeFunctor<local_matrix_type, true, true>(lclA, results);
604 ScalarDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, results, filtered_rowptr, nnz_filtered, useBlocking, currentLevel, *this, symmetrize);
605 }
606 }
607 GO numDropped = lclA.nnz() - nnz_filtered;
608 GO numGlobalDropped;
609 Teuchos::reduceAll(*A->getRowMap()->getComm(), Teuchos::REDUCE_SUM, 1, &numDropped, &numGlobalDropped);
610 // We now know the number of entries of filtered A and have the final rowptr.
611
613 // Pass 4: Create local matrix for filtered A
614 //
615 // Dropped entries are optionally lumped to the diagonal.
616
617 RCP<LWGraph_kokkos> graph;
618 RCP<Matrix> filteredA;
619 if (numGlobalDropped > 0) {
620 SubFactoryMonitor mFill(*this, "Filtered matrix fill", currentLevel);
621
622 local_graph_type lclGraph;
623 local_matrix_type lclFilteredA;
624 auto colidx = entries_type("entries", nnz_filtered);
625 lclGraph = local_graph_type(colidx, filtered_rowptr);
626 if (reuseGraph) {
627 // needToBuildFilteredA is true
628 filteredA = MatrixFactory::BuildCopy(A);
629 lclFilteredA = filteredA->getLocalMatrixDevice();
630 } else if (needToBuildFilteredA) {
631 auto values = values_type("values", nnz_filtered);
632 lclFilteredA = local_matrix_type("filteredA", lclGraph, lclA.numCols());
633 }
634
635 if (lumpingChoice != MueLu::MatrixConstruction::no_lumping) {
636 if (reuseGraph) {
637 auto fillFunctor = MatrixConstruction::PointwiseFillReuseFunctor<local_matrix_type, local_graph_type, true>(lclA, results, lclFilteredA, lclGraph, filteringDirichletThreshold);
638 Kokkos::parallel_for("MueLu::CoalesceDrop::Fill_lumped_reuse", range, fillFunctor);
639 } else {
640 if (lumpingChoice == MueLu::MatrixConstruction::diag_lumping) {
641 auto fillFunctor = MatrixConstruction::PointwiseFillNoReuseFunctor<local_matrix_type, MueLu::MatrixConstruction::diag_lumping>(lclA, results, lclGraph, lclFilteredA, filteringDirichletThreshold);
642 Kokkos::parallel_for("MueLu::CoalesceDrop::Fill_lumped_noreuse", range, fillFunctor);
643 } else if (lumpingChoice == MueLu::MatrixConstruction::distributed_lumping) {
644 auto fillFunctor = MatrixConstruction::PointwiseFillNoReuseFunctor<local_matrix_type, MueLu::MatrixConstruction::distributed_lumping>(lclA, results, lclGraph, lclFilteredA, filteringDirichletThreshold);
645 Kokkos::parallel_for("MueLu::CoalesceDrop::Fill_lumped_noreuse", range, fillFunctor);
646 }
647 }
648 } else {
649 if (reuseGraph) {
650 auto fillFunctor = MatrixConstruction::PointwiseFillReuseFunctor<local_matrix_type, local_graph_type, false>(lclA, results, lclFilteredA, lclGraph, filteringDirichletThreshold);
651 Kokkos::parallel_for("MueLu::CoalesceDrop::Fill_unlumped_reuse", range, fillFunctor);
652 } else {
653 if (needToBuildFilteredA) {
654 auto fillFunctor = MatrixConstruction::PointwiseFillNoReuseFunctor<local_matrix_type, MueLu::MatrixConstruction::no_lumping>(lclA, results, lclGraph, lclFilteredA, filteringDirichletThreshold);
655 Kokkos::parallel_for("MueLu::CoalesceDrop::Fill_unlumped_noreuse", range, fillFunctor);
656 } else {
657 auto fillFunctor = MatrixConstruction::PointwiseFillNoReuseFunctor<local_matrix_type, MueLu::MatrixConstruction::no_lumping, /*constructFilteredA=*/false>(lclA, results, lclGraph, lclFilteredA, filteringDirichletThreshold);
658 Kokkos::parallel_for("MueLu::CoalesceDrop::Fill_unlumped_noreuse", range, fillFunctor);
659 }
660 }
661 }
662
663 if (needToBuildFilteredA) {
664 if (!reuseGraph)
665 filteredA = MatrixFactory::Build(lclFilteredA, A->getRowMap(), A->getColMap(), A->getDomainMap(), A->getRangeMap());
666 filteredA->SetFixedBlockSize(A->GetFixedBlockSize());
667
668 if (reuseEigenvalue) {
669 // Reuse max eigenvalue from A
670 // It is unclear what eigenvalue is the best for the smoothing, but we already may have
671 // the D^{-1}A estimate in A, may as well use it.
672 // NOTE: ML does that too
673 filteredA->SetMaxEigenvalueEstimate(A->GetMaxEigenvalueEstimate());
674 } else {
675 filteredA->SetMaxEigenvalueEstimate(-Teuchos::ScalarTraits<SC>::one());
676 }
677 }
678
679 graph = rcp(new LWGraph_kokkos(lclGraph, A->getRowMap(), A->getColMap(), "amalgamated graph of A"));
680 graph->SetBoundaryNodeMap(boundaryNodes);
681 } else {
682 if (needToBuildFilteredA)
683 filteredA = A;
684 graph = rcp(new LWGraph_kokkos(A->getCrsGraph()->getLocalGraphDevice(), A->getRowMap(), A->getColMap(), "amalgamated graph of A"));
685 graph->SetBoundaryNodeMap(boundaryNodes);
686 }
687
688 // Construct a second graph for coloring
689 if (generateColoringGraph) {
690 SubFactoryMonitor mColoringGraph(*this, "Construct coloring graph", currentLevel);
691
692 filtered_rowptr = rowptr_type("rowptr_coloring_graph", lclA.numRows() + 1);
693 if (localizeColoringGraph) {
694 auto drop_offrank = Misc::DropOffRankFunctor(lclA, results);
695 ScalarDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, results, filtered_rowptr, nnz_filtered, useBlocking, currentLevel, *this, drop_offrank);
696 }
697 if (symmetrizeColoringGraph == "strong wins") {
698 auto symmetrize = Misc::SymmetrizeFunctor<local_matrix_type, false, false>(lclA, results);
699 ScalarDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, results, filtered_rowptr, nnz_filtered, useBlocking, currentLevel, *this, symmetrize);
700 } else { // weak wins
701 if (!pL.get<bool>("aggregation: dropping may create Dirichlet")) {
702 auto symmetrize = Misc::SymmetrizeFunctor<local_matrix_type, true, false>(lclA, results);
703 ScalarDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, results, filtered_rowptr, nnz_filtered, useBlocking, currentLevel, *this, symmetrize);
704 } else {
705 auto symmetrize = Misc::SymmetrizeFunctor<local_matrix_type, true, true>(lclA, results);
706 ScalarDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, results, filtered_rowptr, nnz_filtered, useBlocking, currentLevel, *this, symmetrize);
707 }
708 }
709 auto colidx = entries_type("entries_coloring_graph", nnz_filtered);
710 auto lclGraph = local_graph_type(colidx, filtered_rowptr);
711 auto graphConstruction = MatrixConstruction::GraphConstruction<local_matrix_type, local_graph_type>(lclA, results, lclGraph);
712 Kokkos::parallel_for("MueLu::CoalesceDrop::Construct_coloring_graph", range, graphConstruction);
713
714 auto colorGraph = rcp(new LWGraph_kokkos(lclGraph, A->getRowMap(), A->getColMap(), "coloring graph"));
715 Set(currentLevel, "Coloring Graph", colorGraph);
716 }
717
718 if (needToBuildFilteredA && pL.get<bool>("filtered matrix: count negative diagonals")) {
719 // Count the negative diagonals (and display that information)
721 GetOStream(Runtime0) << "CoalesceDrop: Negative diagonals: " << neg_count << std::endl;
722 }
723
724 LO dofsPerNode = 1;
725 Set(currentLevel, "DofsPerNode", dofsPerNode);
726 Set(currentLevel, "Graph", graph);
727 if (needToBuildFilteredA)
728 Set(currentLevel, "A", filteredA);
729
730 return std::make_tuple(numDropped, (GlobalOrdinal)nnz_filtered, boundaryNodes);
731}
732
733template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
734std::tuple<GlobalOrdinal, GlobalOrdinal, typename MueLu::LWGraph_kokkos<LocalOrdinal, GlobalOrdinal, Node>::boundary_nodes_type> CoalesceDropFactory_kokkos<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
735 BuildVector(Level& currentLevel) const {
736 FactoryMonitor m(*this, "BuildVector", currentLevel);
737
738 using MatrixType = Xpetra::CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>;
739 using GraphType = Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>;
740 using local_matrix_type = typename MatrixType::local_matrix_device_type;
741 using local_graph_type = typename GraphType::local_graph_device_type;
742 using rowptr_type = typename local_graph_type::row_map_type::non_const_type;
743 using entries_type = typename local_graph_type::entries_type::non_const_type;
744 using values_type = typename local_matrix_type::values_type::non_const_type;
745 using device_type = typename Node::device_type;
746 using memory_space = typename device_type::memory_space;
747 using results_view_type = Kokkos::View<DecisionType*, memory_space>;
748 using magnitudeType = typename Teuchos::ScalarTraits<Scalar>::magnitudeType;
749 using doubleMultiVector = Xpetra::MultiVector<magnitudeType, LO, GO, NO>;
750
751 typedef Teuchos::ScalarTraits<Scalar> STS;
752 const magnitudeType zero = Teuchos::ScalarTraits<magnitudeType>::zero();
753
754 auto A = Get<RCP<Matrix>>(currentLevel, "A");
755
756 const bool needToBuildFilteredA = currentLevel.IsRequested("A", this);
757
758 /* NOTE: storageblocksize (from GetStorageBlockSize()) is the size of a block in the chosen storage scheme.
759 blkSize is the number of storage blocks that must kept together during the amalgamation process.
760
761 Both of these quantities may be different than numPDEs (from GetFixedBlockSize()), but the following must always hold:
762
763 numPDEs = blkSize * storageblocksize.
764
765 If numPDEs==1
766 Matrix is point storage (classical CRS storage). storageblocksize=1 and blkSize=1
767 No other values makes sense.
768
769 If numPDEs>1
770 If matrix uses point storage, then storageblocksize=1 and blkSize=numPDEs.
771 If matrix uses block storage, with block size of n, then storageblocksize=n, and blkSize=numPDEs/n.
772 Thus far, only storageblocksize=numPDEs and blkSize=1 has been tested.
773 */
774
775 TEUCHOS_TEST_FOR_EXCEPTION(A->GetFixedBlockSize() % A->GetStorageBlockSize() != 0, Exceptions::RuntimeError, "A->GetFixedBlockSize() needs to be a multiple of A->GetStorageBlockSize()");
776 LO blkSize = A->GetFixedBlockSize() / A->GetStorageBlockSize();
777
778 auto amalInfo = Get<RCP<AmalgamationInfo>>(currentLevel, "UnAmalgamationInfo");
779
780 const RCP<const Map> rowMap = A->getRowMap();
781 const RCP<const Map> colMap = A->getColMap();
782
783 // build a node row map (uniqueMap = non-overlapping) and a node column map
784 // (nonUniqueMap = overlapping). The arrays rowTranslation and colTranslation
785 // stored in the AmalgamationInfo class container contain the local node id
786 // given a local dof id. The data is calculated in the AmalgamationFactory and
787 // stored in the variable "UnAmalgamationInfo" (which is of type AmalagamationInfo)
788 const RCP<const Map> uniqueMap = amalInfo->getNodeRowMap();
789 const RCP<const Map> nonUniqueMap = amalInfo->getNodeColMap();
790 Array<LO> rowTranslationArray = *(amalInfo->getRowTranslation()); // TAW should be transform that into a View?
791 Array<LO> colTranslationArray = *(amalInfo->getColTranslation());
792
793 Kokkos::View<LO*, Kokkos::MemoryUnmanaged>
794 rowTranslationView(rowTranslationArray.getRawPtr(), rowTranslationArray.size());
795 Kokkos::View<LO*, Kokkos::MemoryUnmanaged>
796 colTranslationView(colTranslationArray.getRawPtr(), colTranslationArray.size());
797
798 // get number of local nodes
799 LO numNodes = Teuchos::as<LocalOrdinal>(uniqueMap->getLocalNumElements());
800 typedef typename Kokkos::View<LocalOrdinal*, typename Node::device_type> id_translation_type;
801 id_translation_type rowTranslation("dofId2nodeId", rowTranslationArray.size());
802 id_translation_type colTranslation("ov_dofId2nodeId", colTranslationArray.size());
803 Kokkos::deep_copy(rowTranslation, rowTranslationView);
804 Kokkos::deep_copy(colTranslation, colTranslationView);
805
806 // extract striding information
807 blkSize = A->GetFixedBlockSize(); //< the full block size (number of dofs per node in strided map)
808 LocalOrdinal blkId = -1; //< the block id within a strided map or -1 if it is a full block map
809 LocalOrdinal blkPartSize = A->GetFixedBlockSize(); //< stores block size of part blkId (or the full block size)
810 if (A->IsView("stridedMaps") == true) {
811 const RCP<const Map> myMap = A->getRowMap("stridedMaps");
812 const RCP<const StridedMap> strMap = Teuchos::rcp_dynamic_cast<const StridedMap>(myMap);
813 TEUCHOS_TEST_FOR_EXCEPTION(strMap.is_null() == true, Exceptions::RuntimeError, "Map is not of type stridedMap");
814 blkSize = Teuchos::as<const LocalOrdinal>(strMap->getFixedBlockSize());
815 blkId = strMap->getStridedBlockId();
816 if (blkId > -1)
817 blkPartSize = Teuchos::as<LocalOrdinal>(strMap->getStridingData()[blkId]);
818 }
819
820 TEUCHOS_TEST_FOR_EXCEPTION(A->getRowMap()->getLocalNumElements() % blkPartSize != 0, MueLu::Exceptions::RuntimeError, "MueLu::CoalesceDropFactory: Number of local elements is " << A->getRowMap()->getLocalNumElements() << " but should be a multiple of " << blkPartSize);
821
823 // Process parameterlist
824 const ParameterList& pL = GetParameterList();
825
826 // Boundary detection
827 const magnitudeType dirichletThreshold = STS::magnitude(as<SC>(pL.get<double>("aggregation: Dirichlet threshold")));
828 const magnitudeType rowSumTol = as<magnitudeType>(pL.get<double>("aggregation: row sum drop tol"));
829 const LocalOrdinal dirichletNonzeroThreshold = 1;
830 const bool useGreedyDirichlet = pL.get<bool>("aggregation: greedy Dirichlet");
831 TEUCHOS_TEST_FOR_EXCEPTION(rowSumTol > zero, MueLu::Exceptions::RuntimeError, "MueLu::CoalesceDropFactory: RowSum is not implemented for vectorial problems.");
832
833 // Dropping
834 bool useBlocking = pL.get<bool>("aggregation: use blocking");
835 std::string droppingMethod = pL.get<std::string>("aggregation: drop scheme");
836 std::string socUsesMatrix = pL.get<std::string>("aggregation: strength-of-connection: matrix");
837 std::string socUsesMeasure = pL.get<std::string>("aggregation: strength-of-connection: measure");
838 std::string distanceLaplacianMetric = pL.get<std::string>("aggregation: distance laplacian metric");
839 std::string symmetrizeDroppedGraph = pL.get<std::string>("aggregation: symmetrize graph after dropping");
840 std::string symmetrizeColoringGraph = pL.get<std::string>("aggregation: symmetrize color graph");
841 magnitudeType threshold;
842 // If we're doing the ML-style halving of the drop tol at each level, we do that here.
843 if (pL.get<bool>("aggregation: use ml scaling of drop tol"))
844 threshold = pL.get<double>("aggregation: drop tol") / pow(2.0, currentLevel.GetLevelID());
845 else
846 threshold = as<magnitudeType>(pL.get<double>("aggregation: drop tol"));
847 bool aggregationMayCreateDirichlet = pL.get<bool>("aggregation: dropping may create Dirichlet");
848
849 // Fill
850 const bool reuseGraph = pL.get<bool>("filtered matrix: reuse graph") && needToBuildFilteredA;
851 const bool reuseEigenvalue = pL.get<bool>("filtered matrix: reuse eigenvalue");
852
853 const bool useRootStencil = pL.get<bool>("filtered matrix: use root stencil");
854 const bool useSpreadLumping = pL.get<bool>("filtered matrix: use spread lumping");
855 const std::string lumpingChoiceString = pL.get<std::string>("filtered matrix: lumping choice");
857 if (needToBuildFilteredA) {
858 if (lumpingChoiceString == "diag lumping")
860 else if (lumpingChoiceString == "distributed lumping")
862 }
863
864 const magnitudeType filteringDirichletThreshold = as<magnitudeType>(pL.get<double>("filtered matrix: Dirichlet threshold"));
865
866 // coloring graph
867 bool generateColoringGraph = pL.get<bool>("aggregation: coloring: use color graph");
868 const bool localizeColoringGraph = pL.get<bool>("aggregation: coloring: localize color graph");
869
870#ifdef HAVE_MUELU_COALESCEDROP_ALLOW_OLD_PARAMETERS
871 translateOldAlgoParam(pL, droppingMethod, useBlocking, socUsesMatrix, socUsesMeasure, symmetrizeDroppedGraph, generateColoringGraph, threshold, lumpingChoice);
872#endif
873 {
874 std::stringstream ss;
875 ss << "dropping scheme = \"" << droppingMethod << "\", strength-of-connection measure = \"" << socUsesMeasure << "\", strength-of-connection matrix = \"" << socUsesMatrix << "\", ";
876 if (socUsesMatrix == "distance laplacian")
877 ss << "distance laplacian metric = \"" << distanceLaplacianMetric << "\", ";
878 ss << "threshold = " << threshold << ", blocksize = " << A->GetFixedBlockSize() << ", useBlocking = " << useBlocking;
879 ss << ", symmetrizeDroppedGraph = " << symmetrizeDroppedGraph << std::endl;
880
881 GetOStream(Runtime0) << ss.str();
882 }
883
884 TEUCHOS_ASSERT(!useRootStencil);
885 TEUCHOS_ASSERT(!useSpreadLumping);
886 TEUCHOS_ASSERT((lumpingChoice != MueLu::MatrixConstruction::distributed_lumping) || !reuseGraph);
887 if (droppingMethod == "cut-drop")
888 TEUCHOS_TEST_FOR_EXCEPTION(threshold > 1.0, Exceptions::RuntimeError, "For cut-drop algorithms, \"aggregation: drop tol\" = " << threshold << ", needs to be <= 1.0");
889
891 // We perform four sweeps over the rows of A:
892 // Pass 1: detection of boundary nodes
893 // Pass 2: diagonal extraction
894 // Pass 3: drop decision for each entry and construction of the rowptr of the filtered matrix
895 // Pass 4: fill of the filtered matrix
896 //
897 // Pass 1 and 3 apply a sequence of criteria to each row of the matrix.
898
899 // TODO: We could merge pass 1 and 2.
900
901 auto crsA = toCrsMatrix(A);
902 auto lclA = crsA->getLocalMatrixDevice();
903 auto range = range_type(0, numNodes);
904
906 // Pass 1: Detect boundary nodes
907 //
908 // The following criteria are available:
909 // - BoundaryDetection::VectorDirichletFunctor
910 // Marks rows as Dirichlet based on value threshold and number of off-diagonal entries
911
912 // Dirichlet nodes
913 auto boundaryNodes = boundary_nodes_type("boundaryNodes", numNodes); // initialized to false
914 {
915 SubFactoryMonitor mBoundary(*this, "Boundary detection", currentLevel);
916
917 if (useGreedyDirichlet) {
918 auto dirichlet_detection = BoundaryDetection::VectorDirichletFunctor<local_matrix_type, true>(lclA, blkPartSize, boundaryNodes, dirichletThreshold, dirichletNonzeroThreshold);
919 runBoundaryFunctors(lclA, boundaryNodes, dirichlet_detection);
920 } else {
921 auto dirichlet_detection = BoundaryDetection::VectorDirichletFunctor<local_matrix_type, false>(lclA, blkPartSize, boundaryNodes, dirichletThreshold, dirichletNonzeroThreshold);
922 runBoundaryFunctors(lclA, boundaryNodes, dirichlet_detection);
923 }
924 }
925 // In what follows, boundaryNodes can still still get modified if aggregationMayCreateDirichlet == true.
926 // Otherwise we're now done with it now.
927
929 // Pass 2 & 3: Diagonal extraction and determine dropping and construct
930 // rowptr of filtered matrix
931 //
932 // The following criteria are available:
933 // - Misc::VectorDropBoundaryFunctor
934 // Drop all rows that have been marked as Dirichlet
935 // - Misc::DropOffRankFunctor
936 // Drop all entries that are off-rank
937 // - ClassicalDropping::DropFunctor
938 // Classical dropping
939 // - DistanceLaplacian::VectorDropFunctor
940 // Distance Laplacian dropping
941 // - Misc::KeepDiagonalFunctor
942 // Mark diagonal as KEEP
943 // - Misc::MarkSingletonFunctor
944 // Mark singletons after dropping as Dirichlet
945
946 // rowptr of filtered A
947 auto filtered_rowptr = rowptr_type("rowptr", lclA.numRows() + 1);
948 auto graph_rowptr = rowptr_type("rowptr", numNodes + 1);
949 // Number of nonzeros of filtered A and graph
950 Kokkos::pair<LocalOrdinal, LocalOrdinal> nnz = {0, 0};
951
952 // dropping decisions for each entry
953 auto results = results_view_type("results", lclA.nnz()); // initialized to UNDECIDED
954
955 RCP<Matrix> mergedA;
956 {
957 SubFactoryMonitor mDropping(*this, "Dropping decisions", currentLevel);
958
959 {
960 // Construct merged A.
961
962 auto merged_rowptr = rowptr_type("rowptr", numNodes + 1);
963 LocalOrdinal nnz_merged = 0;
964
965 auto functor = MatrixConstruction::MergeCountFunctor(lclA, blkPartSize, colTranslation, merged_rowptr);
966 Kokkos::parallel_scan("MergeCount", range, functor, nnz_merged);
967
968 local_graph_type lclMergedGraph;
969 auto colidx_merged = entries_type("entries", nnz_merged);
970 auto values_merged = values_type("values", nnz_merged);
971
972 local_matrix_type lclMergedA = local_matrix_type("mergedA",
973 numNodes, nonUniqueMap->getLocalNumElements(),
974 nnz_merged,
975 values_merged, merged_rowptr, colidx_merged);
976
977 auto fillFunctor = MatrixConstruction::MergeFillFunctor<local_matrix_type>(lclA, blkSize, colTranslation, lclMergedA);
978 Kokkos::parallel_for("MueLu::CoalesceDrop::MergeFill", range, fillFunctor);
979
980 mergedA = Xpetra::MatrixFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(lclMergedA, uniqueMap, nonUniqueMap, uniqueMap, uniqueMap);
981 }
982
983 if (threshold != zero) {
984 if (socUsesMatrix == "A") {
985 if (socUsesMeasure == "unscaled") {
986 VectorDroppingClassical<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::UnscaledMeasure>::runDroppingFunctors_on_A(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, boundaryNodes, droppingMethod, threshold, aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, currentLevel, *this);
987 } else if (socUsesMeasure == "smoothed aggregation") {
988 VectorDroppingClassical<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::SmoothedAggregationMeasure>::runDroppingFunctors_on_A(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, boundaryNodes, droppingMethod, threshold, aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, currentLevel, *this);
989 } else if (socUsesMeasure == "signed ruge-stueben") {
990 VectorDroppingClassical<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::SignedRugeStuebenMeasure>::runDroppingFunctors_on_A(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, boundaryNodes, droppingMethod, threshold, aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, currentLevel, *this);
991 } else if (socUsesMeasure == "signed smoothed aggregation") {
992 VectorDroppingClassical<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::SignedSmoothedAggregationMeasure>::runDroppingFunctors_on_A(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, boundaryNodes, droppingMethod, threshold, aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, currentLevel, *this);
993 }
994 } else if (socUsesMatrix == "distance laplacian") {
995 auto coords = Get<RCP<doubleMultiVector>>(currentLevel, "Coordinates");
996
997 Array<double> dlap_weights = pL.get<Array<double>>("aggregation: distance laplacian directional weights");
998 LocalOrdinal interleaved_blocksize = as<LocalOrdinal>(pL.get<int>("aggregation: block diagonal: interleaved blocksize"));
999 if (socUsesMeasure == "distance laplacian") {
1000 LO dim = (LO)coords->getNumVectors();
1001 // If anything isn't 1.0 we need to turn on the weighting
1002 bool non_unity = false;
1003 for (LO i = 0; !non_unity && i < (LO)dlap_weights.size(); i++) {
1004 if (dlap_weights[i] != 1.0) {
1005 non_unity = true;
1006 }
1007 }
1008 if (non_unity) {
1009 if ((LO)dlap_weights.size() == dim) {
1010 distanceLaplacianMetric = "weighted";
1011 } else if ((LO)dlap_weights.size() == interleaved_blocksize * dim)
1012 distanceLaplacianMetric = "block weighted";
1013 else {
1014 TEUCHOS_TEST_FOR_EXCEPTION(1, Exceptions::RuntimeError,
1015 "length of 'aggregation: distance laplacian directional weights' must equal the coordinate dimension OR the coordinate dimension times the blocksize");
1016 }
1017 if (GetVerbLevel() & Statistics1)
1018 GetOStream(Statistics1) << "Using distance laplacian weights: " << dlap_weights << std::endl;
1019 }
1020 }
1021
1022 if (socUsesMeasure == "unscaled") {
1023 VectorDroppingDistanceLaplacian<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::UnscaledMeasure>::runDroppingFunctors_on_dlap(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, boundaryNodes, droppingMethod, threshold, aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, distanceLaplacianMetric, dlap_weights, interleaved_blocksize, currentLevel, *this);
1024 } else if (socUsesMeasure == "smoothed aggregation") {
1025 VectorDroppingDistanceLaplacian<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::SmoothedAggregationMeasure>::runDroppingFunctors_on_dlap(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, boundaryNodes, droppingMethod, threshold, aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, distanceLaplacianMetric, dlap_weights, interleaved_blocksize, currentLevel, *this);
1026 } else if (socUsesMeasure == "signed ruge-stueben") {
1027 VectorDroppingDistanceLaplacian<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::SignedRugeStuebenMeasure>::runDroppingFunctors_on_dlap(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, boundaryNodes, droppingMethod, threshold, aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, distanceLaplacianMetric, dlap_weights, interleaved_blocksize, currentLevel, *this);
1028 } else if (socUsesMeasure == "signed smoothed aggregation") {
1029 VectorDroppingDistanceLaplacian<Scalar, LocalOrdinal, GlobalOrdinal, Node, Misc::SignedSmoothedAggregationMeasure>::runDroppingFunctors_on_dlap(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, boundaryNodes, droppingMethod, threshold, aggregationMayCreateDirichlet, symmetrizeDroppedGraph, useBlocking, distanceLaplacianMetric, dlap_weights, interleaved_blocksize, currentLevel, *this);
1030 }
1031 }
1032 } else {
1033 Kokkos::deep_copy(results, KEEP);
1034
1035 auto no_op = Misc::NoOpFunctor<LocalOrdinal>();
1036 VectorDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, useBlocking, currentLevel, *this, no_op);
1037 }
1038
1039 if (symmetrizeDroppedGraph == "strong wins") {
1040 auto symmetrize = Misc::SymmetrizeFunctor<local_matrix_type, false, false>(lclA, results);
1041 VectorDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, useBlocking, currentLevel, *this, symmetrize);
1042 } else if ((symmetrizeDroppedGraph == "weak wins") && (!pL.get<bool>("aggregation: dropping may create Dirichlet"))) {
1043 auto symmetrize = Misc::SymmetrizeFunctor<local_matrix_type, true, false>(lclA, results);
1044 VectorDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, useBlocking, currentLevel, *this, symmetrize);
1045 } else if (symmetrizeDroppedGraph == "weak wins") { // pL.get<bool>("aggregation: dropping may create Dirichlet") must be true
1046 auto symmetrize = Misc::SymmetrizeFunctor<local_matrix_type, true, true>(lclA, results);
1047 VectorDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, useBlocking, currentLevel, *this, symmetrize);
1048 }
1049 }
1050 LocalOrdinal nnz_filtered = nnz.first;
1051 LocalOrdinal nnz_graph = nnz.second;
1052 GO numTotal = mergedA->getLocalNumEntries();
1053 GO numDropped = numTotal - nnz_graph;
1054 GO numGlobalDropped;
1055 Teuchos::reduceAll(*A->getRowMap()->getComm(), Teuchos::REDUCE_SUM, 1, &numDropped, &numGlobalDropped);
1056 // We now know the number of entries of filtered A and have the final rowptr.
1057
1059 // Pass 4: Create local matrix for filtered A
1060 //
1061 // Dropped entries are optionally lumped to the diagonal.
1062
1063 RCP<LWGraph_kokkos> graph;
1064 RCP<Matrix> filteredA;
1065 if (numGlobalDropped > 0) {
1066 SubFactoryMonitor mFill(*this, "Filtered matrix fill", currentLevel);
1067
1068 local_graph_type lclGraph;
1069 {
1070 auto colidx = entries_type("entries", nnz_graph);
1071 lclGraph = local_graph_type(colidx, graph_rowptr);
1072 }
1073
1074 local_matrix_type lclFilteredA;
1075 if (reuseGraph) {
1076 // needToBuildFilteredA is true
1077 lclFilteredA = local_matrix_type("filteredA", lclA.graph, lclA.numCols());
1078 } else if (needToBuildFilteredA) {
1079 auto colidx = entries_type("entries", nnz_filtered);
1080 auto values = values_type("values", nnz_filtered);
1081 lclFilteredA = local_matrix_type("filteredA",
1082 lclA.numRows(), lclA.numCols(),
1083 nnz_filtered,
1084 values, filtered_rowptr, colidx);
1085 }
1086
1087 if (lumpingChoice != MueLu::MatrixConstruction::no_lumping) {
1088 if (reuseGraph) {
1089 auto fillFunctor = MatrixConstruction::VectorFillFunctor<local_matrix_type, true, true>(lclA, blkPartSize, colTranslation, results, lclFilteredA, lclGraph, filteringDirichletThreshold);
1090 Kokkos::parallel_for("MueLu::CoalesceDrop::Fill_lumped_reuse", range, fillFunctor);
1091 } else {
1092 auto fillFunctor = MatrixConstruction::VectorFillFunctor<local_matrix_type, true, false>(lclA, blkPartSize, colTranslation, results, lclFilteredA, lclGraph, filteringDirichletThreshold);
1093 Kokkos::parallel_for("MueLu::CoalesceDrop::Fill_lumped_noreuse", range, fillFunctor);
1094 }
1095 } else {
1096 if (reuseGraph) {
1097 auto fillFunctor = MatrixConstruction::VectorFillFunctor<local_matrix_type, false, true>(lclA, blkSize, colTranslation, results, lclFilteredA, lclGraph, filteringDirichletThreshold);
1098 Kokkos::parallel_for("MueLu::CoalesceDrop::Fill_unlumped_reuse", range, fillFunctor);
1099 } else {
1100 if (needToBuildFilteredA) {
1101 auto fillFunctor = MatrixConstruction::VectorFillFunctor<local_matrix_type, false, false>(lclA, blkSize, colTranslation, results, lclFilteredA, lclGraph, filteringDirichletThreshold);
1102 Kokkos::parallel_for("MueLu::CoalesceDrop::Fill_unlumped_noreuse", range, fillFunctor);
1103 } else {
1104 auto fillFunctor = MatrixConstruction::VectorFillFunctor<local_matrix_type, false, false, /*constructFilteredA=*/false>(lclA, blkSize, colTranslation, results, lclFilteredA, lclGraph, filteringDirichletThreshold);
1105 Kokkos::parallel_for("MueLu::CoalesceDrop::Fill_unlumped_noreuse", range, fillFunctor);
1106 }
1107 }
1108 }
1109
1110 if (needToBuildFilteredA) {
1111 filteredA = Xpetra::MatrixFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Build(lclFilteredA, A->getRowMap(), A->getColMap(), A->getDomainMap(), A->getRangeMap());
1112 filteredA->SetFixedBlockSize(blkSize);
1113
1114 if (reuseEigenvalue) {
1115 // Reuse max eigenvalue from A
1116 // It is unclear what eigenvalue is the best for the smoothing, but we already may have
1117 // the D^{-1}A estimate in A, may as well use it.
1118 // NOTE: ML does that too
1119 filteredA->SetMaxEigenvalueEstimate(A->GetMaxEigenvalueEstimate());
1120 } else {
1121 filteredA->SetMaxEigenvalueEstimate(-Teuchos::ScalarTraits<SC>::one());
1122 }
1123 }
1124
1125 graph = rcp(new LWGraph_kokkos(lclGraph, uniqueMap, nonUniqueMap, "amalgamated graph of A"));
1126 graph->SetBoundaryNodeMap(boundaryNodes);
1127 } else {
1128 if (needToBuildFilteredA)
1129 filteredA = A;
1130 graph = rcp(new LWGraph_kokkos(mergedA->getCrsGraph()->getLocalGraphDevice(), uniqueMap, nonUniqueMap, "amalgamated graph of A"));
1131 graph->SetBoundaryNodeMap(boundaryNodes);
1132 }
1133
1134 // Construct a second graph for coloring
1135 if (generateColoringGraph) {
1136 SubFactoryMonitor mColoringGraph(*this, "Construct coloring graph", currentLevel);
1137
1138 filtered_rowptr = rowptr_type("rowptr_coloring_graph", lclA.numRows() + 1);
1139 graph_rowptr = rowptr_type("rowptr", numNodes + 1);
1140 if (localizeColoringGraph) {
1141 auto drop_offrank = Misc::DropOffRankFunctor(lclA, results);
1142 VectorDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, useBlocking, currentLevel, *this, drop_offrank);
1143 }
1144 if (symmetrizeColoringGraph == "strong wins") {
1145 auto symmetrize = Misc::SymmetrizeFunctor<local_matrix_type, false, false>(lclA, results);
1146 VectorDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, useBlocking, currentLevel, *this, symmetrize);
1147 } else { // weak wins
1148 if (!pL.get<bool>("aggregation: dropping may create Dirichlet")) {
1149 auto symmetrize = Misc::SymmetrizeFunctor<local_matrix_type, true, false>(lclA, results);
1150 VectorDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, useBlocking, currentLevel, *this, symmetrize);
1151 } else {
1152 auto symmetrize = Misc::SymmetrizeFunctor<local_matrix_type, true, true>(lclA, results);
1153 VectorDroppingBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::template runDroppingFunctors<>(*A, *mergedA, blkPartSize, rowTranslation, colTranslation, results, filtered_rowptr, graph_rowptr, nnz, useBlocking, currentLevel, *this, symmetrize);
1154 }
1155 }
1156 auto colidx = entries_type("entries_coloring_graph", nnz_filtered);
1157 auto lclGraph = local_graph_type(colidx, filtered_rowptr);
1158 auto graphConstruction = MatrixConstruction::GraphConstruction<local_matrix_type, local_graph_type>(lclA, results, lclGraph);
1159 Kokkos::parallel_for("MueLu::CoalesceDrop::Construct_coloring_graph", range, graphConstruction);
1160
1161 auto colorGraph = rcp(new LWGraph_kokkos(lclGraph, A->getRowMap(), A->getColMap(), "coloring graph"));
1162 Set(currentLevel, "Coloring Graph", colorGraph);
1163 }
1164
1165 LO dofsPerNode = blkSize;
1166
1167 Set(currentLevel, "DofsPerNode", dofsPerNode);
1168 Set(currentLevel, "Graph", graph);
1169 if (needToBuildFilteredA)
1170 Set(currentLevel, "A", filteredA);
1171
1172 return std::make_tuple(numDropped, (GlobalOrdinal)nnz_graph, boundaryNodes);
1173}
1174
1175} // namespace MueLu
1176#endif // MUELU_COALESCEDROPFACTORY_KOKKOS_DEF_HPP
#define SET_VALID_ENTRY(name)
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultGlobalOrdinal GlobalOrdinal
Functor that serially applies sub-functors to rows.
Functor for marking nodes as Dirichlet based on rowsum.
Functor for marking nodes as Dirichlet in a block operator.
void DeclareInput(Level &currentLevel) const
Input.
std::tuple< GlobalOrdinal, GlobalOrdinal, boundary_nodes_type > BuildScalar(Level &currentLevel) const
Kokkos::RangePolicy< local_ordinal_type, execution_space > range_type
void Build(Level &currentLevel) const
Build an object with this factory.
typename MueLu::LWGraph_kokkos< LocalOrdinal, GlobalOrdinal, Node >::boundary_nodes_type boundary_nodes_type
RCP< const ParameterList > GetValidParameterList() const
Return a const parameter list of valid parameters that setParameterList() will accept.
std::tuple< GlobalOrdinal, GlobalOrdinal, boundary_nodes_type > BuildVector(Level &currentLevel) const
Exception throws to report errors in the internal logical of the program.
void Input(Level &level, const std::string &varName) const
T Get(Level &level, const std::string &varName) const
void Set(Level &level, const std::string &varName, const T &data) const
bool IsAvailable(Level &level, const std::string &varName) const
Timer to be used in factories. Similar to Monitor but with additional timers.
Lightweight MueLu representation of a compressed row storage graph.
Class that holds all level-specific information.
bool IsAvailable(const std::string &ename, const FactoryBase *factory=NoFactory::get()) const
Test whether a need's value has been saved.
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())
bool IsRequested(const std::string &ename, const FactoryBase *factory=NoFactory::get()) const
Test whether a need has been requested. Note: this tells nothing about whether the need's value exist...
Functor does not reuse the graph of the matrix for a problem with blockSize == 1.
Functor that fills the filtered matrix while reusing the graph of the matrix before dropping,...
Functor that drops off-rank entries.
Functor that drops boundary nodes for a blockSize == 1 problem.
Functor that symmetrizes the dropping decisions.
static const NoFactory * get()
virtual const Teuchos::ParameterList & GetParameterList() const
static void runDroppingFunctors_on_A(matrix_type &A, results_view &results, rowptr_type &filtered_rowptr, LocalOrdinal &nnz_filtered, boundary_nodes_type &boundaryNodes, const std::string &droppingMethod, const magnitudeType threshold, const bool aggregationMayCreateDirichlet, const std::string &symmetrizeDroppedGraph, const bool useBlocking, Level &level, const Factory &factory)
static void runDroppingFunctors_on_dlap(matrix_type &A, results_view &results, rowptr_type &filtered_rowptr, LocalOrdinal &nnz_filtered, boundary_nodes_type &boundaryNodes, const std::string &droppingMethod, const magnitudeType threshold, const bool aggregationMayCreateDirichlet, const std::string &symmetrizeDroppedGraph, const bool useBlocking, const std::string &distanceLaplacianMetric, Level &level, const Factory &factory)
Timer to be used in factories. Similar to SubMonitor but adds a timer level by level.
static void ZeroDirichletCols(Teuchos::RCP< Matrix > &A, const Teuchos::ArrayRCP< const bool > &dirichletCols, Scalar replaceWith=Teuchos::ScalarTraits< Scalar >::zero())
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)
static void ApplyOAZToMatrixRows(Teuchos::RCP< Xpetra::Matrix< Scalar, DefaultLocalOrdinal, DefaultGlobalOrdinal, DefaultNode > > &A, const std::vector< DefaultLocalOrdinal > &dirichletRows)
static RCP< Xpetra::Matrix< Scalar, DefaultLocalOrdinal, DefaultGlobalOrdinal, DefaultNode > > SPAI(const RCP< Matrix > &original, const std::string &MinvScheme)
static Teuchos::ArrayRCP< const bool > DetectDirichletCols(const Xpetra::Matrix< Scalar, DefaultLocalOrdinal, DefaultGlobalOrdinal, DefaultNode > &A, const Teuchos::ArrayRCP< const bool > &dirichletRows)
static void runDroppingFunctors_on_A(matrix_type &A, matrix_type &mergedA, LocalOrdinal blkPartSize, block_indices_view_type &rowTranslation, block_indices_view_type &colTranslation, results_view &results, rowptr_type &filtered_rowptr, rowptr_type &graph_rowptr, nnz_count_type &nnz, boundary_nodes_type &boundaryNodes, const std::string &droppingMethod, const magnitudeType threshold, const bool aggregationMayCreateDirichlet, const std::string &symmetrizeDroppedGraph, const bool useBlocking, Level &level, const Factory &factory)
static void runDroppingFunctors_on_dlap(matrix_type &A, matrix_type &mergedA, LocalOrdinal blkPartSize, block_indices_view_type &rowTranslation, block_indices_view_type &colTranslation, results_view &results, rowptr_type &filtered_rowptr, rowptr_type &graph_rowptr, nnz_count_type &nnz, boundary_nodes_type &boundaryNodes, const std::string &droppingMethod, const magnitudeType threshold, const bool aggregationMayCreateDirichlet, const std::string &symmetrizeDroppedGraph, const bool useBlocking, const std::string &distanceLaplacianMetric, Teuchos::Array< double > &dlap_weights, LocalOrdinal interleaved_blocksize, Level &level, const Factory &factory)
Teuchos::FancyOStream & GetOStream(MsgType type, int thisProcRankOnly=0) const
Get an output stream for outputting the input message type.
VerbLevel GetVerbLevel() const
Get the verbosity level.
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.
@ Statistics2
Print even more statistics.
@ Statistics1
Print more statistics.
@ Runtime0
One-liner description of what is happening.
void runBoundaryFunctors(local_matrix_type &lclA, boundary_nodes_view &boundaryNodes, Functors &... functors)
void translateOldAlgoParam(const Teuchos::ParameterList &pL, std::string &droppingMethod, bool &useBlocking, std::string &socUsesMatrix, std::string &socUsesMeasure, std::string &symmetrizeDroppedGraph, bool &generateColoringGraph, magnitudeType &threshold, MueLu::MatrixConstruction::lumpingType &lumpingChoice)