MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_UncoupledAggregationFactory_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_UNCOUPLEDAGGREGATIONFACTORY_DEF_HPP_
11#define MUELU_UNCOUPLEDAGGREGATIONFACTORY_DEF_HPP_
12
13#include <climits>
14
15#include <Xpetra_Map.hpp>
16#include <Xpetra_Vector.hpp>
17#include <Xpetra_MultiVectorFactory.hpp>
18#include <Xpetra_VectorFactory.hpp>
19#include <sstream>
20
22
23#include "MueLu_InterfaceAggregationAlgorithm.hpp"
24#include "MueLu_OnePtAggregationAlgorithm.hpp"
25#include "MueLu_PreserveDirichletAggregationAlgorithm.hpp"
26
27#include "MueLu_AggregationPhase1Algorithm.hpp"
28#include "MueLu_AggregationPhase2aAlgorithm.hpp"
29#include "MueLu_AggregationPhase2bAlgorithm.hpp"
30#include "MueLu_AggregationPhase3Algorithm.hpp"
31
32#include "MueLu_Level.hpp"
33#include "MueLu_LWGraph.hpp"
34#include "MueLu_Aggregates.hpp"
35#include "MueLu_MasterList.hpp"
36#include "MueLu_Monitor.hpp"
37
38#include "KokkosGraph_Distance2ColorHandle.hpp"
39#include "KokkosGraph_Distance2Color.hpp"
40#include "KokkosGraph_MIS2.hpp"
41#include "Kokkos_UnorderedMap.hpp"
42
43namespace MueLu {
44
45template <class LocalOrdinal, class GlobalOrdinal, class Node>
48
49template <class LocalOrdinal, class GlobalOrdinal, class Node>
51
52template <class LocalOrdinal, class GlobalOrdinal, class Node>
54 RCP<ParameterList> validParamList = rcp(new ParameterList());
55
56 // Aggregation parameters (used in aggregation algorithms)
57 // TODO introduce local member function for each aggregation algorithm such that each aggregation algorithm can define its own parameters
58
59#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
60 SET_VALID_ENTRY("aggregation: max agg size");
61 SET_VALID_ENTRY("aggregation: min agg size");
62 SET_VALID_ENTRY("aggregation: max selected neighbors");
63 SET_VALID_ENTRY("aggregation: ordering");
64 validParamList->getEntry("aggregation: ordering").setValidator(rcp(new Teuchos::StringValidator(Teuchos::tuple<std::string>("natural", "graph", "random"))));
65 SET_VALID_ENTRY("aggregation: deterministic");
66 SET_VALID_ENTRY("aggregation: coloring algorithm");
67 SET_VALID_ENTRY("aggregation: enable phase 1");
68 SET_VALID_ENTRY("aggregation: enable phase 2a");
69 SET_VALID_ENTRY("aggregation: enable phase 2b");
70 SET_VALID_ENTRY("aggregation: enable phase 3");
71 SET_VALID_ENTRY("aggregation: match ML phase1");
72 SET_VALID_ENTRY("aggregation: match ML phase2a");
73 SET_VALID_ENTRY("aggregation: match ML phase2b");
74 SET_VALID_ENTRY("aggregation: phase2a agg factor");
75 SET_VALID_ENTRY("aggregation: preserve Dirichlet points");
76 SET_VALID_ENTRY("aggregation: allow user-specified singletons");
77 SET_VALID_ENTRY("aggregation: use interface aggregation");
78 SET_VALID_ENTRY("aggregation: error on nodes with no on-rank neighbors");
79 SET_VALID_ENTRY("aggregation: phase3 avoid singletons");
80 SET_VALID_ENTRY("aggregation: phase 1 algorithm");
81 SET_VALID_ENTRY("aggregation: backend");
82 validParamList->getEntry("aggregation: backend").setValidator(rcp(new Teuchos::StringValidator(Teuchos::tuple<std::string>("default", "host", "kokkos"))));
83#undef SET_VALID_ENTRY
84
85 // general variables needed in AggregationFactory
86 validParamList->set<RCP<const FactoryBase>>("Graph", null, "Generating factory of the graph");
87 validParamList->set<RCP<const FactoryBase>>("DofsPerNode", null, "Generating factory for variable \'DofsPerNode\', usually the same as for \'Graph\'");
88
89 // special variables necessary for OnePtAggregationAlgorithm
90 validParamList->set<std::string>("OnePt aggregate map name", "", "Name of input map for single node aggregates. (default='')");
91 validParamList->set<std::string>("OnePt aggregate map factory", "", "Generating factory of (DOF) map for single node aggregates.");
92 // validParamList->set< RCP<const FactoryBase> >("OnePt aggregate map factory", NoFactory::getRCP(), "Generating factory of (DOF) map for single node aggregates.");
93
94 // InterfaceAggregation parameters
95 // validParamList->set< bool > ("aggregation: use interface aggregation", "false", "Flag to trigger aggregation along an interface using specified aggregate seeds.");
96 validParamList->set<std::string>("Interface aggregate map name", "", "Name of input map for interface aggregates. (default='')");
97 validParamList->set<std::string>("Interface aggregate map factory", "", "Generating factory of (DOF) map for interface aggregates.");
98 validParamList->set<RCP<const FactoryBase>>("nodeOnInterface", Teuchos::null, "Array specifying whether or not a node is on the interface (1 or 0).");
99
100 return validParamList;
101}
102
103template <class LocalOrdinal, class GlobalOrdinal, class Node>
105 Input(currentLevel, "Graph");
106 Input(currentLevel, "DofsPerNode");
107
108 const ParameterList& pL = GetParameterList();
109
110 // request special data necessary for OnePtAggregationAlgorithm
111 std::string mapOnePtName = pL.get<std::string>("OnePt aggregate map name");
112 if (mapOnePtName.length() > 0) {
113 std::string mapOnePtFactName = pL.get<std::string>("OnePt aggregate map factory");
114 if (mapOnePtFactName == "" || mapOnePtFactName == "NoFactory") {
115 currentLevel.DeclareInput(mapOnePtName, NoFactory::get());
116 } else {
117 RCP<const FactoryBase> mapOnePtFact = GetFactory(mapOnePtFactName);
118 currentLevel.DeclareInput(mapOnePtName, mapOnePtFact.get());
119 }
120 }
121
122 // request special data necessary for InterfaceAggregation
123 if (pL.get<bool>("aggregation: use interface aggregation") == true) {
124 if (currentLevel.GetLevelID() == 0) {
125 if (currentLevel.IsAvailable("nodeOnInterface", NoFactory::get())) {
126 currentLevel.DeclareInput("nodeOnInterface", NoFactory::get(), this);
127 } else {
128 TEUCHOS_TEST_FOR_EXCEPTION(currentLevel.IsAvailable("nodeOnInterface", NoFactory::get()),
130 "nodeOnInterface was not provided by the user on level0!");
131 }
132 } else {
133 Input(currentLevel, "nodeOnInterface");
134 }
135 }
136}
137
138template <class LocalOrdinal, class GlobalOrdinal, class Node>
140 FactoryMonitor m(*this, "Build", currentLevel);
141
142 ParameterList pL = GetParameterList();
143 bDefinitionPhase_ = false; // definition phase is finished, now all aggregation algorithm information is fixed
144
145 if (pL.get<int>("aggregation: max agg size") == -1)
146 pL.set("aggregation: max agg size", INT_MAX);
147
148 // define aggregation algorithms
149 RCP<const FactoryBase> graphFact = GetFactory("Graph");
150
151 // TODO Can we keep different aggregation algorithms over more Build calls?
152 algos_.clear();
153 algos_.push_back(rcp(new PreserveDirichletAggregationAlgorithm(graphFact)));
154 if (pL.get<bool>("aggregation: use interface aggregation") == true) algos_.push_back(rcp(new InterfaceAggregationAlgorithm(graphFact)));
155 if (pL.get<bool>("aggregation: allow user-specified singletons") == true) algos_.push_back(rcp(new OnePtAggregationAlgorithm(graphFact)));
156 if (pL.get<bool>("aggregation: enable phase 1") == true) algos_.push_back(rcp(new AggregationPhase1Algorithm(graphFact)));
157 if (pL.get<bool>("aggregation: enable phase 2a") == true) algos_.push_back(rcp(new AggregationPhase2aAlgorithm(graphFact)));
158 if (pL.get<bool>("aggregation: enable phase 2b") == true) algos_.push_back(rcp(new AggregationPhase2bAlgorithm(graphFact)));
159 if (pL.get<bool>("aggregation: enable phase 3") == true) algos_.push_back(rcp(new AggregationPhase3Algorithm(graphFact)));
160
161 std::string mapOnePtName = pL.get<std::string>("OnePt aggregate map name");
162 RCP<Map> OnePtMap = Teuchos::null;
163 if (mapOnePtName.length()) {
164 std::string mapOnePtFactName = pL.get<std::string>("OnePt aggregate map factory");
165 if (mapOnePtFactName == "" || mapOnePtFactName == "NoFactory") {
166 OnePtMap = currentLevel.Get<RCP<Map>>(mapOnePtName, NoFactory::get());
167 } else {
168 RCP<const FactoryBase> mapOnePtFact = GetFactory(mapOnePtFactName);
169 OnePtMap = currentLevel.Get<RCP<Map>>(mapOnePtName, mapOnePtFact.get());
170 }
171 }
172
173 // Set map for interface aggregates
174 std::string mapInterfaceName = pL.get<std::string>("Interface aggregate map name");
175 RCP<Map> InterfaceMap = Teuchos::null;
176
177 RCP<const LWGraph> graph;
178 RCP<const LWGraph_kokkos> graph_kokkos;
179 RCP<Aggregates> aggregates;
180 RCP<const Teuchos::Comm<int>> comm;
181 LO numRows;
182
183 const std::string aggregationBackend = pL.get<std::string>("aggregation: backend");
184
185 // "Graph" can have type "LWGraph" or "LWGraph_kokkos".
186 // The aggregation phases can call either "BuildAggregatesNonKokkos" or "BuildAggregates".
187
188 // "aggregation: backend" can take values "default", "non-Kokkos" or "Kokkos".
189 // "default": run depending on the type of "Graph"
190 // "non-Kokkos": run the non-Kokkos aggregation, moving "Graph" to host if necessary
191 // "Kokkos": run the Kokkos aggregation, potentially move "Graph", moving "Graph" to device if necessary
192
193 bool runOnHost;
194 if (IsType<RCP<LWGraph>>(currentLevel, "Graph")) {
195 if ((aggregationBackend == "default") || (aggregationBackend == "host")) {
196 graph = Get<RCP<LWGraph>>(currentLevel, "Graph");
197 aggregates = rcp(new Aggregates(*graph));
198 comm = graph->GetComm();
199 numRows = graph->GetNodeNumVertices();
200 runOnHost = true;
201 } else {
202 RCP<LWGraph> tmp_graph = Get<RCP<LWGraph>>(currentLevel, "Graph");
203 graph_kokkos = tmp_graph->copyToDevice();
204 aggregates = rcp(new Aggregates(*graph_kokkos));
205 comm = graph_kokkos->GetComm();
206 numRows = graph_kokkos->GetNodeNumVertices();
207 runOnHost = false;
208 }
209 } else if (IsType<RCP<LWGraph_kokkos>>(currentLevel, "Graph")) {
210 if ((aggregationBackend == "default") || (aggregationBackend == "kokkos")) {
211 graph_kokkos = Get<RCP<LWGraph_kokkos>>(currentLevel, "Graph");
212 aggregates = rcp(new Aggregates(*graph_kokkos));
213 comm = graph_kokkos->GetComm();
214 numRows = graph_kokkos->GetNodeNumVertices();
215 runOnHost = false;
216 } else {
217 RCP<LWGraph_kokkos> tmp_graph_kokkos = Get<RCP<LWGraph_kokkos>>(currentLevel, "Graph");
218 graph = tmp_graph_kokkos->copyToHost();
219 aggregates = rcp(new Aggregates(*graph));
220 comm = graph->GetComm();
221 numRows = graph->GetNodeNumVertices();
222 runOnHost = true;
223 }
224 } else {
225 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Graph has bad type.");
226 }
227
228 if (!runOnHost) {
229 TEUCHOS_TEST_FOR_EXCEPTION(pL.get<bool>("aggregation: use interface aggregation"), std::invalid_argument, "Option: 'aggregation: use interface aggregation' is not supported in the Kokkos version of uncoupled aggregation");
230 // Sanity Checking: match ML behavior is not supported in UncoupledAggregation_Kokkos in Phase 1 , but it is in 2a and 2b
231 TEUCHOS_TEST_FOR_EXCEPTION(pL.get<bool>("aggregation: match ML phase1"), std::invalid_argument, "Option: 'aggregation: match ML phase1' is not supported in the Kokkos version of uncoupled aggregation");
232 }
233
234 // Build
235 aggregates->setObjectLabel("UC");
236
237 // construct aggStat information
240 AggStatHostType aggStatHost;
241 AggStatType aggStat;
242
243 if (runOnHost) {
244 aggStatHost = AggStatHostType(Kokkos::ViewAllocateWithoutInitializing("aggregation status"), numRows);
245 Kokkos::deep_copy(aggStatHost, READY);
246 } else {
247 aggStat = AggStatType(Kokkos::ViewAllocateWithoutInitializing("aggregation status"), numRows);
248 Kokkos::deep_copy(aggStat, READY);
249 }
250
251 // interface
252 if (pL.get<bool>("aggregation: use interface aggregation") == true) {
253 Teuchos::Array<LO> nodeOnInterface = Get<Array<LO>>(currentLevel, "nodeOnInterface");
254 for (LO i = 0; i < numRows; i++) {
255 if (nodeOnInterface[i])
256 aggStatHost[i] = INTERFACE;
257 }
258 }
259
260 // Dirichlet nodes
261 {
262 if (runOnHost) {
263 auto dirichletBoundaryMap = graph->GetBoundaryNodeMap();
264 Kokkos::parallel_for(
265 "MueLu - UncoupledAggregation: tagging boundary nodes in aggStat",
266 Kokkos::RangePolicy<LocalOrdinal, typename LWGraph::execution_space>(0, numRows),
267 KOKKOS_LAMBDA(const LocalOrdinal nodeIdx) {
268 if (dirichletBoundaryMap(nodeIdx) == true) {
269 aggStatHost(nodeIdx) = BOUNDARY;
270 }
271 });
272 } else {
273 auto dirichletBoundaryMap = graph_kokkos->GetBoundaryNodeMap();
274 Kokkos::parallel_for(
275 "MueLu - UncoupledAggregation: tagging boundary nodes in aggStat",
276 Kokkos::RangePolicy<LocalOrdinal, typename LWGraph_kokkos::execution_space>(0, numRows),
277 KOKKOS_LAMBDA(const LocalOrdinal nodeIdx) {
278 if (dirichletBoundaryMap(nodeIdx) == true) {
279 aggStat(nodeIdx) = BOUNDARY;
280 }
281 });
282 }
283 }
284
285 if (OnePtMap != Teuchos::null) {
286 LO nDofsPerNode = Get<LO>(currentLevel, "DofsPerNode");
287
288 if (runOnHost) {
289 GO indexBase = graph->GetDomainMap()->getIndexBase();
290 for (LO i = 0; i < numRows; i++) {
291 // reconstruct global row id (FIXME only works for contiguous maps)
292 GO grid = (graph->GetDomainMap()->getGlobalElement(i) - indexBase) * nDofsPerNode + indexBase;
293
294 for (LO kr = 0; kr < nDofsPerNode; kr++)
295 if (OnePtMap->isNodeGlobalElement(grid + kr))
296 aggStatHost(i) = ONEPT;
297 }
298 } else {
299 GO indexBase = graph_kokkos->GetDomainMap()->getIndexBase();
300 auto lclDomainMap = graph_kokkos->GetDomainMap()->getLocalMap();
301 auto lclOnePtMap = OnePtMap->getLocalMap();
302 const LocalOrdinal INVALID = Tpetra::Details::OrdinalTraits<LocalOrdinal>::invalid();
303 Kokkos::parallel_for(
304 "MueLu - UncoupledAggregation: tagging OnePt map",
305 Kokkos::RangePolicy<LocalOrdinal, typename LWGraph_kokkos::execution_space>(0, numRows),
306 KOKKOS_LAMBDA(const LocalOrdinal i) {
307 // reconstruct global row id (FIXME only works for contiguous maps)
308 GO grid = (lclDomainMap.getGlobalElement(i) - indexBase) * nDofsPerNode + indexBase;
309
310 for (LO kr = 0; kr < nDofsPerNode; kr++)
311 if (lclOnePtMap.getLocalElement(grid + kr) != INVALID)
312 aggStat(i) = ONEPT;
313 });
314 }
315 }
316
317 LO numNonAggregatedNodes = numRows;
318 std::string aggAlgo = pL.get<std::string>("aggregation: coloring algorithm");
319 if (aggAlgo == "mis2 coarsening" || aggAlgo == "mis2 aggregation") {
320 TEUCHOS_ASSERT(!runOnHost);
321
322 SubFactoryMonitor sfm(*this, "Algo \"MIS2\"", currentLevel);
323 using graph_t = typename LWGraph_kokkos::local_graph_type;
324 using device_t = typename graph_t::device_type;
325 using exec_space = typename device_t::execution_space;
326 using rowmap_t = typename graph_t::row_map_type;
327 using colinds_t = typename graph_t::entries_type;
328 using lno_t = typename colinds_t::non_const_value_type;
329 rowmap_t aRowptrs = graph_kokkos->getRowPtrs();
330 colinds_t aColinds = graph_kokkos->getEntries();
331 lno_t numAggs = 0;
332 typename colinds_t::non_const_type labels;
333
334 if (aggAlgo == "mis2 coarsening") {
335 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: MIS-2 coarsening" << std::endl;
336 labels = KokkosGraph::graph_mis2_coarsen<device_t, rowmap_t, colinds_t>(aRowptrs, aColinds, numAggs);
337 } else if (aggAlgo == "mis2 aggregation") {
338 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: MIS-2 aggregation" << std::endl;
339 labels = KokkosGraph::graph_mis2_aggregate<device_t, rowmap_t, colinds_t>(aRowptrs, aColinds, numAggs);
340 }
341 {
342 size_t labelCapacity = numAggs * 1.5;
343 // until all hashmap insertions succeed...
344 while (true) {
345 // find aggregates that are not empty
346 Kokkos::UnorderedMap<LocalOrdinal, void, exec_space> used_labels(labelCapacity);
347 Kokkos::parallel_for(
348 "MueLu::UncoupledAggregationFactory::MIS2::nonempty_aggs",
349 Kokkos::RangePolicy<exec_space>(0, numRows),
350 KOKKOS_LAMBDA(lno_t i) {
351 if (aggStat(i) == READY)
352 used_labels.insert(labels(i));
353 });
354 Kokkos::fence();
355 if (used_labels.failed_insert()) {
356 // Retry with larger hashmap capacity
357 labelCapacity = (labelCapacity + 1) * 1.5;
358 continue;
359 }
360
361 // compute aggIds for non-empty aggs
362 Kokkos::View<LO*, typename device_t::memory_space> new_labels("new_labels", numAggs);
363 Kokkos::parallel_scan(
364 "MueLu::UncoupledAggregationFactory::MIS2::set_new_labels",
365 Kokkos::RangePolicy<exec_space>(0, used_labels.capacity()),
366 KOKKOS_LAMBDA(lno_t i, lno_t & update, const bool is_final) {
367 if (used_labels.valid_at(i)) {
368 auto label = used_labels.key_at(i);
369 if (is_final) {
370 new_labels(label) = update;
371 }
372 ++update;
373 }
374 },
375 numAggs);
376
377 // reassign aggIds
378 Kokkos::parallel_for(
379 "MueLu::UncoupledAggregationFactory::MIS2::reassign_labels",
380 Kokkos::RangePolicy<exec_space>(0, numRows),
381 KOKKOS_LAMBDA(lno_t i) {
382 labels(i) = new_labels(labels(i));
383 });
384 // Hashmap insertions all succeeded and we were able to update labels
385 break;
386 }
387
388 auto vertex2AggId = aggregates->GetVertex2AggId()->getLocalViewDevice(Tpetra::Access::ReadWrite);
389 auto procWinner = aggregates->GetProcWinner()->getLocalViewDevice(Tpetra::Access::OverwriteAll);
390 int rank = comm->getRank();
391 Kokkos::parallel_for(
392 Kokkos::RangePolicy<exec_space>(0, numRows),
393 KOKKOS_LAMBDA(lno_t i) {
394 if (aggStat(i) == READY) {
395#ifdef HAVE_MUELU_DEBUG
396 KOKKOS_ASSERT(labels(i) >= 0);
397#endif
398 procWinner(i, 0) = rank;
399 aggStat(i) = AGGREGATED;
400 vertex2AggId(i, 0) = labels(i);
401 } else {
402 procWinner(i, 0) = MUELU_UNASSIGNED;
403 aggStat(i) = IGNORED;
404 vertex2AggId(i, 0) = MUELU_UNAGGREGATED;
405 }
406 });
407 }
408 numNonAggregatedNodes = 0;
409 aggregates->SetNumAggregates(numAggs);
410 } else {
411 if (!runOnHost) {
412 DoGraphColoring(currentLevel, aggAlgo, pL.get<bool>("aggregation: deterministic"), graph_kokkos, aggregates);
413 if (IsPrint(Statistics1)) {
414 GetOStream(Statistics1) << " num colors: " << aggregates->GetGraphNumColors() << std::endl;
415 }
416 }
417
418 std::vector<GO> localStats;
419 if (IsPrint(Statistics1)) {
420 localStats = std::vector<GO>(1 + 2 * algos_.size());
421 localStats[0] = numRows;
422 }
423 for (size_t a = 0; a < algos_.size(); a++) {
424 std::string phase = algos_[a]->description();
425
426 SubFactoryMonitor sfm2(*this, "Algo \"" + phase + "\"" + (numNonAggregatedNodes == 0 ? " [skipped since no nodes are left to aggregate]" : ""), currentLevel);
427 int oldRank = algos_[a]->SetProcRankVerbose(this->GetProcRankVerbose());
428
429 algos_[a]->SetupPhase(pL, comm, numRows, numNonAggregatedNodes);
430
431 if (numNonAggregatedNodes > 0) {
432 if (runOnHost)
433 algos_[a]->BuildAggregatesNonKokkos(pL, *graph, *aggregates, aggStatHost, numNonAggregatedNodes);
434 else
435 algos_[a]->BuildAggregates(pL, *graph_kokkos, *aggregates, aggStat, numNonAggregatedNodes);
436 }
437 algos_[a]->SetProcRankVerbose(oldRank);
438
439 if (IsPrint(Statistics1)) {
440 localStats[2 * a + 1] = numRows - numNonAggregatedNodes; // num local aggregated nodes
441 localStats[2 * a + 2] = aggregates->GetNumAggregates(); // num local aggregates
442 }
443 }
444 if (IsPrint(Statistics1)) {
445 std::vector<GO> globalStats(1 + 2 * algos_.size());
446 Teuchos::reduceAll(*comm, Teuchos::REDUCE_SUM, (int)localStats.size(), localStats.data(), globalStats.data());
447 GO numGlobalRows = globalStats[0];
448 GO numGlobalAggregatedPrev = 0, numGlobalAggsPrev = 0;
449 std::stringstream ss;
450 for (size_t a = 0; a < algos_.size(); a++) {
451 std::string phase = algos_[a]->description();
452 GO numGlobalAggregated = globalStats[2 * a + 1];
453 GO numGlobalAggs = globalStats[2 * a + 2];
454 GO numGlobalNonAggregatedNodes = numGlobalRows - numGlobalAggregatedPrev;
455 double aggPercent = 100 * as<double>(numGlobalAggregated) / as<double>(numGlobalRows);
456 if (aggPercent > 99.99 && aggPercent < 100.00) {
457 // Due to round off (for instance, for 140465733/140466897), we could
458 // get 100.00% display even if there are some remaining nodes. This
459 // is bad from the users point of view. It is much better to change
460 // it to display 99.99%.
461 aggPercent = 99.99;
462 }
463
464 ss << "Algo \"" + phase + "\"" + (numGlobalNonAggregatedNodes == 0 ? " [skipped since no nodes are left to aggregate]" : "") << std::endl
465 << " aggregated : " << (numGlobalAggregated - numGlobalAggregatedPrev) << " (phase), " << std::fixed
466 << std::setprecision(2) << numGlobalAggregated << "/" << numGlobalRows << " [" << aggPercent << "%] (total)\n"
467 << " remaining : " << numGlobalRows - numGlobalAggregated << "\n"
468 << " aggregates : " << numGlobalAggs - numGlobalAggsPrev << " (phase), " << numGlobalAggs << " (total)" << std::endl;
469 numGlobalAggregatedPrev = numGlobalAggregated;
470 numGlobalAggsPrev = numGlobalAggs;
471 }
472 GetOStream(Statistics1) << ss.str();
473 }
474 }
475
476 TEUCHOS_TEST_FOR_EXCEPTION(numNonAggregatedNodes, Exceptions::RuntimeError, "MueLu::UncoupledAggregationFactory::Build: Leftover nodes found! Error!");
477
478 aggregates->AggregatesCrossProcessors(false);
479 aggregates->ComputeAggregateSizes(true /*forceRecompute*/);
480
481 Set(currentLevel, "Aggregates", aggregates);
482}
483
484template <class LocalOrdinal, class GlobalOrdinal, class Node>
486 DoGraphColoring(Level& currentLevel,
487 const std::string& aggAlgo,
488 const bool deterministic,
489 const RCP<const LWGraph_kokkos> graph,
490 RCP<Aggregates> aggregates) const {
491 SubFactoryMonitor sfm(*this, "Algo \"Graph Coloring\"", currentLevel);
492
493 // LBV on Sept 06 2019: the note below is a little worrisome,
494 // can we guarantee that MueLu is never used on a non-symmetric
495 // graph?
496 // note: just using colinds_view in place of scalar_view_t type
497 // (it won't be used at all by symbolic SPGEMM)
498 using graph_t = typename LWGraph_kokkos::local_graph_type;
499 using KernelHandle = KokkosKernels::Experimental::
500 KokkosKernelsHandle<typename graph_t::row_map_type::value_type,
501 typename graph_t::entries_type::value_type,
502 typename graph_t::entries_type::value_type,
503 typename graph_t::device_type::execution_space,
504 typename graph_t::device_type::memory_space,
505 typename graph_t::device_type::memory_space>;
506 KernelHandle kh;
507 // leave gc algorithm choice as the default
508 kh.create_distance2_graph_coloring_handle();
509
510 // get the distance-2 graph coloring handle
511 auto coloringHandle = kh.get_distance2_graph_coloring_handle();
512
513 const LO numRows = graph->GetNodeNumVertices();
514
515 // Set the distance-2 graph coloring algorithm to use.
516 // Options:
517 // COLORING_D2_DEFAULT - Let the kernel handle pick the variation
518 // COLORING_D2_SERIAL - Use the legacy serial-only implementation
519 // COLORING_D2_VB - Use the parallel vertex based direct method
520 // COLORING_D2_VB_BIT - Same as VB but using the bitvector forbidden array
521 // COLORING_D2_VB_BIT_EF - Add experimental edge-filtering to VB_BIT
522 // COLORING_D2_NB_BIT - Net-based coloring (generally the fastest)
523 if (deterministic) {
524 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_SERIAL);
525 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: serial" << std::endl;
526 } else if (aggAlgo == "serial") {
527 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_SERIAL);
528 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: serial" << std::endl;
529 } else if (aggAlgo == "default") {
530 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_DEFAULT);
531 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: default" << std::endl;
532 } else if (aggAlgo == "vertex based") {
533 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_VB);
534 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: vertex based" << std::endl;
535 } else if (aggAlgo == "vertex based bit set") {
536 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_VB_BIT);
537 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: vertex based bit set" << std::endl;
538 } else if (aggAlgo == "edge filtering") {
539 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_VB_BIT_EF);
540 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: edge filtering" << std::endl;
541 } else if (aggAlgo == "net based bit set") {
542 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_NB_BIT);
543 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: net based bit set" << std::endl;
544 } else {
545 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unrecognized distance 2 coloring algorithm, valid options are: serial, default, matrix squared, vertex based, vertex based bit set, edge filtering")
546 }
547
548 // Create device views for graph rowptrs/colinds
549 typename graph_t::row_map_type aRowptrs = graph->getRowPtrs();
550 typename graph_t::entries_type aColinds = graph->getEntries();
551
552 // run d2 graph coloring
553 // graph is symmetric so row map/entries and col map/entries are the same
554 {
555 SubFactoryMonitor sfm2(*this, "Algo \"Graph Coloring\": KokkosGraph Call", currentLevel); // CMS HACK
556 KokkosGraph::Experimental::graph_color_distance2(&kh, numRows, aRowptrs, aColinds);
557 }
558
559 // extract the colors and store them in the aggregates
560 aggregates->SetGraphColors(coloringHandle->get_vertex_colors());
561 aggregates->SetGraphNumColors(static_cast<LO>(coloringHandle->get_num_colors()));
562
563 // clean up coloring handle
564 kh.destroy_distance2_graph_coloring_handle();
565}
566
567} // namespace MueLu
568
569#endif /* MUELU_UNCOUPLEDAGGREGATIONFACTORY_DEF_HPP_ */
#define MUELU_UNAGGREGATED
#define MUELU_UNASSIGNED
#define SET_VALID_ENTRY(name)
MueLu::DefaultLocalOrdinal LocalOrdinal
Container class for aggregation information.
Kokkos::View< unsigned *, typename LWGraphHostType::device_type > AggStatHostType
Kokkos::View< unsigned *, typename LWGraphType::device_type > AggStatType
Algorithm for coarsening a graph with uncoupled aggregation.
Among unaggregated points, see if we can make a reasonable size aggregate out of it.
Handle leftover nodes. Try to avoid singleton nodes.
Exception throws to report errors in the internal logical of the program.
Timer to be used in factories. Similar to Monitor but with additional timers.
Algorithm for coarsening a graph with uncoupled aggregation. creates aggregates along an interface us...
typename std::conditional< OnHost, typename local_graph_device_type::host_mirror_type, local_graph_device_type >::type local_graph_type
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.
void DeclareInput(const std::string &ename, const FactoryBase *factory, const FactoryBase *requestedBy=NoFactory::get())
Callback from FactoryBase::CallDeclareInput() and FactoryBase::DeclareInput()
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)....
static const NoFactory * get()
Algorithm for coarsening a graph with uncoupled aggregation. keep special marked nodes as singleton n...
Builds one-to-one aggregates for all Dirichlet boundary nodes. For some applications this might be ne...
Timer to be used in factories. Similar to SubMonitor but adds a timer level by level.
RCP< const ParameterList > GetValidParameterList() const
Return a const parameter list of valid parameters that setParameterList() will accept.
void Build(Level &currentLevel) const
Build aggregates.
virtual ~UncoupledAggregationFactory()
Destructor.
Namespace for MueLu classes and methods.
@ Statistics1
Print more statistics.