libMesh
point_locator_tree.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 
20 // C++ includes
21 
22 // Local Includes
23 #include "libmesh/elem.h"
24 #include "libmesh/libmesh_logging.h"
25 #include "libmesh/mesh_base.h"
26 #include "libmesh/mesh_tools.h"
27 #include "libmesh/point_locator_tree.h"
28 #include "libmesh/tree.h"
29 
30 namespace libMesh
31 {
32 
33 
34 
35 //------------------------------------------------------------------
36 // PointLocator methods
38  const PointLocatorBase * master) :
39  PointLocatorBase (mesh,master),
40  _tree (nullptr),
41  _element (nullptr),
42  _out_of_mesh_mode(false),
43  _target_bin_size (200),
44  _build_type(Trees::NODES)
45 {
46  this->init(_build_type);
47 }
48 
49 
50 
52  const Trees::BuildType build_type,
53  const PointLocatorBase * master) :
54  PointLocatorBase (mesh,master),
55  _tree (nullptr),
56  _element (nullptr),
57  _out_of_mesh_mode(false),
58  _target_bin_size (200),
59  _build_type(build_type)
60 {
61  this->init(_build_type);
62 }
63 
64 
65 
67 
68 
69 
71 {
72  this->_initialized = false;
73 
74  // reset() actually frees the memory if we are master, otherwise it
75  // just reduces the ref. count.
76  _tree.reset();
77 }
78 
79 
80 
82 {
83  this->init(_build_type);
84 }
85 
86 
87 
89 {
90  libmesh_assert (!this->_tree);
91 
92  if (this->_initialized)
93  {
94  // Warn that we are already initialized
95  libMesh::err << "Warning: PointLocatorTree already initialized! Will ignore this call..." << std::endl;
96 
97  // Further warn if we try to init() again with a different build_type
98  if (_build_type != build_type)
99  {
100  libMesh::err << "Warning: PointLocatorTree is using build_type = " << _build_type << ".\n"
101  << "Your requested build_type, " << build_type << " will not be used!" << std::endl;
102  }
103  }
104 
105  else
106  {
107  // Let the requested build_type override the _build_type we were
108  // constructed with. This is no big deal since we have not been
109  // initialized before.
110  _build_type = build_type;
111 
112  if (this->_master == nullptr)
113  {
114  LOG_SCOPE("init(no master)", "PointLocatorTree");
115 
116  if (LIBMESH_DIM == 1)
117  _tree = std::make_shared<Trees::BinaryTree>(this->_mesh, get_target_bin_size(), _build_type);
118  else if (LIBMESH_DIM == 2)
119  _tree = std::make_shared<Trees::QuadTree>(this->_mesh, get_target_bin_size(), _build_type);
120  else if (this->_mesh.mesh_dimension() == 3) // && LIBMESH_DIM==3
121  _tree = std::make_shared<Trees::OctTree>(this->_mesh, get_target_bin_size(), _build_type);
122  else
123  {
124  // LIBMESH_DIM==3 but we have a mesh with only 1D/2D
125  // elements, which needs special consideration. If the
126  // mesh is planar XY, we want to build a QuadTree to
127  // search efficiently. If the mesh is truly a manifold,
128  // then we need an octree
129  bool is_planar_xy = false;
130 
131  // Build the bounding box for the mesh. If the delta-z bound is
132  // negligibly small then we can use a quadtree.
133  BoundingBox bbox = MeshTools::create_bounding_box(this->_mesh);
134 
135  const Real
136  Dx = bbox.second(0) - bbox.first(0),
137  Dz = bbox.second(2) - bbox.first(2);
138 
139  // In order to satisfy is_planar_xy the mesh should be planar and should
140  // also be in the z=0 plane, since otherwise it is incorrect to use a
141  // QuadTree since QuadTrees assume z=0.
142  if ( (std::abs(Dz/(Dx + 1.e-20)) < 1e-10) && (std::abs(bbox.second(2)) < 1.e-10) )
143  is_planar_xy = true;
144 
145  if (is_planar_xy)
146  _tree = std::make_shared<Trees::QuadTree>(this->_mesh, get_target_bin_size(), _build_type);
147  else
148  _tree = std::make_shared<Trees::OctTree>(this->_mesh, get_target_bin_size(), _build_type);
149  }
150  }
151 
152  else
153  {
154  // We are _not_ the master. Let our Tree point to
155  // the master's tree. But for this we first transform
156  // the master in a state for which we are friends.
157  // And make sure the master has a tree!
158  const PointLocatorTree * my_master =
159  cast_ptr<const PointLocatorTree *>(this->_master);
160 
161  if (my_master->initialized())
162  this->_tree = my_master->_tree;
163  else
164  libmesh_error_msg("ERROR: Initialize master first, then servants!");
165  }
166 
167  // Not all PointLocators may own a tree, but all of them
168  // use their own element pointer. Let the element pointer
169  // be unique for every interpolator.
170  // Suppose the interpolators are used concurrently
171  // at different locations in the mesh, then it makes quite
172  // sense to have unique start elements.
173  this->_element = nullptr;
174  }
175 
176  // ready for take-off
177  this->_initialized = true;
178 }
179 
181  const std::set<subdomain_id_type> * allowed_subdomains) const
182 {
184 
185  LOG_SCOPE("operator()", "PointLocatorTree");
186 
187  // If we're provided with an allowed_subdomains list and have a cached element, make sure it complies
188  if (allowed_subdomains && this->_element && !allowed_subdomains->count(this->_element->subdomain_id()))
189  this->_element = nullptr;
190 
191  if (this->_element != nullptr)
192  {
193  // If the user specified a custom tolerance, we actually call
194  // Elem::close_to_point() instead, since Elem::contains_point()
195  // warns about using non-default BoundingBox tolerances.
197  this->_element = nullptr;
198 
199  // Otherwise, just call contains_point(p) with default tolerances.
200  else if (!(this->_element->contains_point(p)))
201  this->_element = nullptr;
202  }
203 
204  // First check the element from last time before asking the tree
205  if (this->_element==nullptr)
206  {
207  // ask the tree
209  this->_element = this->_tree->find_element(p, allowed_subdomains, _contains_point_tol);
210  else
211  this->_element = this->_tree->find_element(p, allowed_subdomains);
212 
213  if (this->_element == nullptr)
214  {
215  // If we haven't found the element, we may want to do a linear
216  // search using a tolerance.
218  {
219  if (_verbose)
220  {
221  libMesh::out << "Performing linear search using close-to-point tolerance "
223  << std::endl;
224  }
225 
226  this->_element =
227  this->perform_linear_search(p,
228  allowed_subdomains,
229  /*use_close_to_point*/ true,
231 
232  return this->_element;
233  }
234 
235  // No element seems to contain this point. In theory, our
236  // tree now correctly handles curved elements. In
237  // out-of-mesh mode this is sometimes expected, and we can
238  // just return nullptr without searching further. Out of
239  // out-of-mesh mode, something must have gone wrong.
240  libmesh_assert_equal_to (_out_of_mesh_mode, true);
241 
242  return this->_element;
243  }
244  }
245 
246  // If we found an element, it should be active
247  libmesh_assert (!this->_element || this->_element->active());
248 
249  // If we found an element and have a restriction list, they better match
250  libmesh_assert (!this->_element || !allowed_subdomains || allowed_subdomains->count(this->_element->subdomain_id()));
251 
252  // return the element
253  return this->_element;
254 }
255 
256 
258  std::set<const Elem *> & candidate_elements,
259  const std::set<subdomain_id_type> * allowed_subdomains) const
260 {
262 
263  LOG_SCOPE("operator() - Version 2", "PointLocatorTree");
264 
265  // ask the tree
266  this->_tree->find_elements (p, candidate_elements, allowed_subdomains, _close_to_point_tol);
267 }
268 
269 
270 
272  const std::set<subdomain_id_type> * allowed_subdomains,
273  bool use_close_to_point,
274  Real close_to_point_tolerance) const
275 {
276  LOG_SCOPE("perform_linear_search", "PointLocatorTree");
277 
278  // The type of iterator depends on the Trees::BuildType
279  // used for this PointLocator. If it's
280  // TREE_LOCAL_ELEMENTS, we only want to double check
281  // local elements during this linear search.
284  this->_mesh.active_local_element_ptr_range() :
285  this->_mesh.active_element_ptr_range();
286 
287  for (const auto & elem : r)
288  {
289  if (!allowed_subdomains ||
290  allowed_subdomains->count(elem->subdomain_id()))
291  {
292  if (!use_close_to_point)
293  {
294  if (elem->contains_point(p, _contains_point_tol))
295  return elem;
296  }
297  else
298  {
299  if (elem->close_to_point(p, close_to_point_tolerance))
300  return elem;
301  }
302  }
303  }
304 
305  return nullptr;
306 }
307 
308 
309 std::set<const Elem *> PointLocatorTree::perform_fuzzy_linear_search(const Point & p,
310  const std::set<subdomain_id_type> * allowed_subdomains,
311  Real close_to_point_tolerance) const
312 {
313  LOG_SCOPE("perform_fuzzy_linear_search", "PointLocatorTree");
314 
315  std::set<const Elem *> candidate_elements;
316 
317  // The type of iterator depends on the Trees::BuildType
318  // used for this PointLocator. If it's
319  // TREE_LOCAL_ELEMENTS, we only want to double check
320  // local elements during this linear search.
323  this->_mesh.active_local_element_ptr_range() :
324  this->_mesh.active_element_ptr_range();
325 
326  for (const auto & elem : r)
327  if ((!allowed_subdomains || allowed_subdomains->count(elem->subdomain_id())) && elem->close_to_point(p, close_to_point_tolerance))
328  candidate_elements.insert(elem);
329 
330  return candidate_elements;
331 }
332 
333 
334 
336 {
337  // Out-of-mesh mode should now work properly even on meshes with
338  // non-affine elements.
339  _out_of_mesh_mode = true;
340 }
341 
342 
344 {
345  _out_of_mesh_mode = false;
346 }
347 
348 
349 void PointLocatorTree::set_target_bin_size (unsigned int target_bin_size)
350 {
351  _target_bin_size = target_bin_size;
352 }
353 
354 
356 {
357  return _target_bin_size;
358 }
359 
360 
361 } // namespace libMesh
OStreamProxy err
virtual void enable_out_of_mesh_mode() override final
Enables out-of-mesh mode.
~PointLocatorTree()
Destructor.
The SimpleRange templated class is intended to make it easy to construct ranges from pairs of iterato...
Definition: simple_range.h:36
BuildType
enum defining how to build the tree.
Definition: tree_base.h:58
virtual const Elem * operator()(const Point &p, const std::set< subdomain_id_type > *allowed_subdomains=nullptr) const override final
Locates the element in which the point with global coordinates p is located, optionally restricted to...
This is a point locator.
bool _use_close_to_point_tol
true if we will use a user-specified tolerance for locating the element in an exhaustive search...
libMesh::BoundingBox create_bounding_box(const MeshBase &mesh)
Definition: mesh_tools.C:559
virtual void clear() override final
Clears the locator.
Real _close_to_point_tol
The tolerance to use.
virtual void init() override final
Initializes the locator, so that the operator() methods can be used.
std::shared_ptr< TreeBase > _tree
Pointer to our tree.
unsigned int get_target_bin_size() const
Get the target bin size.
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
bool _initialized
true when properly initialized, false otherwise.
MeshBase & mesh
The libMesh namespace provides an interface to certain functionality in the library.
const Elem * _element
Pointer to the last element that was found by the tree.
This is the MeshBase class.
Definition: mesh_base.h:75
PointLocatorTree(const MeshBase &mesh, const PointLocatorBase *master=nullptr)
Constructor.
virtual bool contains_point(const Point &p, Real tol=TOLERANCE) const
Definition: elem.C:2751
const MeshBase & _mesh
constant reference to the mesh in which the point is looked for.
libmesh_assert(ctx)
This is the base class for point locators.
Trees::BuildType _build_type
How the underlying tree is built.
unsigned int _target_bin_size
Target bin size, which gets passed to the constructor of _tree.
Defines a Cartesian bounding box by the two corner extremum.
Definition: bounding_box.h:40
bool _out_of_mesh_mode
true if out-of-mesh mode is enabled.
virtual bool close_to_point(const Point &p, Real tol) const
Definition: elem.C:2776
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
bool _verbose
Boolean flag to indicate whether to print out extra info.
OStreamProxy out
const PointLocatorBase * _master
Const pointer to our master, initialized to nullptr if none given.
const Elem * perform_linear_search(const Point &p, const std::set< subdomain_id_type > *allowed_subdomains, bool use_close_to_point, Real close_to_point_tolerance=TOLERANCE) const
As a fallback option, it&#39;s helpful to be able to do a linear search over the entire mesh...
std::set< const Elem * > perform_fuzzy_linear_search(const Point &p, const std::set< subdomain_id_type > *allowed_subdomains, Real close_to_point_tolerance=TOLERANCE) const
A method to check if "fat" point p is in multiple elements.
void set_target_bin_size(unsigned int target)
Set the target bin size.
bool active() const
Definition: elem.h:2941
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
bool _use_contains_point_tol
true if we will use a user-specified tolerance for locating the element.
virtual void disable_out_of_mesh_mode() override final
Disables out-of-mesh mode (default).
Real _contains_point_tol
The tolerance to use when locating an element in the tree.