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