libMesh
cell_inf_prism.C
Go to the documentation of this file.
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 dof_id_type InfPrism::key (const unsigned int s) const
81 {
82  libmesh_assert_less (s, this->n_sides());
83 
84  switch (s)
85  {
86  case 0: // the triangular face at z=-1, base face
87  return this->compute_key (this->node_id(InfPrism6::side_nodes_map[s][0]),
89  this->node_id(InfPrism6::side_nodes_map[s][2]));
90 
91  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  return this->compute_key (this->node_id(InfPrism6::side_nodes_map[s][0]),
97  this->node_id(InfPrism6::side_nodes_map[s][3]));
98 
99  default:
100  libmesh_error_msg("Invalid side s = " << s);
101  }
102 }
103 
104 
105 
106 dof_id_type InfPrism::low_order_key (const unsigned int s) const
107 {
108  libmesh_assert_less (s, this->n_sides());
109 
110  switch (s)
111  {
112  case 0: // the triangular face at z=-1, base face
113  return this->compute_key (this->node_id(InfPrism6::side_nodes_map[s][0]),
114  this->node_id(InfPrism6::side_nodes_map[s][1]),
115  this->node_id(InfPrism6::side_nodes_map[s][2]));
116 
117  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  return this->compute_key (this->node_id(InfPrism6::side_nodes_map[s][0]),
121  this->node_id(InfPrism6::side_nodes_map[s][1]),
122  this->node_id(InfPrism6::side_nodes_map[s][2]),
123  this->node_id(InfPrism6::side_nodes_map[s][3]));
124 
125  default:
126  libmesh_error_msg("Invalid side s = " << s);
127  }
128 }
129 
130 
131 
132 unsigned int InfPrism::local_side_node(unsigned int side,
133  unsigned int side_node) const
134 {
135  libmesh_assert_less (side, this->n_sides());
136 
137  // Never more than 4 nodes per side.
138  libmesh_assert_less(side_node, InfPrism6::nodes_per_side);
139 
140  // Some sides have 3 nodes.
141  libmesh_assert(side != 0 || side_node < 3);
142 
143  return InfPrism6::side_nodes_map[side][side_node];
144 }
145 
146 
147 
148 unsigned int InfPrism::local_edge_node(unsigned int edge,
149  unsigned int edge_node) const
150 {
151  libmesh_assert_less(edge, this->n_edges());
152  libmesh_assert_less(edge_node, InfPrism6::nodes_per_edge);
153 
154  return InfPrism6::edge_nodes_map[edge][edge_node];
155 }
156 
157 
158 
159 std::unique_ptr<Elem> InfPrism::side_ptr (const unsigned int i)
160 {
161  libmesh_assert_less (i, this->n_sides());
162 
163  std::unique_ptr<Elem> face;
164 
165  switch (i)
166  {
167  case 0: // the triangular face at z=-1, base face
168  {
169  face = std::make_unique<Tri3>();
170  break;
171  }
172 
173  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  face = std::make_unique<InfQuad4>();
178  break;
179  }
180 
181  default:
182  libmesh_error_msg("Invalid side i = " << i);
183  }
184 
185  // Set the nodes
186  for (auto n : face->node_index_range())
187  face->set_node(n, this->node_ptr(InfPrism6::side_nodes_map[i][n]));
188 
189  return face;
190 }
191 
192 
193 
194 void InfPrism::side_ptr (std::unique_ptr<Elem> & side,
195  const unsigned int i)
196 {
197  libmesh_assert_less (i, this->n_sides());
198 
199  switch (i)
200  {
201  // the base face
202  case 0:
203  {
204  if (!side.get() || side->type() != TRI3)
205  {
206  side = this->side_ptr(i);
207  return;
208  }
209  break;
210  }
211 
212  // connecting to another infinite element
213  case 1:
214  case 2:
215  case 3:
216  case 4:
217  {
218  if (!side.get() || side->type() != INFQUAD4)
219  {
220  side = this->side_ptr(i);
221  return;
222  }
223  break;
224  }
225 
226  default:
227  libmesh_error_msg("Invalid side i = " << i);
228  }
229 
230  side->subdomain_id() = this->subdomain_id();
231 
232  // Set the nodes
233  for (auto n : side->node_index_range())
234  side->set_node(n, this->node_ptr(InfPrism6::side_nodes_map[i][n]));
235 }
236 
237 
238 
239 bool InfPrism::is_child_on_side(const unsigned int c,
240  const unsigned int s) const
241 {
242  libmesh_assert_less (c, this->n_children());
243  libmesh_assert_less (s, this->n_sides());
244 
245  return (s == 0 || c+1 == s || c == s%3);
246 }
247 
248 
249 
250 bool InfPrism::is_edge_on_side (const unsigned int e,
251  const unsigned int s) const
252 {
253  libmesh_assert_less (e, this->n_edges());
254  libmesh_assert_less (s, this->n_sides());
255 
256  return (edge_sides_map[e][0] == s || edge_sides_map[e][1] == s);
257 }
258 
259 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  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  Point pt0_o(this->point(0) - my_origin);
278  Point pt1_o(this->point(1) - my_origin);
279  Point pt2_o(this->point(2) - my_origin);
280  const Real tmp_min_distance_sq = std::min(pt0_o.norm_sq(),
281  std::min(pt1_o.norm_sq(),
282  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  - .5*std::max((point(0)-point(1)).norm_sq(),
290  std::max((point(0)-point(2)).norm_sq(),
291  (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  const Real conservative_p_dist_sq = 1.01 * (Point(p - my_origin).norm_sq());
296 
297  if (conservative_p_dist_sq < min_distance_sq)
298  {
299  // the physical point is definitely not contained in the element:
300  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  Point p_o(p - my_origin);
306  pt0_o /= pt0_o.norm();
307  pt1_o /= pt1_o.norm();
308  pt2_o /= pt2_o.norm();
309  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  if ((p_o - pt0_o).norm_sq() > std::max((pt0_o - pt1_o).norm_sq(), (pt0_o - pt2_o).norm_sq()) ||
315  (p_o - pt1_o).norm_sq() > std::max((pt1_o - pt2_o).norm_sq(), (pt1_o - pt0_o).norm_sq()) ||
316  (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  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  FEType fe_type(default_order());
328 
329  const Point mapped_point = InfFEMap::inverse_map(dim(), this, p,
330  tol, false);
331 
332  return this->on_reference_element(mapped_point, tol);
333 }
334 
335 std::vector<unsigned>
336 InfPrism::sides_on_edge(const unsigned int e) const
337 {
338  libmesh_assert_less(e, n_edges());
339  return {std::begin(edge_sides_map[e]), std::end(edge_sides_map[e])};
340 }
341 
342 
343 bool
345 {
346  return (triple_product(this->point(1)-this->point(0),
347  this->point(2)-this->point(0),
348  this->point(3)-this->point(0)) < 0);
349 }
350 
351 std::vector<unsigned int>
352 InfPrism::edges_adjacent_to_node(const unsigned int n) const
353 {
354  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  if (this->is_vertex(n))
362  {
363  auto trim = (n < 3) ? 0 : 2;
364  return {std::begin(adjacent_edges_map[n]), std::end(adjacent_edges_map[n]) - trim};
365  }
366  else if (this->is_edge(n))
367  return {n - this->n_vertices()};
368 
369  libmesh_assert(this->is_face(n) || this->is_internal(n));
370  return {};
371 }
372 
373 
374 
376  const Real eps) const
377 {
378  const Real & xi = p(0);
379  const Real & eta = p(1);
380  const Real & zeta = p(2);
381 
382  // inside the reference triangle with zeta in [-1,1]
383  return ((xi >= 0.-eps) &&
384  (eta >= 0.-eps) &&
385  (zeta >= -1.-eps) &&
386  (zeta <= 1.+eps) &&
387  ((xi + eta) <= 1.+eps));
388 }
389 
390 
391 
392 
393 } // namespace libMesh
394 
395 
396 
397 #endif // ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
virtual unsigned int n_vertices() const override final
auto norm() const -> decltype(std::norm(T()))
Definition: type_vector.h:907
virtual bool is_vertex(const unsigned int i) const override final
We number vertices first.
static const Real _master_points[12][3]
Master element node locations.
virtual dof_id_type key() const
Definition: elem.C:753
static const int num_edges
virtual unsigned int n_edges() const override final
virtual bool is_face(const unsigned int i) const override final
We number faces last.
The libMesh namespace provides an interface to certain functionality in the library.
virtual bool is_flipped() const override final
static Point inverse_map(const unsigned int dim, const Elem *elem, const Point &p, const Real tolerance=TOLERANCE, const bool secure=true)
Definition: inf_fe_map.C:96
virtual unsigned int n_children() const override final
virtual bool is_edge(const unsigned int i) const override final
We number edges next.
std::vector< unsigned int > sides_on_edge(const unsigned int e) const override final
static const int num_sides
Geometric constants for all InfPrisms.
virtual unsigned int local_side_node(unsigned int side, unsigned int side_node) const override
T triple_product(const TypeVector< T > &a, const TypeVector< T > &b, const TypeVector< T > &c)
Definition: type_vector.h:1029
auto norm_sq() const -> decltype(std::norm(T()))
Definition: type_vector.h:926
virtual std::vector< unsigned int > edges_adjacent_to_node(const unsigned int n) const override
static const unsigned int side_nodes_map[num_sides][nodes_per_side]
This maps the node of the side to element node numbers.
virtual unsigned int n_nodes() const =0
virtual unsigned int local_edge_node(unsigned int edge, unsigned int edge_node) const override
static const unsigned int adjacent_edges_map[6][3]
This maps the node to the 3 (or fewer) edge ids adjacent to the node.
virtual std::unique_ptr< Elem > side_ptr(const unsigned int i) override final
libmesh_assert(ctx)
virtual bool is_child_on_side(const unsigned int c, const unsigned int s) const override final
virtual bool is_edge_on_side(const unsigned int e, const unsigned int s) const override final
static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]
This maps the node of the edge to element node numbers.
virtual Point origin() const override
Definition: cell_inf.h:77
static const unsigned int edge_sides_map[6][2]
This maps each edge to the sides that contain said edge.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
subdomain_id_type subdomain_id() const
Definition: elem.h:2582
static const int nodes_per_side
auto norm_sq(const T &a) -> decltype(std::norm(a))
Definition: tensor_tools.h:104
virtual bool on_reference_element(const Point &p, const Real eps=TOLERANCE) const override final
virtual unsigned short dim() const override
Definition: cell_inf.h:66
virtual bool contains_point(const Point &p, Real tol=TOLERANCE) const override
static const int nodes_per_edge
static const int num_children
static dof_id_type compute_key(dof_id_type n0)
Definition: elem.h:3294
virtual Order default_order() const =0
virtual unsigned int n_sides() const override final
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
dof_id_type node_id(const unsigned int i) const
Definition: elem.h:2475
const Point & point(const unsigned int i) const
Definition: elem.h:2453
virtual dof_id_type low_order_key(const unsigned int s) const override
uint8_t dof_id_type
Definition: id_types.h:67
bool is_internal(const unsigned int i) const
Definition: elem.C:3566