xref: /petsc/src/dm/dt/interface/dt.c (revision d3ef4daaf99d19e98efa749367c59a1afaa53b93)
1 /* Discretization tools */
2 
3 #include <petscdt.h>            /*I "petscdt.h" I*/
4 #include <petscblaslapack.h>
5 #include <petsc/private/petscimpl.h>
6 #include <petsc/private/dtimpl.h>
7 #include <petscviewer.h>
8 #include <petscdmplex.h>
9 #include <petscdmshell.h>
10 
11 #if defined(PETSC_HAVE_MPFR)
12 #include <mpfr.h>
13 #endif
14 
15 const char *const PetscDTNodeTypes_shifted[] = {"default", "gaussjacobi", "equispaced", "tanhsinh", "PETSCDTNODES_", NULL};
16 const char *const*const PetscDTNodeTypes = PetscDTNodeTypes_shifted + 1;
17 
18 const char *const PetscDTSimplexQuadratureTypes_shifted[] = {"default", "conic", "minsym", "PETSCDTSIMPLEXQUAD_", NULL};
19 const char *const*const PetscDTSimplexQuadratureTypes = PetscDTSimplexQuadratureTypes_shifted + 1;
20 
21 static PetscBool GolubWelschCite       = PETSC_FALSE;
22 const char       GolubWelschCitation[] = "@article{GolubWelsch1969,\n"
23                                          "  author  = {Golub and Welsch},\n"
24                                          "  title   = {Calculation of Quadrature Rules},\n"
25                                          "  journal = {Math. Comp.},\n"
26                                          "  volume  = {23},\n"
27                                          "  number  = {106},\n"
28                                          "  pages   = {221--230},\n"
29                                          "  year    = {1969}\n}\n";
30 
31 /* Numerical tests in src/dm/dt/tests/ex1.c show that when computing the nodes and weights of Gauss-Jacobi
32    quadrature rules:
33 
34    - in double precision, Newton's method and Golub & Welsch both work for moderate degrees (< 100),
35    - in single precision, Newton's method starts producing incorrect roots around n = 15, but
36      the weights from Golub & Welsch become a problem before then: they produces errors
37      in computing the Jacobi-polynomial Gram matrix around n = 6.
38 
39    So we default to Newton's method (required fewer dependencies) */
40 PetscBool PetscDTGaussQuadratureNewton_Internal = PETSC_TRUE;
41 
42 PetscClassId PETSCQUADRATURE_CLASSID = 0;
43 
44 /*@
45   PetscQuadratureCreate - Create a PetscQuadrature object
46 
47   Collective
48 
49   Input Parameter:
50 . comm - The communicator for the PetscQuadrature object
51 
52   Output Parameter:
53 . q  - The PetscQuadrature object
54 
55   Level: beginner
56 
57 .seealso: `PetscQuadratureDestroy()`, `PetscQuadratureGetData()`
58 @*/
59 PetscErrorCode PetscQuadratureCreate(MPI_Comm comm, PetscQuadrature *q)
60 {
61   PetscFunctionBegin;
62   PetscValidPointer(q, 2);
63   PetscCall(DMInitializePackage());
64   PetscCall(PetscHeaderCreate(*q,PETSCQUADRATURE_CLASSID,"PetscQuadrature","Quadrature","DT",comm,PetscQuadratureDestroy,PetscQuadratureView));
65   (*q)->dim       = -1;
66   (*q)->Nc        =  1;
67   (*q)->order     = -1;
68   (*q)->numPoints = 0;
69   (*q)->points    = NULL;
70   (*q)->weights   = NULL;
71   PetscFunctionReturn(0);
72 }
73 
74 /*@
75   PetscQuadratureDuplicate - Create a deep copy of the PetscQuadrature object
76 
77   Collective on q
78 
79   Input Parameter:
80 . q  - The PetscQuadrature object
81 
82   Output Parameter:
83 . r  - The new PetscQuadrature object
84 
85   Level: beginner
86 
87 .seealso: `PetscQuadratureCreate()`, `PetscQuadratureDestroy()`, `PetscQuadratureGetData()`
88 @*/
89 PetscErrorCode PetscQuadratureDuplicate(PetscQuadrature q, PetscQuadrature *r)
90 {
91   PetscInt         order, dim, Nc, Nq;
92   const PetscReal *points, *weights;
93   PetscReal       *p, *w;
94 
95   PetscFunctionBegin;
96   PetscValidPointer(q, 1);
97   PetscCall(PetscQuadratureCreate(PetscObjectComm((PetscObject) q), r));
98   PetscCall(PetscQuadratureGetOrder(q, &order));
99   PetscCall(PetscQuadratureSetOrder(*r, order));
100   PetscCall(PetscQuadratureGetData(q, &dim, &Nc, &Nq, &points, &weights));
101   PetscCall(PetscMalloc1(Nq*dim, &p));
102   PetscCall(PetscMalloc1(Nq*Nc, &w));
103   PetscCall(PetscArraycpy(p, points, Nq*dim));
104   PetscCall(PetscArraycpy(w, weights, Nc * Nq));
105   PetscCall(PetscQuadratureSetData(*r, dim, Nc, Nq, p, w));
106   PetscFunctionReturn(0);
107 }
108 
109 /*@
110   PetscQuadratureDestroy - Destroys a PetscQuadrature object
111 
112   Collective on q
113 
114   Input Parameter:
115 . q  - The PetscQuadrature object
116 
117   Level: beginner
118 
119 .seealso: `PetscQuadratureCreate()`, `PetscQuadratureGetData()`
120 @*/
121 PetscErrorCode PetscQuadratureDestroy(PetscQuadrature *q)
122 {
123   PetscFunctionBegin;
124   if (!*q) PetscFunctionReturn(0);
125   PetscValidHeaderSpecific((*q),PETSCQUADRATURE_CLASSID,1);
126   if (--((PetscObject)(*q))->refct > 0) {
127     *q = NULL;
128     PetscFunctionReturn(0);
129   }
130   PetscCall(PetscFree((*q)->points));
131   PetscCall(PetscFree((*q)->weights));
132   PetscCall(PetscHeaderDestroy(q));
133   PetscFunctionReturn(0);
134 }
135 
136 /*@
137   PetscQuadratureGetOrder - Return the order of the method
138 
139   Not collective
140 
141   Input Parameter:
142 . q - The PetscQuadrature object
143 
144   Output Parameter:
145 . order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated
146 
147   Level: intermediate
148 
149 .seealso: `PetscQuadratureSetOrder()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
150 @*/
151 PetscErrorCode PetscQuadratureGetOrder(PetscQuadrature q, PetscInt *order)
152 {
153   PetscFunctionBegin;
154   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
155   PetscValidIntPointer(order, 2);
156   *order = q->order;
157   PetscFunctionReturn(0);
158 }
159 
160 /*@
161   PetscQuadratureSetOrder - Return the order of the method
162 
163   Not collective
164 
165   Input Parameters:
166 + q - The PetscQuadrature object
167 - order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated
168 
169   Level: intermediate
170 
171 .seealso: `PetscQuadratureGetOrder()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
172 @*/
173 PetscErrorCode PetscQuadratureSetOrder(PetscQuadrature q, PetscInt order)
174 {
175   PetscFunctionBegin;
176   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
177   q->order = order;
178   PetscFunctionReturn(0);
179 }
180 
181 /*@
182   PetscQuadratureGetNumComponents - Return the number of components for functions to be integrated
183 
184   Not collective
185 
186   Input Parameter:
187 . q - The PetscQuadrature object
188 
189   Output Parameter:
190 . Nc - The number of components
191 
192   Note: We are performing an integral int f(x) . w(x) dx, where both f and w (the weight) have Nc components.
193 
194   Level: intermediate
195 
196 .seealso: `PetscQuadratureSetNumComponents()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
197 @*/
198 PetscErrorCode PetscQuadratureGetNumComponents(PetscQuadrature q, PetscInt *Nc)
199 {
200   PetscFunctionBegin;
201   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
202   PetscValidIntPointer(Nc, 2);
203   *Nc = q->Nc;
204   PetscFunctionReturn(0);
205 }
206 
207 /*@
208   PetscQuadratureSetNumComponents - Return the number of components for functions to be integrated
209 
210   Not collective
211 
212   Input Parameters:
213 + q  - The PetscQuadrature object
214 - Nc - The number of components
215 
216   Note: We are performing an integral int f(x) . w(x) dx, where both f and w (the weight) have Nc components.
217 
218   Level: intermediate
219 
220 .seealso: `PetscQuadratureGetNumComponents()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
221 @*/
222 PetscErrorCode PetscQuadratureSetNumComponents(PetscQuadrature q, PetscInt Nc)
223 {
224   PetscFunctionBegin;
225   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
226   q->Nc = Nc;
227   PetscFunctionReturn(0);
228 }
229 
230 /*@C
231   PetscQuadratureGetData - Returns the data defining the quadrature
232 
233   Not collective
234 
235   Input Parameter:
236 . q  - The PetscQuadrature object
237 
238   Output Parameters:
239 + dim - The spatial dimension
240 . Nc - The number of components
241 . npoints - The number of quadrature points
242 . points - The coordinates of each quadrature point
243 - weights - The weight of each quadrature point
244 
245   Level: intermediate
246 
247   Fortran Notes:
248     From Fortran you must call PetscQuadratureRestoreData() when you are done with the data
249 
250 .seealso: `PetscQuadratureCreate()`, `PetscQuadratureSetData()`
251 @*/
252 PetscErrorCode PetscQuadratureGetData(PetscQuadrature q, PetscInt *dim, PetscInt *Nc, PetscInt *npoints, const PetscReal *points[], const PetscReal *weights[])
253 {
254   PetscFunctionBegin;
255   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
256   if (dim) {
257     PetscValidIntPointer(dim, 2);
258     *dim = q->dim;
259   }
260   if (Nc) {
261     PetscValidIntPointer(Nc, 3);
262     *Nc = q->Nc;
263   }
264   if (npoints) {
265     PetscValidIntPointer(npoints, 4);
266     *npoints = q->numPoints;
267   }
268   if (points) {
269     PetscValidPointer(points, 5);
270     *points = q->points;
271   }
272   if (weights) {
273     PetscValidPointer(weights, 6);
274     *weights = q->weights;
275   }
276   PetscFunctionReturn(0);
277 }
278 
279 /*@
280   PetscQuadratureEqual - determine whether two quadratures are equivalent
281 
282   Input Parameters:
283 + A - A PetscQuadrature object
284 - B - Another PetscQuadrature object
285 
286   Output Parameters:
287 . equal - PETSC_TRUE if the quadratures are the same
288 
289   Level: intermediate
290 
291 .seealso: `PetscQuadratureCreate()`
292 @*/
293 PetscErrorCode PetscQuadratureEqual(PetscQuadrature A, PetscQuadrature B, PetscBool *equal)
294 {
295   PetscFunctionBegin;
296   PetscValidHeaderSpecific(A, PETSCQUADRATURE_CLASSID, 1);
297   PetscValidHeaderSpecific(B, PETSCQUADRATURE_CLASSID, 2);
298   PetscValidBoolPointer(equal, 3);
299   *equal = PETSC_FALSE;
300   if (A->dim != B->dim || A->Nc != B->Nc || A->order != B->order || A->numPoints != B->numPoints) {
301     PetscFunctionReturn(0);
302   }
303   for (PetscInt i=0; i<A->numPoints*A->dim; i++) {
304     if (PetscAbsReal(A->points[i] - B->points[i]) > PETSC_SMALL) {
305       PetscFunctionReturn(0);
306     }
307   }
308   if (!A->weights && !B->weights) {
309     *equal = PETSC_TRUE;
310     PetscFunctionReturn(0);
311   }
312   if (A->weights && B->weights) {
313     for (PetscInt i=0; i<A->numPoints; i++) {
314       if (PetscAbsReal(A->weights[i] - B->weights[i]) > PETSC_SMALL) {
315         PetscFunctionReturn(0);
316       }
317     }
318     *equal = PETSC_TRUE;
319   }
320   PetscFunctionReturn(0);
321 }
322 
323 static PetscErrorCode PetscDTJacobianInverse_Internal(PetscInt m, PetscInt n, const PetscReal J[], PetscReal Jinv[])
324 {
325   PetscScalar    *Js, *Jinvs;
326   PetscInt       i, j, k;
327   PetscBLASInt   bm, bn, info;
328 
329   PetscFunctionBegin;
330   if (!m || !n) PetscFunctionReturn(0);
331   PetscCall(PetscBLASIntCast(m, &bm));
332   PetscCall(PetscBLASIntCast(n, &bn));
333 #if defined(PETSC_USE_COMPLEX)
334   PetscCall(PetscMalloc2(m*n, &Js, m*n, &Jinvs));
335   for (i = 0; i < m*n; i++) Js[i] = J[i];
336 #else
337   Js = (PetscReal *) J;
338   Jinvs = Jinv;
339 #endif
340   if (m == n) {
341     PetscBLASInt *pivots;
342     PetscScalar *W;
343 
344     PetscCall(PetscMalloc2(m, &pivots, m, &W));
345 
346     PetscCall(PetscArraycpy(Jinvs, Js, m * m));
347     PetscCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, Jinvs, &bm, pivots, &info));
348     PetscCheck(!info,PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetrf %" PetscInt_FMT,(PetscInt)info);
349     PetscCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, Jinvs, &bm, pivots, W, &bm, &info));
350     PetscCheck(!info,PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetri %" PetscInt_FMT,(PetscInt)info);
351     PetscCall(PetscFree2(pivots, W));
352   } else if (m < n) {
353     PetscScalar *JJT;
354     PetscBLASInt *pivots;
355     PetscScalar *W;
356 
357     PetscCall(PetscMalloc1(m*m, &JJT));
358     PetscCall(PetscMalloc2(m, &pivots, m, &W));
359     for (i = 0; i < m; i++) {
360       for (j = 0; j < m; j++) {
361         PetscScalar val = 0.;
362 
363         for (k = 0; k < n; k++) val += Js[i * n + k] * Js[j * n + k];
364         JJT[i * m + j] = val;
365       }
366     }
367 
368     PetscCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, JJT, &bm, pivots, &info));
369     PetscCheck(!info,PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetrf %" PetscInt_FMT,(PetscInt)info);
370     PetscCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, JJT, &bm, pivots, W, &bm, &info));
371     PetscCheck(!info,PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetri %" PetscInt_FMT,(PetscInt)info);
372     for (i = 0; i < n; i++) {
373       for (j = 0; j < m; j++) {
374         PetscScalar val = 0.;
375 
376         for (k = 0; k < m; k++) val += Js[k * n + i] * JJT[k * m + j];
377         Jinvs[i * m + j] = val;
378       }
379     }
380     PetscCall(PetscFree2(pivots, W));
381     PetscCall(PetscFree(JJT));
382   } else {
383     PetscScalar *JTJ;
384     PetscBLASInt *pivots;
385     PetscScalar *W;
386 
387     PetscCall(PetscMalloc1(n*n, &JTJ));
388     PetscCall(PetscMalloc2(n, &pivots, n, &W));
389     for (i = 0; i < n; i++) {
390       for (j = 0; j < n; j++) {
391         PetscScalar val = 0.;
392 
393         for (k = 0; k < m; k++) val += Js[k * n + i] * Js[k * n + j];
394         JTJ[i * n + j] = val;
395       }
396     }
397 
398     PetscCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bn, &bn, JTJ, &bn, pivots, &info));
399     PetscCheck(!info,PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetrf %" PetscInt_FMT,(PetscInt)info);
400     PetscCallBLAS("LAPACKgetri", LAPACKgetri_(&bn, JTJ, &bn, pivots, W, &bn, &info));
401     PetscCheck(!info,PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetri %" PetscInt_FMT,(PetscInt)info);
402     for (i = 0; i < n; i++) {
403       for (j = 0; j < m; j++) {
404         PetscScalar val = 0.;
405 
406         for (k = 0; k < n; k++) val += JTJ[i * n + k] * Js[j * n + k];
407         Jinvs[i * m + j] = val;
408       }
409     }
410     PetscCall(PetscFree2(pivots, W));
411     PetscCall(PetscFree(JTJ));
412   }
413 #if defined(PETSC_USE_COMPLEX)
414   for (i = 0; i < m*n; i++) Jinv[i] = PetscRealPart(Jinvs[i]);
415   PetscCall(PetscFree2(Js, Jinvs));
416 #endif
417   PetscFunctionReturn(0);
418 }
419 
420 /*@
421    PetscQuadraturePushForward - Push forward a quadrature functional under an affine transformation.
422 
423    Collecive on PetscQuadrature
424 
425    Input Parameters:
426 +  q - the quadrature functional
427 .  imageDim - the dimension of the image of the transformation
428 .  origin - a point in the original space
429 .  originImage - the image of the origin under the transformation
430 .  J - the Jacobian of the image: an [imageDim x dim] matrix in row major order
431 -  formDegree - transform the quadrature weights as k-forms of this form degree (if the number of components is a multiple of (dim choose formDegree), it is assumed that they represent multiple k-forms) [see PetscDTAltVPullback() for interpretation of formDegree]
432 
433    Output Parameters:
434 .  Jinvstarq - a quadrature rule where each point is the image of a point in the original quadrature rule, and where the k-form weights have been pulled-back by the pseudoinverse of J to the k-form weights in the image space.
435 
436    Note: the new quadrature rule will have a different number of components if spaces have different dimensions.  For example, pushing a 2-form forward from a two dimensional space to a three dimensional space changes the number of components from 1 to 3.
437 
438    Level: intermediate
439 
440 .seealso: `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()`
441 @*/
442 PetscErrorCode PetscQuadraturePushForward(PetscQuadrature q, PetscInt imageDim, const PetscReal origin[], const PetscReal originImage[], const PetscReal J[], PetscInt formDegree, PetscQuadrature *Jinvstarq)
443 {
444   PetscInt         dim, Nc, imageNc, formSize, Ncopies, imageFormSize, Npoints, pt, i, j, c;
445   const PetscReal *points;
446   const PetscReal *weights;
447   PetscReal       *imagePoints, *imageWeights;
448   PetscReal       *Jinv;
449   PetscReal       *Jinvstar;
450 
451   PetscFunctionBegin;
452   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
453   PetscCheck(imageDim >= PetscAbsInt(formDegree),PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Cannot represent a %" PetscInt_FMT "-form in %" PetscInt_FMT " dimensions", PetscAbsInt(formDegree), imageDim);
454   PetscCall(PetscQuadratureGetData(q, &dim, &Nc, &Npoints, &points, &weights));
455   PetscCall(PetscDTBinomialInt(dim, PetscAbsInt(formDegree), &formSize));
456   PetscCheck(Nc % formSize == 0,PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Number of components %" PetscInt_FMT " is not a multiple of formSize %" PetscInt_FMT, Nc, formSize);
457   Ncopies = Nc / formSize;
458   PetscCall(PetscDTBinomialInt(imageDim, PetscAbsInt(formDegree), &imageFormSize));
459   imageNc = Ncopies * imageFormSize;
460   PetscCall(PetscMalloc1(Npoints * imageDim, &imagePoints));
461   PetscCall(PetscMalloc1(Npoints * imageNc, &imageWeights));
462   PetscCall(PetscMalloc2(imageDim * dim, &Jinv, formSize * imageFormSize, &Jinvstar));
463   PetscCall(PetscDTJacobianInverse_Internal(imageDim, dim, J, Jinv));
464   PetscCall(PetscDTAltVPullbackMatrix(imageDim, dim, Jinv, formDegree, Jinvstar));
465   for (pt = 0; pt < Npoints; pt++) {
466     const PetscReal *point = &points[pt * dim];
467     PetscReal       *imagePoint = &imagePoints[pt * imageDim];
468 
469     for (i = 0; i < imageDim; i++) {
470       PetscReal val = originImage[i];
471 
472       for (j = 0; j < dim; j++) val += J[i * dim + j] * (point[j] - origin[j]);
473       imagePoint[i] = val;
474     }
475     for (c = 0; c < Ncopies; c++) {
476       const PetscReal *form = &weights[pt * Nc + c * formSize];
477       PetscReal       *imageForm = &imageWeights[pt * imageNc + c * imageFormSize];
478 
479       for (i = 0; i < imageFormSize; i++) {
480         PetscReal val = 0.;
481 
482         for (j = 0; j < formSize; j++) val += Jinvstar[i * formSize + j] * form[j];
483         imageForm[i] = val;
484       }
485     }
486   }
487   PetscCall(PetscQuadratureCreate(PetscObjectComm((PetscObject)q), Jinvstarq));
488   PetscCall(PetscQuadratureSetData(*Jinvstarq, imageDim, imageNc, Npoints, imagePoints, imageWeights));
489   PetscCall(PetscFree2(Jinv, Jinvstar));
490   PetscFunctionReturn(0);
491 }
492 
493 /*@C
494   PetscQuadratureSetData - Sets the data defining the quadrature
495 
496   Not collective
497 
498   Input Parameters:
499 + q  - The PetscQuadrature object
500 . dim - The spatial dimension
501 . Nc - The number of components
502 . npoints - The number of quadrature points
503 . points - The coordinates of each quadrature point
504 - weights - The weight of each quadrature point
505 
506   Note: This routine owns the references to points and weights, so they must be allocated using PetscMalloc() and the user should not free them.
507 
508   Level: intermediate
509 
510 .seealso: `PetscQuadratureCreate()`, `PetscQuadratureGetData()`
511 @*/
512 PetscErrorCode PetscQuadratureSetData(PetscQuadrature q, PetscInt dim, PetscInt Nc, PetscInt npoints, const PetscReal points[], const PetscReal weights[])
513 {
514   PetscFunctionBegin;
515   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
516   if (dim >= 0)     q->dim       = dim;
517   if (Nc >= 0)      q->Nc        = Nc;
518   if (npoints >= 0) q->numPoints = npoints;
519   if (points) {
520     PetscValidRealPointer(points, 5);
521     q->points = points;
522   }
523   if (weights) {
524     PetscValidRealPointer(weights, 6);
525     q->weights = weights;
526   }
527   PetscFunctionReturn(0);
528 }
529 
530 static PetscErrorCode PetscQuadratureView_Ascii(PetscQuadrature quad, PetscViewer v)
531 {
532   PetscInt          q, d, c;
533   PetscViewerFormat format;
534 
535   PetscFunctionBegin;
536   if (quad->Nc > 1) PetscCall(PetscViewerASCIIPrintf(v, "Quadrature of order %" PetscInt_FMT " on %" PetscInt_FMT " points (dim %" PetscInt_FMT ") with %" PetscInt_FMT " components\n", quad->order, quad->numPoints, quad->dim, quad->Nc));
537   else              PetscCall(PetscViewerASCIIPrintf(v, "Quadrature of order %" PetscInt_FMT " on %" PetscInt_FMT " points (dim %" PetscInt_FMT ")\n", quad->order, quad->numPoints, quad->dim));
538   PetscCall(PetscViewerGetFormat(v, &format));
539   if (format != PETSC_VIEWER_ASCII_INFO_DETAIL) PetscFunctionReturn(0);
540   for (q = 0; q < quad->numPoints; ++q) {
541     PetscCall(PetscViewerASCIIPrintf(v, "p%" PetscInt_FMT " (", q));
542     PetscCall(PetscViewerASCIIUseTabs(v, PETSC_FALSE));
543     for (d = 0; d < quad->dim; ++d) {
544       if (d) PetscCall(PetscViewerASCIIPrintf(v, ", "));
545       PetscCall(PetscViewerASCIIPrintf(v, "%+g", (double)quad->points[q*quad->dim+d]));
546     }
547     PetscCall(PetscViewerASCIIPrintf(v, ") "));
548     if (quad->Nc > 1) PetscCall(PetscViewerASCIIPrintf(v, "w%" PetscInt_FMT " (", q));
549     for (c = 0; c < quad->Nc; ++c) {
550       if (c) PetscCall(PetscViewerASCIIPrintf(v, ", "));
551       PetscCall(PetscViewerASCIIPrintf(v, "%+g", (double)quad->weights[q*quad->Nc+c]));
552     }
553     if (quad->Nc > 1) PetscCall(PetscViewerASCIIPrintf(v, ")"));
554     PetscCall(PetscViewerASCIIPrintf(v, "\n"));
555     PetscCall(PetscViewerASCIIUseTabs(v, PETSC_TRUE));
556   }
557   PetscFunctionReturn(0);
558 }
559 
560 /*@C
561   PetscQuadratureView - Views a PetscQuadrature object
562 
563   Collective on quad
564 
565   Input Parameters:
566 + quad  - The PetscQuadrature object
567 - viewer - The PetscViewer object
568 
569   Level: beginner
570 
571 .seealso: `PetscQuadratureCreate()`, `PetscQuadratureGetData()`
572 @*/
573 PetscErrorCode PetscQuadratureView(PetscQuadrature quad, PetscViewer viewer)
574 {
575   PetscBool      iascii;
576 
577   PetscFunctionBegin;
578   PetscValidHeader(quad, 1);
579   if (viewer) PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
580   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject) quad), &viewer));
581   PetscCall(PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii));
582   PetscCall(PetscViewerASCIIPushTab(viewer));
583   if (iascii) PetscCall(PetscQuadratureView_Ascii(quad, viewer));
584   PetscCall(PetscViewerASCIIPopTab(viewer));
585   PetscFunctionReturn(0);
586 }
587 
588 /*@C
589   PetscQuadratureExpandComposite - Return a quadrature over the composite element, which has the original quadrature in each subelement
590 
591   Not collective
592 
593   Input Parameters:
594 + q - The original PetscQuadrature
595 . numSubelements - The number of subelements the original element is divided into
596 . v0 - An array of the initial points for each subelement
597 - jac - An array of the Jacobian mappings from the reference to each subelement
598 
599   Output Parameters:
600 . dim - The dimension
601 
602   Note: Together v0 and jac define an affine mapping from the original reference element to each subelement
603 
604  Not available from Fortran
605 
606   Level: intermediate
607 
608 .seealso: `PetscFECreate()`, `PetscSpaceGetDimension()`, `PetscDualSpaceGetDimension()`
609 @*/
610 PetscErrorCode PetscQuadratureExpandComposite(PetscQuadrature q, PetscInt numSubelements, const PetscReal v0[], const PetscReal jac[], PetscQuadrature *qref)
611 {
612   const PetscReal *points,    *weights;
613   PetscReal       *pointsRef, *weightsRef;
614   PetscInt         dim, Nc, order, npoints, npointsRef, c, p, cp, d, e;
615 
616   PetscFunctionBegin;
617   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
618   PetscValidRealPointer(v0, 3);
619   PetscValidRealPointer(jac, 4);
620   PetscValidPointer(qref, 5);
621   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, qref));
622   PetscCall(PetscQuadratureGetOrder(q, &order));
623   PetscCall(PetscQuadratureGetData(q, &dim, &Nc, &npoints, &points, &weights));
624   npointsRef = npoints*numSubelements;
625   PetscCall(PetscMalloc1(npointsRef*dim,&pointsRef));
626   PetscCall(PetscMalloc1(npointsRef*Nc, &weightsRef));
627   for (c = 0; c < numSubelements; ++c) {
628     for (p = 0; p < npoints; ++p) {
629       for (d = 0; d < dim; ++d) {
630         pointsRef[(c*npoints + p)*dim+d] = v0[c*dim+d];
631         for (e = 0; e < dim; ++e) {
632           pointsRef[(c*npoints + p)*dim+d] += jac[(c*dim + d)*dim+e]*(points[p*dim+e] + 1.0);
633         }
634       }
635       /* Could also use detJ here */
636       for (cp = 0; cp < Nc; ++cp) weightsRef[(c*npoints+p)*Nc+cp] = weights[p*Nc+cp]/numSubelements;
637     }
638   }
639   PetscCall(PetscQuadratureSetOrder(*qref, order));
640   PetscCall(PetscQuadratureSetData(*qref, dim, Nc, npointsRef, pointsRef, weightsRef));
641   PetscFunctionReturn(0);
642 }
643 
644 /* Compute the coefficients for the Jacobi polynomial recurrence,
645  *
646  * J^{a,b}_n(x) = (cnm1 + cnm1x * x) * J^{a,b}_{n-1}(x) - cnm2 * J^{a,b}_{n-2}(x).
647  */
648 #define PetscDTJacobiRecurrence_Internal(n,a,b,cnm1,cnm1x,cnm2) \
649 do {                                                            \
650   PetscReal _a = (a);                                           \
651   PetscReal _b = (b);                                           \
652   PetscReal _n = (n);                                           \
653   if (n == 1) {                                                 \
654     (cnm1) = (_a-_b) * 0.5;                                     \
655     (cnm1x) = (_a+_b+2.)*0.5;                                   \
656     (cnm2) = 0.;                                                \
657   } else {                                                      \
658     PetscReal _2n = _n+_n;                                      \
659     PetscReal _d = (_2n*(_n+_a+_b)*(_2n+_a+_b-2));              \
660     PetscReal _n1 = (_2n+_a+_b-1.)*(_a*_a-_b*_b);               \
661     PetscReal _n1x = (_2n+_a+_b-1.)*(_2n+_a+_b)*(_2n+_a+_b-2);  \
662     PetscReal _n2 = 2.*((_n+_a-1.)*(_n+_b-1.)*(_2n+_a+_b));     \
663     (cnm1) = _n1 / _d;                                          \
664     (cnm1x) = _n1x / _d;                                        \
665     (cnm2) = _n2 / _d;                                          \
666   }                                                             \
667 } while (0)
668 
669 /*@
670   PetscDTJacobiNorm - Compute the weighted L2 norm of a Jacobi polynomial.
671 
672   $\| P^{\alpha,\beta}_n \|_{\alpha,\beta}^2 = \int_{-1}^1 (1 + x)^{\alpha} (1 - x)^{\beta} P^{\alpha,\beta}_n (x)^2 dx.$
673 
674   Input Parameters:
675 - alpha - the left exponent > -1
676 . beta - the right exponent > -1
677 + n - the polynomial degree
678 
679   Output Parameter:
680 . norm - the weighted L2 norm
681 
682   Level: beginner
683 
684 .seealso: `PetscDTJacobiEval()`
685 @*/
686 PetscErrorCode PetscDTJacobiNorm(PetscReal alpha, PetscReal beta, PetscInt n, PetscReal *norm)
687 {
688   PetscReal twoab1;
689   PetscReal gr;
690 
691   PetscFunctionBegin;
692   PetscCheck(alpha > -1.,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Exponent alpha %g <= -1. invalid", (double) alpha);
693   PetscCheck(beta > -1.,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Exponent beta %g <= -1. invalid", (double) beta);
694   PetscCheck(n >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "n %" PetscInt_FMT " < 0 invalid", n);
695   twoab1 = PetscPowReal(2., alpha + beta + 1.);
696 #if defined(PETSC_HAVE_LGAMMA)
697   if (!n) {
698     gr = PetscExpReal(PetscLGamma(alpha+1.) + PetscLGamma(beta+1.) - PetscLGamma(alpha+beta+2.));
699   } else {
700     gr = PetscExpReal(PetscLGamma(n+alpha+1.) + PetscLGamma(n+beta+1.) - (PetscLGamma(n+1.) + PetscLGamma(n+alpha+beta+1.))) / (n+n+alpha+beta+1.);
701   }
702 #else
703   {
704     PetscInt alphai = (PetscInt) alpha;
705     PetscInt betai = (PetscInt) beta;
706     PetscInt i;
707 
708     gr = n ? (1. / (n+n+alpha+beta+1.)) : 1.;
709     if ((PetscReal) alphai == alpha) {
710       if (!n) {
711         for (i = 0; i < alphai; i++) gr *= (i+1.) / (beta+i+1.);
712         gr /= (alpha+beta+1.);
713       } else {
714         for (i = 0; i < alphai; i++) gr *= (n+i+1.) / (n+beta+i+1.);
715       }
716     } else if ((PetscReal) betai == beta) {
717       if (!n) {
718         for (i = 0; i < betai; i++) gr *= (i+1.) / (alpha+i+2.);
719         gr /= (alpha+beta+1.);
720       } else {
721         for (i = 0; i < betai; i++) gr *= (n+i+1.) / (n+alpha+i+1.);
722       }
723     } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"lgamma() - math routine is unavailable.");
724   }
725 #endif
726   *norm = PetscSqrtReal(twoab1 * gr);
727   PetscFunctionReturn(0);
728 }
729 
730 static PetscErrorCode PetscDTJacobiEval_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscInt k, const PetscReal *points, PetscInt ndegree, const PetscInt *degrees, PetscReal *p)
731 {
732   PetscReal ak, bk;
733   PetscReal abk1;
734   PetscInt i,l,maxdegree;
735 
736   PetscFunctionBegin;
737   maxdegree = degrees[ndegree-1] - k;
738   ak = a + k;
739   bk = b + k;
740   abk1 = a + b + k + 1.;
741   if (maxdegree < 0) {
742     for (i = 0; i < npoints; i++) for (l = 0; l < ndegree; l++) p[i*ndegree+l] = 0.;
743     PetscFunctionReturn(0);
744   }
745   for (i=0; i<npoints; i++) {
746     PetscReal pm1,pm2,x;
747     PetscReal cnm1, cnm1x, cnm2;
748     PetscInt  j,m;
749 
750     x    = points[i];
751     pm2  = 1.;
752     PetscDTJacobiRecurrence_Internal(1,ak,bk,cnm1,cnm1x,cnm2);
753     pm1 = (cnm1 + cnm1x*x);
754     l    = 0;
755     while (l < ndegree && degrees[l] - k < 0) {
756       p[l++] = 0.;
757     }
758     while (l < ndegree && degrees[l] - k == 0) {
759       p[l] = pm2;
760       for (m = 0; m < k; m++) p[l] *= (abk1 + m) * 0.5;
761       l++;
762     }
763     while (l < ndegree && degrees[l] - k == 1) {
764       p[l] = pm1;
765       for (m = 0; m < k; m++) p[l] *= (abk1 + 1 + m) * 0.5;
766       l++;
767     }
768     for (j=2; j<=maxdegree; j++) {
769       PetscReal pp;
770 
771       PetscDTJacobiRecurrence_Internal(j,ak,bk,cnm1,cnm1x,cnm2);
772       pp   = (cnm1 + cnm1x*x)*pm1 - cnm2*pm2;
773       pm2  = pm1;
774       pm1  = pp;
775       while (l < ndegree && degrees[l] - k == j) {
776         p[l] = pp;
777         for (m = 0; m < k; m++) p[l] *= (abk1 + j + m) * 0.5;
778         l++;
779       }
780     }
781     p += ndegree;
782   }
783   PetscFunctionReturn(0);
784 }
785 
786 /*@
787   PetscDTJacobiEvalJet - Evaluate the jet (function and derivatives) of the Jacobi polynomials basis up to a given degree.  The Jacobi polynomials with indices $\alpha$ and $\beta$ are orthogonal with respect to the weighted inner product $\langle f, g \rangle = \int_{-1}^1 (1+x)^{\alpha} (1-x)^{\beta) f(x) g(x) dx$.
788 
789   Input Parameters:
790 + alpha - the left exponent of the weight
791 . beta - the right exponetn of the weight
792 . npoints - the number of points to evaluate the polynomials at
793 . points - [npoints] array of point coordinates
794 . degree - the maximm degree polynomial space to evaluate, (degree + 1) will be evaluated total.
795 - k - the maximum derivative to evaluate in the jet, (k + 1) will be evaluated total.
796 
797   Output Parameters:
798 - p - an array containing the evaluations of the Jacobi polynomials's jets on the points.  the size is (degree + 1) x
799   (k + 1) x npoints, which also describes the order of the dimensions of this three-dimensional array: the first
800   (slowest varying) dimension is polynomial degree; the second dimension is derivative order; the third (fastest
801   varying) dimension is the index of the evaluation point.
802 
803   Level: advanced
804 
805 .seealso: `PetscDTJacobiEval()`, `PetscDTPKDEvalJet()`
806 @*/
807 PetscErrorCode PetscDTJacobiEvalJet(PetscReal alpha, PetscReal beta, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt k, PetscReal p[])
808 {
809   PetscInt        i, j, l;
810   PetscInt       *degrees;
811   PetscReal      *psingle;
812 
813   PetscFunctionBegin;
814   if (degree == 0) {
815     PetscInt zero = 0;
816 
817     for (i = 0; i <= k; i++) {
818       PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, i, points, 1, &zero, &p[i*npoints]));
819     }
820     PetscFunctionReturn(0);
821   }
822   PetscCall(PetscMalloc1(degree + 1, &degrees));
823   PetscCall(PetscMalloc1((degree + 1) * npoints, &psingle));
824   for (i = 0; i <= degree; i++) degrees[i] = i;
825   for (i = 0; i <= k; i++) {
826     PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, i, points, degree + 1, degrees, psingle));
827     for (j = 0; j <= degree; j++) {
828       for (l = 0; l < npoints; l++) {
829         p[(j * (k + 1) + i) * npoints + l] = psingle[l * (degree + 1) + j];
830       }
831     }
832   }
833   PetscCall(PetscFree(psingle));
834   PetscCall(PetscFree(degrees));
835   PetscFunctionReturn(0);
836 }
837 
838 /*@
839    PetscDTJacobiEval - evaluate Jacobi polynomials for the weight function $(1.+x)^{\alpha} (1.-x)^{\beta}$
840                        at points
841 
842    Not Collective
843 
844    Input Parameters:
845 +  npoints - number of spatial points to evaluate at
846 .  alpha - the left exponent > -1
847 .  beta - the right exponent > -1
848 .  points - array of locations to evaluate at
849 .  ndegree - number of basis degrees to evaluate
850 -  degrees - sorted array of degrees to evaluate
851 
852    Output Parameters:
853 +  B - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension npoints*ndegrees, allocated by caller) (or NULL)
854 .  D - row-oriented derivative evaluation matrix (or NULL)
855 -  D2 - row-oriented second derivative evaluation matrix (or NULL)
856 
857    Level: intermediate
858 
859 .seealso: `PetscDTGaussQuadrature()`
860 @*/
861 PetscErrorCode PetscDTJacobiEval(PetscInt npoints,PetscReal alpha, PetscReal beta, const PetscReal *points,PetscInt ndegree,const PetscInt *degrees,PetscReal *B,PetscReal *D,PetscReal *D2)
862 {
863   PetscFunctionBegin;
864   PetscCheck(alpha > -1.,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"alpha must be > -1.");
865   PetscCheck(beta > -1.,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"beta must be > -1.");
866   if (!npoints || !ndegree) PetscFunctionReturn(0);
867   if (B)  PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, 0, points, ndegree, degrees, B));
868   if (D)  PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, 1, points, ndegree, degrees, D));
869   if (D2) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, 2, points, ndegree, degrees, D2));
870   PetscFunctionReturn(0);
871 }
872 
873 /*@
874    PetscDTLegendreEval - evaluate Legendre polynomials at points
875 
876    Not Collective
877 
878    Input Parameters:
879 +  npoints - number of spatial points to evaluate at
880 .  points - array of locations to evaluate at
881 .  ndegree - number of basis degrees to evaluate
882 -  degrees - sorted array of degrees to evaluate
883 
884    Output Parameters:
885 +  B - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension npoints*ndegrees, allocated by caller) (or NULL)
886 .  D - row-oriented derivative evaluation matrix (or NULL)
887 -  D2 - row-oriented second derivative evaluation matrix (or NULL)
888 
889    Level: intermediate
890 
891 .seealso: `PetscDTGaussQuadrature()`
892 @*/
893 PetscErrorCode PetscDTLegendreEval(PetscInt npoints,const PetscReal *points,PetscInt ndegree,const PetscInt *degrees,PetscReal *B,PetscReal *D,PetscReal *D2)
894 {
895   PetscFunctionBegin;
896   PetscCall(PetscDTJacobiEval(npoints, 0., 0., points, ndegree, degrees, B, D, D2));
897   PetscFunctionReturn(0);
898 }
899 
900 /*@
901   PetscDTIndexToGradedOrder - convert an index into a tuple of monomial degrees in a graded order (that is, if the degree sum of tuple x is less than the degree sum of tuple y, then the index of x is smaller than the index of y)
902 
903   Input Parameters:
904 + len - the desired length of the degree tuple
905 - index - the index to convert: should be >= 0
906 
907   Output Parameter:
908 . degtup - will be filled with a tuple of degrees
909 
910   Level: beginner
911 
912   Note: for two tuples x and y with the same degree sum, partial degree sums over the final elements of the tuples
913   acts as a tiebreaker.  For example, (2, 1, 1) and (1, 2, 1) have the same degree sum, but the degree sum over the
914   last two elements is smaller for the former, so (2, 1, 1) < (1, 2, 1).
915 
916 .seealso: `PetscDTGradedOrderToIndex()`
917 @*/
918 PetscErrorCode PetscDTIndexToGradedOrder(PetscInt len, PetscInt index, PetscInt degtup[])
919 {
920   PetscInt i, total;
921   PetscInt sum;
922 
923   PetscFunctionBeginHot;
924   PetscCheck(len >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
925   PetscCheck(index >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index must be non-negative");
926   total = 1;
927   sum = 0;
928   while (index >= total) {
929     index -= total;
930     total = (total * (len + sum)) / (sum + 1);
931     sum++;
932   }
933   for (i = 0; i < len; i++) {
934     PetscInt c;
935 
936     degtup[i] = sum;
937     for (c = 0, total = 1; c < sum; c++) {
938       /* going into the loop, total is the number of way to have a tuple of sum exactly c with length len - 1 - i */
939       if (index < total) break;
940       index -= total;
941       total = (total * (len - 1 - i + c)) / (c + 1);
942       degtup[i]--;
943     }
944     sum -= degtup[i];
945   }
946   PetscFunctionReturn(0);
947 }
948 
949 /*@
950   PetscDTGradedOrderToIndex - convert a tuple into an index in a graded order, the inverse of PetscDTIndexToGradedOrder().
951 
952   Input Parameters:
953 + len - the length of the degree tuple
954 - degtup - tuple with this length
955 
956   Output Parameter:
957 . index - index in graded order: >= 0
958 
959   Level: Beginner
960 
961   Note: for two tuples x and y with the same degree sum, partial degree sums over the final elements of the tuples
962   acts as a tiebreaker.  For example, (2, 1, 1) and (1, 2, 1) have the same degree sum, but the degree sum over the
963   last two elements is smaller for the former, so (2, 1, 1) < (1, 2, 1).
964 
965 .seealso: `PetscDTIndexToGradedOrder()`
966 @*/
967 PetscErrorCode PetscDTGradedOrderToIndex(PetscInt len, const PetscInt degtup[], PetscInt *index)
968 {
969   PetscInt i, idx, sum, total;
970 
971   PetscFunctionBeginHot;
972   PetscCheck(len >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
973   for (i = 0, sum = 0; i < len; i++) sum += degtup[i];
974   idx = 0;
975   total = 1;
976   for (i = 0; i < sum; i++) {
977     idx += total;
978     total = (total * (len + i)) / (i + 1);
979   }
980   for (i = 0; i < len - 1; i++) {
981     PetscInt c;
982 
983     total = 1;
984     sum -= degtup[i];
985     for (c = 0; c < sum; c++) {
986       idx += total;
987       total = (total * (len - 1 - i + c)) / (c + 1);
988     }
989   }
990   *index = idx;
991   PetscFunctionReturn(0);
992 }
993 
994 static PetscBool PKDCite = PETSC_FALSE;
995 const char       PKDCitation[] = "@article{Kirby2010,\n"
996                                  "  title={Singularity-free evaluation of collapsed-coordinate orthogonal polynomials},\n"
997                                  "  author={Kirby, Robert C},\n"
998                                  "  journal={ACM Transactions on Mathematical Software (TOMS)},\n"
999                                  "  volume={37},\n"
1000                                  "  number={1},\n"
1001                                  "  pages={1--16},\n"
1002                                  "  year={2010},\n"
1003                                  "  publisher={ACM New York, NY, USA}\n}\n";
1004 
1005 /*@
1006   PetscDTPKDEvalJet - Evaluate the jet (function and derivatives) of the Proriol-Koornwinder-Dubiner (PKD) basis for
1007   the space of polynomials up to a given degree.  The PKD basis is L2-orthonormal on the biunit simplex (which is used
1008   as the reference element for finite elements in PETSc), which makes it a stable basis to use for evaluating
1009   polynomials in that domain.
1010 
1011   Input Parameters:
1012 + dim - the number of variables in the multivariate polynomials
1013 . npoints - the number of points to evaluate the polynomials at
1014 . points - [npoints x dim] array of point coordinates
1015 . degree - the degree (sum of degrees on the variables in a monomial) of the polynomial space to evaluate.  There are ((dim + degree) choose dim) polynomials in this space.
1016 - k - the maximum order partial derivative to evaluate in the jet.  There are (dim + k choose dim) partial derivatives
1017   in the jet.  Choosing k = 0 means to evaluate just the function and no derivatives
1018 
1019   Output Parameters:
1020 - p - an array containing the evaluations of the PKD polynomials' jets on the points.  The size is ((dim + degree)
1021   choose dim) x ((dim + k) choose dim) x npoints, which also describes the order of the dimensions of this
1022   three-dimensional array: the first (slowest varying) dimension is basis function index; the second dimension is jet
1023   index; the third (fastest varying) dimension is the index of the evaluation point.
1024 
1025   Level: advanced
1026 
1027   Note: The ordering of the basis functions, and the ordering of the derivatives in the jet, both follow the graded
1028   ordering of PetscDTIndexToGradedOrder() and PetscDTGradedOrderToIndex().  For example, in 3D, the polynomial with
1029   leading monomial x^2,y^0,z^1, which has degree tuple (2,0,1), which by PetscDTGradedOrderToIndex() has index 12 (it is the 13th basis function in the space);
1030   the partial derivative $\partial_x \partial_z$ has order tuple (1,0,1), appears at index 6 in the jet (it is the 7th partial derivative in the jet).
1031 
1032   The implementation uses Kirby's singularity-free evaluation algorithm, https://doi.org/10.1145/1644001.1644006.
1033 
1034 .seealso: `PetscDTGradedOrderToIndex()`, `PetscDTIndexToGradedOrder()`, `PetscDTJacobiEvalJet()`
1035 @*/
1036 PetscErrorCode PetscDTPKDEvalJet(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt k, PetscReal p[])
1037 {
1038   PetscInt        degidx, kidx, d, pt;
1039   PetscInt        Nk, Ndeg;
1040   PetscInt       *ktup, *degtup;
1041   PetscReal      *scales, initscale, scaleexp;
1042 
1043   PetscFunctionBegin;
1044   PetscCall(PetscCitationsRegister(PKDCitation, &PKDCite));
1045   PetscCall(PetscDTBinomialInt(dim + k, k, &Nk));
1046   PetscCall(PetscDTBinomialInt(degree + dim, degree, &Ndeg));
1047   PetscCall(PetscMalloc2(dim, &degtup, dim, &ktup));
1048   PetscCall(PetscMalloc1(Ndeg, &scales));
1049   initscale = 1.;
1050   if (dim > 1) {
1051     PetscCall(PetscDTBinomial(dim,2,&scaleexp));
1052     initscale = PetscPowReal(2.,scaleexp*0.5);
1053   }
1054   for (degidx = 0; degidx < Ndeg; degidx++) {
1055     PetscInt e, i;
1056     PetscInt m1idx = -1, m2idx = -1;
1057     PetscInt n;
1058     PetscInt degsum;
1059     PetscReal alpha;
1060     PetscReal cnm1, cnm1x, cnm2;
1061     PetscReal norm;
1062 
1063     PetscCall(PetscDTIndexToGradedOrder(dim, degidx, degtup));
1064     for (d = dim - 1; d >= 0; d--) if (degtup[d]) break;
1065     if (d < 0) { /* constant is 1 everywhere, all derivatives are zero */
1066       scales[degidx] = initscale;
1067       for (e = 0; e < dim; e++) {
1068         PetscCall(PetscDTJacobiNorm(e,0.,0,&norm));
1069         scales[degidx] /= norm;
1070       }
1071       for (i = 0; i < npoints; i++) p[degidx * Nk * npoints + i] = 1.;
1072       for (i = 0; i < (Nk - 1) * npoints; i++) p[(degidx * Nk + 1) * npoints + i] = 0.;
1073       continue;
1074     }
1075     n = degtup[d];
1076     degtup[d]--;
1077     PetscCall(PetscDTGradedOrderToIndex(dim, degtup, &m1idx));
1078     if (degtup[d] > 0) {
1079       degtup[d]--;
1080       PetscCall(PetscDTGradedOrderToIndex(dim, degtup, &m2idx));
1081       degtup[d]++;
1082     }
1083     degtup[d]++;
1084     for (e = 0, degsum = 0; e < d; e++) degsum += degtup[e];
1085     alpha = 2 * degsum + d;
1086     PetscDTJacobiRecurrence_Internal(n,alpha,0.,cnm1,cnm1x,cnm2);
1087 
1088     scales[degidx] = initscale;
1089     for (e = 0, degsum = 0; e < dim; e++) {
1090       PetscInt  f;
1091       PetscReal ealpha;
1092       PetscReal enorm;
1093 
1094       ealpha = 2 * degsum + e;
1095       for (f = 0; f < degsum; f++) scales[degidx] *= 2.;
1096       PetscCall(PetscDTJacobiNorm(ealpha,0.,degtup[e],&enorm));
1097       scales[degidx] /= enorm;
1098       degsum += degtup[e];
1099     }
1100 
1101     for (pt = 0; pt < npoints; pt++) {
1102       /* compute the multipliers */
1103       PetscReal thetanm1, thetanm1x, thetanm2;
1104 
1105       thetanm1x = dim - (d+1) + 2.*points[pt * dim + d];
1106       for (e = d+1; e < dim; e++) thetanm1x += points[pt * dim + e];
1107       thetanm1x *= 0.5;
1108       thetanm1 = (2. - (dim-(d+1)));
1109       for (e = d+1; e < dim; e++) thetanm1 -= points[pt * dim + e];
1110       thetanm1 *= 0.5;
1111       thetanm2 = thetanm1 * thetanm1;
1112 
1113       for (kidx = 0; kidx < Nk; kidx++) {
1114         PetscInt f;
1115 
1116         PetscCall(PetscDTIndexToGradedOrder(dim, kidx, ktup));
1117         /* first sum in the same derivative terms */
1118         p[(degidx * Nk + kidx) * npoints + pt] = (cnm1 * thetanm1 + cnm1x * thetanm1x) * p[(m1idx * Nk + kidx) * npoints + pt];
1119         if (m2idx >= 0) {
1120           p[(degidx * Nk + kidx) * npoints + pt] -= cnm2 * thetanm2 * p[(m2idx * Nk + kidx) * npoints + pt];
1121         }
1122 
1123         for (f = d; f < dim; f++) {
1124           PetscInt km1idx, mplty = ktup[f];
1125 
1126           if (!mplty) continue;
1127           ktup[f]--;
1128           PetscCall(PetscDTGradedOrderToIndex(dim, ktup, &km1idx));
1129 
1130           /* the derivative of  cnm1x * thetanm1x  wrt x variable f is 0.5 * cnm1x if f > d otherwise it is cnm1x */
1131           /* the derivative of  cnm1  * thetanm1   wrt x variable f is 0 if f == d, otherwise it is -0.5 * cnm1 */
1132           /* the derivative of -cnm2  * thetanm2   wrt x variable f is 0 if f == d, otherwise it is cnm2 * thetanm1 */
1133           if (f > d) {
1134             PetscInt f2;
1135 
1136             p[(degidx * Nk + kidx) * npoints + pt] += mplty * 0.5 * (cnm1x - cnm1) * p[(m1idx * Nk + km1idx) * npoints + pt];
1137             if (m2idx >= 0) {
1138               p[(degidx * Nk + kidx) * npoints + pt] += mplty * cnm2 * thetanm1 * p[(m2idx * Nk + km1idx) * npoints + pt];
1139               /* second derivatives of -cnm2  * thetanm2   wrt x variable f,f2 is like - 0.5 * cnm2 */
1140               for (f2 = f; f2 < dim; f2++) {
1141                 PetscInt km2idx, mplty2 = ktup[f2];
1142                 PetscInt factor;
1143 
1144                 if (!mplty2) continue;
1145                 ktup[f2]--;
1146                 PetscCall(PetscDTGradedOrderToIndex(dim, ktup, &km2idx));
1147 
1148                 factor = mplty * mplty2;
1149                 if (f == f2) factor /= 2;
1150                 p[(degidx * Nk + kidx) * npoints + pt] -= 0.5 * factor * cnm2 * p[(m2idx * Nk + km2idx) * npoints + pt];
1151                 ktup[f2]++;
1152               }
1153             }
1154           } else {
1155             p[(degidx * Nk + kidx) * npoints + pt] += mplty * cnm1x * p[(m1idx * Nk + km1idx) * npoints + pt];
1156           }
1157           ktup[f]++;
1158         }
1159       }
1160     }
1161   }
1162   for (degidx = 0; degidx < Ndeg; degidx++) {
1163     PetscReal scale = scales[degidx];
1164     PetscInt i;
1165 
1166     for (i = 0; i < Nk * npoints; i++) p[degidx*Nk*npoints + i] *= scale;
1167   }
1168   PetscCall(PetscFree(scales));
1169   PetscCall(PetscFree2(degtup, ktup));
1170   PetscFunctionReturn(0);
1171 }
1172 
1173 /*@
1174   PetscDTPTrimmedSize - The size of the trimmed polynomial space of k-forms with a given degree and form degree,
1175   which can be evaluated in PetscDTPTrimmedEvalJet().
1176 
1177   Input Parameters:
1178 + dim - the number of variables in the multivariate polynomials
1179 . degree - the degree (sum of degrees on the variables in a monomial) of the trimmed polynomial space.
1180 - formDegree - the degree of the form
1181 
1182   Output Parameters:
1183 - size - The number ((dim + degree) choose (dim + formDegree)) x ((degree + formDegree - 1) choose (formDegree))
1184 
1185   Level: advanced
1186 
1187 .seealso: `PetscDTPTrimmedEvalJet()`
1188 @*/
1189 PetscErrorCode PetscDTPTrimmedSize(PetscInt dim, PetscInt degree, PetscInt formDegree, PetscInt *size)
1190 {
1191   PetscInt       Nrk, Nbpt; // number of trimmed polynomials
1192 
1193   PetscFunctionBegin;
1194   formDegree = PetscAbsInt(formDegree);
1195   PetscCall(PetscDTBinomialInt(degree + dim, degree + formDegree, &Nbpt));
1196   PetscCall(PetscDTBinomialInt(degree + formDegree - 1, formDegree, &Nrk));
1197   Nbpt *= Nrk;
1198   *size = Nbpt;
1199   PetscFunctionReturn(0);
1200 }
1201 
1202 /* there was a reference implementation based on section 4.4 of Arnold, Falk & Winther (acta numerica, 2006), but it
1203  * was inferior to this implementation */
1204 static PetscErrorCode PetscDTPTrimmedEvalJet_Internal(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt formDegree, PetscInt jetDegree, PetscReal p[])
1205 {
1206   PetscInt       formDegreeOrig = formDegree;
1207   PetscBool      formNegative = (formDegreeOrig < 0) ? PETSC_TRUE : PETSC_FALSE;
1208 
1209   PetscFunctionBegin;
1210   formDegree = PetscAbsInt(formDegreeOrig);
1211   if (formDegree == 0) {
1212     PetscCall(PetscDTPKDEvalJet(dim, npoints, points, degree, jetDegree, p));
1213     PetscFunctionReturn(0);
1214   }
1215   if (formDegree == dim) {
1216     PetscCall(PetscDTPKDEvalJet(dim, npoints, points, degree - 1, jetDegree, p));
1217     PetscFunctionReturn(0);
1218   }
1219   PetscInt Nbpt;
1220   PetscCall(PetscDTPTrimmedSize(dim, degree, formDegree, &Nbpt));
1221   PetscInt Nf;
1222   PetscCall(PetscDTBinomialInt(dim, formDegree, &Nf));
1223   PetscInt Nk;
1224   PetscCall(PetscDTBinomialInt(dim + jetDegree, dim, &Nk));
1225   PetscCall(PetscArrayzero(p, Nbpt * Nf * Nk * npoints));
1226 
1227   PetscInt Nbpm1; // number of scalar polynomials up to degree - 1;
1228   PetscCall(PetscDTBinomialInt(dim + degree - 1, dim, &Nbpm1));
1229   PetscReal *p_scalar;
1230   PetscCall(PetscMalloc1(Nbpm1 * Nk * npoints, &p_scalar));
1231   PetscCall(PetscDTPKDEvalJet(dim, npoints, points, degree - 1, jetDegree, p_scalar));
1232   PetscInt total = 0;
1233   // First add the full polynomials up to degree - 1 into the basis: take the scalar
1234   // and copy one for each form component
1235   for (PetscInt i = 0; i < Nbpm1; i++) {
1236     const PetscReal *src = &p_scalar[i * Nk * npoints];
1237     for (PetscInt f = 0; f < Nf; f++) {
1238       PetscReal *dest = &p[(total++ * Nf + f) * Nk * npoints];
1239       PetscCall(PetscArraycpy(dest, src, Nk * npoints));
1240     }
1241   }
1242   PetscInt *form_atoms;
1243   PetscCall(PetscMalloc1(formDegree + 1, &form_atoms));
1244   // construct the interior product pattern
1245   PetscInt (*pattern)[3];
1246   PetscInt Nf1; // number of formDegree + 1 forms
1247   PetscCall(PetscDTBinomialInt(dim, formDegree + 1, &Nf1));
1248   PetscInt nnz = Nf1 * (formDegree+1);
1249   PetscCall(PetscMalloc1(Nf1 * (formDegree+1), &pattern));
1250   PetscCall(PetscDTAltVInteriorPattern(dim, formDegree+1, pattern));
1251   PetscReal centroid = (1. - dim) / (dim + 1.);
1252   PetscInt *deriv;
1253   PetscCall(PetscMalloc1(dim, &deriv));
1254   for (PetscInt d = dim; d >= formDegree + 1; d--) {
1255     PetscInt Nfd1; // number of formDegree + 1 forms in dimension d that include dx_0
1256                    // (equal to the number of formDegree forms in dimension d-1)
1257     PetscCall(PetscDTBinomialInt(d - 1, formDegree, &Nfd1));
1258     // The number of homogeneous (degree-1) scalar polynomials in d variables
1259     PetscInt Nh;
1260     PetscCall(PetscDTBinomialInt(d - 1 + degree - 1, d - 1, &Nh));
1261     const PetscReal *h_scalar = &p_scalar[(Nbpm1 - Nh) * Nk * npoints];
1262     for (PetscInt b = 0; b < Nh; b++) {
1263       const PetscReal *h_s = &h_scalar[b * Nk * npoints];
1264       for (PetscInt f = 0; f < Nfd1; f++) {
1265         // construct all formDegree+1 forms that start with dx_(dim - d) /\ ...
1266         form_atoms[0] = dim - d;
1267         PetscCall(PetscDTEnumSubset(d-1, formDegree, f, &form_atoms[1]));
1268         for (PetscInt i = 0; i < formDegree; i++) {
1269           form_atoms[1+i] += form_atoms[0] + 1;
1270         }
1271         PetscInt f_ind; // index of the resulting form
1272         PetscCall(PetscDTSubsetIndex(dim, formDegree + 1, form_atoms, &f_ind));
1273         PetscReal *p_f = &p[total++ * Nf * Nk * npoints];
1274         for (PetscInt nz = 0; nz < nnz; nz++) {
1275           PetscInt i = pattern[nz][0]; // formDegree component
1276           PetscInt j = pattern[nz][1]; // (formDegree + 1) component
1277           PetscInt v = pattern[nz][2]; // coordinate component
1278           PetscReal scale = v < 0 ? -1. : 1.;
1279 
1280           i = formNegative ? (Nf - 1 - i) : i;
1281           scale = (formNegative && (i & 1)) ? -scale : scale;
1282           v = v < 0 ? -(v + 1) : v;
1283           if (j != f_ind) {
1284             continue;
1285           }
1286           PetscReal *p_i = &p_f[i * Nk * npoints];
1287           for (PetscInt jet = 0; jet < Nk; jet++) {
1288             const PetscReal *h_jet = &h_s[jet * npoints];
1289             PetscReal *p_jet = &p_i[jet * npoints];
1290 
1291             for (PetscInt pt = 0; pt < npoints; pt++) {
1292               p_jet[pt] += scale * h_jet[pt] * (points[pt * dim + v] - centroid);
1293             }
1294             PetscCall(PetscDTIndexToGradedOrder(dim, jet, deriv));
1295             deriv[v]++;
1296             PetscReal mult = deriv[v];
1297             PetscInt l;
1298             PetscCall(PetscDTGradedOrderToIndex(dim, deriv, &l));
1299             if (l >= Nk) {
1300               continue;
1301             }
1302             p_jet = &p_i[l * npoints];
1303             for (PetscInt pt = 0; pt < npoints; pt++) {
1304               p_jet[pt] += scale * mult * h_jet[pt];
1305             }
1306             deriv[v]--;
1307           }
1308         }
1309       }
1310     }
1311   }
1312   PetscCheck(total == Nbpt,PETSC_COMM_SELF, PETSC_ERR_PLIB, "Incorrectly counted P trimmed polynomials");
1313   PetscCall(PetscFree(deriv));
1314   PetscCall(PetscFree(pattern));
1315   PetscCall(PetscFree(form_atoms));
1316   PetscCall(PetscFree(p_scalar));
1317   PetscFunctionReturn(0);
1318 }
1319 
1320 /*@
1321   PetscDTPTrimmedEvalJet - Evaluate the jet (function and derivatives) of a basis of the trimmed polynomial k-forms up to
1322   a given degree.
1323 
1324   Input Parameters:
1325 + dim - the number of variables in the multivariate polynomials
1326 . npoints - the number of points to evaluate the polynomials at
1327 . points - [npoints x dim] array of point coordinates
1328 . degree - the degree (sum of degrees on the variables in a monomial) of the trimmed polynomial space to evaluate.
1329            There are ((dim + degree) choose (dim + formDegree)) x ((degree + formDegree - 1) choose (formDegree)) polynomials in this space.
1330            (You can use PetscDTPTrimmedSize() to compute this size.)
1331 . formDegree - the degree of the form
1332 - jetDegree - the maximum order partial derivative to evaluate in the jet.  There are ((dim + jetDegree) choose dim) partial derivatives
1333               in the jet.  Choosing jetDegree = 0 means to evaluate just the function and no derivatives
1334 
1335   Output Parameters:
1336 - p - an array containing the evaluations of the PKD polynomials' jets on the points.  The size is
1337       PetscDTPTrimmedSize() x ((dim + formDegree) choose dim) x ((dim + k) choose dim) x npoints,
1338       which also describes the order of the dimensions of this
1339       four-dimensional array:
1340         the first (slowest varying) dimension is basis function index;
1341         the second dimension is component of the form;
1342         the third dimension is jet index;
1343         the fourth (fastest varying) dimension is the index of the evaluation point.
1344 
1345   Level: advanced
1346 
1347   Note: The ordering of the basis functions is not graded, so the basis functions are not nested by degree like PetscDTPKDEvalJet().
1348         The basis functions are not an L2-orthonormal basis on any particular domain.
1349 
1350   The implementation is based on the description of the trimmed polynomials up to degree r as
1351   the direct sum of polynomials up to degree (r-1) and the Koszul differential applied to
1352   homogeneous polynomials of degree (r-1).
1353 
1354 .seealso: `PetscDTPKDEvalJet()`, `PetscDTPTrimmedSize()`
1355 @*/
1356 PetscErrorCode PetscDTPTrimmedEvalJet(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt formDegree, PetscInt jetDegree, PetscReal p[])
1357 {
1358   PetscFunctionBegin;
1359   PetscCall(PetscDTPTrimmedEvalJet_Internal(dim, npoints, points, degree, formDegree, jetDegree, p));
1360   PetscFunctionReturn(0);
1361 }
1362 
1363 /* solve the symmetric tridiagonal eigenvalue system, writing the eigenvalues into eigs and the eigenvectors into V
1364  * with lds n; diag and subdiag are overwritten */
1365 static PetscErrorCode PetscDTSymmetricTridiagonalEigensolve(PetscInt n, PetscReal diag[], PetscReal subdiag[],
1366                                                             PetscReal eigs[], PetscScalar V[])
1367 {
1368   char jobz = 'V'; /* eigenvalues and eigenvectors */
1369   char range = 'A'; /* all eigenvalues will be found */
1370   PetscReal VL = 0.; /* ignored because range is 'A' */
1371   PetscReal VU = 0.; /* ignored because range is 'A' */
1372   PetscBLASInt IL = 0; /* ignored because range is 'A' */
1373   PetscBLASInt IU = 0; /* ignored because range is 'A' */
1374   PetscReal abstol = 0.; /* unused */
1375   PetscBLASInt bn, bm, ldz; /* bm will equal bn on exit */
1376   PetscBLASInt *isuppz;
1377   PetscBLASInt lwork, liwork;
1378   PetscReal workquery;
1379   PetscBLASInt  iworkquery;
1380   PetscBLASInt *iwork;
1381   PetscBLASInt info;
1382   PetscReal *work = NULL;
1383 
1384   PetscFunctionBegin;
1385 #if !defined(PETSCDTGAUSSIANQUADRATURE_EIG)
1386   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
1387 #endif
1388   PetscCall(PetscBLASIntCast(n, &bn));
1389   PetscCall(PetscBLASIntCast(n, &ldz));
1390 #if !defined(PETSC_MISSING_LAPACK_STEGR)
1391   PetscCall(PetscMalloc1(2 * n, &isuppz));
1392   lwork = -1;
1393   liwork = -1;
1394   PetscCallBLAS("LAPACKstegr",LAPACKstegr_(&jobz,&range,&bn,diag,subdiag,&VL,&VU,&IL,&IU,&abstol,&bm,eigs,V,&ldz,isuppz,&workquery,&lwork,&iworkquery,&liwork,&info));
1395   PetscCheck(!info,PETSC_COMM_SELF,PETSC_ERR_PLIB,"xSTEGR error");
1396   lwork = (PetscBLASInt) workquery;
1397   liwork = (PetscBLASInt) iworkquery;
1398   PetscCall(PetscMalloc2(lwork, &work, liwork, &iwork));
1399   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
1400   PetscCallBLAS("LAPACKstegr",LAPACKstegr_(&jobz,&range,&bn,diag,subdiag,&VL,&VU,&IL,&IU,&abstol,&bm,eigs,V,&ldz,isuppz,work,&lwork,iwork,&liwork,&info));
1401   PetscCall(PetscFPTrapPop());
1402   PetscCheck(!info,PETSC_COMM_SELF,PETSC_ERR_PLIB,"xSTEGR error");
1403   PetscCall(PetscFree2(work, iwork));
1404   PetscCall(PetscFree(isuppz));
1405 #elif !defined(PETSC_MISSING_LAPACK_STEQR)
1406   jobz = 'I'; /* Compute eigenvalues and eigenvectors of the
1407                  tridiagonal matrix.  Z is initialized to the identity
1408                  matrix. */
1409   PetscCall(PetscMalloc1(PetscMax(1,2*n-2),&work));
1410   PetscCallBLAS("LAPACKsteqr",LAPACKsteqr_("I",&bn,diag,subdiag,V,&ldz,work,&info));
1411   PetscCall(PetscFPTrapPop());
1412   PetscCheck(!info,PETSC_COMM_SELF,PETSC_ERR_PLIB,"xSTEQR error");
1413   PetscCall(PetscFree(work));
1414   PetscCall(PetscArraycpy(eigs,diag,n));
1415 #endif
1416   PetscFunctionReturn(0);
1417 }
1418 
1419 /* Formula for the weights at the endpoints (-1 and 1) of Gauss-Lobatto-Jacobi
1420  * quadrature rules on the interval [-1, 1] */
1421 static PetscErrorCode PetscDTGaussLobattoJacobiEndweights_Internal(PetscInt n, PetscReal alpha, PetscReal beta, PetscReal *leftw, PetscReal *rightw)
1422 {
1423   PetscReal twoab1;
1424   PetscInt  m = n - 2;
1425   PetscReal a = alpha + 1.;
1426   PetscReal b = beta + 1.;
1427   PetscReal gra, grb;
1428 
1429   PetscFunctionBegin;
1430   twoab1 = PetscPowReal(2., a + b - 1.);
1431 #if defined(PETSC_HAVE_LGAMMA)
1432   grb = PetscExpReal(2. * PetscLGamma(b+1.) + PetscLGamma(m+1.) + PetscLGamma(m+a+1.) -
1433                      (PetscLGamma(m+b+1) + PetscLGamma(m+a+b+1.)));
1434   gra = PetscExpReal(2. * PetscLGamma(a+1.) + PetscLGamma(m+1.) + PetscLGamma(m+b+1.) -
1435                      (PetscLGamma(m+a+1) + PetscLGamma(m+a+b+1.)));
1436 #else
1437   {
1438     PetscInt alphai = (PetscInt) alpha;
1439     PetscInt betai = (PetscInt) beta;
1440 
1441     if ((PetscReal) alphai == alpha && (PetscReal) betai == beta) {
1442       PetscReal binom1, binom2;
1443 
1444       PetscCall(PetscDTBinomial(m+b, b, &binom1));
1445       PetscCall(PetscDTBinomial(m+a+b, b, &binom2));
1446       grb = 1./ (binom1 * binom2);
1447       PetscCall(PetscDTBinomial(m+a, a, &binom1));
1448       PetscCall(PetscDTBinomial(m+a+b, a, &binom2));
1449       gra = 1./ (binom1 * binom2);
1450     } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"lgamma() - math routine is unavailable.");
1451   }
1452 #endif
1453   *leftw = twoab1 * grb / b;
1454   *rightw = twoab1 * gra / a;
1455   PetscFunctionReturn(0);
1456 }
1457 
1458 /* Evaluates the nth jacobi polynomial with weight parameters a,b at a point x.
1459    Recurrence relations implemented from the pseudocode given in Karniadakis and Sherwin, Appendix B */
1460 static inline PetscErrorCode PetscDTComputeJacobi(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscReal *P)
1461 {
1462   PetscReal pn1, pn2;
1463   PetscReal cnm1, cnm1x, cnm2;
1464   PetscInt  k;
1465 
1466   PetscFunctionBegin;
1467   if (!n) {*P = 1.0; PetscFunctionReturn(0);}
1468   PetscDTJacobiRecurrence_Internal(1,a,b,cnm1,cnm1x,cnm2);
1469   pn2 = 1.;
1470   pn1 = cnm1 + cnm1x*x;
1471   if (n == 1) {*P = pn1; PetscFunctionReturn(0);}
1472   *P  = 0.0;
1473   for (k = 2; k < n+1; ++k) {
1474     PetscDTJacobiRecurrence_Internal(k,a,b,cnm1,cnm1x,cnm2);
1475 
1476     *P  = (cnm1 + cnm1x*x)*pn1 - cnm2*pn2;
1477     pn2 = pn1;
1478     pn1 = *P;
1479   }
1480   PetscFunctionReturn(0);
1481 }
1482 
1483 /* Evaluates the first derivative of P_{n}^{a,b} at a point x. */
1484 static inline PetscErrorCode PetscDTComputeJacobiDerivative(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscInt k, PetscReal *P)
1485 {
1486   PetscReal      nP;
1487   PetscInt       i;
1488 
1489   PetscFunctionBegin;
1490   *P = 0.0;
1491   if (k > n) PetscFunctionReturn(0);
1492   PetscCall(PetscDTComputeJacobi(a+k, b+k, n-k, x, &nP));
1493   for (i = 0; i < k; i++) nP *= (a + b + n + 1. + i) * 0.5;
1494   *P = nP;
1495   PetscFunctionReturn(0);
1496 }
1497 
1498 static PetscErrorCode PetscDTGaussJacobiQuadrature_Newton_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal x[], PetscReal w[])
1499 {
1500   PetscInt       maxIter = 100;
1501   PetscReal      eps     = PetscExpReal(0.75 * PetscLogReal(PETSC_MACHINE_EPSILON));
1502   PetscReal      a1, a6, gf;
1503   PetscInt       k;
1504 
1505   PetscFunctionBegin;
1506 
1507   a1 = PetscPowReal(2.0, a+b+1);
1508 #if defined(PETSC_HAVE_LGAMMA)
1509   {
1510     PetscReal a2, a3, a4, a5;
1511     a2 = PetscLGamma(a + npoints + 1);
1512     a3 = PetscLGamma(b + npoints + 1);
1513     a4 = PetscLGamma(a + b + npoints + 1);
1514     a5 = PetscLGamma(npoints + 1);
1515     gf = PetscExpReal(a2 + a3 - (a4 + a5));
1516   }
1517 #else
1518   {
1519     PetscInt ia, ib;
1520 
1521     ia = (PetscInt) a;
1522     ib = (PetscInt) b;
1523     gf = 1.;
1524     if (ia == a && ia >= 0) { /* compute ratio of rising factorals wrt a */
1525       for (k = 0; k < ia; k++) gf *= (npoints + 1. + k) / (npoints + b + 1. + k);
1526     } else if (b == b && ib >= 0) { /* compute ratio of rising factorials wrt b */
1527       for (k = 0; k < ib; k++) gf *= (npoints + 1. + k) / (npoints + a + 1. + k);
1528     } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"lgamma() - math routine is unavailable.");
1529   }
1530 #endif
1531 
1532   a6   = a1 * gf;
1533   /* Computes the m roots of P_{m}^{a,b} on [-1,1] by Newton's method with Chebyshev points as initial guesses.
1534    Algorithm implemented from the pseudocode given by Karniadakis and Sherwin and Python in FIAT */
1535   for (k = 0; k < npoints; ++k) {
1536     PetscReal r = PetscCosReal(PETSC_PI * (1. - (4.*k + 3. + 2.*b) / (4.*npoints + 2.*(a + b + 1.)))), dP;
1537     PetscInt  j;
1538 
1539     if (k > 0) r = 0.5 * (r + x[k-1]);
1540     for (j = 0; j < maxIter; ++j) {
1541       PetscReal s = 0.0, delta, f, fp;
1542       PetscInt  i;
1543 
1544       for (i = 0; i < k; ++i) s = s + 1.0 / (r - x[i]);
1545       PetscCall(PetscDTComputeJacobi(a, b, npoints, r, &f));
1546       PetscCall(PetscDTComputeJacobiDerivative(a, b, npoints, r, 1, &fp));
1547       delta = f / (fp - f * s);
1548       r     = r - delta;
1549       if (PetscAbsReal(delta) < eps) break;
1550     }
1551     x[k] = r;
1552     PetscCall(PetscDTComputeJacobiDerivative(a, b, npoints, x[k], 1, &dP));
1553     w[k] = a6 / (1.0 - PetscSqr(x[k])) / PetscSqr(dP);
1554   }
1555   PetscFunctionReturn(0);
1556 }
1557 
1558 /* Compute the diagonals of the Jacobi matrix used in Golub & Welsch algorithms for Gauss-Jacobi
1559  * quadrature weight calculations on [-1,1] for exponents (1. + x)^a (1.-x)^b */
1560 static PetscErrorCode PetscDTJacobiMatrix_Internal(PetscInt nPoints, PetscReal a, PetscReal b, PetscReal *d, PetscReal *s)
1561 {
1562   PetscInt       i;
1563 
1564   PetscFunctionBegin;
1565   for (i = 0; i < nPoints; i++) {
1566     PetscReal A, B, C;
1567 
1568     PetscDTJacobiRecurrence_Internal(i+1,a,b,A,B,C);
1569     d[i] = -A / B;
1570     if (i) s[i-1] *= C / B;
1571     if (i < nPoints - 1) s[i] = 1. / B;
1572   }
1573   PetscFunctionReturn(0);
1574 }
1575 
1576 static PetscErrorCode PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal *x, PetscReal *w)
1577 {
1578   PetscReal mu0;
1579   PetscReal ga, gb, gab;
1580   PetscInt i;
1581 
1582   PetscFunctionBegin;
1583   PetscCall(PetscCitationsRegister(GolubWelschCitation, &GolubWelschCite));
1584 
1585 #if defined(PETSC_HAVE_TGAMMA)
1586   ga  = PetscTGamma(a + 1);
1587   gb  = PetscTGamma(b + 1);
1588   gab = PetscTGamma(a + b + 2);
1589 #else
1590   {
1591     PetscInt ia, ib;
1592 
1593     ia = (PetscInt) a;
1594     ib = (PetscInt) b;
1595     if (ia == a && ib == b && ia + 1 > 0 && ib + 1 > 0 && ia + ib + 2 > 0) { /* All gamma(x) terms are (x-1)! terms */
1596       PetscCall(PetscDTFactorial(ia, &ga));
1597       PetscCall(PetscDTFactorial(ib, &gb));
1598       PetscCall(PetscDTFactorial(ia + ib + 1, &gb));
1599     } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"tgamma() - math routine is unavailable.");
1600   }
1601 #endif
1602   mu0 = PetscPowReal(2.,a + b + 1.) * ga * gb / gab;
1603 
1604 #if defined(PETSCDTGAUSSIANQUADRATURE_EIG)
1605   {
1606     PetscReal *diag, *subdiag;
1607     PetscScalar *V;
1608 
1609     PetscCall(PetscMalloc2(npoints, &diag, npoints, &subdiag));
1610     PetscCall(PetscMalloc1(npoints*npoints, &V));
1611     PetscCall(PetscDTJacobiMatrix_Internal(npoints, a, b, diag, subdiag));
1612     for (i = 0; i < npoints - 1; i++) subdiag[i] = PetscSqrtReal(subdiag[i]);
1613     PetscCall(PetscDTSymmetricTridiagonalEigensolve(npoints, diag, subdiag, x, V));
1614     for (i = 0; i < npoints; i++) w[i] = PetscSqr(PetscRealPart(V[i * npoints])) * mu0;
1615     PetscCall(PetscFree(V));
1616     PetscCall(PetscFree2(diag, subdiag));
1617   }
1618 #else
1619   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
1620 #endif
1621   { /* As of March 2, 2020, The Sun Performance Library breaks the LAPACK contract for xstegr and xsteqr: the
1622        eigenvalues are not guaranteed to be in ascending order.  So we heave a passive aggressive sigh and check that
1623        the eigenvalues are sorted */
1624     PetscBool sorted;
1625 
1626     PetscCall(PetscSortedReal(npoints, x, &sorted));
1627     if (!sorted) {
1628       PetscInt *order, i;
1629       PetscReal *tmp;
1630 
1631       PetscCall(PetscMalloc2(npoints, &order, npoints, &tmp));
1632       for (i = 0; i < npoints; i++) order[i] = i;
1633       PetscCall(PetscSortRealWithPermutation(npoints, x, order));
1634       PetscCall(PetscArraycpy(tmp, x, npoints));
1635       for (i = 0; i < npoints; i++) x[i] = tmp[order[i]];
1636       PetscCall(PetscArraycpy(tmp, w, npoints));
1637       for (i = 0; i < npoints; i++) w[i] = tmp[order[i]];
1638       PetscCall(PetscFree2(order, tmp));
1639     }
1640   }
1641   PetscFunctionReturn(0);
1642 }
1643 
1644 static PetscErrorCode PetscDTGaussJacobiQuadrature_Internal(PetscInt npoints,PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
1645 {
1646   PetscFunctionBegin;
1647   PetscCheck(npoints >= 1,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Number of points must be positive");
1648   /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
1649   PetscCheck(alpha > -1.,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"alpha must be > -1.");
1650   PetscCheck(beta > -1.,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"beta must be > -1.");
1651 
1652   if (newton) PetscCall(PetscDTGaussJacobiQuadrature_Newton_Internal(npoints, alpha, beta, x, w));
1653   else PetscCall(PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(npoints, alpha, beta, x, w));
1654   if (alpha == beta) { /* symmetrize */
1655     PetscInt i;
1656     for (i = 0; i < (npoints + 1) / 2; i++) {
1657       PetscInt  j  = npoints - 1 - i;
1658       PetscReal xi = x[i];
1659       PetscReal xj = x[j];
1660       PetscReal wi = w[i];
1661       PetscReal wj = w[j];
1662 
1663       x[i] = (xi - xj) / 2.;
1664       x[j] = (xj - xi) / 2.;
1665       w[i] = w[j] = (wi + wj) / 2.;
1666     }
1667   }
1668   PetscFunctionReturn(0);
1669 }
1670 
1671 /*@
1672   PetscDTGaussJacobiQuadrature - quadrature for the interval [a, b] with the weight function
1673   $(x-a)^\alpha (x-b)^\beta$.
1674 
1675   Not collective
1676 
1677   Input Parameters:
1678 + npoints - the number of points in the quadrature rule
1679 . a - the left endpoint of the interval
1680 . b - the right endpoint of the interval
1681 . alpha - the left exponent
1682 - beta - the right exponent
1683 
1684   Output Parameters:
1685 + x - array of length npoints, the locations of the quadrature points
1686 - w - array of length npoints, the weights of the quadrature points
1687 
1688   Level: intermediate
1689 
1690   Note: this quadrature rule is exact for polynomials up to degree 2*npoints - 1.
1691 @*/
1692 PetscErrorCode PetscDTGaussJacobiQuadrature(PetscInt npoints,PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
1693 {
1694   PetscInt       i;
1695 
1696   PetscFunctionBegin;
1697   PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal));
1698   if (a != -1. || b != 1.) { /* shift */
1699     for (i = 0; i < npoints; i++) {
1700       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1701       w[i] *= (b - a) / 2.;
1702     }
1703   }
1704   PetscFunctionReturn(0);
1705 }
1706 
1707 static PetscErrorCode PetscDTGaussLobattoJacobiQuadrature_Internal(PetscInt npoints,PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
1708 {
1709   PetscInt       i;
1710 
1711   PetscFunctionBegin;
1712   PetscCheck(npoints >= 2,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Number of points must be positive");
1713   /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
1714   PetscCheck(alpha > -1.,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"alpha must be > -1.");
1715   PetscCheck(beta > -1.,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"beta must be > -1.");
1716 
1717   x[0] = -1.;
1718   x[npoints-1] = 1.;
1719   if (npoints > 2) {
1720     PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints-2, alpha+1., beta+1., &x[1], &w[1], newton));
1721   }
1722   for (i = 1; i < npoints - 1; i++) {
1723     w[i] /= (1. - x[i]*x[i]);
1724   }
1725   PetscCall(PetscDTGaussLobattoJacobiEndweights_Internal(npoints, alpha, beta, &w[0], &w[npoints-1]));
1726   PetscFunctionReturn(0);
1727 }
1728 
1729 /*@
1730   PetscDTGaussLobattoJacobiQuadrature - quadrature for the interval [a, b] with the weight function
1731   $(x-a)^\alpha (x-b)^\beta$, with endpoints a and b included as quadrature points.
1732 
1733   Not collective
1734 
1735   Input Parameters:
1736 + npoints - the number of points in the quadrature rule
1737 . a - the left endpoint of the interval
1738 . b - the right endpoint of the interval
1739 . alpha - the left exponent
1740 - beta - the right exponent
1741 
1742   Output Parameters:
1743 + x - array of length npoints, the locations of the quadrature points
1744 - w - array of length npoints, the weights of the quadrature points
1745 
1746   Level: intermediate
1747 
1748   Note: this quadrature rule is exact for polynomials up to degree 2*npoints - 3.
1749 @*/
1750 PetscErrorCode PetscDTGaussLobattoJacobiQuadrature(PetscInt npoints,PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
1751 {
1752   PetscInt       i;
1753 
1754   PetscFunctionBegin;
1755   PetscCall(PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal));
1756   if (a != -1. || b != 1.) { /* shift */
1757     for (i = 0; i < npoints; i++) {
1758       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1759       w[i] *= (b - a) / 2.;
1760     }
1761   }
1762   PetscFunctionReturn(0);
1763 }
1764 
1765 /*@
1766    PetscDTGaussQuadrature - create Gauss-Legendre quadrature
1767 
1768    Not Collective
1769 
1770    Input Parameters:
1771 +  npoints - number of points
1772 .  a - left end of interval (often-1)
1773 -  b - right end of interval (often +1)
1774 
1775    Output Parameters:
1776 +  x - quadrature points
1777 -  w - quadrature weights
1778 
1779    Level: intermediate
1780 
1781    References:
1782 .  * - Golub and Welsch, Calculation of Quadrature Rules, Math. Comp. 23(106), 1969.
1783 
1784 .seealso: `PetscDTLegendreEval()`
1785 @*/
1786 PetscErrorCode PetscDTGaussQuadrature(PetscInt npoints,PetscReal a,PetscReal b,PetscReal *x,PetscReal *w)
1787 {
1788   PetscInt       i;
1789 
1790   PetscFunctionBegin;
1791   PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints, 0., 0., x, w, PetscDTGaussQuadratureNewton_Internal));
1792   if (a != -1. || b != 1.) { /* shift */
1793     for (i = 0; i < npoints; i++) {
1794       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1795       w[i] *= (b - a) / 2.;
1796     }
1797   }
1798   PetscFunctionReturn(0);
1799 }
1800 
1801 /*@C
1802    PetscDTGaussLobattoLegendreQuadrature - creates a set of the locations and weights of the Gauss-Lobatto-Legendre
1803                       nodes of a given size on the domain [-1,1]
1804 
1805    Not Collective
1806 
1807    Input Parameters:
1808 +  n - number of grid nodes
1809 -  type - PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA or PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON
1810 
1811    Output Parameters:
1812 +  x - quadrature points
1813 -  w - quadrature weights
1814 
1815    Notes:
1816     For n > 30  the Newton approach computes duplicate (incorrect) values for some nodes because the initial guess is apparently not
1817           close enough to the desired solution
1818 
1819    These are useful for implementing spectral methods based on Gauss-Lobatto-Legendre (GLL) nodes
1820 
1821    See  https://epubs.siam.org/doi/abs/10.1137/110855442  https://epubs.siam.org/doi/abs/10.1137/120889873 for better ways to compute GLL nodes
1822 
1823    Level: intermediate
1824 
1825 .seealso: `PetscDTGaussQuadrature()`
1826 
1827 @*/
1828 PetscErrorCode PetscDTGaussLobattoLegendreQuadrature(PetscInt npoints,PetscGaussLobattoLegendreCreateType type,PetscReal *x,PetscReal *w)
1829 {
1830   PetscBool      newton;
1831 
1832   PetscFunctionBegin;
1833   PetscCheck(npoints >= 2,PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Must provide at least 2 grid points per element");
1834   newton = (PetscBool) (type == PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON);
1835   PetscCall(PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, 0., 0., x, w, newton));
1836   PetscFunctionReturn(0);
1837 }
1838 
1839 /*@
1840   PetscDTGaussTensorQuadrature - creates a tensor-product Gauss quadrature
1841 
1842   Not Collective
1843 
1844   Input Parameters:
1845 + dim     - The spatial dimension
1846 . Nc      - The number of components
1847 . npoints - number of points in one dimension
1848 . a       - left end of interval (often-1)
1849 - b       - right end of interval (often +1)
1850 
1851   Output Parameter:
1852 . q - A PetscQuadrature object
1853 
1854   Level: intermediate
1855 
1856 .seealso: `PetscDTGaussQuadrature()`, `PetscDTLegendreEval()`
1857 @*/
1858 PetscErrorCode PetscDTGaussTensorQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
1859 {
1860   PetscInt       totpoints = dim > 1 ? dim > 2 ? npoints*PetscSqr(npoints) : PetscSqr(npoints) : npoints, i, j, k, c;
1861   PetscReal     *x, *w, *xw, *ww;
1862 
1863   PetscFunctionBegin;
1864   PetscCall(PetscMalloc1(totpoints*dim,&x));
1865   PetscCall(PetscMalloc1(totpoints*Nc,&w));
1866   /* Set up the Golub-Welsch system */
1867   switch (dim) {
1868   case 0:
1869     PetscCall(PetscFree(x));
1870     PetscCall(PetscFree(w));
1871     PetscCall(PetscMalloc1(1, &x));
1872     PetscCall(PetscMalloc1(Nc, &w));
1873     x[0] = 0.0;
1874     for (c = 0; c < Nc; ++c) w[c] = 1.0;
1875     break;
1876   case 1:
1877     PetscCall(PetscMalloc1(npoints,&ww));
1878     PetscCall(PetscDTGaussQuadrature(npoints, a, b, x, ww));
1879     for (i = 0; i < npoints; ++i) for (c = 0; c < Nc; ++c) w[i*Nc+c] = ww[i];
1880     PetscCall(PetscFree(ww));
1881     break;
1882   case 2:
1883     PetscCall(PetscMalloc2(npoints,&xw,npoints,&ww));
1884     PetscCall(PetscDTGaussQuadrature(npoints, a, b, xw, ww));
1885     for (i = 0; i < npoints; ++i) {
1886       for (j = 0; j < npoints; ++j) {
1887         x[(i*npoints+j)*dim+0] = xw[i];
1888         x[(i*npoints+j)*dim+1] = xw[j];
1889         for (c = 0; c < Nc; ++c) w[(i*npoints+j)*Nc+c] = ww[i] * ww[j];
1890       }
1891     }
1892     PetscCall(PetscFree2(xw,ww));
1893     break;
1894   case 3:
1895     PetscCall(PetscMalloc2(npoints,&xw,npoints,&ww));
1896     PetscCall(PetscDTGaussQuadrature(npoints, a, b, xw, ww));
1897     for (i = 0; i < npoints; ++i) {
1898       for (j = 0; j < npoints; ++j) {
1899         for (k = 0; k < npoints; ++k) {
1900           x[((i*npoints+j)*npoints+k)*dim+0] = xw[i];
1901           x[((i*npoints+j)*npoints+k)*dim+1] = xw[j];
1902           x[((i*npoints+j)*npoints+k)*dim+2] = xw[k];
1903           for (c = 0; c < Nc; ++c) w[((i*npoints+j)*npoints+k)*Nc+c] = ww[i] * ww[j] * ww[k];
1904         }
1905       }
1906     }
1907     PetscCall(PetscFree2(xw,ww));
1908     break;
1909   default:
1910     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %" PetscInt_FMT, dim);
1911   }
1912   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
1913   PetscCall(PetscQuadratureSetOrder(*q, 2*npoints-1));
1914   PetscCall(PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w));
1915   PetscCall(PetscObjectChangeTypeName((PetscObject)*q,"GaussTensor"));
1916   PetscFunctionReturn(0);
1917 }
1918 
1919 /*@
1920   PetscDTStroudConicalQuadrature - create Stroud conical quadrature for a simplex
1921 
1922   Not Collective
1923 
1924   Input Parameters:
1925 + dim     - The simplex dimension
1926 . Nc      - The number of components
1927 . npoints - The number of points in one dimension
1928 . a       - left end of interval (often-1)
1929 - b       - right end of interval (often +1)
1930 
1931   Output Parameter:
1932 . q - A PetscQuadrature object
1933 
1934   Level: intermediate
1935 
1936   References:
1937 . * - Karniadakis and Sherwin.  FIAT
1938 
1939   Note: For dim == 1, this is Gauss-Legendre quadrature
1940 
1941 .seealso: `PetscDTGaussTensorQuadrature()`, `PetscDTGaussQuadrature()`
1942 @*/
1943 PetscErrorCode PetscDTStroudConicalQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
1944 {
1945   PetscInt       totprev, totrem;
1946   PetscInt       totpoints;
1947   PetscReal     *p1, *w1;
1948   PetscReal     *x, *w;
1949   PetscInt       i, j, k, l, m, pt, c;
1950 
1951   PetscFunctionBegin;
1952   PetscCheck(!(a != -1.0) && !(b != 1.0),PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must use default internal right now");
1953   totpoints = 1;
1954   for (i = 0, totpoints = 1; i < dim; i++) totpoints *= npoints;
1955   PetscCall(PetscMalloc1(totpoints*dim, &x));
1956   PetscCall(PetscMalloc1(totpoints*Nc, &w));
1957   PetscCall(PetscMalloc2(npoints, &p1, npoints, &w1));
1958   for (i = 0; i < totpoints*Nc; i++) w[i] = 1.;
1959   for (i = 0, totprev = 1, totrem = totpoints / npoints; i < dim; i++) {
1960     PetscReal mul;
1961 
1962     mul = PetscPowReal(2.,-i);
1963     PetscCall(PetscDTGaussJacobiQuadrature(npoints, -1., 1., i, 0.0, p1, w1));
1964     for (pt = 0, l = 0; l < totprev; l++) {
1965       for (j = 0; j < npoints; j++) {
1966         for (m = 0; m < totrem; m++, pt++) {
1967           for (k = 0; k < i; k++) x[pt*dim+k] = (x[pt*dim+k]+1.)*(1.-p1[j])*0.5 - 1.;
1968           x[pt * dim + i] = p1[j];
1969           for (c = 0; c < Nc; c++) w[pt*Nc + c] *= mul * w1[j];
1970         }
1971       }
1972     }
1973     totprev *= npoints;
1974     totrem /= npoints;
1975   }
1976   PetscCall(PetscFree2(p1, w1));
1977   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
1978   PetscCall(PetscQuadratureSetOrder(*q, 2*npoints-1));
1979   PetscCall(PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w));
1980   PetscCall(PetscObjectChangeTypeName((PetscObject)*q,"StroudConical"));
1981   PetscFunctionReturn(0);
1982 }
1983 
1984 static PetscBool MinSymTriQuadCite = PETSC_FALSE;
1985 const char       MinSymTriQuadCitation[] =
1986 "@article{WitherdenVincent2015,\n"
1987 "  title = {On the identification of symmetric quadrature rules for finite element methods},\n"
1988 "  journal = {Computers & Mathematics with Applications},\n"
1989 "  volume = {69},\n"
1990 "  number = {10},\n"
1991 "  pages = {1232-1241},\n"
1992 "  year = {2015},\n"
1993 "  issn = {0898-1221},\n"
1994 "  doi = {10.1016/j.camwa.2015.03.017},\n"
1995 "  url = {https://www.sciencedirect.com/science/article/pii/S0898122115001224},\n"
1996 "  author = {F.D. Witherden and P.E. Vincent},\n"
1997 "}\n";
1998 
1999 #include "petscdttriquadrules.h"
2000 
2001 static PetscBool MinSymTetQuadCite = PETSC_FALSE;
2002 const char       MinSymTetQuadCitation[] =
2003   "@article{JaskowiecSukumar2021\n"
2004   "  author = {Jaskowiec, Jan and Sukumar, N.},\n"
2005   "  title = {High-order symmetric cubature rules for tetrahedra and pyramids},\n"
2006   "  journal = {International Journal for Numerical Methods in Engineering},\n"
2007   "  volume = {122},\n"
2008   "  number = {1},\n"
2009   "  pages = {148-171},\n"
2010   "  doi = {10.1002/nme.6528},\n"
2011   "  url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/nme.6528},\n"
2012   "  eprint = {https://onlinelibrary.wiley.com/doi/pdf/10.1002/nme.6528},\n"
2013   "  year = {2021}\n"
2014   "}\n";
2015 
2016 #include "petscdttetquadrules.h"
2017 
2018 // https://en.wikipedia.org/wiki/Partition_(number_theory)
2019 static PetscErrorCode PetscDTPartitionNumber(PetscInt n, PetscInt *p)
2020 {
2021   // sequence A000041 in the OEIS
2022   const PetscInt partition[] = {1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42, 56, 77, 101, 135, 176, 231, 297, 385, 490, 627, 792, 1002, 1255, 1575, 1958, 2436, 3010, 3718, 4565, 5604};
2023   PetscInt tabulated_max = PETSC_STATIC_ARRAY_LENGTH(partition) - 1;
2024 
2025   PetscFunctionBegin;
2026   PetscCheck(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Partition number not defined for negative number %" PetscInt_FMT, n);
2027   // not implementing the pentagonal number recurrence, we don't need partition numbers for n that high
2028   PetscCheck(n <= tabulated_max, PETSC_COMM_SELF, PETSC_ERR_SUP, "Partition numbers only tabulated up to %" PetscInt_FMT", not computed for %" PetscInt_FMT, tabulated_max, n);
2029   *p = partition[n];
2030   PetscFunctionReturn(0);
2031 }
2032 
2033 /*@
2034   PetscDTSimplexQuadrature - Create a quadrature rule for a simplex that exactly integrates polynomials up to a given degree.
2035 
2036   Not Collective
2037 
2038   Input Parameters:
2039 + dim     - The spatial dimension of the simplex (1 = segment, 2 = triangle, 3 = tetrahedron)
2040 . degree  - The largest polynomial degree that is required to be integrated exactly
2041 - type    - left end of interval (often-1)
2042 
2043   Output Parameter:
2044 . quad    - A PetscQuadrature object for integration over the biunit simplex
2045             (defined by the bounds $x_i >= -1$ and $\sum_i x_i <= 2 - d$) that is exact for
2046             polynomials up to the given degree
2047 
2048   Level: intermediate
2049 
2050 .seealso: `PetscDTSimplexQuadratureType`, `PetscDTGaussQuadrature()`, `PetscDTStroudCononicalQuadrature()`
2051 @*/
2052 PetscErrorCode PetscDTSimplexQuadrature(PetscInt dim, PetscInt degree, PetscDTSimplexQuadratureType type, PetscQuadrature *quad)
2053 {
2054   PetscDTSimplexQuadratureType orig_type = type;
2055 
2056   PetscFunctionBegin;
2057   PetscCheck(dim >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative dimension %" PetscInt_FMT, dim);
2058   PetscCheck(degree >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative degree %" PetscInt_FMT, degree);
2059   if (type == PETSCDTSIMPLEXQUAD_DEFAULT) {
2060     type = PETSCDTSIMPLEXQUAD_MINSYM;
2061   }
2062   if (type == PETSCDTSIMPLEXQUAD_CONIC || dim < 2) {
2063     PetscInt points_per_dim = (degree + 2) / 2; // ceil((degree + 1) / 2);
2064     PetscCall(PetscDTStroudConicalQuadrature(dim, 1, points_per_dim, -1, 1, quad));
2065   } else {
2066     PetscInt n = dim + 1;
2067     PetscInt fact = 1;
2068     PetscInt *part, *perm;
2069     PetscInt p = 0;
2070     PetscInt max_degree;
2071     const PetscInt *nodes_per_type = NULL;
2072     const PetscInt *all_num_full_nodes = NULL;
2073     const PetscReal **weights_list = NULL;
2074     const PetscReal **compact_nodes_list = NULL;
2075     const char *citation = NULL;
2076     PetscBool *cited = NULL;
2077 
2078     switch (dim) {
2079     case 2:
2080       cited = &MinSymTriQuadCite;
2081       citation = MinSymTriQuadCitation;
2082       max_degree = PetscDTWVTriQuad_max_degree;
2083       nodes_per_type = PetscDTWVTriQuad_num_orbits;
2084       all_num_full_nodes = PetscDTWVTriQuad_num_nodes;
2085       weights_list = PetscDTWVTriQuad_weights;
2086       compact_nodes_list = PetscDTWVTriQuad_orbits;
2087       break;
2088     case 3:
2089       cited = &MinSymTetQuadCite;
2090       citation = MinSymTetQuadCitation;
2091       max_degree = PetscDTJSTetQuad_max_degree;
2092       nodes_per_type = PetscDTJSTetQuad_num_orbits;
2093       all_num_full_nodes = PetscDTJSTetQuad_num_nodes;
2094       weights_list = PetscDTJSTetQuad_weights;
2095       compact_nodes_list = PetscDTJSTetQuad_orbits;
2096       break;
2097     default:
2098       max_degree = - 1;
2099       break;
2100     }
2101 
2102     if (degree > max_degree) {
2103       if (orig_type == PETSCDTSIMPLEXQUAD_DEFAULT) {
2104         // fall back to conic
2105         PetscCall(PetscDTSimplexQuadrature(dim, degree, PETSCDTSIMPLEXQUAD_CONIC, quad));
2106         PetscFunctionReturn(0);
2107       } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Minimal symmetric quadrature for dim %" PetscInt_FMT ", degree %" PetscInt_FMT " unsupported", dim, degree);
2108     }
2109 
2110     PetscCall(PetscCitationsRegister(citation, cited));
2111 
2112     PetscCall(PetscDTPartitionNumber(n, &p));
2113     for (PetscInt d = 2; d <= n; d++) fact *= d;
2114 
2115     PetscInt num_full_nodes = all_num_full_nodes[degree];
2116     const PetscReal *all_compact_nodes = compact_nodes_list[degree];
2117     const PetscReal *all_compact_weights = weights_list[degree];
2118     nodes_per_type = &nodes_per_type[p * degree];
2119 
2120     PetscReal *points;
2121     PetscReal *counts;
2122     PetscReal *weights;
2123     PetscReal *bary_to_biunit; // row-major transformation of barycentric coordinate to biunit
2124     PetscQuadrature q;
2125 
2126     // compute the transformation
2127     PetscCall(PetscMalloc1(n * dim, &bary_to_biunit));
2128     for (PetscInt d = 0; d < dim; d++) {
2129       for (PetscInt b = 0; b < n; b++) {
2130         bary_to_biunit[d * n + b] = (d == b) ? 1.0 : -1.0;
2131       }
2132     }
2133 
2134     PetscCall(PetscMalloc3(n, &part, n, &perm, n, &counts));
2135     PetscCall(PetscCalloc1(num_full_nodes * dim, &points));
2136     PetscCall(PetscMalloc1(num_full_nodes, &weights));
2137 
2138     // (0, 0, ...) is the first partition lexicographically
2139     PetscCall(PetscArrayzero(part, n));
2140     PetscCall(PetscArrayzero(counts, n));
2141     counts[0] = n;
2142 
2143     // for each partition
2144     for (PetscInt s = 0, node_offset = 0; s < p; s++) {
2145       PetscInt num_compact_coords = part[n-1] + 1;
2146 
2147       const PetscReal *compact_nodes = all_compact_nodes;
2148       const PetscReal *compact_weights = all_compact_weights;
2149       all_compact_nodes += num_compact_coords * nodes_per_type[s];
2150       all_compact_weights += nodes_per_type[s];
2151 
2152       // for every permutation of the vertices
2153       for (PetscInt f = 0; f < fact; f++) {
2154         PetscCall(PetscDTEnumPerm(n, f, perm, NULL));
2155 
2156         // check if it is a valid permutation
2157         PetscInt digit;
2158         for (digit = 1; digit < n; digit++) {
2159           // skip permutations that would duplicate a node because it has a smaller symmetry group
2160           if (part[digit - 1] == part[digit] && perm[digit - 1] > perm[digit]) break;
2161         }
2162         if (digit < n) continue;
2163 
2164         // create full nodes from this permutation of the compact nodes
2165         PetscReal *full_nodes = &points[node_offset * dim];
2166         PetscReal *full_weights = &weights[node_offset];
2167 
2168         PetscCall(PetscArraycpy(full_weights, compact_weights, nodes_per_type[s]));
2169         for (PetscInt b = 0; b < n; b++) {
2170           for (PetscInt d = 0; d < dim; d++) {
2171             for (PetscInt node = 0; node < nodes_per_type[s]; node++) {
2172               full_nodes[node * dim + d] += bary_to_biunit[d * n + perm[b]] * compact_nodes[node * num_compact_coords + part[b]];
2173             }
2174           }
2175         }
2176         node_offset += nodes_per_type[s];
2177       }
2178 
2179       if (s < p - 1) { // Generate the next partition
2180         /* A partition is described by the number of coordinates that are in
2181          * each set of duplicates (counts) and redundantly by mapping each
2182          * index to its set of duplicates (part)
2183          *
2184          * Counts should always be in nonincreasing order
2185          *
2186          * We want to generate the partitions lexically by part, which means
2187          * finding the last index where count > 1 and reducing by 1.
2188          *
2189          * For the new counts beyond that index, we eagerly assign the remaining
2190          * capacity of the partition to smaller indices (ensures lexical ordering),
2191          * while respecting the nonincreasing invariant of the counts
2192          */
2193         PetscInt last_digit = part[n-1];
2194         PetscInt last_digit_with_extra = last_digit;
2195         while (counts[last_digit_with_extra] == 1) last_digit_with_extra--;
2196         PetscInt limit = --counts[last_digit_with_extra];
2197         PetscInt total_to_distribute = last_digit - last_digit_with_extra + 1;
2198         for (PetscInt digit = last_digit_with_extra + 1; digit < n; digit++) {
2199           counts[digit] = PetscMin(limit, total_to_distribute);
2200           total_to_distribute -= PetscMin(limit, total_to_distribute);
2201         }
2202         for (PetscInt digit = 0, offset = 0; digit < n; digit++) {
2203           PetscInt count = counts[digit];
2204           for (PetscInt c = 0; c < count; c++) {
2205             part[offset++] = digit;
2206           }
2207         }
2208       }
2209     }
2210     PetscCall(PetscFree3(part, perm, counts));
2211     PetscCall(PetscFree(bary_to_biunit));
2212     PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, &q));
2213     PetscCall(PetscQuadratureSetData(q, dim, 1, num_full_nodes, points, weights));
2214     *quad = q;
2215   }
2216   PetscFunctionReturn(0);
2217 }
2218 
2219 /*@
2220   PetscDTTanhSinhTensorQuadrature - create tanh-sinh quadrature for a tensor product cell
2221 
2222   Not Collective
2223 
2224   Input Parameters:
2225 + dim   - The cell dimension
2226 . level - The number of points in one dimension, 2^l
2227 . a     - left end of interval (often-1)
2228 - b     - right end of interval (often +1)
2229 
2230   Output Parameter:
2231 . q - A PetscQuadrature object
2232 
2233   Level: intermediate
2234 
2235 .seealso: `PetscDTGaussTensorQuadrature()`
2236 @*/
2237 PetscErrorCode PetscDTTanhSinhTensorQuadrature(PetscInt dim, PetscInt level, PetscReal a, PetscReal b, PetscQuadrature *q)
2238 {
2239   const PetscInt  p     = 16;                        /* Digits of precision in the evaluation */
2240   const PetscReal alpha = (b-a)/2.;                  /* Half-width of the integration interval */
2241   const PetscReal beta  = (b+a)/2.;                  /* Center of the integration interval */
2242   const PetscReal h     = PetscPowReal(2.0, -level); /* Step size, length between x_k */
2243   PetscReal       xk;                                /* Quadrature point x_k on reference domain [-1, 1] */
2244   PetscReal       wk    = 0.5*PETSC_PI;              /* Quadrature weight at x_k */
2245   PetscReal      *x, *w;
2246   PetscInt        K, k, npoints;
2247 
2248   PetscFunctionBegin;
2249   PetscCheck(dim <= 1,PETSC_COMM_SELF, PETSC_ERR_SUP, "Dimension %" PetscInt_FMT " not yet implemented", dim);
2250   PetscCheck(level,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a number of significant digits");
2251   /* Find K such that the weights are < 32 digits of precision */
2252   for (K = 1; PetscAbsReal(PetscLog10Real(wk)) < 2*p; ++K) {
2253     wk = 0.5*h*PETSC_PI*PetscCoshReal(K*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(K*h)));
2254   }
2255   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
2256   PetscCall(PetscQuadratureSetOrder(*q, 2*K+1));
2257   npoints = 2*K-1;
2258   PetscCall(PetscMalloc1(npoints*dim, &x));
2259   PetscCall(PetscMalloc1(npoints, &w));
2260   /* Center term */
2261   x[0] = beta;
2262   w[0] = 0.5*alpha*PETSC_PI;
2263   for (k = 1; k < K; ++k) {
2264     wk = 0.5*alpha*h*PETSC_PI*PetscCoshReal(k*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h)));
2265     xk = PetscTanhReal(0.5*PETSC_PI*PetscSinhReal(k*h));
2266     x[2*k-1] = -alpha*xk+beta;
2267     w[2*k-1] = wk;
2268     x[2*k+0] =  alpha*xk+beta;
2269     w[2*k+0] = wk;
2270   }
2271   PetscCall(PetscQuadratureSetData(*q, dim, 1, npoints, x, w));
2272   PetscFunctionReturn(0);
2273 }
2274 
2275 PetscErrorCode PetscDTTanhSinhIntegrate(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, void *ctx, PetscReal *sol)
2276 {
2277   const PetscInt  p     = 16;        /* Digits of precision in the evaluation */
2278   const PetscReal alpha = (b-a)/2.;  /* Half-width of the integration interval */
2279   const PetscReal beta  = (b+a)/2.;  /* Center of the integration interval */
2280   PetscReal       h     = 1.0;       /* Step size, length between x_k */
2281   PetscInt        l     = 0;         /* Level of refinement, h = 2^{-l} */
2282   PetscReal       osum  = 0.0;       /* Integral on last level */
2283   PetscReal       psum  = 0.0;       /* Integral on the level before the last level */
2284   PetscReal       sum;               /* Integral on current level */
2285   PetscReal       yk;                /* Quadrature point 1 - x_k on reference domain [-1, 1] */
2286   PetscReal       lx, rx;            /* Quadrature points to the left and right of 0 on the real domain [a, b] */
2287   PetscReal       wk;                /* Quadrature weight at x_k */
2288   PetscReal       lval, rval;        /* Terms in the quadature sum to the left and right of 0 */
2289   PetscInt        d;                 /* Digits of precision in the integral */
2290 
2291   PetscFunctionBegin;
2292   PetscCheck(digits > 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
2293   /* Center term */
2294   func(&beta, ctx, &lval);
2295   sum = 0.5*alpha*PETSC_PI*lval;
2296   /* */
2297   do {
2298     PetscReal lterm, rterm, maxTerm = 0.0, d1, d2, d3, d4;
2299     PetscInt  k = 1;
2300 
2301     ++l;
2302     /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %" PetscInt_FMT " sum: %15.15f\n", l, sum); */
2303     /* At each level of refinement, h --> h/2 and sum --> sum/2 */
2304     psum = osum;
2305     osum = sum;
2306     h   *= 0.5;
2307     sum *= 0.5;
2308     do {
2309       wk = 0.5*h*PETSC_PI*PetscCoshReal(k*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h)));
2310       yk = 1.0/(PetscExpReal(0.5*PETSC_PI*PetscSinhReal(k*h)) * PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h)));
2311       lx = -alpha*(1.0 - yk)+beta;
2312       rx =  alpha*(1.0 - yk)+beta;
2313       func(&lx, ctx, &lval);
2314       func(&rx, ctx, &rval);
2315       lterm   = alpha*wk*lval;
2316       maxTerm = PetscMax(PetscAbsReal(lterm), maxTerm);
2317       sum    += lterm;
2318       rterm   = alpha*wk*rval;
2319       maxTerm = PetscMax(PetscAbsReal(rterm), maxTerm);
2320       sum    += rterm;
2321       ++k;
2322       /* Only need to evaluate every other point on refined levels */
2323       if (l != 1) ++k;
2324     } while (PetscAbsReal(PetscLog10Real(wk)) < p); /* Only need to evaluate sum until weights are < 32 digits of precision */
2325 
2326     d1 = PetscLog10Real(PetscAbsReal(sum - osum));
2327     d2 = PetscLog10Real(PetscAbsReal(sum - psum));
2328     d3 = PetscLog10Real(maxTerm) - p;
2329     if (PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)) == 0.0) d4 = 0.0;
2330     else d4 = PetscLog10Real(PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)));
2331     d  = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1)/d2, 2*d1), d3), d4)));
2332   } while (d < digits && l < 12);
2333   *sol = sum;
2334 
2335   PetscFunctionReturn(0);
2336 }
2337 
2338 #if defined(PETSC_HAVE_MPFR)
2339 PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, void *ctx, PetscReal *sol)
2340 {
2341   const PetscInt  safetyFactor = 2;  /* Calculate abcissa until 2*p digits */
2342   PetscInt        l            = 0;  /* Level of refinement, h = 2^{-l} */
2343   mpfr_t          alpha;             /* Half-width of the integration interval */
2344   mpfr_t          beta;              /* Center of the integration interval */
2345   mpfr_t          h;                 /* Step size, length between x_k */
2346   mpfr_t          osum;              /* Integral on last level */
2347   mpfr_t          psum;              /* Integral on the level before the last level */
2348   mpfr_t          sum;               /* Integral on current level */
2349   mpfr_t          yk;                /* Quadrature point 1 - x_k on reference domain [-1, 1] */
2350   mpfr_t          lx, rx;            /* Quadrature points to the left and right of 0 on the real domain [a, b] */
2351   mpfr_t          wk;                /* Quadrature weight at x_k */
2352   PetscReal       lval, rval, rtmp;  /* Terms in the quadature sum to the left and right of 0 */
2353   PetscInt        d;                 /* Digits of precision in the integral */
2354   mpfr_t          pi2, kh, msinh, mcosh, maxTerm, curTerm, tmp;
2355 
2356   PetscFunctionBegin;
2357   PetscCheck(digits > 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
2358   /* Create high precision storage */
2359   mpfr_inits2(PetscCeilReal(safetyFactor*digits*PetscLogReal(10.)/PetscLogReal(2.)), alpha, beta, h, sum, osum, psum, yk, wk, lx, rx, tmp, maxTerm, curTerm, pi2, kh, msinh, mcosh, NULL);
2360   /* Initialization */
2361   mpfr_set_d(alpha, 0.5*(b-a), MPFR_RNDN);
2362   mpfr_set_d(beta,  0.5*(b+a), MPFR_RNDN);
2363   mpfr_set_d(osum,  0.0,       MPFR_RNDN);
2364   mpfr_set_d(psum,  0.0,       MPFR_RNDN);
2365   mpfr_set_d(h,     1.0,       MPFR_RNDN);
2366   mpfr_const_pi(pi2, MPFR_RNDN);
2367   mpfr_mul_d(pi2, pi2, 0.5, MPFR_RNDN);
2368   /* Center term */
2369   rtmp = 0.5*(b+a);
2370   func(&rtmp, ctx, &lval);
2371   mpfr_set(sum, pi2, MPFR_RNDN);
2372   mpfr_mul(sum, sum, alpha, MPFR_RNDN);
2373   mpfr_mul_d(sum, sum, lval, MPFR_RNDN);
2374   /* */
2375   do {
2376     PetscReal d1, d2, d3, d4;
2377     PetscInt  k = 1;
2378 
2379     ++l;
2380     mpfr_set_d(maxTerm, 0.0, MPFR_RNDN);
2381     /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %" PetscInt_FMT " sum: %15.15f\n", l, sum); */
2382     /* At each level of refinement, h --> h/2 and sum --> sum/2 */
2383     mpfr_set(psum, osum, MPFR_RNDN);
2384     mpfr_set(osum,  sum, MPFR_RNDN);
2385     mpfr_mul_d(h,   h,   0.5, MPFR_RNDN);
2386     mpfr_mul_d(sum, sum, 0.5, MPFR_RNDN);
2387     do {
2388       mpfr_set_si(kh, k, MPFR_RNDN);
2389       mpfr_mul(kh, kh, h, MPFR_RNDN);
2390       /* Weight */
2391       mpfr_set(wk, h, MPFR_RNDN);
2392       mpfr_sinh_cosh(msinh, mcosh, kh, MPFR_RNDN);
2393       mpfr_mul(msinh, msinh, pi2, MPFR_RNDN);
2394       mpfr_mul(mcosh, mcosh, pi2, MPFR_RNDN);
2395       mpfr_cosh(tmp, msinh, MPFR_RNDN);
2396       mpfr_sqr(tmp, tmp, MPFR_RNDN);
2397       mpfr_mul(wk, wk, mcosh, MPFR_RNDN);
2398       mpfr_div(wk, wk, tmp, MPFR_RNDN);
2399       /* Abscissa */
2400       mpfr_set_d(yk, 1.0, MPFR_RNDZ);
2401       mpfr_cosh(tmp, msinh, MPFR_RNDN);
2402       mpfr_div(yk, yk, tmp, MPFR_RNDZ);
2403       mpfr_exp(tmp, msinh, MPFR_RNDN);
2404       mpfr_div(yk, yk, tmp, MPFR_RNDZ);
2405       /* Quadrature points */
2406       mpfr_sub_d(lx, yk, 1.0, MPFR_RNDZ);
2407       mpfr_mul(lx, lx, alpha, MPFR_RNDU);
2408       mpfr_add(lx, lx, beta, MPFR_RNDU);
2409       mpfr_d_sub(rx, 1.0, yk, MPFR_RNDZ);
2410       mpfr_mul(rx, rx, alpha, MPFR_RNDD);
2411       mpfr_add(rx, rx, beta, MPFR_RNDD);
2412       /* Evaluation */
2413       rtmp = mpfr_get_d(lx, MPFR_RNDU);
2414       func(&rtmp, ctx, &lval);
2415       rtmp = mpfr_get_d(rx, MPFR_RNDD);
2416       func(&rtmp, ctx, &rval);
2417       /* Update */
2418       mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
2419       mpfr_mul_d(tmp, tmp, lval, MPFR_RNDN);
2420       mpfr_add(sum, sum, tmp, MPFR_RNDN);
2421       mpfr_abs(tmp, tmp, MPFR_RNDN);
2422       mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
2423       mpfr_set(curTerm, tmp, MPFR_RNDN);
2424       mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
2425       mpfr_mul_d(tmp, tmp, rval, MPFR_RNDN);
2426       mpfr_add(sum, sum, tmp, MPFR_RNDN);
2427       mpfr_abs(tmp, tmp, MPFR_RNDN);
2428       mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
2429       mpfr_max(curTerm, curTerm, tmp, MPFR_RNDN);
2430       ++k;
2431       /* Only need to evaluate every other point on refined levels */
2432       if (l != 1) ++k;
2433       mpfr_log10(tmp, wk, MPFR_RNDN);
2434       mpfr_abs(tmp, tmp, MPFR_RNDN);
2435     } while (mpfr_get_d(tmp, MPFR_RNDN) < safetyFactor*digits); /* Only need to evaluate sum until weights are < 32 digits of precision */
2436     mpfr_sub(tmp, sum, osum, MPFR_RNDN);
2437     mpfr_abs(tmp, tmp, MPFR_RNDN);
2438     mpfr_log10(tmp, tmp, MPFR_RNDN);
2439     d1 = mpfr_get_d(tmp, MPFR_RNDN);
2440     mpfr_sub(tmp, sum, psum, MPFR_RNDN);
2441     mpfr_abs(tmp, tmp, MPFR_RNDN);
2442     mpfr_log10(tmp, tmp, MPFR_RNDN);
2443     d2 = mpfr_get_d(tmp, MPFR_RNDN);
2444     mpfr_log10(tmp, maxTerm, MPFR_RNDN);
2445     d3 = mpfr_get_d(tmp, MPFR_RNDN) - digits;
2446     mpfr_log10(tmp, curTerm, MPFR_RNDN);
2447     d4 = mpfr_get_d(tmp, MPFR_RNDN);
2448     d  = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1)/d2, 2*d1), d3), d4)));
2449   } while (d < digits && l < 8);
2450   *sol = mpfr_get_d(sum, MPFR_RNDN);
2451   /* Cleanup */
2452   mpfr_clears(alpha, beta, h, sum, osum, psum, yk, wk, lx, rx, tmp, maxTerm, curTerm, pi2, kh, msinh, mcosh, NULL);
2453   PetscFunctionReturn(0);
2454 }
2455 #else
2456 
2457 PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, void *ctx, PetscReal *sol)
2458 {
2459   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "This method will not work without MPFR. Reconfigure using --download-mpfr --download-gmp");
2460 }
2461 #endif
2462 
2463 /*@
2464   PetscDTTensorQuadratureCreate - create the tensor product quadrature from two lower-dimensional quadratures
2465 
2466   Not Collective
2467 
2468   Input Parameters:
2469 + q1 - The first quadrature
2470 - q2 - The second quadrature
2471 
2472   Output Parameter:
2473 . q - A PetscQuadrature object
2474 
2475   Level: intermediate
2476 
2477 .seealso: `PetscDTGaussTensorQuadrature()`
2478 @*/
2479 PetscErrorCode PetscDTTensorQuadratureCreate(PetscQuadrature q1, PetscQuadrature q2, PetscQuadrature *q)
2480 {
2481   const PetscReal *x1, *w1, *x2, *w2;
2482   PetscReal       *x, *w;
2483   PetscInt         dim1, Nc1, Np1, order1, qa, d1;
2484   PetscInt         dim2, Nc2, Np2, order2, qb, d2;
2485   PetscInt         dim,  Nc,  Np,  order, qc, d;
2486 
2487   PetscFunctionBegin;
2488   PetscValidHeaderSpecific(q1, PETSCQUADRATURE_CLASSID, 1);
2489   PetscValidHeaderSpecific(q2, PETSCQUADRATURE_CLASSID, 2);
2490   PetscValidPointer(q, 3);
2491   PetscCall(PetscQuadratureGetOrder(q1, &order1));
2492   PetscCall(PetscQuadratureGetOrder(q2, &order2));
2493   PetscCheck(order1 == order2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Order1 %" PetscInt_FMT " != %" PetscInt_FMT " Order2", order1, order2);
2494   PetscCall(PetscQuadratureGetData(q1, &dim1, &Nc1, &Np1, &x1, &w1));
2495   PetscCall(PetscQuadratureGetData(q2, &dim2, &Nc2, &Np2, &x2, &w2));
2496   PetscCheck(Nc1 == Nc2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "NumComp1 %" PetscInt_FMT " != %" PetscInt_FMT " NumComp2", Nc1, Nc2);
2497 
2498   dim   = dim1 + dim2;
2499   Nc    = Nc1;
2500   Np    = Np1 * Np2;
2501   order = order1;
2502   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
2503   PetscCall(PetscQuadratureSetOrder(*q, order));
2504   PetscCall(PetscMalloc1(Np*dim, &x));
2505   PetscCall(PetscMalloc1(Np, &w));
2506   for (qa = 0, qc = 0; qa < Np1; ++qa) {
2507     for (qb = 0; qb < Np2; ++qb, ++qc) {
2508       for (d1 = 0, d = 0; d1 < dim1; ++d1, ++d) {
2509         x[qc*dim+d] = x1[qa*dim1+d1];
2510       }
2511       for (d2 = 0; d2 < dim2; ++d2, ++d) {
2512         x[qc*dim+d] = x2[qb*dim2+d2];
2513       }
2514       w[qc] = w1[qa] * w2[qb];
2515     }
2516   }
2517   PetscCall(PetscQuadratureSetData(*q, dim, Nc, Np, x, w));
2518   PetscFunctionReturn(0);
2519 }
2520 
2521 /* Overwrites A. Can only handle full-rank problems with m>=n
2522  * A in column-major format
2523  * Ainv in row-major format
2524  * tau has length m
2525  * worksize must be >= max(1,n)
2526  */
2527 static PetscErrorCode PetscDTPseudoInverseQR(PetscInt m,PetscInt mstride,PetscInt n,PetscReal *A_in,PetscReal *Ainv_out,PetscScalar *tau,PetscInt worksize,PetscScalar *work)
2528 {
2529   PetscBLASInt   M,N,K,lda,ldb,ldwork,info;
2530   PetscScalar    *A,*Ainv,*R,*Q,Alpha;
2531 
2532   PetscFunctionBegin;
2533 #if defined(PETSC_USE_COMPLEX)
2534   {
2535     PetscInt i,j;
2536     PetscCall(PetscMalloc2(m*n,&A,m*n,&Ainv));
2537     for (j=0; j<n; j++) {
2538       for (i=0; i<m; i++) A[i+m*j] = A_in[i+mstride*j];
2539     }
2540     mstride = m;
2541   }
2542 #else
2543   A = A_in;
2544   Ainv = Ainv_out;
2545 #endif
2546 
2547   PetscCall(PetscBLASIntCast(m,&M));
2548   PetscCall(PetscBLASIntCast(n,&N));
2549   PetscCall(PetscBLASIntCast(mstride,&lda));
2550   PetscCall(PetscBLASIntCast(worksize,&ldwork));
2551   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
2552   PetscCallBLAS("LAPACKgeqrf",LAPACKgeqrf_(&M,&N,A,&lda,tau,work,&ldwork,&info));
2553   PetscCall(PetscFPTrapPop());
2554   PetscCheck(!info,PETSC_COMM_SELF,PETSC_ERR_LIB,"xGEQRF error");
2555   R = A; /* Upper triangular part of A now contains R, the rest contains the elementary reflectors */
2556 
2557   /* Extract an explicit representation of Q */
2558   Q = Ainv;
2559   PetscCall(PetscArraycpy(Q,A,mstride*n));
2560   K = N;                        /* full rank */
2561   PetscCallBLAS("LAPACKorgqr",LAPACKorgqr_(&M,&N,&K,Q,&lda,tau,work,&ldwork,&info));
2562   PetscCheck(!info,PETSC_COMM_SELF,PETSC_ERR_LIB,"xORGQR/xUNGQR error");
2563 
2564   /* Compute A^{-T} = (R^{-1} Q^T)^T = Q R^{-T} */
2565   Alpha = 1.0;
2566   ldb = lda;
2567   PetscCallBLAS("BLAStrsm",BLAStrsm_("Right","Upper","ConjugateTranspose","NotUnitTriangular",&M,&N,&Alpha,R,&lda,Q,&ldb));
2568   /* Ainv is Q, overwritten with inverse */
2569 
2570 #if defined(PETSC_USE_COMPLEX)
2571   {
2572     PetscInt i;
2573     for (i=0; i<m*n; i++) Ainv_out[i] = PetscRealPart(Ainv[i]);
2574     PetscCall(PetscFree2(A,Ainv));
2575   }
2576 #endif
2577   PetscFunctionReturn(0);
2578 }
2579 
2580 /* Computes integral of L_p' over intervals {(x0,x1),(x1,x2),...} */
2581 static PetscErrorCode PetscDTLegendreIntegrate(PetscInt ninterval,const PetscReal *x,PetscInt ndegree,const PetscInt *degrees,PetscBool Transpose,PetscReal *B)
2582 {
2583   PetscReal      *Bv;
2584   PetscInt       i,j;
2585 
2586   PetscFunctionBegin;
2587   PetscCall(PetscMalloc1((ninterval+1)*ndegree,&Bv));
2588   /* Point evaluation of L_p on all the source vertices */
2589   PetscCall(PetscDTLegendreEval(ninterval+1,x,ndegree,degrees,Bv,NULL,NULL));
2590   /* Integral over each interval: \int_a^b L_p' = L_p(b)-L_p(a) */
2591   for (i=0; i<ninterval; i++) {
2592     for (j=0; j<ndegree; j++) {
2593       if (Transpose) B[i+ninterval*j] = Bv[(i+1)*ndegree+j] - Bv[i*ndegree+j];
2594       else           B[i*ndegree+j]   = Bv[(i+1)*ndegree+j] - Bv[i*ndegree+j];
2595     }
2596   }
2597   PetscCall(PetscFree(Bv));
2598   PetscFunctionReturn(0);
2599 }
2600 
2601 /*@
2602    PetscDTReconstructPoly - create matrix representing polynomial reconstruction using cell intervals and evaluation at target intervals
2603 
2604    Not Collective
2605 
2606    Input Parameters:
2607 +  degree - degree of reconstruction polynomial
2608 .  nsource - number of source intervals
2609 .  sourcex - sorted coordinates of source cell boundaries (length nsource+1)
2610 .  ntarget - number of target intervals
2611 -  targetx - sorted coordinates of target cell boundaries (length ntarget+1)
2612 
2613    Output Parameter:
2614 .  R - reconstruction matrix, utarget = sum_s R[t*nsource+s] * usource[s]
2615 
2616    Level: advanced
2617 
2618 .seealso: `PetscDTLegendreEval()`
2619 @*/
2620 PetscErrorCode PetscDTReconstructPoly(PetscInt degree,PetscInt nsource,const PetscReal *sourcex,PetscInt ntarget,const PetscReal *targetx,PetscReal *R)
2621 {
2622   PetscInt       i,j,k,*bdegrees,worksize;
2623   PetscReal      xmin,xmax,center,hscale,*sourcey,*targety,*Bsource,*Bsinv,*Btarget;
2624   PetscScalar    *tau,*work;
2625 
2626   PetscFunctionBegin;
2627   PetscValidRealPointer(sourcex,3);
2628   PetscValidRealPointer(targetx,5);
2629   PetscValidRealPointer(R,6);
2630   PetscCheck(degree < nsource,PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Reconstruction degree %" PetscInt_FMT " must be less than number of source intervals %" PetscInt_FMT,degree,nsource);
2631   if (PetscDefined(USE_DEBUG)) {
2632     for (i=0; i<nsource; i++) {
2633       PetscCheck(sourcex[i] < sourcex[i+1],PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Source interval %" PetscInt_FMT " has negative orientation (%g,%g)",i,(double)sourcex[i],(double)sourcex[i+1]);
2634     }
2635     for (i=0; i<ntarget; i++) {
2636       PetscCheck(targetx[i] < targetx[i+1],PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Target interval %" PetscInt_FMT " has negative orientation (%g,%g)",i,(double)targetx[i],(double)targetx[i+1]);
2637     }
2638   }
2639   xmin = PetscMin(sourcex[0],targetx[0]);
2640   xmax = PetscMax(sourcex[nsource],targetx[ntarget]);
2641   center = (xmin + xmax)/2;
2642   hscale = (xmax - xmin)/2;
2643   worksize = nsource;
2644   PetscCall(PetscMalloc4(degree+1,&bdegrees,nsource+1,&sourcey,nsource*(degree+1),&Bsource,worksize,&work));
2645   PetscCall(PetscMalloc4(nsource,&tau,nsource*(degree+1),&Bsinv,ntarget+1,&targety,ntarget*(degree+1),&Btarget));
2646   for (i=0; i<=nsource; i++) sourcey[i] = (sourcex[i]-center)/hscale;
2647   for (i=0; i<=degree; i++) bdegrees[i] = i+1;
2648   PetscCall(PetscDTLegendreIntegrate(nsource,sourcey,degree+1,bdegrees,PETSC_TRUE,Bsource));
2649   PetscCall(PetscDTPseudoInverseQR(nsource,nsource,degree+1,Bsource,Bsinv,tau,nsource,work));
2650   for (i=0; i<=ntarget; i++) targety[i] = (targetx[i]-center)/hscale;
2651   PetscCall(PetscDTLegendreIntegrate(ntarget,targety,degree+1,bdegrees,PETSC_FALSE,Btarget));
2652   for (i=0; i<ntarget; i++) {
2653     PetscReal rowsum = 0;
2654     for (j=0; j<nsource; j++) {
2655       PetscReal sum = 0;
2656       for (k=0; k<degree+1; k++) {
2657         sum += Btarget[i*(degree+1)+k] * Bsinv[k*nsource+j];
2658       }
2659       R[i*nsource+j] = sum;
2660       rowsum += sum;
2661     }
2662     for (j=0; j<nsource; j++) R[i*nsource+j] /= rowsum; /* normalize each row */
2663   }
2664   PetscCall(PetscFree4(bdegrees,sourcey,Bsource,work));
2665   PetscCall(PetscFree4(tau,Bsinv,targety,Btarget));
2666   PetscFunctionReturn(0);
2667 }
2668 
2669 /*@C
2670    PetscGaussLobattoLegendreIntegrate - Compute the L2 integral of a function on the GLL points
2671 
2672    Not Collective
2673 
2674    Input Parameters:
2675 +  n - the number of GLL nodes
2676 .  nodes - the GLL nodes
2677 .  weights - the GLL weights
2678 -  f - the function values at the nodes
2679 
2680    Output Parameter:
2681 .  in - the value of the integral
2682 
2683    Level: beginner
2684 
2685 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`
2686 
2687 @*/
2688 PetscErrorCode PetscGaussLobattoLegendreIntegrate(PetscInt n,PetscReal *nodes,PetscReal *weights,const PetscReal *f,PetscReal *in)
2689 {
2690   PetscInt          i;
2691 
2692   PetscFunctionBegin;
2693   *in = 0.;
2694   for (i=0; i<n; i++) {
2695     *in += f[i]*f[i]*weights[i];
2696   }
2697   PetscFunctionReturn(0);
2698 }
2699 
2700 /*@C
2701    PetscGaussLobattoLegendreElementLaplacianCreate - computes the Laplacian for a single 1d GLL element
2702 
2703    Not Collective
2704 
2705    Input Parameters:
2706 +  n - the number of GLL nodes
2707 .  nodes - the GLL nodes
2708 -  weights - the GLL weights
2709 
2710    Output Parameter:
2711 .  A - the stiffness element
2712 
2713    Level: beginner
2714 
2715    Notes:
2716     Destroy this with PetscGaussLobattoLegendreElementLaplacianDestroy()
2717 
2718    You can access entries in this array with AA[i][j] but in memory it is stored in contiguous memory, row oriented (the array is symmetric)
2719 
2720 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianDestroy()`
2721 
2722 @*/
2723 PetscErrorCode PetscGaussLobattoLegendreElementLaplacianCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2724 {
2725   PetscReal        **A;
2726   const PetscReal  *gllnodes = nodes;
2727   const PetscInt   p = n-1;
2728   PetscReal        z0,z1,z2 = -1,x,Lpj,Lpr;
2729   PetscInt         i,j,nn,r;
2730 
2731   PetscFunctionBegin;
2732   PetscCall(PetscMalloc1(n,&A));
2733   PetscCall(PetscMalloc1(n*n,&A[0]));
2734   for (i=1; i<n; i++) A[i] = A[i-1]+n;
2735 
2736   for (j=1; j<p; j++) {
2737     x  = gllnodes[j];
2738     z0 = 1.;
2739     z1 = x;
2740     for (nn=1; nn<p; nn++) {
2741       z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
2742       z0 = z1;
2743       z1 = z2;
2744     }
2745     Lpj=z2;
2746     for (r=1; r<p; r++) {
2747       if (r == j) {
2748         A[j][j]=2./(3.*(1.-gllnodes[j]*gllnodes[j])*Lpj*Lpj);
2749       } else {
2750         x  = gllnodes[r];
2751         z0 = 1.;
2752         z1 = x;
2753         for (nn=1; nn<p; nn++) {
2754           z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
2755           z0 = z1;
2756           z1 = z2;
2757         }
2758         Lpr     = z2;
2759         A[r][j] = 4./(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*Lpr*(gllnodes[j]-gllnodes[r])*(gllnodes[j]-gllnodes[r]));
2760       }
2761     }
2762   }
2763   for (j=1; j<p+1; j++) {
2764     x  = gllnodes[j];
2765     z0 = 1.;
2766     z1 = x;
2767     for (nn=1; nn<p; nn++) {
2768       z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
2769       z0 = z1;
2770       z1 = z2;
2771     }
2772     Lpj     = z2;
2773     A[j][0] = 4.*PetscPowRealInt(-1.,p)/(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*(1.+gllnodes[j])*(1.+gllnodes[j]));
2774     A[0][j] = A[j][0];
2775   }
2776   for (j=0; j<p; j++) {
2777     x  = gllnodes[j];
2778     z0 = 1.;
2779     z1 = x;
2780     for (nn=1; nn<p; nn++) {
2781       z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
2782       z0 = z1;
2783       z1 = z2;
2784     }
2785     Lpj=z2;
2786 
2787     A[p][j] = 4./(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*(1.-gllnodes[j])*(1.-gllnodes[j]));
2788     A[j][p] = A[p][j];
2789   }
2790   A[0][0]=0.5+(((PetscReal)p)*(((PetscReal)p)+1.)-2.)/6.;
2791   A[p][p]=A[0][0];
2792   *AA = A;
2793   PetscFunctionReturn(0);
2794 }
2795 
2796 /*@C
2797    PetscGaussLobattoLegendreElementLaplacianDestroy - frees the Laplacian for a single 1d GLL element
2798 
2799    Not Collective
2800 
2801    Input Parameters:
2802 +  n - the number of GLL nodes
2803 .  nodes - the GLL nodes
2804 .  weights - the GLL weightss
2805 -  A - the stiffness element
2806 
2807    Level: beginner
2808 
2809 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`
2810 
2811 @*/
2812 PetscErrorCode PetscGaussLobattoLegendreElementLaplacianDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2813 {
2814   PetscFunctionBegin;
2815   PetscCall(PetscFree((*AA)[0]));
2816   PetscCall(PetscFree(*AA));
2817   *AA  = NULL;
2818   PetscFunctionReturn(0);
2819 }
2820 
2821 /*@C
2822    PetscGaussLobattoLegendreElementGradientCreate - computes the gradient for a single 1d GLL element
2823 
2824    Not Collective
2825 
2826    Input Parameter:
2827 +  n - the number of GLL nodes
2828 .  nodes - the GLL nodes
2829 .  weights - the GLL weights
2830 
2831    Output Parameters:
2832 .  AA - the stiffness element
2833 -  AAT - the transpose of AA (pass in NULL if you do not need this array)
2834 
2835    Level: beginner
2836 
2837    Notes:
2838     Destroy this with PetscGaussLobattoLegendreElementGradientDestroy()
2839 
2840    You can access entries in these arrays with AA[i][j] but in memory it is stored in contiguous memory, row oriented
2841 
2842 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianDestroy()`
2843 
2844 @*/
2845 PetscErrorCode PetscGaussLobattoLegendreElementGradientCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA,PetscReal ***AAT)
2846 {
2847   PetscReal        **A, **AT = NULL;
2848   const PetscReal  *gllnodes = nodes;
2849   const PetscInt   p = n-1;
2850   PetscReal        Li, Lj,d0;
2851   PetscInt         i,j;
2852 
2853   PetscFunctionBegin;
2854   PetscCall(PetscMalloc1(n,&A));
2855   PetscCall(PetscMalloc1(n*n,&A[0]));
2856   for (i=1; i<n; i++) A[i] = A[i-1]+n;
2857 
2858   if (AAT) {
2859     PetscCall(PetscMalloc1(n,&AT));
2860     PetscCall(PetscMalloc1(n*n,&AT[0]));
2861     for (i=1; i<n; i++) AT[i] = AT[i-1]+n;
2862   }
2863 
2864   if (n==1) {A[0][0] = 0.;}
2865   d0 = (PetscReal)p*((PetscReal)p+1.)/4.;
2866   for  (i=0; i<n; i++) {
2867     for  (j=0; j<n; j++) {
2868       A[i][j] = 0.;
2869       PetscCall(PetscDTComputeJacobi(0., 0., p, gllnodes[i], &Li));
2870       PetscCall(PetscDTComputeJacobi(0., 0., p, gllnodes[j], &Lj));
2871       if (i!=j)             A[i][j] = Li/(Lj*(gllnodes[i]-gllnodes[j]));
2872       if ((j==i) && (i==0)) A[i][j] = -d0;
2873       if (j==i && i==p)     A[i][j] = d0;
2874       if (AT) AT[j][i] = A[i][j];
2875     }
2876   }
2877   if (AAT) *AAT = AT;
2878   *AA  = A;
2879   PetscFunctionReturn(0);
2880 }
2881 
2882 /*@C
2883    PetscGaussLobattoLegendreElementGradientDestroy - frees the gradient for a single 1d GLL element obtained with PetscGaussLobattoLegendreElementGradientCreate()
2884 
2885    Not Collective
2886 
2887    Input Parameters:
2888 +  n - the number of GLL nodes
2889 .  nodes - the GLL nodes
2890 .  weights - the GLL weights
2891 .  AA - the stiffness element
2892 -  AAT - the transpose of the element
2893 
2894    Level: beginner
2895 
2896 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`, `PetscGaussLobattoLegendreElementAdvectionCreate()`
2897 
2898 @*/
2899 PetscErrorCode PetscGaussLobattoLegendreElementGradientDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA,PetscReal ***AAT)
2900 {
2901   PetscFunctionBegin;
2902   PetscCall(PetscFree((*AA)[0]));
2903   PetscCall(PetscFree(*AA));
2904   *AA  = NULL;
2905   if (*AAT) {
2906     PetscCall(PetscFree((*AAT)[0]));
2907     PetscCall(PetscFree(*AAT));
2908     *AAT  = NULL;
2909   }
2910   PetscFunctionReturn(0);
2911 }
2912 
2913 /*@C
2914    PetscGaussLobattoLegendreElementAdvectionCreate - computes the advection operator for a single 1d GLL element
2915 
2916    Not Collective
2917 
2918    Input Parameters:
2919 +  n - the number of GLL nodes
2920 .  nodes - the GLL nodes
2921 -  weights - the GLL weightss
2922 
2923    Output Parameter:
2924 .  AA - the stiffness element
2925 
2926    Level: beginner
2927 
2928    Notes:
2929     Destroy this with PetscGaussLobattoLegendreElementAdvectionDestroy()
2930 
2931    This is the same as the Gradient operator multiplied by the diagonal mass matrix
2932 
2933    You can access entries in this array with AA[i][j] but in memory it is stored in contiguous memory, row oriented
2934 
2935 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`, `PetscGaussLobattoLegendreElementAdvectionDestroy()`
2936 
2937 @*/
2938 PetscErrorCode PetscGaussLobattoLegendreElementAdvectionCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2939 {
2940   PetscReal       **D;
2941   const PetscReal  *gllweights = weights;
2942   const PetscInt   glln = n;
2943   PetscInt         i,j;
2944 
2945   PetscFunctionBegin;
2946   PetscCall(PetscGaussLobattoLegendreElementGradientCreate(n,nodes,weights,&D,NULL));
2947   for (i=0; i<glln; i++) {
2948     for (j=0; j<glln; j++) {
2949       D[i][j] = gllweights[i]*D[i][j];
2950     }
2951   }
2952   *AA = D;
2953   PetscFunctionReturn(0);
2954 }
2955 
2956 /*@C
2957    PetscGaussLobattoLegendreElementAdvectionDestroy - frees the advection stiffness for a single 1d GLL element
2958 
2959    Not Collective
2960 
2961    Input Parameters:
2962 +  n - the number of GLL nodes
2963 .  nodes - the GLL nodes
2964 .  weights - the GLL weights
2965 -  A - advection
2966 
2967    Level: beginner
2968 
2969 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementAdvectionCreate()`
2970 
2971 @*/
2972 PetscErrorCode PetscGaussLobattoLegendreElementAdvectionDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2973 {
2974   PetscFunctionBegin;
2975   PetscCall(PetscFree((*AA)[0]));
2976   PetscCall(PetscFree(*AA));
2977   *AA  = NULL;
2978   PetscFunctionReturn(0);
2979 }
2980 
2981 PetscErrorCode PetscGaussLobattoLegendreElementMassCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2982 {
2983   PetscReal        **A;
2984   const PetscReal  *gllweights = weights;
2985   const PetscInt   glln = n;
2986   PetscInt         i,j;
2987 
2988   PetscFunctionBegin;
2989   PetscCall(PetscMalloc1(glln,&A));
2990   PetscCall(PetscMalloc1(glln*glln,&A[0]));
2991   for (i=1; i<glln; i++) A[i] = A[i-1]+glln;
2992   if (glln==1) {A[0][0] = 0.;}
2993   for  (i=0; i<glln; i++) {
2994     for  (j=0; j<glln; j++) {
2995       A[i][j] = 0.;
2996       if (j==i)     A[i][j] = gllweights[i];
2997     }
2998   }
2999   *AA  = A;
3000   PetscFunctionReturn(0);
3001 }
3002 
3003 PetscErrorCode PetscGaussLobattoLegendreElementMassDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
3004 {
3005   PetscFunctionBegin;
3006   PetscCall(PetscFree((*AA)[0]));
3007   PetscCall(PetscFree(*AA));
3008   *AA  = NULL;
3009   PetscFunctionReturn(0);
3010 }
3011 
3012 /*@
3013   PetscDTIndexToBary - convert an index into a barycentric coordinate.
3014 
3015   Input Parameters:
3016 + len - the desired length of the barycentric tuple (usually 1 more than the dimension it represents, so a barycentric coordinate in a triangle has length 3)
3017 . sum - the value that the sum of the barycentric coordinates (which will be non-negative integers) should sum to
3018 - index - the index to convert: should be >= 0 and < Binomial(len - 1 + sum, sum)
3019 
3020   Output Parameter:
3021 . coord - will be filled with the barycentric coordinate
3022 
3023   Level: beginner
3024 
3025   Note: the indices map to barycentric coordinates in lexicographic order, where the first index is the
3026   least significant and the last index is the most significant.
3027 
3028 .seealso: `PetscDTBaryToIndex()`
3029 @*/
3030 PetscErrorCode PetscDTIndexToBary(PetscInt len, PetscInt sum, PetscInt index, PetscInt coord[])
3031 {
3032   PetscInt c, d, s, total, subtotal, nexttotal;
3033 
3034   PetscFunctionBeginHot;
3035   PetscCheck(len >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
3036   PetscCheck(index >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index must be non-negative");
3037   if (!len) {
3038     if (!sum && !index) PetscFunctionReturn(0);
3039     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid index or sum for length 0 barycentric coordinate");
3040   }
3041   for (c = 1, total = 1; c <= len; c++) {
3042     /* total is the number of ways to have a tuple of length c with sum */
3043     if (index < total) break;
3044     total = (total * (sum + c)) / c;
3045   }
3046   PetscCheck(c <= len,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index out of range");
3047   for (d = c; d < len; d++) coord[d] = 0;
3048   for (s = 0, subtotal = 1, nexttotal = 1; c > 0;) {
3049     /* subtotal is the number of ways to have a tuple of length c with sum s */
3050     /* nexttotal is the number of ways to have a tuple of length c-1 with sum s */
3051     if ((index + subtotal) >= total) {
3052       coord[--c] = sum - s;
3053       index -= (total - subtotal);
3054       sum = s;
3055       total = nexttotal;
3056       subtotal = 1;
3057       nexttotal = 1;
3058       s = 0;
3059     } else {
3060       subtotal = (subtotal * (c + s)) / (s + 1);
3061       nexttotal = (nexttotal * (c - 1 + s)) / (s + 1);
3062       s++;
3063     }
3064   }
3065   PetscFunctionReturn(0);
3066 }
3067 
3068 /*@
3069   PetscDTBaryToIndex - convert a barycentric coordinate to an index
3070 
3071   Input Parameters:
3072 + len - the desired length of the barycentric tuple (usually 1 more than the dimension it represents, so a barycentric coordinate in a triangle has length 3)
3073 . sum - the value that the sum of the barycentric coordinates (which will be non-negative integers) should sum to
3074 - coord - a barycentric coordinate with the given length and sum
3075 
3076   Output Parameter:
3077 . index - the unique index for the coordinate, >= 0 and < Binomial(len - 1 + sum, sum)
3078 
3079   Level: beginner
3080 
3081   Note: the indices map to barycentric coordinates in lexicographic order, where the first index is the
3082   least significant and the last index is the most significant.
3083 
3084 .seealso: `PetscDTIndexToBary`
3085 @*/
3086 PetscErrorCode PetscDTBaryToIndex(PetscInt len, PetscInt sum, const PetscInt coord[], PetscInt *index)
3087 {
3088   PetscInt c;
3089   PetscInt i;
3090   PetscInt total;
3091 
3092   PetscFunctionBeginHot;
3093   PetscCheck(len >= 0,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
3094   if (!len) {
3095     if (!sum) {
3096       *index = 0;
3097       PetscFunctionReturn(0);
3098     }
3099     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid index or sum for length 0 barycentric coordinate");
3100   }
3101   for (c = 1, total = 1; c < len; c++) total = (total * (sum + c)) / c;
3102   i = total - 1;
3103   c = len - 1;
3104   sum -= coord[c];
3105   while (sum > 0) {
3106     PetscInt subtotal;
3107     PetscInt s;
3108 
3109     for (s = 1, subtotal = 1; s < sum; s++) subtotal = (subtotal * (c + s)) / s;
3110     i   -= subtotal;
3111     sum -= coord[--c];
3112   }
3113   *index = i;
3114   PetscFunctionReturn(0);
3115 }
3116