Date: 2025-03-29T05:58:19+00:00

Actual source code: dt.c

      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 <petsc/private/petscfeimpl.h>
      8: #include <petscviewer.h>
      9: #include <petscdmplex.h>
     10: #include <petscdmshell.h>

     12: #if defined(PETSC_HAVE_MPFR)
     13:   #include <mpfr.h>
     14: #endif

     16: const char *const        PetscDTNodeTypes_shifted[] = {"default", "gaussjacobi", "equispaced", "tanhsinh", "PETSCDTNODES_", NULL};
     17: const char *const *const PetscDTNodeTypes           = PetscDTNodeTypes_shifted + 1;

     19: const char *const        PetscDTSimplexQuadratureTypes_shifted[] = {"default", "conic", "minsym", "PETSCDTSIMPLEXQUAD_", NULL};
     20: const char *const *const PetscDTSimplexQuadratureTypes           = PetscDTSimplexQuadratureTypes_shifted + 1;

     22: static PetscBool GolubWelschCite       = PETSC_FALSE;
     23: const char       GolubWelschCitation[] = "@article{GolubWelsch1969,\n"
     24:                                          "  author  = {Golub and Welsch},\n"
     25:                                          "  title   = {Calculation of Quadrature Rules},\n"
     26:                                          "  journal = {Math. Comp.},\n"
     27:                                          "  volume  = {23},\n"
     28:                                          "  number  = {106},\n"
     29:                                          "  pages   = {221--230},\n"
     30:                                          "  year    = {1969}\n}\n";

     32: /* Numerical tests in src/dm/dt/tests/ex1.c show that when computing the nodes and weights of Gauss-Jacobi
     33:    quadrature rules:

     35:    - in double precision, Newton's method and Golub & Welsch both work for moderate degrees (< 100),
     36:    - in single precision, Newton's method starts producing incorrect roots around n = 15, but
     37:      the weights from Golub & Welsch become a problem before then: they produces errors
     38:      in computing the Jacobi-polynomial Gram matrix around n = 6.

     40:    So we default to Newton's method (required fewer dependencies) */
     41: PetscBool PetscDTGaussQuadratureNewton_Internal = PETSC_TRUE;

     43: PetscClassId PETSCQUADRATURE_CLASSID = 0;

     45: /*@
     46:   PetscQuadratureCreate - Create a `PetscQuadrature` object

     48:   Collective

     50:   Input Parameter:
     51: . comm - The communicator for the `PetscQuadrature` object

     53:   Output Parameter:
     54: . q - The `PetscQuadrature` object

     56:   Level: beginner

     58: .seealso: `PetscQuadrature`, `Petscquadraturedestroy()`, `PetscQuadratureGetData()`
     59: @*/
     60: PetscErrorCode PetscQuadratureCreate(MPI_Comm comm, PetscQuadrature *q)
     61: {
     62:   PetscFunctionBegin;
     63:   PetscAssertPointer(q, 2);
     64:   PetscCall(DMInitializePackage());
     65:   PetscCall(PetscHeaderCreate(*q, PETSCQUADRATURE_CLASSID, "PetscQuadrature", "Quadrature", "DT", comm, PetscQuadratureDestroy, PetscQuadratureView));
     66:   (*q)->ct        = DM_POLYTOPE_UNKNOWN;
     67:   (*q)->dim       = -1;
     68:   (*q)->Nc        = 1;
     69:   (*q)->order     = -1;
     70:   (*q)->numPoints = 0;
     71:   (*q)->points    = NULL;
     72:   (*q)->weights   = NULL;
     73:   PetscFunctionReturn(PETSC_SUCCESS);
     74: }

     76: /*@
     77:   PetscQuadratureDuplicate - Create a deep copy of the `PetscQuadrature` object

     79:   Collective

     81:   Input Parameter:
     82: . q - The `PetscQuadrature` object

     84:   Output Parameter:
     85: . r - The new `PetscQuadrature` object

     87:   Level: beginner

     89: .seealso: `PetscQuadrature`, `PetscQuadratureCreate()`, `PetscQuadratureDestroy()`, `PetscQuadratureGetData()`
     90: @*/
     91: PetscErrorCode PetscQuadratureDuplicate(PetscQuadrature q, PetscQuadrature *r)
     92: {
     93:   DMPolytopeType   ct;
     94:   PetscInt         order, dim, Nc, Nq;
     95:   const PetscReal *points, *weights;
     96:   PetscReal       *p, *w;

     98:   PetscFunctionBegin;
     99:   PetscAssertPointer(q, 1);
    100:   PetscCall(PetscQuadratureCreate(PetscObjectComm((PetscObject)q), r));
    101:   PetscCall(PetscQuadratureGetCellType(q, &ct));
    102:   PetscCall(PetscQuadratureSetCellType(*r, ct));
    103:   PetscCall(PetscQuadratureGetOrder(q, &order));
    104:   PetscCall(PetscQuadratureSetOrder(*r, order));
    105:   PetscCall(PetscQuadratureGetData(q, &dim, &Nc, &Nq, &points, &weights));
    106:   PetscCall(PetscMalloc1(Nq * dim, &p));
    107:   PetscCall(PetscMalloc1(Nq * Nc, &w));
    108:   PetscCall(PetscArraycpy(p, points, Nq * dim));
    109:   PetscCall(PetscArraycpy(w, weights, Nc * Nq));
    110:   PetscCall(PetscQuadratureSetData(*r, dim, Nc, Nq, p, w));
    111:   PetscFunctionReturn(PETSC_SUCCESS);
    112: }

    114: /*@
    115:   PetscQuadratureDestroy - Destroys a `PetscQuadrature` object

    117:   Collective

    119:   Input Parameter:
    120: . q - The `PetscQuadrature` object

    122:   Level: beginner

    124: .seealso: `PetscQuadrature`, `PetscQuadratureCreate()`, `PetscQuadratureGetData()`
    125: @*/
    126: PetscErrorCode PetscQuadratureDestroy(PetscQuadrature *q)
    127: {
    128:   PetscFunctionBegin;
    129:   if (!*q) PetscFunctionReturn(PETSC_SUCCESS);
    131:   if (--((PetscObject)*q)->refct > 0) {
    132:     *q = NULL;
    133:     PetscFunctionReturn(PETSC_SUCCESS);
    134:   }
    135:   PetscCall(PetscFree((*q)->points));
    136:   PetscCall(PetscFree((*q)->weights));
    137:   PetscCall(PetscHeaderDestroy(q));
    138:   PetscFunctionReturn(PETSC_SUCCESS);
    139: }

    141: /*@
    142:   PetscQuadratureGetCellType - Return the cell type of the integration domain

    144:   Not Collective

    146:   Input Parameter:
    147: . q - The `PetscQuadrature` object

    149:   Output Parameter:
    150: . ct - The cell type of the integration domain

    152:   Level: intermediate

    154: .seealso: `PetscQuadrature`, `PetscQuadratureSetCellType()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
    155: @*/
    156: PetscErrorCode PetscQuadratureGetCellType(PetscQuadrature q, DMPolytopeType *ct)
    157: {
    158:   PetscFunctionBegin;
    160:   PetscAssertPointer(ct, 2);
    161:   *ct = q->ct;
    162:   PetscFunctionReturn(PETSC_SUCCESS);
    163: }

    165: /*@
    166:   PetscQuadratureSetCellType - Set the cell type of the integration domain

    168:   Not Collective

    170:   Input Parameters:
    171: + q  - The `PetscQuadrature` object
    172: - ct - The cell type of the integration domain

    174:   Level: intermediate

    176: .seealso: `PetscQuadrature`, `PetscQuadratureGetCellType()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
    177: @*/
    178: PetscErrorCode PetscQuadratureSetCellType(PetscQuadrature q, DMPolytopeType ct)
    179: {
    180:   PetscFunctionBegin;
    182:   q->ct = ct;
    183:   PetscFunctionReturn(PETSC_SUCCESS);
    184: }

    186: /*@
    187:   PetscQuadratureGetOrder - Return the order of the method in the `PetscQuadrature`

    189:   Not Collective

    191:   Input Parameter:
    192: . q - The `PetscQuadrature` object

    194:   Output Parameter:
    195: . order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated

    197:   Level: intermediate

    199: .seealso: `PetscQuadrature`, `PetscQuadratureSetOrder()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
    200: @*/
    201: PetscErrorCode PetscQuadratureGetOrder(PetscQuadrature q, PetscInt *order)
    202: {
    203:   PetscFunctionBegin;
    205:   PetscAssertPointer(order, 2);
    206:   *order = q->order;
    207:   PetscFunctionReturn(PETSC_SUCCESS);
    208: }

    210: /*@
    211:   PetscQuadratureSetOrder - Set the order of the method in the `PetscQuadrature`

    213:   Not Collective

    215:   Input Parameters:
    216: + q     - The `PetscQuadrature` object
    217: - order - The order of the quadrature, i.e. the highest degree polynomial that is exactly integrated

    219:   Level: intermediate

    221: .seealso: `PetscQuadrature`, `PetscQuadratureGetOrder()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
    222: @*/
    223: PetscErrorCode PetscQuadratureSetOrder(PetscQuadrature q, PetscInt order)
    224: {
    225:   PetscFunctionBegin;
    227:   q->order = order;
    228:   PetscFunctionReturn(PETSC_SUCCESS);
    229: }

    231: /*@
    232:   PetscQuadratureGetNumComponents - Return the number of components for functions to be integrated

    234:   Not Collective

    236:   Input Parameter:
    237: . q - The `PetscQuadrature` object

    239:   Output Parameter:
    240: . Nc - The number of components

    242:   Level: intermediate

    244:   Note:
    245:   We are performing an integral $\int f(x) w(x) dx$, where both $f$ and $w$ (the weight) have `Nc` components.

    247: .seealso: `PetscQuadrature`, `PetscQuadratureSetNumComponents()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
    248: @*/
    249: PetscErrorCode PetscQuadratureGetNumComponents(PetscQuadrature q, PetscInt *Nc)
    250: {
    251:   PetscFunctionBegin;
    253:   PetscAssertPointer(Nc, 2);
    254:   *Nc = q->Nc;
    255:   PetscFunctionReturn(PETSC_SUCCESS);
    256: }

    258: /*@
    259:   PetscQuadratureSetNumComponents - Sets the number of components for functions to be integrated

    261:   Not Collective

    263:   Input Parameters:
    264: + q  - The `PetscQuadrature` object
    265: - Nc - The number of components

    267:   Level: intermediate

    269:   Note:
    270:   We are performing an integral $\int f(x) w(x) dx$, where both $f$ and $w$ (the weight) have `Nc` components.

    272: .seealso: `PetscQuadrature`, `PetscQuadratureGetNumComponents()`, `PetscQuadratureGetData()`, `PetscQuadratureSetData()`
    273: @*/
    274: PetscErrorCode PetscQuadratureSetNumComponents(PetscQuadrature q, PetscInt Nc)
    275: {
    276:   PetscFunctionBegin;
    278:   q->Nc = Nc;
    279:   PetscFunctionReturn(PETSC_SUCCESS);
    280: }

    282: /*@C
    283:   PetscQuadratureGetData - Returns the data defining the `PetscQuadrature`

    285:   Not Collective

    287:   Input Parameter:
    288: . q - The `PetscQuadrature` object

    290:   Output Parameters:
    291: + dim     - The spatial dimension
    292: . Nc      - The number of components
    293: . npoints - The number of quadrature points
    294: . points  - The coordinates of each quadrature point
    295: - weights - The weight of each quadrature point

    297:   Level: intermediate

    299:   Note:
    300:   All output arguments are optional, pass `NULL` for any argument not required

    302:   Fortran Note:
    303:   Call `PetscQuadratureRestoreData()` when you are done with the data

    305: .seealso: `PetscQuadrature`, `PetscQuadratureCreate()`, `PetscQuadratureSetData()`
    306: @*/
    307: PetscErrorCode PetscQuadratureGetData(PetscQuadrature q, PeOp PetscInt *dim, PeOp PetscInt *Nc, PeOp PetscInt *npoints, PeOp const PetscReal *points[], PeOp const PetscReal *weights[])
    308: {
    309:   PetscFunctionBegin;
    311:   if (dim) {
    312:     PetscAssertPointer(dim, 2);
    313:     *dim = q->dim;
    314:   }
    315:   if (Nc) {
    316:     PetscAssertPointer(Nc, 3);
    317:     *Nc = q->Nc;
    318:   }
    319:   if (npoints) {
    320:     PetscAssertPointer(npoints, 4);
    321:     *npoints = q->numPoints;
    322:   }
    323:   if (points) {
    324:     PetscAssertPointer(points, 5);
    325:     *points = q->points;
    326:   }
    327:   if (weights) {
    328:     PetscAssertPointer(weights, 6);
    329:     *weights = q->weights;
    330:   }
    331:   PetscFunctionReturn(PETSC_SUCCESS);
    332: }

    334: /*@
    335:   PetscQuadratureEqual - determine whether two quadratures are equivalent

    337:   Input Parameters:
    338: + A - A `PetscQuadrature` object
    339: - B - Another `PetscQuadrature` object

    341:   Output Parameter:
    342: . equal - `PETSC_TRUE` if the quadratures are the same

    344:   Level: intermediate

    346: .seealso: `PetscQuadrature`, `PetscQuadratureCreate()`
    347: @*/
    348: PetscErrorCode PetscQuadratureEqual(PetscQuadrature A, PetscQuadrature B, PetscBool *equal)
    349: {
    350:   PetscFunctionBegin;
    353:   PetscAssertPointer(equal, 3);
    354:   *equal = PETSC_FALSE;
    355:   if (A->ct != B->ct || A->dim != B->dim || A->Nc != B->Nc || A->order != B->order || A->numPoints != B->numPoints) PetscFunctionReturn(PETSC_SUCCESS);
    356:   for (PetscInt i = 0; i < A->numPoints * A->dim; i++) {
    357:     if (PetscAbsReal(A->points[i] - B->points[i]) > PETSC_SMALL) PetscFunctionReturn(PETSC_SUCCESS);
    358:   }
    359:   if (!A->weights && !B->weights) {
    360:     *equal = PETSC_TRUE;
    361:     PetscFunctionReturn(PETSC_SUCCESS);
    362:   }
    363:   if (A->weights && B->weights) {
    364:     for (PetscInt i = 0; i < A->numPoints; i++) {
    365:       if (PetscAbsReal(A->weights[i] - B->weights[i]) > PETSC_SMALL) PetscFunctionReturn(PETSC_SUCCESS);
    366:     }
    367:     *equal = PETSC_TRUE;
    368:   }
    369:   PetscFunctionReturn(PETSC_SUCCESS);
    370: }

    372: static PetscErrorCode PetscDTJacobianInverse_Internal(PetscInt m, PetscInt n, const PetscReal J[], PetscReal Jinv[])
    373: {
    374:   PetscScalar *Js, *Jinvs;
    375:   PetscInt     i, j, k;
    376:   PetscBLASInt bm, bn, info;

    378:   PetscFunctionBegin;
    379:   if (!m || !n) PetscFunctionReturn(PETSC_SUCCESS);
    380:   PetscCall(PetscBLASIntCast(m, &bm));
    381:   PetscCall(PetscBLASIntCast(n, &bn));
    382: #if defined(PETSC_USE_COMPLEX)
    383:   PetscCall(PetscMalloc2(m * n, &Js, m * n, &Jinvs));
    384:   for (i = 0; i < m * n; i++) Js[i] = J[i];
    385: #else
    386:   Js    = (PetscReal *)J;
    387:   Jinvs = Jinv;
    388: #endif
    389:   if (m == n) {
    390:     PetscBLASInt *pivots;
    391:     PetscScalar  *W;

    393:     PetscCall(PetscMalloc2(m, &pivots, m, &W));

    395:     PetscCall(PetscArraycpy(Jinvs, Js, m * m));
    396:     PetscCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, Jinvs, &bm, pivots, &info));
    397:     PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetrf %" PetscBLASInt_FMT, info);
    398:     PetscCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, Jinvs, &bm, pivots, W, &bm, &info));
    399:     PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetri %" PetscBLASInt_FMT, info);
    400:     PetscCall(PetscFree2(pivots, W));
    401:   } else if (m < n) {
    402:     PetscScalar  *JJT;
    403:     PetscBLASInt *pivots;
    404:     PetscScalar  *W;

    406:     PetscCall(PetscMalloc1(m * m, &JJT));
    407:     PetscCall(PetscMalloc2(m, &pivots, m, &W));
    408:     for (i = 0; i < m; i++) {
    409:       for (j = 0; j < m; j++) {
    410:         PetscScalar val = 0.;

    412:         for (k = 0; k < n; k++) val += Js[i * n + k] * Js[j * n + k];
    413:         JJT[i * m + j] = val;
    414:       }
    415:     }

    417:     PetscCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bm, &bm, JJT, &bm, pivots, &info));
    418:     PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetrf %" PetscBLASInt_FMT, info);
    419:     PetscCallBLAS("LAPACKgetri", LAPACKgetri_(&bm, JJT, &bm, pivots, W, &bm, &info));
    420:     PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetri %" PetscBLASInt_FMT, info);
    421:     for (i = 0; i < n; i++) {
    422:       for (j = 0; j < m; j++) {
    423:         PetscScalar val = 0.;

    425:         for (k = 0; k < m; k++) val += Js[k * n + i] * JJT[k * m + j];
    426:         Jinvs[i * m + j] = val;
    427:       }
    428:     }
    429:     PetscCall(PetscFree2(pivots, W));
    430:     PetscCall(PetscFree(JJT));
    431:   } else {
    432:     PetscScalar  *JTJ;
    433:     PetscBLASInt *pivots;
    434:     PetscScalar  *W;

    436:     PetscCall(PetscMalloc1(n * n, &JTJ));
    437:     PetscCall(PetscMalloc2(n, &pivots, n, &W));
    438:     for (i = 0; i < n; i++) {
    439:       for (j = 0; j < n; j++) {
    440:         PetscScalar val = 0.;

    442:         for (k = 0; k < m; k++) val += Js[k * n + i] * Js[k * n + j];
    443:         JTJ[i * n + j] = val;
    444:       }
    445:     }

    447:     PetscCallBLAS("LAPACKgetrf", LAPACKgetrf_(&bn, &bn, JTJ, &bn, pivots, &info));
    448:     PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetrf %" PetscBLASInt_FMT, info);
    449:     PetscCallBLAS("LAPACKgetri", LAPACKgetri_(&bn, JTJ, &bn, pivots, W, &bn, &info));
    450:     PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error returned from LAPACKgetri %" PetscBLASInt_FMT, info);
    451:     for (i = 0; i < n; i++) {
    452:       for (j = 0; j < m; j++) {
    453:         PetscScalar val = 0.;

    455:         for (k = 0; k < n; k++) val += JTJ[i * n + k] * Js[j * n + k];
    456:         Jinvs[i * m + j] = val;
    457:       }
    458:     }
    459:     PetscCall(PetscFree2(pivots, W));
    460:     PetscCall(PetscFree(JTJ));
    461:   }
    462: #if defined(PETSC_USE_COMPLEX)
    463:   for (i = 0; i < m * n; i++) Jinv[i] = PetscRealPart(Jinvs[i]);
    464:   PetscCall(PetscFree2(Js, Jinvs));
    465: #endif
    466:   PetscFunctionReturn(PETSC_SUCCESS);
    467: }

    469: /*@
    470:   PetscQuadraturePushForward - Push forward a quadrature functional under an affine transformation.

    472:   Collective

    474:   Input Parameters:
    475: + q           - the quadrature functional
    476: . imageDim    - the dimension of the image of the transformation
    477: . origin      - a point in the original space
    478: . originImage - the image of the origin under the transformation
    479: . J           - the Jacobian of the image: an [imageDim x dim] matrix in row major order
    480: - formDegree  - transform the quadrature weights as k-forms of this form degree (if the number of components is a multiple of (dim choose `formDegree`),
    481:                 it is assumed that they represent multiple k-forms) [see `PetscDTAltVPullback()` for interpretation of `formDegree`]

    483:   Output Parameter:
    484: . Jinvstarq - a quadrature rule where each point is the image of a point in the original quadrature rule, and where the k-form weights have
    485:   been pulled-back by the pseudoinverse of `J` to the k-form weights in the image space.

    487:   Level: intermediate

    489:   Note:
    490:   The new quadrature rule will have a different number of components if spaces have different dimensions.
    491:   For example, pushing a 2-form forward from a two dimensional space to a three dimensional space changes the number of components from 1 to 3.

    493: .seealso: `PetscQuadrature`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()`
    494: @*/
    495: PetscErrorCode PetscQuadraturePushForward(PetscQuadrature q, PetscInt imageDim, const PetscReal origin[], const PetscReal originImage[], const PetscReal J[], PetscInt formDegree, PetscQuadrature *Jinvstarq)
    496: {
    497:   PetscInt         dim, Nc, imageNc, formSize, Ncopies, imageFormSize, Npoints, pt, i, j, c;
    498:   const PetscReal *points;
    499:   const PetscReal *weights;
    500:   PetscReal       *imagePoints, *imageWeights;
    501:   PetscReal       *Jinv;
    502:   PetscReal       *Jinvstar;

    504:   PetscFunctionBegin;
    506:   PetscCheck(imageDim >= PetscAbsInt(formDegree), PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Cannot represent a %" PetscInt_FMT "-form in %" PetscInt_FMT " dimensions", PetscAbsInt(formDegree), imageDim);
    507:   PetscCall(PetscQuadratureGetData(q, &dim, &Nc, &Npoints, &points, &weights));
    508:   PetscCall(PetscDTBinomialInt(dim, PetscAbsInt(formDegree), &formSize));
    509:   PetscCheck(Nc % formSize == 0, PetscObjectComm((PetscObject)q), PETSC_ERR_ARG_INCOMP, "Number of components %" PetscInt_FMT " is not a multiple of formSize %" PetscInt_FMT, Nc, formSize);
    510:   Ncopies = Nc / formSize;
    511:   PetscCall(PetscDTBinomialInt(imageDim, PetscAbsInt(formDegree), &imageFormSize));
    512:   imageNc = Ncopies * imageFormSize;
    513:   PetscCall(PetscMalloc1(Npoints * imageDim, &imagePoints));
    514:   PetscCall(PetscMalloc1(Npoints * imageNc, &imageWeights));
    515:   PetscCall(PetscMalloc2(imageDim * dim, &Jinv, formSize * imageFormSize, &Jinvstar));
    516:   PetscCall(PetscDTJacobianInverse_Internal(imageDim, dim, J, Jinv));
    517:   PetscCall(PetscDTAltVPullbackMatrix(imageDim, dim, Jinv, formDegree, Jinvstar));
    518:   for (pt = 0; pt < Npoints; pt++) {
    519:     const PetscReal *point      = PetscSafePointerPlusOffset(points, pt * dim);
    520:     PetscReal       *imagePoint = &imagePoints[pt * imageDim];

    522:     for (i = 0; i < imageDim; i++) {
    523:       PetscReal val = originImage[i];

    525:       for (j = 0; j < dim; j++) val += J[i * dim + j] * (point[j] - origin[j]);
    526:       imagePoint[i] = val;
    527:     }
    528:     for (c = 0; c < Ncopies; c++) {
    529:       const PetscReal *form      = &weights[pt * Nc + c * formSize];
    530:       PetscReal       *imageForm = &imageWeights[pt * imageNc + c * imageFormSize];

    532:       for (i = 0; i < imageFormSize; i++) {
    533:         PetscReal val = 0.;

    535:         for (j = 0; j < formSize; j++) val += Jinvstar[i * formSize + j] * form[j];
    536:         imageForm[i] = val;
    537:       }
    538:     }
    539:   }
    540:   PetscCall(PetscQuadratureCreate(PetscObjectComm((PetscObject)q), Jinvstarq));
    541:   PetscCall(PetscQuadratureSetData(*Jinvstarq, imageDim, imageNc, Npoints, imagePoints, imageWeights));
    542:   PetscCall(PetscFree2(Jinv, Jinvstar));
    543:   PetscFunctionReturn(PETSC_SUCCESS);
    544: }

    546: /*@C
    547:   PetscQuadratureSetData - Sets the data defining the quadrature

    549:   Not Collective

    551:   Input Parameters:
    552: + q       - The `PetscQuadrature` object
    553: . dim     - The spatial dimension
    554: . Nc      - The number of components
    555: . npoints - The number of quadrature points
    556: . points  - The coordinates of each quadrature point
    557: - weights - The weight of each quadrature point

    559:   Level: intermediate

    561:   Note:
    562:   `q` owns the references to points and weights, so they must be allocated using `PetscMalloc()` and the user should not free them.

    564: .seealso: `PetscQuadrature`, `PetscQuadratureCreate()`, `PetscQuadratureGetData()`
    565: @*/
    566: PetscErrorCode PetscQuadratureSetData(PetscQuadrature q, PetscInt dim, PetscInt Nc, PetscInt npoints, const PetscReal points[], const PetscReal weights[])
    567: {
    568:   PetscFunctionBegin;
    570:   if (dim >= 0) q->dim = dim;
    571:   if (Nc >= 0) q->Nc = Nc;
    572:   if (npoints >= 0) q->numPoints = npoints;
    573:   if (points) {
    574:     PetscAssertPointer(points, 5);
    575:     q->points = points;
    576:   }
    577:   if (weights) {
    578:     PetscAssertPointer(weights, 6);
    579:     q->weights = weights;
    580:   }
    581:   PetscFunctionReturn(PETSC_SUCCESS);
    582: }

    584: static PetscErrorCode PetscQuadratureView_Ascii(PetscQuadrature quad, PetscViewer v)
    585: {
    586:   PetscInt          q, d, c;
    587:   PetscViewerFormat format;

    589:   PetscFunctionBegin;
    590:   if (quad->Nc > 1)
    591:     PetscCall(PetscViewerASCIIPrintf(v, "Quadrature on a %s of order %" PetscInt_FMT " on %" PetscInt_FMT " points (dim %" PetscInt_FMT ") with %" PetscInt_FMT " components\n", DMPolytopeTypes[quad->ct], quad->order, quad->numPoints, quad->dim, quad->Nc));
    592:   else PetscCall(PetscViewerASCIIPrintf(v, "Quadrature on a %s of order %" PetscInt_FMT " on %" PetscInt_FMT " points (dim %" PetscInt_FMT ")\n", DMPolytopeTypes[quad->ct], quad->order, quad->numPoints, quad->dim));
    593:   PetscCall(PetscViewerGetFormat(v, &format));
    594:   if (format != PETSC_VIEWER_ASCII_INFO_DETAIL) PetscFunctionReturn(PETSC_SUCCESS);
    595:   for (q = 0; q < quad->numPoints; ++q) {
    596:     PetscCall(PetscViewerASCIIPrintf(v, "p%" PetscInt_FMT " (", q));
    597:     PetscCall(PetscViewerASCIIUseTabs(v, PETSC_FALSE));
    598:     for (d = 0; d < quad->dim; ++d) {
    599:       if (d) PetscCall(PetscViewerASCIIPrintf(v, ", "));
    600:       PetscCall(PetscViewerASCIIPrintf(v, "%+g", (double)quad->points[q * quad->dim + d]));
    601:     }
    602:     PetscCall(PetscViewerASCIIPrintf(v, ") "));
    603:     if (quad->Nc > 1) PetscCall(PetscViewerASCIIPrintf(v, "w%" PetscInt_FMT " (", q));
    604:     for (c = 0; c < quad->Nc; ++c) {
    605:       if (c) PetscCall(PetscViewerASCIIPrintf(v, ", "));
    606:       PetscCall(PetscViewerASCIIPrintf(v, "%+g", (double)quad->weights[q * quad->Nc + c]));
    607:     }
    608:     if (quad->Nc > 1) PetscCall(PetscViewerASCIIPrintf(v, ")"));
    609:     PetscCall(PetscViewerASCIIPrintf(v, "\n"));
    610:     PetscCall(PetscViewerASCIIUseTabs(v, PETSC_TRUE));
    611:   }
    612:   PetscFunctionReturn(PETSC_SUCCESS);
    613: }

    615: /*@
    616:   PetscQuadratureView - View a `PetscQuadrature` object

    618:   Collective

    620:   Input Parameters:
    621: + quad   - The `PetscQuadrature` object
    622: - viewer - The `PetscViewer` object

    624:   Level: beginner

    626: .seealso: `PetscQuadrature`, `PetscViewer`, `PetscQuadratureCreate()`, `PetscQuadratureGetData()`
    627: @*/
    628: PetscErrorCode PetscQuadratureView(PetscQuadrature quad, PetscViewer viewer)
    629: {
    630:   PetscBool iascii;

    632:   PetscFunctionBegin;
    635:   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)quad), &viewer));
    636:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
    637:   PetscCall(PetscViewerASCIIPushTab(viewer));
    638:   if (iascii) PetscCall(PetscQuadratureView_Ascii(quad, viewer));
    639:   PetscCall(PetscViewerASCIIPopTab(viewer));
    640:   PetscFunctionReturn(PETSC_SUCCESS);
    641: }

    643: /*@C
    644:   PetscQuadratureExpandComposite - Return a quadrature over the composite element, which has the original quadrature in each subelement

    646:   Not Collective; No Fortran Support

    648:   Input Parameters:
    649: + q              - The original `PetscQuadrature`
    650: . numSubelements - The number of subelements the original element is divided into
    651: . v0             - An array of the initial points for each subelement
    652: - jac            - An array of the Jacobian mappings from the reference to each subelement

    654:   Output Parameter:
    655: . qref - The dimension

    657:   Level: intermediate

    659:   Note:
    660:   Together `v0` and `jac` define an affine mapping from the original reference element to each subelement

    662: .seealso: `PetscQuadrature`, `PetscFECreate()`, `PetscSpaceGetDimension()`, `PetscDualSpaceGetDimension()`
    663: @*/
    664: PetscErrorCode PetscQuadratureExpandComposite(PetscQuadrature q, PetscInt numSubelements, const PetscReal v0[], const PetscReal jac[], PetscQuadrature *qref)
    665: {
    666:   DMPolytopeType   ct;
    667:   const PetscReal *points, *weights;
    668:   PetscReal       *pointsRef, *weightsRef;
    669:   PetscInt         dim, Nc, order, npoints, npointsRef, c, p, cp, d, e;

    671:   PetscFunctionBegin;
    673:   PetscAssertPointer(v0, 3);
    674:   PetscAssertPointer(jac, 4);
    675:   PetscAssertPointer(qref, 5);
    676:   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, qref));
    677:   PetscCall(PetscQuadratureGetCellType(q, &ct));
    678:   PetscCall(PetscQuadratureGetOrder(q, &order));
    679:   PetscCall(PetscQuadratureGetData(q, &dim, &Nc, &npoints, &points, &weights));
    680:   npointsRef = npoints * numSubelements;
    681:   PetscCall(PetscMalloc1(npointsRef * dim, &pointsRef));
    682:   PetscCall(PetscMalloc1(npointsRef * Nc, &weightsRef));
    683:   for (c = 0; c < numSubelements; ++c) {
    684:     for (p = 0; p < npoints; ++p) {
    685:       for (d = 0; d < dim; ++d) {
    686:         pointsRef[(c * npoints + p) * dim + d] = v0[c * dim + d];
    687:         for (e = 0; e < dim; ++e) pointsRef[(c * npoints + p) * dim + d] += jac[(c * dim + d) * dim + e] * (points[p * dim + e] + 1.0);
    688:       }
    689:       /* Could also use detJ here */
    690:       for (cp = 0; cp < Nc; ++cp) weightsRef[(c * npoints + p) * Nc + cp] = weights[p * Nc + cp] / numSubelements;
    691:     }
    692:   }
    693:   PetscCall(PetscQuadratureSetCellType(*qref, ct));
    694:   PetscCall(PetscQuadratureSetOrder(*qref, order));
    695:   PetscCall(PetscQuadratureSetData(*qref, dim, Nc, npointsRef, pointsRef, weightsRef));
    696:   PetscFunctionReturn(PETSC_SUCCESS);
    697: }

    699: /* Compute the coefficients for the Jacobi polynomial recurrence,

    701:    J^{a,b}_n(x) = (cnm1 + cnm1x * x) * J^{a,b}_{n-1}(x) - cnm2 * J^{a,b}_{n-2}(x).
    702:  */
    703: #define PetscDTJacobiRecurrence_Internal(n, a, b, cnm1, cnm1x, cnm2) \
    704:   do { \
    705:     PetscReal _a = (a); \
    706:     PetscReal _b = (b); \
    707:     PetscReal _n = (n); \
    708:     if (n == 1) { \
    709:       (cnm1)  = (_a - _b) * 0.5; \
    710:       (cnm1x) = (_a + _b + 2.) * 0.5; \
    711:       (cnm2)  = 0.; \
    712:     } else { \
    713:       PetscReal _2n  = _n + _n; \
    714:       PetscReal _d   = (_2n * (_n + _a + _b) * (_2n + _a + _b - 2)); \
    715:       PetscReal _n1  = (_2n + _a + _b - 1.) * (_a * _a - _b * _b); \
    716:       PetscReal _n1x = (_2n + _a + _b - 1.) * (_2n + _a + _b) * (_2n + _a + _b - 2); \
    717:       PetscReal _n2  = 2. * ((_n + _a - 1.) * (_n + _b - 1.) * (_2n + _a + _b)); \
    718:       (cnm1)         = _n1 / _d; \
    719:       (cnm1x)        = _n1x / _d; \
    720:       (cnm2)         = _n2 / _d; \
    721:     } \
    722:   } while (0)

    724: /*@
    725:   PetscDTJacobiNorm - Compute the weighted L2 norm of a Jacobi polynomial.

    727:   $\| P^{\alpha,\beta}_n \|_{\alpha,\beta}^2 = \int_{-1}^1 (1 + x)^{\alpha} (1 - x)^{\beta} P^{\alpha,\beta}_n (x)^2 dx.$

    729:   Input Parameters:
    730: + alpha - the left exponent > -1
    731: . beta  - the right exponent > -1
    732: - n     - the polynomial degree

    734:   Output Parameter:
    735: . norm - the weighted L2 norm

    737:   Level: beginner

    739: .seealso: `PetscQuadrature`, `PetscDTJacobiEval()`
    740: @*/
    741: PetscErrorCode PetscDTJacobiNorm(PetscReal alpha, PetscReal beta, PetscInt n, PetscReal *norm)
    742: {
    743:   PetscReal twoab1;
    744:   PetscReal gr;

    746:   PetscFunctionBegin;
    747:   PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Exponent alpha %g <= -1. invalid", (double)alpha);
    748:   PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Exponent beta %g <= -1. invalid", (double)beta);
    749:   PetscCheck(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "n %" PetscInt_FMT " < 0 invalid", n);
    750:   twoab1 = PetscPowReal(2., alpha + beta + 1.);
    751: #if defined(PETSC_HAVE_LGAMMA)
    752:   if (!n) {
    753:     gr = PetscExpReal(PetscLGamma(alpha + 1.) + PetscLGamma(beta + 1.) - PetscLGamma(alpha + beta + 2.));
    754:   } else {
    755:     gr = PetscExpReal(PetscLGamma(n + alpha + 1.) + PetscLGamma(n + beta + 1.) - (PetscLGamma(n + 1.) + PetscLGamma(n + alpha + beta + 1.))) / (n + n + alpha + beta + 1.);
    756:   }
    757: #else
    758:   {
    759:     PetscInt alphai = (PetscInt)alpha;
    760:     PetscInt betai  = (PetscInt)beta;
    761:     PetscInt i;

    763:     gr = n ? (1. / (n + n + alpha + beta + 1.)) : 1.;
    764:     if ((PetscReal)alphai == alpha) {
    765:       if (!n) {
    766:         for (i = 0; i < alphai; i++) gr *= (i + 1.) / (beta + i + 1.);
    767:         gr /= (alpha + beta + 1.);
    768:       } else {
    769:         for (i = 0; i < alphai; i++) gr *= (n + i + 1.) / (n + beta + i + 1.);
    770:       }
    771:     } else if ((PetscReal)betai == beta) {
    772:       if (!n) {
    773:         for (i = 0; i < betai; i++) gr *= (i + 1.) / (alpha + i + 2.);
    774:         gr /= (alpha + beta + 1.);
    775:       } else {
    776:         for (i = 0; i < betai; i++) gr *= (n + i + 1.) / (n + alpha + i + 1.);
    777:       }
    778:     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "lgamma() - math routine is unavailable.");
    779:   }
    780: #endif
    781:   *norm = PetscSqrtReal(twoab1 * gr);
    782:   PetscFunctionReturn(PETSC_SUCCESS);
    783: }

    785: static PetscErrorCode PetscDTJacobiEval_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscInt k, const PetscReal *points, PetscInt ndegree, const PetscInt *degrees, PetscReal *p)
    786: {
    787:   PetscReal ak, bk;
    788:   PetscReal abk1;
    789:   PetscInt  i, l, maxdegree;

    791:   PetscFunctionBegin;
    792:   maxdegree = degrees[ndegree - 1] - k;
    793:   ak        = a + k;
    794:   bk        = b + k;
    795:   abk1      = a + b + k + 1.;
    796:   if (maxdegree < 0) {
    797:     for (i = 0; i < npoints; i++)
    798:       for (l = 0; l < ndegree; l++) p[i * ndegree + l] = 0.;
    799:     PetscFunctionReturn(PETSC_SUCCESS);
    800:   }
    801:   for (i = 0; i < npoints; i++) {
    802:     PetscReal pm1, pm2, x;
    803:     PetscReal cnm1, cnm1x, cnm2;
    804:     PetscInt  j, m;

    806:     x   = points[i];
    807:     pm2 = 1.;
    808:     PetscDTJacobiRecurrence_Internal(1, ak, bk, cnm1, cnm1x, cnm2);
    809:     pm1 = (cnm1 + cnm1x * x);
    810:     l   = 0;
    811:     while (l < ndegree && degrees[l] - k < 0) p[l++] = 0.;
    812:     while (l < ndegree && degrees[l] - k == 0) {
    813:       p[l] = pm2;
    814:       for (m = 0; m < k; m++) p[l] *= (abk1 + m) * 0.5;
    815:       l++;
    816:     }
    817:     while (l < ndegree && degrees[l] - k == 1) {
    818:       p[l] = pm1;
    819:       for (m = 0; m < k; m++) p[l] *= (abk1 + 1 + m) * 0.5;
    820:       l++;
    821:     }
    822:     for (j = 2; j <= maxdegree; j++) {
    823:       PetscReal pp;

    825:       PetscDTJacobiRecurrence_Internal(j, ak, bk, cnm1, cnm1x, cnm2);
    826:       pp  = (cnm1 + cnm1x * x) * pm1 - cnm2 * pm2;
    827:       pm2 = pm1;
    828:       pm1 = pp;
    829:       while (l < ndegree && degrees[l] - k == j) {
    830:         p[l] = pp;
    831:         for (m = 0; m < k; m++) p[l] *= (abk1 + j + m) * 0.5;
    832:         l++;
    833:       }
    834:     }
    835:     p += ndegree;
    836:   }
    837:   PetscFunctionReturn(PETSC_SUCCESS);
    838: }

    840: /*@
    841:   PetscDTJacobiEvalJet - Evaluate the jet (function and derivatives) of the Jacobi polynomials basis up to a given degree.

    843:   Input Parameters:
    844: + alpha   - the left exponent of the weight
    845: . beta    - the right exponetn of the weight
    846: . npoints - the number of points to evaluate the polynomials at
    847: . points  - [npoints] array of point coordinates
    848: . degree  - the maximm degree polynomial space to evaluate, (degree + 1) will be evaluated total.
    849: - k       - the maximum derivative to evaluate in the jet, (k + 1) will be evaluated total.

    851:   Output Parameter:
    852: . p - an array containing the evaluations of the Jacobi polynomials's jets on the points.  the size is (degree + 1) x
    853:       (k + 1) x npoints, which also describes the order of the dimensions of this three-dimensional array: the first
    854:       (slowest varying) dimension is polynomial degree; the second dimension is derivative order; the third (fastest
    855:       varying) dimension is the index of the evaluation point.

    857:   Level: advanced

    859:   Notes:
    860:   The Jacobi polynomials with indices $\alpha$ and $\beta$ are orthogonal with respect to the
    861:   weighted inner product $\langle f, g \rangle = \int_{-1}^1 (1+x)^{\alpha} (1-x)^{\beta} f(x)
    862:   g(x) dx$.

    864: .seealso: `PetscDTJacobiEval()`, `PetscDTPKDEvalJet()`
    865: @*/
    866: PetscErrorCode PetscDTJacobiEvalJet(PetscReal alpha, PetscReal beta, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt k, PetscReal p[])
    867: {
    868:   PetscInt   i, j, l;
    869:   PetscInt  *degrees;
    870:   PetscReal *psingle;

    872:   PetscFunctionBegin;
    873:   if (degree == 0) {
    874:     PetscInt zero = 0;

    876:     for (i = 0; i <= k; i++) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, i, points, 1, &zero, &p[i * npoints]));
    877:     PetscFunctionReturn(PETSC_SUCCESS);
    878:   }
    879:   PetscCall(PetscMalloc1(degree + 1, &degrees));
    880:   PetscCall(PetscMalloc1((degree + 1) * npoints, &psingle));
    881:   for (i = 0; i <= degree; i++) degrees[i] = i;
    882:   for (i = 0; i <= k; i++) {
    883:     PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, i, points, degree + 1, degrees, psingle));
    884:     for (j = 0; j <= degree; j++) {
    885:       for (l = 0; l < npoints; l++) p[(j * (k + 1) + i) * npoints + l] = psingle[l * (degree + 1) + j];
    886:     }
    887:   }
    888:   PetscCall(PetscFree(psingle));
    889:   PetscCall(PetscFree(degrees));
    890:   PetscFunctionReturn(PETSC_SUCCESS);
    891: }

    893: /*@
    894:   PetscDTJacobiEval - evaluate Jacobi polynomials for the weight function $(1.+x)^{\alpha} (1.-x)^{\beta}$ at a set of points
    895:   at points

    897:   Not Collective

    899:   Input Parameters:
    900: + npoints - number of spatial points to evaluate at
    901: . alpha   - the left exponent > -1
    902: . beta    - the right exponent > -1
    903: . points  - array of locations to evaluate at
    904: . ndegree - number of basis degrees to evaluate
    905: - degrees - sorted array of degrees to evaluate

    907:   Output Parameters:
    908: + B  - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension npoints*ndegrees, allocated by caller) (or `NULL`)
    909: . D  - row-oriented derivative evaluation matrix (or `NULL`)
    910: - D2 - row-oriented second derivative evaluation matrix (or `NULL`)

    912:   Level: intermediate

    914: .seealso: `PetscDTGaussQuadrature()`, `PetscDTLegendreEval()`
    915: @*/
    916: PetscErrorCode PetscDTJacobiEval(PetscInt npoints, PetscReal alpha, PetscReal beta, const PetscReal *points, PetscInt ndegree, const PetscInt *degrees, PeOp PetscReal B[], PeOp PetscReal D[], PeOp PetscReal D2[])
    917: {
    918:   PetscFunctionBegin;
    919:   PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "alpha must be > -1.");
    920:   PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "beta must be > -1.");
    921:   if (!npoints || !ndegree) PetscFunctionReturn(PETSC_SUCCESS);
    922:   if (B) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, 0, points, ndegree, degrees, B));
    923:   if (D) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, 1, points, ndegree, degrees, D));
    924:   if (D2) PetscCall(PetscDTJacobiEval_Internal(npoints, alpha, beta, 2, points, ndegree, degrees, D2));
    925:   PetscFunctionReturn(PETSC_SUCCESS);
    926: }

    928: /*@
    929:   PetscDTLegendreEval - evaluate Legendre polynomials at points

    931:   Not Collective

    933:   Input Parameters:
    934: + npoints - number of spatial points to evaluate at
    935: . points  - array of locations to evaluate at
    936: . ndegree - number of basis degrees to evaluate
    937: - degrees - sorted array of degrees to evaluate

    939:   Output Parameters:
    940: + B  - row-oriented basis evaluation matrix B[point*ndegree + degree] (dimension `npoints`*`ndegrees`, allocated by caller) (or `NULL`)
    941: . D  - row-oriented derivative evaluation matrix (or `NULL`)
    942: - D2 - row-oriented second derivative evaluation matrix (or `NULL`)

    944:   Level: intermediate

    946: .seealso: `PetscDTGaussQuadrature()`
    947: @*/
    948: PetscErrorCode PetscDTLegendreEval(PetscInt npoints, const PetscReal *points, PetscInt ndegree, const PetscInt *degrees, PeOp PetscReal B[], PeOp PetscReal D[], PeOp PetscReal D2[])
    949: {
    950:   PetscFunctionBegin;
    951:   PetscCall(PetscDTJacobiEval(npoints, 0., 0., points, ndegree, degrees, B, D, D2));
    952:   PetscFunctionReturn(PETSC_SUCCESS);
    953: }

    955: /*@
    956:   PetscDTIndexToGradedOrder - convert an index into a tuple of monomial degrees in a graded order (that is, if the degree sum of tuple x is less than the degree sum of tuple y,
    957:   then the index of x is smaller than the index of y)

    959:   Input Parameters:
    960: + len   - the desired length of the degree tuple
    961: - index - the index to convert: should be >= 0

    963:   Output Parameter:
    964: . degtup - filled with a tuple of degrees

    966:   Level: beginner

    968:   Note:
    969:   For two tuples x and y with the same degree sum, partial degree sums over the final elements of the tuples
    970:   acts as a tiebreaker.  For example, (2, 1, 1) and (1, 2, 1) have the same degree sum, but the degree sum over the
    971:   last two elements is smaller for the former, so (2, 1, 1) < (1, 2, 1).

    973: .seealso: `PetscDTGradedOrderToIndex()`
    974: @*/
    975: PetscErrorCode PetscDTIndexToGradedOrder(PetscInt len, PetscInt index, PetscInt degtup[])
    976: {
    977:   PetscInt i, total;
    978:   PetscInt sum;

    980:   PetscFunctionBeginHot;
    981:   PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
    982:   PetscCheck(index >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index must be non-negative");
    983:   total = 1;
    984:   sum   = 0;
    985:   while (index >= total) {
    986:     index -= total;
    987:     total = (total * (len + sum)) / (sum + 1);
    988:     sum++;
    989:   }
    990:   for (i = 0; i < len; i++) {
    991:     PetscInt c;

    993:     degtup[i] = sum;
    994:     for (c = 0, total = 1; c < sum; c++) {
    995:       /* going into the loop, total is the number of way to have a tuple of sum exactly c with length len - 1 - i */
    996:       if (index < total) break;
    997:       index -= total;
    998:       total = (total * (len - 1 - i + c)) / (c + 1);
    999:       degtup[i]--;
    1000:     }
    1001:     sum -= degtup[i];
    1002:   }
    1003:   PetscFunctionReturn(PETSC_SUCCESS);
    1004: }

    1006: /*@
    1007:   PetscDTGradedOrderToIndex - convert a tuple into an index in a graded order, the inverse of `PetscDTIndexToGradedOrder()`.

    1009:   Input Parameters:
    1010: + len    - the length of the degree tuple
    1011: - degtup - tuple with this length

    1013:   Output Parameter:
    1014: . index - index in graded order: >= 0

    1016:   Level: beginner

    1018:   Note:
    1019:   For two tuples x and y with the same degree sum, partial degree sums over the final elements of the tuples
    1020:   acts as a tiebreaker.  For example, (2, 1, 1) and (1, 2, 1) have the same degree sum, but the degree sum over the
    1021:   last two elements is smaller for the former, so (2, 1, 1) < (1, 2, 1).

    1023: .seealso: `PetscDTIndexToGradedOrder()`
    1024: @*/
    1025: PetscErrorCode PetscDTGradedOrderToIndex(PetscInt len, const PetscInt degtup[], PetscInt *index)
    1026: {
    1027:   PetscInt i, idx, sum, total;

    1029:   PetscFunctionBeginHot;
    1030:   PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
    1031:   for (i = 0, sum = 0; i < len; i++) sum += degtup[i];
    1032:   idx   = 0;
    1033:   total = 1;
    1034:   for (i = 0; i < sum; i++) {
    1035:     idx += total;
    1036:     total = (total * (len + i)) / (i + 1);
    1037:   }
    1038:   for (i = 0; i < len - 1; i++) {
    1039:     PetscInt c;

    1041:     total = 1;
    1042:     sum -= degtup[i];
    1043:     for (c = 0; c < sum; c++) {
    1044:       idx += total;
    1045:       total = (total * (len - 1 - i + c)) / (c + 1);
    1046:     }
    1047:   }
    1048:   *index = idx;
    1049:   PetscFunctionReturn(PETSC_SUCCESS);
    1050: }

    1052: static PetscBool PKDCite       = PETSC_FALSE;
    1053: const char       PKDCitation[] = "@article{Kirby2010,\n"
    1054:                                  "  title={Singularity-free evaluation of collapsed-coordinate orthogonal polynomials},\n"
    1055:                                  "  author={Kirby, Robert C},\n"
    1056:                                  "  journal={ACM Transactions on Mathematical Software (TOMS)},\n"
    1057:                                  "  volume={37},\n"
    1058:                                  "  number={1},\n"
    1059:                                  "  pages={1--16},\n"
    1060:                                  "  year={2010},\n"
    1061:                                  "  publisher={ACM New York, NY, USA}\n}\n";

    1063: /*@
    1064:   PetscDTPKDEvalJet - Evaluate the jet (function and derivatives) of the Proriol-Koornwinder-Dubiner (PKD) basis for
    1065:   the space of polynomials up to a given degree.

    1067:   Input Parameters:
    1068: + dim     - the number of variables in the multivariate polynomials
    1069: . npoints - the number of points to evaluate the polynomials at
    1070: . points  - [npoints x dim] array of point coordinates
    1071: . degree  - the degree (sum of degrees on the variables in a monomial) of the polynomial space to evaluate.  There are ((dim + degree) choose dim) polynomials in this space.
    1072: - k       - the maximum order partial derivative to evaluate in the jet.  There are (dim + k choose dim) partial derivatives
    1073:             in the jet.  Choosing k = 0 means to evaluate just the function and no derivatives

    1075:   Output Parameter:
    1076: . p - an array containing the evaluations of the PKD polynomials' jets on the points.  The size is ((dim + degree)
    1077:       choose dim) x ((dim + k) choose dim) x npoints, which also describes the order of the dimensions of this
    1078:       three-dimensional array: the first (slowest varying) dimension is basis function index; the second dimension is jet
    1079:       index; the third (fastest varying) dimension is the index of the evaluation point.

    1081:   Level: advanced

    1083:   Notes:
    1084:   The PKD basis is L2-orthonormal on the biunit simplex (which is used as the reference element
    1085:   for finite elements in PETSc), which makes it a stable basis to use for evaluating
    1086:   polynomials in that domain.

    1088:   The ordering of the basis functions, and the ordering of the derivatives in the jet, both follow the graded
    1089:   ordering of `PetscDTIndexToGradedOrder()` and `PetscDTGradedOrderToIndex()`.  For example, in 3D, the polynomial with
    1090:   leading monomial x^2,y^0,z^1, which has degree tuple (2,0,1), which by `PetscDTGradedOrderToIndex()` has index 12 (it is the 13th basis function in the space);
    1091:   the partial derivative $\partial_x \partial_z$ has order tuple (1,0,1), appears at index 6 in the jet (it is the 7th partial derivative in the jet).

    1093:   The implementation uses Kirby's singularity-free evaluation algorithm, <https://doi.org/10.1145/1644001.1644006>.

    1095: .seealso: `PetscDTGradedOrderToIndex()`, `PetscDTIndexToGradedOrder()`, `PetscDTJacobiEvalJet()`
    1096: @*/
    1097: PetscErrorCode PetscDTPKDEvalJet(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt k, PetscReal p[])
    1098: {
    1099:   PetscInt   degidx, kidx, d, pt;
    1100:   PetscInt   Nk, Ndeg;
    1101:   PetscInt  *ktup, *degtup;
    1102:   PetscReal *scales, initscale, scaleexp;

    1104:   PetscFunctionBegin;
    1105:   PetscCall(PetscCitationsRegister(PKDCitation, &PKDCite));
    1106:   PetscCall(PetscDTBinomialInt(dim + k, k, &Nk));
    1107:   PetscCall(PetscDTBinomialInt(degree + dim, degree, &Ndeg));
    1108:   PetscCall(PetscMalloc2(dim, &degtup, dim, &ktup));
    1109:   PetscCall(PetscMalloc1(Ndeg, &scales));
    1110:   initscale = 1.;
    1111:   if (dim > 1) {
    1112:     PetscCall(PetscDTBinomial(dim, 2, &scaleexp));
    1113:     initscale = PetscPowReal(2., scaleexp * 0.5);
    1114:   }
    1115:   for (degidx = 0; degidx < Ndeg; degidx++) {
    1116:     PetscInt  e, i;
    1117:     PetscInt  m1idx = -1, m2idx = -1;
    1118:     PetscInt  n;
    1119:     PetscInt  degsum;
    1120:     PetscReal alpha;
    1121:     PetscReal cnm1, cnm1x, cnm2;
    1122:     PetscReal norm;

    1124:     PetscCall(PetscDTIndexToGradedOrder(dim, degidx, degtup));
    1125:     for (d = dim - 1; d >= 0; d--)
    1126:       if (degtup[d]) break;
    1127:     if (d < 0) { /* constant is 1 everywhere, all derivatives are zero */
    1128:       scales[degidx] = initscale;
    1129:       for (e = 0; e < dim; e++) {
    1130:         PetscCall(PetscDTJacobiNorm(e, 0., 0, &norm));
    1131:         scales[degidx] /= norm;
    1132:       }
    1133:       for (i = 0; i < npoints; i++) p[degidx * Nk * npoints + i] = 1.;
    1134:       for (i = 0; i < (Nk - 1) * npoints; i++) p[(degidx * Nk + 1) * npoints + i] = 0.;
    1135:       continue;
    1136:     }
    1137:     n = degtup[d];
    1138:     degtup[d]--;
    1139:     PetscCall(PetscDTGradedOrderToIndex(dim, degtup, &m1idx));
    1140:     if (degtup[d] > 0) {
    1141:       degtup[d]--;
    1142:       PetscCall(PetscDTGradedOrderToIndex(dim, degtup, &m2idx));
    1143:       degtup[d]++;
    1144:     }
    1145:     degtup[d]++;
    1146:     for (e = 0, degsum = 0; e < d; e++) degsum += degtup[e];
    1147:     alpha = 2 * degsum + d;
    1148:     PetscDTJacobiRecurrence_Internal(n, alpha, 0., cnm1, cnm1x, cnm2);

    1150:     scales[degidx] = initscale;
    1151:     for (e = 0, degsum = 0; e < dim; e++) {
    1152:       PetscInt  f;
    1153:       PetscReal ealpha;
    1154:       PetscReal enorm;

    1156:       ealpha = 2 * degsum + e;
    1157:       for (f = 0; f < degsum; f++) scales[degidx] *= 2.;
    1158:       PetscCall(PetscDTJacobiNorm(ealpha, 0., degtup[e], &enorm));
    1159:       scales[degidx] /= enorm;
    1160:       degsum += degtup[e];
    1161:     }

    1163:     for (pt = 0; pt < npoints; pt++) {
    1164:       /* compute the multipliers */
    1165:       PetscReal thetanm1, thetanm1x, thetanm2;

    1167:       thetanm1x = dim - (d + 1) + 2. * points[pt * dim + d];
    1168:       for (e = d + 1; e < dim; e++) thetanm1x += points[pt * dim + e];
    1169:       thetanm1x *= 0.5;
    1170:       thetanm1 = (2. - (dim - (d + 1)));
    1171:       for (e = d + 1; e < dim; e++) thetanm1 -= points[pt * dim + e];
    1172:       thetanm1 *= 0.5;
    1173:       thetanm2 = thetanm1 * thetanm1;

    1175:       for (kidx = 0; kidx < Nk; kidx++) {
    1176:         PetscInt f;

    1178:         PetscCall(PetscDTIndexToGradedOrder(dim, kidx, ktup));
    1179:         /* first sum in the same derivative terms */
    1180:         p[(degidx * Nk + kidx) * npoints + pt] = (cnm1 * thetanm1 + cnm1x * thetanm1x) * p[(m1idx * Nk + kidx) * npoints + pt];
    1181:         if (m2idx >= 0) p[(degidx * Nk + kidx) * npoints + pt] -= cnm2 * thetanm2 * p[(m2idx * Nk + kidx) * npoints + pt];

    1183:         for (f = d; f < dim; f++) {
    1184:           PetscInt km1idx, mplty = ktup[f];

    1186:           if (!mplty) continue;
    1187:           ktup[f]--;
    1188:           PetscCall(PetscDTGradedOrderToIndex(dim, ktup, &km1idx));

    1190:           /* the derivative of  cnm1x * thetanm1x  wrt x variable f is 0.5 * cnm1x if f > d otherwise it is cnm1x */
    1191:           /* the derivative of  cnm1  * thetanm1   wrt x variable f is 0 if f == d, otherwise it is -0.5 * cnm1 */
    1192:           /* the derivative of -cnm2  * thetanm2   wrt x variable f is 0 if f == d, otherwise it is cnm2 * thetanm1 */
    1193:           if (f > d) {
    1194:             PetscInt f2;

    1196:             p[(degidx * Nk + kidx) * npoints + pt] += mplty * 0.5 * (cnm1x - cnm1) * p[(m1idx * Nk + km1idx) * npoints + pt];
    1197:             if (m2idx >= 0) {
    1198:               p[(degidx * Nk + kidx) * npoints + pt] += mplty * cnm2 * thetanm1 * p[(m2idx * Nk + km1idx) * npoints + pt];
    1199:               /* second derivatives of -cnm2  * thetanm2   wrt x variable f,f2 is like - 0.5 * cnm2 */
    1200:               for (f2 = f; f2 < dim; f2++) {
    1201:                 PetscInt km2idx, mplty2 = ktup[f2];
    1202:                 PetscInt factor;

    1204:                 if (!mplty2) continue;
    1205:                 ktup[f2]--;
    1206:                 PetscCall(PetscDTGradedOrderToIndex(dim, ktup, &km2idx));

    1208:                 factor = mplty * mplty2;
    1209:                 if (f == f2) factor /= 2;
    1210:                 p[(degidx * Nk + kidx) * npoints + pt] -= 0.5 * factor * cnm2 * p[(m2idx * Nk + km2idx) * npoints + pt];
    1211:                 ktup[f2]++;
    1212:               }
    1213:             }
    1214:           } else {
    1215:             p[(degidx * Nk + kidx) * npoints + pt] += mplty * cnm1x * p[(m1idx * Nk + km1idx) * npoints + pt];
    1216:           }
    1217:           ktup[f]++;
    1218:         }
    1219:       }
    1220:     }
    1221:   }
    1222:   for (degidx = 0; degidx < Ndeg; degidx++) {
    1223:     PetscReal scale = scales[degidx];
    1224:     PetscInt  i;

    1226:     for (i = 0; i < Nk * npoints; i++) p[degidx * Nk * npoints + i] *= scale;
    1227:   }
    1228:   PetscCall(PetscFree(scales));
    1229:   PetscCall(PetscFree2(degtup, ktup));
    1230:   PetscFunctionReturn(PETSC_SUCCESS);
    1231: }

    1233: /*@
    1234:   PetscDTPTrimmedSize - The size of the trimmed polynomial space of k-forms with a given degree and form degree,
    1235:   which can be evaluated in `PetscDTPTrimmedEvalJet()`.

    1237:   Input Parameters:
    1238: + dim        - the number of variables in the multivariate polynomials
    1239: . degree     - the degree (sum of degrees on the variables in a monomial) of the trimmed polynomial space.
    1240: - formDegree - the degree of the form

    1242:   Output Parameter:
    1243: . size - The number ((`dim` + `degree`) choose (`dim` + `formDegree`)) x ((`degree` + `formDegree` - 1) choose (`formDegree`))

    1245:   Level: advanced

    1247: .seealso: `PetscDTPTrimmedEvalJet()`
    1248: @*/
    1249: PetscErrorCode PetscDTPTrimmedSize(PetscInt dim, PetscInt degree, PetscInt formDegree, PetscInt *size)
    1250: {
    1251:   PetscInt Nrk, Nbpt; // number of trimmed polynomials

    1253:   PetscFunctionBegin;
    1254:   formDegree = PetscAbsInt(formDegree);
    1255:   PetscCall(PetscDTBinomialInt(degree + dim, degree + formDegree, &Nbpt));
    1256:   PetscCall(PetscDTBinomialInt(degree + formDegree - 1, formDegree, &Nrk));
    1257:   Nbpt *= Nrk;
    1258:   *size = Nbpt;
    1259:   PetscFunctionReturn(PETSC_SUCCESS);
    1260: }

    1262: /* there was a reference implementation based on section 4.4 of Arnold, Falk & Winther (acta numerica, 2006), but it
    1263:  * was inferior to this implementation */
    1264: static PetscErrorCode PetscDTPTrimmedEvalJet_Internal(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt formDegree, PetscInt jetDegree, PetscReal p[])
    1265: {
    1266:   PetscInt  formDegreeOrig = formDegree;
    1267:   PetscBool formNegative   = (formDegreeOrig < 0) ? PETSC_TRUE : PETSC_FALSE;

    1269:   PetscFunctionBegin;
    1270:   formDegree = PetscAbsInt(formDegreeOrig);
    1271:   if (formDegree == 0) {
    1272:     PetscCall(PetscDTPKDEvalJet(dim, npoints, points, degree, jetDegree, p));
    1273:     PetscFunctionReturn(PETSC_SUCCESS);
    1274:   }
    1275:   if (formDegree == dim) {
    1276:     PetscCall(PetscDTPKDEvalJet(dim, npoints, points, degree - 1, jetDegree, p));
    1277:     PetscFunctionReturn(PETSC_SUCCESS);
    1278:   }
    1279:   PetscInt Nbpt;
    1280:   PetscCall(PetscDTPTrimmedSize(dim, degree, formDegree, &Nbpt));
    1281:   PetscInt Nf;
    1282:   PetscCall(PetscDTBinomialInt(dim, formDegree, &Nf));
    1283:   PetscInt Nk;
    1284:   PetscCall(PetscDTBinomialInt(dim + jetDegree, dim, &Nk));
    1285:   PetscCall(PetscArrayzero(p, Nbpt * Nf * Nk * npoints));

    1287:   PetscInt Nbpm1; // number of scalar polynomials up to degree - 1;
    1288:   PetscCall(PetscDTBinomialInt(dim + degree - 1, dim, &Nbpm1));
    1289:   PetscReal *p_scalar;
    1290:   PetscCall(PetscMalloc1(Nbpm1 * Nk * npoints, &p_scalar));
    1291:   PetscCall(PetscDTPKDEvalJet(dim, npoints, points, degree - 1, jetDegree, p_scalar));
    1292:   PetscInt total = 0;
    1293:   // First add the full polynomials up to degree - 1 into the basis: take the scalar
    1294:   // and copy one for each form component
    1295:   for (PetscInt i = 0; i < Nbpm1; i++) {
    1296:     const PetscReal *src = &p_scalar[i * Nk * npoints];
    1297:     for (PetscInt f = 0; f < Nf; f++) {
    1298:       PetscReal *dest = &p[(total++ * Nf + f) * Nk * npoints];
    1299:       PetscCall(PetscArraycpy(dest, src, Nk * npoints));
    1300:     }
    1301:   }
    1302:   PetscInt *form_atoms;
    1303:   PetscCall(PetscMalloc1(formDegree + 1, &form_atoms));
    1304:   // construct the interior product pattern
    1305:   PetscInt (*pattern)[3];
    1306:   PetscInt Nf1; // number of formDegree + 1 forms
    1307:   PetscCall(PetscDTBinomialInt(dim, formDegree + 1, &Nf1));
    1308:   PetscInt nnz = Nf1 * (formDegree + 1);
    1309:   PetscCall(PetscMalloc1(Nf1 * (formDegree + 1), &pattern));
    1310:   PetscCall(PetscDTAltVInteriorPattern(dim, formDegree + 1, pattern));
    1311:   PetscReal centroid = (1. - dim) / (dim + 1.);
    1312:   PetscInt *deriv;
    1313:   PetscCall(PetscMalloc1(dim, &deriv));
    1314:   for (PetscInt d = dim; d >= formDegree + 1; d--) {
    1315:     PetscInt Nfd1; // number of formDegree + 1 forms in dimension d that include dx_0
    1316:                    // (equal to the number of formDegree forms in dimension d-1)
    1317:     PetscCall(PetscDTBinomialInt(d - 1, formDegree, &Nfd1));
    1318:     // The number of homogeneous (degree-1) scalar polynomials in d variables
    1319:     PetscInt Nh;
    1320:     PetscCall(PetscDTBinomialInt(d - 1 + degree - 1, d - 1, &Nh));
    1321:     const PetscReal *h_scalar = &p_scalar[(Nbpm1 - Nh) * Nk * npoints];
    1322:     for (PetscInt b = 0; b < Nh; b++) {
    1323:       const PetscReal *h_s = &h_scalar[b * Nk * npoints];
    1324:       for (PetscInt f = 0; f < Nfd1; f++) {
    1325:         // construct all formDegree+1 forms that start with dx_(dim - d) /\ ...
    1326:         form_atoms[0] = dim - d;
    1327:         PetscCall(PetscDTEnumSubset(d - 1, formDegree, f, &form_atoms[1]));
    1328:         for (PetscInt i = 0; i < formDegree; i++) form_atoms[1 + i] += form_atoms[0] + 1;
    1329:         PetscInt f_ind; // index of the resulting form
    1330:         PetscCall(PetscDTSubsetIndex(dim, formDegree + 1, form_atoms, &f_ind));
    1331:         PetscReal *p_f = &p[total++ * Nf * Nk * npoints];
    1332:         for (PetscInt nz = 0; nz < nnz; nz++) {
    1333:           PetscInt  i     = pattern[nz][0]; // formDegree component
    1334:           PetscInt  j     = pattern[nz][1]; // (formDegree + 1) component
    1335:           PetscInt  v     = pattern[nz][2]; // coordinate component
    1336:           PetscReal scale = v < 0 ? -1. : 1.;

    1338:           i     = formNegative ? (Nf - 1 - i) : i;
    1339:           scale = (formNegative && (i & 1)) ? -scale : scale;
    1340:           v     = v < 0 ? -(v + 1) : v;
    1341:           if (j != f_ind) continue;
    1342:           PetscReal *p_i = &p_f[i * Nk * npoints];
    1343:           for (PetscInt jet = 0; jet < Nk; jet++) {
    1344:             const PetscReal *h_jet = &h_s[jet * npoints];
    1345:             PetscReal       *p_jet = &p_i[jet * npoints];

    1347:             for (PetscInt pt = 0; pt < npoints; pt++) p_jet[pt] += scale * h_jet[pt] * (points[pt * dim + v] - centroid);
    1348:             PetscCall(PetscDTIndexToGradedOrder(dim, jet, deriv));
    1349:             deriv[v]++;
    1350:             PetscReal mult = deriv[v];
    1351:             PetscInt  l;
    1352:             PetscCall(PetscDTGradedOrderToIndex(dim, deriv, &l));
    1353:             if (l >= Nk) continue;
    1354:             p_jet = &p_i[l * npoints];
    1355:             for (PetscInt pt = 0; pt < npoints; pt++) p_jet[pt] += scale * mult * h_jet[pt];
    1356:             deriv[v]--;
    1357:           }
    1358:         }
    1359:       }
    1360:     }
    1361:   }
    1362:   PetscCheck(total == Nbpt, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Incorrectly counted P trimmed polynomials");
    1363:   PetscCall(PetscFree(deriv));
    1364:   PetscCall(PetscFree(pattern));
    1365:   PetscCall(PetscFree(form_atoms));
    1366:   PetscCall(PetscFree(p_scalar));
    1367:   PetscFunctionReturn(PETSC_SUCCESS);
    1368: }

    1370: /*@
    1371:   PetscDTPTrimmedEvalJet - Evaluate the jet (function and derivatives) of a basis of the trimmed polynomial k-forms up to
    1372:   a given degree.

    1374:   Input Parameters:
    1375: + dim        - the number of variables in the multivariate polynomials
    1376: . npoints    - the number of points to evaluate the polynomials at
    1377: . points     - [npoints x dim] array of point coordinates
    1378: . degree     - the degree (sum of degrees on the variables in a monomial) of the trimmed polynomial space to evaluate.
    1379:            There are ((dim + degree) choose (dim + formDegree)) x ((degree + formDegree - 1) choose (formDegree)) polynomials in this space.
    1380:            (You can use `PetscDTPTrimmedSize()` to compute this size.)
    1381: . formDegree - the degree of the form
    1382: - jetDegree  - the maximum order partial derivative to evaluate in the jet.  There are ((dim + jetDegree) choose dim) partial derivatives
    1383:               in the jet.  Choosing jetDegree = 0 means to evaluate just the function and no derivatives

    1385:   Output Parameter:
    1386: . p - an array containing the evaluations of the PKD polynomials' jets on the points.

    1388:   Level: advanced

    1390:   Notes:
    1391:   The size of `p` is `PetscDTPTrimmedSize()` x ((dim + formDegree) choose dim) x ((dim + k)
    1392:   choose dim) x npoints,which also describes the order of the dimensions of this
    1393:   four-dimensional array\:

    1395:   the first (slowest varying) dimension is basis function index;
    1396:   the second dimension is component of the form;
    1397:   the third dimension is jet index;
    1398:   the fourth (fastest varying) dimension is the index of the evaluation point.

    1400:   The ordering of the basis functions is not graded, so the basis functions are not nested by degree like `PetscDTPKDEvalJet()`.
    1401:   The basis functions are not an L2-orthonormal basis on any particular domain.

    1403:   The implementation is based on the description of the trimmed polynomials up to degree r as
    1404:   the direct sum of polynomials up to degree (r-1) and the Koszul differential applied to
    1405:   homogeneous polynomials of degree (r-1).

    1407: .seealso: `PetscDTPKDEvalJet()`, `PetscDTPTrimmedSize()`
    1408: @*/
    1409: PetscErrorCode PetscDTPTrimmedEvalJet(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt degree, PetscInt formDegree, PetscInt jetDegree, PetscReal p[])
    1410: {
    1411:   PetscFunctionBegin;
    1412:   PetscCall(PetscDTPTrimmedEvalJet_Internal(dim, npoints, points, degree, formDegree, jetDegree, p));
    1413:   PetscFunctionReturn(PETSC_SUCCESS);
    1414: }

    1416: /* solve the symmetric tridiagonal eigenvalue system, writing the eigenvalues into eigs and the eigenvectors into V
    1417:  * with lds n; diag and subdiag are overwritten */
    1418: static PetscErrorCode PetscDTSymmetricTridiagonalEigensolve(PetscInt n, PetscReal diag[], PetscReal subdiag[], PetscReal eigs[], PetscScalar V[])
    1419: {
    1420:   char          jobz   = 'V'; /* eigenvalues and eigenvectors */
    1421:   char          range  = 'A'; /* all eigenvalues will be found */
    1422:   PetscReal     VL     = 0.;  /* ignored because range is 'A' */
    1423:   PetscReal     VU     = 0.;  /* ignored because range is 'A' */
    1424:   PetscBLASInt  IL     = 0;   /* ignored because range is 'A' */
    1425:   PetscBLASInt  IU     = 0;   /* ignored because range is 'A' */
    1426:   PetscReal     abstol = 0.;  /* unused */
    1427:   PetscBLASInt  bn, bm, ldz;  /* bm will equal bn on exit */
    1428:   PetscBLASInt *isuppz;
    1429:   PetscBLASInt  lwork, liwork;
    1430:   PetscReal     workquery;
    1431:   PetscBLASInt  iworkquery;
    1432:   PetscBLASInt *iwork;
    1433:   PetscBLASInt  info;
    1434:   PetscReal    *work = NULL;

    1436:   PetscFunctionBegin;
    1437: #if !defined(PETSCDTGAUSSIANQUADRATURE_EIG)
    1438:   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
    1439: #endif
    1440:   PetscCall(PetscBLASIntCast(n, &bn));
    1441:   PetscCall(PetscBLASIntCast(n, &ldz));
    1442: #if !defined(PETSC_MISSING_LAPACK_STEGR)
    1443:   PetscCall(PetscMalloc1(2 * n, &isuppz));
    1444:   lwork  = -1;
    1445:   liwork = -1;
    1446:   PetscCallBLAS("LAPACKstegr", LAPACKstegr_(&jobz, &range, &bn, diag, subdiag, &VL, &VU, &IL, &IU, &abstol, &bm, eigs, V, &ldz, isuppz, &workquery, &lwork, &iworkquery, &liwork, &info));
    1447:   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_PLIB, "xSTEGR error");
    1448:   lwork  = (PetscBLASInt)workquery;
    1449:   liwork = iworkquery;
    1450:   PetscCall(PetscMalloc2(lwork, &work, liwork, &iwork));
    1451:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    1452:   PetscCallBLAS("LAPACKstegr", LAPACKstegr_(&jobz, &range, &bn, diag, subdiag, &VL, &VU, &IL, &IU, &abstol, &bm, eigs, V, &ldz, isuppz, work, &lwork, iwork, &liwork, &info));
    1453:   PetscCall(PetscFPTrapPop());
    1454:   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_PLIB, "xSTEGR error");
    1455:   PetscCall(PetscFree2(work, iwork));
    1456:   PetscCall(PetscFree(isuppz));
    1457: #elif !defined(PETSC_MISSING_LAPACK_STEQR)
    1458:   jobz = 'I'; /* Compute eigenvalues and eigenvectors of the
    1459:                  tridiagonal matrix.  Z is initialized to the identity
    1460:                  matrix. */
    1461:   PetscCall(PetscMalloc1(PetscMax(1, 2 * n - 2), &work));
    1462:   PetscCallBLAS("LAPACKsteqr", LAPACKsteqr_("I", &bn, diag, subdiag, V, &ldz, work, &info));
    1463:   PetscCall(PetscFPTrapPop());
    1464:   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_PLIB, "xSTEQR error");
    1465:   PetscCall(PetscFree(work));
    1466:   PetscCall(PetscArraycpy(eigs, diag, n));
    1467: #endif
    1468:   PetscFunctionReturn(PETSC_SUCCESS);
    1469: }

    1471: /* Formula for the weights at the endpoints (-1 and 1) of Gauss-Lobatto-Jacobi
    1472:  * quadrature rules on the interval [-1, 1] */
    1473: static PetscErrorCode PetscDTGaussLobattoJacobiEndweights_Internal(PetscInt n, PetscReal alpha, PetscReal beta, PetscReal *leftw, PetscReal *rightw)
    1474: {
    1475:   PetscReal twoab1;
    1476:   PetscInt  m = n - 2;
    1477:   PetscReal a = alpha + 1.;
    1478:   PetscReal b = beta + 1.;
    1479:   PetscReal gra, grb;

    1481:   PetscFunctionBegin;
    1482:   twoab1 = PetscPowReal(2., a + b - 1.);
    1483: #if defined(PETSC_HAVE_LGAMMA)
    1484:   grb = PetscExpReal(2. * PetscLGamma(b + 1.) + PetscLGamma(m + 1.) + PetscLGamma(m + a + 1.) - (PetscLGamma(m + b + 1) + PetscLGamma(m + a + b + 1.)));
    1485:   gra = PetscExpReal(2. * PetscLGamma(a + 1.) + PetscLGamma(m + 1.) + PetscLGamma(m + b + 1.) - (PetscLGamma(m + a + 1) + PetscLGamma(m + a + b + 1.)));
    1486: #else
    1487:   {
    1488:     PetscInt alphai = (PetscInt)alpha;
    1489:     PetscInt betai  = (PetscInt)beta;

    1491:     if ((PetscReal)alphai == alpha && (PetscReal)betai == beta) {
    1492:       PetscReal binom1, binom2;

    1494:       PetscCall(PetscDTBinomial(m + b, b, &binom1));
    1495:       PetscCall(PetscDTBinomial(m + a + b, b, &binom2));
    1496:       grb = 1. / (binom1 * binom2);
    1497:       PetscCall(PetscDTBinomial(m + a, a, &binom1));
    1498:       PetscCall(PetscDTBinomial(m + a + b, a, &binom2));
    1499:       gra = 1. / (binom1 * binom2);
    1500:     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "lgamma() - math routine is unavailable.");
    1501:   }
    1502: #endif
    1503:   *leftw  = twoab1 * grb / b;
    1504:   *rightw = twoab1 * gra / a;
    1505:   PetscFunctionReturn(PETSC_SUCCESS);
    1506: }

    1508: /* Evaluates the nth jacobi polynomial with weight parameters a,b at a point x.
    1509:    Recurrence relations implemented from the pseudocode given in Karniadakis and Sherwin, Appendix B */
    1510: static inline PetscErrorCode PetscDTComputeJacobi(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscReal *P)
    1511: {
    1512:   PetscReal pn1, pn2;
    1513:   PetscReal cnm1, cnm1x, cnm2;
    1514:   PetscInt  k;

    1516:   PetscFunctionBegin;
    1517:   if (!n) {
    1518:     *P = 1.0;
    1519:     PetscFunctionReturn(PETSC_SUCCESS);
    1520:   }
    1521:   PetscDTJacobiRecurrence_Internal(1, a, b, cnm1, cnm1x, cnm2);
    1522:   pn2 = 1.;
    1523:   pn1 = cnm1 + cnm1x * x;
    1524:   if (n == 1) {
    1525:     *P = pn1;
    1526:     PetscFunctionReturn(PETSC_SUCCESS);
    1527:   }
    1528:   *P = 0.0;
    1529:   for (k = 2; k < n + 1; ++k) {
    1530:     PetscDTJacobiRecurrence_Internal(k, a, b, cnm1, cnm1x, cnm2);

    1532:     *P  = (cnm1 + cnm1x * x) * pn1 - cnm2 * pn2;
    1533:     pn2 = pn1;
    1534:     pn1 = *P;
    1535:   }
    1536:   PetscFunctionReturn(PETSC_SUCCESS);
    1537: }

    1539: /* Evaluates the first derivative of P_{n}^{a,b} at a point x. */
    1540: static inline PetscErrorCode PetscDTComputeJacobiDerivative(PetscReal a, PetscReal b, PetscInt n, PetscReal x, PetscInt k, PetscReal *P)
    1541: {
    1542:   PetscReal nP;
    1543:   PetscInt  i;

    1545:   PetscFunctionBegin;
    1546:   *P = 0.0;
    1547:   if (k > n) PetscFunctionReturn(PETSC_SUCCESS);
    1548:   PetscCall(PetscDTComputeJacobi(a + k, b + k, n - k, x, &nP));
    1549:   for (i = 0; i < k; i++) nP *= (a + b + n + 1. + i) * 0.5;
    1550:   *P = nP;
    1551:   PetscFunctionReturn(PETSC_SUCCESS);
    1552: }

    1554: static PetscErrorCode PetscDTGaussJacobiQuadrature_Newton_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal x[], PetscReal w[])
    1555: {
    1556:   PetscInt  maxIter = 100;
    1557:   PetscReal eps     = PetscExpReal(0.75 * PetscLogReal(PETSC_MACHINE_EPSILON));
    1558:   PetscReal a1, a6, gf;
    1559:   PetscInt  k;

    1561:   PetscFunctionBegin;
    1562:   a1 = PetscPowReal(2.0, a + b + 1);
    1563: #if defined(PETSC_HAVE_LGAMMA)
    1564:   {
    1565:     PetscReal a2, a3, a4, a5;
    1566:     a2 = PetscLGamma(a + npoints + 1);
    1567:     a3 = PetscLGamma(b + npoints + 1);
    1568:     a4 = PetscLGamma(a + b + npoints + 1);
    1569:     a5 = PetscLGamma(npoints + 1);
    1570:     gf = PetscExpReal(a2 + a3 - (a4 + a5));
    1571:   }
    1572: #else
    1573:   {
    1574:     PetscInt ia, ib;

    1576:     ia = (PetscInt)a;
    1577:     ib = (PetscInt)b;
    1578:     gf = 1.;
    1579:     if (ia == a && ia >= 0) { /* compute ratio of rising factorals wrt a */
    1580:       for (k = 0; k < ia; k++) gf *= (npoints + 1. + k) / (npoints + b + 1. + k);
    1581:     } else if (b == b && ib >= 0) { /* compute ratio of rising factorials wrt b */
    1582:       for (k = 0; k < ib; k++) gf *= (npoints + 1. + k) / (npoints + a + 1. + k);
    1583:     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "lgamma() - math routine is unavailable.");
    1584:   }
    1585: #endif

    1587:   a6 = a1 * gf;
    1588:   /* Computes the m roots of P_{m}^{a,b} on [-1,1] by Newton's method with Chebyshev points as initial guesses.
    1589:    Algorithm implemented from the pseudocode given by Karniadakis and Sherwin and Python in FIAT */
    1590:   for (k = 0; k < npoints; ++k) {
    1591:     PetscReal r = PetscCosReal(PETSC_PI * (1. - (4. * k + 3. + 2. * b) / (4. * npoints + 2. * (a + b + 1.)))), dP;
    1592:     PetscInt  j;

    1594:     if (k > 0) r = 0.5 * (r + x[k - 1]);
    1595:     for (j = 0; j < maxIter; ++j) {
    1596:       PetscReal s = 0.0, delta, f, fp;
    1597:       PetscInt  i;

    1599:       for (i = 0; i < k; ++i) s = s + 1.0 / (r - x[i]);
    1600:       PetscCall(PetscDTComputeJacobi(a, b, npoints, r, &f));
    1601:       PetscCall(PetscDTComputeJacobiDerivative(a, b, npoints, r, 1, &fp));
    1602:       delta = f / (fp - f * s);
    1603:       r     = r - delta;
    1604:       if (PetscAbsReal(delta) < eps) break;
    1605:     }
    1606:     x[k] = r;
    1607:     PetscCall(PetscDTComputeJacobiDerivative(a, b, npoints, x[k], 1, &dP));
    1608:     w[k] = a6 / (1.0 - PetscSqr(x[k])) / PetscSqr(dP);
    1609:   }
    1610:   PetscFunctionReturn(PETSC_SUCCESS);
    1611: }

    1613: /* Compute the diagonals of the Jacobi matrix used in Golub & Welsch algorithms for Gauss-Jacobi
    1614:  * quadrature weight calculations on [-1,1] for exponents (1. + x)^a (1.-x)^b */
    1615: static PetscErrorCode PetscDTJacobiMatrix_Internal(PetscInt nPoints, PetscReal a, PetscReal b, PetscReal *d, PetscReal *s)
    1616: {
    1617:   PetscInt i;

    1619:   PetscFunctionBegin;
    1620:   for (i = 0; i < nPoints; i++) {
    1621:     PetscReal A, B, C;

    1623:     PetscDTJacobiRecurrence_Internal(i + 1, a, b, A, B, C);
    1624:     d[i] = -A / B;
    1625:     if (i) s[i - 1] *= C / B;
    1626:     if (i < nPoints - 1) s[i] = 1. / B;
    1627:   }
    1628:   PetscFunctionReturn(PETSC_SUCCESS);
    1629: }

    1631: static PetscErrorCode PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(PetscInt npoints, PetscReal a, PetscReal b, PetscReal *x, PetscReal *w)
    1632: {
    1633:   PetscReal mu0;
    1634:   PetscReal ga, gb, gab;
    1635:   PetscInt  i;

    1637:   PetscFunctionBegin;
    1638:   PetscCall(PetscCitationsRegister(GolubWelschCitation, &GolubWelschCite));

    1640: #if defined(PETSC_HAVE_TGAMMA)
    1641:   ga  = PetscTGamma(a + 1);
    1642:   gb  = PetscTGamma(b + 1);
    1643:   gab = PetscTGamma(a + b + 2);
    1644: #else
    1645:   {
    1646:     PetscInt ia, ib;

    1648:     ia = (PetscInt)a;
    1649:     ib = (PetscInt)b;
    1650:     if (ia == a && ib == b && ia + 1 > 0 && ib + 1 > 0 && ia + ib + 2 > 0) { /* All gamma(x) terms are (x-1)! terms */
    1651:       PetscCall(PetscDTFactorial(ia, &ga));
    1652:       PetscCall(PetscDTFactorial(ib, &gb));
    1653:       PetscCall(PetscDTFactorial(ia + ib + 1, &gab));
    1654:     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "tgamma() - math routine is unavailable.");
    1655:   }
    1656: #endif
    1657:   mu0 = PetscPowReal(2., a + b + 1.) * ga * gb / gab;

    1659: #if defined(PETSCDTGAUSSIANQUADRATURE_EIG)
    1660:   {
    1661:     PetscReal   *diag, *subdiag;
    1662:     PetscScalar *V;

    1664:     PetscCall(PetscMalloc2(npoints, &diag, npoints, &subdiag));
    1665:     PetscCall(PetscMalloc1(npoints * npoints, &V));
    1666:     PetscCall(PetscDTJacobiMatrix_Internal(npoints, a, b, diag, subdiag));
    1667:     for (i = 0; i < npoints - 1; i++) subdiag[i] = PetscSqrtReal(subdiag[i]);
    1668:     PetscCall(PetscDTSymmetricTridiagonalEigensolve(npoints, diag, subdiag, x, V));
    1669:     for (i = 0; i < npoints; i++) w[i] = PetscSqr(PetscRealPart(V[i * npoints])) * mu0;
    1670:     PetscCall(PetscFree(V));
    1671:     PetscCall(PetscFree2(diag, subdiag));
    1672:   }
    1673: #else
    1674:   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "A LAPACK symmetric tridiagonal eigensolver could not be found");
    1675: #endif
    1676:   { /* As of March 2, 2020, The Sun Performance Library breaks the LAPACK contract for xstegr and xsteqr: the
    1677:        eigenvalues are not guaranteed to be in ascending order.  So we heave a passive aggressive sigh and check that
    1678:        the eigenvalues are sorted */
    1679:     PetscBool sorted;

    1681:     PetscCall(PetscSortedReal(npoints, x, &sorted));
    1682:     if (!sorted) {
    1683:       PetscInt  *order, i;
    1684:       PetscReal *tmp;

    1686:       PetscCall(PetscMalloc2(npoints, &order, npoints, &tmp));
    1687:       for (i = 0; i < npoints; i++) order[i] = i;
    1688:       PetscCall(PetscSortRealWithPermutation(npoints, x, order));
    1689:       PetscCall(PetscArraycpy(tmp, x, npoints));
    1690:       for (i = 0; i < npoints; i++) x[i] = tmp[order[i]];
    1691:       PetscCall(PetscArraycpy(tmp, w, npoints));
    1692:       for (i = 0; i < npoints; i++) w[i] = tmp[order[i]];
    1693:       PetscCall(PetscFree2(order, tmp));
    1694:     }
    1695:   }
    1696:   PetscFunctionReturn(PETSC_SUCCESS);
    1697: }

    1699: static PetscErrorCode PetscDTGaussJacobiQuadrature_Internal(PetscInt npoints, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
    1700: {
    1701:   PetscFunctionBegin;
    1702:   PetscCheck(npoints >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of points must be positive");
    1703:   /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
    1704:   PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "alpha must be > -1.");
    1705:   PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "beta must be > -1.");

    1707:   if (newton) PetscCall(PetscDTGaussJacobiQuadrature_Newton_Internal(npoints, alpha, beta, x, w));
    1708:   else PetscCall(PetscDTGaussJacobiQuadrature_GolubWelsch_Internal(npoints, alpha, beta, x, w));
    1709:   if (alpha == beta) { /* symmetrize */
    1710:     PetscInt i;
    1711:     for (i = 0; i < (npoints + 1) / 2; i++) {
    1712:       PetscInt  j  = npoints - 1 - i;
    1713:       PetscReal xi = x[i];
    1714:       PetscReal xj = x[j];
    1715:       PetscReal wi = w[i];
    1716:       PetscReal wj = w[j];

    1718:       x[i] = (xi - xj) / 2.;
    1719:       x[j] = (xj - xi) / 2.;
    1720:       w[i] = w[j] = (wi + wj) / 2.;
    1721:     }
    1722:   }
    1723:   PetscFunctionReturn(PETSC_SUCCESS);
    1724: }

    1726: /*@
    1727:   PetscDTGaussJacobiQuadrature - quadrature for the interval $[a, b]$ with the weight function
    1728:   $(x-a)^\alpha (x-b)^\beta$.

    1730:   Not Collective

    1732:   Input Parameters:
    1733: + npoints - the number of points in the quadrature rule
    1734: . a       - the left endpoint of the interval
    1735: . b       - the right endpoint of the interval
    1736: . alpha   - the left exponent
    1737: - beta    - the right exponent

    1739:   Output Parameters:
    1740: + x - array of length `npoints`, the locations of the quadrature points
    1741: - w - array of length `npoints`, the weights of the quadrature points

    1743:   Level: intermediate

    1745:   Note:
    1746:   This quadrature rule is exact for polynomials up to degree 2*`npoints` - 1.

    1748: .seealso: `PetscDTGaussQuadrature()`
    1749: @*/
    1750: PetscErrorCode PetscDTGaussJacobiQuadrature(PetscInt npoints, PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
    1751: {
    1752:   PetscInt i;

    1754:   PetscFunctionBegin;
    1755:   PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal));
    1756:   if (a != -1. || b != 1.) { /* shift */
    1757:     for (i = 0; i < npoints; i++) {
    1758:       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
    1759:       w[i] *= (b - a) / 2.;
    1760:     }
    1761:   }
    1762:   PetscFunctionReturn(PETSC_SUCCESS);
    1763: }

    1765: static PetscErrorCode PetscDTGaussLobattoJacobiQuadrature_Internal(PetscInt npoints, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[], PetscBool newton)
    1766: {
    1767:   PetscInt i;

    1769:   PetscFunctionBegin;
    1770:   PetscCheck(npoints >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of points must be positive");
    1771:   /* If asking for a 1D Lobatto point, just return the non-Lobatto 1D point */
    1772:   PetscCheck(alpha > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "alpha must be > -1.");
    1773:   PetscCheck(beta > -1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "beta must be > -1.");

    1775:   x[0]           = -1.;
    1776:   x[npoints - 1] = 1.;
    1777:   if (npoints > 2) PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints - 2, alpha + 1., beta + 1., &x[1], &w[1], newton));
    1778:   for (i = 1; i < npoints - 1; i++) w[i] /= (1. - x[i] * x[i]);
    1779:   PetscCall(PetscDTGaussLobattoJacobiEndweights_Internal(npoints, alpha, beta, &w[0], &w[npoints - 1]));
    1780:   PetscFunctionReturn(PETSC_SUCCESS);
    1781: }

    1783: /*@
    1784:   PetscDTGaussLobattoJacobiQuadrature - quadrature for the interval $[a, b]$ with the weight function
    1785:   $(x-a)^\alpha (x-b)^\beta$, with endpoints `a` and `b` included as quadrature points.

    1787:   Not Collective

    1789:   Input Parameters:
    1790: + npoints - the number of points in the quadrature rule
    1791: . a       - the left endpoint of the interval
    1792: . b       - the right endpoint of the interval
    1793: . alpha   - the left exponent
    1794: - beta    - the right exponent

    1796:   Output Parameters:
    1797: + x - array of length `npoints`, the locations of the quadrature points
    1798: - w - array of length `npoints`, the weights of the quadrature points

    1800:   Level: intermediate

    1802:   Note:
    1803:   This quadrature rule is exact for polynomials up to degree 2*`npoints` - 3.

    1805: .seealso: `PetscDTGaussJacobiQuadrature()`
    1806: @*/
    1807: PetscErrorCode PetscDTGaussLobattoJacobiQuadrature(PetscInt npoints, PetscReal a, PetscReal b, PetscReal alpha, PetscReal beta, PetscReal x[], PetscReal w[])
    1808: {
    1809:   PetscInt i;

    1811:   PetscFunctionBegin;
    1812:   PetscCall(PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, alpha, beta, x, w, PetscDTGaussQuadratureNewton_Internal));
    1813:   if (a != -1. || b != 1.) { /* shift */
    1814:     for (i = 0; i < npoints; i++) {
    1815:       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
    1816:       w[i] *= (b - a) / 2.;
    1817:     }
    1818:   }
    1819:   PetscFunctionReturn(PETSC_SUCCESS);
    1820: }

    1822: /*@
    1823:   PetscDTGaussQuadrature - create Gauss-Legendre quadrature

    1825:   Not Collective

    1827:   Input Parameters:
    1828: + npoints - number of points
    1829: . a       - left end of interval (often-1)
    1830: - b       - right end of interval (often +1)

    1832:   Output Parameters:
    1833: + x - quadrature points
    1834: - w - quadrature weights

    1836:   Level: intermediate

    1838:   Note:
    1839:   See {cite}`golub1969calculation`

    1841: .seealso: `PetscDTLegendreEval()`, `PetscDTGaussJacobiQuadrature()`
    1842: @*/
    1843: PetscErrorCode PetscDTGaussQuadrature(PetscInt npoints, PetscReal a, PetscReal b, PetscReal x[], PetscReal w[])
    1844: {
    1845:   PetscInt i;

    1847:   PetscFunctionBegin;
    1848:   PetscCall(PetscDTGaussJacobiQuadrature_Internal(npoints, 0., 0., x, w, PetscDTGaussQuadratureNewton_Internal));
    1849:   if (a != -1. || b != 1.) { /* shift */
    1850:     for (i = 0; i < npoints; i++) {
    1851:       x[i] = (x[i] + 1.) * ((b - a) / 2.) + a;
    1852:       w[i] *= (b - a) / 2.;
    1853:     }
    1854:   }
    1855:   PetscFunctionReturn(PETSC_SUCCESS);
    1856: }

    1858: /*@
    1859:   PetscDTGaussLobattoLegendreQuadrature - creates a set of the locations and weights of the Gauss-Lobatto-Legendre
    1860:   nodes of a given size on the domain $[-1,1]$

    1862:   Not Collective

    1864:   Input Parameters:
    1865: + npoints - number of grid nodes
    1866: - type    - `PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA` or `PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON`

    1868:   Output Parameters:
    1869: + x - quadrature points, pass in an array of length `npoints`
    1870: - w - quadrature weights, pass in an array of length `npoints`

    1872:   Level: intermediate

    1874:   Notes:
    1875:   For n > 30  the Newton approach computes duplicate (incorrect) values for some nodes because the initial guess is apparently not
    1876:   close enough to the desired solution

    1878:   These are useful for implementing spectral methods based on Gauss-Lobatto-Legendre (GLL) nodes

    1880:   See <https://epubs.siam.org/doi/abs/10.1137/110855442>  <https://epubs.siam.org/doi/abs/10.1137/120889873> for better ways to compute GLL nodes

    1882: .seealso: `PetscDTGaussQuadrature()`, `PetscGaussLobattoLegendreCreateType`

    1884: @*/
    1885: PetscErrorCode PetscDTGaussLobattoLegendreQuadrature(PetscInt npoints, PetscGaussLobattoLegendreCreateType type, PetscReal x[], PetscReal w[])
    1886: {
    1887:   PetscBool newton;

    1889:   PetscFunctionBegin;
    1890:   PetscCheck(npoints >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must provide at least 2 grid points per element");
    1891:   newton = (PetscBool)(type == PETSCGAUSSLOBATTOLEGENDRE_VIA_NEWTON);
    1892:   PetscCall(PetscDTGaussLobattoJacobiQuadrature_Internal(npoints, 0., 0., x, w, newton));
    1893:   PetscFunctionReturn(PETSC_SUCCESS);
    1894: }

    1896: /*@
    1897:   PetscDTGaussTensorQuadrature - creates a tensor-product Gauss quadrature

    1899:   Not Collective

    1901:   Input Parameters:
    1902: + dim     - The spatial dimension
    1903: . Nc      - The number of components
    1904: . npoints - number of points in one dimension
    1905: . a       - left end of interval (often-1)
    1906: - b       - right end of interval (often +1)

    1908:   Output Parameter:
    1909: . q - A `PetscQuadrature` object

    1911:   Level: intermediate

    1913: .seealso: `PetscDTGaussQuadrature()`, `PetscDTLegendreEval()`
    1914: @*/
    1915: PetscErrorCode PetscDTGaussTensorQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
    1916: {
    1917:   DMPolytopeType ct;
    1918:   PetscInt       totpoints = dim > 1 ? dim > 2 ? npoints * PetscSqr(npoints) : PetscSqr(npoints) : npoints;
    1919:   PetscReal     *x, *w, *xw, *ww;

    1921:   PetscFunctionBegin;
    1922:   PetscCall(PetscMalloc1(totpoints * dim, &x));
    1923:   PetscCall(PetscMalloc1(totpoints * Nc, &w));
    1924:   /* Set up the Golub-Welsch system */
    1925:   switch (dim) {
    1926:   case 0:
    1927:     ct = DM_POLYTOPE_POINT;
    1928:     PetscCall(PetscFree(x));
    1929:     PetscCall(PetscFree(w));
    1930:     PetscCall(PetscMalloc1(1, &x));
    1931:     PetscCall(PetscMalloc1(Nc, &w));
    1932:     totpoints = 1;
    1933:     x[0]      = 0.0;
    1934:     for (PetscInt c = 0; c < Nc; ++c) w[c] = 1.0;
    1935:     break;
    1936:   case 1:
    1937:     ct = DM_POLYTOPE_SEGMENT;
    1938:     PetscCall(PetscMalloc1(npoints, &ww));
    1939:     PetscCall(PetscDTGaussQuadrature(npoints, a, b, x, ww));
    1940:     for (PetscInt i = 0; i < npoints; ++i)
    1941:       for (PetscInt c = 0; c < Nc; ++c) w[i * Nc + c] = ww[i];
    1942:     PetscCall(PetscFree(ww));
    1943:     break;
    1944:   case 2:
    1945:     ct = DM_POLYTOPE_QUADRILATERAL;
    1946:     PetscCall(PetscMalloc2(npoints, &xw, npoints, &ww));
    1947:     PetscCall(PetscDTGaussQuadrature(npoints, a, b, xw, ww));
    1948:     for (PetscInt i = 0; i < npoints; ++i) {
    1949:       for (PetscInt j = 0; j < npoints; ++j) {
    1950:         x[(i * npoints + j) * dim + 0] = xw[i];
    1951:         x[(i * npoints + j) * dim + 1] = xw[j];
    1952:         for (PetscInt c = 0; c < Nc; ++c) w[(i * npoints + j) * Nc + c] = ww[i] * ww[j];
    1953:       }
    1954:     }
    1955:     PetscCall(PetscFree2(xw, ww));
    1956:     break;
    1957:   case 3:
    1958:     ct = DM_POLYTOPE_HEXAHEDRON;
    1959:     PetscCall(PetscMalloc2(npoints, &xw, npoints, &ww));
    1960:     PetscCall(PetscDTGaussQuadrature(npoints, a, b, xw, ww));
    1961:     for (PetscInt i = 0; i < npoints; ++i) {
    1962:       for (PetscInt j = 0; j < npoints; ++j) {
    1963:         for (PetscInt k = 0; k < npoints; ++k) {
    1964:           x[((i * npoints + j) * npoints + k) * dim + 0] = xw[i];
    1965:           x[((i * npoints + j) * npoints + k) * dim + 1] = xw[j];
    1966:           x[((i * npoints + j) * npoints + k) * dim + 2] = xw[k];
    1967:           for (PetscInt c = 0; c < Nc; ++c) w[((i * npoints + j) * npoints + k) * Nc + c] = ww[i] * ww[j] * ww[k];
    1968:         }
    1969:       }
    1970:     }
    1971:     PetscCall(PetscFree2(xw, ww));
    1972:     break;
    1973:   default:
    1974:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %" PetscInt_FMT, dim);
    1975:   }
    1976:   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
    1977:   PetscCall(PetscQuadratureSetCellType(*q, ct));
    1978:   PetscCall(PetscQuadratureSetOrder(*q, 2 * npoints - 1));
    1979:   PetscCall(PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w));
    1980:   PetscCall(PetscObjectChangeTypeName((PetscObject)*q, "GaussTensor"));
    1981:   PetscFunctionReturn(PETSC_SUCCESS);
    1982: }

    1984: /*@
    1985:   PetscDTStroudConicalQuadrature - create Stroud conical quadrature for a simplex {cite}`karniadakis2005spectral`

    1987:   Not Collective

    1989:   Input Parameters:
    1990: + dim     - The simplex dimension
    1991: . Nc      - The number of components
    1992: . npoints - The number of points in one dimension
    1993: . a       - left end of interval (often-1)
    1994: - b       - right end of interval (often +1)

    1996:   Output Parameter:
    1997: . q - A `PetscQuadrature` object

    1999:   Level: intermediate

    2001:   Note:
    2002:   For `dim` == 1, this is Gauss-Legendre quadrature

    2004: .seealso: `PetscDTGaussTensorQuadrature()`, `PetscDTGaussQuadrature()`
    2005: @*/
    2006: PetscErrorCode PetscDTStroudConicalQuadrature(PetscInt dim, PetscInt Nc, PetscInt npoints, PetscReal a, PetscReal b, PetscQuadrature *q)
    2007: {
    2008:   DMPolytopeType ct;
    2009:   PetscInt       totpoints;
    2010:   PetscReal     *p1, *w1;
    2011:   PetscReal     *x, *w;

    2013:   PetscFunctionBegin;
    2014:   PetscCheck(!(a != -1.0) && !(b != 1.0), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must use default internal right now");
    2015:   switch (dim) {
    2016:   case 0:
    2017:     ct = DM_POLYTOPE_POINT;
    2018:     break;
    2019:   case 1:
    2020:     ct = DM_POLYTOPE_SEGMENT;
    2021:     break;
    2022:   case 2:
    2023:     ct = DM_POLYTOPE_TRIANGLE;
    2024:     break;
    2025:   case 3:
    2026:     ct = DM_POLYTOPE_TETRAHEDRON;
    2027:     break;
    2028:   default:
    2029:     ct = DM_POLYTOPE_UNKNOWN;
    2030:   }
    2031:   totpoints = 1;
    2032:   for (PetscInt i = 0; i < dim; ++i) totpoints *= npoints;
    2033:   PetscCall(PetscMalloc1(totpoints * dim, &x));
    2034:   PetscCall(PetscMalloc1(totpoints * Nc, &w));
    2035:   PetscCall(PetscMalloc2(npoints, &p1, npoints, &w1));
    2036:   for (PetscInt i = 0; i < totpoints * Nc; ++i) w[i] = 1.;
    2037:   for (PetscInt i = 0, totprev = 1, totrem = totpoints / npoints; i < dim; ++i) {
    2038:     PetscReal mul;

    2040:     mul = PetscPowReal(2., -i);
    2041:     PetscCall(PetscDTGaussJacobiQuadrature(npoints, -1., 1., i, 0.0, p1, w1));
    2042:     for (PetscInt pt = 0, l = 0; l < totprev; l++) {
    2043:       for (PetscInt j = 0; j < npoints; j++) {
    2044:         for (PetscInt m = 0; m < totrem; m++, pt++) {
    2045:           for (PetscInt k = 0; k < i; k++) x[pt * dim + k] = (x[pt * dim + k] + 1.) * (1. - p1[j]) * 0.5 - 1.;
    2046:           x[pt * dim + i] = p1[j];
    2047:           for (PetscInt c = 0; c < Nc; c++) w[pt * Nc + c] *= mul * w1[j];
    2048:         }
    2049:       }
    2050:     }
    2051:     totprev *= npoints;
    2052:     totrem /= npoints;
    2053:   }
    2054:   PetscCall(PetscFree2(p1, w1));
    2055:   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
    2056:   PetscCall(PetscQuadratureSetCellType(*q, ct));
    2057:   PetscCall(PetscQuadratureSetOrder(*q, 2 * npoints - 1));
    2058:   PetscCall(PetscQuadratureSetData(*q, dim, Nc, totpoints, x, w));
    2059:   PetscCall(PetscObjectChangeTypeName((PetscObject)*q, "StroudConical"));
    2060:   PetscFunctionReturn(PETSC_SUCCESS);
    2061: }

    2063: static PetscBool MinSymTriQuadCite       = PETSC_FALSE;
    2064: const char       MinSymTriQuadCitation[] = "@article{WitherdenVincent2015,\n"
    2065:                                            "  title = {On the identification of symmetric quadrature rules for finite element methods},\n"
    2066:                                            "  journal = {Computers & Mathematics with Applications},\n"
    2067:                                            "  volume = {69},\n"
    2068:                                            "  number = {10},\n"
    2069:                                            "  pages = {1232-1241},\n"
    2070:                                            "  year = {2015},\n"
    2071:                                            "  issn = {0898-1221},\n"
    2072:                                            "  doi = {10.1016/j.camwa.2015.03.017},\n"
    2073:                                            "  url = {https://www.sciencedirect.com/science/article/pii/S0898122115001224},\n"
    2074:                                            "  author = {F.D. Witherden and P.E. Vincent},\n"
    2075:                                            "}\n";

    2077: #include "petscdttriquadrules.h"

    2079: static PetscBool MinSymTetQuadCite       = PETSC_FALSE;
    2080: const char       MinSymTetQuadCitation[] = "@article{JaskowiecSukumar2021\n"
    2081:                                            "  author = {Jaskowiec, Jan and Sukumar, N.},\n"
    2082:                                            "  title = {High-order symmetric cubature rules for tetrahedra and pyramids},\n"
    2083:                                            "  journal = {International Journal for Numerical Methods in Engineering},\n"
    2084:                                            "  volume = {122},\n"
    2085:                                            "  number = {1},\n"
    2086:                                            "  pages = {148-171},\n"
    2087:                                            "  doi = {10.1002/nme.6528},\n"
    2088:                                            "  url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/nme.6528},\n"
    2089:                                            "  eprint = {https://onlinelibrary.wiley.com/doi/pdf/10.1002/nme.6528},\n"
    2090:                                            "  year = {2021}\n"
    2091:                                            "}\n";

    2093: #include "petscdttetquadrules.h"

    2095: // https://en.wikipedia.org/wiki/Partition_(number_theory)
    2096: static PetscErrorCode PetscDTPartitionNumber(PetscInt n, PetscInt *p)
    2097: {
    2098:   // sequence A000041 in the OEIS
    2099:   const PetscInt partition[]   = {1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42, 56, 77, 101, 135, 176, 231, 297, 385, 490, 627, 792, 1002, 1255, 1575, 1958, 2436, 3010, 3718, 4565, 5604};
    2100:   PetscInt       tabulated_max = PETSC_STATIC_ARRAY_LENGTH(partition) - 1;

    2102:   PetscFunctionBegin;
    2103:   PetscCheck(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Partition number not defined for negative number %" PetscInt_FMT, n);
    2104:   // not implementing the pentagonal number recurrence, we don't need partition numbers for n that high
    2105:   PetscCheck(n <= tabulated_max, PETSC_COMM_SELF, PETSC_ERR_SUP, "Partition numbers only tabulated up to %" PetscInt_FMT ", not computed for %" PetscInt_FMT, tabulated_max, n);
    2106:   *p = partition[n];
    2107:   PetscFunctionReturn(PETSC_SUCCESS);
    2108: }

    2110: /*@
    2111:   PetscDTSimplexQuadrature - Create a quadrature rule for a simplex that exactly integrates polynomials up to a given degree.

    2113:   Not Collective

    2115:   Input Parameters:
    2116: + dim    - The spatial dimension of the simplex (1 = segment, 2 = triangle, 3 = tetrahedron)
    2117: . degree - The largest polynomial degree that is required to be integrated exactly
    2118: - type   - left end of interval (often-1)

    2120:   Output Parameter:
    2121: . quad - A `PetscQuadrature` object for integration over the biunit simplex
    2122:             (defined by the bounds $x_i >= -1$ and $\sum_i x_i <= 2 - d$) that is exact for
    2123:             polynomials up to the given degree

    2125:   Level: intermediate

    2127: .seealso: `PetscDTSimplexQuadratureType`, `PetscDTGaussQuadrature()`, `PetscDTStroudCononicalQuadrature()`, `PetscQuadrature`
    2128: @*/
    2129: PetscErrorCode PetscDTSimplexQuadrature(PetscInt dim, PetscInt degree, PetscDTSimplexQuadratureType type, PetscQuadrature *quad)
    2130: {
    2131:   PetscDTSimplexQuadratureType orig_type = type;

    2133:   PetscFunctionBegin;
    2134:   PetscCheck(dim >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative dimension %" PetscInt_FMT, dim);
    2135:   PetscCheck(degree >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Negative degree %" PetscInt_FMT, degree);
    2136:   if (type == PETSCDTSIMPLEXQUAD_DEFAULT) type = PETSCDTSIMPLEXQUAD_MINSYM;
    2137:   if (type == PETSCDTSIMPLEXQUAD_CONIC || dim < 2) {
    2138:     PetscInt points_per_dim = (degree + 2) / 2; // ceil((degree + 1) / 2);
    2139:     PetscCall(PetscDTStroudConicalQuadrature(dim, 1, points_per_dim, -1, 1, quad));
    2140:   } else {
    2141:     DMPolytopeType    ct;
    2142:     PetscInt          n    = dim + 1;
    2143:     PetscInt          fact = 1;
    2144:     PetscInt         *part, *perm;
    2145:     PetscInt          p = 0;
    2146:     PetscInt          max_degree;
    2147:     const PetscInt   *nodes_per_type     = NULL;
    2148:     const PetscInt   *all_num_full_nodes = NULL;
    2149:     const PetscReal **weights_list       = NULL;
    2150:     const PetscReal **compact_nodes_list = NULL;
    2151:     const char       *citation           = NULL;
    2152:     PetscBool        *cited              = NULL;

    2154:     switch (dim) {
    2155:     case 0:
    2156:       ct = DM_POLYTOPE_POINT;
    2157:       break;
    2158:     case 1:
    2159:       ct = DM_POLYTOPE_SEGMENT;
    2160:       break;
    2161:     case 2:
    2162:       ct = DM_POLYTOPE_TRIANGLE;
    2163:       break;
    2164:     case 3:
    2165:       ct = DM_POLYTOPE_TETRAHEDRON;
    2166:       break;
    2167:     default:
    2168:       ct = DM_POLYTOPE_UNKNOWN;
    2169:     }
    2170:     switch (dim) {
    2171:     case 2:
    2172:       cited              = &MinSymTriQuadCite;
    2173:       citation           = MinSymTriQuadCitation;
    2174:       max_degree         = PetscDTWVTriQuad_max_degree;
    2175:       nodes_per_type     = PetscDTWVTriQuad_num_orbits;
    2176:       all_num_full_nodes = PetscDTWVTriQuad_num_nodes;
    2177:       weights_list       = PetscDTWVTriQuad_weights;
    2178:       compact_nodes_list = PetscDTWVTriQuad_orbits;
    2179:       break;
    2180:     case 3:
    2181:       cited              = &MinSymTetQuadCite;
    2182:       citation           = MinSymTetQuadCitation;
    2183:       max_degree         = PetscDTJSTetQuad_max_degree;
    2184:       nodes_per_type     = PetscDTJSTetQuad_num_orbits;
    2185:       all_num_full_nodes = PetscDTJSTetQuad_num_nodes;
    2186:       weights_list       = PetscDTJSTetQuad_weights;
    2187:       compact_nodes_list = PetscDTJSTetQuad_orbits;
    2188:       break;
    2189:     default:
    2190:       max_degree = -1;
    2191:       break;
    2192:     }

    2194:     if (degree > max_degree) {
    2195:       if (orig_type == PETSCDTSIMPLEXQUAD_DEFAULT) {
    2196:         // fall back to conic
    2197:         PetscCall(PetscDTSimplexQuadrature(dim, degree, PETSCDTSIMPLEXQUAD_CONIC, quad));
    2198:         PetscFunctionReturn(PETSC_SUCCESS);
    2199:       } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Minimal symmetric quadrature for dim %" PetscInt_FMT ", degree %" PetscInt_FMT " unsupported", dim, degree);
    2200:     }

    2202:     PetscCall(PetscCitationsRegister(citation, cited));

    2204:     PetscCall(PetscDTPartitionNumber(n, &p));
    2205:     for (PetscInt d = 2; d <= n; d++) fact *= d;

    2207:     PetscInt         num_full_nodes      = all_num_full_nodes[degree];
    2208:     const PetscReal *all_compact_nodes   = compact_nodes_list[degree];
    2209:     const PetscReal *all_compact_weights = weights_list[degree];
    2210:     nodes_per_type                       = &nodes_per_type[p * degree];

    2212:     PetscReal      *points;
    2213:     PetscReal      *counts;
    2214:     PetscReal      *weights;
    2215:     PetscReal      *bary_to_biunit; // row-major transformation of barycentric coordinate to biunit
    2216:     PetscQuadrature q;

    2218:     // compute the transformation
    2219:     PetscCall(PetscMalloc1(n * dim, &bary_to_biunit));
    2220:     for (PetscInt d = 0; d < dim; d++) {
    2221:       for (PetscInt b = 0; b < n; b++) bary_to_biunit[d * n + b] = (d == b) ? 1.0 : -1.0;
    2222:     }

    2224:     PetscCall(PetscMalloc3(n, &part, n, &perm, n, &counts));
    2225:     PetscCall(PetscCalloc1(num_full_nodes * dim, &points));
    2226:     PetscCall(PetscMalloc1(num_full_nodes, &weights));

    2228:     // (0, 0, ...) is the first partition lexicographically
    2229:     PetscCall(PetscArrayzero(part, n));
    2230:     PetscCall(PetscArrayzero(counts, n));
    2231:     counts[0] = n;

    2233:     // for each partition
    2234:     for (PetscInt s = 0, node_offset = 0; s < p; s++) {
    2235:       PetscInt num_compact_coords = part[n - 1] + 1;

    2237:       const PetscReal *compact_nodes   = all_compact_nodes;
    2238:       const PetscReal *compact_weights = all_compact_weights;
    2239:       all_compact_nodes += num_compact_coords * nodes_per_type[s];
    2240:       all_compact_weights += nodes_per_type[s];

    2242:       // for every permutation of the vertices
    2243:       for (PetscInt f = 0; f < fact; f++) {
    2244:         PetscCall(PetscDTEnumPerm(n, f, perm, NULL));

    2246:         // check if it is a valid permutation
    2247:         PetscInt digit;
    2248:         for (digit = 1; digit < n; digit++) {
    2249:           // skip permutations that would duplicate a node because it has a smaller symmetry group
    2250:           if (part[digit - 1] == part[digit] && perm[digit - 1] > perm[digit]) break;
    2251:         }
    2252:         if (digit < n) continue;

    2254:         // create full nodes from this permutation of the compact nodes
    2255:         PetscReal *full_nodes   = &points[node_offset * dim];
    2256:         PetscReal *full_weights = &weights[node_offset];

    2258:         PetscCall(PetscArraycpy(full_weights, compact_weights, nodes_per_type[s]));
    2259:         for (PetscInt b = 0; b < n; b++) {
    2260:           for (PetscInt d = 0; d < dim; d++) {
    2261:             for (PetscInt node = 0; node < nodes_per_type[s]; node++) full_nodes[node * dim + d] += bary_to_biunit[d * n + perm[b]] * compact_nodes[node * num_compact_coords + part[b]];
    2262:           }
    2263:         }
    2264:         node_offset += nodes_per_type[s];
    2265:       }

    2267:       if (s < p - 1) { // Generate the next partition
    2268:         /* A partition is described by the number of coordinates that are in
    2269:          * each set of duplicates (counts) and redundantly by mapping each
    2270:          * index to its set of duplicates (part)
    2271:          *
    2272:          * Counts should always be in nonincreasing order
    2273:          *
    2274:          * We want to generate the partitions lexically by part, which means
    2275:          * finding the last index where count > 1 and reducing by 1.
    2276:          *
    2277:          * For the new counts beyond that index, we eagerly assign the remaining
    2278:          * capacity of the partition to smaller indices (ensures lexical ordering),
    2279:          * while respecting the nonincreasing invariant of the counts
    2280:          */
    2281:         PetscInt last_digit            = part[n - 1];
    2282:         PetscInt last_digit_with_extra = last_digit;
    2283:         while (counts[last_digit_with_extra] == 1) last_digit_with_extra--;
    2284:         PetscInt limit               = --counts[last_digit_with_extra];
    2285:         PetscInt total_to_distribute = last_digit - last_digit_with_extra + 1;
    2286:         for (PetscInt digit = last_digit_with_extra + 1; digit < n; digit++) {
    2287:           counts[digit] = PetscMin(limit, total_to_distribute);
    2288:           total_to_distribute -= PetscMin(limit, total_to_distribute);
    2289:         }
    2290:         for (PetscInt digit = 0, offset = 0; digit < n; digit++) {
    2291:           PetscInt count = counts[digit];
    2292:           for (PetscInt c = 0; c < count; c++) part[offset++] = digit;
    2293:         }
    2294:       }
    2295:     }
    2296:     PetscCall(PetscFree3(part, perm, counts));
    2297:     PetscCall(PetscFree(bary_to_biunit));
    2298:     PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, &q));
    2299:     PetscCall(PetscQuadratureSetCellType(q, ct));
    2300:     PetscCall(PetscQuadratureSetOrder(q, degree));
    2301:     PetscCall(PetscQuadratureSetData(q, dim, 1, num_full_nodes, points, weights));
    2302:     *quad = q;
    2303:   }
    2304:   PetscFunctionReturn(PETSC_SUCCESS);
    2305: }

    2307: /*@
    2308:   PetscDTTanhSinhTensorQuadrature - create tanh-sinh quadrature for a tensor product cell

    2310:   Not Collective

    2312:   Input Parameters:
    2313: + dim   - The cell dimension
    2314: . level - The number of points in one dimension, $2^l$
    2315: . a     - left end of interval (often-1)
    2316: - b     - right end of interval (often +1)

    2318:   Output Parameter:
    2319: . q - A `PetscQuadrature` object

    2321:   Level: intermediate

    2323: .seealso: `PetscDTGaussTensorQuadrature()`, `PetscQuadrature`
    2324: @*/
    2325: PetscErrorCode PetscDTTanhSinhTensorQuadrature(PetscInt dim, PetscInt level, PetscReal a, PetscReal b, PetscQuadrature *q)
    2326: {
    2327:   DMPolytopeType  ct;
    2328:   const PetscInt  p     = 16;                        /* Digits of precision in the evaluation */
    2329:   const PetscReal alpha = (b - a) / 2.;              /* Half-width of the integration interval */
    2330:   const PetscReal beta  = (b + a) / 2.;              /* Center of the integration interval */
    2331:   const PetscReal h     = PetscPowReal(2.0, -level); /* Step size, length between x_k */
    2332:   PetscReal       xk;                                /* Quadrature point x_k on reference domain [-1, 1] */
    2333:   PetscReal       wk = 0.5 * PETSC_PI;               /* Quadrature weight at x_k */
    2334:   PetscReal      *x, *w;
    2335:   PetscInt        K, k, npoints;

    2337:   PetscFunctionBegin;
    2338:   PetscCheck(dim <= 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Dimension %" PetscInt_FMT " not yet implemented", dim);
    2339:   PetscCheck(level, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a number of significant digits");
    2340:   switch (dim) {
    2341:   case 0:
    2342:     ct = DM_POLYTOPE_POINT;
    2343:     break;
    2344:   case 1:
    2345:     ct = DM_POLYTOPE_SEGMENT;
    2346:     break;
    2347:   case 2:
    2348:     ct = DM_POLYTOPE_QUADRILATERAL;
    2349:     break;
    2350:   case 3:
    2351:     ct = DM_POLYTOPE_HEXAHEDRON;
    2352:     break;
    2353:   default:
    2354:     ct = DM_POLYTOPE_UNKNOWN;
    2355:   }
    2356:   /* Find K such that the weights are < 32 digits of precision */
    2357:   for (K = 1; PetscAbsReal(PetscLog10Real(wk)) < 2 * p; ++K) wk = 0.5 * h * PETSC_PI * PetscCoshReal(K * h) / PetscSqr(PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(K * h)));
    2358:   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
    2359:   PetscCall(PetscQuadratureSetCellType(*q, ct));
    2360:   PetscCall(PetscQuadratureSetOrder(*q, 2 * K + 1));
    2361:   npoints = 2 * K - 1;
    2362:   PetscCall(PetscMalloc1(npoints * dim, &x));
    2363:   PetscCall(PetscMalloc1(npoints, &w));
    2364:   /* Center term */
    2365:   x[0] = beta;
    2366:   w[0] = 0.5 * alpha * PETSC_PI;
    2367:   for (k = 1; k < K; ++k) {
    2368:     wk           = 0.5 * alpha * h * PETSC_PI * PetscCoshReal(k * h) / PetscSqr(PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(k * h)));
    2369:     xk           = PetscTanhReal(0.5 * PETSC_PI * PetscSinhReal(k * h));
    2370:     x[2 * k - 1] = -alpha * xk + beta;
    2371:     w[2 * k - 1] = wk;
    2372:     x[2 * k + 0] = alpha * xk + beta;
    2373:     w[2 * k + 0] = wk;
    2374:   }
    2375:   PetscCall(PetscQuadratureSetData(*q, dim, 1, npoints, x, w));
    2376:   PetscFunctionReturn(PETSC_SUCCESS);
    2377: }

    2379: PetscErrorCode PetscDTTanhSinhIntegrate(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, void *ctx, PetscReal *sol)
    2380: {
    2381:   const PetscInt  p     = 16;           /* Digits of precision in the evaluation */
    2382:   const PetscReal alpha = (b - a) / 2.; /* Half-width of the integration interval */
    2383:   const PetscReal beta  = (b + a) / 2.; /* Center of the integration interval */
    2384:   PetscReal       h     = 1.0;          /* Step size, length between x_k */
    2385:   PetscInt        l     = 0;            /* Level of refinement, h = 2^{-l} */
    2386:   PetscReal       osum  = 0.0;          /* Integral on last level */
    2387:   PetscReal       psum  = 0.0;          /* Integral on the level before the last level */
    2388:   PetscReal       sum;                  /* Integral on current level */
    2389:   PetscReal       yk;                   /* Quadrature point 1 - x_k on reference domain [-1, 1] */
    2390:   PetscReal       lx, rx;               /* Quadrature points to the left and right of 0 on the real domain [a, b] */
    2391:   PetscReal       wk;                   /* Quadrature weight at x_k */
    2392:   PetscReal       lval, rval;           /* Terms in the quadature sum to the left and right of 0 */
    2393:   PetscInt        d;                    /* Digits of precision in the integral */

    2395:   PetscFunctionBegin;
    2396:   PetscCheck(digits > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
    2397:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    2398:   /* Center term */
    2399:   func(&beta, ctx, &lval);
    2400:   sum = 0.5 * alpha * PETSC_PI * lval;
    2401:   /* */
    2402:   do {
    2403:     PetscReal lterm, rterm, maxTerm = 0.0, d1, d2, d3, d4;
    2404:     PetscInt  k = 1;

    2406:     ++l;
    2407:     /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %" PetscInt_FMT " sum: %15.15f\n", l, sum); */
    2408:     /* At each level of refinement, h --> h/2 and sum --> sum/2 */
    2409:     psum = osum;
    2410:     osum = sum;
    2411:     h *= 0.5;
    2412:     sum *= 0.5;
    2413:     do {
    2414:       wk = 0.5 * h * PETSC_PI * PetscCoshReal(k * h) / PetscSqr(PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(k * h)));
    2415:       yk = 1.0 / (PetscExpReal(0.5 * PETSC_PI * PetscSinhReal(k * h)) * PetscCoshReal(0.5 * PETSC_PI * PetscSinhReal(k * h)));
    2416:       lx = -alpha * (1.0 - yk) + beta;
    2417:       rx = alpha * (1.0 - yk) + beta;
    2418:       func(&lx, ctx, &lval);
    2419:       func(&rx, ctx, &rval);
    2420:       lterm   = alpha * wk * lval;
    2421:       maxTerm = PetscMax(PetscAbsReal(lterm), maxTerm);
    2422:       sum += lterm;
    2423:       rterm   = alpha * wk * rval;
    2424:       maxTerm = PetscMax(PetscAbsReal(rterm), maxTerm);
    2425:       sum += rterm;
    2426:       ++k;
    2427:       /* Only need to evaluate every other point on refined levels */
    2428:       if (l != 1) ++k;
    2429:     } while (PetscAbsReal(PetscLog10Real(wk)) < p); /* Only need to evaluate sum until weights are < 32 digits of precision */

    2431:     d1 = PetscLog10Real(PetscAbsReal(sum - osum));
    2432:     d2 = PetscLog10Real(PetscAbsReal(sum - psum));
    2433:     d3 = PetscLog10Real(maxTerm) - p;
    2434:     if (PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)) == 0.0) d4 = 0.0;
    2435:     else d4 = PetscLog10Real(PetscMax(PetscAbsReal(lterm), PetscAbsReal(rterm)));
    2436:     d = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1) / d2, 2 * d1), d3), d4)));
    2437:   } while (d < digits && l < 12);
    2438:   *sol = sum;
    2439:   PetscCall(PetscFPTrapPop());
    2440:   PetscFunctionReturn(PETSC_SUCCESS);
    2441: }

    2443: #if defined(PETSC_HAVE_MPFR)
    2444: PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, void *ctx, PetscReal *sol)
    2445: {
    2446:   const PetscInt safetyFactor = 2; /* Calculate abcissa until 2*p digits */
    2447:   PetscInt       l            = 0; /* Level of refinement, h = 2^{-l} */
    2448:   mpfr_t         alpha;            /* Half-width of the integration interval */
    2449:   mpfr_t         beta;             /* Center of the integration interval */
    2450:   mpfr_t         h;                /* Step size, length between x_k */
    2451:   mpfr_t         osum;             /* Integral on last level */
    2452:   mpfr_t         psum;             /* Integral on the level before the last level */
    2453:   mpfr_t         sum;              /* Integral on current level */
    2454:   mpfr_t         yk;               /* Quadrature point 1 - x_k on reference domain [-1, 1] */
    2455:   mpfr_t         lx, rx;           /* Quadrature points to the left and right of 0 on the real domain [a, b] */
    2456:   mpfr_t         wk;               /* Quadrature weight at x_k */
    2457:   PetscReal      lval, rval, rtmp; /* Terms in the quadature sum to the left and right of 0 */
    2458:   PetscInt       d;                /* Digits of precision in the integral */
    2459:   mpfr_t         pi2, kh, msinh, mcosh, maxTerm, curTerm, tmp;

    2461:   PetscFunctionBegin;
    2462:   PetscCheck(digits > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Must give a positive number of significant digits");
    2463:   /* Create high precision storage */
    2464:   mpfr_inits2(PetscCeilReal(safetyFactor * digits * PetscLogReal(10.) / PetscLogReal(2.)), alpha, beta, h, sum, osum, psum, yk, wk, lx, rx, tmp, maxTerm, curTerm, pi2, kh, msinh, mcosh, NULL);
    2465:   /* Initialization */
    2466:   mpfr_set_d(alpha, 0.5 * (b - a), MPFR_RNDN);
    2467:   mpfr_set_d(beta, 0.5 * (b + a), MPFR_RNDN);
    2468:   mpfr_set_d(osum, 0.0, MPFR_RNDN);
    2469:   mpfr_set_d(psum, 0.0, MPFR_RNDN);
    2470:   mpfr_set_d(h, 1.0, MPFR_RNDN);
    2471:   mpfr_const_pi(pi2, MPFR_RNDN);
    2472:   mpfr_mul_d(pi2, pi2, 0.5, MPFR_RNDN);
    2473:   /* Center term */
    2474:   rtmp = 0.5 * (b + a);
    2475:   func(&rtmp, ctx, &lval);
    2476:   mpfr_set(sum, pi2, MPFR_RNDN);
    2477:   mpfr_mul(sum, sum, alpha, MPFR_RNDN);
    2478:   mpfr_mul_d(sum, sum, lval, MPFR_RNDN);
    2479:   /* */
    2480:   do {
    2481:     PetscReal d1, d2, d3, d4;
    2482:     PetscInt  k = 1;

    2484:     ++l;
    2485:     mpfr_set_d(maxTerm, 0.0, MPFR_RNDN);
    2486:     /* PetscPrintf(PETSC_COMM_SELF, "LEVEL %" PetscInt_FMT " sum: %15.15f\n", l, sum); */
    2487:     /* At each level of refinement, h --> h/2 and sum --> sum/2 */
    2488:     mpfr_set(psum, osum, MPFR_RNDN);
    2489:     mpfr_set(osum, sum, MPFR_RNDN);
    2490:     mpfr_mul_d(h, h, 0.5, MPFR_RNDN);
    2491:     mpfr_mul_d(sum, sum, 0.5, MPFR_RNDN);
    2492:     do {
    2493:       mpfr_set_si(kh, k, MPFR_RNDN);
    2494:       mpfr_mul(kh, kh, h, MPFR_RNDN);
    2495:       /* Weight */
    2496:       mpfr_set(wk, h, MPFR_RNDN);
    2497:       mpfr_sinh_cosh(msinh, mcosh, kh, MPFR_RNDN);
    2498:       mpfr_mul(msinh, msinh, pi2, MPFR_RNDN);
    2499:       mpfr_mul(mcosh, mcosh, pi2, MPFR_RNDN);
    2500:       mpfr_cosh(tmp, msinh, MPFR_RNDN);
    2501:       mpfr_sqr(tmp, tmp, MPFR_RNDN);
    2502:       mpfr_mul(wk, wk, mcosh, MPFR_RNDN);
    2503:       mpfr_div(wk, wk, tmp, MPFR_RNDN);
    2504:       /* Abscissa */
    2505:       mpfr_set_d(yk, 1.0, MPFR_RNDZ);
    2506:       mpfr_cosh(tmp, msinh, MPFR_RNDN);
    2507:       mpfr_div(yk, yk, tmp, MPFR_RNDZ);
    2508:       mpfr_exp(tmp, msinh, MPFR_RNDN);
    2509:       mpfr_div(yk, yk, tmp, MPFR_RNDZ);
    2510:       /* Quadrature points */
    2511:       mpfr_sub_d(lx, yk, 1.0, MPFR_RNDZ);
    2512:       mpfr_mul(lx, lx, alpha, MPFR_RNDU);
    2513:       mpfr_add(lx, lx, beta, MPFR_RNDU);
    2514:       mpfr_d_sub(rx, 1.0, yk, MPFR_RNDZ);
    2515:       mpfr_mul(rx, rx, alpha, MPFR_RNDD);
    2516:       mpfr_add(rx, rx, beta, MPFR_RNDD);
    2517:       /* Evaluation */
    2518:       rtmp = mpfr_get_d(lx, MPFR_RNDU);
    2519:       func(&rtmp, ctx, &lval);
    2520:       rtmp = mpfr_get_d(rx, MPFR_RNDD);
    2521:       func(&rtmp, ctx, &rval);
    2522:       /* Update */
    2523:       mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
    2524:       mpfr_mul_d(tmp, tmp, lval, MPFR_RNDN);
    2525:       mpfr_add(sum, sum, tmp, MPFR_RNDN);
    2526:       mpfr_abs(tmp, tmp, MPFR_RNDN);
    2527:       mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
    2528:       mpfr_set(curTerm, tmp, MPFR_RNDN);
    2529:       mpfr_mul(tmp, wk, alpha, MPFR_RNDN);
    2530:       mpfr_mul_d(tmp, tmp, rval, MPFR_RNDN);
    2531:       mpfr_add(sum, sum, tmp, MPFR_RNDN);
    2532:       mpfr_abs(tmp, tmp, MPFR_RNDN);
    2533:       mpfr_max(maxTerm, maxTerm, tmp, MPFR_RNDN);
    2534:       mpfr_max(curTerm, curTerm, tmp, MPFR_RNDN);
    2535:       ++k;
    2536:       /* Only need to evaluate every other point on refined levels */
    2537:       if (l != 1) ++k;
    2538:       mpfr_log10(tmp, wk, MPFR_RNDN);
    2539:       mpfr_abs(tmp, tmp, MPFR_RNDN);
    2540:     } while (mpfr_get_d(tmp, MPFR_RNDN) < safetyFactor * digits); /* Only need to evaluate sum until weights are < 32 digits of precision */
    2541:     mpfr_sub(tmp, sum, osum, MPFR_RNDN);
    2542:     mpfr_abs(tmp, tmp, MPFR_RNDN);
    2543:     mpfr_log10(tmp, tmp, MPFR_RNDN);
    2544:     d1 = mpfr_get_d(tmp, MPFR_RNDN);
    2545:     mpfr_sub(tmp, sum, psum, MPFR_RNDN);
    2546:     mpfr_abs(tmp, tmp, MPFR_RNDN);
    2547:     mpfr_log10(tmp, tmp, MPFR_RNDN);
    2548:     d2 = mpfr_get_d(tmp, MPFR_RNDN);
    2549:     mpfr_log10(tmp, maxTerm, MPFR_RNDN);
    2550:     d3 = mpfr_get_d(tmp, MPFR_RNDN) - digits;
    2551:     mpfr_log10(tmp, curTerm, MPFR_RNDN);
    2552:     d4 = mpfr_get_d(tmp, MPFR_RNDN);
    2553:     d  = PetscAbsInt(PetscMin(0, PetscMax(PetscMax(PetscMax(PetscSqr(d1) / d2, 2 * d1), d3), d4)));
    2554:   } while (d < digits && l < 8);
    2555:   *sol = mpfr_get_d(sum, MPFR_RNDN);
    2556:   /* Cleanup */
    2557:   mpfr_clears(alpha, beta, h, sum, osum, psum, yk, wk, lx, rx, tmp, maxTerm, curTerm, pi2, kh, msinh, mcosh, NULL);
    2558:   PetscFunctionReturn(PETSC_SUCCESS);
    2559: }
    2560: #else

    2562: PetscErrorCode PetscDTTanhSinhIntegrateMPFR(void (*func)(const PetscReal[], void *, PetscReal *), PetscReal a, PetscReal b, PetscInt digits, void *ctx, PetscReal *sol)
    2563: {
    2564:   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "This method will not work without MPFR. Reconfigure using --download-mpfr --download-gmp");
    2565: }
    2566: #endif

    2568: /*@
    2569:   PetscDTTensorQuadratureCreate - create the tensor product quadrature from two lower-dimensional quadratures

    2571:   Not Collective

    2573:   Input Parameters:
    2574: + q1 - The first quadrature
    2575: - q2 - The second quadrature

    2577:   Output Parameter:
    2578: . q - A `PetscQuadrature` object

    2580:   Level: intermediate

    2582: .seealso: `PetscQuadrature`, `PetscDTGaussTensorQuadrature()`
    2583: @*/
    2584: PetscErrorCode PetscDTTensorQuadratureCreate(PetscQuadrature q1, PetscQuadrature q2, PetscQuadrature *q)
    2585: {
    2586:   DMPolytopeType   ct1, ct2, ct;
    2587:   const PetscReal *x1, *w1, *x2, *w2;
    2588:   PetscReal       *x, *w;
    2589:   PetscInt         dim1, Nc1, Np1, order1, qa, d1;
    2590:   PetscInt         dim2, Nc2, Np2, order2, qb, d2;
    2591:   PetscInt         dim, Nc, Np, order, qc, d;

    2593:   PetscFunctionBegin;
    2596:   PetscAssertPointer(q, 3);

    2598:   PetscCall(PetscQuadratureGetOrder(q1, &order1));
    2599:   PetscCall(PetscQuadratureGetOrder(q2, &order2));
    2600:   PetscCheck(order1 == order2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Order1 %" PetscInt_FMT " != %" PetscInt_FMT " Order2", order1, order2);
    2601:   PetscCall(PetscQuadratureGetData(q1, &dim1, &Nc1, &Np1, &x1, &w1));
    2602:   PetscCall(PetscQuadratureGetCellType(q1, &ct1));
    2603:   PetscCall(PetscQuadratureGetData(q2, &dim2, &Nc2, &Np2, &x2, &w2));
    2604:   PetscCall(PetscQuadratureGetCellType(q2, &ct2));
    2605:   PetscCheck(Nc1 == Nc2, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "NumComp1 %" PetscInt_FMT " != %" PetscInt_FMT " NumComp2", Nc1, Nc2);

    2607:   switch (ct1) {
    2608:   case DM_POLYTOPE_POINT:
    2609:     ct = ct2;
    2610:     break;
    2611:   case DM_POLYTOPE_SEGMENT:
    2612:     switch (ct2) {
    2613:     case DM_POLYTOPE_POINT:
    2614:       ct = ct1;
    2615:       break;
    2616:     case DM_POLYTOPE_SEGMENT:
    2617:       ct = DM_POLYTOPE_QUADRILATERAL;
    2618:       break;
    2619:     case DM_POLYTOPE_TRIANGLE:
    2620:       ct = DM_POLYTOPE_TRI_PRISM;
    2621:       break;
    2622:     case DM_POLYTOPE_QUADRILATERAL:
    2623:       ct = DM_POLYTOPE_HEXAHEDRON;
    2624:       break;
    2625:     case DM_POLYTOPE_TETRAHEDRON:
    2626:       ct = DM_POLYTOPE_UNKNOWN;
    2627:       break;
    2628:     case DM_POLYTOPE_HEXAHEDRON:
    2629:       ct = DM_POLYTOPE_UNKNOWN;
    2630:       break;
    2631:     default:
    2632:       ct = DM_POLYTOPE_UNKNOWN;
    2633:     }
    2634:     break;
    2635:   case DM_POLYTOPE_TRIANGLE:
    2636:     switch (ct2) {
    2637:     case DM_POLYTOPE_POINT:
    2638:       ct = ct1;
    2639:       break;
    2640:     case DM_POLYTOPE_SEGMENT:
    2641:       ct = DM_POLYTOPE_TRI_PRISM;
    2642:       break;
    2643:     case DM_POLYTOPE_TRIANGLE:
    2644:       ct = DM_POLYTOPE_UNKNOWN;
    2645:       break;
    2646:     case DM_POLYTOPE_QUADRILATERAL:
    2647:       ct = DM_POLYTOPE_UNKNOWN;
    2648:       break;
    2649:     case DM_POLYTOPE_TETRAHEDRON:
    2650:       ct = DM_POLYTOPE_UNKNOWN;
    2651:       break;
    2652:     case DM_POLYTOPE_HEXAHEDRON:
    2653:       ct = DM_POLYTOPE_UNKNOWN;
    2654:       break;
    2655:     default:
    2656:       ct = DM_POLYTOPE_UNKNOWN;
    2657:     }
    2658:     break;
    2659:   case DM_POLYTOPE_QUADRILATERAL:
    2660:     switch (ct2) {
    2661:     case DM_POLYTOPE_POINT:
    2662:       ct = ct1;
    2663:       break;
    2664:     case DM_POLYTOPE_SEGMENT:
    2665:       ct = DM_POLYTOPE_HEXAHEDRON;
    2666:       break;
    2667:     case DM_POLYTOPE_TRIANGLE:
    2668:       ct = DM_POLYTOPE_UNKNOWN;
    2669:       break;
    2670:     case DM_POLYTOPE_QUADRILATERAL:
    2671:       ct = DM_POLYTOPE_UNKNOWN;
    2672:       break;
    2673:     case DM_POLYTOPE_TETRAHEDRON:
    2674:       ct = DM_POLYTOPE_UNKNOWN;
    2675:       break;
    2676:     case DM_POLYTOPE_HEXAHEDRON:
    2677:       ct = DM_POLYTOPE_UNKNOWN;
    2678:       break;
    2679:     default:
    2680:       ct = DM_POLYTOPE_UNKNOWN;
    2681:     }
    2682:     break;
    2683:   case DM_POLYTOPE_TETRAHEDRON:
    2684:     switch (ct2) {
    2685:     case DM_POLYTOPE_POINT:
    2686:       ct = ct1;
    2687:       break;
    2688:     case DM_POLYTOPE_SEGMENT:
    2689:       ct = DM_POLYTOPE_UNKNOWN;
    2690:       break;
    2691:     case DM_POLYTOPE_TRIANGLE:
    2692:       ct = DM_POLYTOPE_UNKNOWN;
    2693:       break;
    2694:     case DM_POLYTOPE_QUADRILATERAL:
    2695:       ct = DM_POLYTOPE_UNKNOWN;
    2696:       break;
    2697:     case DM_POLYTOPE_TETRAHEDRON:
    2698:       ct = DM_POLYTOPE_UNKNOWN;
    2699:       break;
    2700:     case DM_POLYTOPE_HEXAHEDRON:
    2701:       ct = DM_POLYTOPE_UNKNOWN;
    2702:       break;
    2703:     default:
    2704:       ct = DM_POLYTOPE_UNKNOWN;
    2705:     }
    2706:     break;
    2707:   case DM_POLYTOPE_HEXAHEDRON:
    2708:     switch (ct2) {
    2709:     case DM_POLYTOPE_POINT:
    2710:       ct = ct1;
    2711:       break;
    2712:     case DM_POLYTOPE_SEGMENT:
    2713:       ct = DM_POLYTOPE_UNKNOWN;
    2714:       break;
    2715:     case DM_POLYTOPE_TRIANGLE:
    2716:       ct = DM_POLYTOPE_UNKNOWN;
    2717:       break;
    2718:     case DM_POLYTOPE_QUADRILATERAL:
    2719:       ct = DM_POLYTOPE_UNKNOWN;
    2720:       break;
    2721:     case DM_POLYTOPE_TETRAHEDRON:
    2722:       ct = DM_POLYTOPE_UNKNOWN;
    2723:       break;
    2724:     case DM_POLYTOPE_HEXAHEDRON:
    2725:       ct = DM_POLYTOPE_UNKNOWN;
    2726:       break;
    2727:     default:
    2728:       ct = DM_POLYTOPE_UNKNOWN;
    2729:     }
    2730:     break;
    2731:   default:
    2732:     ct = DM_POLYTOPE_UNKNOWN;
    2733:   }
    2734:   dim   = dim1 + dim2;
    2735:   Nc    = Nc1;
    2736:   Np    = Np1 * Np2;
    2737:   order = order1;
    2738:   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
    2739:   PetscCall(PetscQuadratureSetCellType(*q, ct));
    2740:   PetscCall(PetscQuadratureSetOrder(*q, order));
    2741:   PetscCall(PetscMalloc1(Np * dim, &x));
    2742:   PetscCall(PetscMalloc1(Np, &w));
    2743:   for (qa = 0, qc = 0; qa < Np1; ++qa) {
    2744:     for (qb = 0; qb < Np2; ++qb, ++qc) {
    2745:       for (d1 = 0, d = 0; d1 < dim1; ++d1, ++d) x[qc * dim + d] = x1[qa * dim1 + d1];
    2746:       for (d2 = 0; d2 < dim2; ++d2, ++d) x[qc * dim + d] = x2[qb * dim2 + d2];
    2747:       w[qc] = w1[qa] * w2[qb];
    2748:     }
    2749:   }
    2750:   PetscCall(PetscQuadratureSetData(*q, dim, Nc, Np, x, w));
    2751:   PetscFunctionReturn(PETSC_SUCCESS);
    2752: }

    2754: /* Overwrites A. Can only handle full-rank problems with m>=n
    2755:    A in column-major format
    2756:    Ainv in row-major format
    2757:    tau has length m
    2758:    worksize must be >= max(1,n)
    2759:  */
    2760: static PetscErrorCode PetscDTPseudoInverseQR(PetscInt m, PetscInt mstride, PetscInt n, PetscReal *A_in, PetscReal *Ainv_out, PetscScalar *tau, PetscInt worksize, PetscScalar *work)
    2761: {
    2762:   PetscBLASInt M, N, K, lda, ldb, ldwork, info;
    2763:   PetscScalar *A, *Ainv, *R, *Q, Alpha;

    2765:   PetscFunctionBegin;
    2766: #if defined(PETSC_USE_COMPLEX)
    2767:   {
    2768:     PetscInt i, j;
    2769:     PetscCall(PetscMalloc2(m * n, &A, m * n, &Ainv));
    2770:     for (j = 0; j < n; j++) {
    2771:       for (i = 0; i < m; i++) A[i + m * j] = A_in[i + mstride * j];
    2772:     }
    2773:     mstride = m;
    2774:   }
    2775: #else
    2776:   A    = A_in;
    2777:   Ainv = Ainv_out;
    2778: #endif

    2780:   PetscCall(PetscBLASIntCast(m, &M));
    2781:   PetscCall(PetscBLASIntCast(n, &N));
    2782:   PetscCall(PetscBLASIntCast(mstride, &lda));
    2783:   PetscCall(PetscBLASIntCast(worksize, &ldwork));
    2784:   PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    2785:   PetscCallBLAS("LAPACKgeqrf", LAPACKgeqrf_(&M, &N, A, &lda, tau, work, &ldwork, &info));
    2786:   PetscCall(PetscFPTrapPop());
    2787:   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "xGEQRF error");
    2788:   R = A; /* Upper triangular part of A now contains R, the rest contains the elementary reflectors */

    2790:   /* Extract an explicit representation of Q */
    2791:   Q = Ainv;
    2792:   PetscCall(PetscArraycpy(Q, A, mstride * n));
    2793:   K = N; /* full rank */
    2794:   PetscCallBLAS("LAPACKorgqr", LAPACKorgqr_(&M, &N, &K, Q, &lda, tau, work, &ldwork, &info));
    2795:   PetscCheck(!info, PETSC_COMM_SELF, PETSC_ERR_LIB, "xORGQR/xUNGQR error");

    2797:   /* Compute A^{-T} = (R^{-1} Q^T)^T = Q R^{-T} */
    2798:   Alpha = 1.0;
    2799:   ldb   = lda;
    2800:   PetscCallBLAS("BLAStrsm", BLAStrsm_("Right", "Upper", "ConjugateTranspose", "NotUnitTriangular", &M, &N, &Alpha, R, &lda, Q, &ldb));
    2801:   /* Ainv is Q, overwritten with inverse */

    2803: #if defined(PETSC_USE_COMPLEX)
    2804:   {
    2805:     PetscInt i;
    2806:     for (i = 0; i < m * n; i++) Ainv_out[i] = PetscRealPart(Ainv[i]);
    2807:     PetscCall(PetscFree2(A, Ainv));
    2808:   }
    2809: #endif
    2810:   PetscFunctionReturn(PETSC_SUCCESS);
    2811: }

    2813: /* Computes integral of L_p' over intervals {(x0,x1),(x1,x2),...} */
    2814: static PetscErrorCode PetscDTLegendreIntegrate(PetscInt ninterval, const PetscReal *x, PetscInt ndegree, const PetscInt *degrees, PetscBool Transpose, PetscReal *B)
    2815: {
    2816:   PetscReal *Bv;
    2817:   PetscInt   i, j;

    2819:   PetscFunctionBegin;
    2820:   PetscCall(PetscMalloc1((ninterval + 1) * ndegree, &Bv));
    2821:   /* Point evaluation of L_p on all the source vertices */
    2822:   PetscCall(PetscDTLegendreEval(ninterval + 1, x, ndegree, degrees, Bv, NULL, NULL));
    2823:   /* Integral over each interval: \int_a^b L_p' = L_p(b)-L_p(a) */
    2824:   for (i = 0; i < ninterval; i++) {
    2825:     for (j = 0; j < ndegree; j++) {
    2826:       if (Transpose) B[i + ninterval * j] = Bv[(i + 1) * ndegree + j] - Bv[i * ndegree + j];
    2827:       else B[i * ndegree + j] = Bv[(i + 1) * ndegree + j] - Bv[i * ndegree + j];
    2828:     }
    2829:   }
    2830:   PetscCall(PetscFree(Bv));
    2831:   PetscFunctionReturn(PETSC_SUCCESS);
    2832: }

    2834: /*@
    2835:   PetscDTReconstructPoly - create matrix representing polynomial reconstruction using cell intervals and evaluation at target intervals

    2837:   Not Collective

    2839:   Input Parameters:
    2840: + degree  - degree of reconstruction polynomial
    2841: . nsource - number of source intervals
    2842: . sourcex - sorted coordinates of source cell boundaries (length `nsource`+1)
    2843: . ntarget - number of target intervals
    2844: - targetx - sorted coordinates of target cell boundaries (length `ntarget`+1)

    2846:   Output Parameter:
    2847: . R - reconstruction matrix, utarget = sum_s R[t*nsource+s] * usource[s]

    2849:   Level: advanced

    2851: .seealso: `PetscDTLegendreEval()`
    2852: @*/
    2853: PetscErrorCode PetscDTReconstructPoly(PetscInt degree, PetscInt nsource, const PetscReal sourcex[], PetscInt ntarget, const PetscReal targetx[], PetscReal R[])
    2854: {
    2855:   PetscInt     i, j, k, *bdegrees, worksize;
    2856:   PetscReal    xmin, xmax, center, hscale, *sourcey, *targety, *Bsource, *Bsinv, *Btarget;
    2857:   PetscScalar *tau, *work;

    2859:   PetscFunctionBegin;
    2860:   PetscAssertPointer(sourcex, 3);
    2861:   PetscAssertPointer(targetx, 5);
    2862:   PetscAssertPointer(R, 6);
    2863:   PetscCheck(degree < nsource, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Reconstruction degree %" PetscInt_FMT " must be less than number of source intervals %" PetscInt_FMT, degree, nsource);
    2864:   if (PetscDefined(USE_DEBUG)) {
    2865:     for (i = 0; i < nsource; i++) PetscCheck(sourcex[i] < sourcex[i + 1], PETSC_COMM_SELF, PETSC_ERR_ARG_CORRUPT, "Source interval %" PetscInt_FMT " has negative orientation (%g,%g)", i, (double)sourcex[i], (double)sourcex[i + 1]);
    2866:     for (i = 0; i < ntarget; i++) PetscCheck(targetx[i] < targetx[i + 1], PETSC_COMM_SELF, PETSC_ERR_ARG_CORRUPT, "Target interval %" PetscInt_FMT " has negative orientation (%g,%g)", i, (double)targetx[i], (double)targetx[i + 1]);
    2867:   }
    2868:   xmin     = PetscMin(sourcex[0], targetx[0]);
    2869:   xmax     = PetscMax(sourcex[nsource], targetx[ntarget]);
    2870:   center   = (xmin + xmax) / 2;
    2871:   hscale   = (xmax - xmin) / 2;
    2872:   worksize = nsource;
    2873:   PetscCall(PetscMalloc4(degree + 1, &bdegrees, nsource + 1, &sourcey, nsource * (degree + 1), &Bsource, worksize, &work));
    2874:   PetscCall(PetscMalloc4(nsource, &tau, nsource * (degree + 1), &Bsinv, ntarget + 1, &targety, ntarget * (degree + 1), &Btarget));
    2875:   for (i = 0; i <= nsource; i++) sourcey[i] = (sourcex[i] - center) / hscale;
    2876:   for (i = 0; i <= degree; i++) bdegrees[i] = i + 1;
    2877:   PetscCall(PetscDTLegendreIntegrate(nsource, sourcey, degree + 1, bdegrees, PETSC_TRUE, Bsource));
    2878:   PetscCall(PetscDTPseudoInverseQR(nsource, nsource, degree + 1, Bsource, Bsinv, tau, nsource, work));
    2879:   for (i = 0; i <= ntarget; i++) targety[i] = (targetx[i] - center) / hscale;
    2880:   PetscCall(PetscDTLegendreIntegrate(ntarget, targety, degree + 1, bdegrees, PETSC_FALSE, Btarget));
    2881:   for (i = 0; i < ntarget; i++) {
    2882:     PetscReal rowsum = 0;
    2883:     for (j = 0; j < nsource; j++) {
    2884:       PetscReal sum = 0;
    2885:       for (k = 0; k < degree + 1; k++) sum += Btarget[i * (degree + 1) + k] * Bsinv[k * nsource + j];
    2886:       R[i * nsource + j] = sum;
    2887:       rowsum += sum;
    2888:     }
    2889:     for (j = 0; j < nsource; j++) R[i * nsource + j] /= rowsum; /* normalize each row */
    2890:   }
    2891:   PetscCall(PetscFree4(bdegrees, sourcey, Bsource, work));
    2892:   PetscCall(PetscFree4(tau, Bsinv, targety, Btarget));
    2893:   PetscFunctionReturn(PETSC_SUCCESS);
    2894: }

    2896: /*@
    2897:   PetscGaussLobattoLegendreIntegrate - Compute the L2 integral of a function on the GLL points

    2899:   Not Collective

    2901:   Input Parameters:
    2902: + n       - the number of GLL nodes
    2903: . nodes   - the GLL nodes
    2904: . weights - the GLL weights
    2905: - f       - the function values at the nodes

    2907:   Output Parameter:
    2908: . in - the value of the integral

    2910:   Level: beginner

    2912: .seealso: `PetscDTGaussLobattoLegendreQuadrature()`
    2913: @*/
    2914: PetscErrorCode PetscGaussLobattoLegendreIntegrate(PetscInt n, PetscReal nodes[], PetscReal weights[], const PetscReal f[], PetscReal *in)
    2915: {
    2916:   PetscInt i;

    2918:   PetscFunctionBegin;
    2919:   *in = 0.;
    2920:   for (i = 0; i < n; i++) *in += f[i] * f[i] * weights[i];
    2921:   PetscFunctionReturn(PETSC_SUCCESS);
    2922: }

    2924: /*@C
    2925:   PetscGaussLobattoLegendreElementLaplacianCreate - computes the Laplacian for a single 1d GLL element

    2927:   Not Collective

    2929:   Input Parameters:
    2930: + n       - the number of GLL nodes
    2931: . nodes   - the GLL nodes, of length `n`
    2932: - weights - the GLL weights, of length `n`

    2934:   Output Parameter:
    2935: . AA - the stiffness element, of size `n` by `n`

    2937:   Level: beginner

    2939:   Notes:
    2940:   Destroy this with `PetscGaussLobattoLegendreElementLaplacianDestroy()`

    2942:   You can access entries in this array with AA[i][j] but in memory it is stored in contiguous memory, row-oriented (the array is symmetric)

    2944: .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianDestroy()`
    2945: @*/
    2946: PetscErrorCode PetscGaussLobattoLegendreElementLaplacianCreate(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA)
    2947: {
    2948:   PetscReal      **A;
    2949:   const PetscReal *gllnodes = nodes;
    2950:   const PetscInt   p        = n - 1;
    2951:   PetscReal        z0, z1, z2 = -1, x, Lpj, Lpr;
    2952:   PetscInt         i, j, nn, r;

    2954:   PetscFunctionBegin;
    2955:   PetscCall(PetscMalloc1(n, &A));
    2956:   PetscCall(PetscMalloc1(n * n, &A[0]));
    2957:   for (i = 1; i < n; i++) A[i] = A[i - 1] + n;

    2959:   for (j = 1; j < p; j++) {
    2960:     x  = gllnodes[j];
    2961:     z0 = 1.;
    2962:     z1 = x;
    2963:     for (nn = 1; nn < p; nn++) {
    2964:       z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
    2965:       z0 = z1;
    2966:       z1 = z2;
    2967:     }
    2968:     Lpj = z2;
    2969:     for (r = 1; r < p; r++) {
    2970:       if (r == j) {
    2971:         A[j][j] = 2. / (3. * (1. - gllnodes[j] * gllnodes[j]) * Lpj * Lpj);
    2972:       } else {
    2973:         x  = gllnodes[r];
    2974:         z0 = 1.;
    2975:         z1 = x;
    2976:         for (nn = 1; nn < p; nn++) {
    2977:           z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
    2978:           z0 = z1;
    2979:           z1 = z2;
    2980:         }
    2981:         Lpr     = z2;
    2982:         A[r][j] = 4. / (((PetscReal)p) * (((PetscReal)p) + 1.) * Lpj * Lpr * (gllnodes[j] - gllnodes[r]) * (gllnodes[j] - gllnodes[r]));
    2983:       }
    2984:     }
    2985:   }
    2986:   for (j = 1; j < p + 1; j++) {
    2987:     x  = gllnodes[j];
    2988:     z0 = 1.;
    2989:     z1 = x;
    2990:     for (nn = 1; nn < p; nn++) {
    2991:       z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
    2992:       z0 = z1;
    2993:       z1 = z2;
    2994:     }
    2995:     Lpj     = z2;
    2996:     A[j][0] = 4. * PetscPowRealInt(-1., p) / (((PetscReal)p) * (((PetscReal)p) + 1.) * Lpj * (1. + gllnodes[j]) * (1. + gllnodes[j]));
    2997:     A[0][j] = A[j][0];
    2998:   }
    2999:   for (j = 0; j < p; j++) {
    3000:     x  = gllnodes[j];
    3001:     z0 = 1.;
    3002:     z1 = x;
    3003:     for (nn = 1; nn < p; nn++) {
    3004:       z2 = x * z1 * (2. * ((PetscReal)nn) + 1.) / (((PetscReal)nn) + 1.) - z0 * (((PetscReal)nn) / (((PetscReal)nn) + 1.));
    3005:       z0 = z1;
    3006:       z1 = z2;
    3007:     }
    3008:     Lpj = z2;

    3010:     A[p][j] = 4. / (((PetscReal)p) * (((PetscReal)p) + 1.) * Lpj * (1. - gllnodes[j]) * (1. - gllnodes[j]));
    3011:     A[j][p] = A[p][j];
    3012:   }
    3013:   A[0][0] = 0.5 + (((PetscReal)p) * (((PetscReal)p) + 1.) - 2.) / 6.;
    3014:   A[p][p] = A[0][0];
    3015:   *AA     = A;
    3016:   PetscFunctionReturn(PETSC_SUCCESS);
    3017: }

    3019: /*@C
    3020:   PetscGaussLobattoLegendreElementLaplacianDestroy - frees the Laplacian for a single 1d GLL element created with `PetscGaussLobattoLegendreElementLaplacianCreate()`

    3022:   Not Collective

    3024:   Input Parameters:
    3025: + n       - the number of GLL nodes
    3026: . nodes   - the GLL nodes, ignored
    3027: . weights - the GLL weightss, ignored
    3028: - AA      - the stiffness element from `PetscGaussLobattoLegendreElementLaplacianCreate()`

    3030:   Level: beginner

    3032: .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`
    3033: @*/
    3034: PetscErrorCode PetscGaussLobattoLegendreElementLaplacianDestroy(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA)
    3035: {
    3036:   PetscFunctionBegin;
    3037:   PetscCall(PetscFree((*AA)[0]));
    3038:   PetscCall(PetscFree(*AA));
    3039:   *AA = NULL;
    3040:   PetscFunctionReturn(PETSC_SUCCESS);
    3041: }

    3043: /*@C
    3044:   PetscGaussLobattoLegendreElementGradientCreate - computes the gradient for a single 1d GLL element

    3046:   Not Collective

    3048:   Input Parameters:
    3049: + n       - the number of GLL nodes
    3050: . nodes   - the GLL nodes, of length `n`
    3051: - weights - the GLL weights, of length `n`

    3053:   Output Parameters:
    3054: + AA  - the stiffness element, of dimension `n` by `n`
    3055: - AAT - the transpose of AA (pass in `NULL` if you do not need this array), of dimension `n` by `n`

    3057:   Level: beginner

    3059:   Notes:
    3060:   Destroy this with `PetscGaussLobattoLegendreElementGradientDestroy()`

    3062:   You can access entries in these arrays with AA[i][j] but in memory it is stored in contiguous memory, row-oriented

    3064: .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianDestroy()`, `PetscGaussLobattoLegendreElementGradientDestroy()`
    3065: @*/
    3066: PetscErrorCode PetscGaussLobattoLegendreElementGradientCreate(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA, PetscReal ***AAT)
    3067: {
    3068:   PetscReal      **A, **AT = NULL;
    3069:   const PetscReal *gllnodes = nodes;
    3070:   const PetscInt   p        = n - 1;
    3071:   PetscReal        Li, Lj, d0;
    3072:   PetscInt         i, j;

    3074:   PetscFunctionBegin;
    3075:   PetscCall(PetscMalloc1(n, &A));
    3076:   PetscCall(PetscMalloc1(n * n, &A[0]));
    3077:   for (i = 1; i < n; i++) A[i] = A[i - 1] + n;

    3079:   if (AAT) {
    3080:     PetscCall(PetscMalloc1(n, &AT));
    3081:     PetscCall(PetscMalloc1(n * n, &AT[0]));
    3082:     for (i = 1; i < n; i++) AT[i] = AT[i - 1] + n;
    3083:   }

    3085:   if (n == 1) A[0][0] = 0.;
    3086:   d0 = (PetscReal)p * ((PetscReal)p + 1.) / 4.;
    3087:   for (i = 0; i < n; i++) {
    3088:     for (j = 0; j < n; j++) {
    3089:       A[i][j] = 0.;
    3090:       PetscCall(PetscDTComputeJacobi(0., 0., p, gllnodes[i], &Li));
    3091:       PetscCall(PetscDTComputeJacobi(0., 0., p, gllnodes[j], &Lj));
    3092:       if (i != j) A[i][j] = Li / (Lj * (gllnodes[i] - gllnodes[j]));
    3093:       if ((j == i) && (i == 0)) A[i][j] = -d0;
    3094:       if (j == i && i == p) A[i][j] = d0;
    3095:       if (AT) AT[j][i] = A[i][j];
    3096:     }
    3097:   }
    3098:   if (AAT) *AAT = AT;
    3099:   *AA = A;
    3100:   PetscFunctionReturn(PETSC_SUCCESS);
    3101: }

    3103: /*@C
    3104:   PetscGaussLobattoLegendreElementGradientDestroy - frees the gradient for a single 1d GLL element obtained with `PetscGaussLobattoLegendreElementGradientCreate()`

    3106:   Not Collective

    3108:   Input Parameters:
    3109: + n       - the number of GLL nodes
    3110: . nodes   - the GLL nodes, ignored
    3111: . weights - the GLL weights, ignored
    3112: . AA      - the stiffness element obtained with `PetscGaussLobattoLegendreElementGradientCreate()`
    3113: - AAT     - the transpose of the element obtained with `PetscGaussLobattoLegendreElementGradientCreate()`

    3115:   Level: beginner

    3117: .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`, `PetscGaussLobattoLegendreElementAdvectionCreate()`
    3118: @*/
    3119: PetscErrorCode PetscGaussLobattoLegendreElementGradientDestroy(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA, PetscReal ***AAT)
    3120: {
    3121:   PetscFunctionBegin;
    3122:   PetscCall(PetscFree((*AA)[0]));
    3123:   PetscCall(PetscFree(*AA));
    3124:   *AA = NULL;
    3125:   if (AAT) {
    3126:     PetscCall(PetscFree((*AAT)[0]));
    3127:     PetscCall(PetscFree(*AAT));
    3128:     *AAT = NULL;
    3129:   }
    3130:   PetscFunctionReturn(PETSC_SUCCESS);
    3131: }

    3133: /*@C
    3134:   PetscGaussLobattoLegendreElementAdvectionCreate - computes the advection operator for a single 1d GLL element

    3136:   Not Collective

    3138:   Input Parameters:
    3139: + n       - the number of GLL nodes
    3140: . nodes   - the GLL nodes, of length `n`
    3141: - weights - the GLL weights, of length `n`

    3143:   Output Parameter:
    3144: . AA - the stiffness element, of dimension `n` by `n`

    3146:   Level: beginner

    3148:   Notes:
    3149:   Destroy this with `PetscGaussLobattoLegendreElementAdvectionDestroy()`

    3151:   This is the same as the Gradient operator multiplied by the diagonal mass matrix

    3153:   You can access entries in this array with AA[i][j] but in memory it is stored in contiguous memory, row-oriented

    3155: .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementLaplacianCreate()`, `PetscGaussLobattoLegendreElementAdvectionDestroy()`
    3156: @*/
    3157: PetscErrorCode PetscGaussLobattoLegendreElementAdvectionCreate(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA)
    3158: {
    3159:   PetscReal      **D;
    3160:   const PetscReal *gllweights = weights;
    3161:   const PetscInt   glln       = n;
    3162:   PetscInt         i, j;

    3164:   PetscFunctionBegin;
    3165:   PetscCall(PetscGaussLobattoLegendreElementGradientCreate(n, nodes, weights, &D, NULL));
    3166:   for (i = 0; i < glln; i++) {
    3167:     for (j = 0; j < glln; j++) D[i][j] = gllweights[i] * D[i][j];
    3168:   }
    3169:   *AA = D;
    3170:   PetscFunctionReturn(PETSC_SUCCESS);
    3171: }

    3173: /*@C
    3174:   PetscGaussLobattoLegendreElementAdvectionDestroy - frees the advection stiffness for a single 1d GLL element created with `PetscGaussLobattoLegendreElementAdvectionCreate()`

    3176:   Not Collective

    3178:   Input Parameters:
    3179: + n       - the number of GLL nodes
    3180: . nodes   - the GLL nodes, ignored
    3181: . weights - the GLL weights, ignored
    3182: - AA      - advection obtained with `PetscGaussLobattoLegendreElementAdvectionCreate()`

    3184:   Level: beginner

    3186: .seealso: `PetscDTGaussLobattoLegendreQuadrature()`, `PetscGaussLobattoLegendreElementAdvectionCreate()`
    3187: @*/
    3188: PetscErrorCode PetscGaussLobattoLegendreElementAdvectionDestroy(PetscInt n, PetscReal nodes[], PetscReal weights[], PetscReal ***AA)
    3189: {
    3190:   PetscFunctionBegin;
    3191:   PetscCall(PetscFree((*AA)[0]));
    3192:   PetscCall(PetscFree(*AA));
    3193:   *AA = NULL;
    3194:   PetscFunctionReturn(PETSC_SUCCESS);
    3195: }

    3197: PetscErrorCode PetscGaussLobattoLegendreElementMassCreate(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA)
    3198: {
    3199:   PetscReal      **A;
    3200:   const PetscReal *gllweights = weights;
    3201:   const PetscInt   glln       = n;
    3202:   PetscInt         i, j;

    3204:   PetscFunctionBegin;
    3205:   PetscCall(PetscMalloc1(glln, &A));
    3206:   PetscCall(PetscMalloc1(glln * glln, &A[0]));
    3207:   for (i = 1; i < glln; i++) A[i] = A[i - 1] + glln;
    3208:   if (glln == 1) A[0][0] = 0.;
    3209:   for (i = 0; i < glln; i++) {
    3210:     for (j = 0; j < glln; j++) {
    3211:       A[i][j] = 0.;
    3212:       if (j == i) A[i][j] = gllweights[i];
    3213:     }
    3214:   }
    3215:   *AA = A;
    3216:   PetscFunctionReturn(PETSC_SUCCESS);
    3217: }

    3219: PetscErrorCode PetscGaussLobattoLegendreElementMassDestroy(PetscInt n, PetscReal *nodes, PetscReal *weights, PetscReal ***AA)
    3220: {
    3221:   PetscFunctionBegin;
    3222:   PetscCall(PetscFree((*AA)[0]));
    3223:   PetscCall(PetscFree(*AA));
    3224:   *AA = NULL;
    3225:   PetscFunctionReturn(PETSC_SUCCESS);
    3226: }

    3228: /*@
    3229:   PetscDTIndexToBary - convert an index into a barycentric coordinate.

    3231:   Input Parameters:
    3232: + len   - the desired length of the barycentric tuple (usually 1 more than the dimension it represents, so a barycentric coordinate in a triangle has length 3)
    3233: . sum   - the value that the sum of the barycentric coordinates (which will be non-negative integers) should sum to
    3234: - index - the index to convert: should be >= 0 and < Binomial(len - 1 + sum, sum)

    3236:   Output Parameter:
    3237: . coord - will be filled with the barycentric coordinate, of length `n`

    3239:   Level: beginner

    3241:   Note:
    3242:   The indices map to barycentric coordinates in lexicographic order, where the first index is the
    3243:   least significant and the last index is the most significant.

    3245: .seealso: `PetscDTBaryToIndex()`
    3246: @*/
    3247: PetscErrorCode PetscDTIndexToBary(PetscInt len, PetscInt sum, PetscInt index, PetscInt coord[])
    3248: {
    3249:   PetscInt c, d, s, total, subtotal, nexttotal;

    3251:   PetscFunctionBeginHot;
    3252:   PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
    3253:   PetscCheck(index >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index must be non-negative");
    3254:   if (!len) {
    3255:     if (!sum && !index) PetscFunctionReturn(PETSC_SUCCESS);
    3256:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid index or sum for length 0 barycentric coordinate");
    3257:   }
    3258:   for (c = 1, total = 1; c <= len; c++) {
    3259:     /* total is the number of ways to have a tuple of length c with sum */
    3260:     if (index < total) break;
    3261:     total = (total * (sum + c)) / c;
    3262:   }
    3263:   PetscCheck(c <= len, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "index out of range");
    3264:   for (d = c; d < len; d++) coord[d] = 0;
    3265:   for (s = 0, subtotal = 1, nexttotal = 1; c > 0;) {
    3266:     /* subtotal is the number of ways to have a tuple of length c with sum s */
    3267:     /* nexttotal is the number of ways to have a tuple of length c-1 with sum s */
    3268:     if ((index + subtotal) >= total) {
    3269:       coord[--c] = sum - s;
    3270:       index -= (total - subtotal);
    3271:       sum       = s;
    3272:       total     = nexttotal;
    3273:       subtotal  = 1;
    3274:       nexttotal = 1;
    3275:       s         = 0;
    3276:     } else {
    3277:       subtotal  = (subtotal * (c + s)) / (s + 1);
    3278:       nexttotal = (nexttotal * (c - 1 + s)) / (s + 1);
    3279:       s++;
    3280:     }
    3281:   }
    3282:   PetscFunctionReturn(PETSC_SUCCESS);
    3283: }

    3285: /*@
    3286:   PetscDTBaryToIndex - convert a barycentric coordinate to an index

    3288:   Input Parameters:
    3289: + len   - the desired length of the barycentric tuple (usually 1 more than the dimension it represents, so a barycentric coordinate in a triangle has length 3)
    3290: . sum   - the value that the sum of the barycentric coordinates (which will be non-negative integers) should sum to
    3291: - coord - a barycentric coordinate with the given length `len` and `sum`

    3293:   Output Parameter:
    3294: . index - the unique index for the coordinate, >= 0 and < Binomial(len - 1 + sum, sum)

    3296:   Level: beginner

    3298:   Note:
    3299:   The indices map to barycentric coordinates in lexicographic order, where the first index is the
    3300:   least significant and the last index is the most significant.

    3302: .seealso: `PetscDTIndexToBary`
    3303: @*/
    3304: PetscErrorCode PetscDTBaryToIndex(PetscInt len, PetscInt sum, const PetscInt coord[], PetscInt *index)
    3305: {
    3306:   PetscInt c;
    3307:   PetscInt i;
    3308:   PetscInt total;

    3310:   PetscFunctionBeginHot;
    3311:   PetscCheck(len >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "length must be non-negative");
    3312:   if (!len) {
    3313:     if (!sum) {
    3314:       *index = 0;
    3315:       PetscFunctionReturn(PETSC_SUCCESS);
    3316:     }
    3317:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid index or sum for length 0 barycentric coordinate");
    3318:   }
    3319:   for (c = 1, total = 1; c < len; c++) total = (total * (sum + c)) / c;
    3320:   i = total - 1;
    3321:   c = len - 1;
    3322:   sum -= coord[c];
    3323:   while (sum > 0) {
    3324:     PetscInt subtotal;
    3325:     PetscInt s;

    3327:     for (s = 1, subtotal = 1; s < sum; s++) subtotal = (subtotal * (c + s)) / s;
    3328:     i -= subtotal;
    3329:     sum -= coord[--c];
    3330:   }
    3331:   *index = i;
    3332:   PetscFunctionReturn(PETSC_SUCCESS);
    3333: }

    3335: /*@
    3336:   PetscQuadratureComputePermutations - Compute permutations of quadrature points corresponding to domain orientations

    3338:   Input Parameter:
    3339: . quad - The `PetscQuadrature`

    3341:   Output Parameters:
    3342: + Np   - The number of domain orientations
    3343: - perm - An array of `IS` permutations, one for ech orientation,

    3345:   Level: developer

    3347: .seealso: `PetscQuadratureSetCellType()`, `PetscQuadrature`
    3348: @*/
    3349: PetscErrorCode PetscQuadratureComputePermutations(PetscQuadrature quad, PeOp PetscInt *Np, IS *perm[])
    3350: {
    3351:   DMPolytopeType   ct;
    3352:   const PetscReal *xq, *wq;
    3353:   PetscInt         dim, qdim, d, Na, o, Nq, q, qp;

    3355:   PetscFunctionBegin;
    3356:   PetscCall(PetscQuadratureGetData(quad, &qdim, NULL, &Nq, &xq, &wq));
    3357:   PetscCall(PetscQuadratureGetCellType(quad, &ct));
    3358:   dim = DMPolytopeTypeGetDim(ct);
    3359:   Na  = DMPolytopeTypeGetNumArrangements(ct);
    3360:   PetscCall(PetscMalloc1(Na, perm));
    3361:   if (Np) *Np = Na;
    3362:   Na /= 2;
    3363:   for (o = -Na; o < Na; ++o) {
    3364:     DM        refdm;
    3365:     PetscInt *idx;
    3366:     PetscReal xi0[3] = {-1., -1., -1.}, v0[3], J[9], detJ, txq[3];
    3367:     PetscBool flg;

    3369:     PetscCall(DMPlexCreateReferenceCell(PETSC_COMM_SELF, ct, &refdm));
    3370:     PetscCall(DMPlexOrientPoint(refdm, 0, o));
    3371:     PetscCall(DMPlexComputeCellGeometryFEM(refdm, 0, NULL, v0, J, NULL, &detJ));
    3372:     PetscCall(PetscMalloc1(Nq, &idx));
    3373:     for (q = 0; q < Nq; ++q) {
    3374:       CoordinatesRefToReal(dim, dim, xi0, v0, J, &xq[q * dim], txq);
    3375:       for (qp = 0; qp < Nq; ++qp) {
    3376:         PetscReal diff = 0.;

    3378:         for (d = 0; d < dim; ++d) diff += PetscAbsReal(txq[d] - xq[qp * dim + d]);
    3379:         if (diff < PETSC_SMALL) break;
    3380:       }
    3381:       PetscCheck(qp < Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Transformed quad point %" PetscInt_FMT " does not match another quad point", q);
    3382:       idx[q] = qp;
    3383:     }
    3384:     PetscCall(DMDestroy(&refdm));
    3385:     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, Nq, idx, PETSC_OWN_POINTER, &(*perm)[o + Na]));
    3386:     PetscCall(ISGetInfo((*perm)[o + Na], IS_PERMUTATION, IS_LOCAL, PETSC_TRUE, &flg));
    3387:     PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Ordering for orientation %" PetscInt_FMT " was not a permutation", o);
    3388:     PetscCall(ISSetPermutation((*perm)[o + Na]));
    3389:   }
    3390:   if (!Na) (*perm)[0] = NULL;
    3391:   PetscFunctionReturn(PETSC_SUCCESS);
    3392: }

    3394: /*@
    3395:   PetscDTCreateDefaultQuadrature - Create default quadrature for a given cell

    3397:   Not collective

    3399:   Input Parameters:
    3400: + ct     - The integration domain
    3401: - qorder - The desired quadrature order

    3403:   Output Parameters:
    3404: + q  - The cell quadrature
    3405: - fq - The face quadrature

    3407:   Level: developer

    3409: .seealso: `PetscFECreateDefault()`, `PetscDTGaussTensorQuadrature()`, `PetscDTSimplexQuadrature()`, `PetscDTTensorQuadratureCreate()`
    3410: @*/
    3411: PetscErrorCode PetscDTCreateDefaultQuadrature(DMPolytopeType ct, PetscInt qorder, PetscQuadrature *q, PetscQuadrature *fq)
    3412: {
    3413:   const PetscInt quadPointsPerEdge = PetscMax(qorder + 1, 1);
    3414:   const PetscInt dim               = DMPolytopeTypeGetDim(ct);

    3416:   PetscFunctionBegin;
    3417:   switch (ct) {
    3418:   case DM_POLYTOPE_SEGMENT:
    3419:   case DM_POLYTOPE_POINT_PRISM_TENSOR:
    3420:   case DM_POLYTOPE_QUADRILATERAL:
    3421:   case DM_POLYTOPE_SEG_PRISM_TENSOR:
    3422:   case DM_POLYTOPE_HEXAHEDRON:
    3423:   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
    3424:     PetscCall(PetscDTGaussTensorQuadrature(dim, 1, quadPointsPerEdge, -1.0, 1.0, q));
    3425:     PetscCall(PetscDTGaussTensorQuadrature(dim - 1, 1, quadPointsPerEdge, -1.0, 1.0, fq));
    3426:     break;
    3427:   case DM_POLYTOPE_TRIANGLE:
    3428:   case DM_POLYTOPE_TETRAHEDRON:
    3429:     PetscCall(PetscDTSimplexQuadrature(dim, 2 * qorder, PETSCDTSIMPLEXQUAD_DEFAULT, q));
    3430:     PetscCall(PetscDTSimplexQuadrature(dim - 1, 2 * qorder, PETSCDTSIMPLEXQUAD_DEFAULT, fq));
    3431:     break;
    3432:   case DM_POLYTOPE_TRI_PRISM:
    3433:   case DM_POLYTOPE_TRI_PRISM_TENSOR: {
    3434:     PetscQuadrature q1, q2;

    3436:     // TODO: this should be able to use symmetric rules, but doing so causes tests to fail
    3437:     PetscCall(PetscDTSimplexQuadrature(2, 2 * qorder, PETSCDTSIMPLEXQUAD_CONIC, &q1));
    3438:     PetscCall(PetscDTGaussTensorQuadrature(1, 1, quadPointsPerEdge, -1.0, 1.0, &q2));
    3439:     PetscCall(PetscDTTensorQuadratureCreate(q1, q2, q));
    3440:     PetscCall(PetscQuadratureDestroy(&q2));
    3441:     *fq = q1;
    3442:     /* TODO Need separate quadratures for each face */
    3443:   } break;
    3444:   default:
    3445:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No quadrature for celltype %s", DMPolytopeTypes[PetscMin(ct, DM_POLYTOPE_UNKNOWN)]);
    3446:   }
    3447:   PetscFunctionReturn(PETSC_SUCCESS);
    3448: }