ug4
ntree_impl.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2015: G-CSC, Goethe University Frankfurt
3  * Author: Sebastian Reiter
4  *
5  * This file is part of UG4.
6  *
7  * UG4 is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License version 3 (as published by the
9  * Free Software Foundation) with the following additional attribution
10  * requirements (according to LGPL/GPL v3 §7):
11  *
12  * (1) The following notice must be displayed in the Appropriate Legal Notices
13  * of covered and combined works: "Based on UG4 (www.ug4.org/license)".
14  *
15  * (2) The following notice must be displayed at a prominent place in the
16  * terminal output of covered works: "Based on UG4 (www.ug4.org/license)".
17  *
18  * (3) The following bibliography is recommended for citation and must be
19  * preserved in all covered files:
20  * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively
21  * parallel geometric multigrid solver on hierarchically distributed grids.
22  * Computing and visualization in science 16, 4 (2013), 151-164"
23  * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel
24  * flexible software system for simulating pde based models on high performance
25  * computers. Computing and visualization in science 16, 4 (2013), 165-179"
26  *
27  * This program is distributed in the hope that it will be useful,
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30  * GNU Lesser General Public License for more details.
31  */
32 
33 #ifndef __H__UG__ntree_impl__
34 #define __H__UG__ntree_impl__
35 
36 #include <cassert>
37 #include "ntree.h"
38 
39 namespace ug{
40 
41 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
43 ntree() :
44  m_warningsEnabled (true)
45 {
46  m_nodes.resize(1);
47 }
48 
49 
50 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
53 {
54  m_nodes.clear();
55  m_nodes.resize(1);
56  m_nodes[0].level = 0;
57 }
58 
59 
60 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
62 clear()
63 {
64  clear_nodes();
65  m_entries.clear();
66  m_numDelayedElements = 0;
67 }
68 
69 
70 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
72 set_desc(const NTreeDesc& desc)
73 {
74  m_desc = desc;
75 }
76 
77 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
79 desc() const
80 {
81  return m_desc;
82 }
83 
84 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
86 set_common_data(const common_data_t& commonData)
87 {
88  m_commonData = commonData;
89 }
90 
91 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
93 common_data() const
94 {
95  return m_commonData;
96 }
97 
98 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
100 empty() const
101 {
102  return size() == 0;
103 }
104 
105 
106 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
108 size() const
109 {
110  return m_entries.size() - m_numDelayedElements;
111 }
112 
113 
114 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
116 num_delayed_elements() const
117 {
118  return m_numDelayedElements;
119 }
120 
121 
122 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
124 add_element(const elem_t& elem)
125 {
126 // size_t entryInd = m_entries.size();
127  m_entries.push_back(Entry(elem));
128 
129  ++m_numDelayedElements;
130 
131 //todo If we allowed for immediate on-the-fly insertion of elements, the following
132 // code could serve as an implementation base. Note, that it is not yet complete.
133 // One would have to update parent boxes if a child box changes etc.
134 // It would make sense to also allow for the specification of a
135 // minimum root-bounding-box, since else most insertions would trigger a rebalance.
136 // {
137 // vector_t center;
138 // traits::calculate_center(center, elem, m_commonData);
139 // size_t nodeInd = find_leaf_node(center);
140 // if(nodeInd == s_invalidIndex){
141 // ++m_numDelayedElements;
142 // rebalance();
143 // }
144 // else{
145 // Node& node = m_nodes[nodeInd];
146 // add_entry_to_node(node, entryInd);
147 // if(node.numEntries >= m_desc.splitThreshold)
148 // split_leaf_node(nodeInd);
149 // else{
150 // //todo the update could be much more efficient (simply unite the
151 // // old bounding box with the new element's bounding box.
152 // update_loose_bounding_box(node);
153 // }
154 // }
155 // }
156 }
157 
158 
159 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
161 rebalance()
162 {
163 // push all elements into the root node, calculate its bounding box
164 // and call split_leaf_node if the element threshold is surpassed.
165  clear_nodes();
166  Node& root = m_nodes.back();
167  if(!m_entries.empty()){
168  root.firstEntryInd = 0;
169  root.lastEntryInd = m_entries.size() - 1;
170  root.numEntries = m_entries.size();
171  for(size_t i = 0; i < m_entries.size(); ++i)
172  m_entries[i].nextEntryInd = i+1;
173  m_entries.back().nextEntryInd = s_invalidIndex;
174 
175  // the tight bounding box and the loose bounding box of the root node
176  // should be the same.
177  update_loose_bounding_box(root);
178  root.tightBox = root.looseBox;
179 
180  if(root.numEntries >= m_desc.splitThreshold)
181  split_leaf_node(0);
182  }
183 }
184 
185 
186 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
188 split_leaf_node(size_t nodeIndex)
189 {
190 
191  if(m_nodes[nodeIndex].numEntries <= 1)
192  return;
193 
194  if(m_nodes[nodeIndex].level >= m_desc.maxDepth){
195  if(m_warningsEnabled){
196  UG_LOG("WARNING in ntree::split_leaf_node(): maximum tree depth "
197  << m_desc.maxDepth << " reached. No further splits are performed for "
198  " this node. Note that too many elements per node may lead to performance issues.\n"
199  << " Number of elements in this node: " << m_nodes[nodeIndex].numEntries << std::endl
200  << " Corner coordinates of this node: " << m_nodes[nodeIndex].tightBox << std::endl);
201  }
202  return;
203  }
204 
205  if(m_nodes[nodeIndex].childNodeInd[0] != s_invalidIndex)
206  return;
207 
208  const size_t firstChild = m_nodes.size();
209  m_nodes.resize(firstChild + s_numChildren);
210 
211 // ATTENTION: Be careful not to resize m_nodes while using node, since this would invalidate the reference!
212  Node& node = m_nodes[nodeIndex];
213 
214 // calculate center of mass and use the traits class to split the box of
215 // the current node into 's_numChildren' child boxes. Each child box thereby
216 // spanned by one of the corners of the original box and 'centerOfMass'.
217  vector_t centerOfMass = calculate_center_of_mass(node);
218  box_t childBoxes[s_numChildren];
219  traits::split_box(childBoxes, node.tightBox, centerOfMass);
220 
221 // iterate over all entries in the current node and assign them to child nodes.
222  size_t numEntriesAssigned = 0;
223  for(size_t entryInd = node.firstEntryInd; entryInd != s_invalidIndex;){
224  Entry& entry = m_entries[entryInd];
225  size_t nextEntryInd = entry.nextEntryInd;
226 
227  size_t i_child;
228  vector_t center;
229  traits::calculate_center(center, entry.elem, m_commonData);
230  for(i_child = 0; i_child < s_numChildren; ++i_child){
231  if(traits::box_contains_point(childBoxes[i_child], center)){
232  add_entry_to_node(m_nodes[firstChild + i_child], entryInd);
233  ++numEntriesAssigned;
234  break;
235  }
236  }
237  /*-- For debugging only: --*
238  if(i_child == s_numChildren){
239  UG_LOG ("ERROR in ntree::split_leaf_node(): Element with center @ " << center
240  << " does not belong to any child of the box " << node.tightBox << std::endl);
241  }
242  *--*/
243 
244  entryInd = nextEntryInd;
245  }
246 
247 // all elements of the current box now should be assigned to child boxes.
248 // we thus clear element lists and entry-count from the current node.
249  UG_COND_THROW(numEntriesAssigned != node.numEntries, "Couldn't find a matching "
250  "child node for some elements during split_leaf_node in "
251  "ntree::split_leaf_node");
252 
253  node.firstEntryInd = node.lastEntryInd = s_invalidIndex;
254  node.numEntries = 0;
255 
256  for(size_t i_child = 0; i_child < s_numChildren; ++i_child){
257  node.childNodeInd[i_child] = firstChild + i_child;
258  Node& childNode = m_nodes[firstChild + i_child];
259  childNode.level = node.level + 1;
260  childNode.tightBox = childBoxes[i_child];
261  update_loose_bounding_box(childNode);
262  }
263 
264 // since split_leaf_node resizes m_nodes and since this invalidates any references
265 // to m_nodes, we perform the recursion in a last step
266  for(size_t i_child = 0; i_child < s_numChildren; ++i_child){
267  size_t childNodeInd = firstChild + i_child;
268  if(m_nodes[childNodeInd].numEntries >= m_desc.splitThreshold)
269  split_leaf_node(childNodeInd);
270  }
271 }
272 
273 
274 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
276 find_leaf_node(const vector_t& point, size_t curNode)
277 {
278  Node& n = m_nodes[curNode];
279  if(traits::box_contains_point(n.tightBox, point)){
280  if(n.childNodeInd[0] != s_invalidIndex){
281  for(size_t i = 0; i < s_numChildren; ++i){
282  size_t result = find_leaf_node(point, n.childNodeInd[i]);
283  if(result != s_invalidIndex)
284  return result;
285  }
286  }
287  else{
288  return curNode;
289  }
290  }
291  return s_invalidIndex;
292 }
293 
294 
295 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
297 add_entry_to_node(Node& node, size_t entryInd)
298 {
299  if(node.firstEntryInd == s_invalidIndex){
300  assert(node.numEntries == 0);
301  node.firstEntryInd = node.lastEntryInd = entryInd;
302  }
303  else{
304  m_entries[node.lastEntryInd].nextEntryInd = entryInd;
305  node.lastEntryInd = entryInd;
306  }
307 
308  m_entries[entryInd].nextEntryInd = s_invalidIndex;
309  ++node.numEntries;
310 }
311 
312 
313 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
316 {
317  size_t entryInd = node.firstEntryInd;
318  if(entryInd == s_invalidIndex){
319  node.looseBox = node.tightBox;
320  return;
321  }
322 
323  Entry& entry = m_entries[entryInd];
324  traits::calculate_bounding_box(node.looseBox, entry.elem, m_commonData);
325 
326  entryInd = entry.nextEntryInd;
327  while(entryInd != s_invalidIndex){
328  Entry& entry = m_entries[entryInd];
329  box_t nbox;
330  traits::calculate_bounding_box(nbox, entry.elem, m_commonData);
331  traits::merge_boxes(node.looseBox, node.looseBox, nbox);
332  entryInd = entry.nextEntryInd;
333  }
334 
335 //todo: Solve box-growing in a more portable way!
336  typename traits::vector_t offset;
337  offset = SMALL;
338  traits::grow_box(node.looseBox, node.looseBox, offset);
339 }
340 
341 
342 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
346 {
347  vector_t centerOfMass;
348  VecSet(centerOfMass, 0);
349 
350  for(size_t entryInd = node.firstEntryInd; entryInd != s_invalidIndex;){
351  Entry& entry = m_entries[entryInd];
352 
353  vector_t center;
354  traits::calculate_center(center, entry.elem, m_commonData);
355  VecAdd(centerOfMass, centerOfMass, center);
356  entryInd = entry.nextEntryInd;
357  }
358 
359  if(node.numEntries > 0)
360  VecScale(centerOfMass, centerOfMass, (real_t)1 / (real_t)node.numEntries);
361 
362  return centerOfMass;
363 }
364 
365 
366 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
368 num_nodes() const
369 {
370  return m_nodes.size();
371 }
372 
373 
374 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
376 num_child_nodes(size_t nodeId) const
377 {
378  assert(nodeId < m_nodes.size());
379  if(m_nodes[nodeId].childNodeInd[0] == s_invalidIndex)
380  return 0;
381  return s_numChildren;
382 }
383 
384 
385 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
387 child_node_ids(size_t nodeId) const
388 {
389  assert(nodeId < m_nodes.size());
390  return m_nodes[nodeId].childNodeInd;
391 }
392 
393 
394 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
397 elems_begin(size_t nodeId) const
398 {
399  assert(nodeId < m_nodes.size());
400  if(m_entries.empty())
401  return elem_iterator_t(NULL, s_invalidIndex);
402  return elem_iterator_t(&m_entries.front(), m_nodes[nodeId].firstEntryInd);
403 }
404 
405 
406 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
409 elems_end(size_t nodeId) const
410 {
411  return elem_iterator_t(NULL, s_invalidIndex);
412 }
413 
414 
415 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
417 num_elements(size_t nodeId) const
418 {
419  assert(nodeId < m_nodes.size());
420  return m_nodes[nodeId].numEntries;
421 }
422 
423 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
425 level(size_t nodeId) const
426 {
427  assert(nodeId < m_nodes.size());
428  return m_nodes[nodeId].level;
429 }
430 
431 template <int tree_dim, int world_dim, class elem_t, class common_data_t>
434 bounding_box(size_t nodeId) const
435 {
436  assert(nodeId < m_nodes.size());
437  return m_nodes[nodeId].looseBox;
438 }
439 
440 }// end of namespace
441 
442 #endif
this iterator is used by the ntree class to provide access to the elements of a given node
Definition: ntree_iterator.h:43
void add_entry_to_node(Node &node, size_t entryInd)
adds an entry to the given node
Definition: ntree_impl.hpp:297
size_t num_elements(size_t nodeId) const
returns the number of elements that the given node contains
Definition: ntree_impl.hpp:417
size_t num_delayed_elements() const
returns the number of elements which have been added but are not yet accessible in the tree.
Definition: ntree_impl.hpp:116
void add_element(const elem_t &elem)
adds an element to the tree.
Definition: ntree_impl.hpp:124
elem_iterator_t elems_end(size_t nodeId) const
returns an iterator to the end of the element-sequence of a given node
Definition: ntree_impl.hpp:409
void clear()
Definition: ntree_impl.hpp:62
const NTreeDesc & desc() const
returns the balancing-parameters of the tree.
Definition: ntree_impl.hpp:79
void split_leaf_node(size_t nodeIndex)
splits a node into 2^tree_dim child nodes and assigns entries to those children.
Definition: ntree_impl.hpp:188
size_t num_child_nodes(size_t nodeId) const
returns the number of children of a node
Definition: ntree_impl.hpp:376
size_t num_nodes() const
returns the total number of nodes in the tree
Definition: ntree_impl.hpp:368
size_t size() const
returns the number of entries in the tree (delayed entries excluded)
Definition: ntree_impl.hpp:108
void clear_nodes()
clear only the nodes
Definition: ntree_impl.hpp:52
TCommonData common_data_t
Definition: ntree.h:169
TElem elem_t
Definition: ntree.h:165
bool empty() const
returns true if the tree is empty
Definition: ntree_impl.hpp:100
ntree()
Definition: ntree_impl.hpp:43
vector_t calculate_center_of_mass(Node &node)
calculates the center of mass of a given node
Definition: ntree_impl.hpp:345
const box_t & bounding_box(size_t nodeId) const
returns the smallest box which contains all elements of the given node
Definition: ntree_impl.hpp:434
void rebalance()
rebalances the whole tree
Definition: ntree_impl.hpp:161
const common_data_t & common_data() const
returns the common-data stored in the tree
Definition: ntree_impl.hpp:93
size_t level(size_t nodeId) const
returns the number tree-level in which the node is located
Definition: ntree_impl.hpp:425
size_t find_leaf_node(const vector_t &point, size_t curNode=0)
returns an index to the leaf-node which contains the given point
Definition: ntree_impl.hpp:276
void update_loose_bounding_box(Node &node)
updates the loose bounding box of the given node
Definition: ntree_impl.hpp:315
void set_desc(const NTreeDesc &desc)
sets the balancing-parameters of the tree.
Definition: ntree_impl.hpp:72
elem_iterator_t elems_begin(size_t nodeId) const
returns an iterator to the first element of a given node
Definition: ntree_impl.hpp:397
void set_common_data(const common_data_t &commonData)
sets the common-data which the tree passes on to callback methods
Definition: ntree_impl.hpp:86
const size_t * child_node_ids(size_t nodeId) const
returns an array of child-id's for the given node
Definition: ntree_impl.hpp:387
traits::real_t real_t
Definition: ntree.h:171
std::vector< Node > m_nodes
m_nodes[0] is always considered to be the root node.
Definition: ntree.h:334
#define UG_LOG(msg)
Definition: log.h:367
#define UG_COND_THROW(cond, msg)
UG_COND_THROW(cond, msg) : performs a UG_THROW(msg) if cond == true.
Definition: error.h:61
void VecSet(vector_t &vInOut, typename vector_t::value_type s)
Set each vector component to scalar (componentwise)
Definition: math_vector_functions_common_impl.hpp:539
void VecAdd(vector_t &vOut, const vector_t &v1, const vector_t &v2)
adds two MathVector<N>s and stores the result in a third one
Definition: math_vector_functions_common_impl.hpp:185
void VecScale(vector_t &vOut, const vector_t &v, typename vector_t::value_type s)
scales a MathVector<N>
Definition: math_vector_functions_common_impl.hpp:252
the ug namespace
const number SMALL
Definition: math_constants.h:41
Definition: ntree.h:100
An Entry combines an element with the index to the next entry in a leaf-node's entry list.
Definition: ntree.h:273
elem_t elem
Definition: ntree.h:274
size_t nextEntryInd
Definition: ntree.h:277
Definition: ntree.h:286
size_t numEntries
number of entries in the node
Definition: ntree.h:290
size_t lastEntryInd
index into m_entries. s_invalidIndex: no entry
Definition: ntree.h:289
box_t tightBox
tight bounding box - disjunct partition of the root box
Definition: ntree.h:292
box_t looseBox
loose bounding box - contains all bounding boxes of its entries
Definition: ntree.h:293
size_t firstEntryInd
< index into m_nodes. s_invalidIndex: no child node.
Definition: ntree.h:288
size_t childNodeInd[s_numChildren]
Definition: ntree.h:287