mpi_bounding_boxes.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_BOUNDING_BOXES_H_
8 #define MPI_BOUNDING_BOXES_H_
9 
10 
11 #ifdef PORTAGE_ENABLE_MPI
12 
13 #include <cassert>
14 #include <algorithm>
15 #include <numeric>
16 #include <memory>
17 #include <unordered_map>
18 #include <map>
19 #include <vector>
20 #include <set>
21 
23 #include "wonton/support/Point.h"
24 #include "wonton/state/state_vector_uni.h"
25 #include "mpi.h"
26 
32 namespace Portage {
33 
34 using Wonton::Point;
35 using Wonton::GID_t;
36 using Wonton::to_MPI_Datatype;
37 
44 class MPI_Bounding_Boxes {
45  public:
46 
50  MPI_Bounding_Boxes(Wonton::MPIExecutor_type const *mpiexecutor) {
51  assert(mpiexecutor);
52  comm_ = mpiexecutor->mpicomm;
53  }
54 
55 
59  ~MPI_Bounding_Boxes() = default;
60 
61 
65  struct comm_info_t {
67  int sourceNum = 0, sourceNumOwned = 0;
69  int newNum = 0, newNumOwned = 0;
71  std::vector<int> sendCounts {}, sendOwnedCounts {};
73  std::vector<int> recvCounts {}, recvOwnedCounts {};
74  };
75 
76 
84  template <class Source_Mesh, class Target_Mesh>
85  bool is_bob_hungry(const Source_Mesh &source_mesh,
86  const Target_Mesh &target_mesh)
87  {
88  // Get the MPI communicator size and rank information
89  int commSize, commRank;
90  MPI_Comm_size(comm_, &commSize);
91  MPI_Comm_rank(comm_, &commRank);
92 
93  dim_ = source_mesh.space_dimension();
94  assert(dim_ == target_mesh.space_dimension());
95 
96  // sendFlags, which partitions to send data
97  // this is computed via intersection of whole partition bounding boxes
98  std::vector<bool> sendFlags(commSize);
99  compute_sendflags(source_mesh, target_mesh, sendFlags);
100 
101  std::vector<int> sendFlagsInt(commSize);
102  for (int i=0; i<commSize; ++i) sendFlagsInt[i]=sendFlags[i]?1:0;
103 
104  // Each rank will tell each other rank if it is going to send it
105  std::vector<int> recvFlags(commSize);
106  MPI_Alltoall(&(sendFlagsInt[0]), 1, MPI_INT,
107  &(recvFlags[0]), 1, MPI_INT, comm_);
108 
109  // loop over the partitions
110  for (int i=0; i<commSize; ++i)
111  // if any other other partition sends me data, we need to redistribute
112  if (i!=commRank && recvFlags[i])
113  return true;
114 
115  // the fall through case means no redistribution
116  return false;
117  }
118 
119 
126  template <class Source_Mesh, class Target_Mesh>
127  bool is_redistribution_needed(const Source_Mesh &source_mesh,
128  const Target_Mesh &target_mesh)
129  {
130  // does this partition need data from an other partition
131  bool r = is_bob_hungry(source_mesh, target_mesh);
132 
133  // convert to an int
134  int ir = r ? 1 : 0;
135 
136  // allocate the result
137  int result;
138 
139  // do the MPI_ALLReduce to aggregate the results
140  MPI_Allreduce(&ir, &result, 1, MPI_INT, MPI_LOR, comm_);
141 
142  return result;
143  }
144 
145 
146 
156  template <class Source_Mesh, class Source_State, class Target_Mesh, class Target_State>
157  void distribute(Source_Mesh &source_mesh_flat, Source_State &source_state_flat,
158  Target_Mesh &target_mesh, Target_State &target_state)
159  {
160  // Get the MPI communicator size and rank information
161  int commSize, commRank;
162  MPI_Comm_size(comm_, &commSize);
163  MPI_Comm_rank(comm_, &commRank);
164 
165  int dim = dim_ = source_mesh_flat.space_dimension();
166  assert(dim == target_mesh.space_dimension());
167 
168  // sendFlags, which partitions to send data
169  // this is computed via intersection of whole partition bounding boxes
170  std::vector<bool> sendFlags(commSize);
171  compute_sendflags(source_mesh_flat, target_mesh, sendFlags);
172 
173  // set counts for cells
174  comm_info_t cellInfo;
175  int sourceNumOwnedCells = source_mesh_flat.num_owned_cells();
176  int sourceNumCells = sourceNumOwnedCells + source_mesh_flat.num_ghost_cells();
177  setSendRecvCounts(&cellInfo, commSize, sendFlags,sourceNumCells, sourceNumOwnedCells);
178 
179  // set counts for nodes
180  comm_info_t nodeInfo;
181  int sourceNumOwnedNodes = source_mesh_flat.num_owned_nodes();
182  int sourceNumNodes = sourceNumOwnedNodes + source_mesh_flat.num_ghost_nodes();
183  setSendRecvCounts(&nodeInfo, commSize, sendFlags,sourceNumNodes, sourceNumOwnedNodes);
184 
186  // always distributed
188 
189  // SEND GLOBAL CELL IDS
190 
191  std::vector<GID_t>& sourceCellGlobalIds = source_mesh_flat.get_global_cell_ids();
192  std::vector<GID_t> distributedCellGlobalIds(cellInfo.newNum);
193  sendField(cellInfo, commRank, commSize, to_MPI_Datatype<GID_t>(), 1,
194  sourceCellGlobalIds, &distributedCellGlobalIds);
195 
196  // SEND GLOBAL NODE IDS
197  std::vector<GID_t>& sourceNodeGlobalIds = source_mesh_flat.get_global_node_ids();
198  std::vector<GID_t> distributedNodeGlobalIds(nodeInfo.newNum);
199  sendField(nodeInfo, commRank, commSize, to_MPI_Datatype<GID_t>(), 1,
200  sourceNodeGlobalIds, &distributedNodeGlobalIds);
201 
202  // Using the post distribution global id's, compress the data so that each
203  // global id appears only once. The trick here is to get the ghosts correct.
204  // In the flat mesh, after distribution, an entity that was owned by any
205  // partition is considered owned in the flat mesh. Likewise, any entity that
206  // appears only as a ghost will be a ghost in the flat mesh
207  compress_with_ghosts(distributedCellGlobalIds, cellInfo.newNumOwned,
208  distributedCellIds_, flatCellGlobalIds_, flatCellNumOwned_);
209  compress_with_ghosts(distributedNodeGlobalIds, nodeInfo.newNumOwned,
210  distributedNodeIds_, flatNodeGlobalIds_, flatNodeNumOwned_);
211 
212  // create the map from cell global id to flat cell index
213  create_gid_to_flat_map(flatCellGlobalIds_, gidToFlatCellId_);
214  create_gid_to_flat_map(flatNodeGlobalIds_, gidToFlatNodeId_);
215 
216  // SEND NODE COORDINATES
217  std::vector<double>& sourceCoords = source_mesh_flat.get_coords();
218  std::vector<double> distributedCoords(dim*nodeInfo.newNum);
219  sendField(nodeInfo, commRank, commSize, MPI_DOUBLE, dim,
220  sourceCoords, &distributedCoords);
221 
222  // merge and set coordinates in the flat mesh
223  merge_duplicate_data(distributedCoords, distributedNodeIds_, sourceCoords, dim_);
224 
225 
226 
228  // 2D distributed
230 
231  if (dim == 2)
232  {
233 
234  // send cell node counts
235  std::vector<int>& sourceCellNodeCounts = source_mesh_flat.get_cell_node_counts();
236  std::vector<int> distributedCellNodeCounts(cellInfo.newNum);
237  sendField(cellInfo, commRank, commSize, MPI_INT, 1,
238  sourceCellNodeCounts, &distributedCellNodeCounts);
239 
240  // merge and set cell node counts
241  merge_duplicate_data(distributedCellNodeCounts, distributedCellIds_, sourceCellNodeCounts);
242 
243  // mesh data references
244  std::vector<int>& sourceCellNodeOffsets = source_mesh_flat.get_cell_node_offsets();
245  std::vector<int>& sourceCellToNodeList = source_mesh_flat.get_cell_to_node_list();
246 
247  int sizeCellToNodeList = sourceCellToNodeList.size();
248  int sizeOwnedCellToNodeList = (
249  sourceNumCells == sourceNumOwnedCells ? sizeCellToNodeList :
250  sourceCellNodeOffsets[sourceNumOwnedCells]);
251 
252  comm_info_t cellToNodeInfo;
253  setSendRecvCounts(&cellToNodeInfo, commSize, sendFlags,
254  sizeCellToNodeList, sizeOwnedCellToNodeList);
255 
256  // send cell to node lists
257  std::vector<GID_t> distributedCellToNodeList(cellToNodeInfo.newNum);
258  sendField(cellToNodeInfo, commRank, commSize, to_MPI_Datatype<GID_t>(), 1,
259  to_gid(sourceCellToNodeList, sourceNodeGlobalIds), &distributedCellToNodeList);
260 
261 
262  // merge and map cell node lists
263  merge_duplicate_lists(distributedCellToNodeList, distributedCellNodeCounts,
264  distributedCellIds_, gidToFlatNodeId_, sourceCellToNodeList);
265 
266  }
267 
268 
270  // 3D distributed
272 
273  if (dim == 3)
274  {
275 
276  int sourceNumOwnedFaces = source_mesh_flat.num_owned_faces();
277  int sourceNumFaces = sourceNumOwnedFaces + source_mesh_flat.num_ghost_faces();
278 
279  comm_info_t faceInfo;
280  setSendRecvCounts(&faceInfo, commSize, sendFlags,sourceNumFaces, sourceNumOwnedFaces);
281 
282  // SEND GLOBAL FACE IDS
283  std::vector<GID_t>& sourceFaceGlobalIds = source_mesh_flat.get_global_face_ids();
284  std::vector<GID_t> distributedFaceGlobalIds(faceInfo.newNum);
285  sendField(faceInfo, commRank, commSize, MPI_LONG_LONG, 1,
286  sourceFaceGlobalIds, &distributedFaceGlobalIds);
287 
288  // Create map from distributed gid's to distributed index and flat indices
289  compress_with_ghosts(distributedFaceGlobalIds, faceInfo.newNumOwned,
290  distributedFaceIds_, flatFaceGlobalIds_, flatFaceNumOwned_);
291 
292  // create the map from face global id to flat cell index
293  create_gid_to_flat_map(flatFaceGlobalIds_, gidToFlatFaceId_);
294 
295  // mesh data references
296  std::vector<int>& sourceCellFaceOffsets = source_mesh_flat.get_cell_face_offsets();
297  std::vector<int>& sourceCellToFaceList = source_mesh_flat.get_cell_to_face_list();
298 
299  int sizeCellToFaceList = sourceCellToFaceList.size();
300  int sizeOwnedCellToFaceList = (
301  sourceNumCells == sourceNumOwnedCells ? sizeCellToFaceList :
302  sourceCellFaceOffsets[sourceNumOwnedCells]);
303 
304  comm_info_t cellToFaceInfo;
305  setSendRecvCounts(&cellToFaceInfo, commSize, sendFlags,
306  sizeCellToFaceList, sizeOwnedCellToFaceList);
307 
308  // SEND NUMBER OF FACES FOR EACH CELL
309  std::vector<int>& sourceCellFaceCounts = source_mesh_flat.get_cell_face_counts();
310  std::vector<int> distributedCellFaceCounts(cellInfo.newNum);
311  sendField(cellInfo, commRank, commSize, MPI_INT, 1,
312  sourceCellFaceCounts, &distributedCellFaceCounts);
313 
314  // merge and set cell face counts
315  merge_duplicate_data( distributedCellFaceCounts, distributedCellIds_, sourceCellFaceCounts);
316 
317  // SEND CELL-TO-FACE MAP
318  // map the cell face list vector to gid's
319  std::vector<GID_t> sourceCellToFaceList_ = to_gid(sourceCellToFaceList, sourceFaceGlobalIds);
320 
321  // For this array only, pack up face IDs + dirs and send together
322  std::vector<bool>& sourceCellToFaceDirs = source_mesh_flat.get_cell_to_face_dirs();
323  int const sourceCellToFaceListSize = sourceCellToFaceList.size();
324 
325  for (int j = 0; j < sourceCellToFaceListSize; ++j) {
326  int f = sourceCellToFaceList_[j];
327  int dir = static_cast<int>(sourceCellToFaceDirs[j]);
328  sourceCellToFaceList_[j] = (f << 1) | dir;
329  }
330 
331  std::vector<GID_t> distributedCellToFaceList(cellToFaceInfo.newNum);
332  sendField(cellToFaceInfo, commRank, commSize, MPI_LONG_LONG, 1,
333  sourceCellToFaceList_, &distributedCellToFaceList);
334 
335  // Unpack face IDs and dirs
336  std::vector<bool> distributedCellToFaceDirs(cellToFaceInfo.newNum);
337  int const distributedCellToFaceListSize = distributedCellToFaceList.size();
338 
339  for (int j = 0; j < distributedCellToFaceListSize; ++j) {
340  int fd = distributedCellToFaceList[j];
341  distributedCellToFaceList[j] = fd >> 1;
342  distributedCellToFaceDirs[j] = fd & 1;
343  }
344 
345 
346  // merge and map cell face lists
347  merge_duplicate_lists(distributedCellToFaceList, distributedCellFaceCounts,
348  distributedCellIds_, gidToFlatFaceId_, sourceCellToFaceList);
349 
350  // merge cell face directions
351  merge_duplicate_lists(distributedCellToFaceDirs, distributedCellFaceCounts,
352  distributedCellIds_, sourceCellToFaceDirs);
353 
354  // mesh data references
355  std::vector<int>& sourceFaceNodeOffsets = source_mesh_flat.get_face_node_offsets();
356  std::vector<int>& sourceFaceToNodeList = source_mesh_flat.get_face_to_node_list();
357 
358  int sizeFaceToNodeList = sourceFaceToNodeList.size();
359  int sizeOwnedFaceToNodeList = (
360  sourceNumFaces == sourceNumOwnedFaces ? sizeFaceToNodeList :
361  sourceFaceNodeOffsets[sourceNumOwnedFaces]);
362 
363  comm_info_t faceToNodeInfo;
364  setSendRecvCounts(&faceToNodeInfo, commSize, sendFlags,
365  sizeFaceToNodeList, sizeOwnedFaceToNodeList);
366 
367  // SEND NUMBER OF NODES FOR EACH FACE
368  std::vector<int>& sourceFaceNodeCounts = source_mesh_flat.get_face_node_counts();
369  std::vector<int> distributedFaceNodeCounts(faceInfo.newNum);
370  sendField(faceInfo, commRank, commSize, MPI_INT, 1,
371  sourceFaceNodeCounts, &distributedFaceNodeCounts);
372 
373  // SEND FACE-TO-NODE MAP
374  std::vector<GID_t> distributedFaceToNodeList(faceToNodeInfo.newNum);
375  sendField(faceToNodeInfo, commRank, commSize, MPI_LONG_LONG, 1,
376  to_gid(sourceFaceToNodeList, sourceNodeGlobalIds), &distributedFaceToNodeList);
377 
378  // merge and set face node counts
379  merge_duplicate_data( distributedFaceNodeCounts, distributedFaceIds_, sourceFaceNodeCounts);
380 
381  // merge and map face node lists
382  merge_duplicate_lists(distributedFaceToNodeList, distributedFaceNodeCounts,
383  distributedFaceIds_, gidToFlatNodeId_, sourceFaceToNodeList);
384 
385  // merge face global ids
386  merge_duplicate_data(distributedFaceGlobalIds, distributedFaceIds_,sourceFaceGlobalIds);
387 
388  // set counts for faces in the flat mesh
389  source_mesh_flat.set_num_owned_faces(flatFaceNumOwned_);
390 
391  }
392 
393  // SEND FIELD VALUES
394 
395  // multimaterial state info
396  int nmats = source_state_flat.num_materials();
397  comm_info_t num_mat_cells_info {};
398 
399  // Is the a multimaterial problem? If so we need to pass the cell indices
400  // in addition to the field values
401  if (nmats>0){
402  #ifdef ENABLE_DEBUG
403  std::cout << "in distribute, this a multimaterial problem with " << nmats << " materials\n";
404  #endif
405  // get the material ids across all nodes
408 
409  // set the info for the number of materials on each node
410  comm_info_t num_mats_info;
411  setSendRecvCounts(&num_mats_info, commSize, sendFlags, nmats, nmats);
412 
413  // get the sorted material ids on this node
414  std::vector<int> material_ids=source_state_flat.get_material_ids();
415 
416  // get all material ids across
417  distributedMaterialIds_.resize(num_mats_info.newNum);
418 
419  // send all materials to all nodes, num_mats_info.recvCounts is the shape
420  sendData(commRank, commSize, MPI_INT, 1, 0, num_mats_info.sourceNum, 0,
421  num_mats_info.sendCounts, num_mats_info.recvCounts,
422  material_ids, &distributedMaterialIds_
423  );
424 
426  // get the material cell shapes across all nodes
428 
429  // get the sorted material shapes on this node
430  std::vector<int> material_shapes=source_state_flat.get_material_shapes();
431 
432  // resize the post distribute material id vector
433  distributedMaterialShapes_.resize(num_mats_info.newNum);
434 
435  // send all material shapes to all nodes
436  sendData(commRank, commSize, MPI_INT, 1, 0, num_mats_info.sourceNum, 0,
437  num_mats_info.sendCounts, num_mats_info.recvCounts,
438  material_shapes, &distributedMaterialShapes_
439  );
440 
442  // get the lists of material cell ids across all nodes
444 
445  // get the total number of material cell id's on this node
446  int nmatcells = source_state_flat.num_material_cells();
447 
448  // set the info for the number of materials on each node
449  setSendRecvCounts(&num_mat_cells_info, commSize, sendFlags, nmatcells, nmatcells);
450 
451  // get the sorted material ids on this node
452  std::vector<int> material_cells=source_state_flat.get_material_cells();
453 
454  // resize the post distribute material id vector
455  distributedMaterialCells_.resize(num_mat_cells_info.newNum);
456 
457  // send material cells to all nodes, but first translate to gid
458  sendData(commRank, commSize, to_MPI_Datatype<GID_t>(), 1, 0,
459  num_mat_cells_info.sourceNum, 0, num_mat_cells_info.sendCounts,
460  num_mat_cells_info.recvCounts, to_gid(material_cells, sourceCellGlobalIds),
461  &distributedMaterialCells_
462  );
463 
465  // We need to turn the flattened material cells into a correctly shaped
466  // ragged right structure for use as the material cells in the flat
467  // state wrapper. We aren't removing duplicates yet, just concatenating by material
469 
481  // allocate the material indices
482  std::unordered_map<int,std::vector<GID_t>> material_indices;
483 
484  // reset the running counter
485  int running_counter=0;
486 
487  // loop over material ids on different nodes
488  int const distributedMaterialIdsSize = distributedMaterialIds_.size();
489 
490  for (int i = 0; i < distributedMaterialIdsSize; ++i) {
491 
492  // get the current working material
493  int mat_id = distributedMaterialIds_[i];
494 
495  // get the current number of material cells for this material
496  int nmat_cells = distributedMaterialShapes_[i];
497 
498  // get or create a reference to the correct material cell vector
499  std::vector<GID_t>& these_material_cells = material_indices[mat_id];
500 
501  // loop over the correct number of material cells
502  for (int j=0; j<nmat_cells; ++j){
503  these_material_cells.push_back(distributedMaterialCells_[running_counter++]);
504  }
505  }
506 
507  // cell material indices are added not replaced by vector so we need to
508  // start clean
509  source_state_flat.clear_material_cells();
510 
511  // merge the material cells and convert to local id (gid sort order)
512  for (auto &kv: material_indices){
513 
514  // get the material id
515  int m = kv.first;
516 
517  // compress the material ids so each gid appears only once per material
518  compress(kv.second, distributedMaterialCellIds_[m]);
519 
520  // allocate the flat material cell ids for this material
521  std::vector<int> flatMaterialCellIds;
522  flatMaterialCellIds.reserve(distributedMaterialCellIds_[m].size());
523 
524  // loop of material cell indices, converting to gid, then flat cell
525  for (auto id: distributedMaterialCellIds_[m])
526  flatMaterialCellIds.push_back(gidToFlatCellId_[kv.second[id]]);
527 
528  // add the material cells to the state manager
529  source_state_flat.mat_add_cells(kv.first, flatMaterialCellIds);
530 
531  }
532  }
533 
534  // Send and receive each field to be remapped
535  for (std::string field_name : source_state_flat.names())
536  {
537 
538  // this is a packed version of the field with copied field values and is
539  // not a pointer to the original field
540  std::vector<double> sourceField = source_state_flat.pack(field_name);
541 
542  // get the field stride
543  int sourceFieldStride = source_state_flat.get_field_stride(field_name);
544 
545  comm_info_t info;
546  if (source_state_flat.get_entity(field_name) == Entity_kind::NODE){
547  // node mesh field
548  info = nodeInfo;
549  } else if (source_state_flat.field_type(Entity_kind::CELL, field_name) == Wonton::Field_type::MESH_FIELD){
550  // mesh cell field
551  info = cellInfo;
552  } else {
553  // multi material field
554  info = num_mat_cells_info;
555  }
556 
557  // allocate storage for the new distributed data, note that this data
558  // has raw doubles and will need to be merged and type converted
559 
560  std::vector<double> distributedField(sourceFieldStride*info.newNum);
561 
562  // send the field
563  sendField(info, commRank, commSize, MPI_DOUBLE, sourceFieldStride,
564  sourceField, &distributedField);
565 
566  std::vector<double> tempDistributedField;
567 
568  if (source_state_flat.get_entity(field_name) == Entity_kind::NODE){
569 
570  // node mesh field
571  // merge duplicates, but data still is raw doubles
572  merge_duplicate_data(distributedField, distributedNodeIds_, tempDistributedField, sourceFieldStride);
573 
574  // unpack the field, has the correct data type
575  source_state_flat.unpack(field_name, tempDistributedField);
576 
577  } else if (source_state_flat.field_type(Entity_kind::CELL, field_name) == Wonton::Field_type::MESH_FIELD){
578 
579  // cell mesh field
580  // merge duplicates, but data still is raw doubles
581  merge_duplicate_data(distributedField, distributedCellIds_, tempDistributedField, sourceFieldStride);
582 
583  // unpack the field, has the correct data types
584  source_state_flat.unpack(field_name, tempDistributedField);
585 
586  } else {
587 
588  // multi material field
589  // as opposed to the preceeding two cases, the merging and type conversion
590  // are both done in the unpack routine because there is more to do
591  // getting the shapes correct.
592  source_state_flat.unpack(field_name, distributedField,
593  distributedMaterialIds_, distributedMaterialShapes_,
594  distributedMaterialCellIds_);
595  }
596  }
597 
598  // need to do this at the end of the mesh stuff, because converting to
599  // gid uses the global id's and we don't want to modify them before we are
600  // done converting the old relationships
601  // merge global ids and set in the flat mesh
602  merge_duplicate_data(distributedCellGlobalIds, distributedCellIds_, sourceCellGlobalIds);
603  merge_duplicate_data(distributedNodeGlobalIds, distributedNodeIds_, sourceNodeGlobalIds);
604 
605  // set counts for cells and nodes in the flat mesh
606  source_mesh_flat.set_num_owned_cells(flatCellNumOwned_);
607  source_mesh_flat.set_num_owned_nodes(flatNodeNumOwned_);
608 
609  // Finish initialization using redistributed data
610  source_mesh_flat.finish_init();
611 
612 
613  } // distribute
614 
615  private:
616 
617  // The communicator we are using
618  MPI_Comm comm_ = MPI_COMM_NULL;
619 
620  int dim_ = 1;
621 
622  // the number of nodes "owned" by the flat mesh. "Owned" is in quotes because
623  // a node may be "owned" by multiple partitions in the flat mesh. A node is
624  // owned by the flat mesh if it was owned by any partition.
625  int flatNodeNumOwned_ = 0;
626 
627  // the global id's of the kept nodes in the flat mesh and their indices in the
628  // distributed node global id's
629  std::vector<GID_t> flatNodeGlobalIds_ {};
630  std::vector<int> distributedNodeIds_ {};
631 
632  // maps from gid to distributed node index and flat node index
633  std::map<GID_t,int> gidToFlatNodeId_ {};
634 
635  // the number of faces "owned" by the flat mesh. "Owned" is in quotes because
636  // a face may be "owned" by multiple partitions in the flat mesh. A face is
637  // owned by the flat mesh if it was owned by any partition.
638  int flatFaceNumOwned_ = 0;
639 
640  // the global id's of the kept faces in the flat mesh and their indices in the
641  // distributed face global id's
642  std::vector<GID_t> flatFaceGlobalIds_ {};
643  std::vector<int> distributedFaceIds_ {};
644 
645  // maps from gid to distributed face index and flat face index
646  std::map<GID_t,int> gidToFlatFaceId_ {};
647 
648  // the number of cells "owned" by the flat mesh. "Owned" is in quotes because
649  // a cell may be "owned" by multiple partitions in the flat mesh. A cell is
650  // owned by the flat mesh if it was owned by any partition.
651  int flatCellNumOwned_ = 0;
652 
653  // the global id's of the kept cells in the flat mesh and their indices in the
654  // distributed cell global id's
655  std::vector<GID_t> flatCellGlobalIds_ {};
656  std::vector<int> distributedCellIds_ {};
657 
658  // maps from gid to distributed cell index and flat cell index
659  std::map<GID_t,int> gidToFlatCellId_ {};
660 
661  // vectors for distributed multimaterial data
662  std::vector<int> distributedMaterialIds_ {};
663  std::vector<int> distributedMaterialShapes_ {};
664  std::vector<GID_t> distributedMaterialCells_ {};
665 
666  // map for the distributed material cell indices
667  // for each material there is a vector of unique distributed indices
668  std::map<int, std::vector<int>> distributedMaterialCellIds_ {};
669 
678  void setSendRecvCounts(comm_info_t* info,
679  const int commSize,
680  const std::vector<bool>& sendFlags,
681  const int sourceNum,
682  const int sourceNumOwned)
683  {
684  // Set my counts of all entities and owned entities
685  info->sourceNum = sourceNum;
686  info->sourceNumOwned = sourceNumOwned;
687 
688  // Each rank will tell each other rank how many indexes it is going to send it
689  info->sendCounts.resize(commSize);
690  info->recvCounts.resize(commSize);
691  for (int i=0; i<commSize; i++)
692  info->sendCounts[i] = sendFlags[i] ? info->sourceNum : 0;
693  MPI_Alltoall(&(info->sendCounts[0]), 1, MPI_INT,
694  &(info->recvCounts[0]), 1, MPI_INT, comm_);
695 
696  // Each rank will tell each other rank how many owned indexes it is going to send it
697  info->sendOwnedCounts.resize(commSize);
698  info->recvOwnedCounts.resize(commSize);
699  for (int i=0; i<commSize; i++)
700  info->sendOwnedCounts[i] = sendFlags[i] ? info->sourceNumOwned : 0;
701  MPI_Alltoall(&(info->sendOwnedCounts[0]), 1, MPI_INT,
702  &(info->recvOwnedCounts[0]), 1, MPI_INT, comm_);
703 
704  // Compute the total number of indexes this rank will receive from all ranks
705  for (int i=0; i<commSize; i++)
706  info->newNum += info->recvCounts[i];
707  for (int i=0; i<commSize; i++)
708  info->newNumOwned += info->recvOwnedCounts[i];
709  } // setSendRecvCounts
710 
711 
723  template<typename T>
724  void sendField(const comm_info_t& info,
725  int commRank, int commSize,
726  MPI_Datatype mpiType, int stride,
727  const std::vector<T>& sourceData,
728  std::vector<T>* newData)
729  {
730  // Perform two rounds of sends: the first for owned cells, and the second for ghost cells
731  std::vector<int> recvGhostCounts(commSize);
732  std::vector<int> sendGhostCounts(commSize);
733 
734  for (int i=0; i<commSize; i++)
735  {
736  recvGhostCounts[i] = info.recvCounts[i] - info.recvOwnedCounts[i];
737  sendGhostCounts[i] = info.sendCounts[i] - info.sendOwnedCounts[i];
738  }
739 
740  sendData(commRank, commSize, mpiType, stride,
741  0, info.sourceNumOwned,
742  0,
743  info.sendOwnedCounts, info.recvOwnedCounts,
744  sourceData, newData);
745  sendData(commRank, commSize, mpiType, stride,
746  info.sourceNumOwned, info.sourceNum,
747  info.newNumOwned,
748  sendGhostCounts, recvGhostCounts,
749  sourceData, newData);
750 
751 #ifdef DEBUG_MPI
752  std::cout << "Number of values on rank " << commRank << ": " << (*newData).size() << std::endl;
753  for (int f=0; f<(*newData).size(); f++)
754  std::cout << (*newData)[f] << " ";
755  std::cout << std::endl << std::endl;
756 #endif
757  } // sendField
758 
759 
775  template<typename T>
776  void sendData(int commRank, int commSize,
777  MPI_Datatype mpiType, int stride,
778  int sourceStart, int sourceEnd,
779  int newStart,
780  const std::vector<int>& curSendCounts,
781  const std::vector<int>& curRecvCounts,
782  const std::vector<T>& sourceData,
783  std::vector<T>* newData)
784  {
785  // Each rank will do a non-blocking receive from each rank from
786  // which it will receive data values
787  int writeOffset = newStart;
788  int myOffset = 0;
789  std::vector<MPI_Request> requests;
790  for (int i=0; i<commSize; i++)
791  {
792  if ((i != commRank) && (curRecvCounts[i] > 0))
793  {
794  MPI_Request request;
795  MPI_Irecv((void *)&((*newData)[stride*writeOffset]),
796  stride*curRecvCounts[i], mpiType, i,
797  MPI_ANY_TAG, comm_, &request);
798  requests.push_back(request);
799  }
800  else if (i == commRank)
801  {
802  myOffset = writeOffset;
803  }
804  writeOffset += curRecvCounts[i];
805  }
806 
807  // Copy data values that will stay on this rank into the
808  // proper place in the new vector
809  if (curRecvCounts[commRank] > 0)
810  std::copy(sourceData.begin() + stride*sourceStart,
811  sourceData.begin() + stride*sourceEnd,
812  newData->begin() + stride*myOffset);
813 
814  // Each rank will send its data values to appropriate ranks
815  for (int i=0; i<commSize; i++)
816  {
817  if ((i != commRank) && (curSendCounts[i] > 0))
818  {
819  MPI_Send((void *)&(sourceData[stride*sourceStart]),
820  stride*curSendCounts[i], mpiType, i, 0, comm_);
821  }
822  }
823 
824  // Wait for all receives to complete
825  if (!requests.empty())
826  {
827  std::vector<MPI_Status> statuses(requests.size());
828  MPI_Waitall(requests.size(), &(requests[0]), &(statuses[0]));
829  }
830  } // sendData
831 
832 
833  template <class Source_Mesh, class Target_Mesh>
834  void compute_sendflags(Source_Mesh & source_mesh, Target_Mesh &target_mesh,
835  std::vector<bool> &sendFlags){
836 
837  // Get the MPI communicator size and rank information
838  int commSize, commRank;
839  MPI_Comm_size(comm_, &commSize);
840  MPI_Comm_rank(comm_, &commRank);
841 
842  int dim = source_mesh.space_dimension();
843 
844  // Compute the bounding box for the target mesh on this rank, and put it in an
845  // array that will later store the target bounding box for each rank
846  int targetNumOwnedCells = target_mesh.num_owned_cells();
847  std::vector<double> targetBoundingBoxes(2*dim*commSize);
848  for (int i=0; i<2*dim; i+=2)
849  {
850  targetBoundingBoxes[2*dim*commRank+i+0] = std::numeric_limits<double>::max();
851  targetBoundingBoxes[2*dim*commRank+i+1] = -std::numeric_limits<double>::max();
852  }
853  for (int c=0; c<targetNumOwnedCells; ++c)
854  {
855  std::vector<int> nodes;
856  target_mesh.cell_get_nodes(c, &nodes);
857  int cellNumNodes = nodes.size();
858  for (int j=0; j<cellNumNodes; ++j)
859  {
860  int n = nodes[j];
861  // ugly hack, since dim is not known at compile time
862  if (dim == 3)
863  {
864  Point<3> nodeCoord;
865  target_mesh.node_get_coordinates(n, &nodeCoord);
866  for (int k=0; k<dim; ++k)
867  {
868  if (nodeCoord[k] < targetBoundingBoxes[2*dim*commRank+2*k])
869  targetBoundingBoxes[2*dim*commRank+2*k] = nodeCoord[k];
870  if (nodeCoord[k] > targetBoundingBoxes[2*dim*commRank+2*k+1])
871  targetBoundingBoxes[2*dim*commRank+2*k+1] = nodeCoord[k];
872  }
873  }
874  else if (dim == 2)
875  {
876  Point<2> nodeCoord;
877  target_mesh.node_get_coordinates(n, &nodeCoord);
878  for (int k=0; k<dim; ++k)
879  {
880  if (nodeCoord[k] < targetBoundingBoxes[2*dim*commRank+2*k])
881  targetBoundingBoxes[2*dim*commRank+2*k] = nodeCoord[k];
882  if (nodeCoord[k] > targetBoundingBoxes[2*dim*commRank+2*k+1])
883  targetBoundingBoxes[2*dim*commRank+2*k+1] = nodeCoord[k];
884  }
885  }
886  } // for j
887  } // for c
888 
889  // Get the source mesh properties and cordinates
890  int sourceNumOwnedCells = source_mesh.num_owned_cells();
891 
892  // Compute the bounding box for the source mesh on this rank
893  std::vector<double> sourceBoundingBox(2*dim);
894  for (int i=0; i<2*dim; i+=2)
895  {
896  sourceBoundingBox[i+0] = std::numeric_limits<double>::max();
897  sourceBoundingBox[i+1] = -std::numeric_limits<double>::max();
898  }
899  for (int c=0; c<sourceNumOwnedCells; ++c)
900  {
901  std::vector<int> nodes;
902  source_mesh.cell_get_nodes(c, &nodes);
903  int cellNumNodes = nodes.size();
904  for (int j=0; j<cellNumNodes; ++j)
905  {
906  int n = nodes[j];
907  if (dim ==3 )
908  {
909  Point<3> nodeCoord;
910  source_mesh.node_get_coordinates(n, &nodeCoord);
911  for (int k=0; k<dim; ++k)
912  {
913  if (nodeCoord[k] < sourceBoundingBox[2*k])
914  sourceBoundingBox[2*k] = nodeCoord[k];
915  if (nodeCoord[k] > sourceBoundingBox[2*k+1])
916  sourceBoundingBox[2*k+1] = nodeCoord[k];
917  }
918  }
919  else if (dim ==2)
920  {
921  Point<2> nodeCoord;
922  source_mesh.node_get_coordinates(n, &nodeCoord);
923  for (int k=0; k<dim; ++k)
924  {
925  if (nodeCoord[k] < sourceBoundingBox[2*k])
926  sourceBoundingBox[2*k] = nodeCoord[k];
927  if (nodeCoord[k] > sourceBoundingBox[2*k+1])
928  sourceBoundingBox[2*k+1] = nodeCoord[k];
929  }
930  }
931  } // for j
932  } // for c
933 
934  // Broadcast the target bounding boxes so that each rank knows the bounding boxes for all ranks
935  for (int i=0; i<commSize; i++)
936  MPI_Bcast(&(targetBoundingBoxes[0])+i*2*dim, 2*dim, MPI_DOUBLE, i, comm_);
937 
938 
939  // Offset the source bounding boxes by a fudge factor so that we don't send source data to a rank
940  // with a target bounding box that is incident to but not overlapping the source bounding box
941  const double boxOffset = 2.0*std::numeric_limits<double>::epsilon();
942  double min2[dim], max2[dim];
943  for (int k=0; k<dim; ++k)
944  {
945  min2[k] = sourceBoundingBox[2*k]+boxOffset;
946  max2[k] = sourceBoundingBox[2*k+1]-boxOffset;
947  }
948 
949  // For each target rank with a bounding box that overlaps the bounding box for this rank's partition
950  // of the source mesh, we will send it all our source cells; otherwise, we will send it nothing
951  for (int i=0; i<commSize; ++i)
952  {
953  double min1[dim], max1[dim];
954  bool sendThis = true;
955  for (int k=0; k<dim; ++k)
956  {
957  min1[k] = targetBoundingBoxes[2*dim*i+2*k]+boxOffset;
958  max1[k] = targetBoundingBoxes[2*dim*i+2*k+1]-boxOffset;
959  sendThis = sendThis &&
960  ((min1[k] <= min2[k] && min2[k] <= max1[k]) ||
961  (min2[k] <= min1[k] && min1[k] <= max2[k]));
962  }
963 
964  sendFlags[i] = sendThis;
965  }
966  }
967 
968 
981  std::vector<GID_t> to_gid(std::vector<int> const& in, vector<GID_t>const& gids) const {
982  std::vector<GID_t> result;
983  result.reserve(in.size());
984  for (auto x:in) result.push_back(gids[x]);
985  return result;
986  }
987 
988 
1017  void compress(std::vector<GID_t> const& distributedGlobalIds,
1018  std::vector<int>& distributedIds) const {
1019 
1020  // a map for keeping track of what we have already seen
1021  std::map<GID_t,int> uniqueGid;
1022 
1023  int const num_distributed_global_ids = distributedGlobalIds.size();
1024 
1025  // loop over owned cells in the distributed global id's
1026  for (int i = 0 ; i < num_distributed_global_ids; ++i){
1027 
1028  // get the current gid
1029  GID_t gid = distributedGlobalIds[i];
1030 
1031  // is this gid new
1032  if (uniqueGid.find(gid) == uniqueGid.end()){
1033 
1034  // make the gid seen
1035  uniqueGid[gid]=i;
1036 
1037  }
1038 
1039  }
1040 
1041  // clear and reserve the result
1042  distributedIds.clear();
1043  distributedIds.reserve(uniqueGid.size());
1044 
1045  // push the cells in gid order
1046  for (auto const& kv : uniqueGid){
1047 
1048  // push to the distributed cells id
1049  distributedIds.push_back(kv.second);
1050 
1051  }
1052  }
1106  void compress_with_ghosts(std::vector<GID_t> const& distributedGlobalIds,
1107  int const distributedNumOwned, std::vector<int>& distributedIds,
1108  std::vector<GID_t>& flatGlobalIds, int &flatNumOwned) const {
1109 
1110  // a map for keeping track of what we have already seen
1111  std::map<GID_t,int> uniqueGid, uniqueGhostGid;
1112 
1113  // loop over owned entitites in the distributed global id's
1114  for (int i=0 ; i<distributedNumOwned ; ++i){
1115 
1116  // get the current gid
1117  GID_t gid = distributedGlobalIds[i];
1118 
1119  // is this gid new
1120  if (uniqueGid.find(gid)==uniqueGid.end()){
1121 
1122  // make the gid seen
1123  uniqueGid[gid]=i;
1124 
1125  }
1126 
1127  }
1128 
1129  // We have processed owned entitites in the distributed mesh, so everything
1130  // we have collected to this point is considered owned
1131  flatNumOwned = uniqueGid.size();
1132 
1133  // push the owned entitites first and in gid order
1134  for (auto const& kv : uniqueGid){
1135 
1136  // push to the flat entitites gid
1137  flatGlobalIds.push_back(kv.first);
1138 
1139  // push to the distributed entitites id
1140  distributedIds.push_back(kv.second);
1141 
1142  }
1143 
1144  int const num_distributed_global_ids = distributedGlobalIds.size();
1145 
1146  // loop over ghost entitites in the distributed global id's
1147  for (int i = distributedNumOwned ; i < num_distributed_global_ids; ++i){
1148 
1149  // get the current gid
1150  GID_t gid = distributedGlobalIds[i];
1151 
1152  // is this gid new
1153  if (uniqueGid.find(gid)==uniqueGid.end() &&
1154  uniqueGhostGid.find(gid)==uniqueGhostGid.end()){
1155 
1156  // make the gid seen
1157  uniqueGhostGid[gid]=i;
1158 
1159  }
1160 
1161  }
1162 
1163  // push the ghost entities in gid order
1164  for (auto const& kv : uniqueGhostGid){
1165 
1166  // push to the flat entities gid
1167  flatGlobalIds.push_back(kv.first);
1168 
1169  // push to the distributed entities id
1170  distributedIds.push_back(kv.second);
1171 
1172  }
1173 
1174  }
1175 
1176 
1191  void create_gid_to_flat_map(std::vector<GID_t> const& flatGlobalIds,
1192  std::map<GID_t,int>& gidToFlat) const {
1193 
1194  int const num_flat_global_ids = flatGlobalIds.size();
1195  for (int i = 0; i < num_flat_global_ids; ++i)
1196  gidToFlat[flatGlobalIds[i]]=i;
1197 
1198  }
1199 
1200 
1214  template<class T>
1215  void merge_duplicate_data(std::vector<T>const& in, std::vector<int>const& distributedIds,
1216  std::vector<T>& result, int stride = 1){
1217 
1218  // clear result and reserve to stride * the number kept
1219  result.clear();
1220  result.reserve(distributedIds.size()*stride);
1221 
1222  // since the vector is the correct size
1223  for (auto id: distributedIds)
1224  for (int d=0; d<stride; ++d)
1225  result.push_back(in[stride*id + d]);
1226  }
1227 
1246  void merge_duplicate_lists(std::vector<GID_t>const& in, std::vector<int> const& counts,
1247  std::vector<int>const& distributedIds, std::map<GID_t,int>const& gidToFlatId,
1248  std::vector<int>& result){
1249 
1250  // allocate offsets
1251  std::vector<int> offsets(counts.size());
1252 
1253  // compute offsets (note the first element is zero and correct)
1254  std::partial_sum(counts.begin(), counts.end()-1, offsets.begin()+1);
1255 
1256  // make sure the result is clear and approximately sized
1257  result.clear();
1258  result.reserve(distributedIds.size()*(dim_+1)); // estimate, lower bound
1259 
1260  // loop over the compressed distributed entities
1261  for (int distributedId : distributedIds){
1262 
1263  // temp for the offset of this id
1264  int const thisOffset = offsets[distributedId];
1265 
1266  // loop over the references and map them
1267  for (int j=0; j<counts[distributedId]; ++j)
1268 
1269  // push the mapped reference
1270  result.push_back(gidToFlatId.at(in[thisOffset+j]));
1271 
1272  }
1273  }
1274 
1275 
1276 
1277 
1293  template<class T>
1294  void merge_duplicate_lists(std::vector<T>const& in, std::vector<int> const & counts,
1295  std::vector<int>const& distributedIds, std::vector<T>& result){
1296 
1297  // allocate offses
1298  std::vector<int> offsets(counts.size());
1299 
1300  // compute offsets (note the first element is zero and correct)
1301  std::partial_sum(counts.begin(), counts.end()-1, offsets.begin()+1);
1302 
1303  // make sure the result is clear and approximately sized
1304  result.clear();
1305  result.reserve(distributedIds.size()*(dim_+1)); // estimate, lower bound
1306 
1307  // loop over the compressed distributed entities
1308  for (int distributedId : distributedIds){
1309 
1310  // temp for the offset of this id
1311  int const thisOffset = offsets[distributedId];
1312 
1313  // loop over the references and map them
1314  for (int j=0; j<counts[distributedId]; ++j)
1315 
1316  // push the mapped reference
1317  result.push_back(in[thisOffset+j]);
1318 
1319  }
1320  }
1321 
1322 
1323 
1324 }; // MPI_Bounding_Boxes
1325 
1326 } // namespace Portage
1327 
1328 #endif // PORTAGE_ENABLE_MPI
1329 
1330 #endif // MPI_Bounding_Boxes_H_
double const epsilon
Numerical tolerance.
Definition: weight.h:34
Definition: coredriver.h:42