libMesh
Loading...
Searching...
No Matches
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/remote_elem.h>
10#include <libmesh/enum_to_string.h>
11#include <libmesh/elem_quality.h>
12#include <libmesh/node.h>
13
14using namespace libMesh;
15
16template <ElemType elem_type>
17class ElemTest : public PerElemTest<elem_type> {
18public:
20 {
21 LOG_UNIT_TEST;
22
23 for (const auto & elem :
24 this->_mesh->active_local_element_ptr_range())
25 {
26 const BoundingBox bbox = elem->loose_bounding_box();
27
28 // The "loose" bounding box should actually be pretty tight
29 // in most of these cases, but for weirdly aligned triangles
30 // (such as occur in pyramid elements) it won't be, so we'll
31 // just test against a widened bounding box.
32 BoundingBox wide_bbox(elem->point(0), elem->point(0));
33
34 for (unsigned int n = 0; n != elem->n_nodes(); ++n)
35 {
36 const Point & p = elem->point(n);
37
38 if (!elem->infinite())
39 CPPUNIT_ASSERT(bbox.contains_point(p));
40
41 wide_bbox.union_with
42 (BoundingBox(elem->point(n), elem->point(n)));
43 }
44
45 wide_bbox.scale(1. / 3.);
46
47 if (!elem->infinite() && elem->dim())
48 {
49 CPPUNIT_ASSERT(!bbox.contains_point(wide_bbox.min()));
50 CPPUNIT_ASSERT(!bbox.contains_point(wide_bbox.max()));
51 }
52 }
53 }
54
56 {
57 LOG_UNIT_TEST;
58
59 for (const auto & elem : this->_mesh->active_local_element_ptr_range())
60 {
61 // We don't currently support reference infinite elements
62 if (elem->infinite())
63 continue;
64
65 // We'll probably never support reference polytopes
66 if (elem->runtime_topology())
67 continue;
68
69 const Elem & ref_elem = *elem->reference_elem();
70
71 for (auto i : elem->node_index_range())
72 {
73 LIBMESH_ASSERT_REALVEC_EQUAL(elem->master_point(i),
74 ref_elem.point(i),
76 }
77 }
78 }
79
80 // The Elem::side_type/local_side_node/local_edge_node overloads that
81 // take an ElemType have to agree with the virtual versions they shadow;
82 // an element of each type is the only honest way to check that.
84 {
85 LOG_UNIT_TEST;
86
87 for (const auto & elem : this->_mesh->active_local_element_ptr_range())
88 {
89 const ElemType type = elem->type();
90
91 for (const auto s : elem->side_index_range())
92 {
93 const ElemType side_type = Elem::side_type(type, s);
94 CPPUNIT_ASSERT_EQUAL(elem->side_type(s), side_type);
95
96 // A polytope's side type follows from its element type even
97 // though its side count does not, but it has no static node
98 // map to check; neither are the infinite elements' maps read
99 // by these lookups, so both stop at the side type.
100 if (elem->runtime_topology() || elem->infinite())
101 continue;
102
103 const auto nodes = elem->nodes_on_side(s);
104 CPPUNIT_ASSERT_EQUAL(std::size_t(Elem::type_to_n_nodes_map[side_type]),
105 nodes.size());
106 for (auto n : index_range(nodes))
107 {
108 CPPUNIT_ASSERT_EQUAL(elem->local_side_node(s, n),
109 Elem::local_side_node(type, s, n));
110 CPPUNIT_ASSERT_EQUAL(nodes[n],
111 Elem::local_side_node(type, s, n));
112 }
113 }
114
115 // 1D elements have no edges. A 2D element's edges are its sides,
116 // but nodes_on_edge() and local_edge_node() are not the APIs the
117 // loop above exercised, so those are checked here too.
118 if (elem->infinite() || elem->dim() < 2)
119 continue;
120
121 for (const auto e : elem->edge_index_range())
122 {
123 // There is no virtual edge_type() to check against, but the
124 // element classes state the same fact when they build an
125 // edge, so compare with that instead
126 CPPUNIT_ASSERT_EQUAL(elem->build_edge_ptr(e)->type(),
127 Elem::edge_type(type));
128
129 if (elem->runtime_topology())
130 continue;
131
132 const auto nodes = elem->nodes_on_edge(e);
133 CPPUNIT_ASSERT_EQUAL(std::size_t(Elem::type_to_n_nodes_map[Elem::edge_type(type)]),
134 nodes.size());
135 for (auto n : index_range(nodes))
136 {
137 CPPUNIT_ASSERT_EQUAL(elem->local_edge_node(e, n),
138 Elem::local_edge_node(type, e, n));
139 CPPUNIT_ASSERT_EQUAL(nodes[n],
140 Elem::local_edge_node(type, e, n));
141 }
142 }
143 }
144 }
145
147 {
148 LOG_UNIT_TEST;
149
150 // The side/edge map consistency is a static_assert in each element
151 // header now; what's left to check at runtime is where the
152 // higher-order nodes sit. Every non-vertex reference node is at
153 // the centroid of the vertices of its edge, its face, or the whole
154 // element, except for EDGE4's nodes, which trisect it.
155 for (const auto & elem : this->_mesh->active_local_element_ptr_range())
156 {
157 if (elem->infinite() || elem->runtime_topology())
158 continue;
159
160 const ElemType type = elem->type();
161
162 for (const auto i : elem->node_index_range())
163 {
164 if (elem->is_vertex(i))
165 continue;
166
167 // EDGE4's interior nodes trisect it instead
168 if (type == EDGE4)
169 {
170 LIBMESH_ASSERT_REALVEC_EQUAL(Point(i == 2 ? Real(-1)/3 : Real(1)/3),
171 elem->master_point(i),
173 continue;
174 }
175
176 // Find the smallest subentity the node belongs to: an edge
177 // if it sits on one, else a face, else the element itself.
178 // Its vertices are what the node should be the centroid of.
179 std::vector<unsigned int> subentity_vertices;
180 if (elem->dim() > 1)
181 for (const auto e : elem->edge_index_range())
182 {
183 const auto nodes = elem->nodes_on_edge(e);
184 if (std::find(nodes.begin(), nodes.end(), i) != nodes.end())
185 subentity_vertices = {nodes[0], nodes[1]};
186 }
187 if (subentity_vertices.empty())
188 for (const auto s : elem->side_index_range())
189 {
190 const auto nodes = elem->nodes_on_side(s);
191 if (std::find(nodes.begin(), nodes.end(), i) != nodes.end())
192 for (auto n : nodes)
193 if (elem->is_vertex(n))
194 subentity_vertices.push_back(n);
195 }
196 if (subentity_vertices.empty())
197 for (const auto n : elem->node_index_range())
198 if (elem->is_vertex(n))
199 subentity_vertices.push_back(n);
200
201 Point centroid;
202 for (auto v : subentity_vertices)
203 centroid += elem->master_point(v);
204 centroid /= Real(subentity_vertices.size());
205
206 LIBMESH_ASSERT_REALVEC_EQUAL(centroid, elem->master_point(i),
208 }
209 }
210 }
211
213 {
214 LOG_UNIT_TEST;
215
216 for (const auto & elem : this->_mesh->active_local_element_ptr_range())
217 {
218 // EDGE_LENGTH_RATIO is one metric that is defined on all elements
219 const Real edge_length_ratio = elem->quality(EDGE_LENGTH_RATIO);
220
221 // We use "0" to mean infinity rather than inf or NaN, and
222 // every quality other than that should be 1 or larger (worse)
223 CPPUNIT_ASSERT_LESSEQUAL(edge_length_ratio, Real(1)); // 1 <= edge_length_ratio
224
225 // We're building isotropic meshes, where even elements
226 // dissected from cubes ought to have tolerable quality.
227 //
228 // Worst I see is 2 on tets and the skewed C0Polyhedron, but
229 // let's add a little tolerance in case we decide to play with
230 // rotated meshes here later.
231 CPPUNIT_ASSERT_LESSEQUAL(Real(2+TOLERANCE), edge_length_ratio); // edge_length_ratio <= 2
232
233 // The MIN_ANGLE and MAX_ANGLE quality metrics are also defined on all elements
234 const Real min_angle = elem->quality(MIN_ANGLE);
235 const Real max_angle = elem->quality(MAX_ANGLE);
236
237 // Reference Quads/Hexes have maximum internal angles of 90
238 // degrees, but pyramids actually have an obtuse interior
239 // angle of acos(-1/3) ~ 109.47 deg formed by edge pairs
240 // {(0, 4), (2, 4)} and {(1,4), (3,4)}.
241 if (elem->type() != C0POLYGON)
242 CPPUNIT_ASSERT_LESSEQUAL((std::acos(Real(-1)/3) * 180 / libMesh::pi) + TOLERANCE, max_angle);
243
244 // We're doing some polygon testing with a squashed pentagon
245 // that has a 135 degree angle
246 CPPUNIT_ASSERT_LESSEQUAL(135 + TOLERANCE, max_angle);
247
248 const auto assert_quality =
249 [elem](const ElemQuality quality,
250 const Real expected_value,
251 const Real expected_lower_bound,
252 const Real expected_upper_bound)
253 {
254 const auto bounds = elem->qual_bounds(quality);
255 LIBMESH_ASSERT_FP_EQUAL(expected_lower_bound, bounds.first, TOLERANCE);
256 LIBMESH_ASSERT_FP_EQUAL(expected_upper_bound, bounds.second, TOLERANCE);
257 LIBMESH_ASSERT_FP_EQUAL(expected_value, elem->quality(quality), TOLERANCE);
258 };
259
260 if (elem->type() == C0POLYGON)
261 {
262 const std::vector<ElemQuality> expected = {
264 JACOBIAN,
266 MAX_ANGLE,
268 };
269
270 CPPUNIT_ASSERT(expected == Quality::valid(elem->type()));
271 CPPUNIT_ASSERT_EQUAL(
272 std::string("Edge Length Ratio"),
274
275 assert_quality(EDGE_LENGTH_RATIO, std::sqrt(Real(2)), 1., 4.);
276 assert_quality(JACOBIAN, 0.5, 0.5, 1.);
277 assert_quality(
278 SCALED_JACOBIAN, Real(1) / std::sqrt(Real(2)), 0.5, 1.);
279 assert_quality(MAX_ANGLE, 135., 108., 144.);
280 assert_quality(MIN_ANGLE, 90., 54., 108.);
281 }
282 else if (elem->type() == C0POLYHEDRON)
283 {
284 const std::vector<ElemQuality> expected = {
286 MAX_ANGLE,
287 MIN_ANGLE,
290 };
291
292 CPPUNIT_ASSERT(expected == Quality::valid(elem->type()));
293
294 const Real min_polyhedron_angle =
295 std::acos(Real(1) / std::sqrt(Real(17))) *
296 Real(180) / libMesh::pi;
297 assert_quality(EDGE_LENGTH_RATIO, 2., 1., 4.);
298 assert_quality(
299 MAX_ANGLE, Real(180) - min_polyhedron_angle, 60., 180.);
300 assert_quality(MIN_ANGLE, min_polyhedron_angle, 30., 180.);
301 assert_quality(MAX_DIHEDRAL_ANGLE, 90., 60., 90.);
302 assert_quality(
303 MIN_DIHEDRAL_ANGLE, min_polyhedron_angle, 30., 90.);
304 }
305
306 // Notes on minimum angle we expect to see:
307 // 1.) 1D Elements don't have interior angles, so the base
308 // class implementation currently returns 0 for those
309 // elements.
310 // 2.) Reference triangles/tetrahedra have min interior angle
311 // of 45 degrees, however, here we are checking Tets in a
312 // build_cube() mesh which have angles as small as
313 // acos(2/sqrt(6)) so we use that as our lower bound here.
314 if (elem->dim() > 1)
315 CPPUNIT_ASSERT_GREATEREQUAL((std::acos(Real(2)/std::sqrt(Real(6))) * 180 / libMesh::pi) - TOLERANCE, min_angle);
316
317 // MIN,MAX_DIHEDRAL_ANGLE are implemented for all 3D elements
318 // tested here.
319 if (elem->dim() > 2)
320 {
321 const Real min_dihedral_angle = elem->quality(MIN_DIHEDRAL_ANGLE);
322 const Real max_dihedral_angle = elem->quality(MAX_DIHEDRAL_ANGLE);
323
324 // Debugging
325 // libMesh::out << "Elem type: " << Utility::enum_to_string(elem->type())
326 // << ", min_dihedral_angle = " << min_dihedral_angle
327 // << ", max_dihedral_angle = " << max_dihedral_angle
328 // << std::endl;
329
330 // Assert that we match expected values for build_cube() meshes.
331 // * build_cube() meshes of hexes, tetrahedra, and prisms have max_dihedral_angle == 90
332 // * build_cube() meshes of pyramids have max_dihedral_angle == 60 between adjacent triangular faces
333 CPPUNIT_ASSERT_LESSEQUAL (90 + TOLERANCE, max_dihedral_angle);
334
335 // * build_cube() meshes of hexes have min_dihedral_angle == 90
336 // * build_cube() meshes of prisms, tets, and pyramids have min_dihedral_angle == 45
337 // * For the InfPrism tests, we construct a single
338 // InfPrism by hand with interior angle ~53.13 deg. so that
339 // is the minimum dihedral angle we expect in that case.
340 CPPUNIT_ASSERT_GREATEREQUAL(45 - TOLERANCE, min_dihedral_angle);
341 }
342
343 // The generic JACOBIAN metrics require exactly dim() adjacent
344 // edges at a vertex, which is not guaranteed for arbitrary
345 // C0Polyhedron topology.
346 if (elem->type() != C0POLYHEDRON)
347 {
348 // The largest non-infinite elements in this test (Hexes and
349 // Prisms) have a nodal Jacobian (volume) of 8, e.g. the 2x2x2
350 // reference Hex. The infinite elements that we construct for
351 // this testing have a max nodal area of 64 since they are
352 // created (see setUp()) with max side lengths of 4 (4*4*4=64).
353 const Real jac = elem->quality(JACOBIAN);
354 if (elem->infinite())
355 CPPUNIT_ASSERT_LESSEQUAL (64 + TOLERANCE, jac);
356 else
357 CPPUNIT_ASSERT_LESSEQUAL (8 + TOLERANCE, jac);
358
359 // The smallest 2D/3D nodal areas for regular elements in this
360 // test are found in tetrahedra, which have a minimum value of
361 // 2.0. However, we return a default value of 1.0 for 0D and
362 // 1D elements here, and we have a custom distorted-pentagon
363 // C0Polygon with a 0.5 at 3 nodes, so we handle those cases
364 // too.
365 if (elem->dim() < 2)
366 CPPUNIT_ASSERT_GREATEREQUAL(1 - TOLERANCE, jac);
367 else if (elem->type() == C0POLYGON)
368 CPPUNIT_ASSERT_GREATEREQUAL(0.5 - TOLERANCE, jac);
369 else
370 CPPUNIT_ASSERT_GREATEREQUAL(2 - TOLERANCE, jac);
371
372 // The scaled Jacobian should always be <= 1. The minimum
373 // scaled Jacobian value I observed was ~0.408248 for the
374 // tetrahedral meshes. This is consistent with the way that
375 // we generate Tet meshes with build_cube(), as we don't
376 // simply refine the reference tetrahedron in that case.
377 const Real scaled_jac = elem->quality(SCALED_JACOBIAN);
378 CPPUNIT_ASSERT_LESSEQUAL (1 + TOLERANCE, scaled_jac);
379 CPPUNIT_ASSERT_GREATEREQUAL( Real(0.4), scaled_jac);
380
381 // Debugging
382 // libMesh::out << "elem->type() = " << Utility::enum_to_string(elem->type())
383 // << ", jac = " << jac
384 // << ", scaled_jac = " << scaled_jac
385 // << std::endl;
386 }
387 }
388 }
389
391 {
392 LOG_UNIT_TEST;
393
394 // Every quality metric that libMesh considers valid for a type
395 // should evaluate to a value inside its suggested qual_bounds() on
396 // a reasonably-shaped element of that type.
397 for (const auto & elem :
398 this->_mesh->active_local_element_ptr_range())
399 {
400 // qual_bounds() isn't well defined for infinite elements.
401 if (elem->infinite())
402 continue;
403
404 for (const ElemQuality q : Quality::valid(elem->type()))
405 {
406 const std::pair<Real, Real> bounds = elem->qual_bounds(q);
407
408 // A metric may be listed as valid for a type without having
409 // suggested bounds defined; qual_bounds() returns (-1, -1)
410 // as a sentinel in that case, which we skip.
411 if (bounds.first == -1. && bounds.second == -1.)
412 continue;
413
414 const Real value = elem->quality(q);
415
416 // build_cube()'s particular tet dissection (splitting each
417 // cube into 6 tets) is a bit more elongated than the
418 // "suggested" SCALED_JACOBIAN range assumes: its measured
419 // floor is ~0.408 (see the SCALED_JACOBIAN check in
420 // test_quality() above), just under the suggested 0.5.
421 // That's a real, known property of this specific mesh, not
422 // a quality regression, so widen the tolerance for exactly
423 // this combination.
424 const bool is_tet = (elem->type() == TET4 ||
425 elem->type() == TET10 ||
426 elem->type() == TET14);
427 const Real tol = (q == SCALED_JACOBIAN && is_tet) ?
428 Real(0.1) : TOLERANCE;
429
430 std::ostringstream msg;
431 msg << "Quality metric " << Utility::enum_to_string(q)
432 << " on a " << Utility::enum_to_string(elem->type())
433 << " evaluated to " << value
434 << ", outside its suggested bounds ["
435 << bounds.first << ", " << bounds.second << "]";
436
437 CPPUNIT_ASSERT_MESSAGE(msg.str(),
438 value >= bounds.first - tol &&
439 value <= bounds.second + tol);
440 }
441 }
442 }
443
445 {
446 LOG_UNIT_TEST;
447
448 // A genuine element-shape quality metric cannot depend on how the
449 // element's nodes happen to be numbered: permuting the nodes (i.e.
450 // relabeling which physical corner sits at which local index, via
451 // one of the reference element's symmetries) leaves the physical
452 // shape -- and therefore every quality metric -- unchanged.
453 for (const auto & elem :
454 this->_mesh->active_local_element_ptr_range())
455 {
456 // Permutations of infinite elements aren't well defined.
457 if (elem->infinite())
458 continue;
459
460 const std::vector<ElemQuality> valid_metrics =
461 Quality::valid(elem->type());
462
463 if (valid_metrics.empty())
464 continue;
465
466 std::vector<Real> baseline(valid_metrics.size());
467 for (std::size_t i = 0; i != valid_metrics.size(); ++i)
468 baseline[i] = elem->quality(valid_metrics[i]);
469
470 for (const auto p : IntRange<unsigned int>(0, elem->n_permutations()))
471 {
472 elem->permute(p);
473
474 for (std::size_t i = 0; i != valid_metrics.size(); ++i)
475 {
476 const Real permuted_value = elem->quality(valid_metrics[i]);
477
478 std::ostringstream msg;
479 msg << "Quality metric " << Utility::enum_to_string(valid_metrics[i])
480 << " on a " << Utility::enum_to_string(elem->type())
481 << " changed from " << baseline[i]
482 << " to " << permuted_value
483 << " after applying permutation " << p;
484
485 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE
486 (msg.str(), baseline[i], permuted_value, TOLERANCE);
487 }
488 }
489 }
490 }
491
493 {
494 LOG_UNIT_TEST;
495
496 for (const auto & elem :
497 this->_mesh->active_local_element_ptr_range())
498 {
499 for (const auto edge : elem->edge_index_range())
500 for (const auto side_on_edge : elem->sides_on_edge(edge))
501 for (const auto node_on_edge : elem->nodes_on_edge(edge))
502 CPPUNIT_ASSERT(elem->is_node_on_side(node_on_edge, side_on_edge));
503
504 for (const auto side : elem->side_index_range())
505 for (const auto node_on_side : elem->nodes_on_side(side))
506 CPPUNIT_ASSERT(elem->is_node_on_side(node_on_side, side));
507
508 for (const auto edge : elem->edge_index_range())
509 for (const auto node_on_edge : elem->nodes_on_edge(edge))
510 CPPUNIT_ASSERT(elem->is_node_on_edge(node_on_edge, edge));
511
512 for (const auto edge : elem->edge_index_range())
513 for (const auto side_on_edge : elem->sides_on_edge(edge))
514 CPPUNIT_ASSERT(elem->is_edge_on_side(edge, side_on_edge));
515 }
516 }
517
519 {
520 LOG_UNIT_TEST;
521
522 for (const auto & elem :
523 this->_mesh->active_local_element_ptr_range())
524 {
525 CPPUNIT_ASSERT(elem->n_nodes() <= Elem::max_n_nodes);
526
527 const ElemType etype = elem->type();
528
529 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(elem->dim()),
530 Elem::type_to_dim_map[etype]);
531 CPPUNIT_ASSERT_EQUAL(elem->default_order(),
533
534 // If we have an element type with topology defined solely by
535 // the type, then that should match the runtime topology. If
536 // not, then we should be aware of it.
537 if (!elem->runtime_topology())
538 {
539 CPPUNIT_ASSERT_EQUAL(elem->n_nodes(), Elem::type_to_n_nodes_map[etype]);
540 CPPUNIT_ASSERT_EQUAL(elem->n_sides(), Elem::type_to_n_sides_map[etype]);
541 CPPUNIT_ASSERT_EQUAL(elem->n_edges(), Elem::type_to_n_edges_map[etype]);
542 }
543 else
544 {
545 CPPUNIT_ASSERT_EQUAL(invalid_uint, Elem::type_to_n_nodes_map[etype]);
546 CPPUNIT_ASSERT_EQUAL(invalid_uint, Elem::type_to_n_sides_map[etype]);
547 CPPUNIT_ASSERT_EQUAL(invalid_uint, Elem::type_to_n_edges_map[etype]);
548 }
549 }
550 }
551
553 {
554 LOG_UNIT_TEST;
555
556 for (const auto & elem :
557 this->_mesh->active_local_element_ptr_range())
558 {
559 if (elem->infinite())
560 continue;
561
562 for (const auto n : elem->node_index_range())
563#ifndef LIBMESH_ENABLE_EXCEPTIONS
564 // If this node has a singular Jacobian, we need exceptions in order
565 // to catch the failed inverse_map solve and return the singular
566 // master point. Therefore, if we don't have exceptions and we're
567 // at a singular node, we can't test this. As of the writing of
568 // this comment, this issue exists for only Pyramid elements at
569 // the apex.
570 if (elem->local_singular_node(elem->point(n), TOLERANCE*TOLERANCE) == invalid_uint)
571#endif
572 CPPUNIT_ASSERT(elem->contains_point(elem->point(n)));
573 }
574 }
575
577 {
578 LOG_UNIT_TEST;
579
580 for (const auto & elem :
581 this->_mesh->active_local_element_ptr_range())
582 {
583 if (elem->infinite())
584 continue;
585
586 const Point centroid = elem->true_centroid();
587 const Point vertex_avg = elem->vertex_average();
588 Point quasicc;
589 if (elem->dim() < 3)
590 quasicc = elem->quasicircumcenter();
591
592 for (const auto p : IntRange<unsigned int>(0, elem->n_permutations()))
593 {
594 elem->permute(p);
595 CPPUNIT_ASSERT(elem->has_invertible_map());
596 const Point new_centroid = elem->true_centroid();
597 const Point new_vertex_avg = elem->vertex_average();
598 Point new_quasicc;
599 if (elem->dim() < 3)
600 new_quasicc = elem->quasicircumcenter();
601 for (const auto d : make_range(LIBMESH_DIM))
602 {
603 // Getting a little FP error from Pyramid18
604 LIBMESH_ASSERT_FP_EQUAL(centroid(d), new_centroid(d),
605 TOLERANCE*std::sqrt(TOLERANCE));
606 LIBMESH_ASSERT_FP_EQUAL(vertex_avg(d), new_vertex_avg(d),
608 LIBMESH_ASSERT_FP_EQUAL(quasicc(d), new_quasicc(d),
610 }
611 }
612 }
613 }
614
616 {
617 LOG_UNIT_TEST;
618
619 BoundaryInfo & boundary_info = this->_mesh->get_boundary_info();
620
621 for (const auto & elem :
622 this->_mesh->active_local_element_ptr_range())
623 {
624 if (elem->infinite())
625 continue;
626
627 if (elem->type() == C0POLYHEDRON)
628 continue;
629
630 const Point vertex_avg = elem->vertex_average();
631
632 const unsigned int n_sides = elem->n_sides();
633 std::vector<std::set<Point*>> side_nodes(n_sides);
634 std::vector<Elem*> neighbors(n_sides);
635 std::vector<std::vector<boundary_id_type>> bcids(n_sides);
636 for (auto s : make_range(n_sides))
637 {
638 for (auto n : elem->nodes_on_side(s))
639 side_nodes[s].insert(elem->node_ptr(n));
640 neighbors[s] = elem->neighbor_ptr(s);
641 boundary_info.boundary_ids(elem, s, bcids[s]);
642 }
643
644 elem->flip(&boundary_info);
645
646 // We should just be flipped, not twisted, so our map should
647 // still be affine.
648 // ... except for stupid singular pyramid maps
649 // ... or the polygons we're deliberately testing non-affine
650 if ((elem->dim() < 3 ||
651 elem->n_vertices() != 5) &&
652 elem_type != C0POLYGON)
653 CPPUNIT_ASSERT(elem->has_affine_map());
654 else if (elem_type == C0POLYGON)
655 CPPUNIT_ASSERT(!elem->has_affine_map());
656
657 // The neighbors and bcids should have flipped in a way
658 // consistently with the nodes (unless this is a 0D NodeElem)
659 bool something_changed = false;
660 for (auto s : make_range(n_sides))
661 {
662 std::set<Point*> new_side_nodes;
663 for (auto n : elem->nodes_on_side(s))
664 new_side_nodes.insert(elem->node_ptr(n));
665
666 std::vector<boundary_id_type> new_bcids;
667 boundary_info.boundary_ids(elem, s, new_bcids);
668
669 unsigned int old_side = libMesh::invalid_uint;
670 for (auto os : make_range(n_sides))
671 if (new_side_nodes == side_nodes[os])
672 old_side = os;
673
674 if (old_side != s)
675 something_changed = true;
676
677 CPPUNIT_ASSERT(old_side != libMesh::invalid_uint);
678
679 CPPUNIT_ASSERT(neighbors[old_side] ==
680 elem->neighbor_ptr(s));
681
682 CPPUNIT_ASSERT(bcids[old_side] == new_bcids);
683 }
684 CPPUNIT_ASSERT(!elem->dim() || something_changed);
685
686 const Point new_vertex_avg = elem->vertex_average();
687 for (const auto d : make_range(LIBMESH_DIM))
688 LIBMESH_ASSERT_FP_EQUAL(vertex_avg(d), new_vertex_avg(d),
690 }
691 }
692
694 {
695 LOG_UNIT_TEST;
696
697 BoundaryInfo & boundary_info = this->_mesh->get_boundary_info();
698
699 for (const auto & elem :
700 this->_mesh->active_local_element_ptr_range())
701 {
702 if (elem->infinite())
703 continue;
704
705 const Point vertex_avg = elem->vertex_average();
706
707 const unsigned int n_sides = elem->n_sides();
708 std::vector<std::set<Point*>> side_nodes(n_sides);
709 std::vector<Elem*> neighbors(n_sides);
710 std::vector<std::vector<boundary_id_type>> bcids(n_sides);
711 for (auto s : make_range(n_sides))
712 {
713 for (auto n : elem->nodes_on_side(s))
714 side_nodes[s].insert(elem->node_ptr(n));
715 neighbors[s] = elem->neighbor_ptr(s);
716 boundary_info.boundary_ids(elem, s, bcids[s]);
717 }
718
719 CPPUNIT_ASSERT(!elem->is_flipped());
720
721 if (elem->id()%2)
722 {
723 elem->flip(&boundary_info);
724 CPPUNIT_ASSERT(elem->is_flipped());
725 }
726
727 elem->orient(&boundary_info);
728 CPPUNIT_ASSERT(!elem->is_flipped());
729
730 // Our map should still be affine.
731 // ... except for stupid singular pyramid maps
732 // ... or the polygons we're deliberately testing non-affine
733 // ... or the polyhedra we deliberately don't define "affine
734 // map" for.
735 if ((elem->dim() < 3 ||
736 elem->n_vertices() != 5) &&
737 elem_type != C0POLYGON &&
738 elem_type != C0POLYHEDRON)
739 CPPUNIT_ASSERT(elem->has_affine_map());
740 else if (elem_type == C0POLYGON &&
741 elem_type != C0POLYHEDRON)
742 CPPUNIT_ASSERT(!elem->has_affine_map());
743
744 // The neighbors and bcids should have flipped back to where
745 // they were.
746 for (auto s : make_range(n_sides))
747 {
748 std::set<Point*> new_side_nodes;
749 for (auto n : elem->nodes_on_side(s))
750 new_side_nodes.insert(elem->node_ptr(n));
751
752 std::vector<boundary_id_type> new_bcids;
753 boundary_info.boundary_ids(elem, s, new_bcids);
754
755 CPPUNIT_ASSERT(side_nodes[s] ==
756 new_side_nodes);
757
758 CPPUNIT_ASSERT(neighbors[s] ==
759 elem->neighbor_ptr(s));
760
761 CPPUNIT_ASSERT(bcids[s] == new_bcids);
762 }
763
764 const Point new_vertex_avg = elem->vertex_average();
765 for (const auto d : make_range(LIBMESH_DIM))
766 LIBMESH_ASSERT_FP_EQUAL(vertex_avg(d), new_vertex_avg(d),
768 }
769 }
770
772 {
773 LOG_UNIT_TEST;
774
775 const Mesh old_mesh {*this->_mesh};
776
777 BoundaryInfo & boundary_info = this->_mesh->get_boundary_info();
778 const BoundaryInfo & old_boundary_info = old_mesh.get_boundary_info();
779 CPPUNIT_ASSERT(&boundary_info != &old_boundary_info);
780
781 for (const auto & elem :
782 this->_mesh->active_local_element_ptr_range())
783 {
784 if (elem->infinite())
785 continue;
786
787 if (elem->id()%2)
788 {
789 elem->flip(&boundary_info);
790 CPPUNIT_ASSERT(elem->is_flipped());
791 }
792 }
793
795
796 // I should really create a MeshBase::operator==()...
797 for (const auto & elem :
798 this->_mesh->active_local_element_ptr_range())
799 {
800 const Elem & old_elem = old_mesh.elem_ref(elem->id());
801
802 CPPUNIT_ASSERT(!elem->is_flipped());
803
804 // Elem::operator==() uses node ids to compare
805 CPPUNIT_ASSERT(*elem == old_elem);
806
807 const unsigned int n_sides = elem->n_sides();
808 for (auto s : make_range(n_sides))
809 {
810 std::vector<boundary_id_type> bcids, old_bcids;
811 boundary_info.boundary_ids(elem, s, bcids);
812 old_boundary_info.boundary_ids(&old_elem, s, old_bcids);
813 CPPUNIT_ASSERT(bcids == old_bcids);
814
815 if (elem->neighbor_ptr(s))
816 {
817 CPPUNIT_ASSERT(old_elem.neighbor_ptr(s));
818 CPPUNIT_ASSERT_EQUAL(elem->neighbor_ptr(s)->id(),
819 old_elem.neighbor_ptr(s)->id());
820 }
821 else
822 CPPUNIT_ASSERT(!old_elem.neighbor_ptr(s));
823 }
824 }
825 }
826
828 std::vector<const Node *> oriented_face_nodes(const Elem & elem, const unsigned int face)
829 {
830 std::vector<const Node *> sequence;
831 for (const auto v : elem.oriented_face_vertices(face))
832 sequence.push_back(elem.node_ptr(v));
833
834 return sequence;
835 }
836
838 {
839 LOG_UNIT_TEST;
840
841 for (const auto & elem : this->_mesh->active_local_element_ptr_range())
842 {
843 if (elem->infinite() || elem->type() == C0POLYHEDRON)
844 continue;
845
846 // Permuting renumbers the vertices without moving them
847 for (const auto p : make_range(elem->n_permutations()))
848 {
849 elem->permute(p);
850
851 for (const auto e : make_range(elem->n_edges()))
852 {
853 const bool positive = elem->point(elem->local_edge_node(e, 0)) >
854 elem->point(elem->local_edge_node(e, 1));
855
856 CPPUNIT_ASSERT_EQUAL(positive, elem->positive_edge_orientation(e));
857 }
858
859 for (const auto f : make_range(elem->n_faces()))
860 {
861 const unsigned int n_vertices =
862 Elem::type_to_n_sides_map[elem->side_type(f)];
863 const std::vector<unsigned int> nodes = elem->nodes_on_side(f);
864
865 // The least vertex of the face and the direction from it
866 unsigned int least = 0;
867 for (const auto v : make_range(1u, n_vertices))
868 if (elem->point(nodes[v]) < elem->point(nodes[least]))
869 least = v;
870
871 const bool positive =
872 elem->point(nodes[(least + n_vertices - 1) % n_vertices]) <
873 elem->point(nodes[(least + 1) % n_vertices]);
874
875 const unsigned int orientation = elem->face_orientation(f);
876
877 CPPUNIT_ASSERT_LESS(2 * n_vertices, orientation);
878 CPPUNIT_ASSERT_EQUAL(2 * least + static_cast<unsigned int>(positive),
879 orientation);
880 CPPUNIT_ASSERT_EQUAL(positive, elem->positive_face_orientation(f));
881 }
882 }
883 }
884 }
885
887 {
888 LOG_UNIT_TEST;
889
890 // Elements sharing a face may have different orientation indices for it, but the indices
891 // must encode the same vertex sequence.
892 for (const auto & elem : this->_mesh->active_local_element_ptr_range())
893 {
894 if (elem->infinite() || elem->type() == C0POLYHEDRON)
895 continue;
896
897 for (const auto f : make_range(elem->n_faces()))
898 {
899 const Elem * neighbor = elem->neighbor_ptr(f);
900 if (!neighbor || neighbor == remote_elem || neighbor->infinite())
901 continue;
902
903 const unsigned int f_neighbor = neighbor->which_neighbor_am_i(elem);
904
905 CPPUNIT_ASSERT(oriented_face_nodes(*elem, f) ==
906 oriented_face_nodes(*neighbor, f_neighbor));
907 }
908 }
909 }
910
912 {
913 LOG_UNIT_TEST;
914
915 for (const auto & elem :
916 this->_mesh->active_local_element_ptr_range())
917 for (const auto s : elem->side_index_range())
918 {
919 if (elem->type() == EDGE2 || elem->type() == EDGE3 || elem->type() == EDGE4)
920 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s), elem->center_node_on_side(s));
921 else if (elem->type() == TRI6 || elem->type() == TRI7)
922 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s + 3), elem->center_node_on_side(s));
923 else if (elem->type() == QUAD8 || elem->type() == QUAD9 ||
924 elem->type() == QUADSHELL8 || elem->type() == QUADSHELL9)
925 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s + 4), elem->center_node_on_side(s));
926 else if (elem->type() == HEX27)
927 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s + 20), elem->center_node_on_side(s));
928 else if (elem->type() == PRISM18 && s >= 1 && s <= 3)
929 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s + 14), elem->center_node_on_side(s));
930 else if ((elem->type() == PRISM20 ||
931 elem->type() == PRISM21) && s >= 1 && s <= 3)
932 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s + 14), elem->center_node_on_side(s));
933 else if (elem->type() == PRISM20 ||
934 elem->type() == PRISM21)
935 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(18 + (s == 4)), elem->center_node_on_side(s));
936 else if (elem->type() == PYRAMID14 && s == 4)
937 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(13), elem->center_node_on_side(s));
938 else if (elem->type() == PYRAMID18)
939 {
940 if (s < 4)
941 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(s + 14), elem->center_node_on_side(s));
942 else
943 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(13), elem->center_node_on_side(s));
944 }
945 else
946 CPPUNIT_ASSERT_EQUAL(invalid_uint, elem->center_node_on_side(s));
947 }
948 }
949
951 {
952 LOG_UNIT_TEST;
953
954 for (const auto & elem :
955 this->_mesh->active_local_element_ptr_range())
956 for (const auto s : elem->side_index_range())
957 CPPUNIT_ASSERT_EQUAL(elem->build_side_ptr(s)->type(), elem->side_type(s));
958 }
959
961 {
962 LOG_UNIT_TEST;
963
964 for (const auto & elem :
965 this->_mesh->active_local_element_ptr_range())
966 {
967 std::unique_ptr<const Elem> side;
968
969 for (const auto s : elem->side_index_range())
970 {
971 CPPUNIT_ASSERT_EQUAL(elem->build_side_ptr(s)->subdomain_id(), elem->subdomain_id());
972
973 elem->build_side_ptr(side, s);
974 CPPUNIT_ASSERT_EQUAL(side->subdomain_id(), elem->subdomain_id());
975
976 // We don't require that the "lightweight" side_ptr work
977 // the same way
978 // CPPUNIT_ASSERT_EQUAL(elem->side_ptr(s)->subdomain_id(), elem->subdomain_id());
979 }
980 }
981 }
982
984 {
985 LOG_UNIT_TEST;
986
987 ElemSideBuilder cache;
988 for (auto & elem : this->_mesh->active_local_element_ptr_range())
989 for (const auto s : elem->side_index_range())
990 {
991 const auto side = elem->build_side_ptr(s);
992
993 auto & cached_side = cache(*elem, s);
994 CPPUNIT_ASSERT_EQUAL(side->type(), cached_side.type());
995 for (const auto n : side->node_index_range())
996 CPPUNIT_ASSERT_EQUAL(side->node_ref(n), cached_side.node_ref(n));
997
998 const auto & const_cached_side = cache(const_cast<const Elem &>(*elem), s);
999 CPPUNIT_ASSERT_EQUAL(side->type(), const_cached_side.type());
1000 for (const auto n : side->node_index_range())
1001 CPPUNIT_ASSERT_EQUAL(side->node_ref(n), const_cached_side.node_ref(n));
1002 }
1003 }
1004
1005 void test_n_refinements(unsigned int n)
1006 {
1007#ifdef LIBMESH_ENABLE_AMR
1008 // We don't support refinement of all element types
1009 if (elem_type == EDGE4 ||
1010 elem_type == PRISM20 ||
1011 elem_type == PYRAMID5 ||
1012 elem_type == PYRAMID13 ||
1013 elem_type == PYRAMID14 ||
1014 elem_type == PYRAMID18 ||
1015 elem_type == C0POLYGON ||
1016 elem_type == C0POLYHEDRON)
1017 return;
1018
1019 auto refining_mesh = this->_mesh->clone();
1020
1021 MeshRefinement mr(*refining_mesh);
1022 mr.uniformly_refine(n);
1023
1024 std::set<std::pair<dof_id_type, unsigned int>> parent_node_was_touched;
1025 std::set<std::pair<dof_id_type, unsigned int>> parent_child_was_touched;
1026
1027 for (const Elem * elem : refining_mesh->active_element_ptr_range())
1028 {
1029 CPPUNIT_ASSERT_EQUAL(elem->level(), n);
1030 CPPUNIT_ASSERT(!elem->ancestor());
1031 CPPUNIT_ASSERT(elem->active());
1032 CPPUNIT_ASSERT(!elem->subactive());
1033 CPPUNIT_ASSERT(!elem->has_children());
1034 CPPUNIT_ASSERT(!elem->has_ancestor_children());
1035 CPPUNIT_ASSERT(!elem->interior_parent());
1036
1037 const Elem * parent = elem->parent();
1038 CPPUNIT_ASSERT(parent);
1039 CPPUNIT_ASSERT(parent->ancestor());
1040 CPPUNIT_ASSERT(!parent->active());
1041 CPPUNIT_ASSERT(!parent->subactive());
1042 CPPUNIT_ASSERT(parent->has_children());
1043 CPPUNIT_ASSERT(!parent->has_ancestor_children());
1044 CPPUNIT_ASSERT(!parent->interior_parent());
1045 if (n == 1)
1046 {
1047 CPPUNIT_ASSERT_EQUAL(parent, elem->top_parent());
1048 CPPUNIT_ASSERT_EQUAL(parent, parent->top_parent());
1049 }
1050 else
1051 {
1052 CPPUNIT_ASSERT(parent != elem->top_parent());
1053 CPPUNIT_ASSERT(parent != parent->top_parent());
1054 CPPUNIT_ASSERT_EQUAL(elem->top_parent(), parent->top_parent());
1055 }
1056
1057 CPPUNIT_ASSERT(parent->is_ancestor_of(elem));
1058 const unsigned int c = parent->which_child_am_i(elem);
1059 CPPUNIT_ASSERT(c < parent->n_children());
1060 CPPUNIT_ASSERT_EQUAL(elem, parent->child_ptr(c));
1061 parent_child_was_touched.emplace(parent->id(), c);
1062
1063 CPPUNIT_ASSERT_EQUAL(parent->n_nodes_in_child(c), elem->n_nodes());
1064 for (auto n : make_range(elem->n_nodes()))
1065 {
1066 CPPUNIT_ASSERT_EQUAL(parent->is_vertex_on_child(c, n), elem->is_vertex(n));
1067
1068 auto pn = parent->as_parent_node(c, n);
1069 CPPUNIT_ASSERT_EQUAL(pn, parent->get_node_index(elem->node_ptr(n)));
1070 if (pn == libMesh::invalid_uint)
1071 continue;
1072 CPPUNIT_ASSERT_EQUAL(parent->is_vertex_on_parent(c, n), parent->is_vertex(pn));
1073 parent_node_was_touched.emplace(parent->id(), pn);
1074 }
1075
1076 for (auto s : make_range(parent->n_sides()))
1077 {
1078 if (parent->is_child_on_side(c,s))
1079 {
1080 auto parent_side = parent->build_side_ptr(s);
1081
1082 // Implicitly assuming here that s is the child side
1083 // too - we support that now and hopefully won't have
1084 // to change it later
1085 auto child_side = elem->build_side_ptr(s);
1086
1087 // 2D Inf FE inverse_map not yet implemented?
1088 if (!parent_side->infinite())
1089 for (const Node & node : child_side->node_ref_range())
1090 CPPUNIT_ASSERT(parent_side->contains_point(node));
1091
1092 // We can at least check for sharing some node with
1093 // a side, on both finite and infinite elements, for
1094 // those children that aren't just the middle elements
1095 // of triangular sides.
1096 bool shares_a_side_node = false;
1097 bool shares_a_vertex = false;
1098 for (const Node & node : child_side->node_ref_range())
1099 if (parent_side->local_node(node.id()) != invalid_uint)
1100 shares_a_side_node = true;
1101 for (const Node & node : elem->node_ref_range())
1102 if (parent->local_node(node.id()) < parent->n_vertices())
1103 shares_a_vertex = true;
1104
1105 CPPUNIT_ASSERT(shares_a_side_node || !shares_a_vertex);
1106
1107 if (elem->neighbor_ptr(s) && !elem->neighbor_ptr(s)->is_remote())
1108 CPPUNIT_ASSERT_EQUAL(parent->child_neighbor(elem->neighbor_ptr(s)), elem);
1109 }
1110 }
1111
1112 for (auto e : make_range(parent->n_edges()))
1113 {
1114 if (parent->is_child_on_edge(c,e))
1115 {
1116 auto parent_edge = parent->build_edge_ptr(e);
1117
1118 // Implicitly assuming here that e is the child edge
1119 // too - we support that now and hopefully won't have
1120 // to change it later
1121 auto child_edge = elem->build_edge_ptr(e);
1122
1123 // 1D Inf FE inverse_map not yet implemented?
1124 if (!parent_edge->infinite())
1125 for (const Node & node : child_edge->node_ref_range())
1126 CPPUNIT_ASSERT(parent_edge->contains_point(node));
1127
1128 // We can at least check for sharing some node with
1129 // an edge, on both finite and infinite elements
1130 bool shares_an_edge_node = false;
1131 for (const Node & node : child_edge->node_ref_range())
1132 if (parent_edge->local_node(node.id()) != invalid_uint)
1133 shares_an_edge_node = true;
1134
1135 CPPUNIT_ASSERT(shares_an_edge_node);
1136 }
1137 }
1138
1139 if (parent->has_affine_map())
1140 CPPUNIT_ASSERT(elem->has_affine_map());
1141 }
1142
1143 // It's possible for a parent element on a distributed mesh to not
1144 // have all its children available on any one processor
1145 TestCommWorld->set_union(parent_child_was_touched);
1146 TestCommWorld->set_union(parent_node_was_touched);
1147
1148 for (const Elem * elem : refining_mesh->local_element_ptr_range())
1149 {
1150 if (elem->active())
1151 continue;
1152
1153 // With only one layer of refinement the family tree methods
1154 // should have the full number of elements, even if some are
1155 // remote.
1156 if (n == 1)
1157 {
1158 std::vector<const Elem *> family;
1159 elem->family_tree(family);
1160 CPPUNIT_ASSERT_EQUAL(family.size(),
1161 std::size_t(elem->n_children() + 1));
1162
1163 family.clear();
1164 elem->total_family_tree(family);
1165 CPPUNIT_ASSERT_EQUAL(family.size(),
1166 std::size_t(elem->n_children() + 1));
1167
1168 family.clear();
1169 elem->active_family_tree(family);
1170 CPPUNIT_ASSERT_EQUAL(family.size(),
1171 std::size_t(elem->n_children()));
1172
1173 for (auto s : make_range(elem->n_sides()))
1174 {
1175 family.clear();
1176 elem->active_family_tree_by_side(family,s);
1177 if (!elem->build_side_ptr(s)->infinite())
1178 CPPUNIT_ASSERT_EQUAL(double(family.size()),
1179 std::pow(2.0, int(elem->dim()-1)));
1180 else
1181 CPPUNIT_ASSERT_EQUAL(double(family.size()),
1182 std::pow(2.0, int(elem->dim()-2)));
1183 for (const Elem * child : family)
1184 {
1185 if (child->is_remote())
1186 continue;
1187
1188 unsigned int c = elem->which_child_am_i(child);
1189 CPPUNIT_ASSERT(elem->is_child_on_side(c, s));
1190 }
1191 }
1192 }
1193
1194 if (elem->level() + 1 == n)
1195 {
1196 for (auto c : make_range(elem->n_children()))
1197 {
1198 auto it = parent_child_was_touched.find(std::make_pair(elem->id(), c));
1199 CPPUNIT_ASSERT(it != parent_child_was_touched.end());
1200 }
1201
1202 for (auto n : make_range(elem->n_nodes()))
1203 {
1204 auto it = parent_node_was_touched.find(std::make_pair(elem->id(), n));
1205 CPPUNIT_ASSERT(it != parent_node_was_touched.end());
1206 }
1207 }
1208 }
1209#endif
1210 }
1211
1213 {
1214 LOG_UNIT_TEST;
1215
1217 }
1218
1220 {
1221 LOG_UNIT_TEST;
1222
1224 }
1225
1227 {
1228 LOG_UNIT_TEST;
1229
1230 for (const auto & elem :
1231 this->_mesh->active_local_element_ptr_range())
1232 for (const auto nd : elem->node_index_range())
1233 {
1234 if ((elem->type() == EDGE3 || elem->type() == EDGE4) && nd >= 2)
1235 CPPUNIT_ASSERT(elem->is_internal(nd));
1236 else if (elem->type() == HEX27 && nd == 26)
1237 CPPUNIT_ASSERT(elem->is_internal(nd));
1238 else if (elem->type() == PRISM21 && nd == 20)
1239 CPPUNIT_ASSERT(elem->is_internal(nd));
1240 else if ((elem->type() == QUAD9 || elem->type() == QUADSHELL9) && nd == 8)
1241 CPPUNIT_ASSERT(elem->is_internal(nd));
1242 else if (elem->type() == TRI7 && nd == 6)
1243 CPPUNIT_ASSERT(elem->is_internal(nd));
1244 else if (elem->type() == INFHEX18 && nd == 17)
1245 CPPUNIT_ASSERT(elem->is_internal(nd));
1246 else if (elem->type() == INFQUAD6 && nd == 5)
1247 CPPUNIT_ASSERT(elem->is_internal(nd));
1248 // Accomodate for mid-element node of C0 polyhedron
1249 else if (elem->type() == C0POLYHEDRON && nd == elem->n_vertices())
1250 CPPUNIT_ASSERT(elem->is_internal(nd));
1251 else
1252 CPPUNIT_ASSERT(!elem->is_internal(nd));
1253 }
1254 }
1255
1257 {
1258 LOG_UNIT_TEST;
1259
1260 for (const auto & elem : this->_mesh->active_local_element_ptr_range())
1261 {
1262 for (const auto nd : elem->node_index_range())
1263 {
1264 auto adjacent_edge_ids = elem->edges_adjacent_to_node(nd);
1265
1266 if (elem->dim() < 2)
1267 {
1268 // 0D elements don't have edges.
1269 // 1D elements *are* "edges", but we don't consider
1270 // them to *have* edges, so assert that here.
1271 CPPUNIT_ASSERT(adjacent_edge_ids.empty());
1272 }
1273 else
1274 {
1275 // For 2D and 3D elements, on each edge which is
1276 // claimed to be adjacent, check that "nd" is indeed
1277 // on it
1278 for (const auto & edge_id : adjacent_edge_ids)
1279 {
1280 auto node_ids_on_edge = elem->nodes_on_edge(edge_id);
1281 CPPUNIT_ASSERT(std::find(node_ids_on_edge.begin(), node_ids_on_edge.end(), nd) != node_ids_on_edge.end());
1282 }
1283 }
1284 }
1285 }
1286 }
1287
1288};
1289
1290#define ELEMTEST \
1291 CPPUNIT_TEST( test_bounding_box ); \
1292 CPPUNIT_TEST( test_ref_elem ); \
1293 CPPUNIT_TEST( test_static_topology ); \
1294 CPPUNIT_TEST( test_higher_order_node_placement ); \
1295 CPPUNIT_TEST( test_quality ); \
1296 CPPUNIT_TEST( test_quality_bounds ); \
1297 CPPUNIT_TEST( test_quality_permutation_invariance ); \
1298 CPPUNIT_TEST( test_node_edge_map_consistency ); \
1299 CPPUNIT_TEST( test_maps ); \
1300 CPPUNIT_TEST( test_static_data ); \
1301 CPPUNIT_TEST( test_permute ); \
1302 CPPUNIT_TEST( test_flip ); \
1303 CPPUNIT_TEST( test_orient ); \
1304 CPPUNIT_TEST( test_orient_elements ); \
1305 CPPUNIT_TEST( test_orientation ); \
1306 CPPUNIT_TEST( test_orientation_conformity ); \
1307 CPPUNIT_TEST( test_contains_point_node ); \
1308 CPPUNIT_TEST( test_center_node_on_side ); \
1309 CPPUNIT_TEST( test_side_type ); \
1310 CPPUNIT_TEST( test_side_subdomain ); \
1311 CPPUNIT_TEST( test_elem_side_builder ); \
1312 CPPUNIT_TEST( test_refinement ); \
1313 CPPUNIT_TEST( test_double_refinement ); \
1314 CPPUNIT_TEST( test_is_internal )
1315
1316#define INSTANTIATE_ELEMTEST(elemtype) \
1317 class ElemTest_##elemtype : public ElemTest<elemtype> { \
1318 public: \
1319 ElemTest_##elemtype() : \
1320 ElemTest<elemtype>() { \
1321 if (unitlog->summarized_logs_enabled()) \
1322 this->libmesh_suite_name = "ElemTest"; \
1323 else \
1324 this->libmesh_suite_name = "ElemTest_" #elemtype; \
1325 } \
1326 CPPUNIT_TEST_SUITE( ElemTest_##elemtype ); \
1327 ELEMTEST; \
1328 CPPUNIT_TEST_SUITE_END(); \
1329 }; \
1330 \
1331 CPPUNIT_TEST_SUITE_REGISTRATION( ElemTest_##elemtype )
1332
1334
1338
1339#if LIBMESH_DIM > 1
1344
1351
1352// This just tests with one pentagon, but better than nothing
1354
1355// And this is just one skewed box; also better than nothing
1357
1358#ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
1361#endif
1362#endif // LIBMESH_DIM > 1
1363
1364#if LIBMESH_DIM > 2
1368
1372
1378
1379// These tests use PointLocator, which uses contains_point(), which
1380// uses inverse_map(), which doesn't play nicely on Pyramids unless we
1381// have exceptions support
1382#ifdef LIBMESH_ENABLE_EXCEPTIONS
1387#endif
1388
1389#ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
1393
1396#endif
1397#endif // LIBMESH_DIM > 2
void test_elem_side_builder()
Definition elem_test.C:983
void test_is_internal()
Definition elem_test.C:1226
void test_quality_permutation_invariance()
Definition elem_test.C:444
void test_maps()
Definition elem_test.C:492
void test_contains_point_node()
Definition elem_test.C:552
void test_side_type()
Definition elem_test.C:950
void test_flip()
Definition elem_test.C:615
void test_orientation()
Definition elem_test.C:837
void test_higher_order_node_placement()
Definition elem_test.C:146
void test_double_refinement()
Definition elem_test.C:1219
void test_center_node_on_side()
Definition elem_test.C:911
void test_orientation_conformity()
Definition elem_test.C:886
void test_n_refinements(unsigned int n)
Definition elem_test.C:1005
void test_side_subdomain()
Definition elem_test.C:960
void test_orient()
Definition elem_test.C:693
void test_ref_elem()
Definition elem_test.C:55
void test_quality_bounds()
Definition elem_test.C:390
void test_static_topology()
Definition elem_test.C:83
void test_permute()
Definition elem_test.C:576
void test_quality()
Definition elem_test.C:212
void test_bounding_box()
Definition elem_test.C:19
std::vector< const Node * > oriented_face_nodes(const Elem &elem, const unsigned int face)
The nodes at the vertices of face, in the order Elem::oriented_face_vertices() gives.
Definition elem_test.C:828
void test_orient_elements()
Definition elem_test.C:771
void test_refinement()
Definition elem_test.C:1212
void test_node_edge_map_consistency()
Definition elem_test.C:1256
void test_static_data()
Definition elem_test.C:518
std::unique_ptr< Mesh > _mesh
Definition elem_test.h:26
The BoundaryInfo class contains information relevant to boundary conditions including storing faces,...
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.
Defines a Cartesian bounding box by the two corner extremum.
const Point & max() const
void scale(const Real factor)
Scales each dimension of the bounding box by factor.
const Point & min() const
bool contains_point(const Point &) const
void union_with(const Point &p)
Enlarges this bounding box to include the given point.
dof_id_type id() const
Definition dof_object.h:833
Helper for building element sides that minimizes the construction of new elements.
This is the base class from which all geometric element types are derived.
Definition elem.h:96
bool has_ancestor_children() const
Definition elem.h:3131
virtual unsigned int n_nodes_in_child(unsigned int) const
Definition elem.h:748
virtual bool is_child_on_side(const unsigned int c, const unsigned int s) const =0
bool active() const
Definition elem.h:3079
bool is_ancestor_of(const Elem *descendant) const
Definition elem.h:3147
virtual unsigned int n_vertices() const =0
Elem * child_neighbor(Elem *elem)
Definition elem.h:2778
const Point & point(const unsigned int i) const
Definition elem.h:2583
virtual unsigned int local_side_node(unsigned int side, unsigned int side_node) const =0
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:841
virtual bool is_child_on_edge(const unsigned int c, const unsigned int e) const
Definition elem.C:2435
bool has_children() const
Definition elem.h:3117
static ElemType edge_type(const ElemType t)
Definition elem.C:3380
const Elem * parent() const
Definition elem.h:3168
const Elem * child_ptr(unsigned int i) const
Definition elem.h:3301
bool ancestor() const
Definition elem.C:2121
unsigned int which_neighbor_am_i(const Elem *e) const
This function tells you which neighbor e is.
Definition elem.h:3057
virtual bool is_vertex_on_parent(unsigned int c, unsigned int n) const
Definition elem.C:3935
unsigned int local_node(const dof_id_type i) const
Definition elem.h:2617
unsigned int which_child_am_i(const Elem *e) const
Definition elem.h:3330
virtual bool is_vertex_on_child(unsigned int, unsigned int n) const
Definition elem.h:872
virtual unsigned int local_edge_node(unsigned int edge, unsigned int edge_node) const =0
Similar to Elem::local_side_node(), but instead of a side id, takes an edge id and a node id on that ...
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:649
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:759
static const unsigned int max_n_nodes
The maximum number of nodes any element can contain.
Definition elem.h:735
static constexpr 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:664
std::vector< unsigned int > oriented_face_vertices(const unsigned int i) const
Definition elem.C:4078
const Node * node_ptr(const unsigned int i) const
Definition elem.h:2637
virtual unsigned int as_parent_node(unsigned int c, unsigned int n) const
Definition elem.C:2515
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:1089
const Elem * reference_elem() const
Definition elem.C:508
virtual std::unique_ptr< Elem > build_edge_ptr(const unsigned int i)=0
virtual bool is_vertex(const unsigned int i) const =0
unsigned int get_node_index(const Node *node_ptr) const
Definition elem.h:2675
const Elem * interior_parent() const
Definition elem.C:1100
virtual unsigned int n_edges() const =0
virtual ElemType side_type(const unsigned int s) const =0
virtual unsigned int n_sides() const =0
bool subactive() const
Definition elem.h:3097
virtual bool has_affine_map() const
Definition elem.h:1312
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2736
virtual bool infinite() const =0
const Elem * top_parent() const
Definition elem.h:3194
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
The IntRange templated class is intended to make it easy to loop over integers which are indices of a...
Definition int_range.h:54
Implements (adaptive) mesh refinement algorithms for a MeshBase.
void uniformly_refine(unsigned int n=1)
Uniformly refines the mesh n times.
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
A Node is like a Point, but with more information.
Definition node.h:55
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:41
Communicator * TestCommWorld
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...
std::string name(const ElemQuality q)
This function returns a string containing some name for q.
std::vector< ElemQuality > valid(const ElemType t)
std::string enum_to_string(const T e)
The libMesh namespace provides an interface to certain functionality in the library.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
ElemType
Defines an enum for geometric element types.
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:303
const Real pi
.
Definition libmesh.h:292
const RemoteElem * remote_elem
Definition remote_elem.C:57
ElemQuality
Defines an enum for element quality metrics.
static constexpr Real TOLERANCE
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:176
static const bool value
Definition xdr_io.C:55