libMesh
Loading...
Searching...
No Matches
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
19using namespace libMesh;
20
21class AllTriTest : public CppUnit::TestCase
22{
29public:
31
32 // 2D tests
33#if LIBMESH_DIM > 1
40#endif
41
42 // 3D tests
43#if LIBMESH_DIM > 2
52#endif
53
55
56protected:
57 // Helper function called by the test implementations, saves a few lines of code.
58 void test_helper(ElemType elem_type,
59 dof_id_type n_elem_expected,
60 std::size_t n_boundary_conds_expected)
61 {
63
64 // Build a 2x1 2D or 1x1x1 3D mesh, ask to split it into simplices
65 const unsigned int dim = Elem::type_to_dim_map[elem_type];
67 /*nx=*/dim < 3 ? 2 : 1,
68 /*ny=*/1,
69 /*nz=*/dim < 3 ? 0 : 1,
70 /*xmin=*/0., /*xmax=*/1.,
71 /*ymin=*/0., /*ymax=*/1.,
72 /*zmin=*/0., /*zmax=*/1.,
73 elem_type);
74
76
77 // Make sure that the expected number of elements is found.
78 CPPUNIT_ASSERT_EQUAL(n_elem_expected, mesh.n_elem());
79
80 const BoundaryInfo & boundary_info = mesh.get_boundary_info();
81
82 // Make sure the expected number of BCs is found.
83 CPPUNIT_ASSERT_EQUAL(n_boundary_conds_expected, boundary_info.n_boundary_conds());
84
85 // In these tests we expect all our elements to have the proper
86 // orientation, and we set up the inputs such that our outputs
87 // should be non-curved and should have no boundary sides that
88 // aren't on the previous external boundaries
89 for (const Elem * elem : mesh.element_ptr_range())
90 {
91 CPPUNIT_ASSERT(!elem->is_flipped());
92 CPPUNIT_ASSERT(elem->has_affine_map());
93 for (auto s : elem->side_index_range())
94 if (!elem->neighbor_ptr(s))
95 CPPUNIT_ASSERT_EQUAL(boundary_info.n_boundary_ids(elem, s), 1u);
96 }
97
98 // We set up inputs with measure 1
99 LIBMESH_ASSERT_NUMBERS_EQUAL(1, MeshTools::volume(mesh), TOLERANCE);
100 }
101
102public:
103 void setUp() {}
104
105 void tearDown() {}
106
107 // 4 TRIs no-op
108 void testAllTriTri() { LOG_UNIT_TEST; test_helper(TRI3, /*nelem=*/4, /*nbcs=*/6); }
109
110 // 2 quads split into 4 TRIs.
111 void testAllTriQuad() { LOG_UNIT_TEST; test_helper(QUAD4, /*nelem=*/4, /*nbcs=*/6); }
112
113 // 2 QUAD8s split into 4 TRIs.
114 void testAllTriQuad8() { LOG_UNIT_TEST; test_helper(QUAD8, /*nelem=*/4, /*nbcs=*/6); }
115
116 // 2 QUAD9s split into 4 TRIs.
117 void testAllTriQuad9() { LOG_UNIT_TEST; test_helper(QUAD9, /*nelem=*/4, /*nbcs=*/6); }
118
119 // 2 PRISMs split into 6 TETs with 2 boundary faces per side.
120 void testAllTriPrism6() { LOG_UNIT_TEST; test_helper(PRISM6, /*nelem=*/6, /*nbcs=*/12); }
121 void testAllTriPrism18() { LOG_UNIT_TEST; test_helper(PRISM18, /*nelem=*/6, /*nbcs=*/12); }
122 void testAllTriPrism20() { LOG_UNIT_TEST; test_helper(PRISM20, /*nelem=*/6, /*nbcs=*/12); }
123 void testAllTriPrism21() { LOG_UNIT_TEST; test_helper(PRISM21, /*nelem=*/6, /*nbcs=*/12); }
124
125 // 6 PYRAMIDs split into 12 TETs with 2 boundary faces per side
126 void testAllTriPyramid5() { LOG_UNIT_TEST; test_helper(PYRAMID5, /*nelem=*/12, /*nbcs=*/12); }
127 void testAllTriPyramid14() { LOG_UNIT_TEST; test_helper(PYRAMID14, /*nelem=*/12, /*nbcs=*/12); }
128
129 // Build a C0Polygon paving (triangles, quads, hexagons) via
130 // build_square and split it into a pure TRI3 mesh.
132 {
133 LOG_UNIT_TEST;
134
135 ReplicatedMesh mesh(*TestCommWorld, /*dim=*/2);
136
138 /*nx=*/2, /*ny=*/2,
139 /*xmin=*/0., /*xmax=*/1.,
140 /*ymin=*/0., /*ymax=*/1.,
141 C0POLYGON);
142
143 // The post-all_tri element count is the sum of the per-polygon
144 // subtriangle counts, and external sides should be preserved one
145 // for one as TRI3 sides.
146 dof_id_type n_elem_expected = 0;
147 for (const Elem * elem : mesh.element_ptr_range())
148 {
149 const Polygon * poly = dynamic_cast<const Polygon *>(elem);
150 CPPUNIT_ASSERT(poly != nullptr);
151 n_elem_expected += poly->n_subtriangles();
152 }
153
154 const std::size_t n_bcs_before =
156
158
159 CPPUNIT_ASSERT_EQUAL(n_elem_expected, mesh.n_elem());
160 CPPUNIT_ASSERT_EQUAL(n_bcs_before,
162
163 for (const Elem * elem : mesh.element_ptr_range())
164 CPPUNIT_ASSERT_EQUAL(ElemType(TRI3), elem->type());
165 }
166
167 // A single regular octagon (8 sides, 6 subtriangles) exercises the
168 // path where a polygon requires more subelements than any non-polygon
169 // 2D element type would.
171 {
172 LOG_UNIT_TEST;
173
174 constexpr unsigned int n_sides = 8;
175
176 ReplicatedMesh mesh(*TestCommWorld, /*dim=*/2);
177
178 std::unique_ptr<Elem> octagon = std::make_unique<C0Polygon>(n_sides);
179 for (unsigned int i = 0; i < n_sides; ++i)
180 {
181 const Real angle = 2 * libMesh::pi * i / n_sides;
182 Node * node = mesh.add_point(Point(std::cos(angle), std::sin(angle), 0.),
183 /*id=*/i);
184 octagon->set_node(i, node);
185 }
186 octagon->set_id() = 0;
187 Real poly_volume = octagon->volume();
188 Elem * elem = mesh.add_elem(std::move(octagon));
189
190 // Mark every external side with a boundary id so we can verify
191 // boundary information is transferred to the new triangles.
192 for (unsigned int s = 0; s < n_sides; ++s)
193 mesh.get_boundary_info().add_side(elem, s, /*bnd_id=*/0);
194
196
198
199 // n_sides - 2 = 6 subtriangles
200 CPPUNIT_ASSERT_EQUAL(dof_id_type(n_sides - 2), mesh.n_elem());
201 CPPUNIT_ASSERT_EQUAL(std::size_t(n_sides),
203
204 for (const Elem * e : mesh.element_ptr_range())
205 CPPUNIT_ASSERT_EQUAL(ElemType(TRI3), e->type());
206
207 LIBMESH_ASSERT_NUMBERS_EQUAL(poly_volume, MeshTools::volume(mesh), TOLERANCE);
208 }
209
210protected:
211
212 // Builds a C0Polyhedron in mesh whose faces are described by
213 // nodes_on_side (indices into the existing mesh node list), runs
214 // all_tri, and verifies that the result is a pure TET4 mesh with the
215 // expected sub-element count and preserved boundary data.
217 (const std::vector<Point> & points,
218 const std::vector<std::vector<unsigned int>> & nodes_on_side)
219 {
220 ReplicatedMesh mesh(*TestCommWorld, /*dim=*/3);
221
222 for (auto p : index_range(points))
223 mesh.add_point(points[p], /*id=*/p);
224
225 std::vector<std::shared_ptr<Polygon>> sides(nodes_on_side.size());
226 for (auto s : index_range(nodes_on_side))
227 {
228 const auto & nodes_on_s = nodes_on_side[s];
229 sides[s] = std::make_shared<C0Polygon>(nodes_on_s.size());
230 for (auto i : index_range(nodes_on_s))
231 sides[s]->set_node(i, mesh.node_ptr(nodes_on_s[i]));
232 }
233
234 std::unique_ptr<Node> mid_elem_node;
235 std::unique_ptr<Elem> polyhedron =
236 std::make_unique<C0Polyhedron>(sides, mid_elem_node);
237 if (mid_elem_node)
238 mesh.add_node(std::move(mid_elem_node));
239 polyhedron->set_id() = 0;
240 Elem * elem = mesh.add_elem(std::move(polyhedron));
241
242 const auto * poly = cast_ptr<const C0Polyhedron *>(elem);
243 const dof_id_type n_elem_expected = poly->n_subelements();
244
245 // Mark every external face with a boundary id so we can verify
246 // boundary information is transferred to the new tets.
247 for (unsigned int s = 0; s < elem->n_sides(); ++s)
248 mesh.get_boundary_info().add_side(elem, s, /*bnd_id=*/s);
249
250 // The number of boundary triangles produced is the total number of
251 // subtriangles across the polyhedron's polygonal faces.
252 std::size_t n_bcs_expected = 0;
253 for (unsigned int s = 0; s < elem->n_sides(); ++s)
254 n_bcs_expected += sides[s]->n_subtriangles();
255
256 // Keep this map for future checks
257 std::vector<std::array<int, 4>> sub_elem_sides_to_parent_side(elem->n_sub_elem());
258 for (unsigned int b = 0; b < elem->n_sub_elem(); ++b)
259 sub_elem_sides_to_parent_side[b] = poly->subelement_sides_to_poly_sides(b);
260
261 Real poly_volume = poly->volume();
263
265
266 CPPUNIT_ASSERT_EQUAL(n_elem_expected, mesh.n_elem());
267 CPPUNIT_ASSERT_EQUAL(n_bcs_expected,
269
270 for (const Elem * e : mesh.element_ptr_range())
271 CPPUNIT_ASSERT_EQUAL(ElemType(TET4), e->type());
272
273 // Check that the boundary info and numbering is as expected
274 for (const Elem * e : mesh.element_ptr_range())
275 for (const auto s : make_range(e->n_sides()))
276 {
277 // Get parent side
278 const auto side = sub_elem_sides_to_parent_side[e->id()][s];
279 // Check boundary exists on the tet side if the tet side is on a poly side
280 if (side != invalid_int)
281 CPPUNIT_ASSERT_EQUAL(mesh.get_boundary_info().has_boundary_id(e, s, side), true);
282 }
283 LIBMESH_ASSERT_NUMBERS_EQUAL(poly_volume, MeshTools::volume(mesh), TOLERANCE);
284 }
285
286public:
287
288 // A cube built as a C0Polyhedron exercises the path where the
289 // optimal tetrahedralization succeeds (no mid-element node needed).
291 {
292 LOG_UNIT_TEST;
293
294 const std::vector<Point> points =
295 { {0,0,0}, {1,0,0}, {1,1,0}, {0,1,0},
296 {0,0,1}, {1,0,1}, {1,1,1}, {0,1,1} };
297
298 const std::vector<std::vector<unsigned int>> nodes_on_side =
299 { {0, 1, 2, 3}, // min z
300 {0, 1, 5, 4}, // min y
301 {2, 6, 5, 1}, // max x
302 {2, 3, 7, 6}, // max y
303 {0, 4, 7, 3}, // min x
304 {5, 6, 7, 4} }; // max z
305
306 test_helper_c0polyhedron(points, nodes_on_side);
307 }
308
309 // A hexagonal prism exercises the fallback path where a mid-element
310 // node is added to tetrahedralize the polyhedron.
312 {
313 LOG_UNIT_TEST;
314
315 const std::vector<Point> points =
316 { { 0, -2, 0}, {-1, -1, 0}, {-1, 1, 0},
317 { 0, 2, 0}, { 1, 1, 0}, { 1, -1, 0},
318 { 0, -2, 1}, {-1, -1, 1}, {-1, 1, 1},
319 { 0, 2, 1}, { 1, 1, 1}, { 1, -1, 1} };
320
321 const std::vector<std::vector<unsigned int>> nodes_on_side =
322 { {0, 1, 2, 3, 4, 5},
323 {0, 1, 7, 6},
324 {1, 2, 8, 7},
325 {2, 3, 9, 8},
326 {3, 4, 10, 9},
327 {4, 5, 11, 10},
328 {5, 0, 6, 11},
329 {6, 7, 8, 9, 10, 11} };
330
331 test_helper_c0polyhedron(points, nodes_on_side);
332 }
333};
334
335
unsigned int dim
CPPUNIT_TEST_SUITE_REGISTRATION(AllTriTest)
void testAllTriPyramid5()
Definition all_tri.C:126
CPPUNIT_TEST(testAllTriPrism21)
CPPUNIT_TEST(testAllTriC0PolyhedronHexagonalPrism)
CPPUNIT_TEST(testAllTriPyramid14)
CPPUNIT_TEST(testAllTriPrism20)
LIBMESH_CPPUNIT_TEST_SUITE(AllTriTest)
The goal of this test is to verify proper operation of the Mesh Extruder with the optional object cal...
void testAllTriTri()
Definition all_tri.C:108
CPPUNIT_TEST(testAllTriQuad8)
void testAllTriPrism21()
Definition all_tri.C:123
void testAllTriQuad()
Definition all_tri.C:111
void testAllTriPrism20()
Definition all_tri.C:122
void test_helper_c0polyhedron(const std::vector< Point > &points, const std::vector< std::vector< unsigned int > > &nodes_on_side)
Definition all_tri.C:217
CPPUNIT_TEST(testAllTriC0Polygon)
void testAllTriQuad8()
Definition all_tri.C:114
CPPUNIT_TEST(testAllTriC0PolyhedronCube)
void testAllTriC0PolygonOctagon()
Definition all_tri.C:170
void tearDown()
Definition all_tri.C:105
void testAllTriC0Polygon()
Definition all_tri.C:131
CPPUNIT_TEST(testAllTriQuad9)
CPPUNIT_TEST(testAllTriC0PolygonOctagon)
void testAllTriPyramid14()
Definition all_tri.C:127
CPPUNIT_TEST(testAllTriPrism6)
void setUp()
Definition all_tri.C:103
CPPUNIT_TEST(testAllTriTri)
void testAllTriC0PolyhedronHexagonalPrism()
Definition all_tri.C:311
void test_helper(ElemType elem_type, dof_id_type n_elem_expected, std::size_t n_boundary_conds_expected)
Definition all_tri.C:58
void testAllTriQuad9()
Definition all_tri.C:117
void testAllTriPrism18()
Definition all_tri.C:121
CPPUNIT_TEST_SUITE_END()
CPPUNIT_TEST(testAllTriPyramid5)
CPPUNIT_TEST(testAllTriQuad)
void testAllTriPrism6()
Definition all_tri.C:120
CPPUNIT_TEST(testAllTriPrism18)
void testAllTriC0PolyhedronCube()
Definition all_tri.C:290
The BoundaryInfo class contains information relevant to boundary conditions including storing faces,...
std::size_t n_boundary_ids() const
std::size_t n_boundary_conds() const
bool has_boundary_id(const Node *const node, const boundary_id_type id) const
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.
dof_id_type & set_id()
Definition dof_object.h:827
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual unsigned int n_sub_elem() const =0
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:628
virtual unsigned int n_sides() const =0
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
virtual const Node * node_ptr(const dof_id_type i) const =0
virtual dof_id_type n_elem() const =0
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
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.
virtual Node * add_node(Node *n)=0
Add Node n to the end of the vertex array.
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
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:40
The Polygon is an element in 2D with an arbitrary (but fixed) number of sides.
unsigned int n_subtriangles() const
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
static const Real b
Communicator * TestCommWorld
MeshBase & mesh
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.
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 all_tri(MeshBase &mesh)
Subdivides any non-simplex elements in a Mesh to produce simplex (triangular in 2D,...
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 =...
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 int invalid_int
A number which is used quite often to represent an invalid or uninitialized value for an integer.
Definition libmesh.h:309
const Real pi
.
Definition libmesh.h:292
static constexpr Real TOLERANCE
uint8_t dof_id_type
Definition id_types.h:67
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