Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 :
4 : // This library is free software; you can redistribute it and/or
5 : // modify it under the terms of the GNU Lesser General Public
6 : // License as published by the Free Software Foundation; either
7 : // version 2.1 of the License, or (at your option) any later version.
8 :
9 : // This library is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : // Lesser General Public License for more details.
13 :
14 : // You should have received a copy of the GNU Lesser General Public
15 : // License along with this library; if not, write to the Free Software
16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 :
18 : #include "libmesh/libmesh_config.h"
19 :
20 : #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
21 :
22 : // Local includes
23 : #include "libmesh/cell_inf_prism.h"
24 : #include "libmesh/cell_inf_prism6.h"
25 : #include "libmesh/face_tri3.h"
26 : #include "libmesh/face_inf_quad4.h"
27 : #include "libmesh/fe_type.h"
28 : #include "libmesh/fe_interface.h"
29 : #include "libmesh/inf_fe_map.h"
30 : #include "libmesh/enum_order.h"
31 :
32 : namespace libMesh
33 : {
34 :
35 :
36 : // ------------------------------------------------------------
37 : // InfPrism class static member initializations
38 : const int InfPrism::num_sides;
39 : const int InfPrism::num_edges;
40 : const int InfPrism::num_children;
41 :
42 : const Real InfPrism::_master_points[12][3] =
43 : {
44 : {0, 0, 0},
45 : {1, 0, 0},
46 : {0, 1, 0},
47 : {0, 0, 1},
48 : {1, 0, 1},
49 : {0, 1, 1},
50 : {.5, 0, 0},
51 : {.5, .5, 0},
52 : {0, .5, 0},
53 : {.5, 0, 1},
54 : {.5, .5, 1},
55 : {0, .5, 1}
56 : };
57 :
58 : const unsigned int InfPrism::edge_sides_map[6][2] =
59 : {
60 : {0, 1}, // Edge 0
61 : {0, 2}, // Edge 1
62 : {0, 3}, // Edge 2
63 : {1, 3}, // Edge 3
64 : {1, 2}, // Edge 4
65 : {2, 3} // Edge 5
66 : };
67 :
68 : const unsigned int InfPrism::adjacent_edges_map[/*num_vertices*/6][/*max_adjacent_edges*/3] =
69 : {
70 : {0, 2, 3}, // Edges adjacent to node 0
71 : {0, 1, 4}, // Edges adjacent to node 1
72 : {1, 2, 5}, // Edges adjacent to node 2
73 : {3, 99, 99}, // Edges adjacent to node 3
74 : {4, 99, 99}, // Edges adjacent to node 4
75 : {5, 99, 99}, // Edges adjacent to node 5
76 : };
77 :
78 : // ------------------------------------------------------------
79 : // InfPrism class member functions
80 0 : dof_id_type InfPrism::key (const unsigned int s) const
81 : {
82 0 : libmesh_assert_less (s, this->n_sides());
83 :
84 0 : switch (s)
85 : {
86 0 : case 0: // the triangular face at z=-1, base face
87 0 : return this->compute_key (this->node_id(InfPrism6::side_nodes_map[s][0]),
88 0 : this->node_id(InfPrism6::side_nodes_map[s][1]),
89 0 : this->node_id(InfPrism6::side_nodes_map[s][2]));
90 :
91 0 : case 1: // the quad face at y=0
92 : case 2: // the other quad face
93 : case 3: // the quad face at x=0
94 0 : return this->compute_key (this->node_id(InfPrism6::side_nodes_map[s][0]),
95 0 : this->node_id(InfPrism6::side_nodes_map[s][1]),
96 0 : this->node_id(InfPrism6::side_nodes_map[s][2]),
97 0 : this->node_id(InfPrism6::side_nodes_map[s][3]));
98 :
99 0 : default:
100 0 : libmesh_error_msg("Invalid side s = " << s);
101 : }
102 : }
103 :
104 :
105 :
106 15000 : dof_id_type InfPrism::low_order_key (const unsigned int s) const
107 : {
108 6000 : libmesh_assert_less (s, this->n_sides());
109 :
110 15000 : switch (s)
111 : {
112 3750 : case 0: // the triangular face at z=-1, base face
113 5250 : return this->compute_key (this->node_id(InfPrism6::side_nodes_map[s][0]),
114 3750 : this->node_id(InfPrism6::side_nodes_map[s][1]),
115 5250 : this->node_id(InfPrism6::side_nodes_map[s][2]));
116 :
117 11250 : case 1: // the quad face at y=0
118 : case 2: // the other quad face
119 : case 3: // the quad face at x=0
120 15750 : return this->compute_key (this->node_id(InfPrism6::side_nodes_map[s][0]),
121 11250 : this->node_id(InfPrism6::side_nodes_map[s][1]),
122 11250 : this->node_id(InfPrism6::side_nodes_map[s][2]),
123 15750 : this->node_id(InfPrism6::side_nodes_map[s][3]));
124 :
125 0 : default:
126 0 : libmesh_error_msg("Invalid side s = " << s);
127 : }
128 : }
129 :
130 :
131 :
132 0 : unsigned int InfPrism::local_side_node(unsigned int side,
133 : unsigned int side_node) const
134 : {
135 0 : libmesh_assert_less (side, this->n_sides());
136 :
137 : // Never more than 4 nodes per side.
138 0 : libmesh_assert_less(side_node, InfPrism6::nodes_per_side);
139 :
140 : // Some sides have 3 nodes.
141 0 : libmesh_assert(side != 0 || side_node < 3);
142 :
143 0 : return InfPrism6::side_nodes_map[side][side_node];
144 : }
145 :
146 :
147 :
148 360 : unsigned int InfPrism::local_edge_node(unsigned int edge,
149 : unsigned int edge_node) const
150 : {
151 120 : libmesh_assert_less(edge, this->n_edges());
152 120 : libmesh_assert_less(edge_node, InfPrism6::nodes_per_edge);
153 :
154 360 : return InfPrism6::edge_nodes_map[edge][edge_node];
155 : }
156 :
157 :
158 :
159 10137 : std::unique_ptr<Elem> InfPrism::side_ptr (const unsigned int i)
160 : {
161 4054 : libmesh_assert_less (i, this->n_sides());
162 :
163 10137 : std::unique_ptr<Elem> face;
164 :
165 10137 : switch (i)
166 : {
167 3341 : case 0: // the triangular face at z=-1, base face
168 : {
169 3341 : face = std::make_unique<Tri3>();
170 3341 : break;
171 : }
172 :
173 6796 : case 1: // the quad face at y=0
174 : case 2: // the other quad face
175 : case 3: // the quad face at x=0
176 : {
177 6796 : face = std::make_unique<InfQuad4>();
178 6796 : break;
179 : }
180 :
181 0 : default:
182 0 : libmesh_error_msg("Invalid side i = " << i);
183 : }
184 :
185 : // Set the nodes
186 47344 : for (auto n : face->node_index_range())
187 52087 : face->set_node(n, this->node_ptr(InfPrism6::side_nodes_map[i][n]));
188 :
189 10137 : return face;
190 0 : }
191 :
192 :
193 :
194 13856 : void InfPrism::side_ptr (std::unique_ptr<Elem> & side,
195 : const unsigned int i)
196 : {
197 5540 : libmesh_assert_less (i, this->n_sides());
198 :
199 13856 : switch (i)
200 : {
201 : // the base face
202 1337 : case 0:
203 : {
204 3344 : if (!side.get() || side->type() != TRI3)
205 : {
206 3992 : side = this->side_ptr(i);
207 3326 : return;
208 : }
209 7 : break;
210 : }
211 :
212 : // connecting to another infinite element
213 4203 : case 1:
214 : case 2:
215 : case 3:
216 : case 4:
217 : {
218 10512 : if (!side.get() || side->type() != INFQUAD4)
219 : {
220 8120 : side = this->side_ptr(i);
221 6766 : return;
222 : }
223 1497 : break;
224 : }
225 :
226 0 : default:
227 0 : libmesh_error_msg("Invalid side i = " << i);
228 : }
229 :
230 5268 : side->subdomain_id() = this->subdomain_id();
231 :
232 : // Set the nodes
233 18802 : for (auto n : side->node_index_range())
234 21047 : side->set_node(n, this->node_ptr(InfPrism6::side_nodes_map[i][n]));
235 : }
236 :
237 :
238 :
239 1436 : bool InfPrism::is_child_on_side(const unsigned int c,
240 : const unsigned int s) const
241 : {
242 852 : libmesh_assert_less (c, this->n_children());
243 852 : libmesh_assert_less (s, this->n_sides());
244 :
245 1436 : return (s == 0 || c+1 == s || c == s%3);
246 : }
247 :
248 :
249 :
250 2592 : bool InfPrism::is_edge_on_side (const unsigned int e,
251 : const unsigned int s) const
252 : {
253 2544 : libmesh_assert_less (e, this->n_edges());
254 2544 : libmesh_assert_less (s, this->n_sides());
255 :
256 2592 : return (edge_sides_map[e][0] == s || edge_sides_map[e][1] == s);
257 : }
258 :
259 14 : bool InfPrism::contains_point (const Point & p, Real tol) const
260 : {
261 : // For infinite elements with linear base interpolation:
262 : // make use of the fact that infinite elements do not
263 : // live inside the envelope. Use a fast scheme to
264 : // check whether point \p p is inside or outside
265 : // our relevant part of the envelope. Note that
266 : // this is not exclusive: only when the distance is less,
267 : // we are safe. Otherwise, we cannot say anything. The
268 : // envelope may be non-spherical, the physical point may lie
269 : // inside the envelope, outside the envelope, or even inside
270 : // this infinite element. Therefore if this fails,
271 : // fall back to the inverse_map()
272 14 : const Point my_origin (this->origin());
273 :
274 : // determine the minimal distance of the base from the origin
275 : // use norm_sq(), it is faster than norm() and produces
276 : // the same behavior. Shift my_origin to center first:
277 12 : Point pt0_o(this->point(0) - my_origin);
278 6 : Point pt1_o(this->point(1) - my_origin);
279 6 : Point pt2_o(this->point(2) - my_origin);
280 14 : const Real tmp_min_distance_sq = std::min(pt0_o.norm_sq(),
281 10 : std::min(pt1_o.norm_sq(),
282 28 : pt2_o.norm_sq()));
283 :
284 : // For a coarse grid, it is important to account for the fact
285 : // that the sides are not spherical, thus the closest point
286 : // can be closer than all edges.
287 : // This is an estimator using Pythagoras:
288 : const Real min_distance_sq = tmp_min_distance_sq
289 14 : - .5*std::max((point(0)-point(1)).norm_sq(),
290 16 : std::max((point(0)-point(2)).norm_sq(),
291 34 : (point(1)-point(2)).norm_sq()));
292 :
293 : // work with 1% allowable deviation. We can still fall
294 : // back to the InfFE::inverse_map()
295 14 : const Real conservative_p_dist_sq = 1.01 * (Point(p - my_origin).norm_sq());
296 :
297 14 : if (conservative_p_dist_sq < min_distance_sq)
298 : {
299 : // the physical point is definitely not contained in the element:
300 6 : return false;
301 : }
302 :
303 : // this captures the case that the point is not (almost) in the direction of the element.:
304 : // first, project the problem onto the unit sphere:
305 0 : Point p_o(p - my_origin);
306 1 : pt0_o /= pt0_o.norm();
307 1 : pt1_o /= pt1_o.norm();
308 1 : pt2_o /= pt2_o.norm();
309 1 : p_o /= p_o.norm();
310 :
311 : // now, check if it is in the projected face; by comparing the distance of
312 : // any point in the element to \p p with the largest distance between this point
313 : // to any other point in the element.
314 1 : if ((p_o - pt0_o).norm_sq() > std::max((pt0_o - pt1_o).norm_sq(), (pt0_o - pt2_o).norm_sq()) ||
315 1 : (p_o - pt1_o).norm_sq() > std::max((pt1_o - pt2_o).norm_sq(), (pt1_o - pt0_o).norm_sq()) ||
316 0 : (p_o - pt2_o).norm_sq() > std::max((pt2_o - pt0_o).norm_sq(), (pt2_o - pt1_o).norm_sq()) )
317 : {
318 : // the physical point is definitely not contained in the element
319 1 : return false;
320 : }
321 :
322 :
323 : // It seems that \p p is at least close to this element.
324 : //
325 : // Declare a basic FEType. Will use default in the base,
326 : // and something else (not important) in radial direction.
327 0 : FEType fe_type(default_order());
328 :
329 0 : const Point mapped_point = InfFEMap::inverse_map(dim(), this, p,
330 0 : tol, false);
331 :
332 0 : return this->on_reference_element(mapped_point, tol);
333 : }
334 :
335 : std::vector<unsigned>
336 144 : InfPrism::sides_on_edge(const unsigned int e) const
337 : {
338 48 : libmesh_assert_less(e, n_edges());
339 192 : return {std::begin(edge_sides_map[e]), std::end(edge_sides_map[e])};
340 : }
341 :
342 :
343 : bool
344 16 : InfPrism::is_flipped() const
345 : {
346 10 : return (triple_product(this->point(1)-this->point(0),
347 10 : this->point(2)-this->point(0),
348 28 : this->point(3)-this->point(0)) < 0);
349 : }
350 :
351 : std::vector<unsigned int>
352 270 : InfPrism::edges_adjacent_to_node(const unsigned int n) const
353 : {
354 90 : libmesh_assert_less(n, this->n_nodes());
355 :
356 : // For vertices, we use the InfPrism::adjacent_edges_map with
357 : // appropriate "trimming" based on whether the vertices are "at
358 : // infinity" or not. Otherwise each of the mid-edge nodes is
359 : // adjacent only to the edge it is on, and the remaining nodes are
360 : // not adjacent to any edge.
361 270 : if (this->is_vertex(n))
362 : {
363 180 : auto trim = (n < 3) ? 0 : 2;
364 180 : return {std::begin(adjacent_edges_map[n]), std::end(adjacent_edges_map[n]) - trim};
365 : }
366 90 : else if (this->is_edge(n))
367 45 : return {n - this->n_vertices()};
368 :
369 15 : libmesh_assert(this->is_face(n) || this->is_internal(n));
370 30 : return {};
371 : }
372 :
373 :
374 :
375 0 : bool InfPrism::on_reference_element(const Point & p,
376 : const Real eps) const
377 : {
378 0 : const Real & xi = p(0);
379 0 : const Real & eta = p(1);
380 0 : const Real & zeta = p(2);
381 :
382 : // inside the reference triangle with zeta in [-1,1]
383 0 : return ((xi >= 0.-eps) &&
384 0 : (eta >= 0.-eps) &&
385 0 : (zeta >= -1.-eps) &&
386 0 : (zeta <= 1.+eps) &&
387 0 : ((xi + eta) <= 1.+eps));
388 : }
389 :
390 :
391 :
392 :
393 : } // namespace libMesh
394 :
395 :
396 :
397 : #endif // ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
|