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