LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
mesh.cc
1
9#include "mesh.h"
10
11#include <fmt/ranges.h>
12#include <spdlog/spdlog.h>
13
14#include <iostream>
15#include <map>
16#include <numeric>
17
18namespace lf::mesh::hybrid2d {
19
21 LF_ASSERT_MSG(codim >= 0, "codim negative.");
22 LF_ASSERT_MSG(codim <= dim_world_, "codim > dimWorld.");
23
24 return entity_pointers_[codim];
25
26 // auto l = [&](auto i) -> const mesh::Entity & { return **i; };
27 // switch (codim) {
28 // case 0: {
29 // return {base::make_DereferenceLambdaRandomAccessIterator(
30 // entity_pointers_[0].begin(), l),
31 // base::make_DereferenceLambdaRandomAccessIterator(
32 // entity_pointers_[0].end(), l)};
33 // }
34 // case 1:
35 // //return {segments_.begin(), segments_.end()};
36 // case 2:
37 // return {points_.begin(), points_.end()};
38 // default: {
39 // LF_VERIFY_MSG(false, "Something is horribly wrong, codim = " +
40 // std::to_string(codim) + " is out of bounds.");
41 // return {
42 // base::ForwardIterator<const Entity>(static_cast<Entity
43 // *>(nullptr)), base::ForwardIterator<const
44 // Entity>(static_cast<Entity *>(nullptr))};
45 // }
46 // }
47}
48
49Mesh::size_type Mesh::NumEntities(unsigned codim) const {
50 switch (codim) {
51 case 0:
52 return trias_.size() + quads_.size();
53 case 1:
54 return segments_.size();
55 case 2:
56 return points_.size();
57 default:
58 LF_VERIFY_MSG(false, "codim out of bounds.");
59 }
60}
61
62Mesh::size_type Mesh::NumEntities(lf::base::RefEl ref_el_type) const {
63 switch (ref_el_type) {
64 case lf::base::RefEl::kPoint(): {
65 return points_.size();
66 }
67 case lf::base::RefEl::kSegment(): {
68 return segments_.size();
69 }
70 case lf::base::RefEl::kTria(): {
71 return trias_.size();
72 }
73 case lf::base::RefEl::kQuad(): {
74 return quads_.size();
75 }
76 default: {
77 LF_ASSERT_MSG(false, "Illegal entity type");
78 break;
79 }
80 }
81 return 0;
82}
83
84Mesh::size_type Mesh::Index(const Entity &e) const {
85 switch (e.Codim()) {
86 case 0: {
87 if (e.RefEl() == lf::base::RefEl::kTria()) {
88 return dynamic_cast<const Triangle &>(e).index();
89 }
90 if (e.RefEl() == lf::base::RefEl::kQuad()) {
91 return dynamic_cast<const Quadrilateral &>(e).index();
92 }
93 LF_VERIFY_MSG(false, "Illegal cell type");
94 }
95 case 1:
96 return dynamic_cast<const Segment &>(e).index();
97 case 2:
98 return dynamic_cast<const Point &>(e).index();
99 default:
100 LF_VERIFY_MSG(false,
101 "Something is horribyl wrong, this entity has codim = " +
102 std::to_string(e.Codim()));
103 }
104}
105
106const Entity *Mesh::EntityByIndex(dim_t codim, glb_idx_t index) const {
107 LF_ASSERT_MSG(codim <= 2, "Illegal codimension " << codim);
108 LF_ASSERT_MSG(index < NumEntities(codim),
109 "Index " << index << " > " << NumEntities(codim));
110 return entity_pointers_[codim][index];
111}
112
113bool Mesh::Contains(const Entity &e) const {
114 switch (e.Codim()) {
115 case 0:
116 return (!trias_.empty() && &e >= &trias_.front() &&
117 &e <= &trias_.back()) ||
118 (!quads_.empty() && &e >= &quads_.front() && &e <= &quads_.back());
119 case 1:
120 return &e >= &segments_.front() && &e <= &segments_.back();
121 case 2:
122 return &e >= &points_.front() && &e <= &points_.back();
123 default:
124 return false;
125 }
126}
127
128namespace /*anonymous */ {
130class EndpointIndexPair {
131 public:
132 using size_type = Mesh::size_type;
133 // Constructor
134 EndpointIndexPair(size_type p0, size_type p1) : p0_(p0), p1_(p1) {
135 LF_ASSERT_MSG(p0 != p1, "No loops allowed");
136 if (p1 > p0) {
137 cmp_p0_ = p0;
138 cmp_p1_ = p1;
139 } else {
140 cmp_p0_ = p1;
141 cmp_p1_ = p0;
142 }
143 }
144 // Access operators
145 [[nodiscard]] size_type first_node() const { return p0_; }
146 [[nodiscard]] size_type second_node() const { return p1_; }
147 // The only comparison operator expected from a Map key
148 // Edges are considered equal even if they have the opposite orientation
149 friend bool operator<(const EndpointIndexPair &e1,
150 const EndpointIndexPair &e2) {
151 return ((e1.cmp_p0_ == e2.cmp_p0_) ? (e1.cmp_p1_ < e2.cmp_p1_)
152 : (e1.cmp_p0_ < e2.cmp_p0_));
153 }
154 // Reorienting an edge
155 void flip() { std::swap(p0_, p1_); }
156 // Checking topological equality (taking into account orientation)
157 friend bool coincide(const EndpointIndexPair &e1,
158 const EndpointIndexPair &e2) {
159 return ((e1.p0_ == e2.p0_) && (e1.p1_ == e2.p1_));
160 }
161
162 private:
163 size_type p0_, p1_; // indices of endpoints
164 size_type cmp_p0_, cmp_p1_;
165};
166} // namespace
167
168// **********************************************************************
169// Construction of a 2D hybrid mesh
170//
171// Data types for arguments
172// GeometryPtr = std::unique_ptr<geometry::Geometry>;
173// NodeCoordList = std::vector<GeometryPtr>;
174// EdgeList = std::vector<std::pair<std::array<size_type, 2>, GeometryPtr>>;
175// CellList = std::vector<std::pair<std::array<size_type, 4>, GeometryPtr>>;
176// **********************************************************************
177Mesh::Mesh(dim_t dim_world, NodeCoordList nodes, EdgeList edges, CellList cells,
178 bool check_completeness)
179 : dim_world_(dim_world) {
180 // Auxiliary data type for gathering information about cells adjacent to an
181 // edge
182 struct AdjCellInfo {
183 AdjCellInfo(size_type _cell_idx, size_type _edge_idx)
184 : cell_idx(_cell_idx), edge_idx(_edge_idx) {}
185 size_type cell_idx; // index of adjacent cell
186 size_type edge_idx; // local index of edge in adjacent cell
187 };
188 // Information about cells adjacent to an edge
189 using AdjCellsList = std::vector<AdjCellInfo>;
190 // Information about edge in auxiliary array
191 struct EdgeData {
192 EdgeData(GeometryPtr geo_uptr, AdjCellsList _adj_cells_list)
193 : geo_uptr(std::move(geo_uptr)),
194 adj_cells_list(std::move(_adj_cells_list)),
195 edge_global_index(-1),
196 reversed(false) {}
197 EdgeData(GeometryPtr geo_uptr, AdjCellsList _adj_cells_list,
198 glb_idx_t global_index)
199 : geo_uptr(std::move(geo_uptr)),
200 adj_cells_list(std::move(_adj_cells_list)),
201 edge_global_index(global_index),
202 reversed(false) {}
203 EdgeData(const EdgeData &) = delete;
204 EdgeData(EdgeData &&) = default;
205 EdgeData &operator=(const EdgeData &) = delete;
206 EdgeData &operator=(EdgeData &&) = default;
207 ~EdgeData() = default;
208 // temporary storage for unique pointer to edge geometry, if provided
209 GeometryPtr geo_uptr;
210 // Information about neighbors
211 AdjCellsList adj_cells_list;
212 // Index of the edge
213 glb_idx_t edge_global_index;
214 // Flag indicating reversed orientation
215 bool reversed;
216 };
217
218 // Type of associative auxiliary array for edge information
219 using EdgeMap = std::map<EndpointIndexPair, EdgeData>;
220
221 // For extracting point coordinates
222 const Eigen::MatrixXd zero_point = base::RefEl::kPoint().NodeCoords();
223
224 // ASSUMPTION: The length of the nodes vector gives the number of nodes
225
226 const size_type no_of_nodes(nodes.size());
227 SPDLOG_LOGGER_DEBUG(Logger(), "Constructing mesh: {} nodes", no_of_nodes);
228
229 // ======================================================================
230 // STEP I: Initialize array of edges using pointers to
231 // entries of the array of nodes
232
233 // Register supplied edges in auxiliary map data structure
234 SPDLOG_LOGGER_DEBUG(Logger(), "Initializing edge map");
235
236 EdgeMap edge_map;
237 glb_idx_t edge_index = 0; // position in the array gives index of edge
238
239 for (auto &e : edges) {
240 // Node indices of endpoints: the KEY
241 std::array<size_type, 2> end_nodes(e.first);
242 EndpointIndexPair e_endpoint_idx(end_nodes[0], end_nodes[1]);
243 LF_ASSERT_MSG(
244 (end_nodes[0] < no_of_nodes) && (end_nodes[1] < no_of_nodes),
245 "Illegal edge node numbers " << end_nodes[0] << ", " << end_nodes[1]);
246 SPDLOG_LOGGER_TRACE(Logger(), "Register edge: {} <-> {}", end_nodes[0],
247 end_nodes[1]);
248
249 // If one of the endpoints of a edge does not have a geometry, supply it
250 // with one inherited from the edge.
251 for (int j = 0; j < 2; ++j) {
252 if (nodes[end_nodes[j]] == nullptr) {
253 // if no geometry for node exists request geomtry for an endpoint of the
254 // edge Note: endpoints are entities of relative co-dimension 1
255 nodes[end_nodes[j]] = e.second->SubGeometry(1, j);
256 }
257 }
258
259 // Store provided geometry information; information on adjacent cells
260 // not yet available.
261 LF_ASSERT_MSG(e.second != nullptr,
262 "Edge " << edge_index << ": missing geometry!");
263 AdjCellsList empty_cells_list{};
264 EdgeData edge_data(std::move(e.second), empty_cells_list, edge_index);
265 EdgeMap::value_type edge_info =
266 std::make_pair(e_endpoint_idx, std::move(edge_data));
267 std::pair<EdgeMap::iterator, bool> insert_status =
268 edge_map.insert(std::move(edge_info));
269 LF_ASSERT_MSG(insert_status.second,
270 "Duplicate edge " << end_nodes[0] << " <-> " << end_nodes[1]);
271
272 edge_index++;
273 } // end loop over predefined edges
274 // ======================================================================
275
276 // ======================================================================
277 // Step II: Building edge map from cell information
278 //
279 // At this point all predefined edges have been stored in the auxiliary
280 // associative array, though without information about adjacent cells.
281 // The variable edge_index contains the number of edges with externally
282 // supplied geometry. The indexing of all extra edges created below must start
283 // from this offset.
284
285 if (Logger()->should_log(spdlog::level::trace)) {
286 std::stringstream ss;
287 ss << "Edge map after edge registration" << std::endl;
288 for (auto &edge_info : edge_map) {
289 const EndpointIndexPair &eip(edge_info.first);
290 const EdgeData &edat(edge_info.second);
291 ss << "Edge " << eip.first_node() << " <-> " << eip.second_node() << ": ";
292 const AdjCellsList &acl(edat.adj_cells_list);
293 const GeometryPtr &gptr(edat.geo_uptr);
294 for (const auto &i : acl) {
295 ss << "[" << i.cell_idx << "," << i.edge_idx << "] ";
296 }
297 SPDLOG_LOGGER_TRACE(Logger(), ss.str());
298 }
299 }
300
301 // Run through cells in order to
302 // (i) build edges missing in the list of predefined edges
303 // (ii) determine cells adjacent to edges
304 size_type cell_index = 0;
305 size_type no_of_trilaterals = 0;
306 size_type no_of_quadrilaterals = 0;
307 // Diagnostics
308 SPDLOG_LOGGER_DEBUG(Logger(), "Scanning list of cells");
309
310 for (const auto &c : cells) {
311 // node indices of corners of cell c
312 const std::array<size_type, 4> &cell_node_list(c.first);
313 // Geometry of current cell
314 const GeometryPtr &cell_geometry(c.second);
315 // Can be either a trilateral or a quadrilateral
316 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
317 // A triangle is marked by an invalid node number
318 // in the last position
319 size_type no_of_vertices;
320 if (cell_node_list[3] == idx_nil) {
321 no_of_vertices = 3; // triangle
322 no_of_trilaterals++;
323 } else {
324 no_of_vertices = 4; // quadrilateral
325 no_of_quadrilaterals++;
326 }
327 // Fix the type of the cell
328 base::RefEl ref_el =
329 (no_of_vertices == 3) ? base::RefEl::kTria() : base::RefEl::kQuad();
330 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
331
332 std::stringstream ss_log_line;
333 if (Logger()->should_log(spdlog::level::trace)) {
334 ss_log_line << "Cell " << cell_index;
335 if (no_of_vertices == 3) {
336 ss_log_line << ", tria " << no_of_trilaterals << ": ";
337 } else {
338 ss_log_line << ", quad " << no_of_quadrilaterals << ": ";
339 }
340 }
341
342 // Verify validity of vertex indices
343 for (unsigned l = 0; l < no_of_vertices; l++) {
344 LF_VERIFY_MSG(cell_node_list[l] < no_of_nodes,
345 "Node " << l << " of cell " << cell_index
346 << ": invalid index " << cell_node_list[l]);
347 }
348
349 // If the cell has a geometry, it can be used to generate the
350 // geometry for its vertices through the SubGeometry() method of Geometry
351 // objects
352 if (cell_geometry != nullptr) {
353 for (unsigned j = 0; j < no_of_vertices; ++j) {
354 if (nodes[cell_node_list[j]] == nullptr) {
355 // if no geometry for node exists request geomtry for an vertex from
356 // the cell Note: vertices are entities of relative co-dimension 2
357 nodes[cell_node_list[j]] = cell_geometry->SubGeometry(2, j);
358 }
359 }
360 }
361
362 // A variant:
363 // There may be cells without a specified geometry.
364 // In case an edge is not equipped with a geometry and not
365 // adjacent to a cell with a specific geometry, assume a straight
366 // edge connecting the two endpoints.
367 //
368
369 // Visit all edges of the current cell
370 for (unsigned int j = 0; j < ref_el.NumSubEntities(1); j++) {
371 // Fetch local indices of endpoints of edge j
372 const size_type p0_local_index =
373 ref_el.SubSubEntity2SubEntity(1, j, 1, 0);
374 const size_type p1_local_index =
375 ref_el.SubSubEntity2SubEntity(1, j, 1, 1);
376 // Fetch global indices of the endnodes of edge j
377 EndpointIndexPair c_edge_vertex_indices(cell_node_list[p0_local_index],
378 cell_node_list[p1_local_index]);
379
380 if (Logger()->should_log(spdlog::level::trace)) {
381 ss_log_line << "e(" << j << ") = local " << p0_local_index << " <-> "
382 << p1_local_index << ", global "
383 << c_edge_vertex_indices.first_node() << " <-> "
384 << c_edge_vertex_indices.second_node() << " # ";
385 }
386 // Store number of cell and the local index j of the edge
387 AdjCellInfo edge_cell_info(cell_index, j);
388 // Check whether edge exists already
389 auto edge_ptr = edge_map.find(c_edge_vertex_indices);
390 if (edge_ptr == edge_map.end()) {
391 // Inherit geometry of edge from adjacent cell
392 GeometryPtr edge_geo_ptr;
393 if (cell_geometry) {
394 edge_geo_ptr = cell_geometry->SubGeometry(1, j);
395 }
396 // Beginning of list of adjacent elements
397 AdjCellsList single_cell_list{edge_cell_info};
398 EdgeData edge_data(std::move(edge_geo_ptr), single_cell_list);
399 std::pair<EdgeMap::iterator, bool> insert_status = edge_map.insert(
400 std::make_pair(c_edge_vertex_indices, std::move(edge_data)));
401 LF_ASSERT_MSG(insert_status.second, "Duplicate not found earlier!");
402 edge_ptr = insert_status.first; // pointer to newly inserted edge
403 } else {
404 // Here *edge_ptr is a valid std::pair<EndpointIndexPair,EdgeData>
405
406 // Store information about neighboring cell
407 AdjCellsList &edge_adj_cellslist((edge_ptr->second).adj_cells_list);
408 edge_adj_cellslist.push_back(edge_cell_info);
409 // Check, if the edge possesses geometry information
410 GeometryPtr &edge_supplied_geo_uptr((*edge_ptr).second.geo_uptr);
411 if (edge_supplied_geo_uptr == nullptr) {
412 // Edge does not know its geometry yet. Try to obtain it from the
413 // cell.
414 if (cell_geometry) {
415 edge_supplied_geo_uptr =
416 std::move(cell_geometry->SubGeometry(1, j));
417 // NOTE: the local orientation of the edge of the cell and that
418 // pointed to by edge_ptr can differ. In this case the
419 // endpoints of the edge have to be swapped. To detect orientation
420 // mismatch, compare c_edge_vertex_indices (cell infomation) and
421 // edge_ptr->first (edge information)
422 if (!coincide(c_edge_vertex_indices, edge_ptr->first)) {
423 // Reverse the direction of the edge
424 (edge_ptr->second).reversed = true;
425 }
426 }
427 }
428 }
429 // At this point edge_ptr points to the map entry for the current edge
430 } // end of loop over edges
431 cell_index++;
432
433 // Diagnostics
434 SPDLOG_LOGGER_TRACE(Logger(), ss_log_line.str());
435 } // end loop over cells
436
437 // DIAGNOSTICS
438 {
439 SPDLOG_LOGGER_DEBUG(Logger(),
440 "=============================================");
441
442 if (Logger()->should_log(spdlog::level::trace)) {
443 SPDLOG_LOGGER_TRACE(Logger(), "Edge map after cell scan");
444
445 size_type edge_cnt = 0;
446 for (auto &edge_info : edge_map) {
447 std::stringstream ss_log_line;
448
449 const EndpointIndexPair &eip(edge_info.first);
450 const EdgeData &edat(edge_info.second);
451 ss_log_line << "Edge " << edge_cnt << ": " << eip.first_node()
452 << " <-> " << eip.second_node();
453 if (edat.edge_global_index == -1) {
454 ss_log_line << " no index : ";
455 } else {
456 ss_log_line << ": index = " << edat.edge_global_index << ": ";
457 }
458 const AdjCellsList &acl(edat.adj_cells_list);
459 const GeometryPtr &gptr(edat.geo_uptr);
460 for (const auto &i : acl) {
461 ss_log_line << "[" << i.cell_idx << "," << i.edge_idx << "] ";
462 }
463 ss_log_line << " geo = " << std::endl;
464 if (gptr) {
465 Eigen::MatrixXd edp_c(
466 gptr->Global(base::RefEl::kSegment().NodeCoords()));
467 ss_log_line << edp_c;
468 } else {
469 ss_log_line << "NO GEOMETRY";
470 }
471 edge_cnt++;
472 SPDLOG_LOGGER_TRACE(Logger(), ss_log_line.str());
473 }
474 SPDLOG_LOGGER_TRACE(Logger(),
475 "=============================================");
476 }
477 }
478
479 // ======================================================================
480 // NEXT STEP : Set up and fill array of nodes: points_
481 // In the beginning initialize vector of vertices and do not touch it anymore
482 // Initialize vector for Node entities of size `no_of_nodes`
483 points_.reserve(no_of_nodes);
484
485 size_type node_index = 0;
486 for (hybrid2d::Mesh::GeometryPtr &pt_geo_ptr : nodes) {
487 // OLD VERSION. In the new version the geomtry of a point is passed in
488 // 'nodes' const Eigen::VectorXd &node_coordinates(v); GeometryPtr point_geo
489 // = std::make_unique<geometry::Point>(node_coordinates);
490 LF_VERIFY_MSG(pt_geo_ptr != nullptr,
491 "Missing geometry for node " << node_index);
492 SPDLOG_LOGGER_TRACE(
493 Logger(), "-> Adding node {} at {}", node_index,
494 (pt_geo_ptr->Global(Eigen::Matrix<double, 0, 1>())).transpose());
495
496 points_.emplace_back(node_index, std::move(pt_geo_ptr));
497 node_index++;
498 }
499
500 // Run through the entire associative container for edges
501 // and build edge Entities
502 // This is the length to be reserved for the edge vector
503 size_type no_of_edges = edge_map.size();
504
505 // Initialized vector of Edge entities here
506 segments_.reserve(no_of_edges);
507
508 // if we check for mesh completeness, initialize a vector that stores for
509 // every node if he has a super entity.
510 std::vector<bool> nodeHasSuperEntity;
511 if (check_completeness) {
512 nodeHasSuperEntity.resize(nodes.size(), false);
513 }
514
515 // Note: the variable edge_index contains the number externally supplied
516 // edges, whose index must agree with their position in the
517 // 'edges' array.
518
519 // Note: iteration variable cannot be declared const, because
520 // moving the geometry pointer changes it!
521 for (EdgeMap::value_type &edge : edge_map) {
522 // Indices of the two endpoints of the current edge
523 // Use this to obtain pointers/references to nodes
524 // from the vector of nodes
525 size_type p0(edge.first.first_node()); // index of first endpoint
526 size_type p1(edge.first.second_node()); // index of second endpoint
527
528 // Swap the indices of the endpoints in case of a geometry mismatch
529 // that requires a reversal of orientation.
530 if (edge.second.reversed) {
531 std::swap(p0, p1);
532 }
533
534 const Point *p0_ptr = &points_[p0]; // pointer to first endpoint
535 const Point *p1_ptr = &points_[p1]; // pointer to second endpoint
536
537 // obtain geometry of the edge
538 GeometryPtr edge_geo_ptr(std::move(edge.second.geo_uptr));
539 if (!edge_geo_ptr) {
540 // If the edge does not have a geometry build a straight edge
541 Eigen::Matrix<double, 2, 2> straight_edge_coords;
542 straight_edge_coords.block<2, 1>(0, 0) =
543 p0_ptr->Geometry()->Global(zero_point);
544 straight_edge_coords.block<2, 1>(0, 1) =
545 p1_ptr->Geometry()->Global(zero_point);
546 edge_geo_ptr =
547 std::make_unique<geometry::SegmentO1>(straight_edge_coords);
548 }
549 // Determine index of current edge. Two cases have to be distinguished:
550 // (i) the edge geometry was specified in the `edges` argument. In this case
551 // the edge index must agree with its possition in that array. This position
553 // (ii) the edge has to be created internally. In this case assign an index
554 // larger than the index of any supplied edge.
555 if (edge.second.edge_global_index == idx_nil) {
556 // Internally created edge needs new index.
557 edge.second.edge_global_index = edge_index;
558 // Increment 'edge_index', which will give the index of the next
559 // internally created edge
560 edge_index++;
561 }
562
563 if (check_completeness) {
564 // record that the nodes of this edge have a super-entity (this edge):
565 nodeHasSuperEntity[p0] = true;
566 nodeHasSuperEntity[p1] = true;
567
568 // make sure that the edge belongs to at least one cell:
569 LF_VERIFY_MSG(!edge.second.adj_cells_list.empty(),
570 "Mesh is incomplete: Edge with global index "
571 << edge.second.edge_global_index
572 << " does not belong to a cell.");
573 }
574
575 // Diagnostics
576 SPDLOG_LOGGER_TRACE(Logger(), "Registering edge {}: {} <-> {}",
577 edge.second.edge_global_index, p0, p1);
578 // Building edge by adding another element to the edge vector.
579 segments_.emplace_back(edge.second.edge_global_index,
580 std::move(edge_geo_ptr), p0_ptr, p1_ptr);
581 } // end loop over all edges
582 LF_ASSERT_MSG(edge_index == no_of_edges, "Edge index mismatch");
583
584 if (check_completeness) {
585 // Check that all nodes have a super entity:
586 for (std::size_t i = 0; i < nodes.size(); ++i) {
587 LF_VERIFY_MSG(nodeHasSuperEntity[i],
588 "Mesh is incomplete: Node with global index "
589 << i << " is not part of any edge.");
590 }
591 }
592 // ======================================================================
593 // NEXT STEP: Create cells
594
595 const size_type no_of_cells = cells.size();
596
597 // Create an auxiliary data structure to store the edges for the cells
598 // For every cell and its edges the global edge index is extracted
599 // from the auxiliary associative array.
600 std::vector<std::array<size_type, 4>> edge_indices(no_of_cells);
601
602 size_type edge_array_position = 0; // actual position in edge array
603 for (const EdgeMap::value_type &edge : edge_map) {
604 // Obtain array of indices of adjacent cells
605 AdjCellsList adjacent_cells(edge.second.adj_cells_list);
606 for (const auto &adj_cell : adjacent_cells) {
607 const size_type adj_cell_index = adj_cell.cell_idx;
608 // Local index of edge in adjacent cell
609 const size_type edge_local_index = adj_cell.edge_idx;
610 LF_ASSERT_MSG(adj_cell_index < no_of_cells, "adj_cell_idx out of bounds");
611 LF_ASSERT_MSG(edge_local_index < cells[adj_cell_index].first.size(),
612 "local edge index out of bounds");
613 // Set edge index in auxiliary cell matrix
614 edge_indices[adj_cell_index][edge_local_index] = edge_array_position;
615 }
616 edge_array_position++;
617 }
618
619 // Diagnostics
620 SPDLOG_LOGGER_TRACE(Logger(), "########################################");
621 SPDLOG_LOGGER_TRACE(Logger(), "Edge array indices for cells ");
622
623 // Now complete information is available for the construction
624 // of cells = entities of co-dimension 0
625 // Initialize two vectors, one for trilaterals of size `no_of_trilaterals`
626 // and a second for quadrilaterals of size `no_of_quadrilaterals`
627 trias_.reserve(no_of_trilaterals);
628 quads_.reserve(no_of_quadrilaterals);
629 // Loop over all cells
630 cell_index = 0;
631 for (auto &c : cells) {
632 // Node indices for the current cell
633 const std::array<size_type, 4> &c_node_indices(c.first);
634 const std::array<size_type, 4> &c_edge_indices(edge_indices[cell_index]);
635 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
636 // A triangle is marked by an invalid node number
637 // in the last position (also see above)
638 size_type no_of_vertices;
639 if (c_node_indices[3] == size_type(-1)) {
640 no_of_vertices = 3; // triangle
641 } else {
642 no_of_vertices = 4; // quadrilateral
643 }
644
645 // Verify validity of node/ede indices
646 for (unsigned l = 0; l < no_of_vertices; l++) {
647 LF_VERIFY_MSG(c_node_indices[l] < no_of_nodes,
648 "Node " << l << " of cell " << cell_index
649 << ": invalid index " << c_node_indices[l]);
650 LF_VERIFY_MSG(c_edge_indices[l] < no_of_edges,
651 "Edge " << l << " of cell " << cell_index
652 << ": invalid index " << c_edge_indices[l]);
653 }
654
655 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
656 GeometryPtr c_geo_ptr(std::move(c.second));
657 if (no_of_vertices == 3) {
658 // Case of a trilateral
659
660 // Diagnostics
661 SPDLOG_LOGGER_TRACE(
662 Logger(), "Triangular cell {}: nodes {}, {}, {}, edges {}, {}, {}",
663 cell_index, c_node_indices[0], c_node_indices[1], c_node_indices[2],
664 c_edge_indices[0], c_edge_indices[1], c_edge_indices[2]);
665
666 /*
667 Add a trilateral entity to the vector of trilaterals
668 Use information in c_node_indices, c_edge_indices to
669 obtain pointers to nodes and edges.
670 index = cell_index
671 */
672 const Point *corner0 = &points_[c_node_indices[0]];
673 const Point *corner1 = &points_[c_node_indices[1]];
674 const Point *corner2 = &points_[c_node_indices[2]];
675 const Segment *edge0 = &segments_[c_edge_indices[0]];
676 const Segment *edge1 = &segments_[c_edge_indices[1]];
677 const Segment *edge2 = &segments_[c_edge_indices[2]];
678 if (!c_geo_ptr) {
679 // Cell is lacking a geometry and its shape has to
680 // be determined from the shape of the edges or
681 // location of the vertices
682 // At this point only the latter policy is implemented
683 // and we build an affine triangle.
684 // First assemble corner coordinates into matrix
685 Eigen::Matrix<double, 2, 3> triag_corner_coords;
686 triag_corner_coords.block<2, 1>(0, 0) =
687 corner0->Geometry()->Global(zero_point);
688 triag_corner_coords.block<2, 1>(0, 1) =
689 corner1->Geometry()->Global(zero_point);
690 triag_corner_coords.block<2, 1>(0, 2) =
691 corner2->Geometry()->Global(zero_point);
692
693 // Diagnostics
694 SPDLOG_LOGGER_TRACE(Logger(), "Creating triangle with geometry \n{}",
695 triag_corner_coords);
696
697 // Then create geometry of an affine triangle
698 c_geo_ptr = std::make_unique<geometry::TriaO1>(triag_corner_coords);
699 // For later:
700 // If blended geometries are available, a cell could also
701 // inherit its geometry from the edges
702 }
703 trias_.emplace_back(cell_index, std::move(c_geo_ptr), corner0, corner1,
704 corner2, edge0, edge1, edge2);
705 } else {
706 // Case of a quadrilateral
707
708 // Diagnostics
709 SPDLOG_LOGGER_TRACE(Logger(), "Quadrilateral cell {}: nodes {}, edges {}",
710 cell_index, c_node_indices, c_edge_indices);
711
712 /*
713 Add a quadrilateral entity to the vector of quadrilaterals
714 Use information in c_node_indices, c_edge_indices to
715 obtain pointers to nodes and edges.
716 index = cell_index
717 */
718 const Point *corner0 = &points_[c_node_indices[0]];
719 const Point *corner1 = &points_[c_node_indices[1]];
720 const Point *corner2 = &points_[c_node_indices[2]];
721 const Point *corner3 = &points_[c_node_indices[3]];
722 const Segment *edge0 = &segments_[c_edge_indices[0]];
723 const Segment *edge1 = &segments_[c_edge_indices[1]];
724 const Segment *edge2 = &segments_[c_edge_indices[2]];
725 const Segment *edge3 = &segments_[c_edge_indices[3]];
726 if (!c_geo_ptr) {
727 // Cell is lacking a geometry and its shape has to
728 // be determined from the shape of the edges or
729 // location of the vertices
730 // At this point we can only build a quadrilateral
731 // with straight edges ("bilinear quadrilateral")
732 // First assemble corner coordinates into matrix
733
734 Eigen::Matrix<double, 2, 4> quad_corner_coords;
735 quad_corner_coords.block<2, 1>(0, 0) =
736 corner0->Geometry()->Global(zero_point);
737 quad_corner_coords.block<2, 1>(0, 1) =
738 corner1->Geometry()->Global(zero_point);
739 quad_corner_coords.block<2, 1>(0, 2) =
740 corner2->Geometry()->Global(zero_point);
741 quad_corner_coords.block<2, 1>(0, 3) =
742 corner3->Geometry()->Global(zero_point);
743 // Then create geometry of an affine triangle
744
745 // Diagnostics
746 SPDLOG_LOGGER_TRACE(Logger(),
747 "Creating quadrilateral with geometry\n{}",
748 quad_corner_coords);
749
750 c_geo_ptr = std::make_unique<geometry::QuadO1>(quad_corner_coords);
751 }
752 quads_.emplace_back(cell_index, std::move(c_geo_ptr), corner0, corner1,
753 corner2, corner3, edge0, edge1, edge2, edge3);
754 }
755 cell_index++;
756 }
757 // ======================================================================
758 // Initialization of auxiliary entity pointer arrays
759
760 // Order the cells according to their indices!
761 // First fill the array with NIL pointers
762 entity_pointers_[0] =
763 std::vector<const mesh::Entity *>(trias_.size() + quads_.size(), nullptr);
764 size_type cell_ptr_cnt = 0;
765
766 // First retrieve pointers to triangular cells
767 for (int j = 0; j < trias_.size(); j++, cell_ptr_cnt++) {
768 // Fetch index of a triangle by a non-virtual call to Index()
769 const glb_idx_t cell_index = lf::mesh::hybrid2d::Mesh::Index(trias_[j]);
770 LF_ASSERT_MSG(cell_index < entity_pointers_[0].size(),
771 "Cell index out of range");
772 // This index must be unique !
773 LF_ASSERT_MSG(entity_pointers_[0][cell_index] == nullptr,
774 "Cell index " << cell_index << " occurs twice!");
775 entity_pointers_[0][cell_index] = &(trias_[j]);
776 } // end loop over triangles
777
778 // Second deal with the quadrilateral cells
779 for (int j = 0; j < quads_.size(); j++, cell_ptr_cnt++) {
780 // Fetch index of a quadrilateral by a non-virtual call to Index()
781 const glb_idx_t cell_index = lf::mesh::hybrid2d::Mesh::Index(quads_[j]);
782 LF_ASSERT_MSG(cell_index < entity_pointers_[0].size(),
783 "Cell index out of range");
784 // This index must be unique !
785 LF_ASSERT_MSG(entity_pointers_[0][cell_index] == nullptr,
786 "Cell index " << cell_index << " occurs twice!");
787 entity_pointers_[0][cell_index] = &(quads_[j]);
788 }
789 LF_VERIFY_MSG(cell_ptr_cnt == entity_pointers_[0].size(),
790 "Size mismatch for cell counters array");
791
792 // Next the edges and nodes
793
794 entity_pointers_[1] =
795 std::vector<const mesh::Entity *>(segments_.size(), nullptr);
796 entity_pointers_[2] =
797 std::vector<const mesh::Entity *>(points_.size(), nullptr);
798
799 // set the entity pointers (note that the entities in segments_ and points_
800 // are not ordered according to their index):
801 for (auto &p : points_) {
802 entity_pointers_[2][p.index()] = &p;
803 }
804 for (auto &s : segments_) {
805 entity_pointers_[1][s.index()] = &s;
806 }
807
808} // namespace lf::mesh::hybrid2d
809
810} // namespace lf::mesh::hybrid2d
Abstract interface for objects representing a single mesh.
Mesh()=default
size_type Index(const Entity &e) const override
Acess to the index of a mesh entity of any co-dimension.
Definition: mesh.cc:84
nonstd::span< const Entity *const > Entities(unsigned codim) const override
All entities of a given codimension.
Definition: mesh.cc:20
An alternative implementation of a hybrid2d mesh manager that uses Pointers to store sub-entity relat...
Definition: hybrid2d.h:13