MueLu  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 
25 namespace 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
29 template <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
170 template <class LocalOrdinal, class GlobalOrdinal, class Node>
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
189 template <class LocalOrdinal, class GlobalOrdinal, class Node>
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()->getDeviceLocalView(Xpetra::Access::ReadWrite);
206  auto procWinner = aggregates.GetProcWinner()->getDeviceLocalView(Xpetra::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_ */
Kokkos::View< unsigned *, typename LWGraphHostType::device_type > AggStatHostType
Lightweight MueLu representation of a compressed row storage graph.
const RCP< LOVector > & GetProcWinner() const
Returns constant vector that maps local node IDs to owning processor IDs.
Container class for aggregation information.
KOKKOS_INLINE_FUNCTION LO GetNumAggregates() const
void BuildAggregates(const ParameterList &params, const LWGraph_kokkos &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatType &aggStat, LO &numNonAggregatedNodes) const
T & get(const std::string &name, T def_value)
LocalOrdinal LO
One-liner description of what is happening.
void BuildAggregatesNonKokkos(const ParameterList &params, const LWGraph &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatHostType &aggStat, LO &numNonAggregatedNodes) const
Local aggregation.
KOKKOS_INLINE_FUNCTION size_type GetNodeNumVertices() const
Return number of graph vertices.
void SetIsRoot(LO i, bool value=true)
Set root node information.
bool isParameter(const std::string &name) const
LO GetGraphNumColors()
Get the number of colors needed by the distance 2 coloring.
colors_view_type & GetGraphColors()
Get a distance 2 coloring of the underlying graph. The coloring is computed and set during Phase1 of ...
KOKKOS_INLINE_FUNCTION bool isLocalNeighborVertex(LO i) const
Return true if vertex with local id &#39;v&#39; is on current process.
const RCP< LOMultiVector > & GetVertex2AggId() const
Returns constant vector that maps local node IDs to local aggregates IDs.
KOKKOS_INLINE_FUNCTION neighbor_vertices_type getNeighborVertices(LO i) const
Return the list of vertices adjacent to the vertex &#39;v&#39;.
Timer to be used in non-factories.
Lightweight MueLu representation of a compressed row storage graph.
Exception throws to report errors in the internal logical of the program.
void BuildAggregatesRandom(const ParameterList &params, const LWGraph_kokkos &graph, Aggregates &aggregates, typename AggregationAlgorithmBase< LocalOrdinal, GlobalOrdinal, Node >::AggStatType &aggStat, LO &numNonAggregatedNodes) const
typename std::conditional< OnHost, Kokkos::Device< Kokkos::Serial, Kokkos::HostSpace >, typename Node::device_type >::type device_type
const RCP< const Teuchos::Comm< int > > GetComm() const
Kokkos::View< unsigned *, typename LWGraphType::device_type > AggStatType
void SetNumAggregates(LO nAggregates)
Set number of local aggregates on current processor.