LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
Loading...
Searching...
No Matches
mesh_hierarchy.cc
1
6#include "mesh_hierarchy.h"
7
8#include <spdlog/fmt/ostr.h>
9#include <spdlog/spdlog.h>
10
11#include "lf/mesh/hybrid2d/hybrid2d.h"
12#include "lf/mesh/utils/utils.h"
13
14namespace lf::refinement {
15
16// Debugging function: check validity of index vectors
17bool checkValidIndex(const std::vector<glb_idx_t> &idx_vec) {
18 const size_type n_idx = idx_vec.size();
19 for (int n = 0; n < n_idx; n++) {
20 if (idx_vec[n] == idx_nil) {
21 return false;
22 }
23 }
24 return true;
25}
26
27std::shared_ptr<spdlog::logger> &MeshHierarchy::Logger() {
28 static auto logger =
29 base::InitLogger("lf::refinement::MeshHierarchy::Logger");
30 return logger;
31}
32
33// Implementation of MeshHierarchy
34MeshHierarchy::MeshHierarchy(const std::shared_ptr<mesh::Mesh> &base_mesh,
35 std::unique_ptr<mesh::MeshFactory> mesh_factory)
36 : mesh_factory_(std::move(mesh_factory)) {
37 LF_VERIFY_MSG(base_mesh, "No valid mesh supplied");
38 LF_VERIFY_MSG(base_mesh->DimMesh() == 2, "Implemented only for 2D meshes");
39 // Set coarsest mesh
40 meshes_.push_back(base_mesh);
41 {
42 // Refinement edge has to be set for edges
43 refinement_edges_.emplace_back(base_mesh->NumEntities(0), idx_nil);
44 for (const mesh::Entity *cell : base_mesh->Entities(0)) {
45 const lf::base::glb_idx_t cell_index = base_mesh->Index(*cell);
46 if (cell->RefEl() == lf::base::RefEl::kTria()) {
47 (refinement_edges_.back())[cell_index] = LongestEdge(*cell);
48 }
49 } // end loop over cells
50
51 // Setting up child information
52 std::vector<CellChildInfo> cell_child_info(base_mesh->NumEntities(0));
53 std::vector<EdgeChildInfo> edge_child_info(base_mesh->NumEntities(1));
54 std::vector<PointChildInfo> point_child_info(base_mesh->NumEntities(2));
55 cell_child_infos_.push_back(std::move(cell_child_info));
56 edge_child_infos_.push_back(std::move(edge_child_info));
57 point_child_infos_.push_back(std::move(point_child_info));
58 }
59 {
60 // No parents for entities on the coarsest level
61 std::vector<ParentInfo> cell_parent_info(base_mesh->NumEntities(0));
62 std::vector<ParentInfo> edge_parent_info(base_mesh->NumEntities(1));
63 std::vector<ParentInfo> point_parent_info(base_mesh->NumEntities(2));
64 parent_infos_.push_back({std::move(cell_parent_info),
65 std::move(edge_parent_info),
66 std::move(point_parent_info)});
67 }
68 edge_marked_.push_back(
69 std::move(std::vector<bool>(base_mesh->NumEntities(1), false)));
70}
71
73 LF_VERIFY_MSG(
74 ref_pat == RefPat::rp_regular || ref_pat == RefPat::rp_barycentric,
75 "Only regular or barycentric uniform refinement possible");
76 // Retrieve the finest mesh in the hierarchy
77 const mesh::Mesh &finest_mesh(*meshes_.back());
78 // Arrays containing refinement information for finest mesh
79 std::vector<PointChildInfo> &finest_point_ci(point_child_infos_.back());
80 std::vector<EdgeChildInfo> &finest_edge_ci(edge_child_infos_.back());
81 std::vector<CellChildInfo> &finest_cell_ci(cell_child_infos_.back());
82
83 LF_VERIFY_MSG(finest_point_ci.size() == finest_mesh.NumEntities(2),
84 "length mismatch PointChildInfo vector");
85 LF_VERIFY_MSG(finest_edge_ci.size() == finest_mesh.NumEntities(1),
86 "length mismatch EdgeChildInfo vector");
87 LF_VERIFY_MSG(finest_cell_ci.size() == finest_mesh.NumEntities(0),
88 "length mismatch CellChildInfo vector");
89
90 // Flag all points as to be copied
91 for (const mesh::Entity *point : finest_mesh.Entities(2)) {
92 const lf::base::glb_idx_t point_index = finest_mesh.Index(*point);
93 PointChildInfo &pt_child_info(finest_point_ci[point_index]);
94 // Set the information about a point's children except the child pointer
95 pt_child_info.ref_pat = RefPat::rp_copy;
96 }
97 // Flag all edges as to be split
98 for (const mesh::Entity *edge : finest_mesh.Entities(1)) {
99 const lf::base::glb_idx_t edge_index = finest_mesh.Index(*edge);
100 EdgeChildInfo &ed_child_info(finest_edge_ci[edge_index]);
101 ed_child_info.ref_pat_ = RefPat::rp_split;
102 }
103 // All cells are to be refined regularly
104 for (const mesh::Entity *cell : finest_mesh.Entities(0)) {
105 const lf::base::glb_idx_t cell_index = finest_mesh.Index(*cell);
106 CellChildInfo &cell_child_info(finest_cell_ci[cell_index]);
107 cell_child_info.ref_pat_ = ref_pat;
108 }
109 // With all refinement patterns set, generate the new mesh
111 // Finish initialization
113}
114
115// NOLINTNEXTLINE(readability-function-cognitive-complexity)
117 // Target the finest mesh
118 const lf::mesh::Mesh &finest_mesh(*meshes_.back());
119 // Arrays containing refinement information for finest mesh
120 std::vector<PointChildInfo> &finest_point_ci(point_child_infos_.back());
121 std::vector<EdgeChildInfo> &finest_edge_ci(edge_child_infos_.back());
122 std::vector<CellChildInfo> &finest_cell_ci(cell_child_infos_.back());
123
124 LF_VERIFY_MSG(finest_point_ci.size() == finest_mesh.NumEntities(2),
125 "length mismatch PointChildInfo vector");
126 LF_VERIFY_MSG(finest_edge_ci.size() == finest_mesh.NumEntities(1),
127 "length mismatch EdgeChildInfo vector");
128 LF_VERIFY_MSG(finest_cell_ci.size() == finest_mesh.NumEntities(0),
129 "length mismatch CellChildInfo vector");
130
131 // Flag all points as to be copied
132 for (const mesh::Entity *point : finest_mesh.Entities(2)) {
133 const glb_idx_t point_index = finest_mesh.Index(*point);
134 PointChildInfo &pt_child_info(finest_point_ci[point_index]);
135 // Set the information about a point's children except the child pointer
136 pt_child_info.ref_pat = RefPat::rp_copy;
137 }
138
139 // Set the split refinement patterrn for all marked edges
140 for (const lf::mesh::Entity *edge : finest_mesh.Entities(1)) {
141 LF_VERIFY_MSG(edge->RefEl() == lf::base::RefEl::kSegment(),
142 "Wrong type for an edge");
143 const glb_idx_t edge_index = finest_mesh.Index(*edge);
144 EdgeChildInfo &ed_ci(finest_edge_ci[edge_index]);
145 LF_VERIFY_MSG(ed_ci.ref_pat_ == RefPat::rp_nil,
146 "Edge " << edge_index << " already refined!");
147 if ((edge_marked_.back())[edge_index]) {
148 // Edge is to be refined
150 } else {
151 // Just copy edge
153 }
154 }
155 // Now all edges are initially marked to be split or copied
156
157 // To keep the mesh conforming refinement might have to propagate
158 // This is achieved in the following REPEAT ... UNTIL loop.
159 bool refinement_complete;
160 do {
161 refinement_complete = true;
162 // Visit all cells and update their refinement patterns
163 for (const lf::mesh::Entity *cell : finest_mesh.Entities(0)) {
164 // Obtain unique index of current cell
165 const glb_idx_t cell_index = finest_mesh.Index(*cell);
166 // Detailed description of refinement status
167 const CellChildInfo &cell_child_info(finest_cell_ci[cell_index]);
168
169 // Global indices of edges
170 std::array<glb_idx_t, 4> cell_edge_indices{};
171
172 // Find edges which are marked as split
173 std::array<bool, 4> edge_split{{false, false, false, false}};
174 // Local indices of edges marked as split
175 std::array<sub_idx_t, 4> split_edge_idx{};
176 // Array of references to edge sub-entities of current cell
177 const std::span<const lf::mesh::Entity *const> sub_edges =
178 cell->SubEntities(1);
179 const size_type num_edges = cell->RefEl().NumSubEntities(1);
180 LF_VERIFY_MSG(num_edges <= 4, "Too many edges = " << num_edges);
181 // Obtain information about current splitting pattern of
182 // the edges of the cell. Count edges that will be split.
183 size_type split_edge_cnt = 0;
184 for (int k = 0; k < num_edges; k++) {
185 const glb_idx_t edge_index = finest_mesh.Index(*sub_edges[k]);
186 cell_edge_indices[k] = edge_index;
187 edge_split[k] =
188 (finest_edge_ci[edge_index].ref_pat_ == RefPat::rp_split);
189 if (edge_split[k]) {
190 split_edge_idx[split_edge_cnt] = k;
191 split_edge_cnt++;
192 }
193 }
194 switch (cell->RefEl()) {
195 case lf::base::RefEl::kTria(): {
196 // Case of a triangular cell: In this case bisection refinement
197 // is performed starting with the refinement edge.
198 // Local index of refinement edge for the current triangle, also
199 // called the "anchor edge" in the case of repeated bisection
200 const sub_idx_t anchor = refinement_edges_.back()[cell_index];
201 LF_VERIFY_MSG(anchor < 3, "Illegal anchor = " << anchor);
202
203 // Refinement edge will always be the anchor edge
204 finest_cell_ci[cell_index].anchor_ = anchor;
205 const sub_idx_t mod_0 = anchor;
206 const sub_idx_t mod_1 = (anchor + 1) % 3;
207 const sub_idx_t mod_2 = (anchor + 2) % 3;
208 // Flag tuple indicating splitting status of an edge: true <-> split
209 const std::tuple<bool, bool, bool> split_status(
210 {edge_split[mod_0], edge_split[mod_1], edge_split[mod_2]});
211
212 // Determine updated refinement pattern for triangle depending on the
213 // splitting status of its edges. If the triangle is subdivided, the
214 // refinement must always be split in a first bisection step, even if
215 // it may not have been marked as split. In this case refinement may
216 // spread to neighboring cells and we have to iterative once more.
217 // Therefore the refinement_complete flag has to be set to 'false'.
218
219 if (split_status ==
220 std::tuple<bool, bool, bool>({false, false, false})) {
221 // No edge to be split: just copy triangle
222 LF_VERIFY_MSG(split_edge_cnt == 0, "Wrong number of split edges");
223 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_copy;
224 } else if (split_status ==
225 std::tuple<bool, bool, bool>({true, false, false})) {
226 // Only refinement edge has to be split by a single bisection
227 // No additional edge will be split
228 LF_VERIFY_MSG(split_edge_cnt == 1, "Wrong number of split edges");
229 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_bisect;
230 } else if (split_status ==
231 std::tuple<bool, bool, bool>({true, true, false})) {
232 // Trisection refinement, no extra splitting of edges
233 LF_VERIFY_MSG(split_edge_cnt == 2, "Wrong number of split edges");
234 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_trisect;
235 } else if (split_status ==
236 std::tuple<bool, bool, bool>({false, true, false})) {
237 // Trisection refinement, triggering splitting of refinement edge
238 LF_VERIFY_MSG(split_edge_cnt == 1, "Wrong number of split edges");
239 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_trisect;
240 finest_edge_ci[cell_edge_indices[anchor]].ref_pat_ =
242 refinement_complete = false;
243 } else if (split_status ==
244 std::tuple<bool, bool, bool>({true, false, true})) {
245 // Trisection refinement (other side), no extra splitting of edges
246 LF_VERIFY_MSG(split_edge_cnt == 2, "Wrong number of split edges");
247 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_trisect_left;
248 } else if (split_status ==
249 std::tuple<bool, bool, bool>({false, false, true})) {
250 // Trisection refinement (other side), triggering splitting of
251 // refinement edge
252 LF_VERIFY_MSG(split_edge_cnt == 1, "Wrong number of split edges");
253 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_trisect_left;
254 finest_edge_ci[cell_edge_indices[anchor]].ref_pat_ =
256 refinement_complete = false;
257 } else if (split_status ==
258 std::tuple<bool, bool, bool>({true, true, true})) {
259 // All edges of the triangle are to be split
260 // If the implementation relies on quadsection refinement, the the
261 // child triangles may become more and more distorted! Thus it is
262 // advisable to employ regular refinement.
263 LF_VERIFY_MSG(split_edge_cnt == 3, "Wrong number of split edges");
264 // OLD IMPLEMENTATION
265 // finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_quadsect;
266 // NEW IMPLEMENTATION
267 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_regular;
268 } else if (split_status ==
269 std::tuple<bool, bool, bool>({false, true, true})) {
270 // Quadsection refinement requiring splitting of refinement edge
271 LF_VERIFY_MSG(split_edge_cnt == 2, "Wrong number of split edges");
272 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_quadsect;
273 finest_edge_ci[cell_edge_indices[anchor]].ref_pat_ =
275 refinement_complete = false;
276 } else {
277 LF_VERIFY_MSG(false, "Impossible case");
278 }
279 break;
280 } // end case of a triangle
281 case lf::base::RefEl::kQuad(): {
282 // There is no refinement edge for quadrilaterals and so no extra edge
283 // splitting will be necessary. The refinement pattern for a
284 // quadrilateral will be determined from the number of edges split and
285 // their location to each other.
286 switch (split_edge_cnt) {
287 case 0: {
288 // No edge split: quadrilateral has to be copied
289 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_copy;
290 break;
291 }
292 case 1: {
293 // One edge split: trisection refinement of the quadrilateral
294 // Anchor edge is the split edge
295 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_trisect;
296 finest_cell_ci[cell_index].anchor_ = split_edge_idx[0];
297 break;
298 }
299 case 2: {
300 if ((split_edge_idx[1] - split_edge_idx[0]) == 2) {
301 // If the two split edges are opposite to each other, then
302 // bisection of the quadrilateral is the right refinement
303 // pattern.
304 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_bisect;
305 finest_cell_ci[cell_index].anchor_ = split_edge_idx[0];
306 } else {
307 // Tthe two split edges are adjacent, this case can be
308 // accommodated by quadsection refinement. Anchor is the split
309 // edge with the lower index (modulo 4).
310 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_quadsect;
311 if (((split_edge_idx[0] + 1) % 4) == split_edge_idx[1]) {
312 finest_cell_ci[cell_index].anchor_ = split_edge_idx[0];
313 } else if (((split_edge_idx[1] + 1) % 4) == split_edge_idx[0]) {
314 finest_cell_ci[cell_index].anchor_ = split_edge_idx[1];
315 } else {
316 LF_VERIFY_MSG(false,
317 "Quad: impossible situation for 2 split edges");
318 }
319 }
320 break;
321 }
322 case 3: {
323 // Three edges of the quadrilateral are split, which can be
324 // accommodated only by the rp_threeedge refinement pattern
325 // anchor is the edge with the middle index
326 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_threeedge;
327 if (!edge_split[0]) { // Split edges 1,2,3, middle edge 2
328 finest_cell_ci[cell_index].anchor_ = 2;
329 } else if (!edge_split[1]) { // Split edges 0,2,3, middle edge 3
330 finest_cell_ci[cell_index].anchor_ = 3;
331 } else if (!edge_split[2]) { // Split edges 0,1,3, middle edge 0
332 finest_cell_ci[cell_index].anchor_ = 0;
333 } else if (!edge_split[3]) { // Split edges 0,1,2, middle edge 1
334 finest_cell_ci[cell_index].anchor_ = 1;
335 } else {
336 LF_VERIFY_MSG(false, "Inconsistent split pattern");
337 }
338 break;
339 }
340 case 4: {
341 // All edges are split => regular refinement
342 finest_cell_ci[cell_index].ref_pat_ = RefPat::rp_regular;
343 break;
344 }
345 default: {
346 LF_VERIFY_MSG(false, "Illegal number " << split_edge_cnt
347 << " of split edges");
348 break;
349 }
350 } // end switch split_edge_cnt
351 break;
352 } // end case of a quadrilateral
353 default: {
354 LF_VERIFY_MSG(false, "Illegal cell type");
355 break;
356 }
357 } // end switch cell type
358 } // end loop over cells
359 } while (!refinement_complete);
360
361 // Create finer mesh according to set refinment edges
363 // Finish initialization
365} // end RefineMarked
366
367// NOLINTNEXTLINE
369 SPDLOG_LOGGER_DEBUG(Logger(),
370 "Entering MeshHierarchy::PerformRefinement: {} levels",
371 meshes_.size());
372
373 // This function expects that the refinement patterns stored in the
374 // vectors point_child_infos_, edge_child_infos_ and cell_child_infos_
375 // have been initialized consistently for the finest mesh.
376
377 // This functions will augement these vectors with information about
378 // the indices of child entities on a newly created finest mesh.
379
380 // Retrieve the finest mesh in the hierarchy = parent mesh
381 const mesh::Mesh &parent_mesh(*meshes_.back());
382
383 {
384 // Partly intialized vectors of child information
385 std::vector<PointChildInfo> &pt_child_info(point_child_infos_.back());
386 // ======================================================================
387 // First run through the vertices, create child vertices and register
388 // them with the mesh factory
389 // Store child indices in an auxiliary array
390 size_type new_node_cnt = 0;
393 for (const mesh::Entity *node : parent_mesh.Entities(2)) {
394 // Obtain index of node in coarse mesh
395 const lf::base::glb_idx_t node_index = parent_mesh.Index(*node);
396 LF_VERIFY_MSG(node_index < pt_child_info.size(),
397 "Node index " << node_index << " out of range");
398 // Find position of node in physical coordinates
399 const lf::geometry::Geometry &pt_geo(*node->Geometry());
400 if (pt_child_info[node_index].ref_pat != RefPat::rp_nil) {
401 // Generate a node for the fine mesh at the same position
402 std::vector<std::unique_ptr<geometry::Geometry>> pt_child_geo_ptrs(
403 pt_geo.ChildGeometry(rp_copy_node, 0));
404 LF_VERIFY_MSG(pt_child_geo_ptrs.size() == 1,
405 "A point can only have one chile");
406 // Store index of child node
407 pt_child_info[node_index].child_point_idx =
408 mesh_factory_->AddPoint(std::move(pt_child_geo_ptrs[0]));
409 new_node_cnt++;
410 }
411 } // end loop over nodes
412 SPDLOG_LOGGER_DEBUG(Logger(), "{} new nodes added", new_node_cnt);
413
414 // ======================================================================
415
416 // Partly intialized vectors of child information
417 std::vector<EdgeChildInfo> &ed_child_info(edge_child_infos_.back());
418 // ======================================================================
419 // Now traverse the edges. Depending on the refinement pattern,
420 // either copy them or split them.
421 // Supplement the refinement information for edges accordingly.
422 size_type new_edge_cnt = 0;
423 for (const mesh::Entity *edge : parent_mesh.Entities(1)) {
424 // Fetch global index of edge
425 lf::base::glb_idx_t edge_index = parent_mesh.Index(*edge);
426
427 // Get indices of endpoints in parent mesh
428 auto ed_nodes = edge->SubEntities(1);
429 const lf::base::glb_idx_t ed_p0_coarse_idx =
430 parent_mesh.Index(*ed_nodes[0]);
431 const lf::base::glb_idx_t ed_p1_coarse_idx =
432 parent_mesh.Index(*ed_nodes[1]);
433
434 // Obtain indices of the nodes at the same position in the fine mesh
435 const lf::base::glb_idx_t ed_p0_fine_idx =
436 pt_child_info[ed_p0_coarse_idx].child_point_idx;
437 const lf::base::glb_idx_t ed_p1_fine_idx =
438 pt_child_info[ed_p1_coarse_idx].child_point_idx;
439 // Prepare request of geometry after refinement
440 EdgeChildInfo &edge_ci(ed_child_info[edge_index]);
441 const RefPat edge_refpat(edge_ci.ref_pat_);
442 const Hybrid2DRefinementPattern rp(edge->RefEl(), edge_refpat);
443
444 // Distinguish between different local refinement patterns
445 switch (edge_refpat) {
446 case RefPat::rp_copy: {
447 // Edge has to be duplicated
448 std::vector<std::unique_ptr<lf::geometry::Geometry>> ed_copy(
449 edge->Geometry()->ChildGeometry(rp, 0));
450 LF_VERIFY_MSG(ed_copy.size() == 1,
451 "Copy may create only a single child!");
452 // Register the new edge
453 SPDLOG_LOGGER_TRACE(Logger(), "Copy edge {} new edge [{},{}]",
454 edge_index, ed_p0_fine_idx, ed_p1_fine_idx);
455
456 // Store index of copy of edge on finer mesh
457 edge_ci.child_edge_idx.push_back(
458 mesh_factory_->AddEntity(edge->RefEl(),
459 std::array<lf::base::glb_idx_t, 2>{
460 {ed_p0_fine_idx, ed_p1_fine_idx}},
461 std::move(ed_copy[0])));
462 break;
463 } // end rp_copy
464 case rp_split: {
465 // Edge has to be split into two halves with a new node created at the
466 // midpoint position. First obtain geometry information about new node
467 // (sub-entity with relative co-dimension 1)
468 std::vector<std::unique_ptr<geometry::Geometry>> edge_nodes_geo_ptrs(
469 edge->Geometry()->ChildGeometry(rp, 1));
470 LF_VERIFY_MSG(edge_nodes_geo_ptrs.size() == 1,
471 "Split edge with " << edge_nodes_geo_ptrs.size()
472 << " child nodes!");
473 // Register midpoint as new node
474 const lf::base::glb_idx_t midpoint_fine_idx =
475 mesh_factory_->AddPoint(std::move(edge_nodes_geo_ptrs[0]));
476 edge_ci.child_point_idx.push_back(midpoint_fine_idx);
477 // Next get the geometry objects for the two child edges (co-dim == 0)
478 std::vector<std::unique_ptr<geometry::Geometry>> edge_child_geo_ptrs(
479 edge->Geometry()->ChildGeometry(rp, 0));
480 LF_VERIFY_MSG(
481 edge_child_geo_ptrs.size() == 2,
482 "Split edge with " << edge_child_geo_ptrs.size() << " parts!");
483 // Register the two new edges
484 // CAREFUL: Assignment of endpoints has to match implementation in
485 // refinement.cc
486 // First child edge adjacent to endpoint #0, second child edge
487 // adjacent to endpoint #1
488 SPDLOG_LOGGER_TRACE(Logger(),
489 "Split Edge {} new edges [{},{}], [{},{}]",
490 edge_index, ed_p0_fine_idx, midpoint_fine_idx,
491 midpoint_fine_idx, ed_p1_fine_idx);
492
493 edge_ci.child_edge_idx.push_back(
494 mesh_factory_->AddEntity(edge->RefEl(),
495 std::array<lf::base::glb_idx_t, 2>{
496 {ed_p0_fine_idx, midpoint_fine_idx}},
497 std::move(edge_child_geo_ptrs[0])));
498 edge_ci.child_edge_idx.push_back(
499 mesh_factory_->AddEntity(edge->RefEl(),
500 std::array<lf::base::glb_idx_t, 2>{
501 {midpoint_fine_idx, ed_p1_fine_idx}},
502 std::move(edge_child_geo_ptrs[1])));
503 break;
504 } // end rp_split
505 default: {
506 LF_VERIFY_MSG(false, "Refinement pattern "
507 << static_cast<int>(edge_refpat)
508 << " illegal for edge");
509 break;
510 }
511 } // end switch refpat
512 new_edge_cnt++;
513 } // end edge loop
514
515 SPDLOG_LOGGER_DEBUG(Logger(), "{} edges added", new_edge_cnt);
516 // ======================================================================
517
518 // Partly intialized vectors of child information
519 std::vector<CellChildInfo> &cell_child_info(cell_child_infos_.back());
520 // ======================================================================
521 // Visit all cells, examine their refinement patterns, retrieve indices of
522 // their sub-entities, and those of the children.
523 LF_VERIFY_MSG(cell_child_info.size() == parent_mesh.NumEntities(0),
524 "Size mismatch for CellChildInfos");
525 for (const mesh::Entity *cell : parent_mesh.Entities(0)) {
526 // type of cell
527 const lf::base::RefEl ref_el(cell->RefEl());
528 const lf::base::size_type num_edges = ref_el.NumSubEntities(1);
529 const lf::base::size_type num_vertices = ref_el.NumSubEntities(2);
530 // fetch index of current cell
531 const lf::base::glb_idx_t cell_index(parent_mesh.Index(*cell));
532
533 // Set up refinement object -> variable rp
534 CellChildInfo &cell_ci(cell_child_info[cell_index]);
535 const RefPat cell_refpat(cell_ci.ref_pat_);
536 const sub_idx_t anchor = cell_ci.anchor_; // anchor edge index
537
538 SPDLOG_LOGGER_TRACE(Logger(), "Cell {} = {}, refpat = {}, anchor = {}",
539 cell_index, ref_el, static_cast<int>(cell_refpat),
540 anchor);
541
542 const Hybrid2DRefinementPattern rp(cell->RefEl(), cell_refpat, anchor);
543
544 // Index offsets for refinement patterns requiring an ancchor edge
545 std::array<sub_idx_t, 4> mod{};
546 if (anchor != idx_nil) {
547 for (int k = 0; k < num_vertices; k++) {
548 mod[k] = (k + anchor) % num_vertices;
549 }
550 }
551
552 // Obtain indices of subentities (co-dimension = outer array index)
553 std::array<std::vector<lf::base::glb_idx_t>, 3> cell_subent_idx;
554 cell_subent_idx[0].push_back(cell_index);
555 for (int codim = 1; codim <= 2; codim++) {
556 auto subentities = cell->SubEntities(codim);
557 for (const mesh::Entity *sub_ent : subentities) {
558 cell_subent_idx[codim].push_back(parent_mesh.Index(*sub_ent));
559 }
560 LF_VERIFY_MSG(
561 cell_subent_idx[codim].size() == ref_el.NumSubEntities(codim),
562 ref_el.ToString() << ": only " << cell_subent_idx[codim].size()
563 << " subents of codim = " << codim);
564
565 SPDLOG_LOGGER_TRACE(Logger(), " Subent({}) = [{}], ", codim,
566 cell_subent_idx[codim]);
567 }
568 // Index information for sub-entities with respect to fine mesh
569 // Retrieve indices of vertices of cell on the fine mesh
570 std::array<lf::base::glb_idx_t, 4> vertex_child_idx{
572 for (lf::base::sub_idx_t vt_lidx = 0; vt_lidx < num_vertices; vt_lidx++) {
573 LF_VERIFY_MSG(pt_child_info[cell_subent_idx[2][vt_lidx]].ref_pat ==
575 "Vertex must have been copied!");
576 vertex_child_idx[vt_lidx] =
577 pt_child_info[cell_subent_idx[2][vt_lidx]].child_point_idx;
578 }
579
580 // Retrieve indices of midpoints of edges, if they exist
581 std::array<lf::base::glb_idx_t, 4> edge_midpoint_idx{
583 for (lf::base::sub_idx_t ed_lidx = 0; ed_lidx < num_edges; ed_lidx++) {
584 const EdgeChildInfo &ed_ci(ed_child_info[cell_subent_idx[1][ed_lidx]]);
585 if (!ed_ci.child_point_idx.empty()) {
586 LF_VERIFY_MSG(ed_child_info[cell_subent_idx[1][ed_lidx]].ref_pat_ ==
588 "Edge with a midpoint must have been split");
589 edge_midpoint_idx[ed_lidx] = ed_ci.child_point_idx[0];
590 }
591 } // end loop over local edges
592
593 SPDLOG_LOGGER_TRACE(Logger(), "vt_child_idx = {}, ed_mp_idx = {}",
594 std::span(vertex_child_idx.data(), num_vertices),
595 std::span(edge_midpoint_idx.data(), num_edges));
596
597 // Array of node indices (w.r.t. fine mesh) for sub-cells (triangles or
598 // quadrilaterals)
599 std::vector<std::vector<glb_idx_t>> child_cell_nodes;
600 std::vector<glb_idx_t> tria_ccn_tmp(3);
601 std::vector<glb_idx_t> quad_ccn_tmp(4);
602 // Array of node indices for interior child edges
603 std::vector<std::array<glb_idx_t, 2>> child_edge_nodes;
604 std::array<glb_idx_t, 2> cen_tmp{};
605
606 // Local linking of child entities is very different depending on cell
607 // type and refinement patterns.
608 if (ref_el == lf::base::RefEl::kTria()) {
609 // Case of a triangle
610 switch (cell_refpat) {
611 case RefPat::rp_nil: {
612 LF_VERIFY_MSG(false, "Every triangle must be refined");
613 break;
614 }
615 case RefPat::rp_copy: {
616 // Just copy the parent triangle
617 child_cell_nodes.push_back({vertex_child_idx[0],
618 vertex_child_idx[1],
619 vertex_child_idx[2]});
620 break;
621 }
622 case RefPat::rp_bisect: {
623 LF_VERIFY_MSG(
624 anchor != idx_nil,
625 "Anchor must be set for bisection refinement of triangle");
626 // Splitting a triangle in two by bisecting anchor edge
627 tria_ccn_tmp[0] = vertex_child_idx[mod[0]];
628 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
629 tria_ccn_tmp[2] = vertex_child_idx[mod[2]];
630 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
631 "refpat = " << (int)cell_refpat << ": illegal index");
632 child_cell_nodes.push_back(tria_ccn_tmp);
633
634 tria_ccn_tmp[0] = vertex_child_idx[mod[1]];
635 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
636 tria_ccn_tmp[2] = vertex_child_idx[mod[2]];
637 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
638 "refpat = " << (int)cell_refpat << ": illegal index");
639 child_cell_nodes.push_back(tria_ccn_tmp);
640
641 // edges
642 cen_tmp[0] = edge_midpoint_idx[mod[0]];
643 cen_tmp[1] = vertex_child_idx[mod[2]];
644 child_edge_nodes.push_back(cen_tmp);
645 break;
646 }
647 case RefPat::rp_trisect: {
648 LF_VERIFY_MSG(
649 anchor != idx_nil,
650 "Anchor must be set for trisection refinement of triangle");
651 // Bisect through anchor edge first and then bisect through
652 // edge with the next larger index (mod 3); creates three
653 // child triangles.
654 tria_ccn_tmp[0] = vertex_child_idx[mod[0]];
655 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
656 tria_ccn_tmp[2] = vertex_child_idx[mod[2]];
657 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
658 "refpat = " << (int)cell_refpat << ": illegal index");
659 child_cell_nodes.push_back(tria_ccn_tmp);
660
661 tria_ccn_tmp[0] = vertex_child_idx[mod[1]];
662 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
663 tria_ccn_tmp[2] = edge_midpoint_idx[mod[1]];
664 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
665 "refpat = " << (int)cell_refpat << ": illegal index");
666 child_cell_nodes.push_back(tria_ccn_tmp);
667
668 tria_ccn_tmp[0] = vertex_child_idx[mod[2]];
669 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
670 tria_ccn_tmp[2] = edge_midpoint_idx[mod[1]];
671 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
672 "refpat = " << (int)cell_refpat << ": illegal index");
673 child_cell_nodes.push_back(tria_ccn_tmp);
674
675 // edges
676 cen_tmp[0] = edge_midpoint_idx[mod[0]];
677 cen_tmp[1] = vertex_child_idx[mod[2]];
678 child_edge_nodes.push_back(cen_tmp);
679
680 cen_tmp[0] = edge_midpoint_idx[mod[0]];
681 cen_tmp[1] = edge_midpoint_idx[mod[1]];
682 child_edge_nodes.push_back(cen_tmp);
683 break;
684 }
686 LF_VERIFY_MSG(
687 anchor != idx_nil,
688 "Anchor must be set for trisection refinement of triangle");
689
690 // Bisect through anchor edge first and then bisect through
691 // edge with the next smaller index (mod 3); creates three
692 // child triangles.
693 tria_ccn_tmp[0] = vertex_child_idx[mod[0]];
694 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
695 tria_ccn_tmp[2] = edge_midpoint_idx[mod[2]];
696 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
697 "refpat = " << (int)cell_refpat << ": illegal index");
698 child_cell_nodes.push_back(tria_ccn_tmp);
699
700 tria_ccn_tmp[0] = vertex_child_idx[mod[1]];
701 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
702 tria_ccn_tmp[2] = vertex_child_idx[mod[2]];
703 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
704 "refpat = " << (int)cell_refpat << ": illegal index");
705 child_cell_nodes.push_back(tria_ccn_tmp);
706
707 tria_ccn_tmp[0] = vertex_child_idx[mod[2]];
708 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
709 tria_ccn_tmp[2] = edge_midpoint_idx[mod[2]];
710 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
711 "refpat = " << (int)cell_refpat << ": illegal index");
712 child_cell_nodes.push_back(tria_ccn_tmp);
713
714 // edges
715 cen_tmp[0] = edge_midpoint_idx[mod[0]];
716 cen_tmp[1] = vertex_child_idx[mod[2]];
717 child_edge_nodes.push_back(cen_tmp);
718
719 cen_tmp[0] = edge_midpoint_idx[mod[0]];
720 cen_tmp[1] = edge_midpoint_idx[mod[2]];
721 child_edge_nodes.push_back(cen_tmp);
722 break;
723 }
724 case RefPat::rp_quadsect: {
725 LF_VERIFY_MSG(
726 anchor != idx_nil,
727 "Anchor must be set for quadsection refinement of triangle");
728 // Bisect through the anchor edge first and then
729 // through the two remaining edges; creates four child
730 // triangles; every edge is split.
731 tria_ccn_tmp[0] = vertex_child_idx[mod[0]];
732 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
733 tria_ccn_tmp[2] = edge_midpoint_idx[mod[2]];
734 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
735 "refpat = " << (int)cell_refpat << ": illegal index");
736 child_cell_nodes.push_back(tria_ccn_tmp);
737
738 tria_ccn_tmp[0] = vertex_child_idx[mod[1]];
739 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
740 tria_ccn_tmp[2] = edge_midpoint_idx[mod[1]];
741 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
742 "refpat = " << (int)cell_refpat << ": illegal index");
743 child_cell_nodes.push_back(tria_ccn_tmp);
744
745 tria_ccn_tmp[0] = vertex_child_idx[mod[2]];
746 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
747 tria_ccn_tmp[2] = edge_midpoint_idx[mod[1]];
748 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
749 "refpat = " << (int)cell_refpat << ": illegal index");
750 child_cell_nodes.push_back(tria_ccn_tmp);
751
752 tria_ccn_tmp[0] = vertex_child_idx[mod[2]];
753 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
754 tria_ccn_tmp[2] = edge_midpoint_idx[mod[2]];
755 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
756 "refpat = " << (int)cell_refpat << ": illegal index");
757 child_cell_nodes.push_back(tria_ccn_tmp);
758
759 // edges
760 cen_tmp[0] = edge_midpoint_idx[mod[0]];
761 cen_tmp[1] = vertex_child_idx[mod[2]];
762 child_edge_nodes.push_back(cen_tmp);
763
764 cen_tmp[0] = edge_midpoint_idx[mod[0]];
765 cen_tmp[1] = edge_midpoint_idx[mod[2]];
766 child_edge_nodes.push_back(cen_tmp);
767
768 cen_tmp[0] = edge_midpoint_idx[mod[0]];
769 cen_tmp[1] = edge_midpoint_idx[mod[1]];
770 child_edge_nodes.push_back(cen_tmp);
771 break;
772 }
774 // Split triangle into 6 smaller triangles by connecting
775 // the center of gravity with the vertices and the midpoints
776 // of the edges.
777 // Create a new interior vertex
778 std::vector<std::unique_ptr<geometry::Geometry>>
779 cell_center_geo_ptrs(cell->Geometry()->ChildGeometry(
780 rp, 2)); // point: co-dim == 2
781 LF_VERIFY_MSG(cell_center_geo_ptrs.size() == 1,
782 "Barycentrically refined triangle with "
783 << cell_center_geo_ptrs.size()
784 << " interior child nodes ??");
785 // Register midpoint as new node
786 const glb_idx_t center_fine_idx =
787 mesh_factory_->AddPoint(std::move(cell_center_geo_ptrs[0]));
788 cell_ci.child_point_idx.push_back(center_fine_idx);
789
790 tria_ccn_tmp[0] = vertex_child_idx[0];
791 tria_ccn_tmp[1] = edge_midpoint_idx[0];
792 tria_ccn_tmp[2] = center_fine_idx;
793 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
794 "refpat = " << (int)cell_refpat << ": illegal index");
795 child_cell_nodes.push_back(tria_ccn_tmp);
796
797 tria_ccn_tmp[0] = vertex_child_idx[1];
798 tria_ccn_tmp[1] = edge_midpoint_idx[0];
799 tria_ccn_tmp[2] = center_fine_idx;
800 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
801 "refpat = " << (int)cell_refpat << ": illegal index");
802 child_cell_nodes.push_back(tria_ccn_tmp);
803
804 tria_ccn_tmp[0] = vertex_child_idx[1];
805 tria_ccn_tmp[1] = edge_midpoint_idx[1];
806 tria_ccn_tmp[2] = center_fine_idx;
807 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
808 "refpat = " << (int)cell_refpat << ": illegal index");
809 child_cell_nodes.push_back(tria_ccn_tmp);
810
811 tria_ccn_tmp[0] = vertex_child_idx[2];
812 tria_ccn_tmp[1] = edge_midpoint_idx[1];
813 tria_ccn_tmp[2] = center_fine_idx;
814 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
815 "refpat = " << (int)cell_refpat << ": illegal index");
816 child_cell_nodes.push_back(tria_ccn_tmp);
817
818 tria_ccn_tmp[0] = vertex_child_idx[2];
819 tria_ccn_tmp[1] = edge_midpoint_idx[2];
820 tria_ccn_tmp[2] = center_fine_idx;
821 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
822 "refpat = " << (int)cell_refpat << ": illegal index");
823 child_cell_nodes.push_back(tria_ccn_tmp);
824
825 tria_ccn_tmp[0] = vertex_child_idx[0];
826 tria_ccn_tmp[1] = edge_midpoint_idx[2];
827 tria_ccn_tmp[2] = center_fine_idx;
828 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
829 "refpat = " << (int)cell_refpat << ": illegal index");
830 child_cell_nodes.push_back(tria_ccn_tmp);
831
832 // edges
833 cen_tmp[0] = vertex_child_idx[0];
834 cen_tmp[1] = center_fine_idx;
835 child_edge_nodes.push_back(cen_tmp);
836
837 cen_tmp[0] = vertex_child_idx[1];
838 cen_tmp[1] = center_fine_idx;
839 child_edge_nodes.push_back(cen_tmp);
840
841 cen_tmp[0] = vertex_child_idx[2];
842 cen_tmp[1] = center_fine_idx;
843 child_edge_nodes.push_back(cen_tmp);
844
845 cen_tmp[0] = edge_midpoint_idx[0];
846 cen_tmp[1] = center_fine_idx;
847 child_edge_nodes.push_back(cen_tmp);
848
849 cen_tmp[0] = edge_midpoint_idx[1];
850 cen_tmp[1] = center_fine_idx;
851 child_edge_nodes.push_back(cen_tmp);
852
853 cen_tmp[0] = edge_midpoint_idx[2];
854 cen_tmp[1] = center_fine_idx;
855 child_edge_nodes.push_back(cen_tmp);
856 break;
857 }
858 case RefPat::rp_regular: {
859 // regular refinement into four congruent triangles
860 // Child cells
861 tria_ccn_tmp[0] = vertex_child_idx[0];
862 tria_ccn_tmp[1] = edge_midpoint_idx[0];
863 tria_ccn_tmp[2] = edge_midpoint_idx[2];
864 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
865 "refpat = " << (int)cell_refpat << ": illegal index");
866 child_cell_nodes.push_back(tria_ccn_tmp);
867
868 tria_ccn_tmp[0] = vertex_child_idx[1];
869 tria_ccn_tmp[1] = edge_midpoint_idx[0];
870 tria_ccn_tmp[2] = edge_midpoint_idx[1];
871 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
872 "refpat = " << (int)cell_refpat << ": illegal index");
873 child_cell_nodes.push_back(tria_ccn_tmp);
874
875 tria_ccn_tmp[0] = vertex_child_idx[2];
876 tria_ccn_tmp[1] = edge_midpoint_idx[2];
877 tria_ccn_tmp[2] = edge_midpoint_idx[1];
878 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
879 "refpat = " << (int)cell_refpat << ": illegal index");
880 child_cell_nodes.push_back(tria_ccn_tmp);
881
882 tria_ccn_tmp[0] = edge_midpoint_idx[0];
883 tria_ccn_tmp[1] = edge_midpoint_idx[1];
884 tria_ccn_tmp[2] = edge_midpoint_idx[2];
885 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
886 "refpat = " << (int)cell_refpat << ": illegal index");
887 child_cell_nodes.push_back(tria_ccn_tmp);
888
889 // Child edges (interior edges)
890 cen_tmp[0] = edge_midpoint_idx[0];
891 cen_tmp[1] = edge_midpoint_idx[2];
892 child_edge_nodes.push_back(cen_tmp);
893
894 cen_tmp[0] = edge_midpoint_idx[0];
895 cen_tmp[1] = edge_midpoint_idx[1];
896 child_edge_nodes.push_back(cen_tmp);
897
898 cen_tmp[0] = edge_midpoint_idx[2];
899 cen_tmp[1] = edge_midpoint_idx[1];
900 child_edge_nodes.push_back(cen_tmp);
901 // No new interior node
902 break;
903 }
904 default: {
905 LF_VERIFY_MSG(
906 false, "Refinement pattern not (yet) implemented for triangle");
907 break;
908 }
909 } // end switch refpat
910 } else if (ref_el == lf::base::RefEl::kQuad()) {
911 // Case of a quadrilateral
912 switch (cell_refpat) {
913 case RefPat::rp_nil: {
914 LF_VERIFY_MSG(false, "Every quadrilateral must be refined");
915 break;
916 }
917 case RefPat::rp_copy: {
918 // Just copy the parent triangle
919 child_cell_nodes.push_back(
920 {vertex_child_idx[0], vertex_child_idx[1], vertex_child_idx[2],
921 vertex_child_idx[3]});
922 break;
923 }
924 case RefPat::rp_trisect: {
925 LF_VERIFY_MSG(
926 anchor != idx_nil,
927 "Anchor must be set for trisection refinement of quad");
928
929 // Partition a quad into three triangle, the anchor edge
930 // being split in the process
931 tria_ccn_tmp[0] = edge_midpoint_idx[mod[0]];
932 tria_ccn_tmp[1] = vertex_child_idx[mod[2]];
933 tria_ccn_tmp[2] = vertex_child_idx[mod[3]];
934 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
935 "refpat = " << (int)cell_refpat << ": illegal index");
936 child_cell_nodes.push_back(tria_ccn_tmp);
937
938 tria_ccn_tmp[0] = edge_midpoint_idx[mod[0]];
939 tria_ccn_tmp[1] = vertex_child_idx[mod[0]];
940 tria_ccn_tmp[2] = vertex_child_idx[mod[3]];
941 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
942 "refpat = " << (int)cell_refpat << ": illegal index");
943 child_cell_nodes.push_back(tria_ccn_tmp);
944
945 tria_ccn_tmp[0] = edge_midpoint_idx[mod[0]];
946 tria_ccn_tmp[1] = vertex_child_idx[mod[1]];
947 tria_ccn_tmp[2] = vertex_child_idx[mod[2]];
948 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
949 "refpat = " << (int)cell_refpat << ": illegal index");
950 child_cell_nodes.push_back(tria_ccn_tmp);
951
952 // edges
953 cen_tmp[0] = edge_midpoint_idx[mod[0]];
954 cen_tmp[1] = vertex_child_idx[mod[2]];
955 child_edge_nodes.push_back(cen_tmp);
956
957 cen_tmp[0] = edge_midpoint_idx[mod[0]];
958 cen_tmp[1] = vertex_child_idx[mod[3]];
959 child_edge_nodes.push_back(cen_tmp);
960 break;
961 }
962 case RefPat::rp_quadsect: {
963 LF_VERIFY_MSG(
964 anchor != idx_nil,
965 "Anchor must be set for quadsection refinement of triangle");
966 // Partition a quad into four triangle, thus
967 // splitting two edges. The one with the smaller sub index is the
968 // anchor edge
969 tria_ccn_tmp[0] = vertex_child_idx[mod[0]];
970 tria_ccn_tmp[1] = vertex_child_idx[mod[3]];
971 tria_ccn_tmp[2] = edge_midpoint_idx[mod[0]];
972 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
973 "refpat = " << (int)cell_refpat << ": illegal index");
974 child_cell_nodes.push_back(tria_ccn_tmp);
975
976 tria_ccn_tmp[0] = vertex_child_idx[mod[1]];
977 tria_ccn_tmp[1] = edge_midpoint_idx[mod[1]];
978 tria_ccn_tmp[2] = edge_midpoint_idx[mod[0]];
979 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
980 "refpat = " << (int)cell_refpat << ": illegal index");
981 child_cell_nodes.push_back(tria_ccn_tmp);
982
983 tria_ccn_tmp[0] = vertex_child_idx[mod[2]];
984 tria_ccn_tmp[1] = vertex_child_idx[mod[3]];
985 tria_ccn_tmp[2] = edge_midpoint_idx[mod[1]];
986 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
987 "refpat = " << (int)cell_refpat << ": illegal index");
988 child_cell_nodes.push_back(tria_ccn_tmp);
989
990 tria_ccn_tmp[0] = edge_midpoint_idx[mod[0]];
991 tria_ccn_tmp[1] = edge_midpoint_idx[mod[1]];
992 tria_ccn_tmp[2] = vertex_child_idx[mod[3]];
993 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
994 "refpat = " << (int)cell_refpat << ": illegal index");
995 child_cell_nodes.push_back(tria_ccn_tmp);
996
997 // edges
998 cen_tmp[0] = vertex_child_idx[mod[3]];
999 cen_tmp[1] = edge_midpoint_idx[mod[0]];
1000 child_edge_nodes.push_back(cen_tmp);
1001
1002 cen_tmp[0] = edge_midpoint_idx[mod[1]];
1003 cen_tmp[1] = edge_midpoint_idx[mod[0]];
1004 child_edge_nodes.push_back(cen_tmp);
1005
1006 cen_tmp[0] = vertex_child_idx[mod[3]];
1007 cen_tmp[1] = edge_midpoint_idx[mod[1]];
1008 child_edge_nodes.push_back(cen_tmp);
1009 break;
1010 }
1011 case RefPat::rp_bisect:
1012 case RefPat::rp_split: {
1013 LF_VERIFY_MSG(anchor != idx_nil,
1014 "Anchor must be set for splitting of quad");
1015
1016 // Cut a quadrilateral into two
1017 quad_ccn_tmp[0] = vertex_child_idx[mod[0]];
1018 quad_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
1019 quad_ccn_tmp[2] = edge_midpoint_idx[mod[2]];
1020 quad_ccn_tmp[3] = vertex_child_idx[mod[3]];
1021 LF_ASSERT_MSG(checkValidIndex(quad_ccn_tmp),
1022 "refpat = " << (int)cell_refpat << ": illegal index");
1023 child_cell_nodes.push_back(quad_ccn_tmp);
1024
1025 quad_ccn_tmp[0] = vertex_child_idx[mod[1]];
1026 quad_ccn_tmp[1] = vertex_child_idx[mod[2]];
1027 quad_ccn_tmp[2] = edge_midpoint_idx[mod[2]];
1028 quad_ccn_tmp[3] = edge_midpoint_idx[mod[0]];
1029 LF_ASSERT_MSG(checkValidIndex(quad_ccn_tmp),
1030 "refpat = " << (int)cell_refpat << ": illegal index");
1031 child_cell_nodes.push_back(quad_ccn_tmp);
1032
1033 // edges
1034 cen_tmp[0] = edge_midpoint_idx[mod[0]];
1035 cen_tmp[1] = edge_midpoint_idx[mod[2]];
1036 child_edge_nodes.push_back(cen_tmp);
1037 break;
1038 }
1039 case RefPat::rp_threeedge: {
1040 LF_VERIFY_MSG(
1041 anchor != idx_nil,
1042 "Anchor must be set for three edge refinement of a quad");
1043
1044 quad_ccn_tmp[0] = vertex_child_idx[mod[2]];
1045 quad_ccn_tmp[1] = vertex_child_idx[mod[3]];
1046 quad_ccn_tmp[2] = edge_midpoint_idx[mod[3]];
1047 quad_ccn_tmp[3] = edge_midpoint_idx[mod[1]];
1048 LF_ASSERT_MSG(checkValidIndex(quad_ccn_tmp),
1049 "refpat = " << (int)cell_refpat << ": illegal index");
1050 child_cell_nodes.push_back(quad_ccn_tmp);
1051
1052 tria_ccn_tmp[0] = vertex_child_idx[mod[0]];
1053 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
1054 tria_ccn_tmp[2] = edge_midpoint_idx[mod[3]];
1055 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
1056 "refpat = " << (int)cell_refpat << ": illegal index");
1057 child_cell_nodes.push_back(tria_ccn_tmp);
1058
1059 tria_ccn_tmp[0] = vertex_child_idx[mod[1]];
1060 tria_ccn_tmp[1] = edge_midpoint_idx[mod[0]];
1061 tria_ccn_tmp[2] = edge_midpoint_idx[mod[1]];
1062 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
1063 "refpat = " << (int)cell_refpat << ": illegal index");
1064 child_cell_nodes.push_back(tria_ccn_tmp);
1065
1066 tria_ccn_tmp[0] = edge_midpoint_idx[mod[0]];
1067 tria_ccn_tmp[1] = edge_midpoint_idx[mod[1]];
1068 tria_ccn_tmp[2] = edge_midpoint_idx[mod[3]];
1069 LF_ASSERT_MSG(checkValidIndex(tria_ccn_tmp),
1070 "refpat = " << (int)cell_refpat << ": illegal index");
1071 child_cell_nodes.push_back(tria_ccn_tmp);
1072
1073 // edges
1074 cen_tmp[0] = edge_midpoint_idx[mod[3]];
1075 cen_tmp[1] = edge_midpoint_idx[mod[1]];
1076 child_edge_nodes.push_back(cen_tmp);
1077
1078 cen_tmp[0] = edge_midpoint_idx[mod[0]];
1079 cen_tmp[1] = edge_midpoint_idx[mod[3]];
1080 child_edge_nodes.push_back(cen_tmp);
1081
1082 cen_tmp[0] = edge_midpoint_idx[mod[0]];
1083 cen_tmp[1] = edge_midpoint_idx[mod[1]];
1084 child_edge_nodes.push_back(cen_tmp);
1085 break;
1086 }
1088 case RefPat::rp_regular: {
1089 // Create a new interior vertex
1090 std::vector<std::unique_ptr<geometry::Geometry>>
1091 cell_center_geo_ptrs(cell->Geometry()->ChildGeometry(
1092 rp, 2)); // point: co-dim == 2
1093 LF_VERIFY_MSG(cell_center_geo_ptrs.size() == 1,
1094 "Regularly refined quadrilateral with "
1095 << cell_center_geo_ptrs.size()
1096 << " interior child nodes!");
1097 // Register midpoint as new node
1098 const glb_idx_t center_fine_idx =
1099 mesh_factory_->AddPoint(std::move(cell_center_geo_ptrs[0]));
1100 cell_ci.child_point_idx.push_back(center_fine_idx);
1101
1102 // Set the node indices (w.r.t. fine mesh) of the four sub-quads
1103 quad_ccn_tmp[0] = vertex_child_idx[0];
1104 quad_ccn_tmp[1] = edge_midpoint_idx[0];
1105 quad_ccn_tmp[2] = center_fine_idx;
1106 quad_ccn_tmp[3] = edge_midpoint_idx[3];
1107 LF_ASSERT_MSG(checkValidIndex(quad_ccn_tmp),
1108 "refpat = " << (int)cell_refpat << ": illegal index");
1109 child_cell_nodes.push_back(quad_ccn_tmp);
1110
1111 quad_ccn_tmp[0] = vertex_child_idx[1];
1112 quad_ccn_tmp[1] = edge_midpoint_idx[1];
1113 quad_ccn_tmp[2] = center_fine_idx;
1114 quad_ccn_tmp[3] = edge_midpoint_idx[0];
1115 LF_ASSERT_MSG(checkValidIndex(quad_ccn_tmp),
1116 "refpat = " << (int)cell_refpat << ": illegal index");
1117 child_cell_nodes.push_back(quad_ccn_tmp);
1118
1119 quad_ccn_tmp[0] = vertex_child_idx[2];
1120 quad_ccn_tmp[1] = edge_midpoint_idx[1];
1121 quad_ccn_tmp[2] = center_fine_idx;
1122 quad_ccn_tmp[3] = edge_midpoint_idx[2];
1123 LF_ASSERT_MSG(checkValidIndex(quad_ccn_tmp),
1124 "refpat = " << (int)cell_refpat << ": illegal index");
1125 child_cell_nodes.push_back(quad_ccn_tmp);
1126
1127 quad_ccn_tmp[0] = vertex_child_idx[3];
1128 quad_ccn_tmp[1] = edge_midpoint_idx[2];
1129 quad_ccn_tmp[2] = center_fine_idx;
1130 quad_ccn_tmp[3] = edge_midpoint_idx[3];
1131 LF_ASSERT_MSG(checkValidIndex(quad_ccn_tmp),
1132 "refpat = " << (int)cell_refpat << ": illegal index");
1133 child_cell_nodes.push_back(quad_ccn_tmp);
1134
1135 // set node indices of the four new interior edges
1136 cen_tmp[0] = edge_midpoint_idx[0];
1137 cen_tmp[1] = center_fine_idx;
1138 child_edge_nodes.push_back(cen_tmp);
1139
1140 cen_tmp[0] = edge_midpoint_idx[1];
1141 cen_tmp[1] = center_fine_idx;
1142 child_edge_nodes.push_back(cen_tmp);
1143
1144 cen_tmp[0] = edge_midpoint_idx[2];
1145 cen_tmp[1] = center_fine_idx;
1146 child_edge_nodes.push_back(cen_tmp);
1147
1148 cen_tmp[0] = edge_midpoint_idx[3];
1149 cen_tmp[1] = center_fine_idx;
1150 child_edge_nodes.push_back(cen_tmp);
1151 break;
1152 }
1153 default: {
1154 LF_VERIFY_MSG(
1155 false,
1156 "Refinement pattern not (yet) implemented for quadrilateral");
1157 break;
1158 }
1159 } // end switch cell_refpat
1160 } // end quadrilateral case
1161 else {
1162 LF_VERIFY_MSG(false, "Unknown cell type" << ref_el.ToString());
1163 }
1164 // Register new edges
1165 {
1166 std::vector<std::unique_ptr<geometry::Geometry>> cell_edge_geo_ptrs(
1167 cell->Geometry()->ChildGeometry(rp, 1)); // child edge: co-dim == 1
1168 const size_type num_new_edges = child_edge_nodes.size();
1169 LF_VERIFY_MSG(num_new_edges == rp.NumChildren(1),
1170 "num_new_edges = " << num_new_edges << " <-> "
1171 << rp.NumChildren(1));
1172 for (int k = 0; k < num_new_edges; k++) {
1173 const std::array<glb_idx_t, 2> &cen(child_edge_nodes[k]);
1174 SPDLOG_LOGGER_TRACE(
1175 Logger(), "{}({}), ref_pat = {}: new edge {}[{},{}]", ref_el,
1176 cell_index, (int)cell_refpat, k, cen[0], cen[1]);
1177
1178 const glb_idx_t new_edge_index = mesh_factory_->AddEntity(
1180 std::array<base::glb_idx_t, 2>{{cen[0], cen[1]}},
1181 std::move(cell_edge_geo_ptrs[k]));
1182 cell_ci.child_edge_idx.push_back(new_edge_index);
1183 } // end loop over new edges
1184 } // end register new edges
1185 // Register new cells
1186 {
1187 std::vector<std::unique_ptr<geometry::Geometry>> childcell_geo_ptrs(
1188 cell->Geometry()->ChildGeometry(rp, 0)); // child cell: co-dim == 0
1189 const size_type num_new_cells = child_cell_nodes.size();
1190 LF_VERIFY_MSG(num_new_cells == rp.NumChildren(0),
1191 "num_new_cells = " << num_new_cells << " <-> "
1192 << rp.NumChildren(0));
1193 for (int k = 0; k < num_new_cells; k++) {
1194 const std::vector<glb_idx_t> &ccn(child_cell_nodes[k]);
1195 glb_idx_t new_cell_index;
1196 if (ccn.size() == 3) {
1197 // New cell is a triangle
1198 SPDLOG_LOGGER_TRACE(
1199 Logger(), "{}({}), ref_pat = {}: new triangle {}[{},{},{}]",
1200 ref_el, cell_index, (int)cell_refpat, k, ccn[0], ccn[1],
1201 ccn[2]);
1202
1203 new_cell_index = mesh_factory_->AddEntity(
1205 std::array<base::glb_idx_t, 3>{ccn[0], ccn[1], ccn[2]},
1206 std::move(childcell_geo_ptrs[k]));
1207 } else if (ccn.size() == 4) {
1208 // New cell is a quadrilateral
1209 SPDLOG_LOGGER_TRACE(
1210 Logger(), "{}({}), ref_pat = {}: new quad {}[{},{},{},{}]",
1211 ref_el, cell_index, (int)cell_refpat, k, ccn[0], ccn[1], ccn[2],
1212 ccn[3]);
1213
1214 new_cell_index =
1215 mesh_factory_->AddEntity(lf::base::RefEl::kQuad(),
1216 std::array<base::glb_idx_t, 4>{
1217 {ccn[0], ccn[1], ccn[2], ccn[3]}},
1218 std::move(childcell_geo_ptrs[k]));
1219 } else {
1220 LF_VERIFY_MSG(false,
1221 "Child cells must be either triangles or quads");
1222 }
1223 cell_ci.child_cell_idx.push_back(new_cell_index);
1224 } // end loop over new cells
1225 } // end register new cells
1226 } // end loop over cells
1227 }
1228 // At this point the MeshFactory has complete information to generate the new
1229 // finest mesh
1230 meshes_.push_back(mesh_factory_->Build()); // MESH CONSTRUCTION
1231 const mesh::Mesh &child_mesh(*meshes_.back());
1232
1233 SPDLOG_LOGGER_DEBUG(Logger(), "Child mesh {} nodes, {} edges, {} cells.",
1234 child_mesh.NumEntities(2), child_mesh.NumEntities(1),
1235 child_mesh.NumEntities(0));
1236
1237 // Create space for data pertaining to the new mesh
1238 // Note that references to vectors may become invalid
1239 {
1240 // Arrays containing parent information
1241 parent_infos_.push_back(
1242 {std::move(std::vector<ParentInfo>(child_mesh.NumEntities(0))),
1243 std::move(std::vector<ParentInfo>(child_mesh.NumEntities(1))),
1244 std::move(std::vector<ParentInfo>(child_mesh.NumEntities(2)))});
1245
1246 // Initialize (empty) refinement information for newly created fine mesh
1247 // Same code as in the constructor of MeshHierarchy
1248 std::vector<CellChildInfo> fine_cell_child_info(child_mesh.NumEntities(0));
1249 std::vector<EdgeChildInfo> fine_edge_child_info(child_mesh.NumEntities(1));
1250 std::vector<PointChildInfo> fine_point_child_info(
1251 child_mesh.NumEntities(2));
1252 cell_child_infos_.push_back(std::move(fine_cell_child_info));
1253 edge_child_infos_.push_back(std::move(fine_edge_child_info));
1254 point_child_infos_.push_back(std::move(fine_point_child_info));
1255
1256 // Array containing information about refinement edges
1257 refinement_edges_.push_back(
1258 std::move(std::vector<sub_idx_t>(child_mesh.NumEntities(0), idx_nil)));
1259
1260 // Finally set up vector for edge flags
1261 edge_marked_.emplace_back(child_mesh.NumEntities(1), false);
1262 }
1263
1264 // Finally, we have to initialize the parent pointers for the entities of the
1265 // newly created finest mesh.
1266 {
1267 const size_type n_levels = meshes_.size();
1268 LF_VERIFY_MSG(n_levels > 1, "A least two levels after refinement");
1269 // Access parent information for finest level
1270 std::vector<ParentInfo> &fine_node_parent_info(parent_infos_.back()[2]);
1271 std::vector<ParentInfo> &fine_edge_parent_info(parent_infos_.back()[1]);
1272 std::vector<ParentInfo> &fine_cell_parent_info(parent_infos_.back()[0]);
1273
1274 LF_VERIFY_MSG(fine_node_parent_info.size() == child_mesh.NumEntities(2),
1275 "fine_node_parent_info size mismatch");
1276 LF_VERIFY_MSG(fine_edge_parent_info.size() == child_mesh.NumEntities(1),
1277 "fine_edge_parent_info size mismatch");
1278 LF_VERIFY_MSG(fine_cell_parent_info.size() == child_mesh.NumEntities(0),
1279 "fine_edge_parent_info size mismatch");
1280
1281 // Visit all nodes of the parent mesh and retrieve their children by index
1282 std::vector<PointChildInfo> &pt_child_info(
1283 point_child_infos_.at(n_levels - 2));
1284 for (const mesh::Entity *node : parent_mesh.Entities(2)) {
1285 // Obtain index of node in coarse mesh
1286 const glb_idx_t node_index = parent_mesh.Index(*node);
1287 const glb_idx_t child_index = pt_child_info[node_index].child_point_idx;
1288 if (child_index != idx_nil) {
1289 LF_VERIFY_MSG(child_index < fine_node_parent_info.size(),
1290 "index " << child_index << " illegal for child vertex");
1291 fine_node_parent_info[child_index].child_number = 0;
1292 fine_node_parent_info[child_index].parent_ptr = node;
1293 fine_node_parent_info[child_index].parent_index = node_index;
1294 }
1295 } // end loop over nodes
1296
1297 // Traverse edges and set the parent for their interior nodes and child
1298 // edges
1299 std::vector<EdgeChildInfo> &ed_child_info(
1300 edge_child_infos_.at(n_levels - 2));
1301 for (const mesh::Entity *edge : parent_mesh.Entities(1)) {
1302 // Obtain index of edge in coarse mesh
1303 const glb_idx_t edge_index = parent_mesh.Index(*edge);
1304 const EdgeChildInfo &ci_edge(ed_child_info[edge_index]);
1305
1306 // Visit child edges
1307 const size_type num_child_edges = ci_edge.child_edge_idx.size();
1308 for (int l = 0; l < num_child_edges; l++) {
1309 const glb_idx_t edge_child_idx = ci_edge.child_edge_idx[l];
1310 LF_VERIFY_MSG(edge_child_idx < fine_edge_parent_info.size(),
1311 "index " << edge_child_idx << " illegal for child edge");
1312 fine_edge_parent_info[edge_child_idx].child_number = l;
1313 fine_edge_parent_info[edge_child_idx].parent_ptr = edge;
1314 fine_edge_parent_info[edge_child_idx].parent_index = edge_index;
1315 } // end loop over child edges
1316
1317 // Visit child nodes
1318 const size_type num_child_nodes = ci_edge.child_point_idx.size();
1319 for (int l = 0; l < num_child_nodes; l++) {
1320 const glb_idx_t node_child_idx = ci_edge.child_point_idx[l];
1321 LF_VERIFY_MSG(node_child_idx < fine_node_parent_info.size(),
1322 "index " << node_child_idx << " illegal for child point");
1323 fine_node_parent_info[node_child_idx].child_number = l;
1324 fine_node_parent_info[node_child_idx].parent_ptr = edge;
1325 fine_node_parent_info[node_child_idx].parent_index = edge_index;
1326 } // end loop over child points
1327 } // end loop over edges
1328
1329 // Loop over cells
1330 std::vector<CellChildInfo> &cell_child_info(
1331 cell_child_infos_.at(n_levels - 2));
1332 for (const mesh::Entity *cell : parent_mesh.Entities(0)) {
1333 // fetch index of current cell
1334 const glb_idx_t cell_index(parent_mesh.Index(*cell));
1335 // Get infformation about children
1336 CellChildInfo &cell_ci(cell_child_info[cell_index]);
1337
1338 // Visit child cells
1339 const size_type num_child_cells = cell_ci.child_cell_idx.size();
1340 for (int l = 0; l < num_child_cells; l++) {
1341 const glb_idx_t child_cell_idx = cell_ci.child_cell_idx[l];
1342 LF_VERIFY_MSG(child_cell_idx < fine_cell_parent_info.size(),
1343 "index " << child_cell_idx << " illegal for child cell");
1344 fine_cell_parent_info[child_cell_idx].child_number = l;
1345 fine_cell_parent_info[child_cell_idx].parent_ptr = cell;
1346 fine_cell_parent_info[child_cell_idx].parent_index = cell_index;
1347 }
1348
1349 // Visit child edges
1350 const size_type num_child_edges = cell_ci.child_edge_idx.size();
1351 for (int l = 0; l < num_child_edges; l++) {
1352 const glb_idx_t edge_child_idx = cell_ci.child_edge_idx[l];
1353 LF_VERIFY_MSG(edge_child_idx < fine_edge_parent_info.size(),
1354 "index " << edge_child_idx << " illegal for child edge");
1355 fine_edge_parent_info[edge_child_idx].child_number = l;
1356 fine_edge_parent_info[edge_child_idx].parent_ptr = cell;
1357 fine_edge_parent_info[edge_child_idx].parent_index = cell_index;
1358 } // end loop over child edges
1359
1360 // Visit child nodes
1361 const size_type num_child_nodes = cell_ci.child_point_idx.size();
1362 for (int l = 0; l < num_child_nodes; l++) {
1363 const glb_idx_t node_child_idx = cell_ci.child_point_idx[l];
1364 LF_VERIFY_MSG(node_child_idx < fine_node_parent_info.size(),
1365 "index " << node_child_idx << " illegal for child point");
1366 fine_node_parent_info[node_child_idx].child_number = l;
1367 fine_node_parent_info[node_child_idx].parent_ptr = cell;
1368 fine_node_parent_info[node_child_idx].parent_index = cell_index;
1369 } // end loop over child points
1370 } // end loop over cells of parent mesh
1371 }
1372
1373 // Finally set refinement edges for fine mesh
1374 {
1375 const size_type n_levels = meshes_.size();
1376 LF_VERIFY_MSG(n_levels > 1, "At least two levels after refinement!");
1377 std::vector<sub_idx_t> &child_ref_edges(refinement_edges_.at(n_levels - 1));
1378 std::vector<ParentInfo> &cell_parent_info(
1379 parent_infos_.at(n_levels - 1)[0]);
1380 std::vector<CellChildInfo> &parent_cell_ci(
1381 cell_child_infos_.at(n_levels - 2));
1382 std::vector<sub_idx_t> &parent_ref_edges(
1383 refinement_edges_.at(n_levels - 2));
1384
1385 // Traverse the cells of the fine mesh
1386 SPDLOG_LOGGER_DEBUG(Logger(), "Setting refinement edges");
1387
1388 for (const mesh::Entity *fine_cell : child_mesh.Entities(0)) {
1389 const glb_idx_t cell_index = child_mesh.Index(*fine_cell);
1390 child_ref_edges[cell_index] = idx_nil;
1391 // Refinement edge relevant for triangles onyl
1392 if (fine_cell->RefEl() == lf::base::RefEl::kTria()) {
1393 // pointer to cell whose refinement has created the current one
1394 const mesh::Entity *parent_ptr =
1395 cell_parent_info[cell_index].parent_ptr;
1396 LF_VERIFY_MSG(parent_ptr != nullptr,
1397 "Cell " << cell_index << " has no parent, paren_index = "
1398 << cell_parent_info[cell_index].parent_index);
1399
1400 if (parent_ptr->RefEl() == lf::base::RefEl::kTria()) {
1401 // Current cell was created by splitting a triangle
1402 // Inheritance rules for refinement edges apply
1403 // Together with refinement pattern allows identification of child
1404 // cell
1405 const sub_idx_t fine_cell_child_number =
1406 (parent_infos_.back())[0][cell_index].child_number;
1407 const glb_idx_t parent_index = parent_mesh.Index(*parent_ptr);
1408 LF_VERIFY_MSG(parent_index < parent_mesh.NumEntities(0),
1409 "parent_index = " << parent_index << " out of range");
1410
1411 SPDLOG_LOGGER_TRACE(Logger(),
1412 "Cell {}: triangle child {} of parent {}",
1413 cell_index, fine_cell_child_number, parent_index);
1414
1415 const CellChildInfo &parent_ci(parent_cell_ci[parent_index]);
1416 LF_VERIFY_MSG(
1417 parent_ci.child_cell_idx[fine_cell_child_number] == cell_index,
1418 "Parent child index mismatch!");
1419 const RefPat parent_ref_pat = parent_ci.ref_pat_;
1420
1421 switch (parent_ref_pat) {
1422 case RefPat::rp_nil: {
1423 LF_VERIFY_MSG(false,
1424 "Parent cannot carry nil refinement pattern");
1425 break;
1426 }
1427 case RefPat::rp_copy: {
1428 // Inherit refinement edge from parent triangle
1429 child_ref_edges[cell_index] = parent_ref_edges[parent_index];
1430 break;
1431 }
1432 case RefPat::rp_bisect: {
1433 // Both children have refinement edge 2
1434 LF_VERIFY_MSG(fine_cell_child_number < 2,
1435 "Only 2 children for rp_bisect");
1436 child_ref_edges[cell_index] = 2;
1437 break;
1438 }
1439 case RefPat::rp_trisect: {
1440 // Refinement edges: 0 -> 2, 1 -> 1, 2 -> 0
1441 LF_VERIFY_MSG(fine_cell_child_number < 3,
1442 "Only 3 children for rp_trisect");
1443 switch (fine_cell_child_number) {
1444 case 0: {
1445 child_ref_edges[cell_index] = 2;
1446 break;
1447 }
1448 case 1: {
1449 child_ref_edges[cell_index] = 1;
1450 break;
1451 }
1452 case 2: {
1453 child_ref_edges[cell_index] = 0;
1454 break;
1455 }
1456 default:
1457 LF_VERIFY_MSG(false,
1458 "invalid value for fine_cell_child_number");
1459 }
1460 break;
1461 }
1462 case RefPat::rp_trisect_left: {
1463 // Refinement edges: 0 -> 2, 1 -> 1, 2 -> 0
1464 LF_VERIFY_MSG(fine_cell_child_number < 4,
1465 "Only 3 children for rp_quadsect");
1466 switch (fine_cell_child_number) {
1467 case 0:
1468 case 1: {
1469 child_ref_edges[cell_index] = 2;
1470 break;
1471 }
1472 case 2: {
1473 child_ref_edges[cell_index] = 0;
1474 break;
1475 }
1476 default:
1477 LF_VERIFY_MSG(false,
1478 "invalid value for fine_cell_child_number");
1479 }
1480 break;
1481 }
1482 case RefPat::rp_quadsect: {
1483 // Refinement edges: 0 -> 2, 1 -> 0, 2 -> 0, 3-> 0
1484 switch (fine_cell_child_number) {
1485 case 0: {
1486 child_ref_edges[cell_index] = 2;
1487 break;
1488 }
1489 case 1:
1490 case 2:
1491 case 3: {
1492 child_ref_edges[cell_index] = 0;
1493 break;
1494 }
1495 default: {
1496 LF_VERIFY_MSG(false, "Illegal child number");
1497 break;
1498 }
1499 }
1500 break;
1501 }
1502 case rp_regular: {
1503 // Inherit the refinement edge of the parent triangle
1504 const sub_idx_t parent_ref_edge_idx =
1505 parent_ref_edges[parent_index];
1506 switch (parent_ref_edge_idx) {
1507 case 0: {
1508 switch (fine_cell_child_number) {
1509 case 0:
1510 case 1: {
1511 child_ref_edges[cell_index] = 0;
1512 break;
1513 }
1514 case 2:
1515 case 3: {
1516 child_ref_edges[cell_index] = 1;
1517 break;
1518 }
1519 default: {
1520 LF_VERIFY_MSG(false, "Illegal child number");
1521 break;
1522 }
1523 }
1524 break;
1525 }
1526 case 1: {
1527 switch (fine_cell_child_number) {
1528 case 0: {
1529 child_ref_edges[cell_index] = 1;
1530 break;
1531 }
1532 case 1:
1533 case 2:
1534 case 3: {
1535 child_ref_edges[cell_index] = 2;
1536 break;
1537 }
1538 default: {
1539 LF_VERIFY_MSG(false, "Illegal child number");
1540 break;
1541 }
1542 }
1543 break;
1544 }
1545 case 2: {
1546 switch (fine_cell_child_number) {
1547 case 0: {
1548 child_ref_edges[cell_index] = 2;
1549 break;
1550 }
1551 case 1: {
1552 child_ref_edges[cell_index] = 1;
1553 break;
1554 }
1555 case 2:
1556 case 3: {
1557 child_ref_edges[cell_index] = 0;
1558 break;
1559 }
1560 default: {
1561 LF_VERIFY_MSG(false, "Illegal child number");
1562 break;
1563 }
1564 }
1565 break;
1566 }
1567 default:
1568 LF_VERIFY_MSG(false, "invalid parent_ref_edge_idx")
1569 } // end switch parent ref_edge_idx
1570 break;
1571 }
1572 case rp_barycentric: {
1573 // In the case of barycentric refinement choose the longest edge
1574 // as refinement edge for every child triangle
1575 child_ref_edges[cell_index] = LongestEdge(*fine_cell);
1576 break;
1577 }
1578 default: {
1579 LF_VERIFY_MSG(false, "Illegal refinement type for a triangle");
1580 break;
1581 }
1582 } // end switch parent_ref_pat
1583
1584 SPDLOG_LOGGER_TRACE(Logger(), "ref_pat = {}({}) ref edge = {}",
1585 (int)parent_ref_pat, parent_ref_pat,
1586 child_ref_edges[cell_index]);
1587
1588 } // end treatment of triangular child cell
1589 else if (parent_ptr->RefEl() == lf::base::RefEl::kQuad()) {
1590 // Parent is a quadrilateral:
1591 // refinement edge will be set to the longest edge
1592 child_ref_edges[cell_index] = LongestEdge(*fine_cell);
1593 } else {
1594 LF_VERIFY_MSG(false, "Unknown parent cell type");
1595 }
1596 }
1597 }
1598 }
1599} // end Perform Refinement
1600
1601// **********************************************************************
1602// Initialization of rel_ref_geo fields of ParentInfo structures
1603// **********************************************************************
1604// NOLINTNEXTLINE(readability-function-cognitive-complexity)
1605void MeshHierarchy::initGeometryInParent() {
1606 // number of meshes contained in the hierarchy
1607 const size_type num_levels = NumLevels();
1608 SPDLOG_LOGGER_DEBUG(Logger(),
1609 "Entering MeshHierarchy::initGeometryInParent: {} levels",
1610 num_levels);
1611
1612 // Invoking this function makes sense only if the finest mesh has been
1613 // created by refinement.
1614 LF_ASSERT_MSG(num_levels > 1, "Must have been refined at least once");
1615 // Obtain references to finest and second finest mesh
1616 const lf::mesh::Mesh &parent_mesh{*meshes_.at(num_levels - 2)};
1617 const lf::mesh::Mesh &child_mesh{*meshes_.at(num_levels - 1)};
1618 // Partly intialized vectors of child information
1619 const std::vector<PointChildInfo> &pt_child_info{
1620 point_child_infos_.at(num_levels - 2)};
1621 std::vector<EdgeChildInfo> &ed_child_info{
1622 edge_child_infos_.at(num_levels - 2)};
1623 std::vector<CellChildInfo> &cell_child_info{
1624 cell_child_infos_.at(num_levels - 2)};
1625 // Dummy relative geometry for a point in a point
1626 const Eigen::MatrixXd nil_coords(0, 1);
1627
1628 // Visit all entities of the fine mesh. Outer loop covers co-dimensions
1629 // starting with cells (codim = 0)
1630 for (dim_t codim = 0; codim <= 2; ++codim) {
1631 // Reference to ParentInfo vector for entities of co-dimension codim
1632 // of the new mesh
1633 std::vector<ParentInfo> &child_entities_parent_info(
1634 parent_infos_.back()[codim]);
1635 // Loop over all nodes of the new mesh (co-dimension = 2)
1636 for (const lf::mesh::Entity *child_entity : child_mesh.Entities(codim)) {
1637 // Obtain index of the child entity
1638 const glb_idx_t child_idx = child_mesh.Index(*child_entity);
1639 // Obtain ParentInfo for the current child entity
1640 ParentInfo &child_pi{child_entities_parent_info[child_idx]};
1641 LF_ASSERT_MSG(child_pi.rel_ref_geo_ == nullptr,
1642 "No double initialization of rel_ref_geo_");
1643 // Get number of child
1644 const sub_idx_t child_number = child_pi.child_number;
1645 LF_ASSERT_MSG(child_number != idx_nil, "Child number missing");
1646 // Obtain parent entity
1647 LF_ASSERT_MSG(child_pi.parent_ptr != nullptr, "No valid parent pointer");
1648 const lf::mesh::Entity &parent{*child_pi.parent_ptr};
1649 // Obtain type of parent entity
1650 const lf::base::RefEl parent_ref_el(parent.RefEl());
1651 // Obtain co-dimension of parent entity
1652 const dim_t parent_codim = parent.Codim();
1653 // Index of parent entity
1654 const glb_idx_t parent_idx = parent_mesh.Index(parent);
1655 // Difference in co-dimensions
1656 LF_ASSERT_MSG(parent_codim <= codim, "Paren codim > child codim!");
1657 const dim_t child_rel_codim = codim - parent_codim;
1658 // Obtain local geometry of child. To that end we have to do
1659 // different things for different parent types
1660 switch (parent_codim) {
1661 case 0: { // the parent entity is a cell
1662 // Fetch refinement information
1663 const CellChildInfo &parent_child_info{cell_child_info[parent_idx]};
1664 const RefPat parent_ref_pat{parent_child_info.ref_pat_};
1665 const sub_idx_t anchor{parent_child_info.anchor_};
1666 // Obtain geometry of child
1667 const Hybrid2DRefinementPattern rp(parent_ref_el, parent_ref_pat,
1668 anchor);
1669 const std::vector<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic>>
1670 child_polygon_vec{rp.ChildPolygons(child_rel_codim)};
1671 LF_ASSERT_MSG(child_number < child_polygon_vec.size(),
1672 "Child number = " << child_number << " >= "
1673 << child_polygon_vec.size());
1674 const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic>
1675 &child_polygon{child_polygon_vec[child_number]};
1676 LF_ASSERT_MSG(child_polygon.rows() == 2,
1677 "Need two coordinates in parent cell");
1678 // Matrix containing the relative reference coordinates of the
1679 // child's corners in its columns
1680 const Eigen::MatrixXd child_corners =
1681 child_polygon.cast<double>() /
1682 static_cast<double>(rp.LatticeConst());
1683 switch (codim) {
1684 case 0: { // cell in cell
1685 LF_ASSERT_MSG(parent_child_info.child_cell_idx.at(child_number) ==
1686 child_idx,
1687 "parent_child_info.child_cell_idx.at("
1688 << child_number << " ) !== child_idx");
1689 switch (child_corners.cols()) {
1690 case 3: { // Child is a triangle
1691 LF_ASSERT_MSG(
1692 child_entity->RefEl() == lf::base::RefEl::kTria(),
1693 "Must be triangle!");
1694 SPDLOG_LOGGER_TRACE(Logger(), "Triangle in {}: geo = {}",
1695 parent_ref_el, child_corners);
1696
1697 child_pi.rel_ref_geo_ =
1698 std::make_unique<lf::geometry::TriaO1>(child_corners);
1699 break;
1700 }
1701 case 4: { // Child is a quadrilateral
1702 LF_ASSERT_MSG(
1703 child_entity->RefEl() == lf::base::RefEl::kQuad(),
1704 "Must be quad!");
1705 SPDLOG_LOGGER_TRACE(Logger(), "Quad in {}: geo = {}",
1706 parent_ref_el, child_corners);
1707
1708 child_pi.rel_ref_geo_ =
1709 std::make_unique<lf::geometry::QuadO1>(child_corners);
1710 break;
1711 }
1712 default: {
1713 LF_ASSERT_MSG(false, "Illegal number "
1714 << child_corners.cols()
1715 << " of corners for a child cell");
1716 break;
1717 }
1718 }
1719 break;
1720 }
1721 case 1: { // segment in cell, child_rel_codim = 1
1722 LF_ASSERT_MSG(parent_child_info.child_edge_idx.at(child_number) ==
1723 child_idx,
1724 "parent_child_info.child_edge_idx.at("
1725 << child_number << " ) !== child_idx");
1726 LF_ASSERT_MSG(
1727 child_entity->RefEl() == lf::base::RefEl::kSegment(),
1728 "Must be an edge!");
1729 LF_ASSERT_MSG(child_corners.cols() == 2,
1730 "Segement must have two endpoints");
1731 SPDLOG_LOGGER_TRACE(Logger(), "Segment in {}: geo = {}",
1732 parent_ref_el, child_corners);
1733
1734 child_pi.rel_ref_geo_ =
1735 std::make_unique<lf::geometry::SegmentO1>(child_corners);
1736 break;
1737 }
1738 case 2: { // point in cell, child_rel_codim = 2
1739 LF_ASSERT_MSG(parent_child_info.child_point_idx.at(
1740 child_number) == child_idx,
1741 "parent_child_info.child_point_idx.at("
1742 << child_number << " ) !== child_idx");
1743 LF_ASSERT_MSG(child_entity->RefEl() == lf::base::RefEl::kPoint(),
1744 "Must be a point!");
1745 LF_ASSERT_MSG(child_corners.cols() == 1,
1746 "Only a single coordindate for a point!");
1747 SPDLOG_LOGGER_TRACE(Logger(), "Point in {}: geo = {}",
1748 parent_ref_el, child_corners);
1749
1750 child_pi.rel_ref_geo_ =
1751 std::make_unique<lf::geometry::Point>(child_corners);
1752 break;
1753 }
1754 default:
1755 LF_VERIFY_MSG(false, "unexpected codim " << codim);
1756 } // end switch codim
1757 break;
1758 }
1759 case 1: { // the parent entity is an edge
1760 // Fetch refinement information
1761 const EdgeChildInfo &parent_child_info{ed_child_info[parent_idx]};
1762 const RefPat parent_ref_pat{parent_child_info.ref_pat_};
1763 const sub_idx_t anchor{idx_nil};
1764 // Obtain geometry of child
1765 const Hybrid2DRefinementPattern rp(parent_ref_el, parent_ref_pat,
1766 anchor);
1767 const std::vector<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic>>
1768 child_polygon_vec{rp.ChildPolygons(child_rel_codim)};
1769 LF_ASSERT_MSG(child_number < child_polygon_vec.size(),
1770 "Child number = " << child_number << " >= "
1771 << child_polygon_vec.size());
1772 const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic>
1773 &child_polygon{child_polygon_vec[child_number]};
1774 LF_ASSERT_MSG(child_polygon.rows() == 1,
1775 "Need one coordinate only in parent segment");
1776 // Row vector containing the relative reference coordinates of the
1777 // child's corners
1778 const Eigen::MatrixXd child_corners =
1779 child_polygon.cast<double>() /
1780 static_cast<double>(rp.LatticeConst());
1781 switch (codim) {
1782 case 1: { // edge in edge, child_rel_codim = 0
1783 LF_ASSERT_MSG(parent_child_info.child_edge_idx.at(child_number) ==
1784 child_idx,
1785 "edge_child_info.child_edge_idx.at("
1786 << child_number << " ) !== child_idx");
1787 LF_ASSERT_MSG(
1788 child_entity->RefEl() == lf::base::RefEl::kSegment(),
1789 "Must be an edge!");
1790 LF_ASSERT_MSG(child_corners.cols() == 2,
1791 "Segement must have two endpoints");
1792 SPDLOG_LOGGER_TRACE(Logger(), "Segment in {}: geo = {}",
1793 parent_ref_el, child_corners);
1794
1795 child_pi.rel_ref_geo_ =
1796 std::make_unique<lf::geometry::SegmentO1>(child_corners);
1797 break;
1798 }
1799 case 2: { // point in edge
1800 LF_ASSERT_MSG(parent_child_info.child_point_idx.at(
1801 child_number) == child_idx,
1802 "edge_child_info.child_point_idx.at("
1803 << child_number << " ) !== child_idx");
1804 LF_ASSERT_MSG(child_entity->RefEl() == lf::base::RefEl::kPoint(),
1805 "Must be a point!");
1806 LF_ASSERT_MSG(child_corners.cols() == 1,
1807 "Only a single coordindate for a point!");
1808 SPDLOG_LOGGER_TRACE(Logger(), "Point in {}: geo = {}",
1809 parent_ref_el, child_corners);
1810
1811 child_pi.rel_ref_geo_ =
1812 std::make_unique<lf::geometry::Point>(child_corners);
1813 break;
1814 }
1815 default: {
1816 LF_VERIFY_MSG(
1817 false, "Child of an edge can only be a point or a segment");
1818 break;
1819 }
1820 } // end switch codim
1821 break;
1822 }
1823 case 2: { // the parent entity is a point
1824 // No relative geometry for a point
1825 SPDLOG_LOGGER_TRACE(Logger(), "point in {}", parent_ref_el);
1826 child_pi.rel_ref_geo_ =
1827 std::make_unique<lf::geometry::Point>(nil_coords);
1828 break;
1829 }
1830 default: {
1831 LF_VERIFY_MSG(false, "Illegal parent co-dimension" << parent_codim);
1832 break;
1833 }
1834 } // end switch(parent_codim)
1835 } // end loop over entities of the fine mesh
1836 } // end loop over codims
1837} // end initGeometryInParent
1838
1839sub_idx_t MeshHierarchy::LongestEdge(const lf::mesh::Entity &T) {
1840 LF_VERIFY_MSG(T.Codim() == 0, "Entity must be a call");
1841 // Obtain iterator over the edges
1842 const size_type num_edges = T.RefEl().NumSubEntities(1);
1843 auto sub_edges = T.SubEntities(1);
1844 double max_len = 0.0;
1845 sub_idx_t idx_longest_edge = 0;
1846 Eigen::MatrixXd mp_refc(1, 1);
1847 mp_refc(0, 0) = 0.5;
1848 for (int k = 0; k < num_edges; k++) {
1849 // Approximate length by "1-point quadrature"
1850 const double approx_length =
1851 (sub_edges[k]->Geometry()->IntegrationElement(mp_refc))[0];
1852 if (max_len < approx_length) {
1853 idx_longest_edge = k;
1854 max_len = approx_length;
1855 }
1856 }
1857 return idx_longest_edge;
1858}
1859
1860const lf::geometry::Geometry *MeshHierarchy::GeometryInParent(
1861 size_type level, const lf::mesh::Entity &e) const {
1862 LF_ASSERT_MSG(level > 0, "level must be that of a child mesh!");
1863 // Obtain reference to fine mesh
1864 const lf::mesh::Mesh &mesh{*getMesh(level)};
1865 // Get index of entity in fine mesh
1866 const lf::base::glb_idx_t idx_e{mesh.Index(e)};
1867 // Access information on parent
1868 const std::vector<ParentInfo> &parent_infos{ParentInfos(level, e.Codim())};
1869 // Fetch ParentInfo structure for entity e
1870 const ParentInfo &e_parent_info{parent_infos[idx_e]};
1871 // Just return the information contained in the relevant ParentInfo
1872 // structure
1873 LF_VERIFY_MSG(e_parent_info.rel_ref_geo_ != nullptr,
1874 "No valid parent information for " << e);
1875 return e_parent_info.rel_ref_geo_.get();
1876}
1877
1878const lf::mesh::Entity *MeshHierarchy::ParentEntity(
1879 size_type level, const lf::mesh::Entity &e) const {
1880 LF_ASSERT_MSG(level > 0, "level > 0 required!");
1881 // Obtain reference to fine mesh
1882 const lf::mesh::Mesh &mesh{*getMesh(level)};
1883 // Get index of entity in fine mesh
1884 const lf::base::glb_idx_t idx_e{mesh.Index(e)};
1885 // Access information about parent entity on next coarser mesh
1886 const std::vector<ParentInfo> &parent_infos{ParentInfos(level, e.Codim())};
1887 // Fetch ParentInfo structure for entity e
1888 const ParentInfo &e_parent_info{parent_infos[idx_e]};
1889 // Just return the information contained in the relevant ParentInfo
1890 // structure
1891 LF_VERIFY_MSG(e_parent_info.parent_ptr != nullptr,
1892 "No valid parent information for " << e);
1893 return e_parent_info.parent_ptr;
1894}
1895
1896std::ostream &MeshHierarchy::PrintInfo(std::ostream &o, unsigned ctrl) const {
1897 o << "MeshHierarchy, " << NumLevels() << " levels: " << '\n';
1898 for (unsigned level = 0; level < NumLevels(); ++level) {
1899 const lf::mesh::Mesh &mesh{*getMesh(level)};
1900 o << "l=" << level << ": ";
1901 if (ctrl != 0) {
1902 lf::mesh::utils::PrintInfo(o, mesh);
1903 } else {
1904 o << static_cast<int>(mesh.DimMesh()) << "D -> "
1905 << static_cast<int>(mesh.DimWorld()) << "D, " << mesh.NumEntities(0)
1906 << " cells [" << mesh.NumEntities(lf::base::RefEl::kTria()) << " tria, "
1907 << mesh.NumEntities(lf::base::RefEl::kQuad()) << " quad], "
1908 << mesh.NumEntities(1) << " edges, " << mesh.NumEntities(2) << " nodes"
1909 << '\n';
1910 }
1911 }
1912 return o;
1913}
1914
1915// Utility function for generating a hierarchy of meshes
1916/* SAM_LISTING_BEGIN_1 */
1917std::shared_ptr<MeshHierarchy> GenerateMeshHierarchyByUniformRefinemnt(
1918 const std::shared_ptr<lf::mesh::Mesh> &mesh_p, lf::base::size_type ref_lev,
1919 RefPat ref_pat) {
1920 LF_ASSERT_MSG(mesh_p != nullptr, "No valid mesh supplied!");
1921 // Set up the builder object for mesh entities, here suitable for a 2D
1922 // hybrid mesh comprising triangles and quadrilaterals
1923 std::unique_ptr<lf::mesh::hybrid2d::MeshFactory> mesh_factory_ptr =
1924 std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
1925 // Create a mesh hierarchy with a single level
1926 std::shared_ptr<MeshHierarchy> multi_mesh_p =
1927 std::make_shared<MeshHierarchy>(mesh_p, std::move(mesh_factory_ptr));
1928 // Perform the desired number of steps of uniform refinement
1929 for (unsigned refstep = 0; refstep < ref_lev; ++refstep) {
1930 // Conduct regular refinement of all cells of the currently finest mesh.
1931 // This adds another mesh to the sequence of meshes.
1932 multi_mesh_p->RefineRegular(ref_pat);
1933 }
1934 return multi_mesh_p;
1935}
1936/* SAM_LISTING_END_1 */
1937
1938} // namespace lf::refinement
Represents a reference element with all its properties.
Definition ref_el.h:109
static constexpr RefEl kSegment()
Returns the (1-dimensional) reference segment.
Definition ref_el.h:153
static constexpr RefEl kPoint()
Returns the (0-dimensional) reference point.
Definition ref_el.h:144
static constexpr RefEl kTria()
Returns the reference triangle.
Definition ref_el.h:161
static constexpr RefEl kQuad()
Returns the reference quadrilateral.
Definition ref_el.h:169
Interface class for shape information on a mesh cell in the spirit of parametric finite element metho...
virtual std::vector< std::unique_ptr< Geometry > > ChildGeometry(const RefinementPattern &ref_pat, lf::base::dim_t codim) const =0
Generate geometry objects for child entities created in the course of refinement.
Interface class representing a topological entity in a cellular complex
Definition entity.h:42
Abstract interface for objects representing a single mesh.
virtual std::span< const Entity *const > Entities(unsigned codim) const =0
All entities of a given codimension.
virtual size_type NumEntities(unsigned codim) const =0
The number of Entities that have the given codimension.
virtual size_type Index(const Entity &e) const =0
Acess to the index of a mesh entity of any co-dimension.
Class containing information about the refinement of a cell.
A hierarchy of nested 2D hybrid meshes created by refinement.
MeshHierarchy(const std::shared_ptr< mesh::Mesh > &base_mesh, std::unique_ptr< mesh::MeshFactory > mesh_factory)
Initialize mesh hierarchy with an existing coarsest mesh.
std::unique_ptr< mesh::MeshFactory > mesh_factory_
The mesh factory to be used to creating a new mesh.
void RefineRegular(RefPat ref_pat=RefPat::rp_regular)
Perform regular or barycentric uniform refinement of the finest mesh in the hierarchy.
void RefineMarked()
Conduct local refinement of the mesh splitting all marked edges.
std::vector< std::shared_ptr< mesh::Mesh > > meshes_
the meshes managed by the MeshHierarchy object
std::vector< std::vector< sub_idx_t > > refinement_edges_
Information about local refinement edges of triangles.
std::vector< std::vector< CellChildInfo > > cell_child_infos_
information about children of cells for each level
std::vector< std::array< std::vector< ParentInfo >, 3 > > parent_infos_
information about parent entities on each level
std::vector< std::vector< bool > > edge_marked_
Information about marked edges.
std::vector< std::vector< PointChildInfo > > point_child_infos_
information about children of nodes for each level
static std::shared_ptr< spdlog::logger > & Logger()
Is used by MeshHierarchy to log additional information for debugging purposes.
void PerformRefinement()
Create new mesh according to refinement pattern provided for entities.
std::vector< std::vector< EdgeChildInfo > > edge_child_infos_
information about children of edges for each level
void initGeometryInParent()
Initialization of rel_ref_geo fields of ParentInfo structures.
static sub_idx_t LongestEdge(const lf::mesh::Entity &T)
Finds the index of the longest edge of a triangle.
unsigned int size_type
general type for variables related to size of arrays
Definition types.h:20
unsigned int sub_idx_t
type for local indices of sub-entities
Definition types.h:28
unsigned int glb_idx_t
type for global index of mesh entities (nodes, edges, cells)
Definition types.h:24
std::shared_ptr< spdlog::logger > InitLogger(const std::string &name)
Create a spdlog logger, register it in the spdlog registry and initialize it with LehrFEM++ specific ...
const unsigned int idx_nil
Definition mesh.h:28
tools for regular or local refinement of 2D hybrid meshes
bool checkValidIndex(const std::vector< glb_idx_t > &idx_vec)
const unsigned int idx_nil
RefPat
(possibly incomplete) list of refinement patterns for triangles/quadrilaterals
Information about the refinement status of a cell.
Information about the refinement status of an edge.
std::vector< glb_idx_t > child_edge_idx
global indices of child edges in fine mesh
std::vector< glb_idx_t > child_point_idx
global indices of interior child points in fine mesh
RefPat ref_pat_
type of refinement edge has undergone, see RefPat
Information about possible parent entities.
Information about the refinement status of a point.
RefPat ref_pat
a flag indicating whether the point is to be duplicated (rp_copy)