$extrastylesheet
mesh_communication_global_indices.C
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 // C++ Includes   -----------------------------------
00021 
00022 // Local Includes -----------------------------------
00023 #include "libmesh/libmesh_config.h"
00024 #include "libmesh/libmesh_common.h"
00025 #include "libmesh/libmesh_logging.h"
00026 #include "libmesh/mesh_base.h"
00027 #include "libmesh/mesh_tools.h"
00028 #include "libmesh/mesh_communication.h"
00029 #include "libmesh/parallel.h"
00030 #include "libmesh/parallel_hilbert.h"
00031 #include "libmesh/parallel_sort.h"
00032 #include "libmesh/elem.h"
00033 #include "libmesh/elem_range.h"
00034 #include "libmesh/node_range.h"
00035 #ifdef LIBMESH_HAVE_LIBHILBERT
00036 #  include "hilbert.h"
00037 #endif
00038 
00039 #ifdef LIBMESH_HAVE_LIBHILBERT
00040 namespace { // anonymous namespace for helper functions
00041 
00042 using namespace libMesh;
00043 
00044 // Utility function to map (x,y,z) in [bbox.min, bbox.max]^3 into
00045 // [0,max_inttype]^3 for computing Hilbert keys
00046 void get_hilbert_coords (const Point &p,
00047                          const MeshTools::BoundingBox &bbox,
00048                          CFixBitVec icoords[3])
00049 {
00050   static const Hilbert::inttype max_inttype = static_cast<Hilbert::inttype>(-1);
00051 
00052   const long double // put (x,y,z) in [0,1]^3 (don't divide by 0)
00053     x = ((bbox.first(0) == bbox.second(0)) ? 0. :
00054          (p(0)-bbox.first(0))/(bbox.second(0)-bbox.first(0))),
00055 
00056 #if LIBMESH_DIM > 1
00057     y = ((bbox.first(1) == bbox.second(1)) ? 0. :
00058          (p(1)-bbox.first(1))/(bbox.second(1)-bbox.first(1))),
00059 #else
00060     y = 0.,
00061 #endif
00062 
00063 #if LIBMESH_DIM > 2
00064     z = ((bbox.first(2) == bbox.second(2)) ? 0. :
00065          (p(2)-bbox.first(2))/(bbox.second(2)-bbox.first(2)));
00066 #else
00067   z = 0.;
00068 #endif
00069 
00070   // (icoords) in [0,max_inttype]^3
00071   icoords[0] = static_cast<Hilbert::inttype>(x*max_inttype);
00072   icoords[1] = static_cast<Hilbert::inttype>(y*max_inttype);
00073   icoords[2] = static_cast<Hilbert::inttype>(z*max_inttype);
00074 }
00075 
00076 
00077 
00078 // Compute the hilbert index
00079 template <typename T>
00080 Hilbert::HilbertIndices
00081 get_hilbert_index (const T *p,
00082                    const MeshTools::BoundingBox &bbox)
00083 {
00084   static const unsigned int sizeof_inttype = sizeof(Hilbert::inttype);
00085 
00086   Hilbert::HilbertIndices index;
00087   CFixBitVec icoords[3];
00088   Hilbert::BitVecType bv;
00089   get_hilbert_coords (*p, bbox, icoords);
00090   Hilbert::coordsToIndex (icoords, 8*sizeof_inttype, 3, bv);
00091   index = bv;
00092 
00093   return index;
00094 }
00095 
00096 template <>
00097 Hilbert::HilbertIndices
00098 get_hilbert_index (const Elem *e,
00099                    const MeshTools::BoundingBox &bbox)
00100 {
00101   static const unsigned int sizeof_inttype = sizeof(Hilbert::inttype);
00102 
00103   Hilbert::HilbertIndices index;
00104   CFixBitVec icoords[3];
00105   Hilbert::BitVecType bv;
00106   get_hilbert_coords (e->centroid(), bbox, icoords);
00107   Hilbert::coordsToIndex (icoords, 8*sizeof_inttype, 3, bv);
00108   index = bv;
00109 
00110   return index;
00111 }
00112 
00113 
00114 
00115 // Compute the hilbert index
00116 Hilbert::HilbertIndices
00117 get_hilbert_index (const Point &p,
00118                    const MeshTools::BoundingBox &bbox)
00119 {
00120   static const unsigned int sizeof_inttype = sizeof(Hilbert::inttype);
00121 
00122   Hilbert::HilbertIndices index;
00123   CFixBitVec icoords[3];
00124   Hilbert::BitVecType bv;
00125   get_hilbert_coords (p, bbox, icoords);
00126   Hilbert::coordsToIndex (icoords, 8*sizeof_inttype, 3, bv);
00127   index = bv;
00128 
00129   return index;
00130 }
00131 
00132 // Helper class for threaded Hilbert key computation
00133 class ComputeHilbertKeys
00134 {
00135 public:
00136   ComputeHilbertKeys (const MeshTools::BoundingBox &bbox,
00137                       std::vector<Hilbert::HilbertIndices> &keys) :
00138     _bbox(bbox),
00139     _keys(keys)
00140   {}
00141 
00142   // computes the hilbert index for a node
00143   void operator() (const ConstNodeRange &range) const
00144   {
00145     std::size_t pos = range.first_idx();
00146     for (ConstNodeRange::const_iterator it = range.begin(); it!=range.end(); ++it)
00147       {
00148         const Node* node = (*it);
00149         libmesh_assert(node);
00150         libmesh_assert_less (pos, _keys.size());
00151         _keys[pos++] = get_hilbert_index (*node, _bbox);
00152       }
00153   }
00154 
00155   // computes the hilbert index for an element
00156   void operator() (const ConstElemRange &range) const
00157   {
00158     std::size_t pos = range.first_idx();
00159     for (ConstElemRange::const_iterator it = range.begin(); it!=range.end(); ++it)
00160       {
00161         const Elem* elem = (*it);
00162         libmesh_assert(elem);
00163         libmesh_assert_less (pos, _keys.size());
00164         _keys[pos++] = get_hilbert_index (elem->centroid(), _bbox);
00165       }
00166   }
00167 
00168 private:
00169   const MeshTools::BoundingBox &_bbox;
00170   std::vector<Hilbert::HilbertIndices> &_keys;
00171 };
00172 }
00173 #endif
00174 
00175 
00176 namespace libMesh
00177 {
00178 
00179 
00180 // ------------------------------------------------------------
00181 // MeshCommunication class members
00182 #if defined(LIBMESH_HAVE_LIBHILBERT) && defined(LIBMESH_HAVE_MPI)
00183 void MeshCommunication::assign_global_indices (MeshBase& mesh) const
00184 {
00185   START_LOG ("assign_global_indices()", "MeshCommunication");
00186 
00187   // This method determines partition-agnostic global indices
00188   // for nodes and elements.
00189 
00190   // Algorithm:
00191   // (1) compute the Hilbert key for each local node/element
00192   // (2) perform a parallel sort of the Hilbert key
00193   // (3) get the min/max value on each processor
00194   // (4) determine the position in the global ranking for
00195   //     each local object
00196 
00197   const Parallel::Communicator &communicator (mesh.comm());
00198 
00199   // Global bounding box
00200   MeshTools::BoundingBox bbox =
00201     MeshTools::bounding_box (mesh);
00202 
00203   //-------------------------------------------------------------
00204   // (1) compute Hilbert keys
00205   std::vector<Hilbert::HilbertIndices>
00206     node_keys, elem_keys;
00207 
00208   {
00209     // Nodes first
00210     {
00211       ConstNodeRange nr (mesh.local_nodes_begin(),
00212                          mesh.local_nodes_end());
00213       node_keys.resize (nr.size());
00214       Threads::parallel_for (nr, ComputeHilbertKeys (bbox, node_keys));
00215 
00216 #if 0
00217       // It's O(N^2) to check that these keys don't duplicate before the
00218       // sort...
00219       MeshBase::const_node_iterator nodei = mesh.local_nodes_begin();
00220       for (std::size_t i = 0; i != node_keys.size(); ++i, ++nodei)
00221         {
00222           MeshBase::const_node_iterator nodej = mesh.local_nodes_begin();
00223           for (std::size_t j = 0; j != i; ++j, ++nodej)
00224             {
00225               if (node_keys[i] == node_keys[j])
00226                 {
00227                   CFixBitVec icoords[3], jcoords[3];
00228                   get_hilbert_coords(**nodej, bbox, jcoords);
00229                   libMesh::err <<
00230                     "node " << (*nodej)->id() << ", " <<
00231                     *(Point*)(*nodej) << " has HilbertIndices " <<
00232                     node_keys[j] << std::endl;
00233                   get_hilbert_coords(**nodei, bbox, icoords);
00234                   libMesh::err <<
00235                     "node " << (*nodei)->id() << ", " <<
00236                     *(Point*)(*nodei) << " has HilbertIndices " <<
00237                     node_keys[i] << std::endl;
00238                   libmesh_error_msg("Error: nodes with duplicate Hilbert keys!");
00239                 }
00240             }
00241         }
00242 #endif
00243     }
00244 
00245     // Elements next
00246     {
00247       ConstElemRange er (mesh.local_elements_begin(),
00248                          mesh.local_elements_end());
00249       elem_keys.resize (er.size());
00250       Threads::parallel_for (er, ComputeHilbertKeys (bbox, elem_keys));
00251 
00252 #if 0
00253       // For elements, the keys can be (and in the case of TRI, are
00254       // expected to be) duplicates, but only if the elements are at
00255       // different levels
00256       MeshBase::const_element_iterator elemi = mesh.local_elements_begin();
00257       for (std::size_t i = 0; i != elem_keys.size(); ++i, ++elemi)
00258         {
00259           MeshBase::const_element_iterator elemj = mesh.local_elements_begin();
00260           for (std::size_t j = 0; j != i; ++j, ++elemj)
00261             {
00262               if ((elem_keys[i] == elem_keys[j]) &&
00263                   ((*elemi)->level() == (*elemj)->level()))
00264                 {
00265                   libMesh::err <<
00266                     "level " << (*elemj)->level() << " elem\n" <<
00267                     (**elemj) << " centroid " <<
00268                     (*elemj)->centroid() << " has HilbertIndices " <<
00269                     elem_keys[j] << " or " <<
00270                     get_hilbert_index((*elemj)->centroid(), bbox) <<
00271                     std::endl;
00272                   libMesh::err <<
00273                     "level " << (*elemi)->level() << " elem\n" <<
00274                     (**elemi) << " centroid " <<
00275                     (*elemi)->centroid() << " has HilbertIndices " <<
00276                     elem_keys[i] << " or " <<
00277                     get_hilbert_index((*elemi)->centroid(), bbox) <<
00278                     std::endl;
00279                   libmesh_error_msg("Error: level " << (*elemi)->level() << " elements with duplicate Hilbert keys!");
00280                 }
00281             }
00282         }
00283 #endif
00284     }
00285   } // done computing Hilbert keys
00286 
00287 
00288 
00289   //-------------------------------------------------------------
00290   // (2) parallel sort the Hilbert keys
00291   Parallel::Sort<Hilbert::HilbertIndices> node_sorter (communicator,
00292                                                        node_keys);
00293   node_sorter.sort(); /* done with node_keys */ //node_keys.clear();
00294 
00295   const std::vector<Hilbert::HilbertIndices> &my_node_bin =
00296     node_sorter.bin();
00297 
00298   Parallel::Sort<Hilbert::HilbertIndices> elem_sorter (communicator,
00299                                                        elem_keys);
00300   elem_sorter.sort(); /* done with elem_keys */ //elem_keys.clear();
00301 
00302   const std::vector<Hilbert::HilbertIndices> &my_elem_bin =
00303     elem_sorter.bin();
00304 
00305 
00306 
00307   //-------------------------------------------------------------
00308   // (3) get the max value on each processor
00309   std::vector<Hilbert::HilbertIndices>
00310     node_upper_bounds(communicator.size()),
00311     elem_upper_bounds(communicator.size());
00312 
00313   { // limit scope of temporaries
00314     std::vector<Hilbert::HilbertIndices> recvbuf(2*communicator.size());
00315     std::vector<unsigned short int> /* do not use a vector of bools here since it is not always so! */
00316       empty_nodes (communicator.size()),
00317       empty_elem  (communicator.size());
00318     std::vector<Hilbert::HilbertIndices> my_max(2);
00319 
00320     communicator.allgather (static_cast<unsigned short int>(my_node_bin.empty()), empty_nodes);
00321     communicator.allgather (static_cast<unsigned short int>(my_elem_bin.empty()),  empty_elem);
00322 
00323     if (!my_node_bin.empty()) my_max[0] = my_node_bin.back();
00324     if (!my_elem_bin.empty()) my_max[1] = my_elem_bin.back();
00325 
00326     communicator.allgather (my_max, /* identical_buffer_sizes = */ true);
00327 
00328     // Be cereful here.  The *_upper_bounds will be used to find the processor
00329     // a given object belongs to.  So, if a processor contains no objects (possible!)
00330     // then copy the bound from the lower processor id.
00331     for (processor_id_type p=0; p<communicator.size(); p++)
00332       {
00333         node_upper_bounds[p] = my_max[2*p+0];
00334         elem_upper_bounds[p] = my_max[2*p+1];
00335 
00336         if (p > 0) // default hilbert index value is the OK upper bound for processor 0.
00337           {
00338             if (empty_nodes[p]) node_upper_bounds[p] = node_upper_bounds[p-1];
00339             if (empty_elem[p])  elem_upper_bounds[p] = elem_upper_bounds[p-1];
00340           }
00341       }
00342   }
00343 
00344 
00345 
00346   //-------------------------------------------------------------
00347   // (4) determine the position in the global ranking for
00348   //     each local object
00349   {
00350     //----------------------------------------------
00351     // Nodes first -- all nodes, not just local ones
00352     {
00353       // Request sets to send to each processor
00354       std::vector<std::vector<Hilbert::HilbertIndices> >
00355         requested_ids (communicator.size());
00356       // Results to gather from each processor
00357       std::vector<std::vector<dof_id_type> >
00358         filled_request (communicator.size());
00359 
00360       {
00361         MeshBase::const_node_iterator       it  = mesh.nodes_begin();
00362         const MeshBase::const_node_iterator end = mesh.nodes_end();
00363 
00364         // build up list of requests
00365         for (; it != end; ++it)
00366           {
00367             const Node* node = (*it);
00368             libmesh_assert(node);
00369             const Hilbert::HilbertIndices hi =
00370               get_hilbert_index (*node, bbox);
00371             const processor_id_type pid =
00372               cast_int<processor_id_type>
00373               (std::distance (node_upper_bounds.begin(),
00374                               std::lower_bound(node_upper_bounds.begin(),
00375                                                node_upper_bounds.end(),
00376                                                hi)));
00377 
00378             libmesh_assert_less (pid, communicator.size());
00379 
00380             requested_ids[pid].push_back(hi);
00381           }
00382       }
00383 
00384       // The number of objects in my_node_bin on each processor
00385       std::vector<dof_id_type> node_bin_sizes(communicator.size());
00386       communicator.allgather (static_cast<dof_id_type>(my_node_bin.size()), node_bin_sizes);
00387 
00388       // The offset of my first global index
00389       dof_id_type my_offset = 0;
00390       for (processor_id_type pid=0; pid<communicator.rank(); pid++)
00391         my_offset += node_bin_sizes[pid];
00392 
00393       // start with pid=0, so that we will trade with ourself
00394       for (processor_id_type pid=0; pid<communicator.size(); pid++)
00395         {
00396           // Trade my requests with processor procup and procdown
00397           const processor_id_type procup = cast_int<processor_id_type>
00398             ((communicator.rank() + pid) % communicator.size());
00399           const processor_id_type procdown = cast_int<processor_id_type>
00400             ((communicator.size() + communicator.rank() - pid) %
00401              communicator.size());
00402 
00403           std::vector<Hilbert::HilbertIndices> request_to_fill;
00404           communicator.send_receive(procup, requested_ids[procup],
00405                                     procdown, request_to_fill);
00406 
00407           // Fill the requests
00408           std::vector<dof_id_type> global_ids;  global_ids.reserve(request_to_fill.size());
00409           for (std::size_t idx=0; idx<request_to_fill.size(); idx++)
00410             {
00411               const Hilbert::HilbertIndices &hi = request_to_fill[idx];
00412               libmesh_assert_less_equal (hi, node_upper_bounds[communicator.rank()]);
00413 
00414               // find the requested index in my node bin
00415               std::vector<Hilbert::HilbertIndices>::const_iterator pos =
00416                 std::lower_bound (my_node_bin.begin(), my_node_bin.end(), hi);
00417               libmesh_assert (pos != my_node_bin.end());
00418               libmesh_assert_equal_to (*pos, hi);
00419 
00420               // Finally, assign the global index based off the position of the index
00421               // in my array, properly offset.
00422               global_ids.push_back(cast_int<dof_id_type>(std::distance(my_node_bin.begin(), pos) + my_offset));
00423             }
00424 
00425           // and trade back
00426           communicator.send_receive (procdown, global_ids,
00427                                      procup,   filled_request[procup]);
00428         }
00429 
00430       // We now have all the filled requests, so we can loop through our
00431       // nodes once and assign the global index to each one.
00432       {
00433         std::vector<std::vector<dof_id_type>::const_iterator>
00434           next_obj_on_proc; next_obj_on_proc.reserve(communicator.size());
00435         for (processor_id_type pid=0; pid<communicator.size(); pid++)
00436           next_obj_on_proc.push_back(filled_request[pid].begin());
00437 
00438         {
00439           MeshBase::node_iterator       it  = mesh.nodes_begin();
00440           const MeshBase::node_iterator end = mesh.nodes_end();
00441 
00442           for (; it != end; ++it)
00443             {
00444               Node* node = (*it);
00445               libmesh_assert(node);
00446               const Hilbert::HilbertIndices hi =
00447                 get_hilbert_index (*node, bbox);
00448               const processor_id_type pid =
00449                 cast_int<processor_id_type>
00450                 (std::distance (node_upper_bounds.begin(),
00451                                 std::lower_bound(node_upper_bounds.begin(),
00452                                                  node_upper_bounds.end(),
00453                                                  hi)));
00454 
00455               libmesh_assert_less (pid, communicator.size());
00456               libmesh_assert (next_obj_on_proc[pid] != filled_request[pid].end());
00457 
00458               const dof_id_type global_index = *next_obj_on_proc[pid];
00459               libmesh_assert_less (global_index, mesh.n_nodes());
00460               node->set_id() = global_index;
00461 
00462               ++next_obj_on_proc[pid];
00463             }
00464         }
00465       }
00466     }
00467 
00468     //---------------------------------------------------
00469     // elements next -- all elements, not just local ones
00470     {
00471       // Request sets to send to each processor
00472       std::vector<std::vector<Hilbert::HilbertIndices> >
00473         requested_ids (communicator.size());
00474       // Results to gather from each processor
00475       std::vector<std::vector<dof_id_type> >
00476         filled_request (communicator.size());
00477 
00478       {
00479         MeshBase::const_element_iterator       it  = mesh.elements_begin();
00480         const MeshBase::const_element_iterator end = mesh.elements_end();
00481 
00482         for (; it != end; ++it)
00483           {
00484             const Elem* elem = (*it);
00485             libmesh_assert(elem);
00486             const Hilbert::HilbertIndices hi =
00487               get_hilbert_index (elem->centroid(), bbox);
00488             const processor_id_type pid =
00489               cast_int<processor_id_type>
00490               (std::distance (elem_upper_bounds.begin(),
00491                               std::lower_bound(elem_upper_bounds.begin(),
00492                                                elem_upper_bounds.end(),
00493                                                hi)));
00494 
00495             libmesh_assert_less (pid, communicator.size());
00496 
00497             requested_ids[pid].push_back(hi);
00498           }
00499       }
00500 
00501       // The number of objects in my_elem_bin on each processor
00502       std::vector<dof_id_type> elem_bin_sizes(communicator.size());
00503       communicator.allgather (static_cast<dof_id_type>(my_elem_bin.size()), elem_bin_sizes);
00504 
00505       // The offset of my first global index
00506       dof_id_type my_offset = 0;
00507       for (processor_id_type pid=0; pid<communicator.rank(); pid++)
00508         my_offset += elem_bin_sizes[pid];
00509 
00510       // start with pid=0, so that we will trade with ourself
00511       for (processor_id_type pid=0; pid<communicator.size(); pid++)
00512         {
00513           // Trade my requests with processor procup and procdown
00514           const processor_id_type procup = cast_int<processor_id_type>
00515             ((communicator.rank() + pid) % communicator.size());
00516           const processor_id_type procdown = cast_int<processor_id_type>
00517             ((communicator.size() + communicator.rank() - pid) %
00518              communicator.size());
00519 
00520           std::vector<Hilbert::HilbertIndices> request_to_fill;
00521           communicator.send_receive(procup, requested_ids[procup],
00522                                     procdown, request_to_fill);
00523 
00524           // Fill the requests
00525           std::vector<dof_id_type> global_ids;  global_ids.reserve(request_to_fill.size());
00526           for (std::size_t idx=0; idx<request_to_fill.size(); idx++)
00527             {
00528               const Hilbert::HilbertIndices &hi = request_to_fill[idx];
00529               libmesh_assert_less_equal (hi, elem_upper_bounds[communicator.rank()]);
00530 
00531               // find the requested index in my elem bin
00532               std::vector<Hilbert::HilbertIndices>::const_iterator pos =
00533                 std::lower_bound (my_elem_bin.begin(), my_elem_bin.end(), hi);
00534               libmesh_assert (pos != my_elem_bin.end());
00535               libmesh_assert_equal_to (*pos, hi);
00536 
00537               // Finally, assign the global index based off the position of the index
00538               // in my array, properly offset.
00539               global_ids.push_back (cast_int<dof_id_type>(std::distance(my_elem_bin.begin(), pos) + my_offset));
00540             }
00541 
00542           // and trade back
00543           communicator.send_receive (procdown, global_ids,
00544                                      procup,   filled_request[procup]);
00545         }
00546 
00547       // We now have all the filled requests, so we can loop through our
00548       // elements once and assign the global index to each one.
00549       {
00550         std::vector<std::vector<dof_id_type>::const_iterator>
00551           next_obj_on_proc; next_obj_on_proc.reserve(communicator.size());
00552         for (processor_id_type pid=0; pid<communicator.size(); pid++)
00553           next_obj_on_proc.push_back(filled_request[pid].begin());
00554 
00555         {
00556           MeshBase::element_iterator       it  = mesh.elements_begin();
00557           const MeshBase::element_iterator end = mesh.elements_end();
00558 
00559           for (; it != end; ++it)
00560             {
00561               Elem* elem = (*it);
00562               libmesh_assert(elem);
00563               const Hilbert::HilbertIndices hi =
00564                 get_hilbert_index (elem->centroid(), bbox);
00565               const processor_id_type pid =
00566                 cast_int<processor_id_type>
00567                 (std::distance (elem_upper_bounds.begin(),
00568                                 std::lower_bound(elem_upper_bounds.begin(),
00569                                                  elem_upper_bounds.end(),
00570                                                  hi)));
00571 
00572               libmesh_assert_less (pid, communicator.size());
00573               libmesh_assert (next_obj_on_proc[pid] != filled_request[pid].end());
00574 
00575               const dof_id_type global_index = *next_obj_on_proc[pid];
00576               libmesh_assert_less (global_index, mesh.n_elem());
00577               elem->set_id() = global_index;
00578 
00579               ++next_obj_on_proc[pid];
00580             }
00581         }
00582       }
00583     }
00584   }
00585 
00586   STOP_LOG ("assign_global_indices()", "MeshCommunication");
00587 }
00588 #else // LIBMESH_HAVE_LIBHILBERT, LIBMESH_HAVE_MPI
00589 void MeshCommunication::assign_global_indices (MeshBase&) const
00590 {
00591 }
00592 #endif // LIBMESH_HAVE_LIBHILBERT, LIBMESH_HAVE_MPI
00593 
00594 
00595 
00596 #if defined(LIBMESH_HAVE_LIBHILBERT) && defined(LIBMESH_HAVE_MPI)
00597 template <typename ForwardIterator>
00598 void MeshCommunication::find_global_indices (const Parallel::Communicator &communicator,
00599                                              const MeshTools::BoundingBox &bbox,
00600                                              const ForwardIterator &begin,
00601                                              const ForwardIterator &end,
00602                                              std::vector<dof_id_type> &index_map) const
00603 {
00604   START_LOG ("find_global_indices()", "MeshCommunication");
00605 
00606   // This method determines partition-agnostic global indices
00607   // for nodes and elements.
00608 
00609   // Algorithm:
00610   // (1) compute the Hilbert key for each local node/element
00611   // (2) perform a parallel sort of the Hilbert key
00612   // (3) get the min/max value on each processor
00613   // (4) determine the position in the global ranking for
00614   //     each local object
00615   index_map.clear();
00616   index_map.reserve(std::distance (begin, end));
00617 
00618   //-------------------------------------------------------------
00619   // (1) compute Hilbert keys
00620   // These aren't trivial to compute, and we will need them again.
00621   // But the binsort will sort the input vector, trashing the order
00622   // that we'd like to rely on.  So, two vectors...
00623   std::vector<Hilbert::HilbertIndices>
00624     sorted_hilbert_keys,
00625     hilbert_keys;
00626   sorted_hilbert_keys.reserve(index_map.capacity());
00627   hilbert_keys.reserve(index_map.capacity());
00628   {
00629     START_LOG("compute_hilbert_indices()", "MeshCommunication");
00630     for (ForwardIterator it=begin; it!=end; ++it)
00631       {
00632         const Hilbert::HilbertIndices hi(get_hilbert_index (*it, bbox));
00633         hilbert_keys.push_back(hi);
00634 
00635         if ((*it)->processor_id() == communicator.rank())
00636           sorted_hilbert_keys.push_back(hi);
00637 
00638         // someone needs to take care of unpartitioned objects!
00639         if ((communicator.rank() == 0) &&
00640             ((*it)->processor_id() == DofObject::invalid_processor_id))
00641           sorted_hilbert_keys.push_back(hi);
00642       }
00643     STOP_LOG("compute_hilbert_indices()", "MeshCommunication");
00644   }
00645 
00646   //-------------------------------------------------------------
00647   // (2) parallel sort the Hilbert keys
00648   START_LOG ("parallel_sort()", "MeshCommunication");
00649   Parallel::Sort<Hilbert::HilbertIndices> sorter (communicator,
00650                                                   sorted_hilbert_keys);
00651   sorter.sort();
00652   STOP_LOG ("parallel_sort()", "MeshCommunication");
00653   const std::vector<Hilbert::HilbertIndices> &my_bin = sorter.bin();
00654 
00655   // The number of objects in my_bin on each processor
00656   std::vector<unsigned int> bin_sizes(communicator.size());
00657   communicator.allgather (static_cast<unsigned int>(my_bin.size()), bin_sizes);
00658 
00659   // The offset of my first global index
00660   unsigned int my_offset = 0;
00661   for (unsigned int pid=0; pid<communicator.rank(); pid++)
00662     my_offset += bin_sizes[pid];
00663 
00664   //-------------------------------------------------------------
00665   // (3) get the max value on each processor
00666   std::vector<Hilbert::HilbertIndices>
00667     upper_bounds(1);
00668 
00669   if (!my_bin.empty())
00670     upper_bounds[0] = my_bin.back();
00671 
00672   communicator.allgather (upper_bounds, /* identical_buffer_sizes = */ true);
00673 
00674   // Be cereful here.  The *_upper_bounds will be used to find the processor
00675   // a given object belongs to.  So, if a processor contains no objects (possible!)
00676   // then copy the bound from the lower processor id.
00677   for (unsigned int p=1; p<communicator.size(); p++)
00678     if (!bin_sizes[p]) upper_bounds[p] = upper_bounds[p-1];
00679 
00680 
00681   //-------------------------------------------------------------
00682   // (4) determine the position in the global ranking for
00683   //     each local object
00684   {
00685     //----------------------------------------------
00686     // all objects, not just local ones
00687 
00688     // Request sets to send to each processor
00689     std::vector<std::vector<Hilbert::HilbertIndices> >
00690       requested_ids (communicator.size());
00691     // Results to gather from each processor
00692     std::vector<std::vector<dof_id_type> >
00693       filled_request (communicator.size());
00694 
00695     // build up list of requests
00696     std::vector<Hilbert::HilbertIndices>::const_iterator hi =
00697       hilbert_keys.begin();
00698 
00699     for (ForwardIterator it = begin; it != end; ++it)
00700       {
00701         libmesh_assert (hi != hilbert_keys.end());
00702         const processor_id_type pid =
00703           cast_int<processor_id_type>
00704           (std::distance (upper_bounds.begin(),
00705                           std::lower_bound(upper_bounds.begin(),
00706                                            upper_bounds.end(),
00707                                            *hi)));
00708 
00709         libmesh_assert_less (pid, communicator.size());
00710 
00711         requested_ids[pid].push_back(*hi);
00712 
00713         ++hi;
00714         // go ahead and put pid in index_map, that way we
00715         // don't have to repeat the std::lower_bound()
00716         index_map.push_back(pid);
00717       }
00718 
00719     // start with pid=0, so that we will trade with ourself
00720     std::vector<Hilbert::HilbertIndices> request_to_fill;
00721     std::vector<dof_id_type> global_ids;
00722     for (processor_id_type pid=0; pid<communicator.size(); pid++)
00723       {
00724         // Trade my requests with processor procup and procdown
00725         const processor_id_type procup = cast_int<processor_id_type>
00726           ((communicator.rank() + pid) % communicator.size());
00727         const processor_id_type procdown = cast_int<processor_id_type>
00728           ((communicator.size() + communicator.rank() - pid) %
00729            communicator.size());
00730 
00731         communicator.send_receive(procup, requested_ids[procup],
00732                                   procdown, request_to_fill);
00733 
00734         // Fill the requests
00735         global_ids.clear();  global_ids.reserve(request_to_fill.size());
00736         for (unsigned int idx=0; idx<request_to_fill.size(); idx++)
00737           {
00738             const Hilbert::HilbertIndices &hilbert_indices = request_to_fill[idx];
00739             libmesh_assert_less_equal (hilbert_indices, upper_bounds[communicator.rank()]);
00740 
00741             // find the requested index in my node bin
00742             std::vector<Hilbert::HilbertIndices>::const_iterator pos =
00743               std::lower_bound (my_bin.begin(), my_bin.end(), hilbert_indices);
00744             libmesh_assert (pos != my_bin.end());
00745 #ifdef DEBUG
00746             // If we could not find the requested Hilbert index in
00747             // my_bin, something went terribly wrong, possibly the
00748             // Mesh was displaced differently on different processors,
00749             // and therefore the Hilbert indices don't agree.
00750             if (*pos != hilbert_indices)
00751               {
00752                 // The input will be hilbert_indices.  We convert it
00753                 // to BitVecType using the operator= provided by the
00754                 // BitVecType class. BitVecType is a CBigBitVec!
00755                 Hilbert::BitVecType input;
00756                 input = hilbert_indices;
00757 
00758                 // Get output in a vector of CBigBitVec
00759                 std::vector<CBigBitVec> output(3);
00760 
00761                 // Call the indexToCoords function
00762                 Hilbert::indexToCoords(&output[0], 8*sizeof(Hilbert::inttype), 3, input);
00763 
00764                 // The entries in the output racks are integers in the
00765                 // range [0, Hilbert::inttype::max] which can be
00766                 // converted to floating point values in [0,1] and
00767                 // finally to actual values using the bounding box.
00768                 Real max_int_as_real = static_cast<Real>(std::numeric_limits<Hilbert::inttype>::max());
00769 
00770                 // Get the points in [0,1]^3.  The zeroth rack of each entry in
00771                 // 'output' maps to the normalized x, y, and z locations,
00772                 // respectively.
00773                 Point p_hat(static_cast<Real>(output[0].racks()[0]) / max_int_as_real,
00774                             static_cast<Real>(output[1].racks()[0]) / max_int_as_real,
00775                             static_cast<Real>(output[2].racks()[0]) / max_int_as_real);
00776 
00777                 // Convert the points from [0,1]^3 to their actual (x,y,z) locations
00778                 Real
00779                   xmin = bbox.first(0),
00780                   xmax = bbox.second(0),
00781                   ymin = bbox.first(1),
00782                   ymax = bbox.second(1),
00783                   zmin = bbox.first(2),
00784                   zmax = bbox.second(2);
00785 
00786                 // Convert the points from [0,1]^3 to their actual (x,y,z) locations
00787                 Point p(xmin + (xmax-xmin)*p_hat(0),
00788                         ymin + (ymax-ymin)*p_hat(1),
00789                         zmin + (zmax-zmin)*p_hat(2));
00790 
00791                 libmesh_error_msg("Could not find hilbert indices: "
00792                                   << hilbert_indices
00793                                   << " corresponding to point " << p);
00794               }
00795 #endif
00796 
00797             // Finally, assign the global index based off the position of the index
00798             // in my array, properly offset.
00799             global_ids.push_back (cast_int<dof_id_type>(std::distance(my_bin.begin(), pos) + my_offset));
00800           }
00801 
00802         // and trade back
00803         communicator.send_receive (procdown, global_ids,
00804                                    procup,   filled_request[procup]);
00805       }
00806 
00807     // We now have all the filled requests, so we can loop through our
00808     // nodes once and assign the global index to each one.
00809     {
00810       std::vector<std::vector<dof_id_type>::const_iterator>
00811         next_obj_on_proc; next_obj_on_proc.reserve(communicator.size());
00812       for (unsigned int pid=0; pid<communicator.size(); pid++)
00813         next_obj_on_proc.push_back(filled_request[pid].begin());
00814 
00815       unsigned int cnt=0;
00816       for (ForwardIterator it = begin; it != end; ++it, cnt++)
00817         {
00818           const processor_id_type pid = cast_int<processor_id_type>
00819             (index_map[cnt]);
00820 
00821           libmesh_assert_less (pid, communicator.size());
00822           libmesh_assert (next_obj_on_proc[pid] != filled_request[pid].end());
00823 
00824           const dof_id_type global_index = *next_obj_on_proc[pid];
00825           index_map[cnt] = global_index;
00826 
00827           ++next_obj_on_proc[pid];
00828         }
00829     }
00830   }
00831 
00832   STOP_LOG ("find_global_indices()", "MeshCommunication");
00833 }
00834 #else // LIBMESH_HAVE_LIBHILBERT, LIBMESH_HAVE_MPI
00835 template <typename ForwardIterator>
00836 void MeshCommunication::find_global_indices (const Parallel::Communicator &,
00837                                              const MeshTools::BoundingBox &,
00838                                              const ForwardIterator &begin,
00839                                              const ForwardIterator &end,
00840                                              std::vector<dof_id_type> &index_map) const
00841 {
00842   index_map.clear();
00843   index_map.reserve(std::distance (begin, end));
00844   unsigned int index = 0;
00845   for (ForwardIterator it=begin; it!=end; ++it)
00846     index_map.push_back(index++);
00847 }
00848 #endif // LIBMESH_HAVE_LIBHILBERT, LIBMESH_HAVE_MPI
00849 
00850 
00851 
00852 //------------------------------------------------------------------
00853 template void MeshCommunication::find_global_indices<MeshBase::const_node_iterator> (const Parallel::Communicator &,
00854                                                                                      const MeshTools::BoundingBox &,
00855                                                                                      const MeshBase::const_node_iterator &,
00856                                                                                      const MeshBase::const_node_iterator &,
00857                                                                                      std::vector<dof_id_type> &) const;
00858 
00859 template void MeshCommunication::find_global_indices<MeshBase::const_element_iterator> (const Parallel::Communicator &,
00860                                                                                         const MeshTools::BoundingBox &,
00861                                                                                         const MeshBase::const_element_iterator &,
00862                                                                                         const MeshBase::const_element_iterator &,
00863                                                                                         std::vector<dof_id_type> &) const;
00864 template void MeshCommunication::find_global_indices<MeshBase::node_iterator> (const Parallel::Communicator &,
00865                                                                                const MeshTools::BoundingBox &,
00866                                                                                const MeshBase::node_iterator &,
00867                                                                                const MeshBase::node_iterator &,
00868                                                                                std::vector<dof_id_type> &) const;
00869 
00870 template void MeshCommunication::find_global_indices<MeshBase::element_iterator> (const Parallel::Communicator &,
00871                                                                                   const MeshTools::BoundingBox &,
00872                                                                                   const MeshBase::element_iterator &,
00873                                                                                   const MeshBase::element_iterator &,
00874                                                                                   std::vector<dof_id_type> &) const;
00875 
00876 } // namespace libMesh