xref: /petsc/src/dm/dt/interface/dt.c (revision 445d9d7940d4919037aab5848bb34f2b756ffbc0)
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 GaussCite       = PETSC_FALSE;
16 const char       GaussCitation[] = "@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 /*@
26   PetscQuadratureCreate - Create a PetscQuadrature object
27 
28   Collective
29 
30   Input Parameter:
31 . comm - The communicator for the PetscQuadrature object
32 
33   Output Parameter:
34 . q  - The PetscQuadrature object
35 
36   Level: beginner
37 
38 .seealso: PetscQuadratureDestroy(), PetscQuadratureGetData()
39 @*/
40 PetscErrorCode PetscQuadratureCreate(MPI_Comm comm, PetscQuadrature *q)
41 {
42   PetscErrorCode ierr;
43 
44   PetscFunctionBegin;
45   PetscValidPointer(q, 2);
46   ierr = PetscSysInitializePackage();CHKERRQ(ierr);
47   ierr = PetscHeaderCreate(*q,PETSC_OBJECT_CLASSID,"PetscQuadrature","Quadrature","DT",comm,PetscQuadratureDestroy,PetscQuadratureView);CHKERRQ(ierr);
48   (*q)->dim       = -1;
49   (*q)->Nc        =  1;
50   (*q)->order     = -1;
51   (*q)->numPoints = 0;
52   (*q)->points    = NULL;
53   (*q)->weights   = NULL;
54   PetscFunctionReturn(0);
55 }
56 
57 /*@
58   PetscQuadratureDuplicate - Create a deep copy of the PetscQuadrature object
59 
60   Collective on q
61 
62   Input Parameter:
63 . q  - The PetscQuadrature object
64 
65   Output Parameter:
66 . r  - The new PetscQuadrature object
67 
68   Level: beginner
69 
70 .seealso: PetscQuadratureCreate(), PetscQuadratureDestroy(), PetscQuadratureGetData()
71 @*/
72 PetscErrorCode PetscQuadratureDuplicate(PetscQuadrature q, PetscQuadrature *r)
73 {
74   PetscInt         order, dim, Nc, Nq;
75   const PetscReal *points, *weights;
76   PetscReal       *p, *w;
77   PetscErrorCode   ierr;
78 
79   PetscFunctionBegin;
80   PetscValidPointer(q, 2);
81   ierr = PetscQuadratureCreate(PetscObjectComm((PetscObject) q), r);CHKERRQ(ierr);
82   ierr = PetscQuadratureGetOrder(q, &order);CHKERRQ(ierr);
83   ierr = PetscQuadratureSetOrder(*r, order);CHKERRQ(ierr);
84   ierr = PetscQuadratureGetData(q, &dim, &Nc, &Nq, &points, &weights);CHKERRQ(ierr);
85   ierr = PetscMalloc1(Nq*dim, &p);CHKERRQ(ierr);
86   ierr = PetscMalloc1(Nq*Nc, &w);CHKERRQ(ierr);
87   ierr = PetscArraycpy(p, points, Nq*dim);CHKERRQ(ierr);
88   ierr = PetscArraycpy(w, weights, Nc * Nq);CHKERRQ(ierr);
89   ierr = PetscQuadratureSetData(*r, dim, Nc, Nq, p, w);CHKERRQ(ierr);
90   PetscFunctionReturn(0);
91 }
92 
93 /*@
94   PetscQuadratureDestroy - Destroys a PetscQuadrature object
95 
96   Collective on q
97 
98   Input Parameter:
99 . q  - The PetscQuadrature object
100 
101   Level: beginner
102 
103 .seealso: PetscQuadratureCreate(), PetscQuadratureGetData()
104 @*/
105 PetscErrorCode PetscQuadratureDestroy(PetscQuadrature *q)
106 {
107   PetscErrorCode ierr;
108 
109   PetscFunctionBegin;
110   if (!*q) PetscFunctionReturn(0);
111   PetscValidHeaderSpecific((*q),PETSC_OBJECT_CLASSID,1);
112   if (--((PetscObject)(*q))->refct > 0) {
113     *q = NULL;
114     PetscFunctionReturn(0);
115   }
116   ierr = PetscFree((*q)->points);CHKERRQ(ierr);
117   ierr = PetscFree((*q)->weights);CHKERRQ(ierr);
118   ierr = PetscHeaderDestroy(q);CHKERRQ(ierr);
119   PetscFunctionReturn(0);
120 }
121 
122 /*@
123   PetscQuadratureGetOrder - Return the order of the method
124 
125   Not collective
126 
127   Input Parameter:
128 . q - The PetscQuadrature object
129 
130   Output Parameter:
131 . order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated
132 
133   Level: intermediate
134 
135 .seealso: PetscQuadratureSetOrder(), PetscQuadratureGetData(), PetscQuadratureSetData()
136 @*/
137 PetscErrorCode PetscQuadratureGetOrder(PetscQuadrature q, PetscInt *order)
138 {
139   PetscFunctionBegin;
140   PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1);
141   PetscValidPointer(order, 2);
142   *order = q->order;
143   PetscFunctionReturn(0);
144 }
145 
146 /*@
147   PetscQuadratureSetOrder - Return the order of the method
148 
149   Not collective
150 
151   Input Parameters:
152 + q - The PetscQuadrature object
153 - order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated
154 
155   Level: intermediate
156 
157 .seealso: PetscQuadratureGetOrder(), PetscQuadratureGetData(), PetscQuadratureSetData()
158 @*/
159 PetscErrorCode PetscQuadratureSetOrder(PetscQuadrature q, PetscInt order)
160 {
161   PetscFunctionBegin;
162   PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1);
163   q->order = order;
164   PetscFunctionReturn(0);
165 }
166 
167 /*@
168   PetscQuadratureGetNumComponents - Return the number of components for functions to be integrated
169 
170   Not collective
171 
172   Input Parameter:
173 . q - The PetscQuadrature object
174 
175   Output Parameter:
176 . Nc - The number of components
177 
178   Note: We are performing an integral int f(x) . w(x) dx, where both f and w (the weight) have Nc components.
179 
180   Level: intermediate
181 
182 .seealso: PetscQuadratureSetNumComponents(), PetscQuadratureGetData(), PetscQuadratureSetData()
183 @*/
184 PetscErrorCode PetscQuadratureGetNumComponents(PetscQuadrature q, PetscInt *Nc)
185 {
186   PetscFunctionBegin;
187   PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1);
188   PetscValidPointer(Nc, 2);
189   *Nc = q->Nc;
190   PetscFunctionReturn(0);
191 }
192 
193 /*@
194   PetscQuadratureSetNumComponents - Return the number of components for functions to be integrated
195 
196   Not collective
197 
198   Input Parameters:
199 + q  - The PetscQuadrature object
200 - Nc - The number of components
201 
202   Note: We are performing an integral int f(x) . w(x) dx, where both f and w (the weight) have Nc components.
203 
204   Level: intermediate
205 
206 .seealso: PetscQuadratureGetNumComponents(), PetscQuadratureGetData(), PetscQuadratureSetData()
207 @*/
208 PetscErrorCode PetscQuadratureSetNumComponents(PetscQuadrature q, PetscInt Nc)
209 {
210   PetscFunctionBegin;
211   PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1);
212   q->Nc = Nc;
213   PetscFunctionReturn(0);
214 }
215 
216 /*@C
217   PetscQuadratureGetData - Returns the data defining the quadrature
218 
219   Not collective
220 
221   Input Parameter:
222 . q  - The PetscQuadrature object
223 
224   Output Parameters:
225 + dim - The spatial dimension
226 . Nc - The number of components
227 . npoints - The number of quadrature points
228 . points - The coordinates of each quadrature point
229 - weights - The weight of each quadrature point
230 
231   Level: intermediate
232 
233   Fortran Notes:
234     From Fortran you must call PetscQuadratureRestoreData() when you are done with the data
235 
236 .seealso: PetscQuadratureCreate(), PetscQuadratureSetData()
237 @*/
238 PetscErrorCode PetscQuadratureGetData(PetscQuadrature q, PetscInt *dim, PetscInt *Nc, PetscInt *npoints, const PetscReal *points[], const PetscReal *weights[])
239 {
240   PetscFunctionBegin;
241   PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1);
242   if (dim) {
243     PetscValidPointer(dim, 2);
244     *dim = q->dim;
245   }
246   if (Nc) {
247     PetscValidPointer(Nc, 3);
248     *Nc = q->Nc;
249   }
250   if (npoints) {
251     PetscValidPointer(npoints, 4);
252     *npoints = q->numPoints;
253   }
254   if (points) {
255     PetscValidPointer(points, 5);
256     *points = q->points;
257   }
258   if (weights) {
259     PetscValidPointer(weights, 6);
260     *weights = q->weights;
261   }
262   PetscFunctionReturn(0);
263 }
264 
265 /*@C
266   PetscQuadratureSetData - Sets the data defining the quadrature
267 
268   Not collective
269 
270   Input Parameters:
271 + q  - The PetscQuadrature object
272 . dim - The spatial dimension
273 . Nc - The number of components
274 . npoints - The number of quadrature points
275 . points - The coordinates of each quadrature point
276 - weights - The weight of each quadrature point
277 
278   Note: This routine owns the references to points and weights, so they must be allocated using PetscMalloc() and the user should not free them.
279 
280   Level: intermediate
281 
282 .seealso: PetscQuadratureCreate(), PetscQuadratureGetData()
283 @*/
284 PetscErrorCode PetscQuadratureSetData(PetscQuadrature q, PetscInt dim, PetscInt Nc, PetscInt npoints, const PetscReal points[], const PetscReal weights[])
285 {
286   PetscFunctionBegin;
287   PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1);
288   if (dim >= 0)     q->dim       = dim;
289   if (Nc >= 0)      q->Nc        = Nc;
290   if (npoints >= 0) q->numPoints = npoints;
291   if (points) {
292     PetscValidPointer(points, 4);
293     q->points = points;
294   }
295   if (weights) {
296     PetscValidPointer(weights, 5);
297     q->weights = weights;
298   }
299   PetscFunctionReturn(0);
300 }
301 
302 static PetscErrorCode PetscQuadratureView_Ascii(PetscQuadrature quad, PetscViewer v)
303 {
304   PetscInt          q, d, c;
305   PetscViewerFormat format;
306   PetscErrorCode    ierr;
307 
308   PetscFunctionBegin;
309   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);}
310   else              {ierr = PetscViewerASCIIPrintf(v, "Quadrature of order %D on %D points (dim %D)\n", quad->order, quad->numPoints, quad->dim);CHKERRQ(ierr);}
311   ierr = PetscViewerGetFormat(v, &format);CHKERRQ(ierr);
312   if (format != PETSC_VIEWER_ASCII_INFO_DETAIL) PetscFunctionReturn(0);
313   for (q = 0; q < quad->numPoints; ++q) {
314     ierr = PetscViewerASCIIPrintf(v, "p%D (", q);CHKERRQ(ierr);
315     ierr = PetscViewerASCIIUseTabs(v, PETSC_FALSE);CHKERRQ(ierr);
316     for (d = 0; d < quad->dim; ++d) {
317       if (d) {ierr = PetscViewerASCIIPrintf(v, ", ");CHKERRQ(ierr);}
318       ierr = PetscViewerASCIIPrintf(v, "%+g", (double)quad->points[q*quad->dim+d]);CHKERRQ(ierr);
319     }
320     ierr = PetscViewerASCIIPrintf(v, ") ");CHKERRQ(ierr);
321     if (quad->Nc > 1) {ierr = PetscViewerASCIIPrintf(v, "w%D (", q);CHKERRQ(ierr);}
322     for (c = 0; c < quad->Nc; ++c) {
323       if (c) {ierr = PetscViewerASCIIPrintf(v, ", ");CHKERRQ(ierr);}
324       ierr = PetscViewerASCIIPrintf(v, "%+g", (double)quad->weights[q*quad->Nc+c]);CHKERRQ(ierr);
325     }
326     if (quad->Nc > 1) {ierr = PetscViewerASCIIPrintf(v, ")");CHKERRQ(ierr);}
327     ierr = PetscViewerASCIIPrintf(v, "\n");CHKERRQ(ierr);
328     ierr = PetscViewerASCIIUseTabs(v, PETSC_TRUE);CHKERRQ(ierr);
329   }
330   PetscFunctionReturn(0);
331 }
332 
333 /*@C
334   PetscQuadratureView - Views a PetscQuadrature object
335 
336   Collective on quad
337 
338   Input Parameters:
339 + quad  - The PetscQuadrature object
340 - viewer - The PetscViewer object
341 
342   Level: beginner
343 
344 .seealso: PetscQuadratureCreate(), PetscQuadratureGetData()
345 @*/
346 PetscErrorCode PetscQuadratureView(PetscQuadrature quad, PetscViewer viewer)
347 {
348   PetscBool      iascii;
349   PetscErrorCode ierr;
350 
351   PetscFunctionBegin;
352   PetscValidHeader(quad, 1);
353   if (viewer) PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
354   if (!viewer) {ierr = PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject) quad), &viewer);CHKERRQ(ierr);}
355   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii);CHKERRQ(ierr);
356   ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
357   if (iascii) {ierr = PetscQuadratureView_Ascii(quad, viewer);CHKERRQ(ierr);}
358   ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
359   PetscFunctionReturn(0);
360 }
361 
362 /*@C
363   PetscQuadratureExpandComposite - Return a quadrature over the composite element, which has the original quadrature in each subelement
364 
365   Not collective
366 
367   Input Parameter:
368 + q - The original PetscQuadrature
369 . numSubelements - The number of subelements the original element is divided into
370 . v0 - An array of the initial points for each subelement
371 - jac - An array of the Jacobian mappings from the reference to each subelement
372 
373   Output Parameters:
374 . dim - The dimension
375 
376   Note: Together v0 and jac define an affine mapping from the original reference element to each subelement
377 
378  Not available from Fortran
379 
380   Level: intermediate
381 
382 .seealso: PetscFECreate(), PetscSpaceGetDimension(), PetscDualSpaceGetDimension()
383 @*/
384 PetscErrorCode PetscQuadratureExpandComposite(PetscQuadrature q, PetscInt numSubelements, const PetscReal v0[], const PetscReal jac[], PetscQuadrature *qref)
385 {
386   const PetscReal *points,    *weights;
387   PetscReal       *pointsRef, *weightsRef;
388   PetscInt         dim, Nc, order, npoints, npointsRef, c, p, cp, d, e;
389   PetscErrorCode   ierr;
390 
391   PetscFunctionBegin;
392   PetscValidHeaderSpecific(q, PETSC_OBJECT_CLASSID, 1);
393   PetscValidPointer(v0, 3);
394   PetscValidPointer(jac, 4);
395   PetscValidPointer(qref, 5);
396   ierr = PetscQuadratureCreate(PETSC_COMM_SELF, qref);CHKERRQ(ierr);
397   ierr = PetscQuadratureGetOrder(q, &order);CHKERRQ(ierr);
398   ierr = PetscQuadratureGetData(q, &dim, &Nc, &npoints, &points, &weights);CHKERRQ(ierr);
399   npointsRef = npoints*numSubelements;
400   ierr = PetscMalloc1(npointsRef*dim,&pointsRef);CHKERRQ(ierr);
401   ierr = PetscMalloc1(npointsRef*Nc, &weightsRef);CHKERRQ(ierr);
402   for (c = 0; c < numSubelements; ++c) {
403     for (p = 0; p < npoints; ++p) {
404       for (d = 0; d < dim; ++d) {
405         pointsRef[(c*npoints + p)*dim+d] = v0[c*dim+d];
406         for (e = 0; e < dim; ++e) {
407           pointsRef[(c*npoints + p)*dim+d] += jac[(c*dim + d)*dim+e]*(points[p*dim+e] + 1.0);
408         }
409       }
410       /* Could also use detJ here */
411       for (cp = 0; cp < Nc; ++cp) weightsRef[(c*npoints+p)*Nc+cp] = weights[p*Nc+cp]/numSubelements;
412     }
413   }
414   ierr = PetscQuadratureSetOrder(*qref, order);CHKERRQ(ierr);
415   ierr = PetscQuadratureSetData(*qref, dim, Nc, npointsRef, pointsRef, weightsRef);CHKERRQ(ierr);
416   PetscFunctionReturn(0);
417 }
418 
419 /*@
420    PetscDTLegendreEval - evaluate Legendre polynomial at points
421 
422    Not Collective
423 
424    Input Arguments:
425 +  npoints - number of spatial points to evaluate at
426 .  points - array of locations to evaluate at
427 .  ndegree - number of basis degrees to evaluate
428 -  degrees - sorted array of degrees to evaluate
429 
430    Output Arguments:
431 +  B - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension npoints*ndegrees, allocated by caller) (or NULL)
432 .  D - row-oriented derivative evaluation matrix (or NULL)
433 -  D2 - row-oriented second derivative evaluation matrix (or NULL)
434 
435    Level: intermediate
436 
437 .seealso: PetscDTGaussQuadrature()
438 @*/
439 PetscErrorCode PetscDTLegendreEval(PetscInt npoints,const PetscReal *points,PetscInt ndegree,const PetscInt *degrees,PetscReal *B,PetscReal *D,PetscReal *D2)
440 {
441   PetscInt i,maxdegree;
442 
443   PetscFunctionBegin;
444   if (!npoints || !ndegree) PetscFunctionReturn(0);
445   maxdegree = degrees[ndegree-1];
446   for (i=0; i<npoints; i++) {
447     PetscReal pm1,pm2,pd1,pd2,pdd1,pdd2,x;
448     PetscInt  j,k;
449     x    = points[i];
450     pm2  = 0;
451     pm1  = 1;
452     pd2  = 0;
453     pd1  = 0;
454     pdd2 = 0;
455     pdd1 = 0;
456     k    = 0;
457     if (degrees[k] == 0) {
458       if (B) B[i*ndegree+k] = pm1;
459       if (D) D[i*ndegree+k] = pd1;
460       if (D2) D2[i*ndegree+k] = pdd1;
461       k++;
462     }
463     for (j=1; j<=maxdegree; j++,k++) {
464       PetscReal p,d,dd;
465       p    = ((2*j-1)*x*pm1 - (j-1)*pm2)/j;
466       d    = pd2 + (2*j-1)*pm1;
467       dd   = pdd2 + (2*j-1)*pd1;
468       pm2  = pm1;
469       pm1  = p;
470       pd2  = pd1;
471       pd1  = d;
472       pdd2 = pdd1;
473       pdd1 = dd;
474       if (degrees[k] == j) {
475         if (B) B[i*ndegree+k] = p;
476         if (D) D[i*ndegree+k] = d;
477         if (D2) D2[i*ndegree+k] = dd;
478       }
479     }
480   }
481   PetscFunctionReturn(0);
482 }
483 
484 /*@
485    PetscDTGaussQuadrature - create Gauss quadrature
486 
487    Not Collective
488 
489    Input Arguments:
490 +  npoints - number of points
491 .  a - left end of interval (often-1)
492 -  b - right end of interval (often +1)
493 
494    Output Arguments:
495 +  x - quadrature points
496 -  w - quadrature weights
497 
498    Level: intermediate
499 
500    References:
501 .   1. - Golub and Welsch, Calculation of Quadrature Rules, Math. Comp. 23(106), 1969.
502 
503 .seealso: PetscDTLegendreEval()
504 @*/
505 PetscErrorCode PetscDTGaussQuadrature(PetscInt npoints,PetscReal a,PetscReal b,PetscReal *x,PetscReal *w)
506 {
507   PetscErrorCode ierr;
508   PetscInt       i;
509   PetscReal      *work;
510   PetscScalar    *Z;
511   PetscBLASInt   N,LDZ,info;
512 
513   PetscFunctionBegin;
514   ierr = PetscCitationsRegister(GaussCitation, &GaussCite);CHKERRQ(ierr);
515   /* Set up the Golub-Welsch system */
516   for (i=0; i<npoints; i++) {
517     x[i] = 0;                   /* diagonal is 0 */
518     if (i) w[i-1] = 0.5 / PetscSqrtReal(1 - 1./PetscSqr(2*i));
519   }
520   ierr = PetscMalloc2(npoints*npoints,&Z,PetscMax(1,2*npoints-2),&work);CHKERRQ(ierr);
521   ierr = PetscBLASIntCast(npoints,&N);CHKERRQ(ierr);
522   LDZ  = N;
523   ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr);
524 #if defined(PETSC_MISSING_LAPACK_STEQR)
525   SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"STEQR - Lapack routine is unavailable.");
526 #else
527   PetscStackCallBLAS("LAPACKsteqr",LAPACKsteqr_("I",&N,x,w,Z,&LDZ,work,&info));
528 #endif
529   ierr = PetscFPTrapPop();CHKERRQ(ierr);
530   if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"xSTEQR error");
531 
532   for (i=0; i<(npoints+1)/2; i++) {
533     PetscReal y = 0.5 * (-x[i] + x[npoints-i-1]); /* enforces symmetry */
534     x[i]           = (a+b)/2 - y*(b-a)/2;
535     if (x[i] == -0.0) x[i] = 0.0;
536     x[npoints-i-1] = (a+b)/2 + y*(b-a)/2;
537 
538     w[i] = w[npoints-1-i] = 0.5*(b-a)*(PetscSqr(PetscAbsScalar(Z[i*npoints])) + PetscSqr(PetscAbsScalar(Z[(npoints-i-1)*npoints])));
539   }
540   ierr = PetscFree2(Z,work);CHKERRQ(ierr);
541   PetscFunctionReturn(0);
542 }
543 
544 static void qAndLEvaluation(PetscInt n, PetscReal x, PetscReal *q, PetscReal *qp, PetscReal *Ln)
545 /*
546   Compute the polynomial q(x) = L_{N+1}(x) - L_{n-1}(x) and its derivative in
547   addition to L_N(x) as these are needed for computing the GLL points via Newton's method.
548   Reference: "Implementing Spectral Methods for Partial Differential Equations: Algorithms
549   for Scientists and Engineers" by David A. Kopriva.
550 */
551 {
552   PetscInt k;
553 
554   PetscReal Lnp;
555   PetscReal Lnp1, Lnp1p;
556   PetscReal Lnm1, Lnm1p;
557   PetscReal Lnm2, Lnm2p;
558 
559   Lnm1  = 1.0;
560   *Ln   = x;
561   Lnm1p = 0.0;
562   Lnp   = 1.0;
563 
564   for (k=2; k<=n; ++k) {
565     Lnm2  = Lnm1;
566     Lnm1  = *Ln;
567     Lnm2p = Lnm1p;
568     Lnm1p = Lnp;
569     *Ln   = (2.*((PetscReal)k)-1.)/(1.0*((PetscReal)k))*x*Lnm1 - (((PetscReal)k)-1.)/((PetscReal)k)*Lnm2;
570     Lnp   = Lnm2p + (2.0*((PetscReal)k)-1.)*Lnm1;
571   }
572   k     = n+1;
573   Lnp1  = (2.*((PetscReal)k)-1.)/(((PetscReal)k))*x*(*Ln) - (((PetscReal)k)-1.)/((PetscReal)k)*Lnm1;
574   Lnp1p = Lnm1p + (2.0*((PetscReal)k)-1.)*(*Ln);
575   *q    = Lnp1 - Lnm1;
576   *qp   = Lnp1p - Lnm1p;
577 }
578 
579 /*@C
580    PetscDTGaussLobattoLegendreQuadrature - creates a set of the locations and weights of the Gauss-Lobatto-Legendre
581                       nodes of a given size on the domain [-1,1]
582 
583    Not Collective
584 
585    Input Parameter:
586 +  n - number of grid nodes
587 -  type - PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA or PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON
588 
589    Output Arguments:
590 +  x - quadrature points
591 -  w - quadrature weights
592 
593    Notes:
594     For n > 30  the Newton approach computes duplicate (incorrect) values for some nodes because the initial guess is apparently not
595           close enough to the desired solution
596 
597    These are useful for implementing spectral methods based on Gauss-Lobatto-Legendre (GLL) nodes
598 
599    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
600 
601    Level: intermediate
602 
603 .seealso: PetscDTGaussQuadrature()
604 
605 @*/
606 PetscErrorCode PetscDTGaussLobattoLegendreQuadrature(PetscInt npoints,PetscGaussLobattoLegendreCreateType type,PetscReal *x,PetscReal *w)
607 {
608   PetscErrorCode ierr;
609 
610   PetscFunctionBegin;
611   if (npoints < 2) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Must provide at least 2 grid points per element");
612 
613   if (type == PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA) {
614     PetscReal      *M,si;
615     PetscBLASInt   bn,lierr;
616     PetscReal      x0,z0,z1,z2;
617     PetscInt       i,p = npoints - 1,nn;
618 
619     x[0]   =-1.0;
620     x[npoints-1] = 1.0;
621     if (npoints-2 > 0){
622       ierr = PetscMalloc1(npoints-1,&M);CHKERRQ(ierr);
623       for (i=0; i<npoints-2; i++) {
624         si  = ((PetscReal)i)+1.0;
625         M[i]=0.5*PetscSqrtReal(si*(si+2.0)/((si+0.5)*(si+1.5)));
626       }
627       ierr = PetscBLASIntCast(npoints-2,&bn);CHKERRQ(ierr);
628       ierr = PetscArrayzero(&x[1],bn);CHKERRQ(ierr);
629       ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr);
630       x0=0;
631 #if defined(PETSC_MISSING_LAPACK_STEQR)
632       SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"STEQR - Lapack routine is unavailable.");
633 #else
634       PetscStackCallBLAS("LAPACKsteqr",LAPACKREALsteqr_("N",&bn,&x[1],M,&x0,&bn,M,&lierr));
635       if (lierr) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error in STERF Lapack routine %d",(int)lierr);
636 #endif
637       ierr = PetscFPTrapPop();CHKERRQ(ierr);
638       ierr = PetscFree(M);CHKERRQ(ierr);
639     }
640     if ((npoints-1)%2==0) {
641       x[(npoints-1)/2]   = 0.0; /* hard wire to exactly 0.0 since linear algebra produces nonzero */
642     }
643 
644     w[0] = w[p] = 2.0/(((PetscReal)(p))*(((PetscReal)p)+1.0));
645     z2 = -1.;                      /* Dummy value to avoid -Wmaybe-initialized */
646     for (i=1; i<p; i++) {
647       x0  = x[i];
648       z0 = 1.0;
649       z1 = x0;
650       for (nn=1; nn<p; nn++) {
651         z2 = x0*z1*(2.0*((PetscReal)nn)+1.0)/(((PetscReal)nn)+1.0)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.0));
652         z0 = z1;
653         z1 = z2;
654       }
655       w[i]=2.0/(((PetscReal)p)*(((PetscReal)p)+1.0)*z2*z2);
656     }
657   } else {
658     PetscInt  j,m;
659     PetscReal z1,z,q,qp,Ln;
660     PetscReal *pt;
661     ierr = PetscMalloc1(npoints,&pt);CHKERRQ(ierr);
662 
663     if (npoints > 30) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON produces incorrect answers for n > 30");
664     x[0]     = -1.0;
665     x[npoints-1]   = 1.0;
666     w[0]   = w[npoints-1] = 2./(((PetscReal)npoints)*(((PetscReal)npoints)-1.0));
667     m  = (npoints-1)/2; /* The roots are symmetric, so we only find half of them. */
668     for (j=1; j<=m; j++) { /* Loop over the desired roots. */
669       z = -1.0*PetscCosReal((PETSC_PI*((PetscReal)j)+0.25)/(((PetscReal)npoints)-1.0))-(3.0/(8.0*(((PetscReal)npoints)-1.0)*PETSC_PI))*(1.0/(((PetscReal)j)+0.25));
670       /* Starting with the above approximation to the ith root, we enter */
671       /* the main loop of refinement by Newton's method.                 */
672       do {
673         qAndLEvaluation(npoints-1,z,&q,&qp,&Ln);
674         z1 = z;
675         z  = z1-q/qp; /* Newton's method. */
676       } while (PetscAbs(z-z1) > 10.*PETSC_MACHINE_EPSILON);
677       qAndLEvaluation(npoints-1,z,&q,&qp,&Ln);
678 
679       x[j]       = z;
680       x[npoints-1-j]   = -z;      /* and put in its symmetric counterpart.   */
681       w[j]     = 2.0/(((PetscReal)npoints)*(((PetscReal)npoints)-1.)*Ln*Ln);  /* Compute the weight */
682       w[npoints-1-j] = w[j];                 /* and its symmetric counterpart. */
683       pt[j]=qp;
684     }
685 
686     if ((npoints-1)%2==0) {
687       qAndLEvaluation(npoints-1,0.0,&q,&qp,&Ln);
688       x[(npoints-1)/2]   = 0.0;
689       w[(npoints-1)/2] = 2.0/(((PetscReal)npoints)*(((PetscReal)npoints)-1.)*Ln*Ln);
690     }
691     ierr = PetscFree(pt);CHKERRQ(ierr);
692   }
693   PetscFunctionReturn(0);
694 }
695 
696 /*@
697   PetscDTGaussTensorQuadrature - creates a tensor-product Gauss quadrature
698 
699   Not Collective
700 
701   Input Arguments:
702 + dim     - The spatial dimension
703 . Nc      - The number of components
704 . npoints - number of points in one dimension
705 . a       - left end of interval (often-1)
706 - b       - right end of interval (often +1)
707 
708   Output Argument:
709 . q - A PetscQuadrature object
710 
711   Level: intermediate
712 
713 .seealso: PetscDTGaussQuadrature(), PetscDTLegendreEval()
714 @*/
715 PetscErrorCode PetscDTGaussTensorQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
716 {
717   PetscInt       totpoints = dim > 1 ? dim > 2 ? npoints*PetscSqr(npoints) : PetscSqr(npoints) : npoints, i, j, k, c;
718   PetscReal     *x, *w, *xw, *ww;
719   PetscErrorCode ierr;
720 
721   PetscFunctionBegin;
722   ierr = PetscMalloc1(totpoints*dim,&x);CHKERRQ(ierr);
723   ierr = PetscMalloc1(totpoints*Nc,&w);CHKERRQ(ierr);
724   /* Set up the Golub-Welsch system */
725   switch (dim) {
726   case 0:
727     ierr = PetscFree(x);CHKERRQ(ierr);
728     ierr = PetscFree(w);CHKERRQ(ierr);
729     ierr = PetscMalloc1(1, &x);CHKERRQ(ierr);
730     ierr = PetscMalloc1(Nc, &w);CHKERRQ(ierr);
731     x[0] = 0.0;
732     for (c = 0; c < Nc; ++c) w[c] = 1.0;
733     break;
734   case 1:
735     ierr = PetscMalloc1(npoints,&ww);CHKERRQ(ierr);
736     ierr = PetscDTGaussQuadrature(npoints, a, b, x, ww);CHKERRQ(ierr);
737     for (i = 0; i < npoints; ++i) for (c = 0; c < Nc; ++c) w[i*Nc+c] = ww[i];
738     ierr = PetscFree(ww);CHKERRQ(ierr);
739     break;
740   case 2:
741     ierr = PetscMalloc2(npoints,&xw,npoints,&ww);CHKERRQ(ierr);
742     ierr = PetscDTGaussQuadrature(npoints, a, b, xw, ww);CHKERRQ(ierr);
743     for (i = 0; i < npoints; ++i) {
744       for (j = 0; j < npoints; ++j) {
745         x[(i*npoints+j)*dim+0] = xw[i];
746         x[(i*npoints+j)*dim+1] = xw[j];
747         for (c = 0; c < Nc; ++c) w[(i*npoints+j)*Nc+c] = ww[i] * ww[j];
748       }
749     }
750     ierr = PetscFree2(xw,ww);CHKERRQ(ierr);
751     break;
752   case 3:
753     ierr = PetscMalloc2(npoints,&xw,npoints,&ww);CHKERRQ(ierr);
754     ierr = PetscDTGaussQuadrature(npoints, a, b, xw, ww);CHKERRQ(ierr);
755     for (i = 0; i < npoints; ++i) {
756       for (j = 0; j < npoints; ++j) {
757         for (k = 0; k < npoints; ++k) {
758           x[((i*npoints+j)*npoints+k)*dim+0] = xw[i];
759           x[((i*npoints+j)*npoints+k)*dim+1] = xw[j];
760           x[((i*npoints+j)*npoints+k)*dim+2] = xw[k];
761           for (c = 0; c < Nc; ++c) w[((i*npoints+j)*npoints+k)*Nc+c] = ww[i] * ww[j] * ww[k];
762         }
763       }
764     }
765     ierr = PetscFree2(xw,ww);CHKERRQ(ierr);
766     break;
767   default:
768     SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %d", dim);
769   }
770   ierr = PetscQuadratureCreate(PETSC_COMM_SELF, q);CHKERRQ(ierr);
771   ierr = PetscQuadratureSetOrder(*q, 2*npoints-1);CHKERRQ(ierr);
772   ierr = PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w);CHKERRQ(ierr);
773   ierr = PetscObjectChangeTypeName((PetscObject)*q,"GaussTensor");CHKERRQ(ierr);
774   PetscFunctionReturn(0);
775 }
776 
777 /* Evaluates the nth jacobi polynomial with weight parameters a,b at a point x.
778    Recurrence relations implemented from the pseudocode given in Karniadakis and Sherwin, Appendix B */
779 PETSC_STATIC_INLINE PetscErrorCode PetscDTFactorial_Internal(PetscInt n, PetscReal *factorial)
780 {
781   PetscReal f = 1.0;
782   PetscInt  i;
783 
784   PetscFunctionBegin;
785   for (i = 1; i < n+1; ++i) f *= i;
786   *factorial = f;
787   PetscFunctionReturn(0);
788 }
789 
790 /* Evaluates the nth jacobi polynomial with weight parameters a,b at a point x.
791    Recurrence relations implemented from the pseudocode given in Karniadakis and Sherwin, Appendix B */
792 PETSC_STATIC_INLINE PetscErrorCode PetscDTComputeJacobi(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscReal *P)
793 {
794   PetscReal apb, pn1, pn2;
795   PetscInt  k;
796 
797   PetscFunctionBegin;
798   if (!n) {*P = 1.0; PetscFunctionReturn(0);}
799   if (n == 1) {*P = 0.5 * (a - b + (a + b + 2.0) * x); PetscFunctionReturn(0);}
800   apb = a + b;
801   pn2 = 1.0;
802   pn1 = 0.5 * (a - b + (apb + 2.0) * x);
803   *P  = 0.0;
804   for (k = 2; k < n+1; ++k) {
805     PetscReal a1 = 2.0 * k * (k + apb) * (2.0*k + apb - 2.0);
806     PetscReal a2 = (2.0 * k + apb - 1.0) * (a*a - b*b);
807     PetscReal a3 = (2.0 * k + apb - 2.0) * (2.0 * k + apb - 1.0) * (2.0 * k + apb);
808     PetscReal a4 = 2.0 * (k + a - 1.0) * (k + b - 1.0) * (2.0 * k + apb);
809 
810     a2  = a2 / a1;
811     a3  = a3 / a1;
812     a4  = a4 / a1;
813     *P  = (a2 + a3 * x) * pn1 - a4 * pn2;
814     pn2 = pn1;
815     pn1 = *P;
816   }
817   PetscFunctionReturn(0);
818 }
819 
820 /* Evaluates the first derivative of P_{n}^{a,b} at a point x. */
821 PETSC_STATIC_INLINE PetscErrorCode PetscDTComputeJacobiDerivative(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscReal *P)
822 {
823   PetscReal      nP;
824   PetscErrorCode ierr;
825 
826   PetscFunctionBegin;
827   if (!n) {*P = 0.0; PetscFunctionReturn(0);}
828   ierr = PetscDTComputeJacobi(a+1, b+1, n-1, x, &nP);CHKERRQ(ierr);
829   *P   = 0.5 * (a + b + n + 1) * nP;
830   PetscFunctionReturn(0);
831 }
832 
833 /* Maps from [-1,1]^2 to the (-1,1) reference triangle */
834 PETSC_STATIC_INLINE PetscErrorCode PetscDTMapSquareToTriangle_Internal(PetscReal x, PetscReal y, PetscReal *xi, PetscReal *eta)
835 {
836   PetscFunctionBegin;
837   *xi  = 0.5 * (1.0 + x) * (1.0 - y) - 1.0;
838   *eta = y;
839   PetscFunctionReturn(0);
840 }
841 
842 /* Maps from [-1,1]^2 to the (-1,1) reference triangle */
843 PETSC_STATIC_INLINE PetscErrorCode PetscDTMapCubeToTetrahedron_Internal(PetscReal x, PetscReal y, PetscReal z, PetscReal *xi, PetscReal *eta, PetscReal *zeta)
844 {
845   PetscFunctionBegin;
846   *xi   = 0.25 * (1.0 + x) * (1.0 - y) * (1.0 - z) - 1.0;
847   *eta  = 0.5  * (1.0 + y) * (1.0 - z) - 1.0;
848   *zeta = z;
849   PetscFunctionReturn(0);
850 }
851 
852 static PetscErrorCode PetscDTGaussJacobiQuadrature1D_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal *x, PetscReal *w)
853 {
854   PetscInt       maxIter = 100;
855   PetscReal      eps     = 1.0e-8;
856   PetscReal      a1, a2, a3, a4, a5, a6;
857   PetscInt       k;
858   PetscErrorCode ierr;
859 
860   PetscFunctionBegin;
861 
862   a1      = PetscPowReal(2.0, a+b+1);
863 #if defined(PETSC_HAVE_TGAMMA)
864   a2      = PetscTGamma(a + npoints + 1);
865   a3      = PetscTGamma(b + npoints + 1);
866   a4      = PetscTGamma(a + b + npoints + 1);
867 #else
868   {
869     PetscInt ia, ib;
870 
871     ia = (PetscInt) a;
872     ib = (PetscInt) b;
873     if (ia == a && ib == b && ia + npoints + 1 > 0 && ib + npoints + 1 > 0 && ia + ib + npoints + 1 > 0) { /* All gamma(x) terms are (x-1)! terms */
874       ierr = PetscDTFactorial_Internal(ia + npoints, &a2);CHKERRQ(ierr);
875       ierr = PetscDTFactorial_Internal(ib + npoints, &a3);CHKERRQ(ierr);
876       ierr = PetscDTFactorial_Internal(ia + ib + npoints, &a4);CHKERRQ(ierr);
877     } else {
878       SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"tgamma() - math routine is unavailable.");
879     }
880   }
881 #endif
882 
883   ierr = PetscDTFactorial_Internal(npoints, &a5);CHKERRQ(ierr);
884   a6   = a1 * a2 * a3 / a4 / a5;
885   /* Computes the m roots of P_{m}^{a,b} on [-1,1] by Newton's method with Chebyshev points as initial guesses.
886    Algorithm implemented from the pseudocode given by Karniadakis and Sherwin and Python in FIAT */
887   for (k = 0; k < npoints; ++k) {
888     PetscReal r = -PetscCosReal((2.0*k + 1.0) * PETSC_PI / (2.0 * npoints)), dP;
889     PetscInt  j;
890 
891     if (k > 0) r = 0.5 * (r + x[k-1]);
892     for (j = 0; j < maxIter; ++j) {
893       PetscReal s = 0.0, delta, f, fp;
894       PetscInt  i;
895 
896       for (i = 0; i < k; ++i) s = s + 1.0 / (r - x[i]);
897       ierr = PetscDTComputeJacobi(a, b, npoints, r, &f);CHKERRQ(ierr);
898       ierr = PetscDTComputeJacobiDerivative(a, b, npoints, r, &fp);CHKERRQ(ierr);
899       delta = f / (fp - f * s);
900       r     = r - delta;
901       if (PetscAbsReal(delta) < eps) break;
902     }
903     x[k] = r;
904     ierr = PetscDTComputeJacobiDerivative(a, b, npoints, x[k], &dP);CHKERRQ(ierr);
905     w[k] = a6 / (1.0 - PetscSqr(x[k])) / PetscSqr(dP);
906   }
907   PetscFunctionReturn(0);
908 }
909 
910 /*@
911   PetscDTGaussJacobiQuadrature - create Gauss-Jacobi quadrature for a simplex
912 
913   Not Collective
914 
915   Input Arguments:
916 + dim     - The simplex dimension
917 . Nc      - The number of components
918 . npoints - The number of points in one dimension
919 . a       - left end of interval (often-1)
920 - b       - right end of interval (often +1)
921 
922   Output Argument:
923 . q - A PetscQuadrature object
924 
925   Level: intermediate
926 
927   References:
928 .  1. - Karniadakis and Sherwin.  FIAT
929 
930 .seealso: PetscDTGaussTensorQuadrature(), PetscDTGaussQuadrature()
931 @*/
932 PetscErrorCode PetscDTGaussJacobiQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
933 {
934   PetscInt       totpoints = dim > 1 ? dim > 2 ? npoints*PetscSqr(npoints) : PetscSqr(npoints) : npoints;
935   PetscReal     *px, *wx, *py, *wy, *pz, *wz, *x, *w;
936   PetscInt       i, j, k, c;
937   PetscErrorCode ierr;
938 
939   PetscFunctionBegin;
940   if ((a != -1.0) || (b != 1.0)) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must use default internal right now");
941   ierr = PetscMalloc1(totpoints*dim, &x);CHKERRQ(ierr);
942   ierr = PetscMalloc1(totpoints*Nc, &w);CHKERRQ(ierr);
943   switch (dim) {
944   case 0:
945     ierr = PetscFree(x);CHKERRQ(ierr);
946     ierr = PetscFree(w);CHKERRQ(ierr);
947     ierr = PetscMalloc1(1, &x);CHKERRQ(ierr);
948     ierr = PetscMalloc1(Nc, &w);CHKERRQ(ierr);
949     x[0] = 0.0;
950     for (c = 0; c < Nc; ++c) w[c] = 1.0;
951     break;
952   case 1:
953     ierr = PetscMalloc1(npoints,&wx);CHKERRQ(ierr);
954     ierr = PetscDTGaussJacobiQuadrature1D_Internal(npoints, 0.0, 0.0, x, wx);CHKERRQ(ierr);
955     for (i = 0; i < npoints; ++i) for (c = 0; c < Nc; ++c) w[i*Nc+c] = wx[i];
956     ierr = PetscFree(wx);CHKERRQ(ierr);
957     break;
958   case 2:
959     ierr = PetscMalloc4(npoints,&px,npoints,&wx,npoints,&py,npoints,&wy);CHKERRQ(ierr);
960     ierr = PetscDTGaussJacobiQuadrature1D_Internal(npoints, 0.0, 0.0, px, wx);CHKERRQ(ierr);
961     ierr = PetscDTGaussJacobiQuadrature1D_Internal(npoints, 1.0, 0.0, py, wy);CHKERRQ(ierr);
962     for (i = 0; i < npoints; ++i) {
963       for (j = 0; j < npoints; ++j) {
964         ierr = PetscDTMapSquareToTriangle_Internal(px[i], py[j], &x[(i*npoints+j)*2+0], &x[(i*npoints+j)*2+1]);CHKERRQ(ierr);
965         for (c = 0; c < Nc; ++c) w[(i*npoints+j)*Nc+c] = 0.5 * wx[i] * wy[j];
966       }
967     }
968     ierr = PetscFree4(px,wx,py,wy);CHKERRQ(ierr);
969     break;
970   case 3:
971     ierr = PetscMalloc6(npoints,&px,npoints,&wx,npoints,&py,npoints,&wy,npoints,&pz,npoints,&wz);CHKERRQ(ierr);
972     ierr = PetscDTGaussJacobiQuadrature1D_Internal(npoints, 0.0, 0.0, px, wx);CHKERRQ(ierr);
973     ierr = PetscDTGaussJacobiQuadrature1D_Internal(npoints, 1.0, 0.0, py, wy);CHKERRQ(ierr);
974     ierr = PetscDTGaussJacobiQuadrature1D_Internal(npoints, 2.0, 0.0, pz, wz);CHKERRQ(ierr);
975     for (i = 0; i < npoints; ++i) {
976       for (j = 0; j < npoints; ++j) {
977         for (k = 0; k < npoints; ++k) {
978           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);
979           for (c = 0; c < Nc; ++c) w[((i*npoints+j)*npoints+k)*Nc+c] = 0.125 * wx[i] * wy[j] * wz[k];
980         }
981       }
982     }
983     ierr = PetscFree6(px,wx,py,wy,pz,wz);CHKERRQ(ierr);
984     break;
985   default:
986     SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %d", dim);
987   }
988   ierr = PetscQuadratureCreate(PETSC_COMM_SELF, q);CHKERRQ(ierr);
989   ierr = PetscQuadratureSetOrder(*q, 2*npoints-1);CHKERRQ(ierr);
990   ierr = PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w);CHKERRQ(ierr);
991   ierr = PetscObjectChangeTypeName((PetscObject)*q,"GaussJacobi");CHKERRQ(ierr);
992   PetscFunctionReturn(0);
993 }
994 
995 /*@
996   PetscDTTanhSinhTensorQuadrature - create tanh-sinh quadrature for a tensor product cell
997 
998   Not Collective
999 
1000   Input Arguments:
1001 + dim   - The cell dimension
1002 . level - The number of points in one dimension, 2^l
1003 . a     - left end of interval (often-1)
1004 - b     - right end of interval (often +1)
1005 
1006   Output Argument:
1007 . q - A PetscQuadrature object
1008 
1009   Level: intermediate
1010 
1011 .seealso: PetscDTGaussTensorQuadrature()
1012 @*/
1013 PetscErrorCode PetscDTTanhSinhTensorQuadrature(PetscInt dim, PetscInt level, PetscReal a, PetscReal b, PetscQuadrature *q)
1014 {
1015   const PetscInt  p     = 16;                        /* Digits of precision in the evaluation */
1016   const PetscReal alpha = (b-a)/2.;                  /* Half-width of the integration interval */
1017   const PetscReal beta  = (b+a)/2.;                  /* Center of the integration interval */
1018   const PetscReal h     = PetscPowReal(2.0, -level); /* Step size, length between x_k */
1019   PetscReal       xk;                                /* Quadrature point x_k on reference domain [-1, 1] */
1020   PetscReal       wk    = 0.5*PETSC_PI;              /* Quadrature weight at x_k */
1021   PetscReal      *x, *w;
1022   PetscInt        K, k, npoints;
1023   PetscErrorCode  ierr;
1024 
1025   PetscFunctionBegin;
1026   if (dim > 1) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Dimension %d not yet implemented", dim);
1027   if (!level) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a number of significant digits");
1028   /* Find K such that the weights are < 32 digits of precision */
1029   for (K = 1; PetscAbsReal(PetscLog10Real(wk)) < 2*p; ++K) {
1030     wk = 0.5*h*PETSC_PI*PetscCoshReal(K*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(K*h)));
1031   }
1032   ierr = PetscQuadratureCreate(PETSC_COMM_SELF, q);CHKERRQ(ierr);
1033   ierr = PetscQuadratureSetOrder(*q, 2*K+1);CHKERRQ(ierr);
1034   npoints = 2*K-1;
1035   ierr = PetscMalloc1(npoints*dim, &x);CHKERRQ(ierr);
1036   ierr = PetscMalloc1(npoints, &w);CHKERRQ(ierr);
1037   /* Center term */
1038   x[0] = beta;
1039   w[0] = 0.5*alpha*PETSC_PI;
1040   for (k = 1; k < K; ++k) {
1041     wk = 0.5*alpha*h*PETSC_PI*PetscCoshReal(k*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h)));
1042     xk = PetscTanhReal(0.5*PETSC_PI*PetscSinhReal(k*h));
1043     x[2*k-1] = -alpha*xk+beta;
1044     w[2*k-1] = wk;
1045     x[2*k+0] =  alpha*xk+beta;
1046     w[2*k+0] = wk;
1047   }
1048   ierr = PetscQuadratureSetData(*q, dim, 1, npoints, x, w);CHKERRQ(ierr);
1049   PetscFunctionReturn(0);
1050 }
1051 
1052 PetscErrorCode PetscDTTanhSinhIntegrate(void (*func)(PetscReal, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscReal *sol)
1053 {
1054   const PetscInt  p     = 16;        /* Digits of precision in the evaluation */
1055   const PetscReal alpha = (b-a)/2.;  /* Half-width of the integration interval */
1056   const PetscReal beta  = (b+a)/2.;  /* Center of the integration interval */
1057   PetscReal       h     = 1.0;       /* Step size, length between x_k */
1058   PetscInt        l     = 0;         /* Level of refinement, h = 2^{-l} */
1059   PetscReal       osum  = 0.0;       /* Integral on last level */
1060   PetscReal       psum  = 0.0;       /* Integral on the level before the last level */
1061   PetscReal       sum;               /* Integral on current level */
1062   PetscReal       yk;                /* Quadrature point 1 - x_k on reference domain [-1, 1] */
1063   PetscReal       lx, rx;            /* Quadrature points to the left and right of 0 on the real domain [a, b] */
1064   PetscReal       wk;                /* Quadrature weight at x_k */
1065   PetscReal       lval, rval;        /* Terms in the quadature sum to the left and right of 0 */
1066   PetscInt        d;                 /* Digits of precision in the integral */
1067 
1068   PetscFunctionBegin;
1069   if (digits <= 0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
1070   /* Center term */
1071   func(beta, &lval);
1072   sum = 0.5*alpha*PETSC_PI*lval;
1073   /* */
1074   do {
1075     PetscReal lterm, rterm, maxTerm = 0.0, d1, d2, d3, d4;
1076     PetscInt  k = 1;
1077 
1078     ++l;
1079     /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %D sum: %15.15f\n", l, sum); */
1080     /* At each level of refinement, h --> h/2 and sum --> sum/2 */
1081     psum = osum;
1082     osum = sum;
1083     h   *= 0.5;
1084     sum *= 0.5;
1085     do {
1086       wk = 0.5*h*PETSC_PI*PetscCoshReal(k*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h)));
1087       yk = 1.0/(PetscExpReal(0.5*PETSC_PI*PetscSinhReal(k*h)) * PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h)));
1088       lx = -alpha*(1.0 - yk)+beta;
1089       rx =  alpha*(1.0 - yk)+beta;
1090       func(lx, &lval);
1091       func(rx, &rval);
1092       lterm   = alpha*wk*lval;
1093       maxTerm = PetscMax(PetscAbsReal(lterm), maxTerm);
1094       sum    += lterm;
1095       rterm   = alpha*wk*rval;
1096       maxTerm = PetscMax(PetscAbsReal(rterm), maxTerm);
1097       sum    += rterm;
1098       ++k;
1099       /* Only need to evaluate every other point on refined levels */
1100       if (l != 1) ++k;
1101     } while (PetscAbsReal(PetscLog10Real(wk)) < p); /* Only need to evaluate sum until weights are < 32 digits of precision */
1102 
1103     d1 = PetscLog10Real(PetscAbsReal(sum - osum));
1104     d2 = PetscLog10Real(PetscAbsReal(sum - psum));
1105     d3 = PetscLog10Real(maxTerm) - p;
1106     if (PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)) == 0.0) d4 = 0.0;
1107     else d4 = PetscLog10Real(PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)));
1108     d  = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1)/d2, 2*d1), d3), d4)));
1109   } while (d < digits && l < 12);
1110   *sol = sum;
1111 
1112   PetscFunctionReturn(0);
1113 }
1114 
1115 #if defined(PETSC_HAVE_MPFR)
1116 PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(PetscReal, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscReal *sol)
1117 {
1118   const PetscInt  safetyFactor = 2;  /* Calculate abcissa until 2*p digits */
1119   PetscInt        l            = 0;  /* Level of refinement, h = 2^{-l} */
1120   mpfr_t          alpha;             /* Half-width of the integration interval */
1121   mpfr_t          beta;              /* Center of the integration interval */
1122   mpfr_t          h;                 /* Step size, length between x_k */
1123   mpfr_t          osum;              /* Integral on last level */
1124   mpfr_t          psum;              /* Integral on the level before the last level */
1125   mpfr_t          sum;               /* Integral on current level */
1126   mpfr_t          yk;                /* Quadrature point 1 - x_k on reference domain [-1, 1] */
1127   mpfr_t          lx, rx;            /* Quadrature points to the left and right of 0 on the real domain [a, b] */
1128   mpfr_t          wk;                /* Quadrature weight at x_k */
1129   PetscReal       lval, rval;        /* Terms in the quadature sum to the left and right of 0 */
1130   PetscInt        d;                 /* Digits of precision in the integral */
1131   mpfr_t          pi2, kh, msinh, mcosh, maxTerm, curTerm, tmp;
1132 
1133   PetscFunctionBegin;
1134   if (digits <= 0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
1135   /* Create high precision storage */
1136   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);
1137   /* Initialization */
1138   mpfr_set_d(alpha, 0.5*(b-a), MPFR_RNDN);
1139   mpfr_set_d(beta,  0.5*(b+a), MPFR_RNDN);
1140   mpfr_set_d(osum,  0.0,       MPFR_RNDN);
1141   mpfr_set_d(psum,  0.0,       MPFR_RNDN);
1142   mpfr_set_d(h,     1.0,       MPFR_RNDN);
1143   mpfr_const_pi(pi2, MPFR_RNDN);
1144   mpfr_mul_d(pi2, pi2, 0.5, MPFR_RNDN);
1145   /* Center term */
1146   func(0.5*(b+a), &lval);
1147   mpfr_set(sum, pi2, MPFR_RNDN);
1148   mpfr_mul(sum, sum, alpha, MPFR_RNDN);
1149   mpfr_mul_d(sum, sum, lval, MPFR_RNDN);
1150   /* */
1151   do {
1152     PetscReal d1, d2, d3, d4;
1153     PetscInt  k = 1;
1154 
1155     ++l;
1156     mpfr_set_d(maxTerm, 0.0, MPFR_RNDN);
1157     /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %D sum: %15.15f\n", l, sum); */
1158     /* At each level of refinement, h --> h/2 and sum --> sum/2 */
1159     mpfr_set(psum, osum, MPFR_RNDN);
1160     mpfr_set(osum,  sum, MPFR_RNDN);
1161     mpfr_mul_d(h,   h,   0.5, MPFR_RNDN);
1162     mpfr_mul_d(sum, sum, 0.5, MPFR_RNDN);
1163     do {
1164       mpfr_set_si(kh, k, MPFR_RNDN);
1165       mpfr_mul(kh, kh, h, MPFR_RNDN);
1166       /* Weight */
1167       mpfr_set(wk, h, MPFR_RNDN);
1168       mpfr_sinh_cosh(msinh, mcosh, kh, MPFR_RNDN);
1169       mpfr_mul(msinh, msinh, pi2, MPFR_RNDN);
1170       mpfr_mul(mcosh, mcosh, pi2, MPFR_RNDN);
1171       mpfr_cosh(tmp, msinh, MPFR_RNDN);
1172       mpfr_sqr(tmp, tmp, MPFR_RNDN);
1173       mpfr_mul(wk, wk, mcosh, MPFR_RNDN);
1174       mpfr_div(wk, wk, tmp, MPFR_RNDN);
1175       /* Abscissa */
1176       mpfr_set_d(yk, 1.0, MPFR_RNDZ);
1177       mpfr_cosh(tmp, msinh, MPFR_RNDN);
1178       mpfr_div(yk, yk, tmp, MPFR_RNDZ);
1179       mpfr_exp(tmp, msinh, MPFR_RNDN);
1180       mpfr_div(yk, yk, tmp, MPFR_RNDZ);
1181       /* Quadrature points */
1182       mpfr_sub_d(lx, yk, 1.0, MPFR_RNDZ);
1183       mpfr_mul(lx, lx, alpha, MPFR_RNDU);
1184       mpfr_add(lx, lx, beta, MPFR_RNDU);
1185       mpfr_d_sub(rx, 1.0, yk, MPFR_RNDZ);
1186       mpfr_mul(rx, rx, alpha, MPFR_RNDD);
1187       mpfr_add(rx, rx, beta, MPFR_RNDD);
1188       /* Evaluation */
1189       func(mpfr_get_d(lx, MPFR_RNDU), &lval);
1190       func(mpfr_get_d(rx, MPFR_RNDD), &rval);
1191       /* Update */
1192       mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
1193       mpfr_mul_d(tmp, tmp, lval, MPFR_RNDN);
1194       mpfr_add(sum, sum, tmp, MPFR_RNDN);
1195       mpfr_abs(tmp, tmp, MPFR_RNDN);
1196       mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
1197       mpfr_set(curTerm, tmp, MPFR_RNDN);
1198       mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
1199       mpfr_mul_d(tmp, tmp, rval, MPFR_RNDN);
1200       mpfr_add(sum, sum, tmp, MPFR_RNDN);
1201       mpfr_abs(tmp, tmp, MPFR_RNDN);
1202       mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
1203       mpfr_max(curTerm, curTerm, tmp, MPFR_RNDN);
1204       ++k;
1205       /* Only need to evaluate every other point on refined levels */
1206       if (l != 1) ++k;
1207       mpfr_log10(tmp, wk, MPFR_RNDN);
1208       mpfr_abs(tmp, tmp, MPFR_RNDN);
1209     } while (mpfr_get_d(tmp, MPFR_RNDN) < safetyFactor*digits); /* Only need to evaluate sum until weights are < 32 digits of precision */
1210     mpfr_sub(tmp, sum, osum, MPFR_RNDN);
1211     mpfr_abs(tmp, tmp, MPFR_RNDN);
1212     mpfr_log10(tmp, tmp, MPFR_RNDN);
1213     d1 = mpfr_get_d(tmp, MPFR_RNDN);
1214     mpfr_sub(tmp, sum, psum, MPFR_RNDN);
1215     mpfr_abs(tmp, tmp, MPFR_RNDN);
1216     mpfr_log10(tmp, tmp, MPFR_RNDN);
1217     d2 = mpfr_get_d(tmp, MPFR_RNDN);
1218     mpfr_log10(tmp, maxTerm, MPFR_RNDN);
1219     d3 = mpfr_get_d(tmp, MPFR_RNDN) - digits;
1220     mpfr_log10(tmp, curTerm, MPFR_RNDN);
1221     d4 = mpfr_get_d(tmp, MPFR_RNDN);
1222     d  = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1)/d2, 2*d1), d3), d4)));
1223   } while (d < digits && l < 8);
1224   *sol = mpfr_get_d(sum, MPFR_RNDN);
1225   /* Cleanup */
1226   mpfr_clears(alpha, beta, h, sum, osum, psum, yk, wk, lx, rx, tmp, maxTerm, curTerm, pi2, kh, msinh, mcosh, NULL);
1227   PetscFunctionReturn(0);
1228 }
1229 #else
1230 
1231 PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(PetscReal, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscReal *sol)
1232 {
1233   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "This method will not work without MPFR. Reconfigure using --download-mpfr --download-gmp");
1234 }
1235 #endif
1236 
1237 /* Overwrites A. Can only handle full-rank problems with m>=n
1238  * A in column-major format
1239  * Ainv in row-major format
1240  * tau has length m
1241  * worksize must be >= max(1,n)
1242  */
1243 static PetscErrorCode PetscDTPseudoInverseQR(PetscInt m,PetscInt mstride,PetscInt n,PetscReal *A_in,PetscReal *Ainv_out,PetscScalar *tau,PetscInt worksize,PetscScalar *work)
1244 {
1245   PetscErrorCode ierr;
1246   PetscBLASInt   M,N,K,lda,ldb,ldwork,info;
1247   PetscScalar    *A,*Ainv,*R,*Q,Alpha;
1248 
1249   PetscFunctionBegin;
1250 #if defined(PETSC_USE_COMPLEX)
1251   {
1252     PetscInt i,j;
1253     ierr = PetscMalloc2(m*n,&A,m*n,&Ainv);CHKERRQ(ierr);
1254     for (j=0; j<n; j++) {
1255       for (i=0; i<m; i++) A[i+m*j] = A_in[i+mstride*j];
1256     }
1257     mstride = m;
1258   }
1259 #else
1260   A = A_in;
1261   Ainv = Ainv_out;
1262 #endif
1263 
1264   ierr = PetscBLASIntCast(m,&M);CHKERRQ(ierr);
1265   ierr = PetscBLASIntCast(n,&N);CHKERRQ(ierr);
1266   ierr = PetscBLASIntCast(mstride,&lda);CHKERRQ(ierr);
1267   ierr = PetscBLASIntCast(worksize,&ldwork);CHKERRQ(ierr);
1268   ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr);
1269   PetscStackCallBLAS("LAPACKgeqrf",LAPACKgeqrf_(&M,&N,A,&lda,tau,work,&ldwork,&info));
1270   ierr = PetscFPTrapPop();CHKERRQ(ierr);
1271   if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"xGEQRF error");
1272   R = A; /* Upper triangular part of A now contains R, the rest contains the elementary reflectors */
1273 
1274   /* Extract an explicit representation of Q */
1275   Q = Ainv;
1276   ierr = PetscArraycpy(Q,A,mstride*n);CHKERRQ(ierr);
1277   K = N;                        /* full rank */
1278 #if defined(PETSC_MISSING_LAPACK_ORGQR)
1279   SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"ORGQR - Lapack routine is unavailable.");
1280 #else
1281   PetscStackCallBLAS("LAPACKorgqr",LAPACKorgqr_(&M,&N,&K,Q,&lda,tau,work,&ldwork,&info));
1282   if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"xORGQR/xUNGQR error");
1283 #endif
1284 
1285   /* Compute A^{-T} = (R^{-1} Q^T)^T = Q R^{-T} */
1286   Alpha = 1.0;
1287   ldb = lda;
1288   PetscStackCallBLAS("BLAStrsm",BLAStrsm_("Right","Upper","ConjugateTranspose","NotUnitTriangular",&M,&N,&Alpha,R,&lda,Q,&ldb));
1289   /* Ainv is Q, overwritten with inverse */
1290 
1291 #if defined(PETSC_USE_COMPLEX)
1292   {
1293     PetscInt i;
1294     for (i=0; i<m*n; i++) Ainv_out[i] = PetscRealPart(Ainv[i]);
1295     ierr = PetscFree2(A,Ainv);CHKERRQ(ierr);
1296   }
1297 #endif
1298   PetscFunctionReturn(0);
1299 }
1300 
1301 /* Computes integral of L_p' over intervals {(x0,x1),(x1,x2),...} */
1302 static PetscErrorCode PetscDTLegendreIntegrate(PetscInt ninterval,const PetscReal *x,PetscInt ndegree,const PetscInt *degrees,PetscBool Transpose,PetscReal *B)
1303 {
1304   PetscErrorCode ierr;
1305   PetscReal      *Bv;
1306   PetscInt       i,j;
1307 
1308   PetscFunctionBegin;
1309   ierr = PetscMalloc1((ninterval+1)*ndegree,&Bv);CHKERRQ(ierr);
1310   /* Point evaluation of L_p on all the source vertices */
1311   ierr = PetscDTLegendreEval(ninterval+1,x,ndegree,degrees,Bv,NULL,NULL);CHKERRQ(ierr);
1312   /* Integral over each interval: \int_a^b L_p' = L_p(b)-L_p(a) */
1313   for (i=0; i<ninterval; i++) {
1314     for (j=0; j<ndegree; j++) {
1315       if (Transpose) B[i+ninterval*j] = Bv[(i+1)*ndegree+j] - Bv[i*ndegree+j];
1316       else           B[i*ndegree+j]   = Bv[(i+1)*ndegree+j] - Bv[i*ndegree+j];
1317     }
1318   }
1319   ierr = PetscFree(Bv);CHKERRQ(ierr);
1320   PetscFunctionReturn(0);
1321 }
1322 
1323 /*@
1324    PetscDTReconstructPoly - create matrix representing polynomial reconstruction using cell intervals and evaluation at target intervals
1325 
1326    Not Collective
1327 
1328    Input Arguments:
1329 +  degree - degree of reconstruction polynomial
1330 .  nsource - number of source intervals
1331 .  sourcex - sorted coordinates of source cell boundaries (length nsource+1)
1332 .  ntarget - number of target intervals
1333 -  targetx - sorted coordinates of target cell boundaries (length ntarget+1)
1334 
1335    Output Arguments:
1336 .  R - reconstruction matrix, utarget = sum_s R[t*nsource+s] * usource[s]
1337 
1338    Level: advanced
1339 
1340 .seealso: PetscDTLegendreEval()
1341 @*/
1342 PetscErrorCode PetscDTReconstructPoly(PetscInt degree,PetscInt nsource,const PetscReal *sourcex,PetscInt ntarget,const PetscReal *targetx,PetscReal *R)
1343 {
1344   PetscErrorCode ierr;
1345   PetscInt       i,j,k,*bdegrees,worksize;
1346   PetscReal      xmin,xmax,center,hscale,*sourcey,*targety,*Bsource,*Bsinv,*Btarget;
1347   PetscScalar    *tau,*work;
1348 
1349   PetscFunctionBegin;
1350   PetscValidRealPointer(sourcex,3);
1351   PetscValidRealPointer(targetx,5);
1352   PetscValidRealPointer(R,6);
1353   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);
1354 #if defined(PETSC_USE_DEBUG)
1355   for (i=0; i<nsource; i++) {
1356     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]);
1357   }
1358   for (i=0; i<ntarget; i++) {
1359     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]);
1360   }
1361 #endif
1362   xmin = PetscMin(sourcex[0],targetx[0]);
1363   xmax = PetscMax(sourcex[nsource],targetx[ntarget]);
1364   center = (xmin + xmax)/2;
1365   hscale = (xmax - xmin)/2;
1366   worksize = nsource;
1367   ierr = PetscMalloc4(degree+1,&bdegrees,nsource+1,&sourcey,nsource*(degree+1),&Bsource,worksize,&work);CHKERRQ(ierr);
1368   ierr = PetscMalloc4(nsource,&tau,nsource*(degree+1),&Bsinv,ntarget+1,&targety,ntarget*(degree+1),&Btarget);CHKERRQ(ierr);
1369   for (i=0; i<=nsource; i++) sourcey[i] = (sourcex[i]-center)/hscale;
1370   for (i=0; i<=degree; i++) bdegrees[i] = i+1;
1371   ierr = PetscDTLegendreIntegrate(nsource,sourcey,degree+1,bdegrees,PETSC_TRUE,Bsource);CHKERRQ(ierr);
1372   ierr = PetscDTPseudoInverseQR(nsource,nsource,degree+1,Bsource,Bsinv,tau,nsource,work);CHKERRQ(ierr);
1373   for (i=0; i<=ntarget; i++) targety[i] = (targetx[i]-center)/hscale;
1374   ierr = PetscDTLegendreIntegrate(ntarget,targety,degree+1,bdegrees,PETSC_FALSE,Btarget);CHKERRQ(ierr);
1375   for (i=0; i<ntarget; i++) {
1376     PetscReal rowsum = 0;
1377     for (j=0; j<nsource; j++) {
1378       PetscReal sum = 0;
1379       for (k=0; k<degree+1; k++) {
1380         sum += Btarget[i*(degree+1)+k] * Bsinv[k*nsource+j];
1381       }
1382       R[i*nsource+j] = sum;
1383       rowsum += sum;
1384     }
1385     for (j=0; j<nsource; j++) R[i*nsource+j] /= rowsum; /* normalize each row */
1386   }
1387   ierr = PetscFree4(bdegrees,sourcey,Bsource,work);CHKERRQ(ierr);
1388   ierr = PetscFree4(tau,Bsinv,targety,Btarget);CHKERRQ(ierr);
1389   PetscFunctionReturn(0);
1390 }
1391 
1392 /*@C
1393    PetscGaussLobattoLegendreIntegrate - Compute the L2 integral of a function on the GLL points
1394 
1395    Not Collective
1396 
1397    Input Parameter:
1398 +  n - the number of GLL nodes
1399 .  nodes - the GLL nodes
1400 .  weights - the GLL weights
1401 .  f - the function values at the nodes
1402 
1403    Output Parameter:
1404 .  in - the value of the integral
1405 
1406    Level: beginner
1407 
1408 .seealso: PetscDTGaussLobattoLegendreQuadrature()
1409 
1410 @*/
1411 PetscErrorCode PetscGaussLobattoLegendreIntegrate(PetscInt n,PetscReal *nodes,PetscReal *weights,const PetscReal *f,PetscReal *in)
1412 {
1413   PetscInt          i;
1414 
1415   PetscFunctionBegin;
1416   *in = 0.;
1417   for (i=0; i<n; i++) {
1418     *in += f[i]*f[i]*weights[i];
1419   }
1420   PetscFunctionReturn(0);
1421 }
1422 
1423 /*@C
1424    PetscGaussLobattoLegendreElementLaplacianCreate - computes the Laplacian for a single 1d GLL element
1425 
1426    Not Collective
1427 
1428    Input Parameter:
1429 +  n - the number of GLL nodes
1430 .  nodes - the GLL nodes
1431 .  weights - the GLL weights
1432 
1433    Output Parameter:
1434 .  A - the stiffness element
1435 
1436    Level: beginner
1437 
1438    Notes:
1439     Destroy this with PetscGaussLobattoLegendreElementLaplacianDestroy()
1440 
1441    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)
1442 
1443 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianDestroy()
1444 
1445 @*/
1446 PetscErrorCode PetscGaussLobattoLegendreElementLaplacianCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
1447 {
1448   PetscReal        **A;
1449   PetscErrorCode  ierr;
1450   const PetscReal  *gllnodes = nodes;
1451   const PetscInt   p = n-1;
1452   PetscReal        z0,z1,z2 = -1,x,Lpj,Lpr;
1453   PetscInt         i,j,nn,r;
1454 
1455   PetscFunctionBegin;
1456   ierr = PetscMalloc1(n,&A);CHKERRQ(ierr);
1457   ierr = PetscMalloc1(n*n,&A[0]);CHKERRQ(ierr);
1458   for (i=1; i<n; i++) A[i] = A[i-1]+n;
1459 
1460   for (j=1; j<p; j++) {
1461     x  = gllnodes[j];
1462     z0 = 1.;
1463     z1 = x;
1464     for (nn=1; nn<p; nn++) {
1465       z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
1466       z0 = z1;
1467       z1 = z2;
1468     }
1469     Lpj=z2;
1470     for (r=1; r<p; r++) {
1471       if (r == j) {
1472         A[j][j]=2./(3.*(1.-gllnodes[j]*gllnodes[j])*Lpj*Lpj);
1473       } else {
1474         x  = gllnodes[r];
1475         z0 = 1.;
1476         z1 = x;
1477         for (nn=1; nn<p; nn++) {
1478           z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
1479           z0 = z1;
1480           z1 = z2;
1481         }
1482         Lpr     = z2;
1483         A[r][j] = 4./(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*Lpr*(gllnodes[j]-gllnodes[r])*(gllnodes[j]-gllnodes[r]));
1484       }
1485     }
1486   }
1487   for (j=1; j<p+1; j++) {
1488     x  = gllnodes[j];
1489     z0 = 1.;
1490     z1 = x;
1491     for (nn=1; nn<p; nn++) {
1492       z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
1493       z0 = z1;
1494       z1 = z2;
1495     }
1496     Lpj     = z2;
1497     A[j][0] = 4.*PetscPowRealInt(-1.,p)/(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*(1.+gllnodes[j])*(1.+gllnodes[j]));
1498     A[0][j] = A[j][0];
1499   }
1500   for (j=0; j<p; j++) {
1501     x  = gllnodes[j];
1502     z0 = 1.;
1503     z1 = x;
1504     for (nn=1; nn<p; nn++) {
1505       z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
1506       z0 = z1;
1507       z1 = z2;
1508     }
1509     Lpj=z2;
1510 
1511     A[p][j] = 4./(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*(1.-gllnodes[j])*(1.-gllnodes[j]));
1512     A[j][p] = A[p][j];
1513   }
1514   A[0][0]=0.5+(((PetscReal)p)*(((PetscReal)p)+1.)-2.)/6.;
1515   A[p][p]=A[0][0];
1516   *AA = A;
1517   PetscFunctionReturn(0);
1518 }
1519 
1520 /*@C
1521    PetscGaussLobattoLegendreElementLaplacianDestroy - frees the Laplacian for a single 1d GLL element
1522 
1523    Not Collective
1524 
1525    Input Parameter:
1526 +  n - the number of GLL nodes
1527 .  nodes - the GLL nodes
1528 .  weights - the GLL weightss
1529 -  A - the stiffness element
1530 
1531    Level: beginner
1532 
1533 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianCreate()
1534 
1535 @*/
1536 PetscErrorCode PetscGaussLobattoLegendreElementLaplacianDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
1537 {
1538   PetscErrorCode ierr;
1539 
1540   PetscFunctionBegin;
1541   ierr = PetscFree((*AA)[0]);CHKERRQ(ierr);
1542   ierr = PetscFree(*AA);CHKERRQ(ierr);
1543   *AA  = NULL;
1544   PetscFunctionReturn(0);
1545 }
1546 
1547 /*@C
1548    PetscGaussLobattoLegendreElementGradientCreate - computes the gradient for a single 1d GLL element
1549 
1550    Not Collective
1551 
1552    Input Parameter:
1553 +  n - the number of GLL nodes
1554 .  nodes - the GLL nodes
1555 .  weights - the GLL weights
1556 
1557    Output Parameter:
1558 .  AA - the stiffness element
1559 -  AAT - the transpose of AA (pass in NULL if you do not need this array)
1560 
1561    Level: beginner
1562 
1563    Notes:
1564     Destroy this with PetscGaussLobattoLegendreElementGradientDestroy()
1565 
1566    You can access entries in these arrays with AA[i][j] but in memory it is stored in contiguous memory, row oriented
1567 
1568 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianDestroy()
1569 
1570 @*/
1571 PetscErrorCode PetscGaussLobattoLegendreElementGradientCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA,PetscReal ***AAT)
1572 {
1573   PetscReal        **A, **AT = NULL;
1574   PetscErrorCode  ierr;
1575   const PetscReal  *gllnodes = nodes;
1576   const PetscInt   p = n-1;
1577   PetscReal        q,qp,Li, Lj,d0;
1578   PetscInt         i,j;
1579 
1580   PetscFunctionBegin;
1581   ierr = PetscMalloc1(n,&A);CHKERRQ(ierr);
1582   ierr = PetscMalloc1(n*n,&A[0]);CHKERRQ(ierr);
1583   for (i=1; i<n; i++) A[i] = A[i-1]+n;
1584 
1585   if (AAT) {
1586     ierr = PetscMalloc1(n,&AT);CHKERRQ(ierr);
1587     ierr = PetscMalloc1(n*n,&AT[0]);CHKERRQ(ierr);
1588     for (i=1; i<n; i++) AT[i] = AT[i-1]+n;
1589   }
1590 
1591   if (n==1) {A[0][0] = 0.;}
1592   d0 = (PetscReal)p*((PetscReal)p+1.)/4.;
1593   for  (i=0; i<n; i++) {
1594     for  (j=0; j<n; j++) {
1595       A[i][j] = 0.;
1596       qAndLEvaluation(p,gllnodes[i],&q,&qp,&Li);
1597       qAndLEvaluation(p,gllnodes[j],&q,&qp,&Lj);
1598       if (i!=j)             A[i][j] = Li/(Lj*(gllnodes[i]-gllnodes[j]));
1599       if ((j==i) && (i==0)) A[i][j] = -d0;
1600       if (j==i && i==p)     A[i][j] = d0;
1601       if (AT) AT[j][i] = A[i][j];
1602     }
1603   }
1604   if (AAT) *AAT = AT;
1605   *AA  = A;
1606   PetscFunctionReturn(0);
1607 }
1608 
1609 /*@C
1610    PetscGaussLobattoLegendreElementGradientDestroy - frees the gradient for a single 1d GLL element obtained with PetscGaussLobattoLegendreElementGradientCreate()
1611 
1612    Not Collective
1613 
1614    Input Parameter:
1615 +  n - the number of GLL nodes
1616 .  nodes - the GLL nodes
1617 .  weights - the GLL weights
1618 .  AA - the stiffness element
1619 -  AAT - the transpose of the element
1620 
1621    Level: beginner
1622 
1623 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianCreate(), PetscGaussLobattoLegendreElementAdvectionCreate()
1624 
1625 @*/
1626 PetscErrorCode PetscGaussLobattoLegendreElementGradientDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA,PetscReal ***AAT)
1627 {
1628   PetscErrorCode ierr;
1629 
1630   PetscFunctionBegin;
1631   ierr = PetscFree((*AA)[0]);CHKERRQ(ierr);
1632   ierr = PetscFree(*AA);CHKERRQ(ierr);
1633   *AA  = NULL;
1634   if (*AAT) {
1635     ierr = PetscFree((*AAT)[0]);CHKERRQ(ierr);
1636     ierr = PetscFree(*AAT);CHKERRQ(ierr);
1637     *AAT  = NULL;
1638   }
1639   PetscFunctionReturn(0);
1640 }
1641 
1642 /*@C
1643    PetscGaussLobattoLegendreElementAdvectionCreate - computes the advection operator for a single 1d GLL element
1644 
1645    Not Collective
1646 
1647    Input Parameter:
1648 +  n - the number of GLL nodes
1649 .  nodes - the GLL nodes
1650 .  weights - the GLL weightss
1651 
1652    Output Parameter:
1653 .  AA - the stiffness element
1654 
1655    Level: beginner
1656 
1657    Notes:
1658     Destroy this with PetscGaussLobattoLegendreElementAdvectionDestroy()
1659 
1660    This is the same as the Gradient operator multiplied by the diagonal mass matrix
1661 
1662    You can access entries in this array with AA[i][j] but in memory it is stored in contiguous memory, row oriented
1663 
1664 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianCreate(), PetscGaussLobattoLegendreElementAdvectionDestroy()
1665 
1666 @*/
1667 PetscErrorCode PetscGaussLobattoLegendreElementAdvectionCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
1668 {
1669   PetscReal       **D;
1670   PetscErrorCode  ierr;
1671   const PetscReal  *gllweights = weights;
1672   const PetscInt   glln = n;
1673   PetscInt         i,j;
1674 
1675   PetscFunctionBegin;
1676   ierr = PetscGaussLobattoLegendreElementGradientCreate(n,nodes,weights,&D,NULL);CHKERRQ(ierr);
1677   for (i=0; i<glln; i++){
1678     for (j=0; j<glln; j++) {
1679       D[i][j] = gllweights[i]*D[i][j];
1680     }
1681   }
1682   *AA = D;
1683   PetscFunctionReturn(0);
1684 }
1685 
1686 /*@C
1687    PetscGaussLobattoLegendreElementAdvectionDestroy - frees the advection stiffness for a single 1d GLL element
1688 
1689    Not Collective
1690 
1691    Input Parameter:
1692 +  n - the number of GLL nodes
1693 .  nodes - the GLL nodes
1694 .  weights - the GLL weights
1695 -  A - advection
1696 
1697    Level: beginner
1698 
1699 .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementAdvectionCreate()
1700 
1701 @*/
1702 PetscErrorCode PetscGaussLobattoLegendreElementAdvectionDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
1703 {
1704   PetscErrorCode ierr;
1705 
1706   PetscFunctionBegin;
1707   ierr = PetscFree((*AA)[0]);CHKERRQ(ierr);
1708   ierr = PetscFree(*AA);CHKERRQ(ierr);
1709   *AA  = NULL;
1710   PetscFunctionReturn(0);
1711 }
1712 
1713 PetscErrorCode PetscGaussLobattoLegendreElementMassCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
1714 {
1715   PetscReal        **A;
1716   PetscErrorCode  ierr;
1717   const PetscReal  *gllweights = weights;
1718   const PetscInt   glln = n;
1719   PetscInt         i,j;
1720 
1721   PetscFunctionBegin;
1722   ierr = PetscMalloc1(glln,&A);CHKERRQ(ierr);
1723   ierr = PetscMalloc1(glln*glln,&A[0]);CHKERRQ(ierr);
1724   for (i=1; i<glln; i++) A[i] = A[i-1]+glln;
1725   if (glln==1) {A[0][0] = 0.;}
1726   for  (i=0; i<glln; i++) {
1727     for  (j=0; j<glln; j++) {
1728       A[i][j] = 0.;
1729       if (j==i)     A[i][j] = gllweights[i];
1730     }
1731   }
1732   *AA  = A;
1733   PetscFunctionReturn(0);
1734 }
1735 
1736 PetscErrorCode PetscGaussLobattoLegendreElementMassDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
1737 {
1738   PetscErrorCode ierr;
1739 
1740   PetscFunctionBegin;
1741   ierr = PetscFree((*AA)[0]);CHKERRQ(ierr);
1742   ierr = PetscFree(*AA);CHKERRQ(ierr);
1743   *AA  = NULL;
1744   PetscFunctionReturn(0);
1745 }
1746 
1747