libMesh
elem_test.C
Go to the documentation of this file.
1 #include "elem_test.h"
2 
3 #include <libmesh/boundary_info.h>
4 #include <libmesh/enum_elem_quality.h>
5 #include <libmesh/elem_side_builder.h>
6 #include <libmesh/mesh_modification.h>
7 #include <libmesh/mesh_refinement.h>
8 #include <libmesh/parallel_implementation.h>
9 #include <libmesh/enum_to_string.h>
10 
11 using namespace libMesh;
12 
13 template <ElemType elem_type>
14 class ElemTest : public PerElemTest<elem_type> {
15 public:
17  {
18  LOG_UNIT_TEST;
19 
20  for (const auto & elem :
21  this->_mesh->active_local_element_ptr_range())
22  {
23  const BoundingBox bbox = elem->loose_bounding_box();
24 
25  // The "loose" bounding box should actually be pretty tight
26  // in most of these cases, but for weirdly aligned triangles
27  // (such as occur in pyramid elements) it won't be, so we'll
28  // just test against a widened bounding box.
29  BoundingBox wide_bbox(elem->point(0), elem->point(0));
30 
31  for (unsigned int n = 0; n != elem->n_nodes(); ++n)
32  {
33  const Point & p = elem->point(n);
34 
35  if (!elem->infinite())
36  CPPUNIT_ASSERT(bbox.contains_point(p));
37 
38  wide_bbox.union_with
39  (BoundingBox(elem->point(n), elem->point(n)));
40  }
41 
42  wide_bbox.scale(1. / 3.);
43 
44  if (!elem->infinite() && elem->dim())
45  {
46  CPPUNIT_ASSERT(!bbox.contains_point(wide_bbox.min()));
47  CPPUNIT_ASSERT(!bbox.contains_point(wide_bbox.max()));
48  }
49  }
50  }
51 
52  void test_quality()
53  {
54  LOG_UNIT_TEST;
55 
56  for (const auto & elem : this->_mesh->active_local_element_ptr_range())
57  {
58  // EDGE_LENGTH_RATIO is one metric that is defined on all elements
59  const Real edge_length_ratio = elem->quality(EDGE_LENGTH_RATIO);
60 
61  // We use "0" to mean infinity rather than inf or NaN, and
62  // every quality other than that should be 1 or larger (worse)
63  CPPUNIT_ASSERT_LESSEQUAL(edge_length_ratio, Real(1)); // 1 <= edge_length_ratio
64 
65  // We're building isotropic meshes, where even elements
66  // dissected from cubes ought to have tolerable quality.
67  //
68  // Worst I see is 2 on tets, but let's add a little tolerance
69  // in case we decide to play with rotated meshes here later
70  CPPUNIT_ASSERT_LESSEQUAL(Real(2+TOLERANCE), edge_length_ratio); // edge_length_ratio <= 2
71 
72  // The MIN_ANGLE and MAX_ANGLE quality metrics are also defined on all elements
73  const Real min_angle = elem->quality(MIN_ANGLE);
74  const Real max_angle = elem->quality(MAX_ANGLE);
75 
76  // Reference Quads/Hexes have maximum internal angles of 90
77  // degrees, but pyramids actually have an obtuse interior
78  // angle of acos(-1/3) ~ 109.47 deg formed by edge pairs
79  // {(0, 4), (2, 4)} and {(1,4), (3,4)}.
80  if (elem->type() != C0POLYGON)
81  CPPUNIT_ASSERT_LESSEQUAL((std::acos(Real(-1)/3) * 180 / libMesh::pi) + TOLERANCE, max_angle);
82 
83  // We're doing some polygon testing with a squashed pentagon
84  // that has a 135 degree angle
85  CPPUNIT_ASSERT_LESSEQUAL(135 + TOLERANCE, max_angle);
86 
87  // Notes on minimum angle we expect to see:
88  // 1.) 1D Elements don't have interior angles, so the base
89  // class implementation currently returns 0 for those
90  // elements.
91  // 2.) Reference triangles/tetrahedra have min interior angle
92  // of 45 degrees, however, here we are checking Tets in a
93  // build_cube() mesh which have angles as small as
94  // acos(2/sqrt(6)) so we use that as our lower bound here.
95  if (elem->dim() > 1)
96  CPPUNIT_ASSERT_GREATEREQUAL((std::acos(Real(2)/std::sqrt(Real(6))) * 180 / libMesh::pi) - TOLERANCE, min_angle);
97 
98  // MIN,MAX_DIHEDRAL_ANGLE are implemented for all 3D elements
99  if (elem->dim() > 2)
100  {
101  const Real min_dihedral_angle = elem->quality(MIN_DIHEDRAL_ANGLE);
102  const Real max_dihedral_angle = elem->quality(MAX_DIHEDRAL_ANGLE);
103 
104  // Debugging
105  // libMesh::out << "Elem type: " << Utility::enum_to_string(elem->type())
106  // << ", min_dihedral_angle = " << min_dihedral_angle
107  // << ", max_dihedral_angle = " << max_dihedral_angle
108  // << std::endl;
109 
110  // Assert that we match expected values for build_cube() meshes.
111  // * build_cube() meshes of hexes, tetrahedra, and prisms have max_dihedral_angle == 90
112  // * build_cube() meshes of pyramids have max_dihedral_angle == 60 between adjacent triangular faces
113  CPPUNIT_ASSERT_LESSEQUAL (90 + TOLERANCE, max_dihedral_angle);
114 
115  // * build_cube() meshes of hexes have min_dihedral_angle == 90
116  // * build_cube() meshes of prisms, tets, and pyramids have min_dihedral_angle == 45
117  // * For the InfPrism tests, we construct a single
118  // InfPrism by hand with interior angle ~53.13 deg. so that
119  // is the minimum dihedral angle we expect in that case.
120  CPPUNIT_ASSERT_GREATEREQUAL(45 - TOLERANCE, min_dihedral_angle);
121  }
122 
123  // The JACOBIAN and SCALED_JACOBIAN quality metrics are also defined on all elements
124 
125  // The largest non-infinite elements in this test (Hexes and
126  // Prisms) have a nodal Jacobian (volume) of 8, e.g. the 2x2x2
127  // reference Hex. The infinite elements that we construct for
128  // this testing have a max nodal area of 64 since they are
129  // created (see setUp()) with max side lengths of 4 (4*4*4=64).
130  const Real jac = elem->quality(JACOBIAN);
131  if (elem->infinite())
132  CPPUNIT_ASSERT_LESSEQUAL (64 + TOLERANCE, jac);
133  else
134  CPPUNIT_ASSERT_LESSEQUAL (8 + TOLERANCE, jac);
135 
136  // The smallest 2D/3D nodal areas for regular elements in this
137  // test are found in tetrahedra, which have a minimum value of
138  // 2.0. However, we return a default value of 1.0 for 0D and
139  // 1D elements here, and we have a custom distorted-pentagon
140  // C0Polygon with a 0.5 at 2 nodes and a custom C0Polyhedron
141  // with a 1, so we handle those cases too.
142  if (elem->dim() < 2 || elem->type() == C0POLYHEDRON)
143  CPPUNIT_ASSERT_GREATEREQUAL(1 - TOLERANCE, jac);
144  else if (elem->type() == C0POLYGON)
145  CPPUNIT_ASSERT_GREATEREQUAL(0.5 - TOLERANCE, jac);
146  else
147  CPPUNIT_ASSERT_GREATEREQUAL(2 - TOLERANCE, jac);
148 
149  // The scaled Jacobian should always be <= 1. The minimum
150  // scaled Jacobian value I observed was ~0.408248 for the
151  // tetrahedral meshes. This is consistent with the way that
152  // we generate Tet meshes with build_cube(), as we don't
153  // simply refine the reference tetrahedron in that case.
154  const Real scaled_jac = elem->quality(SCALED_JACOBIAN);
155  CPPUNIT_ASSERT_LESSEQUAL (1 + TOLERANCE, scaled_jac);
156  CPPUNIT_ASSERT_GREATEREQUAL( Real(0.4), scaled_jac);
157 
158  // Debugging
159  // libMesh::out << "elem->type() = " << Utility::enum_to_string(elem->type())
160  // << ", jac = " << jac
161  // << ", scaled_jac = " << scaled_jac
162  // << std::endl;
163  }
164  }
165 
166  void test_maps()
167  {
168  LOG_UNIT_TEST;
169 
170  for (const auto & elem :
171  this->_mesh->active_local_element_ptr_range())
172  {
173  for (const auto edge : elem->edge_index_range())
174  for (const auto side_on_edge : elem->sides_on_edge(edge))
175  for (const auto node_on_edge : elem->nodes_on_edge(edge))
176  CPPUNIT_ASSERT(elem->is_node_on_side(node_on_edge, side_on_edge));
177 
178  for (const auto side : elem->side_index_range())
179  for (const auto node_on_side : elem->nodes_on_side(side))
180  CPPUNIT_ASSERT(elem->is_node_on_side(node_on_side, side));
181 
182  for (const auto edge : elem->edge_index_range())
183  for (const auto node_on_edge : elem->nodes_on_edge(edge))
184  CPPUNIT_ASSERT(elem->is_node_on_edge(node_on_edge, edge));
185 
186  for (const auto edge : elem->edge_index_range())
187  for (const auto side_on_edge : elem->sides_on_edge(edge))
188  CPPUNIT_ASSERT(elem->is_edge_on_side(edge, side_on_edge));
189  }
190  }
191 
193  {
194  LOG_UNIT_TEST;
195 
196  for (const auto & elem :
197  this->_mesh->active_local_element_ptr_range())
198  {
199  CPPUNIT_ASSERT(elem->n_nodes() <= Elem::max_n_nodes);
200 
201  const ElemType etype = elem->type();
202 
203  CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(elem->dim()),
204  Elem::type_to_dim_map[etype]);
205  CPPUNIT_ASSERT_EQUAL(elem->default_order(),
207 
208  // If we have an element type with topology defined solely by
209  // the type, then that should match the runtime topology. If
210  // not, then we should be aware of it.
211  if (!elem->runtime_topology())
212  {
213  CPPUNIT_ASSERT_EQUAL(elem->n_nodes(), Elem::type_to_n_nodes_map[etype]);
214  CPPUNIT_ASSERT_EQUAL(elem->n_sides(), Elem::type_to_n_sides_map[etype]);
215  CPPUNIT_ASSERT_EQUAL(elem->n_edges(), Elem::type_to_n_edges_map[etype]);
216  }
217  else
218  {
219  CPPUNIT_ASSERT_EQUAL(invalid_uint, Elem::type_to_n_nodes_map[etype]);
220  CPPUNIT_ASSERT_EQUAL(invalid_uint, Elem::type_to_n_sides_map[etype]);
221  CPPUNIT_ASSERT_EQUAL(invalid_uint, Elem::type_to_n_edges_map[etype]);
222  }
223  }
224  }
225 
227  {
228  LOG_UNIT_TEST;
229 
230  for (const auto & elem :
231  this->_mesh->active_local_element_ptr_range())
232  {
233  if (elem->infinite())
234  continue;
235 
236  for (const auto n : elem->node_index_range())
237 #ifndef LIBMESH_ENABLE_EXCEPTIONS
238  // If this node has a singular Jacobian, we need exceptions in order
239  // to catch the failed inverse_map solve and return the singular
240  // master point. Therefore, if we don't have exceptions and we're
241  // at a singular node, we can't test this. As of the writing of
242  // this comment, this issue exists for only Pyramid elements at
243  // the apex.
244  if (elem->local_singular_node(elem->point(n), TOLERANCE*TOLERANCE) == invalid_uint)
245 #endif
246  CPPUNIT_ASSERT(elem->contains_point(elem->point(n)));
247  }
248  }
249 
251  {
252  LOG_UNIT_TEST;
253 
254  for (const auto & elem :
255  this->_mesh->active_local_element_ptr_range())
256  {
257  if (elem->infinite())
258  continue;
259 
260  const Point centroid = elem->true_centroid();
261  const Point vertex_avg = elem->vertex_average();
262  Point quasicc;
263  if (elem->dim() < 3)
264  quasicc = elem->quasicircumcenter();
265 
266  for (const auto p : IntRange<unsigned int>(0, elem->n_permutations()))
267  {
268  elem->permute(p);
269  CPPUNIT_ASSERT(elem->has_invertible_map());
270  const Point new_centroid = elem->true_centroid();
271  const Point new_vertex_avg = elem->vertex_average();
272  Point new_quasicc;
273  if (elem->dim() < 3)
274  new_quasicc = elem->quasicircumcenter();
275  for (const auto d : make_range(LIBMESH_DIM))
276  {
277  // Getting a little FP error from Pyramid18
278  LIBMESH_ASSERT_FP_EQUAL(centroid(d), new_centroid(d),
279  TOLERANCE*std::sqrt(TOLERANCE));
280  LIBMESH_ASSERT_FP_EQUAL(vertex_avg(d), new_vertex_avg(d),
282  LIBMESH_ASSERT_FP_EQUAL(quasicc(d), new_quasicc(d),
284  }
285  }
286  }
287  }
288 
289  void test_flip()
290  {
291  LOG_UNIT_TEST;
292 
293  BoundaryInfo & boundary_info = this->_mesh->get_boundary_info();
294 
295  for (const auto & elem :
296  this->_mesh->active_local_element_ptr_range())
297  {
298  if (elem->infinite())
299  continue;
300 
301  if (elem->type() == C0POLYHEDRON)
302  continue;
303 
304  const Point vertex_avg = elem->vertex_average();
305 
306  const unsigned int n_sides = elem->n_sides();
307  std::vector<std::set<Point*>> side_nodes(n_sides);
308  std::vector<Elem*> neighbors(n_sides);
309  std::vector<std::vector<boundary_id_type>> bcids(n_sides);
310  for (auto s : make_range(n_sides))
311  {
312  for (auto n : elem->nodes_on_side(s))
313  side_nodes[s].insert(elem->node_ptr(n));
314  neighbors[s] = elem->neighbor_ptr(s);
315  boundary_info.boundary_ids(elem, s, bcids[s]);
316  }
317 
318  elem->flip(&boundary_info);
319 
320  // We should just be flipped, not twisted, so our map should
321  // still be affine.
322  // ... except for stupid singular pyramid maps
323  // ... or the polygons we're deliberately testing non-affine
324  if ((elem->dim() < 3 ||
325  elem->n_vertices() != 5) &&
326  elem_type != C0POLYGON)
327  CPPUNIT_ASSERT(elem->has_affine_map());
328  else if (elem_type == C0POLYGON)
329  CPPUNIT_ASSERT(!elem->has_affine_map());
330 
331  // The neighbors and bcids should have flipped in a way
332  // consistently with the nodes (unless this is a 0D NodeElem)
333  bool something_changed = false;
334  for (auto s : make_range(n_sides))
335  {
336  std::set<Point*> new_side_nodes;
337  for (auto n : elem->nodes_on_side(s))
338  new_side_nodes.insert(elem->node_ptr(n));
339 
340  std::vector<boundary_id_type> new_bcids;
341  boundary_info.boundary_ids(elem, s, new_bcids);
342 
343  unsigned int old_side = libMesh::invalid_uint;
344  for (auto os : make_range(n_sides))
345  if (new_side_nodes == side_nodes[os])
346  old_side = os;
347 
348  if (old_side != s)
349  something_changed = true;
350 
351  CPPUNIT_ASSERT(old_side != libMesh::invalid_uint);
352 
353  CPPUNIT_ASSERT(neighbors[old_side] ==
354  elem->neighbor_ptr(s));
355 
356  CPPUNIT_ASSERT(bcids[old_side] == new_bcids);
357  }
358  CPPUNIT_ASSERT(!elem->dim() || something_changed);
359 
360  const Point new_vertex_avg = elem->vertex_average();
361  for (const auto d : make_range(LIBMESH_DIM))
362  LIBMESH_ASSERT_FP_EQUAL(vertex_avg(d), new_vertex_avg(d),
364  }
365  }
366 
367  void test_orient()
368  {
369  LOG_UNIT_TEST;
370 
371  BoundaryInfo & boundary_info = this->_mesh->get_boundary_info();
372 
373  for (const auto & elem :
374  this->_mesh->active_local_element_ptr_range())
375  {
376  if (elem->infinite())
377  continue;
378 
379  const Point vertex_avg = elem->vertex_average();
380 
381  const unsigned int n_sides = elem->n_sides();
382  std::vector<std::set<Point*>> side_nodes(n_sides);
383  std::vector<Elem*> neighbors(n_sides);
384  std::vector<std::vector<boundary_id_type>> bcids(n_sides);
385  for (auto s : make_range(n_sides))
386  {
387  for (auto n : elem->nodes_on_side(s))
388  side_nodes[s].insert(elem->node_ptr(n));
389  neighbors[s] = elem->neighbor_ptr(s);
390  boundary_info.boundary_ids(elem, s, bcids[s]);
391  }
392 
393  CPPUNIT_ASSERT(!elem->is_flipped());
394 
395  if (elem->id()%2)
396  {
397  elem->flip(&boundary_info);
398  CPPUNIT_ASSERT(elem->is_flipped());
399  }
400 
401  elem->orient(&boundary_info);
402  CPPUNIT_ASSERT(!elem->is_flipped());
403 
404  // Our map should still be affine.
405  // ... except for stupid singular pyramid maps
406  // ... or the polygons we're deliberately testing non-affine
407  // ... or the polyhedra we deliberately don't define "affine
408  // map" for.
409  if ((elem->dim() < 3 ||
410  elem->n_vertices() != 5) &&
411  elem_type != C0POLYGON &&
412  elem_type != C0POLYHEDRON)
413  CPPUNIT_ASSERT(elem->has_affine_map());
414  else if (elem_type == C0POLYGON &&
415  elem_type != C0POLYHEDRON)
416  CPPUNIT_ASSERT(!elem->has_affine_map());
417 
418  // The neighbors and bcids should have flipped back to where
419  // they were.
420  for (auto s : make_range(n_sides))
421  {
422  std::set<Point*> new_side_nodes;
423  for (auto n : elem->nodes_on_side(s))
424  new_side_nodes.insert(elem->node_ptr(n));
425 
426  std::vector<boundary_id_type> new_bcids;
427  boundary_info.boundary_ids(elem, s, new_bcids);
428 
429  CPPUNIT_ASSERT(side_nodes[s] ==
430  new_side_nodes);
431 
432  CPPUNIT_ASSERT(neighbors[s] ==
433  elem->neighbor_ptr(s));
434 
435  CPPUNIT_ASSERT(bcids[s] == new_bcids);
436  }
437 
438  const Point new_vertex_avg = elem->vertex_average();
439  for (const auto d : make_range(LIBMESH_DIM))
440  LIBMESH_ASSERT_FP_EQUAL(vertex_avg(d), new_vertex_avg(d),
442  }
443  }
444 
446  {
447  LOG_UNIT_TEST;
448 
449  const Mesh old_mesh {*this->_mesh};
450 
451  BoundaryInfo & boundary_info = this->_mesh->get_boundary_info();
452  const BoundaryInfo & old_boundary_info = old_mesh.get_boundary_info();
453  CPPUNIT_ASSERT(&boundary_info != &old_boundary_info);
454 
455  for (const auto & elem :
456  this->_mesh->active_local_element_ptr_range())
457  {
458  if (elem->infinite())
459  continue;
460 
461  if (elem->id()%2)
462  {
463  elem->flip(&boundary_info);
464  CPPUNIT_ASSERT(elem->is_flipped());
465  }
466  }
467 
469 
470  // I should really create a MeshBase::operator==()...
471  for (const auto & elem :
472  this->_mesh->active_local_element_ptr_range())
473  {
474  const Elem & old_elem = old_mesh.elem_ref(elem->id());
475 
476  CPPUNIT_ASSERT(!elem->is_flipped());
477 
478  // Elem::operator==() uses node ids to compare
479  CPPUNIT_ASSERT(*elem == old_elem);
480 
481  const unsigned int n_sides = elem->n_sides();
482  for (auto s : make_range(n_sides))
483  {
484  std::vector<boundary_id_type> bcids, old_bcids;
485  boundary_info.boundary_ids(elem, s, bcids);
486  old_boundary_info.boundary_ids(&old_elem, s, old_bcids);
487  CPPUNIT_ASSERT(bcids == old_bcids);
488 
489  if (elem->neighbor_ptr(s))
490  {
491  CPPUNIT_ASSERT(old_elem.neighbor_ptr(s));
492  CPPUNIT_ASSERT_EQUAL(elem->neighbor_ptr(s)->id(),
493  old_elem.neighbor_ptr(s)->id());
494  }
495  else
496  CPPUNIT_ASSERT(!old_elem.neighbor_ptr(s));
497  }
498  }
499  }
500 
502  {
503  LOG_UNIT_TEST;
504 
505  for (const auto & elem :
506  this->_mesh->active_local_element_ptr_range())
507  for (const auto s : elem->side_index_range())
508  {
509  if (elem->type() == EDGE2 || elem->type() == EDGE3 || elem->type() == EDGE4)
510  CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s), elem->center_node_on_side(s));
511  else if (elem->type() == TRI6 || elem->type() == TRI7)
512  CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s + 3), elem->center_node_on_side(s));
513  else if (elem->type() == QUAD8 || elem->type() == QUAD9 ||
514  elem->type() == QUADSHELL8 || elem->type() == QUADSHELL9)
515  CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s + 4), elem->center_node_on_side(s));
516  else if (elem->type() == HEX27)
517  CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s + 20), elem->center_node_on_side(s));
518  else if (elem->type() == PRISM18 && s >= 1 && s <= 3)
519  CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s + 14), elem->center_node_on_side(s));
520  else if ((elem->type() == PRISM20 ||
521  elem->type() == PRISM21) && s >= 1 && s <= 3)
522  CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s + 14), elem->center_node_on_side(s));
523  else if (elem->type() == PRISM20 ||
524  elem->type() == PRISM21)
525  CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(18 + (s == 4)), elem->center_node_on_side(s));
526  else if (elem->type() == PYRAMID14 && s == 4)
527  CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(13), elem->center_node_on_side(s));
528  else if (elem->type() == PYRAMID18)
529  {
530  if (s < 4)
531  CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s + 14), elem->center_node_on_side(s));
532  else
533  CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(13), elem->center_node_on_side(s));
534  }
535  else
536  CPPUNIT_ASSERT_EQUAL(invalid_uint, elem->center_node_on_side(s));
537  }
538  }
539 
541  {
542  LOG_UNIT_TEST;
543 
544  for (const auto & elem :
545  this->_mesh->active_local_element_ptr_range())
546  for (const auto s : elem->side_index_range())
547  CPPUNIT_ASSERT_EQUAL(elem->build_side_ptr(s)->type(), elem->side_type(s));
548  }
549 
551  {
552  LOG_UNIT_TEST;
553 
554  for (const auto & elem :
555  this->_mesh->active_local_element_ptr_range())
556  {
557  std::unique_ptr<const Elem> side;
558 
559  for (const auto s : elem->side_index_range())
560  {
561  CPPUNIT_ASSERT_EQUAL(elem->build_side_ptr(s)->subdomain_id(), elem->subdomain_id());
562 
563  elem->build_side_ptr(side, s);
564  CPPUNIT_ASSERT_EQUAL(side->subdomain_id(), elem->subdomain_id());
565 
566  // We don't require that the "lightweight" side_ptr work
567  // the same way
568  // CPPUNIT_ASSERT_EQUAL(elem->side_ptr(s)->subdomain_id(), elem->subdomain_id());
569  }
570  }
571  }
572 
574  {
575  LOG_UNIT_TEST;
576 
577  ElemSideBuilder cache;
578  for (auto & elem : this->_mesh->active_local_element_ptr_range())
579  for (const auto s : elem->side_index_range())
580  {
581  const auto side = elem->build_side_ptr(s);
582 
583  auto & cached_side = cache(*elem, s);
584  CPPUNIT_ASSERT_EQUAL(side->type(), cached_side.type());
585  for (const auto n : side->node_index_range())
586  CPPUNIT_ASSERT_EQUAL(side->node_ref(n), cached_side.node_ref(n));
587 
588  const auto & const_cached_side = cache(const_cast<const Elem &>(*elem), s);
589  CPPUNIT_ASSERT_EQUAL(side->type(), const_cached_side.type());
590  for (const auto n : side->node_index_range())
591  CPPUNIT_ASSERT_EQUAL(side->node_ref(n), const_cached_side.node_ref(n));
592  }
593  }
594 
595  void test_n_refinements(unsigned int n)
596  {
597 #ifdef LIBMESH_ENABLE_AMR
598  // We don't support refinement of all element types
599  if (elem_type == EDGE4 ||
600  elem_type == PRISM20 ||
601  elem_type == PYRAMID5 ||
602  elem_type == PYRAMID13 ||
603  elem_type == PYRAMID14 ||
604  elem_type == PYRAMID18 ||
605  elem_type == C0POLYGON ||
606  elem_type == C0POLYHEDRON)
607  return;
608 
609  auto refining_mesh = this->_mesh->clone();
610 
611  MeshRefinement mr(*refining_mesh);
612  mr.uniformly_refine(n);
613 
614  std::set<std::pair<dof_id_type, unsigned int>> parent_node_was_touched;
615  std::set<std::pair<dof_id_type, unsigned int>> parent_child_was_touched;
616 
617  for (const Elem * elem : refining_mesh->active_element_ptr_range())
618  {
619  CPPUNIT_ASSERT_EQUAL(elem->level(), n);
620  CPPUNIT_ASSERT(!elem->ancestor());
621  CPPUNIT_ASSERT(elem->active());
622  CPPUNIT_ASSERT(!elem->subactive());
623  CPPUNIT_ASSERT(!elem->has_children());
624  CPPUNIT_ASSERT(!elem->has_ancestor_children());
625  CPPUNIT_ASSERT(!elem->interior_parent());
626 
627  const Elem * parent = elem->parent();
628  CPPUNIT_ASSERT(parent);
629  CPPUNIT_ASSERT(parent->ancestor());
630  CPPUNIT_ASSERT(!parent->active());
631  CPPUNIT_ASSERT(!parent->subactive());
632  CPPUNIT_ASSERT(parent->has_children());
633  CPPUNIT_ASSERT(!parent->has_ancestor_children());
634  CPPUNIT_ASSERT(!parent->interior_parent());
635  if (n == 1)
636  {
637  CPPUNIT_ASSERT_EQUAL(parent, elem->top_parent());
638  CPPUNIT_ASSERT_EQUAL(parent, parent->top_parent());
639  }
640  else
641  {
642  CPPUNIT_ASSERT(parent != elem->top_parent());
643  CPPUNIT_ASSERT(parent != parent->top_parent());
644  CPPUNIT_ASSERT_EQUAL(elem->top_parent(), parent->top_parent());
645  }
646 
647  CPPUNIT_ASSERT(parent->is_ancestor_of(elem));
648  const unsigned int c = parent->which_child_am_i(elem);
649  CPPUNIT_ASSERT(c < parent->n_children());
650  CPPUNIT_ASSERT_EQUAL(elem, parent->child_ptr(c));
651  parent_child_was_touched.emplace(parent->id(), c);
652 
653  CPPUNIT_ASSERT_EQUAL(parent->n_nodes_in_child(c), elem->n_nodes());
654  for (auto n : make_range(elem->n_nodes()))
655  {
656  CPPUNIT_ASSERT_EQUAL(parent->is_vertex_on_child(c, n), elem->is_vertex(n));
657 
658  auto pn = parent->as_parent_node(c, n);
659  CPPUNIT_ASSERT_EQUAL(pn, parent->get_node_index(elem->node_ptr(n)));
660  if (pn == libMesh::invalid_uint)
661  continue;
662  CPPUNIT_ASSERT_EQUAL(parent->is_vertex_on_parent(c, n), parent->is_vertex(pn));
663  parent_node_was_touched.emplace(parent->id(), pn);
664  }
665 
666  for (auto s : make_range(parent->n_sides()))
667  {
668  if (parent->is_child_on_side(c,s))
669  {
670  auto parent_side = parent->build_side_ptr(s);
671 
672  // Implicitly assuming here that s is the child side
673  // too - we support that now and hopefully won't have
674  // to change it later
675  auto child_side = elem->build_side_ptr(s);
676 
677  // 2D Inf FE inverse_map not yet implemented?
678  if (!parent_side->infinite())
679  for (const Node & node : child_side->node_ref_range())
680  CPPUNIT_ASSERT(parent_side->contains_point(node));
681 
682  if (elem->neighbor_ptr(s) && !elem->neighbor_ptr(s)->is_remote())
683  CPPUNIT_ASSERT_EQUAL(parent->child_neighbor(elem->neighbor_ptr(s)), elem);
684  }
685  }
686 
687  for (auto e : make_range(parent->n_edges()))
688  {
689  if (parent->is_child_on_edge(c,e))
690  {
691  auto parent_edge = parent->build_edge_ptr(e);
692 
693  // Implicitly assuming here that e is the child edge
694  // too - we support that now and hopefully won't have
695  // to change it later
696  auto child_edge = elem->build_edge_ptr(e);
697 
698  // 1D Inf FE inverse_map not yet implemented?
699  if (!parent_edge->infinite())
700  for (const Node & node : child_edge->node_ref_range())
701  CPPUNIT_ASSERT(parent_edge->contains_point(node));
702  }
703  }
704 
705  if (parent->has_affine_map())
706  CPPUNIT_ASSERT(elem->has_affine_map());
707  }
708 
709  // It's possible for a parent element on a distributed mesh to not
710  // have all its children available on any one processor
711  TestCommWorld->set_union(parent_child_was_touched);
712  TestCommWorld->set_union(parent_node_was_touched);
713 
714  for (const Elem * elem : refining_mesh->local_element_ptr_range())
715  {
716  if (elem->active())
717  continue;
718 
719  // With only one layer of refinement the family tree methods
720  // should have the full number of elements, even if some are
721  // remote.
722  if (n == 1)
723  {
724  std::vector<const Elem *> family;
725  elem->family_tree(family);
726  CPPUNIT_ASSERT_EQUAL(family.size(),
727  std::size_t(elem->n_children() + 1));
728 
729  family.clear();
730  elem->total_family_tree(family);
731  CPPUNIT_ASSERT_EQUAL(family.size(),
732  std::size_t(elem->n_children() + 1));
733 
734  family.clear();
735  elem->active_family_tree(family);
736  CPPUNIT_ASSERT_EQUAL(family.size(),
737  std::size_t(elem->n_children()));
738 
739  for (auto s : make_range(elem->n_sides()))
740  {
741  family.clear();
742  elem->active_family_tree_by_side(family,s);
743  if (!elem->build_side_ptr(s)->infinite())
744  CPPUNIT_ASSERT_EQUAL(double(family.size()),
745  std::pow(2.0, int(elem->dim()-1)));
746  else
747  CPPUNIT_ASSERT_EQUAL(double(family.size()),
748  std::pow(2.0, int(elem->dim()-2)));
749  for (const Elem * child : family)
750  {
751  if (child->is_remote())
752  continue;
753 
754  unsigned int c = elem->which_child_am_i(child);
755  CPPUNIT_ASSERT(elem->is_child_on_side(c, s));
756  }
757  }
758  }
759 
760  if (elem->level() + 1 == n)
761  {
762  for (auto c : make_range(elem->n_children()))
763  {
764  auto it = parent_child_was_touched.find(std::make_pair(elem->id(), c));
765  CPPUNIT_ASSERT(it != parent_child_was_touched.end());
766  }
767 
768  for (auto n : make_range(elem->n_nodes()))
769  {
770  auto it = parent_node_was_touched.find(std::make_pair(elem->id(), n));
771  CPPUNIT_ASSERT(it != parent_node_was_touched.end());
772  }
773  }
774  }
775 #endif
776  }
777 
779  {
780  LOG_UNIT_TEST;
781 
782  test_n_refinements(1);
783  }
784 
786  {
787  LOG_UNIT_TEST;
788 
789  test_n_refinements(2);
790  }
791 
793  {
794  LOG_UNIT_TEST;
795 
796  for (const auto & elem :
797  this->_mesh->active_local_element_ptr_range())
798  for (const auto nd : elem->node_index_range())
799  {
800  if ((elem->type() == EDGE3 || elem->type() == EDGE4) && nd >= 2)
801  CPPUNIT_ASSERT(elem->is_internal(nd));
802  else if (elem->type() == HEX27 && nd == 26)
803  CPPUNIT_ASSERT(elem->is_internal(nd));
804  else if (elem->type() == PRISM21 && nd == 20)
805  CPPUNIT_ASSERT(elem->is_internal(nd));
806  else if ((elem->type() == QUAD9 || elem->type() == QUADSHELL9) && nd == 8)
807  CPPUNIT_ASSERT(elem->is_internal(nd));
808  else if (elem->type() == TRI7 && nd == 6)
809  CPPUNIT_ASSERT(elem->is_internal(nd));
810  else if (elem->type() == INFHEX18 && nd == 17)
811  CPPUNIT_ASSERT(elem->is_internal(nd));
812  else if (elem->type() == INFQUAD6 && nd == 5)
813  CPPUNIT_ASSERT(elem->is_internal(nd));
814  else
815  CPPUNIT_ASSERT(!elem->is_internal(nd));
816  }
817  }
818 
820  {
821  LOG_UNIT_TEST;
822 
823  for (const auto & elem : this->_mesh->active_local_element_ptr_range())
824  {
825  for (const auto nd : elem->node_index_range())
826  {
827  auto adjacent_edge_ids = elem->edges_adjacent_to_node(nd);
828 
829  if (elem->dim() < 2)
830  {
831  // 0D elements don't have edges.
832  // 1D elements *are* "edges", but we don't consider
833  // them to *have* edges, so assert that here.
834  CPPUNIT_ASSERT(adjacent_edge_ids.empty());
835  }
836  else
837  {
838  // For 2D and 3D elements, on each edge which is
839  // claimed to be adjacent, check that "nd" is indeed
840  // on it
841  for (const auto & edge_id : adjacent_edge_ids)
842  {
843  auto node_ids_on_edge = elem->nodes_on_edge(edge_id);
844  CPPUNIT_ASSERT(std::find(node_ids_on_edge.begin(), node_ids_on_edge.end(), nd) != node_ids_on_edge.end());
845  }
846  }
847  }
848  }
849  }
850 
851 };
852 
853 #define ELEMTEST \
854  CPPUNIT_TEST( test_bounding_box ); \
855  CPPUNIT_TEST( test_quality ); \
856  CPPUNIT_TEST( test_node_edge_map_consistency ); \
857  CPPUNIT_TEST( test_maps ); \
858  CPPUNIT_TEST( test_static_data ); \
859  CPPUNIT_TEST( test_permute ); \
860  CPPUNIT_TEST( test_flip ); \
861  CPPUNIT_TEST( test_orient ); \
862  CPPUNIT_TEST( test_orient_elements ); \
863  CPPUNIT_TEST( test_contains_point_node ); \
864  CPPUNIT_TEST( test_center_node_on_side ); \
865  CPPUNIT_TEST( test_side_type ); \
866  CPPUNIT_TEST( test_side_subdomain ); \
867  CPPUNIT_TEST( test_elem_side_builder ); \
868  CPPUNIT_TEST( test_refinement ); \
869  CPPUNIT_TEST( test_double_refinement ); \
870  CPPUNIT_TEST( test_is_internal )
871 
872 #define INSTANTIATE_ELEMTEST(elemtype) \
873  class ElemTest_##elemtype : public ElemTest<elemtype> { \
874  public: \
875  ElemTest_##elemtype() : \
876  ElemTest<elemtype>() { \
877  if (unitlog->summarized_logs_enabled()) \
878  this->libmesh_suite_name = "ElemTest"; \
879  else \
880  this->libmesh_suite_name = "ElemTest_" #elemtype; \
881  } \
882  CPPUNIT_TEST_SUITE( ElemTest_##elemtype ); \
883  ELEMTEST; \
884  CPPUNIT_TEST_SUITE_END(); \
885  }; \
886  \
887  CPPUNIT_TEST_SUITE_REGISTRATION( ElemTest_##elemtype )
888 
890 
894 
895 #if LIBMESH_DIM > 1
900 
907 
908 // This just tests with one pentagon, but better than nothing
910 
911 // And this is just one truncated cube; also better than nothing
913 
914 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
917 #endif
918 #endif // LIBMESH_DIM > 1
919 
920 #if LIBMESH_DIM > 2
924 
928 
934 
935 // These tests use PointLocator, which uses contains_point(), which
936 // uses inverse_map(), which doesn't play nicely on Pyramids unless we
937 // have exceptions support
938 #ifdef LIBMESH_ENABLE_EXCEPTIONS
943 #endif
944 
945 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
949 
952 #endif
953 #endif // LIBMESH_DIM > 2
void test_center_node_on_side()
Definition: elem_test.C:501
static const Order type_to_default_order_map[INVALID_ELEM]
This array maps the integer representation of the ElemType enum to the default approximation order of...
Definition: elem.h:991
void test_orient_elements()
Definition: elem_test.C:445
ElemType
Defines an enum for geometric element types.
const Elem * parent() const
Definition: elem.h:3030
A Node is like a Point, but with more information.
Definition: node.h:52
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition: libmesh.h:310
static const unsigned int type_to_n_sides_map[INVALID_ELEM]
This array maps the integer representation of the ElemType enum to the number of sides on the element...
Definition: elem.h:685
void test_elem_side_builder()
Definition: elem_test.C:573
void test_n_refinements(unsigned int n)
Definition: elem_test.C:595
The IntRange templated class is intended to make it easy to loop over integers which are indices of a...
Definition: int_range.h:53
bool contains_point(const Point &) const
Definition: bounding_box.C:35
void test_static_data()
Definition: elem_test.C:192
libMesh::Parallel::Communicator * TestCommWorld
Definition: driver.C:171
static constexpr Real TOLERANCE
void test_flip()
Definition: elem_test.C:289
void test_node_edge_map_consistency()
Definition: elem_test.C:819
static const unsigned int type_to_dim_map[INVALID_ELEM]
This array maps the integer representation of the ElemType enum to the geometric dimension of the ele...
Definition: elem.h:635
void test_maps()
Definition: elem_test.C:166
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
Fills a user-provided std::vector with the boundary ids associated with Node node.
The libMesh namespace provides an interface to certain functionality in the library.
void test_side_subdomain()
Definition: elem_test.C:550
static const unsigned int type_to_n_nodes_map[INVALID_ELEM]
This array maps the integer representation of the ElemType enum to the number of nodes in the element...
Definition: elem.h:650
void test_is_internal()
Definition: elem_test.C:792
void test_double_refinement()
Definition: elem_test.C:785
T pow(const T &x)
Definition: utility.h:328
void test_side_type()
Definition: elem_test.C:540
Implements (adaptive) mesh refinement algorithms for a MeshBase.
dof_id_type id() const
Definition: dof_object.h:828
static const unsigned int type_to_n_edges_map[INVALID_ELEM]
This array maps the integer representation of the ElemType enum to the number of edges on the element...
Definition: elem.h:743
void test_bounding_box()
Definition: elem_test.C:16
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
Definition: boundary_info.h:57
INSTANTIATE_ELEMTEST(NODEELEM)
void orient_elements(MeshBase &mesh)
Redo the nodal ordering of each element as necessary to give the element Jacobian a positive orientat...
Helper for building element sides that minimizes the construction of new elements.
void test_contains_point_node()
Definition: elem_test.C:226
void test_permute()
Definition: elem_test.C:250
Defines a Cartesian bounding box by the two corner extremum.
Definition: bounding_box.h:40
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2598
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
void test_orient()
Definition: elem_test.C:367
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition: mesh.h:50
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
void test_quality()
Definition: elem_test.C:52
void test_refinement()
Definition: elem_test.C:778
static const unsigned int max_n_nodes
The maximum number of nodes any element can contain.
Definition: elem.h:661
const Real pi
.
Definition: libmesh.h:299
void uniformly_refine(unsigned int n=1)
Uniformly refines the mesh n times.
void set_union(T &data, const unsigned int root_id) const