void NearestNodeLocator::findNodes() { Moose::perf_log.push("NearestNodeLocator::findNodes()", "Execution"); /** * If this is the first time through we're going to build up a "neighborhood" of nodes * surrounding each of the slave nodes. This will speed searching later. */ if (_first) { _first=false; // Trial slave nodes are all the nodes on the slave side // We only keep the ones that are either on this processor or are likely // to interact with elements on this processor (ie nodes owned by this processor // are in the "neighborhood" of the slave node std::vector<dof_id_type> trial_slave_nodes; std::vector<dof_id_type> trial_master_nodes; // Build a bounding box. No reason to consider nodes outside of our inflated BB MeshTools::BoundingBox * my_inflated_box = NULL; const std::vector<Real> & inflation = _mesh.getGhostedBoundaryInflation(); // This means there was a user specified inflation... so we can build a BB if (inflation.size() > 0) { MeshTools::BoundingBox my_box = MeshTools::processor_bounding_box(_mesh, _mesh.processor_id()); Real distance_x = 0; Real distance_y = 0; Real distance_z = 0; distance_x = inflation[0]; if (inflation.size() > 1) distance_y = inflation[1]; if (inflation.size() > 2) distance_z = inflation[2]; my_inflated_box = new MeshTools::BoundingBox(Point(my_box.first(0)-distance_x, my_box.first(1)-distance_y, my_box.first(2)-distance_z), Point(my_box.second(0)+distance_x, my_box.second(1)+distance_y, my_box.second(2)+distance_z)); } // Data structures to hold the Nodal Boundary conditions ConstBndNodeRange & bnd_nodes = *_mesh.getBoundaryNodeRange(); for (const auto & bnode : bnd_nodes) { BoundaryID boundary_id = bnode->_bnd_id; dof_id_type node_id = bnode->_node->id(); // If we have a BB only consider saving this node if it's in our inflated BB if (!my_inflated_box || (my_inflated_box->contains_point(*bnode->_node))) { if (boundary_id == _boundary1) trial_master_nodes.push_back(node_id); else if (boundary_id == _boundary2) trial_slave_nodes.push_back(node_id); } } // don't need the BB anymore delete my_inflated_box; const std::map<dof_id_type, std::vector<dof_id_type> > & node_to_elem_map = _mesh.nodeToElemMap(); NodeIdRange trial_slave_node_range(trial_slave_nodes.begin(), trial_slave_nodes.end(), 1); SlaveNeighborhoodThread snt(_mesh, trial_master_nodes, node_to_elem_map, _mesh.getPatchSize()); Threads::parallel_reduce(trial_slave_node_range, snt); _slave_nodes = snt._slave_nodes; _neighbor_nodes = snt._neighbor_nodes; for (const auto & dof : snt._ghosted_elems) _subproblem.addGhostedElem(dof); // Cache the slave_node_range so we don't have to build it each time _slave_node_range = new NodeIdRange(_slave_nodes.begin(), _slave_nodes.end(), 1); } _nearest_node_info.clear(); NearestNodeThread nnt(_mesh, _neighbor_nodes); Threads::parallel_reduce(*_slave_node_range, nnt); _max_patch_percentage = nnt._max_patch_percentage; _nearest_node_info = nnt._nearest_node_info; Moose::perf_log.pop("NearestNodeLocator::findNodes()", "Execution"); }
void NearestNodeLocator::findNodes() { Moose::perf_log.push("NearestNodeLocator::findNodes()", "Execution"); /** * If this is the first time through we're going to build up a "neighborhood" of nodes * surrounding each of the slave nodes. This will speed searching later. */ const std::map<dof_id_type, std::vector<dof_id_type>> & node_to_elem_map = _mesh.nodeToElemMap(); if (_first) { _first = false; // Trial slave nodes are all the nodes on the slave side // We only keep the ones that are either on this processor or are likely // to interact with elements on this processor (ie nodes owned by this processor // are in the "neighborhood" of the slave node std::vector<dof_id_type> trial_slave_nodes; std::vector<dof_id_type> trial_master_nodes; // Build a bounding box. No reason to consider nodes outside of our inflated BB std::unique_ptr<BoundingBox> my_inflated_box = nullptr; const std::vector<Real> & inflation = _mesh.getGhostedBoundaryInflation(); // This means there was a user specified inflation... so we can build a BB if (inflation.size() > 0) { BoundingBox my_box = MeshTools::create_local_bounding_box(_mesh); Point distance; for (unsigned int i = 0; i < inflation.size(); ++i) distance(i) = inflation[i]; my_inflated_box = libmesh_make_unique<BoundingBox>(my_box.first - distance, my_box.second + distance); } // Data structures to hold the boundary nodes ConstBndNodeRange & bnd_nodes = *_mesh.getBoundaryNodeRange(); for (const auto & bnode : bnd_nodes) { BoundaryID boundary_id = bnode->_bnd_id; dof_id_type node_id = bnode->_node->id(); // If we have a BB only consider saving this node if it's in our inflated BB if (!my_inflated_box || (my_inflated_box->contains_point(*bnode->_node))) { if (boundary_id == _boundary1) trial_master_nodes.push_back(node_id); else if (boundary_id == _boundary2) trial_slave_nodes.push_back(node_id); } } // Convert trial master nodes to a vector of Points. This will be used to // construct the Kdtree. std::vector<Point> master_points(trial_master_nodes.size()); for (unsigned int i = 0; i < trial_master_nodes.size(); ++i) { const Node & node = _mesh.nodeRef(trial_master_nodes[i]); master_points[i] = node; } // Create object kd_tree of class KDTree using the coordinates of trial // master nodes. KDTree kd_tree(master_points, _mesh.getMaxLeafSize()); NodeIdRange trial_slave_node_range(trial_slave_nodes.begin(), trial_slave_nodes.end(), 1); SlaveNeighborhoodThread snt( _mesh, trial_master_nodes, node_to_elem_map, _mesh.getPatchSize(), kd_tree); Threads::parallel_reduce(trial_slave_node_range, snt); _slave_nodes = snt._slave_nodes; _neighbor_nodes = snt._neighbor_nodes; // If 'iteration' patch update strategy is used, a second neighborhood // search using the ghosting_patch_size, which is larger than the regular // patch_size used for contact search, is conducted. The ghosted element set // given by this search is used for ghosting the elements connected to the // slave and neighboring master nodes. if (_patch_update_strategy == Moose::Iteration) { SlaveNeighborhoodThread snt_ghosting( _mesh, trial_master_nodes, node_to_elem_map, _mesh.getGhostingPatchSize(), kd_tree); Threads::parallel_reduce(trial_slave_node_range, snt_ghosting); for (const auto & dof : snt_ghosting._ghosted_elems) _subproblem.addGhostedElem(dof); } else { for (const auto & dof : snt._ghosted_elems) _subproblem.addGhostedElem(dof); } // Cache the slave_node_range so we don't have to build it each time _slave_node_range = new NodeIdRange(_slave_nodes.begin(), _slave_nodes.end(), 1); } _nearest_node_info.clear(); NearestNodeThread nnt(_mesh, _neighbor_nodes); Threads::parallel_reduce(*_slave_node_range, nnt); _max_patch_percentage = nnt._max_patch_percentage; _nearest_node_info = nnt._nearest_node_info; if (_patch_update_strategy == Moose::Iteration) { // Get the set of elements that are currently being ghosted std::set<dof_id_type> ghost = _subproblem.ghostedElems(); for (const auto & node_id : *_slave_node_range) { const Node * nearest_node = _nearest_node_info[node_id]._nearest_node; // Check if the elements attached to the nearest node are within the ghosted // set of elements. If not produce an error. auto node_to_elem_pair = node_to_elem_map.find(nearest_node->id()); if (node_to_elem_pair != node_to_elem_map.end()) { const std::vector<dof_id_type> & elems_connected_to_node = node_to_elem_pair->second; for (const auto & dof : elems_connected_to_node) if (std::find(ghost.begin(), ghost.end(), dof) == ghost.end() && _mesh.elemPtr(dof)->processor_id() != _mesh.processor_id()) mooseError("Error in NearestNodeLocator : The nearest neighbor lies outside the " "ghosted set of elements. Increase the ghosting_patch_size parameter in the " "mesh block and try again."); } } } Moose::perf_log.pop("NearestNodeLocator::findNodes()", "Execution"); }