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 const std::string aggAlgo = pL.get<std::string>("aggregation: coloring algorithm");
185
186 // "Graph" can have type "LWGraph" or "LWGraph_kokkos".
187 // The aggregation phases can call either "BuildAggregatesNonKokkos" or "BuildAggregates".
188
189 // "aggregation: backend" can take values "default", "non-Kokkos" or "Kokkos".
190 // "default": run depending on the type of "Graph"
191 // "non-Kokkos": run the non-Kokkos aggregation, moving "Graph" to host if necessary
192 // "Kokkos": run the Kokkos aggregation, potentially move "Graph", moving "Graph" to device if necessary
193
194 bool runOnHost;
195 if (IsType<RCP<LWGraph>>(currentLevel, "Graph")) {
196 if ((aggregationBackend == "default") || (aggregationBackend == "host")) {
197 graph = Get<RCP<LWGraph>>(currentLevel, "Graph");
198 aggregates = rcp(new Aggregates(*graph));
199 comm = graph->GetComm();
200 numRows = graph->GetNodeNumVertices();
201 runOnHost = true;
202 } else {
203 RCP<LWGraph> tmp_graph = Get<RCP<LWGraph>>(currentLevel, "Graph");
204 graph_kokkos = tmp_graph->copyToDevice();
205 aggregates = rcp(new Aggregates(*graph_kokkos));
206 comm = graph_kokkos->GetComm();
207 numRows = graph_kokkos->GetNodeNumVertices();
208 runOnHost = false;
209 }
210 } else if (IsType<RCP<LWGraph_kokkos>>(currentLevel, "Graph")) {
211 if ((aggregationBackend == "default") || (aggregationBackend == "kokkos")) {
212 graph_kokkos = Get<RCP<LWGraph_kokkos>>(currentLevel, "Graph");
213 aggregates = rcp(new Aggregates(*graph_kokkos));
214 comm = graph_kokkos->GetComm();
215 numRows = graph_kokkos->GetNodeNumVertices();
216 runOnHost = false;
217 } else {
218 RCP<LWGraph_kokkos> tmp_graph_kokkos = Get<RCP<LWGraph_kokkos>>(currentLevel, "Graph");
219 graph = tmp_graph_kokkos->copyToHost();
220 aggregates = rcp(new Aggregates(*graph));
221 comm = graph->GetComm();
222 numRows = graph->GetNodeNumVertices();
223 runOnHost = true;
224 }
225 } else {
226 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Graph has bad type.");
227 }
228
229 if (!runOnHost) {
230 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");
231 if (aggAlgo != "mis2 coarsening" && aggAlgo != "mis2 aggregation") {
232 // Sanity Checking: match ML behavior is not supported in UncoupledAggregation_Kokkos in Phase 1 , but it is in 2a and 2b
233 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");
234 }
235 }
236
237 // Build
238 aggregates->setObjectLabel("UC");
239
240 // construct aggStat information
243 AggStatHostType aggStatHost;
244 AggStatType aggStat;
245
246 if (runOnHost) {
247 aggStatHost = AggStatHostType(Kokkos::ViewAllocateWithoutInitializing("aggregation status"), numRows);
248 Kokkos::deep_copy(aggStatHost, READY);
249 } else {
250 aggStat = AggStatType(Kokkos::ViewAllocateWithoutInitializing("aggregation status"), numRows);
251 Kokkos::deep_copy(aggStat, READY);
252 }
253
254 // interface
255 if (pL.get<bool>("aggregation: use interface aggregation") == true) {
256 Teuchos::Array<LO> nodeOnInterface = Get<Array<LO>>(currentLevel, "nodeOnInterface");
257 for (LO i = 0; i < numRows; i++) {
258 if (nodeOnInterface[i])
259 aggStatHost[i] = INTERFACE;
260 }
261 }
262
263 // Dirichlet nodes
264 {
265 if (runOnHost) {
266 auto dirichletBoundaryMap = graph->GetBoundaryNodeMap();
267 Kokkos::parallel_for(
268 "MueLu - UncoupledAggregation: tagging boundary nodes in aggStat",
269 Kokkos::RangePolicy<LocalOrdinal, typename LWGraph::execution_space>(0, numRows),
270 KOKKOS_LAMBDA(const LocalOrdinal nodeIdx) {
271 if (dirichletBoundaryMap(nodeIdx) == true) {
272 aggStatHost(nodeIdx) = BOUNDARY;
273 }
274 });
275 } else {
276 auto dirichletBoundaryMap = graph_kokkos->GetBoundaryNodeMap();
277 Kokkos::parallel_for(
278 "MueLu - UncoupledAggregation: tagging boundary nodes in aggStat",
279 Kokkos::RangePolicy<LocalOrdinal, typename LWGraph_kokkos::execution_space>(0, numRows),
280 KOKKOS_LAMBDA(const LocalOrdinal nodeIdx) {
281 if (dirichletBoundaryMap(nodeIdx) == true) {
282 aggStat(nodeIdx) = BOUNDARY;
283 }
284 });
285 }
286 }
287
288 if (OnePtMap != Teuchos::null) {
289 LO nDofsPerNode = Get<LO>(currentLevel, "DofsPerNode");
290
291 if (runOnHost) {
292 GO indexBase = graph->GetDomainMap()->getIndexBase();
293 for (LO i = 0; i < numRows; i++) {
294 // reconstruct global row id (FIXME only works for contiguous maps)
295 GO grid = (graph->GetDomainMap()->getGlobalElement(i) - indexBase) * nDofsPerNode + indexBase;
296
297 for (LO kr = 0; kr < nDofsPerNode; kr++)
298 if (OnePtMap->isNodeGlobalElement(grid + kr))
299 aggStatHost(i) = ONEPT;
300 }
301 } else {
302 GO indexBase = graph_kokkos->GetDomainMap()->getIndexBase();
303 auto lclDomainMap = graph_kokkos->GetDomainMap()->getLocalMap();
304 auto lclOnePtMap = OnePtMap->getLocalMap();
305 const LocalOrdinal INVALID = Tpetra::Details::OrdinalTraits<LocalOrdinal>::invalid();
306 Kokkos::parallel_for(
307 "MueLu - UncoupledAggregation: tagging OnePt map",
308 Kokkos::RangePolicy<LocalOrdinal, typename LWGraph_kokkos::execution_space>(0, numRows),
309 KOKKOS_LAMBDA(const LocalOrdinal i) {
310 // reconstruct global row id (FIXME only works for contiguous maps)
311 GO grid = (lclDomainMap.getGlobalElement(i) - indexBase) * nDofsPerNode + indexBase;
312
313 for (LO kr = 0; kr < nDofsPerNode; kr++)
314 if (lclOnePtMap.getLocalElement(grid + kr) != INVALID)
315 aggStat(i) = ONEPT;
316 });
317 }
318 }
319
320 LO numNonAggregatedNodes = numRows;
321 if (aggAlgo == "mis2 coarsening" || aggAlgo == "mis2 aggregation") {
322 TEUCHOS_ASSERT(!runOnHost);
323
324 SubFactoryMonitor sfm(*this, "Algo \"MIS2\"", currentLevel);
325 using graph_t = typename LWGraph_kokkos::local_graph_type;
326 using device_t = typename graph_t::device_type;
327 using exec_space = typename device_t::execution_space;
328 using rowmap_t = typename graph_t::row_map_type;
329 using colinds_t = typename graph_t::entries_type;
330 using lno_t = typename colinds_t::non_const_value_type;
331 rowmap_t aRowptrs = graph_kokkos->getRowPtrs();
332 colinds_t aColinds = graph_kokkos->getEntries();
333 lno_t numAggs = 0;
334 typename colinds_t::non_const_type labels;
335
336 if (aggAlgo == "mis2 coarsening") {
337 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: MIS-2 coarsening" << std::endl;
338 labels = KokkosGraph::graph_mis2_coarsen<device_t, rowmap_t, colinds_t>(aRowptrs, aColinds, numAggs);
339 } else if (aggAlgo == "mis2 aggregation") {
340 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: MIS-2 aggregation" << std::endl;
341 labels = KokkosGraph::graph_mis2_aggregate<device_t, rowmap_t, colinds_t>(aRowptrs, aColinds, numAggs);
342 }
343 {
344 size_t labelCapacity = numAggs * 1.5;
345 // until all hashmap insertions succeed...
346 while (true) {
347 // find aggregates that are not empty
348 Kokkos::UnorderedMap<LocalOrdinal, void, exec_space> used_labels(labelCapacity);
349 Kokkos::parallel_for(
350 "MueLu::UncoupledAggregationFactory::MIS2::nonempty_aggs",
351 Kokkos::RangePolicy<exec_space>(0, numRows),
352 KOKKOS_LAMBDA(lno_t i) {
353 if (aggStat(i) == READY)
354 used_labels.insert(labels(i));
355 });
356 Kokkos::fence();
357 if (used_labels.failed_insert()) {
358 // Retry with larger hashmap capacity
359 labelCapacity = (labelCapacity + 1) * 1.5;
360 continue;
361 }
362
363 // compute aggIds for non-empty aggs
364 Kokkos::View<LO*, typename device_t::memory_space> new_labels("new_labels", numAggs);
365 Kokkos::parallel_scan(
366 "MueLu::UncoupledAggregationFactory::MIS2::set_new_labels",
367 Kokkos::RangePolicy<exec_space>(0, used_labels.capacity()),
368 KOKKOS_LAMBDA(lno_t i, lno_t & update, const bool is_final) {
369 if (used_labels.valid_at(i)) {
370 auto label = used_labels.key_at(i);
371 if (is_final) {
372 new_labels(label) = update;
373 }
374 ++update;
375 }
376 },
377 numAggs);
378
379 // reassign aggIds
380 Kokkos::parallel_for(
381 "MueLu::UncoupledAggregationFactory::MIS2::reassign_labels",
382 Kokkos::RangePolicy<exec_space>(0, numRows),
383 KOKKOS_LAMBDA(lno_t i) {
384 labels(i) = new_labels(labels(i));
385 });
386 // Hashmap insertions all succeeded and we were able to update labels
387 break;
388 }
389
390 auto vertex2AggId = aggregates->GetVertex2AggId()->getLocalViewDevice(Tpetra::Access::ReadWrite);
391 auto procWinner = aggregates->GetProcWinner()->getLocalViewDevice(Tpetra::Access::OverwriteAll);
392 int rank = comm->getRank();
393 Kokkos::parallel_for(
394 Kokkos::RangePolicy<exec_space>(0, numRows),
395 KOKKOS_LAMBDA(lno_t i) {
396 if (aggStat(i) == READY) {
397#ifdef HAVE_MUELU_DEBUG
398 KOKKOS_ASSERT(labels(i) >= 0);
399#endif
400 procWinner(i, 0) = rank;
401 aggStat(i) = AGGREGATED;
402 vertex2AggId(i, 0) = labels(i);
403 } else {
404 procWinner(i, 0) = MUELU_UNASSIGNED;
405 aggStat(i) = IGNORED;
406 vertex2AggId(i, 0) = MUELU_UNAGGREGATED;
407 }
408 });
409 }
410 numNonAggregatedNodes = 0;
411 aggregates->SetNumAggregates(numAggs);
412 } else {
413 if (!runOnHost) {
414 DoGraphColoring(currentLevel, aggAlgo, pL.get<bool>("aggregation: deterministic"), graph_kokkos, aggregates);
415 if (IsPrint(Statistics1)) {
416 GetOStream(Statistics1) << " num colors: " << aggregates->GetGraphNumColors() << std::endl;
417 }
418 }
419
420 std::vector<GO> localStats;
421 if (IsPrint(Statistics1)) {
422 localStats = std::vector<GO>(1 + 2 * algos_.size());
423 localStats[0] = numRows;
424 }
425 for (size_t a = 0; a < algos_.size(); a++) {
426 std::string phase = algos_[a]->description();
427
428 SubFactoryMonitor sfm2(*this, "Algo \"" + phase + "\"" + (numNonAggregatedNodes == 0 ? " [skipped since no nodes are left to aggregate]" : ""), currentLevel);
429 int oldRank = algos_[a]->SetProcRankVerbose(this->GetProcRankVerbose());
430
431 algos_[a]->SetupPhase(pL, comm, numRows, numNonAggregatedNodes);
432
433 if (numNonAggregatedNodes > 0) {
434 if (runOnHost)
435 algos_[a]->BuildAggregatesNonKokkos(pL, *graph, *aggregates, aggStatHost, numNonAggregatedNodes);
436 else
437 algos_[a]->BuildAggregates(pL, *graph_kokkos, *aggregates, aggStat, numNonAggregatedNodes);
438 }
439 algos_[a]->SetProcRankVerbose(oldRank);
440
441 if (IsPrint(Statistics1)) {
442 localStats[2 * a + 1] = numRows - numNonAggregatedNodes; // num local aggregated nodes
443 localStats[2 * a + 2] = aggregates->GetNumAggregates(); // num local aggregates
444 }
445 }
446 if (IsPrint(Statistics1)) {
447 std::vector<GO> globalStats(1 + 2 * algos_.size());
448 Teuchos::reduceAll(*comm, Teuchos::REDUCE_SUM, (int)localStats.size(), localStats.data(), globalStats.data());
449 GO numGlobalRows = globalStats[0];
450 GO numGlobalAggregatedPrev = 0, numGlobalAggsPrev = 0;
451 std::stringstream ss;
452 for (size_t a = 0; a < algos_.size(); a++) {
453 std::string phase = algos_[a]->description();
454 GO numGlobalAggregated = globalStats[2 * a + 1];
455 GO numGlobalAggs = globalStats[2 * a + 2];
456 GO numGlobalNonAggregatedNodes = numGlobalRows - numGlobalAggregatedPrev;
457 double aggPercent = 100 * as<double>(numGlobalAggregated) / as<double>(numGlobalRows);
458 if (aggPercent > 99.99 && aggPercent < 100.00) {
459 // Due to round off (for instance, for 140465733/140466897), we could
460 // get 100.00% display even if there are some remaining nodes. This
461 // is bad from the users point of view. It is much better to change
462 // it to display 99.99%.
463 aggPercent = 99.99;
464 }
465
466 ss << "Algo \"" + phase + "\"" + (numGlobalNonAggregatedNodes == 0 ? " [skipped since no nodes are left to aggregate]" : "") << std::endl
467 << " aggregated : " << (numGlobalAggregated - numGlobalAggregatedPrev) << " (phase), " << std::fixed
468 << std::setprecision(2) << numGlobalAggregated << "/" << numGlobalRows << " [" << aggPercent << "%] (total)\n"
469 << " remaining : " << numGlobalRows - numGlobalAggregated << "\n"
470 << " aggregates : " << numGlobalAggs - numGlobalAggsPrev << " (phase), " << numGlobalAggs << " (total)" << std::endl;
471 numGlobalAggregatedPrev = numGlobalAggregated;
472 numGlobalAggsPrev = numGlobalAggs;
473 }
474 GetOStream(Statistics1) << ss.str();
475 }
476 }
477
478 TEUCHOS_TEST_FOR_EXCEPTION(numNonAggregatedNodes, Exceptions::RuntimeError, "MueLu::UncoupledAggregationFactory::Build: Leftover nodes found! Error!");
479
480 aggregates->AggregatesCrossProcessors(false);
481 aggregates->ComputeAggregateSizes(true /*forceRecompute*/);
482
483 Set(currentLevel, "Aggregates", aggregates);
484}
485
486template <class LocalOrdinal, class GlobalOrdinal, class Node>
488 DoGraphColoring(Level& currentLevel,
489 const std::string& aggAlgo,
490 const bool deterministic,
491 const RCP<const LWGraph_kokkos> graph,
492 RCP<Aggregates> aggregates) const {
493 SubFactoryMonitor sfm(*this, "Algo \"Graph Coloring\"", currentLevel);
494
495 // LBV on Sept 06 2019: the note below is a little worrisome,
496 // can we guarantee that MueLu is never used on a non-symmetric
497 // graph?
498 // note: just using colinds_view in place of scalar_view_t type
499 // (it won't be used at all by symbolic SPGEMM)
500 using graph_t = typename LWGraph_kokkos::local_graph_type;
501 using KernelHandle = KokkosKernels::Experimental::
502 KokkosKernelsHandle<typename graph_t::row_map_type::value_type,
503 typename graph_t::entries_type::value_type,
504 typename graph_t::entries_type::value_type,
505 typename graph_t::device_type::execution_space,
506 typename graph_t::device_type::memory_space,
507 typename graph_t::device_type::memory_space>;
508 KernelHandle kh;
509 // leave gc algorithm choice as the default
510 kh.create_distance2_graph_coloring_handle();
511
512 // get the distance-2 graph coloring handle
513 auto coloringHandle = kh.get_distance2_graph_coloring_handle();
514
515 const LO numRows = graph->GetNodeNumVertices();
516
517 // Set the distance-2 graph coloring algorithm to use.
518 // Options:
519 // COLORING_D2_DEFAULT - Let the kernel handle pick the variation
520 // COLORING_D2_SERIAL - Use the legacy serial-only implementation
521 // COLORING_D2_VB - Use the parallel vertex based direct method
522 // COLORING_D2_VB_BIT - Same as VB but using the bitvector forbidden array
523 // COLORING_D2_VB_BIT_EF - Add experimental edge-filtering to VB_BIT
524 // COLORING_D2_NB_BIT - Net-based coloring (generally the fastest)
525 if (deterministic) {
526 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_SERIAL);
527 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: serial" << std::endl;
528 } else if (aggAlgo == "serial") {
529 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_SERIAL);
530 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: serial" << std::endl;
531 } else if (aggAlgo == "default") {
532 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_DEFAULT);
533 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: default" << std::endl;
534 } else if (aggAlgo == "vertex based") {
535 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_VB);
536 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: vertex based" << std::endl;
537 } else if (aggAlgo == "vertex based bit set") {
538 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_VB_BIT);
539 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: vertex based bit set" << std::endl;
540 } else if (aggAlgo == "edge filtering") {
541 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_VB_BIT_EF);
542 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: edge filtering" << std::endl;
543 } else if (aggAlgo == "net based bit set") {
544 coloringHandle->set_algorithm(KokkosGraph::COLORING_D2_NB_BIT);
545 if (IsPrint(Statistics1)) GetOStream(Statistics1) << " algorithm: net based bit set" << std::endl;
546 } else {
547 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")
548 }
549
550 // Create device views for graph rowptrs/colinds
551 typename graph_t::row_map_type aRowptrs = graph->getRowPtrs();
552 typename graph_t::entries_type aColinds = graph->getEntries();
553
554 // run d2 graph coloring
555 // graph is symmetric so row map/entries and col map/entries are the same
556 {
557 SubFactoryMonitor sfm2(*this, "Algo \"Graph Coloring\": KokkosGraph Call", currentLevel); // CMS HACK
558 KokkosGraph::Experimental::graph_color_distance2(&kh, numRows, aRowptrs, aColinds);
559 }
560
561 // extract the colors and store them in the aggregates
562 aggregates->SetGraphColors(coloringHandle->get_vertex_colors());
563 aggregates->SetGraphNumColors(static_cast<LO>(coloringHandle->get_num_colors()));
564
565 // clean up coloring handle
566 kh.destroy_distance2_graph_coloring_handle();
567}
568
569} // namespace MueLu
570
571#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::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...
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.