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.
void iota(ForwardIter first, ForwardIter last, T value)
Utility::iota was created back when std::iota was just an SGI STL extension.
Definition: utility.h:229
T pow(const T &x)
Definition: utility.h:328
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:444
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:117
T binomial(T n, T k)
Definition: utility.h:354