xref: /petsc/src/dm/dt/interface/dt.c (revision a5bc1bf344cccdca9c53fc2bcd040c7b504fff0e)
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 static PetscBool GolubWelschCite       = PETSC_FALSE;
16 const char       GolubWelschCitation[] = "@article{GolubWelsch1969,\n"
17                                          "  author  = {Golub and Welsch},\n"
18                                          "  title   = {Calculation of Quadrature Rules},\n"
19                                          "  journal = {Math. Comp.},\n"
20                                          "  volume  = {23},\n"
21                                          "  number  = {106},\n"
22                                          "  pages   = {221--230},\n"
23                                          "  year    = {1969}\n}\n";
24 
25 /* Numerical tests in src/dm/dt/examples/tests/ex1.c show that when computing the nodes and weights of Gauss-Jacobi
26    quadrature rules:
27 
28    - in double precision, Newton's method and Golub & Welsch both work for moderate degrees (< 100),
29    - in single precision, Newton's method starts producing incorrect roots around n = 15, but
30      the weights from Golub & Welsch become a problem before then: they produces errors
31      in computing the Jacobi-polynomial Gram matrix around n = 6.
32 
33    So we default to Newton's method (required fewer dependencies) */
34 PetscBool PetscDTGaussQuadratureNewton_Internal = PETSC_TRUE;
35 
36 PetscClassId PETSCQUADRATURE_CLASSID = 0;
37 
38 /*@
39   PetscQuadratureCreate - Create a PetscQuadrature object
40 
41   Collective
42 
43   Input Parameter:
44 . comm - The communicator for the PetscQuadrature object
45 
46   Output Parameter:
47 . q  - The PetscQuadrature object
48 
49   Level: beginner
50 
51 .seealso: PetscQuadratureDestroy(), PetscQuadratureGetData()
52 @*/
53 PetscErrorCode PetscQuadratureCreate(MPI_Comm comm, PetscQuadrature *q)
54 {
55   PetscErrorCode ierr;
56 
57   PetscFunctionBegin;
58   PetscValidPointer(q, 2);
59   ierr = DMInitializePackage();CHKERRQ(ierr);
60   ierr = PetscHeaderCreate(*q,PETSCQUADRATURE_CLASSID,"PetscQuadrature","Quadrature","DT",comm,PetscQuadratureDestroy,PetscQuadratureView);CHKERRQ(ierr);
61   (*q)->dim       = -1;
62   (*q)->Nc        =  1;
63   (*q)->order     = -1;
64   (*q)->numPoints = 0;
65   (*q)->points    = NULL;
66   (*q)->weights   = NULL;
67   PetscFunctionReturn(0);
68 }
69 
70 /*@
71   PetscQuadratureDuplicate - Create a deep copy of the PetscQuadrature object
72 
73   Collective on q
74 
75   Input Parameter:
76 . q  - The PetscQuadrature object
77 
78   Output Parameter:
79 . r  - The new PetscQuadrature object
80 
81   Level: beginner
82 
83 .seealso: PetscQuadratureCreate(), PetscQuadratureDestroy(), PetscQuadratureGetData()
84 @*/
85 PetscErrorCode PetscQuadratureDuplicate(PetscQuadrature q, PetscQuadrature *r)
86 {
87   PetscInt         order, dim, Nc, Nq;
88   const PetscReal *points, *weights;
89   PetscReal       *p, *w;
90   PetscErrorCode   ierr;
91 
92   PetscFunctionBegin;
93   PetscValidPointer(q, 2);
94   ierr = PetscQuadratureCreate(PetscObjectComm((PetscObject) q), r);CHKERRQ(ierr);
95   ierr = PetscQuadratureGetOrder(q, &order);CHKERRQ(ierr);
96   ierr = PetscQuadratureSetOrder(*r, order);CHKERRQ(ierr);
97   ierr = PetscQuadratureGetData(q, &dim, &Nc, &Nq, &points, &weights);CHKERRQ(ierr);
98   ierr = PetscMalloc1(Nq*dim, &p);CHKERRQ(ierr);
99   ierr = PetscMalloc1(Nq*Nc, &w);CHKERRQ(ierr);
100   ierr = PetscArraycpy(p, points, Nq*dim);CHKERRQ(ierr);
101   ierr = PetscArraycpy(w, weights, Nc * Nq);CHKERRQ(ierr);
102   ierr = PetscQuadratureSetData(*r, dim, Nc, Nq, p, w);CHKERRQ(ierr);
103   PetscFunctionReturn(0);
104 }
105 
106 /*@
107   PetscQuadratureDestroy - Destroys a PetscQuadrature object
108 
109   Collective on q
110 
111   Input Parameter:
112 . q  - The PetscQuadrature object
113 
114   Level: beginner
115 
116 .seealso: PetscQuadratureCreate(), PetscQuadratureGetData()
117 @*/
118 PetscErrorCode PetscQuadratureDestroy(PetscQuadrature *q)
119 {
120   PetscErrorCode ierr;
121 
122   PetscFunctionBegin;
123   if (!*q) PetscFunctionReturn(0);
124   PetscValidHeaderSpecific((*q),PETSCQUADRATURE_CLASSID,1);
125   if (--((PetscObject)(*q))->refct > 0) {
126     *q = NULL;
127     PetscFunctionReturn(0);
128   }
129   ierr = PetscFree((*q)->points);CHKERRQ(ierr);
130   ierr = PetscFree((*q)->weights);CHKERRQ(ierr);
131   ierr = PetscHeaderDestroy(q);CHKERRQ(ierr);
132   PetscFunctionReturn(0);
133 }
134 
135 /*@
136   PetscQuadratureGetOrder - Return the order of the method
137 
138   Not collective
139 
140   Input Parameter:
141 . q - The PetscQuadrature object
142 
143   Output Parameter:
144 . order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated
145 
146   Level: intermediate
147 
148 .seealso: PetscQuadratureSetOrder(), PetscQuadratureGetData(), PetscQuadratureSetData()
149 @*/
150 PetscErrorCode PetscQuadratureGetOrder(PetscQuadrature q, PetscInt *order)
151 {
152   PetscFunctionBegin;
153   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
154   PetscValidPointer(order, 2);
155   *order = q->order;
156   PetscFunctionReturn(0);
157 }
158 
159 /*@
160   PetscQuadratureSetOrder - Return the order of the method
161 
162   Not collective
163 
164   Input Parameters:
165 + q - The PetscQuadrature object
166 - order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated
167 
168   Level: intermediate
169 
170 .seealso: PetscQuadratureGetOrder(), PetscQuadratureGetData(), PetscQuadratureSetData()
171 @*/
172 PetscErrorCode PetscQuadratureSetOrder(PetscQuadrature q, PetscInt order)
173 {
174   PetscFunctionBegin;
175   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
176   q->order = order;
177   PetscFunctionReturn(0);
178 }
179 
180 /*@
181   PetscQuadratureGetNumComponents - Return the number of components for functions to be integrated
182 
183   Not collective
184 
185   Input Parameter:
186 . q - The PetscQuadrature object
187 
188   Output Parameter:
189 . Nc - The number of components
190 
191   Note: We are performing an integral int f(x) . w(x) dx, where both f and w (the weight) have Nc components.
192 
193   Level: intermediate
194 
195 .seealso: PetscQuadratureSetNumComponents(), PetscQuadratureGetData(), PetscQuadratureSetData()
196 @*/
197 PetscErrorCode PetscQuadratureGetNumComponents(PetscQuadrature q, PetscInt *Nc)
198 {
199   PetscFunctionBegin;
200   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
201   PetscValidPointer(Nc, 2);
202   *Nc = q->Nc;
203   PetscFunctionReturn(0);
204 }
205 
206 /*@
207   PetscQuadratureSetNumComponents - Return the number of components for functions to be integrated
208 
209   Not collective
210 
211   Input Parameters:
212 + q  - The PetscQuadrature object
213 - Nc - The number of components
214 
215   Note: We are performing an integral int f(x) . w(x) dx, where both f and w (the weight) have Nc components.
216 
217   Level: intermediate
218 
219 .seealso: PetscQuadratureGetNumComponents(), PetscQuadratureGetData(), PetscQuadratureSetData()
220 @*/
221 PetscErrorCode PetscQuadratureSetNumComponents(PetscQuadrature q, PetscInt Nc)
222 {
223   PetscFunctionBegin;
224   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
225   q->Nc = Nc;
226   PetscFunctionReturn(0);
227 }
228 
229 /*@C
230   PetscQuadratureGetData - Returns the data defining the quadrature
231 
232   Not collective
233 
234   Input Parameter:
235 . q  - The PetscQuadrature object
236 
237   Output Parameters:
238 + dim - The spatial dimension
239 . Nc - The number of components
240 . npoints - The number of quadrature points
241 . points - The coordinates of each quadrature point
242 - weights - The weight of each quadrature point
243 
244   Level: intermediate
245 
246   Fortran Notes:
247     From Fortran you must call PetscQuadratureRestoreData() when you are done with the data
248 
249 .seealso: PetscQuadratureCreate(), PetscQuadratureSetData()
250 @*/
251 PetscErrorCode PetscQuadratureGetData(PetscQuadrature q, PetscInt *dim, PetscInt *Nc, PetscInt *npoints, const PetscReal *points[], const PetscReal *weights[])
252 {
253   PetscFunctionBegin;
254   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
255   if (dim) {
256     PetscValidPointer(dim, 2);
257     *dim = q->dim;
258   }
259   if (Nc) {
260     PetscValidPointer(Nc, 3);
261     *Nc = q->Nc;
262   }
263   if (npoints) {
264     PetscValidPointer(npoints, 4);
265     *npoints = q->numPoints;
266   }
267   if (points) {
268     PetscValidPointer(points, 5);
269     *points = q->points;
270   }
271   if (weights) {
272     PetscValidPointer(weights, 6);
273     *weights = q->weights;
274   }
275   PetscFunctionReturn(0);
276 }
277 
278 static PetscErrorCode PetscDTJacobianInverse_Internal(PetscInt m, PetscInt n, const PetscReal J[], PetscReal Jinv[])
279 {
280   PetscScalar    *Js, *Jinvs;
281   PetscInt       i, j, k;
282   PetscBLASInt   bm, bn, info;
283   PetscErrorCode ierr;
284 
285   PetscFunctionBegin;
286   ierr = PetscBLASIntCast(m, &bm);CHKERRQ(ierr);
287   ierr = PetscBLASIntCast(n, &bn);CHKERRQ(ierr);
288 #if defined(PETSC_USE_COMPLEX)
289   ierr = PetscMalloc2(m*n, &Js, m*n, &Jinvs);CHKERRQ(ierr);
290   for (i = 0; i < m*n; i++) Js[i] = J[i];
291 #else
292   Js = (PetscReal *) J;
293   Jinvs = Jinv;
294 #endif
295   if (m == n) {
296     PetscBLASInt *pivots;
297     PetscScalar *W;
298 
299     ierr = PetscMalloc2(m, &pivots, m, &W);CHKERRQ(ierr);
300 
301     ierr = PetscArraycpy(Jinvs, Js, m * m);CHKERRQ(ierr);
302     PetscStackCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, Jinvs, &bm, pivots, &info));
303     if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetrf %D",(PetscInt)info);
304     PetscStackCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, Jinvs, &bm, pivots, W, &bm, &info));
305     if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetri %D",(PetscInt)info);
306     ierr = PetscFree2(pivots, W);CHKERRQ(ierr);
307   } else if (m < n) {
308     PetscScalar *JJT;
309     PetscBLASInt *pivots;
310     PetscScalar *W;
311 
312     ierr = PetscMalloc1(m*m, &JJT);CHKERRQ(ierr);
313     ierr = PetscMalloc2(m, &pivots, m, &W);CHKERRQ(ierr);
314     for (i = 0; i < m; i++) {
315       for (j = 0; j < m; j++) {
316         PetscScalar val = 0.;
317 
318         for (k = 0; k < n; k++) val += Js[i * n + k] * Js[j * n + k];
319         JJT[i * m + j] = val;
320       }
321     }
322 
323     PetscStackCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, JJT, &bm, pivots, &info));
324     if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetrf %D",(PetscInt)info);
325     PetscStackCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, JJT, &bm, pivots, W, &bm, &info));
326     if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetri %D",(PetscInt)info);
327     for (i = 0; i < n; i++) {
328       for (j = 0; j < m; j++) {
329         PetscScalar val = 0.;
330 
331         for (k = 0; k < m; k++) val += Js[k * n + i] * JJT[k * m + j];
332         Jinvs[i * m + j] = val;
333       }
334     }
335     ierr = PetscFree2(pivots, W);CHKERRQ(ierr);
336     ierr = PetscFree(JJT);CHKERRQ(ierr);
337   } else {
338     PetscScalar *JTJ;
339     PetscBLASInt *pivots;
340     PetscScalar *W;
341 
342     ierr = PetscMalloc1(n*n, &JTJ);CHKERRQ(ierr);
343     ierr = PetscMalloc2(n, &pivots, n, &W);CHKERRQ(ierr);
344     for (i = 0; i < n; i++) {
345       for (j = 0; j < n; j++) {
346         PetscScalar val = 0.;
347 
348         for (k = 0; k < m; k++) val += Js[k * n + i] * Js[k * n + j];
349         JTJ[i * n + j] = val;
350       }
351     }
352 
353     PetscStackCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bn, &bn, JTJ, &bm, pivots, &info));
354     if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetrf %D",(PetscInt)info);
355     PetscStackCallBLAS("LAPACKgetri", LAPACKgetri_(&bn, JTJ, &bn, pivots, W, &bn, &info));
356     if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetri %D",(PetscInt)info);
357     for (i = 0; i < n; i++) {
358       for (j = 0; j < m; j++) {
359         PetscScalar val = 0.;
360 
361         for (k = 0; k < n; k++) val += JTJ[i * n + k] * Js[j * n + k];
362         Jinvs[i * m + j] = val;
363       }
364     }
365     ierr = PetscFree2(pivots, W);CHKERRQ(ierr);
366     ierr = PetscFree(JTJ);CHKERRQ(ierr);
367   }
368 #if defined(PETSC_USE_COMPLEX)
369   for (i = 0; i < m*n; i++) Jinv[i] = PetscRealPart(Jinvs[i]);
370   ierr = PetscFree2(Js, Jinvs);CHKERRQ(ierr);
371 #endif
372   PetscFunctionReturn(0);
373 }
374 
375 /*@
376    PetscQuadraturePushForward - Push forward a quadrature functional under an affine transformation.
377 
378    Collecive on PetscQuadrature
379 
380    Input Arguments:
381 +  q - the quadrature functional
382 .  imageDim - the dimension of the image of the transformation
383 .  origin - a point in the original space
384 .  originImage - the image of the origin under the transformation
385 .  J - the Jacobian of the image: an [imageDim x dim] matrix in row major order
386 -  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]
387 
388    Output Arguments:
389 .  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.
390 
391    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.
392 
393 .seealso: PetscDTAltVPullback(), PetscDTAltVPullbackMatrix()
394 @*/
395 PetscErrorCode PetscQuadraturePushForward(PetscQuadrature q, PetscInt imageDim, const PetscReal origin[], const PetscReal originImage[], const PetscReal J[], PetscInt formDegree, PetscQuadrature *Jinvstarq)
396 {
397   PetscInt         dim, Nc, imageNc, formSize, Ncopies, imageFormSize, Npoints, pt, i, j, c;
398   const PetscReal *points;
399   const PetscReal *weights;
400   PetscReal       *imagePoints, *imageWeights;
401   PetscReal       *Jinv;
402   PetscReal       *Jinvstar;
403   PetscErrorCode   ierr;
404 
405   PetscFunctionBegin;
406   PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1);
407   if (imageDim < PetscAbsInt(formDegree)) SETERRQ2(PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Cannot represent a %D-form in %D dimensions", PetscAbsInt(formDegree), imageDim);
408   ierr = PetscQuadratureGetData(q, &dim, &Nc, &Npoints, &points, &weights);CHKERRQ(ierr);
409   ierr = PetscDTBinomialInt(dim, PetscAbsInt(formDegree), &formSize);CHKERRQ(ierr);
410   if (Nc % formSize) SETERRQ2(PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Number of components %D is not a multiple of formSize %D\n", Nc, formSize);
411   Ncopies = Nc / formSize;
412   ierr = PetscDTBinomialInt(imageDim, PetscAbsInt(formDegree), &imageFormSize);CHKERRQ(ierr);
413   imageNc = Ncopies * imageFormSize;
414   ierr = PetscMalloc1(Npoints * imageDim, &imagePoints);CHKERRQ(ierr);
415   ierr = PetscMalloc1(Npoints * imageNc, &imageWeights);CHKERRQ(ierr);
416   ierr = PetscMalloc2(imageDim * dim, &Jinv, formSize * imageFormSize, &Jinvstar);CHKERRQ(ierr);
417   ierr = PetscDTJacobianInverse_Internal(dim, imageDim, J, Jinv);CHKERRQ(ierr);
418   ierr = PetscDTAltVPullbackMatrix(imageDim, dim, Jinv, formDegree, Jinvstar);CHKERRQ(ierr);
419   for (pt = 0; pt < Npoints; pt++) {
420     const PetscReal *point = &points[pt * dim];
421     PetscReal       *imagePoint = &imagePoints[pt * imageDim];
422 
423     for (i = 0; i < imageDim; i++) {
424       PetscReal val = originImage[i];
425 
426       for (j = 0; j < dim; j++) val += J[i * dim + j] * (point[j] - origin[j]);
427       imagePoint[i] = val;
428     }
429     for (c = 0; c < Ncopies; c++) {
430       const PetscReal *form = &weights[pt * Nc + c * formSize];
431       PetscReal       *imageForm = &imageWeights[pt * imageNc + c * imageFormSize];
432 
433       for (i = 0; i < imageFormSize; i++) {
434         PetscReal val = 0.;
435 
436         for (j = 0; j < formSize; j++) val += Jinvstar[i * formSize + j] * form[j];
437         imageForm[i] = val;
438       }
439     }
440   }
441   ierr = PetscQuadratureCreate(PetscObjectComm((PetscObject)q), Jinvstarq);CHKERRQ(ierr);
442   ierr = PetscQuadratureSetData(*Jinvstarq, imageDim, imageNc, Npoints, imagePoints, imageWeights);CHKERRQ(ierr);
443   ierr = PetscFree2(Jinv, Jinvstar);CHKERRQ(ierr);
444   PetscFunctionReturn(0);
445 }
446 
447 /*@C
448   PetscQuadratureSetData - Sets the data defining the quadrature
449 
450   Not collective
451 
452   Input Parameters:
453 + q  - The PetscQuadrature object
454 . dim - The spatial dimension
455 . Nc - The number of components
456 . npoints - The number of quadrature points
457 . points - The coordinates of each quadrature point
458 - weights - The weight of each quadrature point
459 
460   Note: This routine owns the references to points and weights, so they must be allocated using PetscMalloc() and the user should not free them.
461 
462   Level: intermediate
463 
464 .seealso: PetscQuadratureCreate(), PetscQuadratureGetData()
465 @*/
466 PetscErrorCode PetscQuadratureSetData(PetscQuadrature q, PetscInt dim, PetscInt Nc, PetscInt npoints, const PetscReal points[], const PetscReal weights[])
467 {
468   PetscFunctionBegin;
469   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
470   if (dim >= 0)     q->dim       = dim;
471   if (Nc >= 0)      q->Nc        = Nc;
472   if (npoints >= 0) q->numPoints = npoints;
473   if (points) {
474     PetscValidPointer(points, 4);
475     q->points = points;
476   }
477   if (weights) {
478     PetscValidPointer(weights, 5);
479     q->weights = weights;
480   }
481   PetscFunctionReturn(0);
482 }
483 
484 static PetscErrorCode PetscQuadratureView_Ascii(PetscQuadrature quad, PetscViewer v)
485 {
486   PetscInt          q, d, c;
487   PetscViewerFormat format;
488   PetscErrorCode    ierr;
489 
490   PetscFunctionBegin;
491   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);}
492   else              {ierr = PetscViewerASCIIPrintf(v, "Quadrature of order %D on %D points (dim %D)\n", quad->order, quad->numPoints, quad->dim);CHKERRQ(ierr);}
493   ierr = PetscViewerGetFormat(v, &format);CHKERRQ(ierr);
494   if (format != PETSC_VIEWER_ASCII_INFO_DETAIL) PetscFunctionReturn(0);
495   for (q = 0; q < quad->numPoints; ++q) {
496     ierr = PetscViewerASCIIPrintf(v, "p%D (", q);CHKERRQ(ierr);
497     ierr = PetscViewerASCIIUseTabs(v, PETSC_FALSE);CHKERRQ(ierr);
498     for (d = 0; d < quad->dim; ++d) {
499       if (d) {ierr = PetscViewerASCIIPrintf(v, ", ");CHKERRQ(ierr);}
500       ierr = PetscViewerASCIIPrintf(v, "%+g", (double)quad->points[q*quad->dim+d]);CHKERRQ(ierr);
501     }
502     ierr = PetscViewerASCIIPrintf(v, ") ");CHKERRQ(ierr);
503     if (quad->Nc > 1) {ierr = PetscViewerASCIIPrintf(v, "w%D (", q);CHKERRQ(ierr);}
504     for (c = 0; c < quad->Nc; ++c) {
505       if (c) {ierr = PetscViewerASCIIPrintf(v, ", ");CHKERRQ(ierr);}
506       ierr = PetscViewerASCIIPrintf(v, "%+g", (double)quad->weights[q*quad->Nc+c]);CHKERRQ(ierr);
507     }
508     if (quad->Nc > 1) {ierr = PetscViewerASCIIPrintf(v, ")");CHKERRQ(ierr);}
509     ierr = PetscViewerASCIIPrintf(v, "\n");CHKERRQ(ierr);
510     ierr = PetscViewerASCIIUseTabs(v, PETSC_TRUE);CHKERRQ(ierr);
511   }
512   PetscFunctionReturn(0);
513 }
514 
515 /*@C
516   PetscQuadratureView - Views a PetscQuadrature object
517 
518   Collective on quad
519 
520   Input Parameters:
521 + quad  - The PetscQuadrature object
522 - viewer - The PetscViewer object
523 
524   Level: beginner
525 
526 .seealso: PetscQuadratureCreate(), PetscQuadratureGetData()
527 @*/
528 PetscErrorCode PetscQuadratureView(PetscQuadrature quad, PetscViewer viewer)
529 {
530   PetscBool      iascii;
531   PetscErrorCode ierr;
532 
533   PetscFunctionBegin;
534   PetscValidHeader(quad, 1);
535   if (viewer) PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
536   if (!viewer) {ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject) quad), &viewer);CHKERRQ(ierr);}
537   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii);CHKERRQ(ierr);
538   ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
539   if (iascii) {ierr = PetscQuadratureView_Ascii(quad, viewer);CHKERRQ(ierr);}
540   ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
541   PetscFunctionReturn(0);
542 }
543 
544 /*@C
545   PetscQuadratureExpandComposite - Return a quadrature over the composite element, which has the original quadrature in each subelement
546 
547   Not collective
548 
549   Input Parameter:
550 + q - The original PetscQuadrature
551 . numSubelements - The number of subelements the original element is divided into
552 . v0 - An array of the initial points for each subelement
553 - jac - An array of the Jacobian mappings from the reference to each subelement
554 
555   Output Parameters:
556 . dim - The dimension
557 
558   Note: Together v0 and jac define an affine mapping from the original reference element to each subelement
559 
560  Not available from Fortran
561 
562   Level: intermediate
563 
564 .seealso: PetscFECreate(), PetscSpaceGetDimension(), PetscDualSpaceGetDimension()
565 @*/
566 PetscErrorCode PetscQuadratureExpandComposite(PetscQuadrature q, PetscInt numSubelements, const PetscReal v0[], const PetscReal jac[], PetscQuadrature *qref)
567 {
568   const PetscReal *points,    *weights;
569   PetscReal       *pointsRef, *weightsRef;
570   PetscInt         dim, Nc, order, npoints, npointsRef, c, p, cp, d, e;
571   PetscErrorCode   ierr;
572 
573   PetscFunctionBegin;
574   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
575   PetscValidPointer(v0, 3);
576   PetscValidPointer(jac, 4);
577   PetscValidPointer(qref, 5);
578   ierr = PetscQuadratureCreate(PETSC_COMM_SELF, qref);CHKERRQ(ierr);
579   ierr = PetscQuadratureGetOrder(q, &order);CHKERRQ(ierr);
580   ierr = PetscQuadratureGetData(q, &dim, &Nc, &npoints, &points, &weights);CHKERRQ(ierr);
581   npointsRef = npoints*numSubelements;
582   ierr = PetscMalloc1(npointsRef*dim,&pointsRef);CHKERRQ(ierr);
583   ierr = PetscMalloc1(npointsRef*Nc, &weightsRef);CHKERRQ(ierr);
584   for (c = 0; c < numSubelements; ++c) {
585     for (p = 0; p < npoints; ++p) {
586       for (d = 0; d < dim; ++d) {
587         pointsRef[(c*npoints + p)*dim+d] = v0[c*dim+d];
588         for (e = 0; e < dim; ++e) {
589           pointsRef[(c*npoints + p)*dim+d] += jac[(c*dim + d)*dim+e]*(points[p*dim+e] + 1.0);
590         }
591       }
592       /* Could also use detJ here */
593       for (cp = 0; cp < Nc; ++cp) weightsRef[(c*npoints+p)*Nc+cp] = weights[p*Nc+cp]/numSubelements;
594     }
595   }
596   ierr = PetscQuadratureSetOrder(*qref, order);CHKERRQ(ierr);
597   ierr = PetscQuadratureSetData(*qref, dim, Nc, npointsRef, pointsRef, weightsRef);CHKERRQ(ierr);
598   PetscFunctionReturn(0);
599 }
600 
601 /* Compute the coefficients for the Jacobi polynomial recurrence,
602  *
603  * J^{a,b}_n(x) = (cnm1 + cnm1x * x) * J^{a,b}_{n-1}(x) - cnm2 * J^{a,b}_{n-2}(x).
604  */
605 #define PetscDTJacobiRecurrence_Internal(n,a,b,cnm1,cnm1x,cnm2) \
606 do {                                                            \
607   PetscReal _a = (a);                                           \
608   PetscReal _b = (b);                                           \
609   PetscReal _n = (n);                                           \
610   if (n == 1) {                                                 \
611     (cnm1) = (_a-_b) * 0.5;                                     \
612     (cnm1x) = (_a+_b+2.)*0.5;                                   \
613     (cnm2) = 0.;                                                \
614   } else {                                                      \
615     PetscReal _2n = _n+_n;                                      \
616     PetscReal _d = (_2n*(_n+_a+_b)*(_2n+_a+_b-2));              \
617     PetscReal _n1 = (_2n+_a+_b-1.)*(_a*_a-_b*_b);               \
618     PetscReal _n1x = (_2n+_a+_b-1.)*(_2n+_a+_b)*(_2n+_a+_b-2);  \
619     PetscReal _n2 = 2.*((_n+_a-1.)*(_n+_b-1.)*(_2n+_a+_b));     \
620     (cnm1) = _n1 / _d;                                          \
621     (cnm1x) = _n1x / _d;                                        \
622     (cnm2) = _n2 / _d;                                          \
623   }                                                             \
624 } while (0)
625 
626 static PetscErrorCode PetscDTJacobiEval_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscInt k, const PetscReal *points, PetscInt ndegree, const PetscInt *degrees, PetscReal *p)
627 {
628   PetscReal ak, bk;
629   PetscReal abk1;
630   PetscInt i,l,maxdegree;
631 
632   PetscFunctionBegin;
633   maxdegree = degrees[ndegree-1] - k;
634   ak = a + k;
635   bk = b + k;
636   abk1 = a + b + k + 1.;
637   if (maxdegree < 0) {
638     for (i = 0; i < npoints; i++) for (l = 0; l < ndegree; l++) p[i*ndegree+l] = 0.;
639     PetscFunctionReturn(0);
640   }
641   for (i=0; i<npoints; i++) {
642     PetscReal pm1,pm2,x;
643     PetscReal cnm1, cnm1x, cnm2;
644     PetscInt  j,m;
645 
646     x    = points[i];
647     pm2  = 1.;
648     PetscDTJacobiRecurrence_Internal(1,ak,bk,cnm1,cnm1x,cnm2);
649     pm1 = (cnm1 + cnm1x*x);
650     l    = 0;
651     while (l < ndegree && degrees[l] - k < 0) {
652       p[l++] = 0.;
653     }
654     while (l < ndegree && degrees[l] - k == 0) {
655       p[l] = pm2;
656       for (m = 0; m < k; m++) p[l] *= (abk1 + m) * 0.5;
657       l++;
658     }
659     while (l < ndegree && degrees[l] - k == 1) {
660       p[l] = pm1;
661       for (m = 0; m < k; m++) p[l] *= (abk1 + 1 + m) * 0.5;
662       l++;
663     }
664     for (j=2; j<=maxdegree; j++) {
665       PetscReal pp;
666 
667       PetscDTJacobiRecurrence_Internal(j,ak,bk,cnm1,cnm1x,cnm2);
668       pp   = (cnm1 + cnm1x*x)*pm1 - cnm2*pm2;
669       pm2  = pm1;
670       pm1  = pp;
671       while (l < ndegree && degrees[l] - k == j) {
672         p[l] = pp;
673         for (m = 0; m < k; m++) p[l] *= (abk1 + j + m) * 0.5;
674         l++;
675       }
676     }
677     p += ndegree;
678   }
679   PetscFunctionReturn(0);
680 }
681 
682 /*@
683    PetscDTJacobiEval - evaluate Jacobi polynomials for the weight function $(1.+x)^{\alpha} (1.-x)^{\beta}$
684                        at points
685 
686    Not Collective
687 
688    Input Arguments:
689 +  npoints - number of spatial points to evaluate at
690 .  alpha - the left exponent > -1
691 .  beta - the right exponent > -1
692 .  points - array of locations to evaluate at
693 .  ndegree - number of basis degrees to evaluate
694 -  degrees - sorted array of degrees to evaluate
695 
696    Output Arguments:
697 +  B - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension npoints*ndegrees, allocated by caller) (or NULL)
698 .  D - row-oriented derivative evaluation matrix (or NULL)
699 -  D2 - row-oriented second derivative evaluation matrix (or NULL)
700 
701    Level: intermediate
702 
703 .seealso: PetscDTGaussQuadrature()
704 @*/
705 PetscErrorCode PetscDTJacobiEval(PetscInt npoints,PetscReal alpha, PetscReal beta, const PetscReal *points,PetscInt ndegree,const PetscInt *degrees,PetscReal *B,PetscReal *D,PetscReal *D2)
706 {
707   PetscErrorCode ierr;
708 
709   PetscFunctionBegin;
710   if (alpha <= -1.) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"alpha must be > -1.");
711   if (beta <= -1.) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"beta must be > -1.");
712   if (!npoints || !ndegree) PetscFunctionReturn(0);
713   if (B)  {ierr = PetscDTJacobiEval_Internal(npoints, alpha, beta, 0, points, ndegree, degrees, B);CHKERRQ(ierr);}
714   if (D)  {ierr = PetscDTJacobiEval_Internal(npoints, alpha, beta, 1, points, ndegree, degrees, D);CHKERRQ(ierr);}
715   if (D2) {ierr = PetscDTJacobiEval_Internal(npoints, alpha, beta, 2, points, ndegree, degrees, D2);CHKERRQ(ierr);}
716   PetscFunctionReturn(0);
717 }
718 
719 /*@
720    PetscDTLegendreEval - evaluate Legendre polynomials at points
721 
722    Not Collective
723 
724    Input Arguments:
725 +  npoints - number of spatial points to evaluate at
726 .  points - array of locations to evaluate at
727 .  ndegree - number of basis degrees to evaluate
728 -  degrees - sorted array of degrees to evaluate
729 
730    Output Arguments:
731 +  B - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension npoints*ndegrees, allocated by caller) (or NULL)
732 .  D - row-oriented derivative evaluation matrix (or NULL)
733 -  D2 - row-oriented second derivative evaluation matrix (or NULL)
734 
735    Level: intermediate
736 
737 .seealso: PetscDTGaussQuadrature()
738 @*/
739 PetscErrorCode PetscDTLegendreEval(PetscInt npoints,const PetscReal *points,PetscInt ndegree,const PetscInt *degrees,PetscReal *B,PetscReal *D,PetscReal *D2)
740 {
741   PetscErrorCode ierr;
742 
743   PetscFunctionBegin;
744   ierr = PetscDTJacobiEval(npoints, 0., 0., points, ndegree, degrees, B, D, D2);CHKERRQ(ierr);
745   PetscFunctionReturn(0);
746 }
747 
748 /* solve the symmetric tridiagonal eigenvalue system, writing the eigenvalues into eigs and the eigenvectors into V
749  * with lds n; diag and subdiag are overwritten */
750 static PetscErrorCode PetscDTSymmetricTridiagonalEigensolve(PetscInt n, PetscReal diag[], PetscReal subdiag[],
751                                                             PetscReal eigs[], PetscScalar V[])
752 {
753   char jobz = 'V'; /* eigenvalues and eigenvectors */
754   char range = 'A'; /* all eigenvalues will be found */
755   PetscReal VL = 0.; /* ignored because range is 'A' */
756   PetscReal VU = 0.; /* ignored because range is 'A' */
757   PetscBLASInt IL = 0; /* ignored because range is 'A' */
758   PetscBLASInt IU = 0; /* ignored because range is 'A' */
759   PetscReal abstol = 0.; /* unused */
760   PetscBLASInt bn, bm, ldz; /* bm will equal bn on exit */
761   PetscBLASInt *isuppz;
762   PetscBLASInt lwork, liwork;
763   PetscReal workquery;
764   PetscBLASInt  iworkquery;
765   PetscBLASInt *iwork;
766   PetscBLASInt info;
767   PetscReal *work = NULL;
768   PetscErrorCode ierr;
769 
770   PetscFunctionBegin;
771 #if !defined(PETSCDTGAUSSIANQUADRATURE_EIG)
772   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
773 #endif
774   ierr = PetscBLASIntCast(n, &bn);CHKERRQ(ierr);
775   ierr = PetscBLASIntCast(n, &ldz);CHKERRQ(ierr);
776 #if !defined(PETSC_MISSING_LAPACK_STEGR)
777   ierr = PetscMalloc1(2 * n, &isuppz);CHKERRQ(ierr);
778   lwork = -1;
779   liwork = -1;
780   PetscStackCallBLAS("LAPACKstegr",LAPACKstegr_(&jobz,&range,&bn,diag,subdiag,&VL,&VU,&IL,&IU,&abstol,&bm,eigs,V,&ldz,isuppz,&workquery,&lwork,&iworkquery,&liwork,&info));
781   if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"xSTEGR error");
782   lwork = (PetscBLASInt) workquery;
783   liwork = (PetscBLASInt) iworkquery;
784   ierr = PetscMalloc2(lwork, &work, liwork, &iwork);CHKERRQ(ierr);
785   ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr);
786   PetscStackCallBLAS("LAPACKstegr",LAPACKstegr_(&jobz,&range,&bn,diag,subdiag,&VL,&VU,&IL,&IU,&abstol,&bm,eigs,V,&ldz,isuppz,work,&lwork,iwork,&liwork,&info));
787   ierr = PetscFPTrapPop();CHKERRQ(ierr);
788   if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"xSTEGR error");
789   ierr = PetscFree2(work, iwork);CHKERRQ(ierr);
790   ierr = PetscFree(isuppz);CHKERRQ(ierr);
791 #elif !defined(PETSC_MISSING_LAPACK_STEQR)
792   jobz = 'I'; /* Compute eigenvalues and eigenvectors of the
793                  tridiagonal matrix.  Z is initialized to the identity
794                  matrix. */
795   ierr = PetscMalloc1(PetscMax(1,2*n-2),&work);CHKERRQ(ierr);
796   PetscStackCallBLAS("LAPACKsteqr",LAPACKsteqr_("I",&bn,diag,subdiag,V,&ldz,work,&info));
797   ierr = PetscFPTrapPop();CHKERRQ(ierr);
798   if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"xSTEQR error");
799   ierr = PetscFree(work);CHKERRQ(ierr);
800   ierr = PetscArraycpy(eigs,diag,n);CHKERRQ(ierr);
801 #endif
802   PetscFunctionReturn(0);
803 }
804 
805 /* Formula for the weights at the endpoints (-1 and 1) of Gauss-Lobatto-Jacobi
806  * quadrature rules on the interval [-1, 1] */
807 static PetscErrorCode PetscDTGaussLobattoJacobiEndweights_Internal(PetscInt n, PetscReal alpha, PetscReal beta, PetscReal *leftw, PetscReal *rightw)
808 {
809   PetscReal twoab1;
810   PetscInt  m = n - 2;
811   PetscReal a = alpha + 1.;
812   PetscReal b = beta + 1.;
813   PetscReal gra, grb;
814 
815   PetscFunctionBegin;
816   twoab1 = PetscPowReal(2., a + b - 1.);
817 #if defined(PETSC_HAVE_LGAMMA)
818   grb = PetscExpReal(2. * PetscLGamma(b+1.) + PetscLGamma(m+1.) + PetscLGamma(m+a+1.) -
819                      (PetscLGamma(m+b+1) + PetscLGamma(m+a+b+1.)));
820   gra = PetscExpReal(2. * PetscLGamma(a+1.) + PetscLGamma(m+1.) + PetscLGamma(m+b+1.) -
821                      (PetscLGamma(m+a+1) + PetscLGamma(m+a+b+1.)));
822 #else
823   {
824     PetscInt alphai = (PetscInt) alpha;
825     PetscInt betai = (PetscInt) beta;
826     PetscErrorCode ierr;
827 
828     if ((PetscReal) alphai == alpha && (PetscReal) betai == beta) {
829       PetscReal binom1, binom2;
830 
831       ierr = PetscDTBinomial(m+b, b, &binom1);CHKERRQ(ierr);
832       ierr = PetscDTBinomial(m+a+b, b, &binom2);CHKERRQ(ierr);
833       grb = 1./ (binom1 * binom2);
834       ierr = PetscDTBinomial(m+a, a, &binom1);CHKERRQ(ierr);
835       ierr = PetscDTBinomial(m+a+b, a, &binom2);CHKERRQ(ierr);
836       gra = 1./ (binom1 * binom2);
837     } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"lgamma() - math routine is unavailable.");
838   }
839 #endif
840   *leftw = twoab1 * grb / b;
841   *rightw = twoab1 * gra / a;
842   PetscFunctionReturn(0);
843 }
844 
845 /* Evaluates the nth jacobi polynomial with weight parameters a,b at a point x.
846    Recurrence relations implemented from the pseudocode given in Karniadakis and Sherwin, Appendix B */
847 PETSC_STATIC_INLINE PetscErrorCode PetscDTComputeJacobi(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscReal *P)
848 {
849   PetscReal pn1, pn2;
850   PetscReal cnm1, cnm1x, cnm2;
851   PetscInt  k;
852 
853   PetscFunctionBegin;
854   if (!n) {*P = 1.0; PetscFunctionReturn(0);}
855   PetscDTJacobiRecurrence_Internal(1,a,b,cnm1,cnm1x,cnm2);
856   pn2 = 1.;
857   pn1 = cnm1 + cnm1x*x;
858   if (n == 1) {*P = pn1; PetscFunctionReturn(0);}
859   *P  = 0.0;
860   for (k = 2; k < n+1; ++k) {
861     PetscDTJacobiRecurrence_Internal(k,a,b,cnm1,cnm1x,cnm2);
862 
863     *P  = (cnm1 + cnm1x*x)*pn1 - cnm2*pn2;
864     pn2 = pn1;
865     pn1 = *P;
866   }
867   PetscFunctionReturn(0);
868 }
869 
870 /* Evaluates the first derivative of P_{n}^{a,b} at a point x. */
871 PETSC_STATIC_INLINE PetscErrorCode PetscDTComputeJacobiDerivative(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscInt k, PetscReal *P)
872 {
873   PetscReal      nP;
874   PetscInt       i;
875   PetscErrorCode ierr;
876 
877   PetscFunctionBegin;
878   if (k > n) {*P = 0.0; PetscFunctionReturn(0);}
879   ierr = PetscDTComputeJacobi(a+k, b+k, n-k, x, &nP);CHKERRQ(ierr);
880   for (i = 0; i < k; i++) nP *= (a + b + n + 1. + i) * 0.5;
881   *P = nP;
882   PetscFunctionReturn(0);
883 }
884 
885 /* Maps from [-1,1]^2 to the (-1,1) reference triangle */
886 PETSC_STATIC_INLINE PetscErrorCode PetscDTMapSquareToTriangle_Internal(PetscReal x, PetscReal y, PetscReal *xi, PetscReal *eta)
887 {
888   PetscFunctionBegin;
889   *xi  = 0.5 * (1.0 + x) * (1.0 - y) - 1.0;
890   *eta = y;
891   PetscFunctionReturn(0);
892 }
893 
894 static PetscErrorCode PetscDTGaussJacobiQuadrature_Newton_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal x[], PetscReal w[])
895 {
896   PetscInt       maxIter = 100;
897   PetscReal      eps     = PetscExpReal(0.75 * PetscLogReal(PETSC_MACHINE_EPSILON));
898   PetscReal      a1, a6, gf;
899   PetscInt       k;
900   PetscErrorCode ierr;
901 
902   PetscFunctionBegin;
903 
904   a1 = PetscPowReal(2.0, a+b+1);
905 #if defined(PETSC_HAVE_LGAMMA)
906   {
907     PetscReal a2, a3, a4, a5;
908     a2 = PetscLGamma(a + npoints + 1);
909     a3 = PetscLGamma(b + npoints + 1);
910     a4 = PetscLGamma(a + b + npoints + 1);
911     a5 = PetscLGamma(npoints + 1);
912     gf = PetscExpReal(a2 + a3 - (a4 + a5));
913   }
914 #else
915   {
916     PetscInt ia, ib;
917 
918     ia = (PetscInt) a;
919     ib = (PetscInt) b;
920     gf = 1.;
921     if (ia == a && ia >= 0) { /* compute ratio of rising factorals wrt a */
922       for (k = 0; k < ia; k++) gf *= (npoints + 1. + k) / (npoints + b + 1. + k);
923     } else if (b == b && ib >= 0) { /* compute ratio of rising factorials wrt b */
924       for (k = 0; k < ib; k++) gf *= (npoints + 1. + k) / (npoints + a + 1. + k);
925     } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"lgamma() - math routine is unavailable.");
926   }
927 #endif
928 
929   a6   = a1 * gf;
930   /* Computes the m roots of P_{m}^{a,b} on [-1,1] by Newton's method with Chebyshev points as initial guesses.
931    Algorithm implemented from the pseudocode given by Karniadakis and Sherwin and Python in FIAT */
932   for (k = 0; k < npoints; ++k) {
933     PetscReal r = PetscCosReal(PETSC_PI * (1. - (4.*k + 3. + 2.*b) / (4.*npoints + 2.*(a + b + 1.)))), dP;
934     PetscInt  j;
935 
936     if (k > 0) r = 0.5 * (r + x[k-1]);
937     for (j = 0; j < maxIter; ++j) {
938       PetscReal s = 0.0, delta, f, fp;
939       PetscInt  i;
940 
941       for (i = 0; i < k; ++i) s = s + 1.0 / (r - x[i]);
942       ierr = PetscDTComputeJacobi(a, b, npoints, r, &f);CHKERRQ(ierr);
943       ierr = PetscDTComputeJacobiDerivative(a, b, npoints, r, 1, &fp);CHKERRQ(ierr);
944       delta = f / (fp - f * s);
945       r     = r - delta;
946       if (PetscAbsReal(delta) < eps) break;
947     }
948     x[k] = r;
949     ierr = PetscDTComputeJacobiDerivative(a, b, npoints, x[k], 1, &dP);CHKERRQ(ierr);
950     w[k] = a6 / (1.0 - PetscSqr(x[k])) / PetscSqr(dP);
951   }
952   PetscFunctionReturn(0);
953 }
954 
955 /* Compute the diagonals of the Jacobi matrix used in Golub & Welsch algorithms for Gauss-Jacobi
956  * quadrature weight calculations on [-1,1] for exponents (1. + x)^a (1.-x)^b */
957 static PetscErrorCode PetscDTJacobiMatrix_Internal(PetscInt nPoints, PetscReal a, PetscReal b, PetscReal *d, PetscReal *s)
958 {
959   PetscInt       i;
960 
961   PetscFunctionBegin;
962   for (i = 0; i < nPoints; i++) {
963     PetscReal A, B, C;
964 
965     PetscDTJacobiRecurrence_Internal(i+1,a,b,A,B,C);
966     d[i] = -A / B;
967     if (i) s[i-1] *= C / B;
968     if (i < nPoints - 1) s[i] = 1. / B;
969   }
970   PetscFunctionReturn(0);
971 }
972 
973 static PetscErrorCode PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal *x, PetscReal *w)
974 {
975   PetscReal mu0;
976   PetscReal ga, gb, gab;
977   PetscInt i;
978   PetscErrorCode ierr;
979 
980   PetscFunctionBegin;
981   ierr = PetscCitationsRegister(GolubWelschCitation, &GolubWelschCite);CHKERRQ(ierr);
982 
983 #if defined(PETSC_HAVE_TGAMMA)
984   ga  = PetscTGamma(a + 1);
985   gb  = PetscTGamma(b + 1);
986   gab = PetscTGamma(a + b + 2);
987 #else
988   {
989     PetscInt ia, ib;
990 
991     ia = (PetscInt) a;
992     ib = (PetscInt) b;
993     if (ia == a && ib == b && ia + 1 > 0 && ib + 1 > 0 && ia + ib + 2 > 0) { /* All gamma(x) terms are (x-1)! terms */
994       ierr = PetscDTFactorial(ia, &ga);CHKERRQ(ierr);
995       ierr = PetscDTFactorial(ib, &gb);CHKERRQ(ierr);
996       ierr = PetscDTFactorial(ia + ib + 1, &gb);CHKERRQ(ierr);
997     } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"tgamma() - math routine is unavailable.");
998   }
999 #endif
1000   mu0 = PetscPowReal(2.,a + b + 1.) * ga * gb / gab;
1001 
1002 #if defined(PETSCDTGAUSSIANQUADRATURE_EIG)
1003   {
1004     PetscReal *diag, *subdiag;
1005     PetscScalar *V;
1006 
1007     ierr = PetscMalloc2(npoints, &diag, npoints, &subdiag);CHKERRQ(ierr);
1008     ierr = PetscMalloc1(npoints*npoints, &V);CHKERRQ(ierr);
1009     ierr = PetscDTJacobiMatrix_Internal(npoints, a, b, diag, subdiag);CHKERRQ(ierr);
1010     for (i = 0; i < npoints - 1; i++) subdiag[i] = PetscSqrtReal(subdiag[i]);
1011     ierr = PetscDTSymmetricTridiagonalEigensolve(npoints, diag, subdiag, x, V);CHKERRQ(ierr);
1012     for (i = 0; i < npoints; i++) w[i] = PetscSqr(PetscRealPart(V[i * npoints])) * mu0;
1013     ierr = PetscFree(V);CHKERRQ(ierr);
1014     ierr = PetscFree2(diag, subdiag);CHKERRQ(ierr);
1015   }
1016 #else
1017   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
1018 #endif
1019   { /* As of March 2, 2020, The Sun Performance Library breaks the LAPACK contract for xstegr and xsteqr: the
1020        eigenvalues are not guaranteed to be in ascending order.  So we heave a passive aggressive sigh and check that
1021        the eigenvalues are sorted */
1022     PetscBool sorted;
1023 
1024     ierr = PetscSortedReal(npoints, x, &sorted);CHKERRQ(ierr);
1025     if (!sorted) {
1026       PetscInt *order, i;
1027       PetscReal *tmp;
1028 
1029       ierr = PetscMalloc2(npoints, &order, npoints, &tmp);CHKERRQ(ierr);
1030       for (i = 0; i < npoints; i++) order[i] = i;
1031       ierr = PetscSortRealWithPermutation(npoints, x, order);CHKERRQ(ierr);
1032       ierr = PetscArraycpy(tmp, x, npoints);CHKERRQ(ierr);
1033       for (i = 0; i < npoints; i++) x[i] = tmp[order[i]];
1034       ierr = PetscArraycpy(tmp, w, npoints);CHKERRQ(ierr);
1035       for (i = 0; i < npoints; i++) w[i] = tmp[order[i]];
1036       ierr = PetscFree2(order, tmp);CHKERRQ(ierr);
1037     }
1038   }
1039   PetscFunctionReturn(0);
1040 }
1041 
1042 static PetscErrorCode PetscDTGaussJacobiQuadrature_Internal(PetscInt npoints,PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
1043 {
1044   PetscErrorCode ierr;
1045 
1046   PetscFunctionBegin;
1047   if (npoints < 1) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Number of points must be positive");
1048   /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
1049   if (alpha <= -1.) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"alpha must be > -1.");
1050   if (beta <= -1.) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"beta must be > -1.");
1051 
1052   if (newton) {
1053     ierr = PetscDTGaussJacobiQuadrature_Newton_Internal(npoints, alpha, beta, x, w);CHKERRQ(ierr);
1054   } else {
1055     ierr = PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(npoints, alpha, beta, x, w);CHKERRQ(ierr);
1056   }
1057   if (alpha == beta) { /* symmetrize */
1058     PetscInt i;
1059     for (i = 0; i < (npoints + 1) / 2; i++) {
1060       PetscInt  j  = npoints - 1 - i;
1061       PetscReal xi = x[i];
1062       PetscReal xj = x[j];
1063       PetscReal wi = w[i];
1064       PetscReal wj = w[j];
1065 
1066       x[i] = (xi - xj) / 2.;
1067       x[j] = (xj - xi) / 2.;
1068       w[i] = w[j] = (wi + wj) / 2.;
1069     }
1070   }
1071   PetscFunctionReturn(0);
1072 }
1073 
1074 /*@
1075   PetscDTGaussJacobiQuadrature - quadrature for the interval [a, b] with the weight function
1076   $(x-a)^\alpha (x-b)^\beta$.
1077 
1078   Not collective
1079 
1080   Input Parameters:
1081 + npoints - the number of points in the quadrature rule
1082 . a - the left endpoint of the interval
1083 . b - the right endpoint of the interval
1084 . alpha - the left exponent
1085 - beta - the right exponent
1086 
1087   Output Parameters:
1088 + x - array of length npoints, the locations of the quadrature points
1089 - w - array of length npoints, the weights of the quadrature points
1090 
1091   Level: intermediate
1092 
1093   Note: this quadrature rule is exact for polynomials up to degree 2*npoints - 1.
1094 @*/
1095 PetscErrorCode PetscDTGaussJacobiQuadrature(PetscInt npoints,PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
1096 {
1097   PetscInt       i;
1098   PetscErrorCode ierr;
1099 
1100   PetscFunctionBegin;
1101   ierr = PetscDTGaussJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal);CHKERRQ(ierr);
1102   if (a != -1. || b != 1.) { /* shift */
1103     for (i = 0; i < npoints; i++) {
1104       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1105       w[i] *= (b - a) / 2.;
1106     }
1107   }
1108   PetscFunctionReturn(0);
1109 }
1110 
1111 static PetscErrorCode PetscDTGaussLobattoJacobiQuadrature_Internal(PetscInt npoints,PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
1112 {
1113   PetscInt       i;
1114   PetscErrorCode ierr;
1115 
1116   PetscFunctionBegin;
1117   if (npoints < 2) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Number of points must be positive");
1118   /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
1119   if (alpha <= -1.) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"alpha must be > -1.");
1120   if (beta <= -1.) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"beta must be > -1.");
1121 
1122   x[0] = -1.;
1123   x[npoints-1] = 1.;
1124   if (npoints > 2) {
1125     ierr = PetscDTGaussJacobiQuadrature_Internal(npoints-2, alpha+1., beta+1., &x[1], &w[1], newton);CHKERRQ(ierr);
1126   }
1127   for (i = 1; i < npoints - 1; i++) {
1128     w[i] /= (1. - x[i]*x[i]);
1129   }
1130   ierr = PetscDTGaussLobattoJacobiEndweights_Internal(npoints, alpha, beta, &w[0], &w[npoints-1]);CHKERRQ(ierr);
1131   PetscFunctionReturn(0);
1132 }
1133 
1134 /*@
1135   PetscDTGaussLobattoJacobiQuadrature - quadrature for the interval [a, b] with the weight function
1136   $(x-a)^\alpha (x-b)^\beta$, with endpoints a and b included as quadrature points.
1137 
1138   Not collective
1139 
1140   Input Parameters:
1141 + npoints - the number of points in the quadrature rule
1142 . a - the left endpoint of the interval
1143 . b - the right endpoint of the interval
1144 . alpha - the left exponent
1145 - beta - the right exponent
1146 
1147   Output Parameters:
1148 + x - array of length npoints, the locations of the quadrature points
1149 - w - array of length npoints, the weights of the quadrature points
1150 
1151   Level: intermediate
1152 
1153   Note: this quadrature rule is exact for polynomials up to degree 2*npoints - 3.
1154 @*/
1155 PetscErrorCode PetscDTGaussLobattoJacobiQuadrature(PetscInt npoints,PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
1156 {
1157   PetscInt       i;
1158   PetscErrorCode ierr;
1159 
1160   PetscFunctionBegin;
1161   ierr = PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal);CHKERRQ(ierr);
1162   if (a != -1. || b != 1.) { /* shift */
1163     for (i = 0; i < npoints; i++) {
1164       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1165       w[i] *= (b - a) / 2.;
1166     }
1167   }
1168   PetscFunctionReturn(0);
1169 }
1170 
1171 /*@
1172    PetscDTGaussQuadrature - create Gauss-Legendre quadrature
1173 
1174    Not Collective
1175 
1176    Input Arguments:
1177 +  npoints - number of points
1178 .  a - left end of interval (often-1)
1179 -  b - right end of interval (often +1)
1180 
1181    Output Arguments:
1182 +  x - quadrature points
1183 -  w - quadrature weights
1184 
1185    Level: intermediate
1186 
1187    References:
1188 .   1. - Golub and Welsch, Calculation of Quadrature Rules, Math. Comp. 23(106), 1969.
1189 
1190 .seealso: PetscDTLegendreEval()
1191 @*/
1192 PetscErrorCode PetscDTGaussQuadrature(PetscInt npoints,PetscReal a,PetscReal b,PetscReal *x,PetscReal *w)
1193 {
1194   PetscInt       i;
1195   PetscErrorCode ierr;
1196 
1197   PetscFunctionBegin;
1198   ierr = PetscDTGaussJacobiQuadrature_Internal(npoints, 0., 0., x, w, PetscDTGaussQuadratureNewton_Internal);CHKERRQ(ierr);
1199   if (a != -1. || b != 1.) { /* shift */
1200     for (i = 0; i < npoints; i++) {
1201       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1202       w[i] *= (b - a) / 2.;
1203     }
1204   }
1205   PetscFunctionReturn(0);
1206 }
1207 
1208 /*@C
1209    PetscDTGaussLobattoLegendreQuadrature - creates a set of the locations and weights of the Gauss-Lobatto-Legendre
1210                       nodes of a given size on the domain [-1,1]
1211 
1212    Not Collective
1213 
1214    Input Parameter:
1215 +  n - number of grid nodes
1216 -  type - PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA or PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON
1217 
1218    Output Arguments:
1219 +  x - quadrature points
1220 -  w - quadrature weights
1221 
1222    Notes:
1223     For n > 30  the Newton approach computes duplicate (incorrect) values for some nodes because the initial guess is apparently not
1224           close enough to the desired solution
1225 
1226    These are useful for implementing spectral methods based on Gauss-Lobatto-Legendre (GLL) nodes
1227 
1228    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
1229 
1230    Level: intermediate
1231 
1232 .seealso: PetscDTGaussQuadrature()
1233 
1234 @*/
1235 PetscErrorCode PetscDTGaussLobattoLegendreQuadrature(PetscInt npoints,PetscGaussLobattoLegendreCreateType type,PetscReal *x,PetscReal *w)
1236 {
1237   PetscBool      newton;
1238   PetscErrorCode ierr;
1239 
1240   PetscFunctionBegin;
1241   if (npoints < 2) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Must provide at least 2 grid points per element");
1242   newton = (PetscBool) (type == PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON);
1243   ierr = PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, 0., 0., x, w, newton);CHKERRQ(ierr);
1244   PetscFunctionReturn(0);
1245 }
1246 
1247 /*@
1248   PetscDTGaussTensorQuadrature - creates a tensor-product Gauss quadrature
1249 
1250   Not Collective
1251 
1252   Input Arguments:
1253 + dim     - The spatial dimension
1254 . Nc      - The number of components
1255 . npoints - number of points in one dimension
1256 . a       - left end of interval (often-1)
1257 - b       - right end of interval (often +1)
1258 
1259   Output Argument:
1260 . q - A PetscQuadrature object
1261 
1262   Level: intermediate
1263 
1264 .seealso: PetscDTGaussQuadrature(), PetscDTLegendreEval()
1265 @*/
1266 PetscErrorCode PetscDTGaussTensorQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
1267 {
1268   PetscInt       totpoints = dim > 1 ? dim > 2 ? npoints*PetscSqr(npoints) : PetscSqr(npoints) : npoints, i, j, k, c;
1269   PetscReal     *x, *w, *xw, *ww;
1270   PetscErrorCode ierr;
1271 
1272   PetscFunctionBegin;
1273   ierr = PetscMalloc1(totpoints*dim,&x);CHKERRQ(ierr);
1274   ierr = PetscMalloc1(totpoints*Nc,&w);CHKERRQ(ierr);
1275   /* Set up the Golub-Welsch system */
1276   switch (dim) {
1277   case 0:
1278     ierr = PetscFree(x);CHKERRQ(ierr);
1279     ierr = PetscFree(w);CHKERRQ(ierr);
1280     ierr = PetscMalloc1(1, &x);CHKERRQ(ierr);
1281     ierr = PetscMalloc1(Nc, &w);CHKERRQ(ierr);
1282     x[0] = 0.0;
1283     for (c = 0; c < Nc; ++c) w[c] = 1.0;
1284     break;
1285   case 1:
1286     ierr = PetscMalloc1(npoints,&ww);CHKERRQ(ierr);
1287     ierr = PetscDTGaussQuadrature(npoints, a, b, x, ww);CHKERRQ(ierr);
1288     for (i = 0; i < npoints; ++i) for (c = 0; c < Nc; ++c) w[i*Nc+c] = ww[i];
1289     ierr = PetscFree(ww);CHKERRQ(ierr);
1290     break;
1291   case 2:
1292     ierr = PetscMalloc2(npoints,&xw,npoints,&ww);CHKERRQ(ierr);
1293     ierr = PetscDTGaussQuadrature(npoints, a, b, xw, ww);CHKERRQ(ierr);
1294     for (i = 0; i < npoints; ++i) {
1295       for (j = 0; j < npoints; ++j) {
1296         x[(i*npoints+j)*dim+0] = xw[i];
1297         x[(i*npoints+j)*dim+1] = xw[j];
1298         for (c = 0; c < Nc; ++c) w[(i*npoints+j)*Nc+c] = ww[i] * ww[j];
1299       }
1300     }
1301     ierr = PetscFree2(xw,ww);CHKERRQ(ierr);
1302     break;
1303   case 3:
1304     ierr = PetscMalloc2(npoints,&xw,npoints,&ww);CHKERRQ(ierr);
1305     ierr = PetscDTGaussQuadrature(npoints, a, b, xw, ww);CHKERRQ(ierr);
1306     for (i = 0; i < npoints; ++i) {
1307       for (j = 0; j < npoints; ++j) {
1308         for (k = 0; k < npoints; ++k) {
1309           x[((i*npoints+j)*npoints+k)*dim+0] = xw[i];
1310           x[((i*npoints+j)*npoints+k)*dim+1] = xw[j];
1311           x[((i*npoints+j)*npoints+k)*dim+2] = xw[k];
1312           for (c = 0; c < Nc; ++c) w[((i*npoints+j)*npoints+k)*Nc+c] = ww[i] * ww[j] * ww[k];
1313         }
1314       }
1315     }
1316     ierr = PetscFree2(xw,ww);CHKERRQ(ierr);
1317     break;
1318   default:
1319     SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %d", dim);
1320   }
1321   ierr = PetscQuadratureCreate(PETSC_COMM_SELF, q);CHKERRQ(ierr);
1322   ierr = PetscQuadratureSetOrder(*q, 2*npoints-1);CHKERRQ(ierr);
1323   ierr = PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w);CHKERRQ(ierr);
1324   ierr = PetscObjectChangeTypeName((PetscObject)*q,"GaussTensor");CHKERRQ(ierr);
1325   PetscFunctionReturn(0);
1326 }
1327 
1328 /* Maps from [-1,1]^2 to the (-1,1) reference triangle */
1329 PETSC_STATIC_INLINE PetscErrorCode PetscDTMapCubeToTetrahedron_Internal(PetscReal x, PetscReal y, PetscReal z, PetscReal *xi, PetscReal *eta, PetscReal *zeta)
1330 {
1331   PetscFunctionBegin;
1332   *xi   = 0.25 * (1.0 + x) * (1.0 - y) * (1.0 - z) - 1.0;
1333   *eta  = 0.5  * (1.0 + y) * (1.0 - z) - 1.0;
1334   *zeta = z;
1335   PetscFunctionReturn(0);
1336 }
1337 
1338 
1339 /*@
1340   PetscDTStroudConicalQuadrature - create Stroud conical quadrature for a simplex
1341 
1342   Not Collective
1343 
1344   Input Arguments:
1345 + dim     - The simplex dimension
1346 . Nc      - The number of components
1347 . npoints - The number of points in one dimension
1348 . a       - left end of interval (often-1)
1349 - b       - right end of interval (often +1)
1350 
1351   Output Argument:
1352 . q - A PetscQuadrature object
1353 
1354   Level: intermediate
1355 
1356   References:
1357 .  1. - Karniadakis and Sherwin.  FIAT
1358 
1359   Note: For dim == 1, this is Gauss-Legendre quadrature
1360 
1361 .seealso: PetscDTGaussTensorQuadrature(), PetscDTGaussQuadrature()
1362 @*/
1363 PetscErrorCode PetscDTStroudConicalQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
1364 {
1365   PetscInt       totpoints = dim > 1 ? dim > 2 ? npoints*PetscSqr(npoints) : PetscSqr(npoints) : npoints;
1366   PetscReal     *px, *wx, *py, *wy, *pz, *wz, *x, *w;
1367   PetscInt       i, j, k, c; PetscErrorCode ierr;
1368 
1369   PetscFunctionBegin;
1370   if ((a != -1.0) || (b != 1.0)) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must use default internal right now");
1371   ierr = PetscMalloc1(totpoints*dim, &x);CHKERRQ(ierr);
1372   ierr = PetscMalloc1(totpoints*Nc, &w);CHKERRQ(ierr);
1373   switch (dim) {
1374   case 0:
1375     ierr = PetscFree(x);CHKERRQ(ierr);
1376     ierr = PetscFree(w);CHKERRQ(ierr);
1377     ierr = PetscMalloc1(1, &x);CHKERRQ(ierr);
1378     ierr = PetscMalloc1(Nc, &w);CHKERRQ(ierr);
1379     x[0] = 0.0;
1380     for (c = 0; c < Nc; ++c) w[c] = 1.0;
1381     break;
1382   case 1:
1383     ierr = PetscMalloc1(npoints,&wx);CHKERRQ(ierr);
1384     ierr = PetscDTGaussJacobiQuadrature(npoints, -1., 1., 0.0, 0.0, x, wx);CHKERRQ(ierr);
1385     for (i = 0; i < npoints; ++i) for (c = 0; c < Nc; ++c) w[i*Nc+c] = wx[i];
1386     ierr = PetscFree(wx);CHKERRQ(ierr);
1387     break;
1388   case 2:
1389     ierr = PetscMalloc4(npoints,&px,npoints,&wx,npoints,&py,npoints,&wy);CHKERRQ(ierr);
1390     ierr = PetscDTGaussJacobiQuadrature(npoints, -1., 1., 0.0, 0.0, px, wx);CHKERRQ(ierr);
1391     ierr = PetscDTGaussJacobiQuadrature(npoints, -1., 1., 1.0, 0.0, py, wy);CHKERRQ(ierr);
1392     for (i = 0; i < npoints; ++i) {
1393       for (j = 0; j < npoints; ++j) {
1394         ierr = PetscDTMapSquareToTriangle_Internal(px[i], py[j], &x[(i*npoints+j)*2+0], &x[(i*npoints+j)*2+1]);CHKERRQ(ierr);
1395         for (c = 0; c < Nc; ++c) w[(i*npoints+j)*Nc+c] = 0.5 * wx[i] * wy[j];
1396       }
1397     }
1398     ierr = PetscFree4(px,wx,py,wy);CHKERRQ(ierr);
1399     break;
1400   case 3:
1401     ierr = PetscMalloc6(npoints,&px,npoints,&wx,npoints,&py,npoints,&wy,npoints,&pz,npoints,&wz);CHKERRQ(ierr);
1402     ierr = PetscDTGaussJacobiQuadrature(npoints, -1., 1., 0.0, 0.0, px, wx);CHKERRQ(ierr);
1403     ierr = PetscDTGaussJacobiQuadrature(npoints, -1., 1., 1.0, 0.0, py, wy);CHKERRQ(ierr);
1404     ierr = PetscDTGaussJacobiQuadrature(npoints, -1., 1., 2.0, 0.0, pz, wz);CHKERRQ(ierr);
1405     for (i = 0; i < npoints; ++i) {
1406       for (j = 0; j < npoints; ++j) {
1407         for (k = 0; k < npoints; ++k) {
1408           ierr = PetscDTMapCubeToTetrahedron_Internal(px[i], py[j], pz[k], &x[((i*npoints+j)*npoints+k)*3+0], &x[((i*npoints+j)*npoints+k)*3+1], &x[((i*npoints+j)*npoints+k)*3+2]);CHKERRQ(ierr);
1409           for (c = 0; c < Nc; ++c) w[((i*npoints+j)*npoints+k)*Nc+c] = 0.125 * wx[i] * wy[j] * wz[k];
1410         }
1411       }
1412     }
1413     ierr = PetscFree6(px,wx,py,wy,pz,wz);CHKERRQ(ierr);
1414     break;
1415   default:
1416     SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %d", dim);
1417   }
1418   ierr = PetscQuadratureCreate(PETSC_COMM_SELF, q);CHKERRQ(ierr);
1419   ierr = PetscQuadratureSetOrder(*q, 2*npoints-1);CHKERRQ(ierr);
1420   ierr = PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w);CHKERRQ(ierr);
1421   ierr = PetscObjectChangeTypeName((PetscObject)*q,"GaussJacobi");CHKERRQ(ierr);
1422   PetscFunctionReturn(0);
1423 }
1424 
1425 /*@
1426   PetscDTTanhSinhTensorQuadrature - create tanh-sinh quadrature for a tensor product cell
1427 
1428   Not Collective
1429 
1430   Input Arguments:
1431 + dim   - The cell dimension
1432 . level - The number of points in one dimension, 2^l
1433 . a     - left end of interval (often-1)
1434 - b     - right end of interval (often +1)
1435 
1436   Output Argument:
1437 . q - A PetscQuadrature object
1438 
1439   Level: intermediate
1440 
1441 .seealso: PetscDTGaussTensorQuadrature()
1442 @*/
1443 PetscErrorCode PetscDTTanhSinhTensorQuadrature(PetscInt dim, PetscInt level, PetscReal a, PetscReal b, PetscQuadrature *q)
1444 {
1445   const PetscInt  p     = 16;                        /* Digits of precision in the evaluation */
1446   const PetscReal alpha = (b-a)/2.;                  /* Half-width of the integration interval */
1447   const PetscReal beta  = (b+a)/2.;                  /* Center of the integration interval */
1448   const PetscReal h     = PetscPowReal(2.0, -level); /* Step size, length between x_k */
1449   PetscReal       xk;                                /* Quadrature point x_k on reference domain [-1, 1] */
1450   PetscReal       wk    = 0.5*PETSC_PI;              /* Quadrature weight at x_k */
1451   PetscReal      *x, *w;
1452   PetscInt        K, k, npoints;
1453   PetscErrorCode  ierr;
1454 
1455   PetscFunctionBegin;
1456   if (dim > 1) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Dimension %d not yet implemented", dim);
1457   if (!level) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a number of significant digits");
1458   /* Find K such that the weights are < 32 digits of precision */
1459   for (K = 1; PetscAbsReal(PetscLog10Real(wk)) < 2*p; ++K) {
1460     wk = 0.5*h*PETSC_PI*PetscCoshReal(K*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(K*h)));
1461   }
1462   ierr = PetscQuadratureCreate(PETSC_COMM_SELF, q);CHKERRQ(ierr);
1463   ierr = PetscQuadratureSetOrder(*q, 2*K+1);CHKERRQ(ierr);
1464   npoints = 2*K-1;
1465   ierr = PetscMalloc1(npoints*dim, &x);CHKERRQ(ierr);
1466   ierr = PetscMalloc1(npoints, &w);CHKERRQ(ierr);
1467   /* Center term */
1468   x[0] = beta;
1469   w[0] = 0.5*alpha*PETSC_PI;
1470   for (k = 1; k < K; ++k) {
1471     wk = 0.5*alpha*h*PETSC_PI*PetscCoshReal(k*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h)));
1472     xk = PetscTanhReal(0.5*PETSC_PI*PetscSinhReal(k*h));
1473     x[2*k-1] = -alpha*xk+beta;
1474     w[2*k-1] = wk;
1475     x[2*k+0] =  alpha*xk+beta;
1476     w[2*k+0] = wk;
1477   }
1478   ierr = PetscQuadratureSetData(*q, dim, 1, npoints, x, w);CHKERRQ(ierr);
1479   PetscFunctionReturn(0);
1480 }
1481 
1482 PetscErrorCode PetscDTTanhSinhIntegrate(void (*func)(PetscReal, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscReal *sol)
1483 {
1484   const PetscInt  p     = 16;        /* Digits of precision in the evaluation */
1485   const PetscReal alpha = (b-a)/2.;  /* Half-width of the integration interval */
1486   const PetscReal beta  = (b+a)/2.;  /* Center of the integration interval */
1487   PetscReal       h     = 1.0;       /* Step size, length between x_k */
1488   PetscInt        l     = 0;         /* Level of refinement, h = 2^{-l} */
1489   PetscReal       osum  = 0.0;       /* Integral on last level */
1490   PetscReal       psum  = 0.0;       /* Integral on the level before the last level */
1491   PetscReal       sum;               /* Integral on current level */
1492   PetscReal       yk;                /* Quadrature point 1 - x_k on reference domain [-1, 1] */
1493   PetscReal       lx, rx;            /* Quadrature points to the left and right of 0 on the real domain [a, b] */
1494   PetscReal       wk;                /* Quadrature weight at x_k */
1495   PetscReal       lval, rval;        /* Terms in the quadature sum to the left and right of 0 */
1496   PetscInt        d;                 /* Digits of precision in the integral */
1497 
1498   PetscFunctionBegin;
1499   if (digits <= 0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
1500   /* Center term */
1501   func(beta, &lval);
1502   sum = 0.5*alpha*PETSC_PI*lval;
1503   /* */
1504   do {
1505     PetscReal lterm, rterm, maxTerm = 0.0, d1, d2, d3, d4;
1506     PetscInt  k = 1;
1507 
1508     ++l;
1509     /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %D sum: %15.15f\n", l, sum); */
1510     /* At each level of refinement, h --> h/2 and sum --> sum/2 */
1511     psum = osum;
1512     osum = sum;
1513     h   *= 0.5;
1514     sum *= 0.5;
1515     do {
1516       wk = 0.5*h*PETSC_PI*PetscCoshReal(k*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h)));
1517       yk = 1.0/(PetscExpReal(0.5*PETSC_PI*PetscSinhReal(k*h)) * PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h)));
1518       lx = -alpha*(1.0 - yk)+beta;
1519       rx =  alpha*(1.0 - yk)+beta;
1520       func(lx, &lval);
1521       func(rx, &rval);
1522       lterm   = alpha*wk*lval;
1523       maxTerm = PetscMax(PetscAbsReal(lterm), maxTerm);
1524       sum    += lterm;
1525       rterm   = alpha*wk*rval;
1526       maxTerm = PetscMax(PetscAbsReal(rterm), maxTerm);
1527       sum    += rterm;
1528       ++k;
1529       /* Only need to evaluate every other point on refined levels */
1530       if (l != 1) ++k;
1531     } while (PetscAbsReal(PetscLog10Real(wk)) < p); /* Only need to evaluate sum until weights are < 32 digits of precision */
1532 
1533     d1 = PetscLog10Real(PetscAbsReal(sum - osum));
1534     d2 = PetscLog10Real(PetscAbsReal(sum - psum));
1535     d3 = PetscLog10Real(maxTerm) - p;
1536     if (PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)) == 0.0) d4 = 0.0;
1537     else d4 = PetscLog10Real(PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)));
1538     d  = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1)/d2, 2*d1), d3), d4)));
1539   } while (d < digits && l < 12);
1540   *sol = sum;
1541 
1542   PetscFunctionReturn(0);
1543 }
1544 
1545 #if defined(PETSC_HAVE_MPFR)
1546 PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(PetscReal, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscReal *sol)
1547 {
1548   const PetscInt  safetyFactor = 2;  /* Calculate abcissa until 2*p digits */
1549   PetscInt        l            = 0;  /* Level of refinement, h = 2^{-l} */
1550   mpfr_t          alpha;             /* Half-width of the integration interval */
1551   mpfr_t          beta;              /* Center of the integration interval */
1552   mpfr_t          h;                 /* Step size, length between x_k */
1553   mpfr_t          osum;              /* Integral on last level */
1554   mpfr_t          psum;              /* Integral on the level before the last level */
1555   mpfr_t          sum;               /* Integral on current level */
1556   mpfr_t          yk;                /* Quadrature point 1 - x_k on reference domain [-1, 1] */
1557   mpfr_t          lx, rx;            /* Quadrature points to the left and right of 0 on the real domain [a, b] */
1558   mpfr_t          wk;                /* Quadrature weight at x_k */
1559   PetscReal       lval, rval;        /* Terms in the quadature sum to the left and right of 0 */
1560   PetscInt        d;                 /* Digits of precision in the integral */
1561   mpfr_t          pi2, kh, msinh, mcosh, maxTerm, curTerm, tmp;
1562 
1563   PetscFunctionBegin;
1564   if (digits <= 0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
1565   /* Create high precision storage */
1566   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);
1567   /* Initialization */
1568   mpfr_set_d(alpha, 0.5*(b-a), MPFR_RNDN);
1569   mpfr_set_d(beta,  0.5*(b+a), MPFR_RNDN);
1570   mpfr_set_d(osum,  0.0,       MPFR_RNDN);
1571   mpfr_set_d(psum,  0.0,       MPFR_RNDN);
1572   mpfr_set_d(h,     1.0,       MPFR_RNDN);
1573   mpfr_const_pi(pi2, MPFR_RNDN);
1574   mpfr_mul_d(pi2, pi2, 0.5, MPFR_RNDN);
1575   /* Center term */
1576   func(0.5*(b+a), &lval);
1577   mpfr_set(sum, pi2, MPFR_RNDN);
1578   mpfr_mul(sum, sum, alpha, MPFR_RNDN);
1579   mpfr_mul_d(sum, sum, lval, MPFR_RNDN);
1580   /* */
1581   do {
1582     PetscReal d1, d2, d3, d4;
1583     PetscInt  k = 1;
1584 
1585     ++l;
1586     mpfr_set_d(maxTerm, 0.0, MPFR_RNDN);
1587     /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %D sum: %15.15f\n", l, sum); */
1588     /* At each level of refinement, h --> h/2 and sum --> sum/2 */
1589     mpfr_set(psum, osum, MPFR_RNDN);
1590     mpfr_set(osum,  sum, MPFR_RNDN);
1591     mpfr_mul_d(h,   h,   0.5, MPFR_RNDN);
1592     mpfr_mul_d(sum, sum, 0.5, MPFR_RNDN);
1593     do {
1594       mpfr_set_si(kh, k, MPFR_RNDN);
1595       mpfr_mul(kh, kh, h, MPFR_RNDN);
1596       /* Weight */
1597       mpfr_set(wk, h, MPFR_RNDN);
1598       mpfr_sinh_cosh(msinh, mcosh, kh, MPFR_RNDN);
1599       mpfr_mul(msinh, msinh, pi2, MPFR_RNDN);
1600       mpfr_mul(mcosh, mcosh, pi2, MPFR_RNDN);
1601       mpfr_cosh(tmp, msinh, MPFR_RNDN);
1602       mpfr_sqr(tmp, tmp, MPFR_RNDN);
1603       mpfr_mul(wk, wk, mcosh, MPFR_RNDN);
1604       mpfr_div(wk, wk, tmp, MPFR_RNDN);
1605       /* Abscissa */
1606       mpfr_set_d(yk, 1.0, MPFR_RNDZ);
1607       mpfr_cosh(tmp, msinh, MPFR_RNDN);
1608       mpfr_div(yk, yk, tmp, MPFR_RNDZ);
1609       mpfr_exp(tmp, msinh, MPFR_RNDN);
1610       mpfr_div(yk, yk, tmp, MPFR_RNDZ);
1611       /* Quadrature points */
1612       mpfr_sub_d(lx, yk, 1.0, MPFR_RNDZ);
1613       mpfr_mul(lx, lx, alpha, MPFR_RNDU);
1614       mpfr_add(lx, lx, beta, MPFR_RNDU);
1615       mpfr_d_sub(rx, 1.0, yk, MPFR_RNDZ);
1616       mpfr_mul(rx, rx, alpha, MPFR_RNDD);
1617       mpfr_add(rx, rx, beta, MPFR_RNDD);
1618       /* Evaluation */
1619       func(mpfr_get_d(lx, MPFR_RNDU), &lval);
1620       func(mpfr_get_d(rx, MPFR_RNDD), &rval);
1621       /* Update */
1622       mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
1623       mpfr_mul_d(tmp, tmp, lval, MPFR_RNDN);
1624       mpfr_add(sum, sum, tmp, MPFR_RNDN);
1625       mpfr_abs(tmp, tmp, MPFR_RNDN);
1626       mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
1627       mpfr_set(curTerm, tmp, MPFR_RNDN);
1628       mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
1629       mpfr_mul_d(tmp, tmp, rval, MPFR_RNDN);
1630       mpfr_add(sum, sum, tmp, MPFR_RNDN);
1631       mpfr_abs(tmp, tmp, MPFR_RNDN);
1632       mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
1633       mpfr_max(curTerm, curTerm, tmp, MPFR_RNDN);
1634       ++k;
1635       /* Only need to evaluate every other point on refined levels */
1636       if (l != 1) ++k;
1637       mpfr_log10(tmp, wk, MPFR_RNDN);
1638       mpfr_abs(tmp, tmp, MPFR_RNDN);
1639     } while (mpfr_get_d(tmp, MPFR_RNDN) < safetyFactor*digits); /* Only need to evaluate sum until weights are < 32 digits of precision */
1640     mpfr_sub(tmp, sum, osum, MPFR_RNDN);
1641     mpfr_abs(tmp, tmp, MPFR_RNDN);
1642     mpfr_log10(tmp, tmp, MPFR_RNDN);
1643     d1 = mpfr_get_d(tmp, MPFR_RNDN);
1644     mpfr_sub(tmp, sum, psum, MPFR_RNDN);
1645     mpfr_abs(tmp, tmp, MPFR_RNDN);
1646     mpfr_log10(tmp, tmp, MPFR_RNDN);
1647     d2 = mpfr_get_d(tmp, MPFR_RNDN);
1648     mpfr_log10(tmp, maxTerm, MPFR_RNDN);
1649     d3 = mpfr_get_d(tmp, MPFR_RNDN) - digits;
1650     mpfr_log10(tmp, curTerm, MPFR_RNDN);
1651     d4 = mpfr_get_d(tmp, MPFR_RNDN);
1652     d  = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1)/d2, 2*d1), d3), d4)));
1653   } while (d < digits && l < 8);
1654   *sol = mpfr_get_d(sum, MPFR_RNDN);
1655   /* Cleanup */
1656   mpfr_clears(alpha, beta, h, sum, osum, psum, yk, wk, lx, rx, tmp, maxTerm, curTerm, pi2, kh, msinh, mcosh, NULL);
1657   PetscFunctionReturn(0);
1658 }
1659 #else
1660 
1661 PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(PetscReal, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscReal *sol)
1662 {
1663   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "This method will not work without MPFR. Reconfigure using --download-mpfr --download-gmp");
1664 }
1665 #endif
1666 
1667 /* Overwrites A. Can only handle full-rank problems with m>=n
1668  * A in column-major format
1669  * Ainv in row-major format
1670  * tau has length m
1671  * worksize must be >= max(1,n)
1672  */
1673 static PetscErrorCode PetscDTPseudoInverseQR(PetscInt m,PetscInt mstride,PetscInt n,PetscReal *A_in,PetscReal *Ainv_out,PetscScalar *tau,PetscInt worksize,PetscScalar *work)
1674 {
1675   PetscErrorCode ierr;
1676   PetscBLASInt   M,N,K,lda,ldb,ldwork,info;
1677   PetscScalar    *A,*Ainv,*R,*Q,Alpha;
1678 
1679   PetscFunctionBegin;
1680 #if defined(PETSC_USE_COMPLEX)
1681   {
1682     PetscInt i,j;
1683     ierr = PetscMalloc2(m*n,&A,m*n,&Ainv);CHKERRQ(ierr);
1684     for (j=0; j<n; j++) {
1685       for (i=0; i<m; i++) A[i+m*j] = A_in[i+mstride*j];
1686     }
1687     mstride = m;
1688   }
1689 #else
1690   A = A_in;
1691   Ainv = Ainv_out;
1692 #endif
1693 
1694   ierr = PetscBLASIntCast(m,&M);CHKERRQ(ierr);
1695   ierr = PetscBLASIntCast(n,&N);CHKERRQ(ierr);
1696   ierr = PetscBLASIntCast(mstride,&lda);CHKERRQ(ierr);
1697   ierr = PetscBLASIntCast(worksize,&ldwork);CHKERRQ(ierr);
1698   ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr);
1699   PetscStackCallBLAS("LAPACKgeqrf",LAPACKgeqrf_(&M,&N,A,&lda,tau,work,&ldwork,&info));
1700   ierr = PetscFPTrapPop();CHKERRQ(ierr);
1701   if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"xGEQRF error");
1702   R = A; /* Upper triangular part of A now contains R, the rest contains the elementary reflectors */
1703 
1704   /* Extract an explicit representation of Q */
1705   Q = Ainv;
1706   ierr = PetscArraycpy(Q,A,mstride*n);CHKERRQ(ierr);
1707   K = N;                        /* full rank */
1708   PetscStackCallBLAS("LAPACKorgqr",LAPACKorgqr_(&M,&N,&K,Q,&lda,tau,work,&ldwork,&info));
1709   if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"xORGQR/xUNGQR error");
1710 
1711   /* Compute A^{-T} = (R^{-1} Q^T)^T = Q R^{-T} */
1712   Alpha = 1.0;
1713   ldb = lda;
1714   PetscStackCallBLAS("BLAStrsm",BLAStrsm_("Right","Upper","ConjugateTranspose","NotUnitTriangular",&M,&N,&Alpha,R,&lda,Q,&ldb));
1715   /* Ainv is Q, overwritten with inverse */
1716 
1717 #if defined(PETSC_USE_COMPLEX)
1718   {
1719     PetscInt i;
1720     for (i=0; i<m*n; i++) Ainv_out[i] = PetscRealPart(Ainv[i]);
1721     ierr = PetscFree2(A,Ainv);CHKERRQ(ierr);
1722   }
1723 #endif
1724   PetscFunctionReturn(0);
1725 }
1726 
1727 /* Computes integral of L_p' over intervals {(x0,x1),(x1,x2),...} */
1728 static PetscErrorCode PetscDTLegendreIntegrate(PetscInt ninterval,const PetscReal *x,PetscInt ndegree,const PetscInt *degrees,PetscBool Transpose,PetscReal *B)
1729 {
1730   PetscErrorCode ierr;
1731   PetscReal      *Bv;
1732   PetscInt       i,j;
1733 
1734   PetscFunctionBegin;
1735   ierr = PetscMalloc1((ninterval+1)*ndegree,&Bv);CHKERRQ(ierr);
1736   /* Point evaluation of L_p on all the source vertices */
1737   ierr = PetscDTLegendreEval(ninterval+1,x,ndegree,degrees,Bv,NULL,NULL);CHKERRQ(ierr);
1738   /* Integral over each interval: \int_a^b L_p' = L_p(b)-L_p(a) */
1739   for (i=0; i<ninterval; i++) {
1740     for (j=0; j<ndegree; j++) {
1741       if (Transpose) B[i+ninterval*j] = Bv[(i+1)*ndegree+j] - Bv[i*ndegree+j];
1742       else           B[i*ndegree+j]   = Bv[(i+1)*ndegree+j] - Bv[i*ndegree+j];
1743     }
1744   }
1745   ierr = PetscFree(Bv);CHKERRQ(ierr);
1746   PetscFunctionReturn(0);
1747 }
1748 
1749 /*@
1750    PetscDTReconstructPoly - create matrix representing polynomial reconstruction using cell intervals and evaluation at target intervals
1751 
1752    Not Collective
1753 
1754    Input Arguments:
1755 +  degree - degree of reconstruction polynomial
1756 .  nsource - number of source intervals
1757 .  sourcex - sorted coordinates of source cell boundaries (length nsource+1)
1758 .  ntarget - number of target intervals
1759 -  targetx - sorted coordinates of target cell boundaries (length ntarget+1)
1760 
1761    Output Arguments:
1762 .  R - reconstruction matrix, utarget = sum_s R[t*nsource+s] * usource[s]
1763 
1764    Level: advanced
1765 
1766 .seealso: PetscDTLegendreEval()
1767 @*/
1768 PetscErrorCode PetscDTReconstructPoly(PetscInt degree,PetscInt nsource,const PetscReal *sourcex,PetscInt ntarget,const PetscReal *targetx,PetscReal *R)
1769 {
1770   PetscErrorCode ierr;
1771   PetscInt       i,j,k,*bdegrees,worksize;
1772   PetscReal      xmin,xmax,center,hscale,*sourcey,*targety,*Bsource,*Bsinv,*Btarget;
1773   PetscScalar    *tau,*work;
1774 
1775   PetscFunctionBegin;
1776   PetscValidRealPointer(sourcex,3);
1777   PetscValidRealPointer(targetx,5);
1778   PetscValidRealPointer(R,6);
1779   if (degree >= nsource) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Reconstruction degree %D must be less than number of source intervals %D",degree,nsource);
1780 #if defined(PETSC_USE_DEBUG)
1781   for (i=0; i<nsource; i++) {
1782     if (sourcex[i] >= sourcex[i+1]) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Source interval %D has negative orientation (%g,%g)",i,(double)sourcex[i],(double)sourcex[i+1]);
1783   }
1784   for (i=0; i<ntarget; i++) {
1785     if (targetx[i] >= targetx[i+1]) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Target interval %D has negative orientation (%g,%g)",i,(double)targetx[i],(double)targetx[i+1]);
1786   }
1787 #endif
1788   xmin = PetscMin(sourcex[0],targetx[0]);
1789   xmax = PetscMax(sourcex[nsource],targetx[ntarget]);
1790   center = (xmin + xmax)/2;
1791   hscale = (xmax - xmin)/2;
1792   worksize = nsource;
1793   ierr = PetscMalloc4(degree+1,&bdegrees,nsource+1,&sourcey,nsource*(degree+1),&Bsource,worksize,&work);CHKERRQ(ierr);
1794   ierr = PetscMalloc4(nsource,&tau,nsource*(degree+1),&Bsinv,ntarget+1,&targety,ntarget*(degree+1),&Btarget);CHKERRQ(ierr);
1795   for (i=0; i<=nsource; i++) sourcey[i] = (sourcex[i]-center)/hscale;
1796   for (i=0; i<=degree; i++) bdegrees[i] = i+1;
1797   ierr = PetscDTLegendreIntegrate(nsource,sourcey,degree+1,bdegrees,PETSC_TRUE,Bsource);CHKERRQ(ierr);
1798   ierr = PetscDTPseudoInverseQR(nsource,nsource,degree+1,Bsource,Bsinv,tau,nsource,work);CHKERRQ(ierr);
1799   for (i=0; i<=ntarget; i++) targety[i] = (targetx[i]-center)/hscale;
1800   ierr = PetscDTLegendreIntegrate(ntarget,targety,degree+1,bdegrees,PETSC_FALSE,Btarget);CHKERRQ(ierr);
1801   for (i=0; i<ntarget; i++) {
1802     PetscReal rowsum = 0;
1803     for (j=0; j<nsource; j++) {
1804       PetscReal sum = 0;
1805       for (k=0; k<degree+1; k++) {
1806         sum += Btarget[i*(degree+1)+k] * Bsinv[k*nsource+j];
1807       }
1808       R[i*nsource+j] = sum;
1809       rowsum += sum;
1810     }
1811     for (j=0; j<nsource; j++) R[i*nsource+j] /= rowsum; /* normalize each row */
1812   }
1813   ierr = PetscFree4(bdegrees,sourcey,Bsource,work);CHKERRQ(ierr);
1814   ierr = PetscFree4(tau,Bsinv,targety,Btarget);CHKERRQ(ierr);
1815   PetscFunctionReturn(0);
1816 }
1817 
1818 /*@C
1819    PetscGaussLobattoLegendreIntegrate - Compute the L2 integral of a function on the GLL points
1820 
1821    Not Collective
1822 
1823    Input Parameter:
1824 +  n - the number of GLL nodes
1825 .  nodes - the GLL nodes
1826 .  weights - the GLL weights
1827 .  f - the function values at the nodes
1828 
1829    Output Parameter:
1830 .  in - the value of the integral
1831 
1832    Level: beginner
1833 
1834 .seealso: PetscDTGaussLobattoLegendreQuadrature()
1835 
1836 @*/
1837 PetscErrorCode PetscGaussLobattoLegendreIntegrate(PetscInt n,PetscReal *nodes,PetscReal *weights,const PetscReal *f,PetscReal *in)
1838 {
1839   PetscInt          i;
1840 
1841   PetscFunctionBegin;
1842   *in = 0.;
1843   for (i=0; i<n; i++) {
1844     *in += f[i]*f[i]*weights[i];
1845   }
1846   PetscFunctionReturn(0);
1847 }
1848 
1849 /*@C
1850    PetscGaussLobattoLegendreElementLaplacianCreate - computes the Laplacian for a single 1d GLL element
1851 
1852    Not Collective
1853 
1854    Input Parameter:
1855 +  n - the number of GLL nodes
1856 .  nodes - the GLL nodes
1857 .  weights - the GLL weights
1858 
1859    Output Parameter:
1860 .  A - the stiffness element
1861 
1862    Level: beginner
1863 
1864    Notes:
1865     Destroy this with PetscGaussLobattoLegendreElementLaplacianDestroy()
1866 
1867    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)
1868 
1869 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianDestroy()
1870 
1871 @*/
1872 PetscErrorCode PetscGaussLobattoLegendreElementLaplacianCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
1873 {
1874   PetscReal        **A;
1875   PetscErrorCode  ierr;
1876   const PetscReal  *gllnodes = nodes;
1877   const PetscInt   p = n-1;
1878   PetscReal        z0,z1,z2 = -1,x,Lpj,Lpr;
1879   PetscInt         i,j,nn,r;
1880 
1881   PetscFunctionBegin;
1882   ierr = PetscMalloc1(n,&A);CHKERRQ(ierr);
1883   ierr = PetscMalloc1(n*n,&A[0]);CHKERRQ(ierr);
1884   for (i=1; i<n; i++) A[i] = A[i-1]+n;
1885 
1886   for (j=1; j<p; j++) {
1887     x  = gllnodes[j];
1888     z0 = 1.;
1889     z1 = x;
1890     for (nn=1; nn<p; nn++) {
1891       z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
1892       z0 = z1;
1893       z1 = z2;
1894     }
1895     Lpj=z2;
1896     for (r=1; r<p; r++) {
1897       if (r == j) {
1898         A[j][j]=2./(3.*(1.-gllnodes[j]*gllnodes[j])*Lpj*Lpj);
1899       } else {
1900         x  = gllnodes[r];
1901         z0 = 1.;
1902         z1 = x;
1903         for (nn=1; nn<p; nn++) {
1904           z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
1905           z0 = z1;
1906           z1 = z2;
1907         }
1908         Lpr     = z2;
1909         A[r][j] = 4./(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*Lpr*(gllnodes[j]-gllnodes[r])*(gllnodes[j]-gllnodes[r]));
1910       }
1911     }
1912   }
1913   for (j=1; j<p+1; j++) {
1914     x  = gllnodes[j];
1915     z0 = 1.;
1916     z1 = x;
1917     for (nn=1; nn<p; nn++) {
1918       z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
1919       z0 = z1;
1920       z1 = z2;
1921     }
1922     Lpj     = z2;
1923     A[j][0] = 4.*PetscPowRealInt(-1.,p)/(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*(1.+gllnodes[j])*(1.+gllnodes[j]));
1924     A[0][j] = A[j][0];
1925   }
1926   for (j=0; j<p; j++) {
1927     x  = gllnodes[j];
1928     z0 = 1.;
1929     z1 = x;
1930     for (nn=1; nn<p; nn++) {
1931       z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
1932       z0 = z1;
1933       z1 = z2;
1934     }
1935     Lpj=z2;
1936 
1937     A[p][j] = 4./(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*(1.-gllnodes[j])*(1.-gllnodes[j]));
1938     A[j][p] = A[p][j];
1939   }
1940   A[0][0]=0.5+(((PetscReal)p)*(((PetscReal)p)+1.)-2.)/6.;
1941   A[p][p]=A[0][0];
1942   *AA = A;
1943   PetscFunctionReturn(0);
1944 }
1945 
1946 /*@C
1947    PetscGaussLobattoLegendreElementLaplacianDestroy - frees the Laplacian for a single 1d GLL element
1948 
1949    Not Collective
1950 
1951    Input Parameter:
1952 +  n - the number of GLL nodes
1953 .  nodes - the GLL nodes
1954 .  weights - the GLL weightss
1955 -  A - the stiffness element
1956 
1957    Level: beginner
1958 
1959 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianCreate()
1960 
1961 @*/
1962 PetscErrorCode PetscGaussLobattoLegendreElementLaplacianDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
1963 {
1964   PetscErrorCode ierr;
1965 
1966   PetscFunctionBegin;
1967   ierr = PetscFree((*AA)[0]);CHKERRQ(ierr);
1968   ierr = PetscFree(*AA);CHKERRQ(ierr);
1969   *AA  = NULL;
1970   PetscFunctionReturn(0);
1971 }
1972 
1973 /*@C
1974    PetscGaussLobattoLegendreElementGradientCreate - computes the gradient for a single 1d GLL element
1975 
1976    Not Collective
1977 
1978    Input Parameter:
1979 +  n - the number of GLL nodes
1980 .  nodes - the GLL nodes
1981 .  weights - the GLL weights
1982 
1983    Output Parameter:
1984 .  AA - the stiffness element
1985 -  AAT - the transpose of AA (pass in NULL if you do not need this array)
1986 
1987    Level: beginner
1988 
1989    Notes:
1990     Destroy this with PetscGaussLobattoLegendreElementGradientDestroy()
1991 
1992    You can access entries in these arrays with AA[i][j] but in memory it is stored in contiguous memory, row oriented
1993 
1994 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianDestroy()
1995 
1996 @*/
1997 PetscErrorCode PetscGaussLobattoLegendreElementGradientCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA,PetscReal ***AAT)
1998 {
1999   PetscReal        **A, **AT = NULL;
2000   PetscErrorCode  ierr;
2001   const PetscReal  *gllnodes = nodes;
2002   const PetscInt   p = n-1;
2003   PetscReal        Li, Lj,d0;
2004   PetscInt         i,j;
2005 
2006   PetscFunctionBegin;
2007   ierr = PetscMalloc1(n,&A);CHKERRQ(ierr);
2008   ierr = PetscMalloc1(n*n,&A[0]);CHKERRQ(ierr);
2009   for (i=1; i<n; i++) A[i] = A[i-1]+n;
2010 
2011   if (AAT) {
2012     ierr = PetscMalloc1(n,&AT);CHKERRQ(ierr);
2013     ierr = PetscMalloc1(n*n,&AT[0]);CHKERRQ(ierr);
2014     for (i=1; i<n; i++) AT[i] = AT[i-1]+n;
2015   }
2016 
2017   if (n==1) {A[0][0] = 0.;}
2018   d0 = (PetscReal)p*((PetscReal)p+1.)/4.;
2019   for  (i=0; i<n; i++) {
2020     for  (j=0; j<n; j++) {
2021       A[i][j] = 0.;
2022       ierr = PetscDTComputeJacobi(0., 0., p, gllnodes[i], &Li);CHKERRQ(ierr);
2023       ierr = PetscDTComputeJacobi(0., 0., p, gllnodes[j], &Lj);CHKERRQ(ierr);
2024       if (i!=j)             A[i][j] = Li/(Lj*(gllnodes[i]-gllnodes[j]));
2025       if ((j==i) && (i==0)) A[i][j] = -d0;
2026       if (j==i && i==p)     A[i][j] = d0;
2027       if (AT) AT[j][i] = A[i][j];
2028     }
2029   }
2030   if (AAT) *AAT = AT;
2031   *AA  = A;
2032   PetscFunctionReturn(0);
2033 }
2034 
2035 /*@C
2036    PetscGaussLobattoLegendreElementGradientDestroy - frees the gradient for a single 1d GLL element obtained with PetscGaussLobattoLegendreElementGradientCreate()
2037 
2038    Not Collective
2039 
2040    Input Parameter:
2041 +  n - the number of GLL nodes
2042 .  nodes - the GLL nodes
2043 .  weights - the GLL weights
2044 .  AA - the stiffness element
2045 -  AAT - the transpose of the element
2046 
2047    Level: beginner
2048 
2049 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianCreate(), PetscGaussLobattoLegendreElementAdvectionCreate()
2050 
2051 @*/
2052 PetscErrorCode PetscGaussLobattoLegendreElementGradientDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA,PetscReal ***AAT)
2053 {
2054   PetscErrorCode ierr;
2055 
2056   PetscFunctionBegin;
2057   ierr = PetscFree((*AA)[0]);CHKERRQ(ierr);
2058   ierr = PetscFree(*AA);CHKERRQ(ierr);
2059   *AA  = NULL;
2060   if (*AAT) {
2061     ierr = PetscFree((*AAT)[0]);CHKERRQ(ierr);
2062     ierr = PetscFree(*AAT);CHKERRQ(ierr);
2063     *AAT  = NULL;
2064   }
2065   PetscFunctionReturn(0);
2066 }
2067 
2068 /*@C
2069    PetscGaussLobattoLegendreElementAdvectionCreate - computes the advection operator for a single 1d GLL element
2070 
2071    Not Collective
2072 
2073    Input Parameter:
2074 +  n - the number of GLL nodes
2075 .  nodes - the GLL nodes
2076 .  weights - the GLL weightss
2077 
2078    Output Parameter:
2079 .  AA - the stiffness element
2080 
2081    Level: beginner
2082 
2083    Notes:
2084     Destroy this with PetscGaussLobattoLegendreElementAdvectionDestroy()
2085 
2086    This is the same as the Gradient operator multiplied by the diagonal mass matrix
2087 
2088    You can access entries in this array with AA[i][j] but in memory it is stored in contiguous memory, row oriented
2089 
2090 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianCreate(), PetscGaussLobattoLegendreElementAdvectionDestroy()
2091 
2092 @*/
2093 PetscErrorCode PetscGaussLobattoLegendreElementAdvectionCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2094 {
2095   PetscReal       **D;
2096   PetscErrorCode  ierr;
2097   const PetscReal  *gllweights = weights;
2098   const PetscInt   glln = n;
2099   PetscInt         i,j;
2100 
2101   PetscFunctionBegin;
2102   ierr = PetscGaussLobattoLegendreElementGradientCreate(n,nodes,weights,&D,NULL);CHKERRQ(ierr);
2103   for (i=0; i<glln; i++){
2104     for (j=0; j<glln; j++) {
2105       D[i][j] = gllweights[i]*D[i][j];
2106     }
2107   }
2108   *AA = D;
2109   PetscFunctionReturn(0);
2110 }
2111 
2112 /*@C
2113    PetscGaussLobattoLegendreElementAdvectionDestroy - frees the advection stiffness for a single 1d GLL element
2114 
2115    Not Collective
2116 
2117    Input Parameter:
2118 +  n - the number of GLL nodes
2119 .  nodes - the GLL nodes
2120 .  weights - the GLL weights
2121 -  A - advection
2122 
2123    Level: beginner
2124 
2125 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementAdvectionCreate()
2126 
2127 @*/
2128 PetscErrorCode PetscGaussLobattoLegendreElementAdvectionDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2129 {
2130   PetscErrorCode ierr;
2131 
2132   PetscFunctionBegin;
2133   ierr = PetscFree((*AA)[0]);CHKERRQ(ierr);
2134   ierr = PetscFree(*AA);CHKERRQ(ierr);
2135   *AA  = NULL;
2136   PetscFunctionReturn(0);
2137 }
2138 
2139 PetscErrorCode PetscGaussLobattoLegendreElementMassCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2140 {
2141   PetscReal        **A;
2142   PetscErrorCode  ierr;
2143   const PetscReal  *gllweights = weights;
2144   const PetscInt   glln = n;
2145   PetscInt         i,j;
2146 
2147   PetscFunctionBegin;
2148   ierr = PetscMalloc1(glln,&A);CHKERRQ(ierr);
2149   ierr = PetscMalloc1(glln*glln,&A[0]);CHKERRQ(ierr);
2150   for (i=1; i<glln; i++) A[i] = A[i-1]+glln;
2151   if (glln==1) {A[0][0] = 0.;}
2152   for  (i=0; i<glln; i++) {
2153     for  (j=0; j<glln; j++) {
2154       A[i][j] = 0.;
2155       if (j==i)     A[i][j] = gllweights[i];
2156     }
2157   }
2158   *AA  = A;
2159   PetscFunctionReturn(0);
2160 }
2161 
2162 PetscErrorCode PetscGaussLobattoLegendreElementMassDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2163 {
2164   PetscErrorCode ierr;
2165 
2166   PetscFunctionBegin;
2167   ierr = PetscFree((*AA)[0]);CHKERRQ(ierr);
2168   ierr = PetscFree(*AA);CHKERRQ(ierr);
2169   *AA  = NULL;
2170   PetscFunctionReturn(0);
2171 }
2172