$extrastylesheet
parallel_ghost_sync.h
Go to the documentation of this file.
00001 // The libMesh Finite Element Library.
00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
00003 
00004 // This library is free software; you can redistribute it and/or
00005 // modify it under the terms of the GNU Lesser General Public
00006 // License as published by the Free Software Foundation; either
00007 // version 2.1 of the License, or (at your option) any later version.
00008 
00009 // This library is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 // Lesser General Public License for more details.
00013 
00014 // You should have received a copy of the GNU Lesser General Public
00015 // License along with this library; if not, write to the Free Software
00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 
00018 
00019 
00020 #ifndef LIBMESH_PARALLEL_GHOST_SYNC_H
00021 #define LIBMESH_PARALLEL_GHOST_SYNC_H
00022 
00023 // Local Includes -----------------------------------
00024 #include "libmesh/auto_ptr.h"
00025 #include "libmesh/elem.h"
00026 #include "libmesh/location_maps.h"
00027 #include "libmesh/mesh_base.h"
00028 #include "libmesh/parallel.h"
00029 
00030 // C++ Includes   -----------------------------------
00031 #include LIBMESH_INCLUDE_UNORDERED_SET
00032 
00033 
00034 namespace libMesh
00035 {
00036 
00037 
00038 
00039 //--------------------------------------------------------------------------
00040 namespace Parallel {
00041 
00042 //------------------------------------------------------------------------
00059 template <typename Iterator,
00060           typename DofObjType,
00061           typename SyncFunctor>
00062 void sync_dofobject_data_by_xyz(const Communicator&      comm,
00063                                 const Iterator&          range_begin,
00064                                 const Iterator&          range_end,
00065                                 LocationMap<DofObjType>* location_map,
00066                                 SyncFunctor&             sync);
00067 
00068 //------------------------------------------------------------------------
00081 template <typename Iterator,
00082           typename SyncFunctor>
00083 void sync_dofobject_data_by_id(const Communicator& comm,
00084                                const Iterator&     range_begin,
00085                                const Iterator&     range_end,
00086                                SyncFunctor&        sync);
00087 
00088 //------------------------------------------------------------------------
00102 template <typename Iterator,
00103           typename SyncFunctor>
00104 void sync_element_data_by_parent_id(MeshBase&       mesh,
00105                                     const Iterator& range_begin,
00106                                     const Iterator& range_end,
00107                                     SyncFunctor&    sync);
00108 
00109 //------------------------------------------------------------------------
00125 template <typename SyncFunctor>
00126 void sync_node_data_by_element_id(MeshBase&       mesh,
00127                                   const MeshBase::const_element_iterator& range_begin,
00128                                   const MeshBase::const_element_iterator& range_end,
00129                                   SyncFunctor&    sync);
00130 
00131 
00132 //------------------------------------------------------------------------
00133 // Parallel members
00134 
00135 template <typename Iterator,
00136           typename DofObjType,
00137           typename SyncFunctor>
00138 void sync_dofobject_data_by_xyz(const Communicator&      comm,
00139                                 const Iterator&          range_begin,
00140                                 const Iterator&          range_end,
00141                                 LocationMap<DofObjType>& location_map,
00142                                 SyncFunctor&             sync)
00143 {
00144   // This function must be run on all processors at once
00145   libmesh_parallel_only(comm);
00146 
00147   // We need a valid location_map
00148 #ifdef DEBUG
00149   bool need_map_update = (range_begin != range_end && location_map.empty());
00150   comm.max(need_map_update);
00151   libmesh_assert(!need_map_update);
00152 #endif
00153 
00154   // Count the objectss to ask each processor about
00155   std::vector<dof_id_type>
00156     ghost_objects_from_proc(comm.size(), 0);
00157 
00158   for (Iterator it = range_begin; it != range_end; ++it)
00159     {
00160       DofObjType *obj = *it;
00161       libmesh_assert (obj);
00162       processor_id_type obj_procid = obj->processor_id();
00163       if (obj_procid != DofObject::invalid_processor_id)
00164         ghost_objects_from_proc[obj_procid]++;
00165     }
00166 
00167   // Request sets to send to each processor
00168   std::vector<std::vector<Real> >
00169     requested_objs_x(comm.size()),
00170     requested_objs_y(comm.size()),
00171     requested_objs_z(comm.size());
00172   // Corresponding ids to keep track of
00173   std::vector<std::vector<dof_id_type> >
00174     requested_objs_id(comm.size());
00175 
00176   // We know how many objects live on each processor, so reserve()
00177   // space for each.
00178   for (processor_id_type p=0; p != comm.size(); ++p)
00179     if (p != comm.rank())
00180       {
00181         requested_objs_x[p].reserve(ghost_objects_from_proc[p]);
00182         requested_objs_y[p].reserve(ghost_objects_from_proc[p]);
00183         requested_objs_z[p].reserve(ghost_objects_from_proc[p]);
00184         requested_objs_id[p].reserve(ghost_objects_from_proc[p]);
00185       }
00186   for (Iterator it = range_begin; it != range_end; ++it)
00187     {
00188       DofObjType *obj = *it;
00189       processor_id_type obj_procid = obj->processor_id();
00190       if (obj_procid == comm.rank() ||
00191           obj_procid == DofObject::invalid_processor_id)
00192         continue;
00193 
00194       Point p = location_map.point_of(*obj);
00195       requested_objs_x[obj_procid].push_back(p(0));
00196       requested_objs_y[obj_procid].push_back(p(1));
00197       requested_objs_z[obj_procid].push_back(p(2));
00198       requested_objs_id[obj_procid].push_back(obj->id());
00199     }
00200 
00201   // Trade requests with other processors
00202   for (processor_id_type p=1; p != comm.size(); ++p)
00203     {
00204       // Trade my requests with processor procup and procdown
00205       const processor_id_type procup =
00206         cast_int<processor_id_type>
00207         ((comm.rank() + p) % comm.size());
00208       const processor_id_type procdown =
00209         cast_int<processor_id_type>
00210         ((comm.size() + comm.rank() - p) %
00211          comm.size());
00212       std::vector<Real> request_to_fill_x,
00213         request_to_fill_y,
00214         request_to_fill_z;
00215       comm.send_receive(procup, requested_objs_x[procup],
00216                         procdown, request_to_fill_x);
00217       comm.send_receive(procup, requested_objs_y[procup],
00218                         procdown, request_to_fill_y);
00219       comm.send_receive(procup, requested_objs_z[procup],
00220                         procdown, request_to_fill_z);
00221 
00222       // Find the local id of each requested object
00223       std::vector<dof_id_type> request_to_fill_id(request_to_fill_x.size());
00224       for (std::size_t i=0; i != request_to_fill_x.size(); ++i)
00225         {
00226           Point pt(request_to_fill_x[i],
00227                    request_to_fill_y[i],
00228                    request_to_fill_z[i]);
00229 
00230           // Look for this object in the multimap
00231           DofObjType *obj = location_map.find(pt);
00232 
00233           // We'd better find every object we're asked for
00234           libmesh_assert (obj);
00235 
00236           // Return the object's correct processor id,
00237           // and our (correct if it's local) id for it.
00238           request_to_fill_id[i] = obj->id();
00239         }
00240 
00241       // Gather whatever data the user wants
00242       std::vector<typename SyncFunctor::datum> data;
00243       sync.gather_data(request_to_fill_id, data);
00244 
00245       // Trade back the results
00246       std::vector<typename SyncFunctor::datum> received_data;
00247       comm.send_receive(procdown, data,
00248                         procup, received_data);
00249       libmesh_assert_equal_to (requested_objs_x[procup].size(),
00250                                received_data.size());
00251 
00252       // Let the user process the results
00253       sync.act_on_data(requested_objs_id[procup], received_data);
00254     }
00255 }
00256 
00257 
00258 
00259 template <typename Iterator,
00260           typename SyncFunctor>
00261 void sync_dofobject_data_by_id(const Communicator& comm,
00262                                const Iterator& range_begin,
00263                                const Iterator& range_end,
00264                                SyncFunctor&    sync)
00265 {
00266   // This function must be run on all processors at once
00267   libmesh_parallel_only(comm);
00268 
00269   // Count the objects to ask each processor about
00270   std::vector<dof_id_type>
00271     ghost_objects_from_proc(comm.size(), 0);
00272 
00273   for (Iterator it = range_begin; it != range_end; ++it)
00274     {
00275       DofObject *obj = *it;
00276       libmesh_assert (obj);
00277       processor_id_type obj_procid = obj->processor_id();
00278       if (obj_procid != DofObject::invalid_processor_id)
00279         ghost_objects_from_proc[obj_procid]++;
00280     }
00281 
00282   // Request sets to send to each processor
00283   std::vector<std::vector<dof_id_type> >
00284     requested_objs_id(comm.size());
00285 
00286   // We know how many objects live on each processor, so reserve()
00287   // space for each.
00288   for (processor_id_type p=0; p != comm.size(); ++p)
00289     if (p != comm.rank())
00290       {
00291         requested_objs_id[p].reserve(ghost_objects_from_proc[p]);
00292       }
00293   for (Iterator it = range_begin; it != range_end; ++it)
00294     {
00295       DofObject *obj = *it;
00296       processor_id_type obj_procid = obj->processor_id();
00297       if (obj_procid == comm.rank() ||
00298           obj_procid == DofObject::invalid_processor_id)
00299         continue;
00300 
00301       requested_objs_id[obj_procid].push_back(obj->id());
00302     }
00303 
00304   // Trade requests with other processors
00305   for (processor_id_type p=1; p != comm.size(); ++p)
00306     {
00307       // Trade my requests with processor procup and procdown
00308       const processor_id_type procup =
00309         cast_int<processor_id_type>
00310         ((comm.rank() + p) % comm.size());
00311       const processor_id_type procdown =
00312         cast_int<processor_id_type>
00313         ((comm.size() + comm.rank() - p) %
00314          comm.size());
00315       std::vector<dof_id_type> request_to_fill_id;
00316       comm.send_receive(procup, requested_objs_id[procup],
00317                         procdown, request_to_fill_id);
00318 
00319       // Gather whatever data the user wants
00320       std::vector<typename SyncFunctor::datum> data;
00321       sync.gather_data(request_to_fill_id, data);
00322 
00323       // Trade back the results
00324       std::vector<typename SyncFunctor::datum> received_data;
00325       comm.send_receive(procdown, data,
00326                         procup, received_data);
00327       libmesh_assert_equal_to (requested_objs_id[procup].size(),
00328                                received_data.size());
00329 
00330       // Let the user process the results
00331       sync.act_on_data(requested_objs_id[procup], received_data);
00332     }
00333 }
00334 
00335 
00336 
00337 // If there's no refined elements, there's nothing to sync
00338 #ifdef LIBMESH_ENABLE_AMR
00339 template <typename Iterator,
00340           typename SyncFunctor>
00341 void sync_element_data_by_parent_id(MeshBase&       mesh,
00342                                     const Iterator& range_begin,
00343                                     const Iterator& range_end,
00344                                     SyncFunctor&    sync)
00345 {
00346   const Communicator &comm (mesh.comm());
00347 
00348   // This function must be run on all processors at once
00349   libmesh_parallel_only(comm);
00350 
00351   // Count the objects to ask each processor about
00352   std::vector<dof_id_type>
00353     ghost_objects_from_proc(comm.size(), 0);
00354 
00355   for (Iterator it = range_begin; it != range_end; ++it)
00356     {
00357       DofObject *obj = *it;
00358       libmesh_assert (obj);
00359       processor_id_type obj_procid = obj->processor_id();
00360       if (obj_procid != DofObject::invalid_processor_id)
00361         ghost_objects_from_proc[obj_procid]++;
00362     }
00363 
00364   // Request sets to send to each processor
00365   std::vector<std::vector<dof_id_type> >
00366     requested_objs_id(comm.size()),
00367     requested_objs_parent_id(comm.size());
00368   std::vector<std::vector<unsigned char> >
00369     requested_objs_child_num(comm.size());
00370 
00371   // We know how many objects live on each processor, so reserve()
00372   // space for each.
00373   for (processor_id_type p=0; p != comm.size(); ++p)
00374     if (p != comm.rank())
00375       {
00376         requested_objs_id[p].reserve(ghost_objects_from_proc[p]);
00377         requested_objs_parent_id[p].reserve(ghost_objects_from_proc[p]);
00378         requested_objs_child_num[p].reserve(ghost_objects_from_proc[p]);
00379       }
00380 
00381   for (Iterator it = range_begin; it != range_end; ++it)
00382     {
00383       Elem *elem = *it;
00384       processor_id_type obj_procid = elem->processor_id();
00385       if (obj_procid == comm.rank() ||
00386           obj_procid == DofObject::invalid_processor_id)
00387         continue;
00388       const Elem *parent = elem->parent();
00389       if (!parent || !elem->active())
00390         continue;
00391 
00392       requested_objs_id[obj_procid].push_back(elem->id());
00393       requested_objs_parent_id[obj_procid].push_back(parent->id());
00394       requested_objs_child_num[obj_procid].push_back
00395         (cast_int<unsigned char>
00396          (parent->which_child_am_i(elem)));
00397     }
00398 
00399   // Trade requests with other processors
00400   for (processor_id_type p=1; p != comm.size(); ++p)
00401     {
00402       // Trade my requests with processor procup and procdown
00403       const processor_id_type procup =
00404         cast_int<processor_id_type>
00405         ((comm.rank() + p) % comm.size());
00406       const processor_id_type procdown =
00407         cast_int<processor_id_type>
00408         ((comm.size() + comm.rank() - p) %
00409          comm.size());
00410       std::vector<dof_id_type>   request_to_fill_parent_id;
00411       std::vector<unsigned char> request_to_fill_child_num;
00412       comm.send_receive(procup, requested_objs_parent_id[procup],
00413                         procdown, request_to_fill_parent_id);
00414       comm.send_receive(procup, requested_objs_child_num[procup],
00415                         procdown, request_to_fill_child_num);
00416 
00417       // Find the id of each requested element
00418       std::size_t request_size = request_to_fill_parent_id.size();
00419       std::vector<dof_id_type> request_to_fill_id(request_size);
00420       for (std::size_t i=0; i != request_size; ++i)
00421         {
00422           Elem *parent = mesh.elem(request_to_fill_parent_id[i]);
00423           libmesh_assert(parent);
00424           libmesh_assert(parent->has_children());
00425           Elem *child = parent->child(request_to_fill_child_num[i]);
00426           libmesh_assert(child);
00427           libmesh_assert(child->active());
00428           request_to_fill_id[i] = child->id();
00429         }
00430 
00431       // Gather whatever data the user wants
00432       std::vector<typename SyncFunctor::datum> data;
00433       sync.gather_data(request_to_fill_id, data);
00434 
00435       // Trade back the results
00436       std::vector<typename SyncFunctor::datum> received_data;
00437       comm.send_receive(procdown, data,
00438                         procup, received_data);
00439       libmesh_assert_equal_to (requested_objs_id[procup].size(),
00440                                received_data.size());
00441 
00442       // Let the user process the results
00443       sync.act_on_data(requested_objs_id[procup], received_data);
00444     }
00445 }
00446 #else
00447 template <typename Iterator,
00448           typename SyncFunctor>
00449 void sync_element_data_by_parent_id(MeshBase&,
00450                                     const Iterator&,
00451                                     const Iterator&,
00452                                     SyncFunctor&)
00453 {
00454 }
00455 #endif // LIBMESH_ENABLE_AMR
00456 
00457 
00458 template <typename SyncFunctor>
00459 void sync_node_data_by_element_id(MeshBase&       mesh,
00460                                   const MeshBase::const_element_iterator& range_begin,
00461                                   const MeshBase::const_element_iterator& range_end,
00462                                   SyncFunctor&    sync)
00463 {
00464   const Communicator &comm (mesh.comm());
00465 
00466   // This function must be run on all processors at once
00467   libmesh_parallel_only(comm);
00468 
00469   // Keep track of which nodes we've asked about, so we only hit each
00470   // once.
00471   LIBMESH_BEST_UNORDERED_SET<dof_id_type> queried_nodes;
00472 
00473   // Count the objects to ask each processor about
00474   std::vector<dof_id_type>
00475     ghost_objects_from_proc(comm.size(), 0);
00476 
00477   for (MeshBase::const_element_iterator it = range_begin;
00478        it != range_end; ++it)
00479     {
00480       const Elem *elem = *it;
00481       libmesh_assert (elem);
00482 
00483       for (unsigned int n=0; n != elem->n_nodes(); ++n)
00484         {
00485           const Node *node = elem->get_node(n);
00486 
00487           const processor_id_type proc_id = node->processor_id();
00488           if (proc_id == comm.rank() ||
00489               proc_id == DofObject::invalid_processor_id)
00490             continue;
00491 
00492           dof_id_type node_id = node->id();
00493           if (!queried_nodes.count(node_id))
00494             {
00495               ghost_objects_from_proc[proc_id]++;
00496               queried_nodes.insert(node_id);
00497             }
00498         }
00499     }
00500 
00501   // Now repeat that iteration, filling request sets this time.
00502   queried_nodes.clear();
00503 
00504   // Request sets to send to each processor
00505   std::vector<std::vector<dof_id_type> >
00506     requested_objs_elem_id(comm.size());
00507   std::vector<std::vector<unsigned char> >
00508     requested_objs_node_num(comm.size());
00509 
00510   // Keep track of current local ids for each too
00511   std::vector<std::vector<dof_id_type> >
00512     requested_objs_id(comm.size());
00513 
00514   // We know how many objects live on each processor, so reserve()
00515   // space for each.
00516   for (processor_id_type p=0; p != comm.size(); ++p)
00517     if (p != comm.rank())
00518       {
00519         requested_objs_elem_id[p].reserve(ghost_objects_from_proc[p]);
00520         requested_objs_node_num[p].reserve(ghost_objects_from_proc[p]);
00521         requested_objs_id[p].reserve(ghost_objects_from_proc[p]);
00522       }
00523 
00524   for (MeshBase::const_element_iterator it = range_begin;
00525        it != range_end; ++it)
00526     {
00527       const Elem *elem = *it;
00528       libmesh_assert (elem);
00529 
00530       const dof_id_type elem_id = elem->id();
00531 
00532       for (unsigned int n=0; n != elem->n_nodes(); ++n)
00533         {
00534           const Node *node = elem->get_node(n);
00535           const dof_id_type node_id = node->id();
00536 
00537           const processor_id_type proc_id = node->processor_id();
00538           if (proc_id == comm.rank() ||
00539               proc_id == DofObject::invalid_processor_id)
00540             continue;
00541 
00542           if (!queried_nodes.count(node_id))
00543             {
00544               requested_objs_elem_id[proc_id].push_back(elem_id);
00545               requested_objs_node_num[proc_id].push_back
00546                 (cast_int<unsigned char>(n));
00547               requested_objs_id[proc_id].push_back(node_id);
00548               queried_nodes.insert(node_id);
00549             }
00550         }
00551     }
00552 
00553   // Trade requests with other processors
00554   for (processor_id_type p=1; p != comm.size(); ++p)
00555     {
00556       // Trade my requests with processor procup and procdown
00557       const processor_id_type procup =
00558         cast_int<processor_id_type>
00559         ((comm.rank() + p) % comm.size());
00560       const processor_id_type procdown =
00561         cast_int<processor_id_type>
00562         ((comm.size() + comm.rank() - p) %
00563          comm.size());
00564 
00565       libmesh_assert_equal_to (requested_objs_id[procup].size(),
00566                                ghost_objects_from_proc[procup]);
00567       libmesh_assert_equal_to (requested_objs_elem_id[procup].size(),
00568                                ghost_objects_from_proc[procup]);
00569       libmesh_assert_equal_to (requested_objs_node_num[procup].size(),
00570                                ghost_objects_from_proc[procup]);
00571 
00572       std::vector<dof_id_type>   request_to_fill_elem_id;
00573       std::vector<unsigned char> request_to_fill_node_num;
00574       comm.send_receive(procup, requested_objs_elem_id[procup],
00575                         procdown, request_to_fill_elem_id);
00576       comm.send_receive(procup, requested_objs_node_num[procup],
00577                         procdown, request_to_fill_node_num);
00578 
00579       // Find the id of each requested element
00580       std::size_t request_size = request_to_fill_elem_id.size();
00581       std::vector<dof_id_type> request_to_fill_id(request_size);
00582       for (std::size_t i=0; i != request_size; ++i)
00583         {
00584           const Elem *elem = mesh.elem(request_to_fill_elem_id[i]);
00585           libmesh_assert(elem);
00586 
00587           const unsigned int n = request_to_fill_node_num[i];
00588           libmesh_assert_less (n, elem->n_nodes());
00589 
00590           Node *node = elem->get_node(n);
00591           libmesh_assert(node);
00592 
00593           // This isn't a safe assertion in the case where we're
00594           // synching processor ids
00595           // libmesh_assert_equal_to (node->processor_id(), comm.rank());
00596 
00597           request_to_fill_id[i] = node->id();
00598         }
00599 
00600       // Gather whatever data the user wants
00601       std::vector<typename SyncFunctor::datum> data;
00602       sync.gather_data(request_to_fill_id, data);
00603 
00604       // Trade back the results
00605       std::vector<typename SyncFunctor::datum> received_data;
00606       comm.send_receive(procdown, data,
00607                         procup, received_data);
00608       libmesh_assert_equal_to (requested_objs_elem_id[procup].size(),
00609                                received_data.size());
00610 
00611       // Let the user process the results
00612       sync.act_on_data(requested_objs_id[procup], received_data);
00613     }
00614 }
00615 
00616 
00617 }
00618 
00619 
00620 
00621 // This struct can be created and passed to the
00622 // Parallel::sync_dofobject_data_by_id() function.
00623 struct SyncNodalPositions
00624 {
00625   // The constructor.  You need a reference to the mesh where you will
00626   // be setting/getting nodal positions.
00627   explicit
00628   SyncNodalPositions(MeshBase& m);
00629 
00630   // The datum typedef is required of this functor, so that the
00631   // Parallel::sync_dofobject_data_by_id() function can create e.g.
00632   // std::vector<datum>.
00633   typedef Point datum;
00634 
00635   // First required interface.  This function must fill up the data vector for the
00636   // ids specified in the ids vector.
00637   void gather_data (const std::vector<dof_id_type>& ids, std::vector<datum>& data);
00638 
00639   // Second required interface.  This function must do something with the data in
00640   // the data vector for the ids in the ids vector.
00641   void act_on_data (const std::vector<dof_id_type>& ids, std::vector<datum>& data);
00642 
00643   MeshBase &mesh;
00644 };
00645 
00646 
00647 } // namespace libMesh
00648 
00649 #endif // LIBMESH_PARALLEL_GHOST_SYNC_H