Actual source code: dt.c
petsc-3.13.6 2020-09-29
1: /* Discretization tools */
3: #include <petscdt.h>
4: #include <petscblaslapack.h>
5: #include <petsc/private/petscimpl.h>
6: #include <petsc/private/dtimpl.h>
7: #include <petscviewer.h>
8: #include <petscdmplex.h>
9: #include <petscdmshell.h>
11: #if defined(PETSC_HAVE_MPFR)
12: #include <mpfr.h>
13: #endif
15: const char *const PetscDTNodeTypes[] = {"gaussjacobi", "equispaced", "tanhsinh", "PETSCDTNODES_", 0};
17: static PetscBool GolubWelschCite = PETSC_FALSE;
18: const char GolubWelschCitation[] = "@article{GolubWelsch1969,\n"
19: " author = {Golub and Welsch},\n"
20: " title = {Calculation of Quadrature Rules},\n"
21: " journal = {Math. Comp.},\n"
22: " volume = {23},\n"
23: " number = {106},\n"
24: " pages = {221--230},\n"
25: " year = {1969}\n}\n";
27: /* Numerical tests in src/dm/dt/tests/ex1.c show that when computing the nodes and weights of Gauss-Jacobi
28: quadrature rules:
30: - in double precision, Newton's method and Golub & Welsch both work for moderate degrees (< 100),
31: - in single precision, Newton's method starts producing incorrect roots around n = 15, but
32: the weights from Golub & Welsch become a problem before then: they produces errors
33: in computing the Jacobi-polynomial Gram matrix around n = 6.
35: So we default to Newton's method (required fewer dependencies) */
36: PetscBool PetscDTGaussQuadratureNewton_Internal = PETSC_TRUE;
38: PetscClassId PETSCQUADRATURE_CLASSID = 0;
40: /*@
41: PetscQuadratureCreate - Create a PetscQuadrature object
43: Collective
45: Input Parameter:
46: . comm - The communicator for the PetscQuadrature object
48: Output Parameter:
49: . q - The PetscQuadrature object
51: Level: beginner
53: .seealso: PetscQuadratureDestroy(), PetscQuadratureGetData()
54: @*/
55: PetscErrorCode PetscQuadratureCreate(MPI_Comm comm, PetscQuadrature *q)
56: {
61: DMInitializePackage();
62: PetscHeaderCreate(*q,PETSCQUADRATURE_CLASSID,"PetscQuadrature","Quadrature","DT",comm,PetscQuadratureDestroy,PetscQuadratureView);
63: (*q)->dim = -1;
64: (*q)->Nc = 1;
65: (*q)->order = -1;
66: (*q)->numPoints = 0;
67: (*q)->points = NULL;
68: (*q)->weights = NULL;
69: return(0);
70: }
72: /*@
73: PetscQuadratureDuplicate - Create a deep copy of the PetscQuadrature object
75: Collective on q
77: Input Parameter:
78: . q - The PetscQuadrature object
80: Output Parameter:
81: . r - The new PetscQuadrature object
83: Level: beginner
85: .seealso: PetscQuadratureCreate(), PetscQuadratureDestroy(), PetscQuadratureGetData()
86: @*/
87: PetscErrorCode PetscQuadratureDuplicate(PetscQuadrature q, PetscQuadrature *r)
88: {
89: PetscInt order, dim, Nc, Nq;
90: const PetscReal *points, *weights;
91: PetscReal *p, *w;
92: PetscErrorCode ierr;
96: PetscQuadratureCreate(PetscObjectComm((PetscObject) q), r);
97: PetscQuadratureGetOrder(q, &order);
98: PetscQuadratureSetOrder(*r, order);
99: PetscQuadratureGetData(q, &dim, &Nc, &Nq, &points, &weights);
100: PetscMalloc1(Nq*dim, &p);
101: PetscMalloc1(Nq*Nc, &w);
102: PetscArraycpy(p, points, Nq*dim);
103: PetscArraycpy(w, weights, Nc * Nq);
104: PetscQuadratureSetData(*r, dim, Nc, Nq, p, w);
105: return(0);
106: }
108: /*@
109: PetscQuadratureDestroy - Destroys a PetscQuadrature object
111: Collective on q
113: Input Parameter:
114: . q - The PetscQuadrature object
116: Level: beginner
118: .seealso: PetscQuadratureCreate(), PetscQuadratureGetData()
119: @*/
120: PetscErrorCode PetscQuadratureDestroy(PetscQuadrature *q)
121: {
125: if (!*q) return(0);
127: if (--((PetscObject)(*q))->refct > 0) {
128: *q = NULL;
129: return(0);
130: }
131: PetscFree((*q)->points);
132: PetscFree((*q)->weights);
133: PetscHeaderDestroy(q);
134: return(0);
135: }
137: /*@
138: PetscQuadratureGetOrder - Return the order of the method
140: Not collective
142: Input Parameter:
143: . q - The PetscQuadrature object
145: Output Parameter:
146: . order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated
148: Level: intermediate
150: .seealso: PetscQuadratureSetOrder(), PetscQuadratureGetData(), PetscQuadratureSetData()
151: @*/
152: PetscErrorCode PetscQuadratureGetOrder(PetscQuadrature q, PetscInt *order)
153: {
157: *order = q->order;
158: return(0);
159: }
161: /*@
162: PetscQuadratureSetOrder - Return the order of the method
164: Not collective
166: Input Parameters:
167: + q - The PetscQuadrature object
168: - order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated
170: Level: intermediate
172: .seealso: PetscQuadratureGetOrder(), PetscQuadratureGetData(), PetscQuadratureSetData()
173: @*/
174: PetscErrorCode PetscQuadratureSetOrder(PetscQuadrature q, PetscInt order)
175: {
178: q->order = order;
179: return(0);
180: }
182: /*@
183: PetscQuadratureGetNumComponents - Return the number of components for functions to be integrated
185: Not collective
187: Input Parameter:
188: . q - The PetscQuadrature object
190: Output Parameter:
191: . Nc - The number of components
193: Note: We are performing an integral int f(x) . w(x) dx, where both f and w (the weight) have Nc components.
195: Level: intermediate
197: .seealso: PetscQuadratureSetNumComponents(), PetscQuadratureGetData(), PetscQuadratureSetData()
198: @*/
199: PetscErrorCode PetscQuadratureGetNumComponents(PetscQuadrature q, PetscInt *Nc)
200: {
204: *Nc = q->Nc;
205: return(0);
206: }
208: /*@
209: PetscQuadratureSetNumComponents - Return the number of components for functions to be integrated
211: Not collective
213: Input Parameters:
214: + q - The PetscQuadrature object
215: - Nc - The number of components
217: Note: We are performing an integral int f(x) . w(x) dx, where both f and w (the weight) have Nc components.
219: Level: intermediate
221: .seealso: PetscQuadratureGetNumComponents(), PetscQuadratureGetData(), PetscQuadratureSetData()
222: @*/
223: PetscErrorCode PetscQuadratureSetNumComponents(PetscQuadrature q, PetscInt Nc)
224: {
227: q->Nc = Nc;
228: return(0);
229: }
231: /*@C
232: PetscQuadratureGetData - Returns the data defining the quadrature
234: Not collective
236: Input Parameter:
237: . q - The PetscQuadrature object
239: Output Parameters:
240: + dim - The spatial dimension
241: . Nc - The number of components
242: . npoints - The number of quadrature points
243: . points - The coordinates of each quadrature point
244: - weights - The weight of each quadrature point
246: Level: intermediate
248: Fortran Notes:
249: From Fortran you must call PetscQuadratureRestoreData() when you are done with the data
251: .seealso: PetscQuadratureCreate(), PetscQuadratureSetData()
252: @*/
253: PetscErrorCode PetscQuadratureGetData(PetscQuadrature q, PetscInt *dim, PetscInt *Nc, PetscInt *npoints, const PetscReal *points[], const PetscReal *weights[])
254: {
257: if (dim) {
259: *dim = q->dim;
260: }
261: if (Nc) {
263: *Nc = q->Nc;
264: }
265: if (npoints) {
267: *npoints = q->numPoints;
268: }
269: if (points) {
271: *points = q->points;
272: }
273: if (weights) {
275: *weights = q->weights;
276: }
277: return(0);
278: }
280: static PetscErrorCode PetscDTJacobianInverse_Internal(PetscInt m, PetscInt n, const PetscReal J[], PetscReal Jinv[])
281: {
282: PetscScalar *Js, *Jinvs;
283: PetscInt i, j, k;
284: PetscBLASInt bm, bn, info;
288: if (!m || !n) return(0);
289: PetscBLASIntCast(m, &bm);
290: PetscBLASIntCast(n, &bn);
291: #if defined(PETSC_USE_COMPLEX)
292: PetscMalloc2(m*n, &Js, m*n, &Jinvs);
293: for (i = 0; i < m*n; i++) Js[i] = J[i];
294: #else
295: Js = (PetscReal *) J;
296: Jinvs = Jinv;
297: #endif
298: if (m == n) {
299: PetscBLASInt *pivots;
300: PetscScalar *W;
302: PetscMalloc2(m, &pivots, m, &W);
304: PetscArraycpy(Jinvs, Js, m * m);
305: PetscStackCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, Jinvs, &bm, pivots, &info));
306: if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetrf %D",(PetscInt)info);
307: PetscStackCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, Jinvs, &bm, pivots, W, &bm, &info));
308: if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetri %D",(PetscInt)info);
309: PetscFree2(pivots, W);
310: } else if (m < n) {
311: PetscScalar *JJT;
312: PetscBLASInt *pivots;
313: PetscScalar *W;
315: PetscMalloc1(m*m, &JJT);
316: PetscMalloc2(m, &pivots, m, &W);
317: for (i = 0; i < m; i++) {
318: for (j = 0; j < m; j++) {
319: PetscScalar val = 0.;
321: for (k = 0; k < n; k++) val += Js[i * n + k] * Js[j * n + k];
322: JJT[i * m + j] = val;
323: }
324: }
326: PetscStackCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, JJT, &bm, pivots, &info));
327: if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetrf %D",(PetscInt)info);
328: PetscStackCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, JJT, &bm, pivots, W, &bm, &info));
329: if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetri %D",(PetscInt)info);
330: for (i = 0; i < n; i++) {
331: for (j = 0; j < m; j++) {
332: PetscScalar val = 0.;
334: for (k = 0; k < m; k++) val += Js[k * n + i] * JJT[k * m + j];
335: Jinvs[i * m + j] = val;
336: }
337: }
338: PetscFree2(pivots, W);
339: PetscFree(JJT);
340: } else {
341: PetscScalar *JTJ;
342: PetscBLASInt *pivots;
343: PetscScalar *W;
345: PetscMalloc1(n*n, &JTJ);
346: PetscMalloc2(n, &pivots, n, &W);
347: for (i = 0; i < n; i++) {
348: for (j = 0; j < n; j++) {
349: PetscScalar val = 0.;
351: for (k = 0; k < m; k++) val += Js[k * n + i] * Js[k * n + j];
352: JTJ[i * n + j] = val;
353: }
354: }
356: PetscStackCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bn, &bn, JTJ, &bn, pivots, &info));
357: if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetrf %D",(PetscInt)info);
358: PetscStackCallBLAS("LAPACKgetri", LAPACKgetri_(&bn, JTJ, &bn, pivots, W, &bn, &info));
359: if (info) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error returned from LAPACKgetri %D",(PetscInt)info);
360: for (i = 0; i < n; i++) {
361: for (j = 0; j < m; j++) {
362: PetscScalar val = 0.;
364: for (k = 0; k < n; k++) val += JTJ[i * n + k] * Js[j * n + k];
365: Jinvs[i * m + j] = val;
366: }
367: }
368: PetscFree2(pivots, W);
369: PetscFree(JTJ);
370: }
371: #if defined(PETSC_USE_COMPLEX)
372: for (i = 0; i < m*n; i++) Jinv[i] = PetscRealPart(Jinvs[i]);
373: PetscFree2(Js, Jinvs);
374: #endif
375: return(0);
376: }
378: /*@
379: PetscQuadraturePushForward - Push forward a quadrature functional under an affine transformation.
381: Collecive on PetscQuadrature
383: Input Arguments:
384: + q - the quadrature functional
385: . imageDim - the dimension of the image of the transformation
386: . origin - a point in the original space
387: . originImage - the image of the origin under the transformation
388: . J - the Jacobian of the image: an [imageDim x dim] matrix in row major order
389: - 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]
391: Output Arguments:
392: . 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.
394: 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.
396: Level: intermediate
398: .seealso: PetscDTAltVPullback(), PetscDTAltVPullbackMatrix()
399: @*/
400: PetscErrorCode PetscQuadraturePushForward(PetscQuadrature q, PetscInt imageDim, const PetscReal origin[], const PetscReal originImage[], const PetscReal J[], PetscInt formDegree, PetscQuadrature *Jinvstarq)
401: {
402: PetscInt dim, Nc, imageNc, formSize, Ncopies, imageFormSize, Npoints, pt, i, j, c;
403: const PetscReal *points;
404: const PetscReal *weights;
405: PetscReal *imagePoints, *imageWeights;
406: PetscReal *Jinv;
407: PetscReal *Jinvstar;
408: PetscErrorCode ierr;
412: if (imageDim < PetscAbsInt(formDegree)) SETERRQ2(PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Cannot represent a %D-form in %D dimensions", PetscAbsInt(formDegree), imageDim);
413: PetscQuadratureGetData(q, &dim, &Nc, &Npoints, &points, &weights);
414: PetscDTBinomialInt(dim, PetscAbsInt(formDegree), &formSize);
415: if (Nc % formSize) SETERRQ2(PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Number of components %D is not a multiple of formSize %D\n", Nc, formSize);
416: Ncopies = Nc / formSize;
417: PetscDTBinomialInt(imageDim, PetscAbsInt(formDegree), &imageFormSize);
418: imageNc = Ncopies * imageFormSize;
419: PetscMalloc1(Npoints * imageDim, &imagePoints);
420: PetscMalloc1(Npoints * imageNc, &imageWeights);
421: PetscMalloc2(imageDim * dim, &Jinv, formSize * imageFormSize, &Jinvstar);
422: PetscDTJacobianInverse_Internal(imageDim, dim, J, Jinv);
423: PetscDTAltVPullbackMatrix(imageDim, dim, Jinv, formDegree, Jinvstar);
424: for (pt = 0; pt < Npoints; pt++) {
425: const PetscReal *point = &points[pt * dim];
426: PetscReal *imagePoint = &imagePoints[pt * imageDim];
428: for (i = 0; i < imageDim; i++) {
429: PetscReal val = originImage[i];
431: for (j = 0; j < dim; j++) val += J[i * dim + j] * (point[j] - origin[j]);
432: imagePoint[i] = val;
433: }
434: for (c = 0; c < Ncopies; c++) {
435: const PetscReal *form = &weights[pt * Nc + c * formSize];
436: PetscReal *imageForm = &imageWeights[pt * imageNc + c * imageFormSize];
438: for (i = 0; i < imageFormSize; i++) {
439: PetscReal val = 0.;
441: for (j = 0; j < formSize; j++) val += Jinvstar[i * formSize + j] * form[j];
442: imageForm[i] = val;
443: }
444: }
445: }
446: PetscQuadratureCreate(PetscObjectComm((PetscObject)q), Jinvstarq);
447: PetscQuadratureSetData(*Jinvstarq, imageDim, imageNc, Npoints, imagePoints, imageWeights);
448: PetscFree2(Jinv, Jinvstar);
449: return(0);
450: }
452: /*@C
453: PetscQuadratureSetData - Sets the data defining the quadrature
455: Not collective
457: Input Parameters:
458: + q - The PetscQuadrature object
459: . dim - The spatial dimension
460: . Nc - The number of components
461: . npoints - The number of quadrature points
462: . points - The coordinates of each quadrature point
463: - weights - The weight of each quadrature point
465: Note: This routine owns the references to points and weights, so they must be allocated using PetscMalloc() and the user should not free them.
467: Level: intermediate
469: .seealso: PetscQuadratureCreate(), PetscQuadratureGetData()
470: @*/
471: PetscErrorCode PetscQuadratureSetData(PetscQuadrature q, PetscInt dim, PetscInt Nc, PetscInt npoints, const PetscReal points[], const PetscReal weights[])
472: {
475: if (dim >= 0) q->dim = dim;
476: if (Nc >= 0) q->Nc = Nc;
477: if (npoints >= 0) q->numPoints = npoints;
478: if (points) {
480: q->points = points;
481: }
482: if (weights) {
484: q->weights = weights;
485: }
486: return(0);
487: }
489: static PetscErrorCode PetscQuadratureView_Ascii(PetscQuadrature quad, PetscViewer v)
490: {
491: PetscInt q, d, c;
492: PetscViewerFormat format;
493: PetscErrorCode ierr;
496: if (quad->Nc > 1) {PetscViewerASCIIPrintf(v, "Quadrature of order %D on %D points (dim %D) with %D components\n", quad->order, quad->numPoints, quad->dim, quad->Nc);}
497: else {PetscViewerASCIIPrintf(v, "Quadrature of order %D on %D points (dim %D)\n", quad->order, quad->numPoints, quad->dim);}
498: PetscViewerGetFormat(v, &format);
499: if (format != PETSC_VIEWER_ASCII_INFO_DETAIL) return(0);
500: for (q = 0; q < quad->numPoints; ++q) {
501: PetscViewerASCIIPrintf(v, "p%D (", q);
502: PetscViewerASCIIUseTabs(v, PETSC_FALSE);
503: for (d = 0; d < quad->dim; ++d) {
504: if (d) {PetscViewerASCIIPrintf(v, ", ");}
505: PetscViewerASCIIPrintf(v, "%+g", (double)quad->points[q*quad->dim+d]);
506: }
507: PetscViewerASCIIPrintf(v, ") ");
508: if (quad->Nc > 1) {PetscViewerASCIIPrintf(v, "w%D (", q);}
509: for (c = 0; c < quad->Nc; ++c) {
510: if (c) {PetscViewerASCIIPrintf(v, ", ");}
511: PetscViewerASCIIPrintf(v, "%+g", (double)quad->weights[q*quad->Nc+c]);
512: }
513: if (quad->Nc > 1) {PetscViewerASCIIPrintf(v, ")");}
514: PetscViewerASCIIPrintf(v, "\n");
515: PetscViewerASCIIUseTabs(v, PETSC_TRUE);
516: }
517: return(0);
518: }
520: /*@C
521: PetscQuadratureView - Views a PetscQuadrature object
523: Collective on quad
525: Input Parameters:
526: + quad - The PetscQuadrature object
527: - viewer - The PetscViewer object
529: Level: beginner
531: .seealso: PetscQuadratureCreate(), PetscQuadratureGetData()
532: @*/
533: PetscErrorCode PetscQuadratureView(PetscQuadrature quad, PetscViewer viewer)
534: {
535: PetscBool iascii;
541: if (!viewer) {PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject) quad), &viewer);}
542: PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii);
543: PetscViewerASCIIPushTab(viewer);
544: if (iascii) {PetscQuadratureView_Ascii(quad, viewer);}
545: PetscViewerASCIIPopTab(viewer);
546: return(0);
547: }
549: /*@C
550: PetscQuadratureExpandComposite - Return a quadrature over the composite element, which has the original quadrature in each subelement
552: Not collective
554: Input Parameter:
555: + q - The original PetscQuadrature
556: . numSubelements - The number of subelements the original element is divided into
557: . v0 - An array of the initial points for each subelement
558: - jac - An array of the Jacobian mappings from the reference to each subelement
560: Output Parameters:
561: . dim - The dimension
563: Note: Together v0 and jac define an affine mapping from the original reference element to each subelement
565: Not available from Fortran
567: Level: intermediate
569: .seealso: PetscFECreate(), PetscSpaceGetDimension(), PetscDualSpaceGetDimension()
570: @*/
571: PetscErrorCode PetscQuadratureExpandComposite(PetscQuadrature q, PetscInt numSubelements, const PetscReal v0[], const PetscReal jac[], PetscQuadrature *qref)
572: {
573: const PetscReal *points, *weights;
574: PetscReal *pointsRef, *weightsRef;
575: PetscInt dim, Nc, order, npoints, npointsRef, c, p, cp, d, e;
576: PetscErrorCode ierr;
583: PetscQuadratureCreate(PETSC_COMM_SELF, qref);
584: PetscQuadratureGetOrder(q, &order);
585: PetscQuadratureGetData(q, &dim, &Nc, &npoints, &points, &weights);
586: npointsRef = npoints*numSubelements;
587: PetscMalloc1(npointsRef*dim,&pointsRef);
588: PetscMalloc1(npointsRef*Nc, &weightsRef);
589: for (c = 0; c < numSubelements; ++c) {
590: for (p = 0; p < npoints; ++p) {
591: for (d = 0; d < dim; ++d) {
592: pointsRef[(c*npoints + p)*dim+d] = v0[c*dim+d];
593: for (e = 0; e < dim; ++e) {
594: pointsRef[(c*npoints + p)*dim+d] += jac[(c*dim + d)*dim+e]*(points[p*dim+e] + 1.0);
595: }
596: }
597: /* Could also use detJ here */
598: for (cp = 0; cp < Nc; ++cp) weightsRef[(c*npoints+p)*Nc+cp] = weights[p*Nc+cp]/numSubelements;
599: }
600: }
601: PetscQuadratureSetOrder(*qref, order);
602: PetscQuadratureSetData(*qref, dim, Nc, npointsRef, pointsRef, weightsRef);
603: return(0);
604: }
606: /* Compute the coefficients for the Jacobi polynomial recurrence,
607: *
608: * J^{a,b}_n(x) = (cnm1 + cnm1x * x) * J^{a,b}_{n-1}(x) - cnm2 * J^{a,b}_{n-2}(x).
609: */
610: #define PetscDTJacobiRecurrence_Internal(n,a,b,cnm1,cnm1x,cnm2) \
611: do { \
612: PetscReal _a = (a); \
613: PetscReal _b = (b); \
614: PetscReal _n = (n); \
615: if (n == 1) { \
616: (cnm1) = (_a-_b) * 0.5; \
617: (cnm1x) = (_a+_b+2.)*0.5; \
618: (cnm2) = 0.; \
619: } else { \
620: PetscReal _2n = _n+_n; \
621: PetscReal _d = (_2n*(_n+_a+_b)*(_2n+_a+_b-2)); \
622: PetscReal _n1 = (_2n+_a+_b-1.)*(_a*_a-_b*_b); \
623: PetscReal _n1x = (_2n+_a+_b-1.)*(_2n+_a+_b)*(_2n+_a+_b-2); \
624: PetscReal _n2 = 2.*((_n+_a-1.)*(_n+_b-1.)*(_2n+_a+_b)); \
625: (cnm1) = _n1 / _d; \
626: (cnm1x) = _n1x / _d; \
627: (cnm2) = _n2 / _d; \
628: } \
629: } while (0)
631: static PetscErrorCode PetscDTJacobiEval_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscInt k, const PetscReal *points, PetscInt ndegree, const PetscInt *degrees, PetscReal *p)
632: {
633: PetscReal ak, bk;
634: PetscReal abk1;
635: PetscInt i,l,maxdegree;
638: maxdegree = degrees[ndegree-1] - k;
639: ak = a + k;
640: bk = b + k;
641: abk1 = a + b + k + 1.;
642: if (maxdegree < 0) {
643: for (i = 0; i < npoints; i++) for (l = 0; l < ndegree; l++) p[i*ndegree+l] = 0.;
644: return(0);
645: }
646: for (i=0; i<npoints; i++) {
647: PetscReal pm1,pm2,x;
648: PetscReal cnm1, cnm1x, cnm2;
649: PetscInt j,m;
651: x = points[i];
652: pm2 = 1.;
653: PetscDTJacobiRecurrence_Internal(1,ak,bk,cnm1,cnm1x,cnm2);
654: pm1 = (cnm1 + cnm1x*x);
655: l = 0;
656: while (l < ndegree && degrees[l] - k < 0) {
657: p[l++] = 0.;
658: }
659: while (l < ndegree && degrees[l] - k == 0) {
660: p[l] = pm2;
661: for (m = 0; m < k; m++) p[l] *= (abk1 + m) * 0.5;
662: l++;
663: }
664: while (l < ndegree && degrees[l] - k == 1) {
665: p[l] = pm1;
666: for (m = 0; m < k; m++) p[l] *= (abk1 + 1 + m) * 0.5;
667: l++;
668: }
669: for (j=2; j<=maxdegree; j++) {
670: PetscReal pp;
672: PetscDTJacobiRecurrence_Internal(j,ak,bk,cnm1,cnm1x,cnm2);
673: pp = (cnm1 + cnm1x*x)*pm1 - cnm2*pm2;
674: pm2 = pm1;
675: pm1 = pp;
676: while (l < ndegree && degrees[l] - k == j) {
677: p[l] = pp;
678: for (m = 0; m < k; m++) p[l] *= (abk1 + j + m) * 0.5;
679: l++;
680: }
681: }
682: p += ndegree;
683: }
684: return(0);
685: }
687: /*@
688: PetscDTJacobiEval - evaluate Jacobi polynomials for the weight function $(1.+x)^{\alpha} (1.-x)^{\beta}$
689: at points
691: Not Collective
693: Input Arguments:
694: + npoints - number of spatial points to evaluate at
695: . alpha - the left exponent > -1
696: . beta - the right exponent > -1
697: . points - array of locations to evaluate at
698: . ndegree - number of basis degrees to evaluate
699: - degrees - sorted array of degrees to evaluate
701: Output Arguments:
702: + B - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension npoints*ndegrees, allocated by caller) (or NULL)
703: . D - row-oriented derivative evaluation matrix (or NULL)
704: - D2 - row-oriented second derivative evaluation matrix (or NULL)
706: Level: intermediate
708: .seealso: PetscDTGaussQuadrature()
709: @*/
710: PetscErrorCode PetscDTJacobiEval(PetscInt npoints,PetscReal alpha, PetscReal beta, const PetscReal *points,PetscInt ndegree,const PetscInt *degrees,PetscReal *B,PetscReal *D,PetscReal *D2)
711: {
715: if (alpha <= -1.) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"alpha must be > -1.");
716: if (beta <= -1.) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"beta must be > -1.");
717: if (!npoints || !ndegree) return(0);
718: if (B) {PetscDTJacobiEval_Internal(npoints, alpha, beta, 0, points, ndegree, degrees, B);}
719: if (D) {PetscDTJacobiEval_Internal(npoints, alpha, beta, 1, points, ndegree, degrees, D);}
720: if (D2) {PetscDTJacobiEval_Internal(npoints, alpha, beta, 2, points, ndegree, degrees, D2);}
721: return(0);
722: }
724: /*@
725: PetscDTLegendreEval - evaluate Legendre polynomials at points
727: Not Collective
729: Input Arguments:
730: + npoints - number of spatial points to evaluate at
731: . points - array of locations to evaluate at
732: . ndegree - number of basis degrees to evaluate
733: - degrees - sorted array of degrees to evaluate
735: Output Arguments:
736: + B - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension npoints*ndegrees, allocated by caller) (or NULL)
737: . D - row-oriented derivative evaluation matrix (or NULL)
738: - D2 - row-oriented second derivative evaluation matrix (or NULL)
740: Level: intermediate
742: .seealso: PetscDTGaussQuadrature()
743: @*/
744: PetscErrorCode PetscDTLegendreEval(PetscInt npoints,const PetscReal *points,PetscInt ndegree,const PetscInt *degrees,PetscReal *B,PetscReal *D,PetscReal *D2)
745: {
749: PetscDTJacobiEval(npoints, 0., 0., points, ndegree, degrees, B, D, D2);
750: return(0);
751: }
753: /* solve the symmetric tridiagonal eigenvalue system, writing the eigenvalues into eigs and the eigenvectors into V
754: * with lds n; diag and subdiag are overwritten */
755: static PetscErrorCode PetscDTSymmetricTridiagonalEigensolve(PetscInt n, PetscReal diag[], PetscReal subdiag[],
756: PetscReal eigs[], PetscScalar V[])
757: {
758: char jobz = 'V'; /* eigenvalues and eigenvectors */
759: char range = 'A'; /* all eigenvalues will be found */
760: PetscReal VL = 0.; /* ignored because range is 'A' */
761: PetscReal VU = 0.; /* ignored because range is 'A' */
762: PetscBLASInt IL = 0; /* ignored because range is 'A' */
763: PetscBLASInt IU = 0; /* ignored because range is 'A' */
764: PetscReal abstol = 0.; /* unused */
765: PetscBLASInt bn, bm, ldz; /* bm will equal bn on exit */
766: PetscBLASInt *isuppz;
767: PetscBLASInt lwork, liwork;
768: PetscReal workquery;
769: PetscBLASInt iworkquery;
770: PetscBLASInt *iwork;
771: PetscBLASInt info;
772: PetscReal *work = NULL;
776: #if !defined(PETSCDTGAUSSIANQUADRATURE_EIG)
777: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
778: #endif
779: PetscBLASIntCast(n, &bn);
780: PetscBLASIntCast(n, &ldz);
781: #if !defined(PETSC_MISSING_LAPACK_STEGR)
782: PetscMalloc1(2 * n, &isuppz);
783: lwork = -1;
784: liwork = -1;
785: PetscStackCallBLAS("LAPACKstegr",LAPACKstegr_(&jobz,&range,&bn,diag,subdiag,&VL,&VU,&IL,&IU,&abstol,&bm,eigs,V,&ldz,isuppz,&workquery,&lwork,&iworkquery,&liwork,&info));
786: if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"xSTEGR error");
787: lwork = (PetscBLASInt) workquery;
788: liwork = (PetscBLASInt) iworkquery;
789: PetscMalloc2(lwork, &work, liwork, &iwork);
790: PetscFPTrapPush(PETSC_FP_TRAP_OFF);
791: PetscStackCallBLAS("LAPACKstegr",LAPACKstegr_(&jobz,&range,&bn,diag,subdiag,&VL,&VU,&IL,&IU,&abstol,&bm,eigs,V,&ldz,isuppz,work,&lwork,iwork,&liwork,&info));
792: PetscFPTrapPop();
793: if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"xSTEGR error");
794: PetscFree2(work, iwork);
795: PetscFree(isuppz);
796: #elif !defined(PETSC_MISSING_LAPACK_STEQR)
797: jobz = 'I'; /* Compute eigenvalues and eigenvectors of the
798: tridiagonal matrix. Z is initialized to the identity
799: matrix. */
800: PetscMalloc1(PetscMax(1,2*n-2),&work);
801: PetscStackCallBLAS("LAPACKsteqr",LAPACKsteqr_("I",&bn,diag,subdiag,V,&ldz,work,&info));
802: PetscFPTrapPop();
803: if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"xSTEQR error");
804: PetscFree(work);
805: PetscArraycpy(eigs,diag,n);
806: #endif
807: return(0);
808: }
810: /* Formula for the weights at the endpoints (-1 and 1) of Gauss-Lobatto-Jacobi
811: * quadrature rules on the interval [-1, 1] */
812: static PetscErrorCode PetscDTGaussLobattoJacobiEndweights_Internal(PetscInt n, PetscReal alpha, PetscReal beta, PetscReal *leftw, PetscReal *rightw)
813: {
814: PetscReal twoab1;
815: PetscInt m = n - 2;
816: PetscReal a = alpha + 1.;
817: PetscReal b = beta + 1.;
818: PetscReal gra, grb;
821: twoab1 = PetscPowReal(2., a + b - 1.);
822: #if defined(PETSC_HAVE_LGAMMA)
823: grb = PetscExpReal(2. * PetscLGamma(b+1.) + PetscLGamma(m+1.) + PetscLGamma(m+a+1.) -
824: (PetscLGamma(m+b+1) + PetscLGamma(m+a+b+1.)));
825: gra = PetscExpReal(2. * PetscLGamma(a+1.) + PetscLGamma(m+1.) + PetscLGamma(m+b+1.) -
826: (PetscLGamma(m+a+1) + PetscLGamma(m+a+b+1.)));
827: #else
828: {
829: PetscInt alphai = (PetscInt) alpha;
830: PetscInt betai = (PetscInt) beta;
833: if ((PetscReal) alphai == alpha && (PetscReal) betai == beta) {
834: PetscReal binom1, binom2;
836: PetscDTBinomial(m+b, b, &binom1);
837: PetscDTBinomial(m+a+b, b, &binom2);
838: grb = 1./ (binom1 * binom2);
839: PetscDTBinomial(m+a, a, &binom1);
840: PetscDTBinomial(m+a+b, a, &binom2);
841: gra = 1./ (binom1 * binom2);
842: } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"lgamma() - math routine is unavailable.");
843: }
844: #endif
845: *leftw = twoab1 * grb / b;
846: *rightw = twoab1 * gra / a;
847: return(0);
848: }
850: /* Evaluates the nth jacobi polynomial with weight parameters a,b at a point x.
851: Recurrence relations implemented from the pseudocode given in Karniadakis and Sherwin, Appendix B */
852: PETSC_STATIC_INLINE PetscErrorCode PetscDTComputeJacobi(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscReal *P)
853: {
854: PetscReal pn1, pn2;
855: PetscReal cnm1, cnm1x, cnm2;
856: PetscInt k;
859: if (!n) {*P = 1.0; return(0);}
860: PetscDTJacobiRecurrence_Internal(1,a,b,cnm1,cnm1x,cnm2);
861: pn2 = 1.;
862: pn1 = cnm1 + cnm1x*x;
863: if (n == 1) {*P = pn1; return(0);}
864: *P = 0.0;
865: for (k = 2; k < n+1; ++k) {
866: PetscDTJacobiRecurrence_Internal(k,a,b,cnm1,cnm1x,cnm2);
868: *P = (cnm1 + cnm1x*x)*pn1 - cnm2*pn2;
869: pn2 = pn1;
870: pn1 = *P;
871: }
872: return(0);
873: }
875: /* Evaluates the first derivative of P_{n}^{a,b} at a point x. */
876: PETSC_STATIC_INLINE PetscErrorCode PetscDTComputeJacobiDerivative(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscInt k, PetscReal *P)
877: {
878: PetscReal nP;
879: PetscInt i;
883: *P = 0.0;
884: if (k > n) return(0);
885: PetscDTComputeJacobi(a+k, b+k, n-k, x, &nP);
886: for (i = 0; i < k; i++) nP *= (a + b + n + 1. + i) * 0.5;
887: *P = nP;
888: return(0);
889: }
891: /* Maps from [-1,1]^2 to the (-1,1) reference triangle */
892: PETSC_STATIC_INLINE PetscErrorCode PetscDTMapSquareToTriangle_Internal(PetscReal x, PetscReal y, PetscReal *xi, PetscReal *eta)
893: {
895: *xi = 0.5 * (1.0 + x) * (1.0 - y) - 1.0;
896: *eta = y;
897: return(0);
898: }
900: static PetscErrorCode PetscDTGaussJacobiQuadrature_Newton_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal x[], PetscReal w[])
901: {
902: PetscInt maxIter = 100;
903: PetscReal eps = PetscExpReal(0.75 * PetscLogReal(PETSC_MACHINE_EPSILON));
904: PetscReal a1, a6, gf;
905: PetscInt k;
910: a1 = PetscPowReal(2.0, a+b+1);
911: #if defined(PETSC_HAVE_LGAMMA)
912: {
913: PetscReal a2, a3, a4, a5;
914: a2 = PetscLGamma(a + npoints + 1);
915: a3 = PetscLGamma(b + npoints + 1);
916: a4 = PetscLGamma(a + b + npoints + 1);
917: a5 = PetscLGamma(npoints + 1);
918: gf = PetscExpReal(a2 + a3 - (a4 + a5));
919: }
920: #else
921: {
922: PetscInt ia, ib;
924: ia = (PetscInt) a;
925: ib = (PetscInt) b;
926: gf = 1.;
927: if (ia == a && ia >= 0) { /* compute ratio of rising factorals wrt a */
928: for (k = 0; k < ia; k++) gf *= (npoints + 1. + k) / (npoints + b + 1. + k);
929: } else if (b == b && ib >= 0) { /* compute ratio of rising factorials wrt b */
930: for (k = 0; k < ib; k++) gf *= (npoints + 1. + k) / (npoints + a + 1. + k);
931: } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"lgamma() - math routine is unavailable.");
932: }
933: #endif
935: a6 = a1 * gf;
936: /* Computes the m roots of P_{m}^{a,b} on [-1,1] by Newton's method with Chebyshev points as initial guesses.
937: Algorithm implemented from the pseudocode given by Karniadakis and Sherwin and Python in FIAT */
938: for (k = 0; k < npoints; ++k) {
939: PetscReal r = PetscCosReal(PETSC_PI * (1. - (4.*k + 3. + 2.*b) / (4.*npoints + 2.*(a + b + 1.)))), dP;
940: PetscInt j;
942: if (k > 0) r = 0.5 * (r + x[k-1]);
943: for (j = 0; j < maxIter; ++j) {
944: PetscReal s = 0.0, delta, f, fp;
945: PetscInt i;
947: for (i = 0; i < k; ++i) s = s + 1.0 / (r - x[i]);
948: PetscDTComputeJacobi(a, b, npoints, r, &f);
949: PetscDTComputeJacobiDerivative(a, b, npoints, r, 1, &fp);
950: delta = f / (fp - f * s);
951: r = r - delta;
952: if (PetscAbsReal(delta) < eps) break;
953: }
954: x[k] = r;
955: PetscDTComputeJacobiDerivative(a, b, npoints, x[k], 1, &dP);
956: w[k] = a6 / (1.0 - PetscSqr(x[k])) / PetscSqr(dP);
957: }
958: return(0);
959: }
961: /* Compute the diagonals of the Jacobi matrix used in Golub & Welsch algorithms for Gauss-Jacobi
962: * quadrature weight calculations on [-1,1] for exponents (1. + x)^a (1.-x)^b */
963: static PetscErrorCode PetscDTJacobiMatrix_Internal(PetscInt nPoints, PetscReal a, PetscReal b, PetscReal *d, PetscReal *s)
964: {
965: PetscInt i;
968: for (i = 0; i < nPoints; i++) {
969: PetscReal A, B, C;
971: PetscDTJacobiRecurrence_Internal(i+1,a,b,A,B,C);
972: d[i] = -A / B;
973: if (i) s[i-1] *= C / B;
974: if (i < nPoints - 1) s[i] = 1. / B;
975: }
976: return(0);
977: }
979: static PetscErrorCode PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal *x, PetscReal *w)
980: {
981: PetscReal mu0;
982: PetscReal ga, gb, gab;
983: PetscInt i;
987: PetscCitationsRegister(GolubWelschCitation, &GolubWelschCite);
989: #if defined(PETSC_HAVE_TGAMMA)
990: ga = PetscTGamma(a + 1);
991: gb = PetscTGamma(b + 1);
992: gab = PetscTGamma(a + b + 2);
993: #else
994: {
995: PetscInt ia, ib;
997: ia = (PetscInt) a;
998: ib = (PetscInt) b;
999: if (ia == a && ib == b && ia + 1 > 0 && ib + 1 > 0 && ia + ib + 2 > 0) { /* All gamma(x) terms are (x-1)! terms */
1000: PetscDTFactorial(ia, &ga);
1001: PetscDTFactorial(ib, &gb);
1002: PetscDTFactorial(ia + ib + 1, &gb);
1003: } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"tgamma() - math routine is unavailable.");
1004: }
1005: #endif
1006: mu0 = PetscPowReal(2.,a + b + 1.) * ga * gb / gab;
1008: #if defined(PETSCDTGAUSSIANQUADRATURE_EIG)
1009: {
1010: PetscReal *diag, *subdiag;
1011: PetscScalar *V;
1013: PetscMalloc2(npoints, &diag, npoints, &subdiag);
1014: PetscMalloc1(npoints*npoints, &V);
1015: PetscDTJacobiMatrix_Internal(npoints, a, b, diag, subdiag);
1016: for (i = 0; i < npoints - 1; i++) subdiag[i] = PetscSqrtReal(subdiag[i]);
1017: PetscDTSymmetricTridiagonalEigensolve(npoints, diag, subdiag, x, V);
1018: for (i = 0; i < npoints; i++) w[i] = PetscSqr(PetscRealPart(V[i * npoints])) * mu0;
1019: PetscFree(V);
1020: PetscFree2(diag, subdiag);
1021: }
1022: #else
1023: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
1024: #endif
1025: { /* As of March 2, 2020, The Sun Performance Library breaks the LAPACK contract for xstegr and xsteqr: the
1026: eigenvalues are not guaranteed to be in ascending order. So we heave a passive aggressive sigh and check that
1027: the eigenvalues are sorted */
1028: PetscBool sorted;
1030: PetscSortedReal(npoints, x, &sorted);
1031: if (!sorted) {
1032: PetscInt *order, i;
1033: PetscReal *tmp;
1035: PetscMalloc2(npoints, &order, npoints, &tmp);
1036: for (i = 0; i < npoints; i++) order[i] = i;
1037: PetscSortRealWithPermutation(npoints, x, order);
1038: PetscArraycpy(tmp, x, npoints);
1039: for (i = 0; i < npoints; i++) x[i] = tmp[order[i]];
1040: PetscArraycpy(tmp, w, npoints);
1041: for (i = 0; i < npoints; i++) w[i] = tmp[order[i]];
1042: PetscFree2(order, tmp);
1043: }
1044: }
1045: return(0);
1046: }
1048: static PetscErrorCode PetscDTGaussJacobiQuadrature_Internal(PetscInt npoints,PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
1049: {
1053: if (npoints < 1) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Number of points must be positive");
1054: /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
1055: if (alpha <= -1.) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"alpha must be > -1.");
1056: if (beta <= -1.) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"beta must be > -1.");
1058: if (newton) {
1059: PetscDTGaussJacobiQuadrature_Newton_Internal(npoints, alpha, beta, x, w);
1060: } else {
1061: PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(npoints, alpha, beta, x, w);
1062: }
1063: if (alpha == beta) { /* symmetrize */
1064: PetscInt i;
1065: for (i = 0; i < (npoints + 1) / 2; i++) {
1066: PetscInt j = npoints - 1 - i;
1067: PetscReal xi = x[i];
1068: PetscReal xj = x[j];
1069: PetscReal wi = w[i];
1070: PetscReal wj = w[j];
1072: x[i] = (xi - xj) / 2.;
1073: x[j] = (xj - xi) / 2.;
1074: w[i] = w[j] = (wi + wj) / 2.;
1075: }
1076: }
1077: return(0);
1078: }
1080: /*@
1081: PetscDTGaussJacobiQuadrature - quadrature for the interval [a, b] with the weight function
1082: $(x-a)^\alpha (x-b)^\beta$.
1084: Not collective
1086: Input Parameters:
1087: + npoints - the number of points in the quadrature rule
1088: . a - the left endpoint of the interval
1089: . b - the right endpoint of the interval
1090: . alpha - the left exponent
1091: - beta - the right exponent
1093: Output Parameters:
1094: + x - array of length npoints, the locations of the quadrature points
1095: - w - array of length npoints, the weights of the quadrature points
1097: Level: intermediate
1099: Note: this quadrature rule is exact for polynomials up to degree 2*npoints - 1.
1100: @*/
1101: PetscErrorCode PetscDTGaussJacobiQuadrature(PetscInt npoints,PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
1102: {
1103: PetscInt i;
1107: PetscDTGaussJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal);
1108: if (a != -1. || b != 1.) { /* shift */
1109: for (i = 0; i < npoints; i++) {
1110: x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1111: w[i] *= (b - a) / 2.;
1112: }
1113: }
1114: return(0);
1115: }
1117: static PetscErrorCode PetscDTGaussLobattoJacobiQuadrature_Internal(PetscInt npoints,PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
1118: {
1119: PetscInt i;
1123: if (npoints < 2) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Number of points must be positive");
1124: /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
1125: if (alpha <= -1.) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"alpha must be > -1.");
1126: if (beta <= -1.) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"beta must be > -1.");
1128: x[0] = -1.;
1129: x[npoints-1] = 1.;
1130: if (npoints > 2) {
1131: PetscDTGaussJacobiQuadrature_Internal(npoints-2, alpha+1., beta+1., &x[1], &w[1], newton);
1132: }
1133: for (i = 1; i < npoints - 1; i++) {
1134: w[i] /= (1. - x[i]*x[i]);
1135: }
1136: PetscDTGaussLobattoJacobiEndweights_Internal(npoints, alpha, beta, &w[0], &w[npoints-1]);
1137: return(0);
1138: }
1140: /*@
1141: PetscDTGaussLobattoJacobiQuadrature - quadrature for the interval [a, b] with the weight function
1142: $(x-a)^\alpha (x-b)^\beta$, with endpoints a and b included as quadrature points.
1144: Not collective
1146: Input Parameters:
1147: + npoints - the number of points in the quadrature rule
1148: . a - the left endpoint of the interval
1149: . b - the right endpoint of the interval
1150: . alpha - the left exponent
1151: - beta - the right exponent
1153: Output Parameters:
1154: + x - array of length npoints, the locations of the quadrature points
1155: - w - array of length npoints, the weights of the quadrature points
1157: Level: intermediate
1159: Note: this quadrature rule is exact for polynomials up to degree 2*npoints - 3.
1160: @*/
1161: PetscErrorCode PetscDTGaussLobattoJacobiQuadrature(PetscInt npoints,PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
1162: {
1163: PetscInt i;
1167: PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal);
1168: if (a != -1. || b != 1.) { /* shift */
1169: for (i = 0; i < npoints; i++) {
1170: x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1171: w[i] *= (b - a) / 2.;
1172: }
1173: }
1174: return(0);
1175: }
1177: /*@
1178: PetscDTGaussQuadrature - create Gauss-Legendre quadrature
1180: Not Collective
1182: Input Arguments:
1183: + npoints - number of points
1184: . a - left end of interval (often-1)
1185: - b - right end of interval (often +1)
1187: Output Arguments:
1188: + x - quadrature points
1189: - w - quadrature weights
1191: Level: intermediate
1193: References:
1194: . 1. - Golub and Welsch, Calculation of Quadrature Rules, Math. Comp. 23(106), 1969.
1196: .seealso: PetscDTLegendreEval()
1197: @*/
1198: PetscErrorCode PetscDTGaussQuadrature(PetscInt npoints,PetscReal a,PetscReal b,PetscReal *x,PetscReal *w)
1199: {
1200: PetscInt i;
1204: PetscDTGaussJacobiQuadrature_Internal(npoints, 0., 0., x, w, PetscDTGaussQuadratureNewton_Internal);
1205: if (a != -1. || b != 1.) { /* shift */
1206: for (i = 0; i < npoints; i++) {
1207: x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
1208: w[i] *= (b - a) / 2.;
1209: }
1210: }
1211: return(0);
1212: }
1214: /*@C
1215: PetscDTGaussLobattoLegendreQuadrature - creates a set of the locations and weights of the Gauss-Lobatto-Legendre
1216: nodes of a given size on the domain [-1,1]
1218: Not Collective
1220: Input Parameter:
1221: + n - number of grid nodes
1222: - type - PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA or PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON
1224: Output Arguments:
1225: + x - quadrature points
1226: - w - quadrature weights
1228: Notes:
1229: For n > 30 the Newton approach computes duplicate (incorrect) values for some nodes because the initial guess is apparently not
1230: close enough to the desired solution
1232: These are useful for implementing spectral methods based on Gauss-Lobatto-Legendre (GLL) nodes
1234: 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
1236: Level: intermediate
1238: .seealso: PetscDTGaussQuadrature()
1240: @*/
1241: PetscErrorCode PetscDTGaussLobattoLegendreQuadrature(PetscInt npoints,PetscGaussLobattoLegendreCreateType type,PetscReal *x,PetscReal *w)
1242: {
1243: PetscBool newton;
1247: if (npoints < 2) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Must provide at least 2 grid points per element");
1248: newton = (PetscBool) (type == PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON);
1249: PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, 0., 0., x, w, newton);
1250: return(0);
1251: }
1253: /*@
1254: PetscDTGaussTensorQuadrature - creates a tensor-product Gauss quadrature
1256: Not Collective
1258: Input Arguments:
1259: + dim - The spatial dimension
1260: . Nc - The number of components
1261: . npoints - number of points in one dimension
1262: . a - left end of interval (often-1)
1263: - b - right end of interval (often +1)
1265: Output Argument:
1266: . q - A PetscQuadrature object
1268: Level: intermediate
1270: .seealso: PetscDTGaussQuadrature(), PetscDTLegendreEval()
1271: @*/
1272: PetscErrorCode PetscDTGaussTensorQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
1273: {
1274: PetscInt totpoints = dim > 1 ? dim > 2 ? npoints*PetscSqr(npoints) : PetscSqr(npoints) : npoints, i, j, k, c;
1275: PetscReal *x, *w, *xw, *ww;
1279: PetscMalloc1(totpoints*dim,&x);
1280: PetscMalloc1(totpoints*Nc,&w);
1281: /* Set up the Golub-Welsch system */
1282: switch (dim) {
1283: case 0:
1284: PetscFree(x);
1285: PetscFree(w);
1286: PetscMalloc1(1, &x);
1287: PetscMalloc1(Nc, &w);
1288: x[0] = 0.0;
1289: for (c = 0; c < Nc; ++c) w[c] = 1.0;
1290: break;
1291: case 1:
1292: PetscMalloc1(npoints,&ww);
1293: PetscDTGaussQuadrature(npoints, a, b, x, ww);
1294: for (i = 0; i < npoints; ++i) for (c = 0; c < Nc; ++c) w[i*Nc+c] = ww[i];
1295: PetscFree(ww);
1296: break;
1297: case 2:
1298: PetscMalloc2(npoints,&xw,npoints,&ww);
1299: PetscDTGaussQuadrature(npoints, a, b, xw, ww);
1300: for (i = 0; i < npoints; ++i) {
1301: for (j = 0; j < npoints; ++j) {
1302: x[(i*npoints+j)*dim+0] = xw[i];
1303: x[(i*npoints+j)*dim+1] = xw[j];
1304: for (c = 0; c < Nc; ++c) w[(i*npoints+j)*Nc+c] = ww[i] * ww[j];
1305: }
1306: }
1307: PetscFree2(xw,ww);
1308: break;
1309: case 3:
1310: PetscMalloc2(npoints,&xw,npoints,&ww);
1311: PetscDTGaussQuadrature(npoints, a, b, xw, ww);
1312: for (i = 0; i < npoints; ++i) {
1313: for (j = 0; j < npoints; ++j) {
1314: for (k = 0; k < npoints; ++k) {
1315: x[((i*npoints+j)*npoints+k)*dim+0] = xw[i];
1316: x[((i*npoints+j)*npoints+k)*dim+1] = xw[j];
1317: x[((i*npoints+j)*npoints+k)*dim+2] = xw[k];
1318: for (c = 0; c < Nc; ++c) w[((i*npoints+j)*npoints+k)*Nc+c] = ww[i] * ww[j] * ww[k];
1319: }
1320: }
1321: }
1322: PetscFree2(xw,ww);
1323: break;
1324: default:
1325: SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %d", dim);
1326: }
1327: PetscQuadratureCreate(PETSC_COMM_SELF, q);
1328: PetscQuadratureSetOrder(*q, 2*npoints-1);
1329: PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w);
1330: PetscObjectChangeTypeName((PetscObject)*q,"GaussTensor");
1331: return(0);
1332: }
1334: /* Maps from [-1,1]^2 to the (-1,1) reference triangle */
1335: PETSC_STATIC_INLINE PetscErrorCode PetscDTMapCubeToTetrahedron_Internal(PetscReal x, PetscReal y, PetscReal z, PetscReal *xi, PetscReal *eta, PetscReal *zeta)
1336: {
1338: *xi = 0.25 * (1.0 + x) * (1.0 - y) * (1.0 - z) - 1.0;
1339: *eta = 0.5 * (1.0 + y) * (1.0 - z) - 1.0;
1340: *zeta = z;
1341: return(0);
1342: }
1345: /*@
1346: PetscDTStroudConicalQuadrature - create Stroud conical quadrature for a simplex
1348: Not Collective
1350: Input Arguments:
1351: + dim - The simplex dimension
1352: . Nc - The number of components
1353: . npoints - The number of points in one dimension
1354: . a - left end of interval (often-1)
1355: - b - right end of interval (often +1)
1357: Output Argument:
1358: . q - A PetscQuadrature object
1360: Level: intermediate
1362: References:
1363: . 1. - Karniadakis and Sherwin. FIAT
1365: Note: For dim == 1, this is Gauss-Legendre quadrature
1367: .seealso: PetscDTGaussTensorQuadrature(), PetscDTGaussQuadrature()
1368: @*/
1369: PetscErrorCode PetscDTStroudConicalQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
1370: {
1371: PetscInt totpoints = dim > 1 ? dim > 2 ? npoints*PetscSqr(npoints) : PetscSqr(npoints) : npoints;
1372: PetscReal *px, *wx, *py, *wy, *pz, *wz, *x, *w;
1376: if ((a != -1.0) || (b != 1.0)) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must use default internal right now");
1377: PetscMalloc1(totpoints*dim, &x);
1378: PetscMalloc1(totpoints*Nc, &w);
1379: switch (dim) {
1380: case 0:
1381: PetscFree(x);
1382: PetscFree(w);
1383: PetscMalloc1(1, &x);
1384: PetscMalloc1(Nc, &w);
1385: x[0] = 0.0;
1386: for (c = 0; c < Nc; ++c) w[c] = 1.0;
1387: break;
1388: case 1:
1389: PetscMalloc1(npoints,&wx);
1390: PetscDTGaussJacobiQuadrature(npoints, -1., 1., 0.0, 0.0, x, wx);
1391: for (i = 0; i < npoints; ++i) for (c = 0; c < Nc; ++c) w[i*Nc+c] = wx[i];
1392: PetscFree(wx);
1393: break;
1394: case 2:
1395: PetscMalloc4(npoints,&px,npoints,&wx,npoints,&py,npoints,&wy);
1396: PetscDTGaussJacobiQuadrature(npoints, -1., 1., 0.0, 0.0, px, wx);
1397: PetscDTGaussJacobiQuadrature(npoints, -1., 1., 1.0, 0.0, py, wy);
1398: for (i = 0; i < npoints; ++i) {
1399: for (j = 0; j < npoints; ++j) {
1400: PetscDTMapSquareToTriangle_Internal(px[i], py[j], &x[(i*npoints+j)*2+0], &x[(i*npoints+j)*2+1]);
1401: for (c = 0; c < Nc; ++c) w[(i*npoints+j)*Nc+c] = 0.5 * wx[i] * wy[j];
1402: }
1403: }
1404: PetscFree4(px,wx,py,wy);
1405: break;
1406: case 3:
1407: PetscMalloc6(npoints,&px,npoints,&wx,npoints,&py,npoints,&wy,npoints,&pz,npoints,&wz);
1408: PetscDTGaussJacobiQuadrature(npoints, -1., 1., 0.0, 0.0, px, wx);
1409: PetscDTGaussJacobiQuadrature(npoints, -1., 1., 1.0, 0.0, py, wy);
1410: PetscDTGaussJacobiQuadrature(npoints, -1., 1., 2.0, 0.0, pz, wz);
1411: for (i = 0; i < npoints; ++i) {
1412: for (j = 0; j < npoints; ++j) {
1413: for (k = 0; k < npoints; ++k) {
1414: PetscDTMapCubeToTetrahedron_Internal(px[i], py[j], pz[k], &x[((i*npoints+j)*npoints+k)*3+0], &x[((i*npoints+j)*npoints+k)*3+1], &x[((i*npoints+j)*npoints+k)*3+2]);
1415: for (c = 0; c < Nc; ++c) w[((i*npoints+j)*npoints+k)*Nc+c] = 0.125 * wx[i] * wy[j] * wz[k];
1416: }
1417: }
1418: }
1419: PetscFree6(px,wx,py,wy,pz,wz);
1420: break;
1421: default:
1422: SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %d", dim);
1423: }
1424: PetscQuadratureCreate(PETSC_COMM_SELF, q);
1425: PetscQuadratureSetOrder(*q, 2*npoints-1);
1426: PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w);
1427: PetscObjectChangeTypeName((PetscObject)*q,"GaussJacobi");
1428: return(0);
1429: }
1431: /*@
1432: PetscDTTanhSinhTensorQuadrature - create tanh-sinh quadrature for a tensor product cell
1434: Not Collective
1436: Input Arguments:
1437: + dim - The cell dimension
1438: . level - The number of points in one dimension, 2^l
1439: . a - left end of interval (often-1)
1440: - b - right end of interval (often +1)
1442: Output Argument:
1443: . q - A PetscQuadrature object
1445: Level: intermediate
1447: .seealso: PetscDTGaussTensorQuadrature()
1448: @*/
1449: PetscErrorCode PetscDTTanhSinhTensorQuadrature(PetscInt dim, PetscInt level, PetscReal a, PetscReal b, PetscQuadrature *q)
1450: {
1451: const PetscInt p = 16; /* Digits of precision in the evaluation */
1452: const PetscReal alpha = (b-a)/2.; /* Half-width of the integration interval */
1453: const PetscReal beta = (b+a)/2.; /* Center of the integration interval */
1454: const PetscReal h = PetscPowReal(2.0, -level); /* Step size, length between x_k */
1455: PetscReal xk; /* Quadrature point x_k on reference domain [-1, 1] */
1456: PetscReal wk = 0.5*PETSC_PI; /* Quadrature weight at x_k */
1457: PetscReal *x, *w;
1458: PetscInt K, k, npoints;
1459: PetscErrorCode ierr;
1462: if (dim > 1) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_SUP, "Dimension %d not yet implemented", dim);
1463: if (!level) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a number of significant digits");
1464: /* Find K such that the weights are < 32 digits of precision */
1465: for (K = 1; PetscAbsReal(PetscLog10Real(wk)) < 2*p; ++K) {
1466: wk = 0.5*h*PETSC_PI*PetscCoshReal(K*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(K*h)));
1467: }
1468: PetscQuadratureCreate(PETSC_COMM_SELF, q);
1469: PetscQuadratureSetOrder(*q, 2*K+1);
1470: npoints = 2*K-1;
1471: PetscMalloc1(npoints*dim, &x);
1472: PetscMalloc1(npoints, &w);
1473: /* Center term */
1474: x[0] = beta;
1475: w[0] = 0.5*alpha*PETSC_PI;
1476: for (k = 1; k < K; ++k) {
1477: wk = 0.5*alpha*h*PETSC_PI*PetscCoshReal(k*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h)));
1478: xk = PetscTanhReal(0.5*PETSC_PI*PetscSinhReal(k*h));
1479: x[2*k-1] = -alpha*xk+beta;
1480: w[2*k-1] = wk;
1481: x[2*k+0] = alpha*xk+beta;
1482: w[2*k+0] = wk;
1483: }
1484: PetscQuadratureSetData(*q, dim, 1, npoints, x, w);
1485: return(0);
1486: }
1488: PetscErrorCode PetscDTTanhSinhIntegrate(void (*func)(PetscReal, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscReal *sol)
1489: {
1490: const PetscInt p = 16; /* Digits of precision in the evaluation */
1491: const PetscReal alpha = (b-a)/2.; /* Half-width of the integration interval */
1492: const PetscReal beta = (b+a)/2.; /* Center of the integration interval */
1493: PetscReal h = 1.0; /* Step size, length between x_k */
1494: PetscInt l = 0; /* Level of refinement, h = 2^{-l} */
1495: PetscReal osum = 0.0; /* Integral on last level */
1496: PetscReal psum = 0.0; /* Integral on the level before the last level */
1497: PetscReal sum; /* Integral on current level */
1498: PetscReal yk; /* Quadrature point 1 - x_k on reference domain [-1, 1] */
1499: PetscReal lx, rx; /* Quadrature points to the left and right of 0 on the real domain [a, b] */
1500: PetscReal wk; /* Quadrature weight at x_k */
1501: PetscReal lval, rval; /* Terms in the quadature sum to the left and right of 0 */
1502: PetscInt d; /* Digits of precision in the integral */
1505: if (digits <= 0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
1506: /* Center term */
1507: func(beta, &lval);
1508: sum = 0.5*alpha*PETSC_PI*lval;
1509: /* */
1510: do {
1511: PetscReal lterm, rterm, maxTerm = 0.0, d1, d2, d3, d4;
1512: PetscInt k = 1;
1514: ++l;
1515: /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %D sum: %15.15f\n", l, sum); */
1516: /* At each level of refinement, h --> h/2 and sum --> sum/2 */
1517: psum = osum;
1518: osum = sum;
1519: h *= 0.5;
1520: sum *= 0.5;
1521: do {
1522: wk = 0.5*h*PETSC_PI*PetscCoshReal(k*h)/PetscSqr(PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h)));
1523: yk = 1.0/(PetscExpReal(0.5*PETSC_PI*PetscSinhReal(k*h)) * PetscCoshReal(0.5*PETSC_PI*PetscSinhReal(k*h)));
1524: lx = -alpha*(1.0 - yk)+beta;
1525: rx = alpha*(1.0 - yk)+beta;
1526: func(lx, &lval);
1527: func(rx, &rval);
1528: lterm = alpha*wk*lval;
1529: maxTerm = PetscMax(PetscAbsReal(lterm), maxTerm);
1530: sum += lterm;
1531: rterm = alpha*wk*rval;
1532: maxTerm = PetscMax(PetscAbsReal(rterm), maxTerm);
1533: sum += rterm;
1534: ++k;
1535: /* Only need to evaluate every other point on refined levels */
1536: if (l != 1) ++k;
1537: } while (PetscAbsReal(PetscLog10Real(wk)) < p); /* Only need to evaluate sum until weights are < 32 digits of precision */
1539: d1 = PetscLog10Real(PetscAbsReal(sum - osum));
1540: d2 = PetscLog10Real(PetscAbsReal(sum - psum));
1541: d3 = PetscLog10Real(maxTerm) - p;
1542: if (PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)) == 0.0) d4 = 0.0;
1543: else d4 = PetscLog10Real(PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)));
1544: d = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1)/d2, 2*d1), d3), d4)));
1545: } while (d < digits && l < 12);
1546: *sol = sum;
1548: return(0);
1549: }
1551: #if defined(PETSC_HAVE_MPFR)
1552: PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(PetscReal, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscReal *sol)
1553: {
1554: const PetscInt safetyFactor = 2; /* Calculate abcissa until 2*p digits */
1555: PetscInt l = 0; /* Level of refinement, h = 2^{-l} */
1556: mpfr_t alpha; /* Half-width of the integration interval */
1557: mpfr_t beta; /* Center of the integration interval */
1558: mpfr_t h; /* Step size, length between x_k */
1559: mpfr_t osum; /* Integral on last level */
1560: mpfr_t psum; /* Integral on the level before the last level */
1561: mpfr_t sum; /* Integral on current level */
1562: mpfr_t yk; /* Quadrature point 1 - x_k on reference domain [-1, 1] */
1563: mpfr_t lx, rx; /* Quadrature points to the left and right of 0 on the real domain [a, b] */
1564: mpfr_t wk; /* Quadrature weight at x_k */
1565: PetscReal lval, rval; /* Terms in the quadature sum to the left and right of 0 */
1566: PetscInt d; /* Digits of precision in the integral */
1567: mpfr_t pi2, kh, msinh, mcosh, maxTerm, curTerm, tmp;
1570: if (digits <= 0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
1571: /* Create high precision storage */
1572: 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);
1573: /* Initialization */
1574: mpfr_set_d(alpha, 0.5*(b-a), MPFR_RNDN);
1575: mpfr_set_d(beta, 0.5*(b+a), MPFR_RNDN);
1576: mpfr_set_d(osum, 0.0, MPFR_RNDN);
1577: mpfr_set_d(psum, 0.0, MPFR_RNDN);
1578: mpfr_set_d(h, 1.0, MPFR_RNDN);
1579: mpfr_const_pi(pi2, MPFR_RNDN);
1580: mpfr_mul_d(pi2, pi2, 0.5, MPFR_RNDN);
1581: /* Center term */
1582: func(0.5*(b+a), &lval);
1583: mpfr_set(sum, pi2, MPFR_RNDN);
1584: mpfr_mul(sum, sum, alpha, MPFR_RNDN);
1585: mpfr_mul_d(sum, sum, lval, MPFR_RNDN);
1586: /* */
1587: do {
1588: PetscReal d1, d2, d3, d4;
1589: PetscInt k = 1;
1591: ++l;
1592: mpfr_set_d(maxTerm, 0.0, MPFR_RNDN);
1593: /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %D sum: %15.15f\n", l, sum); */
1594: /* At each level of refinement, h --> h/2 and sum --> sum/2 */
1595: mpfr_set(psum, osum, MPFR_RNDN);
1596: mpfr_set(osum, sum, MPFR_RNDN);
1597: mpfr_mul_d(h, h, 0.5, MPFR_RNDN);
1598: mpfr_mul_d(sum, sum, 0.5, MPFR_RNDN);
1599: do {
1600: mpfr_set_si(kh, k, MPFR_RNDN);
1601: mpfr_mul(kh, kh, h, MPFR_RNDN);
1602: /* Weight */
1603: mpfr_set(wk, h, MPFR_RNDN);
1604: mpfr_sinh_cosh(msinh, mcosh, kh, MPFR_RNDN);
1605: mpfr_mul(msinh, msinh, pi2, MPFR_RNDN);
1606: mpfr_mul(mcosh, mcosh, pi2, MPFR_RNDN);
1607: mpfr_cosh(tmp, msinh, MPFR_RNDN);
1608: mpfr_sqr(tmp, tmp, MPFR_RNDN);
1609: mpfr_mul(wk, wk, mcosh, MPFR_RNDN);
1610: mpfr_div(wk, wk, tmp, MPFR_RNDN);
1611: /* Abscissa */
1612: mpfr_set_d(yk, 1.0, MPFR_RNDZ);
1613: mpfr_cosh(tmp, msinh, MPFR_RNDN);
1614: mpfr_div(yk, yk, tmp, MPFR_RNDZ);
1615: mpfr_exp(tmp, msinh, MPFR_RNDN);
1616: mpfr_div(yk, yk, tmp, MPFR_RNDZ);
1617: /* Quadrature points */
1618: mpfr_sub_d(lx, yk, 1.0, MPFR_RNDZ);
1619: mpfr_mul(lx, lx, alpha, MPFR_RNDU);
1620: mpfr_add(lx, lx, beta, MPFR_RNDU);
1621: mpfr_d_sub(rx, 1.0, yk, MPFR_RNDZ);
1622: mpfr_mul(rx, rx, alpha, MPFR_RNDD);
1623: mpfr_add(rx, rx, beta, MPFR_RNDD);
1624: /* Evaluation */
1625: func(mpfr_get_d(lx, MPFR_RNDU), &lval);
1626: func(mpfr_get_d(rx, MPFR_RNDD), &rval);
1627: /* Update */
1628: mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
1629: mpfr_mul_d(tmp, tmp, lval, MPFR_RNDN);
1630: mpfr_add(sum, sum, tmp, MPFR_RNDN);
1631: mpfr_abs(tmp, tmp, MPFR_RNDN);
1632: mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
1633: mpfr_set(curTerm, tmp, MPFR_RNDN);
1634: mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
1635: mpfr_mul_d(tmp, tmp, rval, MPFR_RNDN);
1636: mpfr_add(sum, sum, tmp, MPFR_RNDN);
1637: mpfr_abs(tmp, tmp, MPFR_RNDN);
1638: mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
1639: mpfr_max(curTerm, curTerm, tmp, MPFR_RNDN);
1640: ++k;
1641: /* Only need to evaluate every other point on refined levels */
1642: if (l != 1) ++k;
1643: mpfr_log10(tmp, wk, MPFR_RNDN);
1644: mpfr_abs(tmp, tmp, MPFR_RNDN);
1645: } while (mpfr_get_d(tmp, MPFR_RNDN) < safetyFactor*digits); /* Only need to evaluate sum until weights are < 32 digits of precision */
1646: mpfr_sub(tmp, sum, osum, MPFR_RNDN);
1647: mpfr_abs(tmp, tmp, MPFR_RNDN);
1648: mpfr_log10(tmp, tmp, MPFR_RNDN);
1649: d1 = mpfr_get_d(tmp, MPFR_RNDN);
1650: mpfr_sub(tmp, sum, psum, MPFR_RNDN);
1651: mpfr_abs(tmp, tmp, MPFR_RNDN);
1652: mpfr_log10(tmp, tmp, MPFR_RNDN);
1653: d2 = mpfr_get_d(tmp, MPFR_RNDN);
1654: mpfr_log10(tmp, maxTerm, MPFR_RNDN);
1655: d3 = mpfr_get_d(tmp, MPFR_RNDN) - digits;
1656: mpfr_log10(tmp, curTerm, MPFR_RNDN);
1657: d4 = mpfr_get_d(tmp, MPFR_RNDN);
1658: d = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1)/d2, 2*d1), d3), d4)));
1659: } while (d < digits && l < 8);
1660: *sol = mpfr_get_d(sum, MPFR_RNDN);
1661: /* Cleanup */
1662: mpfr_clears(alpha, beta, h, sum, osum, psum, yk, wk, lx, rx, tmp, maxTerm, curTerm, pi2, kh, msinh, mcosh, NULL);
1663: return(0);
1664: }
1665: #else
1667: PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(PetscReal, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, PetscReal *sol)
1668: {
1669: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "This method will not work without MPFR. Reconfigure using --download-mpfr --download-gmp");
1670: }
1671: #endif
1673: /* Overwrites A. Can only handle full-rank problems with m>=n
1674: * A in column-major format
1675: * Ainv in row-major format
1676: * tau has length m
1677: * worksize must be >= max(1,n)
1678: */
1679: static PetscErrorCode PetscDTPseudoInverseQR(PetscInt m,PetscInt mstride,PetscInt n,PetscReal *A_in,PetscReal *Ainv_out,PetscScalar *tau,PetscInt worksize,PetscScalar *work)
1680: {
1682: PetscBLASInt M,N,K,lda,ldb,ldwork,info;
1683: PetscScalar *A,*Ainv,*R,*Q,Alpha;
1686: #if defined(PETSC_USE_COMPLEX)
1687: {
1688: PetscInt i,j;
1689: PetscMalloc2(m*n,&A,m*n,&Ainv);
1690: for (j=0; j<n; j++) {
1691: for (i=0; i<m; i++) A[i+m*j] = A_in[i+mstride*j];
1692: }
1693: mstride = m;
1694: }
1695: #else
1696: A = A_in;
1697: Ainv = Ainv_out;
1698: #endif
1700: PetscBLASIntCast(m,&M);
1701: PetscBLASIntCast(n,&N);
1702: PetscBLASIntCast(mstride,&lda);
1703: PetscBLASIntCast(worksize,&ldwork);
1704: PetscFPTrapPush(PETSC_FP_TRAP_OFF);
1705: PetscStackCallBLAS("LAPACKgeqrf",LAPACKgeqrf_(&M,&N,A,&lda,tau,work,&ldwork,&info));
1706: PetscFPTrapPop();
1707: if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"xGEQRF error");
1708: R = A; /* Upper triangular part of A now contains R, the rest contains the elementary reflectors */
1710: /* Extract an explicit representation of Q */
1711: Q = Ainv;
1712: PetscArraycpy(Q,A,mstride*n);
1713: K = N; /* full rank */
1714: PetscStackCallBLAS("LAPACKorgqr",LAPACKorgqr_(&M,&N,&K,Q,&lda,tau,work,&ldwork,&info));
1715: if (info) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"xORGQR/xUNGQR error");
1717: /* Compute A^{-T} = (R^{-1} Q^T)^T = Q R^{-T} */
1718: Alpha = 1.0;
1719: ldb = lda;
1720: PetscStackCallBLAS("BLAStrsm",BLAStrsm_("Right","Upper","ConjugateTranspose","NotUnitTriangular",&M,&N,&Alpha,R,&lda,Q,&ldb));
1721: /* Ainv is Q, overwritten with inverse */
1723: #if defined(PETSC_USE_COMPLEX)
1724: {
1725: PetscInt i;
1726: for (i=0; i<m*n; i++) Ainv_out[i] = PetscRealPart(Ainv[i]);
1727: PetscFree2(A,Ainv);
1728: }
1729: #endif
1730: return(0);
1731: }
1733: /* Computes integral of L_p' over intervals {(x0,x1),(x1,x2),...} */
1734: static PetscErrorCode PetscDTLegendreIntegrate(PetscInt ninterval,const PetscReal *x,PetscInt ndegree,const PetscInt *degrees,PetscBool Transpose,PetscReal *B)
1735: {
1737: PetscReal *Bv;
1738: PetscInt i,j;
1741: PetscMalloc1((ninterval+1)*ndegree,&Bv);
1742: /* Point evaluation of L_p on all the source vertices */
1743: PetscDTLegendreEval(ninterval+1,x,ndegree,degrees,Bv,NULL,NULL);
1744: /* Integral over each interval: \int_a^b L_p' = L_p(b)-L_p(a) */
1745: for (i=0; i<ninterval; i++) {
1746: for (j=0; j<ndegree; j++) {
1747: if (Transpose) B[i+ninterval*j] = Bv[(i+1)*ndegree+j] - Bv[i*ndegree+j];
1748: else B[i*ndegree+j] = Bv[(i+1)*ndegree+j] - Bv[i*ndegree+j];
1749: }
1750: }
1751: PetscFree(Bv);
1752: return(0);
1753: }
1755: /*@
1756: PetscDTReconstructPoly - create matrix representing polynomial reconstruction using cell intervals and evaluation at target intervals
1758: Not Collective
1760: Input Arguments:
1761: + degree - degree of reconstruction polynomial
1762: . nsource - number of source intervals
1763: . sourcex - sorted coordinates of source cell boundaries (length nsource+1)
1764: . ntarget - number of target intervals
1765: - targetx - sorted coordinates of target cell boundaries (length ntarget+1)
1767: Output Arguments:
1768: . R - reconstruction matrix, utarget = sum_s R[t*nsource+s] * usource[s]
1770: Level: advanced
1772: .seealso: PetscDTLegendreEval()
1773: @*/
1774: PetscErrorCode PetscDTReconstructPoly(PetscInt degree,PetscInt nsource,const PetscReal *sourcex,PetscInt ntarget,const PetscReal *targetx,PetscReal *R)
1775: {
1777: PetscInt i,j,k,*bdegrees,worksize;
1778: PetscReal xmin,xmax,center,hscale,*sourcey,*targety,*Bsource,*Bsinv,*Btarget;
1779: PetscScalar *tau,*work;
1785: if (degree >= nsource) SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Reconstruction degree %D must be less than number of source intervals %D",degree,nsource);
1786: #if defined(PETSC_USE_DEBUG)
1787: for (i=0; i<nsource; i++) {
1788: if (sourcex[i] >= sourcex[i+1]) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Source interval %D has negative orientation (%g,%g)",i,(double)sourcex[i],(double)sourcex[i+1]);
1789: }
1790: for (i=0; i<ntarget; i++) {
1791: if (targetx[i] >= targetx[i+1]) SETERRQ3(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Target interval %D has negative orientation (%g,%g)",i,(double)targetx[i],(double)targetx[i+1]);
1792: }
1793: #endif
1794: xmin = PetscMin(sourcex[0],targetx[0]);
1795: xmax = PetscMax(sourcex[nsource],targetx[ntarget]);
1796: center = (xmin + xmax)/2;
1797: hscale = (xmax - xmin)/2;
1798: worksize = nsource;
1799: PetscMalloc4(degree+1,&bdegrees,nsource+1,&sourcey,nsource*(degree+1),&Bsource,worksize,&work);
1800: PetscMalloc4(nsource,&tau,nsource*(degree+1),&Bsinv,ntarget+1,&targety,ntarget*(degree+1),&Btarget);
1801: for (i=0; i<=nsource; i++) sourcey[i] = (sourcex[i]-center)/hscale;
1802: for (i=0; i<=degree; i++) bdegrees[i] = i+1;
1803: PetscDTLegendreIntegrate(nsource,sourcey,degree+1,bdegrees,PETSC_TRUE,Bsource);
1804: PetscDTPseudoInverseQR(nsource,nsource,degree+1,Bsource,Bsinv,tau,nsource,work);
1805: for (i=0; i<=ntarget; i++) targety[i] = (targetx[i]-center)/hscale;
1806: PetscDTLegendreIntegrate(ntarget,targety,degree+1,bdegrees,PETSC_FALSE,Btarget);
1807: for (i=0; i<ntarget; i++) {
1808: PetscReal rowsum = 0;
1809: for (j=0; j<nsource; j++) {
1810: PetscReal sum = 0;
1811: for (k=0; k<degree+1; k++) {
1812: sum += Btarget[i*(degree+1)+k] * Bsinv[k*nsource+j];
1813: }
1814: R[i*nsource+j] = sum;
1815: rowsum += sum;
1816: }
1817: for (j=0; j<nsource; j++) R[i*nsource+j] /= rowsum; /* normalize each row */
1818: }
1819: PetscFree4(bdegrees,sourcey,Bsource,work);
1820: PetscFree4(tau,Bsinv,targety,Btarget);
1821: return(0);
1822: }
1824: /*@C
1825: PetscGaussLobattoLegendreIntegrate - Compute the L2 integral of a function on the GLL points
1827: Not Collective
1829: Input Parameter:
1830: + n - the number of GLL nodes
1831: . nodes - the GLL nodes
1832: . weights - the GLL weights
1833: - f - the function values at the nodes
1835: Output Parameter:
1836: . in - the value of the integral
1838: Level: beginner
1840: .seealso: PetscDTGaussLobattoLegendreQuadrature()
1842: @*/
1843: PetscErrorCode PetscGaussLobattoLegendreIntegrate(PetscInt n,PetscReal *nodes,PetscReal *weights,const PetscReal *f,PetscReal *in)
1844: {
1845: PetscInt i;
1848: *in = 0.;
1849: for (i=0; i<n; i++) {
1850: *in += f[i]*f[i]*weights[i];
1851: }
1852: return(0);
1853: }
1855: /*@C
1856: PetscGaussLobattoLegendreElementLaplacianCreate - computes the Laplacian for a single 1d GLL element
1858: Not Collective
1860: Input Parameter:
1861: + n - the number of GLL nodes
1862: . nodes - the GLL nodes
1863: - weights - the GLL weights
1865: Output Parameter:
1866: . A - the stiffness element
1868: Level: beginner
1870: Notes:
1871: Destroy this with PetscGaussLobattoLegendreElementLaplacianDestroy()
1873: 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)
1875: .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianDestroy()
1877: @*/
1878: PetscErrorCode PetscGaussLobattoLegendreElementLaplacianCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
1879: {
1880: PetscReal **A;
1881: PetscErrorCode ierr;
1882: const PetscReal *gllnodes = nodes;
1883: const PetscInt p = n-1;
1884: PetscReal z0,z1,z2 = -1,x,Lpj,Lpr;
1885: PetscInt i,j,nn,r;
1888: PetscMalloc1(n,&A);
1889: PetscMalloc1(n*n,&A[0]);
1890: for (i=1; i<n; i++) A[i] = A[i-1]+n;
1892: for (j=1; j<p; j++) {
1893: x = gllnodes[j];
1894: z0 = 1.;
1895: z1 = x;
1896: for (nn=1; nn<p; nn++) {
1897: z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
1898: z0 = z1;
1899: z1 = z2;
1900: }
1901: Lpj=z2;
1902: for (r=1; r<p; r++) {
1903: if (r == j) {
1904: A[j][j]=2./(3.*(1.-gllnodes[j]*gllnodes[j])*Lpj*Lpj);
1905: } else {
1906: x = gllnodes[r];
1907: z0 = 1.;
1908: z1 = x;
1909: for (nn=1; nn<p; nn++) {
1910: z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
1911: z0 = z1;
1912: z1 = z2;
1913: }
1914: Lpr = z2;
1915: A[r][j] = 4./(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*Lpr*(gllnodes[j]-gllnodes[r])*(gllnodes[j]-gllnodes[r]));
1916: }
1917: }
1918: }
1919: for (j=1; j<p+1; j++) {
1920: x = gllnodes[j];
1921: z0 = 1.;
1922: z1 = x;
1923: for (nn=1; nn<p; nn++) {
1924: z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
1925: z0 = z1;
1926: z1 = z2;
1927: }
1928: Lpj = z2;
1929: A[j][0] = 4.*PetscPowRealInt(-1.,p)/(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*(1.+gllnodes[j])*(1.+gllnodes[j]));
1930: A[0][j] = A[j][0];
1931: }
1932: for (j=0; j<p; j++) {
1933: x = gllnodes[j];
1934: z0 = 1.;
1935: z1 = x;
1936: for (nn=1; nn<p; nn++) {
1937: z2 = x*z1*(2.*((PetscReal)nn)+1.)/(((PetscReal)nn)+1.)-z0*(((PetscReal)nn)/(((PetscReal)nn)+1.));
1938: z0 = z1;
1939: z1 = z2;
1940: }
1941: Lpj=z2;
1943: A[p][j] = 4./(((PetscReal)p)*(((PetscReal)p)+1.)*Lpj*(1.-gllnodes[j])*(1.-gllnodes[j]));
1944: A[j][p] = A[p][j];
1945: }
1946: A[0][0]=0.5+(((PetscReal)p)*(((PetscReal)p)+1.)-2.)/6.;
1947: A[p][p]=A[0][0];
1948: *AA = A;
1949: return(0);
1950: }
1952: /*@C
1953: PetscGaussLobattoLegendreElementLaplacianDestroy - frees the Laplacian for a single 1d GLL element
1955: Not Collective
1957: Input Parameter:
1958: + n - the number of GLL nodes
1959: . nodes - the GLL nodes
1960: . weights - the GLL weightss
1961: - A - the stiffness element
1963: Level: beginner
1965: .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianCreate()
1967: @*/
1968: PetscErrorCode PetscGaussLobattoLegendreElementLaplacianDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
1969: {
1973: PetscFree((*AA)[0]);
1974: PetscFree(*AA);
1975: *AA = NULL;
1976: return(0);
1977: }
1979: /*@C
1980: PetscGaussLobattoLegendreElementGradientCreate - computes the gradient for a single 1d GLL element
1982: Not Collective
1984: Input Parameter:
1985: + n - the number of GLL nodes
1986: . nodes - the GLL nodes
1987: . weights - the GLL weights
1989: Output Parameter:
1990: . AA - the stiffness element
1991: - AAT - the transpose of AA (pass in NULL if you do not need this array)
1993: Level: beginner
1995: Notes:
1996: Destroy this with PetscGaussLobattoLegendreElementGradientDestroy()
1998: You can access entries in these arrays with AA[i][j] but in memory it is stored in contiguous memory, row oriented
2000: .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianDestroy()
2002: @*/
2003: PetscErrorCode PetscGaussLobattoLegendreElementGradientCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA,PetscReal ***AAT)
2004: {
2005: PetscReal **A, **AT = NULL;
2006: PetscErrorCode ierr;
2007: const PetscReal *gllnodes = nodes;
2008: const PetscInt p = n-1;
2009: PetscReal Li, Lj,d0;
2010: PetscInt i,j;
2013: PetscMalloc1(n,&A);
2014: PetscMalloc1(n*n,&A[0]);
2015: for (i=1; i<n; i++) A[i] = A[i-1]+n;
2017: if (AAT) {
2018: PetscMalloc1(n,&AT);
2019: PetscMalloc1(n*n,&AT[0]);
2020: for (i=1; i<n; i++) AT[i] = AT[i-1]+n;
2021: }
2023: if (n==1) {A[0][0] = 0.;}
2024: d0 = (PetscReal)p*((PetscReal)p+1.)/4.;
2025: for (i=0; i<n; i++) {
2026: for (j=0; j<n; j++) {
2027: A[i][j] = 0.;
2028: PetscDTComputeJacobi(0., 0., p, gllnodes[i], &Li);
2029: PetscDTComputeJacobi(0., 0., p, gllnodes[j], &Lj);
2030: if (i!=j) A[i][j] = Li/(Lj*(gllnodes[i]-gllnodes[j]));
2031: if ((j==i) && (i==0)) A[i][j] = -d0;
2032: if (j==i && i==p) A[i][j] = d0;
2033: if (AT) AT[j][i] = A[i][j];
2034: }
2035: }
2036: if (AAT) *AAT = AT;
2037: *AA = A;
2038: return(0);
2039: }
2041: /*@C
2042: PetscGaussLobattoLegendreElementGradientDestroy - frees the gradient for a single 1d GLL element obtained with PetscGaussLobattoLegendreElementGradientCreate()
2044: Not Collective
2046: Input Parameter:
2047: + n - the number of GLL nodes
2048: . nodes - the GLL nodes
2049: . weights - the GLL weights
2050: . AA - the stiffness element
2051: - AAT - the transpose of the element
2053: Level: beginner
2055: .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianCreate(), PetscGaussLobattoLegendreElementAdvectionCreate()
2057: @*/
2058: PetscErrorCode PetscGaussLobattoLegendreElementGradientDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA,PetscReal ***AAT)
2059: {
2063: PetscFree((*AA)[0]);
2064: PetscFree(*AA);
2065: *AA = NULL;
2066: if (*AAT) {
2067: PetscFree((*AAT)[0]);
2068: PetscFree(*AAT);
2069: *AAT = NULL;
2070: }
2071: return(0);
2072: }
2074: /*@C
2075: PetscGaussLobattoLegendreElementAdvectionCreate - computes the advection operator for a single 1d GLL element
2077: Not Collective
2079: Input Parameter:
2080: + n - the number of GLL nodes
2081: . nodes - the GLL nodes
2082: - weights - the GLL weightss
2084: Output Parameter:
2085: . AA - the stiffness element
2087: Level: beginner
2089: Notes:
2090: Destroy this with PetscGaussLobattoLegendreElementAdvectionDestroy()
2092: This is the same as the Gradient operator multiplied by the diagonal mass matrix
2094: You can access entries in this array with AA[i][j] but in memory it is stored in contiguous memory, row oriented
2096: .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementLaplacianCreate(), PetscGaussLobattoLegendreElementAdvectionDestroy()
2098: @*/
2099: PetscErrorCode PetscGaussLobattoLegendreElementAdvectionCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2100: {
2101: PetscReal **D;
2102: PetscErrorCode ierr;
2103: const PetscReal *gllweights = weights;
2104: const PetscInt glln = n;
2105: PetscInt i,j;
2108: PetscGaussLobattoLegendreElementGradientCreate(n,nodes,weights,&D,NULL);
2109: for (i=0; i<glln; i++){
2110: for (j=0; j<glln; j++) {
2111: D[i][j] = gllweights[i]*D[i][j];
2112: }
2113: }
2114: *AA = D;
2115: return(0);
2116: }
2118: /*@C
2119: PetscGaussLobattoLegendreElementAdvectionDestroy - frees the advection stiffness for a single 1d GLL element
2121: Not Collective
2123: Input Parameter:
2124: + n - the number of GLL nodes
2125: . nodes - the GLL nodes
2126: . weights - the GLL weights
2127: - A - advection
2129: Level: beginner
2131: .seealso: PetscDTGaussLobattoLegendreQuadrature(), PetscGaussLobattoLegendreElementAdvectionCreate()
2133: @*/
2134: PetscErrorCode PetscGaussLobattoLegendreElementAdvectionDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2135: {
2139: PetscFree((*AA)[0]);
2140: PetscFree(*AA);
2141: *AA = NULL;
2142: return(0);
2143: }
2145: PetscErrorCode PetscGaussLobattoLegendreElementMassCreate(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2146: {
2147: PetscReal **A;
2148: PetscErrorCode ierr;
2149: const PetscReal *gllweights = weights;
2150: const PetscInt glln = n;
2151: PetscInt i,j;
2154: PetscMalloc1(glln,&A);
2155: PetscMalloc1(glln*glln,&A[0]);
2156: for (i=1; i<glln; i++) A[i] = A[i-1]+glln;
2157: if (glln==1) {A[0][0] = 0.;}
2158: for (i=0; i<glln; i++) {
2159: for (j=0; j<glln; j++) {
2160: A[i][j] = 0.;
2161: if (j==i) A[i][j] = gllweights[i];
2162: }
2163: }
2164: *AA = A;
2165: return(0);
2166: }
2168: PetscErrorCode PetscGaussLobattoLegendreElementMassDestroy(PetscInt n,PetscReal *nodes,PetscReal *weights,PetscReal ***AA)
2169: {
2173: PetscFree((*AA)[0]);
2174: PetscFree(*AA);
2175: *AA = NULL;
2176: return(0);
2177: }
2179: /*@
2180: PetscDTIndexToBary - convert an index into a barycentric coordinate.
2182: Input Parameters:
2183: + 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)
2184: . sum - the value that the sum of the barycentric coordinates (which will be non-negative integers) should sum to
2185: - index - the index to convert: should be >= 0 and < Binomial(len - 1 + sum, sum)
2187: Output Parameter:
2188: . coord - will be filled with the barycentric coordinate
2190: Level: beginner
2192: Note: the indices map to barycentric coordinates in lexicographic order, where the first index is the
2193: least significant and the last index is the most significant.
2195: .seealso: PetscDTBaryToIndex
2196: @*/
2197: PetscErrorCode PetscDTIndexToBary(PetscInt len, PetscInt sum, PetscInt index, PetscInt coord[])
2198: {
2199: PetscInt c, d, s, total, subtotal, nexttotal;
2202: if (len < 0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
2203: if (index < 0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index must be non-negative");
2204: if (!len) {
2205: if (!sum && !index) return(0);
2206: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid index or sum for length 0 barycentric coordinate");
2207: }
2208: for (c = 1, total = 1; c <= len; c++) {
2209: /* total is the number of ways to have a tuple of length c with sum */
2210: if (index < total) break;
2211: total = (total * (sum + c)) / c;
2212: }
2213: if (c > len) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index out of range");
2214: for (d = c; d < len; d++) coord[d] = 0;
2215: for (s = 0, subtotal = 1, nexttotal = 1; c > 0;) {
2216: /* subtotal is the number of ways to have a tuple of length c with sum s */
2217: /* nexttotal is the number of ways to have a tuple of length c-1 with sum s */
2218: if ((index + subtotal) >= total) {
2219: coord[--c] = sum - s;
2220: index -= (total - subtotal);
2221: sum = s;
2222: total = nexttotal;
2223: subtotal = 1;
2224: nexttotal = 1;
2225: s = 0;
2226: } else {
2227: subtotal = (subtotal * (c + s)) / (s + 1);
2228: nexttotal = (nexttotal * (c - 1 + s)) / (s + 1);
2229: s++;
2230: }
2231: }
2232: return(0);
2233: }
2235: /*@
2236: PetscDTBaryToIndex - convert a barycentric coordinate to an index
2238: Input Parameters:
2239: + 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)
2240: . sum - the value that the sum of the barycentric coordinates (which will be non-negative integers) should sum to
2241: - coord - a barycentric coordinate with the given length and sum
2243: Output Parameter:
2244: . index - the unique index for the coordinate, >= 0 and < Binomial(len - 1 + sum, sum)
2246: Level: beginner
2248: Note: the indices map to barycentric coordinates in lexicographic order, where the first index is the
2249: least significant and the last index is the most significant.
2251: .seealso: PetscDTIndexToBary
2252: @*/
2253: PetscErrorCode PetscDTBaryToIndex(PetscInt len, PetscInt sum, const PetscInt coord[], PetscInt *index)
2254: {
2255: PetscInt c;
2256: PetscInt i;
2257: PetscInt total;
2260: if (len < 0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
2261: if (!len) {
2262: if (!sum) {
2263: *index = 0;
2264: return(0);
2265: }
2266: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid index or sum for length 0 barycentric coordinate");
2267: }
2268: for (c = 1, total = 1; c < len; c++) total = (total * (sum + c)) / c;
2269: i = total - 1;
2270: c = len - 1;
2271: sum -= coord[c];
2272: while (sum > 0) {
2273: PetscInt subtotal;
2274: PetscInt s;
2276: for (s = 1, subtotal = 1; s < sum; s++) subtotal = (subtotal * (c + s)) / s;
2277: i -= subtotal;
2278: sum -= coord[--c];
2279: }
2280: *index = i;
2281: return(0);
2282: }