MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_AggregationPhase3Algorithm_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_AGGREGATIONPHASE3ALGORITHM_DEF_HPP_
11#define MUELU_AGGREGATIONPHASE3ALGORITHM_DEF_HPP_
12
13#include <Teuchos_Comm.hpp>
14#include <Teuchos_CommHelpers.hpp>
15
16#include <Xpetra_Vector.hpp>
17
19
20#include "MueLu_Aggregates.hpp"
21#include "MueLu_Exceptions.hpp"
22#include "MueLu_LWGraph.hpp"
23#include "MueLu_Monitor.hpp"
24
25namespace MueLu {
26
27// Try to stick unaggregated nodes into a neighboring aggregate if they are
28// not already too big. Otherwise, make a new aggregate
29template <class LocalOrdinal, class GlobalOrdinal, class Node>
31 Monitor m(*this, "BuildAggregatesNonKokkos");
32
33 bool makeNonAdjAggs = false;
34 bool error_on_isolated = false;
35 if (params.isParameter("aggregation: error on nodes with no on-rank neighbors"))
36 error_on_isolated = params.get<bool>("aggregation: error on nodes with no on-rank neighbors");
37 if (params.isParameter("aggregation: phase3 avoid singletons"))
38 makeNonAdjAggs = params.get<bool>("aggregation: phase3 avoid singletons");
39
40 size_t numSingletons = 0;
41
42 const LO numRows = graph.GetNodeNumVertices();
43 const int myRank = graph.GetComm()->getRank();
44
45 ArrayRCP<LO> vertex2AggId = aggregates.GetVertex2AggId()->getDataNonConst(0);
46 ArrayRCP<LO> procWinner = aggregates.GetProcWinner()->getDataNonConst(0);
47
48 LO numLocalAggregates = aggregates.GetNumAggregates();
49
50 for (LO i = 0; i < numRows; i++) {
51 if (aggStat[i] == AGGREGATED || aggStat[i] == IGNORED)
52 continue;
53
54 auto neighOfINode = graph.getNeighborVertices(i);
55
56 // We don't want a singleton. So lets see if there is an unaggregated
57 // neighbor that we can also put with this point.
58 bool isNewAggregate = false;
59 bool failedToAggregate = true;
60 for (int j = 0; j < neighOfINode.length; j++) {
61 LO neigh = neighOfINode(j);
62
63 if (neigh != i && graph.isLocalNeighborVertex(neigh) && aggStat[neigh] == READY) {
64 isNewAggregate = true;
65
66 aggStat[neigh] = AGGREGATED;
67 vertex2AggId[neigh] = numLocalAggregates;
68 procWinner[neigh] = myRank;
69
70 numNonAggregatedNodes--;
71 }
72 }
73
74 if (isNewAggregate) {
75 // Create new aggregate (not singleton)
76 aggStat[i] = AGGREGATED;
77 procWinner[i] = myRank;
78 numNonAggregatedNodes--;
79 aggregates.SetIsRoot(i);
80 vertex2AggId[i] = numLocalAggregates++;
81
82 failedToAggregate = false;
83 } else {
84 // We do not want a singleton, but there are no non-aggregated
85 // neighbors. Lets see if we can connect to any other aggregates
86 // NOTE: This is very similar to phase 2b, but simplier: we stop with
87 // the first found aggregate
88 int j = 0;
89 for (; j < neighOfINode.length; j++) {
90 LO neigh = neighOfINode(j);
91
92 // We don't check (neigh != rootCandidate), as it is covered by checking (aggStat[neigh] == AGGREGATED)
93 if (graph.isLocalNeighborVertex(neigh) && aggStat[neigh] == AGGREGATED)
94 break;
95 }
96
97 if (j < neighOfINode.length) {
98 // Assign to an adjacent aggregate
99 vertex2AggId[i] = vertex2AggId[neighOfINode(j)];
100 numNonAggregatedNodes--;
101 failedToAggregate = false;
102 }
103 }
104
105 if (failedToAggregate && makeNonAdjAggs) {
106 // it we are still didn't find an aggregate home for i (i.e., we have
107 // a potential singleton), we are desperate. Basically, we seek to
108 // group i with any other local point to form an aggregate (even if
109 // it is not a neighbor of i. Either we find a vertex that is already
110 // aggregated or not aggregated.
111 // 1) if found vertex is aggregated, then assign i to this aggregate
112 // 2) if found vertex is not aggregated, create new aggregate
113
114 for (LO ii = 0; ii < numRows; ii++) { // look for anyone else
115 if ((ii != i) && (aggStat[ii] != IGNORED)) {
116 failedToAggregate = false; // found someone so start
117 aggStat[i] = AGGREGATED; // marking i as aggregated
118 procWinner[i] = myRank;
119
120 if (aggStat[ii] == AGGREGATED)
121 vertex2AggId[i] = vertex2AggId[ii];
122 else {
123 vertex2AggId[i] = numLocalAggregates;
124 vertex2AggId[ii] = numLocalAggregates;
125 aggStat[ii] = AGGREGATED;
126 procWinner[ii] = myRank;
127 numNonAggregatedNodes--; // acounts for ii now being aggregated
128 aggregates.SetIsRoot(i);
129 numLocalAggregates++;
130 }
131 numNonAggregatedNodes--; // accounts for i now being aggregated
132 break;
133 } // if ( (ii != i) && (aggStat[ii] != IGNORED ...
134 } // for (LO ii = 0; ...
135 }
136 if (failedToAggregate) {
137 if (error_on_isolated) {
138 // Error on this isolated node, as the user has requested
139 std::ostringstream oss;
140 oss << "MueLu::AggregationPhase3Algorithm::BuildAggregatesNonKokkos: MueLu has detected a non-Dirichlet node that has no on-rank neighbors and is terminating (by user request). " << std::endl;
141 oss << "If this error is being generated at level 0, this is due to an initial partitioning problem in your matrix." << std::endl;
142 oss << "If this error is being generated at any other level, try turning on repartitioning, which may fix this problem." << std::endl;
143 throw Exceptions::RuntimeError(oss.str());
144 } else {
145 // Create new aggregate (singleton)
146 // this->GetOStream(Warnings1) << "Found singleton: " << i << std::endl;
147 numSingletons++;
148
149 aggregates.SetIsRoot(i);
150 vertex2AggId[i] = numLocalAggregates++;
151 numNonAggregatedNodes--;
152 }
153 }
154
155 // One way or another, the node is aggregated (possibly into a singleton)
156 aggStat[i] = AGGREGATED;
157 procWinner[i] = myRank;
158
159 } // loop over numRows
160
161 if (numSingletons > 0)
162 this->GetOStream(Runtime0) << " WARNING Rank " << myRank << " singletons :" << numSingletons << " (phase)" << std::endl;
163
164 // update aggregate object
165 aggregates.SetNumAggregates(numLocalAggregates);
166}
167
168// Try to stick unaggregated nodes into a neighboring aggregate if they are
169// not already too big. Otherwise, make a new aggregate
170template <class LocalOrdinal, class GlobalOrdinal, class Node>
172 BuildAggregates(const ParameterList& params,
173 const LWGraph_kokkos& graph,
174 Aggregates& aggregates,
176 LO& numNonAggregatedNodes) const {
177 // So far we only have the non-deterministic version of the algorithm...
178 if (params.get<bool>("aggregation: deterministic")) {
179 Monitor m(*this, "BuildAggregatesDeterministic");
180 BuildAggregatesRandom(params, graph, aggregates, aggStat, numNonAggregatedNodes);
181 } else {
182 Monitor m(*this, "BuildAggregatesRandom");
183 BuildAggregatesRandom(params, graph, aggregates, aggStat, numNonAggregatedNodes);
184 }
185}
186
187// Try to stick unaggregated nodes into a neighboring aggregate if they are
188// not already too big. Otherwise, make a new aggregate
189template <class LocalOrdinal, class GlobalOrdinal, class Node>
191 BuildAggregatesRandom(const ParameterList& params,
192 const LWGraph_kokkos& graph,
193 Aggregates& aggregates,
195 LO& numNonAggregatedNodes) const {
196 using device_type = typename LWGraph_kokkos::device_type;
197 using execution_space = typename LWGraph_kokkos::execution_space;
198
199 bool error_on_isolated = params.get<bool>("aggregation: error on nodes with no on-rank neighbors");
200 bool makeNonAdjAggs = params.get<bool>("aggregation: phase3 avoid singletons");
201
202 const LO numRows = graph.GetNodeNumVertices();
203 const int myRank = graph.GetComm()->getRank();
204
205 auto vertex2AggId = aggregates.GetVertex2AggId()->getLocalViewDevice(Tpetra::Access::ReadWrite);
206 auto procWinner = aggregates.GetProcWinner()->getLocalViewDevice(Tpetra::Access::ReadWrite);
207 auto colors = aggregates.GetGraphColors();
208 const LO numColors = aggregates.GetGraphNumColors();
209
210 auto lclLWGraph = graph;
211
212 Kokkos::View<LO, device_type> numAggregates("numAggregates");
213 Kokkos::deep_copy(numAggregates, aggregates.GetNumAggregates());
214
215 Kokkos::View<unsigned*, device_type> aggStatOld(Kokkos::ViewAllocateWithoutInitializing("Initial aggregation status"), aggStat.extent(0));
216 Kokkos::deep_copy(aggStatOld, aggStat);
217 Kokkos::View<LO, device_type> numNonAggregated("numNonAggregated");
218 Kokkos::deep_copy(numNonAggregated, numNonAggregatedNodes);
219 for (int color = 1; color < numColors + 1; ++color) {
220 Kokkos::parallel_for(
221 "Aggregation Phase 3: aggregates clean-up",
222 Kokkos::RangePolicy<execution_space>(0, numRows),
223 KOKKOS_LAMBDA(const LO nodeIdx) {
224 // Check if node has already been treated?
225 if ((colors(nodeIdx) != color) ||
226 (aggStatOld(nodeIdx) == AGGREGATED) ||
227 (aggStatOld(nodeIdx) == IGNORED)) {
228 return;
229 }
230
231 // Grab node neighbors
232 auto neighbors = lclLWGraph.getNeighborVertices(nodeIdx);
233 LO neighIdx;
234
235 // We don't want a singleton.
236 // So lets see if any neighbors can be used to form a new aggregate?
237 bool isNewAggregate = false;
238 for (int neigh = 0; neigh < neighbors.length; ++neigh) {
239 neighIdx = neighbors(neigh);
240
241 if ((neighIdx != nodeIdx) &&
242 lclLWGraph.isLocalNeighborVertex(neighIdx) &&
243 (aggStatOld(neighIdx) == READY)) {
244 isNewAggregate = true;
245 break;
246 }
247 }
248
249 // We can form a new non singleton aggregate!
250 if (isNewAggregate) {
251 // If this is the aggregate root
252 // we need to process the nodes in the aggregate
253 const LO aggId = Kokkos::atomic_fetch_add(&numAggregates(), 1);
254 aggStat(nodeIdx) = AGGREGATED;
255 procWinner(nodeIdx, 0) = myRank;
256 vertex2AggId(nodeIdx, 0) = aggId;
257 // aggregates.SetIsRoot(nodeIdx);
258 Kokkos::atomic_dec(&numNonAggregated());
259 for (int neigh = 0; neigh < neighbors.length; ++neigh) {
260 neighIdx = neighbors(neigh);
261 if ((neighIdx != nodeIdx) &&
262 lclLWGraph.isLocalNeighborVertex(neighIdx) &&
263 (aggStatOld(neighIdx) == READY)) {
264 aggStat(neighIdx) = AGGREGATED;
265 procWinner(neighIdx, 0) = myRank;
266 vertex2AggId(neighIdx, 0) = aggId;
267 Kokkos::atomic_dec(&numNonAggregated());
268 }
269 }
270 return;
271 }
272
273 // Getting a little desperate!
274 // Let us try to aggregate into a neighboring aggregate
275 for (int neigh = 0; neigh < neighbors.length; ++neigh) {
276 neighIdx = neighbors(neigh);
277 if (lclLWGraph.isLocalNeighborVertex(neighIdx) &&
278 (aggStatOld(neighIdx) == AGGREGATED)) {
279 aggStat(nodeIdx) = AGGREGATED;
280 procWinner(nodeIdx, 0) = myRank;
281 vertex2AggId(nodeIdx, 0) = vertex2AggId(neighIdx, 0);
282 Kokkos::atomic_dec(&numNonAggregated());
283 return;
284 }
285 }
286
287 // Getting quite desperate!
288 // Let us try to make a non contiguous aggregate
289 if (makeNonAdjAggs) {
290 for (LO otherNodeIdx = 0; otherNodeIdx < numRows; ++otherNodeIdx) {
291 if ((otherNodeIdx != nodeIdx) &&
292 (aggStatOld(otherNodeIdx) == AGGREGATED)) {
293 aggStat(nodeIdx) = AGGREGATED;
294 procWinner(nodeIdx, 0) = myRank;
295 vertex2AggId(nodeIdx, 0) = vertex2AggId(otherNodeIdx, 0);
296 Kokkos::atomic_dec(&numNonAggregated());
297 return;
298 }
299 }
300 }
301
302 // Total deperation!
303 // Let us make a singleton
304 if (!error_on_isolated) {
305 const LO aggId = Kokkos::atomic_fetch_add(&numAggregates(), 1);
306 aggStat(nodeIdx) = AGGREGATED;
307 procWinner(nodeIdx, 0) = myRank;
308 vertex2AggId(nodeIdx, 0) = aggId;
309 Kokkos::atomic_dec(&numNonAggregated());
310 }
311 });
312 // LBV on 09/27/19: here we could copy numNonAggregated to host
313 // and check for it to be equal to 0 in which case we can stop
314 // looping over the different colors...
315 Kokkos::deep_copy(aggStatOld, aggStat);
316 } // loop over colors
317
318 auto numNonAggregated_h = Kokkos::create_mirror_view(numNonAggregated);
319 Kokkos::deep_copy(numNonAggregated_h, numNonAggregated);
320 numNonAggregatedNodes = numNonAggregated_h();
321 if ((error_on_isolated) && (numNonAggregatedNodes > 0)) {
322 // Error on this isolated node, as the user has requested
323 std::ostringstream oss;
324 oss << "MueLu::AggregationPhase3Algorithm::BuildAggregates: MueLu has detected a non-Dirichlet node that has no on-rank neighbors and is terminating (by user request). " << std::endl;
325 oss << "If this error is being generated at level 0, this is due to an initial partitioning problem in your matrix." << std::endl;
326 oss << "If this error is being generated at any other level, try turning on repartitioning, which may fix this problem." << std::endl;
327 throw Exceptions::RuntimeError(oss.str());
328 }
329
330 // update aggregate object
331 auto numAggregates_h = Kokkos::create_mirror_view(numAggregates);
332 Kokkos::deep_copy(numAggregates_h, numAggregates);
333 aggregates.SetNumAggregates(numAggregates_h());
334}
335
336} // namespace MueLu
337
338#endif /* MUELU_AGGREGATIONPHASE3ALGORITHM_DEF_HPP_ */
Container class for aggregation information.
LO GetGraphNumColors()
Get the number of colors needed by the distance 2 coloring.
KOKKOS_INLINE_FUNCTION LO GetNumAggregates() const
void SetIsRoot(LO i, bool value=true)
Set root node information.
const RCP< LOMultiVector > & GetVertex2AggId() const
Returns constant vector that maps local node IDs to local aggregates IDs.
colors_view_type & GetGraphColors()
Get a distance 2 coloring of the underlying graph. The coloring is computed and set during Phase1 of ...
const RCP< LOVector > & GetProcWinner() const
Returns constant vector that maps local node IDs to owning processor IDs.
void SetNumAggregates(LO nAggregates)
Set number of local aggregates on current processor.
Kokkos::View< unsigned *, typename LWGraphHostType::device_type > AggStatHostType
Kokkos::View< unsigned *, typename LWGraphType::device_type > AggStatType
void BuildAggregates(const ParameterList &params, const LWGraph_kokkos &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatType &aggStat, LO &numNonAggregatedNodes) const
void BuildAggregatesRandom(const ParameterList &params, const LWGraph_kokkos &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatType &aggStat, LO &numNonAggregatedNodes) const
void BuildAggregatesNonKokkos(const ParameterList &params, const LWGraph &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatHostType &aggStat, LO &numNonAggregatedNodes) const
Local aggregation.
Exception throws to report errors in the internal logical of the program.
KOKKOS_INLINE_FUNCTION size_type GetNodeNumVertices() const
Return number of graph vertices.
typename std::conditional< OnHost, Kokkos::Device< Kokkos::Serial, Kokkos::HostSpace >, typename Node::device_type >::type device_type
KOKKOS_INLINE_FUNCTION bool isLocalNeighborVertex(LO i) const
Return true if vertex with local id 'v' is on current process.
const RCP< const Teuchos::Comm< int > > GetComm() const
KOKKOS_INLINE_FUNCTION neighbor_vertices_type getNeighborVertices(LO i) const
Return the list of vertices adjacent to the vertex 'v'.
Lightweight MueLu representation of a compressed row storage graph.
Lightweight MueLu representation of a compressed row storage graph.
Timer to be used in non-factories.
Namespace for MueLu classes and methods.
@ Runtime0
One-liner description of what is happening.