libMesh
all_tri.C
Go to the documentation of this file.
1 #include <libmesh/libmesh.h>
2 #include <libmesh/replicated_mesh.h>
3 #include <libmesh/elem.h>
4 #include <libmesh/cell_c0polyhedron.h>
5 #include <libmesh/cell_polyhedron.h>
6 #include <libmesh/face_c0polygon.h>
7 #include <libmesh/face_polygon.h>
8 #include <libmesh/mesh_generation.h>
9 #include <libmesh/mesh_modification.h>
10 #include <libmesh/mesh_tools.h>
11 #include <libmesh/boundary_info.h>
12 
13 #include "test_comm.h"
14 #include "libmesh_cppunit.h"
15 
16 #include <cmath>
17 
18 
19 using namespace libMesh;
20 
21 class AllTriTest : public CppUnit::TestCase
22 {
29 public:
30  LIBMESH_CPPUNIT_TEST_SUITE( AllTriTest );
31 
32  // 2D tests
33 #if LIBMESH_DIM > 1
34  CPPUNIT_TEST( testAllTriTri );
35  CPPUNIT_TEST( testAllTriQuad );
36  CPPUNIT_TEST( testAllTriQuad8 );
37  CPPUNIT_TEST( testAllTriQuad9 );
38  CPPUNIT_TEST( testAllTriC0Polygon );
39  CPPUNIT_TEST( testAllTriC0PolygonOctagon );
40 #endif
41 
42  // 3D tests
43 #if LIBMESH_DIM > 2
44  CPPUNIT_TEST( testAllTriPrism6 );
45  CPPUNIT_TEST( testAllTriPrism18 );
46  CPPUNIT_TEST( testAllTriPrism20 );
47  CPPUNIT_TEST( testAllTriPrism21 );
48  CPPUNIT_TEST( testAllTriC0PolyhedronCube );
49  CPPUNIT_TEST( testAllTriC0PolyhedronHexagonalPrism );
50 #endif
51 
52  CPPUNIT_TEST_SUITE_END();
53 
54 protected:
55  // Helper function called by the test implementations, saves a few lines of code.
56  void test_helper_2D(ElemType elem_type,
57  dof_id_type n_elem_expected,
58  std::size_t n_boundary_conds_expected)
59  {
60  ReplicatedMesh mesh(*TestCommWorld, /*dim=*/2);
61 
62  // Build a 2x1 TRI3 mesh and ask to split it into triangles.
63  // Should be a no-op
65  /*nx=*/2, /*ny=*/1,
66  /*xmin=*/0., /*xmax=*/1.,
67  /*ymin=*/0., /*ymax=*/1.,
68  elem_type);
69 
71 
72  // Make sure that the expected number of elements is found.
73  CPPUNIT_ASSERT_EQUAL(n_elem_expected, mesh.n_elem());
74 
75  // Make sure the expected number of BCs is found.
76  CPPUNIT_ASSERT_EQUAL(n_boundary_conds_expected, mesh.get_boundary_info().n_boundary_conds());
77  }
78 
79  // Helper function called by the test implementations in 3D, saves a few lines of code.
80  void test_helper_3D(ElemType elem_type,
81  dof_id_type n_elem_expected,
82  std::size_t n_boundary_conds_expected)
83  {
84  ReplicatedMesh mesh(*TestCommWorld, /*dim=*/3);
85 
86  // Build a 2x1 TRI3 mesh and ask to split it into triangles.
87  // Should be a no-op
89  /*nx=*/1, /*ny=*/1, /*nz=*/1,
90  /*xmin=*/0., /*xmax=*/1.,
91  /*ymin=*/0., /*ymax=*/1.,
92  /*zmin=*/0., /*zmax=*/1.,
93  elem_type);
94 
96 
97  // Make sure that the expected number of elements is found.
98  CPPUNIT_ASSERT_EQUAL(n_elem_expected, mesh.n_elem());
99 
100  // Make sure the expected number of BCs is found.
101  CPPUNIT_ASSERT_EQUAL(n_boundary_conds_expected, mesh.get_boundary_info().n_boundary_conds());
102  }
103 
104 public:
105  void setUp() {}
106 
107  void tearDown() {}
108 
109  // 4 TRIs no-op
110  void testAllTriTri() { LOG_UNIT_TEST; test_helper_2D(TRI3, /*nelem=*/4, /*nbcs=*/6); }
111 
112  // 2 quads split into 4 TRIs.
113  void testAllTriQuad() { LOG_UNIT_TEST; test_helper_2D(QUAD4, /*nelem=*/4, /*nbcs=*/6); }
114 
115  // 2 QUAD8s split into 4 TRIs.
116  void testAllTriQuad8() { LOG_UNIT_TEST; test_helper_2D(QUAD8, /*nelem=*/4, /*nbcs=*/6); }
117 
118  // 2 QUAD9s split into 4 TRIs.
119  void testAllTriQuad9() { LOG_UNIT_TEST; test_helper_2D(QUAD9, /*nelem=*/4, /*nbcs=*/6); }
120 
121  // 2 PRISMs split into 6 TETs with 2 boundary faces per side.
122  void testAllTriPrism6() { LOG_UNIT_TEST; test_helper_3D(PRISM6, /*nelem=*/6, /*nbcs=*/12); }
123  void testAllTriPrism18() { LOG_UNIT_TEST; test_helper_3D(PRISM18, /*nelem=*/6, /*nbcs=*/12); }
124  void testAllTriPrism20() { LOG_UNIT_TEST; test_helper_3D(PRISM20, /*nelem=*/6, /*nbcs=*/12); }
125  void testAllTriPrism21() { LOG_UNIT_TEST; test_helper_3D(PRISM21, /*nelem=*/6, /*nbcs=*/12); }
126 
127  // Build a C0Polygon paving (triangles, quads, hexagons) via
128  // build_square and split it into a pure TRI3 mesh.
130  {
131  LOG_UNIT_TEST;
132 
133  ReplicatedMesh mesh(*TestCommWorld, /*dim=*/2);
134 
136  /*nx=*/2, /*ny=*/2,
137  /*xmin=*/0., /*xmax=*/1.,
138  /*ymin=*/0., /*ymax=*/1.,
139  C0POLYGON);
140 
141  // The post-all_tri element count is the sum of the per-polygon
142  // subtriangle counts, and external sides should be preserved one
143  // for one as TRI3 sides.
144  dof_id_type n_elem_expected = 0;
145  for (const Elem * elem : mesh.element_ptr_range())
146  {
147  const Polygon * poly = dynamic_cast<const Polygon *>(elem);
148  CPPUNIT_ASSERT(poly != nullptr);
149  n_elem_expected += poly->n_subtriangles();
150  }
151 
152  const std::size_t n_bcs_before =
154 
156 
157  CPPUNIT_ASSERT_EQUAL(n_elem_expected, mesh.n_elem());
158  CPPUNIT_ASSERT_EQUAL(n_bcs_before,
160 
161  for (const Elem * elem : mesh.element_ptr_range())
162  CPPUNIT_ASSERT_EQUAL(ElemType(TRI3), elem->type());
163  }
164 
165  // A single regular octagon (8 sides, 6 subtriangles) exercises the
166  // path where a polygon requires more subelements than any non-polygon
167  // 2D element type would.
169  {
170  LOG_UNIT_TEST;
171 
172  constexpr unsigned int n_sides = 8;
173 
174  ReplicatedMesh mesh(*TestCommWorld, /*dim=*/2);
175 
176  std::unique_ptr<Elem> octagon = std::make_unique<C0Polygon>(n_sides);
177  for (unsigned int i = 0; i < n_sides; ++i)
178  {
179  const Real angle = 2 * libMesh::pi * i / n_sides;
180  Node * node = mesh.add_point(Point(std::cos(angle), std::sin(angle), 0.),
181  /*id=*/i);
182  octagon->set_node(i, node);
183  }
184  octagon->set_id() = 0;
185  Real poly_volume = octagon->volume();
186  Elem * elem = mesh.add_elem(std::move(octagon));
187 
188  // Mark every external side with a boundary id so we can verify
189  // boundary information is transferred to the new triangles.
190  for (unsigned int s = 0; s < n_sides; ++s)
191  mesh.get_boundary_info().add_side(elem, s, /*bnd_id=*/0);
192 
194 
196 
197  // n_sides - 2 = 6 subtriangles
198  CPPUNIT_ASSERT_EQUAL(dof_id_type(n_sides - 2), mesh.n_elem());
199  CPPUNIT_ASSERT_EQUAL(std::size_t(n_sides),
201 
202  for (const Elem * e : mesh.element_ptr_range())
203  CPPUNIT_ASSERT_EQUAL(ElemType(TRI3), e->type());
204 
205  LIBMESH_ASSERT_NUMBERS_EQUAL(poly_volume, MeshTools::volume(mesh), TOLERANCE);
206  }
207 
208 protected:
209 
210  // Builds a C0Polyhedron in mesh whose faces are described by
211  // nodes_on_side (indices into the existing mesh node list), runs
212  // all_tri, and verifies that the result is a pure TET4 mesh with the
213  // expected sub-element count and preserved boundary data.
214  void test_helper_c0polyhedron
215  (const std::vector<Point> & points,
216  const std::vector<std::vector<unsigned int>> & nodes_on_side)
217  {
218  ReplicatedMesh mesh(*TestCommWorld, /*dim=*/3);
219 
220  for (auto p : index_range(points))
221  mesh.add_point(points[p], /*id=*/p);
222 
223  std::vector<std::shared_ptr<Polygon>> sides(nodes_on_side.size());
224  for (auto s : index_range(nodes_on_side))
225  {
226  const auto & nodes_on_s = nodes_on_side[s];
227  sides[s] = std::make_shared<C0Polygon>(nodes_on_s.size());
228  for (auto i : index_range(nodes_on_s))
229  sides[s]->set_node(i, mesh.node_ptr(nodes_on_s[i]));
230  }
231 
232  std::unique_ptr<Node> mid_elem_node;
233  std::unique_ptr<Elem> polyhedron =
234  std::make_unique<C0Polyhedron>(sides, mid_elem_node);
235  if (mid_elem_node)
236  mesh.add_node(std::move(mid_elem_node));
237  polyhedron->set_id() = 0;
238  Elem * elem = mesh.add_elem(std::move(polyhedron));
239 
240  const auto * poly = cast_ptr<const C0Polyhedron *>(elem);
241  const dof_id_type n_elem_expected = poly->n_subelements();
242 
243  // Mark every external face with a boundary id so we can verify
244  // boundary information is transferred to the new tets.
245  for (unsigned int s = 0; s < elem->n_sides(); ++s)
246  mesh.get_boundary_info().add_side(elem, s, /*bnd_id=*/s);
247 
248  // The number of boundary triangles produced is the total number of
249  // subtriangles across the polyhedron's polygonal faces.
250  std::size_t n_bcs_expected = 0;
251  for (unsigned int s = 0; s < elem->n_sides(); ++s)
252  n_bcs_expected += sides[s]->n_subtriangles();
253 
254  // Keep this map for future checks
255  std::vector<std::array<int, 4>> sub_elem_sides_to_parent_side(elem->n_sub_elem());
256  for (unsigned int b = 0; b < elem->n_sub_elem(); ++b)
257  sub_elem_sides_to_parent_side[b] = poly->subelement_sides_to_poly_sides(b);
258 
259  Real poly_volume = poly->volume();
261 
263 
264  CPPUNIT_ASSERT_EQUAL(n_elem_expected, mesh.n_elem());
265  CPPUNIT_ASSERT_EQUAL(n_bcs_expected,
267 
268  for (const Elem * e : mesh.element_ptr_range())
269  CPPUNIT_ASSERT_EQUAL(ElemType(TET4), e->type());
270 
271  // Check that the boundary info and numbering is as expected
272  for (const Elem * e : mesh.element_ptr_range())
273  for (const auto s : make_range(e->n_sides()))
274  {
275  // Get parent side
276  const auto side = sub_elem_sides_to_parent_side[e->id()][s];
277  // Check boundary exists on the tet side if the tet side is on a poly side
278  if (side != invalid_int)
279  CPPUNIT_ASSERT_EQUAL(mesh.get_boundary_info().has_boundary_id(e, s, side), true);
280  }
281  LIBMESH_ASSERT_NUMBERS_EQUAL(poly_volume, MeshTools::volume(mesh), TOLERANCE);
282  }
283 
284 public:
285 
286  // A cube built as a C0Polyhedron exercises the path where the
287  // optimal tetrahedralization succeeds (no mid-element node needed).
289  {
290  LOG_UNIT_TEST;
291 
292  const std::vector<Point> points =
293  { {0,0,0}, {1,0,0}, {1,1,0}, {0,1,0},
294  {0,0,1}, {1,0,1}, {1,1,1}, {0,1,1} };
295 
296  const std::vector<std::vector<unsigned int>> nodes_on_side =
297  { {0, 1, 2, 3}, // min z
298  {0, 1, 5, 4}, // min y
299  {2, 6, 5, 1}, // max x
300  {2, 3, 7, 6}, // max y
301  {0, 4, 7, 3}, // min x
302  {5, 6, 7, 4} }; // max z
303 
304  test_helper_c0polyhedron(points, nodes_on_side);
305  }
306 
307  // A hexagonal prism exercises the fallback path where a mid-element
308  // node is added to tetrahedralize the polyhedron.
310  {
311  LOG_UNIT_TEST;
312 
313  const std::vector<Point> points =
314  { { 0, -2, 0}, {-1, -1, 0}, {-1, 1, 0},
315  { 0, 2, 0}, { 1, 1, 0}, { 1, -1, 0},
316  { 0, -2, 1}, {-1, -1, 1}, {-1, 1, 1},
317  { 0, 2, 1}, { 1, 1, 1}, { 1, -1, 1} };
318 
319  const std::vector<std::vector<unsigned int>> nodes_on_side =
320  { {0, 1, 2, 3, 4, 5},
321  {0, 1, 7, 6},
322  {1, 2, 8, 7},
323  {2, 3, 9, 8},
324  {3, 4, 10, 9},
325  {4, 5, 11, 10},
326  {5, 0, 6, 11},
327  {6, 7, 8, 9, 10, 11} };
328 
329  test_helper_c0polyhedron(points, nodes_on_side);
330  }
331 };
332 
333 
void testAllTriTri()
Definition: all_tri.C:110
void testAllTriPrism20()
Definition: all_tri.C:124
std::size_t n_boundary_conds() const
ElemType
Defines an enum for geometric element types.
void testAllTriPrism6()
Definition: all_tri.C:122
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
A Node is like a Point, but with more information.
Definition: node.h:52
bool has_boundary_id(const Node *const node, const boundary_id_type id) const
unsigned int n_subtriangles() const
Definition: face_polygon.h:212
libMesh::Parallel::Communicator * TestCommWorld
Definition: driver.C:218
static constexpr Real TOLERANCE
void testAllTriQuad()
Definition: all_tri.C:113
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly created (or read) mesh for use.
Definition: mesh_base.C:824
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
void testAllTriQuad8()
Definition: all_tri.C:116
void testAllTriPrism18()
Definition: all_tri.C:123
MeshBase & mesh
void testAllTriC0Polygon()
Definition: all_tri.C:129
void build_square(UnstructuredMesh &mesh, const unsigned int nx, const unsigned int ny, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
A specialized build_cube() for 2D meshes.
The libMesh namespace provides an interface to certain functionality in the library.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:170
void tearDown()
Definition: all_tri.C:107
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
dof_id_type & set_id()
Definition: dof_object.h:827
void all_tri(MeshBase &mesh)
Subdivides any non-simplex elements in a Mesh to produce simplex (triangular in 2D, tetrahedral in 3D) elements.
const int invalid_int
A number which is used quite often to represent an invalid or uninitialized value for an integer...
Definition: libmesh.h:309
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
The Polygon is an element in 2D with an arbitrary (but fixed) number of sides.
Definition: face_polygon.h:39
void testAllTriC0PolygonOctagon()
Definition: all_tri.C:168
void testAllTriC0PolyhedronHexagonalPrism()
Definition: all_tri.C:309
Real volume(const MeshBase &mesh, unsigned int dim=libMesh::invalid_uint)
Find the total volume of a mesh (interpreting that as area for dim = 2, or total arc length for dim =...
Definition: mesh_tools.C:1020
virtual Node * add_node(Node *n)=0
Add Node n to the end of the vertex array.
void test_helper_3D(ElemType elem_type, dof_id_type n_elem_expected, std::size_t n_boundary_conds_expected)
Definition: all_tri.C:80
virtual unsigned int n_sides() const =0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
static const Real b
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
Add side side of element number elem with boundary id id to the boundary information data structure...
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
void test_helper_2D(ElemType elem_type, dof_id_type n_elem_expected, std::size_t n_boundary_conds_expected)
Definition: all_tri.C:56
void testAllTriQuad9()
Definition: all_tri.C:119
CPPUNIT_TEST_SUITE_REGISTRATION(AllTriTest)
virtual unsigned int n_sub_elem() const =0
virtual dof_id_type n_elem() const =0
virtual const Node * node_ptr(const dof_id_type i) const =0
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
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
void testAllTriC0PolyhedronCube()
Definition: all_tri.C:288
void build_cube(UnstructuredMesh &mesh, const unsigned int nx=0, const unsigned int ny=0, const unsigned int nz=0, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const Real zmin=0., const Real zmax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
Builds a (elements) cube.
void testAllTriPrism21()
Definition: all_tri.C:125
uint8_t dof_id_type
Definition: id_types.h:67
const Real pi
.
Definition: libmesh.h:292
void setUp()
Definition: all_tri.C:105