libMesh
quadrature_test.C
Go to the documentation of this file.
1 #include <libmesh/elem.h>
2 #include <libmesh/enum_quadrature_type.h>
3 #include <libmesh/quadrature.h>
4 #include <libmesh/string_to_enum.h>
5 #include <libmesh/utility.h>
6 
7 #include <iomanip>
8 #include <numeric> // std::iota
9 
10 #include "libmesh_cppunit.h"
11 
12 
13 using namespace libMesh;
14 
15 #define MACROCOMMA ,
16 
17 #if LIBMESH_DIM > 2
18 #define TEST_ONE_ORDER(qtype, order, maxorder) \
19  CPPUNIT_TEST( testBuild<qtype MACROCOMMA order> ); \
20  CPPUNIT_TEST( test1DWeights<qtype MACROCOMMA order MACROCOMMA maxorder> ); \
21  CPPUNIT_TEST( test2DWeights<qtype MACROCOMMA order MACROCOMMA maxorder> ); \
22  CPPUNIT_TEST( test3DWeights<qtype MACROCOMMA order MACROCOMMA maxorder> );
23 #elif LIBMESH_DIM > 1
24 #define TEST_ONE_ORDER(qtype, order, maxorder) \
25  CPPUNIT_TEST( testBuild<qtype MACROCOMMA order> ); \
26  CPPUNIT_TEST( test1DWeights<qtype MACROCOMMA order MACROCOMMA maxorder> ); \
27  CPPUNIT_TEST( test2DWeights<qtype MACROCOMMA order MACROCOMMA maxorder> );
28 #else
29 #define TEST_ONE_ORDER(qtype, order, maxorder) \
30  CPPUNIT_TEST( testBuild<qtype MACROCOMMA order> ); \
31  CPPUNIT_TEST( test1DWeights<qtype MACROCOMMA order MACROCOMMA maxorder> );
32 #endif
33 
34 // std::min isn't constexpr, and C++03 lacks constexpr anyway
35 #define mymin(a, b) (a < b ? a : b)
36 
37 #define TEST_NINTH_ORDER(qtype, maxorder) \
38  TEST_ONE_ORDER(qtype, FIRST, mymin(1,maxorder)); \
39  TEST_ONE_ORDER(qtype, SECOND, mymin(2,maxorder)); \
40  TEST_ONE_ORDER(qtype, THIRD, mymin(3,maxorder)); \
41  TEST_ONE_ORDER(qtype, FOURTH, mymin(4,maxorder)); \
42  TEST_ONE_ORDER(qtype, FIFTH, mymin(5,maxorder)); \
43  TEST_ONE_ORDER(qtype, SIXTH, mymin(6,maxorder)); \
44  TEST_ONE_ORDER(qtype, SEVENTH, mymin(7,maxorder)); \
45  TEST_ONE_ORDER(qtype, EIGHTH, mymin(8,maxorder)); \
46  TEST_ONE_ORDER(qtype, NINTH, mymin(9,maxorder));
47 
48 #define TEST_TWENTIETH_ORDER(qtype, maxorder) \
49  TEST_NINTH_ORDER(qtype, maxorder) \
50  TEST_ONE_ORDER(qtype, TENTH, mymin(10,maxorder)); \
51  TEST_ONE_ORDER(qtype, ELEVENTH, mymin(11,maxorder)); \
52  TEST_ONE_ORDER(qtype, TWELFTH, mymin(12,maxorder)); \
53  TEST_ONE_ORDER(qtype, THIRTEENTH, mymin(13,maxorder));\
54  TEST_ONE_ORDER(qtype, FOURTEENTH, mymin(14,maxorder));\
55  TEST_ONE_ORDER(qtype, FIFTEENTH, mymin(15,maxorder)); \
56  TEST_ONE_ORDER(qtype, SIXTEENTH, mymin(16,maxorder)); \
57  TEST_ONE_ORDER(qtype, SEVENTEENTH, mymin(17,maxorder));\
58  TEST_ONE_ORDER(qtype, EIGHTEENTH, mymin(18,maxorder));\
59  TEST_ONE_ORDER(qtype, NINETEENTH, mymin(19,maxorder));\
60  TEST_ONE_ORDER(qtype, TWENTIETH, mymin(20,maxorder));
61 
62 #define LIBMESH_ASSERT_REALS_EQUAL(first, second, tolerance) \
63  if (std::abs(first-second) >= tolerance) \
64  { \
65  std::cerr << "first = " << first << std::endl; \
66  std::cerr << "second = " << second << std::endl; \
67  std::cerr << "error = " << std::abs(first-second) << std::endl; \
68  std::cerr << "tolerance = " << tolerance << std::endl; \
69  } \
70  CPPUNIT_ASSERT (std::abs(first-second) < tolerance)
71 
72 class QuadratureTest : public CppUnit::TestCase {
73 public:
74  LIBMESH_CPPUNIT_TEST_SUITE( QuadratureTest );
75 
76  TEST_TWENTIETH_ORDER(QGAUSS, 9999);
77  TEST_ONE_ORDER(QSIMPSON, FIRST, 1);
78  TEST_ONE_ORDER(QSIMPSON, SECOND, 2);
79  TEST_ONE_ORDER(QSIMPSON, THIRD, 3);
80  TEST_ONE_ORDER(QTRAP, FIRST, 1);
81  TEST_NINTH_ORDER(QGRID, 1);
82 
83  // In general, QNodal rules (e.g. QTRAP) are only exact for linears.
84  // QSIMPSON is a special case of a nodal quadrature which obtains
85  // higher accuracy.
86  TEST_ONE_ORDER(QNODAL, /*ignored*/FIRST, /*max order=*/1);
87 
88  TEST_NINTH_ORDER(QGAUSS_LOBATTO, 9);
89 
90  // The super-high-order Gauss-Lobatto quadrature rules only take
91  // ~0.2s to test for me these days. - RHS
92  TEST_ONE_ORDER(QGAUSS_LOBATTO, ELEVENTH, 11);
93  TEST_ONE_ORDER(QGAUSS_LOBATTO, THIRTEENTH, 13);
94  TEST_ONE_ORDER(QGAUSS_LOBATTO, FIFTEENTH, 15);
95  TEST_ONE_ORDER(QGAUSS_LOBATTO, SEVENTEENTH, 17);
96  TEST_ONE_ORDER(QGAUSS_LOBATTO, NINETEENTH, 19);
97  TEST_ONE_ORDER(QGAUSS_LOBATTO, TWENTYFIRST, 21);
98  TEST_ONE_ORDER(QGAUSS_LOBATTO, TWENTYTHIRD, 23);
99  TEST_ONE_ORDER(QGAUSS_LOBATTO, TWENTYFIFTH, 25);
100  TEST_ONE_ORDER(QGAUSS_LOBATTO, TWENTYSEVENTH, 27);
101  TEST_ONE_ORDER(QGAUSS_LOBATTO, TWENTYNINTH, 29);
102  TEST_ONE_ORDER(QGAUSS_LOBATTO, THIRTYFIRST, 31);
103  TEST_ONE_ORDER(QGAUSS_LOBATTO, THIRTYTHIRD, 33);
104  TEST_ONE_ORDER(QGAUSS_LOBATTO, THIRTYFIFTH, 35);
105  TEST_ONE_ORDER(QGAUSS_LOBATTO, THIRTYSEVENTH, 37);
106  TEST_ONE_ORDER(QGAUSS_LOBATTO, THIRTYNINTH, 39);
107  TEST_ONE_ORDER(QGAUSS_LOBATTO, FORTYFIRST, 41);
108  TEST_ONE_ORDER(QGAUSS_LOBATTO, FORTYTHIRD, 43);
109 
110  // Might as well test super-high 1-D Gauss while we're at it.
111  // Some of the really high order stuff is getting pulled in
112  // indirectly; we'll just hit what lcov says we were missing.
113  CPPUNIT_TEST( test1DWeights<QGAUSS MACROCOMMA TWENTYFIRST MACROCOMMA 21> );
114  CPPUNIT_TEST( test1DWeights<QGAUSS MACROCOMMA TWENTYFIFTH MACROCOMMA 25> );
115  CPPUNIT_TEST( test1DWeights<QGAUSS MACROCOMMA TWENTYSEVENTH MACROCOMMA 27> );
116  CPPUNIT_TEST( test1DWeights<QGAUSS MACROCOMMA TWENTYNINTH MACROCOMMA 29> );
117  CPPUNIT_TEST( test1DWeights<QGAUSS MACROCOMMA THIRTYFIRST MACROCOMMA 31> );
118  CPPUNIT_TEST( test1DWeights<QGAUSS MACROCOMMA THIRTYTHIRD MACROCOMMA 33> );
119  CPPUNIT_TEST( test1DWeights<QGAUSS MACROCOMMA THIRTYFIFTH MACROCOMMA 35> );
120  CPPUNIT_TEST( test1DWeights<QGAUSS MACROCOMMA THIRTYSEVENTH MACROCOMMA 37> );
121  CPPUNIT_TEST( test1DWeights<QGAUSS MACROCOMMA THIRTYNINTH MACROCOMMA 39> );
122  CPPUNIT_TEST( test1DWeights<QGAUSS MACROCOMMA FORTYFIRST MACROCOMMA 41> );
123  CPPUNIT_TEST( test1DWeights<QGAUSS MACROCOMMA FORTYTHIRD MACROCOMMA 43> );
124 
125  // Test monomial quadrature rules on quads and hexes
126  CPPUNIT_TEST( testMonomialQuadrature );
127 
128  // Test nodal quadrature rules on every specific type
129  CPPUNIT_TEST( testNodalQuadrature );
130 
131  // Test quadrature rules on Triangles
132 #if LIBMESH_DIM > 1
133  CPPUNIT_TEST( testTriQuadrature );
134 #endif
135 
136  // Test quadrature rules on Tetrahedra
137 #if LIBMESH_DIM > 2
138  CPPUNIT_TEST( testTetQuadrature );
139 #endif
140 
141  // Test Jacobi quadrature rules with special weighting function
142  CPPUNIT_TEST( testJacobi );
143 
144  CPPUNIT_TEST_SUITE_END();
145 
146 private:
147 
149 
150  void testPolynomial(QBase & qrule,
151  int xp, int yp, int zp,
152  Real true_value)
153  {
154  Real sum = 0.;
155  for (unsigned int qp=0; qp<qrule.n_points(); qp++)
156  {
157  const Point p = qrule.qp(qp);
158  Real val = qrule.w(qp) * std::pow(p(0),xp);
159  if (qrule.get_dim() > 1)
160  val *= std::pow(p(1),yp);
161  if (qrule.get_dim() > 2)
162  val *= std::pow(p(2),zp);
163  sum += val;
164  }
165 
166  // Make sure that the weighted evaluations add up to the true
167  // integral value we expect
168  LIBMESH_ASSERT_REALS_EQUAL(true_value, sum, quadrature_tolerance);
169  }
170 
171  void testPolynomials(QuadratureType qtype, int order,
172  ElemType elem_type,
173  const std::function<Real(int,int,int)> & true_value,
174  int exactorder)
175  {
176  auto elem = Elem::build(elem_type);
177  unsigned int dim = elem->dim();
178  std::unique_ptr<QBase> qrule =
179  QBase::build(qtype, dim, static_cast<Order>(order));
180 
181  // We are testing integration on the reference elements here, so
182  // the element map is not relevant, and we can safely use nodal
183  // Pyramid quadrature.
184  if (elem_type == PYRAMID5 || elem_type == PYRAMID13 ||
185  elem_type == PYRAMID14 || elem_type == PYRAMID18)
186  qrule->allow_nodal_pyramid_quadrature = true;
187 
188  qrule->init (*elem);
189 
190  const int max_y_order = dim>1 ? 0 : exactorder;
191  const int max_z_order = dim>2 ? 0 : exactorder;
192 
193  for (int xp=0; xp <= exactorder; ++xp)
194  for (int yp=0; yp <= max_y_order; ++yp)
195  for (int zp=0; zp <= max_z_order; ++zp)
196  {
197  // Only try to integrate polynomials we can integrate exactly
198  if (xp + yp + zp > exactorder)
199  continue;
200 
201  const Real exact = true_value(xp, yp, zp);
202  testPolynomial(*qrule, xp, yp, zp, exact);
203  }
204  }
205 
206  const std::function<Real(int,int,int)> edge_integrals =
207  [](int mode, int, int) {
208  return (mode % 2) ? 0 : (Real(2.0) / (mode+1));
209  };
210 
211  const std::function<Real(int,int,int)> quad_integrals =
212  [](int modex, int modey, int) {
213  const Real exactx = (modex % 2) ?
214  0 : (Real(2.0) / (modex+1));
215 
216  const Real exacty = (modey % 2) ?
217  0 : (Real(2.0) / (modey+1));
218 
219  return exactx*exacty;
220  };
221 
222  const std::function<Real(int,int,int)> tri_integrals =
223  [](int x_power, int y_power, int) {
224  // Compute the true integral, a! b! / (a + b + 2)!
225  Real analytical = 1.0;
226 
227  unsigned
228  larger_power = std::max(x_power, y_power),
229  smaller_power = std::min(x_power, y_power);
230 
231  // Cancel the larger of the two numerator terms with the
232  // denominator, and fill in the remaining entries.
233  std::vector<unsigned>
234  numerator(smaller_power > 1 ? smaller_power-1 : 0),
235  denominator(2+smaller_power);
236 
237  // Fill up the vectors with sequences starting at the right values.
238  std::iota(numerator.begin(), numerator.end(), 2);
239  std::iota(denominator.begin(), denominator.end(), larger_power+1);
240 
241  // The denominator is guaranteed to have more terms...
242  for (std::size_t i=0; i<denominator.size(); ++i)
243  {
244  if (i < numerator.size())
245  analytical *= numerator[i];
246  analytical /= denominator[i];
247  }
248  return analytical;
249  };
250 
251  const std::function<Real(int,int,int)> hex_integrals =
252  [](int modex, int modey, int modez) {
253  const Real exactx = (modex % 2) ?
254  0 : (Real(2.0) / (modex+1));
255 
256  const Real exacty = (modey % 2) ?
257  0 : (Real(2.0) / (modey+1));
258 
259  const Real exactz = (modez % 2) ?
260  0 : (Real(2.0) / (modez+1));
261 
262  return exactx*exacty*exactz;
263  };
264 
265  const std::function<Real(int,int,int)> tet_integrals =
266  [](int x_power, int y_power, int z_power) {
267  // Compute the true integral, a! b! c! / (a + b + c + 3)!
268  Real analytical = 1.0;
269 
270  // Sort the a, b, c values
271  int sorted_powers[3] = {x_power, y_power, z_power};
272  std::sort(sorted_powers, sorted_powers+3);
273 
274  // Cancel the largest power with the denominator, fill in the
275  // entries for the remaining numerator terms and the denominator.
276  std::vector<int>
277  numerator_1(sorted_powers[0] > 1 ? sorted_powers[0]-1 : 0),
278  numerator_2(sorted_powers[1] > 1 ? sorted_powers[1]-1 : 0),
279  denominator(3 + sorted_powers[0] + sorted_powers[1]);
280 
281  // Fill up the vectors with sequences starting at the right values.
282  std::iota(numerator_1.begin(), numerator_1.end(), 2);
283  std::iota(numerator_2.begin(), numerator_2.end(), 2);
284  std::iota(denominator.begin(), denominator.end(), sorted_powers[2]+1);
285 
286  // The denominator is guaranteed to have the most terms...
287  for (std::size_t i=0; i<denominator.size(); ++i)
288  {
289  if (i < numerator_1.size())
290  analytical *= numerator_1[i];
291 
292  if (i < numerator_2.size())
293  analytical *= numerator_2[i];
294 
295  analytical /= denominator[i];
296  }
297  return analytical;
298  };
299 
300  const std::function<Real(int,int,int)> prism_integrals =
301  [this](int modex, int modey, int modez) {
302  const Real exactz = (modez % 2) ?
303  0 : (Real(2.0) / (modez+1));
304 
305  return exactz * tri_integrals(modex, modey, 0);
306  };
307 
308  const std::function<Real(int,int,int)> pyramid_integrals =
309  [](int modex, int modey, int modez) {
310 
311  const int binom = Utility::binomial(modex+modey+modez+3, modez);
312 
313  if (modex%2 || modey%2)
314  return Real(0);
315 
316  return Real(4)/((modex+1)*(modey+1)*binom*(modex+modey+modez+3));
317  };
318 
319 
320 public:
321  void setUp ()
322  { quadrature_tolerance = TOLERANCE * std::sqrt(TOLERANCE); }
323 
324  void tearDown ()
325  {}
326 
328  {
329  LOG_UNIT_TEST;
330 
331  const std::vector<std::vector<ElemType>> all_types =
332  {{EDGE2, EDGE3, EDGE4},
333  {TRI3, TRI6, TRI7},
334  {QUAD4, QUAD8, QUAD9},
335  {TET4, TET10, TET14},
336  {HEX8, HEX20, HEX27},
339 
340  const std::function<Real(int,int,int)> true_values[] =
341  {edge_integrals,
342  tri_integrals,
343  quad_integrals,
344  tet_integrals,
345  hex_integrals,
346  prism_integrals,
347  pyramid_integrals};
348 
349  for (auto i : index_range(all_types))
350  for (ElemType elem_type : all_types[i])
351  {
352  const unsigned int order = Elem::build(elem_type)->default_order();
353 
354  unsigned int exactorder = order;
355  // There are some sad exceptions to the "positive nodal
356  // quadrature can integrate polynomials up to the element
357  // order" rule.
358  //
359  // Some "quadratic" elements can only exactly integrate
360  // linears when mass lumping.
361  if (elem_type == TRI6 || elem_type == QUAD8 ||
362  elem_type == TET10 || elem_type == HEX20 ||
363  elem_type == PRISM15 || elem_type == PRISM18 ||
364  elem_type == PYRAMID13 || elem_type == PYRAMID14 ||
365  // And some partially-cubic elements can only do quadratics
366  elem_type == TET14 || elem_type == PRISM20)
367  exactorder--;
368 
369  // And one partially-cubic element can only do linears.
370  // Seriously.
371  if (elem_type == PYRAMID18)
372  exactorder=1;
373 
374  // Our PRISM20 rule was only computed in double precision.
375  if ((sizeof(Real) > sizeof(double)) && elem_type == PRISM20)
376  quadrature_tolerance = TOLERANCE;
377  else // restore the default
378  quadrature_tolerance = TOLERANCE * std::sqrt(TOLERANCE);
379 
380  testPolynomials(QNODAL, order, elem_type, true_values[i], exactorder);
381  }
382  }
383 
384 
386  {
387  LOG_UNIT_TEST;
388 
389  const std::vector<std::vector<ElemType>> all_types =
390  {{EDGE2}, {QUAD4, TRI3}, {HEX8, TET4, PRISM6, PYRAMID5}};
391  const std::vector<std::vector<std::function<Real(int,int,int)>>>
392  true_values =
393  {{edge_integrals},
394  {quad_integrals, tri_integrals},
395  {hex_integrals, tet_integrals, prism_integrals, pyramid_integrals}};
396 
397  for (auto i : index_range(all_types))
398  for (auto j : index_range(all_types[i]))
399  for (int order=0; order<17; ++order)
400  testPolynomials(QMONOMIAL, order, all_types[i][j], true_values[i][j], order);
401  }
402 
404  {
405  LOG_UNIT_TEST;
406 
407  // There are 3 different families of quadrature rules for tetrahedra
409 
410  int end_order = 7;
411 
412  for (int qt=0; qt<3; ++qt)
413  for (int order=0; order<end_order; ++order)
414  {
415  // Our higher order tet rules were only computed to double
416  // precision
417  if ((sizeof(Real) > sizeof(double)) && order > 2)
418  quadrature_tolerance = TOLERANCE;
419  testPolynomials(qtype[qt], order, TET4, tet_integrals, order);
420  }
421  }
422 
424  {
425  LOG_UNIT_TEST;
426 
428 
429  for (int qt=0; qt<4; ++qt)
430  for (int order=0; order<10; ++order)
431  testPolynomials(qtype[qt], order, TRI3, tri_integrals, order);
432  }
433 
434  void testJacobi ()
435  {
436  LOG_UNIT_TEST;
437 
438  constexpr int max_order = 43; // We seriously implemented that?!
439 
440  // LibMesh supports two different types of Jacobi quadrature
441  QuadratureType qtype[2] = {QJACOBI_1_0, QJACOBI_2_0};
442 
443  // The weights of the Jacobi quadrature rules in libmesh have been
444  // scaled based on their intended use:
445  // (alpha=1, beta=0) rule weights sum to 1/2.
446  // (alpha=2, beta=0) rule weights sum to 1/3.
447  Real initial_sum_weights[2] = {.5, 1./3.};
448 
449  // The points of the Jacobi rules in LibMesh are also defined on
450  // [0,1]... this has to be taken into account when computing the
451  // exact integral values in Maple! Also note: if you scale the
452  // points to a different interval, you need to also compute what
453  // the sum of the weights should be for that interval, it will not
454  // simply be the element length for weighted quadrature rules like
455  // Jacobi. For general alpha and beta=0, the sum of the weights
456  // on the interval [-1,1] is 2^(alpha+1) / (alpha+1).
457  std::vector<std::vector<Real>> true_integrals(2);
458 
459  // alpha=1 integral values
460  // int((1-x)*x^p, x=0..1) = 1 / (p^2 + 3p + 2)
461  true_integrals[0].resize(max_order);
462  for (std::size_t p=0; p<true_integrals[0].size(); ++p)
463  true_integrals[0][p] = 1. / (p*p + 3.*p + 2.);
464 
465  // alpha=2 integral values
466  // int((1-x)^2*x^p, x=0..1) = 2 / (p^3 + 6*p^2 + 11*p + 6)
467  true_integrals[1].resize(max_order);
468  for (std::size_t p=0; p<true_integrals[1].size(); ++p)
469  true_integrals[1][p] = 2. / (p*p*p + 6.*p*p + 11.*p + 6.);
470 
471  // Test both types of Jacobi quadrature rules
472  for (int qt=0; qt<2; ++qt)
473  {
474  for (int order=0; order<max_order; ++order)
475  {
476  std::unique_ptr<QBase> qrule = QBase::build(qtype[qt],
477  /*dim=*/1,
478  static_cast<Order>(order));
479 
480  // Initialize on a 1D element, EDGE2/3/4 should not matter...
481  qrule->init (EDGE2, 0, true);
482 
483  // Test the sum of the weights for this order
484  Real sumw = 0.;
485  for (unsigned int qp=0; qp<qrule->n_points(); qp++)
486  sumw += qrule->w(qp);
487 
488  // Make sure that the weights add up to the value we expect
489  LIBMESH_ASSERT_REALS_EQUAL(initial_sum_weights[qt], sumw, quadrature_tolerance);
490 
491  // Test integrating different polynomial powers
492  for (int testpower=0; testpower<=order; ++testpower)
493  {
494  // Note that the weighting function, (1-x)^alpha *
495  // (1+x)^beta, is built into these quadrature rules;
496  // the polynomials we actually integrate are just the
497  // usual monomials.
498  Real sumq = 0.;
499  for (unsigned int qp=0; qp<qrule->n_points(); qp++)
500  sumq += qrule->w(qp) * std::pow(qrule->qp(qp)(0), testpower);
501 
502  // Make sure that the computed integral agrees with the "true" value
503  LIBMESH_ASSERT_REALS_EQUAL(true_integrals[qt][testpower], sumq, quadrature_tolerance);
504  } // end for(testpower)
505  } // end for(order)
506  } // end for(qt)
507  } // testJacobi
508 
509 
510 
511  template <QuadratureType qtype, Order order>
512  void testBuild ()
513  {
514  LOG_UNIT_TEST;
515 
516  std::unique_ptr<QBase> qrule1D = QBase::build (qtype, 1, order);
517  std::unique_ptr<QBase> qrule2D = QBase::build (qtype, 2, order);
518  std::unique_ptr<QBase> qrule3D = QBase::build (qtype, 3, order);
519 
520  CPPUNIT_ASSERT_EQUAL ( qtype, qrule1D->type() );
521  CPPUNIT_ASSERT_EQUAL ( qtype, qrule2D->type() );
522  CPPUNIT_ASSERT_EQUAL ( qtype, qrule3D->type() );
523 
524  // Simpson's Rule is inherently third-order
525  if (qtype != QSIMPSON)
526  {
527  CPPUNIT_ASSERT_EQUAL ( order, qrule1D->get_base_order() );
528  CPPUNIT_ASSERT_EQUAL ( order, qrule2D->get_base_order() );
529  CPPUNIT_ASSERT_EQUAL ( order, qrule3D->get_base_order() );
530  }
531 
532  CPPUNIT_ASSERT_EQUAL ( static_cast<unsigned int>(1) , qrule1D->get_dim() );
533  CPPUNIT_ASSERT_EQUAL ( static_cast<unsigned int>(2) , qrule2D->get_dim() );
534  CPPUNIT_ASSERT_EQUAL ( static_cast<unsigned int>(3) , qrule3D->get_dim() );
535 
536  // Test the enum-to-string conversion for this qtype is
537  // implemented, but not what the actual value is.
539  }
540 
541 
542 
543  //-------------------------------------------------------
544  // 1D Quadrature Rule Test
545  template <QuadratureType qtype, Order order, unsigned int exactorder>
547  {
548  LOG_UNIT_TEST;
549 
550  testPolynomials(qtype, order, EDGE3, edge_integrals, exactorder);
551  }
552 
553 
554 
555  //-------------------------------------------------------
556  // 2D Quadrature Rule Test
557  template <QuadratureType qtype, Order order, unsigned int exactorder>
559  {
560  LOG_UNIT_TEST;
561 
562  testPolynomials(qtype, order, QUAD8, quad_integrals, exactorder);
563 
564  // We may eventually support Gauss-Lobatto type quadrature on triangles...
565  if (qtype == QGAUSS_LOBATTO)
566  return;
567 
568  // QGrid needs to be changed to use symmetric offsets on triangles
569  // so it can at *least* get linears right...
570  if (qtype == QGRID)
571  testPolynomials(qtype, order, TRI6, tri_integrals, 0);
572  // QSimpson doesn't even get all quadratics on a triangle
573  else if (qtype == QSIMPSON)
574  testPolynomials(qtype, order, TRI6, tri_integrals, std::min(1u,exactorder));
575  else
576  {
577  // Our 18th order and higher triangle rules were only computed
578  // in double precision, so we use a lower tolerance here.
579  if ((sizeof(Real) > sizeof(double)) && (order > 17))
580  quadrature_tolerance = TOLERANCE;
581 
582  testPolynomials(qtype, order, TRI6, tri_integrals, exactorder);
583  }
584  }
585 
586 
587 
588  //-------------------------------------------------------
589  // 3D Gauss Rule Test
590  template <QuadratureType qtype, Order order, unsigned int exactorder>
592  {
593  LOG_UNIT_TEST;
594 
595  testPolynomials(qtype, order, HEX20, hex_integrals, exactorder);
596 
597  // We may eventually support Gauss-Lobatto type quadrature on tets and prisms...
598  if (qtype == QGAUSS_LOBATTO)
599  return;
600 
601  // QGrid needs to be changed to use symmetric offsets on
602  // non-tensor product elements so it can at *least* get linears
603  // right...
604  if (qtype == QGRID)
605  {
606  testPolynomials(qtype, order, TET10, tet_integrals, 0);
607  testPolynomials(qtype, order, PYRAMID14, pyramid_integrals, 0);
608  }
609  // QSimpson doesn't get all quadratics on a simplex or its extrusion
610  else if (qtype == QSIMPSON)
611  {
612  testPolynomials(qtype, order, TET10, tet_integrals, std::min(1u,exactorder));
613  testPolynomials(qtype, order, PRISM15, prism_integrals, std::min(1u,exactorder));
614 
615  // And on pyramids we gave up and redid QTrap
616  testPolynomials(qtype, order, PYRAMID14, pyramid_integrals, std::min(1u,exactorder));
617  }
618  else
619  {
620  testPolynomials(qtype, order, TET10, tet_integrals, exactorder);
621  testPolynomials(qtype, order, PYRAMID14, pyramid_integrals, exactorder);
622 
623  // Our 18th order and higher triangle rules (used to construct
624  // prism rules) were only computed in double precision, so we
625  // use a lower tolerance here.
626  if ((sizeof(Real) > sizeof(double)) && (order > 17))
627  quadrature_tolerance = TOLERANCE;
628 
629  testPolynomials(qtype, order, PRISM15, prism_integrals, exactorder);
630  }
631  }
632 };
633 
void testPolynomial(QBase &qrule, int xp, int yp, int zp, Real true_value)
ElemType
Defines an enum for geometric element types.
void testTetQuadrature()
void testNodalQuadrature()
Point qp(const unsigned int i) const
Definition: quadrature.h:179
static constexpr Real TOLERANCE
unsigned int dim
QuadratureType
Defines an enum for currently available quadrature rules.
The libMesh namespace provides an interface to certain functionality in the library.
T pow(const T &x)
Definition: utility.h:296
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:442
void testMonomialQuadrature()
unsigned int get_dim() const
Definition: quadrature.h:150
unsigned int n_points() const
Definition: quadrature.h:131
std::string enum_to_string(const T e)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
CPPUNIT_TEST_SUITE_REGISTRATION(QuadratureTest)
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 testPolynomials(QuadratureType qtype, int order, ElemType elem_type, const std::function< Real(int, int, int)> &true_value, int exactorder)
void testTriQuadrature()
Real w(const unsigned int i) const
Definition: quadrature.h:188
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
The QBase class provides the basic functionality from which various quadrature rules can be derived...
Definition: quadrature.h:61
void ErrorVector unsigned int
Definition: adjoints_ex3.C:360
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
T binomial(T n, T k)
Definition: utility.h:322