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