Bembel
 
Loading...
Searching...
No Matches
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
15namespace Bembel {
33 public:
35
41 ElementTree(const ElementTree &) = delete;
42
50
59 ElementTree &operator=(const ElementTree &) = delete;
60
73
88 explicit ElementTree(const Geometry &g, unsigned int max_level = 0) {
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) {
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
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_;
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
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.
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.
ElementTreeNode & root()
Return reference to the root ElementTreeNode.
void refineUniformly()
Refine all patches uniformly.
ElementTree(const Geometry &g, unsigned int max_level=0)
Explicit constructor for the ElementTree class.
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.
ElementTree & operator=(const ElementTree &)=delete
Deleted copy assignment operator for the ElementTree class.
int get_number_of_points() const
getter
const ElementTreeNode & root() const
Return const reference to the root ElementTreeNode.
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.
const PatchVector & get_geometry() const
Return const reference to the Geometry.
ElementTree & operator=(ElementTree &&)=delete
Deleted move assignment operator for the ElementTree class.
std::vector< int > computeReorderingVector() const
The ordering of elements in the element tree does not correspond to the element order underlying the ...
std::vector< std::array< int, 4 > > patchTopologyInfo() const
Resolves neighborhood relations of the patches.
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.
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
void refineUniformly_recursion(ElementTreeNode &el)
Refine the given element or it's sons.
int get_max_level() const
Return maximum level of refinement.
The ElementTreeNode corresponds to an element in the element tree.
std::vector< ElementTreeNode > sons_
member variables
this class wraps a GeometryVector and provides some basic functionality, like reading Geometry files
Definition Geometry.hpp:20
std::vector< Bembel::Patch > PatchVector
typedef for PatchVector
Routines for the evalutation of pointwise errors.
constexpr int getFunctionSpaceOutputDimension()
iterator struct for element tree nodes. They may be used to iterator over the elements in a cluster....