Bembel
ElementTree.hpp
1 // This file is part of Bembel, the higher order C++ boundary element library.
2 //
3 // Copyright (C) 2022 see <http://www.bembel.eu>
4 //
5 // It was written as part of a cooperation of J. Doelz, H. Harbrecht, S. Kurz,
6 // M. Multerer, S. Schoeps, and F. Wolf at Technische Universitaet Darmstadt,
7 // Universitaet Basel, and Universita della Svizzera italiana, Lugano. This
8 // source code is subject to the GNU General Public License version 3 and
9 // provided WITHOUT ANY WARRANTY, see <http://www.bembel.eu> for further
10 // information.
11 
12 #ifndef BEMBEL_SRC_CLUSTERTREE_ELEMENTTREE_HPP_
13 #define BEMBEL_SRC_CLUSTERTREE_ELEMENTTREE_HPP_
14 
15 namespace Bembel {
32 class ElementTree {
33  public:
35 
41  ElementTree(const ElementTree &) = delete;
42 
49  ElementTree(ElementTree &&) = delete;
50 
59  ElementTree &operator=(const ElementTree &) = delete;
60 
73 
88  explicit ElementTree(const Geometry &g, unsigned int max_level = 0) {
89  init_ElementTree(g, max_level);
90  }
94 
105  void init_ElementTree(const Geometry &g, unsigned int max_level) {
106  // initialise the data fields
107  geometry_ = g.get_geometry_ptr();
108  max_level_ = max_level;
109  number_of_patches_ = geometry_->size();
110  // create the patches and set up the topology
111  {
112  std::vector<Eigen::Vector3d> uniquePts;
113  std::vector<ElementTreeNode *> patches;
114  Eigen::Vector3d v;
115  number_of_points_ = 0;
116  number_of_elements_ = number_of_patches_;
117  ElementTreeNode &root = root_;
118  root.sons_.resize(number_of_patches_);
119  for (auto i = 0; i < number_of_patches_; ++i) {
120  root.sons_[i].adjcents_ = std::vector<ElementTreeNode *>(4, nullptr);
121  root.sons_[i].vertices_.resize(4);
122  root.sons_[i].id_ = i;
123  root.sons_[i].level_ = 0;
124  root.sons_[i].patch_ = i;
125  // add linked list structure to the panels
126  if (i == 0) {
127  root.sons_[0].prev_ = nullptr;
128  pfirst_ = std::addressof(root.sons_[0]);
129  } else {
130  root.sons_[i].prev_ = std::addressof(root.sons_[i - 1]);
131  }
132  if (i == number_of_patches_ - 1) {
133  root.sons_[i].next_ = nullptr;
134  plast_ = std::addressof(root.sons_[i]);
135  } else {
136  root.sons_[i].next_ = std::addressof(root.sons_[i + 1]);
137  }
138  // get images of the four corners of the unit square under the
139  // diffeomorphism geo[i]
140  for (auto j = 0; j < 4; ++j) {
141  v = (*geometry_)[i].eval(Constants::corners[0][j],
142  Constants::corners[1][j]);
143  unsigned int index = 0;
144  for (; index < uniquePts.size(); ++index)
145  if ((uniquePts[index] - v).norm() < Constants::pt_comp_tolerance)
146  break;
147  if (index != uniquePts.size()) {
148  root.sons_[i].vertices_[j] = index;
149  } else {
150  uniquePts.push_back(v);
151  root.sons_[i].vertices_[j] = number_of_points_;
152  ++number_of_points_;
153  }
154  }
155  patches.push_back(std::addressof(root.sons_[i]));
156  }
157  updateTopology(patches);
158  }
159  for (auto i = 0; i < max_level; ++i) refineUniformly();
160  return;
161  }
163 
170  if (el.sons_.size()) {
171  for (auto i = 0; i < el.sons_.size(); ++i)
173  } else {
174  refineLeaf(el);
175  }
176  return;
177  }
179 
184  return;
185  }
187 
190  void refinePatch(int patch) {
191  refineUniformly_recursion(root_.sons_[patch]);
192  return;
193  }
195 
206  Eigen::MatrixXd generatePointList(Eigen::VectorXi *idct = nullptr) const {
207  Eigen::MatrixXd pts(3, number_of_points_);
208  if (idct != nullptr) {
209  idct->resize(number_of_points_);
210  idct->setZero();
211  }
212  for (auto it = pbegin(); it != pend(); ++it) {
213  double h = double(1) / double(1 << it->level_);
214  pts.col(it->vertices_[0]) =
215  (*geometry_)[it->patch_].eval(it->llc_(0), it->llc_(1));
216  pts.col(it->vertices_[1]) =
217  (*geometry_)[it->patch_].eval(it->llc_(0) + h, it->llc_(1));
218  pts.col(it->vertices_[2]) =
219  (*geometry_)[it->patch_].eval(it->llc_(0) + h, it->llc_(1) + h);
220  pts.col(it->vertices_[3]) =
221  (*geometry_)[it->patch_].eval(it->llc_(0), it->llc_(1) + h);
222  if (idct != nullptr) {
223  ++((*idct)(it->vertices_[0]));
224  ++((*idct)(it->vertices_[1]));
225  ++((*idct)(it->vertices_[2]));
226  ++((*idct)(it->vertices_[3]));
227  }
228  }
229  return pts;
230  }
232 
240  Eigen::MatrixXi generateElementList() const {
241  unsigned int elID = 0;
242  Eigen::MatrixXi retval(4, number_of_elements_);
243  for (auto it = pbegin(); it != pend(); ++it) {
244  retval.col(elID) << it->vertices_[0], it->vertices_[1], it->vertices_[2],
245  it->vertices_[3];
246  ++elID;
247  }
248  return retval;
249  }
253 
258  int get_number_of_points() const { return number_of_points_; }
264  int get_number_of_elements() const { return number_of_elements_; }
270  int get_max_level() const { return max_level_; }
276  ElementTreeNode &root() { return root_; }
282  const ElementTreeNode &root() const { return root_; }
288  const PatchVector &get_geometry() const { return *geometry_; }
292 
299  return ElementTreeNode::const_iterator(pfirst_);
300  }
308  return ElementTreeNode::const_iterator(plast_->next_);
309  }
332  const ElementTreeNode &cl) const {
333  return cl.cbegin();
334  }
343  return cl.cend();
344  }
346 
354  Eigen::MatrixXd computeElementEnclosings() {
355  // compute point list
356  Eigen::MatrixXd P = generatePointList();
357  for (auto i = 0; i < root_.sons_.size(); ++i)
359  return P;
360  }
362 
369  const Eigen::MatrixXd &P) {
370  Eigen::Vector3d mp1, mp2;
371  double r1, r2;
372  // compute point list
373  if (!el.sons_.size()) {
374  // assign enclosing balls to leafs
375  util::computeEnclosingBall(&mp1, &r1, P.col(el.vertices_[0]), 0,
376  P.col(el.vertices_[2]), 0);
377  util::computeEnclosingBall(&mp2, &r2, P.col(el.vertices_[1]), 0,
378  P.col(el.vertices_[3]), 0);
379  util::computeEnclosingBall(&(el.midpoint_), &(el.radius_), mp1, r1, mp2,
380  r2);
381  } else {
382  // handle the four(!!!) children
383  for (auto i = 0; i < 4; ++i)
385  // assign enclosing balls to fathers bottom up
386  util::computeEnclosingBall(&mp1, &r1, el.sons_[0].midpoint_,
387  el.sons_[0].radius_, el.sons_[2].midpoint_,
388  el.sons_[2].radius_);
389  util::computeEnclosingBall(&mp2, &r2, el.sons_[1].midpoint_,
390  el.sons_[1].radius_, el.sons_[3].midpoint_,
391  el.sons_[3].radius_);
392  util::computeEnclosingBall(&(el.midpoint_), &(el.radius_), mp1, r1, mp2,
393  r2);
394  }
395  return;
396  }
398 
401  void printPanels() const {
402  auto i = 0;
403  for (auto it = pbegin(); it != pend(); ++it) {
404  std::cout << "element[" << i++ << "] = ";
405  it->print();
406  }
407  return;
408  }
412 
417  Eigen::MatrixXd generateMidpointList() const {
418  Eigen::MatrixXd retval(3, number_of_elements_);
419  unsigned int i = 0;
420  for (auto it = pbegin(); it != pend(); ++it) {
421  retval.col(i) = it->midpoint_;
422  ++i;
423  }
424  return retval;
425  }
427 
432  Eigen::MatrixXd generateRadiusList() const {
433  Eigen::VectorXd retval(number_of_elements_);
434  unsigned int i = 0;
435  for (auto it = pbegin(); it != pend(); ++it) {
436  retval(i) = it->radius_;
437  ++i;
438  }
439  return retval;
440  }
442 
447  Eigen::VectorXi generateElementLabels() const {
448  Eigen::VectorXi retval(number_of_elements_);
449  unsigned int i = 0;
450  for (auto it = pbegin(); it != pend(); ++it) {
451  retval(i) = compute_global_id(*it);
452  ++i;
453  }
454  return retval;
455  }
457 
467  Eigen::VectorXi generatePatchBoundaryLabels() const {
468  Eigen::VectorXi retval(number_of_elements_);
469  retval.setZero();
470  unsigned int i = 0;
471  for (auto it = pbegin(); it != pend(); ++it) {
472  for (auto j = 0; j < 4; ++j)
473  if (it->adjcents_[j] == nullptr) {
474  retval[i] = -1;
475  break;
476  } else if (it->adjcents_[j]->patch_ != it->patch_) {
477  ++(retval[i]);
478  }
479  ++i;
480  }
481  return retval;
482  }
484 
492  Eigen::VectorXi identifyPatch(unsigned int pn) const {
493  Eigen::VectorXi retval(number_of_elements_);
494  retval.setZero();
495  assert(pn < number_of_patches_);
496  unsigned int i = 0;
497  for (auto it = pbegin(); it != pend(); ++it) {
498  retval[i] = it->patch_ == pn ? 1 : 0;
499  ++i;
500  }
501  return retval;
502  }
510  std::vector<std::array<int, 4>> patchTopologyInfo() const {
511  std::vector<std::array<int, 4>> retval;
512  for (auto it = root_.sons_.begin(); it != root_.sons_.end(); ++it) {
513  for (auto j = 0; j < 4; ++j) {
514  // do we have a neighbour?
515  if (it->adjcents_[j] != nullptr) {
516  const ElementTreeNode &cur_neighbour = *(it->adjcents_[j]);
517  // add the edge only if it->id_ < neighbour->id_
518  // otherwise the edge has already been added
519  if (it->id_ < cur_neighbour.id_) {
520  int k = 0;
521  for (; k < 4; ++k)
522  if (cur_neighbour.adjcents_[k] != nullptr &&
523  cur_neighbour.adjcents_[k]->id_ == it->id_)
524  break;
525  retval.push_back({it->id_, cur_neighbour.id_, j, k});
526  }
527  } else {
528  retval.push_back({it->id_, -1, j, -1});
529  }
530  }
531  }
532  return retval;
533  }
544  std::vector<int> computeReorderingVector() const {
545  std::vector<int> out(number_of_elements_);
546  for (auto it = pbegin(); it != pend(); ++it) {
547  const double h = it->get_h();
548  const Eigen::Vector2d ref_mid =
549  it->llc_ + Eigen::Vector2d(.5 * h, .5 * h);
550  const int x_idx = std::floor(ref_mid(0) / h);
551  const int y_idx = std::floor(ref_mid(1) / h);
552  const int num_in_one_dir = 1 << max_level_;
553  const int num_per_patch = num_in_one_dir * num_in_one_dir;
554  const int tp_idx =
555  it->patch_ * num_per_patch + y_idx * num_in_one_dir + x_idx;
556  out[tp_idx] = it->id_;
557  }
558  return out;
559  }
561  size_t compute_global_id(const ElementTreeNode &el) const {
562  return number_of_patches_ *
563  (((1 << el.level_) * (1 << el.level_) - 1) / 3) +
564  el.id_;
565  }
569  private:
573  template <typename T>
574  struct isEqual {
575  bool operator()(const T &v1, const T &v2) const {
576  return ((v1 - v2).norm() < Constants::pt_comp_tolerance);
577  }
578  };
584  void updateTopology(const std::vector<ElementTreeNode *> &elements) {
585  std::map<std::array<int, 2>, ElementTreeNode *> edges;
586  std::array<int, 2> e1, e2;
587  // generate edge list for all elements in question
588  for (auto i = 0; i < elements.size(); ++i)
589  for (auto j = 0; j < 4; ++j) {
590  // compute a unique id for each edge
591  const int v1 = elements[i]->vertices_[j];
592  const int v2 = elements[i]->vertices_[(j + 1) % 4];
593  e1 = {v1 < v2 ? v1 : v2, v1 < v2 ? v2 : v1};
594  // perferm a look up if the edge is already existing.
595  auto it = edges.find(e1);
596  // if so, identify the two neighbours
597  if (it != edges.end()) {
598  elements[i]->adjcents_[j] = it->second;
599  // now find the edge also in the patch that added it to the set
600  for (auto k = 0; k < 4; ++k) {
601  const int v3 = it->second->vertices_[k];
602  const int v4 = it->second->vertices_[(k + 1) % 4];
603  e2 = {v3 < v4 ? v3 : v4, v3 < v4 ? v4 : v3};
604  if (e1 == e2) {
605  it->second->adjcents_[k] = elements[i];
606  break;
607  }
608  }
609  // otherwise add the edge to the list
610  } else {
611  edges.insert(std::make_pair(e1, elements[i]));
612  }
613  }
614  return;
615  }
617 
624  void refineLeaf(ElementTreeNode &cur_el) {
625  // check if we have actually a panel
626  if (cur_el.sons_.size()) return;
627  std::vector<int> ptIds(5);
628  std::vector<int> refNeighbours(4);
629  std::vector<ElementTreeNode *> elements;
630  // determine position of p with respect to its neighbours
631  for (auto i = 0; i < 4; ++i) {
632  // is there a neighbour?
633  if (cur_el.adjcents_[i] != nullptr) {
634  for (auto j = 0; j < 4; ++j)
635  if (cur_el.adjcents_[i]->adjcents_[j] == std::addressof(cur_el)) {
636  refNeighbours[i] = j;
637  break;
638  }
639  } else {
640  refNeighbours[i] = -1;
641  }
642  }
643  // determine new points
644  for (auto i = 0; i < 4; ++i) {
645  // is there a neighbour?
646  if (cur_el.adjcents_[i] != nullptr) {
647  ElementTreeNode &ref_cur_neighbour = *(cur_el.adjcents_[i]);
648  // is the neighbour already refined?
649  if (ref_cur_neighbour.sons_.size()) {
650  // this is the midpoint of the shared edge
651  ptIds[i] = ref_cur_neighbour.sons_[refNeighbours[i]]
652  .vertices_[(refNeighbours[i] + 1) % 4];
653  // these are the two elements adjacent to the edge
654  elements.push_back(
655  std::addressof(ref_cur_neighbour.sons_[refNeighbours[i]]));
656  elements.push_back(std::addressof(
657  ref_cur_neighbour.sons_[(refNeighbours[i] + 1) % 4]));
658  } else {
659  // otherwise add the point id to the tree
660  ptIds[i] = number_of_points_;
661  ++number_of_points_;
662  }
663  } else {
664  // otherwise add the point id to the tree
665  ptIds[i] = number_of_points_;
666  ++number_of_points_;
667  }
668  }
669  // add midpoint of the current element, which is always a new point
670  ptIds[4] = number_of_points_;
671  ++number_of_points_;
672  // set up new elements
673  cur_el.sons_.resize(4);
674  number_of_elements_ += 3;
675  max_level_ =
676  max_level_ < cur_el.level_ + 1 ? cur_el.level_ + 1 : max_level_;
677  // auto it = leafs_.find(compute_global_id(cur_el));
678  // if (it != leafs_.end()) leafs_.erase(it);
679  for (auto i = 0; i < 4; ++i) {
680  // add linked list structure to the panels
682  if (i == 0) {
683  cur_el.sons_[i].prev_ = cur_el.prev_;
684  if (cur_el.prev_ != nullptr)
685  (cur_el.prev_)->next_ = std::addressof(cur_el.sons_[i]);
686  cur_el.prev_ = nullptr;
687  if (std::addressof(cur_el) == pfirst_)
688  pfirst_ = std::addressof(cur_el.sons_[i]);
689 
690  } else {
691  cur_el.sons_[i].prev_ = std::addressof(cur_el.sons_[i - 1]);
692  }
693  if (i == 3) {
694  cur_el.sons_[i].next_ = cur_el.next_;
695  if (cur_el.next_ != nullptr)
696  (cur_el.next_)->prev_ = std::addressof(cur_el.sons_[i]);
697  cur_el.next_ = nullptr;
698  if (std::addressof(cur_el) == plast_)
699  plast_ = std::addressof(cur_el.sons_[i]);
700  } else {
701  cur_el.sons_[i].next_ = std::addressof(cur_el.sons_[i + 1]);
702  }
704  cur_el.sons_[i].patch_ = cur_el.patch_;
705  cur_el.sons_[i].level_ = cur_el.level_ + 1;
706  cur_el.sons_[i].id_ = 4 * cur_el.id_ + i;
707  cur_el.sons_[i].adjcents_ = std::vector<ElementTreeNode *>(4, nullptr);
708  cur_el.sons_[i].llc_(0) =
709  cur_el.llc_(0) + Constants::llcs[0][i] / double(1 << cur_el.level_);
710  cur_el.sons_[i].llc_(1) =
711  cur_el.llc_(1) + Constants::llcs[1][i] / double(1 << cur_el.level_);
712  elements.push_back(std::addressof(cur_el.sons_[i]));
713  }
714  // set vertices
715  // first element
716  cur_el.sons_[0].vertices_.push_back(cur_el.vertices_[0]);
717  cur_el.sons_[0].vertices_.push_back(ptIds[0]);
718  cur_el.sons_[0].vertices_.push_back(ptIds[4]);
719  cur_el.sons_[0].vertices_.push_back(ptIds[3]);
720  // second element
721  cur_el.sons_[1].vertices_.push_back(ptIds[0]);
722  cur_el.sons_[1].vertices_.push_back(cur_el.vertices_[1]);
723  cur_el.sons_[1].vertices_.push_back(ptIds[1]);
724  cur_el.sons_[1].vertices_.push_back(ptIds[4]);
725  // third element
726  cur_el.sons_[2].vertices_.push_back(ptIds[4]);
727  cur_el.sons_[2].vertices_.push_back(ptIds[1]);
728  cur_el.sons_[2].vertices_.push_back(cur_el.vertices_[2]);
729  cur_el.sons_[2].vertices_.push_back(ptIds[2]);
730  // fourth element
731  cur_el.sons_[3].vertices_.push_back(ptIds[3]);
732  cur_el.sons_[3].vertices_.push_back(ptIds[4]);
733  cur_el.sons_[3].vertices_.push_back(ptIds[2]);
734  cur_el.sons_[3].vertices_.push_back(cur_el.vertices_[3]);
735  // fix adjecency relations
736  updateTopology(elements);
737 
738  return;
739  }
743  std::shared_ptr<PatchVector> geometry_;
744  ElementTreeNode root_;
745  ElementTreeNode *pfirst_;
746  ElementTreeNode *plast_;
747  int number_of_patches_;
748  int max_level_;
749  int number_of_points_;
750  int number_of_elements_;
752 };
753 } // namespace Bembel
754 #endif // BEMBEL_SRC_CLUSTERTREE_ELEMENTTREE_HPP_
This class organizes an element structure on a Geometry object and handles refinement.
Definition: ElementTree.hpp:32
ElementTreeNode::const_iterator cpbegin() const
Returns an iterator to the beginning of the sequence represented by the leafs as ElementTreeNodes of ...
Eigen::MatrixXd computeElementEnclosings()
Computes enclosing balls surrounding all elements.
std::vector< std::array< int, 4 > > patchTopologyInfo() const
Resolves neighborhood relations of the patches.
ElementTree & operator=(ElementTree &&)=delete
Deleted move assignment operator for the ElementTree class.
void refineUniformly()
Refine all patches uniformly.
const ElementTreeNode & root() const
Return const reference to the root ElementTreeNode.
ElementTree(const Geometry &g, unsigned int max_level=0)
Explicit constructor for the ElementTree class.
Definition: ElementTree.hpp:88
ElementTreeNode::const_iterator cpend() const
Returns an iterator one past the end of the sequence represented by the leafs as ElementTreeNodes of ...
ElementTreeNode::const_iterator pbegin() const
iterators
ElementTreeNode::const_iterator cluster_begin(const ElementTreeNode &cl) const
Returns a cluster iterator to the beginning of the sequence represented by the the given ElementTreeN...
ElementTree(const ElementTree &)=delete
Deleted copy constructor for the ElementTree class.
int get_number_of_points() const
getter
Eigen::VectorXi generatePatchBoundaryLabels() const
Generate list of labels if elements are on the patch boundary or at the boundary of the geometry.
ElementTreeNode::const_iterator pend() const
Returns an iterator one past the end of the sequence represented by the leafs as ElementTreeNodes of ...
Eigen::MatrixXd generateRadiusList() const
Return a matrix with all radii of the element enclosing.
Eigen::VectorXi identifyPatch(unsigned int pn) const
Generate list of labels if elements contained within a given patch.
void refinePatch(int patch)
Refine a given patch.
void printPanels() const
Prints all Elements of the Tree.
void init_ElementTree(const Geometry &g, unsigned int max_level)
init
void computeElementEnclosings_recursion(ElementTreeNode &el, const Eigen::MatrixXd &P)
Computes enclosing balls of one element and all its sons.
int get_number_of_elements() const
Return number of elements in the ElementTree.
Eigen::MatrixXd generatePointList(Eigen::VectorXi *idct=nullptr) const
Return the coordinates of all points of the elements.
Eigen::MatrixXd generateMidpointList() const
other Stuff
ElementTree(ElementTree &&)=delete
Deleted move constructor for the ElementTree class.
Eigen::VectorXi generateElementLabels() const
Return a vector with computed global indices of the elements.
std::vector< int > computeReorderingVector() const
The ordering of elements in the element tree does not correspond to the element order underlying the ...
Eigen::MatrixXi generateElementList() const
Return list of elements containing the indices of the vertices.
ElementTreeNode::const_iterator cluster_end(const ElementTreeNode &cl) const
Returns a cluster iterator one past the end of the sequence represented by the the given ElementTreeN...
ElementTree()
constructors
Definition: ElementTree.hpp:76
void refineUniformly_recursion(ElementTreeNode &el)
Refine the given element or it's sons.
const PatchVector & get_geometry() const
Return const reference to the Geometry.
int get_max_level() const
Return maximum level of refinement.
ElementTreeNode & root()
Return reference to the root ElementTreeNode.
ElementTree & operator=(const ElementTree &)=delete
Deleted copy assignment operator for the ElementTree class.
The ElementTreeNode corresponds to an element in the element tree.
int id_
radius of the element
const_iterator cend() const
Returns an iterator pointing to the element in the sequence after this ElementTreeNode.
int level_
element id with respect to the level
std::vector< int > vertices_
neighbouring elements indices
const_iterator cbegin() const
Returns an iterator pointing to the element in the sequence before this ElementTreeNodes.
Eigen::Vector3d midpoint_
indices of the vertices
std::vector< ElementTreeNode * > adjcents_
children
std::vector< ElementTreeNode > sons_
member variables
this class wraps a GeometryVector and provides some basic functionality, like reading Geometry files
Definition: Geometry.hpp:20
const std::shared_ptr< PatchVector > get_geometry_ptr() const
Return const pointer to the geometry.
Definition: Geometry.hpp:123
std::vector< Bembel::Patch > PatchVector
typedef for PatchVector
Definition: PatchVector.hpp:24
Routines for the evalutation of pointwise errors.
Definition: AnsatzSpace.hpp:14
iterator struct for element tree nodes. They may be used to iterator over the elements in a cluster....