xref: /petsc/src/dm/dt/interface/dt.c (revision 4d4d2bdc375ba73538fdff3fed6ff2932e6875ae)
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", "PETSCDTNODES_", NULL};
17 const char *const *const PetscDTNodeTypes           = PetscDTNodeTypes_shifted + 1;
18 
19 const char *const        PetscDTSimplexQuadratureTypes_shifted[] = {"default", "conic", "minsym", "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 iascii;
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, &iascii));
637   PetscCall(PetscViewerASCIIPushTab(viewer));
638   if (iascii) 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     PetscInt alphai = (PetscInt)alpha;
1489     PetscInt betai  = (PetscInt)beta;
1490 
1491     if ((PetscReal)alphai == alpha && (PetscReal)betai == beta) {
1492       PetscReal binom1, binom2;
1493 
1494       PetscCall(PetscDTBinomial(m + b, b, &binom1));
1495       PetscCall(PetscDTBinomial(m + a + b, b, &binom2));
1496       grb = 1. / (binom1 * binom2);
1497       PetscCall(PetscDTBinomial(m + a, a, &binom1));
1498       PetscCall(PetscDTBinomial(m + a + b, a, &binom2));
1499       gra = 1. / (binom1 * binom2);
1500     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "lgamma() - math routine is unavailable.");
1501   }
1502 #endif
1503   *leftw  = twoab1 * grb / b;
1504   *rightw = twoab1 * gra / a;
1505   PetscFunctionReturn(PETSC_SUCCESS);
1506 }
1507 
1508 /* Evaluates the nth jacobi polynomial with weight parameters a,b at a point x.
1509    Recurrence relations implemented from the pseudocode given in Karniadakis and Sherwin, Appendix B */
1510 static inline PetscErrorCode PetscDTComputeJacobi(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscReal *P)
1511 {
1512   PetscReal pn1, pn2;
1513   PetscReal cnm1, cnm1x, cnm2;
1514   PetscInt  k;
1515 
1516   PetscFunctionBegin;
1517   if (!n) {
1518     *P = 1.0;
1519     PetscFunctionReturn(PETSC_SUCCESS);
1520   }
1521   PetscDTJacobiRecurrence_Internal(1, a, b, cnm1, cnm1x, cnm2);
1522   pn2 = 1.;
1523   pn1 = cnm1 + cnm1x * x;
1524   if (n == 1) {
1525     *P = pn1;
1526     PetscFunctionReturn(PETSC_SUCCESS);
1527   }
1528   *P = 0.0;
1529   for (k = 2; k < n + 1; ++k) {
1530     PetscDTJacobiRecurrence_Internal(k, a, b, cnm1, cnm1x, cnm2);
1531 
1532     *P  = (cnm1 + cnm1x * x) * pn1 - cnm2 * pn2;
1533     pn2 = pn1;
1534     pn1 = *P;
1535   }
1536   PetscFunctionReturn(PETSC_SUCCESS);
1537 }
1538 
1539 /* Evaluates the first derivative of P_{n}^{a,b} at a point x. */
1540 static inline PetscErrorCode PetscDTComputeJacobiDerivative(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscInt k, PetscReal *P)
1541 {
1542   PetscReal nP;
1543   PetscInt  i;
1544 
1545   PetscFunctionBegin;
1546   *P = 0.0;
1547   if (k > n) PetscFunctionReturn(PETSC_SUCCESS);
1548   PetscCall(PetscDTComputeJacobi(a + k, b + k, n - k, x, &nP));
1549   for (i = 0; i < k; i++) nP *= (a + b + n + 1. + i) * 0.5;
1550   *P = nP;
1551   PetscFunctionReturn(PETSC_SUCCESS);
1552 }
1553 
1554 static PetscErrorCode PetscDTGaussJacobiQuadrature_Newton_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal x[], PetscReal w[])
1555 {
1556   PetscInt  maxIter = 100;
1557   PetscReal eps     = PetscExpReal(0.75 * PetscLogReal(PETSC_MACHINE_EPSILON));
1558   PetscReal a1, a6, gf;
1559   PetscInt  k;
1560 
1561   PetscFunctionBegin;
1562   a1 = PetscPowReal(2.0, a + b + 1);
1563 #if defined(PETSC_HAVE_LGAMMA)
1564   {
1565     PetscReal a2, a3, a4, a5;
1566     a2 = PetscLGamma(a + npoints + 1);
1567     a3 = PetscLGamma(b + npoints + 1);
1568     a4 = PetscLGamma(a + b + npoints + 1);
1569     a5 = PetscLGamma(npoints + 1);
1570     gf = PetscExpReal(a2 + a3 - (a4 + a5));
1571   }
1572 #else
1573   {
1574     PetscInt ia, ib;
1575 
1576     ia = (PetscInt)a;
1577     ib = (PetscInt)b;
1578     gf = 1.;
1579     if (ia == a && ia >= 0) { /* compute ratio of rising factorals wrt a */
1580       for (k = 0; k < ia; k++) gf *= (npoints + 1. + k) / (npoints + b + 1. + k);
1581     } else if (b == b && ib >= 0) { /* compute ratio of rising factorials wrt b */
1582       for (k = 0; k < ib; k++) gf *= (npoints + 1. + k) / (npoints + a + 1. + k);
1583     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "lgamma() - math routine is unavailable.");
1584   }
1585 #endif
1586 
1587   a6 = a1 * gf;
1588   /* Computes the m roots of P_{m}^{a,b} on [-1,1] by Newton's method with Chebyshev points as initial guesses.
1589    Algorithm implemented from the pseudocode given by Karniadakis and Sherwin and Python in FIAT */
1590   for (k = 0; k < npoints; ++k) {
1591     PetscReal r = PetscCosReal(PETSC_PI * (1. - (4. * k + 3. + 2. * b) / (4. * npoints + 2. * (a + b + 1.)))), dP;
1592     PetscInt  j;
1593 
1594     if (k > 0) r = 0.5 * (r + x[k - 1]);
1595     for (j = 0; j < maxIter; ++j) {
1596       PetscReal s = 0.0, delta, f, fp;
1597       PetscInt  i;
1598 
1599       for (i = 0; i < k; ++i) s = s + 1.0 / (r - x[i]);
1600       PetscCall(PetscDTComputeJacobi(a, b, npoints, r, &f));
1601       PetscCall(PetscDTComputeJacobiDerivative(a, b, npoints, r, 1, &fp));
1602       delta = f / (fp - f * s);
1603       r     = r - delta;
1604       if (PetscAbsReal(delta) < eps) break;
1605     }
1606     x[k] = r;
1607     PetscCall(PetscDTComputeJacobiDerivative(a, b, npoints, x[k], 1, &dP));
1608     w[k] = a6 / (1.0 - PetscSqr(x[k])) / PetscSqr(dP);
1609   }
1610   PetscFunctionReturn(PETSC_SUCCESS);
1611 }
1612 
1613 /* Compute the diagonals of the Jacobi matrix used in Golub & Welsch algorithms for Gauss-Jacobi
1614  * quadrature weight calculations on [-1,1] for exponents (1. + x)^a (1.-x)^b */
1615 static PetscErrorCode PetscDTJacobiMatrix_Internal(PetscInt nPoints, PetscReal a, PetscReal b, PetscReal *d, PetscReal *s)
1616 {
1617   PetscInt i;
1618 
1619   PetscFunctionBegin;
1620   for (i = 0; i < nPoints; i++) {
1621     PetscReal A, B, C;
1622 
1623     PetscDTJacobiRecurrence_Internal(i + 1, a, b, A, B, C);
1624     d[i] = -A / B;
1625     if (i) s[i - 1] *= C / B;
1626     if (i < nPoints - 1) s[i] = 1. / B;
1627   }
1628   PetscFunctionReturn(PETSC_SUCCESS);
1629 }
1630 
1631 static PetscErrorCode PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal *x, PetscReal *w)
1632 {
1633   PetscReal mu0;
1634   PetscReal ga, gb, gab;
1635   PetscInt  i;
1636 
1637   PetscFunctionBegin;
1638   PetscCall(PetscCitationsRegister(GolubWelschCitation, &GolubWelschCite));
1639 
1640 #if defined(PETSC_HAVE_TGAMMA)
1641   ga  = PetscTGamma(a + 1);
1642   gb  = PetscTGamma(b + 1);
1643   gab = PetscTGamma(a + b + 2);
1644 #else
1645   {
1646     PetscInt ia, ib;
1647 
1648     ia = (PetscInt)a;
1649     ib = (PetscInt)b;
1650     if (ia == a && ib == b && ia + 1 > 0 && ib + 1 > 0 && ia + ib + 2 > 0) { /* All gamma(x) terms are (x-1)! terms */
1651       PetscCall(PetscDTFactorial(ia, &ga));
1652       PetscCall(PetscDTFactorial(ib, &gb));
1653       PetscCall(PetscDTFactorial(ia + ib + 1, &gab));
1654     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "tgamma() - math routine is unavailable.");
1655   }
1656 #endif
1657   mu0 = PetscPowReal(2., a + b + 1.) * ga * gb / gab;
1658 
1659 #if defined(PETSCDTGAUSSIANQUADRATURE_EIG)
1660   {
1661     PetscReal   *diag, *subdiag;
1662     PetscScalar *V;
1663 
1664     PetscCall(PetscMalloc2(npoints, &diag, npoints, &subdiag));
1665     PetscCall(PetscMalloc1(npoints * npoints, &V));
1666     PetscCall(PetscDTJacobiMatrix_Internal(npoints, a, b, diag, subdiag));
1667     for (i = 0; i < npoints - 1; i++) subdiag[i] = PetscSqrtReal(subdiag[i]);
1668     PetscCall(PetscDTSymmetricTridiagonalEigensolve(npoints, diag, subdiag, x, V));
1669     for (i = 0; i < npoints; i++) w[i] = PetscSqr(PetscRealPart(V[i * npoints])) * mu0;
1670     PetscCall(PetscFree(V));
1671     PetscCall(PetscFree2(diag, subdiag));
1672   }
1673 #else
1674   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
1675 #endif
1676   { /* As of March 2, 2020, The Sun Performance Library breaks the LAPACK contract for xstegr and xsteqr: the
1677        eigenvalues are not guaranteed to be in ascending order.  So we heave a passive aggressive sigh and check that
1678        the eigenvalues are sorted */
1679     PetscBool sorted;
1680 
1681     PetscCall(PetscSortedReal(npoints, x, &sorted));
1682     if (!sorted) {
1683       PetscInt  *order, i;
1684       PetscReal *tmp;
1685 
1686       PetscCall(PetscMalloc2(npoints, &order, npoints, &tmp));
1687       for (i = 0; i < npoints; i++) order[i] = i;
1688       PetscCall(PetscSortRealWithPermutation(npoints, x, order));
1689       PetscCall(PetscArraycpy(tmp, x, npoints));
1690       for (i = 0; i < npoints; i++) x[i] = tmp[order[i]];
1691       PetscCall(PetscArraycpy(tmp, w, npoints));
1692       for (i = 0; i < npoints; i++) w[i] = tmp[order[i]];
1693       PetscCall(PetscFree2(order, tmp));
1694     }
1695   }
1696   PetscFunctionReturn(PETSC_SUCCESS);
1697 }
1698 
1699 static PetscErrorCode PetscDTGaussJacobiQuadrature_Internal(PetscInt npoints, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
1700 {
1701   PetscFunctionBegin;
1702   PetscCheck(npoints >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of points must be positive");
1703   /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
1704   PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "alpha must be > -1.");
1705   PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "beta must be > -1.");
1706 
1707   if (newton) PetscCall(PetscDTGaussJacobiQuadrature_Newton_Internal(npoints, alpha, beta, x, w));
1708   else PetscCall(PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(npoints, alpha, beta, x, w));
1709   if (alpha == beta) { /* symmetrize */
1710     PetscInt i;
1711     for (i = 0; i < (npoints + 1) / 2; i++) {
1712       PetscInt  j  = npoints - 1 - i;
1713       PetscReal xi = x[i];
1714       PetscReal xj = x[j];
1715       PetscReal wi = w[i];
1716       PetscReal wj = w[j];
1717 
1718       x[i] = (xi - xj) / 2.;
1719       x[j] = (xj - xi) / 2.;
1720       w[i] = w[j] = (wi + wj) / 2.;
1721     }
1722   }
1723   PetscFunctionReturn(PETSC_SUCCESS);
1724 }
1725 
1726 /*@
1727   PetscDTGaussJacobiQuadrature - quadrature for the interval $[a, b]$ with the weight function
1728   $(x-a)^\alpha (x-b)^\beta$.
1729 
1730   Not Collective
1731 
1732   Input Parameters:
1733 + npoints - the number of points in the quadrature rule
1734 . a       - the left endpoint of the interval
1735 . b       - the right endpoint of the interval
1736 . alpha   - the left exponent
1737 - beta    - the right exponent
1738 
1739   Output Parameters:
1740 + x - array of length `npoints`, the locations of the quadrature points
1741 - w - array of length `npoints`, the weights of the quadrature points
1742 
1743   Level: intermediate
1744 
1745   Note:
1746   This quadrature rule is exact for polynomials up to degree 2*`npoints` - 1.
1747 
1748 .seealso: `PetscDTGaussQuadrature()`
1749 @*/
1750 PetscErrorCode PetscDTGaussJacobiQuadrature(PetscInt npoints, PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
1751 {
1752   PetscInt i;
1753 
1754   PetscFunctionBegin;
1755   PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal));
1756   if (a != -1. || b != 1.) { /* shift */
1757     for (i = 0; i < npoints; i++) {
1758       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1759       w[i] *= (b - a) / 2.;
1760     }
1761   }
1762   PetscFunctionReturn(PETSC_SUCCESS);
1763 }
1764 
1765 static PetscErrorCode PetscDTGaussLobattoJacobiQuadrature_Internal(PetscInt npoints, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
1766 {
1767   PetscInt i;
1768 
1769   PetscFunctionBegin;
1770   PetscCheck(npoints >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of points must be positive");
1771   /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
1772   PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "alpha must be > -1.");
1773   PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "beta must be > -1.");
1774 
1775   x[0]           = -1.;
1776   x[npoints - 1] = 1.;
1777   if (npoints > 2) PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints - 2, alpha + 1., beta + 1., &x[1], &w[1], newton));
1778   for (i = 1; i < npoints - 1; i++) w[i] /= (1. - x[i] * x[i]);
1779   PetscCall(PetscDTGaussLobattoJacobiEndweights_Internal(npoints, alpha, beta, &w[0], &w[npoints - 1]));
1780   PetscFunctionReturn(PETSC_SUCCESS);
1781 }
1782 
1783 /*@
1784   PetscDTGaussLobattoJacobiQuadrature - quadrature for the interval $[a, b]$ with the weight function
1785   $(x-a)^\alpha (x-b)^\beta$, with endpoints `a` and `b` included as quadrature points.
1786 
1787   Not Collective
1788 
1789   Input Parameters:
1790 + npoints - the number of points in the quadrature rule
1791 . a       - the left endpoint of the interval
1792 . b       - the right endpoint of the interval
1793 . alpha   - the left exponent
1794 - beta    - the right exponent
1795 
1796   Output Parameters:
1797 + x - array of length `npoints`, the locations of the quadrature points
1798 - w - array of length `npoints`, the weights of the quadrature points
1799 
1800   Level: intermediate
1801 
1802   Note:
1803   This quadrature rule is exact for polynomials up to degree 2*`npoints` - 3.
1804 
1805 .seealso: `PetscDTGaussJacobiQuadrature()`
1806 @*/
1807 PetscErrorCode PetscDTGaussLobattoJacobiQuadrature(PetscInt npoints, PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
1808 {
1809   PetscInt i;
1810 
1811   PetscFunctionBegin;
1812   PetscCall(PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal));
1813   if (a != -1. || b != 1.) { /* shift */
1814     for (i = 0; i < npoints; i++) {
1815       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1816       w[i] *= (b - a) / 2.;
1817     }
1818   }
1819   PetscFunctionReturn(PETSC_SUCCESS);
1820 }
1821 
1822 /*@
1823   PetscDTGaussQuadrature - create Gauss-Legendre quadrature
1824 
1825   Not Collective
1826 
1827   Input Parameters:
1828 + npoints - number of points
1829 . a       - left end of interval (often-1)
1830 - b       - right end of interval (often +1)
1831 
1832   Output Parameters:
1833 + x - quadrature points
1834 - w - quadrature weights
1835 
1836   Level: intermediate
1837 
1838   Note:
1839   See {cite}`golub1969calculation`
1840 
1841 .seealso: `PetscDTLegendreEval()`, `PetscDTGaussJacobiQuadrature()`
1842 @*/
1843 PetscErrorCode PetscDTGaussQuadrature(PetscInt npoints, PetscReal a, PetscReal b, PetscReal x[], PetscReal w[])
1844 {
1845   PetscInt i;
1846 
1847   PetscFunctionBegin;
1848   PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints, 0., 0., x, w, PetscDTGaussQuadratureNewton_Internal));
1849   if (a != -1. || b != 1.) { /* shift */
1850     for (i = 0; i < npoints; i++) {
1851       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1852       w[i] *= (b - a) / 2.;
1853     }
1854   }
1855   PetscFunctionReturn(PETSC_SUCCESS);
1856 }
1857 
1858 /*@
1859   PetscDTGaussLobattoLegendreQuadrature - creates a set of the locations and weights of the Gauss-Lobatto-Legendre
1860   nodes of a given size on the domain $[-1,1]$
1861 
1862   Not Collective
1863 
1864   Input Parameters:
1865 + npoints - number of grid nodes
1866 - type    - `PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA` or `PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON`
1867 
1868   Output Parameters:
1869 + x - quadrature points, pass in an array of length `npoints`
1870 - w - quadrature weights, pass in an array of length `npoints`
1871 
1872   Level: intermediate
1873 
1874   Notes:
1875   For n > 30  the Newton approach computes duplicate (incorrect) values for some nodes because the initial guess is apparently not
1876   close enough to the desired solution
1877 
1878   These are useful for implementing spectral methods based on Gauss-Lobatto-Legendre (GLL) nodes
1879 
1880   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
1881 
1882 .seealso: `PetscDTGaussQuadrature()`, `PetscGaussLobattoLegendreCreateType`
1883 
1884 @*/
1885 PetscErrorCode PetscDTGaussLobattoLegendreQuadrature(PetscInt npoints, PetscGaussLobattoLegendreCreateType type, PetscReal x[], PetscReal w[])
1886 {
1887   PetscBool newton;
1888 
1889   PetscFunctionBegin;
1890   PetscCheck(npoints >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must provide at least 2 grid points per element");
1891   newton = (PetscBool)(type == PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON);
1892   PetscCall(PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, 0., 0., x, w, newton));
1893   PetscFunctionReturn(PETSC_SUCCESS);
1894 }
1895 
1896 /*@
1897   PetscDTGaussTensorQuadrature - creates a tensor-product Gauss quadrature
1898 
1899   Not Collective
1900 
1901   Input Parameters:
1902 + dim     - The spatial dimension
1903 . Nc      - The number of components
1904 . npoints - number of points in one dimension
1905 . a       - left end of interval (often-1)
1906 - b       - right end of interval (often +1)
1907 
1908   Output Parameter:
1909 . q - A `PetscQuadrature` object
1910 
1911   Level: intermediate
1912 
1913 .seealso: `PetscDTGaussQuadrature()`, `PetscDTLegendreEval()`
1914 @*/
1915 PetscErrorCode PetscDTGaussTensorQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
1916 {
1917   DMPolytopeType ct;
1918   PetscInt       totpoints = dim > 1 ? dim > 2 ? npoints * PetscSqr(npoints) : PetscSqr(npoints) : npoints;
1919   PetscReal     *x, *w, *xw, *ww;
1920 
1921   PetscFunctionBegin;
1922   PetscCall(PetscMalloc1(totpoints * dim, &x));
1923   PetscCall(PetscMalloc1(totpoints * Nc, &w));
1924   /* Set up the Golub-Welsch system */
1925   switch (dim) {
1926   case 0:
1927     ct = DM_POLYTOPE_POINT;
1928     PetscCall(PetscFree(x));
1929     PetscCall(PetscFree(w));
1930     PetscCall(PetscMalloc1(1, &x));
1931     PetscCall(PetscMalloc1(Nc, &w));
1932     totpoints = 1;
1933     x[0]      = 0.0;
1934     for (PetscInt c = 0; c < Nc; ++c) w[c] = 1.0;
1935     break;
1936   case 1:
1937     ct = DM_POLYTOPE_SEGMENT;
1938     PetscCall(PetscMalloc1(npoints, &ww));
1939     PetscCall(PetscDTGaussQuadrature(npoints, a, b, x, ww));
1940     for (PetscInt i = 0; i < npoints; ++i)
1941       for (PetscInt c = 0; c < Nc; ++c) w[i * Nc + c] = ww[i];
1942     PetscCall(PetscFree(ww));
1943     break;
1944   case 2:
1945     ct = DM_POLYTOPE_QUADRILATERAL;
1946     PetscCall(PetscMalloc2(npoints, &xw, npoints, &ww));
1947     PetscCall(PetscDTGaussQuadrature(npoints, a, b, xw, ww));
1948     for (PetscInt i = 0; i < npoints; ++i) {
1949       for (PetscInt j = 0; j < npoints; ++j) {
1950         x[(i * npoints + j) * dim + 0] = xw[i];
1951         x[(i * npoints + j) * dim + 1] = xw[j];
1952         for (PetscInt c = 0; c < Nc; ++c) w[(i * npoints + j) * Nc + c] = ww[i] * ww[j];
1953       }
1954     }
1955     PetscCall(PetscFree2(xw, ww));
1956     break;
1957   case 3:
1958     ct = DM_POLYTOPE_HEXAHEDRON;
1959     PetscCall(PetscMalloc2(npoints, &xw, npoints, &ww));
1960     PetscCall(PetscDTGaussQuadrature(npoints, a, b, xw, ww));
1961     for (PetscInt i = 0; i < npoints; ++i) {
1962       for (PetscInt j = 0; j < npoints; ++j) {
1963         for (PetscInt k = 0; k < npoints; ++k) {
1964           x[((i * npoints + j) * npoints + k) * dim + 0] = xw[i];
1965           x[((i * npoints + j) * npoints + k) * dim + 1] = xw[j];
1966           x[((i * npoints + j) * npoints + k) * dim + 2] = xw[k];
1967           for (PetscInt c = 0; c < Nc; ++c) w[((i * npoints + j) * npoints + k) * Nc + c] = ww[i] * ww[j] * ww[k];
1968         }
1969       }
1970     }
1971     PetscCall(PetscFree2(xw, ww));
1972     break;
1973   default:
1974     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %" PetscInt_FMT, dim);
1975   }
1976   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
1977   PetscCall(PetscQuadratureSetCellType(*q, ct));
1978   PetscCall(PetscQuadratureSetOrder(*q, 2 * npoints - 1));
1979   PetscCall(PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w));
1980   PetscCall(PetscObjectChangeTypeName((PetscObject)*q, "GaussTensor"));
1981   PetscFunctionReturn(PETSC_SUCCESS);
1982 }
1983 
1984 /*@
1985   PetscDTStroudConicalQuadrature - create Stroud conical quadrature for a simplex {cite}`karniadakis2005spectral`
1986 
1987   Not Collective
1988 
1989   Input Parameters:
1990 + dim     - The simplex dimension
1991 . Nc      - The number of components
1992 . npoints - The number of points in one dimension
1993 . a       - left end of interval (often-1)
1994 - b       - right end of interval (often +1)
1995 
1996   Output Parameter:
1997 . q - A `PetscQuadrature` object
1998 
1999   Level: intermediate
2000 
2001   Note:
2002   For `dim` == 1, this is Gauss-Legendre quadrature
2003 
2004 .seealso: `PetscDTGaussTensorQuadrature()`, `PetscDTGaussQuadrature()`
2005 @*/
2006 PetscErrorCode PetscDTStroudConicalQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
2007 {
2008   DMPolytopeType ct;
2009   PetscInt       totpoints;
2010   PetscReal     *p1, *w1;
2011   PetscReal     *x, *w;
2012 
2013   PetscFunctionBegin;
2014   PetscCheck(!(a != -1.0) && !(b != 1.0), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must use default internal right now");
2015   switch (dim) {
2016   case 0:
2017     ct = DM_POLYTOPE_POINT;
2018     break;
2019   case 1:
2020     ct = DM_POLYTOPE_SEGMENT;
2021     break;
2022   case 2:
2023     ct = DM_POLYTOPE_TRIANGLE;
2024     break;
2025   case 3:
2026     ct = DM_POLYTOPE_TETRAHEDRON;
2027     break;
2028   default:
2029     ct = DM_POLYTOPE_UNKNOWN;
2030   }
2031   totpoints = 1;
2032   for (PetscInt i = 0; i < dim; ++i) totpoints *= npoints;
2033   PetscCall(PetscMalloc1(totpoints * dim, &x));
2034   PetscCall(PetscMalloc1(totpoints * Nc, &w));
2035   PetscCall(PetscMalloc2(npoints, &p1, npoints, &w1));
2036   for (PetscInt i = 0; i < totpoints * Nc; ++i) w[i] = 1.;
2037   for (PetscInt i = 0, totprev = 1, totrem = totpoints / npoints; i < dim; ++i) {
2038     PetscReal mul;
2039 
2040     mul = PetscPowReal(2., -i);
2041     PetscCall(PetscDTGaussJacobiQuadrature(npoints, -1., 1., i, 0.0, p1, w1));
2042     for (PetscInt pt = 0, l = 0; l < totprev; l++) {
2043       for (PetscInt j = 0; j < npoints; j++) {
2044         for (PetscInt m = 0; m < totrem; m++, pt++) {
2045           for (PetscInt k = 0; k < i; k++) x[pt * dim + k] = (x[pt * dim + k] + 1.) * (1. - p1[j]) * 0.5 - 1.;
2046           x[pt * dim + i] = p1[j];
2047           for (PetscInt c = 0; c < Nc; c++) w[pt * Nc + c] *= mul * w1[j];
2048         }
2049       }
2050     }
2051     totprev *= npoints;
2052     totrem /= npoints;
2053   }
2054   PetscCall(PetscFree2(p1, w1));
2055   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
2056   PetscCall(PetscQuadratureSetCellType(*q, ct));
2057   PetscCall(PetscQuadratureSetOrder(*q, 2 * npoints - 1));
2058   PetscCall(PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w));
2059   PetscCall(PetscObjectChangeTypeName((PetscObject)*q, "StroudConical"));
2060   PetscFunctionReturn(PETSC_SUCCESS);
2061 }
2062 
2063 static PetscBool MinSymTriQuadCite       = PETSC_FALSE;
2064 const char       MinSymTriQuadCitation[] = "@article{WitherdenVincent2015,\n"
2065                                            "  title = {On the identification of symmetric quadrature rules for finite element methods},\n"
2066                                            "  journal = {Computers & Mathematics with Applications},\n"
2067                                            "  volume = {69},\n"
2068                                            "  number = {10},\n"
2069                                            "  pages = {1232-1241},\n"
2070                                            "  year = {2015},\n"
2071                                            "  issn = {0898-1221},\n"
2072                                            "  doi = {10.1016/j.camwa.2015.03.017},\n"
2073                                            "  url = {https://www.sciencedirect.com/science/article/pii/S0898122115001224},\n"
2074                                            "  author = {F.D. Witherden and P.E. Vincent},\n"
2075                                            "}\n";
2076 
2077 #include "petscdttriquadrules.h"
2078 
2079 static PetscBool MinSymTetQuadCite       = PETSC_FALSE;
2080 const char       MinSymTetQuadCitation[] = "@article{JaskowiecSukumar2021\n"
2081                                            "  author = {Jaskowiec, Jan and Sukumar, N.},\n"
2082                                            "  title = {High-order symmetric cubature rules for tetrahedra and pyramids},\n"
2083                                            "  journal = {International Journal for Numerical Methods in Engineering},\n"
2084                                            "  volume = {122},\n"
2085                                            "  number = {1},\n"
2086                                            "  pages = {148-171},\n"
2087                                            "  doi = {10.1002/nme.6528},\n"
2088                                            "  url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/nme.6528},\n"
2089                                            "  eprint = {https://onlinelibrary.wiley.com/doi/pdf/10.1002/nme.6528},\n"
2090                                            "  year = {2021}\n"
2091                                            "}\n";
2092 
2093 #include "petscdttetquadrules.h"
2094 
2095 // https://en.wikipedia.org/wiki/Partition_(number_theory)
2096 static PetscErrorCode PetscDTPartitionNumber(PetscInt n, PetscInt *p)
2097 {
2098   // sequence A000041 in the OEIS
2099   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};
2100   PetscInt       tabulated_max = PETSC_STATIC_ARRAY_LENGTH(partition) - 1;
2101 
2102   PetscFunctionBegin;
2103   PetscCheck(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Partition number not defined for negative number %" PetscInt_FMT, n);
2104   // not implementing the pentagonal number recurrence, we don't need partition numbers for n that high
2105   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);
2106   *p = partition[n];
2107   PetscFunctionReturn(PETSC_SUCCESS);
2108 }
2109 
2110 /*@
2111   PetscDTSimplexQuadrature - Create a quadrature rule for a simplex that exactly integrates polynomials up to a given degree.
2112 
2113   Not Collective
2114 
2115   Input Parameters:
2116 + dim    - The spatial dimension of the simplex (1 = segment, 2 = triangle, 3 = tetrahedron)
2117 . degree - The largest polynomial degree that is required to be integrated exactly
2118 - type   - left end of interval (often-1)
2119 
2120   Output Parameter:
2121 . quad - A `PetscQuadrature` object for integration over the biunit simplex
2122             (defined by the bounds $x_i >= -1$ and $\sum_i x_i <= 2 - d$) that is exact for
2123             polynomials up to the given degree
2124 
2125   Level: intermediate
2126 
2127 .seealso: `PetscDTSimplexQuadratureType`, `PetscDTGaussQuadrature()`, `PetscDTStroudCononicalQuadrature()`, `PetscQuadrature`
2128 @*/
2129 PetscErrorCode PetscDTSimplexQuadrature(PetscInt dim, PetscInt degree, PetscDTSimplexQuadratureType type, PetscQuadrature *quad)
2130 {
2131   PetscDTSimplexQuadratureType orig_type = type;
2132 
2133   PetscFunctionBegin;
2134   PetscCheck(dim >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative dimension %" PetscInt_FMT, dim);
2135   PetscCheck(degree >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative degree %" PetscInt_FMT, degree);
2136   if (type == PETSCDTSIMPLEXQUAD_DEFAULT) type = PETSCDTSIMPLEXQUAD_MINSYM;
2137   if (type == PETSCDTSIMPLEXQUAD_CONIC || dim < 2) {
2138     PetscInt points_per_dim = (degree + 2) / 2; // ceil((degree + 1) / 2);
2139     PetscCall(PetscDTStroudConicalQuadrature(dim, 1, points_per_dim, -1, 1, quad));
2140   } else {
2141     DMPolytopeType    ct;
2142     PetscInt          n    = dim + 1;
2143     PetscInt          fact = 1;
2144     PetscInt         *part, *perm;
2145     PetscInt          p = 0;
2146     PetscInt          max_degree;
2147     const PetscInt   *nodes_per_type     = NULL;
2148     const PetscInt   *all_num_full_nodes = NULL;
2149     const PetscReal **weights_list       = NULL;
2150     const PetscReal **compact_nodes_list = NULL;
2151     const char       *citation           = NULL;
2152     PetscBool        *cited              = NULL;
2153 
2154     switch (dim) {
2155     case 0:
2156       ct = DM_POLYTOPE_POINT;
2157       break;
2158     case 1:
2159       ct = DM_POLYTOPE_SEGMENT;
2160       break;
2161     case 2:
2162       ct = DM_POLYTOPE_TRIANGLE;
2163       break;
2164     case 3:
2165       ct = DM_POLYTOPE_TETRAHEDRON;
2166       break;
2167     default:
2168       ct = DM_POLYTOPE_UNKNOWN;
2169     }
2170     switch (dim) {
2171     case 2:
2172       cited              = &MinSymTriQuadCite;
2173       citation           = MinSymTriQuadCitation;
2174       max_degree         = PetscDTWVTriQuad_max_degree;
2175       nodes_per_type     = PetscDTWVTriQuad_num_orbits;
2176       all_num_full_nodes = PetscDTWVTriQuad_num_nodes;
2177       weights_list       = PetscDTWVTriQuad_weights;
2178       compact_nodes_list = PetscDTWVTriQuad_orbits;
2179       break;
2180     case 3:
2181       cited              = &MinSymTetQuadCite;
2182       citation           = MinSymTetQuadCitation;
2183       max_degree         = PetscDTJSTetQuad_max_degree;
2184       nodes_per_type     = PetscDTJSTetQuad_num_orbits;
2185       all_num_full_nodes = PetscDTJSTetQuad_num_nodes;
2186       weights_list       = PetscDTJSTetQuad_weights;
2187       compact_nodes_list = PetscDTJSTetQuad_orbits;
2188       break;
2189     default:
2190       max_degree = -1;
2191       break;
2192     }
2193 
2194     if (degree > max_degree) {
2195       if (orig_type == PETSCDTSIMPLEXQUAD_DEFAULT) {
2196         // fall back to conic
2197         PetscCall(PetscDTSimplexQuadrature(dim, degree, PETSCDTSIMPLEXQUAD_CONIC, quad));
2198         PetscFunctionReturn(PETSC_SUCCESS);
2199       } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Minimal symmetric quadrature for dim %" PetscInt_FMT ", degree %" PetscInt_FMT " unsupported", dim, degree);
2200     }
2201 
2202     PetscCall(PetscCitationsRegister(citation, cited));
2203 
2204     PetscCall(PetscDTPartitionNumber(n, &p));
2205     for (PetscInt d = 2; d <= n; d++) fact *= d;
2206 
2207     PetscInt         num_full_nodes      = all_num_full_nodes[degree];
2208     const PetscReal *all_compact_nodes   = compact_nodes_list[degree];
2209     const PetscReal *all_compact_weights = weights_list[degree];
2210     nodes_per_type                       = &nodes_per_type[p * degree];
2211 
2212     PetscReal      *points;
2213     PetscReal      *counts;
2214     PetscReal      *weights;
2215     PetscReal      *bary_to_biunit; // row-major transformation of barycentric coordinate to biunit
2216     PetscQuadrature q;
2217 
2218     // compute the transformation
2219     PetscCall(PetscMalloc1(n * dim, &bary_to_biunit));
2220     for (PetscInt d = 0; d < dim; d++) {
2221       for (PetscInt b = 0; b < n; b++) bary_to_biunit[d * n + b] = (d == b) ? 1.0 : -1.0;
2222     }
2223 
2224     PetscCall(PetscMalloc3(n, &part, n, &perm, n, &counts));
2225     PetscCall(PetscCalloc1(num_full_nodes * dim, &points));
2226     PetscCall(PetscMalloc1(num_full_nodes, &weights));
2227 
2228     // (0, 0, ...) is the first partition lexicographically
2229     PetscCall(PetscArrayzero(part, n));
2230     PetscCall(PetscArrayzero(counts, n));
2231     counts[0] = n;
2232 
2233     // for each partition
2234     for (PetscInt s = 0, node_offset = 0; s < p; s++) {
2235       PetscInt num_compact_coords = part[n - 1] + 1;
2236 
2237       const PetscReal *compact_nodes   = all_compact_nodes;
2238       const PetscReal *compact_weights = all_compact_weights;
2239       all_compact_nodes += num_compact_coords * nodes_per_type[s];
2240       all_compact_weights += nodes_per_type[s];
2241 
2242       // for every permutation of the vertices
2243       for (PetscInt f = 0; f < fact; f++) {
2244         PetscCall(PetscDTEnumPerm(n, f, perm, NULL));
2245 
2246         // check if it is a valid permutation
2247         PetscInt digit;
2248         for (digit = 1; digit < n; digit++) {
2249           // skip permutations that would duplicate a node because it has a smaller symmetry group
2250           if (part[digit - 1] == part[digit] && perm[digit - 1] > perm[digit]) break;
2251         }
2252         if (digit < n) continue;
2253 
2254         // create full nodes from this permutation of the compact nodes
2255         PetscReal *full_nodes   = &points[node_offset * dim];
2256         PetscReal *full_weights = &weights[node_offset];
2257 
2258         PetscCall(PetscArraycpy(full_weights, compact_weights, nodes_per_type[s]));
2259         for (PetscInt b = 0; b < n; b++) {
2260           for (PetscInt d = 0; d < dim; d++) {
2261             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]];
2262           }
2263         }
2264         node_offset += nodes_per_type[s];
2265       }
2266 
2267       if (s < p - 1) { // Generate the next partition
2268         /* A partition is described by the number of coordinates that are in
2269          * each set of duplicates (counts) and redundantly by mapping each
2270          * index to its set of duplicates (part)
2271          *
2272          * Counts should always be in nonincreasing order
2273          *
2274          * We want to generate the partitions lexically by part, which means
2275          * finding the last index where count > 1 and reducing by 1.
2276          *
2277          * For the new counts beyond that index, we eagerly assign the remaining
2278          * capacity of the partition to smaller indices (ensures lexical ordering),
2279          * while respecting the nonincreasing invariant of the counts
2280          */
2281         PetscInt last_digit            = part[n - 1];
2282         PetscInt last_digit_with_extra = last_digit;
2283         while (counts[last_digit_with_extra] == 1) last_digit_with_extra--;
2284         PetscInt limit               = --counts[last_digit_with_extra];
2285         PetscInt total_to_distribute = last_digit - last_digit_with_extra + 1;
2286         for (PetscInt digit = last_digit_with_extra + 1; digit < n; digit++) {
2287           counts[digit] = PetscMin(limit, total_to_distribute);
2288           total_to_distribute -= PetscMin(limit, total_to_distribute);
2289         }
2290         for (PetscInt digit = 0, offset = 0; digit < n; digit++) {
2291           PetscInt count = counts[digit];
2292           for (PetscInt c = 0; c < count; c++) part[offset++] = digit;
2293         }
2294       }
2295     }
2296     PetscCall(PetscFree3(part, perm, counts));
2297     PetscCall(PetscFree(bary_to_biunit));
2298     PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, &q));
2299     PetscCall(PetscQuadratureSetCellType(q, ct));
2300     PetscCall(PetscQuadratureSetOrder(q, degree));
2301     PetscCall(PetscQuadratureSetData(q, dim, 1, num_full_nodes, points, weights));
2302     *quad = q;
2303   }
2304   PetscFunctionReturn(PETSC_SUCCESS);
2305 }
2306 
2307 /*@
2308   PetscDTTanhSinhTensorQuadrature - create tanh-sinh quadrature for a tensor product cell
2309 
2310   Not Collective
2311 
2312   Input Parameters:
2313 + dim   - The cell dimension
2314 . level - The number of points in one dimension, $2^l$
2315 . a     - left end of interval (often-1)
2316 - b     - right end of interval (often +1)
2317 
2318   Output Parameter:
2319 . q - A `PetscQuadrature` object
2320 
2321   Level: intermediate
2322 
2323 .seealso: `PetscDTGaussTensorQuadrature()`, `PetscQuadrature`
2324 @*/
2325 PetscErrorCode PetscDTTanhSinhTensorQuadrature(PetscInt dim, PetscInt level, PetscReal a, PetscReal b, PetscQuadrature *q)
2326 {
2327   DMPolytopeType  ct;
2328   const PetscInt  p     = 16;                        /* Digits of precision in the evaluation */
2329   const PetscReal alpha = (b - a) / 2.;              /* Half-width of the integration interval */
2330   const PetscReal beta  = (b + a) / 2.;              /* Center of the integration interval */
2331   const PetscReal h     = PetscPowReal(2.0, -level); /* Step size, length between x_k */
2332   PetscReal       xk;                                /* Quadrature point x_k on reference domain [-1, 1] */
2333   PetscReal       wk = 0.5 * PETSC_PI;               /* Quadrature weight at x_k */
2334   PetscReal      *x, *w;
2335   PetscInt        K, k, npoints;
2336 
2337   PetscFunctionBegin;
2338   PetscCheck(dim <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Dimension %" PetscInt_FMT " not yet implemented", dim);
2339   PetscCheck(level, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a number of significant digits");
2340   switch (dim) {
2341   case 0:
2342     ct = DM_POLYTOPE_POINT;
2343     break;
2344   case 1:
2345     ct = DM_POLYTOPE_SEGMENT;
2346     break;
2347   case 2:
2348     ct = DM_POLYTOPE_QUADRILATERAL;
2349     break;
2350   case 3:
2351     ct = DM_POLYTOPE_HEXAHEDRON;
2352     break;
2353   default:
2354     ct = DM_POLYTOPE_UNKNOWN;
2355   }
2356   /* Find K such that the weights are < 32 digits of precision */
2357   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)));
2358   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
2359   PetscCall(PetscQuadratureSetCellType(*q, ct));
2360   PetscCall(PetscQuadratureSetOrder(*q, 2 * K + 1));
2361   npoints = 2 * K - 1;
2362   PetscCall(PetscMalloc1(npoints * dim, &x));
2363   PetscCall(PetscMalloc1(npoints, &w));
2364   /* Center term */
2365   x[0] = beta;
2366   w[0] = 0.5 * alpha * PETSC_PI;
2367   for (k = 1; k < K; ++k) {
2368     wk           = 0.5 * alpha * h * PETSC_PI * PetscCoshReal(k * h) / PetscSqr(PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(k * h)));
2369     xk           = PetscTanhReal(0.5 * PETSC_PI * PetscSinhReal(k * h));
2370     x[2 * k - 1] = -alpha * xk + beta;
2371     w[2 * k - 1] = wk;
2372     x[2 * k + 0] = alpha * xk + beta;
2373     w[2 * k + 0] = wk;
2374   }
2375   PetscCall(PetscQuadratureSetData(*q, dim, 1, npoints, x, w));
2376   PetscFunctionReturn(PETSC_SUCCESS);
2377 }
2378 
2379 PetscErrorCode PetscDTTanhSinhIntegrate(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, void *ctx, PetscReal *sol)
2380 {
2381   const PetscInt  p     = 16;           /* Digits of precision in the evaluation */
2382   const PetscReal alpha = (b - a) / 2.; /* Half-width of the integration interval */
2383   const PetscReal beta  = (b + a) / 2.; /* Center of the integration interval */
2384   PetscReal       h     = 1.0;          /* Step size, length between x_k */
2385   PetscInt        l     = 0;            /* Level of refinement, h = 2^{-l} */
2386   PetscReal       osum  = 0.0;          /* Integral on last level */
2387   PetscReal       psum  = 0.0;          /* Integral on the level before the last level */
2388   PetscReal       sum;                  /* Integral on current level */
2389   PetscReal       yk;                   /* Quadrature point 1 - x_k on reference domain [-1, 1] */
2390   PetscReal       lx, rx;               /* Quadrature points to the left and right of 0 on the real domain [a, b] */
2391   PetscReal       wk;                   /* Quadrature weight at x_k */
2392   PetscReal       lval, rval;           /* Terms in the quadature sum to the left and right of 0 */
2393   PetscInt        d;                    /* Digits of precision in the integral */
2394 
2395   PetscFunctionBegin;
2396   PetscCheck(digits > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
2397   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
2398   /* Center term */
2399   func(&beta, ctx, &lval);
2400   sum = 0.5 * alpha * PETSC_PI * lval;
2401   /* */
2402   do {
2403     PetscReal lterm, rterm, maxTerm = 0.0, d1, d2, d3, d4;
2404     PetscInt  k = 1;
2405 
2406     ++l;
2407     /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %" PetscInt_FMT " sum: %15.15f\n", l, sum); */
2408     /* At each level of refinement, h --> h/2 and sum --> sum/2 */
2409     psum = osum;
2410     osum = sum;
2411     h *= 0.5;
2412     sum *= 0.5;
2413     do {
2414       wk = 0.5 * h * PETSC_PI * PetscCoshReal(k * h) / PetscSqr(PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(k * h)));
2415       yk = 1.0 / (PetscExpReal(0.5 * PETSC_PI * PetscSinhReal(k * h)) * PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(k * h)));
2416       lx = -alpha * (1.0 - yk) + beta;
2417       rx = alpha * (1.0 - yk) + beta;
2418       func(&lx, ctx, &lval);
2419       func(&rx, ctx, &rval);
2420       lterm   = alpha * wk * lval;
2421       maxTerm = PetscMax(PetscAbsReal(lterm), maxTerm);
2422       sum += lterm;
2423       rterm   = alpha * wk * rval;
2424       maxTerm = PetscMax(PetscAbsReal(rterm), maxTerm);
2425       sum += rterm;
2426       ++k;
2427       /* Only need to evaluate every other point on refined levels */
2428       if (l != 1) ++k;
2429     } while (PetscAbsReal(PetscLog10Real(wk)) < p); /* Only need to evaluate sum until weights are < 32 digits of precision */
2430 
2431     d1 = PetscLog10Real(PetscAbsReal(sum - osum));
2432     d2 = PetscLog10Real(PetscAbsReal(sum - psum));
2433     d3 = PetscLog10Real(maxTerm) - p;
2434     if (PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)) == 0.0) d4 = 0.0;
2435     else d4 = PetscLog10Real(PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)));
2436     d = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1) / d2, 2 * d1), d3), d4)));
2437   } while (d < digits && l < 12);
2438   *sol = sum;
2439   PetscCall(PetscFPTrapPop());
2440   PetscFunctionReturn(PETSC_SUCCESS);
2441 }
2442 
2443 #if defined(PETSC_HAVE_MPFR)
2444 PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, void *ctx, PetscReal *sol)
2445 {
2446   const PetscInt safetyFactor = 2; /* Calculate abscissa until 2*p digits */
2447   PetscInt       l            = 0; /* Level of refinement, h = 2^{-l} */
2448   mpfr_t         alpha;            /* Half-width of the integration interval */
2449   mpfr_t         beta;             /* Center of the integration interval */
2450   mpfr_t         h;                /* Step size, length between x_k */
2451   mpfr_t         osum;             /* Integral on last level */
2452   mpfr_t         psum;             /* Integral on the level before the last level */
2453   mpfr_t         sum;              /* Integral on current level */
2454   mpfr_t         yk;               /* Quadrature point 1 - x_k on reference domain [-1, 1] */
2455   mpfr_t         lx, rx;           /* Quadrature points to the left and right of 0 on the real domain [a, b] */
2456   mpfr_t         wk;               /* Quadrature weight at x_k */
2457   PetscReal      lval, rval, rtmp; /* Terms in the quadature sum to the left and right of 0 */
2458   PetscInt       d;                /* Digits of precision in the integral */
2459   mpfr_t         pi2, kh, msinh, mcosh, maxTerm, curTerm, tmp;
2460 
2461   PetscFunctionBegin;
2462   PetscCheck(digits > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
2463   /* Create high precision storage */
2464   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);
2465   /* Initialization */
2466   mpfr_set_d(alpha, 0.5 * (b - a), MPFR_RNDN);
2467   mpfr_set_d(beta, 0.5 * (b + a), MPFR_RNDN);
2468   mpfr_set_d(osum, 0.0, MPFR_RNDN);
2469   mpfr_set_d(psum, 0.0, MPFR_RNDN);
2470   mpfr_set_d(h, 1.0, MPFR_RNDN);
2471   mpfr_const_pi(pi2, MPFR_RNDN);
2472   mpfr_mul_d(pi2, pi2, 0.5, MPFR_RNDN);
2473   /* Center term */
2474   rtmp = 0.5 * (b + a);
2475   func(&rtmp, ctx, &lval);
2476   mpfr_set(sum, pi2, MPFR_RNDN);
2477   mpfr_mul(sum, sum, alpha, MPFR_RNDN);
2478   mpfr_mul_d(sum, sum, lval, MPFR_RNDN);
2479   /* */
2480   do {
2481     PetscReal d1, d2, d3, d4;
2482     PetscInt  k = 1;
2483 
2484     ++l;
2485     mpfr_set_d(maxTerm, 0.0, MPFR_RNDN);
2486     /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %" PetscInt_FMT " sum: %15.15f\n", l, sum); */
2487     /* At each level of refinement, h --> h/2 and sum --> sum/2 */
2488     mpfr_set(psum, osum, MPFR_RNDN);
2489     mpfr_set(osum, sum, MPFR_RNDN);
2490     mpfr_mul_d(h, h, 0.5, MPFR_RNDN);
2491     mpfr_mul_d(sum, sum, 0.5, MPFR_RNDN);
2492     do {
2493       mpfr_set_si(kh, k, MPFR_RNDN);
2494       mpfr_mul(kh, kh, h, MPFR_RNDN);
2495       /* Weight */
2496       mpfr_set(wk, h, MPFR_RNDN);
2497       mpfr_sinh_cosh(msinh, mcosh, kh, MPFR_RNDN);
2498       mpfr_mul(msinh, msinh, pi2, MPFR_RNDN);
2499       mpfr_mul(mcosh, mcosh, pi2, MPFR_RNDN);
2500       mpfr_cosh(tmp, msinh, MPFR_RNDN);
2501       mpfr_sqr(tmp, tmp, MPFR_RNDN);
2502       mpfr_mul(wk, wk, mcosh, MPFR_RNDN);
2503       mpfr_div(wk, wk, tmp, MPFR_RNDN);
2504       /* Abscissa */
2505       mpfr_set_d(yk, 1.0, MPFR_RNDZ);
2506       mpfr_cosh(tmp, msinh, MPFR_RNDN);
2507       mpfr_div(yk, yk, tmp, MPFR_RNDZ);
2508       mpfr_exp(tmp, msinh, MPFR_RNDN);
2509       mpfr_div(yk, yk, tmp, MPFR_RNDZ);
2510       /* Quadrature points */
2511       mpfr_sub_d(lx, yk, 1.0, MPFR_RNDZ);
2512       mpfr_mul(lx, lx, alpha, MPFR_RNDU);
2513       mpfr_add(lx, lx, beta, MPFR_RNDU);
2514       mpfr_d_sub(rx, 1.0, yk, MPFR_RNDZ);
2515       mpfr_mul(rx, rx, alpha, MPFR_RNDD);
2516       mpfr_add(rx, rx, beta, MPFR_RNDD);
2517       /* Evaluation */
2518       rtmp = mpfr_get_d(lx, MPFR_RNDU);
2519       func(&rtmp, ctx, &lval);
2520       rtmp = mpfr_get_d(rx, MPFR_RNDD);
2521       func(&rtmp, ctx, &rval);
2522       /* Update */
2523       mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
2524       mpfr_mul_d(tmp, tmp, lval, MPFR_RNDN);
2525       mpfr_add(sum, sum, tmp, MPFR_RNDN);
2526       mpfr_abs(tmp, tmp, MPFR_RNDN);
2527       mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
2528       mpfr_set(curTerm, tmp, MPFR_RNDN);
2529       mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
2530       mpfr_mul_d(tmp, tmp, rval, MPFR_RNDN);
2531       mpfr_add(sum, sum, tmp, MPFR_RNDN);
2532       mpfr_abs(tmp, tmp, MPFR_RNDN);
2533       mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
2534       mpfr_max(curTerm, curTerm, tmp, MPFR_RNDN);
2535       ++k;
2536       /* Only need to evaluate every other point on refined levels */
2537       if (l != 1) ++k;
2538       mpfr_log10(tmp, wk, MPFR_RNDN);
2539       mpfr_abs(tmp, tmp, MPFR_RNDN);
2540     } while (mpfr_get_d(tmp, MPFR_RNDN) < safetyFactor * digits); /* Only need to evaluate sum until weights are < 32 digits of precision */
2541     mpfr_sub(tmp, sum, osum, MPFR_RNDN);
2542     mpfr_abs(tmp, tmp, MPFR_RNDN);
2543     mpfr_log10(tmp, tmp, MPFR_RNDN);
2544     d1 = mpfr_get_d(tmp, MPFR_RNDN);
2545     mpfr_sub(tmp, sum, psum, MPFR_RNDN);
2546     mpfr_abs(tmp, tmp, MPFR_RNDN);
2547     mpfr_log10(tmp, tmp, MPFR_RNDN);
2548     d2 = mpfr_get_d(tmp, MPFR_RNDN);
2549     mpfr_log10(tmp, maxTerm, MPFR_RNDN);
2550     d3 = mpfr_get_d(tmp, MPFR_RNDN) - digits;
2551     mpfr_log10(tmp, curTerm, MPFR_RNDN);
2552     d4 = mpfr_get_d(tmp, MPFR_RNDN);
2553     d  = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1) / d2, 2 * d1), d3), d4)));
2554   } while (d < digits && l < 8);
2555   *sol = mpfr_get_d(sum, MPFR_RNDN);
2556   /* Cleanup */
2557   mpfr_clears(alpha, beta, h, sum, osum, psum, yk, wk, lx, rx, tmp, maxTerm, curTerm, pi2, kh, msinh, mcosh, NULL);
2558   PetscFunctionReturn(PETSC_SUCCESS);
2559 }
2560 #else
2561 
2562 PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, void *ctx, PetscReal *sol)
2563 {
2564   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "This method will not work without MPFR. Reconfigure using --download-mpfr --download-gmp");
2565 }
2566 #endif
2567 
2568 /*@
2569   PetscDTTensorQuadratureCreate - create the tensor product quadrature from two lower-dimensional quadratures
2570 
2571   Not Collective
2572 
2573   Input Parameters:
2574 + q1 - The first quadrature
2575 - q2 - The second quadrature
2576 
2577   Output Parameter:
2578 . q - A `PetscQuadrature` object
2579 
2580   Level: intermediate
2581 
2582 .seealso: `PetscQuadrature`, `PetscDTGaussTensorQuadrature()`
2583 @*/
2584 PetscErrorCode PetscDTTensorQuadratureCreate(PetscQuadrature q1, PetscQuadrature q2, PetscQuadrature *q)
2585 {
2586   DMPolytopeType   ct1, ct2, ct;
2587   const PetscReal *x1, *w1, *x2, *w2;
2588   PetscReal       *x, *w;
2589   PetscInt         dim1, Nc1, Np1, order1, qa, d1;
2590   PetscInt         dim2, Nc2, Np2, order2, qb, d2;
2591   PetscInt         dim, Nc, Np, order, qc, d;
2592 
2593   PetscFunctionBegin;
2594   PetscValidHeaderSpecific(q1, PETSCQUADRATURE_CLASSID, 1);
2595   PetscValidHeaderSpecific(q2, PETSCQUADRATURE_CLASSID, 2);
2596   PetscAssertPointer(q, 3);
2597 
2598   PetscCall(PetscQuadratureGetOrder(q1, &order1));
2599   PetscCall(PetscQuadratureGetOrder(q2, &order2));
2600   PetscCheck(order1 == order2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Order1 %" PetscInt_FMT " != %" PetscInt_FMT " Order2", order1, order2);
2601   PetscCall(PetscQuadratureGetData(q1, &dim1, &Nc1, &Np1, &x1, &w1));
2602   PetscCall(PetscQuadratureGetCellType(q1, &ct1));
2603   PetscCall(PetscQuadratureGetData(q2, &dim2, &Nc2, &Np2, &x2, &w2));
2604   PetscCall(PetscQuadratureGetCellType(q2, &ct2));
2605   PetscCheck(Nc1 == Nc2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "NumComp1 %" PetscInt_FMT " != %" PetscInt_FMT " NumComp2", Nc1, Nc2);
2606 
2607   switch (ct1) {
2608   case DM_POLYTOPE_POINT:
2609     ct = ct2;
2610     break;
2611   case DM_POLYTOPE_SEGMENT:
2612     switch (ct2) {
2613     case DM_POLYTOPE_POINT:
2614       ct = ct1;
2615       break;
2616     case DM_POLYTOPE_SEGMENT:
2617       ct = DM_POLYTOPE_QUADRILATERAL;
2618       break;
2619     case DM_POLYTOPE_TRIANGLE:
2620       ct = DM_POLYTOPE_TRI_PRISM;
2621       break;
2622     case DM_POLYTOPE_QUADRILATERAL:
2623       ct = DM_POLYTOPE_HEXAHEDRON;
2624       break;
2625     case DM_POLYTOPE_TETRAHEDRON:
2626       ct = DM_POLYTOPE_UNKNOWN;
2627       break;
2628     case DM_POLYTOPE_HEXAHEDRON:
2629       ct = DM_POLYTOPE_UNKNOWN;
2630       break;
2631     default:
2632       ct = DM_POLYTOPE_UNKNOWN;
2633     }
2634     break;
2635   case DM_POLYTOPE_TRIANGLE:
2636     switch (ct2) {
2637     case DM_POLYTOPE_POINT:
2638       ct = ct1;
2639       break;
2640     case DM_POLYTOPE_SEGMENT:
2641       ct = DM_POLYTOPE_TRI_PRISM;
2642       break;
2643     case DM_POLYTOPE_TRIANGLE:
2644       ct = DM_POLYTOPE_UNKNOWN;
2645       break;
2646     case DM_POLYTOPE_QUADRILATERAL:
2647       ct = DM_POLYTOPE_UNKNOWN;
2648       break;
2649     case DM_POLYTOPE_TETRAHEDRON:
2650       ct = DM_POLYTOPE_UNKNOWN;
2651       break;
2652     case DM_POLYTOPE_HEXAHEDRON:
2653       ct = DM_POLYTOPE_UNKNOWN;
2654       break;
2655     default:
2656       ct = DM_POLYTOPE_UNKNOWN;
2657     }
2658     break;
2659   case DM_POLYTOPE_QUADRILATERAL:
2660     switch (ct2) {
2661     case DM_POLYTOPE_POINT:
2662       ct = ct1;
2663       break;
2664     case DM_POLYTOPE_SEGMENT:
2665       ct = DM_POLYTOPE_HEXAHEDRON;
2666       break;
2667     case DM_POLYTOPE_TRIANGLE:
2668       ct = DM_POLYTOPE_UNKNOWN;
2669       break;
2670     case DM_POLYTOPE_QUADRILATERAL:
2671       ct = DM_POLYTOPE_UNKNOWN;
2672       break;
2673     case DM_POLYTOPE_TETRAHEDRON:
2674       ct = DM_POLYTOPE_UNKNOWN;
2675       break;
2676     case DM_POLYTOPE_HEXAHEDRON:
2677       ct = DM_POLYTOPE_UNKNOWN;
2678       break;
2679     default:
2680       ct = DM_POLYTOPE_UNKNOWN;
2681     }
2682     break;
2683   case DM_POLYTOPE_TETRAHEDRON:
2684     switch (ct2) {
2685     case DM_POLYTOPE_POINT:
2686       ct = ct1;
2687       break;
2688     case DM_POLYTOPE_SEGMENT:
2689       ct = DM_POLYTOPE_UNKNOWN;
2690       break;
2691     case DM_POLYTOPE_TRIANGLE:
2692       ct = DM_POLYTOPE_UNKNOWN;
2693       break;
2694     case DM_POLYTOPE_QUADRILATERAL:
2695       ct = DM_POLYTOPE_UNKNOWN;
2696       break;
2697     case DM_POLYTOPE_TETRAHEDRON:
2698       ct = DM_POLYTOPE_UNKNOWN;
2699       break;
2700     case DM_POLYTOPE_HEXAHEDRON:
2701       ct = DM_POLYTOPE_UNKNOWN;
2702       break;
2703     default:
2704       ct = DM_POLYTOPE_UNKNOWN;
2705     }
2706     break;
2707   case DM_POLYTOPE_HEXAHEDRON:
2708     switch (ct2) {
2709     case DM_POLYTOPE_POINT:
2710       ct = ct1;
2711       break;
2712     case DM_POLYTOPE_SEGMENT:
2713       ct = DM_POLYTOPE_UNKNOWN;
2714       break;
2715     case DM_POLYTOPE_TRIANGLE:
2716       ct = DM_POLYTOPE_UNKNOWN;
2717       break;
2718     case DM_POLYTOPE_QUADRILATERAL:
2719       ct = DM_POLYTOPE_UNKNOWN;
2720       break;
2721     case DM_POLYTOPE_TETRAHEDRON:
2722       ct = DM_POLYTOPE_UNKNOWN;
2723       break;
2724     case DM_POLYTOPE_HEXAHEDRON:
2725       ct = DM_POLYTOPE_UNKNOWN;
2726       break;
2727     default:
2728       ct = DM_POLYTOPE_UNKNOWN;
2729     }
2730     break;
2731   default:
2732     ct = DM_POLYTOPE_UNKNOWN;
2733   }
2734   dim   = dim1 + dim2;
2735   Nc    = Nc1;
2736   Np    = Np1 * Np2;
2737   order = order1;
2738   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
2739   PetscCall(PetscQuadratureSetCellType(*q, ct));
2740   PetscCall(PetscQuadratureSetOrder(*q, order));
2741   PetscCall(PetscMalloc1(Np * dim, &x));
2742   PetscCall(PetscMalloc1(Np, &w));
2743   for (qa = 0, qc = 0; qa < Np1; ++qa) {
2744     for (qb = 0; qb < Np2; ++qb, ++qc) {
2745       for (d1 = 0, d = 0; d1 < dim1; ++d1, ++d) x[qc * dim + d] = x1[qa * dim1 + d1];
2746       for (d2 = 0; d2 < dim2; ++d2, ++d) x[qc * dim + d] = x2[qb * dim2 + d2];
2747       w[qc] = w1[qa] * w2[qb];
2748     }
2749   }
2750   PetscCall(PetscQuadratureSetData(*q, dim, Nc, Np, x, w));
2751   PetscFunctionReturn(PETSC_SUCCESS);
2752 }
2753 
2754 /* Overwrites A. Can only handle full-rank problems with m>=n
2755    A in column-major format
2756    Ainv in row-major format
2757    tau has length m
2758    worksize must be >= max(1,n)
2759  */
2760 static PetscErrorCode PetscDTPseudoInverseQR(PetscInt m, PetscInt mstride, PetscInt n, PetscReal *A_in, PetscReal *Ainv_out, PetscScalar *tau, PetscInt worksize, PetscScalar *work)
2761 {
2762   PetscBLASInt M, N, K, lda, ldb, ldwork, info;
2763   PetscScalar *A, *Ainv, *R, *Q, Alpha;
2764 
2765   PetscFunctionBegin;
2766 #if defined(PETSC_USE_COMPLEX)
2767   {
2768     PetscInt i, j;
2769     PetscCall(PetscMalloc2(m * n, &A, m * n, &Ainv));
2770     for (j = 0; j < n; j++) {
2771       for (i = 0; i < m; i++) A[i + m * j] = A_in[i + mstride * j];
2772     }
2773     mstride = m;
2774   }
2775 #else
2776   A    = A_in;
2777   Ainv = Ainv_out;
2778 #endif
2779 
2780   PetscCall(PetscBLASIntCast(m, &M));
2781   PetscCall(PetscBLASIntCast(n, &N));
2782   PetscCall(PetscBLASIntCast(mstride, &lda));
2783   PetscCall(PetscBLASIntCast(worksize, &ldwork));
2784   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
2785   PetscCallBLAS("LAPACKgeqrf", LAPACKgeqrf_(&M, &N, A, &lda, tau, work, &ldwork, &info));
2786   PetscCall(PetscFPTrapPop());
2787   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "xGEQRF error");
2788   R = A; /* Upper triangular part of A now contains R, the rest contains the elementary reflectors */
2789 
2790   /* Extract an explicit representation of Q */
2791   Q = Ainv;
2792   PetscCall(PetscArraycpy(Q, A, mstride * n));
2793   K = N; /* full rank */
2794   PetscCallBLAS("LAPACKorgqr", LAPACKorgqr_(&M, &N, &K, Q, &lda, tau, work, &ldwork, &info));
2795   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "xORGQR/xUNGQR error");
2796 
2797   /* Compute A^{-T} = (R^{-1} Q^T)^T = Q R^{-T} */
2798   Alpha = 1.0;
2799   ldb   = lda;
2800   PetscCallBLAS("BLAStrsm", BLAStrsm_("Right", "Upper", "ConjugateTranspose", "NotUnitTriangular", &M, &N, &Alpha, R, &lda, Q, &ldb));
2801   /* Ainv is Q, overwritten with inverse */
2802 
2803 #if defined(PETSC_USE_COMPLEX)
2804   {
2805     PetscInt i;
2806     for (i = 0; i < m * n; i++) Ainv_out[i] = PetscRealPart(Ainv[i]);
2807     PetscCall(PetscFree2(A, Ainv));
2808   }
2809 #endif
2810   PetscFunctionReturn(PETSC_SUCCESS);
2811 }
2812 
2813 /* Computes integral of L_p' over intervals {(x0,x1),(x1,x2),...} */
2814 static PetscErrorCode PetscDTLegendreIntegrate(PetscInt ninterval, const PetscReal *x, PetscInt ndegree, const PetscInt *degrees, PetscBool Transpose, PetscReal *B)
2815 {
2816   PetscReal *Bv;
2817   PetscInt   i, j;
2818 
2819   PetscFunctionBegin;
2820   PetscCall(PetscMalloc1((ninterval + 1) * ndegree, &Bv));
2821   /* Point evaluation of L_p on all the source vertices */
2822   PetscCall(PetscDTLegendreEval(ninterval + 1, x, ndegree, degrees, Bv, NULL, NULL));
2823   /* Integral over each interval: \int_a^b L_p' = L_p(b)-L_p(a) */
2824   for (i = 0; i < ninterval; i++) {
2825     for (j = 0; j < ndegree; j++) {
2826       if (Transpose) B[i + ninterval * j] = Bv[(i + 1) * ndegree + j] - Bv[i * ndegree + j];
2827       else B[i * ndegree + j] = Bv[(i + 1) * ndegree + j] - Bv[i * ndegree + j];
2828     }
2829   }
2830   PetscCall(PetscFree(Bv));
2831   PetscFunctionReturn(PETSC_SUCCESS);
2832 }
2833 
2834 /*@
2835   PetscDTReconstructPoly - create matrix representing polynomial reconstruction using cell intervals and evaluation at target intervals
2836 
2837   Not Collective
2838 
2839   Input Parameters:
2840 + degree  - degree of reconstruction polynomial
2841 . nsource - number of source intervals
2842 . sourcex - sorted coordinates of source cell boundaries (length `nsource`+1)
2843 . ntarget - number of target intervals
2844 - targetx - sorted coordinates of target cell boundaries (length `ntarget`+1)
2845 
2846   Output Parameter:
2847 . R - reconstruction matrix, utarget = sum_s R[t*nsource+s] * usource[s]
2848 
2849   Level: advanced
2850 
2851 .seealso: `PetscDTLegendreEval()`
2852 @*/
2853 PetscErrorCode PetscDTReconstructPoly(PetscInt degree, PetscInt nsource, const PetscReal sourcex[], PetscInt ntarget, const PetscReal targetx[], PetscReal R[])
2854 {
2855   PetscInt     i, j, k, *bdegrees, worksize;
2856   PetscReal    xmin, xmax, center, hscale, *sourcey, *targety, *Bsource, *Bsinv, *Btarget;
2857   PetscScalar *tau, *work;
2858 
2859   PetscFunctionBegin;
2860   PetscAssertPointer(sourcex, 3);
2861   PetscAssertPointer(targetx, 5);
2862   PetscAssertPointer(R, 6);
2863   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);
2864   if (PetscDefined(USE_DEBUG)) {
2865     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]);
2866     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]);
2867   }
2868   xmin     = PetscMin(sourcex[0], targetx[0]);
2869   xmax     = PetscMax(sourcex[nsource], targetx[ntarget]);
2870   center   = (xmin + xmax) / 2;
2871   hscale   = (xmax - xmin) / 2;
2872   worksize = nsource;
2873   PetscCall(PetscMalloc4(degree + 1, &bdegrees, nsource + 1, &sourcey, nsource * (degree + 1), &Bsource, worksize, &work));
2874   PetscCall(PetscMalloc4(nsource, &tau, nsource * (degree + 1), &Bsinv, ntarget + 1, &targety, ntarget * (degree + 1), &Btarget));
2875   for (i = 0; i <= nsource; i++) sourcey[i] = (sourcex[i] - center) / hscale;
2876   for (i = 0; i <= degree; i++) bdegrees[i] = i + 1;
2877   PetscCall(PetscDTLegendreIntegrate(nsource, sourcey, degree + 1, bdegrees, PETSC_TRUE, Bsource));
2878   PetscCall(PetscDTPseudoInverseQR(nsource, nsource, degree + 1, Bsource, Bsinv, tau, nsource, work));
2879   for (i = 0; i <= ntarget; i++) targety[i] = (targetx[i] - center) / hscale;
2880   PetscCall(PetscDTLegendreIntegrate(ntarget, targety, degree + 1, bdegrees, PETSC_FALSE, Btarget));
2881   for (i = 0; i < ntarget; i++) {
2882     PetscReal rowsum = 0;
2883     for (j = 0; j < nsource; j++) {
2884       PetscReal sum = 0;
2885       for (k = 0; k < degree + 1; k++) sum += Btarget[i * (degree + 1) + k] * Bsinv[k * nsource + j];
2886       R[i * nsource + j] = sum;
2887       rowsum += sum;
2888     }
2889     for (j = 0; j < nsource; j++) R[i * nsource + j] /= rowsum; /* normalize each row */
2890   }
2891   PetscCall(PetscFree4(bdegrees, sourcey, Bsource, work));
2892   PetscCall(PetscFree4(tau, Bsinv, targety, Btarget));
2893   PetscFunctionReturn(PETSC_SUCCESS);
2894 }
2895 
2896 /*@
2897   PetscGaussLobattoLegendreIntegrate - Compute the L2 integral of a function on the GLL points
2898 
2899   Not Collective
2900 
2901   Input Parameters:
2902 + n       - the number of GLL nodes
2903 . nodes   - the GLL nodes
2904 . weights - the GLL weights
2905 - f       - the function values at the nodes
2906 
2907   Output Parameter:
2908 . in - the value of the integral
2909 
2910   Level: beginner
2911 
2912 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`
2913 @*/
2914 PetscErrorCode PetscGaussLobattoLegendreIntegrate(PetscInt n, PetscReal nodes[], PetscReal weights[], const PetscReal f[], PetscReal *in)
2915 {
2916   PetscInt i;
2917 
2918   PetscFunctionBegin;
2919   *in = 0.;
2920   for (i = 0; i < n; i++) *in += f[i] * f[i] * weights[i];
2921   PetscFunctionReturn(PETSC_SUCCESS);
2922 }
2923 
2924 /*@C
2925   PetscGaussLobattoLegendreElementLaplacianCreate - computes the Laplacian for a single 1d GLL element
2926 
2927   Not Collective
2928 
2929   Input Parameters:
2930 + n       - the number of GLL nodes
2931 . nodes   - the GLL nodes, of length `n`
2932 - weights - the GLL weights, of length `n`
2933 
2934   Output Parameter:
2935 . AA - the stiffness element, of size `n` by `n`
2936 
2937   Level: beginner
2938 
2939   Notes:
2940   Destroy this with `PetscGaussLobattoLegendreElementLaplacianDestroy()`
2941 
2942   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)
2943 
2944 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianDestroy()`
2945 @*/
2946 PetscErrorCode PetscGaussLobattoLegendreElementLaplacianCreate(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA)
2947 {
2948   PetscReal      **A;
2949   const PetscReal *gllnodes = nodes;
2950   const PetscInt   p        = n - 1;
2951   PetscReal        z0, z1, z2 = -1, x, Lpj, Lpr;
2952   PetscInt         i, j, nn, r;
2953 
2954   PetscFunctionBegin;
2955   PetscCall(PetscMalloc1(n, &A));
2956   PetscCall(PetscMalloc1(n * n, &A[0]));
2957   for (i = 1; i < n; i++) A[i] = A[i - 1] + n;
2958 
2959   for (j = 1; j < p; j++) {
2960     x  = gllnodes[j];
2961     z0 = 1.;
2962     z1 = x;
2963     for (nn = 1; nn < p; nn++) {
2964       z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
2965       z0 = z1;
2966       z1 = z2;
2967     }
2968     Lpj = z2;
2969     for (r = 1; r < p; r++) {
2970       if (r == j) {
2971         A[j][j] = 2. / (3. * (1. - gllnodes[j] * gllnodes[j]) * Lpj * Lpj);
2972       } else {
2973         x  = gllnodes[r];
2974         z0 = 1.;
2975         z1 = x;
2976         for (nn = 1; nn < p; nn++) {
2977           z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
2978           z0 = z1;
2979           z1 = z2;
2980         }
2981         Lpr     = z2;
2982         A[r][j] = 4. / (((PetscReal)p) * (((PetscReal)p) + 1.) * Lpj * Lpr * (gllnodes[j] - gllnodes[r]) * (gllnodes[j] - gllnodes[r]));
2983       }
2984     }
2985   }
2986   for (j = 1; j < p + 1; j++) {
2987     x  = gllnodes[j];
2988     z0 = 1.;
2989     z1 = x;
2990     for (nn = 1; nn < p; nn++) {
2991       z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
2992       z0 = z1;
2993       z1 = z2;
2994     }
2995     Lpj     = z2;
2996     A[j][0] = 4. * PetscPowRealInt(-1., p) / (((PetscReal)p) * (((PetscReal)p) + 1.) * Lpj * (1. + gllnodes[j]) * (1. + gllnodes[j]));
2997     A[0][j] = A[j][0];
2998   }
2999   for (j = 0; j < p; j++) {
3000     x  = gllnodes[j];
3001     z0 = 1.;
3002     z1 = x;
3003     for (nn = 1; nn < p; nn++) {
3004       z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
3005       z0 = z1;
3006       z1 = z2;
3007     }
3008     Lpj = z2;
3009 
3010     A[p][j] = 4. / (((PetscReal)p) * (((PetscReal)p) + 1.) * Lpj * (1. - gllnodes[j]) * (1. - gllnodes[j]));
3011     A[j][p] = A[p][j];
3012   }
3013   A[0][0] = 0.5 + (((PetscReal)p) * (((PetscReal)p) + 1.) - 2.) / 6.;
3014   A[p][p] = A[0][0];
3015   *AA     = A;
3016   PetscFunctionReturn(PETSC_SUCCESS);
3017 }
3018 
3019 /*@C
3020   PetscGaussLobattoLegendreElementLaplacianDestroy - frees the Laplacian for a single 1d GLL element created with `PetscGaussLobattoLegendreElementLaplacianCreate()`
3021 
3022   Not Collective
3023 
3024   Input Parameters:
3025 + n       - the number of GLL nodes
3026 . nodes   - the GLL nodes, ignored
3027 . weights - the GLL weightss, ignored
3028 - AA      - the stiffness element from `PetscGaussLobattoLegendreElementLaplacianCreate()`
3029 
3030   Level: beginner
3031 
3032 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`
3033 @*/
3034 PetscErrorCode PetscGaussLobattoLegendreElementLaplacianDestroy(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA)
3035 {
3036   PetscFunctionBegin;
3037   PetscCall(PetscFree((*AA)[0]));
3038   PetscCall(PetscFree(*AA));
3039   *AA = NULL;
3040   PetscFunctionReturn(PETSC_SUCCESS);
3041 }
3042 
3043 /*@C
3044   PetscGaussLobattoLegendreElementGradientCreate - computes the gradient for a single 1d GLL element
3045 
3046   Not Collective
3047 
3048   Input Parameters:
3049 + n       - the number of GLL nodes
3050 . nodes   - the GLL nodes, of length `n`
3051 - weights - the GLL weights, of length `n`
3052 
3053   Output Parameters:
3054 + AA  - the stiffness element, of dimension `n` by `n`
3055 - AAT - the transpose of AA (pass in `NULL` if you do not need this array), of dimension `n` by `n`
3056 
3057   Level: beginner
3058 
3059   Notes:
3060   Destroy this with `PetscGaussLobattoLegendreElementGradientDestroy()`
3061 
3062   You can access entries in these arrays with AA[i][j] but in memory it is stored in contiguous memory, row-oriented
3063 
3064 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianDestroy()`, `PetscGaussLobattoLegendreElementGradientDestroy()`
3065 @*/
3066 PetscErrorCode PetscGaussLobattoLegendreElementGradientCreate(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA, PetscReal ***AAT)
3067 {
3068   PetscReal      **A, **AT = NULL;
3069   const PetscReal *gllnodes = nodes;
3070   const PetscInt   p        = n - 1;
3071   PetscReal        Li, Lj, d0;
3072   PetscInt         i, j;
3073 
3074   PetscFunctionBegin;
3075   PetscCall(PetscMalloc1(n, &A));
3076   PetscCall(PetscMalloc1(n * n, &A[0]));
3077   for (i = 1; i < n; i++) A[i] = A[i - 1] + n;
3078 
3079   if (AAT) {
3080     PetscCall(PetscMalloc1(n, &AT));
3081     PetscCall(PetscMalloc1(n * n, &AT[0]));
3082     for (i = 1; i < n; i++) AT[i] = AT[i - 1] + n;
3083   }
3084 
3085   if (n == 1) A[0][0] = 0.;
3086   d0 = (PetscReal)p * ((PetscReal)p + 1.) / 4.;
3087   for (i = 0; i < n; i++) {
3088     for (j = 0; j < n; j++) {
3089       A[i][j] = 0.;
3090       PetscCall(PetscDTComputeJacobi(0., 0., p, gllnodes[i], &Li));
3091       PetscCall(PetscDTComputeJacobi(0., 0., p, gllnodes[j], &Lj));
3092       if (i != j) A[i][j] = Li / (Lj * (gllnodes[i] - gllnodes[j]));
3093       if ((j == i) && (i == 0)) A[i][j] = -d0;
3094       if (j == i && i == p) A[i][j] = d0;
3095       if (AT) AT[j][i] = A[i][j];
3096     }
3097   }
3098   if (AAT) *AAT = AT;
3099   *AA = A;
3100   PetscFunctionReturn(PETSC_SUCCESS);
3101 }
3102 
3103 /*@C
3104   PetscGaussLobattoLegendreElementGradientDestroy - frees the gradient for a single 1d GLL element obtained with `PetscGaussLobattoLegendreElementGradientCreate()`
3105 
3106   Not Collective
3107 
3108   Input Parameters:
3109 + n       - the number of GLL nodes
3110 . nodes   - the GLL nodes, ignored
3111 . weights - the GLL weights, ignored
3112 . AA      - the stiffness element obtained with `PetscGaussLobattoLegendreElementGradientCreate()`
3113 - AAT     - the transpose of the element obtained with `PetscGaussLobattoLegendreElementGradientCreate()`
3114 
3115   Level: beginner
3116 
3117 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`, `PetscGaussLobattoLegendreElementAdvectionCreate()`
3118 @*/
3119 PetscErrorCode PetscGaussLobattoLegendreElementGradientDestroy(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA, PetscReal ***AAT)
3120 {
3121   PetscFunctionBegin;
3122   PetscCall(PetscFree((*AA)[0]));
3123   PetscCall(PetscFree(*AA));
3124   *AA = NULL;
3125   if (AAT) {
3126     PetscCall(PetscFree((*AAT)[0]));
3127     PetscCall(PetscFree(*AAT));
3128     *AAT = NULL;
3129   }
3130   PetscFunctionReturn(PETSC_SUCCESS);
3131 }
3132 
3133 /*@C
3134   PetscGaussLobattoLegendreElementAdvectionCreate - computes the advection operator for a single 1d GLL element
3135 
3136   Not Collective
3137 
3138   Input Parameters:
3139 + n       - the number of GLL nodes
3140 . nodes   - the GLL nodes, of length `n`
3141 - weights - the GLL weights, of length `n`
3142 
3143   Output Parameter:
3144 . AA - the stiffness element, of dimension `n` by `n`
3145 
3146   Level: beginner
3147 
3148   Notes:
3149   Destroy this with `PetscGaussLobattoLegendreElementAdvectionDestroy()`
3150 
3151   This is the same as the Gradient operator multiplied by the diagonal mass matrix
3152 
3153   You can access entries in this array with AA[i][j] but in memory it is stored in contiguous memory, row-oriented
3154 
3155 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`, `PetscGaussLobattoLegendreElementAdvectionDestroy()`
3156 @*/
3157 PetscErrorCode PetscGaussLobattoLegendreElementAdvectionCreate(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA)
3158 {
3159   PetscReal      **D;
3160   const PetscReal *gllweights = weights;
3161   const PetscInt   glln       = n;
3162   PetscInt         i, j;
3163 
3164   PetscFunctionBegin;
3165   PetscCall(PetscGaussLobattoLegendreElementGradientCreate(n, nodes, weights, &D, NULL));
3166   for (i = 0; i < glln; i++) {
3167     for (j = 0; j < glln; j++) D[i][j] = gllweights[i] * D[i][j];
3168   }
3169   *AA = D;
3170   PetscFunctionReturn(PETSC_SUCCESS);
3171 }
3172 
3173 /*@C
3174   PetscGaussLobattoLegendreElementAdvectionDestroy - frees the advection stiffness for a single 1d GLL element created with `PetscGaussLobattoLegendreElementAdvectionCreate()`
3175 
3176   Not Collective
3177 
3178   Input Parameters:
3179 + n       - the number of GLL nodes
3180 . nodes   - the GLL nodes, ignored
3181 . weights - the GLL weights, ignored
3182 - AA      - advection obtained with `PetscGaussLobattoLegendreElementAdvectionCreate()`
3183 
3184   Level: beginner
3185 
3186 .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementAdvectionCreate()`
3187 @*/
3188 PetscErrorCode PetscGaussLobattoLegendreElementAdvectionDestroy(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA)
3189 {
3190   PetscFunctionBegin;
3191   PetscCall(PetscFree((*AA)[0]));
3192   PetscCall(PetscFree(*AA));
3193   *AA = NULL;
3194   PetscFunctionReturn(PETSC_SUCCESS);
3195 }
3196 
3197 PetscErrorCode PetscGaussLobattoLegendreElementMassCreate(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA)
3198 {
3199   PetscReal      **A;
3200   const PetscReal *gllweights = weights;
3201   const PetscInt   glln       = n;
3202   PetscInt         i, j;
3203 
3204   PetscFunctionBegin;
3205   PetscCall(PetscMalloc1(glln, &A));
3206   PetscCall(PetscMalloc1(glln * glln, &A[0]));
3207   for (i = 1; i < glln; i++) A[i] = A[i - 1] + glln;
3208   if (glln == 1) A[0][0] = 0.;
3209   for (i = 0; i < glln; i++) {
3210     for (j = 0; j < glln; j++) {
3211       A[i][j] = 0.;
3212       if (j == i) A[i][j] = gllweights[i];
3213     }
3214   }
3215   *AA = A;
3216   PetscFunctionReturn(PETSC_SUCCESS);
3217 }
3218 
3219 PetscErrorCode PetscGaussLobattoLegendreElementMassDestroy(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA)
3220 {
3221   PetscFunctionBegin;
3222   PetscCall(PetscFree((*AA)[0]));
3223   PetscCall(PetscFree(*AA));
3224   *AA = NULL;
3225   PetscFunctionReturn(PETSC_SUCCESS);
3226 }
3227 
3228 /*@
3229   PetscDTIndexToBary - convert an index into a barycentric coordinate.
3230 
3231   Input Parameters:
3232 + 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)
3233 . sum   - the value that the sum of the barycentric coordinates (which will be non-negative integers) should sum to
3234 - index - the index to convert: should be >= 0 and < Binomial(len - 1 + sum, sum)
3235 
3236   Output Parameter:
3237 . coord - will be filled with the barycentric coordinate, of length `n`
3238 
3239   Level: beginner
3240 
3241   Note:
3242   The indices map to barycentric coordinates in lexicographic order, where the first index is the
3243   least significant and the last index is the most significant.
3244 
3245 .seealso: `PetscDTBaryToIndex()`
3246 @*/
3247 PetscErrorCode PetscDTIndexToBary(PetscInt len, PetscInt sum, PetscInt index, PetscInt coord[])
3248 {
3249   PetscInt c, d, s, total, subtotal, nexttotal;
3250 
3251   PetscFunctionBeginHot;
3252   PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
3253   PetscCheck(index >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index must be non-negative");
3254   if (!len) {
3255     if (!sum && !index) PetscFunctionReturn(PETSC_SUCCESS);
3256     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid index or sum for length 0 barycentric coordinate");
3257   }
3258   for (c = 1, total = 1; c <= len; c++) {
3259     /* total is the number of ways to have a tuple of length c with sum */
3260     if (index < total) break;
3261     total = (total * (sum + c)) / c;
3262   }
3263   PetscCheck(c <= len, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index out of range");
3264   for (d = c; d < len; d++) coord[d] = 0;
3265   for (s = 0, subtotal = 1, nexttotal = 1; c > 0;) {
3266     /* subtotal is the number of ways to have a tuple of length c with sum s */
3267     /* nexttotal is the number of ways to have a tuple of length c-1 with sum s */
3268     if ((index + subtotal) >= total) {
3269       coord[--c] = sum - s;
3270       index -= (total - subtotal);
3271       sum       = s;
3272       total     = nexttotal;
3273       subtotal  = 1;
3274       nexttotal = 1;
3275       s         = 0;
3276     } else {
3277       subtotal  = (subtotal * (c + s)) / (s + 1);
3278       nexttotal = (nexttotal * (c - 1 + s)) / (s + 1);
3279       s++;
3280     }
3281   }
3282   PetscFunctionReturn(PETSC_SUCCESS);
3283 }
3284 
3285 /*@
3286   PetscDTBaryToIndex - convert a barycentric coordinate to an index
3287 
3288   Input Parameters:
3289 + 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)
3290 . sum   - the value that the sum of the barycentric coordinates (which will be non-negative integers) should sum to
3291 - coord - a barycentric coordinate with the given length `len` and `sum`
3292 
3293   Output Parameter:
3294 . index - the unique index for the coordinate, >= 0 and < Binomial(len - 1 + sum, sum)
3295 
3296   Level: beginner
3297 
3298   Note:
3299   The indices map to barycentric coordinates in lexicographic order, where the first index is the
3300   least significant and the last index is the most significant.
3301 
3302 .seealso: `PetscDTIndexToBary`
3303 @*/
3304 PetscErrorCode PetscDTBaryToIndex(PetscInt len, PetscInt sum, const PetscInt coord[], PetscInt *index)
3305 {
3306   PetscInt c;
3307   PetscInt i;
3308   PetscInt total;
3309 
3310   PetscFunctionBeginHot;
3311   PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
3312   if (!len) {
3313     if (!sum) {
3314       *index = 0;
3315       PetscFunctionReturn(PETSC_SUCCESS);
3316     }
3317     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid index or sum for length 0 barycentric coordinate");
3318   }
3319   for (c = 1, total = 1; c < len; c++) total = (total * (sum + c)) / c;
3320   i = total - 1;
3321   c = len - 1;
3322   sum -= coord[c];
3323   while (sum > 0) {
3324     PetscInt subtotal;
3325     PetscInt s;
3326 
3327     for (s = 1, subtotal = 1; s < sum; s++) subtotal = (subtotal * (c + s)) / s;
3328     i -= subtotal;
3329     sum -= coord[--c];
3330   }
3331   *index = i;
3332   PetscFunctionReturn(PETSC_SUCCESS);
3333 }
3334 
3335 /*@
3336   PetscQuadratureComputePermutations - Compute permutations of quadrature points corresponding to domain orientations
3337 
3338   Input Parameter:
3339 . quad - The `PetscQuadrature`
3340 
3341   Output Parameters:
3342 + Np   - The number of domain orientations
3343 - perm - An array of `IS` permutations, one for ech orientation,
3344 
3345   Level: developer
3346 
3347 .seealso: `PetscQuadratureSetCellType()`, `PetscQuadrature`
3348 @*/
3349 PetscErrorCode PetscQuadratureComputePermutations(PetscQuadrature quad, PeOp PetscInt *Np, IS *perm[])
3350 {
3351   DMPolytopeType   ct;
3352   const PetscReal *xq, *wq;
3353   PetscInt         dim, qdim, d, Na, o, Nq, q, qp;
3354 
3355   PetscFunctionBegin;
3356   PetscCall(PetscQuadratureGetData(quad, &qdim, NULL, &Nq, &xq, &wq));
3357   PetscCall(PetscQuadratureGetCellType(quad, &ct));
3358   dim = DMPolytopeTypeGetDim(ct);
3359   Na  = DMPolytopeTypeGetNumArrangements(ct);
3360   PetscCall(PetscMalloc1(Na, perm));
3361   if (Np) *Np = Na;
3362   Na /= 2;
3363   for (o = -Na; o < Na; ++o) {
3364     DM        refdm;
3365     PetscInt *idx;
3366     PetscReal xi0[3] = {-1., -1., -1.}, v0[3], J[9], detJ, txq[3];
3367     PetscBool flg;
3368 
3369     PetscCall(DMPlexCreateReferenceCell(PETSC_COMM_SELF, ct, &refdm));
3370     PetscCall(DMPlexOrientPoint(refdm, 0, o));
3371     PetscCall(DMPlexComputeCellGeometryFEM(refdm, 0, NULL, v0, J, NULL, &detJ));
3372     PetscCall(PetscMalloc1(Nq, &idx));
3373     for (q = 0; q < Nq; ++q) {
3374       CoordinatesRefToReal(dim, dim, xi0, v0, J, &xq[q * dim], txq);
3375       for (qp = 0; qp < Nq; ++qp) {
3376         PetscReal diff = 0.;
3377 
3378         for (d = 0; d < dim; ++d) diff += PetscAbsReal(txq[d] - xq[qp * dim + d]);
3379         if (diff < PETSC_SMALL) break;
3380       }
3381       PetscCheck(qp < Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Transformed quad point %" PetscInt_FMT " does not match another quad point", q);
3382       idx[q] = qp;
3383     }
3384     PetscCall(DMDestroy(&refdm));
3385     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, Nq, idx, PETSC_OWN_POINTER, &(*perm)[o + Na]));
3386     PetscCall(ISGetInfo((*perm)[o + Na], IS_PERMUTATION, IS_LOCAL, PETSC_TRUE, &flg));
3387     PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Ordering for orientation %" PetscInt_FMT " was not a permutation", o);
3388     PetscCall(ISSetPermutation((*perm)[o + Na]));
3389   }
3390   if (!Na) (*perm)[0] = NULL;
3391   PetscFunctionReturn(PETSC_SUCCESS);
3392 }
3393 
3394 /*@
3395   PetscDTCreateDefaultQuadrature - Create default quadrature for a given cell
3396 
3397   Not collective
3398 
3399   Input Parameters:
3400 + ct     - The integration domain
3401 - qorder - The desired quadrature order
3402 
3403   Output Parameters:
3404 + q  - The cell quadrature
3405 - fq - The face quadrature
3406 
3407   Level: developer
3408 
3409 .seealso: `PetscFECreateDefault()`, `PetscDTGaussTensorQuadrature()`, `PetscDTSimplexQuadrature()`, `PetscDTTensorQuadratureCreate()`
3410 @*/
3411 PetscErrorCode PetscDTCreateDefaultQuadrature(DMPolytopeType ct, PetscInt qorder, PetscQuadrature *q, PetscQuadrature *fq)
3412 {
3413   const PetscInt quadPointsPerEdge = PetscMax(qorder + 1, 1);
3414   const PetscInt dim               = DMPolytopeTypeGetDim(ct);
3415 
3416   PetscFunctionBegin;
3417   switch (ct) {
3418   case DM_POLYTOPE_SEGMENT:
3419   case DM_POLYTOPE_POINT_PRISM_TENSOR:
3420   case DM_POLYTOPE_QUADRILATERAL:
3421   case DM_POLYTOPE_SEG_PRISM_TENSOR:
3422   case DM_POLYTOPE_HEXAHEDRON:
3423   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
3424     PetscCall(PetscDTGaussTensorQuadrature(dim, 1, quadPointsPerEdge, -1.0, 1.0, q));
3425     PetscCall(PetscDTGaussTensorQuadrature(dim - 1, 1, quadPointsPerEdge, -1.0, 1.0, fq));
3426     break;
3427   case DM_POLYTOPE_TRIANGLE:
3428   case DM_POLYTOPE_TETRAHEDRON:
3429     PetscCall(PetscDTSimplexQuadrature(dim, 2 * qorder, PETSCDTSIMPLEXQUAD_DEFAULT, q));
3430     PetscCall(PetscDTSimplexQuadrature(dim - 1, 2 * qorder, PETSCDTSIMPLEXQUAD_DEFAULT, fq));
3431     break;
3432   case DM_POLYTOPE_TRI_PRISM:
3433   case DM_POLYTOPE_TRI_PRISM_TENSOR: {
3434     PetscQuadrature q1, q2;
3435 
3436     // TODO: this should be able to use symmetric rules, but doing so causes tests to fail
3437     PetscCall(PetscDTSimplexQuadrature(2, 2 * qorder, PETSCDTSIMPLEXQUAD_CONIC, &q1));
3438     PetscCall(PetscDTGaussTensorQuadrature(1, 1, quadPointsPerEdge, -1.0, 1.0, &q2));
3439     PetscCall(PetscDTTensorQuadratureCreate(q1, q2, q));
3440     PetscCall(PetscQuadratureDestroy(&q2));
3441     *fq = q1;
3442     /* TODO Need separate quadratures for each face */
3443   } break;
3444   default:
3445     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No quadrature for celltype %s", DMPolytopeTypes[PetscMin(ct, DM_POLYTOPE_UNKNOWN)]);
3446   }
3447   PetscFunctionReturn(PETSC_SUCCESS);
3448 }
3449