mpi_particle_distribute.h
Go to the documentation of this file.
1 /*
2 This file is part of the Ristra portage project.
3 Please see the license file at the root of this repository, or at:
4  https://github.com/laristra/portage/blob/master/LICENSE
5 */
6 
7 #ifndef MPI_PARTICLE_DISTRIBUTE_H_
8 #define MPI_PARTICLE_DISTRIBUTE_H_
9 
10 #ifdef PORTAGE_ENABLE_MPI
11 
12 #include <cassert>
13 #include <algorithm>
14 #include <numeric>
15 #include <memory>
16 #include <vector>
17 
20 #include "portage/support/weight.h"
21 #include "wonton/support/Point.h"
22 
23 #include "mpi.h"
24 
29 using Wonton::Point;
30 
31 namespace Portage {
32 
37 template<int dim>
38 class MPI_Particle_Distribute {
39 public:
40 
44  explicit MPI_Particle_Distribute(Wonton::MPIExecutor_type const *mpiexecutor) {
45  assert(mpiexecutor);
46  comm_ = mpiexecutor->mpicomm;
47  }
48 
49 
53  ~MPI_Particle_Distribute() = default;;
54 
55 
59  struct comm_info_t {
61  std::vector<int> source_num;
63  int new_num = 0;
65  std::vector<int> send_counts;
67  std::vector<int> recv_counts;
68  };
69 
70 
87  template<class SourceSwarm, class SourceState, class TargetSwarm, class TargetState>
88  void distribute(SourceSwarm& source_swarm, SourceState& source_state,
89  TargetSwarm& target_swarm, TargetState& target_state,
90  Portage::vector<std::vector<std::vector<double>>>& smoothing_lengths,
91  Portage::vector<Point<dim>>& source_extents,
92  Portage::vector<Point<dim>>& target_extents,
95  Meshfree::WeightCenter center = Meshfree::WeightCenter::Gather) {
96  // Get the MPI communicator size and rank information
97  int nb_ranks, rank;
98  MPI_Comm_size(comm_, &nb_ranks);
99  MPI_Comm_rank(comm_, &rank);
100 
101  assert(dim == source_swarm.space_dimension());
102  assert(dim == target_swarm.space_dimension());
103 
104  int nb_target_points = target_swarm.num_particles(Wonton::PARALLEL_OWNED);
105  int nb_source_points = source_swarm.num_particles(Wonton::PARALLEL_OWNED);
106 
107 #ifdef DEBUG
108  if (center == Meshfree::WeightCenter::Gather) {
109  assert(smoothing_lengths.size() == unsigned(nb_target_points));
110  assert(target_extents.size() == unsigned(nb_target_points));
111  assert(kernel_types.size() == unsigned(nb_target_points));
112  assert(geom_types.size() == unsigned(nb_target_points));
113  } else if (center == Meshfree::WeightCenter::Scatter) {
114  assert(smoothing_lengths.size() == unsigned(nb_source_points));
115  assert(source_extents.size() == unsigned(nb_source_points));
116  assert(kernel_types.size() == unsigned(nb_source_points));
117  assert(geom_types.size() == unsigned(nb_source_points));
118  }
119 #endif
120 
121  /**************************************************************************
122  * Step 1: Compute bounding box for target swarm based on weight center *
123  * for the current rank, and put it in a vector that will be *
124  * used later to store the target bounding box for each rank *
125  **************************************************************************/
126 
127  std::vector<double> targetBoundingBoxes(2 * dim * nb_ranks);
128 
129  for (int i = 0; i < 2 * dim; i += 2) {
130  targetBoundingBoxes[2 * dim * rank + i + 0] = std::numeric_limits<double>::max();
131  targetBoundingBoxes[2 * dim * rank + i + 1] = -std::numeric_limits<double>::max();
132  }
133 
134  for (int c = 0; c < nb_target_points; ++c) {
135  Point<dim> coord = target_swarm.get_particle_coordinates(c);
136  Point<dim> ext;
137  if (center == Meshfree::WeightCenter::Gather) {
138  ext = target_extents[c];
139  }
140  for (int k = 0; k < dim; ++k) {
141  double val0 = 0., val1 = 0.;
142  if (center == Meshfree::WeightCenter::Gather) {
143  val0 = coord[k] - ext[k];
144  val1 = coord[k] + ext[k];
145  } else if (center == Meshfree::WeightCenter::Scatter) {
146  val0 = coord[k];
147  val1 = coord[k];
148  }
149  if (val0 < targetBoundingBoxes[2 * dim * rank + 2 * k])
150  targetBoundingBoxes[2 * dim * rank + 2 * k] = val0;
151 
152  if (val1 > targetBoundingBoxes[2 * dim * rank + 2 * k + 1])
153  targetBoundingBoxes[2 * dim * rank + 2 * k + 1] = val1;
154  }
155  }// for c
156 
157  /**************************************************************************
158  * Step 2: Broadcast the target bounding boxes so that each *
159  * rank knows the bounding boxes for all ranks *
160  **************************************************************************/
161  for (int i = 0; i < nb_ranks; i++)
162  MPI_Bcast(&(targetBoundingBoxes[0]) + 2 * dim * i, 2 * dim, MPI_DOUBLE, i, comm_);
163 
164 #ifdef DEBUG_MPI
165  std::cout << "Target boxes: ";
166  for (int i=0; i<2*dim*nb_ranks; i++) std::cout << targetBoundingBoxes[i] << " ";
167  std::cout << std::endl;
168 #endif
169 
170  /**************************************************************************
171  * Step 3: Collect the source particles on the current rank that *
172  * lie in the target bounding box for each rank in the *
173  * communicator *
174  **************************************************************************/
175  std::vector<bool> sendFlags(nb_ranks, false);
176  std::vector<std::vector<int>> sourcePtsToSend(nb_ranks);
177  std::vector<int> sourcePtsToSendSize(nb_ranks);
178 
179  for (int i = 0; i < nb_ranks; ++i) {
180  if (i == rank)
181  continue;
182  else {
183  double min[dim], max[dim];
184 
185  for (int k = 0; k < dim; ++k) {
186  min[k] = targetBoundingBoxes[2 * dim * i + 2 * k];
187  max[k] = targetBoundingBoxes[2 * dim * i + 2 * k + 1];
188  }
189 
190  for (int c = 0; c < nb_source_points; ++c) {
191  Point<dim> coord = source_swarm.get_particle_coordinates(c);
192  Point<dim> ext;
193  if (center == Meshfree::WeightCenter::Scatter) {
194  ext = source_extents[c];
195  }
196  bool thisPt = true;
197  for (int k = 0; k < dim; ++k) {
198  double val0 = 0., val1 = 0.;
199 
200  if (center == Meshfree::WeightCenter::Gather) {
201  val0 = coord[k];
202  val1 = coord[k];
203  } else if (center == Meshfree::WeightCenter::Scatter) {
204  val0 = coord[k] - ext[k];
205  val1 = coord[k] + ext[k];
206  }
207 
208  //check if the coordinates or the bnds of the current
209  //source point either inside or intersecting with the
210  //bounding box of the target swarm.
211  thisPt = thisPt && (val0 <= max[k] && val1 >= min[k]);
212  }
213 
214  if (thisPt) sourcePtsToSend[i].push_back(c);
215  }
216  }
217 
218  sourcePtsToSendSize[i] = sourcePtsToSend[i].size();
219  sendFlags[i] = not sourcePtsToSend[i].empty();
220  }
221 
222  /**************************************************************************
223  * Step 4: Set up communication info and collect coordinate data for *
224  * source particles that need to be sent to other ranks *
225  **************************************************************************/
226  comm_info_t src_info;
227  setInfo(&src_info, nb_ranks, sendFlags, sourcePtsToSendSize);
228 
229  std::vector<std::vector<double>> sourceSendCoords(nb_ranks);
230  for (int i = 0; i < nb_ranks; ++i) {
231  if ((!sendFlags[i]) || (i == rank))
232  continue;
233  else {
234  for (int j = 0; j < sourcePtsToSendSize[i]; ++j) {
235  Point<dim> coord = source_swarm.get_particle_coordinates(sourcePtsToSend[i][j]);
236  for (int d = 0; d < dim; ++d)
237  sourceSendCoords[i].push_back(coord[d]);
238  }
239  }
240  }
241 
242  //move this coordinate data
243  std::vector<double> sourceRecvCoords(src_info.new_num * dim);
244  moveField<double>(&src_info, rank, nb_ranks, MPI_DOUBLE, dim, sourceSendCoords, &sourceRecvCoords);
245 
246  // update local source particle list with received new particles
247  std::vector<Point<dim>> RecvCoords;
248  for (int i = 0; i < src_info.new_num; ++i) {
249  Point<dim> coord;
250  for (int d = 0; d < dim; ++d)
251  coord[d] = sourceRecvCoords[dim * i + d];
252  RecvCoords.push_back(coord);
253  }
254  source_swarm.extend_particle_list(RecvCoords);
255 
256  /**************************************************************************
257  * Step 5: Set up communication info and collect smoothing length data *
258  * for source particles that need to be sent to other ranks for *
259  * the Scatter scheme *
260  ***************************************************************************/
261  if (center == Meshfree::WeightCenter::Scatter) {
262  //collect smoothing length sizes and get max
263  std::vector<std::vector<double>> h0 = smoothing_lengths[0];
264  int smlen_dim = h0[0].size();
265  std::vector<int> smoothing_length_sizes(nb_source_points);
266  int max_slsize = 0;
267  for (int i = 0; i < nb_source_points; i++) {
268  std::vector<std::vector<double>> h = smoothing_lengths[i];
269  smoothing_length_sizes[i] = h.size();
270  max_slsize = std::max<int>(smoothing_length_sizes[i], max_slsize);
271  for (int j = 0; j < smoothing_length_sizes[i]; j++)
272  assert(h[j].size() == unsigned(smlen_dim));
273  }
274 
275  //-----------------------------------------------
276  //communicate smoothing length sizes
277  std::vector<std::vector<int>> sourceSendSmoothSizes(nb_ranks);
278  for (int i = 0; i < nb_ranks; ++i) {
279  if ((!sendFlags[i]) || (i == rank))
280  continue;
281  else {
282  for (int j = 0; j < sourcePtsToSendSize[i]; ++j) {
283  int smlensize = smoothing_length_sizes[sourcePtsToSend[i][j]];
284  sourceSendSmoothSizes[i].push_back(smlensize);
285  }
286  }
287  }
288  //move this smoothing length size data
289  std::vector<int> sourceRecvSmoothSizes(src_info.new_num);
290  moveField<int>(&src_info, rank, nb_ranks, MPI_INT, 1,
291  sourceSendSmoothSizes, &sourceRecvSmoothSizes);
292 
293  // update local source particle list with received new particles
294  for (int i = 0; i < src_info.new_num; ++i) {
295  int smlensize = sourceRecvSmoothSizes[i];
296  smoothing_length_sizes.push_back(smlensize);
297  }
298 
299  // create cumulative received sizes
300  std::vector<int> recvSLSizeCum(src_info.new_num, 0);
301  for (int i = 1; i < src_info.new_num; ++i) recvSLSizeCum[i] += recvSLSizeCum[i - 1];
302 
303  //-----------------------------------------------
304  //communicate smoothing_lengths
305  //this is inefficient if doing facets and there is large variation in number of facets
306  std::vector<std::vector<double>> sourceSendSmoothLengths(nb_ranks);
307  for (int i = 0; i < nb_ranks; ++i) {
308  if ((!sendFlags[i]) || (i == rank))
309  continue;
310  else {
311  for (int j = 0; j < sourcePtsToSendSize[i]; ++j) {
312  std::vector<std::vector<double>> smlen(max_slsize, std::vector<double>(smlen_dim, 0.));
313  std::vector<std::vector<double>> h = smoothing_lengths[sourcePtsToSend[i][j]];
314  std::copy(h.begin(), h.end(), smlen.begin());
315  for (int k = 0; k < max_slsize; ++k)
316  for (int d = 0; d < smlen_dim; d++)
317  sourceSendSmoothLengths[i].push_back(smlen[k][d]);
318  }
319  }
320  }
321  //move this smoothing length data
322  std::vector<double> sourceRecvSmoothLengths(src_info.new_num * max_slsize * smlen_dim);
323  moveField<double>(&src_info, rank, nb_ranks, MPI_DOUBLE, max_slsize * smlen_dim,
324  sourceSendSmoothLengths, &sourceRecvSmoothLengths);
325 
326  // update local source particle list with received new particles
327  for (int i = 0; i < src_info.new_num; ++i) {
328  std::vector<std::vector<double>> smlen
329  (smoothing_length_sizes[nb_source_points + i], std::vector<double>(smlen_dim));
330  for (int k = 0; k < smoothing_length_sizes[nb_source_points + i]; k++)
331  for (int d = 0; d < smlen_dim; ++d)
332  smlen[k][d] = sourceRecvSmoothLengths[recvSLSizeCum[i] + k * smlen_dim + d];
333 
334  smoothing_lengths.push_back(smlen);
335  }
336 
337  //-----------------------------------------------
338  //communicate extents
339  if (center == Meshfree::WeightCenter::Scatter) {
340  std::vector<std::vector<double>> sourceSendExtents(nb_ranks);
341  for (int i = 0; i < nb_ranks; ++i) {
342  if ((!sendFlags[i]) || (i == rank))
343  continue;
344  else {
345  for (int j = 0; j < sourcePtsToSendSize[i]; ++j) {
346  Point<dim> ext = source_extents[sourcePtsToSend[i][j]];
347  for (int d = 0; d < dim; ++d)
348  sourceSendExtents[i].push_back(ext[d]);
349  }
350  }
351  }
352  //move this extents data
353  std::vector<double> sourceRecvExtents(src_info.new_num * dim);
354  moveField<double>(&src_info, rank, nb_ranks, MPI_DOUBLE, dim,
355  sourceSendExtents, &sourceRecvExtents);
356 
357  // update local source particle list with received new particles
358  for (int i = 0; i < src_info.new_num; ++i) {
359  Point<dim> ext;
360  for (int d = 0; d < dim; ++d)
361  ext[d] = sourceRecvExtents[dim * i + d];
362 
363  source_extents.push_back(ext);
364  }
365  }
366 
367  //-----------------------------------------------
368  //communicate kernel types
369  std::vector<std::vector<int>> sourceSendKernels(nb_ranks);
370  for (int i = 0; i < nb_ranks; ++i) {
371  if ((!sendFlags[i]) || (i == rank))
372  continue;
373  else {
374  for (int j = 0; j < sourcePtsToSendSize[i]; ++j) {
375  sourceSendKernels[i].push_back(kernel_types[sourcePtsToSend[i][j]]);
376  }
377  }
378  }
379  //move this kernel type data
380  std::vector<int> sourceRecvKernels(src_info.new_num);
381  moveField<int>(&src_info, rank, nb_ranks, MPI_INT, 1,
382  sourceSendKernels, &sourceRecvKernels);
383 
384  // update local source swarm with received kernel types
385  for (int i = 0; i < src_info.new_num; ++i) {
386  kernel_types.push_back(static_cast<Meshfree::Weight::Kernel>(sourceRecvKernels[i]));
387  }
388 
389  //-----------------------------------------------
390  //communicate geom_types
391  std::vector<std::vector<int>> sourceSendGeoms(nb_ranks);
392  for (int i = 0; i < nb_ranks; ++i) {
393  if ((!sendFlags[i]) || (i == rank))
394  continue;
395  else {
396  for (int j = 0; j < sourcePtsToSendSize[i]; ++j) {
397  sourceSendGeoms[i].push_back(geom_types[sourcePtsToSend[i][j]]);
398  }
399  }
400  }
401  //move this geom type data
402  std::vector<int> sourceRecvGeoms(src_info.new_num);
403  moveField<int>(&src_info, rank, nb_ranks, MPI_INT, 1,
404  sourceSendGeoms, &sourceRecvGeoms);
405 
406  // update local source swarm with received geom types
407  for (int i = 0; i < src_info.new_num; ++i) {
408  geom_types.push_back(static_cast<Meshfree::Weight::Geometry>(sourceRecvGeoms[i]));
409  }
410  }
411  /**************************************************************************
412  * Step 6: Collect integer field data from source swarm to be sent *
413  * to other ranks *
414  **************************************************************************/
415  auto int_field_names = source_state.template get_field_names<int>();
416  int const num_int_fields = int_field_names.size();
417  for (int nvars = 0; nvars < num_int_fields; ++nvars) {
418  // Get field data from source state
419  auto& srcdata = source_state.get_field_int(int_field_names[nvars]);
420 
421  // Collect field data for source particles that need to be sent to other ranks
422  std::vector<std::vector<int>> sourceSendData(nb_ranks);
423  for (int i = 0; i < nb_ranks; ++i) {
424  if ((!sendFlags[i]) || (i == rank))
425  continue;
426  else {
427  for (int j = 0; j < sourcePtsToSendSize[i]; ++j) {
428  int sid = sourcePtsToSend[i][j];
429  sourceSendData[i].push_back(srcdata[sid]);
430  }
431  }
432  }
433 
434  //move this field data
435  std::vector<int> sourceRecvData(src_info.new_num);
436  moveField<int>(&src_info, rank, nb_ranks, MPI_INT, 1, sourceSendData, &sourceRecvData);
437 
438  //update local source field data with the received data
439  {
440  vector<int> recvtmp(sourceRecvData);
441  source_state.extend_field(int_field_names[nvars], recvtmp);
442  }
443  }
444 
445  /**************************************************************************
446  * Step 7: Collect double field data from source swarm to be sent *
447  * to other ranks *
448  **************************************************************************/
449  auto dbl_field_names = source_state.template get_field_names<double>();
450  int const num_dbl_fields = dbl_field_names.size();
451  for (int nvars = 0; nvars < num_dbl_fields; ++nvars) {
452  // Get field data from source state
453  auto& srcdata = source_state.get_field(dbl_field_names[nvars]);
454 
455  // Collect field data for source particles that need to be sent to other ranks
456  std::vector<std::vector<double>> sourceSendData(nb_ranks);
457  for (int i = 0; i < nb_ranks; ++i) {
458  if ((!sendFlags[i]) || (i == rank))
459  continue;
460  else {
461  for (int j = 0; j < sourcePtsToSendSize[i]; ++j) {
462  int sid = sourcePtsToSend[i][j];
463  sourceSendData[i].push_back(srcdata[sid]);
464  }
465  }
466  }
467 
468  //move this field data
469  std::vector<double> sourceRecvData(src_info.new_num);
470  moveField<double>(&src_info, rank, nb_ranks, MPI_DOUBLE, 1, sourceSendData, &sourceRecvData);
471 
472  //update local source field data with the received data
473  {
474  vector<double> recvtmp(sourceRecvData);
475  source_state.extend_field(dbl_field_names[nvars], recvtmp);
476  }
477  }
478 
479 
480  } // distribute
481 
482 private:
483 
484  MPI_Comm comm_ = MPI_COMM_NULL;
485 
493  void setInfo(comm_info_t *info,
494  const int nb_ranks,
495  const std::vector<bool>& sendFlags,
496  const std::vector<int>& sourceNum) {
497  // Set my counts of all entities and owned entities
498  info->source_num.resize(nb_ranks);
499 
500  // Each rank will tell each other rank how many indexes it is going to send it
501  info->send_counts.resize(nb_ranks);
502  info->recv_counts.resize(nb_ranks);
503 
504  for (int i = 0; i < nb_ranks; i++) {
505  info->source_num[i] = sourceNum[i];
506  info->send_counts[i] = sendFlags[i] ? info->source_num[i] : 0;
507  }
508 
509  MPI_Alltoall(&(info->send_counts[0]), 1, MPI_INT,
510  &(info->recv_counts[0]), 1, MPI_INT, comm_);
511 
512  // Compute the total number of indexes this rank will receive from all ranks
513  for (int i = 0; i < nb_ranks; i++)
514  info->new_num += info->recv_counts[i];
515  } // setInfo
516 
528  template<typename T>
529  void moveField(comm_info_t *info,
530  int rank, int nb_ranks,
531  const MPI_Datatype datatype, const int nvals,
532  const std::vector<std::vector<T>>& sourceData,
533  std::vector<T> *newData) {
534  // Each rank will do a non-blocking receive from each rank from
535  // which it will receive data values
536  int writeOffset = 0;
537  std::vector<MPI_Request> requests;
538 
539  for (int i = 0; i < nb_ranks; i++) {
540  if ((i != rank) && (info->recv_counts[i] > 0)) {
541  MPI_Request request;
542  MPI_Irecv((void *) &((*newData)[writeOffset]),
543  info->recv_counts[i] * nvals, datatype, i,
544  MPI_ANY_TAG, comm_, &request);
545  requests.push_back(request);
546  }
547  writeOffset += info->recv_counts[i] * nvals;
548  }
549  assert(writeOffset == info->new_num * nvals);
550 
551  // Each rank will send its data values to appropriate ranks
552  for (int i = 0; i < nb_ranks; i++) {
553  if ((i != rank) && (info->send_counts[i] > 0)) {
554  MPI_Send((void *) &(sourceData[i][0]),
555  info->send_counts[i] * nvals, datatype, i, 0, comm_);
556  }
557  }
558 
559  // Wait for all receives to complete
560  if (not requests.empty()) {
561  std::vector<MPI_Status> statuses(requests.size());
562  MPI_Waitall(requests.size(), &(requests[0]), &(statuses[0]));
563  }
564 
565  } // moveField
566 
567 
568 }; // MPI_Particle_Distribute
569 } // namespace Portage
570 
571 #endif // PORTAGE_ENABLE_MPI
572 
573 #endif // MPI_Particle_Distribute
std::vector< T > vector
Definition: portage.h:238
Definition: coredriver.h:42