void NodalFloodCount::mergeSets() { Moose::perf_log.push("mergeSets()", "NodalFloodCount"); std::set<unsigned int> set_union; std::insert_iterator<std::set<unsigned int> > set_union_inserter(set_union, set_union.begin()); for (unsigned int map_num = 0; map_num < _maps_size; ++map_num) { std::list<BubbleData>::iterator end = _bubble_sets[map_num].end(); for (std::list<BubbleData>::iterator it1 = _bubble_sets[map_num].begin(); it1 != end; /* No increment */) { bool need_it1_increment = true; for (std::list<BubbleData>::iterator it2 = it1; it2 != end; ++it2) { if (it1 != it2 && // Make sure that these iterators aren't pointing at the same set it1->_var_idx == it2->_var_idx && // and that the sets have matching variable indices... // then See if they overlap setsIntersect(it1->_nodes.begin(), it1->_nodes.end(), it2->_nodes.begin(), it2->_nodes.end())) { // Merge these two sets and remove the duplicate set set_union.clear(); std::set_union(it1->_nodes.begin(), it1->_nodes.end(), it2->_nodes.begin(), it2->_nodes.end(), set_union_inserter); // Put the merged set in the latter iterator so that we'll compare earlier sets to it again it2->_nodes = set_union; _bubble_sets[map_num].erase(it1++); // don't increment the outer loop since we just deleted it incremented need_it1_increment = false; // break out of the inner loop and move on break; } } if (need_it1_increment) ++it1; } } Moose::perf_log.pop("mergeSets()", "NodalFloodCount"); }
void FeatureFloodCount::calculateBubbleVolumes() { Moose::perf_log.push("calculateBubbleVolume()", "FeatureFloodCount"); // Figure out which bubbles intersect the boundary if the user has enabled that capability. if (_compute_boundary_intersecting_volume) { // Create a std::set of node IDs which are on the boundary called all_boundary_node_ids. std::set<dof_id_type> all_boundary_node_ids; // Iterate over the boundary nodes, putting them into the std::set data structure MooseMesh::bnd_node_iterator boundary_nodes_it = _mesh.bndNodesBegin(), boundary_nodes_end = _mesh.bndNodesEnd(); for (; boundary_nodes_it != boundary_nodes_end; ++boundary_nodes_it) { BndNode * boundary_node = *boundary_nodes_it; all_boundary_node_ids.insert(boundary_node->_node->id()); } // For each of the _maps_size BubbleData lists, determine if the set // of nodes includes any boundary nodes. for (unsigned int map_num = 0; map_num < _maps_size; ++map_num) { std::list<BubbleData>::iterator bubble_it = _bubble_sets[map_num].begin(), bubble_end = _bubble_sets[map_num].end(); // Determine boundary intersection for each BubbleData object for (; bubble_it != bubble_end; ++bubble_it) bubble_it->_intersects_boundary = setsIntersect(all_boundary_node_ids.begin(), all_boundary_node_ids.end(), bubble_it->_entity_ids.begin(), bubble_it->_entity_ids.end()); } } // Size our temporary data structure std::vector<std::vector<Real> > bubble_volumes(_maps_size); for (unsigned int map_num = 0; map_num < _maps_size; ++map_num) bubble_volumes[map_num].resize(_bubble_sets[map_num].size()); // Clear pre-existing values and allocate space to store the volume // of the boundary-intersecting grains for each variable. _total_volume_intersecting_boundary.clear(); _total_volume_intersecting_boundary.resize(_maps_size); // Loop over the active local elements. For each variable, and for // each BubbleData object, check whether a majority of the element's // nodes belong to that Bubble, and if so assign the element's full // volume to that bubble. const MeshBase::const_element_iterator el_end = _mesh.getMesh().active_local_elements_end(); for (MeshBase::const_element_iterator el = _mesh.getMesh().active_local_elements_begin(); el != el_end; ++el) { Elem * elem = *el; unsigned int elem_n_nodes = elem->n_nodes(); Real curr_volume = elem->volume(); for (unsigned int map_num = 0; map_num < _maps_size; ++map_num) { std::list<BubbleData>::const_iterator bubble_it = _bubble_sets[map_num].begin(), bubble_end = _bubble_sets[map_num].end(); for (unsigned int bubble_counter = 0; bubble_it != bubble_end; ++bubble_it, ++bubble_counter) { // Count the number of nodes on this element which are flooded. unsigned int flooded_nodes = 0; for (unsigned int node = 0; node < elem_n_nodes; ++node) { dof_id_type node_id = elem->node(node); if (bubble_it->_entity_ids.find(node_id) != bubble_it->_entity_ids.end()) ++flooded_nodes; } // If a majority of the nodes for this element are flooded, // assign its volume to the current bubble_counter entry. if (flooded_nodes >= elem_n_nodes / 2) { bubble_volumes[map_num][bubble_counter] += curr_volume; // If the current bubble also intersects the boundary, also // accumlate the volume into the total volume of bubbles // which intersect the boundary. if (bubble_it->_intersects_boundary) _total_volume_intersecting_boundary[map_num] += curr_volume; } } } } // If we're calculating boundary-intersecting volumes, we have to normalize it by the // volume of the entire domain. if (_compute_boundary_intersecting_volume) { // Compute the total area using a bounding box. FIXME: this // assumes the domain is rectangular and 2D, and is probably a // little expensive so we should only do it once if possible. MeshTools::BoundingBox bbox = MeshTools::bounding_box(_mesh); Real total_volume = (bbox.max()(0)-bbox.min()(0))*(bbox.max()(1)-bbox.min()(1)); // Sum up the partial boundary grain volume contributions from all processors _communicator.sum(_total_volume_intersecting_boundary); // Scale the boundary intersecting grain volumes by the total domain volume for (unsigned int i = 0; i<_total_volume_intersecting_boundary.size(); ++i) _total_volume_intersecting_boundary[i] /= total_volume; } // Stick all the partial bubble volumes in one long single vector to be gathered on the root processor for (unsigned int map_num = 0; map_num < _maps_size; ++map_num) _all_bubble_volumes.insert(_all_bubble_volumes.end(), bubble_volumes[map_num].begin(), bubble_volumes[map_num].end()); // do all the sums! _communicator.sum(_all_bubble_volumes); std::sort(_all_bubble_volumes.begin(), _all_bubble_volumes.end(), std::greater<Real>()); Moose::perf_log.pop("calculateBubbleVolume()", "FeatureFloodCount"); }
void FeatureFloodCount::mergeSets(bool use_periodic_boundary_info) { Moose::perf_log.push("mergeSets()", "FeatureFloodCount"); std::set<dof_id_type> set_union; std::insert_iterator<std::set<dof_id_type> > set_union_inserter(set_union, set_union.begin()); /** * If map_num <= n_processors (normal case), each processor up to map_num will handle one list * of nodes and receive the merged nodes from other processors for all other lists. */ for (unsigned int map_num = 0; map_num < _maps_size; ++map_num) { unsigned int owner_id = map_num % _app.n_processors(); if (_single_map_mode || owner_id == processor_id()) { // Get an iterator pointing to the end of the list, we'll reuse it several times in the merge algorithm below std::list<BubbleData>::iterator end = _bubble_sets[map_num].end(); // Next add periodic neighbor information if requested to the BubbleData objects if (use_periodic_boundary_info) for (std::list<BubbleData>::iterator it = _bubble_sets[map_num].begin(); it != end; ++it) appendPeriodicNeighborNodes(*it); // Finally start our merge loops for (std::list<BubbleData>::iterator it1 = _bubble_sets[map_num].begin(); it1 != end; /* No increment */) { bool need_it1_increment = true; for (std::list<BubbleData>::iterator it2 = it1; it2 != end; ++it2) { if (it1 != it2 && // Make sure that these iterators aren't pointing at the same set it1->_var_idx == it2->_var_idx && // and that the sets have matching variable indices... (setsIntersect(it1->_entity_ids.begin(), it1->_entity_ids.end(), // Do they overlap on the current entity type? OR.. it2->_entity_ids.begin(), it2->_entity_ids.end()) || (use_periodic_boundary_info && // Are we merging across periodic boundaries? AND setsIntersect(it1->_periodic_nodes.begin(), it1->_periodic_nodes.end(), // Do they overlap on periodic nodes? it2->_periodic_nodes.begin(), it2->_periodic_nodes.end()) ) ) ) { // Merge these two entity sets set_union.clear(); std::set_union(it1->_entity_ids.begin(), it1->_entity_ids.end(), it2->_entity_ids.begin(), it2->_entity_ids.end(), set_union_inserter); // Put the merged set in the latter iterator so that we'll compare earlier sets to it again it2->_entity_ids = set_union; // If we are merging periodic boundaries we'll need to merge those nodes too if (use_periodic_boundary_info) { set_union.clear(); std::set_union(it1->_periodic_nodes.begin(), it1->_periodic_nodes.end(), it2->_periodic_nodes.begin(), it2->_periodic_nodes.end(), set_union_inserter); it2->_periodic_nodes = set_union; } // Now remove the merged set, the one we didn't update (it1) _bubble_sets[map_num].erase(it1++); // don't increment the outer loop since we just deleted it incremented need_it1_increment = false; // break out of the inner loop and move on break; } } if (need_it1_increment) ++it1; } } } if (!_single_map_mode) for (unsigned int map_num = 0; map_num < _maps_size; ++map_num) // Now communicate this list with all the other processors communicateOneList(_bubble_sets[map_num], map_num % _app.n_processors(), map_num); Moose::perf_log.pop("mergeSets()", "FeatureFloodCount"); }
bool FeatureFloodCount::FeatureData::ghostedIntersect(const FeatureData & rhs) const { return setsIntersect(_ghosted_ids.begin(), _ghosted_ids.end(), rhs._ghosted_ids.begin(), rhs._ghosted_ids.end()); }
bool FeatureFloodCount::FeatureData::periodicBoundariesIntersect(const FeatureData & rhs) const { return setsIntersect(_periodic_nodes.begin(), _periodic_nodes.end(), rhs._periodic_nodes.begin(), rhs._periodic_nodes.end()); }
bool FeatureFloodCount::FeatureData::halosIntersect(const FeatureData & rhs) const { return setsIntersect(_halo_ids.begin(), _halo_ids.end(), rhs._halo_ids.begin(), rhs._halo_ids.end()); }
bool GrainTracker::attemptGrainRenumber(MooseSharedPointer<FeatureData> grain, unsigned int grain_id, unsigned int depth, unsigned int max) { // End the recursion of our breadth first search if (depth > max) return false; unsigned int curr_var_idx = grain->_var_idx; std::map<Node *, CacheValues> cache; std::vector<std::list<GrainDistance> > min_distances(_vars.size()); /** * We have two grains that are getting close represented by the same order parameter. * We need to map to the variable whose closest grain to this one is furthest away by sphere to sphere distance. */ computeMinDistancesFromGrain(grain, min_distances); /** * We have a vector of the distances to the closest grains represented by each of our variables. We just need to pick * a suitable grain to replace with. We will start with the maximum of this this list: (max of the mins), but will settle * for next to largest and so forth as we make more attempts at remapping grains. This is a graph coloring problem so * more work will be required to optimize this process. * * Note: We don't have an explicit check here to avoid remapping a variable to itself. This is unnecessary since the * min_distance of a variable is explicitly set up above. */ std::sort(min_distances.begin(), min_distances.end(), GrainDistanceSorter()); _console << "\n********************************************\nDistances list for grain " << grain_id << '\n'; for (unsigned int i = 0; i < min_distances.size(); ++i) { for (std::list<GrainDistance>::iterator min_it = min_distances[i].begin(); min_it != min_distances[i].end(); ++min_it) _console << min_it->_distance << ": " << min_it->_grain_id << ": " << min_it->_var_index << '\n'; _console << '\n'; } for (unsigned int i = 0; i < min_distances.size(); ++i) { std::list<GrainDistance>::const_iterator target_it = min_distances[i].begin(); if (target_it == min_distances[i].end()) continue; // If the distance is positive we can just remap and be done if (target_it->_distance > 0) { Moose::out << COLOR_GREEN << "- Depth " << depth << ": Remapping grain #" << grain_id << " from variable index " << curr_var_idx << " to " << target_it->_var_index << " whose closest grain (#" << target_it->_grain_id << ") is at a distance of " << target_it->_distance << "\n" << COLOR_DEFAULT; swapSolutionValues(grain, target_it->_var_index, cache, BYPASS, depth); return true; } // If the distance isn't positive we just need to make sure that none of the grains represented by the // target variable index would intersect this one if we were to remap std::list<GrainDistance>::const_iterator next_target_it = target_it; bool intersection_hit = false; std::ostringstream oss; while (!intersection_hit && next_target_it != min_distances[i].end()) { if (next_target_it->_distance > 0) break; mooseAssert(_unique_grains.find(next_target_it->_grain_id) != _unique_grains.end(), "Error in indexing target grain in attemptGrainRenumber"); MooseSharedPointer<FeatureData> next_target_grain = _unique_grains[next_target_it->_grain_id]; // If any grains touch we're done here if (setsIntersect(grain->_halo_ids.begin(), grain->_halo_ids.end(), next_target_grain->_halo_ids.begin(), next_target_grain->_halo_ids.end())) intersection_hit = true; else oss << " #" << next_target_it->_grain_id; ++next_target_it; } if (!intersection_hit) { Moose::out << COLOR_GREEN << "- Depth " << depth << ": Remapping grain #" << grain_id << " from variable index " << curr_var_idx << " to " << target_it->_var_index << " whose closest grain(s):" << oss.str() << " are inside our bounding sphere but whose halo(s) are not touching.\n" << COLOR_DEFAULT; swapSolutionValues(grain, target_it->_var_index, cache, BYPASS, depth); return true; } // If we reach this part of the loop, there is no simple renumbering that can be done. mooseAssert(_unique_grains.find(target_it->_grain_id) != _unique_grains.end(), "Error in indexing target grain in attemptGrainRenumber"); MooseSharedPointer<FeatureData> target_grain = _unique_grains[target_it->_grain_id]; // Make sure this grain isn't marked. If it is, we can't recurse here if (target_grain->_merged) return false; // Save the solution values in case we overright them during recursion swapSolutionValues(grain, target_it->_var_index, cache, FILL, depth); // TODO: Make sure this distance is -1 or higher or fine intersections only exist for a single variable // Propose a new variable index for the current grain and recurse grain->_var_idx = target_it->_var_index; grain->_merged = true; if (attemptGrainRenumber(target_grain, target_it->_grain_id, depth+1, max)) { // SUCCESS! Moose::out << COLOR_GREEN << "- Depth " << depth << ": Remapping grain #" << grain_id << " from variable index " << curr_var_idx << " to " << target_it->_var_index << '\n' << COLOR_DEFAULT; // NOTE: swapSolutionValues currently reads the current variable index off the grain. We need to set // back here before calling this method. grain->_var_idx = curr_var_idx; swapSolutionValues(grain, target_it->_var_index, cache, USE, depth); return true; } else // Need to set our var index back after failed recursive step grain->_var_idx = curr_var_idx; // Always "unmark" the grain after the recursion so it can be used by other remap operations grain->_merged = false; } return false; }
void GrainTracker::remapGrains() { // Don't remap grains if the current simulation step is before the specified tracking step if (_t_step < _tracking_step) return; _console << "Running remap Grains" << std::endl; /** * The remapping algorithm is recursive. We will reuse the "merged" flag in the FeatureData * to track which grains are currently being remapped so we don't have runaway recursion. * TODO: Possibly rename this variable? */ for (std::map<unsigned int, MooseSharedPointer<FeatureData> >::iterator grain_it = _unique_grains.begin(); grain_it != _unique_grains.end(); ++grain_it) grain_it->second->_merged = false; /** * Loop over each grain and see if any grains represented by the same variable are "touching" */ bool grains_remapped; do { grains_remapped = false; for (std::map<unsigned int, MooseSharedPointer<FeatureData> >::iterator grain_it1 = _unique_grains.begin(); grain_it1 != _unique_grains.end(); ++grain_it1) { if (grain_it1->second->_status == INACTIVE) continue; for (std::map<unsigned int, MooseSharedPointer<FeatureData> >::iterator grain_it2 = _unique_grains.begin(); grain_it2 != _unique_grains.end(); ++grain_it2) { // Don't compare a grain with itself and don't try to remap inactive grains if (grain_it1 == grain_it2 || grain_it2->second->_status == INACTIVE) continue; if (grain_it1->second->_var_idx == grain_it2->second->_var_idx && // Are the grains represented by the same variable? grain_it1->second->isStichable(*grain_it2->second) && // If so, do their bboxes intersect (coarse level check)? setsIntersect(grain_it1->second->_halo_ids.begin(), // If so, do they actually overlap (tight "hull" check)? grain_it1->second->_halo_ids.end(), grain_it2->second->_halo_ids.begin(), grain_it2->second->_halo_ids.end())) { Moose::out << COLOR_YELLOW << "Grain #" << grain_it1->first << " intersects Grain #" << grain_it2->first << " (variable index: " << grain_it1->second->_var_idx << ")\n" << COLOR_DEFAULT; for (unsigned int max = 0; max <= _max_renumbering_recursion; ++max) if (max < _max_renumbering_recursion) { if (attemptGrainRenumber(grain_it1->second, grain_it1->first, 0, max) || attemptGrainRenumber(grain_it2->second, grain_it2->first, 0, max)) break; } else if (!attemptGrainRenumber(grain_it1->second, grain_it1->first, 0, max) && !attemptGrainRenumber(grain_it2->second, grain_it2->first, 0, max)) mooseError(COLOR_RED << "Unable to find any suitable grains for remapping. Perhaps you need more op variables?\n\n" << COLOR_DEFAULT); grains_remapped = true; } } } } while (grains_remapped); }
void FeatureFloodCount::mergeSets(bool use_periodic_boundary_info) { Moose::perf_log.push("mergeSets()", "FeatureFloodCount"); std::set<dof_id_type> set_union; processor_id_type n_procs = _app.n_processors(); for (unsigned int map_num = 0; map_num < _maps_size; ++map_num) { for (processor_id_type rank1 = 0; rank1 < n_procs; ++rank1) { REPEAT_MERGE_LOOPS: for (processor_id_type rank2 = 0; rank2 < n_procs; ++rank2) { for (std::vector<FeatureData>::iterator it1 = _partial_feature_sets[rank1][map_num].begin(); it1 != _partial_feature_sets[rank1][map_num].end(); /* no increment ++it1 */) { if (it1->_merged) { ++it1; continue; } bool region_merged = false; for (std::vector<FeatureData>::iterator it2 = _partial_feature_sets[rank2][map_num].begin(); it2 != _partial_feature_sets[rank2][map_num].end(); ++it2) { bool pb_intersect = false; if (it1 != it2 && // Make sure that these iterators aren't pointing at the same set !it2->_merged && // and that it2 is not merged (it1 was already checked) it1->_var_idx == it2->_var_idx && // and that the sets have matching variable indices ((use_periodic_boundary_info && // and (if merging across periodic nodes (pb_intersect = setsIntersect(it1->_periodic_nodes.begin(), // do those periodic nodes intersect? it1->_periodic_nodes.end(), it2->_periodic_nodes.begin(), it2->_periodic_nodes.end()))) || // or (it1->isStichable(*it2) && // if the region bboxes intersect (setsIntersect(it1->_ghosted_ids.begin(), // do those ghosted nodes intersect?) it1->_ghosted_ids.end(), it2->_ghosted_ids.begin(), it2->_ghosted_ids.end())) ) ) ) { /** * Even though we've determined that these two partial regions need to be merged, we don't necessarily know if the _ghost_ids intersect. * We could be in this branch because the periodic boundaries intersect but that doesn't tell us anything about whether or not the ghost_region * also intersects. If the _ghost_ids intersect, that means that we are merging along a periodic boundary, not across one. In this case the * bounding box(s) need to be expanded. */ set_union.clear(); std::set_union(it1->_ghosted_ids.begin(), it1->_ghosted_ids.end(), it2->_ghosted_ids.begin(), it2->_ghosted_ids.end(), std::insert_iterator<std::set<dof_id_type> >(set_union, set_union.begin())); // Was there overlap in the physical region? bool physical_intersection = (it1->_ghosted_ids.size() + it2->_ghosted_ids.size() > set_union.size()); it1->_ghosted_ids.swap(set_union); it2->_ghosted_ids.clear(); set_union.clear(); std::set_union(it1->_periodic_nodes.begin(), it1->_periodic_nodes.end(), it2->_periodic_nodes.begin(), it2->_periodic_nodes.end(), std::insert_iterator<std::set<dof_id_type> >(set_union, set_union.begin())); it1->_periodic_nodes.swap(set_union); it2->_periodic_nodes.clear(); set_union.clear(); std::set_union(it1->_local_ids.begin(), it1->_local_ids.end(), it2->_local_ids.begin(), it2->_local_ids.end(), std::insert_iterator<std::set<dof_id_type> >(set_union, set_union.begin())); it1->_local_ids.swap(set_union); it2->_local_ids.clear(); set_union.clear(); std::set_union(it1->_halo_ids.begin(), it1->_halo_ids.end(), it2->_halo_ids.begin(), it2->_halo_ids.end(), std::insert_iterator<std::set<dof_id_type> >(set_union, set_union.begin())); it1->_halo_ids.swap(set_union); it2->_halo_ids.clear(); /** * If we had a physical intersection, we need to expand boxes. If we had a virtual (periodic) intersection we need to preserve * all of the boxes from each of the regions' sets. */ if (physical_intersection) it1->expandBBox(*it2); else std::copy(it2->_bboxes.begin(), it2->_bboxes.end(), std::back_inserter(it1->_bboxes)); // Update the min feature id it1->_min_entity_id = std::min(it1->_min_entity_id, it2->_min_entity_id); // Set the flag on the merged set so we don't revisit it again it2->_merged = true; // Something was merged so we'll need to repeat this loop region_merged = true; } } // it2 loop // Don't increment if we had a merge, we need to retry earlier candidates again if (!region_merged) ++it1; else goto REPEAT_MERGE_LOOPS; } // it1 loop } // rank2 loop } // rank1 loop } // map loop for (unsigned int map_num = 0; map_num < _maps_size; ++map_num) _feature_sets[map_num].clear(); // Now consolidate the data structure _feature_count = 0; for (processor_id_type rank = 0; rank < n_procs; ++rank) for (unsigned int map_num = 0; map_num < _maps_size; ++map_num) { for (std::vector<FeatureData>::iterator it = _partial_feature_sets[rank][map_num].begin(); it != _partial_feature_sets[rank][map_num].end(); ++it) { if (!it->_merged) { _feature_sets[map_num].push_back(MooseSharedPointer<FeatureData>(new FeatureData(*it))); ++_feature_count; } } _partial_feature_sets[rank][map_num].clear(); } Moose::perf_log.pop("mergeSets()", "FeatureFloodCount"); }