$extrastylesheet
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