xref: /petsc/src/dm/dt/interface/dt.c (revision d71ae5a4db6382e7f06317b8d368875286fe9008)
137045ce4SJed Brown /* Discretization tools */
237045ce4SJed Brown 
30c35b76eSJed Brown #include <petscdt.h> /*I "petscdt.h" I*/
437045ce4SJed Brown #include <petscblaslapack.h>
5af0996ceSBarry Smith #include <petsc/private/petscimpl.h>
6af0996ceSBarry Smith #include <petsc/private/dtimpl.h>
7665c2dedSJed Brown #include <petscviewer.h>
859804f93SMatthew G. Knepley #include <petscdmplex.h>
959804f93SMatthew G. Knepley #include <petscdmshell.h>
1037045ce4SJed Brown 
1198c04793SMatthew G. Knepley #if defined(PETSC_HAVE_MPFR)
1298c04793SMatthew G. Knepley   #include <mpfr.h>
1398c04793SMatthew G. Knepley #endif
1498c04793SMatthew G. Knepley 
15d3c69ad0SToby Isaac const char *const        PetscDTNodeTypes_shifted[] = {"default", "gaussjacobi", "equispaced", "tanhsinh", "PETSCDTNODES_", NULL};
16d3c69ad0SToby Isaac const char *const *const PetscDTNodeTypes           = PetscDTNodeTypes_shifted + 1;
17d3c69ad0SToby Isaac 
18d3c69ad0SToby Isaac const char *const        PetscDTSimplexQuadratureTypes_shifted[] = {"default", "conic", "minsym", "PETSCDTSIMPLEXQUAD_", NULL};
19d3c69ad0SToby Isaac const char *const *const PetscDTSimplexQuadratureTypes           = PetscDTSimplexQuadratureTypes_shifted + 1;
20d4afb720SToby Isaac 
21e6a796c3SToby Isaac static PetscBool GolubWelschCite       = PETSC_FALSE;
22e6a796c3SToby Isaac const char       GolubWelschCitation[] = "@article{GolubWelsch1969,\n"
230bfcf5a5SMatthew G. Knepley                                          "  author  = {Golub and Welsch},\n"
240bfcf5a5SMatthew G. Knepley                                          "  title   = {Calculation of Quadrature Rules},\n"
250bfcf5a5SMatthew G. Knepley                                          "  journal = {Math. Comp.},\n"
260bfcf5a5SMatthew G. Knepley                                          "  volume  = {23},\n"
270bfcf5a5SMatthew G. Knepley                                          "  number  = {106},\n"
280bfcf5a5SMatthew G. Knepley                                          "  pages   = {221--230},\n"
290bfcf5a5SMatthew G. Knepley                                          "  year    = {1969}\n}\n";
300bfcf5a5SMatthew G. Knepley 
31c4762a1bSJed Brown /* Numerical tests in src/dm/dt/tests/ex1.c show that when computing the nodes and weights of Gauss-Jacobi
3294e21283SToby Isaac    quadrature rules:
33e6a796c3SToby Isaac 
3494e21283SToby Isaac    - in double precision, Newton's method and Golub & Welsch both work for moderate degrees (< 100),
3594e21283SToby Isaac    - in single precision, Newton's method starts producing incorrect roots around n = 15, but
3694e21283SToby Isaac      the weights from Golub & Welsch become a problem before then: they produces errors
3794e21283SToby Isaac      in computing the Jacobi-polynomial Gram matrix around n = 6.
3894e21283SToby Isaac 
3994e21283SToby Isaac    So we default to Newton's method (required fewer dependencies) */
4094e21283SToby Isaac PetscBool PetscDTGaussQuadratureNewton_Internal = PETSC_TRUE;
412cd22861SMatthew G. Knepley 
422cd22861SMatthew G. Knepley PetscClassId PETSCQUADRATURE_CLASSID = 0;
432cd22861SMatthew G. Knepley 
4440d8ff71SMatthew G. Knepley /*@
4540d8ff71SMatthew G. Knepley   PetscQuadratureCreate - Create a PetscQuadrature object
4640d8ff71SMatthew G. Knepley 
47d083f849SBarry Smith   Collective
4840d8ff71SMatthew G. Knepley 
4940d8ff71SMatthew G. Knepley   Input Parameter:
5040d8ff71SMatthew G. Knepley . comm - The communicator for the PetscQuadrature object
5140d8ff71SMatthew G. Knepley 
5240d8ff71SMatthew G. Knepley   Output Parameter:
5340d8ff71SMatthew G. Knepley . q  - The PetscQuadrature object
5440d8ff71SMatthew G. Knepley 
5540d8ff71SMatthew G. Knepley   Level: beginner
5640d8ff71SMatthew G. Knepley 
57db781477SPatrick Sanan .seealso: `PetscQuadratureDestroy()`, `PetscQuadratureGetData()`
5840d8ff71SMatthew G. Knepley @*/
59*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadratureCreate(MPI_Comm comm, PetscQuadrature *q)
60*d71ae5a4SJacob Faibussowitsch {
6121454ff5SMatthew G. Knepley   PetscFunctionBegin;
6221454ff5SMatthew G. Knepley   PetscValidPointer(q, 2);
639566063dSJacob Faibussowitsch   PetscCall(DMInitializePackage());
649566063dSJacob Faibussowitsch   PetscCall(PetscHeaderCreate(*q, PETSCQUADRATURE_CLASSID, "PetscQuadrature", "Quadrature", "DT", comm, PetscQuadratureDestroy, PetscQuadratureView));
6521454ff5SMatthew G. Knepley   (*q)->dim       = -1;
66a6b92713SMatthew G. Knepley   (*q)->Nc        = 1;
67bcede257SMatthew G. Knepley   (*q)->order     = -1;
6821454ff5SMatthew G. Knepley   (*q)->numPoints = 0;
6921454ff5SMatthew G. Knepley   (*q)->points    = NULL;
7021454ff5SMatthew G. Knepley   (*q)->weights   = NULL;
7121454ff5SMatthew G. Knepley   PetscFunctionReturn(0);
7221454ff5SMatthew G. Knepley }
7321454ff5SMatthew G. Knepley 
74c9638911SMatthew G. Knepley /*@
75c9638911SMatthew G. Knepley   PetscQuadratureDuplicate - Create a deep copy of the PetscQuadrature object
76c9638911SMatthew G. Knepley 
77d083f849SBarry Smith   Collective on q
78c9638911SMatthew G. Knepley 
79c9638911SMatthew G. Knepley   Input Parameter:
80c9638911SMatthew G. Knepley . q  - The PetscQuadrature object
81c9638911SMatthew G. Knepley 
82c9638911SMatthew G. Knepley   Output Parameter:
83c9638911SMatthew G. Knepley . r  - The new PetscQuadrature object
84c9638911SMatthew G. Knepley 
85c9638911SMatthew G. Knepley   Level: beginner
86c9638911SMatthew G. Knepley 
87db781477SPatrick Sanan .seealso: `PetscQuadratureCreate()`, `PetscQuadratureDestroy()`, `PetscQuadratureGetData()`
88c9638911SMatthew G. Knepley @*/
89*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadratureDuplicate(PetscQuadrature q, PetscQuadrature *r)
90*d71ae5a4SJacob Faibussowitsch {
91a6b92713SMatthew G. Knepley   PetscInt         order, dim, Nc, Nq;
92c9638911SMatthew G. Knepley   const PetscReal *points, *weights;
93c9638911SMatthew G. Knepley   PetscReal       *p, *w;
94c9638911SMatthew G. Knepley 
95c9638911SMatthew G. Knepley   PetscFunctionBegin;
96064a246eSJacob Faibussowitsch   PetscValidPointer(q, 1);
979566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureCreate(PetscObjectComm((PetscObject)q), r));
989566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureGetOrder(q, &order));
999566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetOrder(*r, order));
1009566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureGetData(q, &dim, &Nc, &Nq, &points, &weights));
1019566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(Nq * dim, &p));
1029566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(Nq * Nc, &w));
1039566063dSJacob Faibussowitsch   PetscCall(PetscArraycpy(p, points, Nq * dim));
1049566063dSJacob Faibussowitsch   PetscCall(PetscArraycpy(w, weights, Nc * Nq));
1059566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetData(*r, dim, Nc, Nq, p, w));
106c9638911SMatthew G. Knepley   PetscFunctionReturn(0);
107c9638911SMatthew G. Knepley }
108c9638911SMatthew G. Knepley 
10940d8ff71SMatthew G. Knepley /*@
11040d8ff71SMatthew G. Knepley   PetscQuadratureDestroy - Destroys a PetscQuadrature object
11140d8ff71SMatthew G. Knepley 
112d083f849SBarry Smith   Collective on q
11340d8ff71SMatthew G. Knepley 
11440d8ff71SMatthew G. Knepley   Input Parameter:
11540d8ff71SMatthew G. Knepley . q  - The PetscQuadrature object
11640d8ff71SMatthew G. Knepley 
11740d8ff71SMatthew G. Knepley   Level: beginner
11840d8ff71SMatthew G. Knepley 
119db781477SPatrick Sanan .seealso: `PetscQuadratureCreate()`, `PetscQuadratureGetData()`
12040d8ff71SMatthew G. Knepley @*/
121*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadratureDestroy(PetscQuadrature *q)
122*d71ae5a4SJacob Faibussowitsch {
123bfa639d9SMatthew G. Knepley   PetscFunctionBegin;
12421454ff5SMatthew G. Knepley   if (!*q) PetscFunctionReturn(0);
1252cd22861SMatthew G. Knepley   PetscValidHeaderSpecific((*q), PETSCQUADRATURE_CLASSID, 1);
12621454ff5SMatthew G. Knepley   if (--((PetscObject)(*q))->refct > 0) {
12721454ff5SMatthew G. Knepley     *q = NULL;
12821454ff5SMatthew G. Knepley     PetscFunctionReturn(0);
12921454ff5SMatthew G. Knepley   }
1309566063dSJacob Faibussowitsch   PetscCall(PetscFree((*q)->points));
1319566063dSJacob Faibussowitsch   PetscCall(PetscFree((*q)->weights));
1329566063dSJacob Faibussowitsch   PetscCall(PetscHeaderDestroy(q));
13321454ff5SMatthew G. Knepley   PetscFunctionReturn(0);
13421454ff5SMatthew G. Knepley }
13521454ff5SMatthew G. Knepley 
136bcede257SMatthew G. Knepley /*@
137a6b92713SMatthew G. Knepley   PetscQuadratureGetOrder - Return the order of the method
138bcede257SMatthew G. Knepley 
139bcede257SMatthew G. Knepley   Not collective
140bcede257SMatthew G. Knepley 
141bcede257SMatthew G. Knepley   Input Parameter:
142bcede257SMatthew G. Knepley . q - The PetscQuadrature object
143bcede257SMatthew G. Knepley 
144bcede257SMatthew G. Knepley   Output Parameter:
145bcede257SMatthew G. Knepley . order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated
146bcede257SMatthew G. Knepley 
147bcede257SMatthew G. Knepley   Level: intermediate
148bcede257SMatthew G. Knepley 
149db781477SPatrick Sanan .seealso: `PetscQuadratureSetOrder()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
150bcede257SMatthew G. Knepley @*/
151*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadratureGetOrder(PetscQuadrature q, PetscInt *order)
152*d71ae5a4SJacob Faibussowitsch {
153bcede257SMatthew G. Knepley   PetscFunctionBegin;
1542cd22861SMatthew G. Knepley   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
155dadcf809SJacob Faibussowitsch   PetscValidIntPointer(order, 2);
156bcede257SMatthew G. Knepley   *order = q->order;
157bcede257SMatthew G. Knepley   PetscFunctionReturn(0);
158bcede257SMatthew G. Knepley }
159bcede257SMatthew G. Knepley 
160bcede257SMatthew G. Knepley /*@
161a6b92713SMatthew G. Knepley   PetscQuadratureSetOrder - Return the order of the method
162bcede257SMatthew G. Knepley 
163bcede257SMatthew G. Knepley   Not collective
164bcede257SMatthew G. Knepley 
165bcede257SMatthew G. Knepley   Input Parameters:
166bcede257SMatthew G. Knepley + q - The PetscQuadrature object
167bcede257SMatthew G. Knepley - order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated
168bcede257SMatthew G. Knepley 
169bcede257SMatthew G. Knepley   Level: intermediate
170bcede257SMatthew G. Knepley 
171db781477SPatrick Sanan .seealso: `PetscQuadratureGetOrder()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
172bcede257SMatthew G. Knepley @*/
173*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadratureSetOrder(PetscQuadrature q, PetscInt order)
174*d71ae5a4SJacob Faibussowitsch {
175bcede257SMatthew G. Knepley   PetscFunctionBegin;
1762cd22861SMatthew G. Knepley   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
177bcede257SMatthew G. Knepley   q->order = order;
178bcede257SMatthew G. Knepley   PetscFunctionReturn(0);
179bcede257SMatthew G. Knepley }
180bcede257SMatthew G. Knepley 
181a6b92713SMatthew G. Knepley /*@
182a6b92713SMatthew G. Knepley   PetscQuadratureGetNumComponents - Return the number of components for functions to be integrated
183a6b92713SMatthew G. Knepley 
184a6b92713SMatthew G. Knepley   Not collective
185a6b92713SMatthew G. Knepley 
186a6b92713SMatthew G. Knepley   Input Parameter:
187a6b92713SMatthew G. Knepley . q - The PetscQuadrature object
188a6b92713SMatthew G. Knepley 
189a6b92713SMatthew G. Knepley   Output Parameter:
190a6b92713SMatthew G. Knepley . Nc - The number of components
191a6b92713SMatthew G. Knepley 
192a6b92713SMatthew G. Knepley   Note: We are performing an integral int f(x) . w(x) dx, where both f and w (the weight) have Nc components.
193a6b92713SMatthew G. Knepley 
194a6b92713SMatthew G. Knepley   Level: intermediate
195a6b92713SMatthew G. Knepley 
196db781477SPatrick Sanan .seealso: `PetscQuadratureSetNumComponents()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
197a6b92713SMatthew G. Knepley @*/
198*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadratureGetNumComponents(PetscQuadrature q, PetscInt *Nc)
199*d71ae5a4SJacob Faibussowitsch {
200a6b92713SMatthew G. Knepley   PetscFunctionBegin;
2012cd22861SMatthew G. Knepley   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
202dadcf809SJacob Faibussowitsch   PetscValidIntPointer(Nc, 2);
203a6b92713SMatthew G. Knepley   *Nc = q->Nc;
204a6b92713SMatthew G. Knepley   PetscFunctionReturn(0);
205a6b92713SMatthew G. Knepley }
206a6b92713SMatthew G. Knepley 
207a6b92713SMatthew G. Knepley /*@
208a6b92713SMatthew G. Knepley   PetscQuadratureSetNumComponents - Return the number of components for functions to be integrated
209a6b92713SMatthew G. Knepley 
210a6b92713SMatthew G. Knepley   Not collective
211a6b92713SMatthew G. Knepley 
212a6b92713SMatthew G. Knepley   Input Parameters:
213a6b92713SMatthew G. Knepley + q  - The PetscQuadrature object
214a6b92713SMatthew G. Knepley - Nc - The number of components
215a6b92713SMatthew G. Knepley 
216a6b92713SMatthew G. Knepley   Note: We are performing an integral int f(x) . w(x) dx, where both f and w (the weight) have Nc components.
217a6b92713SMatthew G. Knepley 
218a6b92713SMatthew G. Knepley   Level: intermediate
219a6b92713SMatthew G. Knepley 
220db781477SPatrick Sanan .seealso: `PetscQuadratureGetNumComponents()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
221a6b92713SMatthew G. Knepley @*/
222*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadratureSetNumComponents(PetscQuadrature q, PetscInt Nc)
223*d71ae5a4SJacob Faibussowitsch {
224a6b92713SMatthew G. Knepley   PetscFunctionBegin;
2252cd22861SMatthew G. Knepley   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
226a6b92713SMatthew G. Knepley   q->Nc = Nc;
227a6b92713SMatthew G. Knepley   PetscFunctionReturn(0);
228a6b92713SMatthew G. Knepley }
229a6b92713SMatthew G. Knepley 
23040d8ff71SMatthew G. Knepley /*@C
23140d8ff71SMatthew G. Knepley   PetscQuadratureGetData - Returns the data defining the quadrature
23240d8ff71SMatthew G. Knepley 
23340d8ff71SMatthew G. Knepley   Not collective
23440d8ff71SMatthew G. Knepley 
23540d8ff71SMatthew G. Knepley   Input Parameter:
23640d8ff71SMatthew G. Knepley . q  - The PetscQuadrature object
23740d8ff71SMatthew G. Knepley 
23840d8ff71SMatthew G. Knepley   Output Parameters:
23940d8ff71SMatthew G. Knepley + dim - The spatial dimension
240805e7170SToby Isaac . Nc - The number of components
24140d8ff71SMatthew G. Knepley . npoints - The number of quadrature points
24240d8ff71SMatthew G. Knepley . points - The coordinates of each quadrature point
24340d8ff71SMatthew G. Knepley - weights - The weight of each quadrature point
24440d8ff71SMatthew G. Knepley 
24540d8ff71SMatthew G. Knepley   Level: intermediate
24640d8ff71SMatthew G. Knepley 
24795452b02SPatrick Sanan   Fortran Notes:
24895452b02SPatrick Sanan     From Fortran you must call PetscQuadratureRestoreData() when you are done with the data
2491fd49c25SBarry Smith 
250db781477SPatrick Sanan .seealso: `PetscQuadratureCreate()`, `PetscQuadratureSetData()`
25140d8ff71SMatthew G. Knepley @*/
252*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadratureGetData(PetscQuadrature q, PetscInt *dim, PetscInt *Nc, PetscInt *npoints, const PetscReal *points[], const PetscReal *weights[])
253*d71ae5a4SJacob Faibussowitsch {
25421454ff5SMatthew G. Knepley   PetscFunctionBegin;
2552cd22861SMatthew G. Knepley   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
25621454ff5SMatthew G. Knepley   if (dim) {
257dadcf809SJacob Faibussowitsch     PetscValidIntPointer(dim, 2);
25821454ff5SMatthew G. Knepley     *dim = q->dim;
25921454ff5SMatthew G. Knepley   }
260a6b92713SMatthew G. Knepley   if (Nc) {
261dadcf809SJacob Faibussowitsch     PetscValidIntPointer(Nc, 3);
262a6b92713SMatthew G. Knepley     *Nc = q->Nc;
263a6b92713SMatthew G. Knepley   }
26421454ff5SMatthew G. Knepley   if (npoints) {
265dadcf809SJacob Faibussowitsch     PetscValidIntPointer(npoints, 4);
26621454ff5SMatthew G. Knepley     *npoints = q->numPoints;
26721454ff5SMatthew G. Knepley   }
26821454ff5SMatthew G. Knepley   if (points) {
269a6b92713SMatthew G. Knepley     PetscValidPointer(points, 5);
27021454ff5SMatthew G. Knepley     *points = q->points;
27121454ff5SMatthew G. Knepley   }
27221454ff5SMatthew G. Knepley   if (weights) {
273a6b92713SMatthew G. Knepley     PetscValidPointer(weights, 6);
27421454ff5SMatthew G. Knepley     *weights = q->weights;
27521454ff5SMatthew G. Knepley   }
27621454ff5SMatthew G. Knepley   PetscFunctionReturn(0);
27721454ff5SMatthew G. Knepley }
27821454ff5SMatthew G. Knepley 
2794f9ab2b4SJed Brown /*@
2804f9ab2b4SJed Brown   PetscQuadratureEqual - determine whether two quadratures are equivalent
2814f9ab2b4SJed Brown 
2824f9ab2b4SJed Brown   Input Parameters:
2834f9ab2b4SJed Brown + A - A PetscQuadrature object
2844f9ab2b4SJed Brown - B - Another PetscQuadrature object
2854f9ab2b4SJed Brown 
2864f9ab2b4SJed Brown   Output Parameters:
2874f9ab2b4SJed Brown . equal - PETSC_TRUE if the quadratures are the same
2884f9ab2b4SJed Brown 
2894f9ab2b4SJed Brown   Level: intermediate
2904f9ab2b4SJed Brown 
291db781477SPatrick Sanan .seealso: `PetscQuadratureCreate()`
2924f9ab2b4SJed Brown @*/
293*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadratureEqual(PetscQuadrature A, PetscQuadrature B, PetscBool *equal)
294*d71ae5a4SJacob Faibussowitsch {
2954f9ab2b4SJed Brown   PetscFunctionBegin;
2964f9ab2b4SJed Brown   PetscValidHeaderSpecific(A, PETSCQUADRATURE_CLASSID, 1);
2974f9ab2b4SJed Brown   PetscValidHeaderSpecific(B, PETSCQUADRATURE_CLASSID, 2);
2984f9ab2b4SJed Brown   PetscValidBoolPointer(equal, 3);
2994f9ab2b4SJed Brown   *equal = PETSC_FALSE;
300ad540459SPierre Jolivet   if (A->dim != B->dim || A->Nc != B->Nc || A->order != B->order || A->numPoints != B->numPoints) PetscFunctionReturn(0);
3014f9ab2b4SJed Brown   for (PetscInt i = 0; i < A->numPoints * A->dim; i++) {
302ad540459SPierre Jolivet     if (PetscAbsReal(A->points[i] - B->points[i]) > PETSC_SMALL) PetscFunctionReturn(0);
3034f9ab2b4SJed Brown   }
3044f9ab2b4SJed Brown   if (!A->weights && !B->weights) {
3054f9ab2b4SJed Brown     *equal = PETSC_TRUE;
3064f9ab2b4SJed Brown     PetscFunctionReturn(0);
3074f9ab2b4SJed Brown   }
3084f9ab2b4SJed Brown   if (A->weights && B->weights) {
3094f9ab2b4SJed Brown     for (PetscInt i = 0; i < A->numPoints; i++) {
310ad540459SPierre Jolivet       if (PetscAbsReal(A->weights[i] - B->weights[i]) > PETSC_SMALL) PetscFunctionReturn(0);
3114f9ab2b4SJed Brown     }
3124f9ab2b4SJed Brown     *equal = PETSC_TRUE;
3134f9ab2b4SJed Brown   }
3144f9ab2b4SJed Brown   PetscFunctionReturn(0);
3154f9ab2b4SJed Brown }
3164f9ab2b4SJed Brown 
317*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTJacobianInverse_Internal(PetscInt m, PetscInt n, const PetscReal J[], PetscReal Jinv[])
318*d71ae5a4SJacob Faibussowitsch {
319907761f8SToby Isaac   PetscScalar *Js, *Jinvs;
320907761f8SToby Isaac   PetscInt     i, j, k;
321907761f8SToby Isaac   PetscBLASInt bm, bn, info;
322907761f8SToby Isaac 
323907761f8SToby Isaac   PetscFunctionBegin;
324d4afb720SToby Isaac   if (!m || !n) PetscFunctionReturn(0);
3259566063dSJacob Faibussowitsch   PetscCall(PetscBLASIntCast(m, &bm));
3269566063dSJacob Faibussowitsch   PetscCall(PetscBLASIntCast(n, &bn));
327907761f8SToby Isaac #if defined(PETSC_USE_COMPLEX)
3289566063dSJacob Faibussowitsch   PetscCall(PetscMalloc2(m * n, &Js, m * n, &Jinvs));
32928222859SToby Isaac   for (i = 0; i < m * n; i++) Js[i] = J[i];
330907761f8SToby Isaac #else
331907761f8SToby Isaac   Js    = (PetscReal *)J;
332907761f8SToby Isaac   Jinvs = Jinv;
333907761f8SToby Isaac #endif
334907761f8SToby Isaac   if (m == n) {
335907761f8SToby Isaac     PetscBLASInt *pivots;
336907761f8SToby Isaac     PetscScalar  *W;
337907761f8SToby Isaac 
3389566063dSJacob Faibussowitsch     PetscCall(PetscMalloc2(m, &pivots, m, &W));
339907761f8SToby Isaac 
3409566063dSJacob Faibussowitsch     PetscCall(PetscArraycpy(Jinvs, Js, m * m));
341792fecdfSBarry Smith     PetscCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, Jinvs, &bm, pivots, &info));
34263a3b9bcSJacob Faibussowitsch     PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetrf %" PetscInt_FMT, (PetscInt)info);
343792fecdfSBarry Smith     PetscCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, Jinvs, &bm, pivots, W, &bm, &info));
34463a3b9bcSJacob Faibussowitsch     PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetri %" PetscInt_FMT, (PetscInt)info);
3459566063dSJacob Faibussowitsch     PetscCall(PetscFree2(pivots, W));
346907761f8SToby Isaac   } else if (m < n) {
347907761f8SToby Isaac     PetscScalar  *JJT;
348907761f8SToby Isaac     PetscBLASInt *pivots;
349907761f8SToby Isaac     PetscScalar  *W;
350907761f8SToby Isaac 
3519566063dSJacob Faibussowitsch     PetscCall(PetscMalloc1(m * m, &JJT));
3529566063dSJacob Faibussowitsch     PetscCall(PetscMalloc2(m, &pivots, m, &W));
353907761f8SToby Isaac     for (i = 0; i < m; i++) {
354907761f8SToby Isaac       for (j = 0; j < m; j++) {
355907761f8SToby Isaac         PetscScalar val = 0.;
356907761f8SToby Isaac 
357907761f8SToby Isaac         for (k = 0; k < n; k++) val += Js[i * n + k] * Js[j * n + k];
358907761f8SToby Isaac         JJT[i * m + j] = val;
359907761f8SToby Isaac       }
360907761f8SToby Isaac     }
361907761f8SToby Isaac 
362792fecdfSBarry Smith     PetscCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, JJT, &bm, pivots, &info));
36363a3b9bcSJacob Faibussowitsch     PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetrf %" PetscInt_FMT, (PetscInt)info);
364792fecdfSBarry Smith     PetscCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, JJT, &bm, pivots, W, &bm, &info));
36563a3b9bcSJacob Faibussowitsch     PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetri %" PetscInt_FMT, (PetscInt)info);
366907761f8SToby Isaac     for (i = 0; i < n; i++) {
367907761f8SToby Isaac       for (j = 0; j < m; j++) {
368907761f8SToby Isaac         PetscScalar val = 0.;
369907761f8SToby Isaac 
370907761f8SToby Isaac         for (k = 0; k < m; k++) val += Js[k * n + i] * JJT[k * m + j];
371907761f8SToby Isaac         Jinvs[i * m + j] = val;
372907761f8SToby Isaac       }
373907761f8SToby Isaac     }
3749566063dSJacob Faibussowitsch     PetscCall(PetscFree2(pivots, W));
3759566063dSJacob Faibussowitsch     PetscCall(PetscFree(JJT));
376907761f8SToby Isaac   } else {
377907761f8SToby Isaac     PetscScalar  *JTJ;
378907761f8SToby Isaac     PetscBLASInt *pivots;
379907761f8SToby Isaac     PetscScalar  *W;
380907761f8SToby Isaac 
3819566063dSJacob Faibussowitsch     PetscCall(PetscMalloc1(n * n, &JTJ));
3829566063dSJacob Faibussowitsch     PetscCall(PetscMalloc2(n, &pivots, n, &W));
383907761f8SToby Isaac     for (i = 0; i < n; i++) {
384907761f8SToby Isaac       for (j = 0; j < n; j++) {
385907761f8SToby Isaac         PetscScalar val = 0.;
386907761f8SToby Isaac 
387907761f8SToby Isaac         for (k = 0; k < m; k++) val += Js[k * n + i] * Js[k * n + j];
388907761f8SToby Isaac         JTJ[i * n + j] = val;
389907761f8SToby Isaac       }
390907761f8SToby Isaac     }
391907761f8SToby Isaac 
392792fecdfSBarry Smith     PetscCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bn, &bn, JTJ, &bn, pivots, &info));
39363a3b9bcSJacob Faibussowitsch     PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetrf %" PetscInt_FMT, (PetscInt)info);
394792fecdfSBarry Smith     PetscCallBLAS("LAPACKgetri", LAPACKgetri_(&bn, JTJ, &bn, pivots, W, &bn, &info));
39563a3b9bcSJacob Faibussowitsch     PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetri %" PetscInt_FMT, (PetscInt)info);
396907761f8SToby Isaac     for (i = 0; i < n; i++) {
397907761f8SToby Isaac       for (j = 0; j < m; j++) {
398907761f8SToby Isaac         PetscScalar val = 0.;
399907761f8SToby Isaac 
400907761f8SToby Isaac         for (k = 0; k < n; k++) val += JTJ[i * n + k] * Js[j * n + k];
401907761f8SToby Isaac         Jinvs[i * m + j] = val;
402907761f8SToby Isaac       }
403907761f8SToby Isaac     }
4049566063dSJacob Faibussowitsch     PetscCall(PetscFree2(pivots, W));
4059566063dSJacob Faibussowitsch     PetscCall(PetscFree(JTJ));
406907761f8SToby Isaac   }
407907761f8SToby Isaac #if defined(PETSC_USE_COMPLEX)
40828222859SToby Isaac   for (i = 0; i < m * n; i++) Jinv[i] = PetscRealPart(Jinvs[i]);
4099566063dSJacob Faibussowitsch   PetscCall(PetscFree2(Js, Jinvs));
410907761f8SToby Isaac #endif
411907761f8SToby Isaac   PetscFunctionReturn(0);
412907761f8SToby Isaac }
413907761f8SToby Isaac 
414907761f8SToby Isaac /*@
415907761f8SToby Isaac    PetscQuadraturePushForward - Push forward a quadrature functional under an affine transformation.
416907761f8SToby Isaac 
417907761f8SToby Isaac    Collecive on PetscQuadrature
418907761f8SToby Isaac 
4194165533cSJose E. Roman    Input Parameters:
420907761f8SToby Isaac +  q - the quadrature functional
421907761f8SToby Isaac .  imageDim - the dimension of the image of the transformation
422907761f8SToby Isaac .  origin - a point in the original space
423907761f8SToby Isaac .  originImage - the image of the origin under the transformation
424907761f8SToby Isaac .  J - the Jacobian of the image: an [imageDim x dim] matrix in row major order
42528222859SToby Isaac -  formDegree - transform the quadrature weights as k-forms of this form degree (if the number of components is a multiple of (dim choose formDegree), it is assumed that they represent multiple k-forms) [see PetscDTAltVPullback() for interpretation of formDegree]
426907761f8SToby Isaac 
4274165533cSJose E. Roman    Output Parameters:
428907761f8SToby Isaac .  Jinvstarq - a quadrature rule where each point is the image of a point in the original quadrature rule, and where the k-form weights have been pulled-back by the pseudoinverse of J to the k-form weights in the image space.
429907761f8SToby Isaac 
430907761f8SToby Isaac    Note: the new quadrature rule will have a different number of components if spaces have different dimensions.  For example, pushing a 2-form forward from a two dimensional space to a three dimensional space changes the number of components from 1 to 3.
431907761f8SToby Isaac 
4326c877ef6SSatish Balay    Level: intermediate
4336c877ef6SSatish Balay 
434db781477SPatrick Sanan .seealso: `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()`
435907761f8SToby Isaac @*/
436*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadraturePushForward(PetscQuadrature q, PetscInt imageDim, const PetscReal origin[], const PetscReal originImage[], const PetscReal J[], PetscInt formDegree, PetscQuadrature *Jinvstarq)
437*d71ae5a4SJacob Faibussowitsch {
438907761f8SToby Isaac   PetscInt         dim, Nc, imageNc, formSize, Ncopies, imageFormSize, Npoints, pt, i, j, c;
439907761f8SToby Isaac   const PetscReal *points;
440907761f8SToby Isaac   const PetscReal *weights;
441907761f8SToby Isaac   PetscReal       *imagePoints, *imageWeights;
442907761f8SToby Isaac   PetscReal       *Jinv;
443907761f8SToby Isaac   PetscReal       *Jinvstar;
444907761f8SToby Isaac 
445907761f8SToby Isaac   PetscFunctionBegin;
446d4afb720SToby Isaac   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
44763a3b9bcSJacob Faibussowitsch   PetscCheck(imageDim >= PetscAbsInt(formDegree), PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Cannot represent a %" PetscInt_FMT "-form in %" PetscInt_FMT " dimensions", PetscAbsInt(formDegree), imageDim);
4489566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureGetData(q, &dim, &Nc, &Npoints, &points, &weights));
4499566063dSJacob Faibussowitsch   PetscCall(PetscDTBinomialInt(dim, PetscAbsInt(formDegree), &formSize));
45063a3b9bcSJacob Faibussowitsch   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);
451907761f8SToby Isaac   Ncopies = Nc / formSize;
4529566063dSJacob Faibussowitsch   PetscCall(PetscDTBinomialInt(imageDim, PetscAbsInt(formDegree), &imageFormSize));
453907761f8SToby Isaac   imageNc = Ncopies * imageFormSize;
4549566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(Npoints * imageDim, &imagePoints));
4559566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(Npoints * imageNc, &imageWeights));
4569566063dSJacob Faibussowitsch   PetscCall(PetscMalloc2(imageDim * dim, &Jinv, formSize * imageFormSize, &Jinvstar));
4579566063dSJacob Faibussowitsch   PetscCall(PetscDTJacobianInverse_Internal(imageDim, dim, J, Jinv));
4589566063dSJacob Faibussowitsch   PetscCall(PetscDTAltVPullbackMatrix(imageDim, dim, Jinv, formDegree, Jinvstar));
459907761f8SToby Isaac   for (pt = 0; pt < Npoints; pt++) {
460907761f8SToby Isaac     const PetscReal *point      = &points[pt * dim];
461907761f8SToby Isaac     PetscReal       *imagePoint = &imagePoints[pt * imageDim];
462907761f8SToby Isaac 
463907761f8SToby Isaac     for (i = 0; i < imageDim; i++) {
464907761f8SToby Isaac       PetscReal val = originImage[i];
465907761f8SToby Isaac 
466907761f8SToby Isaac       for (j = 0; j < dim; j++) val += J[i * dim + j] * (point[j] - origin[j]);
467907761f8SToby Isaac       imagePoint[i] = val;
468907761f8SToby Isaac     }
469907761f8SToby Isaac     for (c = 0; c < Ncopies; c++) {
470907761f8SToby Isaac       const PetscReal *form      = &weights[pt * Nc + c * formSize];
471907761f8SToby Isaac       PetscReal       *imageForm = &imageWeights[pt * imageNc + c * imageFormSize];
472907761f8SToby Isaac 
473907761f8SToby Isaac       for (i = 0; i < imageFormSize; i++) {
474907761f8SToby Isaac         PetscReal val = 0.;
475907761f8SToby Isaac 
476907761f8SToby Isaac         for (j = 0; j < formSize; j++) val += Jinvstar[i * formSize + j] * form[j];
477907761f8SToby Isaac         imageForm[i] = val;
478907761f8SToby Isaac       }
479907761f8SToby Isaac     }
480907761f8SToby Isaac   }
4819566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureCreate(PetscObjectComm((PetscObject)q), Jinvstarq));
4829566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetData(*Jinvstarq, imageDim, imageNc, Npoints, imagePoints, imageWeights));
4839566063dSJacob Faibussowitsch   PetscCall(PetscFree2(Jinv, Jinvstar));
484907761f8SToby Isaac   PetscFunctionReturn(0);
485907761f8SToby Isaac }
486907761f8SToby Isaac 
48740d8ff71SMatthew G. Knepley /*@C
48840d8ff71SMatthew G. Knepley   PetscQuadratureSetData - Sets the data defining the quadrature
48940d8ff71SMatthew G. Knepley 
49040d8ff71SMatthew G. Knepley   Not collective
49140d8ff71SMatthew G. Knepley 
49240d8ff71SMatthew G. Knepley   Input Parameters:
49340d8ff71SMatthew G. Knepley + q  - The PetscQuadrature object
49440d8ff71SMatthew G. Knepley . dim - The spatial dimension
495e2b35d93SBarry Smith . Nc - The number of components
49640d8ff71SMatthew G. Knepley . npoints - The number of quadrature points
49740d8ff71SMatthew G. Knepley . points - The coordinates of each quadrature point
49840d8ff71SMatthew G. Knepley - weights - The weight of each quadrature point
49940d8ff71SMatthew G. Knepley 
500c99e0549SMatthew G. Knepley   Note: This routine owns the references to points and weights, so they must be allocated using PetscMalloc() and the user should not free them.
501f2fd9e53SMatthew G. Knepley 
50240d8ff71SMatthew G. Knepley   Level: intermediate
50340d8ff71SMatthew G. Knepley 
504db781477SPatrick Sanan .seealso: `PetscQuadratureCreate()`, `PetscQuadratureGetData()`
50540d8ff71SMatthew G. Knepley @*/
506*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadratureSetData(PetscQuadrature q, PetscInt dim, PetscInt Nc, PetscInt npoints, const PetscReal points[], const PetscReal weights[])
507*d71ae5a4SJacob Faibussowitsch {
50821454ff5SMatthew G. Knepley   PetscFunctionBegin;
5092cd22861SMatthew G. Knepley   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
51021454ff5SMatthew G. Knepley   if (dim >= 0) q->dim = dim;
511a6b92713SMatthew G. Knepley   if (Nc >= 0) q->Nc = Nc;
51221454ff5SMatthew G. Knepley   if (npoints >= 0) q->numPoints = npoints;
51321454ff5SMatthew G. Knepley   if (points) {
514dadcf809SJacob Faibussowitsch     PetscValidRealPointer(points, 5);
51521454ff5SMatthew G. Knepley     q->points = points;
51621454ff5SMatthew G. Knepley   }
51721454ff5SMatthew G. Knepley   if (weights) {
518dadcf809SJacob Faibussowitsch     PetscValidRealPointer(weights, 6);
51921454ff5SMatthew G. Knepley     q->weights = weights;
52021454ff5SMatthew G. Knepley   }
521f9fd7fdbSMatthew G. Knepley   PetscFunctionReturn(0);
522f9fd7fdbSMatthew G. Knepley }
523f9fd7fdbSMatthew G. Knepley 
524*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscQuadratureView_Ascii(PetscQuadrature quad, PetscViewer v)
525*d71ae5a4SJacob Faibussowitsch {
526d9bac1caSLisandro Dalcin   PetscInt          q, d, c;
527d9bac1caSLisandro Dalcin   PetscViewerFormat format;
528d9bac1caSLisandro Dalcin 
529d9bac1caSLisandro Dalcin   PetscFunctionBegin;
53063a3b9bcSJacob Faibussowitsch   if (quad->Nc > 1) PetscCall(PetscViewerASCIIPrintf(v, "Quadrature of order %" PetscInt_FMT " on %" PetscInt_FMT " points (dim %" PetscInt_FMT ") with %" PetscInt_FMT " components\n", quad->order, quad->numPoints, quad->dim, quad->Nc));
53163a3b9bcSJacob Faibussowitsch   else PetscCall(PetscViewerASCIIPrintf(v, "Quadrature of order %" PetscInt_FMT " on %" PetscInt_FMT " points (dim %" PetscInt_FMT ")\n", quad->order, quad->numPoints, quad->dim));
5329566063dSJacob Faibussowitsch   PetscCall(PetscViewerGetFormat(v, &format));
533d9bac1caSLisandro Dalcin   if (format != PETSC_VIEWER_ASCII_INFO_DETAIL) PetscFunctionReturn(0);
534d9bac1caSLisandro Dalcin   for (q = 0; q < quad->numPoints; ++q) {
53563a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(v, "p%" PetscInt_FMT " (", q));
5369566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIUseTabs(v, PETSC_FALSE));
537d9bac1caSLisandro Dalcin     for (d = 0; d < quad->dim; ++d) {
5389566063dSJacob Faibussowitsch       if (d) PetscCall(PetscViewerASCIIPrintf(v, ", "));
5399566063dSJacob Faibussowitsch       PetscCall(PetscViewerASCIIPrintf(v, "%+g", (double)quad->points[q * quad->dim + d]));
540d9bac1caSLisandro Dalcin     }
5419566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(v, ") "));
54263a3b9bcSJacob Faibussowitsch     if (quad->Nc > 1) PetscCall(PetscViewerASCIIPrintf(v, "w%" PetscInt_FMT " (", q));
543d9bac1caSLisandro Dalcin     for (c = 0; c < quad->Nc; ++c) {
5449566063dSJacob Faibussowitsch       if (c) PetscCall(PetscViewerASCIIPrintf(v, ", "));
5459566063dSJacob Faibussowitsch       PetscCall(PetscViewerASCIIPrintf(v, "%+g", (double)quad->weights[q * quad->Nc + c]));
546d9bac1caSLisandro Dalcin     }
5479566063dSJacob Faibussowitsch     if (quad->Nc > 1) PetscCall(PetscViewerASCIIPrintf(v, ")"));
5489566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(v, "\n"));
5499566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIUseTabs(v, PETSC_TRUE));
550d9bac1caSLisandro Dalcin   }
551d9bac1caSLisandro Dalcin   PetscFunctionReturn(0);
552d9bac1caSLisandro Dalcin }
553d9bac1caSLisandro Dalcin 
55440d8ff71SMatthew G. Knepley /*@C
55540d8ff71SMatthew G. Knepley   PetscQuadratureView - Views a PetscQuadrature object
55640d8ff71SMatthew G. Knepley 
557d083f849SBarry Smith   Collective on quad
55840d8ff71SMatthew G. Knepley 
55940d8ff71SMatthew G. Knepley   Input Parameters:
560d9bac1caSLisandro Dalcin + quad  - The PetscQuadrature object
56140d8ff71SMatthew G. Knepley - viewer - The PetscViewer object
56240d8ff71SMatthew G. Knepley 
56340d8ff71SMatthew G. Knepley   Level: beginner
56440d8ff71SMatthew G. Knepley 
565db781477SPatrick Sanan .seealso: `PetscQuadratureCreate()`, `PetscQuadratureGetData()`
56640d8ff71SMatthew G. Knepley @*/
567*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadratureView(PetscQuadrature quad, PetscViewer viewer)
568*d71ae5a4SJacob Faibussowitsch {
569d9bac1caSLisandro Dalcin   PetscBool iascii;
570f9fd7fdbSMatthew G. Knepley 
571f9fd7fdbSMatthew G. Knepley   PetscFunctionBegin;
572d9bac1caSLisandro Dalcin   PetscValidHeader(quad, 1);
573d9bac1caSLisandro Dalcin   if (viewer) PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
5749566063dSJacob Faibussowitsch   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)quad), &viewer));
5759566063dSJacob Faibussowitsch   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
5769566063dSJacob Faibussowitsch   PetscCall(PetscViewerASCIIPushTab(viewer));
5779566063dSJacob Faibussowitsch   if (iascii) PetscCall(PetscQuadratureView_Ascii(quad, viewer));
5789566063dSJacob Faibussowitsch   PetscCall(PetscViewerASCIIPopTab(viewer));
579bfa639d9SMatthew G. Knepley   PetscFunctionReturn(0);
580bfa639d9SMatthew G. Knepley }
581bfa639d9SMatthew G. Knepley 
58289710940SMatthew G. Knepley /*@C
58389710940SMatthew G. Knepley   PetscQuadratureExpandComposite - Return a quadrature over the composite element, which has the original quadrature in each subelement
58489710940SMatthew G. Knepley 
58589710940SMatthew G. Knepley   Not collective
58689710940SMatthew G. Knepley 
587d8d19677SJose E. Roman   Input Parameters:
58889710940SMatthew G. Knepley + q - The original PetscQuadrature
58989710940SMatthew G. Knepley . numSubelements - The number of subelements the original element is divided into
59089710940SMatthew G. Knepley . v0 - An array of the initial points for each subelement
59189710940SMatthew G. Knepley - jac - An array of the Jacobian mappings from the reference to each subelement
59289710940SMatthew G. Knepley 
59389710940SMatthew G. Knepley   Output Parameters:
59489710940SMatthew G. Knepley . dim - The dimension
59589710940SMatthew G. Knepley 
59689710940SMatthew G. Knepley   Note: Together v0 and jac define an affine mapping from the original reference element to each subelement
59789710940SMatthew G. Knepley 
598f5f57ec0SBarry Smith  Not available from Fortran
599f5f57ec0SBarry Smith 
60089710940SMatthew G. Knepley   Level: intermediate
60189710940SMatthew G. Knepley 
602db781477SPatrick Sanan .seealso: `PetscFECreate()`, `PetscSpaceGetDimension()`, `PetscDualSpaceGetDimension()`
60389710940SMatthew G. Knepley @*/
604*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscQuadratureExpandComposite(PetscQuadrature q, PetscInt numSubelements, const PetscReal v0[], const PetscReal jac[], PetscQuadrature *qref)
605*d71ae5a4SJacob Faibussowitsch {
60689710940SMatthew G. Knepley   const PetscReal *points, *weights;
60789710940SMatthew G. Knepley   PetscReal       *pointsRef, *weightsRef;
608a6b92713SMatthew G. Knepley   PetscInt         dim, Nc, order, npoints, npointsRef, c, p, cp, d, e;
60989710940SMatthew G. Knepley 
61089710940SMatthew G. Knepley   PetscFunctionBegin;
6112cd22861SMatthew G. Knepley   PetscValidHeaderSpecific(q, PETSCQUADRATURE_CLASSID, 1);
612dadcf809SJacob Faibussowitsch   PetscValidRealPointer(v0, 3);
613dadcf809SJacob Faibussowitsch   PetscValidRealPointer(jac, 4);
61489710940SMatthew G. Knepley   PetscValidPointer(qref, 5);
6159566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, qref));
6169566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureGetOrder(q, &order));
6179566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureGetData(q, &dim, &Nc, &npoints, &points, &weights));
61889710940SMatthew G. Knepley   npointsRef = npoints * numSubelements;
6199566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(npointsRef * dim, &pointsRef));
6209566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(npointsRef * Nc, &weightsRef));
62189710940SMatthew G. Knepley   for (c = 0; c < numSubelements; ++c) {
62289710940SMatthew G. Knepley     for (p = 0; p < npoints; ++p) {
62389710940SMatthew G. Knepley       for (d = 0; d < dim; ++d) {
62489710940SMatthew G. Knepley         pointsRef[(c * npoints + p) * dim + d] = v0[c * dim + d];
625ad540459SPierre Jolivet         for (e = 0; e < dim; ++e) pointsRef[(c * npoints + p) * dim + d] += jac[(c * dim + d) * dim + e] * (points[p * dim + e] + 1.0);
62689710940SMatthew G. Knepley       }
62789710940SMatthew G. Knepley       /* Could also use detJ here */
628a6b92713SMatthew G. Knepley       for (cp = 0; cp < Nc; ++cp) weightsRef[(c * npoints + p) * Nc + cp] = weights[p * Nc + cp] / numSubelements;
62989710940SMatthew G. Knepley     }
63089710940SMatthew G. Knepley   }
6319566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetOrder(*qref, order));
6329566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetData(*qref, dim, Nc, npointsRef, pointsRef, weightsRef));
63389710940SMatthew G. Knepley   PetscFunctionReturn(0);
63489710940SMatthew G. Knepley }
63589710940SMatthew G. Knepley 
63694e21283SToby Isaac /* Compute the coefficients for the Jacobi polynomial recurrence,
63794e21283SToby Isaac  *
63894e21283SToby Isaac  * J^{a,b}_n(x) = (cnm1 + cnm1x * x) * J^{a,b}_{n-1}(x) - cnm2 * J^{a,b}_{n-2}(x).
63994e21283SToby Isaac  */
64094e21283SToby Isaac #define PetscDTJacobiRecurrence_Internal(n, a, b, cnm1, cnm1x, cnm2) \
64194e21283SToby Isaac   do { \
64294e21283SToby Isaac     PetscReal _a = (a); \
64394e21283SToby Isaac     PetscReal _b = (b); \
64494e21283SToby Isaac     PetscReal _n = (n); \
64594e21283SToby Isaac     if (n == 1) { \
64694e21283SToby Isaac       (cnm1)  = (_a - _b) * 0.5; \
64794e21283SToby Isaac       (cnm1x) = (_a + _b + 2.) * 0.5; \
64894e21283SToby Isaac       (cnm2)  = 0.; \
64994e21283SToby Isaac     } else { \
65094e21283SToby Isaac       PetscReal _2n  = _n + _n; \
65194e21283SToby Isaac       PetscReal _d   = (_2n * (_n + _a + _b) * (_2n + _a + _b - 2)); \
65294e21283SToby Isaac       PetscReal _n1  = (_2n + _a + _b - 1.) * (_a * _a - _b * _b); \
65394e21283SToby Isaac       PetscReal _n1x = (_2n + _a + _b - 1.) * (_2n + _a + _b) * (_2n + _a + _b - 2); \
65494e21283SToby Isaac       PetscReal _n2  = 2. * ((_n + _a - 1.) * (_n + _b - 1.) * (_2n + _a + _b)); \
65594e21283SToby Isaac       (cnm1)         = _n1 / _d; \
65694e21283SToby Isaac       (cnm1x)        = _n1x / _d; \
65794e21283SToby Isaac       (cnm2)         = _n2 / _d; \
65894e21283SToby Isaac     } \
65994e21283SToby Isaac   } while (0)
66094e21283SToby Isaac 
661fbdc3dfeSToby Isaac /*@
662fbdc3dfeSToby Isaac   PetscDTJacobiNorm - Compute the weighted L2 norm of a Jacobi polynomial.
663fbdc3dfeSToby Isaac 
664fbdc3dfeSToby Isaac   $\| P^{\alpha,\beta}_n \|_{\alpha,\beta}^2 = \int_{-1}^1 (1 + x)^{\alpha} (1 - x)^{\beta} P^{\alpha,\beta}_n (x)^2 dx.$
665fbdc3dfeSToby Isaac 
6664165533cSJose E. Roman   Input Parameters:
667fbdc3dfeSToby Isaac - alpha - the left exponent > -1
668fbdc3dfeSToby Isaac . beta - the right exponent > -1
669fbdc3dfeSToby Isaac + n - the polynomial degree
670fbdc3dfeSToby Isaac 
6714165533cSJose E. Roman   Output Parameter:
672fbdc3dfeSToby Isaac . norm - the weighted L2 norm
673fbdc3dfeSToby Isaac 
674fbdc3dfeSToby Isaac   Level: beginner
675fbdc3dfeSToby Isaac 
676db781477SPatrick Sanan .seealso: `PetscDTJacobiEval()`
677fbdc3dfeSToby Isaac @*/
678*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTJacobiNorm(PetscReal alpha, PetscReal beta, PetscInt n, PetscReal *norm)
679*d71ae5a4SJacob Faibussowitsch {
680fbdc3dfeSToby Isaac   PetscReal twoab1;
681fbdc3dfeSToby Isaac   PetscReal gr;
682fbdc3dfeSToby Isaac 
683fbdc3dfeSToby Isaac   PetscFunctionBegin;
68408401ef6SPierre Jolivet   PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Exponent alpha %g <= -1. invalid", (double)alpha);
68508401ef6SPierre Jolivet   PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Exponent beta %g <= -1. invalid", (double)beta);
68663a3b9bcSJacob Faibussowitsch   PetscCheck(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "n %" PetscInt_FMT " < 0 invalid", n);
687fbdc3dfeSToby Isaac   twoab1 = PetscPowReal(2., alpha + beta + 1.);
688fbdc3dfeSToby Isaac #if defined(PETSC_HAVE_LGAMMA)
689fbdc3dfeSToby Isaac   if (!n) {
690fbdc3dfeSToby Isaac     gr = PetscExpReal(PetscLGamma(alpha + 1.) + PetscLGamma(beta + 1.) - PetscLGamma(alpha + beta + 2.));
691fbdc3dfeSToby Isaac   } else {
692fbdc3dfeSToby Isaac     gr = PetscExpReal(PetscLGamma(n + alpha + 1.) + PetscLGamma(n + beta + 1.) - (PetscLGamma(n + 1.) + PetscLGamma(n + alpha + beta + 1.))) / (n + n + alpha + beta + 1.);
693fbdc3dfeSToby Isaac   }
694fbdc3dfeSToby Isaac #else
695fbdc3dfeSToby Isaac   {
696fbdc3dfeSToby Isaac     PetscInt alphai = (PetscInt)alpha;
697fbdc3dfeSToby Isaac     PetscInt betai  = (PetscInt)beta;
698fbdc3dfeSToby Isaac     PetscInt i;
699fbdc3dfeSToby Isaac 
700fbdc3dfeSToby Isaac     gr = n ? (1. / (n + n + alpha + beta + 1.)) : 1.;
701fbdc3dfeSToby Isaac     if ((PetscReal)alphai == alpha) {
702fbdc3dfeSToby Isaac       if (!n) {
703fbdc3dfeSToby Isaac         for (i = 0; i < alphai; i++) gr *= (i + 1.) / (beta + i + 1.);
704fbdc3dfeSToby Isaac         gr /= (alpha + beta + 1.);
705fbdc3dfeSToby Isaac       } else {
706fbdc3dfeSToby Isaac         for (i = 0; i < alphai; i++) gr *= (n + i + 1.) / (n + beta + i + 1.);
707fbdc3dfeSToby Isaac       }
708fbdc3dfeSToby Isaac     } else if ((PetscReal)betai == beta) {
709fbdc3dfeSToby Isaac       if (!n) {
710fbdc3dfeSToby Isaac         for (i = 0; i < betai; i++) gr *= (i + 1.) / (alpha + i + 2.);
711fbdc3dfeSToby Isaac         gr /= (alpha + beta + 1.);
712fbdc3dfeSToby Isaac       } else {
713fbdc3dfeSToby Isaac         for (i = 0; i < betai; i++) gr *= (n + i + 1.) / (n + alpha + i + 1.);
714fbdc3dfeSToby Isaac       }
715fbdc3dfeSToby Isaac     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "lgamma() - math routine is unavailable.");
716fbdc3dfeSToby Isaac   }
717fbdc3dfeSToby Isaac #endif
718fbdc3dfeSToby Isaac   *norm = PetscSqrtReal(twoab1 * gr);
719fbdc3dfeSToby Isaac   PetscFunctionReturn(0);
720fbdc3dfeSToby Isaac }
721fbdc3dfeSToby Isaac 
722*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTJacobiEval_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscInt k, const PetscReal *points, PetscInt ndegree, const PetscInt *degrees, PetscReal *p)
723*d71ae5a4SJacob Faibussowitsch {
72494e21283SToby Isaac   PetscReal ak, bk;
72594e21283SToby Isaac   PetscReal abk1;
72694e21283SToby Isaac   PetscInt  i, l, maxdegree;
72794e21283SToby Isaac 
72894e21283SToby Isaac   PetscFunctionBegin;
72994e21283SToby Isaac   maxdegree = degrees[ndegree - 1] - k;
73094e21283SToby Isaac   ak        = a + k;
73194e21283SToby Isaac   bk        = b + k;
73294e21283SToby Isaac   abk1      = a + b + k + 1.;
73394e21283SToby Isaac   if (maxdegree < 0) {
7349371c9d4SSatish Balay     for (i = 0; i < npoints; i++)
7359371c9d4SSatish Balay       for (l = 0; l < ndegree; l++) p[i * ndegree + l] = 0.;
73694e21283SToby Isaac     PetscFunctionReturn(0);
73794e21283SToby Isaac   }
73894e21283SToby Isaac   for (i = 0; i < npoints; i++) {
73994e21283SToby Isaac     PetscReal pm1, pm2, x;
74094e21283SToby Isaac     PetscReal cnm1, cnm1x, cnm2;
74194e21283SToby Isaac     PetscInt  j, m;
74294e21283SToby Isaac 
74394e21283SToby Isaac     x   = points[i];
74494e21283SToby Isaac     pm2 = 1.;
74594e21283SToby Isaac     PetscDTJacobiRecurrence_Internal(1, ak, bk, cnm1, cnm1x, cnm2);
74694e21283SToby Isaac     pm1 = (cnm1 + cnm1x * x);
74794e21283SToby Isaac     l   = 0;
748ad540459SPierre Jolivet     while (l < ndegree && degrees[l] - k < 0) p[l++] = 0.;
74994e21283SToby Isaac     while (l < ndegree && degrees[l] - k == 0) {
75094e21283SToby Isaac       p[l] = pm2;
75194e21283SToby Isaac       for (m = 0; m < k; m++) p[l] *= (abk1 + m) * 0.5;
75294e21283SToby Isaac       l++;
75394e21283SToby Isaac     }
75494e21283SToby Isaac     while (l < ndegree && degrees[l] - k == 1) {
75594e21283SToby Isaac       p[l] = pm1;
75694e21283SToby Isaac       for (m = 0; m < k; m++) p[l] *= (abk1 + 1 + m) * 0.5;
75794e21283SToby Isaac       l++;
75894e21283SToby Isaac     }
75994e21283SToby Isaac     for (j = 2; j <= maxdegree; j++) {
76094e21283SToby Isaac       PetscReal pp;
76194e21283SToby Isaac 
76294e21283SToby Isaac       PetscDTJacobiRecurrence_Internal(j, ak, bk, cnm1, cnm1x, cnm2);
76394e21283SToby Isaac       pp  = (cnm1 + cnm1x * x) * pm1 - cnm2 * pm2;
76494e21283SToby Isaac       pm2 = pm1;
76594e21283SToby Isaac       pm1 = pp;
76694e21283SToby Isaac       while (l < ndegree && degrees[l] - k == j) {
76794e21283SToby Isaac         p[l] = pp;
76894e21283SToby Isaac         for (m = 0; m < k; m++) p[l] *= (abk1 + j + m) * 0.5;
76994e21283SToby Isaac         l++;
77094e21283SToby Isaac       }
77194e21283SToby Isaac     }
77294e21283SToby Isaac     p += ndegree;
77394e21283SToby Isaac   }
77494e21283SToby Isaac   PetscFunctionReturn(0);
77594e21283SToby Isaac }
77694e21283SToby Isaac 
77737045ce4SJed Brown /*@
778fbdc3dfeSToby Isaac   PetscDTJacobiEvalJet - Evaluate the jet (function and derivatives) of the Jacobi polynomials basis up to a given degree.  The Jacobi polynomials with indices $\alpha$ and $\beta$ are orthogonal with respect to the weighted inner product $\langle f, g \rangle = \int_{-1}^1 (1+x)^{\alpha} (1-x)^{\beta) f(x) g(x) dx$.
779fbdc3dfeSToby Isaac 
7804165533cSJose E. Roman   Input Parameters:
781fbdc3dfeSToby Isaac + alpha - the left exponent of the weight
782fbdc3dfeSToby Isaac . beta - the right exponetn of the weight
783fbdc3dfeSToby Isaac . npoints - the number of points to evaluate the polynomials at
784fbdc3dfeSToby Isaac . points - [npoints] array of point coordinates
785fbdc3dfeSToby Isaac . degree - the maximm degree polynomial space to evaluate, (degree + 1) will be evaluated total.
786fbdc3dfeSToby Isaac - k - the maximum derivative to evaluate in the jet, (k + 1) will be evaluated total.
787fbdc3dfeSToby Isaac 
7886aad120cSJose E. Roman   Output Parameters:
789fbdc3dfeSToby Isaac - p - an array containing the evaluations of the Jacobi polynomials's jets on the points.  the size is (degree + 1) x
790fbdc3dfeSToby Isaac   (k + 1) x npoints, which also describes the order of the dimensions of this three-dimensional array: the first
791fbdc3dfeSToby Isaac   (slowest varying) dimension is polynomial degree; the second dimension is derivative order; the third (fastest
792fbdc3dfeSToby Isaac   varying) dimension is the index of the evaluation point.
793fbdc3dfeSToby Isaac 
794fbdc3dfeSToby Isaac   Level: advanced
795fbdc3dfeSToby Isaac 
796db781477SPatrick Sanan .seealso: `PetscDTJacobiEval()`, `PetscDTPKDEvalJet()`
797fbdc3dfeSToby Isaac @*/
798*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTJacobiEvalJet(PetscReal alpha, PetscReal beta, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt k, PetscReal p[])
799*d71ae5a4SJacob Faibussowitsch {
800fbdc3dfeSToby Isaac   PetscInt   i, j, l;
801fbdc3dfeSToby Isaac   PetscInt  *degrees;
802fbdc3dfeSToby Isaac   PetscReal *psingle;
803fbdc3dfeSToby Isaac 
804fbdc3dfeSToby Isaac   PetscFunctionBegin;
805fbdc3dfeSToby Isaac   if (degree == 0) {
806fbdc3dfeSToby Isaac     PetscInt zero = 0;
807fbdc3dfeSToby Isaac 
80848a46eb9SPierre Jolivet     for (i = 0; i <= k; i++) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, i, points, 1, &zero, &p[i * npoints]));
809fbdc3dfeSToby Isaac     PetscFunctionReturn(0);
810fbdc3dfeSToby Isaac   }
8119566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(degree + 1, &degrees));
8129566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1((degree + 1) * npoints, &psingle));
813fbdc3dfeSToby Isaac   for (i = 0; i <= degree; i++) degrees[i] = i;
814fbdc3dfeSToby Isaac   for (i = 0; i <= k; i++) {
8159566063dSJacob Faibussowitsch     PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, i, points, degree + 1, degrees, psingle));
816fbdc3dfeSToby Isaac     for (j = 0; j <= degree; j++) {
817ad540459SPierre Jolivet       for (l = 0; l < npoints; l++) p[(j * (k + 1) + i) * npoints + l] = psingle[l * (degree + 1) + j];
818fbdc3dfeSToby Isaac     }
819fbdc3dfeSToby Isaac   }
8209566063dSJacob Faibussowitsch   PetscCall(PetscFree(psingle));
8219566063dSJacob Faibussowitsch   PetscCall(PetscFree(degrees));
822fbdc3dfeSToby Isaac   PetscFunctionReturn(0);
823fbdc3dfeSToby Isaac }
824fbdc3dfeSToby Isaac 
825fbdc3dfeSToby Isaac /*@
82694e21283SToby Isaac    PetscDTJacobiEval - evaluate Jacobi polynomials for the weight function $(1.+x)^{\alpha} (1.-x)^{\beta}$
82794e21283SToby Isaac                        at points
82894e21283SToby Isaac 
82994e21283SToby Isaac    Not Collective
83094e21283SToby Isaac 
8314165533cSJose E. Roman    Input Parameters:
83294e21283SToby Isaac +  npoints - number of spatial points to evaluate at
83394e21283SToby Isaac .  alpha - the left exponent > -1
83494e21283SToby Isaac .  beta - the right exponent > -1
83594e21283SToby Isaac .  points - array of locations to evaluate at
83694e21283SToby Isaac .  ndegree - number of basis degrees to evaluate
83794e21283SToby Isaac -  degrees - sorted array of degrees to evaluate
83894e21283SToby Isaac 
8394165533cSJose E. Roman    Output Parameters:
84094e21283SToby Isaac +  B - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension npoints*ndegrees, allocated by caller) (or NULL)
84194e21283SToby Isaac .  D - row-oriented derivative evaluation matrix (or NULL)
84294e21283SToby Isaac -  D2 - row-oriented second derivative evaluation matrix (or NULL)
84394e21283SToby Isaac 
84494e21283SToby Isaac    Level: intermediate
84594e21283SToby Isaac 
846db781477SPatrick Sanan .seealso: `PetscDTGaussQuadrature()`
84794e21283SToby Isaac @*/
848*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTJacobiEval(PetscInt npoints, PetscReal alpha, PetscReal beta, const PetscReal *points, PetscInt ndegree, const PetscInt *degrees, PetscReal *B, PetscReal *D, PetscReal *D2)
849*d71ae5a4SJacob Faibussowitsch {
85094e21283SToby Isaac   PetscFunctionBegin;
85108401ef6SPierre Jolivet   PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "alpha must be > -1.");
85208401ef6SPierre Jolivet   PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "beta must be > -1.");
85394e21283SToby Isaac   if (!npoints || !ndegree) PetscFunctionReturn(0);
8549566063dSJacob Faibussowitsch   if (B) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, 0, points, ndegree, degrees, B));
8559566063dSJacob Faibussowitsch   if (D) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, 1, points, ndegree, degrees, D));
8569566063dSJacob Faibussowitsch   if (D2) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, 2, points, ndegree, degrees, D2));
85794e21283SToby Isaac   PetscFunctionReturn(0);
85894e21283SToby Isaac }
85994e21283SToby Isaac 
86094e21283SToby Isaac /*@
86194e21283SToby Isaac    PetscDTLegendreEval - evaluate Legendre polynomials at points
86237045ce4SJed Brown 
86337045ce4SJed Brown    Not Collective
86437045ce4SJed Brown 
8654165533cSJose E. Roman    Input Parameters:
86637045ce4SJed Brown +  npoints - number of spatial points to evaluate at
86737045ce4SJed Brown .  points - array of locations to evaluate at
86837045ce4SJed Brown .  ndegree - number of basis degrees to evaluate
86937045ce4SJed Brown -  degrees - sorted array of degrees to evaluate
87037045ce4SJed Brown 
8714165533cSJose E. Roman    Output Parameters:
8720298fd71SBarry Smith +  B - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension npoints*ndegrees, allocated by caller) (or NULL)
8730298fd71SBarry Smith .  D - row-oriented derivative evaluation matrix (or NULL)
8740298fd71SBarry Smith -  D2 - row-oriented second derivative evaluation matrix (or NULL)
87537045ce4SJed Brown 
87637045ce4SJed Brown    Level: intermediate
87737045ce4SJed Brown 
878db781477SPatrick Sanan .seealso: `PetscDTGaussQuadrature()`
87937045ce4SJed Brown @*/
880*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTLegendreEval(PetscInt npoints, const PetscReal *points, PetscInt ndegree, const PetscInt *degrees, PetscReal *B, PetscReal *D, PetscReal *D2)
881*d71ae5a4SJacob Faibussowitsch {
88237045ce4SJed Brown   PetscFunctionBegin;
8839566063dSJacob Faibussowitsch   PetscCall(PetscDTJacobiEval(npoints, 0., 0., points, ndegree, degrees, B, D, D2));
88437045ce4SJed Brown   PetscFunctionReturn(0);
88537045ce4SJed Brown }
88637045ce4SJed Brown 
887fbdc3dfeSToby Isaac /*@
888fbdc3dfeSToby Isaac   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, then the index of x is smaller than the index of y)
889fbdc3dfeSToby Isaac 
890fbdc3dfeSToby Isaac   Input Parameters:
891fbdc3dfeSToby Isaac + len - the desired length of the degree tuple
892fbdc3dfeSToby Isaac - index - the index to convert: should be >= 0
893fbdc3dfeSToby Isaac 
894fbdc3dfeSToby Isaac   Output Parameter:
895fbdc3dfeSToby Isaac . degtup - will be filled with a tuple of degrees
896fbdc3dfeSToby Isaac 
897fbdc3dfeSToby Isaac   Level: beginner
898fbdc3dfeSToby Isaac 
899fbdc3dfeSToby Isaac   Note: for two tuples x and y with the same degree sum, partial degree sums over the final elements of the tuples
900fbdc3dfeSToby Isaac   acts as a tiebreaker.  For example, (2, 1, 1) and (1, 2, 1) have the same degree sum, but the degree sum over the
901fbdc3dfeSToby Isaac   last two elements is smaller for the former, so (2, 1, 1) < (1, 2, 1).
902fbdc3dfeSToby Isaac 
903db781477SPatrick Sanan .seealso: `PetscDTGradedOrderToIndex()`
904fbdc3dfeSToby Isaac @*/
905*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTIndexToGradedOrder(PetscInt len, PetscInt index, PetscInt degtup[])
906*d71ae5a4SJacob Faibussowitsch {
907fbdc3dfeSToby Isaac   PetscInt i, total;
908fbdc3dfeSToby Isaac   PetscInt sum;
909fbdc3dfeSToby Isaac 
910fbdc3dfeSToby Isaac   PetscFunctionBeginHot;
91108401ef6SPierre Jolivet   PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
91208401ef6SPierre Jolivet   PetscCheck(index >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index must be non-negative");
913fbdc3dfeSToby Isaac   total = 1;
914fbdc3dfeSToby Isaac   sum   = 0;
915fbdc3dfeSToby Isaac   while (index >= total) {
916fbdc3dfeSToby Isaac     index -= total;
917fbdc3dfeSToby Isaac     total = (total * (len + sum)) / (sum + 1);
918fbdc3dfeSToby Isaac     sum++;
919fbdc3dfeSToby Isaac   }
920fbdc3dfeSToby Isaac   for (i = 0; i < len; i++) {
921fbdc3dfeSToby Isaac     PetscInt c;
922fbdc3dfeSToby Isaac 
923fbdc3dfeSToby Isaac     degtup[i] = sum;
924fbdc3dfeSToby Isaac     for (c = 0, total = 1; c < sum; c++) {
925fbdc3dfeSToby Isaac       /* going into the loop, total is the number of way to have a tuple of sum exactly c with length len - 1 - i */
926fbdc3dfeSToby Isaac       if (index < total) break;
927fbdc3dfeSToby Isaac       index -= total;
928fbdc3dfeSToby Isaac       total = (total * (len - 1 - i + c)) / (c + 1);
929fbdc3dfeSToby Isaac       degtup[i]--;
930fbdc3dfeSToby Isaac     }
931fbdc3dfeSToby Isaac     sum -= degtup[i];
932fbdc3dfeSToby Isaac   }
933fbdc3dfeSToby Isaac   PetscFunctionReturn(0);
934fbdc3dfeSToby Isaac }
935fbdc3dfeSToby Isaac 
936fbdc3dfeSToby Isaac /*@
937fbdc3dfeSToby Isaac   PetscDTGradedOrderToIndex - convert a tuple into an index in a graded order, the inverse of PetscDTIndexToGradedOrder().
938fbdc3dfeSToby Isaac 
939fbdc3dfeSToby Isaac   Input Parameters:
940fbdc3dfeSToby Isaac + len - the length of the degree tuple
941fbdc3dfeSToby Isaac - degtup - tuple with this length
942fbdc3dfeSToby Isaac 
943fbdc3dfeSToby Isaac   Output Parameter:
944fbdc3dfeSToby Isaac . index - index in graded order: >= 0
945fbdc3dfeSToby Isaac 
946fbdc3dfeSToby Isaac   Level: Beginner
947fbdc3dfeSToby Isaac 
948fbdc3dfeSToby Isaac   Note: for two tuples x and y with the same degree sum, partial degree sums over the final elements of the tuples
949fbdc3dfeSToby Isaac   acts as a tiebreaker.  For example, (2, 1, 1) and (1, 2, 1) have the same degree sum, but the degree sum over the
950fbdc3dfeSToby Isaac   last two elements is smaller for the former, so (2, 1, 1) < (1, 2, 1).
951fbdc3dfeSToby Isaac 
952db781477SPatrick Sanan .seealso: `PetscDTIndexToGradedOrder()`
953fbdc3dfeSToby Isaac @*/
954*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTGradedOrderToIndex(PetscInt len, const PetscInt degtup[], PetscInt *index)
955*d71ae5a4SJacob Faibussowitsch {
956fbdc3dfeSToby Isaac   PetscInt i, idx, sum, total;
957fbdc3dfeSToby Isaac 
958fbdc3dfeSToby Isaac   PetscFunctionBeginHot;
95908401ef6SPierre Jolivet   PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
960fbdc3dfeSToby Isaac   for (i = 0, sum = 0; i < len; i++) sum += degtup[i];
961fbdc3dfeSToby Isaac   idx   = 0;
962fbdc3dfeSToby Isaac   total = 1;
963fbdc3dfeSToby Isaac   for (i = 0; i < sum; i++) {
964fbdc3dfeSToby Isaac     idx += total;
965fbdc3dfeSToby Isaac     total = (total * (len + i)) / (i + 1);
966fbdc3dfeSToby Isaac   }
967fbdc3dfeSToby Isaac   for (i = 0; i < len - 1; i++) {
968fbdc3dfeSToby Isaac     PetscInt c;
969fbdc3dfeSToby Isaac 
970fbdc3dfeSToby Isaac     total = 1;
971fbdc3dfeSToby Isaac     sum -= degtup[i];
972fbdc3dfeSToby Isaac     for (c = 0; c < sum; c++) {
973fbdc3dfeSToby Isaac       idx += total;
974fbdc3dfeSToby Isaac       total = (total * (len - 1 - i + c)) / (c + 1);
975fbdc3dfeSToby Isaac     }
976fbdc3dfeSToby Isaac   }
977fbdc3dfeSToby Isaac   *index = idx;
978fbdc3dfeSToby Isaac   PetscFunctionReturn(0);
979fbdc3dfeSToby Isaac }
980fbdc3dfeSToby Isaac 
981e3aa2e09SToby Isaac static PetscBool PKDCite       = PETSC_FALSE;
982e3aa2e09SToby Isaac const char       PKDCitation[] = "@article{Kirby2010,\n"
983e3aa2e09SToby Isaac                                  "  title={Singularity-free evaluation of collapsed-coordinate orthogonal polynomials},\n"
984e3aa2e09SToby Isaac                                  "  author={Kirby, Robert C},\n"
985e3aa2e09SToby Isaac                                  "  journal={ACM Transactions on Mathematical Software (TOMS)},\n"
986e3aa2e09SToby Isaac                                  "  volume={37},\n"
987e3aa2e09SToby Isaac                                  "  number={1},\n"
988e3aa2e09SToby Isaac                                  "  pages={1--16},\n"
989e3aa2e09SToby Isaac                                  "  year={2010},\n"
990e3aa2e09SToby Isaac                                  "  publisher={ACM New York, NY, USA}\n}\n";
991e3aa2e09SToby Isaac 
992fbdc3dfeSToby Isaac /*@
993d8f25ad8SToby Isaac   PetscDTPKDEvalJet - Evaluate the jet (function and derivatives) of the Proriol-Koornwinder-Dubiner (PKD) basis for
994fbdc3dfeSToby Isaac   the space of polynomials up to a given degree.  The PKD basis is L2-orthonormal on the biunit simplex (which is used
995fbdc3dfeSToby Isaac   as the reference element for finite elements in PETSc), which makes it a stable basis to use for evaluating
996fbdc3dfeSToby Isaac   polynomials in that domain.
997fbdc3dfeSToby Isaac 
9984165533cSJose E. Roman   Input Parameters:
999fbdc3dfeSToby Isaac + dim - the number of variables in the multivariate polynomials
1000fbdc3dfeSToby Isaac . npoints - the number of points to evaluate the polynomials at
1001fbdc3dfeSToby Isaac . points - [npoints x dim] array of point coordinates
1002fbdc3dfeSToby Isaac . 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.
1003fbdc3dfeSToby Isaac - k - the maximum order partial derivative to evaluate in the jet.  There are (dim + k choose dim) partial derivatives
1004fbdc3dfeSToby Isaac   in the jet.  Choosing k = 0 means to evaluate just the function and no derivatives
1005fbdc3dfeSToby Isaac 
10066aad120cSJose E. Roman   Output Parameters:
1007fbdc3dfeSToby Isaac - p - an array containing the evaluations of the PKD polynomials' jets on the points.  The size is ((dim + degree)
1008fbdc3dfeSToby Isaac   choose dim) x ((dim + k) choose dim) x npoints, which also describes the order of the dimensions of this
1009fbdc3dfeSToby Isaac   three-dimensional array: the first (slowest varying) dimension is basis function index; the second dimension is jet
1010fbdc3dfeSToby Isaac   index; the third (fastest varying) dimension is the index of the evaluation point.
1011fbdc3dfeSToby Isaac 
1012fbdc3dfeSToby Isaac   Level: advanced
1013fbdc3dfeSToby Isaac 
1014fbdc3dfeSToby Isaac   Note: The ordering of the basis functions, and the ordering of the derivatives in the jet, both follow the graded
1015fbdc3dfeSToby Isaac   ordering of PetscDTIndexToGradedOrder() and PetscDTGradedOrderToIndex().  For example, in 3D, the polynomial with
1016d8f25ad8SToby Isaac   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);
1017fbdc3dfeSToby Isaac   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).
1018fbdc3dfeSToby Isaac 
1019e3aa2e09SToby Isaac   The implementation uses Kirby's singularity-free evaluation algorithm, https://doi.org/10.1145/1644001.1644006.
1020e3aa2e09SToby Isaac 
1021db781477SPatrick Sanan .seealso: `PetscDTGradedOrderToIndex()`, `PetscDTIndexToGradedOrder()`, `PetscDTJacobiEvalJet()`
1022fbdc3dfeSToby Isaac @*/
1023*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTPKDEvalJet(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt k, PetscReal p[])
1024*d71ae5a4SJacob Faibussowitsch {
1025fbdc3dfeSToby Isaac   PetscInt   degidx, kidx, d, pt;
1026fbdc3dfeSToby Isaac   PetscInt   Nk, Ndeg;
1027fbdc3dfeSToby Isaac   PetscInt  *ktup, *degtup;
1028fbdc3dfeSToby Isaac   PetscReal *scales, initscale, scaleexp;
1029fbdc3dfeSToby Isaac 
1030fbdc3dfeSToby Isaac   PetscFunctionBegin;
10319566063dSJacob Faibussowitsch   PetscCall(PetscCitationsRegister(PKDCitation, &PKDCite));
10329566063dSJacob Faibussowitsch   PetscCall(PetscDTBinomialInt(dim + k, k, &Nk));
10339566063dSJacob Faibussowitsch   PetscCall(PetscDTBinomialInt(degree + dim, degree, &Ndeg));
10349566063dSJacob Faibussowitsch   PetscCall(PetscMalloc2(dim, &degtup, dim, &ktup));
10359566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(Ndeg, &scales));
1036fbdc3dfeSToby Isaac   initscale = 1.;
1037fbdc3dfeSToby Isaac   if (dim > 1) {
10389566063dSJacob Faibussowitsch     PetscCall(PetscDTBinomial(dim, 2, &scaleexp));
10392f613bf5SBarry Smith     initscale = PetscPowReal(2., scaleexp * 0.5);
1040fbdc3dfeSToby Isaac   }
1041fbdc3dfeSToby Isaac   for (degidx = 0; degidx < Ndeg; degidx++) {
1042fbdc3dfeSToby Isaac     PetscInt  e, i;
1043fbdc3dfeSToby Isaac     PetscInt  m1idx = -1, m2idx = -1;
1044fbdc3dfeSToby Isaac     PetscInt  n;
1045fbdc3dfeSToby Isaac     PetscInt  degsum;
1046fbdc3dfeSToby Isaac     PetscReal alpha;
1047fbdc3dfeSToby Isaac     PetscReal cnm1, cnm1x, cnm2;
1048fbdc3dfeSToby Isaac     PetscReal norm;
1049fbdc3dfeSToby Isaac 
10509566063dSJacob Faibussowitsch     PetscCall(PetscDTIndexToGradedOrder(dim, degidx, degtup));
10519371c9d4SSatish Balay     for (d = dim - 1; d >= 0; d--)
10529371c9d4SSatish Balay       if (degtup[d]) break;
1053fbdc3dfeSToby Isaac     if (d < 0) { /* constant is 1 everywhere, all derivatives are zero */
1054fbdc3dfeSToby Isaac       scales[degidx] = initscale;
1055fbdc3dfeSToby Isaac       for (e = 0; e < dim; e++) {
10569566063dSJacob Faibussowitsch         PetscCall(PetscDTJacobiNorm(e, 0., 0, &norm));
1057fbdc3dfeSToby Isaac         scales[degidx] /= norm;
1058fbdc3dfeSToby Isaac       }
1059fbdc3dfeSToby Isaac       for (i = 0; i < npoints; i++) p[degidx * Nk * npoints + i] = 1.;
1060fbdc3dfeSToby Isaac       for (i = 0; i < (Nk - 1) * npoints; i++) p[(degidx * Nk + 1) * npoints + i] = 0.;
1061fbdc3dfeSToby Isaac       continue;
1062fbdc3dfeSToby Isaac     }
1063fbdc3dfeSToby Isaac     n = degtup[d];
1064fbdc3dfeSToby Isaac     degtup[d]--;
10659566063dSJacob Faibussowitsch     PetscCall(PetscDTGradedOrderToIndex(dim, degtup, &m1idx));
1066fbdc3dfeSToby Isaac     if (degtup[d] > 0) {
1067fbdc3dfeSToby Isaac       degtup[d]--;
10689566063dSJacob Faibussowitsch       PetscCall(PetscDTGradedOrderToIndex(dim, degtup, &m2idx));
1069fbdc3dfeSToby Isaac       degtup[d]++;
1070fbdc3dfeSToby Isaac     }
1071fbdc3dfeSToby Isaac     degtup[d]++;
1072fbdc3dfeSToby Isaac     for (e = 0, degsum = 0; e < d; e++) degsum += degtup[e];
1073fbdc3dfeSToby Isaac     alpha = 2 * degsum + d;
1074fbdc3dfeSToby Isaac     PetscDTJacobiRecurrence_Internal(n, alpha, 0., cnm1, cnm1x, cnm2);
1075fbdc3dfeSToby Isaac 
1076fbdc3dfeSToby Isaac     scales[degidx] = initscale;
1077fbdc3dfeSToby Isaac     for (e = 0, degsum = 0; e < dim; e++) {
1078fbdc3dfeSToby Isaac       PetscInt  f;
1079fbdc3dfeSToby Isaac       PetscReal ealpha;
1080fbdc3dfeSToby Isaac       PetscReal enorm;
1081fbdc3dfeSToby Isaac 
1082fbdc3dfeSToby Isaac       ealpha = 2 * degsum + e;
1083fbdc3dfeSToby Isaac       for (f = 0; f < degsum; f++) scales[degidx] *= 2.;
10849566063dSJacob Faibussowitsch       PetscCall(PetscDTJacobiNorm(ealpha, 0., degtup[e], &enorm));
1085fbdc3dfeSToby Isaac       scales[degidx] /= enorm;
1086fbdc3dfeSToby Isaac       degsum += degtup[e];
1087fbdc3dfeSToby Isaac     }
1088fbdc3dfeSToby Isaac 
1089fbdc3dfeSToby Isaac     for (pt = 0; pt < npoints; pt++) {
1090fbdc3dfeSToby Isaac       /* compute the multipliers */
1091fbdc3dfeSToby Isaac       PetscReal thetanm1, thetanm1x, thetanm2;
1092fbdc3dfeSToby Isaac 
1093fbdc3dfeSToby Isaac       thetanm1x = dim - (d + 1) + 2. * points[pt * dim + d];
1094fbdc3dfeSToby Isaac       for (e = d + 1; e < dim; e++) thetanm1x += points[pt * dim + e];
1095fbdc3dfeSToby Isaac       thetanm1x *= 0.5;
1096fbdc3dfeSToby Isaac       thetanm1 = (2. - (dim - (d + 1)));
1097fbdc3dfeSToby Isaac       for (e = d + 1; e < dim; e++) thetanm1 -= points[pt * dim + e];
1098fbdc3dfeSToby Isaac       thetanm1 *= 0.5;
1099fbdc3dfeSToby Isaac       thetanm2 = thetanm1 * thetanm1;
1100fbdc3dfeSToby Isaac 
1101fbdc3dfeSToby Isaac       for (kidx = 0; kidx < Nk; kidx++) {
1102fbdc3dfeSToby Isaac         PetscInt f;
1103fbdc3dfeSToby Isaac 
11049566063dSJacob Faibussowitsch         PetscCall(PetscDTIndexToGradedOrder(dim, kidx, ktup));
1105fbdc3dfeSToby Isaac         /* first sum in the same derivative terms */
1106fbdc3dfeSToby Isaac         p[(degidx * Nk + kidx) * npoints + pt] = (cnm1 * thetanm1 + cnm1x * thetanm1x) * p[(m1idx * Nk + kidx) * npoints + pt];
1107ad540459SPierre Jolivet         if (m2idx >= 0) p[(degidx * Nk + kidx) * npoints + pt] -= cnm2 * thetanm2 * p[(m2idx * Nk + kidx) * npoints + pt];
1108fbdc3dfeSToby Isaac 
1109fbdc3dfeSToby Isaac         for (f = d; f < dim; f++) {
1110fbdc3dfeSToby Isaac           PetscInt km1idx, mplty = ktup[f];
1111fbdc3dfeSToby Isaac 
1112fbdc3dfeSToby Isaac           if (!mplty) continue;
1113fbdc3dfeSToby Isaac           ktup[f]--;
11149566063dSJacob Faibussowitsch           PetscCall(PetscDTGradedOrderToIndex(dim, ktup, &km1idx));
1115fbdc3dfeSToby Isaac 
1116fbdc3dfeSToby Isaac           /* the derivative of  cnm1x * thetanm1x  wrt x variable f is 0.5 * cnm1x if f > d otherwise it is cnm1x */
1117fbdc3dfeSToby Isaac           /* the derivative of  cnm1  * thetanm1   wrt x variable f is 0 if f == d, otherwise it is -0.5 * cnm1 */
1118fbdc3dfeSToby Isaac           /* the derivative of -cnm2  * thetanm2   wrt x variable f is 0 if f == d, otherwise it is cnm2 * thetanm1 */
1119fbdc3dfeSToby Isaac           if (f > d) {
1120fbdc3dfeSToby Isaac             PetscInt f2;
1121fbdc3dfeSToby Isaac 
1122fbdc3dfeSToby Isaac             p[(degidx * Nk + kidx) * npoints + pt] += mplty * 0.5 * (cnm1x - cnm1) * p[(m1idx * Nk + km1idx) * npoints + pt];
1123fbdc3dfeSToby Isaac             if (m2idx >= 0) {
1124fbdc3dfeSToby Isaac               p[(degidx * Nk + kidx) * npoints + pt] += mplty * cnm2 * thetanm1 * p[(m2idx * Nk + km1idx) * npoints + pt];
1125fbdc3dfeSToby Isaac               /* second derivatives of -cnm2  * thetanm2   wrt x variable f,f2 is like - 0.5 * cnm2 */
1126fbdc3dfeSToby Isaac               for (f2 = f; f2 < dim; f2++) {
1127fbdc3dfeSToby Isaac                 PetscInt km2idx, mplty2 = ktup[f2];
1128fbdc3dfeSToby Isaac                 PetscInt factor;
1129fbdc3dfeSToby Isaac 
1130fbdc3dfeSToby Isaac                 if (!mplty2) continue;
1131fbdc3dfeSToby Isaac                 ktup[f2]--;
11329566063dSJacob Faibussowitsch                 PetscCall(PetscDTGradedOrderToIndex(dim, ktup, &km2idx));
1133fbdc3dfeSToby Isaac 
1134fbdc3dfeSToby Isaac                 factor = mplty * mplty2;
1135fbdc3dfeSToby Isaac                 if (f == f2) factor /= 2;
1136fbdc3dfeSToby Isaac                 p[(degidx * Nk + kidx) * npoints + pt] -= 0.5 * factor * cnm2 * p[(m2idx * Nk + km2idx) * npoints + pt];
1137fbdc3dfeSToby Isaac                 ktup[f2]++;
1138fbdc3dfeSToby Isaac               }
11393034baaeSToby Isaac             }
1140fbdc3dfeSToby Isaac           } else {
1141fbdc3dfeSToby Isaac             p[(degidx * Nk + kidx) * npoints + pt] += mplty * cnm1x * p[(m1idx * Nk + km1idx) * npoints + pt];
1142fbdc3dfeSToby Isaac           }
1143fbdc3dfeSToby Isaac           ktup[f]++;
1144fbdc3dfeSToby Isaac         }
1145fbdc3dfeSToby Isaac       }
1146fbdc3dfeSToby Isaac     }
1147fbdc3dfeSToby Isaac   }
1148fbdc3dfeSToby Isaac   for (degidx = 0; degidx < Ndeg; degidx++) {
1149fbdc3dfeSToby Isaac     PetscReal scale = scales[degidx];
1150fbdc3dfeSToby Isaac     PetscInt  i;
1151fbdc3dfeSToby Isaac 
1152fbdc3dfeSToby Isaac     for (i = 0; i < Nk * npoints; i++) p[degidx * Nk * npoints + i] *= scale;
1153fbdc3dfeSToby Isaac   }
11549566063dSJacob Faibussowitsch   PetscCall(PetscFree(scales));
11559566063dSJacob Faibussowitsch   PetscCall(PetscFree2(degtup, ktup));
1156fbdc3dfeSToby Isaac   PetscFunctionReturn(0);
1157fbdc3dfeSToby Isaac }
1158fbdc3dfeSToby Isaac 
1159d8f25ad8SToby Isaac /*@
1160d8f25ad8SToby Isaac   PetscDTPTrimmedSize - The size of the trimmed polynomial space of k-forms with a given degree and form degree,
1161d8f25ad8SToby Isaac   which can be evaluated in PetscDTPTrimmedEvalJet().
1162d8f25ad8SToby Isaac 
1163d8f25ad8SToby Isaac   Input Parameters:
1164d8f25ad8SToby Isaac + dim - the number of variables in the multivariate polynomials
1165d8f25ad8SToby Isaac . degree - the degree (sum of degrees on the variables in a monomial) of the trimmed polynomial space.
1166d8f25ad8SToby Isaac - formDegree - the degree of the form
1167d8f25ad8SToby Isaac 
11686aad120cSJose E. Roman   Output Parameters:
1169d8f25ad8SToby Isaac - size - The number ((dim + degree) choose (dim + formDegree)) x ((degree + formDegree - 1) choose (formDegree))
1170d8f25ad8SToby Isaac 
1171d8f25ad8SToby Isaac   Level: advanced
1172d8f25ad8SToby Isaac 
1173db781477SPatrick Sanan .seealso: `PetscDTPTrimmedEvalJet()`
1174d8f25ad8SToby Isaac @*/
1175*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTPTrimmedSize(PetscInt dim, PetscInt degree, PetscInt formDegree, PetscInt *size)
1176*d71ae5a4SJacob Faibussowitsch {
1177d8f25ad8SToby Isaac   PetscInt Nrk, Nbpt; // number of trimmed polynomials
1178d8f25ad8SToby Isaac 
1179d8f25ad8SToby Isaac   PetscFunctionBegin;
1180d8f25ad8SToby Isaac   formDegree = PetscAbsInt(formDegree);
11819566063dSJacob Faibussowitsch   PetscCall(PetscDTBinomialInt(degree + dim, degree + formDegree, &Nbpt));
11829566063dSJacob Faibussowitsch   PetscCall(PetscDTBinomialInt(degree + formDegree - 1, formDegree, &Nrk));
1183d8f25ad8SToby Isaac   Nbpt *= Nrk;
1184d8f25ad8SToby Isaac   *size = Nbpt;
1185d8f25ad8SToby Isaac   PetscFunctionReturn(0);
1186d8f25ad8SToby Isaac }
1187d8f25ad8SToby Isaac 
1188d8f25ad8SToby Isaac /* there was a reference implementation based on section 4.4 of Arnold, Falk & Winther (acta numerica, 2006), but it
1189d8f25ad8SToby Isaac  * was inferior to this implementation */
1190*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTPTrimmedEvalJet_Internal(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt formDegree, PetscInt jetDegree, PetscReal p[])
1191*d71ae5a4SJacob Faibussowitsch {
1192d8f25ad8SToby Isaac   PetscInt  formDegreeOrig = formDegree;
1193d8f25ad8SToby Isaac   PetscBool formNegative   = (formDegreeOrig < 0) ? PETSC_TRUE : PETSC_FALSE;
1194d8f25ad8SToby Isaac 
1195d8f25ad8SToby Isaac   PetscFunctionBegin;
1196d8f25ad8SToby Isaac   formDegree = PetscAbsInt(formDegreeOrig);
1197d8f25ad8SToby Isaac   if (formDegree == 0) {
11989566063dSJacob Faibussowitsch     PetscCall(PetscDTPKDEvalJet(dim, npoints, points, degree, jetDegree, p));
1199d8f25ad8SToby Isaac     PetscFunctionReturn(0);
1200d8f25ad8SToby Isaac   }
1201d8f25ad8SToby Isaac   if (formDegree == dim) {
12029566063dSJacob Faibussowitsch     PetscCall(PetscDTPKDEvalJet(dim, npoints, points, degree - 1, jetDegree, p));
1203d8f25ad8SToby Isaac     PetscFunctionReturn(0);
1204d8f25ad8SToby Isaac   }
1205d8f25ad8SToby Isaac   PetscInt Nbpt;
12069566063dSJacob Faibussowitsch   PetscCall(PetscDTPTrimmedSize(dim, degree, formDegree, &Nbpt));
1207d8f25ad8SToby Isaac   PetscInt Nf;
12089566063dSJacob Faibussowitsch   PetscCall(PetscDTBinomialInt(dim, formDegree, &Nf));
1209d8f25ad8SToby Isaac   PetscInt Nk;
12109566063dSJacob Faibussowitsch   PetscCall(PetscDTBinomialInt(dim + jetDegree, dim, &Nk));
12119566063dSJacob Faibussowitsch   PetscCall(PetscArrayzero(p, Nbpt * Nf * Nk * npoints));
1212d8f25ad8SToby Isaac 
1213d8f25ad8SToby Isaac   PetscInt Nbpm1; // number of scalar polynomials up to degree - 1;
12149566063dSJacob Faibussowitsch   PetscCall(PetscDTBinomialInt(dim + degree - 1, dim, &Nbpm1));
1215d8f25ad8SToby Isaac   PetscReal *p_scalar;
12169566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(Nbpm1 * Nk * npoints, &p_scalar));
12179566063dSJacob Faibussowitsch   PetscCall(PetscDTPKDEvalJet(dim, npoints, points, degree - 1, jetDegree, p_scalar));
1218d8f25ad8SToby Isaac   PetscInt total = 0;
1219d8f25ad8SToby Isaac   // First add the full polynomials up to degree - 1 into the basis: take the scalar
1220d8f25ad8SToby Isaac   // and copy one for each form component
1221d8f25ad8SToby Isaac   for (PetscInt i = 0; i < Nbpm1; i++) {
1222d8f25ad8SToby Isaac     const PetscReal *src = &p_scalar[i * Nk * npoints];
1223d8f25ad8SToby Isaac     for (PetscInt f = 0; f < Nf; f++) {
1224d8f25ad8SToby Isaac       PetscReal *dest = &p[(total++ * Nf + f) * Nk * npoints];
12259566063dSJacob Faibussowitsch       PetscCall(PetscArraycpy(dest, src, Nk * npoints));
1226d8f25ad8SToby Isaac     }
1227d8f25ad8SToby Isaac   }
1228d8f25ad8SToby Isaac   PetscInt *form_atoms;
12299566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(formDegree + 1, &form_atoms));
1230d8f25ad8SToby Isaac   // construct the interior product pattern
1231d8f25ad8SToby Isaac   PetscInt(*pattern)[3];
1232d8f25ad8SToby Isaac   PetscInt Nf1; // number of formDegree + 1 forms
12339566063dSJacob Faibussowitsch   PetscCall(PetscDTBinomialInt(dim, formDegree + 1, &Nf1));
1234d8f25ad8SToby Isaac   PetscInt nnz = Nf1 * (formDegree + 1);
12359566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(Nf1 * (formDegree + 1), &pattern));
12369566063dSJacob Faibussowitsch   PetscCall(PetscDTAltVInteriorPattern(dim, formDegree + 1, pattern));
1237d8f25ad8SToby Isaac   PetscReal centroid = (1. - dim) / (dim + 1.);
1238d8f25ad8SToby Isaac   PetscInt *deriv;
12399566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(dim, &deriv));
1240d8f25ad8SToby Isaac   for (PetscInt d = dim; d >= formDegree + 1; d--) {
1241d8f25ad8SToby Isaac     PetscInt Nfd1; // number of formDegree + 1 forms in dimension d that include dx_0
1242d8f25ad8SToby Isaac                    // (equal to the number of formDegree forms in dimension d-1)
12439566063dSJacob Faibussowitsch     PetscCall(PetscDTBinomialInt(d - 1, formDegree, &Nfd1));
1244d8f25ad8SToby Isaac     // The number of homogeneous (degree-1) scalar polynomials in d variables
1245d8f25ad8SToby Isaac     PetscInt Nh;
12469566063dSJacob Faibussowitsch     PetscCall(PetscDTBinomialInt(d - 1 + degree - 1, d - 1, &Nh));
1247d8f25ad8SToby Isaac     const PetscReal *h_scalar = &p_scalar[(Nbpm1 - Nh) * Nk * npoints];
1248d8f25ad8SToby Isaac     for (PetscInt b = 0; b < Nh; b++) {
1249d8f25ad8SToby Isaac       const PetscReal *h_s = &h_scalar[b * Nk * npoints];
1250d8f25ad8SToby Isaac       for (PetscInt f = 0; f < Nfd1; f++) {
1251d8f25ad8SToby Isaac         // construct all formDegree+1 forms that start with dx_(dim - d) /\ ...
1252d8f25ad8SToby Isaac         form_atoms[0] = dim - d;
12539566063dSJacob Faibussowitsch         PetscCall(PetscDTEnumSubset(d - 1, formDegree, f, &form_atoms[1]));
1254ad540459SPierre Jolivet         for (PetscInt i = 0; i < formDegree; i++) form_atoms[1 + i] += form_atoms[0] + 1;
1255d8f25ad8SToby Isaac         PetscInt f_ind; // index of the resulting form
12569566063dSJacob Faibussowitsch         PetscCall(PetscDTSubsetIndex(dim, formDegree + 1, form_atoms, &f_ind));
1257d8f25ad8SToby Isaac         PetscReal *p_f = &p[total++ * Nf * Nk * npoints];
1258d8f25ad8SToby Isaac         for (PetscInt nz = 0; nz < nnz; nz++) {
1259d8f25ad8SToby Isaac           PetscInt  i     = pattern[nz][0]; // formDegree component
1260d8f25ad8SToby Isaac           PetscInt  j     = pattern[nz][1]; // (formDegree + 1) component
1261d8f25ad8SToby Isaac           PetscInt  v     = pattern[nz][2]; // coordinate component
1262d8f25ad8SToby Isaac           PetscReal scale = v < 0 ? -1. : 1.;
1263d8f25ad8SToby Isaac 
1264d8f25ad8SToby Isaac           i     = formNegative ? (Nf - 1 - i) : i;
1265d8f25ad8SToby Isaac           scale = (formNegative && (i & 1)) ? -scale : scale;
1266d8f25ad8SToby Isaac           v     = v < 0 ? -(v + 1) : v;
1267ad540459SPierre Jolivet           if (j != f_ind) continue;
1268d8f25ad8SToby Isaac           PetscReal *p_i = &p_f[i * Nk * npoints];
1269d8f25ad8SToby Isaac           for (PetscInt jet = 0; jet < Nk; jet++) {
1270d8f25ad8SToby Isaac             const PetscReal *h_jet = &h_s[jet * npoints];
1271d8f25ad8SToby Isaac             PetscReal       *p_jet = &p_i[jet * npoints];
1272d8f25ad8SToby Isaac 
1273ad540459SPierre Jolivet             for (PetscInt pt = 0; pt < npoints; pt++) p_jet[pt] += scale * h_jet[pt] * (points[pt * dim + v] - centroid);
12749566063dSJacob Faibussowitsch             PetscCall(PetscDTIndexToGradedOrder(dim, jet, deriv));
1275d8f25ad8SToby Isaac             deriv[v]++;
1276d8f25ad8SToby Isaac             PetscReal mult = deriv[v];
1277d8f25ad8SToby Isaac             PetscInt  l;
12789566063dSJacob Faibussowitsch             PetscCall(PetscDTGradedOrderToIndex(dim, deriv, &l));
1279ad540459SPierre Jolivet             if (l >= Nk) continue;
1280d8f25ad8SToby Isaac             p_jet = &p_i[l * npoints];
1281ad540459SPierre Jolivet             for (PetscInt pt = 0; pt < npoints; pt++) p_jet[pt] += scale * mult * h_jet[pt];
1282d8f25ad8SToby Isaac             deriv[v]--;
1283d8f25ad8SToby Isaac           }
1284d8f25ad8SToby Isaac         }
1285d8f25ad8SToby Isaac       }
1286d8f25ad8SToby Isaac     }
1287d8f25ad8SToby Isaac   }
128808401ef6SPierre Jolivet   PetscCheck(total == Nbpt, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Incorrectly counted P trimmed polynomials");
12899566063dSJacob Faibussowitsch   PetscCall(PetscFree(deriv));
12909566063dSJacob Faibussowitsch   PetscCall(PetscFree(pattern));
12919566063dSJacob Faibussowitsch   PetscCall(PetscFree(form_atoms));
12929566063dSJacob Faibussowitsch   PetscCall(PetscFree(p_scalar));
1293d8f25ad8SToby Isaac   PetscFunctionReturn(0);
1294d8f25ad8SToby Isaac }
1295d8f25ad8SToby Isaac 
1296d8f25ad8SToby Isaac /*@
1297d8f25ad8SToby Isaac   PetscDTPTrimmedEvalJet - Evaluate the jet (function and derivatives) of a basis of the trimmed polynomial k-forms up to
1298d8f25ad8SToby Isaac   a given degree.
1299d8f25ad8SToby Isaac 
1300d8f25ad8SToby Isaac   Input Parameters:
1301d8f25ad8SToby Isaac + dim - the number of variables in the multivariate polynomials
1302d8f25ad8SToby Isaac . npoints - the number of points to evaluate the polynomials at
1303d8f25ad8SToby Isaac . points - [npoints x dim] array of point coordinates
1304d8f25ad8SToby Isaac . degree - the degree (sum of degrees on the variables in a monomial) of the trimmed polynomial space to evaluate.
1305d8f25ad8SToby Isaac            There are ((dim + degree) choose (dim + formDegree)) x ((degree + formDegree - 1) choose (formDegree)) polynomials in this space.
1306d8f25ad8SToby Isaac            (You can use PetscDTPTrimmedSize() to compute this size.)
1307d8f25ad8SToby Isaac . formDegree - the degree of the form
1308d8f25ad8SToby Isaac - jetDegree - the maximum order partial derivative to evaluate in the jet.  There are ((dim + jetDegree) choose dim) partial derivatives
1309d8f25ad8SToby Isaac               in the jet.  Choosing jetDegree = 0 means to evaluate just the function and no derivatives
1310d8f25ad8SToby Isaac 
13116aad120cSJose E. Roman   Output Parameters:
1312d8f25ad8SToby Isaac - p - an array containing the evaluations of the PKD polynomials' jets on the points.  The size is
1313d8f25ad8SToby Isaac       PetscDTPTrimmedSize() x ((dim + formDegree) choose dim) x ((dim + k) choose dim) x npoints,
1314d8f25ad8SToby Isaac       which also describes the order of the dimensions of this
1315d8f25ad8SToby Isaac       four-dimensional array:
1316d8f25ad8SToby Isaac         the first (slowest varying) dimension is basis function index;
1317d8f25ad8SToby Isaac         the second dimension is component of the form;
1318d8f25ad8SToby Isaac         the third dimension is jet index;
1319d8f25ad8SToby Isaac         the fourth (fastest varying) dimension is the index of the evaluation point.
1320d8f25ad8SToby Isaac 
1321d8f25ad8SToby Isaac   Level: advanced
1322d8f25ad8SToby Isaac 
1323d8f25ad8SToby Isaac   Note: The ordering of the basis functions is not graded, so the basis functions are not nested by degree like PetscDTPKDEvalJet().
1324d8f25ad8SToby Isaac         The basis functions are not an L2-orthonormal basis on any particular domain.
1325d8f25ad8SToby Isaac 
1326d8f25ad8SToby Isaac   The implementation is based on the description of the trimmed polynomials up to degree r as
1327d8f25ad8SToby Isaac   the direct sum of polynomials up to degree (r-1) and the Koszul differential applied to
1328d8f25ad8SToby Isaac   homogeneous polynomials of degree (r-1).
1329d8f25ad8SToby Isaac 
1330db781477SPatrick Sanan .seealso: `PetscDTPKDEvalJet()`, `PetscDTPTrimmedSize()`
1331d8f25ad8SToby Isaac @*/
1332*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTPTrimmedEvalJet(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt formDegree, PetscInt jetDegree, PetscReal p[])
1333*d71ae5a4SJacob Faibussowitsch {
1334d8f25ad8SToby Isaac   PetscFunctionBegin;
13359566063dSJacob Faibussowitsch   PetscCall(PetscDTPTrimmedEvalJet_Internal(dim, npoints, points, degree, formDegree, jetDegree, p));
1336d8f25ad8SToby Isaac   PetscFunctionReturn(0);
1337d8f25ad8SToby Isaac }
1338d8f25ad8SToby Isaac 
1339e6a796c3SToby Isaac /* solve the symmetric tridiagonal eigenvalue system, writing the eigenvalues into eigs and the eigenvectors into V
1340e6a796c3SToby Isaac  * with lds n; diag and subdiag are overwritten */
1341*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTSymmetricTridiagonalEigensolve(PetscInt n, PetscReal diag[], PetscReal subdiag[], PetscReal eigs[], PetscScalar V[])
1342*d71ae5a4SJacob Faibussowitsch {
1343e6a796c3SToby Isaac   char          jobz   = 'V'; /* eigenvalues and eigenvectors */
1344e6a796c3SToby Isaac   char          range  = 'A'; /* all eigenvalues will be found */
1345e6a796c3SToby Isaac   PetscReal     VL     = 0.;  /* ignored because range is 'A' */
1346e6a796c3SToby Isaac   PetscReal     VU     = 0.;  /* ignored because range is 'A' */
1347e6a796c3SToby Isaac   PetscBLASInt  IL     = 0;   /* ignored because range is 'A' */
1348e6a796c3SToby Isaac   PetscBLASInt  IU     = 0;   /* ignored because range is 'A' */
1349e6a796c3SToby Isaac   PetscReal     abstol = 0.;  /* unused */
1350e6a796c3SToby Isaac   PetscBLASInt  bn, bm, ldz;  /* bm will equal bn on exit */
1351e6a796c3SToby Isaac   PetscBLASInt *isuppz;
1352e6a796c3SToby Isaac   PetscBLASInt  lwork, liwork;
1353e6a796c3SToby Isaac   PetscReal     workquery;
1354e6a796c3SToby Isaac   PetscBLASInt  iworkquery;
1355e6a796c3SToby Isaac   PetscBLASInt *iwork;
1356e6a796c3SToby Isaac   PetscBLASInt  info;
1357e6a796c3SToby Isaac   PetscReal    *work = NULL;
1358e6a796c3SToby Isaac 
1359e6a796c3SToby Isaac   PetscFunctionBegin;
1360e6a796c3SToby Isaac #if !defined(PETSCDTGAUSSIANQUADRATURE_EIG)
1361e6a796c3SToby Isaac   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
1362e6a796c3SToby Isaac #endif
13639566063dSJacob Faibussowitsch   PetscCall(PetscBLASIntCast(n, &bn));
13649566063dSJacob Faibussowitsch   PetscCall(PetscBLASIntCast(n, &ldz));
1365e6a796c3SToby Isaac #if !defined(PETSC_MISSING_LAPACK_STEGR)
13669566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(2 * n, &isuppz));
1367e6a796c3SToby Isaac   lwork  = -1;
1368e6a796c3SToby Isaac   liwork = -1;
1369792fecdfSBarry Smith   PetscCallBLAS("LAPACKstegr", LAPACKstegr_(&jobz, &range, &bn, diag, subdiag, &VL, &VU, &IL, &IU, &abstol, &bm, eigs, V, &ldz, isuppz, &workquery, &lwork, &iworkquery, &liwork, &info));
137028b400f6SJacob Faibussowitsch   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_PLIB, "xSTEGR error");
1371e6a796c3SToby Isaac   lwork  = (PetscBLASInt)workquery;
1372e6a796c3SToby Isaac   liwork = (PetscBLASInt)iworkquery;
13739566063dSJacob Faibussowitsch   PetscCall(PetscMalloc2(lwork, &work, liwork, &iwork));
13749566063dSJacob Faibussowitsch   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
1375792fecdfSBarry Smith   PetscCallBLAS("LAPACKstegr", LAPACKstegr_(&jobz, &range, &bn, diag, subdiag, &VL, &VU, &IL, &IU, &abstol, &bm, eigs, V, &ldz, isuppz, work, &lwork, iwork, &liwork, &info));
13769566063dSJacob Faibussowitsch   PetscCall(PetscFPTrapPop());
137728b400f6SJacob Faibussowitsch   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_PLIB, "xSTEGR error");
13789566063dSJacob Faibussowitsch   PetscCall(PetscFree2(work, iwork));
13799566063dSJacob Faibussowitsch   PetscCall(PetscFree(isuppz));
1380e6a796c3SToby Isaac #elif !defined(PETSC_MISSING_LAPACK_STEQR)
1381e6a796c3SToby Isaac   jobz = 'I'; /* Compute eigenvalues and eigenvectors of the
1382e6a796c3SToby Isaac                  tridiagonal matrix.  Z is initialized to the identity
1383e6a796c3SToby Isaac                  matrix. */
13849566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(PetscMax(1, 2 * n - 2), &work));
1385792fecdfSBarry Smith   PetscCallBLAS("LAPACKsteqr", LAPACKsteqr_("I", &bn, diag, subdiag, V, &ldz, work, &info));
13869566063dSJacob Faibussowitsch   PetscCall(PetscFPTrapPop());
138728b400f6SJacob Faibussowitsch   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_PLIB, "xSTEQR error");
13889566063dSJacob Faibussowitsch   PetscCall(PetscFree(work));
13899566063dSJacob Faibussowitsch   PetscCall(PetscArraycpy(eigs, diag, n));
1390e6a796c3SToby Isaac #endif
1391e6a796c3SToby Isaac   PetscFunctionReturn(0);
1392e6a796c3SToby Isaac }
1393e6a796c3SToby Isaac 
1394e6a796c3SToby Isaac /* Formula for the weights at the endpoints (-1 and 1) of Gauss-Lobatto-Jacobi
1395e6a796c3SToby Isaac  * quadrature rules on the interval [-1, 1] */
1396*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTGaussLobattoJacobiEndweights_Internal(PetscInt n, PetscReal alpha, PetscReal beta, PetscReal *leftw, PetscReal *rightw)
1397*d71ae5a4SJacob Faibussowitsch {
1398e6a796c3SToby Isaac   PetscReal twoab1;
1399e6a796c3SToby Isaac   PetscInt  m = n - 2;
1400e6a796c3SToby Isaac   PetscReal a = alpha + 1.;
1401e6a796c3SToby Isaac   PetscReal b = beta + 1.;
1402e6a796c3SToby Isaac   PetscReal gra, grb;
1403e6a796c3SToby Isaac 
1404e6a796c3SToby Isaac   PetscFunctionBegin;
1405e6a796c3SToby Isaac   twoab1 = PetscPowReal(2., a + b - 1.);
1406e6a796c3SToby Isaac #if defined(PETSC_HAVE_LGAMMA)
14079371c9d4SSatish Balay   grb = PetscExpReal(2. * PetscLGamma(b + 1.) + PetscLGamma(m + 1.) + PetscLGamma(m + a + 1.) - (PetscLGamma(m + b + 1) + PetscLGamma(m + a + b + 1.)));
14089371c9d4SSatish Balay   gra = PetscExpReal(2. * PetscLGamma(a + 1.) + PetscLGamma(m + 1.) + PetscLGamma(m + b + 1.) - (PetscLGamma(m + a + 1) + PetscLGamma(m + a + b + 1.)));
1409e6a796c3SToby Isaac #else
1410e6a796c3SToby Isaac   {
1411e6a796c3SToby Isaac     PetscInt alphai = (PetscInt)alpha;
1412e6a796c3SToby Isaac     PetscInt betai  = (PetscInt)beta;
1413e6a796c3SToby Isaac 
1414e6a796c3SToby Isaac     if ((PetscReal)alphai == alpha && (PetscReal)betai == beta) {
1415e6a796c3SToby Isaac       PetscReal binom1, binom2;
1416e6a796c3SToby Isaac 
14179566063dSJacob Faibussowitsch       PetscCall(PetscDTBinomial(m + b, b, &binom1));
14189566063dSJacob Faibussowitsch       PetscCall(PetscDTBinomial(m + a + b, b, &binom2));
1419e6a796c3SToby Isaac       grb = 1. / (binom1 * binom2);
14209566063dSJacob Faibussowitsch       PetscCall(PetscDTBinomial(m + a, a, &binom1));
14219566063dSJacob Faibussowitsch       PetscCall(PetscDTBinomial(m + a + b, a, &binom2));
1422e6a796c3SToby Isaac       gra = 1. / (binom1 * binom2);
1423e6a796c3SToby Isaac     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "lgamma() - math routine is unavailable.");
1424e6a796c3SToby Isaac   }
1425e6a796c3SToby Isaac #endif
1426e6a796c3SToby Isaac   *leftw  = twoab1 * grb / b;
1427e6a796c3SToby Isaac   *rightw = twoab1 * gra / a;
1428e6a796c3SToby Isaac   PetscFunctionReturn(0);
1429e6a796c3SToby Isaac }
1430e6a796c3SToby Isaac 
1431e6a796c3SToby Isaac /* Evaluates the nth jacobi polynomial with weight parameters a,b at a point x.
1432e6a796c3SToby Isaac    Recurrence relations implemented from the pseudocode given in Karniadakis and Sherwin, Appendix B */
1433*d71ae5a4SJacob Faibussowitsch static inline PetscErrorCode PetscDTComputeJacobi(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscReal *P)
1434*d71ae5a4SJacob Faibussowitsch {
143594e21283SToby Isaac   PetscReal pn1, pn2;
143694e21283SToby Isaac   PetscReal cnm1, cnm1x, cnm2;
1437e6a796c3SToby Isaac   PetscInt  k;
1438e6a796c3SToby Isaac 
1439e6a796c3SToby Isaac   PetscFunctionBegin;
14409371c9d4SSatish Balay   if (!n) {
14419371c9d4SSatish Balay     *P = 1.0;
14429371c9d4SSatish Balay     PetscFunctionReturn(0);
14439371c9d4SSatish Balay   }
144494e21283SToby Isaac   PetscDTJacobiRecurrence_Internal(1, a, b, cnm1, cnm1x, cnm2);
144594e21283SToby Isaac   pn2 = 1.;
144694e21283SToby Isaac   pn1 = cnm1 + cnm1x * x;
14479371c9d4SSatish Balay   if (n == 1) {
14489371c9d4SSatish Balay     *P = pn1;
14499371c9d4SSatish Balay     PetscFunctionReturn(0);
14509371c9d4SSatish Balay   }
1451e6a796c3SToby Isaac   *P = 0.0;
1452e6a796c3SToby Isaac   for (k = 2; k < n + 1; ++k) {
145394e21283SToby Isaac     PetscDTJacobiRecurrence_Internal(k, a, b, cnm1, cnm1x, cnm2);
1454e6a796c3SToby Isaac 
145594e21283SToby Isaac     *P  = (cnm1 + cnm1x * x) * pn1 - cnm2 * pn2;
1456e6a796c3SToby Isaac     pn2 = pn1;
1457e6a796c3SToby Isaac     pn1 = *P;
1458e6a796c3SToby Isaac   }
1459e6a796c3SToby Isaac   PetscFunctionReturn(0);
1460e6a796c3SToby Isaac }
1461e6a796c3SToby Isaac 
1462e6a796c3SToby Isaac /* Evaluates the first derivative of P_{n}^{a,b} at a point x. */
1463*d71ae5a4SJacob Faibussowitsch static inline PetscErrorCode PetscDTComputeJacobiDerivative(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscInt k, PetscReal *P)
1464*d71ae5a4SJacob Faibussowitsch {
1465e6a796c3SToby Isaac   PetscReal nP;
1466e6a796c3SToby Isaac   PetscInt  i;
1467e6a796c3SToby Isaac 
1468e6a796c3SToby Isaac   PetscFunctionBegin;
146917a42bb7SSatish Balay   *P = 0.0;
147017a42bb7SSatish Balay   if (k > n) PetscFunctionReturn(0);
14719566063dSJacob Faibussowitsch   PetscCall(PetscDTComputeJacobi(a + k, b + k, n - k, x, &nP));
1472e6a796c3SToby Isaac   for (i = 0; i < k; i++) nP *= (a + b + n + 1. + i) * 0.5;
1473e6a796c3SToby Isaac   *P = nP;
1474e6a796c3SToby Isaac   PetscFunctionReturn(0);
1475e6a796c3SToby Isaac }
1476e6a796c3SToby Isaac 
1477*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTGaussJacobiQuadrature_Newton_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal x[], PetscReal w[])
1478*d71ae5a4SJacob Faibussowitsch {
1479e6a796c3SToby Isaac   PetscInt  maxIter = 100;
148094e21283SToby Isaac   PetscReal eps     = PetscExpReal(0.75 * PetscLogReal(PETSC_MACHINE_EPSILON));
1481200b5abcSJed Brown   PetscReal a1, a6, gf;
1482e6a796c3SToby Isaac   PetscInt  k;
1483e6a796c3SToby Isaac 
1484e6a796c3SToby Isaac   PetscFunctionBegin;
1485e6a796c3SToby Isaac 
1486e6a796c3SToby Isaac   a1 = PetscPowReal(2.0, a + b + 1);
148794e21283SToby Isaac #if defined(PETSC_HAVE_LGAMMA)
1488200b5abcSJed Brown   {
1489200b5abcSJed Brown     PetscReal a2, a3, a4, a5;
149094e21283SToby Isaac     a2 = PetscLGamma(a + npoints + 1);
149194e21283SToby Isaac     a3 = PetscLGamma(b + npoints + 1);
149294e21283SToby Isaac     a4 = PetscLGamma(a + b + npoints + 1);
149394e21283SToby Isaac     a5 = PetscLGamma(npoints + 1);
149494e21283SToby Isaac     gf = PetscExpReal(a2 + a3 - (a4 + a5));
1495200b5abcSJed Brown   }
1496e6a796c3SToby Isaac #else
1497e6a796c3SToby Isaac   {
1498e6a796c3SToby Isaac     PetscInt ia, ib;
1499e6a796c3SToby Isaac 
1500e6a796c3SToby Isaac     ia = (PetscInt)a;
1501e6a796c3SToby Isaac     ib = (PetscInt)b;
150294e21283SToby Isaac     gf = 1.;
150394e21283SToby Isaac     if (ia == a && ia >= 0) { /* compute ratio of rising factorals wrt a */
150494e21283SToby Isaac       for (k = 0; k < ia; k++) gf *= (npoints + 1. + k) / (npoints + b + 1. + k);
150594e21283SToby Isaac     } else if (b == b && ib >= 0) { /* compute ratio of rising factorials wrt b */
150694e21283SToby Isaac       for (k = 0; k < ib; k++) gf *= (npoints + 1. + k) / (npoints + a + 1. + k);
150794e21283SToby Isaac     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "lgamma() - math routine is unavailable.");
1508e6a796c3SToby Isaac   }
1509e6a796c3SToby Isaac #endif
1510e6a796c3SToby Isaac 
151194e21283SToby Isaac   a6 = a1 * gf;
1512e6a796c3SToby Isaac   /* Computes the m roots of P_{m}^{a,b} on [-1,1] by Newton's method with Chebyshev points as initial guesses.
1513e6a796c3SToby Isaac    Algorithm implemented from the pseudocode given by Karniadakis and Sherwin and Python in FIAT */
1514e6a796c3SToby Isaac   for (k = 0; k < npoints; ++k) {
151594e21283SToby Isaac     PetscReal r = PetscCosReal(PETSC_PI * (1. - (4. * k + 3. + 2. * b) / (4. * npoints + 2. * (a + b + 1.)))), dP;
1516e6a796c3SToby Isaac     PetscInt  j;
1517e6a796c3SToby Isaac 
1518e6a796c3SToby Isaac     if (k > 0) r = 0.5 * (r + x[k - 1]);
1519e6a796c3SToby Isaac     for (j = 0; j < maxIter; ++j) {
1520e6a796c3SToby Isaac       PetscReal s = 0.0, delta, f, fp;
1521e6a796c3SToby Isaac       PetscInt  i;
1522e6a796c3SToby Isaac 
1523e6a796c3SToby Isaac       for (i = 0; i < k; ++i) s = s + 1.0 / (r - x[i]);
15249566063dSJacob Faibussowitsch       PetscCall(PetscDTComputeJacobi(a, b, npoints, r, &f));
15259566063dSJacob Faibussowitsch       PetscCall(PetscDTComputeJacobiDerivative(a, b, npoints, r, 1, &fp));
1526e6a796c3SToby Isaac       delta = f / (fp - f * s);
1527e6a796c3SToby Isaac       r     = r - delta;
1528e6a796c3SToby Isaac       if (PetscAbsReal(delta) < eps) break;
1529e6a796c3SToby Isaac     }
1530e6a796c3SToby Isaac     x[k] = r;
15319566063dSJacob Faibussowitsch     PetscCall(PetscDTComputeJacobiDerivative(a, b, npoints, x[k], 1, &dP));
1532e6a796c3SToby Isaac     w[k] = a6 / (1.0 - PetscSqr(x[k])) / PetscSqr(dP);
1533e6a796c3SToby Isaac   }
1534e6a796c3SToby Isaac   PetscFunctionReturn(0);
1535e6a796c3SToby Isaac }
1536e6a796c3SToby Isaac 
153794e21283SToby Isaac /* Compute the diagonals of the Jacobi matrix used in Golub & Welsch algorithms for Gauss-Jacobi
1538e6a796c3SToby Isaac  * quadrature weight calculations on [-1,1] for exponents (1. + x)^a (1.-x)^b */
1539*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTJacobiMatrix_Internal(PetscInt nPoints, PetscReal a, PetscReal b, PetscReal *d, PetscReal *s)
1540*d71ae5a4SJacob Faibussowitsch {
1541e6a796c3SToby Isaac   PetscInt i;
1542e6a796c3SToby Isaac 
1543e6a796c3SToby Isaac   PetscFunctionBegin;
1544e6a796c3SToby Isaac   for (i = 0; i < nPoints; i++) {
154594e21283SToby Isaac     PetscReal A, B, C;
1546e6a796c3SToby Isaac 
154794e21283SToby Isaac     PetscDTJacobiRecurrence_Internal(i + 1, a, b, A, B, C);
154894e21283SToby Isaac     d[i] = -A / B;
154994e21283SToby Isaac     if (i) s[i - 1] *= C / B;
155094e21283SToby Isaac     if (i < nPoints - 1) s[i] = 1. / B;
1551e6a796c3SToby Isaac   }
1552e6a796c3SToby Isaac   PetscFunctionReturn(0);
1553e6a796c3SToby Isaac }
1554e6a796c3SToby Isaac 
1555*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal *x, PetscReal *w)
1556*d71ae5a4SJacob Faibussowitsch {
1557e6a796c3SToby Isaac   PetscReal mu0;
1558e6a796c3SToby Isaac   PetscReal ga, gb, gab;
1559e6a796c3SToby Isaac   PetscInt  i;
1560e6a796c3SToby Isaac 
1561e6a796c3SToby Isaac   PetscFunctionBegin;
15629566063dSJacob Faibussowitsch   PetscCall(PetscCitationsRegister(GolubWelschCitation, &GolubWelschCite));
1563e6a796c3SToby Isaac 
1564e6a796c3SToby Isaac #if defined(PETSC_HAVE_TGAMMA)
1565e6a796c3SToby Isaac   ga  = PetscTGamma(a + 1);
1566e6a796c3SToby Isaac   gb  = PetscTGamma(b + 1);
1567e6a796c3SToby Isaac   gab = PetscTGamma(a + b + 2);
1568e6a796c3SToby Isaac #else
1569e6a796c3SToby Isaac   {
1570e6a796c3SToby Isaac     PetscInt ia, ib;
1571e6a796c3SToby Isaac 
1572e6a796c3SToby Isaac     ia = (PetscInt)a;
1573e6a796c3SToby Isaac     ib = (PetscInt)b;
1574e6a796c3SToby Isaac     if (ia == a && ib == b && ia + 1 > 0 && ib + 1 > 0 && ia + ib + 2 > 0) { /* All gamma(x) terms are (x-1)! terms */
15759566063dSJacob Faibussowitsch       PetscCall(PetscDTFactorial(ia, &ga));
15769566063dSJacob Faibussowitsch       PetscCall(PetscDTFactorial(ib, &gb));
15779566063dSJacob Faibussowitsch       PetscCall(PetscDTFactorial(ia + ib + 1, &gb));
1578e6a796c3SToby Isaac     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "tgamma() - math routine is unavailable.");
1579e6a796c3SToby Isaac   }
1580e6a796c3SToby Isaac #endif
1581e6a796c3SToby Isaac   mu0 = PetscPowReal(2., a + b + 1.) * ga * gb / gab;
1582e6a796c3SToby Isaac 
1583e6a796c3SToby Isaac #if defined(PETSCDTGAUSSIANQUADRATURE_EIG)
1584e6a796c3SToby Isaac   {
1585e6a796c3SToby Isaac     PetscReal   *diag, *subdiag;
1586e6a796c3SToby Isaac     PetscScalar *V;
1587e6a796c3SToby Isaac 
15889566063dSJacob Faibussowitsch     PetscCall(PetscMalloc2(npoints, &diag, npoints, &subdiag));
15899566063dSJacob Faibussowitsch     PetscCall(PetscMalloc1(npoints * npoints, &V));
15909566063dSJacob Faibussowitsch     PetscCall(PetscDTJacobiMatrix_Internal(npoints, a, b, diag, subdiag));
1591e6a796c3SToby Isaac     for (i = 0; i < npoints - 1; i++) subdiag[i] = PetscSqrtReal(subdiag[i]);
15929566063dSJacob Faibussowitsch     PetscCall(PetscDTSymmetricTridiagonalEigensolve(npoints, diag, subdiag, x, V));
159394e21283SToby Isaac     for (i = 0; i < npoints; i++) w[i] = PetscSqr(PetscRealPart(V[i * npoints])) * mu0;
15949566063dSJacob Faibussowitsch     PetscCall(PetscFree(V));
15959566063dSJacob Faibussowitsch     PetscCall(PetscFree2(diag, subdiag));
1596e6a796c3SToby Isaac   }
1597e6a796c3SToby Isaac #else
1598e6a796c3SToby Isaac   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
1599e6a796c3SToby Isaac #endif
160094e21283SToby Isaac   { /* As of March 2, 2020, The Sun Performance Library breaks the LAPACK contract for xstegr and xsteqr: the
160194e21283SToby Isaac        eigenvalues are not guaranteed to be in ascending order.  So we heave a passive aggressive sigh and check that
160294e21283SToby Isaac        the eigenvalues are sorted */
160394e21283SToby Isaac     PetscBool sorted;
160494e21283SToby Isaac 
16059566063dSJacob Faibussowitsch     PetscCall(PetscSortedReal(npoints, x, &sorted));
160694e21283SToby Isaac     if (!sorted) {
160794e21283SToby Isaac       PetscInt  *order, i;
160894e21283SToby Isaac       PetscReal *tmp;
160994e21283SToby Isaac 
16109566063dSJacob Faibussowitsch       PetscCall(PetscMalloc2(npoints, &order, npoints, &tmp));
161194e21283SToby Isaac       for (i = 0; i < npoints; i++) order[i] = i;
16129566063dSJacob Faibussowitsch       PetscCall(PetscSortRealWithPermutation(npoints, x, order));
16139566063dSJacob Faibussowitsch       PetscCall(PetscArraycpy(tmp, x, npoints));
161494e21283SToby Isaac       for (i = 0; i < npoints; i++) x[i] = tmp[order[i]];
16159566063dSJacob Faibussowitsch       PetscCall(PetscArraycpy(tmp, w, npoints));
161694e21283SToby Isaac       for (i = 0; i < npoints; i++) w[i] = tmp[order[i]];
16179566063dSJacob Faibussowitsch       PetscCall(PetscFree2(order, tmp));
161894e21283SToby Isaac     }
161994e21283SToby Isaac   }
1620e6a796c3SToby Isaac   PetscFunctionReturn(0);
1621e6a796c3SToby Isaac }
1622e6a796c3SToby Isaac 
1623*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTGaussJacobiQuadrature_Internal(PetscInt npoints, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
1624*d71ae5a4SJacob Faibussowitsch {
1625e6a796c3SToby Isaac   PetscFunctionBegin;
162608401ef6SPierre Jolivet   PetscCheck(npoints >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of points must be positive");
1627e6a796c3SToby Isaac   /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
162808401ef6SPierre Jolivet   PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "alpha must be > -1.");
162908401ef6SPierre Jolivet   PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "beta must be > -1.");
1630e6a796c3SToby Isaac 
16311baa6e33SBarry Smith   if (newton) PetscCall(PetscDTGaussJacobiQuadrature_Newton_Internal(npoints, alpha, beta, x, w));
16321baa6e33SBarry Smith   else PetscCall(PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(npoints, alpha, beta, x, w));
1633e6a796c3SToby Isaac   if (alpha == beta) { /* symmetrize */
1634e6a796c3SToby Isaac     PetscInt i;
1635e6a796c3SToby Isaac     for (i = 0; i < (npoints + 1) / 2; i++) {
1636e6a796c3SToby Isaac       PetscInt  j  = npoints - 1 - i;
1637e6a796c3SToby Isaac       PetscReal xi = x[i];
1638e6a796c3SToby Isaac       PetscReal xj = x[j];
1639e6a796c3SToby Isaac       PetscReal wi = w[i];
1640e6a796c3SToby Isaac       PetscReal wj = w[j];
1641e6a796c3SToby Isaac 
1642e6a796c3SToby Isaac       x[i] = (xi - xj) / 2.;
1643e6a796c3SToby Isaac       x[j] = (xj - xi) / 2.;
1644e6a796c3SToby Isaac       w[i] = w[j] = (wi + wj) / 2.;
1645e6a796c3SToby Isaac     }
1646e6a796c3SToby Isaac   }
1647e6a796c3SToby Isaac   PetscFunctionReturn(0);
1648e6a796c3SToby Isaac }
1649e6a796c3SToby Isaac 
165094e21283SToby Isaac /*@
165194e21283SToby Isaac   PetscDTGaussJacobiQuadrature - quadrature for the interval [a, b] with the weight function
165294e21283SToby Isaac   $(x-a)^\alpha (x-b)^\beta$.
165394e21283SToby Isaac 
165494e21283SToby Isaac   Not collective
165594e21283SToby Isaac 
165694e21283SToby Isaac   Input Parameters:
165794e21283SToby Isaac + npoints - the number of points in the quadrature rule
165894e21283SToby Isaac . a - the left endpoint of the interval
165994e21283SToby Isaac . b - the right endpoint of the interval
166094e21283SToby Isaac . alpha - the left exponent
166194e21283SToby Isaac - beta - the right exponent
166294e21283SToby Isaac 
166394e21283SToby Isaac   Output Parameters:
166494e21283SToby Isaac + x - array of length npoints, the locations of the quadrature points
166594e21283SToby Isaac - w - array of length npoints, the weights of the quadrature points
166694e21283SToby Isaac 
166794e21283SToby Isaac   Level: intermediate
166894e21283SToby Isaac 
166994e21283SToby Isaac   Note: this quadrature rule is exact for polynomials up to degree 2*npoints - 1.
167094e21283SToby Isaac @*/
1671*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTGaussJacobiQuadrature(PetscInt npoints, PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
1672*d71ae5a4SJacob Faibussowitsch {
167394e21283SToby Isaac   PetscInt i;
1674e6a796c3SToby Isaac 
1675e6a796c3SToby Isaac   PetscFunctionBegin;
16769566063dSJacob Faibussowitsch   PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal));
167794e21283SToby Isaac   if (a != -1. || b != 1.) { /* shift */
167894e21283SToby Isaac     for (i = 0; i < npoints; i++) {
167994e21283SToby Isaac       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
168094e21283SToby Isaac       w[i] *= (b - a) / 2.;
168194e21283SToby Isaac     }
168294e21283SToby Isaac   }
1683e6a796c3SToby Isaac   PetscFunctionReturn(0);
1684e6a796c3SToby Isaac }
1685e6a796c3SToby Isaac 
1686*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTGaussLobattoJacobiQuadrature_Internal(PetscInt npoints, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
1687*d71ae5a4SJacob Faibussowitsch {
1688e6a796c3SToby Isaac   PetscInt i;
1689e6a796c3SToby Isaac 
1690e6a796c3SToby Isaac   PetscFunctionBegin;
169108401ef6SPierre Jolivet   PetscCheck(npoints >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of points must be positive");
1692e6a796c3SToby Isaac   /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
169308401ef6SPierre Jolivet   PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "alpha must be > -1.");
169408401ef6SPierre Jolivet   PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "beta must be > -1.");
1695e6a796c3SToby Isaac 
1696e6a796c3SToby Isaac   x[0]           = -1.;
1697e6a796c3SToby Isaac   x[npoints - 1] = 1.;
169848a46eb9SPierre Jolivet   if (npoints > 2) PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints - 2, alpha + 1., beta + 1., &x[1], &w[1], newton));
1699ad540459SPierre Jolivet   for (i = 1; i < npoints - 1; i++) w[i] /= (1. - x[i] * x[i]);
17009566063dSJacob Faibussowitsch   PetscCall(PetscDTGaussLobattoJacobiEndweights_Internal(npoints, alpha, beta, &w[0], &w[npoints - 1]));
1701e6a796c3SToby Isaac   PetscFunctionReturn(0);
1702e6a796c3SToby Isaac }
1703e6a796c3SToby Isaac 
170437045ce4SJed Brown /*@
170594e21283SToby Isaac   PetscDTGaussLobattoJacobiQuadrature - quadrature for the interval [a, b] with the weight function
170694e21283SToby Isaac   $(x-a)^\alpha (x-b)^\beta$, with endpoints a and b included as quadrature points.
170794e21283SToby Isaac 
170894e21283SToby Isaac   Not collective
170994e21283SToby Isaac 
171094e21283SToby Isaac   Input Parameters:
171194e21283SToby Isaac + npoints - the number of points in the quadrature rule
171294e21283SToby Isaac . a - the left endpoint of the interval
171394e21283SToby Isaac . b - the right endpoint of the interval
171494e21283SToby Isaac . alpha - the left exponent
171594e21283SToby Isaac - beta - the right exponent
171694e21283SToby Isaac 
171794e21283SToby Isaac   Output Parameters:
171894e21283SToby Isaac + x - array of length npoints, the locations of the quadrature points
171994e21283SToby Isaac - w - array of length npoints, the weights of the quadrature points
172094e21283SToby Isaac 
172194e21283SToby Isaac   Level: intermediate
172294e21283SToby Isaac 
172394e21283SToby Isaac   Note: this quadrature rule is exact for polynomials up to degree 2*npoints - 3.
172494e21283SToby Isaac @*/
1725*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTGaussLobattoJacobiQuadrature(PetscInt npoints, PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
1726*d71ae5a4SJacob Faibussowitsch {
172794e21283SToby Isaac   PetscInt i;
172894e21283SToby Isaac 
172994e21283SToby Isaac   PetscFunctionBegin;
17309566063dSJacob Faibussowitsch   PetscCall(PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal));
173194e21283SToby Isaac   if (a != -1. || b != 1.) { /* shift */
173294e21283SToby Isaac     for (i = 0; i < npoints; i++) {
173394e21283SToby Isaac       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
173494e21283SToby Isaac       w[i] *= (b - a) / 2.;
173594e21283SToby Isaac     }
173694e21283SToby Isaac   }
173794e21283SToby Isaac   PetscFunctionReturn(0);
173894e21283SToby Isaac }
173994e21283SToby Isaac 
174094e21283SToby Isaac /*@
1741e6a796c3SToby Isaac    PetscDTGaussQuadrature - create Gauss-Legendre quadrature
174237045ce4SJed Brown 
174337045ce4SJed Brown    Not Collective
174437045ce4SJed Brown 
17454165533cSJose E. Roman    Input Parameters:
174637045ce4SJed Brown +  npoints - number of points
174737045ce4SJed Brown .  a - left end of interval (often-1)
174837045ce4SJed Brown -  b - right end of interval (often +1)
174937045ce4SJed Brown 
17504165533cSJose E. Roman    Output Parameters:
175137045ce4SJed Brown +  x - quadrature points
175237045ce4SJed Brown -  w - quadrature weights
175337045ce4SJed Brown 
175437045ce4SJed Brown    Level: intermediate
175537045ce4SJed Brown 
175637045ce4SJed Brown    References:
1757606c0280SSatish Balay .  * - Golub and Welsch, Calculation of Quadrature Rules, Math. Comp. 23(106), 1969.
175837045ce4SJed Brown 
1759db781477SPatrick Sanan .seealso: `PetscDTLegendreEval()`
176037045ce4SJed Brown @*/
1761*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTGaussQuadrature(PetscInt npoints, PetscReal a, PetscReal b, PetscReal *x, PetscReal *w)
1762*d71ae5a4SJacob Faibussowitsch {
176337045ce4SJed Brown   PetscInt i;
176437045ce4SJed Brown 
176537045ce4SJed Brown   PetscFunctionBegin;
17669566063dSJacob Faibussowitsch   PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints, 0., 0., x, w, PetscDTGaussQuadratureNewton_Internal));
176794e21283SToby Isaac   if (a != -1. || b != 1.) { /* shift */
176837045ce4SJed Brown     for (i = 0; i < npoints; i++) {
1769e6a796c3SToby Isaac       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1770e6a796c3SToby Isaac       w[i] *= (b - a) / 2.;
177137045ce4SJed Brown     }
177237045ce4SJed Brown   }
177337045ce4SJed Brown   PetscFunctionReturn(0);
177437045ce4SJed Brown }
1775194825f6SJed Brown 
17768272889dSSatish Balay /*@C
17778272889dSSatish Balay    PetscDTGaussLobattoLegendreQuadrature - creates a set of the locations and weights of the Gauss-Lobatto-Legendre
17788272889dSSatish Balay                       nodes of a given size on the domain [-1,1]
17798272889dSSatish Balay 
17808272889dSSatish Balay    Not Collective
17818272889dSSatish Balay 
1782d8d19677SJose E. Roman    Input Parameters:
17838272889dSSatish Balay +  n - number of grid nodes
1784f2e8fe4dShannah_mairs -  type - PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA or PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON
17858272889dSSatish Balay 
17864165533cSJose E. Roman    Output Parameters:
17878272889dSSatish Balay +  x - quadrature points
17888272889dSSatish Balay -  w - quadrature weights
17898272889dSSatish Balay 
17908272889dSSatish Balay    Notes:
17918272889dSSatish Balay     For n > 30  the Newton approach computes duplicate (incorrect) values for some nodes because the initial guess is apparently not
17928272889dSSatish Balay           close enough to the desired solution
17938272889dSSatish Balay 
17948272889dSSatish Balay    These are useful for implementing spectral methods based on Gauss-Lobatto-Legendre (GLL) nodes
17958272889dSSatish Balay 
1796a8d69d7bSBarry Smith    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
17978272889dSSatish Balay 
17988272889dSSatish Balay    Level: intermediate
17998272889dSSatish Balay 
1800db781477SPatrick Sanan .seealso: `PetscDTGaussQuadrature()`
18018272889dSSatish Balay 
18028272889dSSatish Balay @*/
1803*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTGaussLobattoLegendreQuadrature(PetscInt npoints, PetscGaussLobattoLegendreCreateType type, PetscReal *x, PetscReal *w)
1804*d71ae5a4SJacob Faibussowitsch {
1805e6a796c3SToby Isaac   PetscBool newton;
18068272889dSSatish Balay 
18078272889dSSatish Balay   PetscFunctionBegin;
180808401ef6SPierre Jolivet   PetscCheck(npoints >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must provide at least 2 grid points per element");
180994e21283SToby Isaac   newton = (PetscBool)(type == PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON);
18109566063dSJacob Faibussowitsch   PetscCall(PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, 0., 0., x, w, newton));
18118272889dSSatish Balay   PetscFunctionReturn(0);
18128272889dSSatish Balay }
18138272889dSSatish Balay 
1814744bafbcSMatthew G. Knepley /*@
1815744bafbcSMatthew G. Knepley   PetscDTGaussTensorQuadrature - creates a tensor-product Gauss quadrature
1816744bafbcSMatthew G. Knepley 
1817744bafbcSMatthew G. Knepley   Not Collective
1818744bafbcSMatthew G. Knepley 
18194165533cSJose E. Roman   Input Parameters:
1820744bafbcSMatthew G. Knepley + dim     - The spatial dimension
1821a6b92713SMatthew G. Knepley . Nc      - The number of components
1822744bafbcSMatthew G. Knepley . npoints - number of points in one dimension
1823744bafbcSMatthew G. Knepley . a       - left end of interval (often-1)
1824744bafbcSMatthew G. Knepley - b       - right end of interval (often +1)
1825744bafbcSMatthew G. Knepley 
18264165533cSJose E. Roman   Output Parameter:
1827744bafbcSMatthew G. Knepley . q - A PetscQuadrature object
1828744bafbcSMatthew G. Knepley 
1829744bafbcSMatthew G. Knepley   Level: intermediate
1830744bafbcSMatthew G. Knepley 
1831db781477SPatrick Sanan .seealso: `PetscDTGaussQuadrature()`, `PetscDTLegendreEval()`
1832744bafbcSMatthew G. Knepley @*/
1833*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTGaussTensorQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
1834*d71ae5a4SJacob Faibussowitsch {
1835a6b92713SMatthew G. Knepley   PetscInt   totpoints = dim > 1 ? dim > 2 ? npoints * PetscSqr(npoints) : PetscSqr(npoints) : npoints, i, j, k, c;
1836744bafbcSMatthew G. Knepley   PetscReal *x, *w, *xw, *ww;
1837744bafbcSMatthew G. Knepley 
1838744bafbcSMatthew G. Knepley   PetscFunctionBegin;
18399566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(totpoints * dim, &x));
18409566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(totpoints * Nc, &w));
1841744bafbcSMatthew G. Knepley   /* Set up the Golub-Welsch system */
1842744bafbcSMatthew G. Knepley   switch (dim) {
1843744bafbcSMatthew G. Knepley   case 0:
18449566063dSJacob Faibussowitsch     PetscCall(PetscFree(x));
18459566063dSJacob Faibussowitsch     PetscCall(PetscFree(w));
18469566063dSJacob Faibussowitsch     PetscCall(PetscMalloc1(1, &x));
18479566063dSJacob Faibussowitsch     PetscCall(PetscMalloc1(Nc, &w));
1848744bafbcSMatthew G. Knepley     x[0] = 0.0;
1849a6b92713SMatthew G. Knepley     for (c = 0; c < Nc; ++c) w[c] = 1.0;
1850744bafbcSMatthew G. Knepley     break;
1851744bafbcSMatthew G. Knepley   case 1:
18529566063dSJacob Faibussowitsch     PetscCall(PetscMalloc1(npoints, &ww));
18539566063dSJacob Faibussowitsch     PetscCall(PetscDTGaussQuadrature(npoints, a, b, x, ww));
18549371c9d4SSatish Balay     for (i = 0; i < npoints; ++i)
18559371c9d4SSatish Balay       for (c = 0; c < Nc; ++c) w[i * Nc + c] = ww[i];
18569566063dSJacob Faibussowitsch     PetscCall(PetscFree(ww));
1857744bafbcSMatthew G. Knepley     break;
1858744bafbcSMatthew G. Knepley   case 2:
18599566063dSJacob Faibussowitsch     PetscCall(PetscMalloc2(npoints, &xw, npoints, &ww));
18609566063dSJacob Faibussowitsch     PetscCall(PetscDTGaussQuadrature(npoints, a, b, xw, ww));
1861744bafbcSMatthew G. Knepley     for (i = 0; i < npoints; ++i) {
1862744bafbcSMatthew G. Knepley       for (j = 0; j < npoints; ++j) {
1863744bafbcSMatthew G. Knepley         x[(i * npoints + j) * dim + 0] = xw[i];
1864744bafbcSMatthew G. Knepley         x[(i * npoints + j) * dim + 1] = xw[j];
1865a6b92713SMatthew G. Knepley         for (c = 0; c < Nc; ++c) w[(i * npoints + j) * Nc + c] = ww[i] * ww[j];
1866744bafbcSMatthew G. Knepley       }
1867744bafbcSMatthew G. Knepley     }
18689566063dSJacob Faibussowitsch     PetscCall(PetscFree2(xw, ww));
1869744bafbcSMatthew G. Knepley     break;
1870744bafbcSMatthew G. Knepley   case 3:
18719566063dSJacob Faibussowitsch     PetscCall(PetscMalloc2(npoints, &xw, npoints, &ww));
18729566063dSJacob Faibussowitsch     PetscCall(PetscDTGaussQuadrature(npoints, a, b, xw, ww));
1873744bafbcSMatthew G. Knepley     for (i = 0; i < npoints; ++i) {
1874744bafbcSMatthew G. Knepley       for (j = 0; j < npoints; ++j) {
1875744bafbcSMatthew G. Knepley         for (k = 0; k < npoints; ++k) {
1876744bafbcSMatthew G. Knepley           x[((i * npoints + j) * npoints + k) * dim + 0] = xw[i];
1877744bafbcSMatthew G. Knepley           x[((i * npoints + j) * npoints + k) * dim + 1] = xw[j];
1878744bafbcSMatthew G. Knepley           x[((i * npoints + j) * npoints + k) * dim + 2] = xw[k];
1879a6b92713SMatthew G. Knepley           for (c = 0; c < Nc; ++c) w[((i * npoints + j) * npoints + k) * Nc + c] = ww[i] * ww[j] * ww[k];
1880744bafbcSMatthew G. Knepley         }
1881744bafbcSMatthew G. Knepley       }
1882744bafbcSMatthew G. Knepley     }
18839566063dSJacob Faibussowitsch     PetscCall(PetscFree2(xw, ww));
1884744bafbcSMatthew G. Knepley     break;
1885*d71ae5a4SJacob Faibussowitsch   default:
1886*d71ae5a4SJacob Faibussowitsch     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %" PetscInt_FMT, dim);
1887744bafbcSMatthew G. Knepley   }
18889566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
18899566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetOrder(*q, 2 * npoints - 1));
18909566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w));
18919566063dSJacob Faibussowitsch   PetscCall(PetscObjectChangeTypeName((PetscObject)*q, "GaussTensor"));
1892744bafbcSMatthew G. Knepley   PetscFunctionReturn(0);
1893744bafbcSMatthew G. Knepley }
1894744bafbcSMatthew G. Knepley 
1895f5f57ec0SBarry Smith /*@
1896e6a796c3SToby Isaac   PetscDTStroudConicalQuadrature - create Stroud conical quadrature for a simplex
1897494e7359SMatthew G. Knepley 
1898494e7359SMatthew G. Knepley   Not Collective
1899494e7359SMatthew G. Knepley 
19004165533cSJose E. Roman   Input Parameters:
1901494e7359SMatthew G. Knepley + dim     - The simplex dimension
1902a6b92713SMatthew G. Knepley . Nc      - The number of components
1903dcce0ee2SMatthew G. Knepley . npoints - The number of points in one dimension
1904494e7359SMatthew G. Knepley . a       - left end of interval (often-1)
1905494e7359SMatthew G. Knepley - b       - right end of interval (often +1)
1906494e7359SMatthew G. Knepley 
19074165533cSJose E. Roman   Output Parameter:
1908552aa4f7SMatthew G. Knepley . q - A PetscQuadrature object
1909494e7359SMatthew G. Knepley 
1910494e7359SMatthew G. Knepley   Level: intermediate
1911494e7359SMatthew G. Knepley 
1912494e7359SMatthew G. Knepley   References:
1913606c0280SSatish Balay . * - Karniadakis and Sherwin.  FIAT
1914494e7359SMatthew G. Knepley 
1915e6a796c3SToby Isaac   Note: For dim == 1, this is Gauss-Legendre quadrature
1916e6a796c3SToby Isaac 
1917db781477SPatrick Sanan .seealso: `PetscDTGaussTensorQuadrature()`, `PetscDTGaussQuadrature()`
1918494e7359SMatthew G. Knepley @*/
1919*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTStroudConicalQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
1920*d71ae5a4SJacob Faibussowitsch {
1921fbdc3dfeSToby Isaac   PetscInt   totprev, totrem;
1922fbdc3dfeSToby Isaac   PetscInt   totpoints;
1923fbdc3dfeSToby Isaac   PetscReal *p1, *w1;
1924fbdc3dfeSToby Isaac   PetscReal *x, *w;
1925fbdc3dfeSToby Isaac   PetscInt   i, j, k, l, m, pt, c;
1926494e7359SMatthew G. Knepley 
1927494e7359SMatthew G. Knepley   PetscFunctionBegin;
192808401ef6SPierre Jolivet   PetscCheck(!(a != -1.0) && !(b != 1.0), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must use default internal right now");
1929fbdc3dfeSToby Isaac   totpoints = 1;
1930fbdc3dfeSToby Isaac   for (i = 0, totpoints = 1; i < dim; i++) totpoints *= npoints;
19319566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(totpoints * dim, &x));
19329566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(totpoints * Nc, &w));
19339566063dSJacob Faibussowitsch   PetscCall(PetscMalloc2(npoints, &p1, npoints, &w1));
1934fbdc3dfeSToby Isaac   for (i = 0; i < totpoints * Nc; i++) w[i] = 1.;
1935fbdc3dfeSToby Isaac   for (i = 0, totprev = 1, totrem = totpoints / npoints; i < dim; i++) {
1936fbdc3dfeSToby Isaac     PetscReal mul;
1937fbdc3dfeSToby Isaac 
1938fbdc3dfeSToby Isaac     mul = PetscPowReal(2., -i);
19399566063dSJacob Faibussowitsch     PetscCall(PetscDTGaussJacobiQuadrature(npoints, -1., 1., i, 0.0, p1, w1));
1940fbdc3dfeSToby Isaac     for (pt = 0, l = 0; l < totprev; l++) {
1941fbdc3dfeSToby Isaac       for (j = 0; j < npoints; j++) {
1942fbdc3dfeSToby Isaac         for (m = 0; m < totrem; m++, pt++) {
1943fbdc3dfeSToby Isaac           for (k = 0; k < i; k++) x[pt * dim + k] = (x[pt * dim + k] + 1.) * (1. - p1[j]) * 0.5 - 1.;
1944fbdc3dfeSToby Isaac           x[pt * dim + i] = p1[j];
1945fbdc3dfeSToby Isaac           for (c = 0; c < Nc; c++) w[pt * Nc + c] *= mul * w1[j];
1946494e7359SMatthew G. Knepley         }
1947494e7359SMatthew G. Knepley       }
1948494e7359SMatthew G. Knepley     }
1949fbdc3dfeSToby Isaac     totprev *= npoints;
1950fbdc3dfeSToby Isaac     totrem /= npoints;
1951494e7359SMatthew G. Knepley   }
19529566063dSJacob Faibussowitsch   PetscCall(PetscFree2(p1, w1));
19539566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
19549566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetOrder(*q, 2 * npoints - 1));
19559566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w));
19569566063dSJacob Faibussowitsch   PetscCall(PetscObjectChangeTypeName((PetscObject)*q, "StroudConical"));
1957494e7359SMatthew G. Knepley   PetscFunctionReturn(0);
1958494e7359SMatthew G. Knepley }
1959494e7359SMatthew G. Knepley 
1960d3c69ad0SToby Isaac static PetscBool MinSymTriQuadCite       = PETSC_FALSE;
19619371c9d4SSatish Balay const char       MinSymTriQuadCitation[] = "@article{WitherdenVincent2015,\n"
1962d3c69ad0SToby Isaac                                            "  title = {On the identification of symmetric quadrature rules for finite element methods},\n"
1963d3c69ad0SToby Isaac                                            "  journal = {Computers & Mathematics with Applications},\n"
1964d3c69ad0SToby Isaac                                            "  volume = {69},\n"
1965d3c69ad0SToby Isaac                                            "  number = {10},\n"
1966d3c69ad0SToby Isaac                                            "  pages = {1232-1241},\n"
1967d3c69ad0SToby Isaac                                            "  year = {2015},\n"
1968d3c69ad0SToby Isaac                                            "  issn = {0898-1221},\n"
1969d3c69ad0SToby Isaac                                            "  doi = {10.1016/j.camwa.2015.03.017},\n"
1970d3c69ad0SToby Isaac                                            "  url = {https://www.sciencedirect.com/science/article/pii/S0898122115001224},\n"
1971d3c69ad0SToby Isaac                                            "  author = {F.D. Witherden and P.E. Vincent},\n"
1972d3c69ad0SToby Isaac                                            "}\n";
1973d3c69ad0SToby Isaac 
1974d3c69ad0SToby Isaac #include "petscdttriquadrules.h"
1975d3c69ad0SToby Isaac 
1976d3c69ad0SToby Isaac static PetscBool MinSymTetQuadCite       = PETSC_FALSE;
19779371c9d4SSatish Balay const char       MinSymTetQuadCitation[] = "@article{JaskowiecSukumar2021\n"
1978d3c69ad0SToby Isaac                                            "  author = {Jaskowiec, Jan and Sukumar, N.},\n"
1979d3c69ad0SToby Isaac                                            "  title = {High-order symmetric cubature rules for tetrahedra and pyramids},\n"
1980d3c69ad0SToby Isaac                                            "  journal = {International Journal for Numerical Methods in Engineering},\n"
1981d3c69ad0SToby Isaac                                            "  volume = {122},\n"
1982d3c69ad0SToby Isaac                                            "  number = {1},\n"
1983d3c69ad0SToby Isaac                                            "  pages = {148-171},\n"
1984d3c69ad0SToby Isaac                                            "  doi = {10.1002/nme.6528},\n"
1985d3c69ad0SToby Isaac                                            "  url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/nme.6528},\n"
1986d3c69ad0SToby Isaac                                            "  eprint = {https://onlinelibrary.wiley.com/doi/pdf/10.1002/nme.6528},\n"
1987d3c69ad0SToby Isaac                                            "  year = {2021}\n"
1988d3c69ad0SToby Isaac                                            "}\n";
1989d3c69ad0SToby Isaac 
1990d3c69ad0SToby Isaac #include "petscdttetquadrules.h"
1991d3c69ad0SToby Isaac 
1992d3c69ad0SToby Isaac // https://en.wikipedia.org/wiki/Partition_(number_theory)
1993*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTPartitionNumber(PetscInt n, PetscInt *p)
1994*d71ae5a4SJacob Faibussowitsch {
1995d3c69ad0SToby Isaac   // sequence A000041 in the OEIS
1996d3c69ad0SToby Isaac   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};
1997d3c69ad0SToby Isaac   PetscInt       tabulated_max = PETSC_STATIC_ARRAY_LENGTH(partition) - 1;
1998d3c69ad0SToby Isaac 
1999d3c69ad0SToby Isaac   PetscFunctionBegin;
2000d3c69ad0SToby Isaac   PetscCheck(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Partition number not defined for negative number %" PetscInt_FMT, n);
2001d3c69ad0SToby Isaac   // not implementing the pentagonal number recurrence, we don't need partition numbers for n that high
2002d3c69ad0SToby Isaac   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);
2003d3c69ad0SToby Isaac   *p = partition[n];
2004d3c69ad0SToby Isaac   PetscFunctionReturn(0);
2005d3c69ad0SToby Isaac }
2006d3c69ad0SToby Isaac 
2007d3c69ad0SToby Isaac /*@
2008d3c69ad0SToby Isaac   PetscDTSimplexQuadrature - Create a quadrature rule for a simplex that exactly integrates polynomials up to a given degree.
2009d3c69ad0SToby Isaac 
2010d3c69ad0SToby Isaac   Not Collective
2011d3c69ad0SToby Isaac 
2012d3c69ad0SToby Isaac   Input Parameters:
2013d3c69ad0SToby Isaac + dim     - The spatial dimension of the simplex (1 = segment, 2 = triangle, 3 = tetrahedron)
2014d3c69ad0SToby Isaac . degree  - The largest polynomial degree that is required to be integrated exactly
2015d3c69ad0SToby Isaac - type    - left end of interval (often-1)
2016d3c69ad0SToby Isaac 
2017d3c69ad0SToby Isaac   Output Parameter:
2018d3c69ad0SToby Isaac . quad    - A PetscQuadrature object for integration over the biunit simplex
2019d3c69ad0SToby Isaac             (defined by the bounds $x_i >= -1$ and $\sum_i x_i <= 2 - d$) that is exact for
2020d3c69ad0SToby Isaac             polynomials up to the given degree
2021d3c69ad0SToby Isaac 
2022d3c69ad0SToby Isaac   Level: intermediate
2023d3c69ad0SToby Isaac 
2024d3c69ad0SToby Isaac .seealso: `PetscDTSimplexQuadratureType`, `PetscDTGaussQuadrature()`, `PetscDTStroudCononicalQuadrature()`
2025d3c69ad0SToby Isaac @*/
2026*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTSimplexQuadrature(PetscInt dim, PetscInt degree, PetscDTSimplexQuadratureType type, PetscQuadrature *quad)
2027*d71ae5a4SJacob Faibussowitsch {
2028d3c69ad0SToby Isaac   PetscDTSimplexQuadratureType orig_type = type;
2029d3c69ad0SToby Isaac 
2030d3c69ad0SToby Isaac   PetscFunctionBegin;
2031d3c69ad0SToby Isaac   PetscCheck(dim >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative dimension %" PetscInt_FMT, dim);
2032d3c69ad0SToby Isaac   PetscCheck(degree >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative degree %" PetscInt_FMT, degree);
2033ad540459SPierre Jolivet   if (type == PETSCDTSIMPLEXQUAD_DEFAULT) type = PETSCDTSIMPLEXQUAD_MINSYM;
2034d3c69ad0SToby Isaac   if (type == PETSCDTSIMPLEXQUAD_CONIC || dim < 2) {
2035d3c69ad0SToby Isaac     PetscInt points_per_dim = (degree + 2) / 2; // ceil((degree + 1) / 2);
2036d3c69ad0SToby Isaac     PetscCall(PetscDTStroudConicalQuadrature(dim, 1, points_per_dim, -1, 1, quad));
2037d3c69ad0SToby Isaac   } else {
2038d3c69ad0SToby Isaac     PetscInt          n    = dim + 1;
2039d3c69ad0SToby Isaac     PetscInt          fact = 1;
2040d3c69ad0SToby Isaac     PetscInt         *part, *perm;
2041d3c69ad0SToby Isaac     PetscInt          p = 0;
2042d3c69ad0SToby Isaac     PetscInt          max_degree;
2043d3c69ad0SToby Isaac     const PetscInt   *nodes_per_type     = NULL;
2044d3c69ad0SToby Isaac     const PetscInt   *all_num_full_nodes = NULL;
2045d3c69ad0SToby Isaac     const PetscReal **weights_list       = NULL;
2046d3c69ad0SToby Isaac     const PetscReal **compact_nodes_list = NULL;
2047d3c69ad0SToby Isaac     const char       *citation           = NULL;
2048d3c69ad0SToby Isaac     PetscBool        *cited              = NULL;
2049d3c69ad0SToby Isaac 
2050d3c69ad0SToby Isaac     switch (dim) {
2051d3c69ad0SToby Isaac     case 2:
2052d3c69ad0SToby Isaac       cited              = &MinSymTriQuadCite;
2053d3c69ad0SToby Isaac       citation           = MinSymTriQuadCitation;
2054d3c69ad0SToby Isaac       max_degree         = PetscDTWVTriQuad_max_degree;
2055d3c69ad0SToby Isaac       nodes_per_type     = PetscDTWVTriQuad_num_orbits;
2056d3c69ad0SToby Isaac       all_num_full_nodes = PetscDTWVTriQuad_num_nodes;
2057d3c69ad0SToby Isaac       weights_list       = PetscDTWVTriQuad_weights;
2058d3c69ad0SToby Isaac       compact_nodes_list = PetscDTWVTriQuad_orbits;
2059d3c69ad0SToby Isaac       break;
2060d3c69ad0SToby Isaac     case 3:
2061d3c69ad0SToby Isaac       cited              = &MinSymTetQuadCite;
2062d3c69ad0SToby Isaac       citation           = MinSymTetQuadCitation;
2063d3c69ad0SToby Isaac       max_degree         = PetscDTJSTetQuad_max_degree;
2064d3c69ad0SToby Isaac       nodes_per_type     = PetscDTJSTetQuad_num_orbits;
2065d3c69ad0SToby Isaac       all_num_full_nodes = PetscDTJSTetQuad_num_nodes;
2066d3c69ad0SToby Isaac       weights_list       = PetscDTJSTetQuad_weights;
2067d3c69ad0SToby Isaac       compact_nodes_list = PetscDTJSTetQuad_orbits;
2068d3c69ad0SToby Isaac       break;
2069*d71ae5a4SJacob Faibussowitsch     default:
2070*d71ae5a4SJacob Faibussowitsch       max_degree = -1;
2071*d71ae5a4SJacob Faibussowitsch       break;
2072d3c69ad0SToby Isaac     }
2073d3c69ad0SToby Isaac 
2074d3c69ad0SToby Isaac     if (degree > max_degree) {
2075d3c69ad0SToby Isaac       if (orig_type == PETSCDTSIMPLEXQUAD_DEFAULT) {
2076d3c69ad0SToby Isaac         // fall back to conic
2077d3c69ad0SToby Isaac         PetscCall(PetscDTSimplexQuadrature(dim, degree, PETSCDTSIMPLEXQUAD_CONIC, quad));
2078d3c69ad0SToby Isaac         PetscFunctionReturn(0);
2079d3c69ad0SToby Isaac       } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Minimal symmetric quadrature for dim %" PetscInt_FMT ", degree %" PetscInt_FMT " unsupported", dim, degree);
2080d3c69ad0SToby Isaac     }
2081d3c69ad0SToby Isaac 
2082d3c69ad0SToby Isaac     PetscCall(PetscCitationsRegister(citation, cited));
2083d3c69ad0SToby Isaac 
2084d3c69ad0SToby Isaac     PetscCall(PetscDTPartitionNumber(n, &p));
2085d3c69ad0SToby Isaac     for (PetscInt d = 2; d <= n; d++) fact *= d;
2086d3c69ad0SToby Isaac 
2087d3c69ad0SToby Isaac     PetscInt         num_full_nodes      = all_num_full_nodes[degree];
2088d3c69ad0SToby Isaac     const PetscReal *all_compact_nodes   = compact_nodes_list[degree];
2089d3c69ad0SToby Isaac     const PetscReal *all_compact_weights = weights_list[degree];
2090d3c69ad0SToby Isaac     nodes_per_type                       = &nodes_per_type[p * degree];
2091d3c69ad0SToby Isaac 
2092d3c69ad0SToby Isaac     PetscReal      *points;
2093d3c69ad0SToby Isaac     PetscReal      *counts;
2094d3c69ad0SToby Isaac     PetscReal      *weights;
2095d3c69ad0SToby Isaac     PetscReal      *bary_to_biunit; // row-major transformation of barycentric coordinate to biunit
2096d3c69ad0SToby Isaac     PetscQuadrature q;
2097d3c69ad0SToby Isaac 
2098d3c69ad0SToby Isaac     // compute the transformation
2099d3c69ad0SToby Isaac     PetscCall(PetscMalloc1(n * dim, &bary_to_biunit));
2100d3c69ad0SToby Isaac     for (PetscInt d = 0; d < dim; d++) {
2101ad540459SPierre Jolivet       for (PetscInt b = 0; b < n; b++) bary_to_biunit[d * n + b] = (d == b) ? 1.0 : -1.0;
2102d3c69ad0SToby Isaac     }
2103d3c69ad0SToby Isaac 
2104d3c69ad0SToby Isaac     PetscCall(PetscMalloc3(n, &part, n, &perm, n, &counts));
2105d3c69ad0SToby Isaac     PetscCall(PetscCalloc1(num_full_nodes * dim, &points));
2106d3c69ad0SToby Isaac     PetscCall(PetscMalloc1(num_full_nodes, &weights));
2107d3c69ad0SToby Isaac 
2108d3c69ad0SToby Isaac     // (0, 0, ...) is the first partition lexicographically
2109d3c69ad0SToby Isaac     PetscCall(PetscArrayzero(part, n));
2110d3c69ad0SToby Isaac     PetscCall(PetscArrayzero(counts, n));
2111d3c69ad0SToby Isaac     counts[0] = n;
2112d3c69ad0SToby Isaac 
2113d3c69ad0SToby Isaac     // for each partition
2114d3c69ad0SToby Isaac     for (PetscInt s = 0, node_offset = 0; s < p; s++) {
2115d3c69ad0SToby Isaac       PetscInt num_compact_coords = part[n - 1] + 1;
2116d3c69ad0SToby Isaac 
2117d3c69ad0SToby Isaac       const PetscReal *compact_nodes   = all_compact_nodes;
2118d3c69ad0SToby Isaac       const PetscReal *compact_weights = all_compact_weights;
2119d3c69ad0SToby Isaac       all_compact_nodes += num_compact_coords * nodes_per_type[s];
2120d3c69ad0SToby Isaac       all_compact_weights += nodes_per_type[s];
2121d3c69ad0SToby Isaac 
2122d3c69ad0SToby Isaac       // for every permutation of the vertices
2123d3c69ad0SToby Isaac       for (PetscInt f = 0; f < fact; f++) {
2124d3c69ad0SToby Isaac         PetscCall(PetscDTEnumPerm(n, f, perm, NULL));
2125d3c69ad0SToby Isaac 
2126d3c69ad0SToby Isaac         // check if it is a valid permutation
2127d3c69ad0SToby Isaac         PetscInt digit;
2128d3c69ad0SToby Isaac         for (digit = 1; digit < n; digit++) {
2129d3c69ad0SToby Isaac           // skip permutations that would duplicate a node because it has a smaller symmetry group
2130d3c69ad0SToby Isaac           if (part[digit - 1] == part[digit] && perm[digit - 1] > perm[digit]) break;
2131d3c69ad0SToby Isaac         }
2132d3c69ad0SToby Isaac         if (digit < n) continue;
2133d3c69ad0SToby Isaac 
2134d3c69ad0SToby Isaac         // create full nodes from this permutation of the compact nodes
2135d3c69ad0SToby Isaac         PetscReal *full_nodes   = &points[node_offset * dim];
2136d3c69ad0SToby Isaac         PetscReal *full_weights = &weights[node_offset];
2137d3c69ad0SToby Isaac 
2138d3c69ad0SToby Isaac         PetscCall(PetscArraycpy(full_weights, compact_weights, nodes_per_type[s]));
2139d3c69ad0SToby Isaac         for (PetscInt b = 0; b < n; b++) {
2140d3c69ad0SToby Isaac           for (PetscInt d = 0; d < dim; d++) {
2141ad540459SPierre Jolivet             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]];
2142d3c69ad0SToby Isaac           }
2143d3c69ad0SToby Isaac         }
2144d3c69ad0SToby Isaac         node_offset += nodes_per_type[s];
2145d3c69ad0SToby Isaac       }
2146d3c69ad0SToby Isaac 
2147d3c69ad0SToby Isaac       if (s < p - 1) { // Generate the next partition
2148d3c69ad0SToby Isaac         /* A partition is described by the number of coordinates that are in
2149d3c69ad0SToby Isaac          * each set of duplicates (counts) and redundantly by mapping each
2150d3c69ad0SToby Isaac          * index to its set of duplicates (part)
2151d3c69ad0SToby Isaac          *
2152d3c69ad0SToby Isaac          * Counts should always be in nonincreasing order
2153d3c69ad0SToby Isaac          *
2154d3c69ad0SToby Isaac          * We want to generate the partitions lexically by part, which means
2155d3c69ad0SToby Isaac          * finding the last index where count > 1 and reducing by 1.
2156d3c69ad0SToby Isaac          *
2157d3c69ad0SToby Isaac          * For the new counts beyond that index, we eagerly assign the remaining
2158d3c69ad0SToby Isaac          * capacity of the partition to smaller indices (ensures lexical ordering),
2159d3c69ad0SToby Isaac          * while respecting the nonincreasing invariant of the counts
2160d3c69ad0SToby Isaac          */
2161d3c69ad0SToby Isaac         PetscInt last_digit            = part[n - 1];
2162d3c69ad0SToby Isaac         PetscInt last_digit_with_extra = last_digit;
2163d3c69ad0SToby Isaac         while (counts[last_digit_with_extra] == 1) last_digit_with_extra--;
2164d3c69ad0SToby Isaac         PetscInt limit               = --counts[last_digit_with_extra];
2165d3c69ad0SToby Isaac         PetscInt total_to_distribute = last_digit - last_digit_with_extra + 1;
2166d3c69ad0SToby Isaac         for (PetscInt digit = last_digit_with_extra + 1; digit < n; digit++) {
2167d3c69ad0SToby Isaac           counts[digit] = PetscMin(limit, total_to_distribute);
2168d3c69ad0SToby Isaac           total_to_distribute -= PetscMin(limit, total_to_distribute);
2169d3c69ad0SToby Isaac         }
2170d3c69ad0SToby Isaac         for (PetscInt digit = 0, offset = 0; digit < n; digit++) {
2171d3c69ad0SToby Isaac           PetscInt count = counts[digit];
2172ad540459SPierre Jolivet           for (PetscInt c = 0; c < count; c++) part[offset++] = digit;
2173d3c69ad0SToby Isaac         }
2174d3c69ad0SToby Isaac       }
2175d3c69ad0SToby Isaac     }
2176d3c69ad0SToby Isaac     PetscCall(PetscFree3(part, perm, counts));
2177d3c69ad0SToby Isaac     PetscCall(PetscFree(bary_to_biunit));
2178d3c69ad0SToby Isaac     PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, &q));
2179d3c69ad0SToby Isaac     PetscCall(PetscQuadratureSetData(q, dim, 1, num_full_nodes, points, weights));
2180d3c69ad0SToby Isaac     *quad = q;
2181d3c69ad0SToby Isaac   }
2182d3c69ad0SToby Isaac   PetscFunctionReturn(0);
2183d3c69ad0SToby Isaac }
2184d3c69ad0SToby Isaac 
2185f5f57ec0SBarry Smith /*@
2186b3c0f97bSTom Klotz   PetscDTTanhSinhTensorQuadrature - create tanh-sinh quadrature for a tensor product cell
2187b3c0f97bSTom Klotz 
2188b3c0f97bSTom Klotz   Not Collective
2189b3c0f97bSTom Klotz 
21904165533cSJose E. Roman   Input Parameters:
2191b3c0f97bSTom Klotz + dim   - The cell dimension
2192b3c0f97bSTom Klotz . level - The number of points in one dimension, 2^l
2193b3c0f97bSTom Klotz . a     - left end of interval (often-1)
2194b3c0f97bSTom Klotz - b     - right end of interval (often +1)
2195b3c0f97bSTom Klotz 
21964165533cSJose E. Roman   Output Parameter:
2197b3c0f97bSTom Klotz . q - A PetscQuadrature object
2198b3c0f97bSTom Klotz 
2199b3c0f97bSTom Klotz   Level: intermediate
2200b3c0f97bSTom Klotz 
2201db781477SPatrick Sanan .seealso: `PetscDTGaussTensorQuadrature()`
2202b3c0f97bSTom Klotz @*/
2203*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTTanhSinhTensorQuadrature(PetscInt dim, PetscInt level, PetscReal a, PetscReal b, PetscQuadrature *q)
2204*d71ae5a4SJacob Faibussowitsch {
2205b3c0f97bSTom Klotz   const PetscInt  p     = 16;                        /* Digits of precision in the evaluation */
2206b3c0f97bSTom Klotz   const PetscReal alpha = (b - a) / 2.;              /* Half-width of the integration interval */
2207b3c0f97bSTom Klotz   const PetscReal beta  = (b + a) / 2.;              /* Center of the integration interval */
2208b3c0f97bSTom Klotz   const PetscReal h     = PetscPowReal(2.0, -level); /* Step size, length between x_k */
2209d84b4d08SMatthew G. Knepley   PetscReal       xk;                                /* Quadrature point x_k on reference domain [-1, 1] */
2210b3c0f97bSTom Klotz   PetscReal       wk = 0.5 * PETSC_PI;               /* Quadrature weight at x_k */
2211b3c0f97bSTom Klotz   PetscReal      *x, *w;
2212b3c0f97bSTom Klotz   PetscInt        K, k, npoints;
2213b3c0f97bSTom Klotz 
2214b3c0f97bSTom Klotz   PetscFunctionBegin;
221563a3b9bcSJacob Faibussowitsch   PetscCheck(dim <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Dimension %" PetscInt_FMT " not yet implemented", dim);
221628b400f6SJacob Faibussowitsch   PetscCheck(level, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a number of significant digits");
2217b3c0f97bSTom Klotz   /* Find K such that the weights are < 32 digits of precision */
2218ad540459SPierre Jolivet   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)));
22199566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
22209566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetOrder(*q, 2 * K + 1));
2221b3c0f97bSTom Klotz   npoints = 2 * K - 1;
22229566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(npoints * dim, &x));
22239566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(npoints, &w));
2224b3c0f97bSTom Klotz   /* Center term */
2225b3c0f97bSTom Klotz   x[0] = beta;
2226b3c0f97bSTom Klotz   w[0] = 0.5 * alpha * PETSC_PI;
2227b3c0f97bSTom Klotz   for (k = 1; k < K; ++k) {
22289add2064SThomas Klotz     wk           = 0.5 * alpha * h * PETSC_PI * PetscCoshReal(k * h) / PetscSqr(PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(k * h)));
22291118d4bcSLisandro Dalcin     xk           = PetscTanhReal(0.5 * PETSC_PI * PetscSinhReal(k * h));
2230b3c0f97bSTom Klotz     x[2 * k - 1] = -alpha * xk + beta;
2231b3c0f97bSTom Klotz     w[2 * k - 1] = wk;
2232b3c0f97bSTom Klotz     x[2 * k + 0] = alpha * xk + beta;
2233b3c0f97bSTom Klotz     w[2 * k + 0] = wk;
2234b3c0f97bSTom Klotz   }
22359566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetData(*q, dim, 1, npoints, x, w));
2236b3c0f97bSTom Klotz   PetscFunctionReturn(0);
2237b3c0f97bSTom Klotz }
2238b3c0f97bSTom Klotz 
2239*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTTanhSinhIntegrate(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, void *ctx, PetscReal *sol)
2240*d71ae5a4SJacob Faibussowitsch {
2241b3c0f97bSTom Klotz   const PetscInt  p     = 16;           /* Digits of precision in the evaluation */
2242b3c0f97bSTom Klotz   const PetscReal alpha = (b - a) / 2.; /* Half-width of the integration interval */
2243b3c0f97bSTom Klotz   const PetscReal beta  = (b + a) / 2.; /* Center of the integration interval */
2244b3c0f97bSTom Klotz   PetscReal       h     = 1.0;          /* Step size, length between x_k */
2245b3c0f97bSTom Klotz   PetscInt        l     = 0;            /* Level of refinement, h = 2^{-l} */
2246b3c0f97bSTom Klotz   PetscReal       osum  = 0.0;          /* Integral on last level */
2247b3c0f97bSTom Klotz   PetscReal       psum  = 0.0;          /* Integral on the level before the last level */
2248b3c0f97bSTom Klotz   PetscReal       sum;                  /* Integral on current level */
2249446c295cSMatthew G. Knepley   PetscReal       yk;                   /* Quadrature point 1 - x_k on reference domain [-1, 1] */
2250b3c0f97bSTom Klotz   PetscReal       lx, rx;               /* Quadrature points to the left and right of 0 on the real domain [a, b] */
2251b3c0f97bSTom Klotz   PetscReal       wk;                   /* Quadrature weight at x_k */
2252b3c0f97bSTom Klotz   PetscReal       lval, rval;           /* Terms in the quadature sum to the left and right of 0 */
2253b3c0f97bSTom Klotz   PetscInt        d;                    /* Digits of precision in the integral */
2254b3c0f97bSTom Klotz 
2255b3c0f97bSTom Klotz   PetscFunctionBegin;
225608401ef6SPierre Jolivet   PetscCheck(digits > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
2257b3c0f97bSTom Klotz   /* Center term */
2258d6685f55SMatthew G. Knepley   func(&beta, ctx, &lval);
2259b3c0f97bSTom Klotz   sum = 0.5 * alpha * PETSC_PI * lval;
2260b3c0f97bSTom Klotz   /* */
2261b3c0f97bSTom Klotz   do {
2262b3c0f97bSTom Klotz     PetscReal lterm, rterm, maxTerm = 0.0, d1, d2, d3, d4;
2263b3c0f97bSTom Klotz     PetscInt  k = 1;
2264b3c0f97bSTom Klotz 
2265b3c0f97bSTom Klotz     ++l;
226663a3b9bcSJacob Faibussowitsch     /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %" PetscInt_FMT " sum: %15.15f\n", l, sum); */
2267b3c0f97bSTom Klotz     /* At each level of refinement, h --> h/2 and sum --> sum/2 */
2268b3c0f97bSTom Klotz     psum = osum;
2269b3c0f97bSTom Klotz     osum = sum;
2270b3c0f97bSTom Klotz     h *= 0.5;
2271b3c0f97bSTom Klotz     sum *= 0.5;
2272b3c0f97bSTom Klotz     do {
22739add2064SThomas Klotz       wk = 0.5 * h * PETSC_PI * PetscCoshReal(k * h) / PetscSqr(PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(k * h)));
2274446c295cSMatthew G. Knepley       yk = 1.0 / (PetscExpReal(0.5 * PETSC_PI * PetscSinhReal(k * h)) * PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(k * h)));
2275446c295cSMatthew G. Knepley       lx = -alpha * (1.0 - yk) + beta;
2276446c295cSMatthew G. Knepley       rx = alpha * (1.0 - yk) + beta;
2277d6685f55SMatthew G. Knepley       func(&lx, ctx, &lval);
2278d6685f55SMatthew G. Knepley       func(&rx, ctx, &rval);
2279b3c0f97bSTom Klotz       lterm   = alpha * wk * lval;
2280b3c0f97bSTom Klotz       maxTerm = PetscMax(PetscAbsReal(lterm), maxTerm);
2281b3c0f97bSTom Klotz       sum += lterm;
2282b3c0f97bSTom Klotz       rterm   = alpha * wk * rval;
2283b3c0f97bSTom Klotz       maxTerm = PetscMax(PetscAbsReal(rterm), maxTerm);
2284b3c0f97bSTom Klotz       sum += rterm;
2285b3c0f97bSTom Klotz       ++k;
2286b3c0f97bSTom Klotz       /* Only need to evaluate every other point on refined levels */
2287b3c0f97bSTom Klotz       if (l != 1) ++k;
22889add2064SThomas Klotz     } while (PetscAbsReal(PetscLog10Real(wk)) < p); /* Only need to evaluate sum until weights are < 32 digits of precision */
2289b3c0f97bSTom Klotz 
2290b3c0f97bSTom Klotz     d1 = PetscLog10Real(PetscAbsReal(sum - osum));
2291b3c0f97bSTom Klotz     d2 = PetscLog10Real(PetscAbsReal(sum - psum));
2292b3c0f97bSTom Klotz     d3 = PetscLog10Real(maxTerm) - p;
229309d48545SBarry Smith     if (PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)) == 0.0) d4 = 0.0;
229409d48545SBarry Smith     else d4 = PetscLog10Real(PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)));
2295b3c0f97bSTom Klotz     d = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1) / d2, 2 * d1), d3), d4)));
22969add2064SThomas Klotz   } while (d < digits && l < 12);
2297b3c0f97bSTom Klotz   *sol = sum;
2298e510cb1fSThomas Klotz 
2299b3c0f97bSTom Klotz   PetscFunctionReturn(0);
2300b3c0f97bSTom Klotz }
2301b3c0f97bSTom Klotz 
2302497880caSRichard Tran Mills #if defined(PETSC_HAVE_MPFR)
2303*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, void *ctx, PetscReal *sol)
2304*d71ae5a4SJacob Faibussowitsch {
2305e510cb1fSThomas Klotz   const PetscInt safetyFactor = 2; /* Calculate abcissa until 2*p digits */
230629f144ccSMatthew G. Knepley   PetscInt       l            = 0; /* Level of refinement, h = 2^{-l} */
230729f144ccSMatthew G. Knepley   mpfr_t         alpha;            /* Half-width of the integration interval */
230829f144ccSMatthew G. Knepley   mpfr_t         beta;             /* Center of the integration interval */
230929f144ccSMatthew G. Knepley   mpfr_t         h;                /* Step size, length between x_k */
231029f144ccSMatthew G. Knepley   mpfr_t         osum;             /* Integral on last level */
231129f144ccSMatthew G. Knepley   mpfr_t         psum;             /* Integral on the level before the last level */
231229f144ccSMatthew G. Knepley   mpfr_t         sum;              /* Integral on current level */
231329f144ccSMatthew G. Knepley   mpfr_t         yk;               /* Quadrature point 1 - x_k on reference domain [-1, 1] */
231429f144ccSMatthew G. Knepley   mpfr_t         lx, rx;           /* Quadrature points to the left and right of 0 on the real domain [a, b] */
231529f144ccSMatthew G. Knepley   mpfr_t         wk;               /* Quadrature weight at x_k */
23161fbc92bbSMatthew G. Knepley   PetscReal      lval, rval, rtmp; /* Terms in the quadature sum to the left and right of 0 */
231729f144ccSMatthew G. Knepley   PetscInt       d;                /* Digits of precision in the integral */
231829f144ccSMatthew G. Knepley   mpfr_t         pi2, kh, msinh, mcosh, maxTerm, curTerm, tmp;
231929f144ccSMatthew G. Knepley 
232029f144ccSMatthew G. Knepley   PetscFunctionBegin;
232108401ef6SPierre Jolivet   PetscCheck(digits > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
232229f144ccSMatthew G. Knepley   /* Create high precision storage */
2323c9f744b5SMatthew G. Knepley   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);
232429f144ccSMatthew G. Knepley   /* Initialization */
232529f144ccSMatthew G. Knepley   mpfr_set_d(alpha, 0.5 * (b - a), MPFR_RNDN);
232629f144ccSMatthew G. Knepley   mpfr_set_d(beta, 0.5 * (b + a), MPFR_RNDN);
232729f144ccSMatthew G. Knepley   mpfr_set_d(osum, 0.0, MPFR_RNDN);
232829f144ccSMatthew G. Knepley   mpfr_set_d(psum, 0.0, MPFR_RNDN);
232929f144ccSMatthew G. Knepley   mpfr_set_d(h, 1.0, MPFR_RNDN);
233029f144ccSMatthew G. Knepley   mpfr_const_pi(pi2, MPFR_RNDN);
233129f144ccSMatthew G. Knepley   mpfr_mul_d(pi2, pi2, 0.5, MPFR_RNDN);
233229f144ccSMatthew G. Knepley   /* Center term */
23331fbc92bbSMatthew G. Knepley   rtmp = 0.5 * (b + a);
23341fbc92bbSMatthew G. Knepley   func(&rtmp, ctx, &lval);
233529f144ccSMatthew G. Knepley   mpfr_set(sum, pi2, MPFR_RNDN);
233629f144ccSMatthew G. Knepley   mpfr_mul(sum, sum, alpha, MPFR_RNDN);
233729f144ccSMatthew G. Knepley   mpfr_mul_d(sum, sum, lval, MPFR_RNDN);
233829f144ccSMatthew G. Knepley   /* */
233929f144ccSMatthew G. Knepley   do {
234029f144ccSMatthew G. Knepley     PetscReal d1, d2, d3, d4;
234129f144ccSMatthew G. Knepley     PetscInt  k = 1;
234229f144ccSMatthew G. Knepley 
234329f144ccSMatthew G. Knepley     ++l;
234429f144ccSMatthew G. Knepley     mpfr_set_d(maxTerm, 0.0, MPFR_RNDN);
234563a3b9bcSJacob Faibussowitsch     /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %" PetscInt_FMT " sum: %15.15f\n", l, sum); */
234629f144ccSMatthew G. Knepley     /* At each level of refinement, h --> h/2 and sum --> sum/2 */
234729f144ccSMatthew G. Knepley     mpfr_set(psum, osum, MPFR_RNDN);
234829f144ccSMatthew G. Knepley     mpfr_set(osum, sum, MPFR_RNDN);
234929f144ccSMatthew G. Knepley     mpfr_mul_d(h, h, 0.5, MPFR_RNDN);
235029f144ccSMatthew G. Knepley     mpfr_mul_d(sum, sum, 0.5, MPFR_RNDN);
235129f144ccSMatthew G. Knepley     do {
235229f144ccSMatthew G. Knepley       mpfr_set_si(kh, k, MPFR_RNDN);
235329f144ccSMatthew G. Knepley       mpfr_mul(kh, kh, h, MPFR_RNDN);
235429f144ccSMatthew G. Knepley       /* Weight */
235529f144ccSMatthew G. Knepley       mpfr_set(wk, h, MPFR_RNDN);
235629f144ccSMatthew G. Knepley       mpfr_sinh_cosh(msinh, mcosh, kh, MPFR_RNDN);
235729f144ccSMatthew G. Knepley       mpfr_mul(msinh, msinh, pi2, MPFR_RNDN);
235829f144ccSMatthew G. Knepley       mpfr_mul(mcosh, mcosh, pi2, MPFR_RNDN);
235929f144ccSMatthew G. Knepley       mpfr_cosh(tmp, msinh, MPFR_RNDN);
236029f144ccSMatthew G. Knepley       mpfr_sqr(tmp, tmp, MPFR_RNDN);
236129f144ccSMatthew G. Knepley       mpfr_mul(wk, wk, mcosh, MPFR_RNDN);
236229f144ccSMatthew G. Knepley       mpfr_div(wk, wk, tmp, MPFR_RNDN);
236329f144ccSMatthew G. Knepley       /* Abscissa */
236429f144ccSMatthew G. Knepley       mpfr_set_d(yk, 1.0, MPFR_RNDZ);
236529f144ccSMatthew G. Knepley       mpfr_cosh(tmp, msinh, MPFR_RNDN);
236629f144ccSMatthew G. Knepley       mpfr_div(yk, yk, tmp, MPFR_RNDZ);
236729f144ccSMatthew G. Knepley       mpfr_exp(tmp, msinh, MPFR_RNDN);
236829f144ccSMatthew G. Knepley       mpfr_div(yk, yk, tmp, MPFR_RNDZ);
236929f144ccSMatthew G. Knepley       /* Quadrature points */
237029f144ccSMatthew G. Knepley       mpfr_sub_d(lx, yk, 1.0, MPFR_RNDZ);
237129f144ccSMatthew G. Knepley       mpfr_mul(lx, lx, alpha, MPFR_RNDU);
237229f144ccSMatthew G. Knepley       mpfr_add(lx, lx, beta, MPFR_RNDU);
237329f144ccSMatthew G. Knepley       mpfr_d_sub(rx, 1.0, yk, MPFR_RNDZ);
237429f144ccSMatthew G. Knepley       mpfr_mul(rx, rx, alpha, MPFR_RNDD);
237529f144ccSMatthew G. Knepley       mpfr_add(rx, rx, beta, MPFR_RNDD);
237629f144ccSMatthew G. Knepley       /* Evaluation */
23771fbc92bbSMatthew G. Knepley       rtmp = mpfr_get_d(lx, MPFR_RNDU);
23781fbc92bbSMatthew G. Knepley       func(&rtmp, ctx, &lval);
23791fbc92bbSMatthew G. Knepley       rtmp = mpfr_get_d(rx, MPFR_RNDD);
23801fbc92bbSMatthew G. Knepley       func(&rtmp, ctx, &rval);
238129f144ccSMatthew G. Knepley       /* Update */
238229f144ccSMatthew G. Knepley       mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
238329f144ccSMatthew G. Knepley       mpfr_mul_d(tmp, tmp, lval, MPFR_RNDN);
238429f144ccSMatthew G. Knepley       mpfr_add(sum, sum, tmp, MPFR_RNDN);
238529f144ccSMatthew G. Knepley       mpfr_abs(tmp, tmp, MPFR_RNDN);
238629f144ccSMatthew G. Knepley       mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
238729f144ccSMatthew G. Knepley       mpfr_set(curTerm, tmp, MPFR_RNDN);
238829f144ccSMatthew G. Knepley       mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
238929f144ccSMatthew G. Knepley       mpfr_mul_d(tmp, tmp, rval, MPFR_RNDN);
239029f144ccSMatthew G. Knepley       mpfr_add(sum, sum, tmp, MPFR_RNDN);
239129f144ccSMatthew G. Knepley       mpfr_abs(tmp, tmp, MPFR_RNDN);
239229f144ccSMatthew G. Knepley       mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
239329f144ccSMatthew G. Knepley       mpfr_max(curTerm, curTerm, tmp, MPFR_RNDN);
239429f144ccSMatthew G. Knepley       ++k;
239529f144ccSMatthew G. Knepley       /* Only need to evaluate every other point on refined levels */
239629f144ccSMatthew G. Knepley       if (l != 1) ++k;
239729f144ccSMatthew G. Knepley       mpfr_log10(tmp, wk, MPFR_RNDN);
239829f144ccSMatthew G. Knepley       mpfr_abs(tmp, tmp, MPFR_RNDN);
2399c9f744b5SMatthew G. Knepley     } while (mpfr_get_d(tmp, MPFR_RNDN) < safetyFactor * digits); /* Only need to evaluate sum until weights are < 32 digits of precision */
240029f144ccSMatthew G. Knepley     mpfr_sub(tmp, sum, osum, MPFR_RNDN);
240129f144ccSMatthew G. Knepley     mpfr_abs(tmp, tmp, MPFR_RNDN);
240229f144ccSMatthew G. Knepley     mpfr_log10(tmp, tmp, MPFR_RNDN);
240329f144ccSMatthew G. Knepley     d1 = mpfr_get_d(tmp, MPFR_RNDN);
240429f144ccSMatthew G. Knepley     mpfr_sub(tmp, sum, psum, MPFR_RNDN);
240529f144ccSMatthew G. Knepley     mpfr_abs(tmp, tmp, MPFR_RNDN);
240629f144ccSMatthew G. Knepley     mpfr_log10(tmp, tmp, MPFR_RNDN);
240729f144ccSMatthew G. Knepley     d2 = mpfr_get_d(tmp, MPFR_RNDN);
240829f144ccSMatthew G. Knepley     mpfr_log10(tmp, maxTerm, MPFR_RNDN);
2409c9f744b5SMatthew G. Knepley     d3 = mpfr_get_d(tmp, MPFR_RNDN) - digits;
241029f144ccSMatthew G. Knepley     mpfr_log10(tmp, curTerm, MPFR_RNDN);
241129f144ccSMatthew G. Knepley     d4 = mpfr_get_d(tmp, MPFR_RNDN);
241229f144ccSMatthew G. Knepley     d  = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1) / d2, 2 * d1), d3), d4)));
2413b0649871SThomas Klotz   } while (d < digits && l < 8);
241429f144ccSMatthew G. Knepley   *sol = mpfr_get_d(sum, MPFR_RNDN);
241529f144ccSMatthew G. Knepley   /* Cleanup */
241629f144ccSMatthew G. Knepley   mpfr_clears(alpha, beta, h, sum, osum, psum, yk, wk, lx, rx, tmp, maxTerm, curTerm, pi2, kh, msinh, mcosh, NULL);
241729f144ccSMatthew G. Knepley   PetscFunctionReturn(0);
241829f144ccSMatthew G. Knepley }
2419d525116cSMatthew G. Knepley #else
2420fbfcfee5SBarry Smith 
2421*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, void *ctx, PetscReal *sol)
2422*d71ae5a4SJacob Faibussowitsch {
2423d525116cSMatthew G. Knepley   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "This method will not work without MPFR. Reconfigure using --download-mpfr --download-gmp");
2424d525116cSMatthew G. Knepley }
242529f144ccSMatthew G. Knepley #endif
242629f144ccSMatthew G. Knepley 
24272df84da0SMatthew G. Knepley /*@
24282df84da0SMatthew G. Knepley   PetscDTTensorQuadratureCreate - create the tensor product quadrature from two lower-dimensional quadratures
24292df84da0SMatthew G. Knepley 
24302df84da0SMatthew G. Knepley   Not Collective
24312df84da0SMatthew G. Knepley 
24322df84da0SMatthew G. Knepley   Input Parameters:
24332df84da0SMatthew G. Knepley + q1 - The first quadrature
24342df84da0SMatthew G. Knepley - q2 - The second quadrature
24352df84da0SMatthew G. Knepley 
24362df84da0SMatthew G. Knepley   Output Parameter:
24372df84da0SMatthew G. Knepley . q - A PetscQuadrature object
24382df84da0SMatthew G. Knepley 
24392df84da0SMatthew G. Knepley   Level: intermediate
24402df84da0SMatthew G. Knepley 
2441db781477SPatrick Sanan .seealso: `PetscDTGaussTensorQuadrature()`
24422df84da0SMatthew G. Knepley @*/
2443*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTTensorQuadratureCreate(PetscQuadrature q1, PetscQuadrature q2, PetscQuadrature *q)
2444*d71ae5a4SJacob Faibussowitsch {
24452df84da0SMatthew G. Knepley   const PetscReal *x1, *w1, *x2, *w2;
24462df84da0SMatthew G. Knepley   PetscReal       *x, *w;
24472df84da0SMatthew G. Knepley   PetscInt         dim1, Nc1, Np1, order1, qa, d1;
24482df84da0SMatthew G. Knepley   PetscInt         dim2, Nc2, Np2, order2, qb, d2;
24492df84da0SMatthew G. Knepley   PetscInt         dim, Nc, Np, order, qc, d;
24502df84da0SMatthew G. Knepley 
24512df84da0SMatthew G. Knepley   PetscFunctionBegin;
24522df84da0SMatthew G. Knepley   PetscValidHeaderSpecific(q1, PETSCQUADRATURE_CLASSID, 1);
24532df84da0SMatthew G. Knepley   PetscValidHeaderSpecific(q2, PETSCQUADRATURE_CLASSID, 2);
24542df84da0SMatthew G. Knepley   PetscValidPointer(q, 3);
24559566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureGetOrder(q1, &order1));
24569566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureGetOrder(q2, &order2));
24572df84da0SMatthew G. Knepley   PetscCheck(order1 == order2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Order1 %" PetscInt_FMT " != %" PetscInt_FMT " Order2", order1, order2);
24589566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureGetData(q1, &dim1, &Nc1, &Np1, &x1, &w1));
24599566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureGetData(q2, &dim2, &Nc2, &Np2, &x2, &w2));
24602df84da0SMatthew G. Knepley   PetscCheck(Nc1 == Nc2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "NumComp1 %" PetscInt_FMT " != %" PetscInt_FMT " NumComp2", Nc1, Nc2);
24612df84da0SMatthew G. Knepley 
24622df84da0SMatthew G. Knepley   dim   = dim1 + dim2;
24632df84da0SMatthew G. Knepley   Nc    = Nc1;
24642df84da0SMatthew G. Knepley   Np    = Np1 * Np2;
24652df84da0SMatthew G. Knepley   order = order1;
24669566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
24679566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetOrder(*q, order));
24689566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(Np * dim, &x));
24699566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(Np, &w));
24702df84da0SMatthew G. Knepley   for (qa = 0, qc = 0; qa < Np1; ++qa) {
24712df84da0SMatthew G. Knepley     for (qb = 0; qb < Np2; ++qb, ++qc) {
2472ad540459SPierre Jolivet       for (d1 = 0, d = 0; d1 < dim1; ++d1, ++d) x[qc * dim + d] = x1[qa * dim1 + d1];
2473ad540459SPierre Jolivet       for (d2 = 0; d2 < dim2; ++d2, ++d) x[qc * dim + d] = x2[qb * dim2 + d2];
24742df84da0SMatthew G. Knepley       w[qc] = w1[qa] * w2[qb];
24752df84da0SMatthew G. Knepley     }
24762df84da0SMatthew G. Knepley   }
24779566063dSJacob Faibussowitsch   PetscCall(PetscQuadratureSetData(*q, dim, Nc, Np, x, w));
24782df84da0SMatthew G. Knepley   PetscFunctionReturn(0);
24792df84da0SMatthew G. Knepley }
24802df84da0SMatthew G. Knepley 
2481194825f6SJed Brown /* Overwrites A. Can only handle full-rank problems with m>=n
2482194825f6SJed Brown  * A in column-major format
2483194825f6SJed Brown  * Ainv in row-major format
2484194825f6SJed Brown  * tau has length m
2485194825f6SJed Brown  * worksize must be >= max(1,n)
2486194825f6SJed Brown  */
2487*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTPseudoInverseQR(PetscInt m, PetscInt mstride, PetscInt n, PetscReal *A_in, PetscReal *Ainv_out, PetscScalar *tau, PetscInt worksize, PetscScalar *work)
2488*d71ae5a4SJacob Faibussowitsch {
2489194825f6SJed Brown   PetscBLASInt M, N, K, lda, ldb, ldwork, info;
2490194825f6SJed Brown   PetscScalar *A, *Ainv, *R, *Q, Alpha;
2491194825f6SJed Brown 
2492194825f6SJed Brown   PetscFunctionBegin;
2493194825f6SJed Brown #if defined(PETSC_USE_COMPLEX)
2494194825f6SJed Brown   {
2495194825f6SJed Brown     PetscInt i, j;
24969566063dSJacob Faibussowitsch     PetscCall(PetscMalloc2(m * n, &A, m * n, &Ainv));
2497194825f6SJed Brown     for (j = 0; j < n; j++) {
2498194825f6SJed Brown       for (i = 0; i < m; i++) A[i + m * j] = A_in[i + mstride * j];
2499194825f6SJed Brown     }
2500194825f6SJed Brown     mstride = m;
2501194825f6SJed Brown   }
2502194825f6SJed Brown #else
2503194825f6SJed Brown   A = A_in;
2504194825f6SJed Brown   Ainv = Ainv_out;
2505194825f6SJed Brown #endif
2506194825f6SJed Brown 
25079566063dSJacob Faibussowitsch   PetscCall(PetscBLASIntCast(m, &M));
25089566063dSJacob Faibussowitsch   PetscCall(PetscBLASIntCast(n, &N));
25099566063dSJacob Faibussowitsch   PetscCall(PetscBLASIntCast(mstride, &lda));
25109566063dSJacob Faibussowitsch   PetscCall(PetscBLASIntCast(worksize, &ldwork));
25119566063dSJacob Faibussowitsch   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
2512792fecdfSBarry Smith   PetscCallBLAS("LAPACKgeqrf", LAPACKgeqrf_(&M, &N, A, &lda, tau, work, &ldwork, &info));
25139566063dSJacob Faibussowitsch   PetscCall(PetscFPTrapPop());
251428b400f6SJacob Faibussowitsch   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "xGEQRF error");
2515194825f6SJed Brown   R = A; /* Upper triangular part of A now contains R, the rest contains the elementary reflectors */
2516194825f6SJed Brown 
2517194825f6SJed Brown   /* Extract an explicit representation of Q */
2518194825f6SJed Brown   Q = Ainv;
25199566063dSJacob Faibussowitsch   PetscCall(PetscArraycpy(Q, A, mstride * n));
2520194825f6SJed Brown   K = N; /* full rank */
2521792fecdfSBarry Smith   PetscCallBLAS("LAPACKorgqr", LAPACKorgqr_(&M, &N, &K, Q, &lda, tau, work, &ldwork, &info));
252228b400f6SJacob Faibussowitsch   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "xORGQR/xUNGQR error");
2523194825f6SJed Brown 
2524194825f6SJed Brown   /* Compute A^{-T} = (R^{-1} Q^T)^T = Q R^{-T} */
2525194825f6SJed Brown   Alpha = 1.0;
2526194825f6SJed Brown   ldb   = lda;
2527792fecdfSBarry Smith   PetscCallBLAS("BLAStrsm", BLAStrsm_("Right", "Upper", "ConjugateTranspose", "NotUnitTriangular", &M, &N, &Alpha, R, &lda, Q, &ldb));
2528194825f6SJed Brown   /* Ainv is Q, overwritten with inverse */
2529194825f6SJed Brown 
2530194825f6SJed Brown #if defined(PETSC_USE_COMPLEX)
2531194825f6SJed Brown   {
2532194825f6SJed Brown     PetscInt i;
2533194825f6SJed Brown     for (i = 0; i < m * n; i++) Ainv_out[i] = PetscRealPart(Ainv[i]);
25349566063dSJacob Faibussowitsch     PetscCall(PetscFree2(A, Ainv));
2535194825f6SJed Brown   }
2536194825f6SJed Brown #endif
2537194825f6SJed Brown   PetscFunctionReturn(0);
2538194825f6SJed Brown }
2539194825f6SJed Brown 
2540194825f6SJed Brown /* Computes integral of L_p' over intervals {(x0,x1),(x1,x2),...} */
2541*d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDTLegendreIntegrate(PetscInt ninterval, const PetscReal *x, PetscInt ndegree, const PetscInt *degrees, PetscBool Transpose, PetscReal *B)
2542*d71ae5a4SJacob Faibussowitsch {
2543194825f6SJed Brown   PetscReal *Bv;
2544194825f6SJed Brown   PetscInt   i, j;
2545194825f6SJed Brown 
2546194825f6SJed Brown   PetscFunctionBegin;
25479566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1((ninterval + 1) * ndegree, &Bv));
2548194825f6SJed Brown   /* Point evaluation of L_p on all the source vertices */
25499566063dSJacob Faibussowitsch   PetscCall(PetscDTLegendreEval(ninterval + 1, x, ndegree, degrees, Bv, NULL, NULL));
2550194825f6SJed Brown   /* Integral over each interval: \int_a^b L_p' = L_p(b)-L_p(a) */
2551194825f6SJed Brown   for (i = 0; i < ninterval; i++) {
2552194825f6SJed Brown     for (j = 0; j < ndegree; j++) {
2553194825f6SJed Brown       if (Transpose) B[i + ninterval * j] = Bv[(i + 1) * ndegree + j] - Bv[i * ndegree + j];
2554194825f6SJed Brown       else B[i * ndegree + j] = Bv[(i + 1) * ndegree + j] - Bv[i * ndegree + j];
2555194825f6SJed Brown     }
2556194825f6SJed Brown   }
25579566063dSJacob Faibussowitsch   PetscCall(PetscFree(Bv));
2558194825f6SJed Brown   PetscFunctionReturn(0);
2559194825f6SJed Brown }
2560194825f6SJed Brown 
2561194825f6SJed Brown /*@
2562194825f6SJed Brown    PetscDTReconstructPoly - create matrix representing polynomial reconstruction using cell intervals and evaluation at target intervals
2563194825f6SJed Brown 
2564194825f6SJed Brown    Not Collective
2565194825f6SJed Brown 
25664165533cSJose E. Roman    Input Parameters:
2567194825f6SJed Brown +  degree - degree of reconstruction polynomial
2568194825f6SJed Brown .  nsource - number of source intervals
2569194825f6SJed Brown .  sourcex - sorted coordinates of source cell boundaries (length nsource+1)
2570194825f6SJed Brown .  ntarget - number of target intervals
2571194825f6SJed Brown -  targetx - sorted coordinates of target cell boundaries (length ntarget+1)
2572194825f6SJed Brown 
25734165533cSJose E. Roman    Output Parameter:
2574194825f6SJed Brown .  R - reconstruction matrix, utarget = sum_s R[t*nsource+s] * usource[s]
2575194825f6SJed Brown 
2576194825f6SJed Brown    Level: advanced
2577194825f6SJed Brown 
2578db781477SPatrick Sanan .seealso: `PetscDTLegendreEval()`
2579194825f6SJed Brown @*/
2580*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTReconstructPoly(PetscInt degree, PetscInt nsource, const PetscReal *sourcex, PetscInt ntarget, const PetscReal *targetx, PetscReal *R)
2581*d71ae5a4SJacob Faibussowitsch {
2582194825f6SJed Brown   PetscInt     i, j, k, *bdegrees, worksize;
2583194825f6SJed Brown   PetscReal    xmin, xmax, center, hscale, *sourcey, *targety, *Bsource, *Bsinv, *Btarget;
2584194825f6SJed Brown   PetscScalar *tau, *work;
2585194825f6SJed Brown 
2586194825f6SJed Brown   PetscFunctionBegin;
2587194825f6SJed Brown   PetscValidRealPointer(sourcex, 3);
2588194825f6SJed Brown   PetscValidRealPointer(targetx, 5);
2589194825f6SJed Brown   PetscValidRealPointer(R, 6);
259063a3b9bcSJacob Faibussowitsch   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);
259176bd3646SJed Brown   if (PetscDefined(USE_DEBUG)) {
2592ad540459SPierre Jolivet     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]);
2593ad540459SPierre Jolivet     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]);
259476bd3646SJed Brown   }
2595194825f6SJed Brown   xmin     = PetscMin(sourcex[0], targetx[0]);
2596194825f6SJed Brown   xmax     = PetscMax(sourcex[nsource], targetx[ntarget]);
2597194825f6SJed Brown   center   = (xmin + xmax) / 2;
2598194825f6SJed Brown   hscale   = (xmax - xmin) / 2;
2599194825f6SJed Brown   worksize = nsource;
26009566063dSJacob Faibussowitsch   PetscCall(PetscMalloc4(degree + 1, &bdegrees, nsource + 1, &sourcey, nsource * (degree + 1), &Bsource, worksize, &work));
26019566063dSJacob Faibussowitsch   PetscCall(PetscMalloc4(nsource, &tau, nsource * (degree + 1), &Bsinv, ntarget + 1, &targety, ntarget * (degree + 1), &Btarget));
2602194825f6SJed Brown   for (i = 0; i <= nsource; i++) sourcey[i] = (sourcex[i] - center) / hscale;
2603194825f6SJed Brown   for (i = 0; i <= degree; i++) bdegrees[i] = i + 1;
26049566063dSJacob Faibussowitsch   PetscCall(PetscDTLegendreIntegrate(nsource, sourcey, degree + 1, bdegrees, PETSC_TRUE, Bsource));
26059566063dSJacob Faibussowitsch   PetscCall(PetscDTPseudoInverseQR(nsource, nsource, degree + 1, Bsource, Bsinv, tau, nsource, work));
2606194825f6SJed Brown   for (i = 0; i <= ntarget; i++) targety[i] = (targetx[i] - center) / hscale;
26079566063dSJacob Faibussowitsch   PetscCall(PetscDTLegendreIntegrate(ntarget, targety, degree + 1, bdegrees, PETSC_FALSE, Btarget));
2608194825f6SJed Brown   for (i = 0; i < ntarget; i++) {
2609194825f6SJed Brown     PetscReal rowsum = 0;
2610194825f6SJed Brown     for (j = 0; j < nsource; j++) {
2611194825f6SJed Brown       PetscReal sum = 0;
2612ad540459SPierre Jolivet       for (k = 0; k < degree + 1; k++) sum += Btarget[i * (degree + 1) + k] * Bsinv[k * nsource + j];
2613194825f6SJed Brown       R[i * nsource + j] = sum;
2614194825f6SJed Brown       rowsum += sum;
2615194825f6SJed Brown     }
2616194825f6SJed Brown     for (j = 0; j < nsource; j++) R[i * nsource + j] /= rowsum; /* normalize each row */
2617194825f6SJed Brown   }
26189566063dSJacob Faibussowitsch   PetscCall(PetscFree4(bdegrees, sourcey, Bsource, work));
26199566063dSJacob Faibussowitsch   PetscCall(PetscFree4(tau, Bsinv, targety, Btarget));
2620194825f6SJed Brown   PetscFunctionReturn(0);
2621194825f6SJed Brown }
2622916e780bShannah_mairs 
2623916e780bShannah_mairs /*@C
2624916e780bShannah_mairs    PetscGaussLobattoLegendreIntegrate - Compute the L2 integral of a function on the GLL points
2625916e780bShannah_mairs 
2626916e780bShannah_mairs    Not Collective
2627916e780bShannah_mairs 
2628d8d19677SJose E. Roman    Input Parameters:
2629916e780bShannah_mairs +  n - the number of GLL nodes
2630916e780bShannah_mairs .  nodes - the GLL nodes
2631916e780bShannah_mairs .  weights - the GLL weights
2632f0fc11ceSJed Brown -  f - the function values at the nodes
2633916e780bShannah_mairs 
2634916e780bShannah_mairs    Output Parameter:
2635916e780bShannah_mairs .  in - the value of the integral
2636916e780bShannah_mairs 
2637916e780bShannah_mairs    Level: beginner
2638916e780bShannah_mairs 
2639db781477SPatrick Sanan .seealso: `PetscDTGaussLobattoLegendreQuadrature()`
2640916e780bShannah_mairs 
2641916e780bShannah_mairs @*/
2642*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscGaussLobattoLegendreIntegrate(PetscInt n, PetscReal *nodes, PetscReal *weights, const PetscReal *f, PetscReal *in)
2643*d71ae5a4SJacob Faibussowitsch {
2644916e780bShannah_mairs   PetscInt i;
2645916e780bShannah_mairs 
2646916e780bShannah_mairs   PetscFunctionBegin;
2647916e780bShannah_mairs   *in = 0.;
2648ad540459SPierre Jolivet   for (i = 0; i < n; i++) *in += f[i] * f[i] * weights[i];
2649916e780bShannah_mairs   PetscFunctionReturn(0);
2650916e780bShannah_mairs }
2651916e780bShannah_mairs 
2652916e780bShannah_mairs /*@C
2653916e780bShannah_mairs    PetscGaussLobattoLegendreElementLaplacianCreate - computes the Laplacian for a single 1d GLL element
2654916e780bShannah_mairs 
2655916e780bShannah_mairs    Not Collective
2656916e780bShannah_mairs 
2657d8d19677SJose E. Roman    Input Parameters:
2658916e780bShannah_mairs +  n - the number of GLL nodes
2659916e780bShannah_mairs .  nodes - the GLL nodes
2660f0fc11ceSJed Brown -  weights - the GLL weights
2661916e780bShannah_mairs 
2662916e780bShannah_mairs    Output Parameter:
2663916e780bShannah_mairs .  A - the stiffness element
2664916e780bShannah_mairs 
2665916e780bShannah_mairs    Level: beginner
2666916e780bShannah_mairs 
2667916e780bShannah_mairs    Notes:
2668916e780bShannah_mairs     Destroy this with PetscGaussLobattoLegendreElementLaplacianDestroy()
2669916e780bShannah_mairs 
2670916e780bShannah_mairs    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)
2671916e780bShannah_mairs 
2672db781477SPatrick Sanan .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianDestroy()`
2673916e780bShannah_mairs 
2674916e780bShannah_mairs @*/
2675*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscGaussLobattoLegendreElementLaplacianCreate(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA)
2676*d71ae5a4SJacob Faibussowitsch {
2677916e780bShannah_mairs   PetscReal      **A;
2678916e780bShannah_mairs   const PetscReal *gllnodes = nodes;
2679916e780bShannah_mairs   const PetscInt   p        = n - 1;
2680916e780bShannah_mairs   PetscReal        z0, z1, z2 = -1, x, Lpj, Lpr;
2681916e780bShannah_mairs   PetscInt         i, j, nn, r;
2682916e780bShannah_mairs 
2683916e780bShannah_mairs   PetscFunctionBegin;
26849566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(n, &A));
26859566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(n * n, &A[0]));
2686916e780bShannah_mairs   for (i = 1; i < n; i++) A[i] = A[i - 1] + n;
2687916e780bShannah_mairs 
2688916e780bShannah_mairs   for (j = 1; j < p; j++) {
2689916e780bShannah_mairs     x  = gllnodes[j];
2690916e780bShannah_mairs     z0 = 1.;
2691916e780bShannah_mairs     z1 = x;
2692916e780bShannah_mairs     for (nn = 1; nn < p; nn++) {
2693916e780bShannah_mairs       z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
2694916e780bShannah_mairs       z0 = z1;
2695916e780bShannah_mairs       z1 = z2;
2696916e780bShannah_mairs     }
2697916e780bShannah_mairs     Lpj = z2;
2698916e780bShannah_mairs     for (r = 1; r < p; r++) {
2699916e780bShannah_mairs       if (r == j) {
2700916e780bShannah_mairs         A[j][j] = 2. / (3. * (1. - gllnodes[j] * gllnodes[j]) * Lpj * Lpj);
2701916e780bShannah_mairs       } else {
2702916e780bShannah_mairs         x  = gllnodes[r];
2703916e780bShannah_mairs         z0 = 1.;
2704916e780bShannah_mairs         z1 = x;
2705916e780bShannah_mairs         for (nn = 1; nn < p; nn++) {
2706916e780bShannah_mairs           z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
2707916e780bShannah_mairs           z0 = z1;
2708916e780bShannah_mairs           z1 = z2;
2709916e780bShannah_mairs         }
2710916e780bShannah_mairs         Lpr     = z2;
2711916e780bShannah_mairs         A[r][j] = 4. / (((PetscReal)p) * (((PetscReal)p) + 1.) * Lpj * Lpr * (gllnodes[j] - gllnodes[r]) * (gllnodes[j] - gllnodes[r]));
2712916e780bShannah_mairs       }
2713916e780bShannah_mairs     }
2714916e780bShannah_mairs   }
2715916e780bShannah_mairs   for (j = 1; j < p + 1; j++) {
2716916e780bShannah_mairs     x  = gllnodes[j];
2717916e780bShannah_mairs     z0 = 1.;
2718916e780bShannah_mairs     z1 = x;
2719916e780bShannah_mairs     for (nn = 1; nn < p; nn++) {
2720916e780bShannah_mairs       z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
2721916e780bShannah_mairs       z0 = z1;
2722916e780bShannah_mairs       z1 = z2;
2723916e780bShannah_mairs     }
2724916e780bShannah_mairs     Lpj     = z2;
2725916e780bShannah_mairs     A[j][0] = 4. * PetscPowRealInt(-1., p) / (((PetscReal)p) * (((PetscReal)p) + 1.) * Lpj * (1. + gllnodes[j]) * (1. + gllnodes[j]));
2726916e780bShannah_mairs     A[0][j] = A[j][0];
2727916e780bShannah_mairs   }
2728916e780bShannah_mairs   for (j = 0; j < p; j++) {
2729916e780bShannah_mairs     x  = gllnodes[j];
2730916e780bShannah_mairs     z0 = 1.;
2731916e780bShannah_mairs     z1 = x;
2732916e780bShannah_mairs     for (nn = 1; nn < p; nn++) {
2733916e780bShannah_mairs       z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
2734916e780bShannah_mairs       z0 = z1;
2735916e780bShannah_mairs       z1 = z2;
2736916e780bShannah_mairs     }
2737916e780bShannah_mairs     Lpj = z2;
2738916e780bShannah_mairs 
2739916e780bShannah_mairs     A[p][j] = 4. / (((PetscReal)p) * (((PetscReal)p) + 1.) * Lpj * (1. - gllnodes[j]) * (1. - gllnodes[j]));
2740916e780bShannah_mairs     A[j][p] = A[p][j];
2741916e780bShannah_mairs   }
2742916e780bShannah_mairs   A[0][0] = 0.5 + (((PetscReal)p) * (((PetscReal)p) + 1.) - 2.) / 6.;
2743916e780bShannah_mairs   A[p][p] = A[0][0];
2744916e780bShannah_mairs   *AA     = A;
2745916e780bShannah_mairs   PetscFunctionReturn(0);
2746916e780bShannah_mairs }
2747916e780bShannah_mairs 
2748916e780bShannah_mairs /*@C
2749916e780bShannah_mairs    PetscGaussLobattoLegendreElementLaplacianDestroy - frees the Laplacian for a single 1d GLL element
2750916e780bShannah_mairs 
2751916e780bShannah_mairs    Not Collective
2752916e780bShannah_mairs 
2753d8d19677SJose E. Roman    Input Parameters:
2754916e780bShannah_mairs +  n - the number of GLL nodes
2755916e780bShannah_mairs .  nodes - the GLL nodes
2756916e780bShannah_mairs .  weights - the GLL weightss
2757916e780bShannah_mairs -  A - the stiffness element
2758916e780bShannah_mairs 
2759916e780bShannah_mairs    Level: beginner
2760916e780bShannah_mairs 
2761db781477SPatrick Sanan .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`
2762916e780bShannah_mairs 
2763916e780bShannah_mairs @*/
2764*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscGaussLobattoLegendreElementLaplacianDestroy(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA)
2765*d71ae5a4SJacob Faibussowitsch {
2766916e780bShannah_mairs   PetscFunctionBegin;
27679566063dSJacob Faibussowitsch   PetscCall(PetscFree((*AA)[0]));
27689566063dSJacob Faibussowitsch   PetscCall(PetscFree(*AA));
2769916e780bShannah_mairs   *AA = NULL;
2770916e780bShannah_mairs   PetscFunctionReturn(0);
2771916e780bShannah_mairs }
2772916e780bShannah_mairs 
2773916e780bShannah_mairs /*@C
2774916e780bShannah_mairs    PetscGaussLobattoLegendreElementGradientCreate - computes the gradient for a single 1d GLL element
2775916e780bShannah_mairs 
2776916e780bShannah_mairs    Not Collective
2777916e780bShannah_mairs 
2778916e780bShannah_mairs    Input Parameter:
2779916e780bShannah_mairs +  n - the number of GLL nodes
2780916e780bShannah_mairs .  nodes - the GLL nodes
2781916e780bShannah_mairs .  weights - the GLL weights
2782916e780bShannah_mairs 
2783d8d19677SJose E. Roman    Output Parameters:
2784916e780bShannah_mairs .  AA - the stiffness element
2785916e780bShannah_mairs -  AAT - the transpose of AA (pass in NULL if you do not need this array)
2786916e780bShannah_mairs 
2787916e780bShannah_mairs    Level: beginner
2788916e780bShannah_mairs 
2789916e780bShannah_mairs    Notes:
2790916e780bShannah_mairs     Destroy this with PetscGaussLobattoLegendreElementGradientDestroy()
2791916e780bShannah_mairs 
2792916e780bShannah_mairs    You can access entries in these arrays with AA[i][j] but in memory it is stored in contiguous memory, row oriented
2793916e780bShannah_mairs 
2794db781477SPatrick Sanan .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianDestroy()`
2795916e780bShannah_mairs 
2796916e780bShannah_mairs @*/
2797*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscGaussLobattoLegendreElementGradientCreate(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA, PetscReal ***AAT)
2798*d71ae5a4SJacob Faibussowitsch {
2799916e780bShannah_mairs   PetscReal      **A, **AT = NULL;
2800916e780bShannah_mairs   const PetscReal *gllnodes = nodes;
2801916e780bShannah_mairs   const PetscInt   p        = n - 1;
2802e6a796c3SToby Isaac   PetscReal        Li, Lj, d0;
2803916e780bShannah_mairs   PetscInt         i, j;
2804916e780bShannah_mairs 
2805916e780bShannah_mairs   PetscFunctionBegin;
28069566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(n, &A));
28079566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(n * n, &A[0]));
2808916e780bShannah_mairs   for (i = 1; i < n; i++) A[i] = A[i - 1] + n;
2809916e780bShannah_mairs 
2810916e780bShannah_mairs   if (AAT) {
28119566063dSJacob Faibussowitsch     PetscCall(PetscMalloc1(n, &AT));
28129566063dSJacob Faibussowitsch     PetscCall(PetscMalloc1(n * n, &AT[0]));
2813916e780bShannah_mairs     for (i = 1; i < n; i++) AT[i] = AT[i - 1] + n;
2814916e780bShannah_mairs   }
2815916e780bShannah_mairs 
2816ad540459SPierre Jolivet   if (n == 1) A[0][0] = 0.;
2817916e780bShannah_mairs   d0 = (PetscReal)p * ((PetscReal)p + 1.) / 4.;
2818916e780bShannah_mairs   for (i = 0; i < n; i++) {
2819916e780bShannah_mairs     for (j = 0; j < n; j++) {
2820916e780bShannah_mairs       A[i][j] = 0.;
28219566063dSJacob Faibussowitsch       PetscCall(PetscDTComputeJacobi(0., 0., p, gllnodes[i], &Li));
28229566063dSJacob Faibussowitsch       PetscCall(PetscDTComputeJacobi(0., 0., p, gllnodes[j], &Lj));
2823916e780bShannah_mairs       if (i != j) A[i][j] = Li / (Lj * (gllnodes[i] - gllnodes[j]));
2824916e780bShannah_mairs       if ((j == i) && (i == 0)) A[i][j] = -d0;
2825916e780bShannah_mairs       if (j == i && i == p) A[i][j] = d0;
2826916e780bShannah_mairs       if (AT) AT[j][i] = A[i][j];
2827916e780bShannah_mairs     }
2828916e780bShannah_mairs   }
2829916e780bShannah_mairs   if (AAT) *AAT = AT;
2830916e780bShannah_mairs   *AA = A;
2831916e780bShannah_mairs   PetscFunctionReturn(0);
2832916e780bShannah_mairs }
2833916e780bShannah_mairs 
2834916e780bShannah_mairs /*@C
2835916e780bShannah_mairs    PetscGaussLobattoLegendreElementGradientDestroy - frees the gradient for a single 1d GLL element obtained with PetscGaussLobattoLegendreElementGradientCreate()
2836916e780bShannah_mairs 
2837916e780bShannah_mairs    Not Collective
2838916e780bShannah_mairs 
2839d8d19677SJose E. Roman    Input Parameters:
2840916e780bShannah_mairs +  n - the number of GLL nodes
2841916e780bShannah_mairs .  nodes - the GLL nodes
2842916e780bShannah_mairs .  weights - the GLL weights
2843916e780bShannah_mairs .  AA - the stiffness element
2844916e780bShannah_mairs -  AAT - the transpose of the element
2845916e780bShannah_mairs 
2846916e780bShannah_mairs    Level: beginner
2847916e780bShannah_mairs 
2848db781477SPatrick Sanan .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`, `PetscGaussLobattoLegendreElementAdvectionCreate()`
2849916e780bShannah_mairs 
2850916e780bShannah_mairs @*/
2851*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscGaussLobattoLegendreElementGradientDestroy(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA, PetscReal ***AAT)
2852*d71ae5a4SJacob Faibussowitsch {
2853916e780bShannah_mairs   PetscFunctionBegin;
28549566063dSJacob Faibussowitsch   PetscCall(PetscFree((*AA)[0]));
28559566063dSJacob Faibussowitsch   PetscCall(PetscFree(*AA));
2856916e780bShannah_mairs   *AA = NULL;
2857916e780bShannah_mairs   if (*AAT) {
28589566063dSJacob Faibussowitsch     PetscCall(PetscFree((*AAT)[0]));
28599566063dSJacob Faibussowitsch     PetscCall(PetscFree(*AAT));
2860916e780bShannah_mairs     *AAT = NULL;
2861916e780bShannah_mairs   }
2862916e780bShannah_mairs   PetscFunctionReturn(0);
2863916e780bShannah_mairs }
2864916e780bShannah_mairs 
2865916e780bShannah_mairs /*@C
2866916e780bShannah_mairs    PetscGaussLobattoLegendreElementAdvectionCreate - computes the advection operator for a single 1d GLL element
2867916e780bShannah_mairs 
2868916e780bShannah_mairs    Not Collective
2869916e780bShannah_mairs 
2870d8d19677SJose E. Roman    Input Parameters:
2871916e780bShannah_mairs +  n - the number of GLL nodes
2872916e780bShannah_mairs .  nodes - the GLL nodes
2873f0fc11ceSJed Brown -  weights - the GLL weightss
2874916e780bShannah_mairs 
2875916e780bShannah_mairs    Output Parameter:
2876916e780bShannah_mairs .  AA - the stiffness element
2877916e780bShannah_mairs 
2878916e780bShannah_mairs    Level: beginner
2879916e780bShannah_mairs 
2880916e780bShannah_mairs    Notes:
2881916e780bShannah_mairs     Destroy this with PetscGaussLobattoLegendreElementAdvectionDestroy()
2882916e780bShannah_mairs 
2883916e780bShannah_mairs    This is the same as the Gradient operator multiplied by the diagonal mass matrix
2884916e780bShannah_mairs 
2885916e780bShannah_mairs    You can access entries in this array with AA[i][j] but in memory it is stored in contiguous memory, row oriented
2886916e780bShannah_mairs 
2887db781477SPatrick Sanan .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`, `PetscGaussLobattoLegendreElementAdvectionDestroy()`
2888916e780bShannah_mairs 
2889916e780bShannah_mairs @*/
2890*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscGaussLobattoLegendreElementAdvectionCreate(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA)
2891*d71ae5a4SJacob Faibussowitsch {
2892916e780bShannah_mairs   PetscReal      **D;
2893916e780bShannah_mairs   const PetscReal *gllweights = weights;
2894916e780bShannah_mairs   const PetscInt   glln       = n;
2895916e780bShannah_mairs   PetscInt         i, j;
2896916e780bShannah_mairs 
2897916e780bShannah_mairs   PetscFunctionBegin;
28989566063dSJacob Faibussowitsch   PetscCall(PetscGaussLobattoLegendreElementGradientCreate(n, nodes, weights, &D, NULL));
2899916e780bShannah_mairs   for (i = 0; i < glln; i++) {
2900ad540459SPierre Jolivet     for (j = 0; j < glln; j++) D[i][j] = gllweights[i] * D[i][j];
2901916e780bShannah_mairs   }
2902916e780bShannah_mairs   *AA = D;
2903916e780bShannah_mairs   PetscFunctionReturn(0);
2904916e780bShannah_mairs }
2905916e780bShannah_mairs 
2906916e780bShannah_mairs /*@C
2907916e780bShannah_mairs    PetscGaussLobattoLegendreElementAdvectionDestroy - frees the advection stiffness for a single 1d GLL element
2908916e780bShannah_mairs 
2909916e780bShannah_mairs    Not Collective
2910916e780bShannah_mairs 
2911d8d19677SJose E. Roman    Input Parameters:
2912916e780bShannah_mairs +  n - the number of GLL nodes
2913916e780bShannah_mairs .  nodes - the GLL nodes
2914916e780bShannah_mairs .  weights - the GLL weights
2915916e780bShannah_mairs -  A - advection
2916916e780bShannah_mairs 
2917916e780bShannah_mairs    Level: beginner
2918916e780bShannah_mairs 
2919db781477SPatrick Sanan .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementAdvectionCreate()`
2920916e780bShannah_mairs 
2921916e780bShannah_mairs @*/
2922*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscGaussLobattoLegendreElementAdvectionDestroy(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA)
2923*d71ae5a4SJacob Faibussowitsch {
2924916e780bShannah_mairs   PetscFunctionBegin;
29259566063dSJacob Faibussowitsch   PetscCall(PetscFree((*AA)[0]));
29269566063dSJacob Faibussowitsch   PetscCall(PetscFree(*AA));
2927916e780bShannah_mairs   *AA = NULL;
2928916e780bShannah_mairs   PetscFunctionReturn(0);
2929916e780bShannah_mairs }
2930916e780bShannah_mairs 
2931*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscGaussLobattoLegendreElementMassCreate(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA)
2932*d71ae5a4SJacob Faibussowitsch {
2933916e780bShannah_mairs   PetscReal      **A;
2934916e780bShannah_mairs   const PetscReal *gllweights = weights;
2935916e780bShannah_mairs   const PetscInt   glln       = n;
2936916e780bShannah_mairs   PetscInt         i, j;
2937916e780bShannah_mairs 
2938916e780bShannah_mairs   PetscFunctionBegin;
29399566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(glln, &A));
29409566063dSJacob Faibussowitsch   PetscCall(PetscMalloc1(glln * glln, &A[0]));
2941916e780bShannah_mairs   for (i = 1; i < glln; i++) A[i] = A[i - 1] + glln;
2942ad540459SPierre Jolivet   if (glln == 1) A[0][0] = 0.;
2943916e780bShannah_mairs   for (i = 0; i < glln; i++) {
2944916e780bShannah_mairs     for (j = 0; j < glln; j++) {
2945916e780bShannah_mairs       A[i][j] = 0.;
2946916e780bShannah_mairs       if (j == i) A[i][j] = gllweights[i];
2947916e780bShannah_mairs     }
2948916e780bShannah_mairs   }
2949916e780bShannah_mairs   *AA = A;
2950916e780bShannah_mairs   PetscFunctionReturn(0);
2951916e780bShannah_mairs }
2952916e780bShannah_mairs 
2953*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscGaussLobattoLegendreElementMassDestroy(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA)
2954*d71ae5a4SJacob Faibussowitsch {
2955916e780bShannah_mairs   PetscFunctionBegin;
29569566063dSJacob Faibussowitsch   PetscCall(PetscFree((*AA)[0]));
29579566063dSJacob Faibussowitsch   PetscCall(PetscFree(*AA));
2958916e780bShannah_mairs   *AA = NULL;
2959916e780bShannah_mairs   PetscFunctionReturn(0);
2960916e780bShannah_mairs }
2961d4afb720SToby Isaac 
2962d4afb720SToby Isaac /*@
2963d4afb720SToby Isaac   PetscDTIndexToBary - convert an index into a barycentric coordinate.
2964d4afb720SToby Isaac 
2965d4afb720SToby Isaac   Input Parameters:
2966d4afb720SToby Isaac + 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)
2967d4afb720SToby Isaac . sum - the value that the sum of the barycentric coordinates (which will be non-negative integers) should sum to
2968d4afb720SToby Isaac - index - the index to convert: should be >= 0 and < Binomial(len - 1 + sum, sum)
2969d4afb720SToby Isaac 
2970d4afb720SToby Isaac   Output Parameter:
2971d4afb720SToby Isaac . coord - will be filled with the barycentric coordinate
2972d4afb720SToby Isaac 
2973d4afb720SToby Isaac   Level: beginner
2974d4afb720SToby Isaac 
2975d4afb720SToby Isaac   Note: the indices map to barycentric coordinates in lexicographic order, where the first index is the
2976d4afb720SToby Isaac   least significant and the last index is the most significant.
2977d4afb720SToby Isaac 
2978db781477SPatrick Sanan .seealso: `PetscDTBaryToIndex()`
2979d4afb720SToby Isaac @*/
2980*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTIndexToBary(PetscInt len, PetscInt sum, PetscInt index, PetscInt coord[])
2981*d71ae5a4SJacob Faibussowitsch {
2982d4afb720SToby Isaac   PetscInt c, d, s, total, subtotal, nexttotal;
2983d4afb720SToby Isaac 
2984d4afb720SToby Isaac   PetscFunctionBeginHot;
298508401ef6SPierre Jolivet   PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
298608401ef6SPierre Jolivet   PetscCheck(index >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index must be non-negative");
2987d4afb720SToby Isaac   if (!len) {
2988d4afb720SToby Isaac     if (!sum && !index) PetscFunctionReturn(0);
2989d4afb720SToby Isaac     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid index or sum for length 0 barycentric coordinate");
2990d4afb720SToby Isaac   }
2991d4afb720SToby Isaac   for (c = 1, total = 1; c <= len; c++) {
2992d4afb720SToby Isaac     /* total is the number of ways to have a tuple of length c with sum */
2993d4afb720SToby Isaac     if (index < total) break;
2994d4afb720SToby Isaac     total = (total * (sum + c)) / c;
2995d4afb720SToby Isaac   }
299608401ef6SPierre Jolivet   PetscCheck(c <= len, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index out of range");
2997d4afb720SToby Isaac   for (d = c; d < len; d++) coord[d] = 0;
2998d4afb720SToby Isaac   for (s = 0, subtotal = 1, nexttotal = 1; c > 0;) {
2999d4afb720SToby Isaac     /* subtotal is the number of ways to have a tuple of length c with sum s */
3000d4afb720SToby Isaac     /* nexttotal is the number of ways to have a tuple of length c-1 with sum s */
3001d4afb720SToby Isaac     if ((index + subtotal) >= total) {
3002d4afb720SToby Isaac       coord[--c] = sum - s;
3003d4afb720SToby Isaac       index -= (total - subtotal);
3004d4afb720SToby Isaac       sum       = s;
3005d4afb720SToby Isaac       total     = nexttotal;
3006d4afb720SToby Isaac       subtotal  = 1;
3007d4afb720SToby Isaac       nexttotal = 1;
3008d4afb720SToby Isaac       s         = 0;
3009d4afb720SToby Isaac     } else {
3010d4afb720SToby Isaac       subtotal  = (subtotal * (c + s)) / (s + 1);
3011d4afb720SToby Isaac       nexttotal = (nexttotal * (c - 1 + s)) / (s + 1);
3012d4afb720SToby Isaac       s++;
3013d4afb720SToby Isaac     }
3014d4afb720SToby Isaac   }
3015d4afb720SToby Isaac   PetscFunctionReturn(0);
3016d4afb720SToby Isaac }
3017d4afb720SToby Isaac 
3018d4afb720SToby Isaac /*@
3019d4afb720SToby Isaac   PetscDTBaryToIndex - convert a barycentric coordinate to an index
3020d4afb720SToby Isaac 
3021d4afb720SToby Isaac   Input Parameters:
3022d4afb720SToby Isaac + 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)
3023d4afb720SToby Isaac . sum - the value that the sum of the barycentric coordinates (which will be non-negative integers) should sum to
3024d4afb720SToby Isaac - coord - a barycentric coordinate with the given length and sum
3025d4afb720SToby Isaac 
3026d4afb720SToby Isaac   Output Parameter:
3027d4afb720SToby Isaac . index - the unique index for the coordinate, >= 0 and < Binomial(len - 1 + sum, sum)
3028d4afb720SToby Isaac 
3029d4afb720SToby Isaac   Level: beginner
3030d4afb720SToby Isaac 
3031d4afb720SToby Isaac   Note: the indices map to barycentric coordinates in lexicographic order, where the first index is the
3032d4afb720SToby Isaac   least significant and the last index is the most significant.
3033d4afb720SToby Isaac 
3034db781477SPatrick Sanan .seealso: `PetscDTIndexToBary`
3035d4afb720SToby Isaac @*/
3036*d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDTBaryToIndex(PetscInt len, PetscInt sum, const PetscInt coord[], PetscInt *index)
3037*d71ae5a4SJacob Faibussowitsch {
3038d4afb720SToby Isaac   PetscInt c;
3039d4afb720SToby Isaac   PetscInt i;
3040d4afb720SToby Isaac   PetscInt total;
3041d4afb720SToby Isaac 
3042d4afb720SToby Isaac   PetscFunctionBeginHot;
304308401ef6SPierre Jolivet   PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
3044d4afb720SToby Isaac   if (!len) {
3045d4afb720SToby Isaac     if (!sum) {
3046d4afb720SToby Isaac       *index = 0;
3047d4afb720SToby Isaac       PetscFunctionReturn(0);
3048d4afb720SToby Isaac     }
3049d4afb720SToby Isaac     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid index or sum for length 0 barycentric coordinate");
3050d4afb720SToby Isaac   }
3051d4afb720SToby Isaac   for (c = 1, total = 1; c < len; c++) total = (total * (sum + c)) / c;
3052d4afb720SToby Isaac   i = total - 1;
3053d4afb720SToby Isaac   c = len - 1;
3054d4afb720SToby Isaac   sum -= coord[c];
3055d4afb720SToby Isaac   while (sum > 0) {
3056d4afb720SToby Isaac     PetscInt subtotal;
3057d4afb720SToby Isaac     PetscInt s;
3058d4afb720SToby Isaac 
3059d4afb720SToby Isaac     for (s = 1, subtotal = 1; s < sum; s++) subtotal = (subtotal * (c + s)) / s;
3060d4afb720SToby Isaac     i -= subtotal;
3061d4afb720SToby Isaac     sum -= coord[--c];
3062d4afb720SToby Isaac   }
3063d4afb720SToby Isaac   *index = i;
3064d4afb720SToby Isaac   PetscFunctionReturn(0);
3065d4afb720SToby Isaac }
3066