LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
sphere_triag_mesh_builder.cc
1#include "sphere_triag_mesh_builder.h"
2
3#include <lf/base/base.h>
4#include <lf/base/span.h>
5#include <lf/geometry/geometry.h>
6
7#include <array>
8#include <cmath>
9#include <vector>
10
12
13std::shared_ptr<lf::mesh::Mesh> SphereTriagMeshBuilder::Build() {
14 const double r = radius_;
16
17 // the order in which we create mesh elements will be top down
18 // in rings, of which the number is computed based on the
19 // refinement_level_ = l
20 const lf::base::size_type ring_count = std::pow(2, l + 1) + 1;
21
22 // number of overall vertices
23 const lf::base::size_type vertex_count =
24 2 + 4 * (ring_count / 2) +
25 4 * ((ring_count - 2) / 2 + 1) * ((ring_count - 2) / 2);
26
27 // difference in angle between the rings (could as well be formulated as z
28 // coordinate)
29 const double d_theta = M_PI / (ring_count - 1);
30
31 // cache that stores the mappings form the global vertex indices to the local
32 // ones on the rings including the corresponding ring The vector will be
33 // filled lazy, hence USE local_vert_idx() intstead of accessing the vector
34 // directly
35 std::vector<std::pair<lf::base::size_type, lf::base::size_type>>
36 vertex_loc_glob_map_vector_cache(vertex_count, std::make_pair(0, 0));
37
38 // returns the ring index with respect to the middle.
39 // This means the first and the last ring will have index 0 and the second and
40 // second last index 1 ...
41 // requires 0 <= `r_idx` < `ring_count`
42 auto r_tilde = [&](lf::base::size_type r_idx) -> lf::base::size_type {
43 LF_ASSERT_MSG(0 <= r_idx && r_idx < ring_count, "ring index out of range");
44 if (r_idx <= (ring_count / 2)) {
45 return r_idx;
46 } else {
47 return ring_count - r_idx - 1;
48 }
49 };
50
51 // returns the number of vertices on a ring
52 // requires 0 <= `r_idx` < `ring_count`
53 auto vert_count_on_ring =
55 LF_ASSERT_MSG(0 <= r_idx && r_idx < ring_count, "ring index out of range");
56 if (r_tilde(r_idx) == 0) {
57 return 1;
58 } else {
59 return r_tilde(r_idx) * 4;
60 }
61 };
62
63 // returns the ring index on which the vertex is located as well as
64 // the local vertex index on that ring
65 //
66 // Internally computes the values only on the first access and then stores
67 // them in a vector for later calls of the function
68 //
69 // requires 0 <= `r_idx` < `ring_count`
70 auto local_vert_idx = [&](lf::base::size_type v_idx)
71 -> std::pair<lf::base::size_type, lf::base::size_type> {
72 LF_ASSERT_MSG(0 <= v_idx && v_idx < vertex_count,
73 "vertex index out of range");
74
75 std::pair<lf::base::size_type, lf::base::size_type> zero_pair =
76 std::make_pair(0, 0);
77
78 if (v_idx == 0) {
79 return zero_pair;
80 }
81
82 if (vertex_loc_glob_map_vector_cache[v_idx] == zero_pair) {
83 // subtract the one because it is not the first index
84 lf::base::size_type v_idx_rest = v_idx - 1;
85 lf::base::size_type r_idx = 1;
86 while (vert_count_on_ring(r_idx) <= v_idx_rest) {
87 v_idx_rest -= vert_count_on_ring(r_idx);
88 r_idx++;
89 }
90
91 // fill the vector
92 vertex_loc_glob_map_vector_cache[v_idx] =
93 std::make_pair(r_idx, v_idx_rest);
94 }
95
96 return vertex_loc_glob_map_vector_cache[v_idx];
97 };
98
99 // returns the global index of a vertex given the ring_index on which the
100 // vertex is located and the local vertex index on the ring
101 //
102 // requires 0 <= `r_idx` < `ring_count`
103 // requires 0 <= `v_local_idx` < `vert_count_on_ring(r_idx)`
104 auto global_vert_idx =
105 [&](lf::base::size_type r_idx,
107 LF_ASSERT_MSG(0 <= r_idx && r_idx < ring_count, "ring index out of range");
108 LF_ASSERT_MSG(0 <= v_local_idx && v_local_idx < vert_count_on_ring(r_idx),
109 "vertex index out of range");
110
111 lf::base::size_type v_idx = 0;
112 for (lf::base::size_type r = 0; r < r_idx; ++r) {
113 v_idx += vert_count_on_ring(r);
114 }
115 v_idx += v_local_idx;
116 return v_idx;
117 };
118
119 // returns the position of a vertex in the space $\mathbb{R}^3$, given the
120 // ring index and the vertex index on that ring
121 //
122 // requires 0 <= `r_idx` < `ring_count`
123 // requires 0 <= `v_idx` < `vert_count_on_ring(r_idx)`
124 auto node_position = [&](lf::base::size_type r_idx,
125 lf::base::size_type v_idx) -> Eigen::Vector3d {
126 LF_ASSERT_MSG(0 <= r_idx && r_idx < ring_count, "ring index out of range");
127 LF_ASSERT_MSG(0 <= v_idx && v_idx < vert_count_on_ring(r_idx),
128 "vertex index out of range");
129 const double theta = r_idx * d_theta;
130 const double d_phi = 2.0 * M_PI / vert_count_on_ring(r_idx);
131 const double phi = v_idx * d_phi;
132 Eigen::Vector3d pos;
133 pos << r * std::cos(phi) * std::sin(theta),
134 r * std::sin(phi) * std::sin(theta), r * std::cos(theta);
135 return pos;
136 };
137
138 // returns the position of a vertex in the space $\mathbb{R}^3$, given the
139 // global index of the vertex
140 //
141 // requires 0 <= `v_idx` < `vertex_count`
142 auto global_node_position =
143 [&](lf::base::size_type v_idx) -> Eigen::Vector3d {
144 LF_ASSERT_MSG(0 <= v_idx && v_idx < vertex_count,
145 "vertex index out of range");
147 lf::base::size_type v_local_idx;
148 std::tie(r_idx, v_local_idx) = local_vert_idx(v_idx);
149 return node_position(r_idx, v_local_idx);
150 };
151
152 // returns the index \in \{0,1,2,3\} which quarter a given local index
153 // is to be found.
154 //
155 // requires be 0 < `r_idx` < `ring_count`
156 // requires 0 <= `v_idx` < `vert_count_on_ring(r_idx)`
157 auto get_quarter = [&](lf::base::size_type r_idx,
159 LF_ASSERT_MSG(0 < r_idx && r_idx < ring_count, "ring index out of range");
160 LF_ASSERT_MSG(0 <= v_idx && v_idx < vert_count_on_ring(r_idx),
161 "vertex index out of range");
162 lf::base::size_type num_v_per_quarter = vert_count_on_ring(r_idx) / 4;
163 return v_idx / num_v_per_quarter;
164 };
165
166 // Add all vertices to the mesh
167 for (lf::base::size_type i = 0; i < vertex_count; ++i) {
169 lf::base::size_type v_local_idx;
170 std::tie(r_idx, v_local_idx) = local_vert_idx(i);
171 mesh_factory_->AddPoint(node_position(r_idx, v_local_idx));
172 }
173
174 // Construct the triangles
175 // two for every vertex except the ones the first and last ring
177 for (lf::base::size_type i = 1; i < vertex_count - 1; ++i) {
179 lf::base::size_type v_local_idx;
180 std::tie(r_idx, v_local_idx) = local_vert_idx(i);
181 const int quarter = get_quarter(r_idx, v_local_idx);
182
186
187 if (vert_count_on_ring(r_idx) > vert_count_on_ring(r_idx - 1)) {
188 // construct upper triangle for upper half of the sphere
189 v1 = i;
190 v2 =
191 global_vert_idx(r_idx, (v_local_idx + 1) % vert_count_on_ring(r_idx));
192 v3 = global_vert_idx(
193 r_idx - 1, (v_local_idx - quarter) % vert_count_on_ring(r_idx - 1));
194
195 } else {
196 // construct upper triangle for lower half of the sphere
197 v1 = i;
198 v2 =
199 global_vert_idx(r_idx, (v_local_idx + 1) % vert_count_on_ring(r_idx));
200 v3 = global_vert_idx(r_idx - 1, v_local_idx + 1 + quarter);
201 }
202
203 const std::array<lf::base::size_type, 3> upper_trig = {v1, v2, v3};
204
205 Eigen::Matrix<double, 3, 3> upper_verts;
206 upper_verts << node_position(r_idx, v_local_idx), global_node_position(v2),
207 global_node_position(v3);
208
209 std::unique_ptr<lf::geometry::Geometry> upper_geom =
210 std::make_unique<lf::geometry::TriaO1>(upper_verts);
211 mesh_factory_->AddEntity(ref_el, nonstd::span(upper_trig.data(), 3),
212 std::move(upper_geom));
213
214 if (vert_count_on_ring(r_idx) > vert_count_on_ring(r_idx + 1)) {
215 // construct lower triangle for upper half of the sphere
216 v1 = i;
217 v2 = global_vert_idx(
218 r_idx + 1, (v_local_idx - quarter) % vert_count_on_ring(r_idx + 1));
219 v3 =
220 global_vert_idx(r_idx, (v_local_idx + 1) % vert_count_on_ring(r_idx));
221 } else {
222 // construct lower triangle for lower half of the sphere
223 v1 = i;
224 v2 = global_vert_idx(r_idx + 1, v_local_idx + 1 + quarter);
225 v3 =
226 global_vert_idx(r_idx, (v_local_idx + 1) % vert_count_on_ring(r_idx));
227 }
228
229 const std::array<lf::base::size_type, 3> lower_trig = {v1, v2, v3};
230
231 Eigen::Matrix<double, 3, 3> lower_verts;
232 lower_verts << node_position(r_idx, v_local_idx), global_node_position(v2),
233 global_node_position(v3);
234
235 std::unique_ptr<lf::geometry::Geometry> lower_geom =
236 std::make_unique<lf::geometry::TriaO1>(lower_verts);
237 mesh_factory_->AddEntity(ref_el, nonstd::span(lower_trig.data(), 3),
238 std::move(lower_geom));
239 }
240
241 return mesh_factory_->Build();
242}
243
244} // namespace projects::hldo_sphere::mesh
Represents a reference element with all its properties.
Definition: ref_el.h:106
static constexpr RefEl kTria()
Returns the reference triangle.
Definition: ref_el.h:158
std::unique_ptr< lf::mesh::MeshFactory > mesh_factory_
std::shared_ptr< lf::mesh::Mesh > Build()
Build the mesh.
unsigned int size_type
general type for variables related to size of arrays
Definition: base.h:24
Contains a generator for triangluations of the surface of the 3-sphere.