www.mooseframework.org
NearestNodeLocator.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #include "NearestNodeLocator.h"
11 #include "MooseMesh.h"
12 #include "SubProblem.h"
14 #include "NearestNodeThread.h"
15 #include "Moose.h"
16 #include "KDTree.h"
17 #include "Conversion.h"
18 #include "MooseApp.h"
19 
20 // libMesh
21 #include "libmesh/boundary_info.h"
22 #include "libmesh/elem.h"
23 #include "libmesh/plane.h"
24 #include "libmesh/mesh_tools.h"
25 
27  MooseMesh & mesh,
28  BoundaryID boundary1,
29  BoundaryID boundary2)
30  : Restartable(subproblem.getMooseApp(),
31  Moose::stringify(boundary1) + Moose::stringify(boundary2),
32  "NearestNodeLocator",
33  0),
34  PerfGraphInterface(subproblem.getMooseApp().perfGraph(),
35  "NearestNodeLocator_" + Moose::stringify(boundary1) + "_" +
36  Moose::stringify(boundary2)),
37  _subproblem(subproblem),
38  _mesh(mesh),
39  _slave_node_range(NULL),
40  _boundary1(boundary1),
41  _boundary2(boundary2),
42  _first(true),
43  _patch_update_strategy(_mesh.getPatchUpdateStrategy()),
44  _find_nodes_timer(registerTimedSection("findNodes", 3)),
45  _update_patch_timer(registerTimedSection("updatePatch", 3)),
46  _reinit_timer(registerTimedSection("reinit", 3)),
47  _update_ghosted_elems_timer(registerTimedSection("updateGhostedElems", 5))
48 {
49  /*
50  //sanity check on boundary ids
51  const std::set<BoundaryID>& bids=_mesh.getBoundaryIDs();
52  std::set<BoundaryID>::const_iterator sit;
53  sit=bids.find(_boundary1);
54  if (sit == bids.end())
55  mooseError("NearestNodeLocator being created for boundaries ", _boundary1, " and ", _boundary2,
56  ", but boundary ", _boundary1, " does not exist");
57  sit=bids.find(_boundary2);
58  if (sit == bids.end())
59  mooseError("NearestNodeLocator being created for boundaries ", _boundary1, " and ", _boundary2,
60  ", but boundary ", _boundary2, " does not exist");
61  */
62 }
63 
65 
66 void
68 {
69  TIME_SECTION(_find_nodes_timer);
70 
75  const std::map<dof_id_type, std::vector<dof_id_type>> & node_to_elem_map = _mesh.nodeToElemMap();
76 
77  if (_first)
78  {
79  _first = false;
80 
81  // Trial slave nodes are all the nodes on the slave side
82  // We only keep the ones that are either on this processor or are likely
83  // to interact with elements on this processor (ie nodes owned by this processor
84  // are in the "neighborhood" of the slave node
85  std::vector<dof_id_type> trial_slave_nodes;
86  std::vector<dof_id_type> trial_master_nodes;
87 
88  // Build a bounding box. No reason to consider nodes outside of our inflated BB
89  std::unique_ptr<BoundingBox> my_inflated_box = nullptr;
90 
91  const std::vector<Real> & inflation = _mesh.getGhostedBoundaryInflation();
92 
93  // This means there was a user specified inflation... so we can build a BB
94  if (inflation.size() > 0)
95  {
96  BoundingBox my_box = MeshTools::create_local_bounding_box(_mesh);
97 
98  Point distance;
99  for (unsigned int i = 0; i < inflation.size(); ++i)
100  distance(i) = inflation[i];
101 
102  my_inflated_box =
103  libmesh_make_unique<BoundingBox>(my_box.first - distance, my_box.second + distance);
104  }
105 
106  // Data structures to hold the boundary nodes
108  for (const auto & bnode : bnd_nodes)
109  {
110  BoundaryID boundary_id = bnode->_bnd_id;
111  dof_id_type node_id = bnode->_node->id();
112 
113  // If we have a BB only consider saving this node if it's in our inflated BB
114  if (!my_inflated_box || (my_inflated_box->contains_point(*bnode->_node)))
115  {
116  if (boundary_id == _boundary1)
117  trial_master_nodes.push_back(node_id);
118  else if (boundary_id == _boundary2)
119  trial_slave_nodes.push_back(node_id);
120  }
121  }
122 
123  // Convert trial master nodes to a vector of Points. This will be used to
124  // construct the Kdtree.
125  std::vector<Point> master_points(trial_master_nodes.size());
126  for (unsigned int i = 0; i < trial_master_nodes.size(); ++i)
127  {
128  const Node & node = _mesh.nodeRef(trial_master_nodes[i]);
129  master_points[i] = node;
130  }
131 
132  // Create object kd_tree of class KDTree using the coordinates of trial
133  // master nodes.
134  KDTree kd_tree(master_points, _mesh.getMaxLeafSize());
135 
136  NodeIdRange trial_slave_node_range(trial_slave_nodes.begin(), trial_slave_nodes.end(), 1);
137 
139  _mesh, trial_master_nodes, node_to_elem_map, _mesh.getPatchSize(), kd_tree);
140 
141  Threads::parallel_reduce(trial_slave_node_range, snt);
142 
143  _slave_nodes = snt._slave_nodes;
144  _neighbor_nodes = snt._neighbor_nodes;
145 
146  // If 'iteration' patch update strategy is used, a second neighborhood
147  // search using the ghosting_patch_size, which is larger than the regular
148  // patch_size used for contact search, is conducted. The ghosted element set
149  // given by this search is used for ghosting the elements connected to the
150  // slave and neighboring master nodes.
152  {
153  SlaveNeighborhoodThread snt_ghosting(
154  _mesh, trial_master_nodes, node_to_elem_map, _mesh.getGhostingPatchSize(), kd_tree);
155 
156  Threads::parallel_reduce(trial_slave_node_range, snt_ghosting);
157 
158  for (const auto & dof : snt_ghosting._ghosted_elems)
160  }
161  else
162  {
163  for (const auto & dof : snt._ghosted_elems)
165  }
166 
167  // Cache the slave_node_range so we don't have to build it each time
168  _slave_node_range = new NodeIdRange(_slave_nodes.begin(), _slave_nodes.end(), 1);
169  }
170 
171  _nearest_node_info.clear();
172 
174 
175  Threads::parallel_reduce(*_slave_node_range, nnt);
176 
178 
180 
182  {
183  // Get the set of elements that are currently being ghosted
184  std::set<dof_id_type> ghost = _subproblem.ghostedElems();
185 
186  for (const auto & node_id : *_slave_node_range)
187  {
188  const Node * nearest_node = _nearest_node_info[node_id]._nearest_node;
189 
190  // Check if the elements attached to the nearest node are within the ghosted
191  // set of elements. If not produce an error.
192  auto node_to_elem_pair = node_to_elem_map.find(nearest_node->id());
193 
194  if (node_to_elem_pair != node_to_elem_map.end())
195  {
196  const std::vector<dof_id_type> & elems_connected_to_node = node_to_elem_pair->second;
197  for (const auto & dof : elems_connected_to_node)
198  if (std::find(ghost.begin(), ghost.end(), dof) == ghost.end() &&
199  _mesh.elemPtr(dof)->processor_id() != _mesh.processor_id())
200  mooseError("Error in NearestNodeLocator : The nearest neighbor lies outside the "
201  "ghosted set of elements. Increase the ghosting_patch_size parameter in the "
202  "mesh block and try again.");
203  }
204  }
205  }
206 }
207 
208 void
210 {
211  TIME_SECTION(_reinit_timer);
212 
213  // Reset all data
214  delete _slave_node_range;
215  _slave_node_range = NULL;
216  _nearest_node_info.clear();
217 
218  _first = true;
219 
220  _slave_nodes.clear();
221  _neighbor_nodes.clear();
222 
223  _new_ghosted_elems.clear();
224 
225  // Redo the search
226  findNodes();
227 }
228 
229 Real
230 NearestNodeLocator::distance(dof_id_type node_id)
231 {
232  return _nearest_node_info[node_id]._distance;
233 }
234 
235 const Node *
237 {
238  return _nearest_node_info[node_id]._nearest_node;
239 }
240 
241 void
242 NearestNodeLocator::updatePatch(std::vector<dof_id_type> & slave_nodes)
243 {
244  TIME_SECTION(_update_patch_timer);
245 
246  std::vector<dof_id_type> trial_master_nodes;
247 
248  // Build a bounding box. No reason to consider nodes outside of our inflated BB
249  std::unique_ptr<BoundingBox> my_inflated_box = nullptr;
250 
251  const std::vector<Real> & inflation = _mesh.getGhostedBoundaryInflation();
252 
253  // This means there was a user specified inflation... so we can build a BB
254  if (inflation.size() > 0)
255  {
256  BoundingBox my_box = MeshTools::create_local_bounding_box(_mesh);
257 
258  Point distance;
259  for (unsigned int i = 0; i < inflation.size(); ++i)
260  distance(i) = inflation[i];
261 
262  my_inflated_box =
263  libmesh_make_unique<BoundingBox>(my_box.first - distance, my_box.second + distance);
264  }
265 
266  // Data structures to hold the boundary nodes
268  for (const auto & bnode : bnd_nodes)
269  {
270  BoundaryID boundary_id = bnode->_bnd_id;
271  dof_id_type node_id = bnode->_node->id();
272 
273  // If we have a BB only consider saving this node if it's in our inflated BB
274  if (!my_inflated_box || (my_inflated_box->contains_point(*bnode->_node)))
275  {
276  if (boundary_id == _boundary1)
277  trial_master_nodes.push_back(node_id);
278  }
279  }
280 
281  // Convert trial master nodes to a vector of Points. This will be used to construct the KDTree.
282  std::vector<Point> master_points(trial_master_nodes.size());
283  for (unsigned int i = 0; i < trial_master_nodes.size(); ++i)
284  {
285  const Node & node = _mesh.nodeRef(trial_master_nodes[i]);
286  master_points[i] = node;
287  }
288 
289  const std::map<dof_id_type, std::vector<dof_id_type>> & node_to_elem_map = _mesh.nodeToElemMap();
290 
291  // Create object kd_tree of class KDTree using the coordinates of trial
292  // master nodes.
293  KDTree kd_tree(master_points, _mesh.getMaxLeafSize());
294 
295  NodeIdRange slave_node_range(slave_nodes.begin(), slave_nodes.end(), 1);
296 
298  _mesh, trial_master_nodes, node_to_elem_map, _mesh.getPatchSize(), kd_tree);
299 
300  Threads::parallel_reduce(slave_node_range, snt);
301 
302  // Calculate new ghosting patch for the slave_node_range
303  SlaveNeighborhoodThread snt_ghosting(
304  _mesh, trial_master_nodes, node_to_elem_map, _mesh.getGhostingPatchSize(), kd_tree);
305 
306  Threads::parallel_reduce(slave_node_range, snt_ghosting);
307 
308  // Add the new set of elements that need to be ghosted into _new_ghosted_elems
309  for (const auto & dof : snt_ghosting._ghosted_elems)
310  _new_ghosted_elems.push_back(dof);
311 
312  std::vector<dof_id_type> tracked_slave_nodes = snt._slave_nodes;
313 
314  // Update the neighbor nodes (patch) for these tracked slave nodes
315  for (const auto & node_id : tracked_slave_nodes)
316  _neighbor_nodes[node_id] = snt._neighbor_nodes[node_id];
317 
318  NodeIdRange tracked_slave_node_range(tracked_slave_nodes.begin(), tracked_slave_nodes.end(), 1);
319 
320  NearestNodeThread nnt(_mesh, snt._neighbor_nodes);
321 
322  Threads::parallel_reduce(tracked_slave_node_range, nnt);
323 
324  _max_patch_percentage = nnt._max_patch_percentage;
325 
326  // Get the set of elements that are currently being ghosted
327  std::set<dof_id_type> ghost = _subproblem.ghostedElems();
328 
329  // Update the nearest node information corresponding to these tracked slave nodes
330  for (const auto & node_id : tracked_slave_node_range)
331  {
332  _nearest_node_info[node_id] = nnt._nearest_node_info[node_id];
333 
334  // Check if the elements attached to the nearest node are within the ghosted
335  // set of elements. If not produce an error.
336  const Node * nearest_node = nnt._nearest_node_info[node_id]._nearest_node;
337 
338  auto node_to_elem_pair = node_to_elem_map.find(nearest_node->id());
339 
340  if (node_to_elem_pair != node_to_elem_map.end())
341  {
342  const std::vector<dof_id_type> & elems_connected_to_node = node_to_elem_pair->second;
343  for (const auto & dof : elems_connected_to_node)
344  if (std::find(ghost.begin(), ghost.end(), dof) == ghost.end() &&
345  _mesh.elemPtr(dof)->processor_id() != _mesh.processor_id())
346  mooseError("Error in NearestNodeLocator : The nearest neighbor lies outside the ghosted "
347  "set of elements. Increase the ghosting_patch_size parameter in the mesh "
348  "block and try again.");
349  }
350  }
351 }
352 
353 void
355 {
356  TIME_SECTION(_update_ghosted_elems_timer);
357 
358  // When 'iteration' patch update strategy is used, add the elements in
359  // _new_ghosted_elems, which were accumulated in the nonlinear iterations
360  // during the previous time step, to the list of ghosted elements. Also clear
361  // the _new_ghosted_elems array for storing the ghosted elements from the
362  // nonlinear iterations in the current time step.
363 
364  for (const auto & dof : _new_ghosted_elems)
366 
367  _new_ghosted_elems.clear();
368 }
369 //===================================================================
371  : _nearest_node(NULL), _distance(std::numeric_limits<Real>::max())
372 {
373 }
std::map< dof_id_type, std::vector< dof_id_type > > _neighbor_nodes
void findNodes()
This is the main method that is going to start the search.
A class for creating restricted objects.
Definition: Restartable.h:29
virtual Elem * elemPtr(const dof_id_type i)
Definition: MooseMesh.C:2267
Definition: KDTree.h:19
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:207
StoredRange< std::vector< dof_id_type >::iterator, dof_id_type > NodeIdRange
Definition: MooseTypes.h:165
unsigned int getGhostingPatchSize() const
Getter for the ghosting_patch_size parameter.
Definition: MooseMesh.h:474
std::map< dof_id_type, NearestNodeLocator::NearestNodeInfo > _nearest_node_info
void updatePatch(std::vector< dof_id_type > &slave_nodes)
Reconstructs the KDtree, updates the patch for the nodes in slave_nodes, and updates the closest neig...
Real distance(dof_id_type node_id)
Valid to call this after findNodes() has been called to get the distance to the nearest node...
const std::vector< Real > & getGhostedBoundaryInflation() const
Return a writable reference to the _ghosted_boundaries_inflation vector.
Definition: MooseMesh.C:2391
void reinit()
Completely redo the search from scratch.
virtual const Node & nodeRef(const dof_id_type i) const
Definition: MooseMesh.C:436
NearestNodeLocator(SubProblem &subproblem, MooseMesh &mesh, BoundaryID boundary1, BoundaryID boundary2)
std::map< dof_id_type, NearestNodeInfo > _nearest_node_info
std::vector< dof_id_type > _slave_nodes
boundary_id_type BoundaryID
unsigned int getMaxLeafSize() const
Getter for the maximum leaf size parameter.
Definition: MooseMesh.h:479
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition: MooseMesh.h:74
const Moose::PatchUpdateType _patch_update_strategy
void updateGhostedElems()
Updates the ghosted elements at the start of the time step for iterion patch update strategy...
NodeIdRange * _slave_node_range
virtual std::set< dof_id_type > & ghostedElems()
Return the list of elements that should have their DoFs ghosted to this processor.
Definition: SubProblem.h:561
std::string stringify(const T &t)
conversion to string
Definition: Conversion.h:60
unsigned int getPatchSize() const
Getter for the patch_size parameter.
Definition: MooseMesh.C:2526
Interface for objects that needs transient capabilities.
Generic class for solving transient nonlinear problems.
Definition: SubProblem.h:59
const Node * nearestNode(dof_id_type node_id)
Valid to call this after findNodes() has been called to get a pointer to the nearest node...
virtual void addGhostedElem(dof_id_type elem_id)=0
Will make sure that all dofs connected to elem_id are ghosted to this processor.
SubProblem & _subproblem
std::vector< dof_id_type > _new_ghosted_elems
Definition: Moose.h:112
StoredRange< MooseMesh::const_bnd_node_iterator, const BndNode * > ConstBndNodeRange
Some useful StoredRange typedefs.
Definition: MooseMesh.h:1258
StoredRange< MooseMesh::const_bnd_node_iterator, const BndNode * > * getBoundaryNodeRange()
Definition: MooseMesh.C:788
const std::map< dof_id_type, std::vector< dof_id_type > > & nodeToElemMap()
If not already created, creates a map from every node to all elements to which they are connected...
Definition: MooseMesh.C:690