https://mooseframework.inl.gov
NearestNodeLocator.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
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  _boundary1(boundary1),
40  _boundary2(boundary2),
41  _first(true),
42  _reinit_iteration(true),
43  _patch_update_strategy(_mesh.getPatchUpdateStrategy())
44 {
45  /*
46  //sanity check on boundary ids
47  const std::set<BoundaryID>& bids=_mesh.getBoundaryIDs();
48  std::set<BoundaryID>::const_iterator sit;
49  sit=bids.find(_boundary1);
50  if (sit == bids.end())
51  mooseError("NearestNodeLocator being created for boundaries ", _boundary1, " and ", _boundary2,
52  ", but boundary ", _boundary1, " does not exist");
53  sit=bids.find(_boundary2);
54  if (sit == bids.end())
55  mooseError("NearestNodeLocator being created for boundaries ", _boundary1, " and ", _boundary2,
56  ", but boundary ", _boundary2, " does not exist");
57  */
58 
59  // Request the nodeToElem map upfront
61 }
62 
64 
65 void
67 {
68  TIME_SECTION("findNodes", 3, "Finding Nearest Nodes");
69 
74  const std::map<dof_id_type, std::vector<dof_id_type>> & node_to_elem_map = _mesh.nodeToElemMap();
75 
77  {
78  _first = false;
79 
80  // Trial secondary nodes are all the nodes on the secondary side
81  // We only keep the ones that are either on this processor or are likely
82  // to interact with elements on this processor (ie nodes owned by this processor
83  // are in the "neighborhood" of the secondary node
84  std::vector<dof_id_type> trial_secondary_nodes;
85  std::vector<dof_id_type> trial_primary_nodes;
86 
87  // Build a bounding box. No reason to consider nodes outside of our inflated BB
88  std::unique_ptr<BoundingBox> my_inflated_box = nullptr;
89 
90  const std::vector<Real> & inflation = _mesh.getGhostedBoundaryInflation();
91 
92  // This means there was a user specified inflation... so we can build a BB
93  if (inflation.size() > 0)
94  {
95  BoundingBox my_box = MeshTools::create_local_bounding_box(_mesh);
96 
97  Point distance;
98  for (unsigned int i = 0; i < inflation.size(); ++i)
99  distance(i) = inflation[i];
100 
101  my_inflated_box =
102  std::make_unique<BoundingBox>(my_box.first - distance, my_box.second + distance);
103  }
104 
105  // Data structures to hold the boundary nodes
107  for (const auto & bnode : bnd_nodes)
108  {
109  BoundaryID boundary_id = bnode->_bnd_id;
110  dof_id_type node_id = bnode->_node->id();
111 
112  // If we have a BB only consider saving this node if it's in our inflated BB
113  if (!my_inflated_box || (my_inflated_box->contains_point(*bnode->_node)))
114  {
115  if (boundary_id == _boundary1)
116  trial_primary_nodes.push_back(node_id);
117  else if (boundary_id == _boundary2)
118  trial_secondary_nodes.push_back(node_id);
119  }
120  }
121 
122  // Convert trial primary nodes to a vector of Points. This will be used to
123  // construct the Kdtree.
124  std::vector<Point> primary_points(trial_primary_nodes.size());
125  for (unsigned int i = 0; i < trial_primary_nodes.size(); ++i)
126  {
127  const Node & node = _mesh.nodeRef(trial_primary_nodes[i]);
128  primary_points[i] = node;
129  }
130 
131  // Create object kd_tree of class KDTree using the coordinates of trial
132  // primary nodes.
133  KDTree kd_tree(primary_points, _mesh.getMaxLeafSize());
134 
135  NodeIdRange trial_secondary_node_range(
136  trial_secondary_nodes.begin(), trial_secondary_nodes.end(), 1);
137 
139  _mesh, trial_primary_nodes, node_to_elem_map, _mesh.getPatchSize(), kd_tree);
140 
141  Threads::parallel_reduce(trial_secondary_node_range, snt);
142 
143  _secondary_nodes = snt._secondary_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  // secondary and neighboring primary nodes.
152  {
153  SecondaryNeighborhoodThread snt_ghosting(
154  _mesh, trial_primary_nodes, node_to_elem_map, _mesh.getGhostingPatchSize(), kd_tree);
155 
156  Threads::parallel_reduce(trial_secondary_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 secondary_node_range so we don't have to build it each time
169  std::make_unique<NodeIdRange>(_secondary_nodes.begin(), _secondary_nodes.end(), 1);
170  }
171 
172  _nearest_node_info.clear();
173 
175 
176  Threads::parallel_reduce(*_secondary_node_range, nnt);
177 
179 
181 
183  {
184  // Get the set of elements that are currently being ghosted
185  std::set<dof_id_type> ghost = _subproblem.ghostedElems();
186 
187  for (const auto & node_id : *_secondary_node_range)
188  {
189  const Node * nearest_node = _nearest_node_info[node_id]._nearest_node;
190 
191  // Check if the elements attached to the nearest node are within the ghosted
192  // set of elements. If not produce an error.
193  auto node_to_elem_pair = node_to_elem_map.find(nearest_node->id());
194 
195  if (node_to_elem_pair != node_to_elem_map.end())
196  {
197  const std::vector<dof_id_type> & elems_connected_to_node = node_to_elem_pair->second;
198  for (const auto & dof : elems_connected_to_node)
199  if (std::find(ghost.begin(), ghost.end(), dof) == ghost.end() &&
200  _mesh.elemPtr(dof)->processor_id() != _mesh.processor_id())
201  mooseError("Error in NearestNodeLocator: The nearest neighbor lies outside the "
202  "ghosted set of elements. Increase the ghosting_patch_size parameter in the "
203  "mesh block and try again.");
204  }
205  }
206  }
207 }
208 
209 void
211 {
212  TIME_SECTION("reinit", 3, "Reinitializing Nearest Node Search");
213 
214  // Reset all data
215  _secondary_node_range.reset();
216  _nearest_node_info.clear();
217 
218  _first = true;
219 
220  _secondary_nodes.clear();
221  _neighbor_nodes.clear();
222 
223  _new_ghosted_elems.clear();
224 
225  // After a call from system reinit, mesh has been updated with initial adaptivity.
226  // Moose::Iteration relies on data generated for ghosting (i.e. trial_primary_nodes)
227  _reinit_iteration = true;
228 
229  // Redo the search
230  findNodes();
231 
232  _reinit_iteration = false;
233 }
234 
235 Real
237 {
238  return _nearest_node_info[node_id]._distance;
239 }
240 
241 const Node *
243 {
244  const Node * returnval = _nearest_node_info[node_id]._nearest_node;
245  libmesh_assert(_mesh.getMesh().get_boundary_info().has_boundary_id(returnval, _boundary1));
246  return returnval;
247 }
248 
249 void
250 NearestNodeLocator::updatePatch(std::vector<dof_id_type> & secondary_nodes)
251 {
252  TIME_SECTION("updatePatch", 3, "Updating Nearest Node Search Patch");
253 
254  std::vector<dof_id_type> trial_primary_nodes;
255 
256  // Build a bounding box. No reason to consider nodes outside of our inflated BB
257  std::unique_ptr<BoundingBox> my_inflated_box = nullptr;
258 
259  const std::vector<Real> & inflation = _mesh.getGhostedBoundaryInflation();
260 
261  // This means there was a user specified inflation... so we can build a BB
262  if (inflation.size() > 0)
263  {
264  BoundingBox my_box = MeshTools::create_local_bounding_box(_mesh);
265 
266  Point distance;
267  for (unsigned int i = 0; i < inflation.size(); ++i)
268  distance(i) = inflation[i];
269 
270  my_inflated_box =
271  std::make_unique<BoundingBox>(my_box.first - distance, my_box.second + distance);
272  }
273 
274  // Data structures to hold the boundary nodes
276  for (const auto & bnode : bnd_nodes)
277  {
278  BoundaryID boundary_id = bnode->_bnd_id;
279  dof_id_type node_id = bnode->_node->id();
280 
281  // If we have a BB only consider saving this node if it's in our inflated BB
282  if (!my_inflated_box || (my_inflated_box->contains_point(*bnode->_node)))
283  {
284  if (boundary_id == _boundary1)
285  trial_primary_nodes.push_back(node_id);
286  }
287  }
288 
289  // Convert trial primary nodes to a vector of Points. This will be used to construct the KDTree.
290  std::vector<Point> primary_points(trial_primary_nodes.size());
291  for (unsigned int i = 0; i < trial_primary_nodes.size(); ++i)
292  {
293  const Node & node = _mesh.nodeRef(trial_primary_nodes[i]);
294  primary_points[i] = node;
295  }
296 
297  const std::map<dof_id_type, std::vector<dof_id_type>> & node_to_elem_map = _mesh.nodeToElemMap();
298 
299  // Create object kd_tree of class KDTree using the coordinates of trial
300  // primary nodes.
301  KDTree kd_tree(primary_points, _mesh.getMaxLeafSize());
302 
303  NodeIdRange secondary_node_range(secondary_nodes.begin(), secondary_nodes.end(), 1);
304 
306  _mesh, trial_primary_nodes, node_to_elem_map, _mesh.getPatchSize(), kd_tree);
307 
308  Threads::parallel_reduce(secondary_node_range, snt);
309 
310  // Calculate new ghosting patch for the secondary_node_range
311  SecondaryNeighborhoodThread snt_ghosting(
312  _mesh, trial_primary_nodes, node_to_elem_map, _mesh.getGhostingPatchSize(), kd_tree);
313 
314  Threads::parallel_reduce(secondary_node_range, snt_ghosting);
315 
316  // Add the new set of elements that need to be ghosted into _new_ghosted_elems
317  for (const auto & dof : snt_ghosting._ghosted_elems)
318  _new_ghosted_elems.push_back(dof);
319 
320  std::vector<dof_id_type> tracked_secondary_nodes = snt._secondary_nodes;
321 
322  // Update the neighbor nodes (patch) for these tracked secondary nodes
323  for (const auto & node_id : tracked_secondary_nodes)
324  _neighbor_nodes[node_id] = snt._neighbor_nodes[node_id];
325 
326  NodeIdRange tracked_secondary_node_range(
327  tracked_secondary_nodes.begin(), tracked_secondary_nodes.end(), 1);
328 
329  NearestNodeThread nnt(_mesh, snt._neighbor_nodes);
330 
331  Threads::parallel_reduce(tracked_secondary_node_range, nnt);
332 
333  _max_patch_percentage = nnt._max_patch_percentage;
334 
335  // Get the set of elements that are currently being ghosted
336  std::set<dof_id_type> ghost = _subproblem.ghostedElems();
337 
338  // Update the nearest node information corresponding to these tracked secondary nodes
339  for (const auto & node_id : tracked_secondary_node_range)
340  {
341  _nearest_node_info[node_id] = nnt._nearest_node_info[node_id];
342 
343  // Check if the elements attached to the nearest node are within the ghosted
344  // set of elements. If not produce an error.
345  const Node * nearest_node = nnt._nearest_node_info[node_id]._nearest_node;
346 
347  auto node_to_elem_pair = node_to_elem_map.find(nearest_node->id());
348 
349  if (node_to_elem_pair != node_to_elem_map.end())
350  {
351  const std::vector<dof_id_type> & elems_connected_to_node = node_to_elem_pair->second;
352  for (const auto & dof : elems_connected_to_node)
353  if (std::find(ghost.begin(), ghost.end(), dof) == ghost.end() &&
354  _mesh.elemPtr(dof)->processor_id() != _mesh.processor_id())
355  mooseError("Error in NearestNodeLocator: The nearest neighbor lies outside the ghosted "
356  "set of elements. Increase the ghosting_patch_size parameter in the mesh "
357  "block and try again.");
358  }
359  }
360 }
361 
362 void
364 {
365  TIME_SECTION("updateGhostedElems", 5, "Updating Nearest Node Search Because of Ghosting");
366 
367  // When 'iteration' patch update strategy is used, add the elements in
368  // _new_ghosted_elems, which were accumulated in the nonlinear iterations
369  // during the previous time step, to the list of ghosted elements. Also clear
370  // the _new_ghosted_elems array for storing the ghosted elements from the
371  // nonlinear iterations in the current time step.
372 
373  for (const auto & dof : _new_ghosted_elems)
375 
376  _new_ghosted_elems.clear();
377 }
378 //===================================================================
380  : _nearest_node(nullptr), _distance(std::numeric_limits<Real>::max())
381 {
382 }
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.
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:40
A class for creating restricted objects.
Definition: Restartable.h:28
virtual Elem * elemPtr(const dof_id_type i)
Definition: MooseMesh.C:3240
Definition: KDTree.h:28
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
void updatePatch(std::vector< dof_id_type > &secondary_nodes)
Reconstructs the KDtree, updates the patch for the nodes in secondary_nodes, and updates the closest ...
unsigned int getGhostingPatchSize() const
Getter for the ghosting_patch_size parameter.
Definition: MooseMesh.h:637
MeshBase & mesh
std::map< dof_id_type, NearestNodeLocator::NearestNodeInfo > _nearest_node_info
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:3374
void reinit()
Completely redo the search from scratch.
virtual const Node & nodeRef(const dof_id_type i) const
Definition: MooseMesh.C:834
NearestNodeLocator(SubProblem &subproblem, MooseMesh &mesh, BoundaryID boundary1, BoundaryID boundary2)
std::string stringify(MOOSEIOType type)
Definition: NEML2Utils.C:18
auto max(const L &left, const R &right)
std::map< dof_id_type, NearestNodeInfo > _nearest_node_info
std::unique_ptr< NodeIdRange > _secondary_node_range
std::vector< dof_id_type > _secondary_nodes
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition: MooseMesh.C:3575
boundary_id_type BoundaryID
unsigned int getMaxLeafSize() const
Getter for the maximum leaf size parameter.
Definition: MooseMesh.h:642
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition: MooseMesh.h:93
libmesh_assert(ctx)
const Moose::PatchUpdateType _patch_update_strategy
void updateGhostedElems()
Updates the ghosted elements at the start of the time step for iteration patch update strategy...
virtual std::set< dof_id_type > & ghostedElems()
Return the list of elements that should have their DoFs ghosted to this processor.
Definition: SubProblem.h:672
unsigned int getPatchSize() const
Getter for the patch_size parameter.
Definition: MooseMesh.C:3528
Interface for objects interacting with the PerfGraph.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Generic class for solving transient nonlinear problems.
Definition: SubProblem.h:78
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
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
processor_id_type processor_id() const
libMesh::StoredRange< MooseMesh::const_bnd_node_iterator, const BndNode * > * getBoundaryNodeRange()
Definition: MooseMesh.C:1312
uint8_t dof_id_type
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:1201