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