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.
void Input(Level &level, const std::string &varName) const
T Get(Level &level, const std::string &varName) const
bool IsType(Level &level, const std::string &varName) const
void Set(Level &level, const std::string &varName, const T &data) const
const RCP< const FactoryBase > GetFactory(const std::string &varName) const
Default implementation of FactoryAcceptor::GetFactory().
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::HostMirror, 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...
virtual const Teuchos::ParameterList & GetParameterList() const
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.
void DoGraphColoring(Level &currentLevel, const std::string &aggAlgo, const bool deterministic, const RCP< const LWGraph_kokkos > graph, RCP< Aggregates > aggregates) const
std::vector< RCP< MueLu::AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node > > > algos_
Append a new aggregation algorithm to list of aggregation algorithms.
Teuchos::FancyOStream & GetOStream(MsgType type, int thisProcRankOnly=0) const
Get an output stream for outputting the input message type.
bool IsPrint(MsgType type, int thisProcRankOnly=-1) const
Find out whether we need to print out information for a specific message type.
int GetProcRankVerbose() const
Get proc rank used for printing. Do not use this information for any other purpose....
Namespace for MueLu classes and methods.
@ Statistics1
Print more statistics.