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