void Partitioner::set_parent_processor_ids(MeshBase & mesh) { // Ignore the parameter when !LIBMESH_ENABLE_AMR libmesh_ignore(mesh); LOG_SCOPE("set_parent_processor_ids()", "Partitioner"); #ifdef LIBMESH_ENABLE_AMR // If the mesh is serial we have access to all the elements, // in particular all the active ones. We can therefore set // the parent processor ids indirectly through their children, and // set the subactive processor ids while examining their active // ancestors. // By convention a parent is assigned to the minimum processor // of all its children, and a subactive is assigned to the processor // of its active ancestor. if (mesh.is_serial()) { for (auto & child : mesh.active_element_ptr_range()) { // First set descendents std::vector<const Elem *> subactive_family; child->total_family_tree(subactive_family); for (std::size_t i = 0; i != subactive_family.size(); ++i) const_cast<Elem *>(subactive_family[i])->processor_id() = child->processor_id(); // Then set ancestors Elem * parent = child->parent(); while (parent) { // invalidate the parent id, otherwise the min below // will not work if the current parent id is less // than all the children! parent->invalidate_processor_id(); for (auto & child : parent->child_ref_range()) { libmesh_assert(!child.is_remote()); libmesh_assert_not_equal_to (child.processor_id(), DofObject::invalid_processor_id); parent->processor_id() = std::min(parent->processor_id(), child.processor_id()); } parent = parent->parent(); } } } // When the mesh is parallel we cannot guarantee that parents have access to // all their children. else { // Setting subactive processor ids is easy: we can guarantee // that children have access to all their parents. // Loop over all the active elements in the mesh for (auto & child : mesh.active_element_ptr_range()) { std::vector<const Elem *> subactive_family; child->total_family_tree(subactive_family); for (std::size_t i = 0; i != subactive_family.size(); ++i) const_cast<Elem *>(subactive_family[i])->processor_id() = child->processor_id(); } // When the mesh is parallel we cannot guarantee that parents have access to // all their children. // We will use a brute-force approach here. Each processor finds its parent // elements and sets the parent pid to the minimum of its // semilocal descendants. // A global reduction is then performed to make sure the true minimum is found. // As noted, this is required because we cannot guarantee that a parent has // access to all its children on any single processor. libmesh_parallel_only(mesh.comm()); libmesh_assert(MeshTools::n_elem(mesh.unpartitioned_elements_begin(), mesh.unpartitioned_elements_end()) == 0); const dof_id_type max_elem_id = mesh.max_elem_id(); std::vector<processor_id_type> parent_processor_ids (std::min(communication_blocksize, max_elem_id)); for (dof_id_type blk=0, last_elem_id=0; last_elem_id<max_elem_id; blk++) { last_elem_id = std::min(static_cast<dof_id_type>((blk+1)*communication_blocksize), max_elem_id); const dof_id_type first_elem_id = blk*communication_blocksize; std::fill (parent_processor_ids.begin(), parent_processor_ids.end(), DofObject::invalid_processor_id); // first build up local contributions to parent_processor_ids MeshBase::element_iterator not_it = mesh.ancestor_elements_begin(); const MeshBase::element_iterator not_end = mesh.ancestor_elements_end(); bool have_parent_in_block = false; for ( ; not_it != not_end; ++not_it) { Elem * parent = *not_it; const dof_id_type parent_idx = parent->id(); libmesh_assert_less (parent_idx, max_elem_id); if ((parent_idx >= first_elem_id) && (parent_idx < last_elem_id)) { have_parent_in_block = true; processor_id_type parent_pid = DofObject::invalid_processor_id; std::vector<const Elem *> active_family; parent->active_family_tree(active_family); for (std::size_t i = 0; i != active_family.size(); ++i) parent_pid = std::min (parent_pid, active_family[i]->processor_id()); const dof_id_type packed_idx = parent_idx - first_elem_id; libmesh_assert_less (packed_idx, parent_processor_ids.size()); parent_processor_ids[packed_idx] = parent_pid; } } // then find the global minimum mesh.comm().min (parent_processor_ids); // and assign the ids, if we have a parent in this block. if (have_parent_in_block) for (not_it = mesh.ancestor_elements_begin(); not_it != not_end; ++not_it) { Elem * parent = *not_it; const dof_id_type parent_idx = parent->id(); if ((parent_idx >= first_elem_id) && (parent_idx < last_elem_id)) { const dof_id_type packed_idx = parent_idx - first_elem_id; libmesh_assert_less (packed_idx, parent_processor_ids.size()); const processor_id_type parent_pid = parent_processor_ids[packed_idx]; libmesh_assert_not_equal_to (parent_pid, DofObject::invalid_processor_id); parent->processor_id() = parent_pid; } } } } #endif // LIBMESH_ENABLE_AMR }
bool MeshRefinement::flag_elements_by_nelem_target (const ErrorVector & error_per_cell) { parallel_object_only(); // Check for valid fractions.. // The fraction values must be in [0,1] libmesh_assert_greater_equal (_refine_fraction, 0); libmesh_assert_less_equal (_refine_fraction, 1); libmesh_assert_greater_equal (_coarsen_fraction, 0); libmesh_assert_less_equal (_coarsen_fraction, 1); // This function is currently only coded to work when coarsening by // parents - it's too hard to guess how many coarsenings will be // performed otherwise. libmesh_assert (_coarsen_by_parents); // The number of active elements in the mesh - hopefully less than // 2 billion on 32 bit machines const dof_id_type n_active_elem = _mesh.n_active_elem(); // The maximum number of active elements to flag for coarsening const dof_id_type max_elem_coarsen = static_cast<dof_id_type>(_coarsen_fraction * n_active_elem) + 1; // The maximum number of elements to flag for refinement const dof_id_type max_elem_refine = static_cast<dof_id_type>(_refine_fraction * n_active_elem) + 1; // Clean up the refinement flags. These could be left // over from previous refinement steps. this->clean_refinement_flags(); // The target number of elements to add or remove const std::ptrdiff_t n_elem_new = std::ptrdiff_t(_nelem_target) - std::ptrdiff_t(n_active_elem); // Create an vector with active element errors and ids, // sorted by highest errors first const dof_id_type max_elem_id = _mesh.max_elem_id(); std::vector<std::pair<ErrorVectorReal, dof_id_type>> sorted_error; sorted_error.reserve (n_active_elem); // On a DistributedMesh, we need to communicate to know which remote ids // correspond to active elements. { std::vector<bool> is_active(max_elem_id, false); for (auto & elem : _mesh.active_local_element_ptr_range()) { const dof_id_type eid = elem->id(); is_active[eid] = true; libmesh_assert_less (eid, error_per_cell.size()); sorted_error.push_back (std::make_pair(error_per_cell[eid], eid)); } this->comm().max(is_active); this->comm().allgather(sorted_error); } // Default sort works since pairs are sorted lexicographically std::sort (sorted_error.begin(), sorted_error.end()); std::reverse (sorted_error.begin(), sorted_error.end()); // Create a sorted error vector with coarsenable parent elements // only, sorted by lowest errors first ErrorVector error_per_parent; std::vector<std::pair<ErrorVectorReal, dof_id_type>> sorted_parent_error; Real parent_error_min, parent_error_max; create_parent_error_vector(error_per_cell, error_per_parent, parent_error_min, parent_error_max); // create_parent_error_vector sets values for non-parents and // non-coarsenable parents to -1. Get rid of them. for (std::size_t i=0; i != error_per_parent.size(); ++i) if (error_per_parent[i] != -1) sorted_parent_error.push_back(std::make_pair(error_per_parent[i], i)); std::sort (sorted_parent_error.begin(), sorted_parent_error.end()); // Keep track of how many elements we plan to coarsen & refine dof_id_type coarsen_count = 0; dof_id_type refine_count = 0; const unsigned int dim = _mesh.mesh_dimension(); unsigned int twotodim = 1; for (unsigned int i=0; i!=dim; ++i) twotodim *= 2; // First, let's try to get our element count to target_nelem if (n_elem_new >= 0) { // Every element refinement creates at least // 2^dim-1 new elements refine_count = std::min(cast_int<dof_id_type>(n_elem_new / (twotodim-1)), max_elem_refine); } else { // Every successful element coarsening is likely to destroy // 2^dim-1 net elements. coarsen_count = std::min(cast_int<dof_id_type>(-n_elem_new / (twotodim-1)), max_elem_coarsen); } // Next, let's see if we can trade any refinement for coarsening while (coarsen_count < max_elem_coarsen && refine_count < max_elem_refine && coarsen_count < sorted_parent_error.size() && refine_count < sorted_error.size() && sorted_error[refine_count].first > sorted_parent_error[coarsen_count].first * _coarsen_threshold) { coarsen_count++; refine_count++; } // On a DistributedMesh, we need to communicate to know which remote ids // correspond to refinable elements dof_id_type successful_refine_count = 0; { std::vector<bool> is_refinable(max_elem_id, false); for (std::size_t i=0; i != sorted_error.size(); ++i) { dof_id_type eid = sorted_error[i].second; Elem * elem = _mesh.query_elem_ptr(eid); if (elem && elem->level() < _max_h_level) is_refinable[eid] = true; } this->comm().max(is_refinable); if (refine_count > max_elem_refine) refine_count = max_elem_refine; for (std::size_t i=0; i != sorted_error.size(); ++i) { if (successful_refine_count >= refine_count) break; dof_id_type eid = sorted_error[i].second; Elem * elem = _mesh.query_elem_ptr(eid); if (is_refinable[eid]) { if (elem) elem->set_refinement_flag(Elem::REFINE); successful_refine_count++; } } } // If we couldn't refine enough elements, don't coarsen too many // either if (coarsen_count < (refine_count - successful_refine_count)) coarsen_count = 0; else coarsen_count -= (refine_count - successful_refine_count); if (coarsen_count > max_elem_coarsen) coarsen_count = max_elem_coarsen; dof_id_type successful_coarsen_count = 0; if (coarsen_count) { for (std::size_t i=0; i != sorted_parent_error.size(); ++i) { if (successful_coarsen_count >= coarsen_count * twotodim) break; dof_id_type parent_id = sorted_parent_error[i].second; Elem * parent = _mesh.query_elem_ptr(parent_id); // On a DistributedMesh we skip remote elements if (!parent) continue; libmesh_assert(parent->has_children()); for (auto & elem : parent->child_ref_range()) { if (&elem != remote_elem) { libmesh_assert(elem.active()); elem.set_refinement_flag(Elem::COARSEN); successful_coarsen_count++; } } } } // Return true if we've done all the AMR/C we can if (!successful_coarsen_count && !successful_refine_count) return true; // And false if there may still be more to do. return false; }