$extrastylesheet
partitioner.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/elem.h"
00024 #include "libmesh/mesh_base.h"
00025 #include "libmesh/parallel.h"
00026 #include "libmesh/partitioner.h"
00027 #include "libmesh/mesh_tools.h"
00028 #include "libmesh/mesh_communication.h"
00029 #include "libmesh/libmesh_logging.h"
00030 
00031 //FIXME
00032 #include "libmesh/parallel_mesh.h"
00033 #include "libmesh/mesh_tools.h"
00034 
00035 namespace libMesh
00036 {
00037 
00038 
00039 
00040 // ------------------------------------------------------------
00041 // Partitioner static data
00042 const dof_id_type Partitioner::communication_blocksize = 1000000;
00043 
00044 
00045 
00046 // ------------------------------------------------------------
00047 // Partitioner implementation
00048 void Partitioner::partition (MeshBase& mesh)
00049 {
00050   this->partition(mesh,mesh.n_processors());
00051 }
00052 
00053 
00054 
00055 void Partitioner::partition (MeshBase& mesh,
00056                              const unsigned int n)
00057 {
00058   libmesh_parallel_only(mesh.comm());
00059 
00060   // BSK - temporary fix while redistribution is integrated 6/26/2008
00061   // Uncomment this to not repartition in parallel
00062   //   if (!mesh.is_serial())
00063   //     return;
00064 
00065   // we cannot partition into more pieces than we have
00066   // active elements!
00067   const unsigned int n_parts =
00068     static_cast<unsigned int>
00069     (std::min(mesh.n_active_elem(), static_cast<dof_id_type>(n)));
00070 
00071   // Set the number of partitions in the mesh
00072   mesh.set_n_partitions()=n_parts;
00073 
00074   if (n_parts == 1)
00075     {
00076       this->single_partition (mesh);
00077       return;
00078     }
00079 
00080   // First assign a temporary partitioning to any unpartitioned elements
00081   Partitioner::partition_unpartitioned_elements(mesh, n_parts);
00082 
00083   // Call the partitioning function
00084   this->_do_partition(mesh,n_parts);
00085 
00086   // Set the parent's processor ids
00087   Partitioner::set_parent_processor_ids(mesh);
00088 
00089   // Redistribute elements if necessary, before setting node processor
00090   // ids, to make sure those will be set consistently
00091   mesh.redistribute();
00092 
00093 #ifdef DEBUG
00094   MeshTools::libmesh_assert_valid_remote_elems(mesh);
00095 
00096   // Messed up elem processor_id()s can leave us without the child
00097   // elements we need to restrict vectors on a distributed mesh
00098   MeshTools::libmesh_assert_valid_procids<Elem>(mesh);
00099 #endif
00100 
00101   // Set the node's processor ids
00102   Partitioner::set_node_processor_ids(mesh);
00103 
00104 #ifdef DEBUG
00105   MeshTools::libmesh_assert_valid_procids<Elem>(mesh);
00106 #endif
00107 
00108   // Give derived Mesh classes a chance to update any cached data to
00109   // reflect the new partitioning
00110   mesh.update_post_partitioning();
00111 }
00112 
00113 
00114 
00115 void Partitioner::repartition (MeshBase& mesh)
00116 {
00117   this->repartition(mesh,mesh.n_processors());
00118 }
00119 
00120 
00121 
00122 void Partitioner::repartition (MeshBase& mesh,
00123                                const unsigned int n)
00124 {
00125   // we cannot partition into more pieces than we have
00126   // active elements!
00127   const unsigned int n_parts =
00128     static_cast<unsigned int>
00129     (std::min(mesh.n_active_elem(), static_cast<dof_id_type>(n)));
00130 
00131   // Set the number of partitions in the mesh
00132   mesh.set_n_partitions()=n_parts;
00133 
00134   if (n_parts == 1)
00135     {
00136       this->single_partition (mesh);
00137       return;
00138     }
00139 
00140   // First assign a temporary partitioning to any unpartitioned elements
00141   Partitioner::partition_unpartitioned_elements(mesh, n_parts);
00142 
00143   // Call the partitioning function
00144   this->_do_repartition(mesh,n_parts);
00145 
00146   // Set the parent's processor ids
00147   Partitioner::set_parent_processor_ids(mesh);
00148 
00149   // Set the node's processor ids
00150   Partitioner::set_node_processor_ids(mesh);
00151 }
00152 
00153 
00154 
00155 
00156 
00157 void Partitioner::single_partition (MeshBase& mesh)
00158 {
00159   START_LOG("single_partition()","Partitioner");
00160 
00161   // Loop over all the elements and assign them to processor 0.
00162   MeshBase::element_iterator       elem_it  = mesh.elements_begin();
00163   const MeshBase::element_iterator elem_end = mesh.elements_end();
00164 
00165   for ( ; elem_it != elem_end; ++elem_it)
00166     (*elem_it)->processor_id() = 0;
00167 
00168   // For a single partition, all the nodes are on processor 0
00169   MeshBase::node_iterator       node_it  = mesh.nodes_begin();
00170   const MeshBase::node_iterator node_end = mesh.nodes_end();
00171 
00172   for ( ; node_it != node_end; ++node_it)
00173     (*node_it)->processor_id() = 0;
00174 
00175   STOP_LOG("single_partition()","Partitioner");
00176 }
00177 
00178 
00179 
00180 void Partitioner::partition_unpartitioned_elements (MeshBase &mesh)
00181 {
00182   Partitioner::partition_unpartitioned_elements(mesh, mesh.n_processors());
00183 }
00184 
00185 
00186 
00187 void Partitioner::partition_unpartitioned_elements (MeshBase &mesh,
00188                                                     const unsigned int n_subdomains)
00189 {
00190   MeshBase::element_iterator       it  = mesh.unpartitioned_elements_begin();
00191   const MeshBase::element_iterator end = mesh.unpartitioned_elements_end();
00192 
00193   const dof_id_type n_unpartitioned_elements = MeshTools::n_elem (it, end);
00194 
00195   // the unpartitioned elements must exist on all processors. If the range is empty on one
00196   // it is empty on all, and we can quit right here.
00197   if (!n_unpartitioned_elements) return;
00198 
00199   // find the target subdomain sizes
00200   std::vector<dof_id_type> subdomain_bounds(mesh.n_processors());
00201 
00202   for (processor_id_type pid=0; pid<mesh.n_processors(); pid++)
00203     {
00204       dof_id_type tgt_subdomain_size = 0;
00205 
00206       // watch out for the case that n_subdomains < n_processors
00207       if (pid < n_subdomains)
00208         {
00209           tgt_subdomain_size = n_unpartitioned_elements/n_subdomains;
00210 
00211           if (pid < n_unpartitioned_elements%n_subdomains)
00212             tgt_subdomain_size++;
00213 
00214         }
00215 
00216       //libMesh::out << "pid, #= " << pid << ", " << tgt_subdomain_size << std::endl;
00217       if (pid == 0)
00218         subdomain_bounds[0] = tgt_subdomain_size;
00219       else
00220         subdomain_bounds[pid] = subdomain_bounds[pid-1] + tgt_subdomain_size;
00221     }
00222 
00223   libmesh_assert_equal_to (subdomain_bounds.back(), n_unpartitioned_elements);
00224 
00225   // create the unique mapping for all unpartitioned elements independent of partitioning
00226   // determine the global indexing for all the unpartitoned elements
00227   std::vector<dof_id_type> global_indices;
00228 
00229   // Calling this on all processors a unique range in [0,n_unpartitioned_elements) is constructed.
00230   // Only the indices for the elements we pass in are returned in the array.
00231   MeshCommunication().find_global_indices (mesh.comm(),
00232                                            MeshTools::bounding_box(mesh), it, end,
00233                                            global_indices);
00234 
00235   for (dof_id_type cnt=0; it != end; ++it)
00236     {
00237       Elem *elem = *it;
00238 
00239       libmesh_assert_less (cnt, global_indices.size());
00240       const dof_id_type global_index =
00241         global_indices[cnt++];
00242 
00243       libmesh_assert_less (global_index, subdomain_bounds.back());
00244       libmesh_assert_less (global_index, n_unpartitioned_elements);
00245 
00246       const processor_id_type subdomain_id =
00247         cast_int<processor_id_type>
00248         (std::distance(subdomain_bounds.begin(),
00249                        std::upper_bound(subdomain_bounds.begin(),
00250                                         subdomain_bounds.end(),
00251                                         global_index)));
00252       libmesh_assert_less (subdomain_id, n_subdomains);
00253 
00254       elem->processor_id() = subdomain_id;
00255       //libMesh::out << "assigning " << global_index << " to " << subdomain_id << std::endl;
00256     }
00257 }
00258 
00259 
00260 
00261 void Partitioner::set_parent_processor_ids(MeshBase&
00262 #ifdef LIBMESH_ENABLE_AMR
00263                                            mesh
00264 #endif
00265                                            )
00266 {
00267   START_LOG("set_parent_processor_ids()","Partitioner");
00268 
00269 #ifdef LIBMESH_ENABLE_AMR
00270 
00271   // If the mesh is serial we have access to all the elements,
00272   // in particular all the active ones.  We can therefore set
00273   // the parent processor ids indirecly through their children, and
00274   // set the subactive processor ids while examining their active
00275   // ancestors.
00276   // By convention a parent is assigned to the minimum processor
00277   // of all its children, and a subactive is assigned to the processor
00278   // of its active ancestor.
00279   if (mesh.is_serial())
00280     {
00281       // Loop over all the active elements in the mesh
00282       MeshBase::element_iterator       it  = mesh.active_elements_begin();
00283       const MeshBase::element_iterator end = mesh.active_elements_end();
00284 
00285       for ( ; it!=end; ++it)
00286         {
00287           Elem *child  = *it;
00288 
00289           // First set descendents
00290 
00291           std::vector<const Elem*> subactive_family;
00292           child->total_family_tree(subactive_family);
00293           for (unsigned int i = 0; i != subactive_family.size(); ++i)
00294             const_cast<Elem*>(subactive_family[i])->processor_id() = child->processor_id();
00295 
00296           // Then set ancestors
00297 
00298           Elem *parent = child->parent();
00299 
00300           while (parent)
00301             {
00302               // invalidate the parent id, otherwise the min below
00303               // will not work if the current parent id is less
00304               // than all the children!
00305               parent->invalidate_processor_id();
00306 
00307               for(unsigned int c=0; c<parent->n_children(); c++)
00308                 {
00309                   child = parent->child(c);
00310                   libmesh_assert(child);
00311                   libmesh_assert(!child->is_remote());
00312                   libmesh_assert_not_equal_to (child->processor_id(), DofObject::invalid_processor_id);
00313                   parent->processor_id() = std::min(parent->processor_id(),
00314                                                     child->processor_id());
00315                 }
00316               parent = parent->parent();
00317             }
00318         }
00319     }
00320 
00321   // When the mesh is parallel we cannot guarantee that parents have access to
00322   // all their children.
00323   else
00324     {
00325       // Setting subactive processor ids is easy: we can guarantee
00326       // that children have access to all their parents.
00327 
00328       // Loop over all the active elements in the mesh
00329       MeshBase::element_iterator       it  = mesh.active_elements_begin();
00330       const MeshBase::element_iterator end = mesh.active_elements_end();
00331 
00332       for ( ; it!=end; ++it)
00333         {
00334           Elem *child  = *it;
00335 
00336           std::vector<const Elem*> subactive_family;
00337           child->total_family_tree(subactive_family);
00338           for (unsigned int i = 0; i != subactive_family.size(); ++i)
00339             const_cast<Elem*>(subactive_family[i])->processor_id() = child->processor_id();
00340         }
00341 
00342       // When the mesh is parallel we cannot guarantee that parents have access to
00343       // all their children.
00344 
00345       // We will use a brute-force approach here.  Each processor finds its parent
00346       // elements and sets the parent pid to the minimum of its
00347       // semilocal descendants.
00348       // A global reduction is then performed to make sure the true minimum is found.
00349       // As noted, this is required because we cannot guarantee that a parent has
00350       // access to all its children on any single processor.
00351       libmesh_parallel_only(mesh.comm());
00352       libmesh_assert(MeshTools::n_elem(mesh.unpartitioned_elements_begin(),
00353                                        mesh.unpartitioned_elements_end()) == 0);
00354 
00355       const dof_id_type max_elem_id = mesh.max_elem_id();
00356 
00357       std::vector<processor_id_type>
00358         parent_processor_ids (std::min(communication_blocksize,
00359                                        max_elem_id));
00360 
00361       for (dof_id_type blk=0, last_elem_id=0; last_elem_id<max_elem_id; blk++)
00362         {
00363           last_elem_id =
00364             std::min(static_cast<dof_id_type>((blk+1)*communication_blocksize),
00365                      max_elem_id);
00366           const dof_id_type first_elem_id = blk*communication_blocksize;
00367 
00368           std::fill (parent_processor_ids.begin(),
00369                      parent_processor_ids.end(),
00370                      DofObject::invalid_processor_id);
00371 
00372           // first build up local contributions to parent_processor_ids
00373           MeshBase::element_iterator       not_it  = mesh.ancestor_elements_begin();
00374           const MeshBase::element_iterator not_end = mesh.ancestor_elements_end();
00375 
00376           bool have_parent_in_block = false;
00377 
00378           for ( ; not_it != not_end; ++not_it)
00379             {
00380               Elem *parent = *not_it;
00381 
00382               const dof_id_type parent_idx = parent->id();
00383               libmesh_assert_less (parent_idx, max_elem_id);
00384 
00385               if ((parent_idx >= first_elem_id) &&
00386                   (parent_idx <  last_elem_id))
00387                 {
00388                   have_parent_in_block = true;
00389                   processor_id_type parent_pid = DofObject::invalid_processor_id;
00390 
00391                   std::vector<const Elem*> active_family;
00392                   parent->active_family_tree(active_family);
00393                   for (unsigned int i = 0; i != active_family.size(); ++i)
00394                     parent_pid = std::min (parent_pid, active_family[i]->processor_id());
00395 
00396                   const dof_id_type packed_idx = parent_idx - first_elem_id;
00397                   libmesh_assert_less (packed_idx, parent_processor_ids.size());
00398 
00399                   parent_processor_ids[packed_idx] = parent_pid;
00400                 }
00401             }
00402 
00403           // then find the global minimum
00404           mesh.comm().min (parent_processor_ids);
00405 
00406           // and assign the ids, if we have a parent in this block.
00407           if (have_parent_in_block)
00408             for (not_it = mesh.ancestor_elements_begin();
00409                  not_it != not_end; ++not_it)
00410               {
00411                 Elem *parent = *not_it;
00412 
00413                 const dof_id_type parent_idx = parent->id();
00414 
00415                 if ((parent_idx >= first_elem_id) &&
00416                     (parent_idx <  last_elem_id))
00417                   {
00418                     const dof_id_type packed_idx = parent_idx - first_elem_id;
00419                     libmesh_assert_less (packed_idx, parent_processor_ids.size());
00420 
00421                     const processor_id_type parent_pid =
00422                       parent_processor_ids[packed_idx];
00423 
00424                     libmesh_assert_not_equal_to (parent_pid, DofObject::invalid_processor_id);
00425 
00426                     parent->processor_id() = parent_pid;
00427                   }
00428               }
00429         }
00430     }
00431 
00432 #endif // LIBMESH_ENABLE_AMR
00433 
00434   STOP_LOG("set_parent_processor_ids()","Partitioner");
00435 }
00436 
00437 
00438 
00439 void Partitioner::set_node_processor_ids(MeshBase& mesh)
00440 {
00441   START_LOG("set_node_processor_ids()","Partitioner");
00442 
00443   // This function must be run on all processors at once
00444   libmesh_parallel_only(mesh.comm());
00445 
00446   // If we have any unpartitioned elements at this
00447   // stage there is a problem
00448   libmesh_assert (MeshTools::n_elem(mesh.unpartitioned_elements_begin(),
00449                                     mesh.unpartitioned_elements_end()) == 0);
00450 
00451 
00452   //   const dof_id_type orig_n_local_nodes = mesh.n_local_nodes();
00453 
00454   //   libMesh::err << "[" << mesh.processor_id() << "]: orig_n_local_nodes="
00455   //     << orig_n_local_nodes << std::endl;
00456 
00457   // Build up request sets.  Each node is currently owned by a processor because
00458   // it is connected to an element owned by that processor.  However, during the
00459   // repartitioning phase that element may have been assigned a new processor id, but
00460   // it is still resident on the original processor.  We need to know where to look
00461   // for new ids before assigning new ids, otherwise we may be asking the wrong processors
00462   // for the wrong information.
00463   //
00464   // The only remaining issue is what to do with unpartitioned nodes.  Since they are required
00465   // to live on all processors we can simply rely on ourselves to number them properly.
00466   std::vector<std::vector<dof_id_type> >
00467     requested_node_ids(mesh.n_processors());
00468 
00469   // Loop over all the nodes, count the ones on each processor.  We can skip ourself
00470   std::vector<dof_id_type> ghost_nodes_from_proc(mesh.n_processors(), 0);
00471 
00472   MeshBase::node_iterator       node_it  = mesh.nodes_begin();
00473   const MeshBase::node_iterator node_end = mesh.nodes_end();
00474 
00475   for (; node_it != node_end; ++node_it)
00476     {
00477       Node *node = *node_it;
00478       libmesh_assert(node);
00479       const processor_id_type current_pid = node->processor_id();
00480       if (current_pid != mesh.processor_id() &&
00481           current_pid != DofObject::invalid_processor_id)
00482         {
00483           libmesh_assert_less (current_pid, ghost_nodes_from_proc.size());
00484           ghost_nodes_from_proc[current_pid]++;
00485         }
00486     }
00487 
00488   // We know how many objects live on each processor, so reserve()
00489   // space for each.
00490   for (processor_id_type pid=0; pid != mesh.n_processors(); ++pid)
00491     requested_node_ids[pid].reserve(ghost_nodes_from_proc[pid]);
00492 
00493   // We need to get the new pid for each node from the processor
00494   // which *currently* owns the node.  We can safely skip ourself
00495   for (node_it = mesh.nodes_begin(); node_it != node_end; ++node_it)
00496     {
00497       Node *node = *node_it;
00498       libmesh_assert(node);
00499       const processor_id_type current_pid = node->processor_id();
00500       if (current_pid != mesh.processor_id() &&
00501           current_pid != DofObject::invalid_processor_id)
00502         {
00503           libmesh_assert_less (current_pid, requested_node_ids.size());
00504           libmesh_assert_less (requested_node_ids[current_pid].size(),
00505                                ghost_nodes_from_proc[current_pid]);
00506           requested_node_ids[current_pid].push_back(node->id());
00507         }
00508 
00509       // Unset any previously-set node processor ids
00510       node->invalidate_processor_id();
00511     }
00512 
00513   // Loop over all the active elements
00514   MeshBase::element_iterator       elem_it  = mesh.active_elements_begin();
00515   const MeshBase::element_iterator elem_end = mesh.active_elements_end();
00516 
00517   for ( ; elem_it != elem_end; ++elem_it)
00518     {
00519       Elem* elem = *elem_it;
00520       libmesh_assert(elem);
00521 
00522       libmesh_assert_not_equal_to (elem->processor_id(), DofObject::invalid_processor_id);
00523 
00524       // For each node, set the processor ID to the min of
00525       // its current value and this Element's processor id.
00526       //
00527       // TODO: we would probably get better parallel partitioning if
00528       // we did something like "min for even numbered nodes, max for
00529       // odd numbered".  We'd need to be careful about how that would
00530       // affect solution ordering for I/O, though.
00531       for (unsigned int n=0; n<elem->n_nodes(); ++n)
00532         elem->get_node(n)->processor_id() = std::min(elem->get_node(n)->processor_id(),
00533                                                      elem->processor_id());
00534     }
00535 
00536   // And loop over the subactive elements, but don't reassign
00537   // nodes that are already active on another processor.
00538   MeshBase::element_iterator       sub_it  = mesh.subactive_elements_begin();
00539   const MeshBase::element_iterator sub_end = mesh.subactive_elements_end();
00540 
00541   for ( ; sub_it != sub_end; ++sub_it)
00542     {
00543       Elem* elem = *sub_it;
00544       libmesh_assert(elem);
00545 
00546       libmesh_assert_not_equal_to (elem->processor_id(), DofObject::invalid_processor_id);
00547 
00548       for (unsigned int n=0; n<elem->n_nodes(); ++n)
00549         if (elem->get_node(n)->processor_id() == DofObject::invalid_processor_id)
00550           elem->get_node(n)->processor_id() = elem->processor_id();
00551     }
00552 
00553   // Same for the inactive elements -- we will have already gotten most of these
00554   // nodes, *except* for the case of a parent with a subset of children which are
00555   // ghost elements.  In that case some of the parent nodes will not have been
00556   // properly handled yet
00557   MeshBase::element_iterator       not_it  = mesh.not_active_elements_begin();
00558   const MeshBase::element_iterator not_end = mesh.not_active_elements_end();
00559 
00560   for ( ; not_it != not_end; ++not_it)
00561     {
00562       Elem* elem = *not_it;
00563       libmesh_assert(elem);
00564 
00565       libmesh_assert_not_equal_to (elem->processor_id(), DofObject::invalid_processor_id);
00566 
00567       for (unsigned int n=0; n<elem->n_nodes(); ++n)
00568         if (elem->get_node(n)->processor_id() == DofObject::invalid_processor_id)
00569           elem->get_node(n)->processor_id() = elem->processor_id();
00570     }
00571 
00572   // We can't assert that all nodes are connected to elements, because
00573   // a ParallelMesh with NodeConstraints might have pulled in some
00574   // remote nodes solely for evaluating those constraints.
00575   // MeshTools::libmesh_assert_connected_nodes(mesh);
00576 
00577   // For such nodes, we'll do a sanity check later when making sure
00578   // that we successfully reset their processor ids to something
00579   // valid.
00580 
00581   // Next set node ids from other processors, excluding self
00582   for (processor_id_type p=1; p != mesh.n_processors(); ++p)
00583     {
00584       // Trade my requests with processor procup and procdown
00585       processor_id_type procup = cast_int<processor_id_type>
00586         ((mesh.processor_id() + p) % mesh.n_processors());
00587       processor_id_type procdown = cast_int<processor_id_type>
00588         ((mesh.n_processors() + mesh.processor_id() - p) %
00589          mesh.n_processors());
00590       std::vector<dof_id_type> request_to_fill;
00591       mesh.comm().send_receive(procup, requested_node_ids[procup],
00592                                procdown, request_to_fill);
00593 
00594       // Fill those requests in-place
00595       for (std::size_t i=0; i != request_to_fill.size(); ++i)
00596         {
00597           Node *node = mesh.node_ptr(request_to_fill[i]);
00598           libmesh_assert(node);
00599           const processor_id_type new_pid = node->processor_id();
00600 
00601 // We may have an invalid processor_id() on nodes that have been
00602 // "detatched" from coarsened-away elements but that have not yet
00603 // themselves been removed.
00604 //          libmesh_assert_not_equal_to (new_pid, DofObject::invalid_processor_id);
00605 //          libmesh_assert_less (new_pid, mesh.n_partitions()); // this is the correct test --
00606 
00607           request_to_fill[i] = new_pid;           //  the number of partitions may
00608         }                                         //  not equal the number of processors
00609 
00610       // Trade back the results
00611       std::vector<dof_id_type> filled_request;
00612       mesh.comm().send_receive(procdown, request_to_fill,
00613                                procup,   filled_request);
00614       libmesh_assert_equal_to (filled_request.size(), requested_node_ids[procup].size());
00615 
00616       // And copy the id changes we've now been informed of
00617       for (std::size_t i=0; i != filled_request.size(); ++i)
00618         {
00619           Node *node = mesh.node_ptr(requested_node_ids[procup][i]);
00620           libmesh_assert(node);
00621 
00622           // this is the correct test -- the number of partitions may
00623           // not equal the number of processors
00624 
00625           // But: we may have an invalid processor_id() on nodes that
00626           // have been "detatched" from coarsened-away elements but
00627           // that have not yet themselves been removed.
00628           // libmesh_assert_less (filled_request[i], mesh.n_partitions());
00629 
00630           node->processor_id(cast_int<processor_id_type>(filled_request[i]));
00631         }
00632     }
00633 
00634 #ifdef DEBUG
00635   MeshTools::libmesh_assert_valid_procids<Node>(mesh);
00636 #endif
00637 
00638   STOP_LOG("set_node_processor_ids()","Partitioner");
00639 }
00640 
00641 } // namespace libMesh