libMesh
cell_inf_hex.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 // Local includes
19 #include "libmesh/libmesh_config.h"
20 
21 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
22 
23 
24 // C++ includes
25 #include <algorithm> // for std::min, std::max
26 
27 // Local includes cont'd
28 #include "libmesh/cell_inf_hex.h"
29 #include "libmesh/cell_inf_hex8.h"
30 #include "libmesh/face_quad4.h"
31 #include "libmesh/face_inf_quad4.h"
32 #include "libmesh/fe_type.h"
33 #include "libmesh/fe_interface.h"
34 #include "libmesh/inf_fe_map.h"
35 #include "libmesh/enum_elem_quality.h"
36 
37 namespace libMesh
38 {
39 
40 
41 
42 // ------------------------------------------------------------
43 // InfHex class static member initializations
44 const int InfHex::num_sides;
45 const int InfHex::num_edges;
46 const int InfHex::num_children;
47 
48 const Real InfHex::_master_points[18][3] =
49  {
50  {-1, -1, 0},
51  {1, -1, 0},
52  {1, 1, 0},
53  {-1, 1, 0},
54  {-1, -1, 1},
55  {1, -1, 1},
56  {1, 1, 1},
57  {-1, 1, 1},
58  {0, -1, 0},
59  {1, 0, 0},
60  {0, 1, 0},
61  {-1, 0, 0},
62  {0, -1, 1},
63  {1, 0, 1},
64  {0, 1, 1},
65  {-1, 0, 1},
66  {0, 0, 0},
67  {0, 0, 1}
68  };
69 
70 const unsigned int InfHex::edge_sides_map[8][2] =
71  {
72  {0, 1}, // Edge 0
73  {0, 2}, // Edge 1
74  {0, 3}, // Edge 2
75  {0, 4}, // Edge 3
76  {1, 4}, // Edge 4
77  {1, 2}, // Edge 5
78  {2, 3}, // Edge 6
79  {3, 4} // Edge 7
80  };
81 
82 const unsigned int InfHex::adjacent_edges_map[/*num_vertices*/8][/*max_adjacent_edges*/3] =
83  {
84  {0, 3, 4}, // Edges adjacent to node 0
85  {0, 1, 5}, // Edges adjacent to node 1
86  {1, 2, 6}, // Edges adjacent to node 2
87  {2, 3, 7}, // Edges adjacent to node 3
88  {4, 99, 99}, // Edges adjacent to node 4
89  {5, 99, 99}, // Edges adjacent to node 5
90  {6, 99, 99}, // Edges adjacent to node 6
91  {7, 99, 99} // Edges adjacent to node 7
92  };
93 
94 // ------------------------------------------------------------
95 // InfHex class member functions
96 dof_id_type InfHex::key (const unsigned int s) const
97 {
98  libmesh_assert_less (s, this->n_sides());
99 
100  // The order of the node ids does not matter, they are sorted by the
101  // compute_key() function.
102  return this->compute_key(this->node_id(InfHex8::side_nodes_map[s][0]),
103  this->node_id(InfHex8::side_nodes_map[s][1]),
104  this->node_id(InfHex8::side_nodes_map[s][2]),
105  this->node_id(InfHex8::side_nodes_map[s][3]));
106 }
107 
108 
109 
110 dof_id_type InfHex::low_order_key (const unsigned int s) const
111 {
112  libmesh_assert_less (s, this->n_sides());
113 
114  // The order of the node ids does not matter, they are sorted by the
115  // compute_key() function.
116  return this->compute_key(this->node_id(InfHex8::side_nodes_map[s][0]),
117  this->node_id(InfHex8::side_nodes_map[s][1]),
118  this->node_id(InfHex8::side_nodes_map[s][2]),
119  this->node_id(InfHex8::side_nodes_map[s][3]));
120 }
121 
122 
123 
124 unsigned int InfHex::local_side_node(unsigned int side,
125  unsigned int side_node) const
126 {
127  libmesh_assert_less (side, this->n_sides());
128  libmesh_assert_less (side_node, InfHex8::nodes_per_side);
129 
130  return InfHex8::side_nodes_map[side][side_node];
131 }
132 
133 
134 
135 unsigned int InfHex::local_edge_node(unsigned int edge,
136  unsigned int edge_node) const
137 {
138  libmesh_assert_less (edge, this->n_edges());
139  libmesh_assert_less (edge_node, InfHex8::nodes_per_edge);
140 
141  return InfHex8::edge_nodes_map[edge][edge_node];
142 }
143 
144 
145 
146 std::unique_ptr<Elem> InfHex::side_ptr (const unsigned int i)
147 {
148  libmesh_assert_less (i, this->n_sides());
149 
150  // Return value
151  std::unique_ptr<Elem> face;
152 
153  // Think of a unit cube: (-1,1) x (-1,1) x (-1,1),
154  // with (in general) the normals pointing outwards
155  switch (i)
156  {
157  // the face at z = -1
158  // the base, where the infinite element couples to conventional
159  // elements
160  case 0:
161  {
162  // Oops, here we are, claiming the normal of the face
163  // elements point outwards -- and this is the exception:
164  // For the side built from the base face,
165  // the normal is pointing _into_ the element!
166  // Why is that? - In agreement with build_side_ptr(),
167  // which in turn _has_ to build the face in this
168  // way as to enable the cool way \p InfFE re-uses \p FE.
169  face = std::make_unique<Quad4>();
170  break;
171  }
172 
173  // These faces connect to other infinite elements.
174  case 1: // the face at y = -1
175  case 2: // the face at x = 1
176  case 3: // the face at y = 1
177  case 4: // the face at x = -1
178  {
179  face = std::make_unique<InfQuad4>();
180  break;
181  }
182 
183  default:
184  libmesh_error_msg("Invalid side i = " << i);
185  }
186 
187  // Set the nodes
188  for (auto n : face->node_index_range())
189  face->set_node(n, this->node_ptr(InfHex8::side_nodes_map[i][n]));
190 
191  return face;
192 }
193 
194 
195 
196 void InfHex::side_ptr (std::unique_ptr<Elem> & side,
197  const unsigned int i)
198 {
199  libmesh_assert_less (i, this->n_sides());
200 
201  // Think of a unit cube: (-1,1) x (-1,1) x (1,1)
202  switch (i)
203  {
204  // the base face
205  case 0:
206  {
207  if (!side.get() || side->type() != QUAD4)
208  {
209  side = this->side_ptr(i);
210  return;
211  }
212  break;
213  }
214 
215  // connecting to another infinite element
216  case 1:
217  case 2:
218  case 3:
219  case 4:
220  {
221  if (!side.get() || side->type() != INFQUAD4)
222  {
223  side = this->side_ptr(i);
224  return;
225  }
226  break;
227  }
228 
229  default:
230  libmesh_error_msg("Invalid side i = " << i);
231  }
232 
233  side->subdomain_id() = this->subdomain_id();
234 
235  // Set the nodes
236  for (auto n : side->node_index_range())
237  side->set_node(n, this->node_ptr(InfHex8::side_nodes_map[i][n]));
238 }
239 
240 
241 
242 bool InfHex::is_child_on_side(const unsigned int c,
243  const unsigned int s) const
244 {
245  libmesh_assert_less (c, this->n_children());
246  libmesh_assert_less (s, this->n_sides());
247 
248  return (s == 0 || c+1 == s || c == s%4);
249 }
250 
251 
252 
253 bool InfHex::is_edge_on_side (const unsigned int e,
254  const unsigned int s) const
255 {
256  libmesh_assert_less (e, this->n_edges());
257  libmesh_assert_less (s, this->n_sides());
258 
259  return (edge_sides_map[e][0] == s || edge_sides_map[e][1] == s);
260 }
261 
262 
263 
264 std::vector<unsigned int> InfHex::sides_on_edge(const unsigned int e) const
265 {
266  libmesh_assert_less(e, this->n_edges());
267 
268  return {edge_sides_map[e][0], edge_sides_map[e][1]};
269 }
270 
271 
272 bool
274 {
275  return (triple_product(this->point(1)-this->point(0),
276  this->point(3)-this->point(0),
277  this->point(4)-this->point(0)) < 0);
278 }
279 
280 std::vector<unsigned int>
281 InfHex::edges_adjacent_to_node(const unsigned int n) const
282 {
283  libmesh_assert_less(n, this->n_nodes());
284 
285  // For vertices, we use the InfHex::adjacent_edges_map with
286  // appropriate "trimming" based on whether the vertices are "at
287  // infinity" or not. Otherwise each of the mid-edge nodes is
288  // adjacent only to the edge it is on, and the remaining nodes are
289  // not adjacent to any edge.
290  if (this->is_vertex(n))
291  {
292  auto trim = (n < 4) ? 0 : 2;
293  return {std::begin(adjacent_edges_map[n]), std::end(adjacent_edges_map[n]) - trim};
294  }
295  else if (this->is_edge(n))
296  return {n - this->n_vertices()};
297 
298  libmesh_assert(this->is_face(n) || this->is_internal(n));
299  return {};
300 }
301 
303 {
304  switch (q)
305  {
306 
316  case DIAGONAL:
317  {
318  // Diagonal between node 0 and node 2
319  const Real d02 = this->length(0,2);
320 
321  // Diagonal between node 1 and node 3
322  const Real d13 = this->length(1,3);
323 
324  // Find the biggest and smallest diagonals
325  const Real min = std::min(d02, d13);
326  const Real max = std::max(d02, d13);
327 
328  libmesh_assert_not_equal_to (max, 0.0);
329 
330  return min / max;
331 
332  break;
333  }
334 
342  case TAPER:
343  {
344 
348  const Real d01 = this->length(0,1);
349  const Real d12 = this->length(1,2);
350  const Real d23 = this->length(2,3);
351  const Real d03 = this->length(0,3);
352 
353  std::vector<Real> edge_ratios(2);
354 
355  // Bottom
356  edge_ratios[0] = std::min(d01, d23) / std::max(d01, d23);
357  edge_ratios[1] = std::min(d03, d12) / std::max(d03, d12);
358 
359  return *(std::min_element(edge_ratios.begin(), edge_ratios.end())) ;
360 
361  break;
362  }
363 
364 
372  case STRETCH:
373  {
377  const Real sqrt3 = 1.73205080756888;
378 
382  const Real d02 = this->length(0,2);
383  const Real d13 = this->length(1,3);
384  const Real max_diag = std::max(d02, d13);
385 
386  libmesh_assert_not_equal_to ( max_diag, 0.0 );
387 
391  std::vector<Real> edges(4);
392  edges[0] = this->length(0,1);
393  edges[1] = this->length(1,2);
394  edges[2] = this->length(2,3);
395  edges[3] = this->length(0,3);
396 
397  const Real min_edge = *(std::min_element(edges.begin(), edges.end()));
398  return sqrt3 * min_edge / max_diag ;
399  }
400 
401 
406  default:
407  return Elem::quality(q);
408  }
409 }
410 
411 
412 
413 std::pair<Real, Real> InfHex::qual_bounds (const ElemQuality) const
414 {
415  libmesh_not_implemented();
416 
417  // We'll never get here
418  return {0., 0.};
419 }
420 
421 
422 
423 
424 
425 const unsigned short int InfHex::_second_order_adjacent_vertices[8][2] =
426  {
427  { 0, 1}, // vertices adjacent to node 8
428  { 1, 2}, // vertices adjacent to node 9
429  { 2, 3}, // vertices adjacent to node 10
430  { 0, 3}, // vertices adjacent to node 11
431 
432  { 4, 5}, // vertices adjacent to node 12
433  { 5, 6}, // vertices adjacent to node 13
434  { 6, 7}, // vertices adjacent to node 14
435  { 4, 7} // vertices adjacent to node 15
436  };
437 
438 
439 
440 const unsigned short int InfHex::_second_order_vertex_child_number[18] =
441  {
442  99,99,99,99,99,99,99,99, // Vertices
443  0,1,2,0, // Edges
444  0,1,2,0,0, // Faces
445  0 // Interior
446  };
447 
448 
449 
450 const unsigned short int InfHex::_second_order_vertex_child_index[18] =
451  {
452  99,99,99,99,99,99,99,99, // Vertices
453  1,2,3,3, // Edges
454  5,6,7,7,2, // Faces
455  6 // Interior
456  };
457 
458 
459 bool InfHex::contains_point (const Point & p, Real tol) const
460 {
461  // For infinite elements with linear base interpolation:
462  // make use of the fact that infinite elements do not
463  // live inside the envelope. Use a fast scheme to
464  // check whether point \p p is inside or outside
465  // our relevant part of the envelope. Note that
466  // this is not exclusive: only when the distance is less,
467  // we are safe. Otherwise, we cannot say anything. The
468  // envelope may be non-spherical, the physical point may lie
469  // inside the envelope, outside the envelope, or even inside
470  // this infinite element. Therefore if this fails,
471  // fall back to the inverse_map()
472  const Point my_origin (this->origin());
473 
474  // determine the minimal distance of the base from the origin
475  // Use norm_sq() instead of norm(), it is faster
476  Point pt0_o(this->point(0) - my_origin);
477  Point pt1_o(this->point(1) - my_origin);
478  Point pt2_o(this->point(2) - my_origin);
479  Point pt3_o(this->point(3) - my_origin);
480  const Real tmp_min_distance_sq = std::min(pt0_o.norm_sq(),
481  std::min(pt1_o.norm_sq(),
482  std::min(pt2_o.norm_sq(),
483  pt3_o.norm_sq())));
484 
485  // For a coarse grid, it is important to account for the fact
486  // that the sides are not spherical, thus the closest point
487  // can be closer than all edges.
488  // This is an estimator using Pythagoras:
489  const Real min_distance_sq = tmp_min_distance_sq
490  - .5*std::max((point(0)-point(2)).norm_sq(),
491  (point(1)-point(3)).norm_sq());
492 
493  // work with 1% allowable deviation. We can still fall
494  // back to the InfFE::inverse_map()
495  const Real conservative_p_dist_sq = 1.01 * (Point(p - my_origin).norm_sq());
496 
497 
498 
499  if (conservative_p_dist_sq < min_distance_sq)
500  {
501  // the physical point is definitely not contained in the element
502  return false;
503  }
504 
505  // this captures the case that the point is not (almost) in the direction of the element.:
506  // first, project the problem onto the unit sphere:
507  Point p_o(p - my_origin);
508  pt0_o /= pt0_o.norm();
509  pt1_o /= pt1_o.norm();
510  pt2_o /= pt2_o.norm();
511  pt3_o /= pt3_o.norm();
512  p_o /= p_o.norm();
513 
514 
515  // now, check if it is in the projected face; using that the diagonal contains
516  // the largest distance between points in it
517  Real max_h = std::max((pt0_o - pt2_o).norm_sq(),
518  (pt1_o - pt3_o).norm_sq())*1.01;
519 
520  if ((p_o - pt0_o).norm_sq() > max_h ||
521  (p_o - pt1_o).norm_sq() > max_h ||
522  (p_o - pt2_o).norm_sq() > max_h ||
523  (p_o - pt3_o).norm_sq() > max_h )
524  {
525  // the physical point is definitely not contained in the element
526  return false;
527  }
528 
529  // Declare a basic FEType. Will use default in the base,
530  // and something else (not important) in radial direction.
531  FEType fe_type(default_order());
532 
533  const Point mapped_point = InfFEMap::inverse_map(dim(), this, p,
534  tol, false);
535 
536  return this->on_reference_element(mapped_point, tol);
537 }
538 
539 
540 
542  const Real eps) const
543 {
544  const Real & xi = p(0);
545  const Real & eta = p(1);
546  const Real & zeta = p(2);
547 
548  // The reference infhex is a [-1,1]^3.
549  return ((xi >= -1.-eps) &&
550  (xi <= 1.+eps) &&
551  (eta >= -1.-eps) &&
552  (eta <= 1.+eps) &&
553  (zeta >= -1.-eps) &&
554  (zeta <= 1.+eps));
555 }
556 
557 
558 
559 } // namespace libMesh
560 
561 
562 
563 
564 #endif // ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
static const int num_children
Definition: cell_inf_hex.h:85
virtual std::vector< unsigned int > sides_on_edge(const unsigned int e) const override final
Definition: cell_inf_hex.C:264
static const int num_sides
Geometric constants for all InfHexes.
Definition: cell_inf_hex.h:83
virtual dof_id_type low_order_key(const unsigned int s) const override
Definition: cell_inf_hex.C:110
auto norm() const -> decltype(std::norm(T()))
Definition: type_vector.h:907
virtual dof_id_type key() const
Definition: elem.C:753
virtual bool is_face(const unsigned int i) const override final
We number faces last.
Definition: cell_inf_hex.h:137
virtual std::pair< Real, Real > qual_bounds(const ElemQuality q) const override
Definition: cell_inf_hex.C:413
static const int nodes_per_side
virtual bool is_edge_on_side(const unsigned int e, const unsigned int s) const override final
Definition: cell_inf_hex.C:253
The libMesh namespace provides an interface to certain functionality in the library.
virtual unsigned int local_edge_node(unsigned int edge, unsigned int edge_node) const override
Definition: cell_inf_hex.C:135
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
static const unsigned short int _second_order_adjacent_vertices[8][2]
For higher-order elements, namely InfHex16 and InfHex18, the matrices for adjacent vertices of second...
Definition: cell_inf_hex.h:250
virtual std::unique_ptr< Elem > side_ptr(const unsigned int i) override final
Definition: cell_inf_hex.C:146
static const int num_edges
Definition: cell_inf_hex.h:84
static const Real _master_points[18][3]
Master element node locations.
Definition: cell_inf_hex.h:265
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
Real length(const unsigned int n1, const unsigned int n2) const
Definition: elem.C:742
virtual unsigned int n_nodes() const =0
virtual unsigned int local_side_node(unsigned int side, unsigned int side_node) const override
Definition: cell_inf_hex.C:124
virtual bool is_edge(const unsigned int i) const override final
We number edges next.
Definition: cell_inf_hex.h:132
libmesh_assert(ctx)
virtual unsigned int n_vertices() const override final
Definition: cell_inf_hex.h:99
ElemQuality
Defines an enum for element quality metrics.
virtual unsigned int n_children() const override final
Definition: cell_inf_hex.h:122
static const unsigned int adjacent_edges_map[8][3]
This maps the node to the 3 (or fewer) edge ids adjacent to the node.
Definition: cell_inf_hex.h:275
virtual bool is_vertex(const unsigned int i) const override final
We number vertices first.
Definition: cell_inf_hex.h:127
virtual bool is_flipped() const override final
Definition: cell_inf_hex.C:273
virtual Point origin() const override
Definition: cell_inf.h:77
static const unsigned int side_nodes_map[num_sides][nodes_per_side]
This maps the node of the side to element node numbers.
static const unsigned int edge_sides_map[8][2]
This maps each edge to the sides that contain said edge.
Definition: cell_inf_hex.h:227
static const unsigned short int _second_order_vertex_child_index[18]
Vector that names the child vertex index for each second order node.
Definition: cell_inf_hex.h:260
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
subdomain_id_type subdomain_id() const
Definition: elem.h:2582
virtual std::vector< unsigned int > edges_adjacent_to_node(const unsigned int n) const override
Definition: cell_inf_hex.C:281
auto norm_sq(const T &a) -> decltype(std::norm(a))
Definition: tensor_tools.h:104
virtual bool contains_point(const Point &p, Real tol=TOLERANCE) const override
Definition: cell_inf_hex.C:459
virtual bool is_child_on_side(const unsigned int c, const unsigned int s) const override final
Definition: cell_inf_hex.C:242
virtual bool on_reference_element(const Point &p, const Real eps=TOLERANCE) const override final
Definition: cell_inf_hex.C:541
virtual Real quality(const ElemQuality q) const
Definition: elem.C:1774
virtual unsigned short dim() const override
Definition: cell_inf.h:66
virtual Real quality(const ElemQuality q) const override
Definition: cell_inf_hex.C:302
static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]
This maps the node of the side to element node numbers.
virtual unsigned int n_edges() const override final
Definition: cell_inf_hex.h:112
static dof_id_type compute_key(dof_id_type n0)
Definition: elem.h:3294
virtual Order default_order() const =0
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
static const unsigned short int _second_order_vertex_child_number[18]
Vector that names a child sharing each second order node.
Definition: cell_inf_hex.h:255
static const int nodes_per_edge
uint8_t dof_id_type
Definition: id_types.h:67
virtual unsigned int n_sides() const override final
Definition: cell_inf_hex.h:92
bool is_internal(const unsigned int i) const
Definition: elem.C:3566