$extrastylesheet
point_locator_tree.C
Go to the documentation of this file.
00001 // The libMesh Finite Element Library.
00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
00003 
00004 // This library is free software; you can redistribute it and/or
00005 // modify it under the terms of the GNU Lesser General Public
00006 // License as published by the Free Software Foundation; either
00007 // version 2.1 of the License, or (at your option) any later version.
00008 
00009 // This library is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 // Lesser General Public License for more details.
00013 
00014 // You should have received a copy of the GNU Lesser General Public
00015 // License along with this library; if not, write to the Free Software
00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 
00018 
00019 
00020 // C++ includes
00021 
00022 // Local Includes
00023 #include "libmesh/elem.h"
00024 #include "libmesh/libmesh_logging.h"
00025 #include "libmesh/mesh_base.h"
00026 #include "libmesh/mesh_tools.h"
00027 #include "libmesh/point_locator_tree.h"
00028 #include "libmesh/tree.h"
00029 
00030 namespace libMesh
00031 {
00032 
00033 
00034 
00035 //------------------------------------------------------------------
00036 // PointLocator methods
00037 PointLocatorTree::PointLocatorTree (const MeshBase& mesh,
00038                                     const PointLocatorBase* master) :
00039   PointLocatorBase (mesh,master),
00040   _tree            (NULL),
00041   _element         (NULL),
00042   _out_of_mesh_mode(false),
00043   _target_bin_size (200),
00044   _build_type(Trees::NODES)
00045 {
00046   this->init(_build_type);
00047 }
00048 
00049 
00050 
00051 PointLocatorTree::PointLocatorTree (const MeshBase& mesh,
00052                                     const Trees::BuildType build_type,
00053                                     const PointLocatorBase* master) :
00054   PointLocatorBase (mesh,master),
00055   _tree            (NULL),
00056   _element         (NULL),
00057   _out_of_mesh_mode(false),
00058   _target_bin_size (200),
00059   _build_type(build_type)
00060 {
00061   this->init(_build_type);
00062 }
00063 
00064 
00065 
00066 PointLocatorTree::~PointLocatorTree ()
00067 {
00068   this->clear ();
00069 }
00070 
00071 
00072 
00073 void PointLocatorTree::clear ()
00074 {
00075   // only delete the tree when we are the master
00076   if (this->_tree != NULL)
00077     {
00078       if (this->_master == NULL)
00079         // we own the tree
00080         delete this->_tree;
00081       else
00082         // someone else owns and therefore deletes the tree
00083         this->_tree = NULL;
00084     }
00085 }
00086 
00087 
00088 
00089 void PointLocatorTree::init()
00090 {
00091   this->init(_build_type);
00092 }
00093 
00094 
00095 
00096 void PointLocatorTree::init (Trees::BuildType build_type)
00097 {
00098   libmesh_assert (!this->_tree);
00099 
00100   if (this->_initialized)
00101     {
00102       // Warn that we are already initialized
00103       libMesh::err << "Warning: PointLocatorTree already initialized!  Will ignore this call..." << std::endl;
00104 
00105       // Further warn if we try to init() again with a different build_type
00106       if (_build_type != build_type)
00107         {
00108           libMesh::err << "Warning: PointLocatorTree is using build_type = " << _build_type << ".\n"
00109                        << "Your requested build_type, " << build_type << " will not be used!" << std::endl;
00110         }
00111     }
00112 
00113   else
00114     {
00115       // Let the requested build_type override the _build_type we were
00116       // constructed with.  This is no big deal since we have not been
00117       // initialized before.
00118       _build_type = build_type;
00119 
00120       if (this->_master == NULL)
00121         {
00122           START_LOG("init(no master)", "PointLocatorTree");
00123 
00124           if (this->_mesh.mesh_dimension() == 3)
00125             _tree = new Trees::OctTree (this->_mesh, get_target_bin_size(), _build_type);
00126           else
00127             {
00128               // A 1D/2D mesh in 3D space needs special consideration.
00129               // If the mesh is planar XY, we want to build a QuadTree
00130               // to search efficiently.  If the mesh is truly a manifold,
00131               // then we need an octree
00132 #if LIBMESH_DIM > 2
00133               bool is_planar_xy = false;
00134 
00135               // Build the bounding box for the mesh.  If the delta-z bound is
00136               // negligibly small then we can use a quadtree.
00137               {
00138                 MeshTools::BoundingBox bbox = MeshTools::bounding_box(this->_mesh);
00139 
00140                 const Real
00141                   Dx = bbox.second(0) - bbox.first(0),
00142                   Dz = bbox.second(2) - bbox.first(2);
00143 
00144                 if (std::abs(Dz/(Dx + 1.e-20)) < 1e-10)
00145                   is_planar_xy = true;
00146               }
00147 
00148               if (!is_planar_xy)
00149                 _tree = new Trees::OctTree (this->_mesh, get_target_bin_size(), _build_type);
00150               else
00151 #endif
00152 #if LIBMESH_DIM > 1
00153                 _tree = new Trees::QuadTree (this->_mesh, get_target_bin_size(), _build_type);
00154 #else
00155               _tree = new Trees::BinaryTree (this->_mesh, get_target_bin_size(), _build_type);
00156 #endif
00157             }
00158 
00159           STOP_LOG("init(no master)", "PointLocatorTree");
00160         }
00161 
00162       else
00163         {
00164           // We are _not_ the master.  Let our Tree point to
00165           // the master's tree.  But for this we first transform
00166           // the master in a state for which we are friends.
00167           // And make sure the master @e has a tree!
00168           const PointLocatorTree* my_master =
00169             cast_ptr<const PointLocatorTree*>(this->_master);
00170 
00171           if (my_master->initialized())
00172             this->_tree = my_master->_tree;
00173           else
00174             libmesh_error_msg("ERROR: Initialize master first, then servants!");
00175         }
00176 
00177       // Not all PointLocators may own a tree, but all of them
00178       // use their own element pointer.  Let the element pointer
00179       // be unique for every interpolator.
00180       // Suppose the interpolators are used concurrently
00181       // at different locations in the mesh, then it makes quite
00182       // sense to have unique start elements.
00183       this->_element = NULL;
00184     }
00185 
00186   // ready for take-off
00187   this->_initialized = true;
00188 }
00189 
00190 
00191 
00192 const Elem* PointLocatorTree::operator() (const Point& p, const std::set<subdomain_id_type> *allowed_subdomains) const
00193 {
00194   libmesh_assert (this->_initialized);
00195 
00196   START_LOG("operator()", "PointLocatorTree");
00197 
00198   // If we're provided with an allowed_subdomains list and have a cached element, make sure it complies
00199   if (allowed_subdomains && this->_element && !allowed_subdomains->count(this->_element->subdomain_id())) this->_element = NULL;
00200 
00201   // First check the element from last time before asking the tree
00202   if (this->_element==NULL || !(this->_element->contains_point(p)))
00203     {
00204       // ask the tree
00205       this->_element = this->_tree->find_element (p,allowed_subdomains);
00206 
00207       if (this->_element == NULL)
00208         {
00209           // No element seems to contain this point. Thus:
00210           // 1.) If _out_of_mesh_mode == true, we can just return NULL
00211           //     without searching further.
00212           // 2.) If _out_of_mesh_mode == false, we perform a linear
00213           //     search over all active (possibly local) elements.
00214           //     The idea here is that, in the case of curved elements,
00215           //     the bounding box computed in \p TreeNode::insert(const
00216           //     Elem*) might be slightly inaccurate and therefore we may
00217           //     have generated a false negative.
00218           if (_out_of_mesh_mode == false)
00219             {
00220               this->_element = this->perform_linear_search(p, allowed_subdomains, /*use_close_to_point*/ false);
00221 
00222               STOP_LOG("operator()", "PointLocatorTree");
00223               return this->_element;
00224             }
00225 
00226           // If we haven't found the element, we may want to do a linear
00227           // search using a tolerance. We only do this if _out_of_mesh_mode == true,
00228           // since we're looking for a point that may be outside of the mesh (within the
00229           // specified tolerance).
00230           if( _use_close_to_point_tol )
00231             {
00232               if(_verbose)
00233                 {
00234                   libMesh::out << "Performing linear search using close-to-point tolerance "
00235                                << _close_to_point_tol
00236                                << std::endl;
00237                 }
00238 
00239               this->_element =
00240                 this->perform_linear_search(p,
00241                                             allowed_subdomains,
00242                                             /*use_close_to_point*/ true,
00243                                             _close_to_point_tol);
00244 
00245               STOP_LOG("operator()", "PointLocatorTree");
00246               return this->_element;
00247             }
00248         }
00249     }
00250 
00251   // If we found an element, it should be active
00252   libmesh_assert (!this->_element || this->_element->active());
00253 
00254   // If we found an element and have a restriction list, they better match
00255   libmesh_assert (!this->_element || !allowed_subdomains || allowed_subdomains->count(this->_element->subdomain_id()));
00256 
00257   STOP_LOG("operator()", "PointLocatorTree");
00258 
00259   // return the element
00260   return this->_element;
00261 }
00262 
00263 
00264 
00265 const Elem* PointLocatorTree::perform_linear_search(const Point& p,
00266                                                     const std::set<subdomain_id_type> *allowed_subdomains,
00267                                                     bool use_close_to_point,
00268                                                     Real close_to_point_tolerance) const
00269 {
00270   START_LOG("perform_linear_search", "PointLocatorTree");
00271 
00272   // The type of iterator depends on the Trees::BuildType
00273   // used for this PointLocator.  If it's
00274   // TREE_LOCAL_ELEMENTS, we only want to double check
00275   // local elements during this linear search.
00276   MeshBase::const_element_iterator pos =
00277     this->_build_type == Trees::LOCAL_ELEMENTS ?
00278     this->_mesh.active_local_elements_begin() : this->_mesh.active_elements_begin();
00279 
00280   const MeshBase::const_element_iterator end_pos =
00281     this->_build_type == Trees::LOCAL_ELEMENTS ?
00282     this->_mesh.active_local_elements_end() : this->_mesh.active_elements_end();
00283 
00284   for ( ; pos != end_pos; ++pos)
00285     {
00286       if (!allowed_subdomains ||
00287           allowed_subdomains->count((*pos)->subdomain_id()))
00288         {
00289           if(!use_close_to_point)
00290             {
00291               if ((*pos)->contains_point(p))
00292                 {
00293                   STOP_LOG("perform_linear_search", "PointLocatorTree");
00294                   return (*pos);
00295                 }
00296             }
00297           else
00298             {
00299               if ((*pos)->close_to_point(p, close_to_point_tolerance))
00300                 {
00301                   STOP_LOG("perform_linear_search", "PointLocatorTree");
00302                   return (*pos);
00303                 }
00304             }
00305         }
00306     }
00307 
00308   STOP_LOG("perform_linear_search", "PointLocatorTree");
00309   return NULL;
00310 }
00311 
00312 
00313 void PointLocatorTree::enable_out_of_mesh_mode ()
00314 {
00315   // Out-of-mesh mode is currently only supported if all of the
00316   // elements have affine mappings.  The reason is that for quadratic
00317   // mappings, it is not easy to construct a reliable bounding box of
00318   // the element, and thus, the fallback linear search in \p
00319   // operator() is required.  Hence, out-of-mesh mode would be
00320   // extremely slow.
00321   if (_out_of_mesh_mode == false)
00322     {
00323 #ifdef DEBUG
00324       MeshBase::const_element_iterator       pos     = this->_mesh.active_elements_begin();
00325       const MeshBase::const_element_iterator end_pos = this->_mesh.active_elements_end();
00326       for ( ; pos != end_pos; ++pos)
00327         if (!(*pos)->has_affine_map())
00328           libmesh_error_msg("ERROR: Out-of-mesh mode is currently only supported if all elements have affine mappings.");
00329 #endif
00330 
00331       _out_of_mesh_mode = true;
00332     }
00333 }
00334 
00335 
00336 void PointLocatorTree::disable_out_of_mesh_mode ()
00337 {
00338   _out_of_mesh_mode = false;
00339 }
00340 
00341 
00342 void PointLocatorTree::set_target_bin_size (unsigned int target_bin_size)
00343 {
00344   _target_bin_size = target_bin_size;
00345 }
00346 
00347 
00348 unsigned int PointLocatorTree::get_target_bin_size () const
00349 {
00350   return _target_bin_size;
00351 }
00352 
00353 
00354 } // namespace libMesh