MFEM  v4.6.0
Finite element discretization library
intrules.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 // Implementation of IntegrationRule(s) classes
13 
14 // Acknowledgment: Some of the high-precision triangular and tetrahedral
15 // quadrature rules below were obtained from the Encyclopaedia of Cubature
16 // Formulas at http://nines.cs.kuleuven.be/research/ecf/ecf.html
17 
18 #include "fem.hpp"
19 #include "../mesh/nurbs.hpp"
20 #include <cmath>
21 
22 #ifdef MFEM_USE_MPFR
23 #include <mpfr.h>
24 #endif
25 
26 using namespace std;
27 
28 namespace mfem
29 {
30 
31 IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry)
32 {
33  int i, j, nx, ny;
34 
35  nx = irx.GetNPoints();
36  ny = iry.GetNPoints();
37  SetSize(nx * ny);
38  SetPointIndices();
39  Order = std::min(irx.GetOrder(), iry.GetOrder());
40 
41  for (j = 0; j < ny; j++)
42  {
43  IntegrationPoint &ipy = iry.IntPoint(j);
44  for (i = 0; i < nx; i++)
45  {
46  IntegrationPoint &ipx = irx.IntPoint(i);
47  IntegrationPoint &ip = IntPoint(j*nx+i);
48 
49  ip.x = ipx.x;
50  ip.y = ipy.x;
51  ip.weight = ipx.weight * ipy.weight;
52  }
53  }
54 }
55 
56 IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry,
57  IntegrationRule &irz)
58 {
59  const int nx = irx.GetNPoints();
60  const int ny = iry.GetNPoints();
61  const int nz = irz.GetNPoints();
62  SetSize(nx*ny*nz);
63  SetPointIndices();
64  Order = std::min({irx.GetOrder(), iry.GetOrder(), irz.GetOrder()});
65 
66  for (int iz = 0; iz < nz; ++iz)
67  {
68  IntegrationPoint &ipz = irz.IntPoint(iz);
69  for (int iy = 0; iy < ny; ++iy)
70  {
71  IntegrationPoint &ipy = iry.IntPoint(iy);
72  for (int ix = 0; ix < nx; ++ix)
73  {
74  IntegrationPoint &ipx = irx.IntPoint(ix);
75  IntegrationPoint &ip = IntPoint(iz*nx*ny + iy*nx + ix);
76 
77  ip.x = ipx.x;
78  ip.y = ipy.x;
79  ip.z = ipz.x;
80  ip.weight = ipx.weight*ipy.weight*ipz.weight;
81  }
82  }
83  }
84 }
85 
86 const Array<double> &IntegrationRule::GetWeights() const
87 {
88  if (weights.Size() != GetNPoints())
89  {
90  weights.SetSize(GetNPoints());
91  for (int i = 0; i < GetNPoints(); i++)
92  {
93  weights[i] = IntPoint(i).weight;
94  }
95  }
96  return weights;
97 }
98 
99 void IntegrationRule::SetPointIndices()
100 {
101  for (int i = 0; i < Size(); i++)
102  {
103  IntPoint(i).index = i;
104  }
105 }
106 
107 void IntegrationRule::GrundmannMollerSimplexRule(int s, int n)
108 {
109  // for pow on older compilers
110  using std::pow;
111  const int d = 2*s + 1;
112  Vector fact(d + n + 1);
113  Array<int> beta(n), sums(n);
114 
115  fact(0) = 1.;
116  for (int i = 1; i < fact.Size(); i++)
117  {
118  fact(i) = fact(i - 1)*i;
119  }
120 
121  // number of points is \binom{n + s + 1}{n + 1}
122  int np = 1, f = 1;
123  for (int i = 0; i <= n; i++)
124  {
125  np *= (s + i + 1), f *= (i + 1);
126  }
127  np /= f;
128  SetSize(np);
129  SetPointIndices();
130  Order = 2*s + 1;
131 
132  int pt = 0;
133  for (int i = 0; i <= s; i++)
134  {
135  double weight;
136 
137  weight = pow(2., -2*s)*pow(static_cast<double>(d + n - 2*i),
138  d)/fact(i)/fact(d + n - i);
139  if (i%2)
140  {
141  weight = -weight;
142  }
143 
144  // loop over all beta : beta_0 + ... + beta_{n-1} <= s - i
145  int k = s - i;
146  beta = 0;
147  sums = 0;
148  while (true)
149  {
150  IntegrationPoint &ip = IntPoint(pt++);
151  ip.weight = weight;
152  ip.x = double(2*beta[0] + 1)/(d + n - 2*i);
153  ip.y = double(2*beta[1] + 1)/(d + n - 2*i);
154  if (n == 3)
155  {
156  ip.z = double(2*beta[2] + 1)/(d + n - 2*i);
157  }
158 
159  int j = 0;
160  while (sums[j] == k)
161  {
162  beta[j++] = 0;
163  if (j == n)
164  {
165  goto done_beta;
166  }
167  }
168  beta[j]++;
169  sums[j]++;
170  for (j--; j >= 0; j--)
171  {
172  sums[j] = sums[j+1];
173  }
174  }
175  done_beta:
176  ;
177  }
178 }
179 
180 IntegrationRule*
181 IntegrationRule::ApplyToKnotIntervals(KnotVector const& kv) const
182 {
183  const int np = this->GetNPoints();
184  const int ne = kv.GetNE();
185 
186  IntegrationRule *kvir = new IntegrationRule(ne * np);
187  kvir->SetOrder(GetOrder());
188 
189  double x0 = kv[0];
190  double x1 = x0;
191 
192  int id = 0;
193  for (int e=0; e<ne; ++e)
194  {
195  x0 = x1;
196 
197  if (e == ne-1)
198  {
199  x1 = kv[kv.Size() - 1];
200  }
201  else
202  {
203  // Find the next unique knot
204  while (id < kv.Size() - 1)
205  {
206  id++;
207  if (kv[id] != x0)
208  {
209  x1 = kv[id];
210  break;
211  }
212  }
213  }
214 
215  const double s = x1 - x0;
216 
217  for (int j=0; j<this->GetNPoints(); ++j)
218  {
219  const double x = x0 + (s * (*this)[j].x);
220  (*kvir)[(e * np) + j].Set1w(x, (*this)[j].weight);
221  }
222  }
223 
224  return kvir;
225 }
226 
227 #ifdef MFEM_USE_MPFR
228 
229 // Class for computing hi-precision (HP) quadrature in 1D
230 class HP_Quadrature1D
231 {
232 protected:
233  mpfr_t pi, z, pp, p1, p2, p3, dz, w, rtol;
234 
235 public:
236  static const mpfr_rnd_t rnd = GMP_RNDN;
237  static const int default_prec = 128;
238 
239  // prec = MPFR precision in bits
240  HP_Quadrature1D(const int prec = default_prec)
241  {
242  mpfr_inits2(prec, pi, z, pp, p1, p2, p3, dz, w, rtol, (mpfr_ptr) 0);
243  mpfr_const_pi(pi, rnd);
244  mpfr_set_si_2exp(rtol, 1, -32, rnd); // 2^(-32) < 2.33e-10
245  }
246 
247  // set rtol = 2^exponent
248  // this is a tolerance for the last correction of x_i in Newton's algorithm;
249  // this gives roughly rtol^2 accuracy for the final x_i.
250  void SetRelTol(const int exponent = -32)
251  {
252  mpfr_set_si_2exp(rtol, 1, exponent, rnd);
253  }
254 
255  // n - number of quadrature points
256  // k - index of the point to compute, 0 <= k < n
257  // see also: QuadratureFunctions1D::GaussLegendre
258  void ComputeGaussLegendrePoint(const int n, const int k)
259  {
260  MFEM_ASSERT(n > 0 && 0 <= k && k < n, "invalid n = " << n
261  << " and/or k = " << k);
262 
263  int i = (k < (n+1)/2) ? k+1 : n-k;
264 
265  // Initial guess for the x-coordinate:
266  // set z = cos(pi * (i - 0.25) / (n + 0.5)) =
267  // = sin(pi * ((n+1-2*i) / (2*n+1)))
268  mpfr_set_si(z, n+1-2*i, rnd);
269  mpfr_div_si(z, z, 2*n+1, rnd);
270  mpfr_mul(z, z, pi, rnd);
271  mpfr_sin(z, z, rnd);
272 
273  bool done = false;
274  while (1)
275  {
276  mpfr_set_si(p2, 1, rnd);
277  mpfr_set(p1, z, rnd);
278  for (int j = 2; j <= n; j++)
279  {
280  mpfr_set(p3, p2, rnd);
281  mpfr_set(p2, p1, rnd);
282  // p1 = ((2 * j - 1) * z * p2 - (j - 1) * p3) / j;
283  mpfr_mul_si(p1, z, 2*j-1, rnd);
284  mpfr_mul_si(p3, p3, j-1, rnd);
285  mpfr_fms(p1, p1, p2, p3, rnd);
286  mpfr_div_si(p1, p1, j, rnd);
287  }
288  // p1 is Legendre polynomial
289 
290  // derivative of the Legendre polynomial:
291  // pp = n * (z*p1-p2) / (z*z - 1);
292  mpfr_fms(pp, z, p1, p2, rnd);
293  mpfr_mul_si(pp, pp, n, rnd);
294  mpfr_sqr(p2, z, rnd);
295  mpfr_sub_si(p2, p2, 1, rnd);
296  mpfr_div(pp, pp, p2, rnd);
297 
298  if (done) { break; }
299 
300  // set delta_z: dz = p1/pp;
301  mpfr_div(dz, p1, pp, rnd);
302  // compute absolute tolerance: atol = rtol*(1-z)
303  mpfr_t &atol = w;
304  mpfr_si_sub(atol, 1, z, rnd);
305  mpfr_mul(atol, atol, rtol, rnd);
306  if (mpfr_cmpabs(dz, atol) <= 0)
307  {
308  done = true;
309  // continue the computation: get pp at the new point, then exit
310  }
311  // update z = z - dz
312  mpfr_sub(z, z, dz, rnd);
313  }
314 
315  // map z to (0,1): z = (1 - z)/2
316  mpfr_si_sub(z, 1, z, rnd);
317  mpfr_div_2si(z, z, 1, rnd);
318 
319  // weight: w = 1/(4*z*(1 - z)*pp*pp)
320  mpfr_sqr(w, pp, rnd);
321  mpfr_mul_2si(w, w, 2, rnd);
322  mpfr_mul(w, w, z, rnd);
323  mpfr_si_sub(p1, 1, z, rnd); // p1 = 1-z
324  mpfr_mul(w, w, p1, rnd);
325  mpfr_si_div(w, 1, w, rnd);
326 
327  if (k >= (n+1)/2) { mpfr_swap(z, p1); }
328  }
329 
330  // n - number of quadrature points
331  // k - index of the point to compute, 0 <= k < n
332  // see also: QuadratureFunctions1D::GaussLobatto
333  void ComputeGaussLobattoPoint(const int n, const int k)
334  {
335  MFEM_ASSERT(n > 1 && 0 <= k && k < n, "invalid n = " << n
336  << " and/or k = " << k);
337 
338  int i = (k < (n+1)/2) ? k : n-1-k;
339 
340  if (i == 0)
341  {
342  mpfr_set_si(z, 0, rnd);
343  mpfr_set_si(p1, 1, rnd);
344  mpfr_set_si(w, n*(n-1), rnd);
345  mpfr_si_div(w, 1, w, rnd); // weight = 1/(n*(n-1))
346  return;
347  }
348  // initial guess is the corresponding Chebyshev point, z:
349  // z = -cos(pi * i/(n-1)) = sin(pi * (2*i-n+1)/(2*n-2))
350  mpfr_set_si(z, 2*i-n+1, rnd);
351  mpfr_div_si(z, z, 2*(n-1), rnd);
352  mpfr_mul(z, pi, z, rnd);
353  mpfr_sin(z, z, rnd);
354  bool done = false;
355  for (int iter = 0 ; true ; ++iter)
356  {
357  // build Legendre polynomials, up to P_{n}(z)
358  mpfr_set_si(p1, 1, rnd);
359  mpfr_set(p2, z, rnd);
360 
361  for (int l = 1 ; l < (n-1) ; ++l)
362  {
363  // P_{l+1}(x) = [ (2*l+1)*x*P_l(x) - l*P_{l-1}(x) ]/(l+1)
364  mpfr_mul_si(p1, p1, l, rnd);
365  mpfr_mul_si(p3, z, 2*l+1, rnd);
366  mpfr_fms(p3, p3, p2, p1, rnd);
367  mpfr_div_si(p3, p3, l+1, rnd);
368 
369  mpfr_set(p1, p2, rnd);
370  mpfr_set(p2, p3, rnd);
371  }
372  if (done) { break; }
373  // compute dz = resid/deriv = (z*p2 - p1) / (n*p2);
374  mpfr_fms(dz, z, p2, p1, rnd);
375  mpfr_mul_si(p3, p2, n, rnd);
376  mpfr_div(dz, dz, p3, rnd);
377  // update: z = z - dz
378  mpfr_sub(z, z, dz, rnd);
379  // compute absolute tolerance: atol = rtol*(1 + z)
380  mpfr_t &atol = w;
381  mpfr_add_si(atol, z, 1, rnd);
382  mpfr_mul(atol, atol, rtol, rnd);
383  // check for convergence
384  if (mpfr_cmpabs(dz, atol) <= 0)
385  {
386  done = true;
387  // continue the computation: get p2 at the new point, then exit
388  }
389  // If the iteration does not converge fast, something is wrong.
390  MFEM_VERIFY(iter < 8, "n = " << n << ", i = " << i
391  << ", dz = " << mpfr_get_d(dz, rnd));
392  }
393  // Map to the interval [0,1] and scale the weights
394  mpfr_add_si(z, z, 1, rnd);
395  mpfr_div_2si(z, z, 1, rnd);
396  // set the symmetric point
397  mpfr_si_sub(p1, 1, z, rnd);
398  // w = 1/[ n*(n-1)*[P_{n-1}(z)]^2 ]
399  mpfr_sqr(w, p2, rnd);
400  mpfr_mul_si(w, w, n*(n-1), rnd);
401  mpfr_si_div(w, 1, w, rnd);
402 
403  if (k >= (n+1)/2) { mpfr_swap(z, p1); }
404  }
405 
406  double GetPoint() const { return mpfr_get_d(z, rnd); }
407  double GetSymmPoint() const { return mpfr_get_d(p1, rnd); }
408  double GetWeight() const { return mpfr_get_d(w, rnd); }
409 
410  const mpfr_t &GetHPPoint() const { return z; }
411  const mpfr_t &GetHPSymmPoint() const { return p1; }
412  const mpfr_t &GetHPWeight() const { return w; }
413 
414  ~HP_Quadrature1D()
415  {
416  mpfr_clears(pi, z, pp, p1, p2, p3, dz, w, rtol, (mpfr_ptr) 0);
417  mpfr_free_cache();
418  }
419 };
420 
421 #endif // MFEM_USE_MPFR
422 
423 
424 void QuadratureFunctions1D::GaussLegendre(const int np, IntegrationRule* ir)
425 {
426  ir->SetSize(np);
427  ir->SetPointIndices();
428  ir->SetOrder(2*np - 1);
429 
430  switch (np)
431  {
432  case 1:
433  ir->IntPoint(0).Set1w(0.5, 1.0);
434  return;
435  case 2:
436  ir->IntPoint(0).Set1w(0.21132486540518711775, 0.5);
437  ir->IntPoint(1).Set1w(0.78867513459481288225, 0.5);
438  return;
439  case 3:
440  ir->IntPoint(0).Set1w(0.11270166537925831148, 5./18.);
441  ir->IntPoint(1).Set1w(0.5, 4./9.);
442  ir->IntPoint(2).Set1w(0.88729833462074168852, 5./18.);
443  return;
444  }
445 
446  const int n = np;
447  const int m = (n+1)/2;
448 
449 #ifndef MFEM_USE_MPFR
450 
451  for (int i = 1; i <= m; i++)
452  {
453  double z = cos(M_PI * (i - 0.25) / (n + 0.5));
454  double pp, p1, dz, xi = 0.;
455  bool done = false;
456  while (1)
457  {
458  double p2 = 1;
459  p1 = z;
460  for (int j = 2; j <= n; j++)
461  {
462  double p3 = p2;
463  p2 = p1;
464  p1 = ((2 * j - 1) * z * p2 - (j - 1) * p3) / j;
465  }
466  // p1 is Legendre polynomial
467 
468  pp = n * (z*p1-p2) / (z*z - 1);
469  if (done) { break; }
470 
471  dz = p1/pp;
472  if (fabs(dz) < 1e-16)
473  {
474  done = true;
475  // map the new point (z-dz) to (0,1):
476  xi = ((1 - z) + dz)/2; // (1 - (z - dz))/2 has bad round-off
477  // continue the computation: get pp at the new point, then exit
478  }
479  // update: z = z - dz
480  z -= dz;
481  }
482 
483  ir->IntPoint(i-1).x = xi;
484  ir->IntPoint(n-i).x = 1 - xi;
485  ir->IntPoint(i-1).weight =
486  ir->IntPoint(n-i).weight = 1./(4*xi*(1 - xi)*pp*pp);
487  }
488 
489 #else // MFEM_USE_MPFR is defined
490 
491  HP_Quadrature1D hp_quad;
492  for (int i = 1; i <= m; i++)
493  {
494  hp_quad.ComputeGaussLegendrePoint(n, i-1);
495 
496  ir->IntPoint(i-1).x = hp_quad.GetPoint();
497  ir->IntPoint(n-i).x = hp_quad.GetSymmPoint();
498  ir->IntPoint(i-1).weight = ir->IntPoint(n-i).weight = hp_quad.GetWeight();
499  }
500 
501 #endif // MFEM_USE_MPFR
502 
503 }
504 
505 void QuadratureFunctions1D::GaussLobatto(const int np, IntegrationRule* ir)
506 {
507  /* An np point Gauss-Lobatto quadrature has (np - 2) free abscissa the other
508  (2) abscissa are the interval endpoints.
509 
510  The interior x_i are the zeros of P'_{np-1}(x). The weights of the
511  interior points on the interval [-1,1] are:
512 
513  w_i = 2/(np*(np-1)*[P_{np-1}(x_i)]^2)
514 
515  The end point weights (on [-1,1]) are: w_{end} = 2/(np*(np-1)).
516 
517  The interior abscissa are found via a nonlinear solve, the initial guess
518  for each point is the corresponding Chebyshev point.
519 
520  After we find all points on the interval [-1,1], we will map and scale the
521  points and weights to the MFEM natural interval [0,1].
522 
523  References:
524  [1] E. E. Lewis and W. F. Millier, "Computational Methods of Neutron
525  Transport", Appendix A
526  [2] the QUADRULE software by John Burkardt,
527  https://people.sc.fsu.edu/~jburkardt/cpp_src/quadrule/quadrule.cpp
528  */
529 
530  ir->SetSize(np);
531  ir->SetPointIndices();
532  if ( np == 1 )
533  {
534  ir->IntPoint(0).Set1w(0.5, 1.0);
535  ir->SetOrder(1);
536  }
537  else
538  {
539  ir->SetOrder(2*np - 3);
540 
541 #ifndef MFEM_USE_MPFR
542 
543  // endpoints and respective weights
544  ir->IntPoint(0).x = 0.0;
545  ir->IntPoint(np-1).x = 1.0;
546  ir->IntPoint(0).weight = ir->IntPoint(np-1).weight = 1.0/(np*(np-1));
547 
548  // interior points and weights
549  // use symmetry and compute just half of the points
550  for (int i = 1 ; i <= (np-1)/2 ; ++i)
551  {
552  // initial guess is the corresponding Chebyshev point, x_i:
553  // x_i = -cos(\pi * (i / (np-1)))
554  double x_i = std::sin(M_PI * ((double)(i)/(np-1) - 0.5));
555  double z_i = 0., p_l;
556  bool done = false;
557  for (int iter = 0 ; true ; ++iter)
558  {
559  // build Legendre polynomials, up to P_{np}(x_i)
560  double p_lm1 = 1.0;
561  p_l = x_i;
562 
563  for (int l = 1 ; l < (np-1) ; ++l)
564  {
565  // The Legendre polynomials can be built by recursion:
566  // x * P_l(x) = 1/(2*l+1)*[ (l+1)*P_{l+1}(x) + l*P_{l-1} ], i.e.
567  // P_{l+1}(x) = [ (2*l+1)*x*P_l(x) - l*P_{l-1} ]/(l+1)
568  double p_lp1 = ( (2*l + 1)*x_i*p_l - l*p_lm1)/(l + 1);
569 
570  p_lm1 = p_l;
571  p_l = p_lp1;
572  }
573  if (done) { break; }
574  // after this loop, p_l holds P_{np-1}(x_i)
575  // resid = (x^2-1)*P'_{np-1}(x_i)
576  // but use the recurrence relationship
577  // (x^2 -1)P'_l(x) = l*[ x*P_l(x) - P_{l-1}(x) ]
578  // thus, resid = (np-1) * (x_i*p_l - p_lm1)
579 
580  // The derivative of the residual is:
581  // \frac{d}{d x} \left[ (x^2 -1)P'_l(x) ] \right] =
582  // l * (l+1) * P_l(x), with l = np-1,
583  // therefore, deriv = np * (np-1) * p_l;
584 
585  // compute dx = resid/deriv
586  double dx = (x_i*p_l - p_lm1) / (np*p_l);
587  if (std::abs(dx) < 1e-16)
588  {
589  done = true;
590  // Map the point to the interval [0,1]
591  z_i = ((1.0 + x_i) - dx)/2;
592  // continue the computation: get p_l at the new point, then exit
593  }
594  // If the iteration does not converge fast, something is wrong.
595  MFEM_VERIFY(iter < 8, "np = " << np << ", i = " << i
596  << ", dx = " << dx);
597  // update x_i:
598  x_i -= dx;
599  }
600  // Map to the interval [0,1] and scale the weights
601  IntegrationPoint &ip = ir->IntPoint(i);
602  ip.x = z_i;
603  // w_i = (2/[ n*(n-1)*[P_{n-1}(x_i)]^2 ]) / 2
604  ip.weight = (double)(1.0 / (np*(np-1)*p_l*p_l));
605 
606  // set the symmetric point
607  IntegrationPoint &symm_ip = ir->IntPoint(np-1-i);
608  symm_ip.x = 1.0 - z_i;
609  symm_ip.weight = ip.weight;
610  }
611 
612 #else // MFEM_USE_MPFR is defined
613 
614  HP_Quadrature1D hp_quad;
615  // use symmetry and compute just half of the points
616  for (int i = 0 ; i <= (np-1)/2 ; ++i)
617  {
618  hp_quad.ComputeGaussLobattoPoint(np, i);
619  ir->IntPoint(i).x = hp_quad.GetPoint();
620  ir->IntPoint(np-1-i).x = hp_quad.GetSymmPoint();
621  ir->IntPoint(i).weight =
622  ir->IntPoint(np-1-i).weight = hp_quad.GetWeight();
623  }
624 
625 #endif // MFEM_USE_MPFR
626 
627  }
628 }
629 
630 void QuadratureFunctions1D::OpenUniform(const int np, IntegrationRule* ir)
631 {
632  ir->SetSize(np);
633  ir->SetPointIndices();
634  ir->SetOrder(np - 1 + np%2);
635 
636  // The Newton-Cotes quadrature is based on weights that integrate exactly the
637  // interpolatory polynomial through the equally spaced quadrature points.
638  for (int i = 0; i < np ; ++i)
639  {
640  ir->IntPoint(i).x = double(i+1) / double(np + 1);
641  }
642 
643  CalculateUniformWeights(ir, Quadrature1D::OpenUniform);
644 }
645 
646 void QuadratureFunctions1D::ClosedUniform(const int np,
647  IntegrationRule* ir)
648 {
649  ir->SetSize(np);
650  ir->SetPointIndices();
651  ir->SetOrder(np - 1 + np%2);
652  if ( np == 1 ) // allow this case as "closed"
653  {
654  ir->IntPoint(0).Set1w(0.5, 1.0);
655  return;
656  }
657 
658  for (int i = 0; i < np ; ++i)
659  {
660  ir->IntPoint(i).x = double(i) / (np-1);
661  }
662 
663  CalculateUniformWeights(ir, Quadrature1D::ClosedUniform);
664 }
665 
666 void QuadratureFunctions1D::OpenHalfUniform(const int np, IntegrationRule* ir)
667 {
668  ir->SetSize(np);
669  ir->SetPointIndices();
670  ir->SetOrder(np - 1 + np%2);
671 
672  // Open half points: the centers of np uniform intervals
673  for (int i = 0; i < np ; ++i)
674  {
675  ir->IntPoint(i).x = double(2*i+1) / (2*np);
676  }
677 
678  CalculateUniformWeights(ir, Quadrature1D::OpenHalfUniform);
679 }
680 
681 void QuadratureFunctions1D::ClosedGL(const int np, IntegrationRule* ir)
682 {
683  ir->SetSize(np);
684  ir->SetPointIndices();
685  ir->IntPoint(0).x = 0.0;
686  ir->IntPoint(np-1).x = 1.0;
687  ir->SetOrder(np - 1 + np%2); // Is this the correct order?
688 
689  if ( np > 2 )
690  {
691  IntegrationRule gl_ir;
692  GaussLegendre(np-1, &gl_ir);
693 
694  for (int i = 1; i < np-1; ++i)
695  {
696  ir->IntPoint(i).x = (gl_ir.IntPoint(i-1).x + gl_ir.IntPoint(i).x)/2;
697  }
698  }
699 
700  CalculateUniformWeights(ir, Quadrature1D::ClosedGL);
701 }
702 
703 void QuadratureFunctions1D::GivePolyPoints(const int np, double *pts,
704  const int type)
705 {
706  IntegrationRule ir(np);
707 
708  switch (type)
709  {
710  case Quadrature1D::GaussLegendre:
711  {
712  GaussLegendre(np,&ir);
713  break;
714  }
715  case Quadrature1D::GaussLobatto:
716  {
717  GaussLobatto(np, &ir);
718  break;
719  }
720  case Quadrature1D::OpenUniform:
721  {
722  OpenUniform(np,&ir);
723  break;
724  }
725  case Quadrature1D::ClosedUniform:
726  {
727  ClosedUniform(np,&ir);
728  break;
729  }
730  case Quadrature1D::OpenHalfUniform:
731  {
732  OpenHalfUniform(np, &ir);
733  break;
734  }
735  case Quadrature1D::ClosedGL:
736  {
737  ClosedGL(np, &ir);
738  break;
739  }
740  default:
741  {
742  MFEM_ABORT("Asking for an unknown type of 1D Quadrature points, "
743  "type = " << type);
744  }
745  }
746 
747  for (int i = 0 ; i < np ; ++i)
748  {
749  pts[i] = ir.IntPoint(i).x;
750  }
751 }
752 
753 void QuadratureFunctions1D::CalculateUniformWeights(IntegrationRule *ir,
754  const int type)
755 {
756  /* The Lagrange polynomials are:
757  p_i = \prod_{j \neq i} {\frac{x - x_j }{x_i - x_j}}
758 
759  The weight associated with each abscissa is the integral of p_i over
760  [0,1]. To calculate the integral of p_i, we use a Gauss-Legendre
761  quadrature rule. This approach does not suffer from bad round-off/
762  cancellation errors for large number of points.
763  */
764  const int n = ir->Size();
765  switch (n)
766  {
767  case 1:
768  ir->IntPoint(0).weight = 1.;
769  return;
770  case 2:
771  ir->IntPoint(0).weight = .5;
772  ir->IntPoint(1).weight = .5;
773  return;
774  }
775 
776 #ifndef MFEM_USE_MPFR
777 
778  // This algorithm should work for any set of points, not just uniform
779  const IntegrationRule &glob_ir = IntRules.Get(Geometry::SEGMENT, n-1);
780  const int m = glob_ir.GetNPoints();
781  Vector xv(n);
782  for (int j = 0; j < n; j++)
783  {
784  xv(j) = ir->IntPoint(j).x;
785  }
786  Poly_1D::Basis basis(n-1, xv.GetData()); // nodal basis, with nodes at 'xv'
787  Vector w(n);
788  // Integrate all nodal basis functions using 'glob_ir':
789  w = 0.0;
790  for (int i = 0; i < m; i++)
791  {
792  const IntegrationPoint &ip = glob_ir.IntPoint(i);
793  basis.Eval(ip.x, xv);
794  w.Add(ip.weight, xv); // w += ip.weight * xv
795  }
796  for (int j = 0; j < n; j++)
797  {
798  ir->IntPoint(j).weight = w(j);
799  }
800 
801 #else // MFEM_USE_MPFR is defined
802 
803  static const mpfr_rnd_t rnd = HP_Quadrature1D::rnd;
804  HP_Quadrature1D hp_quad;
805  mpfr_t l, lk, w0, wi, tmp, *weights;
806  mpfr_inits2(hp_quad.default_prec, l, lk, w0, wi, tmp, (mpfr_ptr) 0);
807  weights = new mpfr_t[n];
808  for (int i = 0; i < n; i++)
809  {
810  mpfr_init2(weights[i], hp_quad.default_prec);
811  mpfr_set_si(weights[i], 0, rnd);
812  }
813  hp_quad.SetRelTol(-48); // rtol = 2^(-48) ~ 3.5e-15
814  const int p = n-1;
815  const int m = p/2+1; // number of points for Gauss-Legendre quadrature
816  int hinv = 0, ihoffset = 0; // x_i = (i+ihoffset/2)/hinv
817  switch (type)
818  {
819  case Quadrature1D::ClosedUniform:
820  // x_i = i/p, i=0,...,p
821  hinv = p;
822  ihoffset = 0;
823  break;
824  case Quadrature1D::OpenUniform:
825  // x_i = (i+1)/(p+2), i=0,...,p
826  hinv = p+2;
827  ihoffset = 2;
828  break;
829  case Quadrature1D::OpenHalfUniform:
830  // x_i = (i+1/2)/(p+1), i=0,...,p
831  hinv = p+1;
832  ihoffset = 1;
833  break;
834  default:
835  MFEM_ABORT("invalid Quadrature1D type: " << type);
836  }
837  // set w0 = (-1)^p*(p!)/(hinv^p)
838  mpfr_fac_ui(w0, p, rnd);
839  mpfr_ui_pow_ui(tmp, hinv, p, rnd);
840  mpfr_div(w0, w0, tmp, rnd);
841  if (p%2) { mpfr_neg(w0, w0, rnd); }
842 
843  for (int j = 0; j < m; j++)
844  {
845  hp_quad.ComputeGaussLegendrePoint(m, j);
846 
847  // Compute l = \prod_{i=0}^p (x-x_i) and lk = l/(x-x_k), where
848  // x = hp_quad.GetHPPoint(), x_i = (i+ihoffset/2)/hinv, and x_k is the
849  // node closest to x, i.e. k = min(max(round(x*hinv-ihoffset/2),0),p)
850  mpfr_mul_si(tmp, hp_quad.GetHPPoint(), hinv, rnd);
851  mpfr_sub_d(tmp, tmp, 0.5*ihoffset, rnd);
852  mpfr_round(tmp, tmp);
853  int k = min(max((int)mpfr_get_si(tmp, rnd), 0), p);
854  mpfr_set_si(lk, 1, rnd);
855  for (int i = 0; i <= p; i++)
856  {
857  mpfr_set_si(tmp, 2*i+ihoffset, rnd);
858  mpfr_div_si(tmp, tmp, 2*hinv, rnd);
859  mpfr_sub(tmp, hp_quad.GetHPPoint(), tmp, rnd);
860  if (i != k)
861  {
862  mpfr_mul(lk, lk, tmp, rnd);
863  }
864  else
865  {
866  mpfr_set(l, tmp, rnd);
867  }
868  }
869  mpfr_mul(l, l, lk, rnd);
870  mpfr_set(wi, w0, rnd);
871  for (int i = 0; true; i++)
872  {
873  if (i != k)
874  {
875  // tmp = l/(wi*(x - x_i))
876  mpfr_set_si(tmp, 2*i+ihoffset, rnd);
877  mpfr_div_si(tmp, tmp, 2*hinv, rnd);
878  mpfr_sub(tmp, hp_quad.GetHPPoint(), tmp, rnd);
879  mpfr_mul(tmp, tmp, wi, rnd);
880  mpfr_div(tmp, l, tmp, rnd);
881  }
882  else
883  {
884  // tmp = lk/wi
885  mpfr_div(tmp, lk, wi, rnd);
886  }
887  // weights[i] += hp_quad.weight*tmp
888  mpfr_mul(tmp, tmp, hp_quad.GetHPWeight(), rnd);
889  mpfr_add(weights[i], weights[i], tmp, rnd);
890 
891  if (i == p) { break; }
892 
893  // update wi *= (i+1)/(i-p)
894  mpfr_mul_si(wi, wi, i+1, rnd);
895  mpfr_div_si(wi, wi, i-p, rnd);
896  }
897  }
898  for (int i = 0; i < n; i++)
899  {
900  ir->IntPoint(i).weight = mpfr_get_d(weights[i], rnd);
901  mpfr_clear(weights[i]);
902  }
903  delete [] weights;
904  mpfr_clears(l, lk, w0, wi, tmp, (mpfr_ptr) 0);
905 
906 #endif // MFEM_USE_MPFR
907 
908 }
909 
910 
911 int Quadrature1D::CheckClosed(int type)
912 {
913  switch (type)
914  {
915  case GaussLobatto:
916  case ClosedUniform:
917  return type;
918  default:
919  return Invalid;
920  }
921 }
922 
923 int Quadrature1D::CheckOpen(int type)
924 {
925  switch (type)
926  {
927  case GaussLegendre:
928  case GaussLobatto:
929  case OpenUniform:
930  case ClosedUniform:
931  case OpenHalfUniform:
932  return type; // all types can work as open
933  default:
934  return Invalid;
935  }
936 }
937 
938 
939 IntegrationRules IntRules(0, Quadrature1D::GaussLegendre);
940 
941 IntegrationRules RefinedIntRules(1, Quadrature1D::GaussLegendre);
942 
943 IntegrationRules::IntegrationRules(int Ref, int type_):
944  quad_type(type_)
945 {
946  refined = Ref;
947 
948  if (refined < 0) { own_rules = 0; return; }
949 
950  own_rules = 1;
951 
952  const MemoryType h_mt = MemoryType::HOST;
953  PointIntRules.SetSize(2, h_mt);
954  PointIntRules = NULL;
955 
956  SegmentIntRules.SetSize(32, h_mt);
957  SegmentIntRules = NULL;
958 
959  // TriangleIntegrationRule() assumes that this size is >= 26
960  TriangleIntRules.SetSize(32, h_mt);
961  TriangleIntRules = NULL;
962 
963  SquareIntRules.SetSize(32, h_mt);
964  SquareIntRules = NULL;
965 
966  // TetrahedronIntegrationRule() assumes that this size is >= 10
967  TetrahedronIntRules.SetSize(32, h_mt);
968  TetrahedronIntRules = NULL;
969 
970  PyramidIntRules.SetSize(32, h_mt);
971  PyramidIntRules = NULL;
972 
973  PrismIntRules.SetSize(32, h_mt);
974  PrismIntRules = NULL;
975 
976  CubeIntRules.SetSize(32, h_mt);
977  CubeIntRules = NULL;
978 }
979 
980 const IntegrationRule &IntegrationRules::Get(int GeomType, int Order)
981 {
982  Array<IntegrationRule *> *ir_array;
983 
984  switch (GeomType)
985  {
986  case Geometry::POINT: ir_array = &PointIntRules; Order = 0; break;
987  case Geometry::SEGMENT: ir_array = &SegmentIntRules; break;
988  case Geometry::TRIANGLE: ir_array = &TriangleIntRules; break;
989  case Geometry::SQUARE: ir_array = &SquareIntRules; break;
990  case Geometry::TETRAHEDRON: ir_array = &TetrahedronIntRules; break;
991  case Geometry::CUBE: ir_array = &CubeIntRules; break;
992  case Geometry::PRISM: ir_array = &PrismIntRules; break;
993  case Geometry::PYRAMID: ir_array = &PyramidIntRules; break;
994  default:
995  mfem_error("IntegrationRules::Get(...) : Unknown geometry type!");
996  ir_array = NULL;
997  }
998 
999  if (Order < 0)
1000  {
1001  Order = 0;
1002  }
1003 
1004  if (!HaveIntRule(*ir_array, Order))
1005  {
1006 #ifdef MFEM_USE_LEGACY_OPENMP
1007  #pragma omp critical
1008 #endif
1009  {
1010  if (!HaveIntRule(*ir_array, Order))
1011  {
1012  IntegrationRule *ir = GenerateIntegrationRule(GeomType, Order);
1013 #ifdef MFEM_DEBUG
1014  int RealOrder = Order;
1015  while (RealOrder+1 < ir_array->Size() &&
1016  (*ir_array)[RealOrder+1] == ir)
1017  {
1018  RealOrder++;
1019  }
1020  MFEM_VERIFY(RealOrder == ir->GetOrder(), "internal error");
1021 #else
1022  MFEM_CONTRACT_VAR(ir);
1023 #endif
1024  }
1025  }
1026  }
1027 
1028  return *(*ir_array)[Order];
1029 }
1030 
1031 void IntegrationRules::Set(int GeomType, int Order, IntegrationRule &IntRule)
1032 {
1033  Array<IntegrationRule *> *ir_array;
1034 
1035  switch (GeomType)
1036  {
1037  case Geometry::POINT: ir_array = &PointIntRules; break;
1038  case Geometry::SEGMENT: ir_array = &SegmentIntRules; break;
1039  case Geometry::TRIANGLE: ir_array = &TriangleIntRules; break;
1040  case Geometry::SQUARE: ir_array = &SquareIntRules; break;
1041  case Geometry::TETRAHEDRON: ir_array = &TetrahedronIntRules; break;
1042  case Geometry::CUBE: ir_array = &CubeIntRules; break;
1043  case Geometry::PRISM: ir_array = &PrismIntRules; break;
1044  case Geometry::PYRAMID: ir_array = &PyramidIntRules; break;
1045  default:
1046  mfem_error("IntegrationRules::Set(...) : Unknown geometry type!");
1047  ir_array = NULL;
1048  }
1049 
1050  if (HaveIntRule(*ir_array, Order))
1051  {
1052  MFEM_ABORT("Overwriting set rules is not supported!");
1053  }
1054 
1055  AllocIntRule(*ir_array, Order);
1056 
1057  (*ir_array)[Order] = &IntRule;
1058 }
1059 
1060 void IntegrationRules::DeleteIntRuleArray(Array<IntegrationRule *> &ir_array)
1061 {
1062  int i;
1063  IntegrationRule *ir = NULL;
1064 
1065  // Many of the intrules have multiple contiguous copies in the ir_array
1066  // so we have to be careful to not delete them twice.
1067  for (i = 0; i < ir_array.Size(); i++)
1068  {
1069  if (ir_array[i] != NULL && ir_array[i] != ir)
1070  {
1071  ir = ir_array[i];
1072  delete ir;
1073  }
1074  }
1075 }
1076 
1078 {
1079  if (!own_rules) { return; }
1080 
1081  DeleteIntRuleArray(PointIntRules);
1082  DeleteIntRuleArray(SegmentIntRules);
1083  DeleteIntRuleArray(TriangleIntRules);
1084  DeleteIntRuleArray(SquareIntRules);
1085  DeleteIntRuleArray(TetrahedronIntRules);
1086  DeleteIntRuleArray(CubeIntRules);
1087  DeleteIntRuleArray(PrismIntRules);
1088  DeleteIntRuleArray(PyramidIntRules);
1089 }
1090 
1091 
1092 IntegrationRule *IntegrationRules::GenerateIntegrationRule(int GeomType,
1093  int Order)
1094 {
1095  switch (GeomType)
1096  {
1097  case Geometry::POINT:
1098  return PointIntegrationRule(Order);
1099  case Geometry::SEGMENT:
1100  return SegmentIntegrationRule(Order);
1101  case Geometry::TRIANGLE:
1102  return TriangleIntegrationRule(Order);
1103  case Geometry::SQUARE:
1104  return SquareIntegrationRule(Order);
1105  case Geometry::TETRAHEDRON:
1106  return TetrahedronIntegrationRule(Order);
1107  case Geometry::CUBE:
1108  return CubeIntegrationRule(Order);
1109  case Geometry::PRISM:
1110  return PrismIntegrationRule(Order);
1111  case Geometry::PYRAMID:
1112  return PyramidIntegrationRule(Order);
1113  default:
1114  mfem_error("IntegrationRules::Set(...) : Unknown geometry type!");
1115  return NULL;
1116  }
1117 }
1118 
1119 
1120 // Integration rules for a point
1121 IntegrationRule *IntegrationRules::PointIntegrationRule(int Order)
1122 {
1123  if (Order > 1)
1124  {
1125  mfem_error("Point Integration Rule of Order > 1 not defined");
1126  return NULL;
1127  }
1128 
1129  IntegrationRule *ir = new IntegrationRule(1);
1130  ir->IntPoint(0).x = .0;
1131  ir->IntPoint(0).weight = 1.;
1132  ir->SetOrder(1);
1133 
1134  PointIntRules[1] = PointIntRules[0] = ir;
1135 
1136  return ir;
1137 }
1138 
1139 // Integration rules for line segment [0,1]
1140 IntegrationRule *IntegrationRules::SegmentIntegrationRule(int Order)
1141 {
1142  int RealOrder = GetSegmentRealOrder(Order); // RealOrder >= Order
1143  // Order is one of {RealOrder-1,RealOrder}
1144  AllocIntRule(SegmentIntRules, RealOrder);
1145 
1146  IntegrationRule *ir = new IntegrationRule;
1147 
1148  int n = 0;
1149  // n is the number of points to achieve the exact integral of a
1150  // degree Order polynomial
1151  switch (quad_type)
1152  {
1154  {
1155  // Gauss-Legendre is exact for 2*n-1
1156  n = Order/2 + 1;
1158  break;
1159  }
1161  {
1162  // Gauss-Lobatto is exact for 2*n-3
1163  n = Order/2 + 2;
1165  break;
1166  }
1168  {
1169  // Open Newton Cotes is exact for n-(n+1)%2 = n-1+n%2
1170  n = Order | 1; // n is always odd
1172  break;
1173  }
1175  {
1176  // Closed Newton Cotes is exact for n-(n+1)%2 = n-1+n%2
1177  n = Order | 1; // n is always odd
1179  break;
1180  }
1182  {
1183  // Open half Newton Cotes is exact for n-(n+1)%2 = n-1+n%2
1184  n = Order | 1; // n is always odd
1186  break;
1187  }
1188  default:
1189  {
1190  MFEM_ABORT("unknown Quadrature1D type: " << quad_type);
1191  }
1192  }
1193  if (refined)
1194  {
1195  // Effectively passing memory management to SegmentIntegrationRules
1196  IntegrationRule *refined_ir = new IntegrationRule(2*n);
1197  refined_ir->SetOrder(ir->GetOrder());
1198  for (int j = 0; j < n; j++)
1199  {
1200  refined_ir->IntPoint(j).x = ir->IntPoint(j).x/2.0;
1201  refined_ir->IntPoint(j).weight = ir->IntPoint(j).weight/2.0;
1202  refined_ir->IntPoint(j+n).x = 0.5 + ir->IntPoint(j).x/2.0;
1203  refined_ir->IntPoint(j+n).weight = ir->IntPoint(j).weight/2.0;
1204  }
1205  delete ir;
1206  ir = refined_ir;
1207  }
1208  SegmentIntRules[RealOrder-1] = SegmentIntRules[RealOrder] = ir;
1209  return ir;
1210 }
1211 
1212 // Integration rules for reference triangle {[0,0],[1,0],[0,1]}
1213 IntegrationRule *IntegrationRules::TriangleIntegrationRule(int Order)
1214 {
1215  IntegrationRule *ir = NULL;
1216  // Note: Set TriangleIntRules[*] to ir only *after* ir is fully constructed.
1217  // This is needed in multithreaded environment.
1218 
1219  // assuming that orders <= 25 are pre-allocated
1220  switch (Order)
1221  {
1222  case 0: // 1 point - degree 1
1223  case 1:
1224  ir = new IntegrationRule(1);
1225  ir->AddTriMidPoint(0, 0.5);
1226  ir->SetOrder(1);
1227  TriangleIntRules[0] = TriangleIntRules[1] = ir;
1228  return ir;
1229 
1230  case 2: // 3 point - 2 degree
1231  ir = new IntegrationRule(3);
1232  ir->AddTriPoints3(0, 1./6., 1./6.);
1233  ir->SetOrder(2);
1234  TriangleIntRules[2] = ir;
1235  // interior points
1236  return ir;
1237 
1238  case 3: // 4 point - 3 degree (has one negative weight)
1239  ir = new IntegrationRule(4);
1240  ir->AddTriMidPoint(0, -0.28125); // -9./32.
1241  ir->AddTriPoints3(1, 0.2, 25./96.);
1242  ir->SetOrder(3);
1243  TriangleIntRules[3] = ir;
1244  return ir;
1245 
1246  case 4: // 6 point - 4 degree
1247  ir = new IntegrationRule(6);
1248  ir->AddTriPoints3(0, 0.091576213509770743460, 0.054975871827660933819);
1249  ir->AddTriPoints3(3, 0.44594849091596488632, 0.11169079483900573285);
1250  ir->SetOrder(4);
1251  TriangleIntRules[4] = ir;
1252  return ir;
1253 
1254  case 5: // 7 point - 5 degree
1255  ir = new IntegrationRule(7);
1256  ir->AddTriMidPoint(0, 0.1125);
1257  ir->AddTriPoints3(1, 0.10128650732345633880, 0.062969590272413576298);
1258  ir->AddTriPoints3(4, 0.47014206410511508977, 0.066197076394253090369);
1259  ir->SetOrder(5);
1260  TriangleIntRules[5] = ir;
1261  return ir;
1262 
1263  case 6: // 12 point - 6 degree
1264  ir = new IntegrationRule(12);
1265  ir->AddTriPoints3(0, 0.063089014491502228340, 0.025422453185103408460);
1266  ir->AddTriPoints3(3, 0.24928674517091042129, 0.058393137863189683013);
1267  ir->AddTriPoints6(6, 0.053145049844816947353, 0.31035245103378440542,
1268  0.041425537809186787597);
1269  ir->SetOrder(6);
1270  TriangleIntRules[6] = ir;
1271  return ir;
1272 
1273  case 7: // 12 point - degree 7
1274  ir = new IntegrationRule(12);
1275  ir->AddTriPoints3R(0, 0.062382265094402118174, 0.067517867073916085443,
1276  0.026517028157436251429);
1277  ir->AddTriPoints3R(3, 0.055225456656926611737, 0.32150249385198182267,
1278  0.043881408714446055037);
1279  // slightly better with explicit 3rd area coordinate
1280  ir->AddTriPoints3R(6, 0.034324302945097146470, 0.66094919618673565761,
1281  0.30472650086816719592, 0.028775042784981585738);
1282  ir->AddTriPoints3R(9, 0.51584233435359177926, 0.27771616697639178257,
1283  0.20644149867001643817, 0.067493187009802774463);
1284  ir->SetOrder(7);
1285  TriangleIntRules[7] = ir;
1286  return ir;
1287 
1288  case 8: // 16 point - 8 degree
1289  ir = new IntegrationRule(16);
1290  ir->AddTriMidPoint(0, 0.0721578038388935841255455552445323);
1291  ir->AddTriPoints3(1, 0.170569307751760206622293501491464,
1292  0.0516086852673591251408957751460645);
1293  ir->AddTriPoints3(4, 0.0505472283170309754584235505965989,
1294  0.0162292488115990401554629641708902);
1295  ir->AddTriPoints3(7, 0.459292588292723156028815514494169,
1296  0.0475458171336423123969480521942921);
1297  ir->AddTriPoints6(10, 0.008394777409957605337213834539296,
1298  0.263112829634638113421785786284643,
1299  0.0136151570872174971324223450369544);
1300  ir->SetOrder(8);
1301  TriangleIntRules[8] = ir;
1302  return ir;
1303 
1304  case 9: // 19 point - 9 degree
1305  ir = new IntegrationRule(19);
1306  ir->AddTriMidPoint(0, 0.0485678981413994169096209912536443);
1307  ir->AddTriPoints3b(1, 0.020634961602524744433,
1308  0.0156673501135695352684274156436046);
1309  ir->AddTriPoints3b(4, 0.12582081701412672546,
1310  0.0389137705023871396583696781497019);
1311  ir->AddTriPoints3(7, 0.188203535619032730240961280467335,
1312  0.0398238694636051265164458871320226);
1313  ir->AddTriPoints3(10, 0.0447295133944527098651065899662763,
1314  0.0127888378293490156308393992794999);
1315  ir->AddTriPoints6(13, 0.0368384120547362836348175987833851,
1316  0.2219629891607656956751025276931919,
1317  0.0216417696886446886446886446886446);
1318  ir->SetOrder(9);
1319  TriangleIntRules[9] = ir;
1320  return ir;
1321 
1322  case 10: // 25 point - 10 degree
1323  ir = new IntegrationRule(25);
1324  ir->AddTriMidPoint(0, 0.0454089951913767900476432975500142);
1325  ir->AddTriPoints3b(1, 0.028844733232685245264984935583748,
1326  0.0183629788782333523585030359456832);
1327  ir->AddTriPoints3(4, 0.109481575485037054795458631340522,
1328  0.0226605297177639673913028223692986);
1329  ir->AddTriPoints6(7, 0.141707219414879954756683250476361,
1330  0.307939838764120950165155022930631,
1331  0.0363789584227100543021575883096803);
1332  ir->AddTriPoints6(13, 0.025003534762686386073988481007746,
1333  0.246672560639902693917276465411176,
1334  0.0141636212655287424183685307910495);
1335  ir->AddTriPoints6(19, 0.0095408154002994575801528096228873,
1336  0.0668032510122002657735402127620247,
1337  4.71083348186641172996373548344341E-03);
1338  ir->SetOrder(10);
1339  TriangleIntRules[10] = ir;
1340  return ir;
1341 
1342  case 11: // 28 point -- 11 degree
1343  ir = new IntegrationRule(28);
1344  ir->AddTriPoints6(0, 0.0,
1345  0.141129718717363295960826061941652,
1346  3.68119189165027713212944752369032E-03);
1347  ir->AddTriMidPoint(6, 0.0439886505811161193990465846607278);
1348  ir->AddTriPoints3(7, 0.0259891409282873952600324854988407,
1349  4.37215577686801152475821439991262E-03);
1350  ir->AddTriPoints3(10, 0.0942875026479224956305697762754049,
1351  0.0190407859969674687575121697178070);
1352  ir->AddTriPoints3b(13, 0.010726449965572372516734795387128,
1353  9.42772402806564602923839129555767E-03);
1354  ir->AddTriPoints3(16, 0.207343382614511333452934024112966,
1355  0.0360798487723697630620149942932315);
1356  ir->AddTriPoints3b(19, 0.122184388599015809877869236727746,
1357  0.0346645693527679499208828254519072);
1358  ir->AddTriPoints6(22, 0.0448416775891304433090523914688007,
1359  0.2772206675282791551488214673424523,
1360  0.0205281577146442833208261574536469);
1361  ir->SetOrder(11);
1362  TriangleIntRules[11] = ir;
1363  return ir;
1364 
1365  case 12: // 33 point - 12 degree
1366  ir = new IntegrationRule(33);
1367  ir->AddTriPoints3b(0, 2.35652204523900E-02, 1.28655332202275E-02);
1368  ir->AddTriPoints3b(3, 1.20551215411079E-01, 2.18462722690190E-02);
1369  ir->AddTriPoints3(6, 2.71210385012116E-01, 3.14291121089425E-02);
1370  ir->AddTriPoints3(9, 1.27576145541586E-01, 1.73980564653545E-02);
1371  ir->AddTriPoints3(12, 2.13173504532100E-02, 3.08313052577950E-03);
1372  ir->AddTriPoints6(15, 1.15343494534698E-01, 2.75713269685514E-01,
1373  2.01857788831905E-02);
1374  ir->AddTriPoints6(21, 2.28383322222570E-02, 2.81325580989940E-01,
1375  1.11783866011515E-02);
1376  ir->AddTriPoints6(27, 2.57340505483300E-02, 1.16251915907597E-01,
1377  8.65811555432950E-03);
1378  ir->SetOrder(12);
1379  TriangleIntRules[12] = ir;
1380  return ir;
1381 
1382  case 13: // 37 point - 13 degree
1383  ir = new IntegrationRule(37);
1384  ir->AddTriPoints3b(0, 0.0,
1385  2.67845189554543044455908674650066E-03);
1386  ir->AddTriMidPoint(3, 0.0293480398063595158995969648597808);
1387  ir->AddTriPoints3(4, 0.0246071886432302181878499494124643,
1388  3.92538414805004016372590903990464E-03);
1389  ir->AddTriPoints3b(7, 0.159382493797610632566158925635800,
1390  0.0253344765879434817105476355306468);
1391  ir->AddTriPoints3(10, 0.227900255506160619646298948153592,
1392  0.0250401630452545330803738542916538);
1393  ir->AddTriPoints3(13, 0.116213058883517905247155321839271,
1394  0.0158235572961491595176634480481793);
1395  ir->AddTriPoints3b(16, 0.046794039901841694097491569577008,
1396  0.0157462815379843978450278590138683);
1397  ir->AddTriPoints6(19, 0.0227978945382486125477207592747430,
1398  0.1254265183163409177176192369310890,
1399  7.90126610763037567956187298486575E-03);
1400  ir->AddTriPoints6(25, 0.0162757709910885409437036075960413,
1401  0.2909269114422506044621801030055257,
1402  7.99081889046420266145965132482933E-03);
1403  ir->AddTriPoints6(31, 0.0897330604516053590796290561145196,
1404  0.2723110556841851025078181617634414,
1405  0.0182757511120486476280967518782978);
1406  ir->SetOrder(13);
1407  TriangleIntRules[13] = ir;
1408  return ir;
1409 
1410  case 14: // 42 point - 14 degree
1411  ir = new IntegrationRule(42);
1412  ir->AddTriPoints3b(0, 2.20721792756430E-02, 1.09417906847145E-02);
1413  ir->AddTriPoints3b(3, 1.64710561319092E-01, 1.63941767720625E-02);
1414  ir->AddTriPoints3(6, 2.73477528308839E-01, 2.58870522536460E-02);
1415  ir->AddTriPoints3(9, 1.77205532412543E-01, 2.10812943684965E-02);
1416  ir->AddTriPoints3(12, 6.17998830908730E-02, 7.21684983488850E-03);
1417  ir->AddTriPoints3(15, 1.93909612487010E-02, 2.46170180120000E-03);
1418  ir->AddTriPoints6(18, 5.71247574036480E-02, 1.72266687821356E-01,
1419  1.23328766062820E-02);
1420  ir->AddTriPoints6(24, 9.29162493569720E-02, 3.36861459796345E-01,
1421  1.92857553935305E-02);
1422  ir->AddTriPoints6(30, 1.46469500556540E-02, 2.98372882136258E-01,
1423  7.21815405676700E-03);
1424  ir->AddTriPoints6(36, 1.26833093287200E-03, 1.18974497696957E-01,
1425  2.50511441925050E-03);
1426  ir->SetOrder(14);
1427  TriangleIntRules[14] = ir;
1428  return ir;
1429 
1430  case 15: // 54 point - 15 degree
1431  ir = new IntegrationRule(54);
1432  ir->AddTriPoints3b(0, 0.0834384072617499333, 0.016330909424402645);
1433  ir->AddTriPoints3b(3, 0.192779070841738867, 0.01370640901568218);
1434  ir->AddTriPoints3(6, 0.293197167913025367, 0.01325501829935165);
1435  ir->AddTriPoints3(9, 0.146467786942772933, 0.014607981068243055);
1436  ir->AddTriPoints3(12, 0.0563628676656034333, 0.005292304033121995);
1437  ir->AddTriPoints3(15, 0.0165751268583703333, 0.0018073215320460175);
1438  ir->AddTriPoints6(18, 0.0099122033092248, 0.239534554154794445,
1439  0.004263874050854718);
1440  ir->AddTriPoints6(24, 0.015803770630228, 0.404878807318339958,
1441  0.006958088258345965);
1442  ir->AddTriPoints6(30, 0.00514360881697066667, 0.0950021131130448885,
1443  0.0021459664703674175);
1444  ir->AddTriPoints6(36, 0.0489223257529888, 0.149753107322273969,
1445  0.008117664640887445);
1446  ir->AddTriPoints6(42, 0.0687687486325192, 0.286919612441334979,
1447  0.012803670460631195);
1448  ir->AddTriPoints6(48, 0.1684044181246992, 0.281835668099084562,
1449  0.016544097765822835);
1450  ir->SetOrder(15);
1451  TriangleIntRules[15] = ir;
1452  return ir;
1453 
1454  case 16: // 61 point - 17 degree (used for 16 as well)
1455  case 17:
1456  ir = new IntegrationRule(61);
1457  ir->AddTriMidPoint(0, 1.67185996454015E-02);
1458  ir->AddTriPoints3b(1, 5.65891888645200E-03, 2.54670772025350E-03);
1459  ir->AddTriPoints3b(4, 3.56473547507510E-02, 7.33543226381900E-03);
1460  ir->AddTriPoints3b(7, 9.95200619584370E-02, 1.21754391768360E-02);
1461  ir->AddTriPoints3b(10, 1.99467521245206E-01, 1.55537754344845E-02);
1462  ir->AddTriPoints3 (13, 2.52141267970953E-01, 1.56285556093100E-02);
1463  ir->AddTriPoints3 (16, 1.62047004658461E-01, 1.24078271698325E-02);
1464  ir->AddTriPoints3 (19, 7.58758822607460E-02, 7.02803653527850E-03);
1465  ir->AddTriPoints3 (22, 1.56547269678220E-02, 1.59733808688950E-03);
1466  ir->AddTriPoints6 (25, 1.01869288269190E-02, 3.34319867363658E-01,
1467  4.05982765949650E-03);
1468  ir->AddTriPoints6 (31, 1.35440871671036E-01, 2.92221537796944E-01,
1469  1.34028711415815E-02);
1470  ir->AddTriPoints6 (37, 5.44239242905830E-02, 3.19574885423190E-01,
1471  9.22999660541100E-03);
1472  ir->AddTriPoints6 (43, 1.28685608336370E-02, 1.90704224192292E-01,
1473  4.23843426716400E-03);
1474  ir->AddTriPoints6 (49, 6.71657824135240E-02, 1.80483211648746E-01,
1475  9.14639838501250E-03);
1476  ir->AddTriPoints6 (55, 1.46631822248280E-02, 8.07113136795640E-02,
1477  3.33281600208250E-03);
1478  ir->SetOrder(17);
1479  TriangleIntRules[16] = TriangleIntRules[17] = ir;
1480  return ir;
1481 
1482  case 18: // 73 point - 19 degree (used for 18 as well)
1483  case 19:
1484  ir = new IntegrationRule(73);
1485  ir->AddTriMidPoint(0, 0.0164531656944595);
1486  ir->AddTriPoints3b(1, 0.020780025853987, 0.005165365945636);
1487  ir->AddTriPoints3b(4, 0.090926214604215, 0.011193623631508);
1488  ir->AddTriPoints3b(7, 0.197166638701138, 0.015133062934734);
1489  ir->AddTriPoints3 (10, 0.255551654403098, 0.015245483901099);
1490  ir->AddTriPoints3 (13, 0.17707794215213, 0.0120796063708205);
1491  ir->AddTriPoints3 (16, 0.110061053227952, 0.0080254017934005);
1492  ir->AddTriPoints3 (19, 0.05552862425184, 0.004042290130892);
1493  ir->AddTriPoints3 (22, 0.012621863777229, 0.0010396810137425);
1494  ir->AddTriPoints6 (25, 0.003611417848412, 0.395754787356943,
1495  0.0019424384524905);
1496  ir->AddTriPoints6 (31, 0.13446675453078, 0.307929983880436,
1497  0.012787080306011);
1498  ir->AddTriPoints6 (37, 0.014446025776115, 0.26456694840652,
1499  0.004440451786669);
1500  ir->AddTriPoints6 (43, 0.046933578838178, 0.358539352205951,
1501  0.0080622733808655);
1502  ir->AddTriPoints6 (49, 0.002861120350567, 0.157807405968595,
1503  0.0012459709087455);
1504  ir->AddTriPoints6 (55, 0.075050596975911, 0.223861424097916,
1505  0.0091214200594755);
1506  ir->AddTriPoints6 (61, 0.03464707481676, 0.142421601113383,
1507  0.0051292818680995);
1508  ir->AddTriPoints6 (67, 0.065494628082938, 0.010161119296278,
1509  0.001899964427651);
1510  ir->SetOrder(19);
1511  TriangleIntRules[18] = TriangleIntRules[19] = ir;
1512  return ir;
1513 
1514  case 20: // 85 point - 20 degree
1515  ir = new IntegrationRule(85);
1516  ir->AddTriMidPoint(0, 0.01380521349884976);
1517  ir->AddTriPoints3b(1, 0.001500649324429, 0.00088951477366337);
1518  ir->AddTriPoints3b(4, 0.0941397519389508667, 0.010056199056980585);
1519  ir->AddTriPoints3b(7, 0.2044721240895264, 0.013408923629665785);
1520  ir->AddTriPoints3(10, 0.264500202532787333, 0.012261566900751005);
1521  ir->AddTriPoints3(13, 0.211018964092076767, 0.008197289205347695);
1522  ir->AddTriPoints3(16, 0.107735607171271333, 0.0073979536993248);
1523  ir->AddTriPoints3(19, 0.0390690878378026667, 0.0022896411388521255);
1524  ir->AddTriPoints3(22, 0.0111743797293296333, 0.0008259132577881085);
1525  ir->AddTriPoints6(25, 0.00534961818733726667, 0.0635496659083522206,
1526  0.001174585454287792);
1527  ir->AddTriPoints6(31, 0.00795481706619893333, 0.157106918940706982,
1528  0.0022329628770908965);
1529  ir->AddTriPoints6(37, 0.0104223982812638, 0.395642114364374018,
1530  0.003049783403953986);
1531  ir->AddTriPoints6(43, 0.0109644147961233333, 0.273167570712910522,
1532  0.0034455406635941015);
1533  ir->AddTriPoints6(49, 0.0385667120854623333, 0.101785382485017108,
1534  0.0039987375362390815);
1535  ir->AddTriPoints6(55, 0.0355805078172182, 0.446658549176413815,
1536  0.003693067142668012);
1537  ir->AddTriPoints6(61, 0.0496708163627641333, 0.199010794149503095,
1538  0.00639966593932413);
1539  ir->AddTriPoints6(67, 0.0585197250843317333, 0.3242611836922827,
1540  0.008629035587848275);
1541  ir->AddTriPoints6(73, 0.121497787004394267, 0.208531363210132855,
1542  0.009336472951467735);
1543  ir->AddTriPoints6(79, 0.140710844943938733, 0.323170566536257485,
1544  0.01140911202919763);
1545  ir->SetOrder(20);
1546  TriangleIntRules[20] = ir;
1547  return ir;
1548 
1549  case 21: // 126 point - 25 degree (used also for degrees from 21 to 24)
1550  case 22:
1551  case 23:
1552  case 24:
1553  case 25:
1554  ir = new IntegrationRule(126);
1555  ir->AddTriPoints3b(0, 0.0279464830731742, 0.0040027909400102085);
1556  ir->AddTriPoints3b(3, 0.131178601327651467, 0.00797353841619525);
1557  ir->AddTriPoints3b(6, 0.220221729512072267, 0.006554570615397765);
1558  ir->AddTriPoints3 (9, 0.298443234019804467, 0.00979150048281781);
1559  ir->AddTriPoints3(12, 0.2340441723373718, 0.008235442720768635);
1560  ir->AddTriPoints3(15, 0.151468334609017567, 0.00427363953704605);
1561  ir->AddTriPoints3(18, 0.112733893545993667, 0.004080942928613246);
1562  ir->AddTriPoints3(21, 0.0777156920915263, 0.0030605732699918895);
1563  ir->AddTriPoints3(24, 0.034893093614297, 0.0014542491324683325);
1564  ir->AddTriPoints3(27, 0.00725818462093236667, 0.00034613762283099815);
1565  ir->AddTriPoints6(30, 0.0012923527044422, 0.227214452153364077,
1566  0.0006241445996386985);
1567  ir->AddTriPoints6(36, 0.0053997012721162, 0.435010554853571706,
1568  0.001702376454401511);
1569  ir->AddTriPoints6(42, 0.006384003033975, 0.320309599272204437,
1570  0.0016798271630320255);
1571  ir->AddTriPoints6(48, 0.00502821150199306667, 0.0917503222800051889,
1572  0.000858078269748377);
1573  ir->AddTriPoints6(54, 0.00682675862178186667, 0.0380108358587243835,
1574  0.000740428158357803);
1575  ir->AddTriPoints6(60, 0.0100161996399295333, 0.157425218485311668,
1576  0.0017556563053643425);
1577  ir->AddTriPoints6(66, 0.02575781317339, 0.239889659778533193,
1578  0.003696775074853242);
1579  ir->AddTriPoints6(72, 0.0302278981199158, 0.361943118126060531,
1580  0.003991543738688279);
1581  ir->AddTriPoints6(78, 0.0305049901071620667, 0.0835519609548285602,
1582  0.0021779813065790205);
1583  ir->AddTriPoints6(84, 0.0459565473625693333, 0.148443220732418205,
1584  0.003682528350708916);
1585  ir->AddTriPoints6(90, 0.0674428005402775333, 0.283739708727534955,
1586  0.005481786423209775);
1587  ir->AddTriPoints6(96, 0.0700450914159106, 0.406899375118787573,
1588  0.00587498087177056);
1589  ir->AddTriPoints6(102, 0.0839115246401166, 0.194113987024892542,
1590  0.005007800356899285);
1591  ir->AddTriPoints6(108, 0.120375535677152667, 0.32413434700070316,
1592  0.00665482039381434);
1593  ir->AddTriPoints6(114, 0.148066899157366667, 0.229277483555980969,
1594  0.00707722325261307);
1595  ir->AddTriPoints6(120, 0.191771865867325067, 0.325618122595983752,
1596  0.007440689780584005);
1597  ir->SetOrder(25);
1598  TriangleIntRules[21] =
1599  TriangleIntRules[22] =
1600  TriangleIntRules[23] =
1601  TriangleIntRules[24] =
1602  TriangleIntRules[25] = ir;
1603  return ir;
1604 
1605  default:
1606  // Grundmann-Moller rules
1607  int i = (Order / 2) * 2 + 1; // Get closest odd # >= Order
1608  AllocIntRule(TriangleIntRules, i);
1609  ir = new IntegrationRule;
1610  ir->GrundmannMollerSimplexRule(i/2,2);
1611  TriangleIntRules[i-1] = TriangleIntRules[i] = ir;
1612  return ir;
1613  }
1614 }
1615 
1616 // Integration rules for unit square
1617 IntegrationRule *IntegrationRules::SquareIntegrationRule(int Order)
1618 {
1619  int RealOrder = GetSegmentRealOrder(Order);
1620  // Order is one of {RealOrder-1,RealOrder}
1621  if (!HaveIntRule(SegmentIntRules, RealOrder))
1622  {
1623  SegmentIntegrationRule(RealOrder);
1624  }
1625  AllocIntRule(SquareIntRules, RealOrder); // RealOrder >= Order
1626  SquareIntRules[RealOrder-1] =
1627  SquareIntRules[RealOrder] =
1628  new IntegrationRule(*SegmentIntRules[RealOrder],
1629  *SegmentIntRules[RealOrder]);
1630  return SquareIntRules[Order];
1631 }
1632 
1633 /** Integration rules for reference tetrahedron
1634  {[0,0,0],[1,0,0],[0,1,0],[0,0,1]} */
1635 IntegrationRule *IntegrationRules::TetrahedronIntegrationRule(int Order)
1636 {
1637  IntegrationRule *ir;
1638  // Note: Set TetrahedronIntRules[*] to ir only *after* ir is fully
1639  // constructed. This is needed in multithreaded environment.
1640 
1641  // assuming that orders <= 9 are pre-allocated
1642  switch (Order)
1643  {
1644  case 0: // 1 point - degree 1
1645  case 1:
1646  ir = new IntegrationRule(1);
1647  ir->AddTetMidPoint(0, 1./6.);
1648  ir->SetOrder(1);
1649  TetrahedronIntRules[0] = TetrahedronIntRules[1] = ir;
1650  return ir;
1651 
1652  case 2: // 4 points - degree 2
1653  ir = new IntegrationRule(4);
1654  // ir->AddTetPoints4(0, 0.13819660112501051518, 1./24.);
1655  ir->AddTetPoints4b(0, 0.58541019662496845446, 1./24.);
1656  ir->SetOrder(2);
1657  TetrahedronIntRules[2] = ir;
1658  return ir;
1659 
1660  case 3: // 5 points - degree 3 (negative weight)
1661  ir = new IntegrationRule(5);
1662  ir->AddTetMidPoint(0, -2./15.);
1663  ir->AddTetPoints4b(1, 0.5, 0.075);
1664  ir->SetOrder(3);
1665  TetrahedronIntRules[3] = ir;
1666  return ir;
1667 
1668  case 4: // 11 points - degree 4 (negative weight)
1669  ir = new IntegrationRule(11);
1670  ir->AddTetPoints4(0, 1./14., 343./45000.);
1671  ir->AddTetMidPoint(4, -74./5625.);
1672  ir->AddTetPoints6(5, 0.10059642383320079500, 28./1125.);
1673  ir->SetOrder(4);
1674  TetrahedronIntRules[4] = ir;
1675  return ir;
1676 
1677  case 5: // 14 points - degree 5
1678  ir = new IntegrationRule(14);
1679  ir->AddTetPoints6(0, 0.045503704125649649492,
1680  7.0910034628469110730E-03);
1681  ir->AddTetPoints4(6, 0.092735250310891226402, 0.012248840519393658257);
1682  ir->AddTetPoints4b(10, 0.067342242210098170608,
1683  0.018781320953002641800);
1684  ir->SetOrder(5);
1685  TetrahedronIntRules[5] = ir;
1686  return ir;
1687 
1688  case 6: // 24 points - degree 6
1689  ir = new IntegrationRule(24);
1690  ir->AddTetPoints4(0, 0.21460287125915202929,
1691  6.6537917096945820166E-03);
1692  ir->AddTetPoints4(4, 0.040673958534611353116,
1693  1.6795351758867738247E-03);
1694  ir->AddTetPoints4b(8, 0.032986329573173468968,
1695  9.2261969239424536825E-03);
1696  ir->AddTetPoints12(12, 0.063661001875017525299, 0.26967233145831580803,
1697  8.0357142857142857143E-03);
1698  ir->SetOrder(6);
1699  TetrahedronIntRules[6] = ir;
1700  return ir;
1701 
1702  case 7: // 31 points - degree 7 (negative weight)
1703  ir = new IntegrationRule(31);
1704  ir->AddTetPoints6(0, 0.0, 9.7001763668430335097E-04);
1705  ir->AddTetMidPoint(6, 0.018264223466108820291);
1706  ir->AddTetPoints4(7, 0.078213192330318064374, 0.010599941524413686916);
1707  ir->AddTetPoints4(11, 0.12184321666390517465,
1708  -0.062517740114331851691);
1709  ir->AddTetPoints4b(15, 2.3825066607381275412E-03,
1710  4.8914252630734993858E-03);
1711  ir->AddTetPoints12(19, 0.1, 0.2, 0.027557319223985890653);
1712  ir->SetOrder(7);
1713  TetrahedronIntRules[7] = ir;
1714  return ir;
1715 
1716  case 8: // 43 points - degree 8 (negative weight)
1717  ir = new IntegrationRule(43);
1718  ir->AddTetPoints4(0, 5.7819505051979972532E-03,
1719  1.6983410909288737984E-04);
1720  ir->AddTetPoints4(4, 0.082103588310546723091,
1721  1.9670333131339009876E-03);
1722  ir->AddTetPoints12(8, 0.036607749553197423679, 0.19048604193463345570,
1723  2.1405191411620925965E-03);
1724  ir->AddTetPoints6(20, 0.050532740018894224426,
1725  4.5796838244672818007E-03);
1726  ir->AddTetPoints12(26, 0.22906653611681113960, 0.035639582788534043717,
1727  5.7044858086819185068E-03);
1728  ir->AddTetPoints4(38, 0.20682993161067320408, 0.014250305822866901248);
1729  ir->AddTetMidPoint(42, -0.020500188658639915841);
1730  ir->SetOrder(8);
1731  TetrahedronIntRules[8] = ir;
1732  return ir;
1733 
1734  case 9: // orders 9 and higher -- Grundmann-Moller rules
1735  ir = new IntegrationRule;
1736  ir->GrundmannMollerSimplexRule(4,3);
1737  TetrahedronIntRules[9] = ir;
1738  return ir;
1739 
1740  default: // Grundmann-Moller rules
1741  int i = (Order / 2) * 2 + 1; // Get closest odd # >= Order
1742  AllocIntRule(TetrahedronIntRules, i);
1743  ir = new IntegrationRule;
1744  ir->GrundmannMollerSimplexRule(i/2,3);
1745  TetrahedronIntRules[i-1] = TetrahedronIntRules[i] = ir;
1746  return ir;
1747  }
1748 }
1749 
1750 // Integration rules for reference pyramid
1751 IntegrationRule *IntegrationRules::PyramidIntegrationRule(int Order)
1752 {
1753  // This is a simple integration rule adapted from an integration
1754  // rule for a cube which seems to be adequate for now. When we
1755  // implement high order finite elements for pyramids we should
1756  // revisit this and see if we can improve upon it.
1757  const IntegrationRule &irc = Get(Geometry::CUBE, Order);
1758  int npts = irc.GetNPoints();
1759  AllocIntRule(PyramidIntRules, Order);
1760  PyramidIntRules[Order] = new IntegrationRule(npts);
1761  PyramidIntRules[Order]->SetOrder(Order); // FIXME: see comment above
1762 
1763  for (int k=0; k<npts; k++)
1764  {
1765  const IntegrationPoint & ipc = irc.IntPoint(k);
1766  IntegrationPoint & ipp = PyramidIntRules[Order]->IntPoint(k);
1767  ipp.x = ipc.x * (1.0 - ipc.z);
1768  ipp.y = ipc.y * (1.0 - ipc.z);
1769  ipp.z = ipc.z;
1770  ipp.weight = ipc.weight / 3.0;
1771  }
1772  return PyramidIntRules[Order];
1773 }
1774 
1775 // Integration rules for reference prism
1776 IntegrationRule *IntegrationRules::PrismIntegrationRule(int Order)
1777 {
1778  const IntegrationRule & irt = Get(Geometry::TRIANGLE, Order);
1779  const IntegrationRule & irs = Get(Geometry::SEGMENT, Order);
1780  int nt = irt.GetNPoints();
1781  int ns = irs.GetNPoints();
1782  AllocIntRule(PrismIntRules, Order);
1783  PrismIntRules[Order] = new IntegrationRule(nt * ns);
1784  PrismIntRules[Order]->SetOrder(std::min(irt.GetOrder(), irs.GetOrder()));
1785 
1786  for (int ks=0; ks<ns; ks++)
1787  {
1788  const IntegrationPoint & ips = irs.IntPoint(ks);
1789  for (int kt=0; kt<nt; kt++)
1790  {
1791  int kp = ks * nt + kt;
1792  const IntegrationPoint & ipt = irt.IntPoint(kt);
1793  IntegrationPoint & ipp = PrismIntRules[Order]->IntPoint(kp);
1794  ipp.x = ipt.x;
1795  ipp.y = ipt.y;
1796  ipp.z = ips.x;
1797  ipp.weight = ipt.weight * ips.weight;
1798  }
1799  }
1800  return PrismIntRules[Order];
1801 }
1802 
1803 // Integration rules for reference cube
1804 IntegrationRule *IntegrationRules::CubeIntegrationRule(int Order)
1805 {
1806  int RealOrder = GetSegmentRealOrder(Order);
1807  if (!HaveIntRule(SegmentIntRules, RealOrder))
1808  {
1809  SegmentIntegrationRule(RealOrder);
1810  }
1811  AllocIntRule(CubeIntRules, RealOrder);
1812  CubeIntRules[RealOrder-1] =
1813  CubeIntRules[RealOrder] =
1814  new IntegrationRule(*SegmentIntRules[RealOrder],
1815  *SegmentIntRules[RealOrder],
1816  *SegmentIntRules[RealOrder]);
1817  return CubeIntRules[Order];
1818 }
1819 
1821  const int patch, const int *ijk,
1822  Array<const KnotVector*> const& kv,
1823  bool & deleteRule) const
1824 {
1825  deleteRule = false;
1826 
1827  // First check whether a rule has been assigned to element index elem.
1828  auto search = elementToRule.find(elem);
1829  if (search != elementToRule.end())
1830  {
1831  return *elementRule[search->second];
1832  }
1833 
1834  MFEM_VERIFY(patchRules1D.NumRows(),
1835  "Undefined rule in NURBSMeshRules::GetElementRule");
1836 
1837  // Use a tensor product of rules on the patch.
1838  MFEM_VERIFY(kv.Size() == dim, "");
1839 
1840  int np = 1;
1841  std::vector<std::vector<double>> el(dim);
1842 
1843  std::vector<int> npd;
1844  npd.assign(3, 0);
1845 
1846  for (int d=0; d<dim; ++d)
1847  {
1848  const int order = kv[d]->GetOrder();
1849 
1850  const double kv0 = (*kv[d])[order + ijk[d]];
1851  const double kv1 = (*kv[d])[order + ijk[d] + 1];
1852 
1853  const bool rightEnd = (order + ijk[d] + 1) == (kv[d]->Size() - 1);
1854 
1855  for (int i=0; i<patchRules1D(patch,d)->Size(); ++i)
1856  {
1857  const IntegrationPoint& ip = (*patchRules1D(patch,d))[i];
1858  if (kv0 <= ip.x && (ip.x < kv1 || rightEnd))
1859  {
1860  const double x = (ip.x - kv0) / (kv1 - kv0);
1861  el[d].push_back(x);
1862  el[d].push_back(ip.weight);
1863  }
1864  }
1865 
1866  npd[d] = el[d].size() / 2;
1867  np *= npd[d];
1868  }
1869 
1870  IntegrationRule *irp = new IntegrationRule(np);
1871  deleteRule = true;
1872 
1873  // Set (*irp)[i + j*npd[0] + k*npd[0]*npd[1]] =
1874  // (el[0][2*i], el[1][2*j], el[2][2*k])
1875 
1876  MFEM_VERIFY(npd[0] > 0 && npd[1] > 0, "Assuming 2D or 3D");
1877 
1878  for (int i = 0; i < npd[0]; ++i)
1879  {
1880  for (int j = 0; j < npd[1]; ++j)
1881  {
1882  for (int k = 0; k < std::max(npd[2], 1); ++k)
1883  {
1884  const int id = i + j*npd[0] + k*npd[0]*npd[1];
1885  (*irp)[id].x = el[0][2*i];
1886  (*irp)[id].y = el[1][2*j];
1887 
1888  (*irp)[id].weight = el[0][(2*i)+1];
1889  (*irp)[id].weight *= el[1][(2*j)+1];
1890 
1891  if (npd[2] > 0)
1892  {
1893  (*irp)[id].z = el[2][2*k];
1894  (*irp)[id].weight *= el[2][(2*k)+1];
1895  }
1896  }
1897  }
1898  }
1899 
1900  return *irp;
1901 }
1902 
1903 void NURBSMeshRules::GetIntegrationPointFrom1D(const int patch, int i, int j,
1904  int k, IntegrationPoint & ip)
1905 {
1906  MFEM_VERIFY(patchRules1D.NumRows() > 0,
1907  "Assuming patchRules1D is set.");
1908 
1909  ip.weight = (*patchRules1D(patch,0))[i].weight;
1910  ip.x = (*patchRules1D(patch,0))[i].x;
1911 
1912  if (dim > 1)
1913  {
1914  ip.weight *= (*patchRules1D(patch,1))[j].weight;
1915  ip.y = (*patchRules1D(patch,1))[j].x; // 1D rule only has x
1916  }
1917 
1918  if (dim > 2)
1919  {
1920  ip.weight *= (*patchRules1D(patch,2))[k].weight;
1921  ip.z = (*patchRules1D(patch,2))[k].x; // 1D rule only has x
1922  }
1923 }
1924 
1926 {
1927  if ((int) pointToElem.size() == npatches) { return; } // Already set
1928 
1929  MFEM_VERIFY(elementToRule.empty() && patchRules1D.NumRows() > 0
1930  && npatches > 0, "Assuming patchRules1D is set.");
1931  MFEM_VERIFY(mesh.NURBSext, "");
1932  MFEM_VERIFY(mesh.Dimension() == dim, "");
1933 
1934  pointToElem.resize(npatches);
1935  patchRules1D_KnotSpan.resize(npatches);
1936 
1937  // First, find all the elements in each patch.
1938  std::vector<std::vector<int>> patchElements(npatches);
1939 
1940  for (int e=0; e<mesh.GetNE(); ++e)
1941  {
1942  patchElements[mesh.NURBSext->GetElementPatch(e)].push_back(e);
1943  }
1944 
1945  Array<int> ijk(3);
1946  Array<int> maxijk(3);
1947  Array<int> np(3); // Number of points in each dimension
1948  ijk = 0;
1949 
1951 
1952  for (int p=0; p<npatches; ++p)
1953  {
1954  patchRules1D_KnotSpan[p].resize(dim);
1955 
1956  // For each patch, get the range of ijk.
1957  mesh.NURBSext->GetPatchKnotVectors(p, pkv);
1958  MFEM_VERIFY((int) pkv.Size() == dim, "");
1959 
1960  maxijk = 1;
1961  np = 1;
1962  for (int d=0; d<dim; ++d)
1963  {
1964  maxijk[d] = pkv[d]->GetNKS();
1965  np[d] = patchRules1D(p,d)->Size();
1966  }
1967 
1968  // For each patch, set a map from ijk to element index.
1969  Array3D<int> ijk2elem(maxijk[0], maxijk[1], maxijk[2]);
1970  ijk2elem = -1;
1971 
1972  for (auto elem : patchElements[p])
1973  {
1974  mesh.NURBSext->GetElementIJK(elem, ijk);
1975  MFEM_VERIFY(ijk2elem(ijk[0], ijk[1], ijk[2]) == -1, "");
1976  ijk2elem(ijk[0], ijk[1], ijk[2]) = elem;
1977  }
1978 
1979  // For each point, find its ijk and from that its element index.
1980  // It is assumed here that the NURBSFiniteElement kv the same as the
1981  // patch kv.
1982 
1983  for (int d=0; d<dim; ++d)
1984  {
1985  patchRules1D_KnotSpan[p][d].SetSize(patchRules1D(p,d)->Size());
1986 
1987  for (int r=0; r<patchRules1D(p,d)->Size(); ++r)
1988  {
1989  const IntegrationPoint& ip = (*patchRules1D(p,d))[r];
1990 
1991  const int order = pkv[d]->GetOrder();
1992 
1993  // Find ijk_d such that ip.x is in the corresponding knot-span.
1994  int ijk_d = 0;
1995  bool found = false;
1996  while (!found)
1997  {
1998  const double kv0 = (*pkv[d])[order + ijk_d];
1999  const double kv1 = (*pkv[d])[order + ijk_d + 1];
2000 
2001  const bool rightEnd = (order + ijk_d + 1) == (pkv[d]->Size() - 1);
2002 
2003  if (kv0 <= ip.x && (ip.x < kv1 || rightEnd))
2004  {
2005  found = true;
2006  }
2007  else
2008  {
2009  ijk_d++;
2010  }
2011  }
2012 
2013  patchRules1D_KnotSpan[p][d][r] = ijk_d;
2014  }
2015  }
2016 
2017  pointToElem[p].SetSize(np[0], np[1], np[2]);
2018  for (int i=0; i<np[0]; ++i)
2019  for (int j=0; j<np[1]; ++j)
2020  for (int k=0; k<np[2]; ++k)
2021  {
2022  const int elem = ijk2elem(patchRules1D_KnotSpan[p][0][i],
2023  patchRules1D_KnotSpan[p][1][j],
2024  patchRules1D_KnotSpan[p][2][k]);
2025  MFEM_VERIFY(elem >= 0, "");
2026  pointToElem[p](i,j,k) = elem;
2027  }
2028  } // Loop (p) over patches
2029 }
2030 
2031 void NURBSMeshRules::SetPatchRules1D(const int patch,
2032  std::vector<const IntegrationRule*> & ir1D)
2033 {
2034  MFEM_VERIFY((int) ir1D.size() == dim, "Wrong dimension");
2035 
2036  for (int i=0; i<dim; ++i)
2037  {
2038  patchRules1D(patch,i) = ir1D[i];
2039  }
2040 }
2041 
2043 {
2044  for (int i=0; i<patchRules1D.NumRows(); ++i)
2045  for (int j=0; j<patchRules1D.NumCols(); ++j)
2046  {
2047  delete patchRules1D(i, j);
2048  }
2049 }
2050 
2051 }
int GetElementPatch(int elem) const
Returns the index of the patch containing element elem.
Definition: nurbs.hpp:492
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition: intrules.hpp:253
Class for an integration rule - an Array of IntegrationPoint.
Definition: intrules.hpp:96
const IntegrationRule & Get(int GeomType, int Order)
Returns an integration rule for given GeomType and Order.
Definition: intrules.cpp:980
static void ClosedUniform(const int np, IntegrationRule *ir)
Definition: intrules.cpp:646
int Dimension() const
Dimension of the reference space used within the elements.
Definition: mesh.hpp:1020
~IntegrationRules()
Destroys an IntegrationRules object.
Definition: intrules.cpp:1077
void GetIntegrationPointFrom1D(const int patch, int i, int j, int k, IntegrationPoint &ip)
For tensor product rules defined on each patch by SetPatchRules1D(), return the integration point wit...
Definition: intrules.cpp:1903
aka "open half" Newton-Cotes
Definition: intrules.hpp:400
Container class for integration rules.
Definition: intrules.hpp:412
IntegrationRules RefinedIntRules(1, Quadrature1D::GaussLegendre)
A global object with all refined integration rules.
Definition: intrules.hpp:483
void GetElementIJK(int elem, Array< int > &ijk)
Definition: nurbs.cpp:4096
Vector beta
static void OpenUniform(const int np, IntegrationRule *ir)
Definition: intrules.cpp:630
void Finalize(Mesh const &mesh)
Finalize() must be called before this class can be used for assembly. In particular, it defines data used by GetPointElement().
Definition: intrules.cpp:1925
STL namespace.
IntegrationRules IntRules(0, Quadrature1D::GaussLegendre)
A global object with all integration rules (defined in intrules.cpp)
Definition: intrules.hpp:480
static void GaussLobatto(const int np, IntegrationRule *ir)
Definition: intrules.cpp:505
IntegrationPoint & IntPoint(int i)
Returns a reference to the i-th integration point.
Definition: intrules.hpp:256
void SetPatchRules1D(const int patch, std::vector< const IntegrationRule *> &ir1D)
Set 1D integration rules to be used as a tensor product rule on the patch with index patch...
Definition: intrules.cpp:2031
void mfem_error(const char *msg)
Function called when an error is encountered. Used by the macros MFEM_ABORT, MFEM_ASSERT, MFEM_VERIFY.
Definition: error.cpp:154
int GetOrder() const
Returns the order of the integration rule.
Definition: intrules.hpp:246
void SetPointIndices()
Sets the indices of each quadrature point on initialization.
Definition: intrules.cpp:99
int Size() const
Definition: nurbs.hpp:53
int GetNE() const
Definition: nurbs.hpp:49
static void OpenHalfUniform(const int np, IntegrationRule *ir)
Definition: intrules.cpp:666
MemoryType
Memory types supported by MFEM.
Definition: mem_manager.hpp:31
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:687
void GetPatchKnotVectors(int p, Array< KnotVector *> &kv)
Definition: nurbs.cpp:2670
void SetOrder(const int order)
Sets the order of the integration rule. This is only for keeping order information, it does not alter any data in the IntegrationRule.
Definition: intrules.hpp:250
int GetNE() const
Returns number of elements.
Definition: mesh.hpp:1086
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition: mesh.hpp:277
aka closed Newton-Cotes
Definition: intrules.hpp:399
Class for integration point with weight.
Definition: intrules.hpp:31
Host memory; using new[] and delete[].
int dim
Definition: ex24.cpp:53
aka open Newton-Cotes
Definition: intrules.hpp:398
int Size() const
Return the logical size of the array.
Definition: array.hpp:141
Vector data type.
Definition: vector.hpp:58
void pts(int iphi, int t, double x[])
RefCoord s[3]
IntegrationRule & GetElementRule(const int elem, const int patch, const int *ijk, Array< const KnotVector *> const &kv, bool &deleteRule) const
Returns a rule for the element.
Definition: intrules.cpp:1820
void Set(int GeomType, int Order, IntegrationRule &IntRule)
Definition: intrules.cpp:1031
void Set1w(const double x1, const double w)
Definition: intrules.hpp:90
static void GaussLegendre(const int np, IntegrationRule *ir)
Definition: intrules.cpp:424
double f(const Vector &p)