Reference documentation for deal.II version 9.0.0
sparsity_tools.cc
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2008 - 2018 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE at
12 // the top level of the deal.II distribution.
13 //
14 // ---------------------------------------------------------------------
15 
16 
17 #include <deal.II/base/exceptions.h>
18 #include <deal.II/lac/exceptions.h>
19 #include <deal.II/lac/sparsity_pattern.h>
20 #include <deal.II/lac/sparsity_tools.h>
21 
22 #include <algorithm>
23 #include <functional>
24 #include <set>
25 #include <deal.II/base/std_cxx14/memory.h>
26 
27 #ifdef DEAL_II_WITH_MPI
28 #include <deal.II/base/utilities.h>
29 #include <deal.II/base/mpi.h>
30 #include <deal.II/lac/dynamic_sparsity_pattern.h>
31 #include <deal.II/lac/block_sparsity_pattern.h>
32 #endif
33 
34 #ifdef DEAL_II_WITH_METIS
35 extern "C"
36 {
37 #include <metis.h>
38 }
39 #endif
40 
41 #ifdef DEAL_II_TRILINOS_WITH_ZOLTAN
42 #include <zoltan_cpp.h>
43 #endif
44 
45 #include <string>
46 
47 
48 DEAL_II_NAMESPACE_OPEN
49 
50 namespace SparsityTools
51 {
52 
53  namespace
54  {
55  void partition_metis (const SparsityPattern &sparsity_pattern,
56  const std::vector<unsigned int> &cell_weights,
57  const unsigned int n_partitions,
58  std::vector<unsigned int> &partition_indices)
59  {
60  // Make sure that METIS is actually
61  // installed and detected
62 #ifndef DEAL_II_WITH_METIS
63  (void) sparsity_pattern;
64  (void) cell_weights;
65  (void) n_partitions;
66  (void) partition_indices;
68 #else
69 
70  // generate the data structures for
71  // METIS. Note that this is particularly
72  // simple, since METIS wants exactly our
73  // compressed row storage format. we only
74  // have to set up a few auxiliary arrays
75  idx_t
76  n = static_cast<signed int>(sparsity_pattern.n_rows()),
77  ncon = 1, // number of balancing constraints (should be >0)
78  nparts = static_cast<int>(n_partitions), // number of subdomains to create
79  dummy; // the numbers of edges cut by the
80  // resulting partition
81 
82  // We can not partition n items into more than n parts. METIS will
83  // generate non-sensical output (everything is owned by a single process)
84  // and complain with a message (but won't return an error code!):
85  // ***Cannot bisect a graph with 0 vertices!
86  // ***You are trying to partition a graph into too many parts!
87  nparts = std::min(n, nparts);
88 
89  // use default options for METIS
90  idx_t options[METIS_NOPTIONS];
91  METIS_SetDefaultOptions (options);
92 
93  // one more nuisance: we have to copy our own data to arrays that store
94  // signed integers :-(
95  std::vector<idx_t> int_rowstart(1);
96  int_rowstart.reserve(sparsity_pattern.n_rows()+1);
97  std::vector<idx_t> int_colnums;
98  int_colnums.reserve(sparsity_pattern.n_nonzero_elements());
99  for (SparsityPattern::size_type row=0; row<sparsity_pattern.n_rows(); ++row)
100  {
101  for (SparsityPattern::iterator col=sparsity_pattern.begin(row);
102  col < sparsity_pattern.end(row); ++col)
103  int_colnums.push_back(col->column());
104  int_rowstart.push_back(int_colnums.size());
105  }
106 
107  std::vector<idx_t> int_partition_indices (sparsity_pattern.n_rows());
108 
109  // Setup cell weighting option
110  std::vector<idx_t> int_cell_weights;
111  if (cell_weights.size() > 0)
112  {
113  Assert(cell_weights.size() == sparsity_pattern.n_rows(),
114  ExcDimensionMismatch(cell_weights.size(),sparsity_pattern.n_rows()));
115  int_cell_weights.resize(cell_weights.size());
116  std::copy (cell_weights.begin(),
117  cell_weights.end(),
118  int_cell_weights.begin());
119  }
120  // Set a pointer to the optional cell weighting information.
121  // METIS expects a null pointer if there are no weights to be considered.
122  idx_t *const p_int_cell_weights = (cell_weights.size() > 0 ? int_cell_weights.data() : nullptr);
123 
124 
125  // Make use of METIS' error code.
126  int ierr;
127 
128  // Select which type of partitioning to create
129 
130  // Use recursive if the number of partitions is less than or equal to 8
131  if (nparts <= 8)
132  ierr = METIS_PartGraphRecursive(&n, &ncon, int_rowstart.data(), int_colnums.data(),
133  p_int_cell_weights, nullptr, nullptr,
134  &nparts,nullptr,nullptr,options,
135  &dummy,int_partition_indices.data());
136 
137  // Otherwise use kway
138  else
139  ierr = METIS_PartGraphKway(&n, &ncon, int_rowstart.data(), int_colnums.data(),
140  p_int_cell_weights, nullptr, nullptr,
141  &nparts,nullptr,nullptr,options,
142  &dummy,int_partition_indices.data());
143 
144  // If metis returns normally, an error code METIS_OK=1 is returned from
145  // the above functions (see metish.h)
146  AssertThrow (ierr == 1, ExcMETISError (ierr));
147 
148  // now copy back generated indices into the output array
149  std::copy (int_partition_indices.begin(),
150  int_partition_indices.end(),
151  partition_indices.begin());
152 #endif
153  }
154 
155 
156 //Query functions unused if zoltan is not installed
157 #ifdef DEAL_II_TRILINOS_WITH_ZOLTAN
158  //Query functions for partition_zoltan
159  int get_number_of_objects(void *data, int *ierr)
160  {
161  SparsityPattern *graph = reinterpret_cast<SparsityPattern *>(data);
162 
163  *ierr = ZOLTAN_OK;
164 
165  return graph->n_rows();
166  }
167 
168 
169  void get_object_list(void *data, int /*sizeGID*/, int /*sizeLID*/,
170  ZOLTAN_ID_PTR globalID, ZOLTAN_ID_PTR localID,
171  int /*wgt_dim*/, float * /*obj_wgts*/, int *ierr)
172  {
173  SparsityPattern *graph = reinterpret_cast<SparsityPattern *>(data);
174  *ierr = ZOLTAN_OK;
175 
176  Assert( globalID != nullptr , ExcInternalError() );
177  Assert( localID != nullptr , ExcInternalError() );
178 
179  //set global degrees of freedom
180  auto n_dofs = graph->n_rows();
181 
182  for (unsigned int i=0; i < n_dofs; i++)
183  {
184  globalID[i] = i;
185  localID[i] = i; //Same as global ids.
186  }
187  }
188 
189 
190  void get_num_edges_list(void *data, int /*sizeGID*/, int /*sizeLID*/,
191  int num_obj,
192  ZOLTAN_ID_PTR globalID, ZOLTAN_ID_PTR /*localID*/,
193  int *numEdges, int *ierr)
194  {
195  SparsityPattern *graph = reinterpret_cast<SparsityPattern *>(data);
196 
197  *ierr = ZOLTAN_OK;
198 
199  Assert ( numEdges != nullptr , ExcInternalError() );
200 
201  for (int i=0; i<num_obj; ++i)
202  {
203  if ( graph->exists(i,i) ) //Check if diagonal element is present
204  numEdges[i] = graph->row_length(globalID[i])-1;
205  else
206  numEdges[i] = graph->row_length(globalID[i]);
207  }
208  }
209 
210 
211 
212  void get_edge_list(void *data, int /*sizeGID*/, int /*sizeLID*/,
213  int num_obj, ZOLTAN_ID_PTR /*globalID*/, ZOLTAN_ID_PTR /*localID*/,
214  int * /*num_edges*/,
215  ZOLTAN_ID_PTR nborGID, int *nborProc,
216  int /*wgt_dim*/, float * /*ewgts*/, int *ierr)
217  {
218  SparsityPattern *graph = reinterpret_cast<SparsityPattern *>(data);
219  *ierr = ZOLTAN_OK;
220 
221  ZOLTAN_ID_PTR nextNborGID = nborGID;
222  int *nextNborProc = nborProc;
223 
224  //Loop through rows corresponding to indices in globalID implicitly
225  for (SparsityPattern::size_type i=0; i < static_cast<SparsityPattern::size_type>(num_obj); ++i)
226  {
227  //Loop through each column to find neighbours
228  for ( SparsityPattern::iterator col=graph->begin(i);
229  col < graph->end(i); ++col )
230  //Ignore diagonal entries. Not needed for partitioning.
231  if ( i != col->column() )
232  {
233  Assert( nextNborGID != nullptr , ExcInternalError() );
234  Assert( nextNborProc != nullptr , ExcInternalError() );
235 
236  *nextNborGID++ = col->column();
237  *nextNborProc++ = 0; //All the vertices on processor 0
238  }
239  }
240  }
241 #endif
242 
243 
244  void partition_zoltan (const SparsityPattern &sparsity_pattern,
245  const std::vector<unsigned int> &cell_weights,
246  const unsigned int n_partitions,
247  std::vector<unsigned int> &partition_indices)
248  {
249  // Make sure that ZOLTAN is actually
250  // installed and detected
251 #ifndef DEAL_II_TRILINOS_WITH_ZOLTAN
252  (void) sparsity_pattern;
253  (void) cell_weights;
254  (void) n_partitions;
255  (void) partition_indices;
257 #else
258 
259  Assert(cell_weights.size() == 0,
260  ExcMessage("The cell weighting functionality for Zoltan has not yet been implemented."));
261  (void) cell_weights;
262 
263  //MPI environment must have been initialized by this point.
264  std::unique_ptr<Zoltan> zz = std_cxx14::make_unique<Zoltan>(MPI_COMM_SELF);
265 
266  //General parameters
267  // DEBUG_LEVEL call must precede the call to LB_METHOD
268  zz->Set_Param( "DEBUG_LEVEL", "0" ); //set level of debug info
269  zz->Set_Param( "LB_METHOD", "GRAPH" ); //graph based partition method (LB-load balancing)
270  zz->Set_Param( "NUM_LOCAL_PARTS", std::to_string(n_partitions) ); //set number of partitions
271 
272  // The PHG partitioner is a hypergraph partitioner that Zoltan could use
273  // for graph partitioning.
274  // If number of vertices in hyperedge divided by total vertices in
275  // hypergraph exceeds PHG_EDGE_SIZE_THRESHOLD,
276  // then the hyperedge will be omitted as such (dense) edges will likely
277  // incur high communication costs regardless of the partition.
278  // PHG_EDGE_SIZE_THRESHOLD value is raised to 0.5 from the default
279  // value of 0.25 so that the PHG partitioner doesn't throw warning saying
280  // "PHG_EDGE_SIZE_THRESHOLD is low ..." after removing all dense edges.
281  // For instance, in two dimensions if the triangulation being partitioned
282  // is two quadrilaterals sharing an edge and if PHG_EDGE_SIZE_THRESHOLD
283  // value is set to 0.25, PHG will remove all the edges throwing the
284  // above warning.
285  zz->Set_Param( "PHG_EDGE_SIZE_THRESHOLD", "0.5" );
286 
287  //Need a non-const object equal to sparsity_pattern
288  SparsityPattern graph;
289  graph.copy_from(sparsity_pattern);
290 
291  //Set query functions
292  zz->Set_Num_Obj_Fn(get_number_of_objects, &graph);
293  zz->Set_Obj_List_Fn(get_object_list, &graph);
294  zz->Set_Num_Edges_Multi_Fn(get_num_edges_list, &graph);
295  zz->Set_Edge_List_Multi_Fn(get_edge_list, &graph);
296 
297  //Variables needed by partition function
298  int changes=0;
299  int num_gid_entries=1;
300  int num_lid_entries=1;
301  int num_import=0;
302  ZOLTAN_ID_PTR import_global_ids=nullptr;
303  ZOLTAN_ID_PTR import_local_ids=nullptr;
304  int *import_procs=nullptr;
305  int *import_to_part=nullptr;
306  int num_export=0;
307  ZOLTAN_ID_PTR export_global_ids=nullptr;
308  ZOLTAN_ID_PTR export_local_ids=nullptr;
309  int *export_procs=nullptr;
310  int *export_to_part=nullptr;
311 
312  //call partitioner
313  const int rc = zz->LB_Partition (changes,
314  num_gid_entries,
315  num_lid_entries,
316  num_import,
317  import_global_ids,
318  import_local_ids,
319  import_procs,
320  import_to_part,
321  num_export,
322  export_global_ids,
323  export_local_ids,
324  export_procs,
325  export_to_part);
326  (void)rc;
327 
328  //check for error code in partitioner
329  Assert( rc == ZOLTAN_OK , ExcInternalError() );
330 
331  //By default, all indices belong to part 0. After zoltan partition
332  //some are migrated to different part ID, which is stored in export_to_part array.
333  std::fill(partition_indices.begin(), partition_indices.end(), 0);
334 
335  //copy from export_to_part to partition_indices, whose part_ids != 0.
336  for (int i=0; i < num_export; i++)
337  partition_indices[export_local_ids[i]] = export_to_part[i];
338 #endif
339  }
340  }
341 
342 
343  void partition (const SparsityPattern &sparsity_pattern,
344  const unsigned int n_partitions,
345  std::vector<unsigned int> &partition_indices,
346  const Partitioner partitioner
347  )
348  {
349  std::vector<unsigned int> cell_weights;
350 
351  // Call the other more general function
352  partition(sparsity_pattern, cell_weights, n_partitions,
353  partition_indices, partitioner);
354  }
355 
356 
357  void partition (const SparsityPattern &sparsity_pattern,
358  const std::vector<unsigned int> &cell_weights,
359  const unsigned int n_partitions,
360  std::vector<unsigned int> &partition_indices,
361  const Partitioner partitioner
362  )
363  {
364  Assert (sparsity_pattern.n_rows()==sparsity_pattern.n_cols(),
365  ExcNotQuadratic());
366  Assert (sparsity_pattern.is_compressed(),
368 
369  Assert (n_partitions > 0, ExcInvalidNumberOfPartitions(n_partitions));
370  Assert (partition_indices.size() == sparsity_pattern.n_rows(),
371  ExcInvalidArraySize (partition_indices.size(),
372  sparsity_pattern.n_rows()));
373 
374  // check for an easy return
375  if (n_partitions == 1 || (sparsity_pattern.n_rows()==1))
376  {
377  std::fill_n (partition_indices.begin(), partition_indices.size(), 0U);
378  return;
379  }
380 
381  if (partitioner == Partitioner::metis)
382  partition_metis(sparsity_pattern, cell_weights, n_partitions, partition_indices);
383  else if (partitioner == Partitioner::zoltan)
384  partition_zoltan(sparsity_pattern, cell_weights, n_partitions, partition_indices);
385  else
386  AssertThrow(false, ExcInternalError());
387  }
388 
389 
390  unsigned int color_sparsity_pattern (const SparsityPattern &sparsity_pattern,
391  std::vector<unsigned int> &color_indices)
392  {
393  // Make sure that ZOLTAN is actually
394  // installed and detected
395 #ifndef DEAL_II_TRILINOS_WITH_ZOLTAN
396  (void) sparsity_pattern;
397  (void) color_indices;
399  return 0;
400 #else
401  //coloring algorithm is run in serial by each processor.
402  std::unique_ptr<Zoltan> zz = std_cxx14::make_unique<Zoltan> (MPI_COMM_SELF);
403 
404  //Coloring parameters
405  // DEBUG_LEVEL must precede all other calls
406  zz->Set_Param ("DEBUG_LEVEL", "0" ); //level of debug info
407  zz->Set_Param ("COLORING_PROBLEM", "DISTANCE-1"); //Standard coloring
408  zz->Set_Param ("NUM_GID_ENTRIES", "1"); // 1 entry represents global ID
409  zz->Set_Param ("NUM_LID_ENTRIES", "1"); // 1 entry represents local ID
410  zz->Set_Param ("OBJ_WEIGHT_DIM", "0"); // object weights not used
411  zz->Set_Param ("RECOLORING_NUM_OF_ITERATIONS", "0");
412 
413  //Zoltan::Color function requires a non-const SparsityPattern object
414  SparsityPattern graph;
415  graph.copy_from (sparsity_pattern);
416 
417  //Set query functions required by coloring function
418  zz->Set_Num_Obj_Fn (get_number_of_objects, &graph);
419  zz->Set_Obj_List_Fn (get_object_list, &graph);
420  zz->Set_Num_Edges_Multi_Fn (get_num_edges_list, &graph);
421  zz->Set_Edge_List_Multi_Fn (get_edge_list, &graph);
422 
423  //Variables needed by coloring function
424  int num_gid_entries=1;
425  const int num_objects = graph.n_rows();
426 
427  //Preallocate input variables. Element type fixed by ZOLTAN.
428  std::vector<ZOLTAN_ID_TYPE> global_ids (num_objects);
429  std::vector<int> color_exp (num_objects);
430 
431  //Set ids for which coloring needs to be done
432  for (int i=0; i < num_objects; i++)
433  global_ids[i] = i;
434 
435  //Call ZOLTAN coloring algorithm
436  int rc = zz->Color (num_gid_entries,
437  num_objects,
438  global_ids.data(),
439  color_exp.data());
440 
441  (void) rc;
442  //Check for error code
443  Assert (rc == ZOLTAN_OK ,
444  ExcInternalError());
445 
446  //Allocate and assign color indices
447  color_indices.resize(num_objects);
448  Assert (color_exp.size() == color_indices.size(),
449  ExcDimensionMismatch (color_exp.size(),color_indices.size()));
450 
451  std::copy (color_exp.begin(), color_exp.end(), color_indices.begin());
452 
453  unsigned int n_colors = *(std::max_element (color_indices.begin(),
454  color_indices.end()));
455  return n_colors;
456 #endif
457  }
458 
459 
460  namespace internal
461  {
468  find_unnumbered_starting_index (const DynamicSparsityPattern &sparsity,
469  const std::vector<DynamicSparsityPattern::size_type> &new_indices)
470  {
472  DynamicSparsityPattern::size_type min_coordination = sparsity.n_rows();
473  for (DynamicSparsityPattern::size_type row=0; row<sparsity.n_rows(); ++row)
474  // look over all as-yet unnumbered indices
475  if (new_indices[row] == numbers::invalid_size_type)
476  {
477  if (sparsity.row_length(row) < min_coordination)
478  {
479  min_coordination = sparsity.row_length(row);
480  starting_point = row;
481  }
482  }
483 
484  // now we still have to care for the case that no unnumbered dof has a
485  // coordination number less than sparsity.n_rows(). this rather exotic
486  // case only happens if we only have one cell, as far as I can see,
487  // but there may be others as well.
488  //
489  // if that should be the case, we can chose an arbitrary dof as
490  // starting point, e.g. the first unnumbered one
491  if (starting_point == numbers::invalid_size_type)
492  {
493  for (DynamicSparsityPattern::size_type i=0; i<new_indices.size(); ++i)
494  if (new_indices[i] == numbers::invalid_size_type)
495  {
496  starting_point = i;
497  break;
498  }
499 
500  Assert (starting_point != numbers::invalid_size_type,
501  ExcInternalError());
502  }
503 
504  return starting_point;
505  }
506  }
507 
508 
509 
510  void
512  std::vector<DynamicSparsityPattern::size_type> &new_indices,
513  const std::vector<DynamicSparsityPattern::size_type> &starting_indices)
514  {
515  Assert (sparsity.n_rows() == sparsity.n_cols(),
516  ExcDimensionMismatch (sparsity.n_rows(), sparsity.n_cols()));
517  Assert (sparsity.n_rows() == new_indices.size(),
518  ExcDimensionMismatch (sparsity.n_rows(), new_indices.size()));
519  Assert (starting_indices.size() <= sparsity.n_rows(),
520  ExcMessage ("You can't specify more starting indices than there are rows"));
521  Assert (sparsity.row_index_set().size() == 0 ||
522  sparsity.row_index_set().size() == sparsity.n_rows(),
523  ExcMessage("Only valid for sparsity patterns which store all rows."));
524  for (SparsityPattern::size_type i=0; i<starting_indices.size(); ++i)
525  Assert (starting_indices[i] < sparsity.n_rows(),
526  ExcMessage ("Invalid starting index: All starting indices need "
527  "to be between zero and the number of rows in the "
528  "sparsity pattern."));
529 
530  // store the indices of the dofs renumbered in the last round. Default to
531  // starting points
532  std::vector<DynamicSparsityPattern::size_type> last_round_dofs (starting_indices);
533 
534  // initialize the new_indices array with invalid values
535  std::fill (new_indices.begin(), new_indices.end(),
537 
538  // if no starting indices were given: find dof with lowest coordination number
539  if (last_round_dofs.empty())
540  last_round_dofs
541  .push_back (internal::find_unnumbered_starting_index (sparsity,
542  new_indices));
543 
544  // store next free dof index
545  DynamicSparsityPattern::size_type next_free_number = 0;
546 
547  // enumerate the first round dofs
548  for (DynamicSparsityPattern::size_type i=0; i!=last_round_dofs.size(); ++i)
549  new_indices[last_round_dofs[i]] = next_free_number++;
550 
551  // now do as many steps as needed to renumber all dofs
552  while (true)
553  {
554  // store the indices of the dofs to be renumbered in the next round
555  std::vector<DynamicSparsityPattern::size_type> next_round_dofs;
556 
557  // find all neighbors of the dofs numbered in the last round
558  for (DynamicSparsityPattern::size_type i=0; i<last_round_dofs.size(); ++i)
559  for (DynamicSparsityPattern::iterator j=sparsity.begin(last_round_dofs[i]);
560  j<sparsity.end(last_round_dofs[i]); ++j)
561  next_round_dofs.push_back (j->column());
562 
563  // sort dof numbers
564  std::sort (next_round_dofs.begin(), next_round_dofs.end());
565 
566  // delete multiple entries
567  std::vector<DynamicSparsityPattern::size_type>::iterator end_sorted;
568  end_sorted = std::unique (next_round_dofs.begin(), next_round_dofs.end());
569  next_round_dofs.erase (end_sorted, next_round_dofs.end());
570 
571  // eliminate dofs which are already numbered
572  for (int s=next_round_dofs.size()-1; s>=0; --s)
573  if (new_indices[next_round_dofs[s]] != numbers::invalid_size_type)
574  next_round_dofs.erase (next_round_dofs.begin() + s);
575 
576  // check whether there are any new dofs in the list. if there are
577  // none, then we have completely numbered the current component of the
578  // graph. check if there are as yet unnumbered components of the graph
579  // that we would then have to do next
580  if (next_round_dofs.empty())
581  {
582  if (std::find (new_indices.begin(), new_indices.end(),
584  ==
585  new_indices.end())
586  // no unnumbered indices, so we can leave now
587  break;
588 
589  // otherwise find a valid starting point for the next component of
590  // the graph and continue with numbering that one. we only do so
591  // if no starting indices were provided by the user (see the
592  // documentation of this function) so produce an error if we got
593  // here and starting indices were given
594  Assert (starting_indices.empty(),
595  ExcMessage ("The input graph appears to have more than one "
596  "component, but as stated in the documentation "
597  "we only want to reorder such graphs if no "
598  "starting indices are given. The function was "
599  "called with starting indices, however."))
600 
601  next_round_dofs
602  .push_back (internal::find_unnumbered_starting_index (sparsity,
603  new_indices));
604  }
605 
606 
607 
608  // store for each coordination number the dofs with these coordination
609  // number
610  std::multimap<DynamicSparsityPattern::size_type, int> dofs_by_coordination;
611 
612  // find coordination number for each of these dofs
613  for (std::vector<DynamicSparsityPattern::size_type>::iterator s=next_round_dofs.begin();
614  s!=next_round_dofs.end(); ++s)
615  {
616  const DynamicSparsityPattern::size_type coordination = sparsity.row_length(*s);
617 
618  // insert this dof at its coordination number
619  const std::pair<const DynamicSparsityPattern::size_type, int> new_entry (coordination, *s);
620  dofs_by_coordination.insert (new_entry);
621  }
622 
623  // assign new DoF numbers to the elements of the present front:
624  std::multimap<DynamicSparsityPattern::size_type, int>::iterator i;
625  for (i = dofs_by_coordination.begin(); i!=dofs_by_coordination.end(); ++i)
626  new_indices[i->second] = next_free_number++;
627 
628  // after that: copy this round's dofs for the next round
629  last_round_dofs = next_round_dofs;
630  }
631 
632  // test for all indices numbered. this mostly tests whether the
633  // front-marching-algorithm (which Cuthill-McKee actually is) has reached
634  // all points.
635  Assert ((std::find (new_indices.begin(), new_indices.end(), numbers::invalid_size_type)
636  ==
637  new_indices.end())
638  &&
639  (next_free_number == sparsity.n_rows()),
640  ExcInternalError());
641  }
642 
643 
644 
645  namespace internal
646  {
647  void
648  reorder_hierarchical (const DynamicSparsityPattern &connectivity,
649  std::vector<DynamicSparsityPattern::size_type> &renumbering)
650  {
651  AssertDimension (connectivity.n_rows(), connectivity.n_cols());
652  AssertDimension (connectivity.n_rows(), renumbering.size());
653  Assert (connectivity.row_index_set().size() == 0 ||
654  connectivity.row_index_set().size() == connectivity.n_rows(),
655  ExcMessage("Only valid for sparsity patterns which store all rows."));
656 
657  std::vector<types::global_dof_index> touched_nodes(connectivity.n_rows(),
659  std::vector<unsigned int> row_lengths(connectivity.n_rows());
660  std::set<types::global_dof_index> current_neighbors;
661  std::vector<std::vector<types::global_dof_index> > groups;
662 
663  // First collect the number of neighbors for each node. We use this
664  // field to find next nodes with the minimum number of non-touched
665  // neighbors in the field n_remaining_neighbors, so we will count down
666  // on this field. We also cache the row lengths because we need this
667  // data frequently and getting it from the sparsity pattern is more
668  // expensive.
669  for (types::global_dof_index row=0; row<connectivity.n_rows(); ++row)
670  {
671  row_lengths[row] = connectivity.row_length(row);
672  Assert(row_lengths[row] > 0, ExcInternalError());
673  }
674  std::vector<unsigned int> n_remaining_neighbors(row_lengths);
675 
676  // This outer loop is typically traversed only once, unless the global
677  // graph is not connected
678  while (true)
679  {
680  // Find cell with the minimal number of neighbors (typically a
681  // corner node when based on FEM meshes). If no cell is left, we are
682  // done. Together with the outer while loop, this loop can possibly
683  // be of quadratic complexity in the number of disconnected
684  // partitions, i.e. up to connectivity.n_rows() in the worst case,
685  // but that is not the usual use case of this loop and thus not
686  // optimized for.
687  std::pair<types::global_dof_index,types::global_dof_index> min_neighbors
689  for (types::global_dof_index i=0; i<touched_nodes.size(); ++i)
690  if (touched_nodes[i] == numbers::invalid_dof_index)
691  if (row_lengths[i] < min_neighbors.second)
692  {
693  min_neighbors = std::make_pair(i, n_remaining_neighbors[i]);
694  if (n_remaining_neighbors[i] <= 1)
695  break;
696  }
697  if (min_neighbors.first == numbers::invalid_dof_index)
698  break;
699 
700  Assert(min_neighbors.second > 0, ExcInternalError());
701 
702  current_neighbors.clear();
703  current_neighbors.insert(min_neighbors.first);
704  while (!current_neighbors.empty())
705  {
706  // Find node with minimum number of untouched neighbors among the
707  // next set of possible neighbors
708  min_neighbors = std::make_pair (numbers::invalid_dof_index,
710  for (std::set<types::global_dof_index>::iterator it=current_neighbors.begin();
711  it != current_neighbors.end(); ++it)
712  {
713  Assert (touched_nodes[*it] == numbers::invalid_dof_index,
714  ExcInternalError());
715  if (n_remaining_neighbors[*it] < min_neighbors.second)
716  min_neighbors = std::make_pair(*it, n_remaining_neighbors[*it]);
717  }
718 
719  // Among the set of nodes with the minimal number of neighbors,
720  // choose the one with the largest number of touched neighbors,
721  // i.e., the one with the largest row length
722  const types::global_dof_index best_row_length = min_neighbors.second;
723  for (std::set<types::global_dof_index>::iterator it=current_neighbors.begin();
724  it != current_neighbors.end(); ++it)
725  if (n_remaining_neighbors[*it] == best_row_length)
726  if (row_lengths[*it] > min_neighbors.second)
727  min_neighbors = std::make_pair(*it, row_lengths[*it]);
728 
729  // Add the pivot and all direct neighbors of the pivot node not
730  // yet touched to the list of new entries.
731  groups.emplace_back();
732  std::vector<types::global_dof_index> &next_group = groups.back();
733 
734  next_group.push_back(min_neighbors.first);
735  touched_nodes[min_neighbors.first] = groups.size()-1;
737  = connectivity.begin(min_neighbors.first);
738  it != connectivity.end(min_neighbors.first); ++it)
739  if (touched_nodes[it->column()] == numbers::invalid_dof_index)
740  {
741  next_group.push_back(it->column());
742  touched_nodes[it->column()] = groups.size()-1;
743  }
744 
745  // Add all neighbors of the current list not yet touched to the
746  // set of possible next pivots. The added node is no longer a
747  // valid neighbor (here we assume symmetry of the
748  // connectivity). Delete the entries of the current list from
749  // the set of possible next pivots.
750  for (unsigned int i=0; i<next_group.size(); ++i)
751  {
753  = connectivity.begin(next_group[i]);
754  it != connectivity.end(next_group[i]); ++it)
755  {
756  if (touched_nodes[it->column()] == numbers::invalid_dof_index)
757  current_neighbors.insert(it->column());
758  n_remaining_neighbors[it->column()]--;
759  }
760  current_neighbors.erase(next_group[i]);
761  }
762  }
763  }
764 
765  // Sanity check: for all nodes, there should not be any neighbors left
766  for (types::global_dof_index row=0; row<connectivity.n_rows(); ++row)
767  Assert(n_remaining_neighbors[row] == 0, ExcInternalError());
768 
769  // If the number of groups is smaller than the number of nodes, we
770  // continue by recursively calling this method
771  if (groups.size() < connectivity.n_rows())
772  {
773  // Form the connectivity of the groups
774  DynamicSparsityPattern connectivity_next(groups.size(),
775  groups.size());
776  for (types::global_dof_index i=0; i<groups.size(); ++i)
777  for (types::global_dof_index col=0; col<groups[i].size(); ++col)
779  = connectivity.begin(groups[i][col]);
780  it != connectivity.end(groups[i][col]); ++it)
781  connectivity_next.add(i, touched_nodes[it->column()]);
782 
783  // Recursively call the reordering
784  std::vector<types::global_dof_index> renumbering_next(groups.size());
785  reorder_hierarchical(connectivity_next, renumbering_next);
786 
787  // Renumber the indices group by group according to the incoming
788  // ordering for the groups
789  for (types::global_dof_index i=0,count=0; i<groups.size(); ++i)
790  for (types::global_dof_index col=0; col<groups[renumbering_next[i]].size(); ++col, ++count)
791  renumbering[count] = groups[renumbering_next[i]][col];
792  }
793  else
794  {
795  // All groups should have size one and no more recursion is possible,
796  // so use the numbering of the groups
797  for (types::global_dof_index i=0,count=0; i<groups.size(); ++i)
798  for (types::global_dof_index col=0; col<groups[i].size(); ++col, ++count)
799  renumbering[count] = groups[i][col];
800  }
801  }
802  }
803 
804  void
806  std::vector<DynamicSparsityPattern::size_type> &renumbering)
807  {
808  // the internal renumbering keeps the numbering the wrong way around (but
809  // we cannot invert the numbering inside that method because it is used
810  // recursively), so invert it here
811  internal::reorder_hierarchical(connectivity, renumbering);
812  renumbering = Utilities::invert_permutation(renumbering);
813  }
814 
815 
816 
817 #ifdef DEAL_II_WITH_MPI
820  const std::vector<DynamicSparsityPattern::size_type> &rows_per_cpu,
821  const MPI_Comm &mpi_comm,
822  const IndexSet &myrange)
823  {
824  const unsigned int myid = Utilities::MPI::this_mpi_process(mpi_comm);
825  std::vector<DynamicSparsityPattern::size_type> start_index(rows_per_cpu.size()+1);
826  start_index[0]=0;
827  for (DynamicSparsityPattern::size_type i=0; i<rows_per_cpu.size(); ++i)
828  start_index[i+1]=start_index[i]+rows_per_cpu[i];
829 
830  typedef std::map<DynamicSparsityPattern::size_type,
831  std::vector<DynamicSparsityPattern::size_type> >
832  map_vec_t;
833 
834  map_vec_t send_data;
835 
836  {
837  unsigned int dest_cpu=0;
838 
839  DynamicSparsityPattern::size_type n_local_rel_rows = myrange.n_elements();
840  for (DynamicSparsityPattern::size_type row_idx=0; row_idx<n_local_rel_rows; ++row_idx)
841  {
843 
844  //calculate destination CPU
845  while (row>=start_index[dest_cpu+1])
846  ++dest_cpu;
847 
848  //skip myself
849  if (dest_cpu==myid)
850  {
851  row_idx+=rows_per_cpu[myid]-1;
852  continue;
853  }
854 
856 
857  //skip empty lines
858  if (!rlen)
859  continue;
860 
861  //save entries
862  std::vector<DynamicSparsityPattern::size_type> &dst = send_data[dest_cpu];
863 
864  dst.push_back(rlen); // number of entries
865  dst.push_back(row); // row index
866  for (DynamicSparsityPattern::size_type c=0; c<rlen; ++c)
867  {
868  //columns
870  dst.push_back(column);
871  }
872  }
873 
874  }
875 
876  unsigned int num_receive=0;
877  {
878  std::vector<unsigned int> send_to;
879  send_to.reserve(send_data.size());
880  for (map_vec_t::iterator it=send_data.begin(); it!=send_data.end(); ++it)
881  send_to.push_back(it->first);
882 
883  num_receive =
885  compute_point_to_point_communication_pattern(mpi_comm, send_to).size();
886  }
887 
888  std::vector<MPI_Request> requests(send_data.size());
889 
890 
891  // send data
892  {
893  unsigned int idx=0;
894  for (map_vec_t::iterator it=send_data.begin(); it!=send_data.end(); ++it, ++idx)
895  {
896  const int ierr = MPI_Isend(&(it->second[0]),
897  it->second.size(),
898  DEAL_II_DOF_INDEX_MPI_TYPE,
899  it->first,
900  124,
901  mpi_comm,
902  &requests[idx]);
903  AssertThrowMPI(ierr);
904  }
905  }
906 
907  {
908  //receive
909  std::vector<DynamicSparsityPattern::size_type> recv_buf;
910  for (unsigned int index=0; index<num_receive; ++index)
911  {
912  MPI_Status status;
913  int len;
914  int ierr = MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, mpi_comm, &status);
915  AssertThrowMPI(ierr);
916  Assert (status.MPI_TAG==124, ExcInternalError());
917 
918  ierr = MPI_Get_count(&status, DEAL_II_DOF_INDEX_MPI_TYPE, &len);
919  AssertThrowMPI(ierr);
920  recv_buf.resize(len);
921  ierr = MPI_Recv(recv_buf.data(), len, DEAL_II_DOF_INDEX_MPI_TYPE, status.MPI_SOURCE,
922  status.MPI_TAG, mpi_comm, &status);
923  AssertThrowMPI(ierr);
924 
925  std::vector<DynamicSparsityPattern::size_type>::const_iterator ptr = recv_buf.begin();
926  std::vector<DynamicSparsityPattern::size_type>::const_iterator end = recv_buf.end();
927  while (ptr!=end)
928  {
930  Assert(ptr!=end, ExcInternalError());
932  for (unsigned int c=0; c<num; ++c)
933  {
934  Assert(ptr!=end, ExcInternalError());
935  dsp.add(row, *ptr);
936  ++ptr;
937  }
938  }
939  Assert(ptr==end, ExcInternalError());
940  }
941  }
942 
943  // complete all sends, so that we can safely destroy the buffers.
944  if (requests.size())
945  {
946  const int ierr = MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
947  AssertThrowMPI(ierr);
948  }
949 
950  }
951 
953  const std::vector<IndexSet> &owned_set_per_cpu,
954  const MPI_Comm &mpi_comm,
955  const IndexSet &myrange)
956  {
957  const unsigned int myid = Utilities::MPI::this_mpi_process(mpi_comm);
958 
960  std::vector<BlockDynamicSparsityPattern::size_type> >
961  map_vec_t;
962  map_vec_t send_data;
963 
964  {
965  unsigned int dest_cpu=0;
966 
967  BlockDynamicSparsityPattern::size_type n_local_rel_rows = myrange.n_elements();
968  for (BlockDynamicSparsityPattern::size_type row_idx=0; row_idx<n_local_rel_rows; ++row_idx)
969  {
971 
972  // calculate destination CPU, note that we start the search
973  // at last destination cpu, because even if the owned ranges
974  // are not contiguous, they hopefully consist of large blocks
975  while (!owned_set_per_cpu[dest_cpu].is_element(row))
976  {
977  ++dest_cpu;
978  if (dest_cpu==owned_set_per_cpu.size()) // wrap around
979  dest_cpu=0;
980  }
981 
982  //skip myself
983  if (dest_cpu==myid)
984  continue;
985 
987 
988  //skip empty lines
989  if (!rlen)
990  continue;
991 
992  //save entries
993  std::vector<BlockDynamicSparsityPattern::size_type> &dst = send_data[dest_cpu];
994 
995  dst.push_back(rlen); // number of entries
996  dst.push_back(row); // row index
997  for (BlockDynamicSparsityPattern::size_type c=0; c<rlen; ++c)
998  {
999  //columns
1001  dst.push_back(column);
1002  }
1003  }
1004 
1005  }
1006 
1007  unsigned int num_receive=0;
1008  {
1009  std::vector<unsigned int> send_to;
1010  send_to.reserve(send_data.size());
1011  for (map_vec_t::iterator it=send_data.begin(); it!=send_data.end(); ++it)
1012  send_to.push_back(it->first);
1013 
1014  num_receive =
1016  compute_point_to_point_communication_pattern(mpi_comm, send_to).size();
1017  }
1018 
1019  std::vector<MPI_Request> requests(send_data.size());
1020 
1021 
1022  // send data
1023  {
1024  unsigned int idx=0;
1025  for (map_vec_t::iterator it=send_data.begin(); it!=send_data.end(); ++it, ++idx)
1026  {
1027  const int ierr = MPI_Isend(&(it->second[0]),
1028  it->second.size(),
1029  DEAL_II_DOF_INDEX_MPI_TYPE,
1030  it->first,
1031  124,
1032  mpi_comm,
1033  &requests[idx]);
1034  AssertThrowMPI(ierr);
1035  }
1036  }
1037 
1038  {
1039  //receive
1040  std::vector<BlockDynamicSparsityPattern::size_type> recv_buf;
1041  for (unsigned int index=0; index<num_receive; ++index)
1042  {
1043  MPI_Status status;
1044  int len;
1045  int ierr = MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, mpi_comm, &status);
1046  AssertThrowMPI(ierr);
1047  Assert (status.MPI_TAG==124, ExcInternalError());
1048 
1049  ierr = MPI_Get_count(&status, DEAL_II_DOF_INDEX_MPI_TYPE, &len);
1050  AssertThrowMPI(ierr);
1051  recv_buf.resize(len);
1052  ierr = MPI_Recv(recv_buf.data(), len, DEAL_II_DOF_INDEX_MPI_TYPE, status.MPI_SOURCE,
1053  status.MPI_TAG, mpi_comm, &status);
1054  AssertThrowMPI(ierr);
1055 
1056  std::vector<BlockDynamicSparsityPattern::size_type>::const_iterator ptr = recv_buf.begin();
1057  std::vector<BlockDynamicSparsityPattern::size_type>::const_iterator end = recv_buf.end();
1058  while (ptr!=end)
1059  {
1061  Assert(ptr!=end, ExcInternalError());
1063  for (unsigned int c=0; c<num; ++c)
1064  {
1065  Assert(ptr!=end, ExcInternalError());
1066  dsp.add(row, *ptr);
1067  ++ptr;
1068  }
1069  }
1070  Assert(ptr==end, ExcInternalError());
1071  }
1072  }
1073 
1074  // complete all sends, so that we can safely destroy the buffers.
1075  if (requests.size())
1076  {
1077  const int ierr = MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
1078  AssertThrowMPI(ierr);
1079  }
1080  }
1081 #endif
1082 }
1083 
1084 DEAL_II_NAMESPACE_CLOSE
const types::global_dof_index invalid_size_type
Definition: types.h:182
size_type column_number(const size_type row, const unsigned int index) const
#define AssertDimension(dim1, dim2)
Definition: exceptions.h:1248
std::size_t n_nonzero_elements() const
size_type nth_index_in_set(const unsigned int local_index) const
Definition: index_set.h:1769
size_type column_number(const size_type row, const size_type index) const
void add(const size_type i, const size_type j)
static ::ExceptionBase & ExcMETISNotInstalled()
void reorder_Cuthill_McKee(const DynamicSparsityPattern &sparsity, std::vector< DynamicSparsityPattern::size_type > &new_indices, const std::vector< DynamicSparsityPattern::size_type > &starting_indices=std::vector< DynamicSparsityPattern::size_type >())
iterator end() const
bool exists(const size_type i, const size_type j) const
#define AssertThrow(cond, exc)
Definition: exceptions.h:1221
size_type n_cols() const
bool is_compressed() const
types::global_dof_index size_type
size_type size() const
Definition: index_set.h:1575
size_type n_rows() const
static ::ExceptionBase & ExcNotCompressed()
static ::ExceptionBase & ExcMessage(std::string arg1)
static ::ExceptionBase & ExcInvalidNumberOfPartitions(int arg1)
void add(const size_type i, const size_type j)
void distribute_sparsity_pattern(DynamicSparsityPattern &dsp, const std::vector< DynamicSparsityPattern::size_type > &rows_per_cpu, const MPI_Comm &mpi_comm, const IndexSet &myrange)
unsigned int global_dof_index
Definition: types.h:88
void partition(const SparsityPattern &sparsity_pattern, const unsigned int n_partitions, std::vector< unsigned int > &partition_indices, const Partitioner partitioner=Partitioner::metis)
#define Assert(cond, exc)
Definition: exceptions.h:1142
static ::ExceptionBase & ExcInvalidArraySize(int arg1, int arg2)
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
const IndexSet & row_index_set() const
void copy_from(const size_type n_rows, const size_type n_cols, const ForwardIterator begin, const ForwardIterator end)
std::vector< unsigned int > invert_permutation(const std::vector< unsigned int > &permutation)
Definition: utilities.cc:509
static ::ExceptionBase & ExcNotQuadratic()
void reorder_hierarchical(const DynamicSparsityPattern &sparsity, std::vector< DynamicSparsityPattern::size_type > &new_indices)
static ::ExceptionBase & ExcZOLTANNotInstalled()
unsigned int color_sparsity_pattern(const SparsityPattern &sparsity_pattern, std::vector< unsigned int > &color_indices)
types::global_dof_index size_type
#define AssertThrowMPI(error_code)
Definition: exceptions.h:1314
size_type row_length(const size_type row) const
unsigned int row_length(const size_type row) const
unsigned int this_mpi_process(const MPI_Comm &mpi_communicator)
Definition: mpi.cc:75
iterator begin() const
const types::global_dof_index invalid_dof_index
Definition: types.h:187
std::vector< unsigned int > compute_point_to_point_communication_pattern(const MPI_Comm &mpi_comm, const std::vector< unsigned int > &destinations)
Definition: mpi.cc:174
size_type n_elements() const
Definition: index_set.h:1717
static ::ExceptionBase & ExcMETISError(int arg1)
unsigned int row_length(const size_type row) const
static ::ExceptionBase & ExcInternalError()