Note: this is documentation for an old release. View the latest documentation at docs.fenicsproject.org/v0.3.0/v0.9.0/cpp
DOLFINx  0.3.0
DOLFINx C++ interface
MPI.h
1 // Copyright (C) 2007-2014 Magnus Vikstrøm and Garth N. Wells
2 //
3 // This file is part of DOLFINx (https://www.fenicsproject.org)
4 //
5 // SPDX-License-Identifier: LGPL-3.0-or-later
6 
7 #pragma once
8 
9 #include <array>
10 #include <cassert>
11 #include <complex>
12 #include <cstdint>
13 #include <dolfinx/graph/AdjacencyList.h>
14 #include <iostream>
15 #include <numeric>
16 #include <set>
17 #include <tuple>
18 #include <type_traits>
19 #include <utility>
20 #include <vector>
21 
22 #define MPICH_IGNORE_CXX_SEEK 1
23 #include <mpi.h>
24 
26 namespace dolfinx::MPI
27 {
28 
31 class Comm
32 {
33 public:
35  explicit Comm(MPI_Comm comm, bool duplicate = true);
36 
38  Comm(const Comm& comm) noexcept;
39 
41  Comm(Comm&& comm) noexcept;
42 
43  // Disable copy assignment operator
44  Comm& operator=(const Comm& comm) = delete;
45 
47  Comm& operator=(Comm&& comm) noexcept;
48 
50  ~Comm();
51 
53  MPI_Comm comm() const noexcept;
54 
55 private:
56  // MPI communicator
57  MPI_Comm _comm;
58 };
59 
61 int rank(MPI_Comm comm);
62 
65 int size(MPI_Comm comm);
66 
69 template <typename T>
71  const graph::AdjacencyList<T>& send_data);
72 
85 std::vector<int> compute_graph_edges(MPI_Comm comm, const std::set<int>& edges);
86 
90 template <typename T>
92 neighbor_all_to_all(MPI_Comm neighbor_comm,
93  const graph::AdjacencyList<T>& send_data);
94 
100 std::tuple<std::vector<int>, std::vector<int>>
101 neighbors(MPI_Comm neighbor_comm);
102 
109 constexpr std::array<std::int64_t, 2> local_range(int rank, std::int64_t N,
110  int size)
111 {
112  assert(rank >= 0);
113  assert(N >= 0);
114  assert(size > 0);
115 
116  // Compute number of items per rank and remainder
117  const std::int64_t n = N / size;
118  const std::int64_t r = N % size;
119 
120  // Compute local range
121  if (rank < r)
122  return {{rank * (n + 1), rank * (n + 1) + n + 1}};
123  else
124  return {{rank * n + r, rank * n + r + n}};
125 }
126 
132 constexpr int index_owner(int size, std::size_t index, std::size_t N)
133 {
134  assert(index < N);
135 
136  // Compute number of items per rank and remainder
137  const std::size_t n = N / size;
138  const std::size_t r = N % size;
139 
140  // First r ranks own n + 1 indices
141  if (index < r * (n + 1))
142  return index / (n + 1);
143 
144  // Remaining ranks own n indices
145  return r + (index - r * (n + 1)) / n;
146 }
147 
148 template <typename T>
149 struct dependent_false : std::false_type
150 {
151 };
152 
154 template <typename T>
155 constexpr MPI_Datatype mpi_type()
156 {
157  if constexpr (std::is_same<T, float>::value)
158  return MPI_FLOAT;
159  else if constexpr (std::is_same<T, double>::value)
160  return MPI_DOUBLE;
161  else if constexpr (std::is_same<T, std::complex<double>>::value)
162  return MPI_DOUBLE_COMPLEX;
163  else if constexpr (std::is_same<T, short int>::value)
164  return MPI_SHORT;
165  else if constexpr (std::is_same<T, int>::value)
166  return MPI_INT;
167  else if constexpr (std::is_same<T, unsigned int>::value)
168  return MPI_UNSIGNED;
169  else if constexpr (std::is_same<T, long int>::value)
170  return MPI_LONG;
171  else if constexpr (std::is_same<T, unsigned long>::value)
172  return MPI_UNSIGNED_LONG;
173  else if constexpr (std::is_same<T, long long>::value)
174  return MPI_LONG_LONG;
175  else if constexpr (std::is_same<T, unsigned long long>::value)
176  return MPI_UNSIGNED_LONG_LONG;
177  else if constexpr (std::is_same<T, bool>::value)
178  return MPI_C_BOOL;
179 }
180 
181 //---------------------------------------------------------------------------
182 template <typename T>
184  const graph::AdjacencyList<T>& send_data)
185 {
186  const std::vector<std::int32_t>& send_offsets = send_data.offsets();
187  const std::vector<T>& values_in = send_data.array();
188 
189  const int comm_size = MPI::size(comm);
190  assert(send_data.num_nodes() == comm_size);
191 
192  // Data size per destination rank
193  std::vector<int> send_size(comm_size);
194  std::adjacent_difference(std::next(send_offsets.begin(), +1),
195  send_offsets.end(), send_size.begin());
196 
197  // Get received data sizes from each rank
198  std::vector<int> recv_size(comm_size);
199  MPI_Alltoall(send_size.data(), 1, mpi_type<int>(), recv_size.data(), 1,
200  mpi_type<int>(), comm);
201 
202  // Compute receive offset
203  std::vector<std::int32_t> recv_offset(comm_size + 1);
204  recv_offset[0] = 0;
205  std::partial_sum(recv_size.begin(), recv_size.end(),
206  std::next(recv_offset.begin()));
207 
208  // Send/receive data
209  std::vector<T> recv_values(recv_offset[comm_size]);
210  MPI_Alltoallv(values_in.data(), send_size.data(), send_offsets.data(),
211  mpi_type<T>(), recv_values.data(), recv_size.data(),
212  recv_offset.data(), mpi_type<T>(), comm);
213 
214  return graph::AdjacencyList<T>(std::move(recv_values),
215  std::move(recv_offset));
216 }
217 //-----------------------------------------------------------------------------
218 template <typename T>
220 neighbor_all_to_all(MPI_Comm neighbor_comm,
221  const graph::AdjacencyList<T>& send_data)
222 {
223  // Get neighbor processes
224  int indegree(-1), outdegree(-2), weighted(-1);
225  MPI_Dist_graph_neighbors_count(neighbor_comm, &indegree, &outdegree,
226  &weighted);
227 
228  // Allocate memory (add '1' to handle empty case as OpenMPI fails for
229  // null pointers
230  std::vector<int> send_sizes(outdegree + 1, 0);
231  std::vector<int> recv_sizes(indegree + 1);
232  std::adjacent_difference(std::next(send_data.offsets().begin()),
233  send_data.offsets().end(), send_sizes.begin());
234  // Get receive sizes
235  MPI_Neighbor_alltoall(send_sizes.data(), 1, MPI::mpi_type<int>(),
236  recv_sizes.data(), 1, MPI::mpi_type<int>(),
237  neighbor_comm);
238 
239  // Work out recv offsets. Note use of std::prev to handle OpenMPI
240  // issue mentioned above
241  std::vector<int> recv_offsets(indegree + 1);
242  recv_offsets[0] = 0;
243  std::partial_sum(recv_sizes.begin(), std::prev(recv_sizes.end()),
244  std::next(recv_offsets.begin(), 1));
245 
246  std::vector<T> recv_data(recv_offsets[recv_offsets.size() - 1]);
247  MPI_Neighbor_alltoallv(
248  send_data.array().data(), send_sizes.data(), send_data.offsets().data(),
249  MPI::mpi_type<T>(), recv_data.data(), recv_sizes.data(),
250  recv_offsets.data(), MPI::mpi_type<T>(), neighbor_comm);
251 
252  return graph::AdjacencyList<T>(std::move(recv_data), std::move(recv_offsets));
253 }
254 //---------------------------------------------------------------------------
255 
256 } // namespace dolfinx::MPI
A duplicate MPI communicator and manage lifetime of the communicator.
Definition: MPI.h:32
MPI_Comm comm() const noexcept
Return the underlying MPI_Comm object.
Definition: MPI.cpp:72
~Comm()
Destructor (frees wrapped communicator)
Definition: MPI.cpp:38
Comm(MPI_Comm comm, bool duplicate=true)
Duplicate communicator and wrap duplicate.
Definition: MPI.cpp:11
This class provides a static adjacency list data structure. It is commonly used to store directed gra...
Definition: AdjacencyList.h:47
const std::vector< std::int32_t > & offsets() const
Offset for each node in array() (const version)
Definition: AdjacencyList.h:153
std::int32_t num_nodes() const
Get the number of nodes.
Definition: AdjacencyList.h:115
const std::vector< T > & array() const
Return contiguous array of links for all nodes (const version)
Definition: AdjacencyList.h:147
MPI support functionality.
Definition: MPI.h:27
graph::AdjacencyList< T > neighbor_all_to_all(MPI_Comm neighbor_comm, const graph::AdjacencyList< T > &send_data)
Neighborhood all-to-all. Send data to neighbors. Send in_values[n0] to neighbor process n0 and receiv...
Definition: MPI.h:220
std::vector< int > compute_graph_edges(MPI_Comm comm, const std::set< int > &edges)
Definition: MPI.cpp:89
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition: MPI.cpp:74
int size(MPI_Comm comm)
Return size of the group (number of processes) associated with the communicator.
Definition: MPI.cpp:82
constexpr int index_owner(int size, std::size_t index, std::size_t N)
Return which process owns index (inverse of local_range)
Definition: MPI.h:132
constexpr MPI_Datatype mpi_type()
MPI Type.
Definition: MPI.h:155
std::tuple< std::vector< int >, std::vector< int > > neighbors(MPI_Comm neighbor_comm)
Definition: MPI.cpp:110
graph::AdjacencyList< T > all_to_all(MPI_Comm comm, const graph::AdjacencyList< T > &send_data)
Send in_values[p0] to process p0 and receive values from process p1 in out_values[p1].
Definition: MPI.h:183
constexpr std::array< std::int64_t, 2 > local_range(int rank, std::int64_t N, int size)
Return local range for given process, splitting [0, N - 1] into size() portions of almost equal size.
Definition: MPI.h:109
Definition: MPI.h:150