libMesh
Loading...
Searching...
No Matches
tree_node.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#include <set>
22#include <array>
23
24// Local includes
25#include "libmesh/libmesh_config.h"
26#include "libmesh/tree_node.h"
27#include "libmesh/mesh_base.h"
28#include "libmesh/elem.h"
29
30namespace libMesh
31{
32
33// ------------------------------------------------------------
34// TreeNode class methods
35template <unsigned int N>
36bool TreeNode<N>::insert (const Node * nd)
37{
39 libmesh_assert_less (nd->id(), mesh.n_nodes());
40
41 // Return if we don't bound the node
42 if (!this->bounds_node(nd))
43 return false;
44
45 // Add the node to ourself if we are active
46 if (this->active())
47 {
48 nodes.push_back (nd);
49
50 // Refine ourself if we reach the target bin size for a TreeNode.
51 if (nodes.size() == tgt_bin_size)
52 this->refine();
53
54 return true;
55 }
56
57 // If we are not active simply pass the node along to
58 // our children
59 libmesh_assert_equal_to (children.size(), N);
60
61 bool was_inserted = false;
62 for (unsigned int c=0; c<N; c++)
63 if (children[c]->insert (nd))
64 was_inserted = true;
65 return was_inserted;
66}
67
68
69
70template <unsigned int N>
71bool TreeNode<N>::insert (const Elem * elem)
72{
73 libmesh_assert(elem);
74
75 // We first want to find the corners of the cuboid surrounding the cell.
76 const BoundingBox bbox = elem->loose_bounding_box();
77
78 // If we are using a QuadTree, it's either because LIBMESH_DIM==2 or
79 // we have a planar xy mesh. Either way, the bounding box
80 // comparison in this case needs to do something slightly different
81 // for the z-coordinate.
82 bool bboxes_intersect = false;
83
84 if (N == 8) // OctTree
85 bboxes_intersect = this->bounding_box.intersects(bbox);
86 else if (N == 4) // QuadTree
87 {
88 // Perform a specialized BoundingBox intersection check that
89 // ignores z-coords. Copied from geom/bounding_box.C Check for
90 // "real" intersection in the x and y directions, then check
91 // that the z-coordinate is "close".
92
93 // Helper macro
94#define IS_BETWEEN(min, check, max) \
95 ((min) <= (check) && (check) <= (max))
96
97 // Make local variables first to make things more clear in a moment
98 const Real & elem_min_x = bbox.first(0);
99 const Real & elem_max_x = bbox.second(0);
100 const Real & tree_min_x = this->bounding_box.first(0);
101 const Real & tree_max_x = this->bounding_box.second(0);
102
103 const Real & elem_min_y = bbox.first(1);
104 const Real & elem_max_y = bbox.second(1);
105 const Real & tree_min_y = this->bounding_box.first(1);
106 const Real & tree_max_y = this->bounding_box.second(1);
107
108 bool x_int =
109 IS_BETWEEN(elem_min_x, tree_min_x, elem_max_x) ||
110 IS_BETWEEN(elem_min_x, tree_max_x, elem_max_x) ||
111 IS_BETWEEN(tree_min_x, elem_min_x, tree_max_x) ||
112 IS_BETWEEN(tree_min_x, elem_max_x, tree_max_x);
113
114 bool y_int =
115 IS_BETWEEN(elem_min_y, tree_min_y, elem_max_y) ||
116 IS_BETWEEN(elem_min_y, tree_max_y, elem_max_y) ||
117 IS_BETWEEN(tree_min_y, elem_min_y, tree_max_y) ||
118 IS_BETWEEN(tree_min_y, elem_max_y, tree_max_y);
119
120 // When LIBMESH_DIM==3, check that the z-coordinates of the elem
121 // bbox and the tree bbox are "close" since the QuadTree is
122 // meant to work in the case when the mesh is planar_xy but not
123 // necessarily lying in the z=0 plane.
124 bool z_match = true;
125 if (LIBMESH_DIM == 3)
126 {
127 const Real & elem_min_z = bbox.first(2);
128 const Real & elem_max_z = bbox.second(2);
129 const Real & tree_min_z = this->bounding_box.first(2);
130 const Real & tree_max_z = this->bounding_box.second(2);
131
132 z_match =
133 (std::abs(elem_min_z - elem_max_z) < TOLERANCE) &&
134 (std::abs(tree_min_z - tree_max_z) < TOLERANCE) &&
135 (std::abs(elem_min_z - tree_max_z) < TOLERANCE);
136 }
137
138 bboxes_intersect = z_match && x_int && y_int;
139 }
140 else // binary tree
141 {
142 // TODO: implement 1D bounding box intersection check
143 libmesh_not_implemented();
144 }
145
146 // Next, find out whether this cuboid has got non-empty intersection
147 // with the bounding box of the current tree node.
148 //
149 // If not, we should not care about this element.
150 if (!bboxes_intersect)
151 return false;
152
153 // Only add the element if we are active
154 if (this->active())
155 {
156 elements.push_back (elem);
157
158#ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
159
160 // flag indicating this node contains
161 // infinite elements
162 if (elem->infinite())
163 this->contains_ifems = true;
164
165#endif
166
167 unsigned int element_count = cast_int<unsigned int>(elements.size());
169 {
170 const std::set<unsigned char> & elem_dimensions = mesh.elem_dimensions();
171 if (elem_dimensions.size() > 1)
172 {
173 element_count = 0;
174 unsigned char highest_dim_elem = *elem_dimensions.rbegin();
175 for (const Elem * other_elem : elements)
176 if (other_elem->dim() == highest_dim_elem)
177 element_count++;
178 }
179 }
180
181 // Refine ourself if we reach the target bin size for a TreeNode.
182 if (element_count == tgt_bin_size)
183 this->refine();
184
185 return true;
186 }
187
188 // If we are not active simply pass the element along to
189 // our children
190 libmesh_assert_equal_to (children.size(), N);
191
192 bool was_inserted = false;
193 for (unsigned int c=0; c<N; c++)
194 if (children[c]->insert (elem))
195 was_inserted = true;
196 return was_inserted;
197}
198
199
200
201template <unsigned int N>
203{
204 // Huh? better be active...
205 libmesh_assert (this->active());
206 libmesh_assert (children.empty());
207
208 // A TreeNode<N> has by definition N children
209 children.resize(N);
210
211 // Scale up the target bin size in child TreeNodes if we have reached
212 // the maximum number of refinement levels.
213 unsigned int new_target_bin_size = tgt_bin_size;
214 if (level() >= target_bin_size_increase_level)
215 {
216 new_target_bin_size *= 2;
217 }
218
219 for (unsigned int c=0; c<N; c++)
220 {
221 // Create the child and set its bounding box.
222 children[c] = std::make_unique<TreeNode<N>>(mesh, new_target_bin_size, this);
223 children[c]->set_bounding_box(this->create_bounding_box(c));
224
225 // Pass off our nodes to our children
226 for (const Node * node : nodes)
227 children[c]->insert(node);
228
229 // Pass off our elements to our children
230 for (const Elem * elem : elements)
231 children[c]->insert(elem);
232 }
233
234 // We don't need to store nodes or elements any more, they have been
235 // added to the children. Use the "swap trick" to actually reduce
236 // the capacity of these vectors.
237 std::vector<const Node *>().swap(nodes);
238 std::vector<const Elem *>().swap(elements);
239
240 libmesh_assert_equal_to (nodes.capacity(), 0);
241 libmesh_assert_equal_to (elements.capacity(), 0);
242}
243
244
245
246template <unsigned int N>
247void TreeNode<N>::set_bounding_box (const std::pair<Point, Point> & bbox)
248{
249 bounding_box = bbox;
250}
251
252
253
254template <unsigned int N>
256 Real relative_tol) const
257{
258 libmesh_assert(nd);
259 return bounds_point(*nd, relative_tol);
260}
261
262
263
264template <unsigned int N>
266 Real relative_tol) const
267{
268 const Point & min = bounding_box.first;
269 const Point & max = bounding_box.second;
270
271 const Real tol = (max - min).norm() * relative_tol;
272
273 if ((p(0) >= min(0) - tol)
274 && (p(0) <= max(0) + tol)
275#if LIBMESH_DIM > 1
276 && (p(1) >= min(1) - tol)
277 && (p(1) <= max(1) + tol)
278#endif
279#if LIBMESH_DIM > 2
280 && (p(2) >= min(2) - tol)
281 && (p(2) <= max(2) + tol)
282#endif
283 )
284 return true;
285
286 return false;
287}
288
289
290
291template <unsigned int N>
294{
295 switch (N)
296 {
297 // How to refine an OctTree Node
298 case 8:
299 {
300 const Real xmin = bounding_box.first(0);
301 const Real ymin = bounding_box.first(1);
302 const Real zmin = bounding_box.first(2);
303
304 const Real xmax = bounding_box.second(0);
305 const Real ymax = bounding_box.second(1);
306 const Real zmax = bounding_box.second(2);
307
308 const Real xc = .5*(xmin + xmax);
309 const Real yc = .5*(ymin + ymax);
310 const Real zc = .5*(zmin + zmax);
311
312 switch (c)
313 {
314 case 0:
315 return BoundingBox (Point(xmin, ymin, zmin),
316 Point(xc, yc, zc));
317 case 1:
318 return BoundingBox (Point(xc, ymin, zmin),
319 Point(xmax, yc, zc));
320 case 2:
321 return BoundingBox (Point(xmin, yc, zmin),
322 Point(xc, ymax, zc));
323 case 3:
324 return BoundingBox (Point(xc, yc, zmin),
325 Point(xmax, ymax, zc));
326 case 4:
327 return BoundingBox (Point(xmin, ymin, zc),
328 Point(xc, yc, zmax));
329 case 5:
330 return BoundingBox (Point(xc, ymin, zc),
331 Point(xmax, yc, zmax));
332 case 6:
333 return BoundingBox (Point(xmin, yc, zc),
334 Point(xc, ymax, zmax));
335 case 7:
336 return BoundingBox (Point(xc, yc, zc),
337 Point(xmax, ymax, zmax));
338 default:
339 libmesh_error_msg("c >= N! : " << c);
340 }
341
342 break;
343 } // case 8
344
345 // How to refine an QuadTree Node
346 case 4:
347 {
348 const Real xmin = bounding_box.first(0);
349 const Real ymin = bounding_box.first(1);
350
351 const Real xmax = bounding_box.second(0);
352 const Real ymax = bounding_box.second(1);
353
354 const Real xc = .5*(xmin + xmax);
355 const Real yc = .5*(ymin + ymax);
356
357 switch (c)
358 {
359 case 0:
360 return BoundingBox (Point(xmin, ymin),
361 Point(xc, yc));
362 case 1:
363 return BoundingBox (Point(xc, ymin),
364 Point(xmax, yc));
365 case 2:
366 return BoundingBox (Point(xmin, yc),
367 Point(xc, ymax));
368 case 3:
369 return BoundingBox (Point(xc, yc),
370 Point(xmax, ymax));
371 default:
372 libmesh_error_msg("c >= N!");
373 }
374
375 break;
376 } // case 4
377
378 // How to refine a BinaryTree Node
379 case 2:
380 {
381 const Real xmin = bounding_box.first(0);
382
383 const Real xmax = bounding_box.second(0);
384
385 const Real xc = .5*(xmin + xmax);
386
387 switch (c)
388 {
389 case 0:
390 return BoundingBox (Point(xmin),
391 Point(xc));
392 case 1:
393 return BoundingBox (Point(xc),
394 Point(xmax));
395 default:
396 libmesh_error_msg("c >= N!");
397 }
398
399 break;
400 } // case 2
401
402 default:
403 libmesh_error_msg("Only implemented for Octrees, QuadTrees, and Binary Trees!");
404 }
405}
406
407
408
409template <unsigned int N>
410void TreeNode<N>::print_nodes(std::ostream & out_stream) const
411{
412 if (this->active())
413 {
414 out_stream << "TreeNode Level: " << this->level() << std::endl;
415
416 for (const Node * node : nodes)
417 out_stream << " " << node->id();
418
419 out_stream << std::endl << std::endl;
420 }
421 else
422 for (const auto & child : children)
423 child->print_nodes();
424}
425
426
427
428template <unsigned int N>
429void TreeNode<N>::print_elements(std::ostream & out_stream) const
430{
431 if (this->active())
432 {
433 out_stream << "TreeNode Level: " << this->level() << std::endl;
434
435 for (const auto & elem : elements)
436 out_stream << " " << elem;
437
438 out_stream << std::endl << std::endl;
439 }
440 else
441 for (const auto & child : children)
442 child->print_elements();
443}
444
445
446
447template <unsigned int N>
448void TreeNode<N>::transform_nodes_to_elements (std::vector<std::vector<const Elem *>> & nodes_to_elem)
449{
450 if (this->active())
451 {
452 elements.clear();
453
454 // Temporarily use a set. Since multiple nodes
455 // will likely map to the same element we use a
456 // set to eliminate the duplication.
457 std::set<const Elem *> elements_set;
458
459 for (const Node * node : nodes)
460 {
461 // the actual global node number we are replacing
462 // with the connected elements
463 const dof_id_type node_number = node->id();
464
465 libmesh_assert_less (node_number, mesh.n_nodes());
466 libmesh_assert_less (node_number, nodes_to_elem.size());
467
468 for (const Elem * elem : nodes_to_elem[node_number])
469 elements_set.insert(elem);
470 }
471
472 // Done with the nodes.
473 std::vector<const Node *>().swap(nodes);
474
475 // Now the set is built. We can copy this to the
476 // vector. Note that the resulting vector will
477 // already be sorted, and will require less memory
478 // than the set.
479 elements.reserve(elements_set.size());
480
481 for (const auto & elem : elements_set)
482 {
483 elements.push_back(elem);
484
485#ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
486
487 // flag indicating this node contains
488 // infinite elements
489 if (elem->infinite())
490 this->contains_ifems = true;
491
492#endif
493 }
494 }
495 else
496 for (auto & child : children)
497 child->transform_nodes_to_elements (nodes_to_elem);
498}
499
500
501
502template <unsigned int N>
503void TreeNode<N>::transform_nodes_to_elements (std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodes_to_elem)
504{
505 if (this->active())
506 {
507 elements.clear();
508
509 // Temporarily use a set. Since multiple nodes
510 // will likely map to the same element we use a
511 // set to eliminate the duplication.
512 std::set<const Elem *> elements_set;
513
514 for (const Node * node : nodes)
515 {
516 // the actual global node number we are replacing
517 // with the connected elements
518 const dof_id_type node_number = node->id();
519
520 libmesh_assert_less (node_number, mesh.n_nodes());
521
522 auto & my_elems = nodes_to_elem[node_number];
523 elements_set.insert(my_elems.begin(), my_elems.end());
524 }
525
526 // Done with the nodes.
527 std::vector<const Node *>().swap(nodes);
528
529 // Now the set is built. We can copy this to the
530 // vector. Note that the resulting vector will
531 // already be sorted, and will require less memory
532 // than the set.
533 elements.reserve(elements_set.size());
534
535 for (const auto & elem : elements_set)
536 {
537 elements.push_back(elem);
538
539#ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
540
541 // flag indicating this node contains
542 // infinite elements
543 if (elem->infinite())
544 this->contains_ifems = true;
545
546#endif
547 }
548 }
549 else
550 for (auto & child : children)
551 child->transform_nodes_to_elements (nodes_to_elem);
552}
553
554
555
556template <unsigned int N>
557unsigned int TreeNode<N>::n_active_bins() const
558{
559 if (this->active())
560 return 1;
561
562 else
563 {
564 unsigned int sum=0;
565
566 for (const auto & child : children)
567 sum += child->n_active_bins();
568
569 return sum;
570 }
571}
572
573
574
575template <unsigned int N>
576const Elem *
578 const std::set<subdomain_id_type> * allowed_subdomains,
579 Real relative_tol) const
580{
581 if (this->active())
582 {
583 // Only check our children if the point is in our bounding box
584 // or if the node contains infinite elements
585 if (this->bounds_point(p, relative_tol) || this->contains_ifems)
586 // Search the active elements in the active TreeNode.
587 for (const auto & elem : elements)
588 if (!allowed_subdomains || allowed_subdomains->count(elem->subdomain_id()))
589 if (elem->active())
590 {
591 bool found = relative_tol > TOLERANCE
592 ? elem->close_to_point(p, relative_tol)
593 : elem->contains_point(p);
594
595 if (found)
596 return elem;
597 }
598
599 // The point was not found in any element
600 return nullptr;
601 }
602 else
603 return this->find_element_in_children(p,allowed_subdomains,
604 relative_tol);
605}
606
607
608
609template <unsigned int N>
610void
612 std::set<const Elem *> & candidate_elements,
613 const std::set<subdomain_id_type> * allowed_subdomains,
614 Real relative_tol) const
615{
616 if (this->active())
617 {
618 // Only check our children if the point is in our bounding box
619 // or if the node contains infinite elements
620 if (this->bounds_point(p, relative_tol) || this->contains_ifems)
621 // Search the active elements in the active TreeNode.
622 for (const auto & elem : elements)
623 if (!allowed_subdomains || allowed_subdomains->count(elem->subdomain_id()))
624 if (elem->active())
625 {
626 bool found = relative_tol > TOLERANCE
627 ? elem->close_to_point(p, relative_tol)
628 : elem->contains_point(p);
629
630 if (found)
631 candidate_elements.insert(elem);
632 }
633 }
634 else
635 this->find_elements_in_children(p, candidate_elements,
636 allowed_subdomains, relative_tol);
637}
638
639
640
641template <unsigned int N>
643 const std::set<subdomain_id_type> * allowed_subdomains,
644 Real relative_tol) const
645{
646 libmesh_assert (!this->active());
647
648 // value-initialization sets all array members to false
649 auto searched_child = std::array<bool, N>();
650
651 // First only look in the children whose bounding box
652 // contain the point p.
653 for (auto c : index_range(children))
654 if (children[c]->bounds_point(p, relative_tol))
655 {
656 const Elem * e =
657 children[c]->find_element(p,allowed_subdomains,
658 relative_tol);
659
660 if (e != nullptr)
661 return e;
662
663 // If we get here then a child that bounds the
664 // point does not have any elements that contain
665 // the point. So, we will search all our children.
666 // However, we have already searched child c so there
667 // is no use searching her again.
668 searched_child[c] = true;
669 }
670
671
672 // If we get here then our child whose bounding box
673 // was searched and did not find any elements containing
674 // the point p. So, let's look at the other children
675 // but exclude the one we have already searched.
676 for (auto c : index_range(children))
677 if (!searched_child[c])
678 {
679 const Elem * e =
680 children[c]->find_element(p,allowed_subdomains,
681 relative_tol);
682
683 if (e != nullptr)
684 return e;
685 }
686
687 // If we get here we have searched all our children.
688 // Since this process was started at the root node then
689 // we have searched all the elements in the tree without
690 // success. So, we should return nullptr since at this point
691 // _no_ elements in the tree claim to contain point p.
692 return nullptr;
693}
694
695
696
697template <unsigned int N>
699 std::set<const Elem *> & candidate_elements,
700 const std::set<subdomain_id_type> * allowed_subdomains,
701 Real relative_tol) const
702{
703 libmesh_assert (!this->active());
704
705 // First only look in the children whose bounding box
706 // contain the point p.
707 for (std::size_t c=0; c<children.size(); c++)
708 if (children[c]->bounds_point(p, relative_tol))
709 children[c]->find_elements(p, candidate_elements,
710 allowed_subdomains, relative_tol);
711}
712
713
714
715// ------------------------------------------------------------
716// Explicit Instantiations
717template class LIBMESH_EXPORT TreeNode<2>;
718template class LIBMESH_EXPORT TreeNode<4>;
719template class LIBMESH_EXPORT TreeNode<8>;
720
721} // namespace libMesh
Defines a Cartesian bounding box by the two corner extremum.
bool intersects(const BoundingBox &) const
dof_id_type id() const
Definition dof_object.h:819
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual BoundingBox loose_bounding_box() const
Definition elem.C:3498
virtual bool close_to_point(const Point &p, Real tol) const
Definition elem.C:2809
virtual bool infinite() const =0
const std::set< unsigned char > & elem_dimensions() const
Definition mesh_base.h:430
virtual dof_id_type n_nodes() const =0
bool get_count_lower_dim_elems_in_point_locator() const
Get the current value of _count_lower_dim_elems_in_point_locator.
Definition mesh_base.C:1880
A Node is like a Point, but with more information.
Definition node.h:55
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
This class defines a node on a tree.
Definition tree_node.h:55
void print_nodes(std::ostream &out_stream=libMesh::out) const
Prints the contents of the node_numbers vector if we are active.
Definition tree_node.C:410
bool bounds_node(const Node *nd, Real relative_tol=0) const
Definition tree_node.C:255
BoundingBox create_bounding_box(unsigned int c) const
Constructs the bounding box for child c.
Definition tree_node.C:293
void print_elements(std::ostream &out_stream=libMesh::out) const
Prints the contents of the elements set if we are active.
Definition tree_node.C:429
void transform_nodes_to_elements(std::vector< std::vector< const Elem * > > &nodes_to_elem)
Transforms node numbers to element pointers.
Definition tree_node.C:448
bool bounds_point(const Point &p, Real relative_tol=0) const
Definition tree_node.C:265
void find_elements_in_children(const Point &p, std::set< const Elem * > &candidate_elements, const std::set< subdomain_id_type > *allowed_subdomains, Real relative_tol) const
Look for points in our children, optionally restricted to a set of allowed subdomains.
Definition tree_node.C:698
void refine()
Refine the tree node into N children if it contains more than tol nodes.
Definition tree_node.C:202
void set_bounding_box(const std::pair< Point, Point > &bbox)
Sets the bounding box;.
Definition tree_node.C:247
const Elem * find_element_in_children(const Point &p, const std::set< subdomain_id_type > *allowed_subdomains, Real relative_tol) const
Look for point p in our children, optionally restricted to a set of allowed subdomains.
Definition tree_node.C:642
void find_elements(const Point &p, std::set< const Elem * > &candidate_elements, const std::set< subdomain_id_type > *allowed_subdomains=nullptr, Real relative_tol=TOLERANCE) const
Adds to candidate_elements any elements containing the specified point p, optionally restricted to a ...
Definition tree_node.C:611
unsigned int n_active_bins() const
Definition tree_node.C:557
bool insert(const Node *nd)
Tries to insert Node nd into the TreeNode.
Definition tree_node.C:36
const Elem * find_element(const Point &p, const std::set< subdomain_id_type > *allowed_subdomains=nullptr, Real relative_tol=TOLERANCE) const
Definition tree_node.C:577
MeshBase & mesh
The libMesh namespace provides an interface to certain functionality in the library.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
libmesh_assert(ctx)
static constexpr Real TOLERANCE
uint8_t dof_id_type
Definition id_types.h:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real