deal.II version GIT relicensing-7058-gb35ee29762 2026-11-11 09:30:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
partitioner.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2012 - 2025 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
14#include <deal.II/base/mpi.h>
16#include <deal.II/base/partitioner.templates.h>
17
18#include <boost/serialization/utility.hpp>
19
20#include <Kokkos_Core.hpp>
21
22#include <algorithm>
23#include <limits>
24#include <string>
25#include <utility>
26#include <vector>
27
29
30#ifndef DOXYGEN
31namespace Utilities
32{
33 namespace MPI
34 {
36 : global_size(0)
37 , local_range_data(
39 , n_ghost_indices_data(0)
40 , n_import_indices_data(0)
41 , n_ghost_indices_in_larger_set(0)
42 , my_pid(0)
43 , n_procs(1)
44 , communicator(MPI_COMM_SELF)
45 , have_ghost_indices(false)
46 {}
47
48
49
50 Partitioner::Partitioner(const unsigned int size)
51 : global_size(size)
52 , locally_owned_range_data(size)
53 , local_range_data(
55 , n_ghost_indices_data(0)
56 , n_import_indices_data(0)
57 , n_ghost_indices_in_larger_set(0)
58 , my_pid(0)
59 , n_procs(1)
60 , communicator(MPI_COMM_SELF)
61 , have_ghost_indices(false)
62 {
63 locally_owned_range_data.add_range(0, size);
64 locally_owned_range_data.compress();
65 ghost_indices_data.set_size(size);
66 }
67
68
69
70 Partitioner::Partitioner(const types::global_dof_index local_size,
71 const types::global_dof_index ghost_size,
72 const MPI_Comm communicator)
73 : global_size(Utilities::MPI::sum<types::global_dof_index>(local_size,
74 communicator))
75 , locally_owned_range_data(global_size)
76 , local_range_data{0, local_size}
77 , n_ghost_indices_data(ghost_size)
78 , n_import_indices_data(0)
79 , n_ghost_indices_in_larger_set(0)
80 , my_pid(Utilities::MPI::this_mpi_process(communicator))
81 , n_procs(Utilities::MPI::n_mpi_processes(communicator))
82 , communicator(communicator)
83 , have_ghost_indices(true)
84 {
85 types::global_dof_index prefix_sum = 0;
86
87# ifdef DEAL_II_WITH_MPI
88 const int ierr =
89 MPI_Exscan(&local_size,
90 &prefix_sum,
91 1,
92 Utilities::MPI::mpi_type_id_for_type<decltype(prefix_sum)>,
93 MPI_SUM,
94 communicator);
95 AssertThrowMPI(ierr);
96# endif
97
98 local_range_data = {prefix_sum, prefix_sum + local_size};
99
100 locally_owned_range_data.add_range(prefix_sum, prefix_sum + local_size);
101 locally_owned_range_data.compress();
102 }
103
104
105
106 Partitioner::Partitioner(const IndexSet &locally_owned_indices,
107 const IndexSet &ghost_indices_in,
108 const MPI_Comm communicator_in)
109 : global_size(
110 static_cast<types::global_dof_index>(locally_owned_indices.size()))
111 , n_ghost_indices_data(0)
112 , n_import_indices_data(0)
113 , n_ghost_indices_in_larger_set(0)
114 , my_pid(0)
115 , n_procs(1)
116 , communicator(communicator_in)
117 , have_ghost_indices(false)
118 {
119 set_owned_indices(locally_owned_indices);
120 set_ghost_indices(ghost_indices_in);
121 }
122
123
124
125 Partitioner::Partitioner(const IndexSet &locally_owned_indices,
126 const MPI_Comm communicator_in)
127 : global_size(
128 static_cast<types::global_dof_index>(locally_owned_indices.size()))
129 , n_ghost_indices_data(0)
130 , n_import_indices_data(0)
131 , n_ghost_indices_in_larger_set(0)
132 , my_pid(0)
133 , n_procs(1)
134 , communicator(communicator_in)
135 , have_ghost_indices(false)
136 {
137 set_owned_indices(locally_owned_indices);
138 }
139
140
141
142 void
143 Partitioner::reinit(const IndexSet &locally_owned_indices,
144 const IndexSet &ghost_indices,
145 const MPI_Comm communicator_in)
146 {
147 have_ghost_indices = false;
148 communicator = communicator_in;
149 set_owned_indices(locally_owned_indices);
150 set_ghost_indices(ghost_indices);
151 }
152
153
154
155 void
156 Partitioner::set_owned_indices(const IndexSet &locally_owned_indices)
157 {
158 my_pid = Utilities::MPI::this_mpi_process(communicator);
160
161 // set the local range
162 Assert(locally_owned_indices.is_contiguous() == true,
163 ExcMessage("The index set specified in locally_owned_indices "
164 "is not contiguous."));
165 locally_owned_indices.compress();
166 if (locally_owned_indices.n_elements() > 0)
167 local_range_data =
168 std::pair<types::global_dof_index, types::global_dof_index>(
169 locally_owned_indices.nth_index_in_set(0),
170 locally_owned_indices.nth_index_in_set(0) +
171 locally_owned_indices.n_elements());
173 local_range_data.second - local_range_data.first <
174 static_cast<types::global_dof_index>(
175 std::numeric_limits<unsigned int>::max()),
177 "Index overflow: This class supports at most 2^32-1 locally owned vector entries"));
178 locally_owned_range_data.set_size(locally_owned_indices.size());
179 locally_owned_range_data.add_range(local_range_data.first,
180 local_range_data.second);
181 locally_owned_range_data.compress();
182
183 ghost_indices_data.set_size(locally_owned_indices.size());
184 }
185
186
187
188 void
189 Partitioner::set_ghost_indices(const IndexSet &ghost_indices_in,
190 const IndexSet &larger_ghost_index_set)
191 {
192 // Set ghost indices from input. To be sure that no entries from the
193 // locally owned range are present, subtract the locally owned indices
194 // in any case.
195 Assert(ghost_indices_in.n_elements() == 0 ||
196 ghost_indices_in.size() == locally_owned_range_data.size(),
197 ExcDimensionMismatch(ghost_indices_in.size(),
198 locally_owned_range_data.size()));
199
200 // The expanded import indices for device-aware MPI are set up lazily
201 // on first use. Discard them as the communication pattern changes.
202 import_indices_plain_dev = {};
203 import_entries_dev = {};
204 import_entry_offsets_dev = {};
205 import_entry_positions_dev = {};
206
207 ghost_indices_data = ghost_indices_in;
208 if (ghost_indices_data.size() != locally_owned_range_data.size())
209 ghost_indices_data.set_size(locally_owned_range_data.size());
210 ghost_indices_data.subtract_set(locally_owned_range_data);
211 ghost_indices_data.compress();
213 ghost_indices_data.n_elements() <
214 static_cast<types::global_dof_index>(
215 std::numeric_limits<unsigned int>::max()),
217 "Index overflow: This class supports at most 2^32-1 ghost elements"));
218 n_ghost_indices_data = ghost_indices_data.n_elements();
219
220 have_ghost_indices =
221 Utilities::MPI::logical_or(n_ghost_indices_data > 0, communicator);
222
223 // In the rest of this function, we determine the point-to-point
224 // communication pattern of the partitioner. We make up a list with both
225 // the processors the ghost indices actually belong to, and the indices
226 // that are locally held but ghost indices of other processors. This
227 // allows then to import and export data very easily.
228
229 // find out the end index for each processor and communicate it (this
230 // implies the start index for the next processor)
231# ifdef DEAL_II_WITH_MPI
232 if (n_procs < 2)
233 {
234 Assert(ghost_indices_data.n_elements() == 0, ExcInternalError());
235 Assert(n_import_indices_data == 0, ExcInternalError());
236 Assert(n_ghost_indices_data == 0, ExcInternalError());
237 return;
238 }
239
241
242 // Allow non-zero start index for the vector. Part 1:
243 // Assume for now that the index set of rank 0 starts with 0
244 // and therefore has an increased size.
245 if (my_pid == 0)
246 my_size += local_range_data.first;
247
248 types::global_dof_index my_shift = 0;
249 {
250 const int ierr = MPI_Exscan(
251 &my_size,
252 &my_shift,
253 1,
254 Utilities::MPI::mpi_type_id_for_type<types::global_dof_index>,
255 MPI_SUM,
256 communicator);
257 AssertThrowMPI(ierr);
258 }
259
260 // Allow non-zero start index for the vector. Part 2:
261 // We correct the assumption made above and let the
262 // index set of rank 0 actually start from the
263 // correct value, i.e. we correct the shift to
264 // its start.
265 if (my_pid == 0)
266 my_shift = local_range_data.first;
267
268 // Fix the index start in case the index set could not give us that
269 // information.
270 if (local_range_data.first == 0 && my_shift != 0)
271 {
272 const types::global_dof_index old_locally_owned_size =
274 local_range_data.first = my_shift;
275 local_range_data.second = my_shift + old_locally_owned_size;
276 }
277
278 const auto [owning_ranks_of_ghosts, import_data] =
280 locally_owned_range_data, ghost_indices_data, communicator);
281
282
283 {
284 ghost_targets_data = {};
285
286 if (owning_ranks_of_ghosts.size() > 0)
287 {
288 ghost_targets_data.emplace_back(owning_ranks_of_ghosts[0], 0);
289 for (auto i : owning_ranks_of_ghosts)
290 {
291 Assert(i >= ghost_targets_data.back().first,
293 "Expect result of ConsensusAlgorithms::Process to be "
294 "sorted."));
295 if (i == ghost_targets_data.back().first)
296 ghost_targets_data.back().second++;
297 else
298 ghost_targets_data.emplace_back(i, 1);
299 }
300 }
301 }
302
303 // count import requests and set up the compressed indices
304 n_import_indices_data = 0;
305 import_targets_data = {};
306 import_targets_data.reserve(import_data.size());
307 import_indices_chunks_by_rank_data = {};
308 import_indices_chunks_by_rank_data.reserve(import_data.size());
309 import_indices_chunks_by_rank_data.resize(1);
310 for (const auto &i : import_data)
311 if (i.second.n_elements() > 0)
312 {
313 import_targets_data.emplace_back(i.first, i.second.n_elements());
314 n_import_indices_data += i.second.n_elements();
315 import_indices_chunks_by_rank_data.push_back(
316 import_indices_chunks_by_rank_data.back() +
317 i.second.n_intervals());
318 }
319
320 // transform import indices to local index space
321 import_indices_data = {};
322 import_indices_data.reserve(import_indices_chunks_by_rank_data.back());
323 for (const auto &i : import_data)
324 {
325 Assert((i.second & locally_owned_range_data) == i.second,
326 ExcInternalError("Requested indices must be in local range"));
327 for (auto interval = i.second.begin_intervals();
328 interval != i.second.end_intervals();
329 ++interval)
330 import_indices_data.emplace_back(*interval->begin() -
331 local_range_data.first,
332 interval->last() + 1 -
333 local_range_data.first);
334 }
335
336 if constexpr (running_in_debug_mode())
337 {
338 // simple check: the number of processors to which we want to send
339 // ghosts and the processors to which ghosts reference should be the
340 // same
342 Utilities::MPI::sum(import_targets_data.size(), communicator),
343 Utilities::MPI::sum(ghost_targets_data.size(), communicator));
344
345 // simple check: the number of indices to exchange should match from
346 // the ghost indices side and the import indices side
348 Utilities::MPI::sum(n_import_indices_data, communicator),
349 Utilities::MPI::sum(n_ghost_indices_data, communicator));
350
351 // expensive check that the communication channel is sane -> do a
352 // ghost exchange step and see whether the ghost indices sent to us by
353 // other processes (ghost_indices) are the same as we hold locally
354 // (ghost_indices_ref).
355 const std::vector<types::global_dof_index> ghost_indices_ref =
356 ghost_indices_data.get_index_vector();
357 AssertDimension(ghost_indices_ref.size(), n_ghost_indices());
358 std::vector<types::global_dof_index> indices_to_send(
359 n_import_indices());
360 std::vector<types::global_dof_index> ghost_indices(n_ghost_indices());
361
362 const std::vector<types::global_dof_index> my_indices =
363 locally_owned_range_data.get_index_vector();
364 std::vector<MPI_Request> requests;
365 n_ghost_indices_in_larger_set = n_ghost_indices_data;
366 export_to_ghosted_array_start(
367 127,
369 my_indices.size()),
370 make_array_view(indices_to_send),
371 make_array_view(ghost_indices),
372 requests);
373 export_to_ghosted_array_finish(make_array_view(ghost_indices),
374 requests);
375 int flag = 0;
376 const int ierr = MPI_Testall(requests.size(),
377 requests.data(),
378 &flag,
379 MPI_STATUSES_IGNORE);
380 AssertThrowMPI(ierr);
381 Assert(flag == 1,
383 "MPI found unfinished requests. Check communication setup"));
384
385 for (unsigned int i = 0; i < ghost_indices.size(); ++i)
386 AssertDimension(ghost_indices[i], ghost_indices_ref[i]);
387 }
388
389# endif // #ifdef DEAL_II_WITH_MPI
390
391 if (larger_ghost_index_set.size() == 0)
392 {
393 ghost_indices_subset_chunks_by_rank_data.clear();
394 ghost_indices_subset_data.emplace_back(0, n_ghost_indices());
395 n_ghost_indices_in_larger_set = n_ghost_indices_data;
396 }
397 else
398 {
399 AssertDimension(larger_ghost_index_set.size(),
400 ghost_indices_data.size());
401 Assert(
402 (larger_ghost_index_set & locally_owned_range_data).n_elements() ==
403 0,
404 ExcMessage("Ghost index set should not overlap with owned set."));
405 Assert((larger_ghost_index_set & ghost_indices_data) ==
406 ghost_indices_data,
407 ExcMessage("Larger ghost index set must contain the tight "
408 "ghost index set."));
409
410 n_ghost_indices_in_larger_set = larger_ghost_index_set.n_elements();
411
412 // first translate tight ghost indices into indices within the large
413 // set:
414 std::vector<unsigned int> expanded_numbering;
415 for (const ::IndexSet::size_type index : ghost_indices_data)
416 {
417 Assert(larger_ghost_index_set.is_element(index),
418 ExcMessage("The given larger ghost index set must contain "
419 "all indices in the actual index set."));
420 Assert(
421 larger_ghost_index_set.index_within_set(index) <
422 static_cast<types::global_dof_index>(
423 std::numeric_limits<unsigned int>::max()),
425 "Index overflow: This class supports at most 2^32-1 ghost elements"));
426 expanded_numbering.push_back(
427 larger_ghost_index_set.index_within_set(index));
428 }
429
430 // now rework expanded_numbering into ranges and store in:
431 std::vector<std::pair<unsigned int, unsigned int>>
432 ghost_indices_subset;
433 ghost_indices_subset_chunks_by_rank_data.resize(
434 ghost_targets_data.size() + 1);
435 // also populate ghost_indices_subset_chunks_by_rank_data
436 ghost_indices_subset_chunks_by_rank_data[0] = 0;
437 unsigned int shift = 0;
438 for (unsigned int p = 0; p < ghost_targets_data.size(); ++p)
439 {
440 unsigned int last_index = numbers::invalid_unsigned_int - 1;
441 for (unsigned int ii = 0; ii < ghost_targets_data[p].second; ++ii)
442 {
443 const unsigned int i = shift + ii;
444 if (expanded_numbering[i] == last_index + 1)
445 // if contiguous, increment the end of last range:
446 ghost_indices_subset.back().second++;
447 else
448 // otherwise start a new range
449 ghost_indices_subset.emplace_back(expanded_numbering[i],
450 expanded_numbering[i] +
451 1);
452 last_index = expanded_numbering[i];
453 }
454 shift += ghost_targets_data[p].second;
455 ghost_indices_subset_chunks_by_rank_data[p + 1] =
456 ghost_indices_subset.size();
457 }
458 ghost_indices_subset_data = std::move(ghost_indices_subset);
459 }
460 }
461
462
463
464 bool
465 Partitioner::is_compatible(const Partitioner &part) const
466 {
467 // if the partitioner points to the same memory location as the calling
468 // processor
469 if (&part == this)
470 return true;
471# ifdef DEAL_II_WITH_MPI
473 {
474 int communicators_same = 0;
475 const int ierr = MPI_Comm_compare(part.communicator,
476 communicator,
477 &communicators_same);
478 AssertThrowMPI(ierr);
479 if (!(communicators_same == MPI_IDENT ||
480 communicators_same == MPI_CONGRUENT))
481 return false;
482 }
483# endif
484 return (global_size == part.global_size &&
485 local_range_data == part.local_range_data &&
486 ghost_indices_data == part.ghost_indices_data);
487 }
488
489
490
491 bool
492 Partitioner::is_globally_compatible(const Partitioner &part) const
493 {
494 return Utilities::MPI::logical_and(is_compatible(part), communicator);
495 }
496
497
498
499 std::size_t
500 Partitioner::memory_consumption() const
501 {
502 std::size_t memory = MemoryConsumption::memory_consumption(global_size);
503 memory += locally_owned_range_data.memory_consumption();
504 memory += MemoryConsumption::memory_consumption(local_range_data);
505 memory += ghost_indices_data.memory_consumption();
506 memory += sizeof(n_ghost_indices_data);
507 memory += MemoryConsumption::memory_consumption(ghost_targets_data);
508 memory += MemoryConsumption::memory_consumption(import_indices_data);
509 memory += sizeof(import_indices_plain_dev) +
510 sizeof(unsigned int) * import_indices_plain_dev.size();
511 memory += sizeof(import_entries_dev) +
512 sizeof(unsigned int) * import_entries_dev.size();
513 memory += sizeof(import_entry_offsets_dev) +
514 sizeof(unsigned int) * import_entry_offsets_dev.size();
515 memory += sizeof(import_entry_positions_dev) +
516 sizeof(unsigned int) * import_entry_positions_dev.size();
517 memory += MemoryConsumption::memory_consumption(n_import_indices_data);
518 memory += MemoryConsumption::memory_consumption(import_targets_data);
520 import_indices_chunks_by_rank_data);
521 memory +=
522 MemoryConsumption::memory_consumption(n_ghost_indices_in_larger_set);
524 ghost_indices_subset_chunks_by_rank_data);
525 memory +=
526 MemoryConsumption::memory_consumption(ghost_indices_subset_data);
529 memory += MemoryConsumption::memory_consumption(communicator);
530 memory += MemoryConsumption::memory_consumption(have_ghost_indices);
531 return memory;
532 }
533
534
535
536 void
537 Partitioner::initialize_import_indices_plain_dev() const
538 {
539 // Expand the indices of all import targets on the host. The ranges in
540 // import_indices_data are sorted by import target, so the expanded
541 // indices are in the same order as the data in the temporary storage
542 // used for communication.
543 std::vector<unsigned int> import_indices_plain_host;
544 import_indices_plain_host.reserve(n_import_indices_data);
545 for (const auto &import_range : import_indices_data)
546 for (unsigned int j = import_range.first; j < import_range.second; ++j)
547 import_indices_plain_host.push_back(j);
548 AssertDimension(import_indices_plain_host.size(), n_import_indices_data);
549
550 // An index can be a ghost on several processors. Sort the positions
551 // by index, keeping the positions of the same index in increasing
552 // order, i.e., in the order of the import targets.
553 std::vector<std::pair<unsigned int, unsigned int>> index_and_position;
554 index_and_position.reserve(import_indices_plain_host.size());
555 for (unsigned int p = 0; p < import_indices_plain_host.size(); ++p)
556 index_and_position.emplace_back(import_indices_plain_host[p], p);
557 std::sort(index_and_position.begin(), index_and_position.end());
558
559 std::vector<unsigned int> import_entries_host;
560 std::vector<unsigned int> import_entry_offsets_host;
561 std::vector<unsigned int> import_entry_positions_host;
562 import_entry_positions_host.reserve(index_and_position.size());
563 for (const auto &[index, position] : index_and_position)
564 {
565 if (import_entries_host.empty() ||
566 import_entries_host.back() != index)
567 {
568 import_entries_host.push_back(index);
569 import_entry_offsets_host.push_back(
570 import_entry_positions_host.size());
571 }
572 import_entry_positions_host.push_back(position);
573 }
574 import_entry_offsets_host.push_back(import_entry_positions_host.size());
575
576 // Move the indices to the device
577 const auto copy_to_device = [](const std::vector<unsigned int> &host,
578 const std::string &name) {
579 Kokkos::View<unsigned int *, MemorySpace::Default::kokkos_space> device(
580 Kokkos::view_alloc(name, Kokkos::WithoutInitializing), host.size());
581 Kokkos::deep_copy(device,
582 Kokkos::View<const unsigned int *, Kokkos::HostSpace>(
583 host.data(), host.size()));
584 return device;
585 };
586 import_indices_plain_dev =
587 copy_to_device(import_indices_plain_host, "import_indices_plain_dev");
588 import_entries_dev =
589 copy_to_device(import_entries_host, "import_entries_dev");
590 import_entry_offsets_dev =
591 copy_to_device(import_entry_offsets_host, "import_entry_offsets_dev");
592 import_entry_positions_dev = copy_to_device(import_entry_positions_host,
593 "import_entry_positions_dev");
594 }
595
596 } // namespace MPI
597
598} // end of namespace Utilities
599
600#endif
601
602// explicit instantiations from .templates.h file
603#include "base/partitioner.inst"
604
*  *  for(const auto &cell :triangulation.active_cell_iterators())
ArrayView< std::remove_reference_t< typename std::iterator_traits< Iterator >::reference >, MemorySpaceType > make_array_view(const Iterator begin, const Iterator end)
bool is_contiguous() const
Definition index_set.h:1900
size_type size() const
Definition index_set.h:1759
size_type index_within_set(const size_type global_index) const
Definition index_set.h:1977
size_type n_elements() const
Definition index_set.h:1917
bool is_element(const size_type index) const
Definition index_set.h:1877
void set_size(const size_type size)
Definition index_set.h:1747
size_type nth_index_in_set(const size_type local_index) const
Definition index_set.h:1958
void compress() const
Definition index_set.h:1767
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertThrowMPI(error_code)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
std::size_t size
Definition mpi.cc:733
types::global_dof_index locally_owned_size
Definition mpi.cc:821
const unsigned int n_procs
Definition mpi.cc:923
void shift(const Tensor< 1, spacedim > &shift_vector, Triangulation< dim, spacedim > &triangulation)
std::enable_if_t< std::is_fundamental_v< T >, std::size_t > memory_consumption(const T &t)
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
std::pair< std::vector< unsigned int >, std::map< unsigned int, IndexSet > > compute_index_owner_and_requesters(const IndexSet &owned_indices, const IndexSet &indices_to_look_up, const MPI_Comm &comm)
Definition mpi.cc:1871
T sum(const T &t, const MPI_Comm mpi_communicator)
bool logical_and(const bool t, const MPI_Comm mpi_communicator)
T logical_or(const T &t, const MPI_Comm mpi_communicator)
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:103
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
bool job_supports_mpi()
Definition mpi.cc:680
const MPI_Datatype mpi_type_id_for_type
Definition mpi.h:1685
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
STL namespace.
Definition types.h:30
unsigned int global_dof_index
Definition types.h:92