libMesh
cell_inf_hex.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2024 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 
45 
46 // We need to require C++11...
47 const Real InfHex::_master_points[18][3] =
48  {
49  {-1, -1, 0},
50  {1, -1, 0},
51  {1, 1, 0},
52  {-1, 1, 0},
53  {-1, -1, 1},
54  {1, -1, 1},
55  {1, 1, 1},
56  {-1, 1, 1},
57  {0, -1, 0},
58  {1, 0, 0},
59  {0, 1, 0},
60  {-1, 0, 0},
61  {0, -1, 1},
62  {1, 0, 1},
63  {0, 1, 1},
64  {-1, 0, 1},
65  {0, 0, 0},
66  {0, 0, 1}
67  };
68 
69 const unsigned int InfHex::edge_sides_map[8][2] =
70  {
71  {0, 1}, // Edge 0
72  {0, 2}, // Edge 1
73  {0, 3}, // Edge 2
74  {0, 4}, // Edge 3
75  {1, 4}, // Edge 4
76  {1, 2}, // Edge 5
77  {2, 3}, // Edge 6
78  {3, 4} // Edge 7
79  };
80 
81 
82 
83 
84 // ------------------------------------------------------------
85 // InfHex class member functions
86 dof_id_type InfHex::key (const unsigned int s) const
87 {
88  libmesh_assert_less (s, this->n_sides());
89 
90  // The order of the node ids does not matter, they are sorted by the
91  // compute_key() function.
92  return this->compute_key(this->node_id(InfHex8::side_nodes_map[s][0]),
93  this->node_id(InfHex8::side_nodes_map[s][1]),
94  this->node_id(InfHex8::side_nodes_map[s][2]),
95  this->node_id(InfHex8::side_nodes_map[s][3]));
96 }
97 
98 
99 
100 dof_id_type InfHex::low_order_key (const unsigned int s) const
101 {
102  libmesh_assert_less (s, this->n_sides());
103 
104  // The order of the node ids does not matter, they are sorted by the
105  // compute_key() function.
106  return this->compute_key(this->node_id(InfHex8::side_nodes_map[s][0]),
107  this->node_id(InfHex8::side_nodes_map[s][1]),
108  this->node_id(InfHex8::side_nodes_map[s][2]),
109  this->node_id(InfHex8::side_nodes_map[s][3]));
110 }
111 
112 
113 
114 unsigned int InfHex::local_side_node(unsigned int side,
115  unsigned int side_node) const
116 {
117  libmesh_assert_less (side, this->n_sides());
118  libmesh_assert_less (side_node, InfHex8::nodes_per_side);
119 
120  return InfHex8::side_nodes_map[side][side_node];
121 }
122 
123 
124 
125 unsigned int InfHex::local_edge_node(unsigned int edge,
126  unsigned int edge_node) const
127 {
128  libmesh_assert_less (edge, this->n_edges());
129  libmesh_assert_less (edge_node, InfHex8::nodes_per_edge);
130 
131  return InfHex8::edge_nodes_map[edge][edge_node];
132 }
133 
134 
135 
136 std::unique_ptr<Elem> InfHex::side_ptr (const unsigned int i)
137 {
138  libmesh_assert_less (i, this->n_sides());
139 
140  // Return value
141  std::unique_ptr<Elem> face;
142 
143  // Think of a unit cube: (-1,1) x (-1,1) x (-1,1),
144  // with (in general) the normals pointing outwards
145  switch (i)
146  {
147  // the face at z = -1
148  // the base, where the infinite element couples to conventional
149  // elements
150  case 0:
151  {
152  // Oops, here we are, claiming the normal of the face
153  // elements point outwards -- and this is the exception:
154  // For the side built from the base face,
155  // the normal is pointing _into_ the element!
156  // Why is that? - In agreement with build_side_ptr(),
157  // which in turn _has_ to build the face in this
158  // way as to enable the cool way \p InfFE re-uses \p FE.
159  face = std::make_unique<Quad4>();
160  break;
161  }
162 
163  // These faces connect to other infinite elements.
164  case 1: // the face at y = -1
165  case 2: // the face at x = 1
166  case 3: // the face at y = 1
167  case 4: // the face at x = -1
168  {
169  face = std::make_unique<InfQuad4>();
170  break;
171  }
172 
173  default:
174  libmesh_error_msg("Invalid side i = " << i);
175  }
176 
177  // Set the nodes
178  for (auto n : face->node_index_range())
179  face->set_node(n) = this->node_ptr(InfHex8::side_nodes_map[i][n]);
180 
181  return face;
182 }
183 
184 
185 
186 void InfHex::side_ptr (std::unique_ptr<Elem> & side,
187  const unsigned int i)
188 {
189  libmesh_assert_less (i, this->n_sides());
190 
191  // Think of a unit cube: (-1,1) x (-1,1) x (1,1)
192  switch (i)
193  {
194  // the base face
195  case 0:
196  {
197  if (!side.get() || side->type() != QUAD4)
198  {
199  side = this->side_ptr(i);
200  return;
201  }
202  break;
203  }
204 
205  // connecting to another infinite element
206  case 1:
207  case 2:
208  case 3:
209  case 4:
210  {
211  if (!side.get() || side->type() != INFQUAD4)
212  {
213  side = this->side_ptr(i);
214  return;
215  }
216  break;
217  }
218 
219  default:
220  libmesh_error_msg("Invalid side i = " << i);
221  }
222 
223  side->subdomain_id() = this->subdomain_id();
224 
225  // Set the nodes
226  for (auto n : side->node_index_range())
227  side->set_node(n) = this->node_ptr(InfHex8::side_nodes_map[i][n]);
228 }
229 
230 
231 
232 bool InfHex::is_child_on_side(const unsigned int c,
233  const unsigned int s) const
234 {
235  libmesh_assert_less (c, this->n_children());
236  libmesh_assert_less (s, this->n_sides());
237 
238  return (s == 0 || c+1 == s || c == s%4);
239 }
240 
241 
242 
243 bool InfHex::is_edge_on_side (const unsigned int e,
244  const unsigned int s) const
245 {
246  libmesh_assert_less (e, this->n_edges());
247  libmesh_assert_less (s, this->n_sides());
248 
249  return (edge_sides_map[e][0] == s || edge_sides_map[e][1] == s);
250 }
251 
252 
253 
254 std::vector<unsigned int> InfHex::sides_on_edge(const unsigned int e) const
255 {
256  libmesh_assert_less(e, this->n_edges());
257 
258  return {edge_sides_map[e][0], edge_sides_map[e][1]};
259 }
260 
261 
262 bool
264 {
265  return (triple_product(this->point(1)-this->point(0),
266  this->point(3)-this->point(0),
267  this->point(4)-this->point(0)) < 0);
268 }
269 
270 
272 {
273  switch (q)
274  {
275 
285  case DIAGONAL:
286  {
287  // Diagonal between node 0 and node 2
288  const Real d02 = this->length(0,2);
289 
290  // Diagonal between node 1 and node 3
291  const Real d13 = this->length(1,3);
292 
293  // Find the biggest and smallest diagonals
294  const Real min = std::min(d02, d13);
295  const Real max = std::max(d02, d13);
296 
297  libmesh_assert_not_equal_to (max, 0.0);
298 
299  return min / max;
300 
301  break;
302  }
303 
311  case TAPER:
312  {
313 
317  const Real d01 = this->length(0,1);
318  const Real d12 = this->length(1,2);
319  const Real d23 = this->length(2,3);
320  const Real d03 = this->length(0,3);
321 
322  std::vector<Real> edge_ratios(2);
323 
324  // Bottom
325  edge_ratios[0] = std::min(d01, d23) / std::max(d01, d23);
326  edge_ratios[1] = std::min(d03, d12) / std::max(d03, d12);
327 
328  return *(std::min_element(edge_ratios.begin(), edge_ratios.end())) ;
329 
330  break;
331  }
332 
333 
341  case STRETCH:
342  {
346  const Real sqrt3 = 1.73205080756888;
347 
351  const Real d02 = this->length(0,2);
352  const Real d13 = this->length(1,3);
353  const Real max_diag = std::max(d02, d13);
354 
355  libmesh_assert_not_equal_to ( max_diag, 0.0 );
356 
360  std::vector<Real> edges(4);
361  edges[0] = this->length(0,1);
362  edges[1] = this->length(1,2);
363  edges[2] = this->length(2,3);
364  edges[3] = this->length(0,3);
365 
366  const Real min_edge = *(std::min_element(edges.begin(), edges.end()));
367  return sqrt3 * min_edge / max_diag ;
368  }
369 
370 
375  default:
376  return Elem::quality(q);
377  }
378 }
379 
380 
381 
382 std::pair<Real, Real> InfHex::qual_bounds (const ElemQuality) const
383 {
384  libmesh_not_implemented();
385 
386  /*
387  std::pair<Real, Real> bounds;
388 
389  switch (q)
390  {
391 
392  case ASPECT_RATIO:
393  bounds.first = 1.;
394  bounds.second = 4.;
395  break;
396 
397  case SKEW:
398  bounds.first = 0.;
399  bounds.second = 0.5;
400  break;
401 
402  case SHEAR:
403  case SHAPE:
404  bounds.first = 0.3;
405  bounds.second = 1.;
406  break;
407 
408  case CONDITION:
409  bounds.first = 1.;
410  bounds.second = 8.;
411  break;
412 
413  case JACOBIAN:
414  bounds.first = 0.5;
415  bounds.second = 1.;
416  break;
417 
418  case DISTORTION:
419  bounds.first = 0.6;
420  bounds.second = 1.;
421  break;
422 
423  case TAPER:
424  bounds.first = 0.;
425  bounds.second = 0.4;
426  break;
427 
428  case STRETCH:
429  bounds.first = 0.25;
430  bounds.second = 1.;
431  break;
432 
433  case DIAGONAL:
434  bounds.first = 0.65;
435  bounds.second = 1.;
436  break;
437 
438  case SIZE:
439  bounds.first = 0.5;
440  bounds.second = 1.;
441  break;
442 
443  default:
444  libMesh::out << "Warning: Invalid quality measure chosen." << std::endl;
445  bounds.first = -1;
446  bounds.second = -1;
447  }
448 
449  return bounds;
450  */
451 
452  return std::pair<Real,Real>();
453 }
454 
455 
456 
457 
458 
459 const unsigned short int InfHex::_second_order_adjacent_vertices[8][2] =
460  {
461  { 0, 1}, // vertices adjacent to node 8
462  { 1, 2}, // vertices adjacent to node 9
463  { 2, 3}, // vertices adjacent to node 10
464  { 0, 3}, // vertices adjacent to node 11
465 
466  { 4, 5}, // vertices adjacent to node 12
467  { 5, 6}, // vertices adjacent to node 13
468  { 6, 7}, // vertices adjacent to node 14
469  { 4, 7} // vertices adjacent to node 15
470  };
471 
472 
473 
474 const unsigned short int InfHex::_second_order_vertex_child_number[18] =
475  {
476  99,99,99,99,99,99,99,99, // Vertices
477  0,1,2,0, // Edges
478  0,1,2,0,0, // Faces
479  0 // Interior
480  };
481 
482 
483 
484 const unsigned short int InfHex::_second_order_vertex_child_index[18] =
485  {
486  99,99,99,99,99,99,99,99, // Vertices
487  1,2,3,3, // Edges
488  5,6,7,7,2, // Faces
489  6 // Interior
490  };
491 
492 
493 bool InfHex::contains_point (const Point & p, Real tol) const
494 {
495  // For infinite elements with linear base interpolation:
496  // make use of the fact that infinite elements do not
497  // live inside the envelope. Use a fast scheme to
498  // check whether point \p p is inside or outside
499  // our relevant part of the envelope. Note that
500  // this is not exclusive: only when the distance is less,
501  // we are safe. Otherwise, we cannot say anything. The
502  // envelope may be non-spherical, the physical point may lie
503  // inside the envelope, outside the envelope, or even inside
504  // this infinite element. Therefore if this fails,
505  // fall back to the inverse_map()
506  const Point my_origin (this->origin());
507 
508  // determine the minimal distance of the base from the origin
509  // Use norm_sq() instead of norm(), it is faster
510  Point pt0_o(this->point(0) - my_origin);
511  Point pt1_o(this->point(1) - my_origin);
512  Point pt2_o(this->point(2) - my_origin);
513  Point pt3_o(this->point(3) - my_origin);
514  const Real tmp_min_distance_sq = std::min(pt0_o.norm_sq(),
515  std::min(pt1_o.norm_sq(),
516  std::min(pt2_o.norm_sq(),
517  pt3_o.norm_sq())));
518 
519  // For a coarse grid, it is important to account for the fact
520  // that the sides are not spherical, thus the closest point
521  // can be closer than all edges.
522  // This is an estimator using Pythagoras:
523  const Real min_distance_sq = tmp_min_distance_sq
524  - .5*std::max((point(0)-point(2)).norm_sq(),
525  (point(1)-point(3)).norm_sq());
526 
527  // work with 1% allowable deviation. We can still fall
528  // back to the InfFE::inverse_map()
529  const Real conservative_p_dist_sq = 1.01 * (Point(p - my_origin).norm_sq());
530 
531 
532 
533  if (conservative_p_dist_sq < min_distance_sq)
534  {
535  // the physical point is definitely not contained in the element
536  return false;
537  }
538 
539  // this captures the case that the point is not (almost) in the direction of the element.:
540  // first, project the problem onto the unit sphere:
541  Point p_o(p - my_origin);
542  pt0_o /= pt0_o.norm();
543  pt1_o /= pt1_o.norm();
544  pt2_o /= pt2_o.norm();
545  pt3_o /= pt3_o.norm();
546  p_o /= p_o.norm();
547 
548 
549  // now, check if it is in the projected face; using that the diagonal contains
550  // the largest distance between points in it
551  Real max_h = std::max((pt0_o - pt2_o).norm_sq(),
552  (pt1_o - pt3_o).norm_sq())*1.01;
553 
554  if ((p_o - pt0_o).norm_sq() > max_h ||
555  (p_o - pt1_o).norm_sq() > max_h ||
556  (p_o - pt2_o).norm_sq() > max_h ||
557  (p_o - pt3_o).norm_sq() > max_h )
558  {
559  // the physical point is definitely not contained in the element
560  return false;
561  }
562 
563  // Declare a basic FEType. Will use default in the base,
564  // and something else (not important) in radial direction.
565  FEType fe_type(default_order());
566 
567  const Point mapped_point = InfFEMap::inverse_map(dim(), this, p,
568  tol, false);
569 
570  return FEInterface::on_reference_element(mapped_point, this->type(), tol);
571 }
572 
573 } // namespace libMesh
574 
575 
576 
577 
578 #endif // ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:182
virtual std::vector< unsigned int > sides_on_edge(const unsigned int e) const override final
Definition: cell_inf_hex.C:254
virtual dof_id_type low_order_key(const unsigned int s) const override
Definition: cell_inf_hex.C:100
auto norm() const -> decltype(std::norm(T()))
Definition: type_vector.h:929
virtual dof_id_type key() const
Definition: elem.C:563
virtual std::pair< Real, Real > qual_bounds(const ElemQuality q) const override
Definition: cell_inf_hex.C:382
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:243
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:125
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:238
virtual std::unique_ptr< Elem > side_ptr(const unsigned int i) override final
Definition: cell_inf_hex.C:136
static const Real _master_points[18][3]
Master element node locations.
Definition: cell_inf_hex.h:253
T triple_product(const TypeVector< T > &a, const TypeVector< T > &b, const TypeVector< T > &c)
Definition: type_vector.h:1068
auto norm_sq() const -> decltype(std::norm(T()))
Definition: type_vector.h:948
Real length(const unsigned int n1, const unsigned int n2) const
Definition: elem.C:552
virtual unsigned int local_side_node(unsigned int side, unsigned int side_node) const override
Definition: cell_inf_hex.C:114
ElemQuality
Defines an enum for element quality metrics.
virtual unsigned int n_children() const override final
Definition: cell_inf_hex.h:115
static bool on_reference_element(const Point &p, const ElemType t, const Real eps=TOLERANCE)
Definition: fe_interface.C:990
virtual bool is_flipped() const override final
Definition: cell_inf_hex.C:263
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:218
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:248
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
subdomain_id_type subdomain_id() const
Definition: elem.h:2391
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:2331
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:493
virtual bool is_child_on_side(const unsigned int c, const unsigned int s) const override final
Definition: cell_inf_hex.C:232
virtual Real quality(const ElemQuality q) const
Definition: elem.C:1582
virtual unsigned short dim() const override
Definition: cell_inf.h:66
virtual Real quality(const ElemQuality q) const override
Definition: cell_inf_hex.C:271
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:105
static dof_id_type compute_key(dof_id_type n0)
Definition: elem.h:3131
virtual Order default_order() const =0
virtual ElemType type() 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:2299
const Point & point(const unsigned int i) const
Definition: elem.h:2277
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:243
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:85