void FeatureFloodCount::flood(const DofObject * dof_object, unsigned long current_idx, FeatureData * feature) { if (dof_object == nullptr) return; // Retrieve the id of the current entity auto entity_id = dof_object->id(); // Has this entity already been marked? - if so move along if (_entities_visited[current_idx].find(entity_id) != _entities_visited[current_idx].end()) return; // Mark this entity as visited _entities_visited[current_idx][entity_id] = true; // Determine which threshold to use based on whether this is an established region auto threshold = feature ? _step_connecting_threshold : _step_threshold; // Get the value of the current variable for the current entity Real entity_value; if (_is_elemental) { const Elem * elem = static_cast<const Elem *>(dof_object); std::vector<Point> centroid(1, elem->centroid()); _subproblem.reinitElemPhys(elem, centroid, 0); entity_value = _vars[current_idx]->sln()[0]; } else entity_value = _vars[current_idx]->getNodalValue(*static_cast<const Node *>(dof_object)); // This node hasn't been marked, is it in a feature? We must respect // the user-selected value of _use_less_than_threshold_comparison. if (_use_less_than_threshold_comparison && (entity_value < threshold)) return; if (!_use_less_than_threshold_comparison && (entity_value > threshold)) return; /** * If we reach this point (i.e. we haven't returned early from this routine), * we've found a new mesh entity that's part of a feature. */ auto map_num = _single_map_mode ? decltype(current_idx)(0) : current_idx; // New Feature (we need to create it and add it to our data structure) if (!feature) _partial_feature_sets[map_num].emplace_back(current_idx); // Get a handle to the feature we will update (always the last feature in the data structure) feature = &_partial_feature_sets[map_num].back(); // Insert the current entity into the local ids map feature->_local_ids.insert(entity_id); if (_is_elemental) visitElementalNeighbors(static_cast<const Elem *>(dof_object), current_idx, feature, /*expand_halos_only =*/false); else visitNodalNeighbors(static_cast<const Node *>(dof_object), current_idx, feature, /*expand_halos_only =*/false); }
void GrainTracker::expandHalos() { processor_id_type n_procs = _app.n_processors(); for (unsigned int map_num = 0; map_num < _maps_size; ++map_num) for (processor_id_type rank = 0; rank < n_procs; ++rank) for (std::vector<FeatureData>::iterator it = _partial_feature_sets[rank][map_num].begin(); it != _partial_feature_sets[rank][map_num].end(); ++it) { FeatureData & feature = *it; for (unsigned int halo_level = 1; halo_level < _halo_level; ++halo_level) { /** * Create a copy of the halo set so that as we insert new ids into the * set we don't continue to iterate on those new ids. */ std::set<dof_id_type> orig_halo_ids(feature._halo_ids); for (std::set<dof_id_type>::iterator entity_it = orig_halo_ids.begin(); entity_it != orig_halo_ids.end(); ++entity_it) { if (_is_elemental) visitElementalNeighbors(_mesh.elemPtr(*entity_it), feature._var_idx, &feature, /*recurse =*/false); else visitNodalNeighbors(_mesh.nodePtr(*entity_it), feature._var_idx, &feature, /*recurse =*/false); } } } }
PolycrystalICTools::AdjacencyMatrix<Real> PolycrystalICTools::buildElementalGrainAdjacencyMatrix( const std::map<dof_id_type, unsigned int> & element_to_grain, MooseMesh & mesh, const PeriodicBoundaries * pb, unsigned int n_grains) { AdjacencyMatrix<Real> adjacency_matrix(n_grains); // We can't call this in the constructor, it appears that _mesh_ptr is always NULL there. mesh.errorIfDistributedMesh("advanced_op_assignment = true"); std::vector<const Elem *> all_active_neighbors; std::vector<std::set<dof_id_type>> local_ids(n_grains); std::vector<std::set<dof_id_type>> halo_ids(n_grains); std::unique_ptr<PointLocatorBase> point_locator = mesh.getMesh().sub_point_locator(); for (const auto & elem : mesh.getMesh().active_element_ptr_range()) { std::map<dof_id_type, unsigned int>::const_iterator grain_it = element_to_grain.find(elem->id()); mooseAssert(grain_it != element_to_grain.end(), "Element not found in map"); unsigned int my_grain = grain_it->second; all_active_neighbors.clear(); // Loop over all neighbors (at the the same level as the current element) for (unsigned int i = 0; i < elem->n_neighbors(); ++i) { const Elem * neighbor_ancestor = elem->topological_neighbor(i, mesh, *point_locator, pb); if (neighbor_ancestor) // Retrieve only the active neighbors for each side of this element, append them to the list // of active neighbors neighbor_ancestor->active_family_tree_by_topological_neighbor( all_active_neighbors, elem, mesh, *point_locator, pb, false); } // Loop over all active element neighbors for (std::vector<const Elem *>::const_iterator neighbor_it = all_active_neighbors.begin(); neighbor_it != all_active_neighbors.end(); ++neighbor_it) { const Elem * neighbor = *neighbor_it; std::map<dof_id_type, unsigned int>::const_iterator grain_it2 = element_to_grain.find(neighbor->id()); mooseAssert(grain_it2 != element_to_grain.end(), "Element not found in map"); unsigned int their_grain = grain_it2->second; if (my_grain != their_grain) { /** * We've found a grain neighbor interface. In order to assign order parameters though, we * need to make sure that we build out a small buffer region to avoid literal "corner cases" * where nodes on opposite corners of a QUAD end up with the same OP because those nodes are * not nodal neighbors. To do that we'll build a halo region based on these interface nodes. * For now, we need to record the nodes inside of the grain and those outside of the grain. */ // First add corresponding element and grain information local_ids[my_grain].insert(elem->id()); local_ids[their_grain].insert(neighbor->id()); // Now add opposing element and grain information halo_ids[my_grain].insert(neighbor->id()); halo_ids[their_grain].insert(elem->id()); } // adjacency_matrix[my_grain][their_grain] = 1; } } // Build up the halos std::set<dof_id_type> set_difference; for (unsigned int i = 0; i < n_grains; ++i) { std::set<dof_id_type> orig_halo_ids(halo_ids[i]); for (unsigned int halo_level = 0; halo_level < PolycrystalICTools::HALO_THICKNESS; ++halo_level) { for (std::set<dof_id_type>::iterator entity_it = orig_halo_ids.begin(); entity_it != orig_halo_ids.end(); ++entity_it) { if (true) visitElementalNeighbors( mesh.elemPtr(*entity_it), mesh.getMesh(), *point_locator, pb, halo_ids[i]); else mooseError("Unimplemented"); } set_difference.clear(); std::set_difference( halo_ids[i].begin(), halo_ids[i].end(), local_ids[i].begin(), local_ids[i].end(), std::insert_iterator<std::set<dof_id_type>>(set_difference, set_difference.begin())); halo_ids[i].swap(set_difference); } } // Finally look at the halo intersections to build the connectivity graph std::set<dof_id_type> set_intersection; for (unsigned int i = 0; i < n_grains; ++i) for (unsigned int j = i + 1; j < n_grains; ++j) { set_intersection.clear(); std::set_intersection( halo_ids[i].begin(), halo_ids[i].end(), halo_ids[j].begin(), halo_ids[j].end(), std::insert_iterator<std::set<dof_id_type>>(set_intersection, set_intersection.begin())); if (!set_intersection.empty()) { adjacency_matrix(i, j) = 1.; adjacency_matrix(j, i) = 1.; } } return adjacency_matrix; }
void FeatureFloodCount::flood(const DofObject * dof_object, std::size_t current_index, FeatureData * feature) { if (dof_object == nullptr) return; // Retrieve the id of the current entity auto entity_id = dof_object->id(); // Has this entity already been marked? - if so move along if (_entities_visited[current_index].find(entity_id) != _entities_visited[current_index].end()) return; // See if the current entity either starts a new feature or continues an existing feature auto new_id = invalid_id; // Writable reference to hold an optional id; if (!isNewFeatureOrConnectedRegion(dof_object, current_index, feature, new_id)) return; /** * If we reach this point (i.e. we haven't returned early from this routine), * we've found a new mesh entity that's part of a feature. We need to mark * the entity as visited at this point (and not before!) to avoid infinite * recursion. If you mark the node too early you risk not coloring in a whole * feature any time a "connecting threshold" is used since we may have * already visited this entity earlier but it was in-between two thresholds. */ _entities_visited[current_index][entity_id] = true; auto map_num = _single_map_mode ? decltype(current_index)(0) : current_index; // New Feature (we need to create it and add it to our data structure) if (!feature) { _partial_feature_sets[map_num].emplace_back(current_index, _feature_count++, processor_id()); // Get a handle to the feature we will update (always the last feature in the data structure) feature = &_partial_feature_sets[map_num].back(); // If new_id is valid, we'll set it in the feature here. if (new_id != invalid_id) feature->_id = new_id; } // Insert the current entity into the local ids map feature->_local_ids.insert(entity_id); /** * See if this particular entity cell contributes to the centroid calculation. We * only deal with elemental floods and only count it if it's owned by the current * processor to avoid skewing the result. */ if (_is_elemental && processor_id() == dof_object->processor_id()) { const Elem * elem = static_cast<const Elem *>(dof_object); // Keep track of how many elements participate in the centroid averaging feature->_vol_count++; // Sum the centroid values for now, we'll average them later feature->_centroid += elem->centroid(); // Does the volume intersect the boundary? if (_all_boundary_entity_ids.find(elem->id()) != _all_boundary_entity_ids.end()) feature->_intersects_boundary = true; } if (_is_elemental) visitElementalNeighbors(static_cast<const Elem *>(dof_object), current_index, feature, /*expand_halos_only =*/false); else visitNodalNeighbors(static_cast<const Node *>(dof_object), current_index, feature, /*expand_halos_only =*/false); }