Reference documentation for deal.II version 9.1.0-pre
partitioner.cc
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 1999 - 2017 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.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 #include <deal.II/base/partitioner.h>
17 #include <deal.II/base/partitioner.templates.h>
18 
19 DEAL_II_NAMESPACE_OPEN
20 
21 namespace Utilities
22 {
23  namespace MPI
24  {
26  : global_size(0)
27  , local_range_data(
28  std::pair<types::global_dof_index, types::global_dof_index>(0, 0))
29  , n_ghost_indices_data(0)
30  , n_import_indices_data(0)
31  , n_ghost_indices_in_larger_set(0)
32  , my_pid(0)
33  , n_procs(1)
34  , communicator(MPI_COMM_SELF)
35  , have_ghost_indices(false)
36  {}
37 
38 
39 
40  Partitioner::Partitioner(const unsigned int size)
41  : global_size(size)
44  std::pair<types::global_dof_index, types::global_dof_index>(0, size))
48  , my_pid(0)
49  , n_procs(1)
50  , communicator(MPI_COMM_SELF)
51  , have_ghost_indices(false)
52  {
56  }
57 
58 
59 
60  Partitioner::Partitioner(const IndexSet &locally_owned_indices,
61  const IndexSet &ghost_indices_in,
62  const MPI_Comm communicator_in)
63  : global_size(
64  static_cast<types::global_dof_index>(locally_owned_indices.size()))
68  , my_pid(0)
69  , n_procs(1)
70  , communicator(communicator_in)
71  , have_ghost_indices(false)
72  {
73  set_owned_indices(locally_owned_indices);
74  set_ghost_indices(ghost_indices_in);
75  }
76 
77 
78 
79  Partitioner::Partitioner(const IndexSet &locally_owned_indices,
80  const MPI_Comm communicator_in)
81  : global_size(
82  static_cast<types::global_dof_index>(locally_owned_indices.size()))
86  , my_pid(0)
87  , n_procs(1)
88  , communicator(communicator_in)
89  , have_ghost_indices(false)
90  {
91  set_owned_indices(locally_owned_indices);
92  }
93 
94 
95 
96  void
97  Partitioner::reinit(const IndexSet &vector_space_vector_index_set,
98  const IndexSet &read_write_vector_index_set,
99  const MPI_Comm &communicator_in)
100  {
101  have_ghost_indices = false;
102  communicator = communicator_in;
103  set_owned_indices(vector_space_vector_index_set);
104  set_ghost_indices(read_write_vector_index_set);
105  }
106 
107 
108 
109  void
110  Partitioner::set_owned_indices(const IndexSet &locally_owned_indices)
111  {
112  if (Utilities::MPI::job_supports_mpi() == true)
113  {
116  }
117  else
118  {
119  my_pid = 0;
120  n_procs = 1;
121  }
122 
123  // set the local range
124  Assert(locally_owned_indices.is_contiguous() == true,
125  ExcMessage("The index set specified in locally_owned_indices "
126  "is not contiguous."));
127  locally_owned_indices.compress();
128  if (locally_owned_indices.n_elements() > 0)
130  std::pair<types::global_dof_index, types::global_dof_index>(
131  locally_owned_indices.nth_index_in_set(0),
132  locally_owned_indices.nth_index_in_set(0) +
133  locally_owned_indices.n_elements());
134  AssertThrow(
135  local_range_data.second - local_range_data.first <
136  static_cast<types::global_dof_index>(
137  std::numeric_limits<unsigned int>::max()),
138  ExcMessage(
139  "Index overflow: This class supports at most 2^32-1 locally owned vector entries"));
140  locally_owned_range_data.set_size(locally_owned_indices.size());
142  local_range_data.second);
144 
145  ghost_indices_data.set_size(locally_owned_indices.size());
146  }
147 
148 
149 
150  void
151  Partitioner::set_ghost_indices(const IndexSet &ghost_indices_in,
152  const IndexSet &larger_ghost_index_set)
153  {
154  // Set ghost indices from input. To be sure that no entries from the
155  // locally owned range are present, subtract the locally owned indices
156  // in any case.
157  Assert(ghost_indices_in.n_elements() == 0 ||
158  ghost_indices_in.size() == locally_owned_range_data.size(),
159  ExcDimensionMismatch(ghost_indices_in.size(),
161 
162  ghost_indices_data = ghost_indices_in;
167  AssertThrow(
169  static_cast<types::global_dof_index>(
170  std::numeric_limits<unsigned int>::max()),
171  ExcMessage(
172  "Index overflow: This class supports at most 2^32-1 ghost elements"));
174 
176  Utilities::MPI::sum(n_ghost_indices_data, communicator) > 0;
177 
178  // In the rest of this function, we determine the point-to-point
179  // communication pattern of the partitioner. We make up a list with both
180  // the processors the ghost indices actually belong to, and the indices
181  // that are locally held but ghost indices of other processors. This
182  // allows then to import and export data very easily.
183 
184  // find out the end index for each processor and communicate it (this
185  // implies the start index for the next processor)
186 #ifdef DEAL_II_WITH_MPI
187  if (n_procs < 2)
188  {
191  Assert(n_ghost_indices_data == 0, ExcInternalError());
192  return;
193  }
194 
195  std::vector<types::global_dof_index> first_index(n_procs + 1);
196  // Allow non-zero start index for the vector. send this data to all
197  // processors
198  first_index[0] = local_range_data.first;
199  int ierr = MPI_Bcast(
200  first_index.data(), 1, DEAL_II_DOF_INDEX_MPI_TYPE, 0, communicator);
201  AssertThrowMPI(ierr);
202 
203  // Get the end-of-local_range for all processors
204  ierr = MPI_Allgather(&local_range_data.second,
205  1,
206  DEAL_II_DOF_INDEX_MPI_TYPE,
207  &first_index[1],
208  1,
209  DEAL_II_DOF_INDEX_MPI_TYPE,
210  communicator);
211  AssertThrowMPI(ierr);
212  first_index[n_procs] = global_size;
213 
214  // fix case when there are some processors without any locally owned
215  // indices: then there might be a zero in some entries
216  if (global_size > 0)
217  {
218  unsigned int first_proc_with_nonzero_dofs = 0;
219  for (unsigned int i = 0; i < n_procs; ++i)
220  if (first_index[i + 1] > 0)
221  {
222  first_proc_with_nonzero_dofs = i;
223  break;
224  }
225  for (unsigned int i = first_proc_with_nonzero_dofs + 1; i < n_procs;
226  ++i)
227  if (first_index[i] == 0)
228  first_index[i] = first_index[i - 1];
229 
230  // correct if our processor has a wrong local range
231  if (first_index[my_pid] != local_range_data.first)
232  {
233  Assert(local_range_data.first == local_range_data.second,
234  ExcInternalError());
235  local_range_data.first = local_range_data.second =
236  first_index[my_pid];
237  }
238  }
239 
240  // Allocate memory for data that will be exported
241  std::vector<types::global_dof_index> expanded_ghost_indices(
242  n_ghost_indices_data);
243  unsigned int n_ghost_targets = 0;
244  if (n_ghost_indices_data > 0)
245  {
246  // Create first a vector of ghost_targets from the list of ghost
247  // indices and then push back new values. When we are done, copy the
248  // data to that field of the partitioner. This way, the variable
249  // ghost_targets will have exactly the size we need, whereas the
250  // vector filled with emplace_back might actually be too long.
251  unsigned int current_proc = 0;
252  ghost_indices_data.fill_index_vector(expanded_ghost_indices);
253  types::global_dof_index current_index = expanded_ghost_indices[0];
254  while (current_index >= first_index[current_proc + 1])
255  current_proc++;
256  std::vector<std::pair<unsigned int, unsigned int>> ghost_targets_temp(
257  1, std::pair<unsigned int, unsigned int>(current_proc, 0));
258  n_ghost_targets++;
259 
260  for (unsigned int iterator = 1; iterator < n_ghost_indices_data;
261  ++iterator)
262  {
263  current_index = expanded_ghost_indices[iterator];
264  while (current_index >= first_index[current_proc + 1])
265  current_proc++;
266  AssertIndexRange(current_proc, n_procs);
267  if (ghost_targets_temp[n_ghost_targets - 1].first < current_proc)
268  {
269  ghost_targets_temp[n_ghost_targets - 1].second =
270  iterator - ghost_targets_temp[n_ghost_targets - 1].second;
271  ghost_targets_temp.emplace_back(current_proc, iterator);
272  n_ghost_targets++;
273  }
274  }
275  ghost_targets_temp[n_ghost_targets - 1].second =
276  n_ghost_indices_data -
277  ghost_targets_temp[n_ghost_targets - 1].second;
278  ghost_targets_data = ghost_targets_temp;
279  }
280  // find the processes that want to import to me
281  {
282  std::vector<int> send_buffer(n_procs, 0);
283  std::vector<int> receive_buffer(n_procs, 0);
284  for (unsigned int i = 0; i < n_ghost_targets; i++)
285  send_buffer[ghost_targets_data[i].first] =
286  ghost_targets_data[i].second;
287 
288  const int ierr = MPI_Alltoall(send_buffer.data(),
289  1,
290  MPI_INT,
291  receive_buffer.data(),
292  1,
293  MPI_INT,
294  communicator);
295  AssertThrowMPI(ierr);
296 
297  // allocate memory for import data
298  std::vector<std::pair<unsigned int, unsigned int>> import_targets_temp;
300  for (unsigned int i = 0; i < n_procs; i++)
301  if (receive_buffer[i] > 0)
302  {
303  n_import_indices_data += receive_buffer[i];
304  import_targets_temp.emplace_back(i, receive_buffer[i]);
305  }
306  // copy, don't move, to get deterministic memory usage.
307  import_targets_data = import_targets_temp;
308  }
309 
310  // send and receive indices for import data. non-blocking receives and
311  // blocking sends
312  std::vector<types::global_dof_index> expanded_import_indices(
314  {
315  unsigned int current_index_start = 0;
316  std::vector<MPI_Request> import_requests(import_targets_data.size());
317  for (unsigned int i = 0; i < import_targets_data.size(); i++)
318  {
319  const int ierr =
320  MPI_Irecv(&expanded_import_indices[current_index_start],
321  import_targets_data[i].second,
322  DEAL_II_DOF_INDEX_MPI_TYPE,
323  import_targets_data[i].first,
324  import_targets_data[i].first,
325  communicator,
326  &import_requests[i]);
327  AssertThrowMPI(ierr);
328  current_index_start += import_targets_data[i].second;
329  }
330  AssertDimension(current_index_start, n_import_indices_data);
331 
332  // use blocking send
333  current_index_start = 0;
334  for (unsigned int i = 0; i < n_ghost_targets; i++)
335  {
336  const int ierr =
337  MPI_Send(&expanded_ghost_indices[current_index_start],
338  ghost_targets_data[i].second,
339  DEAL_II_DOF_INDEX_MPI_TYPE,
340  ghost_targets_data[i].first,
341  my_pid,
342  communicator);
343  AssertThrowMPI(ierr);
344  current_index_start += ghost_targets_data[i].second;
345  }
346  AssertDimension(current_index_start, n_ghost_indices_data);
347 
348  if (import_requests.size() > 0)
349  {
350  const int ierr = MPI_Waitall(import_requests.size(),
351  import_requests.data(),
352  MPI_STATUSES_IGNORE);
353  AssertThrowMPI(ierr);
354  }
355 
356  // transform import indices to local index space and compress
357  // contiguous indices in form of ranges
358  {
360  1);
362  std::vector<std::pair<unsigned int, unsigned int>>
363  compressed_import_indices;
364  unsigned int shift = 0;
365  for (unsigned int p = 0; p < import_targets_data.size(); ++p)
366  {
367  types::global_dof_index last_index =
369  for (unsigned int ii = 0; ii < import_targets_data[p].second;
370  ++ii)
371  {
372  const unsigned int i = shift + ii;
373  Assert(expanded_import_indices[i] >= local_range_data.first &&
374  expanded_import_indices[i] < local_range_data.second,
375  ExcIndexRange(expanded_import_indices[i],
376  local_range_data.first,
377  local_range_data.second));
378  types::global_dof_index new_index =
379  (expanded_import_indices[i] - local_range_data.first);
382  if (new_index == last_index + 1)
383  compressed_import_indices.back().second++;
384  else
385  compressed_import_indices.emplace_back(new_index,
386  new_index + 1);
387  last_index = new_index;
388  }
389  shift += import_targets_data[p].second;
391  compressed_import_indices.size();
392  }
393  import_indices_data = compressed_import_indices;
394 
395  // sanity check
396 # ifdef DEBUG
397  const types::global_dof_index n_local_dofs =
398  local_range_data.second - local_range_data.first;
399  for (unsigned int i = 0; i < import_indices_data.size(); ++i)
400  {
401  AssertIndexRange(import_indices_data[i].first, n_local_dofs);
402  AssertIndexRange(import_indices_data[i].second - 1, n_local_dofs);
403  }
404 # endif
405  }
406  }
407 #endif // #ifdef DEAL_II_WITH_MPI
408 
409  if (larger_ghost_index_set.size() == 0)
410  {
412  ghost_indices_subset_data.emplace_back(local_size(),
413  local_size() +
414  n_ghost_indices());
416  }
417  else
418  {
419  AssertDimension(larger_ghost_index_set.size(),
421  Assert(
422  (larger_ghost_index_set & locally_owned_range_data).n_elements() ==
423  0,
424  ExcMessage("Ghost index set should not overlap with owned set."));
425  Assert((larger_ghost_index_set & ghost_indices_data) ==
426  ghost_indices_data,
427  ExcMessage("Larger ghost index set must contain the tight "
428  "ghost index set."));
429 
430  n_ghost_indices_in_larger_set = larger_ghost_index_set.n_elements();
431 
432  std::vector<unsigned int> expanded_numbering;
433  for (IndexSet::ElementIterator it = ghost_indices_data.begin();
434  it != ghost_indices_data.end();
435  ++it)
436  {
437  Assert(larger_ghost_index_set.is_element(*it),
438  ExcMessage("The given larger ghost index set must contain"
439  "all indices in the actual index set."));
440  expanded_numbering.push_back(
441  larger_ghost_index_set.index_within_set(*it));
442  }
443 
444  std::vector<std::pair<unsigned int, unsigned int>>
445  ghost_indices_subset;
447  ghost_targets_data.size() + 1);
449  unsigned int shift = 0;
450  for (unsigned int p = 0; p < ghost_targets_data.size(); ++p)
451  {
452  unsigned int last_index = numbers::invalid_unsigned_int - 1;
453  for (unsigned int ii = 0; ii < ghost_targets_data[p].second; ii++)
454  {
455  const unsigned int i = shift + ii;
456  if (expanded_numbering[i] == last_index + 1)
457  ghost_indices_subset.back().second++;
458  else
459  ghost_indices_subset.emplace_back(expanded_numbering[i],
460  expanded_numbering[i] +
461  1);
462  last_index = expanded_numbering[i];
463  }
464  shift += ghost_targets_data[p].second;
466  ghost_indices_subset.size();
467  }
468  ghost_indices_subset_data = ghost_indices_subset;
469  }
470  }
471 
472 
473 
474  bool
476  {
477  // if the partitioner points to the same memory location as the calling
478  // processor
479  if (&part == this)
480  return true;
481 #ifdef DEAL_II_WITH_MPI
483  {
484  int communicators_same = 0;
485  const int ierr = MPI_Comm_compare(part.communicator,
486  communicator,
487  &communicators_same);
488  AssertThrowMPI(ierr);
489  if (!(communicators_same == MPI_IDENT ||
490  communicators_same == MPI_CONGRUENT))
491  return false;
492  }
493 #endif
494  return (global_size == part.global_size &&
497  }
498 
499 
500 
501  bool
503  {
504  return Utilities::MPI::min(static_cast<int>(is_compatible(part)),
505  communicator) == 1;
506  }
507 
508 
509 
510  std::size_t
512  {
513  std::size_t memory = (3 * sizeof(types::global_dof_index) +
514  4 * sizeof(unsigned int) + sizeof(MPI_Comm));
523  memory +=
526  return memory;
527  }
528 
529  } // end of namespace MPI
530 
531 } // end of namespace Utilities
532 
533 
534 
535 // explicit instantiations from .templates.h file
536 #include "partitioner.inst"
537 
538 DEAL_II_NAMESPACE_CLOSE
std::vector< std::pair< unsigned int, unsigned int > > import_indices_data
Definition: partitioner.h:613
unsigned int n_ghost_indices() const
static const unsigned int invalid_unsigned_int
Definition: types.h:173
#define AssertDimension(dim1, dim2)
Definition: exceptions.h:1366
types::global_dof_index size() const
types::global_dof_index global_size
Definition: partitioner.h:575
#define AssertIndexRange(index, range)
Definition: exceptions.h:1407
size_type n_elements() const
Definition: index_set.h:1743
unsigned int n_ghost_indices_data
Definition: partitioner.h:599
STL namespace.
#define AssertThrow(cond, exc)
Definition: exceptions.h:1329
size_type size() const
Definition: index_set.h:1611
static::ExceptionBase & ExcIndexRange(int arg1, int arg2, int arg3)
unsigned long long int global_dof_index
Definition: types.h:72
void set_owned_indices(const IndexSet &locally_owned_indices)
Definition: partitioner.cc:110
std::vector< unsigned int > ghost_indices_subset_chunks_by_rank_data
Definition: partitioner.h:643
std::vector< unsigned int > import_indices_chunks_by_rank_data
Definition: partitioner.h:631
bool is_contiguous() const
Definition: index_set.h:1726
static::ExceptionBase & ExcMessage(std::string arg1)
Definition: types.h:31
T sum(const T &t, const MPI_Comm &mpi_communicator)
void subtract_set(const IndexSet &other)
Definition: index_set.cc:265
void fill_index_vector(std::vector< size_type > &indices) const
Definition: index_set.cc:503
#define Assert(cond, exc)
Definition: exceptions.h:1227
std::size_t memory_consumption() const
Definition: partitioner.cc:511
static::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
std::vector< std::pair< unsigned int, unsigned int > > ghost_indices_subset_data
Definition: partitioner.h:651
void compress() const
Definition: index_set.h:1619
bool is_globally_compatible(const Partitioner &part) const
Definition: partitioner.cc:502
unsigned int n_mpi_processes(const MPI_Comm &mpi_communicator)
Definition: mpi.cc:69
void add_range(const size_type begin, const size_type end)
Definition: index_set.cc:96
bool is_compatible(const Partitioner &part) const
Definition: partitioner.cc:475
void set_size(const size_type size)
Definition: index_set.h:1599
Definition: cuda.h:32
#define AssertThrowMPI(error_code)
Definition: exceptions.h:1443
T min(const T &t, const MPI_Comm &mpi_communicator)
std::vector< std::pair< unsigned int, unsigned int > > import_targets_data
Definition: partitioner.h:625
std::pair< types::global_dof_index, types::global_dof_index > local_range_data
Definition: partitioner.h:587
std::vector< std::pair< unsigned int, unsigned int > > ghost_targets_data
Definition: partitioner.h:605
size_type index_within_set(const size_type global_index) const
Definition: index_set.h:1834
unsigned int this_mpi_process(const MPI_Comm &mpi_communicator)
Definition: mpi.cc:80
bool is_element(const size_type index) const
Definition: index_set.h:1676
static::ExceptionBase & ExcNotImplemented()
const types::global_dof_index invalid_dof_index
Definition: types.h:188
size_type nth_index_in_set(const unsigned int local_index) const
Definition: index_set.h:1793
bool job_supports_mpi()
Definition: mpi.cc:690
unsigned int n_import_indices_data
Definition: partitioner.h:619
virtual void reinit(const IndexSet &vector_space_vector_index_set, const IndexSet &read_write_vector_index_set, const MPI_Comm &communicator) override
Definition: partitioner.cc:97
unsigned int local_size() const
std::enable_if< std::is_fundamental< T >::value, std::size_t >::type memory_consumption(const T &t)
unsigned int n_ghost_indices_in_larger_set
Definition: partitioner.h:637
static::ExceptionBase & ExcInternalError()
void set_ghost_indices(const IndexSet &ghost_indices, const IndexSet &larger_ghost_index_set=IndexSet())
Definition: partitioner.cc:151