deal.II version GIT relicensing-6809-ge913b9bb34 2026-09-25 17:20: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
mpi.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
13
16#include <deal.II/base/mpi.h>
17#include <deal.II/base/mpi.templates.h>
22
25
26#include <boost/serialization/utility.hpp>
27
28// In this file, we use offsetof, which is a macro. When compiling
29// with C++20 modules, this presents a problem because we wrap all of
30// namespace std -- and then don't have access to macros. As a
31// consequence, we really do need the following #include, even when
32// building modules:
33#include <cstddef> // Do not convert for module purposes
34#include <iostream>
35#include <limits>
36#include <numeric>
37#include <set>
38#include <vector>
39
40#if defined(DEAL_II_WITH_MPI)
42# include <mpi.h>
44#endif
45
46
48
49
50namespace Utilities
51{
54 const unsigned int my_partition_id,
55 const unsigned int n_partitions,
56 const types::global_dof_index total_size)
57 {
58 static_assert(std::is_same_v<types::global_dof_index, IndexSet::size_type>,
59 "IndexSet::size_type must match types::global_dof_index for "
60 "using this function");
61 const unsigned int remain = total_size % n_partitions;
62
63 const IndexSet::size_type min_size = total_size / n_partitions;
64
66 min_size * my_partition_id + std::min(my_partition_id, remain);
68 min_size * (my_partition_id + 1) + std::min(my_partition_id + 1, remain);
69 IndexSet result(total_size);
70 result.add_range(begin, end);
71 return result;
72 }
73
74 namespace MPI
75 {
76 MinMaxAvg
77 min_max_avg(const double my_value, const MPI_Comm mpi_communicator)
78 {
79 MinMaxAvg result;
82 mpi_communicator);
83
84 return result;
85 }
86
87
88
89 std::vector<MinMaxAvg>
90 min_max_avg(const std::vector<double> &my_values,
91 const MPI_Comm mpi_communicator)
92 {
93 std::vector<MinMaxAvg> results(my_values.size());
94 min_max_avg(my_values, results, mpi_communicator);
95
96 return results;
97 }
98
99
100
101#ifdef DEAL_II_WITH_MPI
102 unsigned int
103 n_mpi_processes(const MPI_Comm mpi_communicator)
104 {
105 if (job_supports_mpi())
106 {
107 int n_jobs = 1;
108 const int ierr = MPI_Comm_size(mpi_communicator, &n_jobs);
109 AssertThrowMPI(ierr);
110 return n_jobs;
111 }
112 else
113 return 1;
114 }
115
116
117 unsigned int
118 this_mpi_process(const MPI_Comm mpi_communicator)
119 {
120 if (job_supports_mpi())
121 {
122 int rank = 0;
123 const int ierr = MPI_Comm_rank(mpi_communicator, &rank);
124 AssertThrowMPI(ierr);
125 return rank;
126 }
127 else
128 return 0;
129 }
130
131
132
133 std::vector<unsigned int>
135 const MPI_Comm comm_small)
136 {
138 return std::vector<unsigned int>{0};
139
140 const unsigned int rank = Utilities::MPI::this_mpi_process(comm_large);
141 const unsigned int size = Utilities::MPI::n_mpi_processes(comm_small);
142
143 std::vector<unsigned int> ranks(size);
144 const int ierr = MPI_Allgather(
145 &rank, 1, MPI_UNSIGNED, ranks.data(), 1, MPI_UNSIGNED, comm_small);
146 AssertThrowMPI(ierr);
147
148 return ranks;
149 }
150
151
152
154 duplicate_communicator(const MPI_Comm mpi_communicator)
155 {
156 MPI_Comm new_communicator;
157 const int ierr = MPI_Comm_dup(mpi_communicator, &new_communicator);
158 AssertThrowMPI(ierr);
159 return new_communicator;
160 }
161
162
163
164 void
165 free_communicator(MPI_Comm mpi_communicator)
166 {
167 // MPI_Comm_free will set the argument to MPI_COMM_NULL automatically.
168 const int ierr = MPI_Comm_free(&mpi_communicator);
169 AssertThrowMPI(ierr);
170 }
171
172
173
174 std::vector<IndexSet>
176 const MPI_Comm comm,
178 {
179 static_assert(
180 std::is_same_v<types::global_dof_index, IndexSet::size_type>,
181 "IndexSet::size_type must match types::global_dof_index for "
182 "using this function");
183 const unsigned int n_proc = n_mpi_processes(comm);
184 const std::vector<IndexSet::size_type> sizes =
186 const auto total_size =
187 std::accumulate(sizes.begin(), sizes.end(), IndexSet::size_type(0));
188
189 std::vector<IndexSet> res(n_proc, IndexSet(total_size));
190
192 for (unsigned int i = 0; i < n_proc; ++i)
193 {
194 res[i].add_range(begin, begin + sizes[i]);
195 begin = begin + sizes[i];
196 }
197
198 return res;
199 }
200
201
202
205 const MPI_Comm comm,
206 const types::global_dof_index total_size)
207 {
208 const unsigned int this_proc = this_mpi_process(comm);
209 const unsigned int n_proc = n_mpi_processes(comm);
210
212 n_proc,
213 total_size);
214 }
215
216
217
218 std::unique_ptr<MPI_Datatype, void (*)(MPI_Datatype *)>
219 create_mpi_data_type_n_bytes(const std::size_t n_bytes)
220 {
221 MPI_Datatype result;
222 int ierr = LargeCount::Type_contiguous_c(n_bytes, MPI_BYTE, &result);
223 AssertThrowMPI(ierr);
224 ierr = MPI_Type_commit(&result);
225 AssertThrowMPI(ierr);
226
227 if constexpr (running_in_debug_mode())
228 {
229 MPI_Count size64;
230 ierr = MPI_Type_size_x(result, &size64);
231 AssertThrowMPI(ierr);
232
233 Assert(size64 == static_cast<MPI_Count>(n_bytes), ExcInternalError());
234 }
235
236 // Now put the new data type into a std::unique_ptr with a custom
237 // deleter. We call the std::unique_ptr constructor that as first
238 // argument takes a pointer (here, a pointer to a copy of the `result`
239 // object, and as second argument a pointer-to-function, for which
240 // we here use a lambda function without captures that acts as the
241 // 'deleter' object: it calls `MPI_Type_free` and then deletes the
242 // pointer. To avoid a compiler warning about a null this pointer
243 // in the lambda (which don't make sense: the lambda doesn't store
244 // anything), we create the deleter first.
245 auto deleter = [](MPI_Datatype *p) {
246 if (p != nullptr)
247 {
248 const int ierr = MPI_Type_free(p);
249 AssertNothrow(ierr == MPI_SUCCESS, ExcMPI(ierr));
250 delete p;
251 }
252 };
253
254 return std::unique_ptr<MPI_Datatype, void (*)(MPI_Datatype *)>(
255 new MPI_Datatype(result), deleter);
256 }
257
258
259 // Have a little function that checks if destinations provided
260 // to the current process are unique. The way it does this is
261 // to create a sorted list of destinations and then walk through
262 // the list and look at successive elements -- if we find the
263 // same number twice, we know that the destinations were not
264 // unique
265 bool
266 destinations_are_unique(const std::vector<unsigned int> &destinations)
267 {
268 std::vector<unsigned int> my_destinations = destinations;
269 std::sort(my_destinations.begin(), my_destinations.end());
270 return (
271 std::adjacent_find(my_destinations.begin(), my_destinations.end()) ==
272 my_destinations.end());
273 }
274
275
276
277 std::vector<unsigned int>
279 const MPI_Comm mpi_comm,
280 const std::vector<unsigned int> &destinations)
281 {
282 const unsigned int myid = Utilities::MPI::this_mpi_process(mpi_comm);
283 const unsigned int n_procs = Utilities::MPI::n_mpi_processes(mpi_comm);
284
285 if constexpr (running_in_debug_mode())
286 {
287 for (const unsigned int destination : destinations)
288 AssertIndexRange(destination, n_procs);
289 }
290
291 // If all processes report that they have unique destinations,
292 // then we can short-cut the process using a consensus algorithm (which
293 // is implemented only for the case of unique destinations):
295 mpi_comm))
296 {
297 return ConsensusAlgorithms::nbx<char, char>(
298 destinations, {}, {}, {}, mpi_comm);
299 }
300
301 // So we need to run a different algorithm, specifically one that
302 // requires more memory -- MPI_Reduce_scatter_block will require memory
303 // proportional to the number of processes involved; that function is
304 // available for MPI 2.2 or later:
305 static CollectiveMutex mutex;
306 CollectiveMutex::ScopedLock lock(mutex, mpi_comm);
307
308 const int mpi_tag =
310
311 // Calculate the number of messages to send to each process
312 std::vector<unsigned int> dest_vector(n_procs);
313 for (const auto &el : destinations)
314 ++dest_vector[el];
315
316 // Find how many processes will send to this one
317 // by reducing with sum and then scattering the
318 // results over all processes
319 unsigned int n_recv_from;
320 const int ierr = MPI_Reduce_scatter_block(
321 dest_vector.data(), &n_recv_from, 1, MPI_UNSIGNED, MPI_SUM, mpi_comm);
322
323 AssertThrowMPI(ierr);
324
325 // Send myid to every process in `destinations` vector...
326 std::vector<MPI_Request> send_requests(destinations.size());
327 for (const auto &el : destinations)
328 {
329 const int ierr =
330 MPI_Isend(&myid,
331 1,
332 MPI_UNSIGNED,
333 el,
334 mpi_tag,
335 mpi_comm,
336 send_requests.data() + (&el - destinations.data()));
337 AssertThrowMPI(ierr);
338 }
339
340
341 // Receive `n_recv_from` times from the processes
342 // who communicate with this one. Store the obtained id's
343 // in the resulting vector
344 std::vector<unsigned int> origins(n_recv_from);
345 for (auto &el : origins)
346 {
347 const int ierr = MPI_Recv(&el,
348 1,
349 MPI_UNSIGNED,
350 MPI_ANY_SOURCE,
351 mpi_tag,
352 mpi_comm,
353 MPI_STATUS_IGNORE);
354 AssertThrowMPI(ierr);
355 }
356
357 if (destinations.size() > 0)
358 {
359 const int ierr = MPI_Waitall(destinations.size(),
360 send_requests.data(),
361 MPI_STATUSES_IGNORE);
362 AssertThrowMPI(ierr);
363 }
364
365 return origins;
366 }
367
368
369
370 unsigned int
372 const MPI_Comm mpi_comm,
373 const std::vector<unsigned int> &destinations)
374 {
375 // If all processes report that they have unique destinations,
376 // then we can short-cut the process using a consensus algorithm:
378 mpi_comm))
379 {
380 return ConsensusAlgorithms::nbx<char, char>(
381 destinations, {}, {}, {}, mpi_comm)
382 .size();
383 }
384 else
385 {
386 const unsigned int n_procs =
388
389 if constexpr (running_in_debug_mode())
390 {
391 for (const unsigned int destination : destinations)
392 {
393 AssertIndexRange(destination, n_procs);
394 Assert(
395 destination != Utilities::MPI::this_mpi_process(mpi_comm),
397 "There is no point in communicating with ourselves."));
398 }
399 }
400
401 // Calculate the number of messages to send to each process
402 std::vector<unsigned int> dest_vector(n_procs);
403 for (const auto &el : destinations)
404 ++dest_vector[el];
405
406 // Find out how many processes will send to this one
407 // MPI_Reduce_scatter(_block) does exactly this
408 unsigned int n_recv_from = 0;
409
410 const int ierr = MPI_Reduce_scatter_block(dest_vector.data(),
411 &n_recv_from,
412 1,
413 MPI_UNSIGNED,
414 MPI_SUM,
415 mpi_comm);
416
417 AssertThrowMPI(ierr);
418
419 return n_recv_from;
420 }
421 }
422
423
424
425 namespace
426 {
427 // custom MIP_Op for calculate_collective_mpi_min_max_avg
428 void
429 max_reduce(const void *in_lhs_,
430 void *inout_rhs_,
431 int *len,
432 MPI_Datatype *)
433 {
434 const MinMaxAvg *in_lhs = static_cast<const MinMaxAvg *>(in_lhs_);
435 MinMaxAvg *inout_rhs = static_cast<MinMaxAvg *>(inout_rhs_);
436
437 for (int i = 0; i < *len; ++i)
438 {
439 inout_rhs[i].sum += in_lhs[i].sum;
440 if (inout_rhs[i].min > in_lhs[i].min)
441 {
442 inout_rhs[i].min = in_lhs[i].min;
443 inout_rhs[i].min_index = in_lhs[i].min_index;
444 }
445 else if (inout_rhs[i].min == in_lhs[i].min)
446 {
447 // choose lower cpu index when tied to make operator commutative
448 if (inout_rhs[i].min_index > in_lhs[i].min_index)
449 inout_rhs[i].min_index = in_lhs[i].min_index;
450 }
451
452 if (inout_rhs[i].max < in_lhs[i].max)
453 {
454 inout_rhs[i].max = in_lhs[i].max;
455 inout_rhs[i].max_index = in_lhs[i].max_index;
456 }
457 else if (inout_rhs[i].max == in_lhs[i].max)
458 {
459 // choose lower cpu index when tied to make operator commutative
460 if (inout_rhs[i].max_index > in_lhs[i].max_index)
461 inout_rhs[i].max_index = in_lhs[i].max_index;
462 }
463 }
464 }
465 } // namespace
466
467
468
469 void
471 const ArrayView<MinMaxAvg> &result,
472 const MPI_Comm mpi_communicator)
473 {
474 // If MPI was not started, we have a serial computation and cannot run
475 // the other MPI commands
476 if (Utilities::MPI::n_mpi_processes(mpi_communicator) <= 1)
477 {
478 for (unsigned int i = 0; i < my_values.size(); ++i)
479 {
480 result[i].sum = my_values[i];
481 result[i].avg = my_values[i];
482 result[i].min = my_values[i];
483 result[i].max = my_values[i];
484 result[i].min_index = 0;
485 result[i].max_index = 0;
486 }
487 return;
488 }
489
490 /*
491 * A custom MPI datatype handle describing the memory layout of the
492 * MinMaxAvg struct. Initialized on first pass control reaches the
493 * static variable. So hopefully not initialized too early.
494 */
495 static MPI_Datatype type = []() {
496 MPI_Datatype type;
497
498 int lengths[] = {3, 2, 1};
499
500 MPI_Aint displacements[] = {0,
501 offsetof(MinMaxAvg, min_index),
502 offsetof(MinMaxAvg, avg)};
503
504 MPI_Datatype types[] = {MPI_DOUBLE, MPI_INT, MPI_DOUBLE};
505
506 int ierr =
507 MPI_Type_create_struct(3, lengths, displacements, types, &type);
508 AssertThrowMPI(ierr);
509
510 ierr = MPI_Type_commit(&type);
511 AssertThrowMPI(ierr);
512
513 /* Ensure that we free the allocated datatype again at the end of
514 * the program run just before we call MPI_Finalize():*/
515 InitFinalize::signals.at_mpi_finalize.connect([type]() mutable {
516 int ierr = MPI_Type_free(&type);
517 AssertThrowMPI(ierr);
518 });
519
520 return type;
521 }();
522
523 /*
524 * A custom MPI op handle for our max_reduce function.
525 * Initialized on first pass control reaches the static variable. So
526 * hopefully not initialized too early.
527 */
528 static MPI_Op op = []() {
529 MPI_Op op;
530
531 int ierr =
532 MPI_Op_create(reinterpret_cast<MPI_User_function *>(&max_reduce),
533 static_cast<int>(true),
534 &op);
535 AssertThrowMPI(ierr);
536
537 /* Ensure that we free the allocated op again at the end of the
538 * program run just before we call MPI_Finalize():*/
539 InitFinalize::signals.at_mpi_finalize.connect([op]() mutable {
540 int ierr = MPI_Op_free(&op);
541 AssertThrowMPI(ierr);
542 });
543
544 return op;
545 }();
546
547 AssertDimension(Utilities::MPI::min(my_values.size(), mpi_communicator),
548 Utilities::MPI::max(my_values.size(), mpi_communicator));
549
550 AssertDimension(my_values.size(), result.size());
551
552 // To avoid uninitialized values on some MPI implementations, provide
553 // result with a default value already...
554 MinMaxAvg dummy = {0.,
555 std::numeric_limits<double>::max(),
556 std::numeric_limits<double>::lowest(),
557 0,
558 0,
559 0.};
560
561 for (auto &i : result)
562 i = dummy;
563
564 const unsigned int my_id =
565 ::Utilities::MPI::this_mpi_process(mpi_communicator);
566 const unsigned int numproc =
567 ::Utilities::MPI::n_mpi_processes(mpi_communicator);
568
569 std::vector<MinMaxAvg> in(my_values.size());
570
571 for (unsigned int i = 0; i < my_values.size(); ++i)
572 {
573 in[i].sum = in[i].min = in[i].max = my_values[i];
574 in[i].min_index = in[i].max_index = my_id;
575 }
576
577 int ierr = MPI_Allreduce(
578 in.data(), result.data(), my_values.size(), type, op, mpi_communicator);
579 AssertThrowMPI(ierr);
580
581 for (auto &r : result)
582 r.avg = r.sum / numproc;
583 }
584
585
586#else
587
588 unsigned int
590 {
591 return 1;
592 }
593
594
595
596 unsigned int
598 {
599 return 0;
600 }
601
602
603
604 std::vector<unsigned int>
606 {
607 return std::vector<unsigned int>{0};
608 }
609
610
611
612 std::vector<IndexSet>
614 const MPI_Comm /*comm*/,
616 {
617 return std::vector<IndexSet>(1, complete_index_set(locally_owned_size));
618 }
619
622 const MPI_Comm /*comm*/,
623 const types::global_dof_index total_size)
624 {
625 return complete_index_set(total_size);
626 }
627
628
629
631 duplicate_communicator(const MPI_Comm mpi_communicator)
632 {
633 return mpi_communicator;
634 }
635
636
637
638 void
639 free_communicator(MPI_Comm /*mpi_communicator*/)
640 {}
641
642
643
644 void
645 min_max_avg(const ArrayView<const double> &my_values,
646 const ArrayView<MinMaxAvg> &result,
647 const MPI_Comm)
648 {
649 AssertDimension(my_values.size(), result.size());
650
651 for (unsigned int i = 0; i < my_values.size(); ++i)
652 {
653 result[i].sum = my_values[i];
654 result[i].avg = my_values[i];
655 result[i].min = my_values[i];
656 result[i].max = my_values[i];
657 result[i].min_index = 0;
658 result[i].max_index = 0;
659 }
660 }
661
662#endif
663
664
666 char **&argv,
667 const unsigned int max_num_threads)
668 : InitFinalize(argc,
669 argv,
674 max_num_threads)
675 {}
676
677
678
679 bool
681 {
682#ifdef DEAL_II_WITH_MPI
683 int MPI_has_been_started = 0;
684 const int ierr = MPI_Initialized(&MPI_has_been_started);
685 AssertThrowMPI(ierr);
686
687 return (MPI_has_been_started > 0);
688#else
689 return false;
690#endif
691 }
692
693
694
695 namespace
696 {
701 namespace ComputeIndexOwner
702 {
703 class FlexibleIndexStorage
704 {
705 public:
706 using index_type = unsigned int;
707 static const index_type invalid_index_value =
709
710 FlexibleIndexStorage(const bool use_vector = true);
711
712 void
713 reinit(const bool use_vector,
714 const bool index_range_contiguous,
715 const std::size_t size);
716
717 void
718 fill(const std::size_t start,
719 const std::size_t end,
720 const index_type &value);
721
722 index_type &
723 operator[](const std::size_t index);
724
725 index_type
726 operator[](const std::size_t index) const;
727
728 [[maybe_unused]] bool
729 entry_has_been_set(const std::size_t index) const;
730
731 private:
733 std::size_t size;
734 std::vector<index_type> data;
735 std::map<std::size_t, index_type> data_map;
736 };
737
738
739
745 struct Dictionary
746 {
768 static constexpr unsigned int range_minimum_grain_size = 64;
769
776 static constexpr unsigned int sparsity_factor = 4;
777
778
784 Dictionary(const IndexSet &owned_indices, const MPI_Comm comm);
785
792 FlexibleIndexStorage actually_owning_ranks;
793
798 std::vector<unsigned int> actually_owning_rank_list;
799
807
813 std::pair<types::global_dof_index, types::global_dof_index>
815
822
827
833
839 unsigned int stride_small_size;
840
846 unsigned int
847 dof_to_dict_rank(const types::global_dof_index i);
848
854 get_index_offset(const unsigned int rank);
855
861 unsigned int
862 get_owning_rank_index(const unsigned int rank_in_owned_indices,
863 const unsigned int guess = 0);
864
865 private:
870 void
871 partition(const IndexSet &owned_indices, const MPI_Comm comm);
872 };
873
874
875
882 class ConsensusAlgorithmsPayload
883 {
884 public:
885 using RequestType = std::vector<
886 std::pair<types::global_dof_index, types::global_dof_index>>;
887 using AnswerType = std::vector<unsigned int>;
888
892 ConsensusAlgorithmsPayload(const IndexSet &owned_indices,
893 const IndexSet &indices_to_look_up,
894 const MPI_Comm comm,
895 std::vector<unsigned int> &owning_ranks,
896 const bool track_index_requesters = false);
897
902
908
913
917 const unsigned int my_rank;
918
923 const unsigned int n_procs;
924
932
938 std::vector<unsigned int> &owning_ranks;
939
943 Dictionary dict;
944
954 std::vector<std::vector<
955 std::pair<unsigned int,
956 std::vector<std::pair<types::global_dof_index,
959
965 std::map<unsigned int,
966 std::pair<std::vector<types::global_dof_index>,
967 std::vector<unsigned int>>>
969
973 std::vector<unsigned int>
974 compute_targets();
975
979 std::vector<
980 std::pair<types::global_dof_index, types::global_dof_index>>
981 create_request(const unsigned int other_rank);
982
986 std::vector<unsigned int>
987 answer_request(
988 const unsigned int other_rank,
989 const std::vector<std::pair<types::global_dof_index,
990 types::global_dof_index>> &buffer_recv);
991
996 void
997 process_answer(const unsigned int other_rank,
998 const std::vector<unsigned int> &recv_buffer);
999
1015 std::map<unsigned int, IndexSet>
1016 get_requesters();
1017
1018 private:
1032 void
1033 append_index_origin(
1034 const types::global_dof_index index_within_dictionary,
1035 const unsigned int rank_of_request,
1036 const unsigned int rank_of_owner,
1037 unsigned int &owner_index_guess);
1038 };
1039
1040
1041
1042 unsigned int
1043 Dictionary::dof_to_dict_rank(const types::global_dof_index i)
1044 {
1045 // note: this formula is also explicitly used in
1046 // get_index_offset(), so keep the two in sync
1047 return (i / dofs_per_process) * stride_small_size;
1048 }
1049
1050
1052 Dictionary::get_index_offset(const unsigned int rank)
1053 {
1054 return std::min(dofs_per_process *
1055 static_cast<types::global_dof_index>(
1056 (rank + stride_small_size - 1) /
1058 size);
1059 }
1060
1061
1062
1063 unsigned int
1064 Dictionary::get_owning_rank_index(
1065 const unsigned int rank_in_owned_indices,
1066 const unsigned int guess)
1067 {
1069 if (actually_owning_rank_list[guess] == rank_in_owned_indices)
1070 return guess;
1071 else
1072 {
1073 auto it = std::lower_bound(actually_owning_rank_list.begin(),
1075 rank_in_owned_indices);
1077 Assert(*it == rank_in_owned_indices, ExcInternalError());
1078 return it - actually_owning_rank_list.begin();
1079 }
1080 }
1081
1082
1083 const FlexibleIndexStorage::index_type
1084 FlexibleIndexStorage::invalid_index_value;
1085
1086
1087
1088 FlexibleIndexStorage::FlexibleIndexStorage(const bool use_vector)
1090 , size(0)
1091 {}
1092
1093
1094
1095 void
1096 FlexibleIndexStorage::reinit(const bool use_vector,
1097 const bool index_range_contiguous,
1098 const std::size_t size)
1099 {
1100 this->use_vector = use_vector;
1101 this->size = size;
1102
1103 data = {};
1104 data_map.clear();
1105
1106 // in case we have contiguous indices, only fill the vector upon
1107 // first request in `fill`
1108 if (use_vector && !index_range_contiguous)
1109 data.resize(size, invalid_index_value);
1110 }
1111
1112
1113
1114 void
1115 FlexibleIndexStorage::fill(
1116 const std::size_t start,
1117 const std::size_t end,
1118 const FlexibleIndexStorage::index_type &value)
1119 {
1120 AssertIndexRange(start, size);
1122
1123 if (use_vector)
1124 {
1125 if (data.empty() && end > start)
1126 {
1127 // in debug mode, we want to track whether we set all
1128 // indices, so we first fill an invalid index and only later
1129 // the actual ones, whereas we simply assign the given rank
1130 // to the complete vector the first time we pass around in
1131 // this function in release mode to avoid touching data
1132 // unnecessarily (and overwrite the smaller pieces), as the
1133 // locally owned part comes first
1134 if constexpr (running_in_debug_mode())
1135 {
1136 data.resize(size, invalid_index_value);
1137 std::fill(data.begin() + start,
1138 data.begin() + end,
1139 value);
1140 }
1141 else
1142 {
1143 data.resize(size, value);
1144 }
1145 }
1146 else
1147 {
1148 AssertDimension(data.size(), size);
1149 std::fill(data.begin() + start, data.begin() + end, value);
1150 }
1151 }
1152 else
1153 {
1154 for (auto i = start; i < end; ++i)
1155 data_map[i] = value;
1156 }
1157 }
1158
1159
1160
1161 FlexibleIndexStorage::index_type &
1162 FlexibleIndexStorage::operator[](const std::size_t index)
1163 {
1164 AssertIndexRange(index, size);
1165
1166 if (use_vector)
1167 {
1168 AssertDimension(data.size(), size);
1169 return data[index];
1170 }
1171 else
1172 {
1173 return data_map.try_emplace(index, invalid_index_value)
1174 .first->second;
1175 }
1176 }
1177
1178
1179
1180 [[maybe_unused]] bool
1181 FlexibleIndexStorage::entry_has_been_set(const std::size_t index) const
1182 {
1183 AssertIndexRange(index, size);
1184
1185 if (use_vector)
1186 {
1187 if (data.empty())
1188 return false;
1189
1190 AssertDimension(data.size(), size);
1191 return data[index] != invalid_index_value;
1192 }
1193 else
1194 return data_map.find(index) != data_map.end();
1195 }
1196
1197
1198
1199 Dictionary::Dictionary(const IndexSet &owned_indices,
1200 const MPI_Comm comm)
1201 {
1202 // 1) set up the partition
1204
1205 unsigned int my_rank = this_mpi_process(comm);
1206
1207 types::global_dof_index dic_local_received = 0;
1208 std::map<unsigned int,
1209 std::vector<std::pair<types::global_dof_index,
1211 buffers;
1212
1213 const auto owned_indices_size_actual =
1215
1216 actually_owning_ranks.reinit((owned_indices_size_actual *
1218 owned_indices_size_actual ==
1221
1222 // 2) collect relevant processes and process local dict entries
1223 for (auto interval = owned_indices.begin_intervals();
1224 interval != owned_indices.end_intervals();
1225 ++interval)
1226 {
1227 // Due to the granularity of the dictionary, the interval
1228 // might be split into several ranges of processor owner
1229 // ranks. Here, we process the interval by breaking into
1230 // smaller pieces in terms of the dictionary number.
1231 std::pair<types::global_dof_index, types::global_dof_index>
1232 index_range(*interval->begin(), interval->last() + 1);
1233
1234 AssertThrow(index_range.second <= size, ExcInternalError());
1235
1236 while (index_range.first != index_range.second)
1237 {
1238 Assert(index_range.first < index_range.second,
1240
1241 const unsigned int owner =
1242 dof_to_dict_rank(index_range.first);
1243
1244 // this explicitly picks up the formula of
1245 // dof_to_dict_rank, so the two places must be in sync
1246 const types::global_dof_index next_index =
1247 std::min(get_index_offset(owner + 1), index_range.second);
1248
1249 Assert(next_index > index_range.first, ExcInternalError());
1250
1251 if constexpr (running_in_debug_mode())
1252 {
1253 // make sure that the owner is the same on the current
1254 // interval
1255 for (types::global_dof_index i = index_range.first + 1;
1256 i < next_index;
1257 ++i)
1258 AssertDimension(owner, dof_to_dict_rank(i));
1259 }
1260
1261 // add the interval, either to the local range or into a
1262 // buffer to be sent to another processor
1263 if (owner == my_rank)
1264 {
1265 actually_owning_ranks.fill(index_range.first -
1266 local_range.first,
1267 next_index - local_range.first,
1268 my_rank);
1269 dic_local_received += next_index - index_range.first;
1270 if (actually_owning_rank_list.empty())
1272 }
1273 else
1274 buffers[owner].emplace_back(index_range.first, next_index);
1275
1276 index_range.first = next_index;
1277 }
1278 }
1279
1280#ifdef DEAL_II_WITH_MPI
1281 n_dict_procs_in_owned_indices = buffers.size();
1282 std::vector<MPI_Request> request;
1283
1284 // Check if index set space is partitioned globally without gaps.
1285 if (owned_indices_size_actual == owned_indices.size())
1286 {
1287 // no gaps: setup is simple! Processes send their locally owned
1288 // indices to the dictionary. The dictionary stores the sending
1289 // rank for each index. The dictionary knows exactly
1290 // when it is set up when all indices it is responsible for
1291 // have been processed.
1292
1293 request.reserve(n_dict_procs_in_owned_indices);
1294
1295 // protect the following communication steps using a mutex:
1296 static CollectiveMutex mutex;
1297 CollectiveMutex::ScopedLock lock(mutex, comm);
1298
1299 const int mpi_tag =
1301
1302
1303 // 3) send messages with local dofs to the right dict process
1304 for (const auto &rank_pair : buffers)
1305 {
1306 request.push_back(MPI_Request());
1307 const int ierr =
1308 MPI_Isend(rank_pair.second.data(),
1309 rank_pair.second.size() * 2,
1312 rank_pair.first,
1313 mpi_tag,
1314 comm,
1315 &request.back());
1316 AssertThrowMPI(ierr);
1317 }
1318
1319 // 4) receive messages until all dofs in dict are processed
1320 while (this->locally_owned_size != dic_local_received)
1321 {
1322 // wait for an incoming message
1323 MPI_Status status;
1324 int ierr = MPI_Probe(MPI_ANY_SOURCE, mpi_tag, comm, &status);
1325 AssertThrowMPI(ierr);
1326
1327 // retrieve size of incoming message
1328 int number_amount;
1329 ierr = MPI_Get_count(&status,
1332 &number_amount);
1333 AssertThrowMPI(ierr);
1334
1335 const auto other_rank = status.MPI_SOURCE;
1336 actually_owning_rank_list.push_back(other_rank);
1337
1338 // receive message
1339 Assert(number_amount % 2 == 0, ExcInternalError());
1340 std::vector<
1341 std::pair<types::global_dof_index, types::global_dof_index>>
1342 buffer(number_amount / 2);
1343 ierr = MPI_Recv(buffer.data(),
1344 number_amount,
1347 status.MPI_SOURCE,
1348 status.MPI_TAG,
1349 comm,
1350 MPI_STATUS_IGNORE);
1351 AssertThrowMPI(ierr);
1352 // process message: loop over all intervals
1353 for (auto interval : buffer)
1354 {
1355 if constexpr (library_build_mode ==
1357 {
1358 for (types::global_dof_index i = interval.first;
1359 i < interval.second;
1360 i++)
1361 Assert(actually_owning_ranks.entry_has_been_set(
1362 i - local_range.first) == false,
1364 Assert(interval.first >= local_range.first &&
1365 interval.first < local_range.second,
1367 Assert(interval.second > local_range.first &&
1368 interval.second <= local_range.second,
1370 }
1371
1372 actually_owning_ranks.fill(interval.first -
1373 local_range.first,
1374 interval.second -
1375 local_range.first,
1376 other_rank);
1377 dic_local_received += interval.second - interval.first;
1378 }
1379 }
1380 }
1381 else
1382 {
1383 // with gap: use a ConsensusAlgorithm to determine when all
1384 // dictionaries have been set up.
1385
1386 // 3/4) use a ConsensusAlgorithm to send messages with local
1387 // dofs to the right dict process
1388
1389 using RequestType = std::vector<
1390 std::pair<types::global_dof_index, types::global_dof_index>>;
1391
1392 std::vector<unsigned int> targets;
1393 targets.reserve(buffers.size());
1394 for (const auto &rank_pair : buffers)
1395 targets.emplace_back(rank_pair.first);
1396
1397 ConsensusAlgorithms::selector<RequestType>(
1398 targets,
1399
1400 /* create_request = */
1401 [&buffers](const unsigned int target_rank) -> RequestType {
1402 return buffers.at(target_rank);
1403 },
1404
1405 /* process_request = */
1406 [&](const unsigned int source_rank,
1407 const RequestType &request) -> void {
1408 // process message: loop over all intervals
1409 for (auto interval : request)
1410 {
1411 if constexpr (library_build_mode ==
1413 {
1414 for (types::global_dof_index i = interval.first;
1415 i < interval.second;
1416 i++)
1417 Assert(
1418 actually_owning_ranks.entry_has_been_set(
1419 i - local_range.first) == false,
1420 ExcMessage(
1421 "Multiple processes seem to own the same global index. "
1422 "A possible reason is that the sets of locally owned "
1423 "indices are not distinct."));
1424 Assert(interval.first < interval.second,
1426 Assert(
1427 local_range.first <= interval.first &&
1428 interval.second <= local_range.second,
1429 ExcMessage(
1430 "The specified interval is not handled by the current process."));
1431 }
1432 actually_owning_ranks.fill(interval.first -
1433 local_range.first,
1434 interval.second -
1435 local_range.first,
1436 source_rank);
1437 }
1438 actually_owning_rank_list.push_back(source_rank);
1439 },
1440
1441 comm);
1442 }
1443
1444 std::sort(actually_owning_rank_list.begin(),
1446
1447 for (unsigned int i = 1; i < actually_owning_rank_list.size(); ++i)
1451
1452 // 5) make sure that all messages have been sent
1453 if (request.size() > 0)
1454 {
1455 const int ierr = MPI_Waitall(request.size(),
1456 request.data(),
1457 MPI_STATUSES_IGNORE);
1458 AssertThrowMPI(ierr);
1459 }
1460
1461#else
1462 Assert(buffers.empty(), ExcInternalError());
1463 (void)comm;
1464 (void)dic_local_received;
1465#endif
1466 }
1467
1468
1469
1470 void
1471 Dictionary::partition(const IndexSet &owned_indices,
1472 const MPI_Comm comm)
1473 {
1474 const unsigned int n_procs = n_mpi_processes(comm);
1475 const unsigned int my_rank = this_mpi_process(comm);
1476
1478
1480
1482 std::max<types::global_dof_index>((size + n_procs - 1) / n_procs,
1484
1486 std::max<unsigned int>(dofs_per_process * n_procs / size, 1);
1487
1488 local_range.first = get_index_offset(my_rank);
1489 local_range.second = get_index_offset(my_rank + 1);
1490
1492 }
1493
1494
1495 ConsensusAlgorithmsPayload::ConsensusAlgorithmsPayload(
1496 const IndexSet &owned_indices,
1498 const MPI_Comm comm,
1499 std::vector<unsigned int> &owning_ranks,
1500 const bool track_index_requesters)
1503 , comm(comm)
1510 {}
1511
1512
1513
1514 std::vector<unsigned int>
1515 ConsensusAlgorithmsPayload::compute_targets()
1516 {
1517 std::vector<unsigned int> targets;
1518
1520 unsigned int index = 0;
1521 unsigned int owner_index_guess = 0;
1522 for (auto i : indices_to_look_up)
1523 {
1524 unsigned int other_rank = dict.dof_to_dict_rank(i);
1525 if (other_rank == my_rank)
1526 {
1528 dict.actually_owning_ranks[i - dict.local_range.first];
1530 append_index_origin(i - dict.local_range.first,
1531 my_rank,
1532 owning_ranks[index],
1533 owner_index_guess);
1534 }
1535 else
1536 {
1537 if (targets.empty() || targets.back() != other_rank)
1538 targets.push_back(other_rank);
1539 auto &indices = indices_to_look_up_by_dict_rank[other_rank];
1540 indices.first.push_back(i);
1541 indices.second.push_back(index);
1542 }
1543 ++index;
1544 }
1545
1546 Assert(targets.size() == indices_to_look_up_by_dict_rank.size(),
1547 ExcMessage("Size does not match!"));
1548
1549 return targets;
1550 }
1551
1552
1553
1554 std::vector<std::pair<types::global_dof_index, types::global_dof_index>>
1555 ConsensusAlgorithmsPayload::create_request(
1556 const unsigned int other_rank)
1557 {
1558 std::vector<
1559 std::pair<types::global_dof_index, types::global_dof_index>>
1560 send_buffer;
1561
1562 // create index set and compress data to be sent
1563 auto &indices_i = indices_to_look_up_by_dict_rank[other_rank].first;
1564 IndexSet is(dict.size);
1565 is.add_indices(indices_i.begin(), indices_i.end());
1566 is.compress();
1567
1568 for (auto interval = is.begin_intervals();
1569 interval != is.end_intervals();
1570 ++interval)
1571 send_buffer.emplace_back(*interval->begin(), interval->last() + 1);
1572
1573 return send_buffer;
1574 }
1575
1576
1577
1578 std::vector<unsigned int>
1579 ConsensusAlgorithmsPayload::answer_request(
1580 const unsigned int other_rank,
1581 const std::vector<std::pair<types::global_dof_index,
1582 types::global_dof_index>> &buffer_recv)
1583 {
1584 std::vector<unsigned int> request_buffer;
1585
1586 unsigned int owner_index_guess = 0;
1587 for (const auto &interval : buffer_recv)
1588 for (auto i = interval.first; i < interval.second; ++i)
1589 {
1590 const unsigned int actual_owner =
1591 dict.actually_owning_ranks[i - dict.local_range.first];
1592 request_buffer.push_back(actual_owner);
1593
1595 append_index_origin(i - dict.local_range.first,
1596 other_rank,
1597 actual_owner,
1598 owner_index_guess);
1599 }
1600
1601 return request_buffer;
1602 }
1603
1604
1605
1606 void
1607 ConsensusAlgorithmsPayload::process_answer(
1608 const unsigned int other_rank,
1609 const std::vector<unsigned int> &recv_buffer)
1610 {
1611 const auto &recv_indices =
1612 indices_to_look_up_by_dict_rank[other_rank].second;
1613 AssertDimension(recv_indices.size(), recv_buffer.size());
1614 for (unsigned int j = 0; j < recv_indices.size(); ++j)
1615 owning_ranks[recv_indices[j]] = recv_buffer[j];
1616 }
1617
1618
1619
1620 std::map<unsigned int, IndexSet>
1621 ConsensusAlgorithmsPayload::get_requesters()
1622 {
1624 ExcMessage("Must enable index range tracking in "
1625 "constructor of ConsensusAlgorithmProcess"));
1626
1627 std::map<unsigned int, ::IndexSet> requested_indices;
1628
1629#ifdef DEAL_II_WITH_MPI
1630
1631 static CollectiveMutex mutex;
1632 CollectiveMutex::ScopedLock lock(mutex, comm);
1633
1634 const int mpi_tag = Utilities::MPI::internal::Tags::
1636
1637 // reserve enough slots for the requests ahead; depending on
1638 // whether the owning rank is one of the requesters or not, we
1639 // might have one less requests to execute, so fill the requests
1640 // on demand.
1641 std::vector<MPI_Request> send_requests;
1642 send_requests.reserve(requesters.size());
1643
1644 // We use an integer vector for the data exchange. Since we send
1645 // data associated to intervals with different requesters, we will
1646 // need to send (a) the MPI rank of the requester, (b) the number
1647 // of intervals directed to this requester, and (c) a list of
1648 // intervals, i.e., two integers per interval. The number of items
1649 // sent in total can be deduced both via the MPI status message at
1650 // the receiver site as well as be counting the buckets from
1651 // different requesters.
1652 std::vector<std::vector<types::global_dof_index>> send_data(
1653 requesters.size());
1654 for (unsigned int i = 0; i < requesters.size(); ++i)
1655 {
1656 // special code for our own indices
1657 if (dict.actually_owning_rank_list[i] == my_rank)
1658 {
1659 for (const auto &j : requesters[i])
1660 {
1661 const types::global_dof_index index_offset =
1662 dict.get_index_offset(my_rank);
1663 IndexSet &my_index_set = requested_indices[j.first];
1664 my_index_set.set_size(owned_indices.size());
1665 for (const auto &interval : j.second)
1666 my_index_set.add_range(index_offset + interval.first,
1667 index_offset + interval.second);
1668 }
1669 }
1670 else
1671 {
1672 for (const auto &j : requesters[i])
1673 {
1674 send_data[i].push_back(j.first);
1675 send_data[i].push_back(j.second.size());
1676 for (const auto &interval : j.second)
1677 {
1678 send_data[i].push_back(interval.first);
1679 send_data[i].push_back(interval.second);
1680 }
1681 }
1682 send_requests.push_back(MPI_Request());
1683 const int ierr =
1684 MPI_Isend(send_data[i].data(),
1685 send_data[i].size(),
1688 dict.actually_owning_rank_list[i],
1689 mpi_tag,
1690 comm,
1691 &send_requests.back());
1692 AssertThrowMPI(ierr);
1693 }
1694 }
1695
1696 // receive the data
1697 for (unsigned int c = 0; c < dict.n_dict_procs_in_owned_indices; ++c)
1698 {
1699 // wait for an incoming message
1700 MPI_Status status;
1701 int ierr = MPI_Probe(MPI_ANY_SOURCE, mpi_tag, comm, &status);
1702 AssertThrowMPI(ierr);
1703
1704 // retrieve size of incoming message
1705 int number_amount;
1706 ierr = MPI_Get_count(
1707 &status,
1708 Utilities::MPI::mpi_type_id_for_type<types::global_dof_index>,
1709 &number_amount);
1710 AssertThrowMPI(ierr);
1711
1712 // receive message
1713 Assert(number_amount % 2 == 0, ExcInternalError());
1714 std::vector<
1715 std::pair<types::global_dof_index, types::global_dof_index>>
1716 buffer(number_amount / 2);
1717 ierr = MPI_Recv(
1718 buffer.data(),
1719 number_amount,
1720 Utilities::MPI::mpi_type_id_for_type<types::global_dof_index>,
1721 status.MPI_SOURCE,
1722 status.MPI_TAG,
1723 comm,
1724 &status);
1725 AssertThrowMPI(ierr);
1726
1727 // unpack the message and translate the dictionary-local
1728 // indices coming via MPI to the global index range
1729 const types::global_dof_index index_offset =
1730 dict.get_index_offset(status.MPI_SOURCE);
1731 unsigned int offset = 0;
1732 while (offset < buffer.size())
1733 {
1734 AssertIndexRange(offset + buffer[offset].second,
1735 buffer.size());
1736
1737 IndexSet my_index_set(owned_indices.size());
1738 for (unsigned int i = offset + 1;
1739 i < offset + buffer[offset].second + 1;
1740 ++i)
1741 my_index_set.add_range(index_offset + buffer[i].first,
1742 index_offset + buffer[i].second);
1743
1744 // the underlying index set is able to merge ranges coming
1745 // from different ranks due to the partitioning in the
1746 // dictionary
1747 IndexSet &index_set = requested_indices[buffer[offset].first];
1748 if (index_set.size() == 0)
1749 index_set.set_size(owned_indices.size());
1750 index_set.add_indices(my_index_set);
1751
1752 offset += buffer[offset].second + 1;
1753 }
1754 AssertDimension(offset, buffer.size());
1755 }
1756
1757 if (send_requests.size() > 0)
1758 {
1759 const auto ierr = MPI_Waitall(send_requests.size(),
1760 send_requests.data(),
1761 MPI_STATUSES_IGNORE);
1762 AssertThrowMPI(ierr);
1763 }
1764
1765
1766 if constexpr (running_in_debug_mode())
1767 {
1768 for (const auto &it : requested_indices)
1769 {
1770 IndexSet copy_set = it.second;
1771 copy_set.subtract_set(owned_indices);
1772 Assert(copy_set.n_elements() == 0,
1774 "The indices requested from the current "
1775 "MPI rank should be locally owned here!"));
1776 }
1777 }
1778
1779#endif // DEAL_II_WITH_MPI
1780
1781 return requested_indices;
1782 }
1783
1784
1785
1786 void
1787 ConsensusAlgorithmsPayload::append_index_origin(
1788 const types::global_dof_index index_within_dict,
1789 const unsigned int rank_of_request,
1790 const unsigned int rank_of_owner,
1791 unsigned int &owner_index_guess)
1792 {
1793 // remember who requested which index. We want to use an
1794 // std::vector with simple addressing, via a good guess from the
1795 // preceding index, rather than std::map, because this is an inner
1796 // loop and it avoids the map lookup in every iteration
1797 owner_index_guess =
1798 dict.get_owning_rank_index(rank_of_owner, owner_index_guess);
1799
1800 auto &request = requesters[owner_index_guess];
1801 if (request.empty() || request.back().first != rank_of_request)
1802 request.emplace_back(
1803 rank_of_request,
1804 std::vector<
1805 std::pair<types::global_dof_index, types::global_dof_index>>());
1806
1807 auto &intervals = request.back().second;
1808 if (intervals.empty() || intervals.back().second != index_within_dict)
1809 intervals.emplace_back(index_within_dict, index_within_dict + 1);
1810 else
1811 ++intervals.back().second;
1812 }
1813
1814 } // namespace ComputeIndexOwner
1815 } // namespace
1816
1817
1818
1819 std::vector<unsigned int>
1822 const MPI_Comm comm)
1823 {
1825 ExcMessage("IndexSets have to have the same sizes."));
1826
1827 Assert(
1829 ExcMessage("IndexSets have to have the same size on all processes."));
1830
1831 std::vector<unsigned int> owning_ranks(indices_to_look_up.n_elements());
1832
1833 // Step 1: setup dictionary
1834 // The input owned_indices can be partitioned arbitrarily. In the
1835 // dictionary, the index set is statically repartitioned among the
1836 // processes again and extended with information with the actual owner
1837 // of that the index.
1838 ComputeIndexOwner::ConsensusAlgorithmsPayload process(
1841 comm,
1843 /* keep track of requesters = */ false);
1844
1845 // Step 2: read dictionary
1846 // Communicate with the process who owns the index in the static
1847 // partition (i.e. in the dictionary). This process returns the actual
1848 // owner of the index.
1849 using RequestType =
1850 ComputeIndexOwner::ConsensusAlgorithmsPayload::RequestType;
1851 using AnswerType =
1852 ComputeIndexOwner::ConsensusAlgorithmsPayload::AnswerType;
1853 ConsensusAlgorithms::selector<RequestType, AnswerType>(
1854 process.compute_targets(),
1855 [&process](const unsigned int other_rank) -> RequestType {
1856 return process.create_request(other_rank);
1857 },
1858 [&process](const unsigned int other_rank, const RequestType &r)
1859 -> AnswerType { return process.answer_request(other_rank, r); },
1860 [&process](const unsigned int other_rank, const AnswerType &a) -> void {
1861 process.process_answer(other_rank, a);
1862 },
1863 comm);
1864
1865 return owning_ranks;
1866 }
1867
1868
1869
1870 std::pair<std::vector<unsigned int>, std::map<unsigned int, IndexSet>>
1873 const MPI_Comm &comm)
1874 {
1876 ExcMessage("IndexSets have to have the same sizes."));
1877
1878 Assert(
1880 ExcMessage("IndexSets have to have the same size on all processes."));
1881
1882 std::vector<unsigned int> owning_ranks(indices_to_look_up.n_elements());
1883
1884 // Step 1: setup dictionary
1885 // The input owned_indices can be partitioned arbitrarily. In the
1886 // dictionary, the index set is statically repartitioned among the
1887 // processes again and extended with information with the actual owner
1888 // of that the index.
1889 ComputeIndexOwner::ConsensusAlgorithmsPayload process(
1891
1892 // Step 2: read dictionary
1893 // Communicate with the process who owns the index in the static
1894 // partition (i.e. in the dictionary). This process returns the actual
1895 // owner of the index.
1896 using RequestType =
1897 ComputeIndexOwner::ConsensusAlgorithmsPayload::RequestType;
1898 using AnswerType =
1899 ComputeIndexOwner::ConsensusAlgorithmsPayload::AnswerType;
1900 ConsensusAlgorithms::selector<RequestType, AnswerType>(
1901 process.compute_targets(),
1902 [&process](const unsigned int other_rank) -> RequestType {
1903 return process.create_request(other_rank);
1904 },
1905 [&process](const unsigned int other_rank, const RequestType &r)
1906 -> AnswerType { return process.answer_request(other_rank, r); },
1907 [&process](const unsigned int other_rank, const AnswerType &a) -> void {
1908 process.process_answer(other_rank, a);
1909 },
1910 comm);
1911
1912 return {owning_ranks, process.get_requesters()};
1913 }
1914
1915
1916
1917 namespace internal
1918 {
1919 namespace CollectiveMutexImplementation
1920 {
1925 void
1926 check_exception()
1927 {
1928#ifdef DEAL_II_WITH_MPI
1929 if (std::uncaught_exceptions() > 0)
1930 {
1931 std::cerr
1932 << "---------------------------------------------------------\n"
1933 << "An exception was thrown inside a section of the program\n"
1934 << "guarded by a CollectiveMutex.\n"
1935 << "Because a CollectiveMutex guards critical communication\n"
1936 << "handling the exception would likely\n"
1937 << "deadlock because only the current process is aware of the\n"
1938 << "exception. To prevent this deadlock, the program will be\n"
1939 << "aborted.\n"
1940 << "---------------------------------------------------------"
1941 << std::endl;
1942
1943 MPI_Abort(MPI_COMM_WORLD, 1);
1944 }
1945#endif
1946 }
1947 } // namespace CollectiveMutexImplementation
1948 } // namespace internal
1949
1950
1951
1952 CollectiveMutex::CollectiveMutex()
1953 : locked(false)
1954 , request(MPI_REQUEST_NULL)
1955 {
1957 }
1958
1959
1960
1962 {
1963 // First check if this destructor is called during exception handling
1964 // if so, abort.
1966
1967 Assert(
1968 !locked,
1969 ExcMessage(
1970 "Error: MPI::CollectiveMutex is still locked while being destroyed!"));
1971
1973 }
1974
1975
1976
1977 void
1979 {
1980 Assert(
1981 !locked,
1982 ExcMessage(
1983 "Error: MPI::CollectiveMutex needs to be unlocked before lock()"));
1984
1985#ifdef DEAL_II_WITH_MPI
1986
1987 if (job_supports_mpi())
1988 {
1989 // TODO: For now, we implement this mutex with a blocking barrier in
1990 // the lock and unlock. It needs to be tested, if we can move to a
1991 // nonblocking barrier (code disabled below).
1992
1993 const int ierr = MPI_Barrier(comm);
1994 AssertThrowMPI(ierr);
1995
1996# if 0
1997 // wait for non-blocking barrier to finish. This is a noop the
1998 // first time we lock().
1999 const int ierr = MPI_Wait(&request, MPI_STATUS_IGNORE);
2000 AssertThrowMPI(ierr);
2001# else
2002 // nothing to do as blocking barrier already completed
2003# endif
2004 }
2005#else
2006 (void)comm;
2007#endif
2008
2009 locked = true;
2010 }
2011
2012
2013
2014 void
2016 {
2017 // First check if this function is called during exception handling
2018 // if so, abort. This can happen if a ScopedLock is destroyed.
2020
2021 Assert(
2022 locked,
2023 ExcMessage(
2024 "Error: MPI::CollectiveMutex needs to be locked before unlock()"));
2025
2026#ifdef DEAL_II_WITH_MPI
2027
2028 if (job_supports_mpi())
2029 {
2030 // TODO: For now, we implement this mutex with a blocking barrier
2031 // in the lock and unlock. It needs to be tested, if we can move
2032 // to a nonblocking barrier (code disabled below):
2033# if 0
2034 const int ierr = MPI_Ibarrier(comm, &request);
2035 AssertThrowMPI(ierr);
2036# else
2037 const int ierr = MPI_Barrier(comm);
2038 AssertThrowMPI(ierr);
2039# endif
2040 }
2041#else
2042 (void)comm;
2043#endif
2044
2045 locked = false;
2046 }
2047
2048
2049#ifndef DOXYGEN
2050 // explicit instantiations
2051
2052 // booleans aren't in MPI_SCALARS
2053 template bool
2054 reduce(const bool &,
2055 const MPI_Comm,
2056 const std::function<bool(const bool &, const bool &)> &,
2057 const unsigned int);
2058
2059 template std::vector<bool>
2060 reduce(const std::vector<bool> &,
2061 const MPI_Comm,
2062 const std::function<std::vector<bool>(const std::vector<bool> &,
2063 const std::vector<bool> &)> &,
2064 const unsigned int);
2065
2066 template bool
2067 all_reduce(const bool &,
2068 const MPI_Comm,
2069 const std::function<bool(const bool &, const bool &)> &);
2070
2071 template std::vector<bool>
2072 all_reduce(
2073 const std::vector<bool> &,
2074 const MPI_Comm,
2075 const std::function<std::vector<bool>(const std::vector<bool> &,
2076 const std::vector<bool> &)> &);
2077
2078 // We need an explicit instantiation of this for the same reason as the
2079 // other types described in mpi.inst.in
2080 template void
2081 internal::all_reduce<bool>(const MPI_Op &,
2082 const ArrayView<const bool> &,
2083 const MPI_Comm,
2084 const ArrayView<bool> &);
2085
2086
2087 template bool
2088 logical_or<bool>(const bool &, const MPI_Comm);
2089
2090
2091 template void
2092 logical_or<bool>(const ArrayView<const bool> &,
2093 const MPI_Comm,
2094 const ArrayView<bool> &);
2095
2096
2097 template std::vector<unsigned int>
2098 compute_set_union(const std::vector<unsigned int> &vec,
2099 const MPI_Comm comm);
2100
2101
2102 template std::set<unsigned int>
2103 compute_set_union(const std::set<unsigned int> &set, const MPI_Comm comm);
2104#endif
2105
2106#include "base/mpi.inst"
2107 } // end of namespace MPI
2108} // end of namespace Utilities
2109
*  iterator end()
*  *  for(const auto &cell :triangulation.active_cell_iterators())
*  *  iterator begin()
*  x_component_mask set(0, true)
value_type * data() const noexcept
Definition array_view.h:714
std::size_t size() const
Definition array_view.h:737
IntervalIterator end_intervals() const
Definition index_set.h:1726
size_type size() const
Definition index_set.h:1759
size_type n_elements() const
Definition index_set.h:1917
void set_size(const size_type size)
Definition index_set.h:1747
IntervalIterator begin_intervals() const
Definition index_set.h:1714
void subtract_set(const IndexSet &other)
Definition index_set.cc:496
void add_range(const size_type begin, const size_type end)
Definition index_set.h:1786
void add_indices(const ForwardIterator &begin, const ForwardIterator &end)
Definition index_set.h:1814
static void unregister_request(MPI_Request &request)
static void register_request(MPI_Request &request)
static Signals signals
void lock(const MPI_Comm comm)
Definition mpi.cc:1978
void unlock(const MPI_Comm comm)
Definition mpi.cc:2015
MPI_InitFinalize(int &argc, char **&argv, const unsigned int max_num_threads=numbers::invalid_unsigned_int)
Definition mpi.cc:665
constexpr LibraryBuildMode library_build_mode
Definition config.h:66
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition config.h:636
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition config.h:680
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertThrowMPI(error_code)
#define AssertNothrow(cond, exc)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
IndexSet complete_index_set(const IndexSet::size_type N)
Definition index_set.h:1187
InitializeLibrary
const unsigned int my_rank
Definition mpi.cc:917
const IndexSet & indices_to_look_up
Definition mpi.cc:907
std::vector< index_type > data
Definition mpi.cc:734
std::pair< types::global_dof_index, types::global_dof_index > local_range
Definition mpi.cc:814
std::vector< unsigned int > actually_owning_rank_list
Definition mpi.cc:798
static constexpr unsigned int range_minimum_grain_size
Definition mpi.cc:768
static constexpr unsigned int sparsity_factor
Definition mpi.cc:776
unsigned int stride_small_size
Definition mpi.cc:839
bool use_vector
Definition mpi.cc:732
Dictionary dict
Definition mpi.cc:943
std::map< std::size_t, index_type > data_map
Definition mpi.cc:735
std::vector< unsigned int > & owning_ranks
Definition mpi.cc:938
std::vector< std::vector< std::pair< unsigned int, std::vector< std::pair< types::global_dof_index, types::global_dof_index > > > > > requesters
Definition mpi.cc:958
std::size_t size
Definition mpi.cc:733
const MPI_Comm comm
Definition mpi.cc:912
const IndexSet & owned_indices
Definition mpi.cc:901
std::map< unsigned int, std::pair< std::vector< types::global_dof_index >, std::vector< unsigned int > > > indices_to_look_up_by_dict_rank
Definition mpi.cc:968
static const index_type invalid_index_value
Definition mpi.cc:707
const bool track_index_requesters
Definition mpi.cc:931
FlexibleIndexStorage actually_owning_ranks
Definition mpi.cc:792
types::global_dof_index locally_owned_size
Definition mpi.cc:821
unsigned int n_dict_procs_in_owned_indices
Definition mpi.cc:832
const unsigned int n_procs
Definition mpi.cc:923
types::global_dof_index dofs_per_process
Definition mpi.cc:806
void partition(const SparsityPattern &sparsity_pattern, const unsigned int n_partitions, std::vector< unsigned int > &partition_indices, const Partitioner partitioner=Partitioner::metis)
int Type_contiguous_c(MPI_Count count, MPI_Datatype oldtype, MPI_Datatype *newtype)
@ compute_point_to_point_communication_pattern
Utilities::MPI::compute_point_to_point_communication_pattern()
Definition mpi_tags.h:55
@ consensus_algorithm_payload_get_requesters
ConsensusAlgorithms::Payload::get_requesters()
Definition mpi_tags.h:79
@ dictionary_reinit
Dictionary::reinit()
Definition mpi_tags.h:76
std::vector< IndexSet > create_ascending_partitioning(const MPI_Comm comm, const types::global_dof_index locally_owned_size)
Definition mpi.cc:175
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)
std::unique_ptr< MPI_Datatype, void(*)(MPI_Datatype *)> create_mpi_data_type_n_bytes(const std::size_t n_bytes)
Definition mpi.cc:219
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:103
T max(const T &t, const MPI_Comm mpi_communicator)
T min(const T &t, const MPI_Comm mpi_communicator)
std::vector< T > all_gather(const MPI_Comm comm, const T &object_to_send)
std::vector< unsigned int > compute_index_owner(const IndexSet &owned_indices, const IndexSet &indices_to_look_up, const MPI_Comm comm)
Definition mpi.cc:1820
std::vector< T > compute_set_union(const std::vector< T > &vec, const MPI_Comm comm)
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
T all_reduce(const T &local_value, const MPI_Comm comm, const std::function< T(const T &, const T &)> &combiner)
IndexSet create_evenly_distributed_partitioning(const MPI_Comm comm, const types::global_dof_index total_size)
Definition mpi.cc:204
std::vector< unsigned int > compute_point_to_point_communication_pattern(const MPI_Comm mpi_comm, const std::vector< unsigned int > &destinations)
Definition mpi.cc:278
MPI_Comm duplicate_communicator(const MPI_Comm mpi_communicator)
Definition mpi.cc:154
T reduce(const T &local_value, const MPI_Comm comm, const std::function< T(const T &, const T &)> &combiner, const unsigned int root_process=0)
bool job_supports_mpi()
Definition mpi.cc:680
const MPI_Datatype mpi_type_id_for_type
Definition mpi.h:1685
void free_communicator(MPI_Comm mpi_communicator)
Definition mpi.cc:165
bool destinations_are_unique(const std::vector< unsigned int > &destinations)
Definition mpi.cc:266
std::vector< unsigned int > mpi_processes_within_communicator(const MPI_Comm comm_large, const MPI_Comm comm_small)
Definition mpi.cc:134
unsigned int compute_n_point_to_point_communications(const MPI_Comm mpi_comm, const std::vector< unsigned int > &destinations)
Definition mpi.cc:371
MinMaxAvg min_max_avg(const double my_value, const MPI_Comm mpi_communicator)
Definition mpi.cc:77
IndexSet create_evenly_distributed_partitioning(const unsigned int my_partition_id, const unsigned int n_partitions, const types::global_dof_index total_size)
Definition mpi.cc:53
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
Definition types.h:30
unsigned int global_dof_index
Definition types.h:92
boost::signals2::signal< void()> at_mpi_finalize