libMesh
point_locator_tree.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2026 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  // This may be too expensive to use even in debug mode, but it's
180  // helpful to uncomment for debugging.
181  // this->libmesh_assert_valid_point_locator();
182 }
183 
185  const std::set<subdomain_id_type> * allowed_subdomains) const
186 {
188 
189  LOG_SCOPE("operator()", "PointLocatorTree");
190 
191  // If we're provided with an allowed_subdomains list and have a cached element, make sure it complies
192  if (allowed_subdomains && this->_element && !allowed_subdomains->count(this->_element->subdomain_id()))
193  this->_element = nullptr;
194 
195  if (this->_element != nullptr)
196  {
197  // If the user specified a custom tolerance, we actually call
198  // Elem::close_to_point() instead, since Elem::contains_point()
199  // warns about using non-default BoundingBox tolerances.
201  this->_element = nullptr;
202 
203  // Otherwise, just call contains_point(p) with default tolerances.
204  else if (!(this->_element->contains_point(p)))
205  this->_element = nullptr;
206  }
207 
208  // First check the element from last time before asking the tree
209  if (this->_element==nullptr)
210  {
211  // ask the tree
213  this->_element = this->_tree->find_element(p, allowed_subdomains, _contains_point_tol);
214  else
215  this->_element = this->_tree->find_element(p, allowed_subdomains);
216 
217  if (this->_element == nullptr)
218  {
219  // If we haven't found the element, we may want to do a linear
220  // search using a tolerance.
222  {
223  if (_verbose)
224  {
225  libMesh::out << "Performing linear search using close-to-point tolerance "
227  << std::endl;
228  }
229 
230  this->_element =
231  this->perform_linear_search(p,
232  allowed_subdomains,
233  /*use_close_to_point*/ true,
235 
236  return this->_element;
237  }
238 
239  // No element seems to contain this point. In theory, our
240  // tree now correctly handles curved elements. In
241  // out-of-mesh mode this is sometimes expected, and we can
242  // just return nullptr without searching further. Out of
243  // out-of-mesh mode, something must have gone wrong.
244  libmesh_assert_equal_to (_out_of_mesh_mode, true);
245 
246  return this->_element;
247  }
248  }
249 
250  // If we found an element, it should be active
251  libmesh_assert (!this->_element || this->_element->active());
252 
253  // If we found an element and have a restriction list, they better match
254  libmesh_assert (!this->_element || !allowed_subdomains || allowed_subdomains->count(this->_element->subdomain_id()));
255 
256  // return the element
257  return this->_element;
258 }
259 
260 
262  std::set<const Elem *> & candidate_elements,
263  const std::set<subdomain_id_type> * allowed_subdomains) const
264 {
266 
267  LOG_SCOPE("operator() - Version 2", "PointLocatorTree");
268 
269  // ask the tree
270  this->_tree->find_elements (p, candidate_elements, allowed_subdomains, _close_to_point_tol);
271 }
272 
273 
274 
276  const std::set<subdomain_id_type> * allowed_subdomains,
277  bool use_close_to_point,
278  Real close_to_point_tolerance) const
279 {
280  LOG_SCOPE("perform_linear_search", "PointLocatorTree");
281 
282  // The type of iterator depends on the Trees::BuildType
283  // used for this PointLocator. If it's
284  // TREE_LOCAL_ELEMENTS, we only want to double check
285  // local elements during this linear search.
288  this->_mesh.active_local_element_ptr_range() :
289  this->_mesh.active_element_ptr_range();
290 
291  for (const auto & elem : r)
292  {
293  if (!allowed_subdomains ||
294  allowed_subdomains->count(elem->subdomain_id()))
295  {
296  if (!use_close_to_point)
297  {
298  if (elem->contains_point(p, _contains_point_tol))
299  return elem;
300  }
301  else
302  {
303  if (elem->close_to_point(p, close_to_point_tolerance))
304  return elem;
305  }
306  }
307  }
308 
309  return nullptr;
310 }
311 
312 
313 std::set<const Elem *> PointLocatorTree::perform_fuzzy_linear_search(const Point & p,
314  const std::set<subdomain_id_type> * allowed_subdomains,
315  Real close_to_point_tolerance) const
316 {
317  LOG_SCOPE("perform_fuzzy_linear_search", "PointLocatorTree");
318 
319  std::set<const Elem *> candidate_elements;
320 
321  // The type of iterator depends on the Trees::BuildType
322  // used for this PointLocator. If it's
323  // TREE_LOCAL_ELEMENTS, we only want to double check
324  // local elements during this linear search.
327  this->_mesh.active_local_element_ptr_range() :
328  this->_mesh.active_element_ptr_range();
329 
330  for (const auto & elem : r)
331  if ((!allowed_subdomains || allowed_subdomains->count(elem->subdomain_id())) && elem->close_to_point(p, close_to_point_tolerance))
332  candidate_elements.insert(elem);
333 
334  return candidate_elements;
335 }
336 
337 
338 
340 {
341  // Out-of-mesh mode should now work properly even on meshes with
342  // non-affine elements.
343  _out_of_mesh_mode = true;
344 }
345 
346 
348 {
349  _out_of_mesh_mode = false;
350 }
351 
352 
353 void PointLocatorTree::set_target_bin_size (unsigned int target_bin_size)
354 {
355  _target_bin_size = target_bin_size;
356 }
357 
358 
360 {
361  return _target_bin_size;
362 }
363 
364 
365 } // 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:566
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:80
PointLocatorTree(const MeshBase &mesh, const PointLocatorBase *master=nullptr)
Constructor.
virtual bool contains_point(const Point &p, Real tol=TOLERANCE) const
Definition: elem.C:2784
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:2809
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:2955
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.