LCOV - code coverage report
Current view: top level - src/utils - point_locator_tree.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 71 123 57.7 %
Date: 2025-08-19 19:27:09 Functions: 9 15 60.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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
      37           6 : PointLocatorTree::PointLocatorTree (const MeshBase & mesh,
      38           6 :                                     const PointLocatorBase * master) :
      39             :   PointLocatorBase (mesh,master),
      40             :   _tree            (nullptr),
      41           2 :   _element         (nullptr),
      42           2 :   _out_of_mesh_mode(false),
      43           2 :   _target_bin_size (200),
      44           6 :   _build_type(Trees::NODES)
      45             : {
      46           6 :   this->init(_build_type);
      47           6 : }
      48             : 
      49             : 
      50             : 
      51      500073 : PointLocatorTree::PointLocatorTree (const MeshBase & mesh,
      52             :                                     const Trees::BuildType build_type,
      53      500073 :                                     const PointLocatorBase * master) :
      54             :   PointLocatorBase (mesh,master),
      55             :   _tree            (nullptr),
      56      387479 :   _element         (nullptr),
      57      387479 :   _out_of_mesh_mode(false),
      58      387479 :   _target_bin_size (200),
      59      500073 :   _build_type(build_type)
      60             : {
      61      500073 :   this->init(_build_type);
      62      500073 : }
      63             : 
      64             : 
      65             : 
      66     1162441 : PointLocatorTree::~PointLocatorTree () = default;
      67             : 
      68             : 
      69             : 
      70           0 : void PointLocatorTree::clear ()
      71             : {
      72           0 :   this->_initialized = false;
      73             : 
      74             :   // reset() actually frees the memory if we are master, otherwise it
      75             :   // just reduces the ref. count.
      76           0 :   _tree.reset();
      77           0 : }
      78             : 
      79             : 
      80             : 
      81           0 : void PointLocatorTree::init()
      82             : {
      83           0 :   this->init(_build_type);
      84           0 : }
      85             : 
      86             : 
      87             : 
      88      500079 : void PointLocatorTree::init (Trees::BuildType build_type)
      89             : {
      90       56499 :   libmesh_assert (!this->_tree);
      91             : 
      92      500079 :   if (this->_initialized)
      93             :     {
      94             :       // Warn that we are already initialized
      95           0 :       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           0 :       if (_build_type != build_type)
      99             :         {
     100           0 :           libMesh::err << "Warning: PointLocatorTree is using build_type = " << _build_type << ".\n"
     101           0 :                        << "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      500079 :       _build_type = build_type;
     111             : 
     112      500079 :       if (this->_master == nullptr)
     113             :         {
     114        1012 :           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       12547 :           else if (this->_mesh.mesh_dimension() == 3) // && LIBMESH_DIM==3
     121        5884 :             _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         422 :               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        9563 :               BoundingBox bbox = MeshTools::create_bounding_box(this->_mesh);
     134             : 
     135             :               const Real
     136        9563 :                 Dx = bbox.second(0) - bbox.first(0),
     137        9563 :                 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        9985 :               if ( (std::abs(Dz/(Dx + 1.e-20)) < 1e-10) && (std::abs(bbox.second(2)) < 1.e-10) )
     143         418 :                 is_planar_xy = true;
     144             : 
     145         422 :               if (is_planar_xy)
     146       18424 :                 _tree = std::make_shared<Trees::QuadTree>(this->_mesh, get_target_bin_size(), _build_type);
     147             :               else
     148         280 :                 _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       55993 :             cast_ptr<const PointLocatorTree *>(this->_master);
     160             : 
     161      487532 :           if (my_master->initialized())
     162       55993 :             this->_tree = my_master->_tree;
     163             :           else
     164           0 :             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      500079 :       this->_element = nullptr;
     174             :     }
     175             : 
     176             :   // ready for take-off
     177      500079 :   this->_initialized = true;
     178      500079 : }
     179             : 
     180     4532083 : const Elem * PointLocatorTree::operator() (const Point & p,
     181             :                                            const std::set<subdomain_id_type> * allowed_subdomains) const
     182             : {
     183      327139 :   libmesh_assert (this->_initialized);
     184             : 
     185      654278 :   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     7365686 :   if (allowed_subdomains && this->_element && !allowed_subdomains->count(this->_element->subdomain_id()))
     189        1511 :     this->_element = nullptr;
     190             : 
     191     4532083 :   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.
     196     4213450 :       if (_use_contains_point_tol && !(this->_element->close_to_point(p, _contains_point_tol)))
     197       62172 :         this->_element = nullptr;
     198             : 
     199             :       // Otherwise, just call contains_point(p) with default tolerances.
     200     4151278 :       else if (!(this->_element->contains_point(p)))
     201      337686 :         this->_element = nullptr;
     202             :     }
     203             : 
     204             :   // First check the element from last time before asking the tree
     205     4532083 :   if (this->_element==nullptr)
     206             :     {
     207             :       // ask the tree
     208      718491 :       if (_use_contains_point_tol)
     209       62551 :         this->_element = this->_tree->find_element(p, allowed_subdomains, _contains_point_tol);
     210             :       else
     211      655940 :         this->_element = this->_tree->find_element(p, allowed_subdomains);
     212             : 
     213      718491 :       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.
     217      152101 :           if (_use_close_to_point_tol)
     218             :             {
     219           0 :               if (_verbose)
     220             :                 {
     221           0 :                   libMesh::out << "Performing linear search using close-to-point tolerance "
     222           0 :                                << _close_to_point_tol
     223           0 :                                << std::endl;
     224             :                 }
     225             : 
     226           0 :               this->_element =
     227           0 :                 this->perform_linear_search(p,
     228             :                                             allowed_subdomains,
     229             :                                             /*use_close_to_point*/ true,
     230           0 :                                             _close_to_point_tol);
     231             : 
     232           0 :               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          83 :           libmesh_assert_equal_to (_out_of_mesh_mode, true);
     241             : 
     242          83 :           return this->_element;
     243             :         }
     244             :     }
     245             : 
     246             :   // If we found an element, it should be active
     247      327056 :   libmesh_assert (!this->_element || this->_element->active());
     248             : 
     249             :   // If we found an element and have a restriction list, they better match
     250      327056 :   libmesh_assert (!this->_element || !allowed_subdomains || allowed_subdomains->count(this->_element->subdomain_id()));
     251             : 
     252             :   // return the element
     253     4379982 :   return this->_element;
     254             : }
     255             : 
     256             : 
     257      256693 : void PointLocatorTree::operator() (const Point & p,
     258             :                                    std::set<const Elem *> & candidate_elements,
     259             :                                    const std::set<subdomain_id_type> * allowed_subdomains) const
     260             : {
     261       32252 :   libmesh_assert (this->_initialized);
     262             : 
     263       64504 :   LOG_SCOPE("operator() - Version 2", "PointLocatorTree");
     264             : 
     265             :   // ask the tree
     266      256693 :   this->_tree->find_elements (p, candidate_elements, allowed_subdomains, _close_to_point_tol);
     267      256693 : }
     268             : 
     269             : 
     270             : 
     271           0 : const Elem * PointLocatorTree::perform_linear_search(const Point & p,
     272             :                                                      const std::set<subdomain_id_type> * allowed_subdomains,
     273             :                                                      bool use_close_to_point,
     274             :                                                      Real close_to_point_tolerance) const
     275             : {
     276           0 :   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.
     282             :   SimpleRange<MeshBase::const_element_iterator> r =
     283           0 :     this->_build_type == Trees::LOCAL_ELEMENTS ?
     284           0 :     this->_mesh.active_local_element_ptr_range() :
     285           0 :     this->_mesh.active_element_ptr_range();
     286             : 
     287           0 :   for (const auto & elem : r)
     288             :     {
     289           0 :       if (!allowed_subdomains ||
     290           0 :           allowed_subdomains->count(elem->subdomain_id()))
     291             :         {
     292           0 :           if (!use_close_to_point)
     293             :             {
     294           0 :               if (elem->contains_point(p, _contains_point_tol))
     295           0 :                 return elem;
     296             :             }
     297             :           else
     298             :             {
     299           0 :               if (elem->close_to_point(p, close_to_point_tolerance))
     300           0 :                 return elem;
     301             :             }
     302             :         }
     303             :     }
     304             : 
     305           0 :   return nullptr;
     306           0 : }
     307             : 
     308             : 
     309           0 : 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           0 :   LOG_SCOPE("perform_fuzzy_linear_search", "PointLocatorTree");
     314             : 
     315           0 :   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.
     321             :   SimpleRange<MeshBase::const_element_iterator> r =
     322           0 :     this->_build_type == Trees::LOCAL_ELEMENTS ?
     323           0 :     this->_mesh.active_local_element_ptr_range() :
     324           0 :     this->_mesh.active_element_ptr_range();
     325             : 
     326           0 :   for (const auto & elem : r)
     327           0 :     if ((!allowed_subdomains || allowed_subdomains->count(elem->subdomain_id())) && elem->close_to_point(p, close_to_point_tolerance))
     328           0 :       candidate_elements.insert(elem);
     329             : 
     330           0 :   return candidate_elements;
     331           0 : }
     332             : 
     333             : 
     334             : 
     335      246870 : void PointLocatorTree::enable_out_of_mesh_mode ()
     336             : {
     337             :   // Out-of-mesh mode should now work properly even on meshes with
     338             :   // non-affine elements.
     339      246870 :   _out_of_mesh_mode = true;
     340      246870 : }
     341             : 
     342             : 
     343           0 : void PointLocatorTree::disable_out_of_mesh_mode ()
     344             : {
     345           0 :   _out_of_mesh_mode = false;
     346           0 : }
     347             : 
     348             : 
     349           0 : void PointLocatorTree::set_target_bin_size (unsigned int target_bin_size)
     350             : {
     351           0 :   _target_bin_size = target_bin_size;
     352           0 : }
     353             : 
     354             : 
     355       12547 : unsigned int PointLocatorTree::get_target_bin_size () const
     356             : {
     357       12547 :   return _target_bin_size;
     358             : }
     359             : 
     360             : 
     361             : } // namespace libMesh

Generated by: LCOV version 1.14