libMesh
all_rbb.C
Go to the documentation of this file.
1 #include <libmesh/boundary_info.h>
2 #include <libmesh/elem.h>
3 #include <libmesh/enum_quadrature_type.h>
4 #include <libmesh/fe_map.h>
5 #include <libmesh/mesh.h>
6 #include <libmesh/mesh_generation.h>
7 #include <libmesh/mesh_modification.h>
8 #include <libmesh/mesh_tools.h>
9 #include <libmesh/quadrature.h>
10 
11 #include "test_comm.h"
12 #include "libmesh_cppunit.h"
13 
14 
15 using namespace libMesh;
16 
17 class AllRBBTest : public CppUnit::TestCase
18 {
27 public:
28  LIBMESH_CPPUNIT_TEST_SUITE( AllRBBTest );
29 
30  CPPUNIT_TEST( testAllRBBNodeElem );
31  CPPUNIT_TEST( testAllRBBEdge );
32  CPPUNIT_TEST( testAllRBBEdge3 );
33 
34  // 2D tests
35 #if LIBMESH_DIM > 1
36  CPPUNIT_TEST( testAllRBBTri );
37  CPPUNIT_TEST( testAllRBBTri6 );
38  CPPUNIT_TEST( testAllRBBQuad );
39  CPPUNIT_TEST( testAllRBBQuad8 );
40  CPPUNIT_TEST( testAllRBBQuad9 );
41 
42  // We use AMR when generating circles
43 # ifdef LIBMESH_ENABLE_AMR
44  CPPUNIT_TEST( testAllRBBCircle4 );
45  CPPUNIT_TEST( testAllRBBCircle8 );
46  CPPUNIT_TEST( testAllRBBCircle16 );
47 
48  CPPUNIT_TEST( testAllRBBDisk5 );
49  CPPUNIT_TEST( testAllRBBDisk20 );
50  CPPUNIT_TEST( testAllRBBDisk80 );
51 
52  CPPUNIT_TEST( testAllRBBTri6Disk10 );
53  CPPUNIT_TEST( testAllRBBTri6Disk40 );
54  CPPUNIT_TEST( testAllRBBTri6Disk160 );
55 # endif
56 #endif
57 
58  // 3D tests
59 #if LIBMESH_DIM > 2
60  CPPUNIT_TEST( testAllRBBTet );
61  CPPUNIT_TEST( testAllRBBTet10 );
62  CPPUNIT_TEST( testAllRBBHex );
63  CPPUNIT_TEST( testAllRBBHex20 );
64  CPPUNIT_TEST( testAllRBBHex27 );
65 
66 // We need to add BERNSTEIN support for Prisms!
67 // CPPUNIT_TEST( testAllRBBPrism6 );
68 // CPPUNIT_TEST( testAllRBBPrism18 );
69 
70  // We use AMR when generating circles
71 # ifdef LIBMESH_ENABLE_AMR
72  CPPUNIT_TEST( testAllRBBCylinder10 );
73  CPPUNIT_TEST( testAllRBBCylinder80 );
74 # endif
75 #endif
76 
77  CPPUNIT_TEST_SUITE_END();
78 
79 protected:
80  // We don't do anything interesting to affine elements in all_rbb(),
81  // but we can verify that we're not screwing them up.
82  void test_box(ElemType elem_type)
83  {
85 
86  auto dim = Elem::type_to_dim_map[elem_type];
87 
89  dim > 0 ? 2 : 0, dim > 1 ? 1 : 0, dim > 2 ? 1 : 0,
90  0., 1.,
91  0., 1.,
92  0., 1.,
93  elem_type);
94 
95  const auto n_orig_elem = mesh.n_elem();
96  CPPUNIT_ASSERT_EQUAL(n_orig_elem, mesh.max_elem_id());
97 
98  const Real orig_volume = MeshTools::volume(mesh) / n_orig_elem;
99 
100  std::vector<Real> orig_hmin(n_orig_elem), orig_hmax(n_orig_elem);
101 
102  // In the tet case our elements all have the same volume but
103  // they're stretched differently and have different hmin/hmax
104  for (auto & elem : mesh.element_ptr_range())
105  {
106  orig_hmin[elem->id()] = elem->hmin();
107  orig_hmax[elem->id()] = elem->hmax();
108  }
109 
111 
112  CPPUNIT_ASSERT_EQUAL(n_orig_elem, mesh.n_elem());
113  CPPUNIT_ASSERT_EQUAL(n_orig_elem, mesh.max_elem_id());
114 
115  unsigned char weight_index = mesh.default_mapping_data();
116 
117  for (auto & elem : mesh.element_ptr_range())
118  {
119  CPPUNIT_ASSERT_EQUAL(elem->mapping_type(),
121  CPPUNIT_ASSERT(elem->has_affine_map());
122 
123  // Tri6 has this much FP error??
124  LIBMESH_ASSERT_FP_EQUAL(elem->volume(), orig_volume,
125  20*TOLERANCE*TOLERANCE);
126  LIBMESH_ASSERT_FP_EQUAL(elem->hmax(), orig_hmax[elem->id()],
128  LIBMESH_ASSERT_FP_EQUAL(elem->hmin(), orig_hmin[elem->id()],
130  }
131 
132  for (auto & node : mesh.node_ptr_range())
133  {
134  const Real w = node->get_extra_datum<Real>(weight_index);
135 
136  CPPUNIT_ASSERT_EQUAL(Real(1), w);
137  }
138  }
139 
140  void test_circle(unsigned int n_refinements)
141  {
142  Mesh interior_mesh(*TestCommWorld),
143  boundary_mesh(*TestCommWorld);
144 
145  const Real radius = 1;
146  const Real circumference = 2 * pi * radius;
147  const Real tol = TOLERANCE*TOLERANCE;
148 
149  // Build a filled circle
151  n_refinements, QUAD9);
152 
153  // Get just the outer EDGE3 circle mesh
154  interior_mesh.get_boundary_info().sync(boundary_mesh);
155 
156  const dof_id_type n_edges = 4 << n_refinements;
157 
158  CPPUNIT_ASSERT_EQUAL(boundary_mesh.n_elem(), n_edges);
159  CPPUNIT_ASSERT_EQUAL(boundary_mesh.n_elem(), n_edges);
160 
161  for (auto & node : boundary_mesh.node_ptr_range())
162  {
163  const Point p = *node;
164  LIBMESH_ASSERT_FP_EQUAL(p.norm(), radius, tol);
165  }
166 
167  // We just did Lagrange interpolation, so our mesh measure
168  // shouldn't be *quite* right. Empirically, we converge from
169  // beneath, and our error looks like Ch^4.
170  const Real max_lagrange_error =
171  radius * 5e-2 / (1 << (4*n_refinements));
172  LIBMESH_ASSERT_FP_EQUAL(MeshTools::volume(boundary_mesh),
173  circumference, max_lagrange_error);
174 
175  MeshTools::Modification::all_rbb(boundary_mesh);
176 
177  for (auto & elem : boundary_mesh.element_ptr_range())
178  {
179  CPPUNIT_ASSERT_EQUAL(RATIONAL_BERNSTEIN_MAP, elem->mapping_type());
180 
181  // We can no longer assert that each Node is at a specified
182  // radius from the circle center, because these are now spline
183  // control nodes, but we can assert that physical points
184  // within the element are at the desired radius.
185  constexpr int n_intervals = 4;
186  Point master_pt;
187  for (master_pt(0) = -1; master_pt(0) <= 1 + TOLERANCE;
188  master_pt(0) += Real(2)/n_intervals)
189  {
190  const Point p = FEMap::map(elem->dim(), elem, master_pt);
191  LIBMESH_ASSERT_FP_EQUAL(radius, p.norm(), tol);
192  }
193  }
194 
195  // We're using quadrature for volume approximation, so we still
196  // have error, but our quadrature error looks something like Ch^6
197  // with a much smaller C.
198  const Real max_rbb_error =
199  radius * 1e-3 / (1 << (6*n_refinements));
200  LIBMESH_ASSERT_FP_EQUAL(MeshTools::volume(boundary_mesh),
201  circumference, max_rbb_error);
202  }
203 
204  void test_disk(unsigned int n_refinements, const ElemType type = QUAD9)
205  {
207 
208  const Real radius = 1;
209  const Real area = pi * radius * radius;
210  const Real tol = TOLERANCE*TOLERANCE;
211 
212  // Build a filled circle
214  n_refinements, type);
215 
216  const dof_id_type n_elem =
217  (5 << (n_refinements*2)) * (type == QUAD9 ? 1 : 2);
218 
219  CPPUNIT_ASSERT_EQUAL(mesh.n_elem(), n_elem);
220 
221  // We just did Lagrange interpolation, so our mesh measure
222  // shouldn't be *quite* right. Empirically, we converge from
223  // beneath, and our error looks like Ch^4.
224  const Real max_lagrange_error =
225  radius * 5e-2 / (1 << (4*n_refinements));
226 
227  LIBMESH_ASSERT_FP_EQUAL(MeshTools::volume(mesh),
228  area, max_lagrange_error);
229 
231 
232  for (const Elem * elem : mesh.element_ptr_range())
233  {
234  CPPUNIT_ASSERT_EQUAL(RATIONAL_BERNSTEIN_MAP, elem->mapping_type());
235 
236  // We can no longer assert that each Node is at a specified
237  // radius from the circle center, because these are now spline
238  // control nodes, but we can assert that physical points
239  // within the element are at the desired radius.
240  for (auto s : make_range(elem->n_sides()))
241  {
242  if (elem->neighbor_ptr(s))
243  continue;
244 
245  constexpr int n_intervals = 4;
246  Point master_pt = elem->master_point(s);
247  const Point step =
248  (elem->master_point((s+1)%elem->n_sides()) - master_pt)
249  / n_intervals;
250  for (auto i : make_range(n_intervals+1))
251  {
252  libmesh_ignore(i);
253  const Point p = FEMap::map(elem->dim(), elem, master_pt);
254  LIBMESH_ASSERT_FP_EQUAL(radius, p.norm(), tol);
255  master_pt += step;
256  }
257  }
258  }
259 
260  // We're using quadrature for volume approximation, so we still
261  // have error, but our quadrature error looks like Ch^6 with a
262  // much smaller C.
263  const Real max_rbb_error =
264  radius * 2e-3 / (1 << (6*n_refinements));
265  LIBMESH_ASSERT_FP_EQUAL(MeshTools::volume(mesh),
266  area, max_rbb_error);
267  }
268 
269  void test_cylinder (unsigned int n_refinements, const ElemType type = HEX27)
270  {
271  Mesh disk_mesh(*TestCommWorld), mesh(*TestCommWorld);
272 
273  const Real radius = 1;
274  const Real height = 3;
275  const Real volume = pi * radius * radius * height;
276  const Real tol = TOLERANCE*TOLERANCE;
277 
278  // We're extruding a circle from side 0 up
279  const ElemType side_type = Elem::build(type)->side_type(0);
280 
281  // Build a filled circle
283  n_refinements, side_type);
284 
285  // Then extrude it into a cylinder
286  const unsigned int nz = 2;
287  const RealVectorValue extrusion_vector{0,0,height};
288  MeshTools::Generation::build_extrusion (mesh, disk_mesh, nz, extrusion_vector);
289 
290  const dof_id_type n_elem =
291  (5 << (n_refinements*2)) * (side_type == QUAD9 ? 1 : 2) * nz;
292 
293  CPPUNIT_ASSERT_EQUAL(mesh.n_elem(), n_elem);
294 
295  // We just did Lagrange interpolation, so our mesh measure
296  // shouldn't be *quite* right, but we should converge similarly to
297  // how we did with the filled disk.
298  const Real max_lagrange_error =
299  radius * 5e-2 / (1 << (4*n_refinements)) *
300  radius * radius * height;
301 
302  LIBMESH_ASSERT_FP_EQUAL(MeshTools::volume(mesh),
303  volume, max_lagrange_error);
304 
306 
307  std::unique_ptr<const Elem> elem_side;
308  constexpr int n_intervals = 4;
309  auto qrule = QBase::build(QGRID, /*dim=*/2, Order(n_intervals));
310 
311  for (const Elem * elem : mesh.element_ptr_range())
312  {
313  CPPUNIT_ASSERT_EQUAL(RATIONAL_BERNSTEIN_MAP, elem->mapping_type());
314 
315  // We cannot assert that each Node is at a specified radius
316  // from the cylinder axis, because these are now spline
317  // control nodes, but we can assert that physical points
318  // within the rounded surface are at the desired radius.
319  for (auto s : make_range(elem->n_sides()))
320  {
321  if (elem->neighbor_ptr(s))
322  continue;
323 
324  const Point side_normal =
325  elem->side_vertex_average_normal(s);
326 
327  // We ought to either be on the rounded surface or the end
328  // caps
329  if (std::abs(side_normal(2)) > TOLERANCE*TOLERANCE)
330  {
331  LIBMESH_ASSERT_FP_EQUAL(side_normal(0), 0, TOLERANCE*TOLERANCE);
332  LIBMESH_ASSERT_FP_EQUAL(side_normal(1), 0, TOLERANCE*TOLERANCE);
333  continue;
334  }
335 
336  elem->build_side_ptr(elem_side, s);
337  qrule->init(*elem_side);
338 
339  for (auto i : make_range(qrule->n_points()))
340  {
341  Point p = FEMap::map(elem_side->dim(), elem_side.get(), qrule->qp(i));
342  p(2) = 0; // Just look at r in cylindrical coordinates
343  LIBMESH_ASSERT_FP_EQUAL(radius, p.norm(), tol);
344  }
345  }
346  }
347 
348  // We're using quadrature for volume approximation, so we still
349  // have error, but our quadrature error looks like Ch^6 with a
350  // much smaller C.
351  const Real max_rbb_error =
352  radius * 2e-3 / (1 << (6*n_refinements)) *
353  radius * radius * height;
354  LIBMESH_ASSERT_FP_EQUAL(MeshTools::volume(mesh),
355  volume, max_rbb_error);
356  }
357 
358 
359 public:
360  void setUp() {}
361 
362  void tearDown() {}
363 
364  void testAllRBBNodeElem() { LOG_UNIT_TEST; test_box(NODEELEM); }
365  void testAllRBBEdge() { LOG_UNIT_TEST; test_box(EDGE2); }
366  void testAllRBBEdge3() { LOG_UNIT_TEST; test_box(EDGE3); }
367  void testAllRBBTri() { LOG_UNIT_TEST; test_box(TRI3); }
368  void testAllRBBTri6() { LOG_UNIT_TEST; test_box(TRI6); }
369  void testAllRBBQuad() { LOG_UNIT_TEST; test_box(QUAD4); }
370  void testAllRBBQuad8() { LOG_UNIT_TEST; test_box(QUAD8); }
371  void testAllRBBQuad9() { LOG_UNIT_TEST; test_box(QUAD9); }
372  void testAllRBBTet() { LOG_UNIT_TEST; test_box(TET4); }
373  void testAllRBBTet10() { LOG_UNIT_TEST; test_box(TET10); }
374  void testAllRBBHex() { LOG_UNIT_TEST; test_box(HEX8); }
375  void testAllRBBHex20() { LOG_UNIT_TEST; test_box(HEX20); }
376  void testAllRBBHex27() { LOG_UNIT_TEST; test_box(HEX27); }
377 
378  // We need to add BERNSTEIN support for Prisms!
379  // void testAllRBBPrism6() { LOG_UNIT_TEST; test_box(PRISM6); }
380  // void testAllRBBPrism18() { LOG_UNIT_TEST; test_box(PRISM18); }
381 
382  // We still don't support general Polys, Tri7s or anything with them
383  // as faces, infinite elements, or anything above quadratic.
384 
385  // 0 refinements of our default circle gives us 4 RBB edges
386  void testAllRBBCircle4() { LOG_UNIT_TEST; test_circle(0); }
387  void testAllRBBCircle8() { LOG_UNIT_TEST; test_circle(1); }
388  void testAllRBBCircle16() { LOG_UNIT_TEST; test_circle(2); }
389 
390  // 0 refinements of our default disk gives us 5 RBB quads or 10 RBB
391  // triangles
392  void testAllRBBDisk5() { LOG_UNIT_TEST; test_disk(0); }
393  void testAllRBBDisk20() { LOG_UNIT_TEST; test_disk(1); }
394  void testAllRBBDisk80() { LOG_UNIT_TEST; test_disk(2); }
395 
396  void testAllRBBTri6Disk10() { LOG_UNIT_TEST; test_disk(0, TRI6); }
397  void testAllRBBTri6Disk40() { LOG_UNIT_TEST; test_disk(1, TRI6); }
398  void testAllRBBTri6Disk160() { LOG_UNIT_TEST; test_disk(2, TRI6); }
399 
400  // Extruding a disk into 2 layers gives us twice as many hexes as we
401  // had quads
402  void testAllRBBCylinder10() { LOG_UNIT_TEST; test_cylinder(0); }
403  void testAllRBBCylinder80() { LOG_UNIT_TEST; test_cylinder(1); }
404 };
405 
406 
void test_circle(unsigned int n_refinements)
Definition: all_rbb.C:140
void testAllRBBDisk5()
Definition: all_rbb.C:392
void testAllRBBEdge()
Definition: all_rbb.C:365
ElemType
Defines an enum for geometric element types.
void build_extrusion(UnstructuredMesh &mesh, const MeshBase &cross_section, const unsigned int nz, RealVectorValue extrusion_vector, QueryElemSubdomainIDBase *elem_subdomain=nullptr)
Meshes the tensor product of a 1D and a 1D-or-2D domain.
void testAllRBBCylinder80()
Definition: all_rbb.C:403
Order
defines an enum for polynomial orders.
Definition: enum_order.h:40
void testAllRBBQuad9()
Definition: all_rbb.C:371
const Real radius
auto norm() const
Definition: type_vector.h:908
void testAllRBBCircle4()
Definition: all_rbb.C:386
void testAllRBBQuad()
Definition: all_rbb.C:369
dof_id_type n_elem(const MeshBase::const_element_iterator &begin, const MeshBase::const_element_iterator &end)
Count up the number of elements of a specific type (as defined by an iterator range).
Definition: mesh_tools.C:1004
libMesh::Parallel::Communicator * TestCommWorld
Definition: driver.C:218
static constexpr Real TOLERANCE
void all_rbb(MeshBase &mesh)
Converts all element geometric mappings from the default Lagrange to the more flexible Rational-Bezie...
unsigned int dim
void testAllRBBTri6Disk160()
Definition: all_rbb.C:398
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
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
MeshBase & mesh
void testAllRBBDisk80()
Definition: all_rbb.C:394
The libMesh namespace provides an interface to certain functionality in the library.
void testAllRBBTri6()
Definition: all_rbb.C:368
void setUp()
Definition: all_rbb.C:360
void testAllRBBTri6Disk10()
Definition: all_rbb.C:396
void testAllRBBHex()
Definition: all_rbb.C:374
void testAllRBBTri()
Definition: all_rbb.C:367
void libmesh_ignore(const Args &...)
void testAllRBBTet10()
Definition: all_rbb.C:373
void testAllRBBDisk20()
Definition: all_rbb.C:393
unsigned char default_mapping_data() const
Returns any default data value used by the master space to physical space mapping.
Definition: mesh_base.h:949
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:442
virtual dof_id_type max_elem_id() const =0
void testAllRBBQuad8()
Definition: all_rbb.C:370
void testAllRBBHex20()
Definition: all_rbb.C:375
void tearDown()
Definition: all_rbb.C:362
void testAllRBBCylinder10()
Definition: all_rbb.C:402
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
void testAllRBBEdge3()
Definition: all_rbb.C:366
void testAllRBBCircle16()
Definition: all_rbb.C:388
void test_box(ElemType elem_type)
Definition: all_rbb.C:82
void build_sphere(UnstructuredMesh &mesh, const Real radius=1, const unsigned int n_refinements=2, const ElemType type=INVALID_ELEM, const unsigned int n_smooth=2, const bool flat=true)
Meshes a spherical or mapped-spherical domain.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void testAllRBBHex27()
Definition: all_rbb.C:376
static Point map(const unsigned int dim, const Elem *elem, const Point &reference_point)
Definition: fe_map.C:1954
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 std::unique_ptr< QBase > build(std::string_view name, const unsigned int dim, const Order order=INVALID_ORDER)
Builds a specific quadrature rule based on the name string.
void testAllRBBTet()
Definition: all_rbb.C:372
void testAllRBBCircle8()
Definition: all_rbb.C:387
void test_cylinder(unsigned int n_refinements, const ElemType type=HEX27)
Definition: all_rbb.C:269
virtual dof_id_type n_elem() const override final
void testAllRBBTri6Disk40()
Definition: all_rbb.C:397
virtual dof_id_type n_elem() const =0
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition: mesh.h:50
void test_disk(unsigned int n_refinements, const ElemType type=QUAD9)
Definition: all_rbb.C:204
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
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 testAllRBBNodeElem()
Definition: all_rbb.C:364
CPPUNIT_TEST_SUITE_REGISTRATION(AllRBBTest)
uint8_t dof_id_type
Definition: id_types.h:67
const Real pi
.
Definition: libmesh.h:292