Date: 2026-07-30T21:14:00+00:00

Actual source code: fe.c

      1: /* Basis Jet Tabulation

      3: We would like to tabulate the nodal basis functions and derivatives at a set of points, usually quadrature points. We
      4: follow here the derviation in http://www.math.ttu.edu/~kirby/papers/fiat-toms-2004.pdf. The nodal basis $\psi_i$ can
      5: be expressed in terms of a prime basis $\phi_i$ which can be stably evaluated. In PETSc, we will use the Legendre basis
      6: as a prime basis.

      8:   \psi_i = \sum_k \alpha_{ki} \phi_k

     10: Our nodal basis is defined in terms of the dual basis $n_j$

     12:   n_j \cdot \psi_i = \delta_{ji}

     14: and we may act on the first equation to obtain

     16:   n_j \cdot \psi_i = \sum_k \alpha_{ki} n_j \cdot \phi_k
     17:        \delta_{ji} = \sum_k \alpha_{ki} V_{jk}
     18:                  I = V \alpha

     20: so the coefficients of the nodal basis in the prime basis are

     22:    \alpha = V^{-1}

     24: We will define the dual basis vectors $n_j$ using a quadrature rule.

     26: Right now, we will just use the polynomial spaces P^k. I know some elements use the space of symmetric polynomials
     27: (I think Nedelec), but we will neglect this for now. Constraints in the space, e.g. Arnold-Winther elements, can
     28: be implemented exactly as in FIAT using functionals $L_j$.

     30: I will have to count the degrees correctly for the Legendre product when we are on simplices.

     32: We will have three objects:
     33:  - Space, P: this just need point evaluation I think
     34:  - Dual Space, P'+K: This looks like a set of functionals that can act on members of P, each n is defined by a Q
     35:  - FEM: This keeps {P, P', Q}
     36: */
     37: #include <petsc/private/petscfeimpl.h>
     38: #include <petscdmplex.h>

     40: PetscBool  FEcite       = PETSC_FALSE;
     41: const char FECitation[] = "@article{kirby2004,\n"
     42:                           "  title   = {Algorithm 839: FIAT, a New Paradigm for Computing Finite Element Basis Functions},\n"
     43:                           "  journal = {ACM Transactions on Mathematical Software},\n"
     44:                           "  author  = {Robert C. Kirby},\n"
     45:                           "  volume  = {30},\n"
     46:                           "  number  = {4},\n"
     47:                           "  pages   = {502--516},\n"
     48:                           "  doi     = {10.1145/1039813.1039820},\n"
     49:                           "  year    = {2004}\n}\n";

     51: PetscClassId PETSCFE_CLASSID = 0;

     53: PetscLogEvent PETSCFE_SetUp;

     55: PetscFunctionList PetscFEList              = NULL;
     56: PetscBool         PetscFERegisterAllCalled = PETSC_FALSE;

     58: /*@C
     59:   PetscFERegister - Adds a new `PetscFEType`

     61:   Not Collective, No Fortran Support

     63:   Input Parameters:
     64: + sname    - The name of a new user-defined creation routine
     65: - function - The creation routine

     67:   Example Usage:
     68: .vb
     69:     PetscFERegister("my_fe", MyPetscFECreate);
     70: .ve

     72:   Then, your PetscFE type can be chosen with the procedural interface via
     73: .vb
     74:     PetscFECreate(MPI_Comm, PetscFE *);
     75:     PetscFESetType(PetscFE, "my_fe");
     76: .ve
     77:   or at runtime via the option
     78: .vb
     79:     -petscfe_type my_fe
     80: .ve

     82:   Level: advanced

     84:   Note:
     85:   `PetscFERegister()` may be called multiple times to add several user-defined `PetscFE`s

     87: .seealso: `PetscFE`, `PetscFEType`, `PetscFERegisterAll()`, `PetscFERegisterDestroy()`
     88: @*/
     89: PetscErrorCode PetscFERegister(const char sname[], PetscErrorCode (*function)(PetscFE))
     90: {
     91:   PetscFunctionBegin;
     92:   PetscCall(PetscFunctionListAdd(&PetscFEList, sname, function));
     93:   PetscFunctionReturn(PETSC_SUCCESS);
     94: }

     96: /*@
     97:   PetscFESetType - Builds a particular `PetscFE`

     99:   Collective

    101:   Input Parameters:
    102: + fem  - The `PetscFE` object
    103: - name - The kind of FEM space

    105:   Options Database Key:
    106: . -petscfe_type (basic|opencl|composite|vector) - Sets the `PetscFEType`

    108:   Level: intermediate

    110: .seealso: `PetscFEType`, `PetscFE`, `PetscFEGetType()`, `PetscFECreate()`
    111: @*/
    112: PetscErrorCode PetscFESetType(PetscFE fem, PetscFEType name)
    113: {
    114:   PetscErrorCode (*r)(PetscFE);
    115:   PetscBool match;

    117:   PetscFunctionBegin;
    119:   PetscCall(PetscObjectTypeCompare((PetscObject)fem, name, &match));
    120:   if (match) PetscFunctionReturn(PETSC_SUCCESS);

    122:   if (!PetscFERegisterAllCalled) PetscCall(PetscFERegisterAll());
    123:   PetscCall(PetscFunctionListFind(PetscFEList, name, &r));
    124:   PetscCheck(r, PetscObjectComm((PetscObject)fem), PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown PetscFE type: %s", name);

    126:   PetscTryTypeMethod(fem, destroy);
    127:   fem->ops->destroy = NULL;

    129:   PetscCall((*r)(fem));
    130:   PetscCall(PetscObjectChangeTypeName((PetscObject)fem, name));
    131:   PetscFunctionReturn(PETSC_SUCCESS);
    132: }

    134: /*@
    135:   PetscFEGetType - Gets the `PetscFEType` (as a string) from the `PetscFE` object.

    137:   Not Collective

    139:   Input Parameter:
    140: . fem - The `PetscFE`

    142:   Output Parameter:
    143: . name - The `PetscFEType` name

    145:   Level: intermediate

    147: .seealso: `PetscFEType`, `PetscFE`, `PetscFESetType()`, `PetscFECreate()`
    148: @*/
    149: PetscErrorCode PetscFEGetType(PetscFE fem, PetscFEType *name)
    150: {
    151:   PetscFunctionBegin;
    153:   PetscAssertPointer(name, 2);
    154:   if (!PetscFERegisterAllCalled) PetscCall(PetscFERegisterAll());
    155:   *name = ((PetscObject)fem)->type_name;
    156:   PetscFunctionReturn(PETSC_SUCCESS);
    157: }

    159: /*@
    160:   PetscFEViewFromOptions - View a `PetscFE` based on values in the options database

    162:   Collective

    164:   Input Parameters:
    165: + A    - the `PetscFE` object
    166: . obj  - Optional object that provides the options prefix, pass `NULL` to use the options prefix of `A`
    167: - name - command line option name

    169:   Options Database Key:
    170: . -name [viewertype][:...] - option name and values. See `PetscObjectViewFromOptions()` for the possible arguments

    172:   Level: intermediate

    174: .seealso: `PetscFE`, `PetscFEView()`, `PetscObjectViewFromOptions()`, `PetscFECreate()`
    175: @*/
    176: PetscErrorCode PetscFEViewFromOptions(PetscFE A, PeOp PetscObject obj, const char name[])
    177: {
    178:   PetscFunctionBegin;
    180:   PetscCall(PetscObjectViewFromOptions((PetscObject)A, obj, name));
    181:   PetscFunctionReturn(PETSC_SUCCESS);
    182: }

    184: /*@
    185:   PetscFEView - Views a `PetscFE`

    187:   Collective

    189:   Input Parameters:
    190: + fem    - the `PetscFE` object to view
    191: - viewer - the viewer

    193:   Level: beginner

    195: .seealso: `PetscFE`, `PetscViewer`, `PetscFEDestroy()`, `PetscFEViewFromOptions()`
    196: @*/
    197: PetscErrorCode PetscFEView(PetscFE fem, PetscViewer viewer)
    198: {
    199:   PetscBool isascii;

    201:   PetscFunctionBegin;
    204:   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)fem), &viewer));
    205:   PetscCall(PetscObjectPrintClassNamePrefixType((PetscObject)fem, viewer));
    206:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
    207:   PetscTryTypeMethod(fem, view, viewer);
    208:   PetscFunctionReturn(PETSC_SUCCESS);
    209: }

    211: /*@
    212:   PetscFESetFromOptions - sets parameters in a `PetscFE` from the options database

    214:   Collective

    216:   Input Parameter:
    217: . fem - the `PetscFE` object to set options for

    219:   Options Database Keys:
    220: + -petscfe_num_blocks  nblocks  - the number of cell blocks to integrate concurrently
    221: - -petscfe_num_batches nbatches - the number of cell batches to integrate serially

    223:   Level: intermediate

    225: .seealso: `PetscFE`, `PetscFEView()`
    226: @*/
    227: PetscErrorCode PetscFESetFromOptions(PetscFE fem)
    228: {
    229:   const char *defaultType;
    230:   char        name[256];
    231:   PetscBool   flg;

    233:   PetscFunctionBegin;
    235:   if (!((PetscObject)fem)->type_name) defaultType = PETSCFEBASIC;
    236:   else defaultType = ((PetscObject)fem)->type_name;
    237:   if (!PetscFERegisterAllCalled) PetscCall(PetscFERegisterAll());

    239:   PetscObjectOptionsBegin((PetscObject)fem);
    240:   PetscCall(PetscOptionsFList("-petscfe_type", "Finite element space", "PetscFESetType", PetscFEList, defaultType, name, 256, &flg));
    241:   if (flg) PetscCall(PetscFESetType(fem, name));
    242:   else if (!((PetscObject)fem)->type_name) PetscCall(PetscFESetType(fem, defaultType));
    243:   PetscCall(PetscOptionsBoundedInt("-petscfe_num_blocks", "The number of cell blocks to integrate concurrently", "PetscSpaceSetTileSizes", fem->numBlocks, &fem->numBlocks, NULL, 1));
    244:   PetscCall(PetscOptionsBoundedInt("-petscfe_num_batches", "The number of cell batches to integrate serially", "PetscSpaceSetTileSizes", fem->numBatches, &fem->numBatches, NULL, 1));
    245:   PetscTryTypeMethod(fem, setfromoptions, PetscOptionsObject);
    246:   /* process any options handlers added with PetscObjectAddOptionsHandler() */
    247:   PetscCall(PetscObjectProcessOptionsHandlers((PetscObject)fem, PetscOptionsObject));
    248:   PetscOptionsEnd();
    249:   PetscCall(PetscFEViewFromOptions(fem, NULL, "-petscfe_view"));
    250:   PetscFunctionReturn(PETSC_SUCCESS);
    251: }

    253: /*@
    254:   PetscFESetUp - Construct data structures for the `PetscFE` after the `PetscFEType` has been set

    256:   Collective

    258:   Input Parameter:
    259: . fem - the `PetscFE` object to setup

    261:   Level: intermediate

    263: .seealso: `PetscFE`, `PetscFEView()`, `PetscFEDestroy()`
    264: @*/
    265: PetscErrorCode PetscFESetUp(PetscFE fem)
    266: {
    267:   PetscFunctionBegin;
    269:   if (fem->setupcalled) PetscFunctionReturn(PETSC_SUCCESS);
    270:   PetscCall(PetscLogEventBegin(PETSCFE_SetUp, fem, 0, 0, 0));
    271:   fem->setupcalled = PETSC_TRUE;
    272:   PetscTryTypeMethod(fem, setup);
    273:   PetscCall(PetscLogEventEnd(PETSCFE_SetUp, fem, 0, 0, 0));
    274:   PetscFunctionReturn(PETSC_SUCCESS);
    275: }

    277: /*@
    278:   PetscFEDestroy - Destroys a `PetscFE` object

    280:   Collective

    282:   Input Parameter:
    283: . fem - the `PetscFE` object to destroy

    285:   Level: beginner

    287: .seealso: `PetscFE`, `PetscFEView()`
    288: @*/
    289: PetscErrorCode PetscFEDestroy(PetscFE *fem)
    290: {
    291:   PetscFunctionBegin;
    292:   if (!*fem) PetscFunctionReturn(PETSC_SUCCESS);

    295:   if (--((PetscObject)*fem)->refct > 0) {
    296:     *fem = NULL;
    297:     PetscFunctionReturn(PETSC_SUCCESS);
    298:   }
    299:   ((PetscObject)*fem)->refct = 0;

    301:   if ((*fem)->subspaces) {
    302:     PetscInt dim, d;

    304:     PetscCall(PetscDualSpaceGetDimension((*fem)->dualSpace, &dim));
    305:     for (d = 0; d < dim; ++d) PetscCall(PetscFEDestroy(&(*fem)->subspaces[d]));
    306:   }
    307:   PetscCall(PetscFree((*fem)->subspaces));
    308:   PetscCall(PetscFree((*fem)->invV));
    309:   PetscCall(PetscTabulationDestroy(&(*fem)->T));
    310:   PetscCall(PetscTabulationDestroy(&(*fem)->Tf));
    311:   PetscCall(PetscTabulationDestroy(&(*fem)->Tc));
    312:   PetscCall(PetscSpaceDestroy(&(*fem)->basisSpace));
    313:   PetscCall(PetscDualSpaceDestroy(&(*fem)->dualSpace));
    314:   PetscCall(PetscQuadratureDestroy(&(*fem)->quadrature));
    315:   PetscCall(PetscQuadratureDestroy(&(*fem)->faceQuadrature));
    316: #ifdef PETSC_HAVE_LIBCEED
    317:   PetscCallCEED(CeedBasisDestroy(&(*fem)->ceedBasis));
    318:   PetscCallCEED(CeedDestroy(&(*fem)->ceed));
    319: #endif

    321:   PetscTryTypeMethod(*fem, destroy);
    322:   PetscCall(PetscHeaderDestroy(fem));
    323:   PetscFunctionReturn(PETSC_SUCCESS);
    324: }

    326: /*@
    327:   PetscFECreate - Creates an empty `PetscFE` object. The type can then be set with `PetscFESetType()`.

    329:   Collective

    331:   Input Parameter:
    332: . comm - The communicator for the `PetscFE` object

    334:   Output Parameter:
    335: . fem - The `PetscFE` object

    337:   Level: beginner

    339: .seealso: `PetscFE`, `PetscFEType`, `PetscFESetType()`, `PetscFECreateDefault()`, `PETSCFEGALERKIN`
    340: @*/
    341: PetscErrorCode PetscFECreate(MPI_Comm comm, PetscFE *fem)
    342: {
    343:   PetscFE f;

    345:   PetscFunctionBegin;
    346:   PetscAssertPointer(fem, 2);
    347:   PetscCall(PetscCitationsRegister(FECitation, &FEcite));
    348:   PetscCall(PetscFEInitializePackage());

    350:   PetscCall(PetscHeaderCreate(f, PETSCFE_CLASSID, "PetscFE", "Finite Element", "PetscFE", comm, PetscFEDestroy, PetscFEView));

    352:   f->basisSpace    = NULL;
    353:   f->dualSpace     = NULL;
    354:   f->numComponents = 1;
    355:   f->subspaces     = NULL;
    356:   f->invV          = NULL;
    357:   f->T             = NULL;
    358:   f->Tf            = NULL;
    359:   f->Tc            = NULL;
    360:   PetscCall(PetscArrayzero(&f->quadrature, 1));
    361:   PetscCall(PetscArrayzero(&f->faceQuadrature, 1));
    362:   f->blockSize  = 0;
    363:   f->numBlocks  = 1;
    364:   f->batchSize  = 0;
    365:   f->numBatches = 1;

    367:   *fem = f;
    368:   PetscFunctionReturn(PETSC_SUCCESS);
    369: }

    371: /*@
    372:   PetscFEGetSpatialDimension - Returns the spatial dimension of the element

    374:   Not Collective

    376:   Input Parameter:
    377: . fem - The `PetscFE` object

    379:   Output Parameter:
    380: . dim - The spatial dimension

    382:   Level: intermediate

    384: .seealso: `PetscFE`, `PetscFECreate()`
    385: @*/
    386: PetscErrorCode PetscFEGetSpatialDimension(PetscFE fem, PetscInt *dim)
    387: {
    388:   DM dm;

    390:   PetscFunctionBegin;
    392:   PetscAssertPointer(dim, 2);
    393:   PetscCall(PetscDualSpaceGetDM(fem->dualSpace, &dm));
    394:   PetscCall(DMGetDimension(dm, dim));
    395:   PetscFunctionReturn(PETSC_SUCCESS);
    396: }

    398: /*@
    399:   PetscFESetNumComponents - Sets the number of field components in the element

    401:   Not Collective

    403:   Input Parameters:
    404: + fem  - The `PetscFE` object
    405: - comp - The number of field components

    407:   Level: intermediate

    409: .seealso: `PetscFE`, `PetscFECreate()`, `PetscFEGetSpatialDimension()`, `PetscFEGetNumComponents()`
    410: @*/
    411: PetscErrorCode PetscFESetNumComponents(PetscFE fem, PetscInt comp)
    412: {
    413:   PetscFunctionBegin;
    415:   fem->numComponents = comp;
    416:   PetscFunctionReturn(PETSC_SUCCESS);
    417: }

    419: /*@
    420:   PetscFEGetNumComponents - Returns the number of components in the element

    422:   Not Collective

    424:   Input Parameter:
    425: . fem - The `PetscFE` object

    427:   Output Parameter:
    428: . comp - The number of field components

    430:   Level: intermediate

    432: .seealso: `PetscFE`, `PetscFECreate()`, `PetscFEGetSpatialDimension()`
    433: @*/
    434: PetscErrorCode PetscFEGetNumComponents(PetscFE fem, PetscInt *comp)
    435: {
    436:   PetscFunctionBegin;
    438:   PetscAssertPointer(comp, 2);
    439:   *comp = fem->numComponents;
    440:   PetscFunctionReturn(PETSC_SUCCESS);
    441: }

    443: /*@
    444:   PetscFESetTileSizes - Sets the tile sizes for evaluation

    446:   Not Collective

    448:   Input Parameters:
    449: + fem        - The `PetscFE` object
    450: . blockSize  - The number of elements in a block
    451: . numBlocks  - The number of blocks in a batch
    452: . batchSize  - The number of elements in a batch
    453: - numBatches - The number of batches in a chunk

    455:   Level: intermediate

    457: .seealso: `PetscFE`, `PetscFECreate()`, `PetscFEGetTileSizes()`
    458: @*/
    459: PetscErrorCode PetscFESetTileSizes(PetscFE fem, PetscInt blockSize, PetscInt numBlocks, PetscInt batchSize, PetscInt numBatches)
    460: {
    461:   PetscFunctionBegin;
    463:   fem->blockSize  = blockSize;
    464:   fem->numBlocks  = numBlocks;
    465:   fem->batchSize  = batchSize;
    466:   fem->numBatches = numBatches;
    467:   PetscFunctionReturn(PETSC_SUCCESS);
    468: }

    470: /*@
    471:   PetscFEGetTileSizes - Returns the tile sizes for evaluation

    473:   Not Collective

    475:   Input Parameter:
    476: . fem - The `PetscFE` object

    478:   Output Parameters:
    479: + blockSize  - The number of elements in a block, pass `NULL` if not needed
    480: . numBlocks  - The number of blocks in a batch, pass `NULL` if not needed
    481: . batchSize  - The number of elements in a batch, pass `NULL` if not needed
    482: - numBatches - The number of batches in a chunk, pass `NULL` if not needed

    484:   Level: intermediate

    486: .seealso: `PetscFE`, `PetscFECreate()`, `PetscFESetTileSizes()`
    487: @*/
    488: PetscErrorCode PetscFEGetTileSizes(PetscFE fem, PeOp PetscInt *blockSize, PeOp PetscInt *numBlocks, PeOp PetscInt *batchSize, PeOp PetscInt *numBatches)
    489: {
    490:   PetscFunctionBegin;
    492:   if (blockSize) PetscAssertPointer(blockSize, 2);
    493:   if (numBlocks) PetscAssertPointer(numBlocks, 3);
    494:   if (batchSize) PetscAssertPointer(batchSize, 4);
    495:   if (numBatches) PetscAssertPointer(numBatches, 5);
    496:   if (blockSize) *blockSize = fem->blockSize;
    497:   if (numBlocks) *numBlocks = fem->numBlocks;
    498:   if (batchSize) *batchSize = fem->batchSize;
    499:   if (numBatches) *numBatches = fem->numBatches;
    500:   PetscFunctionReturn(PETSC_SUCCESS);
    501: }

    503: /*@
    504:   PetscFEGetBasisSpace - Returns the `PetscSpace` used for the approximation of the solution for the `PetscFE`

    506:   Not Collective

    508:   Input Parameter:
    509: . fem - The `PetscFE` object

    511:   Output Parameter:
    512: . sp - The `PetscSpace` object

    514:   Level: intermediate

    516: .seealso: `PetscFE`, `PetscSpace`, `PetscFECreate()`
    517: @*/
    518: PetscErrorCode PetscFEGetBasisSpace(PetscFE fem, PetscSpace *sp)
    519: {
    520:   PetscFunctionBegin;
    522:   PetscAssertPointer(sp, 2);
    523:   *sp = fem->basisSpace;
    524:   PetscFunctionReturn(PETSC_SUCCESS);
    525: }

    527: /*@
    528:   PetscFESetBasisSpace - Sets the `PetscSpace` used for the approximation of the solution

    530:   Not Collective

    532:   Input Parameters:
    533: + fem - The `PetscFE` object
    534: - sp  - The `PetscSpace` object

    536:   Level: intermediate

    538: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscFECreate()`, `PetscFESetDualSpace()`
    539: @*/
    540: PetscErrorCode PetscFESetBasisSpace(PetscFE fem, PetscSpace sp)
    541: {
    542:   PetscFunctionBegin;
    545:   PetscCall(PetscSpaceDestroy(&fem->basisSpace));
    546:   fem->basisSpace = sp;
    547:   PetscCall(PetscObjectReference((PetscObject)fem->basisSpace));
    548:   PetscFunctionReturn(PETSC_SUCCESS);
    549: }

    551: /*@
    552:   PetscFEGetDualSpace - Returns the `PetscDualSpace` used to define the inner product for a `PetscFE`

    554:   Not Collective

    556:   Input Parameter:
    557: . fem - The `PetscFE` object

    559:   Output Parameter:
    560: . sp - The `PetscDualSpace` object

    562:   Level: intermediate

    564: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscFECreate()`
    565: @*/
    566: PetscErrorCode PetscFEGetDualSpace(PetscFE fem, PetscDualSpace *sp)
    567: {
    568:   PetscFunctionBegin;
    570:   PetscAssertPointer(sp, 2);
    571:   *sp = fem->dualSpace;
    572:   PetscFunctionReturn(PETSC_SUCCESS);
    573: }

    575: /*@
    576:   PetscFESetDualSpace - Sets the `PetscDualSpace` used to define the inner product

    578:   Not Collective

    580:   Input Parameters:
    581: + fem - The `PetscFE` object
    582: - sp  - The `PetscDualSpace` object

    584:   Level: intermediate

    586: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscFECreate()`, `PetscFESetBasisSpace()`
    587: @*/
    588: PetscErrorCode PetscFESetDualSpace(PetscFE fem, PetscDualSpace sp)
    589: {
    590:   PetscFunctionBegin;
    593:   PetscCall(PetscDualSpaceDestroy(&fem->dualSpace));
    594:   fem->dualSpace = sp;
    595:   PetscCall(PetscObjectReference((PetscObject)fem->dualSpace));
    596:   PetscFunctionReturn(PETSC_SUCCESS);
    597: }

    599: /*@
    600:   PetscFEGetQuadrature - Returns the `PetscQuadrature` used to calculate inner products

    602:   Not Collective

    604:   Input Parameter:
    605: . fem - The `PetscFE` object

    607:   Output Parameter:
    608: . q - The `PetscQuadrature` object

    610:   Level: intermediate

    612: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`
    613: @*/
    614: PetscErrorCode PetscFEGetQuadrature(PetscFE fem, PetscQuadrature *q)
    615: {
    616:   PetscFunctionBegin;
    618:   PetscAssertPointer(q, 2);
    619:   *q = fem->quadrature;
    620:   PetscFunctionReturn(PETSC_SUCCESS);
    621: }

    623: /*@
    624:   PetscFESetQuadrature - Sets the `PetscQuadrature` used to calculate inner products

    626:   Not Collective

    628:   Input Parameters:
    629: + fem - The `PetscFE` object
    630: - q   - The `PetscQuadrature` object

    632:   Level: intermediate

    634: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`, `PetscFEGetFaceQuadrature()`
    635: @*/
    636: PetscErrorCode PetscFESetQuadrature(PetscFE fem, PetscQuadrature q)
    637: {
    638:   PetscInt Nc, qNc;

    640:   PetscFunctionBegin;
    642:   if (q == fem->quadrature) PetscFunctionReturn(PETSC_SUCCESS);
    643:   PetscCall(PetscFEGetNumComponents(fem, &Nc));
    644:   PetscCall(PetscQuadratureGetNumComponents(q, &qNc));
    645:   PetscCheck(!(qNc != 1) || !(Nc != qNc), PetscObjectComm((PetscObject)fem), PETSC_ERR_ARG_SIZ, "FE components %" PetscInt_FMT " != Quadrature components %" PetscInt_FMT " and non-scalar quadrature", Nc, qNc);
    646:   PetscCall(PetscTabulationDestroy(&fem->T));
    647:   PetscCall(PetscTabulationDestroy(&fem->Tc));
    648:   PetscCall(PetscObjectReference((PetscObject)q));
    649:   PetscCall(PetscQuadratureDestroy(&fem->quadrature));
    650:   fem->quadrature = q;
    651:   PetscFunctionReturn(PETSC_SUCCESS);
    652: }

    654: /*@
    655:   PetscFEGetFaceQuadrature - Returns the `PetscQuadrature` used to calculate inner products on faces

    657:   Not Collective

    659:   Input Parameter:
    660: . fem - The `PetscFE` object

    662:   Output Parameter:
    663: . q - The `PetscQuadrature` object

    665:   Level: intermediate

    667:   Developer Notes:
    668:   There is a special face quadrature but not edge, likely this API would benefit from a refactorization

    670: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`, `PetscFESetQuadrature()`, `PetscFESetFaceQuadrature()`
    671: @*/
    672: PetscErrorCode PetscFEGetFaceQuadrature(PetscFE fem, PetscQuadrature *q)
    673: {
    674:   PetscFunctionBegin;
    676:   PetscAssertPointer(q, 2);
    677:   *q = fem->faceQuadrature;
    678:   PetscFunctionReturn(PETSC_SUCCESS);
    679: }

    681: /*@
    682:   PetscFESetFaceQuadrature - Sets the `PetscQuadrature` used to calculate inner products on faces

    684:   Not Collective

    686:   Input Parameters:
    687: + fem - The `PetscFE` object
    688: - q   - The `PetscQuadrature` object

    690:   Level: intermediate

    692: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`, `PetscFESetQuadrature()`
    693: @*/
    694: PetscErrorCode PetscFESetFaceQuadrature(PetscFE fem, PetscQuadrature q)
    695: {
    696:   PetscInt Nc, qNc;

    698:   PetscFunctionBegin;
    700:   if (q == fem->faceQuadrature) PetscFunctionReturn(PETSC_SUCCESS);
    701:   PetscCall(PetscFEGetNumComponents(fem, &Nc));
    702:   PetscCall(PetscQuadratureGetNumComponents(q, &qNc));
    703:   PetscCheck(!(qNc != 1) || !(Nc != qNc), PetscObjectComm((PetscObject)fem), PETSC_ERR_ARG_SIZ, "FE components %" PetscInt_FMT " != Quadrature components %" PetscInt_FMT " and non-scalar quadrature", Nc, qNc);
    704:   PetscCall(PetscTabulationDestroy(&fem->Tf));
    705:   PetscCall(PetscObjectReference((PetscObject)q));
    706:   PetscCall(PetscQuadratureDestroy(&fem->faceQuadrature));
    707:   fem->faceQuadrature = q;
    708:   PetscFunctionReturn(PETSC_SUCCESS);
    709: }

    711: /*@
    712:   PetscFECopyQuadrature - Copy both volumetric and surface quadrature to a new `PetscFE`

    714:   Not Collective

    716:   Input Parameters:
    717: + sfe - The `PetscFE` source for the quadratures
    718: - tfe - The `PetscFE` target for the quadratures

    720:   Level: intermediate

    722: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`, `PetscFECreate()`, `PetscFESetQuadrature()`, `PetscFESetFaceQuadrature()`
    723: @*/
    724: PetscErrorCode PetscFECopyQuadrature(PetscFE sfe, PetscFE tfe)
    725: {
    726:   PetscQuadrature q;

    728:   PetscFunctionBegin;
    731:   PetscCall(PetscFEGetQuadrature(sfe, &q));
    732:   PetscCall(PetscFESetQuadrature(tfe, q));
    733:   PetscCall(PetscFEGetFaceQuadrature(sfe, &q));
    734:   PetscCall(PetscFESetFaceQuadrature(tfe, q));
    735:   PetscFunctionReturn(PETSC_SUCCESS);
    736: }

    738: /*@C
    739:   PetscFEGetNumDof - Returns the number of dofs (dual basis vectors) associated to mesh points on the reference cell of a given dimension

    741:   Not Collective

    743:   Input Parameter:
    744: . fem - The `PetscFE` object

    746:   Output Parameter:
    747: . numDof - Array of length `dim` with the number of dofs in each dimension

    749:   Level: intermediate

    751: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscFECreate()`
    752: @*/
    753: PetscErrorCode PetscFEGetNumDof(PetscFE fem, const PetscInt *numDof[])
    754: {
    755:   PetscFunctionBegin;
    757:   PetscAssertPointer(numDof, 2);
    758:   PetscCall(PetscDualSpaceGetNumDof(fem->dualSpace, numDof));
    759:   PetscFunctionReturn(PETSC_SUCCESS);
    760: }

    762: /*@C
    763:   PetscFEGetCellTabulation - Returns the tabulation of the basis functions at the quadrature points on the reference cell

    765:   Not Collective

    767:   Input Parameters:
    768: + fem - The `PetscFE` object
    769: - k   - The highest derivative we need to tabulate, very often 1

    771:   Output Parameter:
    772: . T - The basis function values and derivatives at quadrature points

    774:   Level: intermediate

    776:   Note:
    777: .vb
    778:   T->T[0] = B[(p*pdim + i)*Nc + c] is the value at point p for basis function i and component c
    779:   T->T[1] = D[((p*pdim + i)*Nc + c)*dim + d] is the derivative value at point p for basis function i, component c, in direction d
    780:   T->T[2] = H[(((p*pdim + i)*Nc + c)*dim + d)*dim + e] is the Hessian value at point p for basis function i, component c, in directions d and e
    781: .ve

    783: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscTabulation`, `PetscFECreateTabulation()`, `PetscTabulationDestroy()`
    784: @*/
    785: PetscErrorCode PetscFEGetCellTabulation(PetscFE fem, PetscInt k, PetscTabulation *T)
    786: {
    787:   PetscInt         npoints;
    788:   const PetscReal *points;

    790:   PetscFunctionBegin;
    792:   PetscAssertPointer(T, 3);
    793:   PetscCall(PetscQuadratureGetData(fem->quadrature, NULL, NULL, &npoints, &points, NULL));
    794:   if (!fem->T) PetscCall(PetscFECreateTabulation(fem, 1, npoints, points, k, &fem->T));
    795:   PetscCheck(!fem->T || k <= fem->T->K || (!fem->T->cdim && !fem->T->K), PetscObjectComm((PetscObject)fem), PETSC_ERR_ARG_OUTOFRANGE, "Requested %" PetscInt_FMT " derivatives, but only tabulated %" PetscInt_FMT, k, fem->T->K);
    796:   *T = fem->T;
    797:   PetscFunctionReturn(PETSC_SUCCESS);
    798: }

    800: /*@
    801:   PetscFEExpandFaceQuadrature - Expand a face quadrature into a cell quadrature by mapping the face
    802:   quadrature points and weights through each face of the cell reference geometry.

    804:   Not Collective

    806:   Input Parameters:
    807: + fe - the `PetscFE` object whose cell geometry defines the faces
    808: - fq - the face quadrature to expand

    810:   Output Parameter:
    811: . efq - the expanded quadrature covering all faces of the cell

    813:   Level: developer

    815: .seealso: `PetscFE`, `PetscQuadrature`, `PetscFECreateFaceQuadrature()`, `PetscFEGetQuadrature()`
    816: @*/
    817: PetscErrorCode PetscFEExpandFaceQuadrature(PetscFE fe, PetscQuadrature fq, PetscQuadrature *efq)
    818: {
    819:   DM               dm;
    820:   PetscDualSpace   sp;
    821:   const PetscInt  *faces;
    822:   const PetscReal *points, *weights;
    823:   DMPolytopeType   ct;
    824:   PetscReal       *facePoints, *faceWeights;
    825:   PetscInt         dim, cStart, Nf, Nc, Np, order;

    827:   PetscFunctionBegin;
    828:   PetscCall(PetscFEGetDualSpace(fe, &sp));
    829:   PetscCall(PetscDualSpaceGetDM(sp, &dm));
    830:   PetscCall(DMGetDimension(dm, &dim));
    831:   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, NULL));
    832:   PetscCall(DMPlexGetConeSize(dm, cStart, &Nf));
    833:   PetscCall(DMPlexGetCone(dm, cStart, &faces));
    834:   PetscCall(PetscQuadratureGetData(fq, NULL, &Nc, &Np, &points, &weights));
    835:   PetscCall(PetscMalloc1(Nf * Np * dim, &facePoints));
    836:   PetscCall(PetscMalloc1(Nf * Np * Nc, &faceWeights));
    837:   for (PetscInt f = 0; f < Nf; ++f) {
    838:     const PetscReal xi0[3] = {-1., -1., -1.};
    839:     PetscReal       v0[3], J[9], detJ;

    841:     PetscCall(DMPlexComputeCellGeometryFEM(dm, faces[f], NULL, v0, J, NULL, &detJ));
    842:     for (PetscInt q = 0; q < Np; ++q) {
    843:       CoordinatesRefToReal(dim, dim - 1, xi0, v0, J, &points[q * (dim - 1)], &facePoints[(f * Np + q) * dim]);
    844:       for (PetscInt c = 0; c < Nc; ++c) faceWeights[(f * Np + q) * Nc + c] = weights[q * Nc + c];
    845:     }
    846:   }
    847:   PetscCall(PetscQuadratureCreate(PetscObjectComm((PetscObject)fq), efq));
    848:   PetscCall(PetscQuadratureGetCellType(fq, &ct));
    849:   PetscCall(PetscQuadratureSetCellType(*efq, ct));
    850:   PetscCall(PetscQuadratureGetOrder(fq, &order));
    851:   PetscCall(PetscQuadratureSetOrder(*efq, order));
    852:   PetscCall(PetscQuadratureSetData(*efq, dim, Nc, Nf * Np, facePoints, faceWeights));
    853:   PetscFunctionReturn(PETSC_SUCCESS);
    854: }

    856: /*@C
    857:   PetscFEGetFaceTabulation - Returns the tabulation of the basis functions at the face quadrature points for each face of the reference cell

    859:   Not Collective

    861:   Input Parameters:
    862: + fem - The `PetscFE` object
    863: - k   - The highest derivative we need to tabulate, very often 1

    865:   Output Parameter:
    866: . Tf - The basis function values and derivatives at face quadrature points

    868:   Level: intermediate

    870:   Note:
    871: .vb
    872:   T->T[0] = Bf[((f*Nq + q)*pdim + i)*Nc + c] is the value at point f,q for basis function i and component c
    873:   T->T[1] = Df[(((f*Nq + q)*pdim + i)*Nc + c)*dim + d] is the derivative value at point f,q for basis function i, component c, in direction d
    874:   T->T[2] = Hf[((((f*Nq + q)*pdim + i)*Nc + c)*dim + d)*dim + e] is the Hessian value at point f,q for basis function i, component c, in directions d and e
    875: .ve

    877: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscTabulation`, `PetscFEGetCellTabulation()`, `PetscFECreateTabulation()`, `PetscTabulationDestroy()`
    878: @*/
    879: PetscErrorCode PetscFEGetFaceTabulation(PetscFE fem, PetscInt k, PetscTabulation *Tf)
    880: {
    881:   PetscFunctionBegin;
    883:   PetscAssertPointer(Tf, 3);
    884:   if (!fem->Tf) {
    885:     PetscQuadrature fq;

    887:     PetscCall(PetscFEGetFaceQuadrature(fem, &fq));
    888:     if (fq) {
    889:       PetscQuadrature  efq;
    890:       const PetscReal *facePoints;
    891:       PetscInt         Np, eNp;

    893:       PetscCall(PetscFEExpandFaceQuadrature(fem, fq, &efq));
    894:       PetscCall(PetscQuadratureGetData(fq, NULL, NULL, &Np, NULL, NULL));
    895:       PetscCall(PetscQuadratureGetData(efq, NULL, NULL, &eNp, &facePoints, NULL));
    896:       if (PetscDefined(USE_DEBUG)) {
    897:         PetscDualSpace sp;
    898:         DM             dm;
    899:         PetscInt       cStart, Nf;

    901:         PetscCall(PetscFEGetDualSpace(fem, &sp));
    902:         PetscCall(PetscDualSpaceGetDM(sp, &dm));
    903:         PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, NULL));
    904:         PetscCall(DMPlexGetConeSize(dm, cStart, &Nf));
    905:         PetscCheck(Nf == eNp / Np, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of faces %" PetscInt_FMT " != %" PetscInt_FMT " number of quadrature replicas", Nf, eNp / Np);
    906:       }
    907:       PetscCall(PetscFECreateTabulation(fem, eNp / Np, Np, facePoints, k, &fem->Tf));
    908:       PetscCall(PetscQuadratureDestroy(&efq));
    909:     }
    910:   }
    911:   PetscCheck(!fem->Tf || k <= fem->Tf->K, PetscObjectComm((PetscObject)fem), PETSC_ERR_ARG_OUTOFRANGE, "Requested %" PetscInt_FMT " derivatives, but only tabulated %" PetscInt_FMT, k, fem->Tf->K);
    912:   *Tf = fem->Tf;
    913:   PetscFunctionReturn(PETSC_SUCCESS);
    914: }

    916: /*@C
    917:   PetscFEGetFaceCentroidTabulation - Returns the tabulation of the basis functions at the face centroid points

    919:   Not Collective

    921:   Input Parameter:
    922: . fem - The `PetscFE` object

    924:   Output Parameter:
    925: . Tc - The basis function values at face centroid points

    927:   Level: intermediate

    929:   Note:
    930: .vb
    931:   T->T[0] = Bf[(f*pdim + i)*Nc + c] is the value at point f for basis function i and component c
    932: .ve

    934: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscTabulation`, `PetscFEGetFaceTabulation()`, `PetscFEGetCellTabulation()`, `PetscFECreateTabulation()`, `PetscTabulationDestroy()`
    935: @*/
    936: PetscErrorCode PetscFEGetFaceCentroidTabulation(PetscFE fem, PetscTabulation *Tc)
    937: {
    938:   PetscFunctionBegin;
    940:   PetscAssertPointer(Tc, 2);
    941:   if (!fem->Tc) {
    942:     PetscDualSpace  sp;
    943:     DM              dm;
    944:     const PetscInt *cone;
    945:     PetscReal      *centroids;
    946:     PetscInt        dim, numFaces, f;

    948:     PetscCall(PetscFEGetDualSpace(fem, &sp));
    949:     PetscCall(PetscDualSpaceGetDM(sp, &dm));
    950:     PetscCall(DMGetDimension(dm, &dim));
    951:     PetscCall(DMPlexGetConeSize(dm, 0, &numFaces));
    952:     PetscCall(DMPlexGetCone(dm, 0, &cone));
    953:     PetscCall(PetscMalloc1(numFaces * dim, &centroids));
    954:     for (f = 0; f < numFaces; ++f) PetscCall(DMPlexComputeCellGeometryFVM(dm, cone[f], NULL, &centroids[f * dim], NULL));
    955:     PetscCall(PetscFECreateTabulation(fem, 1, numFaces, centroids, 0, &fem->Tc));
    956:     PetscCall(PetscFree(centroids));
    957:   }
    958:   *Tc = fem->Tc;
    959:   PetscFunctionReturn(PETSC_SUCCESS);
    960: }

    962: /*@C
    963:   PetscFECreateTabulation - Creates a `PetscTabulation` object to hold the basis functions, and perhaps derivatives, at the points provided.

    965:   Not Collective

    967:   Input Parameters:
    968: + fem     - The `PetscFE` object
    969: . nrepl   - The number of replicas
    970: . npoints - The number of tabulation points in a replica
    971: . points  - The tabulation point coordinates
    972: - K       - The number of derivatives calculated

    974:   Output Parameter:
    975: . T - The `PetscTabulation` to hold the basis function values and derivatives at tabulation points

    977:   Level: intermediate

    979: .seealso: `PetscTabulation`, `PetscFEGetCellTabulation()`, `PetscTabulationDestroy()`, `PetscFEComputeTabulation()`
    980: @*/
    981: PetscErrorCode PetscFECreateTabulation(PetscFE fem, PetscInt nrepl, PetscInt npoints, const PetscReal points[], PetscInt K, PetscTabulation *T)
    982: {
    983:   DM             dm;
    984:   PetscDualSpace Q;
    985:   PetscInt       Nb;   /* Dimension of FE space P */
    986:   PetscInt       Nc;   /* Field components */
    987:   PetscInt       cdim; /* Reference coordinate dimension */
    988:   PetscInt       k;

    990:   PetscFunctionBegin;
    991:   if (!npoints || !fem->dualSpace || K < 0) {
    992:     *T = NULL;
    993:     PetscFunctionReturn(PETSC_SUCCESS);
    994:   }
    996:   PetscAssertPointer(points, 4);
    997:   PetscAssertPointer(T, 6);
    998:   PetscCall(PetscFEGetDualSpace(fem, &Q));
    999:   PetscCall(PetscDualSpaceGetDM(Q, &dm));
    1000:   PetscCall(DMGetDimension(dm, &cdim));
    1001:   PetscCall(PetscDualSpaceGetDimension(Q, &Nb));
    1002:   PetscCall(PetscFEGetNumComponents(fem, &Nc));
    1003:   PetscCall(PetscMalloc1(1, T));
    1004:   (*T)->K    = !cdim ? 0 : K;
    1005:   (*T)->Nr   = nrepl;
    1006:   (*T)->Np   = npoints;
    1007:   (*T)->Nb   = Nb;
    1008:   (*T)->Nc   = Nc;
    1009:   (*T)->cdim = cdim;
    1010:   PetscCall(PetscMalloc1((*T)->K + 1, &(*T)->T));
    1011:   for (k = 0; k <= (*T)->K; ++k) PetscCall(PetscCalloc1(nrepl * npoints * Nb * Nc * PetscPowInt(cdim, k), &(*T)->T[k]));
    1012:   PetscUseTypeMethod(fem, computetabulation, nrepl * npoints, points, K, *T);
    1013:   PetscFunctionReturn(PETSC_SUCCESS);
    1014: }

    1016: /*@C
    1017:   PetscFEComputeTabulation - Tabulates the basis functions, and perhaps derivatives, at the points provided.

    1019:   Not Collective

    1021:   Input Parameters:
    1022: + fem     - The `PetscFE` object
    1023: . npoints - The number of tabulation points
    1024: . points  - The tabulation point coordinates
    1025: . K       - The number of derivatives calculated
    1026: - T       - An existing tabulation object with enough allocated space, created with `PetscFECreateTabulation()`

    1028:   Output Parameter:
    1029: . T - The basis function values and derivatives at tabulation points

    1031:   Level: intermediate

    1033:   Note:
    1034: .vb
    1035:   T->T[0] = B[(p*pdim + i)*Nc + c] is the value at point p for basis function i and component c
    1036:   T->T[1] = D[((p*pdim + i)*Nc + c)*dim + d] is the derivative value at point p for basis function i, component c, in direction d
    1037:   T->T[2] = H[(((p*pdim + i)*Nc + c)*dim + d)*dim + e] is the Hessian value at point p for basis function i, component c, in directions d and e
    1038: .ve

    1040: .seealso: `PetscTabulation`, `PetscFEGetCellTabulation()`, `PetscTabulationDestroy()`, `PetscFECreateTabulation()`
    1041: @*/
    1042: PetscErrorCode PetscFEComputeTabulation(PetscFE fem, PetscInt npoints, const PetscReal points[], PetscInt K, PetscTabulation T)
    1043: {
    1044:   PetscFunctionBeginHot;
    1045:   if (!npoints || !fem->dualSpace || K < 0) PetscFunctionReturn(PETSC_SUCCESS);
    1047:   PetscAssertPointer(points, 3);
    1048:   PetscAssertPointer(T, 5);
    1049:   if (PetscDefined(USE_DEBUG)) {
    1050:     DM             dm;
    1051:     PetscDualSpace Q;
    1052:     PetscInt       Nb;   /* Dimension of FE space P */
    1053:     PetscInt       Nc;   /* Field components */
    1054:     PetscInt       cdim; /* Reference coordinate dimension */

    1056:     PetscCall(PetscFEGetDualSpace(fem, &Q));
    1057:     PetscCall(PetscDualSpaceGetDM(Q, &dm));
    1058:     PetscCall(DMGetDimension(dm, &cdim));
    1059:     PetscCall(PetscDualSpaceGetDimension(Q, &Nb));
    1060:     PetscCall(PetscFEGetNumComponents(fem, &Nc));
    1061:     PetscCheck(T->K == (!cdim ? 0 : K), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation K %" PetscInt_FMT " must match requested K %" PetscInt_FMT, T->K, !cdim ? 0 : K);
    1062:     PetscCheck(T->Nb == Nb, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation Nb %" PetscInt_FMT " must match requested Nb %" PetscInt_FMT, T->Nb, Nb);
    1063:     PetscCheck(T->Nc == Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation Nc %" PetscInt_FMT " must match requested Nc %" PetscInt_FMT, T->Nc, Nc);
    1064:     PetscCheck(T->cdim == cdim, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Tabulation cdim %" PetscInt_FMT " must match requested cdim %" PetscInt_FMT, T->cdim, cdim);
    1065:   }
    1066:   T->Nr = 1;
    1067:   T->Np = npoints;
    1068:   PetscUseTypeMethod(fem, computetabulation, npoints, points, K, T);
    1069:   PetscFunctionReturn(PETSC_SUCCESS);
    1070: }

    1072: /*@
    1073:   PetscTabulationDestroy - Frees memory from the associated tabulation.

    1075:   Not Collective

    1077:   Input Parameter:
    1078: . T - The tabulation

    1080:   Level: intermediate

    1082: .seealso: `PetscTabulation`, `PetscFECreateTabulation()`, `PetscFEGetCellTabulation()`
    1083: @*/
    1084: PetscErrorCode PetscTabulationDestroy(PetscTabulation *T)
    1085: {
    1086:   PetscInt k;

    1088:   PetscFunctionBegin;
    1089:   PetscAssertPointer(T, 1);
    1090:   if (!T || !*T) PetscFunctionReturn(PETSC_SUCCESS);
    1091:   for (k = 0; k <= (*T)->K; ++k) PetscCall(PetscFree((*T)->T[k]));
    1092:   PetscCall(PetscFree((*T)->T));
    1093:   PetscCall(PetscFree(*T));
    1094:   *T = NULL;
    1095:   PetscFunctionReturn(PETSC_SUCCESS);
    1096: }

    1098: static PetscErrorCode PetscFECreatePointTraceDefault_Internal(PetscFE fe, PetscInt refPoint, PetscFE *trFE)
    1099: {
    1100:   PetscSpace      bsp, bsubsp;
    1101:   PetscDualSpace  dsp, dsubsp;
    1102:   PetscInt        dim, depth, numComp, i, j, coneSize, order;
    1103:   DM              dm;
    1104:   DMLabel         label;
    1105:   PetscReal      *xi, *v, *J, detJ;
    1106:   const char     *name;
    1107:   PetscQuadrature origin, fullQuad, subQuad;

    1109:   PetscFunctionBegin;
    1110:   PetscCall(PetscFEGetBasisSpace(fe, &bsp));
    1111:   PetscCall(PetscFEGetDualSpace(fe, &dsp));
    1112:   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
    1113:   PetscCall(DMGetDimension(dm, &dim));
    1114:   PetscCall(DMPlexGetDepthLabel(dm, &label));
    1115:   PetscCall(DMLabelGetValue(label, refPoint, &depth));
    1116:   PetscCall(PetscCalloc1(depth, &xi));
    1117:   PetscCall(PetscMalloc1(dim, &v));
    1118:   PetscCall(PetscMalloc1(dim * dim, &J));
    1119:   for (i = 0; i < depth; i++) xi[i] = 0.;
    1120:   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, &origin));
    1121:   PetscCall(PetscQuadratureSetData(origin, depth, 0, 1, xi, NULL));
    1122:   PetscCall(DMPlexComputeCellGeometryFEM(dm, refPoint, origin, v, J, NULL, &detJ));
    1123:   /* CellGeometryFEM computes the expanded Jacobian, we want the true jacobian */
    1124:   for (i = 1; i < dim; i++) {
    1125:     for (j = 0; j < depth; j++) J[i * depth + j] = J[i * dim + j];
    1126:   }
    1127:   PetscCall(PetscQuadratureDestroy(&origin));
    1128:   PetscCall(PetscDualSpaceGetPointSubspace(dsp, refPoint, &dsubsp));
    1129:   PetscCall(PetscSpaceCreateSubspace(bsp, dsubsp, v, J, NULL, NULL, PETSC_OWN_POINTER, &bsubsp));
    1130:   PetscCall(PetscSpaceSetUp(bsubsp));
    1131:   PetscCall(PetscFECreate(PetscObjectComm((PetscObject)fe), trFE));
    1132:   PetscCall(PetscFESetType(*trFE, PETSCFEBASIC));
    1133:   PetscCall(PetscFEGetNumComponents(fe, &numComp));
    1134:   PetscCall(PetscFESetNumComponents(*trFE, numComp));
    1135:   PetscCall(PetscFESetBasisSpace(*trFE, bsubsp));
    1136:   PetscCall(PetscFESetDualSpace(*trFE, dsubsp));
    1137:   PetscCall(PetscObjectGetName((PetscObject)fe, &name));
    1138:   if (name) PetscCall(PetscFESetName(*trFE, name));
    1139:   PetscCall(PetscFEGetQuadrature(fe, &fullQuad));
    1140:   PetscCall(PetscQuadratureGetOrder(fullQuad, &order));
    1141:   PetscCall(DMPlexGetConeSize(dm, refPoint, &coneSize));
    1142:   if (coneSize == 2 * depth) PetscCall(PetscDTGaussTensorQuadrature(depth, 1, (order + 2) / 2, -1., 1., &subQuad));
    1143:   else PetscCall(PetscDTSimplexQuadrature(depth, order, PETSCDTSIMPLEXQUAD_DEFAULT, &subQuad));
    1144:   PetscCall(PetscFESetQuadrature(*trFE, subQuad));
    1145:   PetscCall(PetscFESetUp(*trFE));
    1146:   PetscCall(PetscQuadratureDestroy(&subQuad));
    1147:   PetscCall(PetscSpaceDestroy(&bsubsp));
    1148:   PetscFunctionReturn(PETSC_SUCCESS);
    1149: }

    1151: PETSC_EXTERN PetscErrorCode PetscFECreatePointTrace(PetscFE fe, PetscInt refPoint, PetscFE *trFE)
    1152: {
    1153:   PetscFunctionBegin;
    1155:   PetscAssertPointer(trFE, 3);
    1156:   if (fe->ops->createpointtrace) PetscUseTypeMethod(fe, createpointtrace, refPoint, trFE);
    1157:   else PetscCall(PetscFECreatePointTraceDefault_Internal(fe, refPoint, trFE));
    1158:   PetscFunctionReturn(PETSC_SUCCESS);
    1159: }

    1161: /*@
    1162:   PetscFECreateHeightTrace - Create the trace `PetscFE` for the first mesh point of the given height stratum.

    1164:   Not Collective

    1166:   Input Parameters:
    1167: + fe     - the `PetscFE` object
    1168: - height - the height of the stratum whose first point is used to construct the trace element

    1170:   Output Parameter:
    1171: . trFE - the trace `PetscFE`, or `NULL` if the requested height stratum is empty

    1173:   Level: developer

    1175: .seealso: `PetscFE`, `PetscFECreatePointTrace()`, `PetscFEGetHeightSubspace()`, `DMPlexGetHeightStratum()`
    1176: @*/
    1177: PetscErrorCode PetscFECreateHeightTrace(PetscFE fe, PetscInt height, PetscFE *trFE)
    1178: {
    1179:   PetscInt       hStart, hEnd;
    1180:   PetscDualSpace dsp;
    1181:   DM             dm;

    1183:   PetscFunctionBegin;
    1185:   PetscAssertPointer(trFE, 3);
    1186:   *trFE = NULL;
    1187:   PetscCall(PetscFEGetDualSpace(fe, &dsp));
    1188:   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
    1189:   PetscCall(DMPlexGetHeightStratum(dm, height, &hStart, &hEnd));
    1190:   if (hEnd <= hStart) PetscFunctionReturn(PETSC_SUCCESS);
    1191:   PetscCall(PetscFECreatePointTrace(fe, hStart, trFE));
    1192:   PetscFunctionReturn(PETSC_SUCCESS);
    1193: }

    1195: /*@
    1196:   PetscFEGetDimension - Get the dimension of the finite element space on a cell

    1198:   Not Collective

    1200:   Input Parameter:
    1201: . fem - The `PetscFE`

    1203:   Output Parameter:
    1204: . dim - The dimension

    1206:   Level: intermediate

    1208: .seealso: `PetscFE`, `PetscFECreate()`, `PetscSpaceGetDimension()`, `PetscDualSpaceGetDimension()`
    1209: @*/
    1210: PetscErrorCode PetscFEGetDimension(PetscFE fem, PetscInt *dim)
    1211: {
    1212:   PetscFunctionBegin;
    1214:   PetscAssertPointer(dim, 2);
    1215:   PetscTryTypeMethod(fem, getdimension, dim);
    1216:   PetscFunctionReturn(PETSC_SUCCESS);
    1217: }

    1219: /*@
    1220:   PetscFEPushforward - Map the reference element function to real space

    1222:   Input Parameters:
    1223: + fe     - The `PetscFE`
    1224: . fegeom - The cell geometry
    1225: . Nv     - The number of function values
    1226: - vals   - The function values

    1228:   Output Parameter:
    1229: . vals - The transformed function values

    1231:   Level: advanced

    1233:   Notes:
    1234:   This just forwards the call onto `PetscDualSpacePushforward()`.

    1236:   It only handles transformations when the embedding dimension of the geometry in `fegeom` is the same as the reference dimension.

    1238: .seealso: `PetscFE`, `PetscFEGeom`, `PetscDualSpace`, `PetscDualSpacePushforward()`
    1239: @*/
    1240: PetscErrorCode PetscFEPushforward(PetscFE fe, PetscFEGeom *fegeom, PetscInt Nv, PetscScalar vals[])
    1241: {
    1242:   PetscFunctionBeginHot;
    1243:   PetscCall(PetscDualSpacePushforward(fe->dualSpace, fegeom, Nv, fe->numComponents, vals));
    1244:   PetscFunctionReturn(PETSC_SUCCESS);
    1245: }

    1247: /*@
    1248:   PetscFEPushforwardGradient - Map the reference element function gradient to real space

    1250:   Input Parameters:
    1251: + fe     - The `PetscFE`
    1252: . fegeom - The cell geometry
    1253: . Nv     - The number of function gradient values
    1254: - vals   - The function gradient values

    1256:   Output Parameter:
    1257: . vals - The transformed function gradient values

    1259:   Level: advanced

    1261:   Notes:
    1262:   This just forwards the call onto `PetscDualSpacePushforwardGradient()`.

    1264:   It only handles transformations when the embedding dimension of the geometry in `fegeom` is the same as the reference dimension.

    1266: .seealso: `PetscFE`, `PetscFEGeom`, `PetscDualSpace`, `PetscFEPushforward()`, `PetscDualSpacePushforwardGradient()`, `PetscDualSpacePushforward()`
    1267: @*/
    1268: PetscErrorCode PetscFEPushforwardGradient(PetscFE fe, PetscFEGeom *fegeom, PetscInt Nv, PetscScalar vals[])
    1269: {
    1270:   PetscFunctionBeginHot;
    1271:   PetscCall(PetscDualSpacePushforwardGradient(fe->dualSpace, fegeom, Nv, fe->numComponents, vals));
    1272:   PetscFunctionReturn(PETSC_SUCCESS);
    1273: }

    1275: /*@
    1276:   PetscFEPushforwardHessian - Map the reference element function Hessian to real space

    1278:   Input Parameters:
    1279: + fe     - The `PetscFE`
    1280: . fegeom - The cell geometry
    1281: . Nv     - The number of function Hessian values
    1282: - vals   - The function Hessian values

    1284:   Output Parameter:
    1285: . vals - The transformed function Hessian values

    1287:   Level: advanced

    1289:   Notes:
    1290:   This just forwards the call onto `PetscDualSpacePushforwardHessian()`.

    1292:   It only handles transformations when the embedding dimension of the geometry in `fegeom` is the same as the reference dimension.

    1294:   Developer Note:
    1295:   It is unclear why all these one line convenience routines are desirable

    1297: .seealso: `PetscFE`, `PetscFEGeom`, `PetscDualSpace`, `PetscFEPushforward()`, `PetscDualSpacePushforwardHessian()`, `PetscDualSpacePushforward()`
    1298: @*/
    1299: PetscErrorCode PetscFEPushforwardHessian(PetscFE fe, PetscFEGeom *fegeom, PetscInt Nv, PetscScalar vals[])
    1300: {
    1301:   PetscFunctionBeginHot;
    1302:   PetscCall(PetscDualSpacePushforwardHessian(fe->dualSpace, fegeom, Nv, fe->numComponents, vals));
    1303:   PetscFunctionReturn(PETSC_SUCCESS);
    1304: }

    1306: /*
    1307: Purpose: Compute element vector for chunk of elements

    1309: Input:
    1310:   Sizes:
    1311:      Ne:  number of elements
    1312:      Nf:  number of fields
    1313:      PetscFE
    1314:        dim: spatial dimension
    1315:        Nb:  number of basis functions
    1316:        Nc:  number of field components
    1317:        PetscQuadrature
    1318:          Nq:  number of quadrature points

    1320:   Geometry:
    1321:      PetscFEGeom[Ne] possibly *Nq
    1322:        PetscReal v0s[dim]
    1323:        PetscReal n[dim]
    1324:        PetscReal jacobians[dim*dim]
    1325:        PetscReal jacobianInverses[dim*dim]
    1326:        PetscReal jacobianDeterminants
    1327:   FEM:
    1328:      PetscFE
    1329:        PetscQuadrature
    1330:          PetscReal   quadPoints[Nq*dim]
    1331:          PetscReal   quadWeights[Nq]
    1332:        PetscReal   basis[Nq*Nb*Nc]
    1333:        PetscReal   basisDer[Nq*Nb*Nc*dim]
    1334:      PetscScalar coefficients[Ne*Nb*Nc]
    1335:      PetscScalar elemVec[Ne*Nb*Nc]

    1337:   Problem:
    1338:      PetscInt f: the active field
    1339:      f0, f1

    1341:   Work Space:
    1342:      PetscFE
    1343:        PetscScalar f0[Nq*dim];
    1344:        PetscScalar f1[Nq*dim*dim];
    1345:        PetscScalar u[Nc];
    1346:        PetscScalar gradU[Nc*dim];
    1347:        PetscReal   x[dim];
    1348:        PetscScalar realSpaceDer[dim];

    1350: Purpose: Compute element vector for N_cb batches of elements

    1352: Input:
    1353:   Sizes:
    1354:      N_cb: Number of serial cell batches

    1356:   Geometry:
    1357:      PetscReal v0s[Ne*dim]
    1358:      PetscReal jacobians[Ne*dim*dim]        possibly *Nq
    1359:      PetscReal jacobianInverses[Ne*dim*dim] possibly *Nq
    1360:      PetscReal jacobianDeterminants[Ne]     possibly *Nq
    1361:   FEM:
    1362:      static PetscReal   quadPoints[Nq*dim]
    1363:      static PetscReal   quadWeights[Nq]
    1364:      static PetscReal   basis[Nq*Nb*Nc]
    1365:      static PetscReal   basisDer[Nq*Nb*Nc*dim]
    1366:      PetscScalar coefficients[Ne*Nb*Nc]
    1367:      PetscScalar elemVec[Ne*Nb*Nc]

    1369: ex62.c:
    1370:   PetscErrorCode PetscFEIntegrateResidualBatch(PetscInt Ne, PetscInt numFields, PetscInt field, PetscQuadrature quad[], const PetscScalar coefficients[],
    1371:                                                const PetscReal v0s[], const PetscReal jacobians[], const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[],
    1372:                                                void (*f0_func)(const PetscScalar u[], const PetscScalar gradU[], const PetscReal x[], PetscScalar f0[]),
    1373:                                                void (*f1_func)(const PetscScalar u[], const PetscScalar gradU[], const PetscReal x[], PetscScalar f1[]), PetscScalar elemVec[])

    1375: ex52.c:
    1376:   PetscErrorCode IntegrateLaplacianBatchCPU(PetscInt Ne, PetscInt Nb, const PetscScalar coefficients[], const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[], PetscInt Nq, const PetscReal quadPoints[], const PetscReal quadWeights[], const PetscReal basisTabulation[], const PetscReal basisDerTabulation[], PetscScalar elemVec[], AppCtx *user)
    1377:   PetscErrorCode IntegrateElasticityBatchCPU(PetscInt Ne, PetscInt Nb, PetscInt Ncomp, const PetscScalar coefficients[], const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[], PetscInt Nq, const PetscReal quadPoints[], const PetscReal quadWeights[], const PetscReal basisTabulation[], const PetscReal basisDerTabulation[], PetscScalar elemVec[], AppCtx *user)

    1379: ex52_integrateElement.cu
    1380: __global__ void integrateElementQuadrature(int N_cb, realType *coefficients, realType *jacobianInverses, realType *jacobianDeterminants, realType *elemVec)

    1382: PETSC_EXTERN PetscErrorCode IntegrateElementBatchGPU(PetscInt spatial_dim, PetscInt Ne, PetscInt Ncb, PetscInt Nbc, PetscInt Nbl, const PetscScalar coefficients[],
    1383:                                                      const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[], PetscScalar elemVec[],
    1384:                                                      PetscLogEvent event, PetscInt debug, PetscInt pde_op)

    1386: ex52_integrateElementOpenCL.c:
    1387: PETSC_EXTERN PetscErrorCode IntegrateElementBatchGPU(PetscInt spatial_dim, PetscInt Ne, PetscInt Ncb, PetscInt Nbc, PetscInt N_bl, const PetscScalar coefficients[],
    1388:                                                      const PetscReal jacobianInverses[], const PetscReal jacobianDeterminants[], PetscScalar elemVec[],
    1389:                                                      PetscLogEvent event, PetscInt debug, PetscInt pde_op)

    1391: __kernel void integrateElementQuadrature(int N_cb, __global float *coefficients, __global float *jacobianInverses, __global float *jacobianDeterminants, __global float *elemVec)
    1392: */

    1394: /*@
    1395:   PetscFEIntegrate - Produce the integral for the given field for a chunk of elements by quadrature integration

    1397:   Not Collective

    1399:   Input Parameters:
    1400: + prob            - The `PetscDS` specifying the discretizations and continuum functions
    1401: . field           - The field being integrated
    1402: . Ne              - The number of elements in the chunk
    1403: . cgeom           - The cell geometry for each cell in the chunk
    1404: . coefficients    - The array of FEM basis coefficients for the elements
    1405: . probAux         - The `PetscDS` specifying the auxiliary discretizations
    1406: - coefficientsAux - The array of FEM auxiliary basis coefficients for the elements

    1408:   Output Parameter:
    1409: . integral - the integral for this field

    1411:   Level: intermediate

    1413: .seealso: `PetscFE`, `PetscDS`, `PetscFEIntegrateResidual()`, `PetscFEIntegrateBd()`
    1414: @*/
    1415: PetscErrorCode PetscFEIntegrate(PetscDS prob, PetscInt field, PetscInt Ne, PetscFEGeom *cgeom, const PetscScalar coefficients[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscScalar integral[])
    1416: {
    1417:   PetscFE fe;

    1419:   PetscFunctionBegin;
    1421:   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
    1422:   if (fe->ops->integrate) PetscCall((*fe->ops->integrate)(prob, field, Ne, cgeom, coefficients, probAux, coefficientsAux, integral));
    1423:   PetscFunctionReturn(PETSC_SUCCESS);
    1424: }

    1426: /*@C
    1427:   PetscFEIntegrateBd - Produce the integral for the given field for a chunk of elements by quadrature integration

    1429:   Not Collective

    1431:   Input Parameters:
    1432: + prob            - The `PetscDS` specifying the discretizations and continuum functions
    1433: . field           - The field being integrated
    1434: . obj_func        - The function to be integrated
    1435: . Ne              - The number of elements in the chunk
    1436: . geom            - The face geometry for each face in the chunk
    1437: . coefficients    - The array of FEM basis coefficients for the elements
    1438: . probAux         - The `PetscDS` specifying the auxiliary discretizations
    1439: - coefficientsAux - The array of FEM auxiliary basis coefficients for the elements

    1441:   Output Parameter:
    1442: . integral - the integral for this field

    1444:   Level: intermediate

    1446: .seealso: `PetscFE`, `PetscDS`, `PetscFEIntegrateResidual()`, `PetscFEIntegrate()`
    1447: @*/
    1448: PetscErrorCode PetscFEIntegrateBd(PetscDS prob, PetscInt field, void (*obj_func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscInt Ne, PetscFEGeom *geom, const PetscScalar coefficients[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscScalar integral[])
    1449: {
    1450:   PetscFE fe;

    1452:   PetscFunctionBegin;
    1454:   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
    1455:   if (fe->ops->integratebd) PetscCall((*fe->ops->integratebd)(prob, field, obj_func, Ne, geom, coefficients, probAux, coefficientsAux, integral));
    1456:   PetscFunctionReturn(PETSC_SUCCESS);
    1457: }

    1459: /*@
    1460:   PetscFEIntegrateResidual - Produce the element residual vector for a chunk of elements by quadrature integration

    1462:   Not Collective

    1464:   Input Parameters:
    1465: + ds              - The `PetscDS` specifying the discretizations and continuum functions
    1466: . key             - The (label+value, field) being integrated
    1467: . Ne              - The number of elements in the chunk
    1468: . cgeom           - The cell geometry for each cell in the chunk
    1469: . coefficients    - The array of FEM basis coefficients for the elements
    1470: . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
    1471: . probAux         - The `PetscDS` specifying the auxiliary discretizations
    1472: . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
    1473: - t               - The time

    1475:   Output Parameter:
    1476: . elemVec - the element residual vectors from each element

    1478:   Level: intermediate

    1480:   Note:
    1481: .vb
    1482:   Loop over batch of elements (e):
    1483:     Loop over quadrature points (q):
    1484:       Make u_q and gradU_q (loops over fields,Nb,Ncomp) and x_q
    1485:       Call f_0 and f_1
    1486:     Loop over element vector entries (f,fc --> i):
    1487:       elemVec[i] += \psi^{fc}_f(q) f0_{fc}(u, \nabla u) + \nabla\psi^{fc}_f(q) \cdot f1_{fc,df}(u, \nabla u)
    1488: .ve

    1490: .seealso: `PetscFEIntegrateBdResidual()`
    1491: @*/
    1492: PetscErrorCode PetscFEIntegrateResidual(PetscDS ds, PetscFormKey key, PetscInt Ne, PetscFEGeom *cgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscScalar elemVec[])
    1493: {
    1494:   PetscFE fe;

    1496:   PetscFunctionBeginHot;
    1498:   PetscCall(PetscDSGetDiscretization(ds, key.field, (PetscObject *)&fe));
    1499:   if (fe->ops->integrateresidual) PetscCall((*fe->ops->integrateresidual)(ds, key, Ne, cgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, elemVec));
    1500:   PetscFunctionReturn(PETSC_SUCCESS);
    1501: }

    1503: /*@
    1504:   PetscFEIntegrateBdResidual - Produce the element residual vector for a chunk of elements by quadrature integration over a boundary

    1506:   Not Collective

    1508:   Input Parameters:
    1509: + ds              - The `PetscDS` specifying the discretizations and continuum functions
    1510: . wf              - The PetscWeakForm object holding the pointwise functions
    1511: . key             - The (label+value, field) being integrated
    1512: . Ne              - The number of elements in the chunk
    1513: . fgeom           - The face geometry for each cell in the chunk
    1514: . coefficients    - The array of FEM basis coefficients for the elements
    1515: . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
    1516: . probAux         - The `PetscDS` specifying the auxiliary discretizations
    1517: . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
    1518: - t               - The time

    1520:   Output Parameter:
    1521: . elemVec - the element residual vectors from each element

    1523:   Level: intermediate

    1525: .seealso: `PetscFEIntegrateResidual()`
    1526: @*/
    1527: PetscErrorCode PetscFEIntegrateBdResidual(PetscDS ds, PetscWeakForm wf, PetscFormKey key, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscScalar elemVec[])
    1528: {
    1529:   PetscFE fe;

    1531:   PetscFunctionBegin;
    1533:   PetscCall(PetscDSGetDiscretization(ds, key.field, (PetscObject *)&fe));
    1534:   if (fe->ops->integratebdresidual) PetscCall((*fe->ops->integratebdresidual)(ds, wf, key, Ne, fgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, elemVec));
    1535:   PetscFunctionReturn(PETSC_SUCCESS);
    1536: }

    1538: /*@
    1539:   PetscFEIntegrateHybridResidual - Produce the element residual vector for a chunk of hybrid element faces by quadrature integration

    1541:   Not Collective

    1543:   Input Parameters:
    1544: + ds              - The `PetscDS` specifying the discretizations and continuum functions
    1545: . dsIn            - The `PetscDS` specifying the discretizations and continuum functions for input
    1546: . key             - The (label+value, field) being integrated
    1547: . s               - The side of the cell being integrated, 0 for negative and 1 for positive
    1548: . Ne              - The number of elements in the chunk
    1549: . fgeom           - The face geometry for each cell in the chunk
    1550: . cgeom           - The cell geometry for each neighbor cell in the chunk
    1551: . coefficients    - The array of FEM basis coefficients for the elements
    1552: . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
    1553: . probAux         - The `PetscDS` specifying the auxiliary discretizations
    1554: . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
    1555: - t               - The time

    1557:   Output Parameter:
    1558: . elemVec - the element residual vectors from each element

    1560:   Level: developer

    1562: .seealso: `PetscFEIntegrateResidual()`
    1563: @*/
    1564: PetscErrorCode PetscFEIntegrateHybridResidual(PetscDS ds, PetscDS dsIn, PetscFormKey key, PetscInt s, PetscInt Ne, PetscFEGeom *fgeom, PetscFEGeom *cgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscScalar elemVec[])
    1565: {
    1566:   PetscFE fe;

    1568:   PetscFunctionBegin;
    1571:   PetscCall(PetscDSGetDiscretization(ds, key.field, (PetscObject *)&fe));
    1572:   if (fe->ops->integratehybridresidual) PetscCall((*fe->ops->integratehybridresidual)(ds, dsIn, key, s, Ne, fgeom, cgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, elemVec));
    1573:   PetscFunctionReturn(PETSC_SUCCESS);
    1574: }

    1576: /*@
    1577:   PetscFEIntegrateJacobian - Produce the element Jacobian for a chunk of elements by quadrature integration

    1579:   Not Collective

    1581:   Input Parameters:
    1582: + rds             - The `PetscDS` specifying the row discretizations and continuum functions
    1583: . cds             - The `PetscDS` specifying the column discretizations
    1584: . jtype           - The type of matrix pointwise functions that should be used
    1585: . key             - The (label+value, fieldI*Nf + fieldJ) being integrated
    1586: . Ne              - The number of elements in the chunk
    1587: . cgeom           - The cell geometry for each cell in the chunk
    1588: . coefficients    - The array of FEM basis coefficients for the elements for the Jacobian evaluation point
    1589: . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
    1590: . dsAux           - The `PetscDS` specifying the auxiliary discretizations
    1591: . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
    1592: . t               - The time
    1593: - u_tshift        - A multiplier for the $dF/du_t$ term (as opposed to the $dF/du$ term)

    1595:   Output Parameter:
    1596: . elemMat - the element matrices for the Jacobian from each element

    1598:   Level: intermediate

    1600:   Note:
    1601: .vb
    1602:   Loop over batch of elements (e):
    1603:     Loop over element matrix entries (f,fc,g,gc --> i,j):
    1604:       Loop over quadrature points (q):
    1605:         Make u_q and gradU_q (loops over fields,Nb,Ncomp)
    1606:           elemMat[i,j] += \psi^{fc}_f(q) g0_{fc,gc}(u, \nabla u) \phi^{gc}_g(q)
    1607:                        + \psi^{fc}_f(q) \cdot g1_{fc,gc,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
    1608:                        + \nabla\psi^{fc}_f(q) \cdot g2_{fc,gc,df}(u, \nabla u) \phi^{gc}_g(q)
    1609:                        + \nabla\psi^{fc}_f(q) \cdot g3_{fc,gc,df,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
    1610: .ve

    1612: .seealso: `PetscFEIntegrateResidual()`
    1613: @*/
    1614: PetscErrorCode PetscFEIntegrateJacobian(PetscDS rds, PetscDS cds, PetscFEJacobianType jtype, PetscFormKey key, PetscInt Ne, PetscFEGeom *cgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS dsAux, const PetscScalar coefficientsAux[], PetscReal t, PetscReal u_tshift, PetscScalar elemMat[])
    1615: {
    1616:   PetscFE  fe;
    1617:   PetscInt Nf;

    1619:   PetscFunctionBegin;
    1622:   PetscCall(PetscDSGetNumFields(rds, &Nf));
    1623:   PetscCall(PetscDSGetDiscretization(rds, key.field / Nf, (PetscObject *)&fe));
    1624:   if (fe->ops->integratejacobian) PetscCall((*fe->ops->integratejacobian)(rds, cds, jtype, key, Ne, cgeom, coefficients, coefficients_t, dsAux, coefficientsAux, t, u_tshift, elemMat));
    1625:   PetscFunctionReturn(PETSC_SUCCESS);
    1626: }

    1628: /*@
    1629:   PetscFEIntegrateBdJacobian - Produce the boundary element Jacobian for a chunk of elements by quadrature integration

    1631:   Not Collective

    1633:   Input Parameters:
    1634: + ds              - The `PetscDS` specifying the discretizations and continuum functions
    1635: . wf              - The PetscWeakForm holding the pointwise functions
    1636: . jtype           - The type of matrix pointwise functions that should be used
    1637: . key             - The (label+value, fieldI*Nf + fieldJ) being integrated
    1638: . Ne              - The number of elements in the chunk
    1639: . fgeom           - The face geometry for each cell in the chunk
    1640: . coefficients    - The array of FEM basis coefficients for the elements for the Jacobian evaluation point
    1641: . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
    1642: . probAux         - The `PetscDS` specifying the auxiliary discretizations
    1643: . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
    1644: . t               - The time
    1645: - u_tshift        - A multiplier for the $dF/du_t$ term (as opposed to the $dF/du$ term)

    1647:   Output Parameter:
    1648: . elemMat - the element matrices for the Jacobian from each element

    1650:   Level: intermediate

    1652:   Note:
    1653: .vb
    1654:   Loop over batch of elements (e):
    1655:     Loop over element matrix entries (f,fc,g,gc --> i,j):
    1656:       Loop over quadrature points (q):
    1657:         Make u_q and gradU_q (loops over fields,Nb,Ncomp)
    1658:           elemMat[i,j] += \psi^{fc}_f(q) g0_{fc,gc}(u, \nabla u) \phi^{gc}_g(q)
    1659:                        + \psi^{fc}_f(q) \cdot g1_{fc,gc,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
    1660:                        + \nabla\psi^{fc}_f(q) \cdot g2_{fc,gc,df}(u, \nabla u) \phi^{gc}_g(q)
    1661:                        + \nabla\psi^{fc}_f(q) \cdot g3_{fc,gc,df,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
    1662: .ve

    1664: .seealso: `PetscFEIntegrateJacobian()`, `PetscFEIntegrateResidual()`
    1665: @*/
    1666: PetscErrorCode PetscFEIntegrateBdJacobian(PetscDS ds, PetscWeakForm wf, PetscFEJacobianType jtype, PetscFormKey key, PetscInt Ne, PetscFEGeom *fgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscReal u_tshift, PetscScalar elemMat[])
    1667: {
    1668:   PetscFE  fe;
    1669:   PetscInt Nf;

    1671:   PetscFunctionBegin;
    1673:   PetscCall(PetscDSGetNumFields(ds, &Nf));
    1674:   PetscCall(PetscDSGetDiscretization(ds, key.field / Nf, (PetscObject *)&fe));
    1675:   if (fe->ops->integratebdjacobian) PetscCall((*fe->ops->integratebdjacobian)(ds, wf, jtype, key, Ne, fgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, u_tshift, elemMat));
    1676:   PetscFunctionReturn(PETSC_SUCCESS);
    1677: }

    1679: /*@
    1680:   PetscFEIntegrateHybridJacobian - Produce the boundary element Jacobian for a chunk of hybrid elements by quadrature integration

    1682:   Not Collective

    1684:   Input Parameters:
    1685: + ds              - The `PetscDS` specifying the discretizations and continuum functions for the output
    1686: . dsIn            - The `PetscDS` specifying the discretizations and continuum functions for the input
    1687: . jtype           - The type of matrix pointwise functions that should be used
    1688: . key             - The (label+value, fieldI*Nf + fieldJ) being integrated
    1689: . s               - The side of the cell being integrated, 0 for negative and 1 for positive
    1690: . Ne              - The number of elements in the chunk
    1691: . fgeom           - The face geometry for each cell in the chunk
    1692: . cgeom           - The cell geometry for each neighbor cell in the chunk
    1693: . coefficients    - The array of FEM basis coefficients for the elements for the Jacobian evaluation point
    1694: . coefficients_t  - The array of FEM basis time derivative coefficients for the elements
    1695: . probAux         - The `PetscDS` specifying the auxiliary discretizations
    1696: . coefficientsAux - The array of FEM auxiliary basis coefficients for the elements
    1697: . t               - The time
    1698: - u_tshift        - A multiplier for the $dF/du_t$ term (as opposed to the $dF/du$ term)

    1700:   Output Parameter:
    1701: . elemMat - the element matrices for the Jacobian from each element

    1703:   Level: developer

    1705:   Note:
    1706: .vb
    1707:   Loop over batch of elements (e):
    1708:     Loop over element matrix entries (f,fc,g,gc --> i,j):
    1709:       Loop over quadrature points (q):
    1710:         Make u_q and gradU_q (loops over fields,Nb,Ncomp)
    1711:           elemMat[i,j] += \psi^{fc}_f(q) g0_{fc,gc}(u, \nabla u) \phi^{gc}_g(q)
    1712:                        + \psi^{fc}_f(q) \cdot g1_{fc,gc,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
    1713:                        + \nabla\psi^{fc}_f(q) \cdot g2_{fc,gc,df}(u, \nabla u) \phi^{gc}_g(q)
    1714:                        + \nabla\psi^{fc}_f(q) \cdot g3_{fc,gc,df,dg}(u, \nabla u) \nabla\phi^{gc}_g(q)
    1715: .ve

    1717: .seealso: `PetscFEIntegrateJacobian()`, `PetscFEIntegrateResidual()`
    1718: @*/
    1719: PetscErrorCode PetscFEIntegrateHybridJacobian(PetscDS ds, PetscDS dsIn, PetscFEJacobianType jtype, PetscFormKey key, PetscInt s, PetscInt Ne, PetscFEGeom *fgeom, PetscFEGeom *cgeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscDS probAux, const PetscScalar coefficientsAux[], PetscReal t, PetscReal u_tshift, PetscScalar elemMat[])
    1720: {
    1721:   PetscFE  fe;
    1722:   PetscInt Nf;

    1724:   PetscFunctionBegin;
    1726:   PetscCall(PetscDSGetNumFields(ds, &Nf));
    1727:   PetscCall(PetscDSGetDiscretization(ds, key.field / Nf, (PetscObject *)&fe));
    1728:   if (fe->ops->integratehybridjacobian) PetscCall((*fe->ops->integratehybridjacobian)(ds, dsIn, jtype, key, s, Ne, fgeom, cgeom, coefficients, coefficients_t, probAux, coefficientsAux, t, u_tshift, elemMat));
    1729:   PetscFunctionReturn(PETSC_SUCCESS);
    1730: }

    1732: /*@
    1733:   PetscFEGetHeightSubspace - Get the subspace of this space for a mesh point of a given height

    1735:   Input Parameters:
    1736: + fe     - The finite element space
    1737: - height - The height of the `DMPLEX` point

    1739:   Output Parameter:
    1740: . subfe - The subspace of this `PetscFE` space

    1742:   Level: advanced

    1744:   Note:
    1745:   For example, if we want the subspace of this space for a face, we would choose height = 1.

    1747: .seealso: `PetscFECreateDefault()`
    1748: @*/
    1749: PetscErrorCode PetscFEGetHeightSubspace(PetscFE fe, PetscInt height, PetscFE *subfe)
    1750: {
    1751:   PetscSpace      P, subP;
    1752:   PetscDualSpace  Q, subQ;
    1753:   PetscQuadrature subq;
    1754:   PetscInt        dim, Nc;

    1756:   PetscFunctionBegin;
    1758:   PetscAssertPointer(subfe, 3);
    1759:   if (height == 0) {
    1760:     *subfe = fe;
    1761:     PetscFunctionReturn(PETSC_SUCCESS);
    1762:   }
    1763:   PetscCall(PetscFEGetBasisSpace(fe, &P));
    1764:   PetscCall(PetscFEGetDualSpace(fe, &Q));
    1765:   PetscCall(PetscFEGetNumComponents(fe, &Nc));
    1766:   PetscCall(PetscFEGetFaceQuadrature(fe, &subq));
    1767:   PetscCall(PetscDualSpaceGetDimension(Q, &dim));
    1768:   PetscCheck(height <= dim && height >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Asked for space at height %" PetscInt_FMT " for dimension %" PetscInt_FMT " space", height, dim);
    1769:   if (!fe->subspaces) PetscCall(PetscCalloc1(dim, &fe->subspaces));
    1770:   if (height <= dim) {
    1771:     if (!fe->subspaces[height - 1]) {
    1772:       PetscFE     sub = NULL;
    1773:       const char *name;

    1775:       PetscCall(PetscSpaceGetHeightSubspace(P, height, &subP));
    1776:       PetscCall(PetscDualSpaceGetHeightSubspace(Q, height, &subQ));
    1777:       if (subQ) {
    1778:         PetscCall(PetscObjectReference((PetscObject)subP));
    1779:         PetscCall(PetscObjectReference((PetscObject)subQ));
    1780:         PetscCall(PetscObjectReference((PetscObject)subq));
    1781:         PetscCall(PetscFECreateFromSpaces(subP, subQ, subq, NULL, &sub));
    1782:       }
    1783:       if (sub) {
    1784:         PetscCall(PetscObjectGetName((PetscObject)fe, &name));
    1785:         if (name) PetscCall(PetscFESetName(sub, name));
    1786:       }
    1787:       fe->subspaces[height - 1] = sub;
    1788:     }
    1789:     *subfe = fe->subspaces[height - 1];
    1790:   } else {
    1791:     *subfe = NULL;
    1792:   }
    1793:   PetscFunctionReturn(PETSC_SUCCESS);
    1794: }

    1796: /*@
    1797:   PetscFERefine - Create a "refined" `PetscFE` object that refines the reference cell into
    1798:   smaller copies.

    1800:   Collective

    1802:   Input Parameter:
    1803: . fe - The initial `PetscFE`

    1805:   Output Parameter:
    1806: . feRef - The refined `PetscFE`

    1808:   Level: advanced

    1810:   Notes:
    1811:   This is typically used to generate a preconditioner for a higher order method from a lower order method on a
    1812:   refined mesh having the same number of dofs (but more sparsity). It is also used to create an
    1813:   interpolation between regularly refined meshes.

    1815: .seealso: `PetscFEType`, `PetscFECreate()`, `PetscFESetType()`
    1816: @*/
    1817: PetscErrorCode PetscFERefine(PetscFE fe, PetscFE *feRef)
    1818: {
    1819:   PetscSpace       P, Pref;
    1820:   PetscDualSpace   Q, Qref;
    1821:   DM               K, Kref;
    1822:   PetscQuadrature  q, qref;
    1823:   const PetscReal *v0, *jac;
    1824:   PetscInt         numComp, numSubelements;
    1825:   PetscInt         cStart, cEnd, c;
    1826:   PetscDualSpace  *cellSpaces;

    1828:   PetscFunctionBegin;
    1829:   PetscCall(PetscFEGetBasisSpace(fe, &P));
    1830:   PetscCall(PetscFEGetDualSpace(fe, &Q));
    1831:   PetscCall(PetscFEGetQuadrature(fe, &q));
    1832:   PetscCall(PetscDualSpaceGetDM(Q, &K));
    1833:   /* Create space */
    1834:   PetscCall(PetscObjectReference((PetscObject)P));
    1835:   Pref = P;
    1836:   /* Create dual space */
    1837:   PetscCall(PetscDualSpaceDuplicate(Q, &Qref));
    1838:   PetscCall(PetscDualSpaceSetType(Qref, PETSCDUALSPACEREFINED));
    1839:   PetscCall(DMRefine(K, PetscObjectComm((PetscObject)fe), &Kref));
    1840:   PetscCall(DMGetCoordinatesLocalSetUp(Kref));
    1841:   PetscCall(PetscDualSpaceSetDM(Qref, Kref));
    1842:   PetscCall(DMPlexGetHeightStratum(Kref, 0, &cStart, &cEnd));
    1843:   PetscCall(PetscMalloc1(cEnd - cStart, &cellSpaces));
    1844:   /* TODO: fix for non-uniform refinement */
    1845:   for (c = 0; c < cEnd - cStart; c++) cellSpaces[c] = Q;
    1846:   PetscCall(PetscDualSpaceRefinedSetCellSpaces(Qref, cellSpaces));
    1847:   PetscCall(PetscFree(cellSpaces));
    1848:   PetscCall(DMDestroy(&Kref));
    1849:   PetscCall(PetscDualSpaceSetUp(Qref));
    1850:   /* Create element */
    1851:   PetscCall(PetscFECreate(PetscObjectComm((PetscObject)fe), feRef));
    1852:   PetscCall(PetscFESetType(*feRef, PETSCFECOMPOSITE));
    1853:   PetscCall(PetscFESetBasisSpace(*feRef, Pref));
    1854:   PetscCall(PetscFESetDualSpace(*feRef, Qref));
    1855:   PetscCall(PetscFEGetNumComponents(fe, &numComp));
    1856:   PetscCall(PetscFESetNumComponents(*feRef, numComp));
    1857:   PetscCall(PetscFESetUp(*feRef));
    1858:   PetscCall(PetscSpaceDestroy(&Pref));
    1859:   PetscCall(PetscDualSpaceDestroy(&Qref));
    1860:   /* Create quadrature */
    1861:   PetscCall(PetscFECompositeGetMapping(*feRef, &numSubelements, &v0, &jac, NULL));
    1862:   PetscCall(PetscQuadratureExpandComposite(q, numSubelements, v0, jac, &qref));
    1863:   PetscCall(PetscFESetQuadrature(*feRef, qref));
    1864:   PetscCall(PetscQuadratureDestroy(&qref));
    1865:   PetscFunctionReturn(PETSC_SUCCESS);
    1866: }

    1868: static PetscErrorCode PetscFESetDefaultName_Private(PetscFE fe)
    1869: {
    1870:   PetscSpace     P;
    1871:   PetscDualSpace Q;
    1872:   DM             K;
    1873:   DMPolytopeType ct;
    1874:   PetscInt       degree;
    1875:   char           name[64];

    1877:   PetscFunctionBegin;
    1878:   PetscCall(PetscFEGetBasisSpace(fe, &P));
    1879:   PetscCall(PetscSpaceGetDegree(P, &degree, NULL));
    1880:   PetscCall(PetscFEGetDualSpace(fe, &Q));
    1881:   PetscCall(PetscDualSpaceGetDM(Q, &K));
    1882:   PetscCall(DMPlexGetCellType(K, 0, &ct));
    1883:   switch (ct) {
    1884:   case DM_POLYTOPE_SEGMENT:
    1885:   case DM_POLYTOPE_POINT_PRISM_TENSOR:
    1886:   case DM_POLYTOPE_QUADRILATERAL:
    1887:   case DM_POLYTOPE_SEG_PRISM_TENSOR:
    1888:   case DM_POLYTOPE_HEXAHEDRON:
    1889:   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
    1890:     PetscCall(PetscSNPrintf(name, sizeof(name), "Q%" PetscInt_FMT, degree));
    1891:     break;
    1892:   case DM_POLYTOPE_TRIANGLE:
    1893:   case DM_POLYTOPE_TETRAHEDRON:
    1894:     PetscCall(PetscSNPrintf(name, sizeof(name), "P%" PetscInt_FMT, degree));
    1895:     break;
    1896:   case DM_POLYTOPE_TRI_PRISM:
    1897:   case DM_POLYTOPE_TRI_PRISM_TENSOR:
    1898:     PetscCall(PetscSNPrintf(name, sizeof(name), "P%" PetscInt_FMT "xQ%" PetscInt_FMT, degree, degree));
    1899:     break;
    1900:   default:
    1901:     PetscCall(PetscSNPrintf(name, sizeof(name), "FE"));
    1902:   }
    1903:   PetscCall(PetscFESetName(fe, name));
    1904:   PetscFunctionReturn(PETSC_SUCCESS);
    1905: }

    1907: /*@
    1908:   PetscFECreateFromSpaces - Create a `PetscFE` from the basis and dual spaces

    1910:   Collective

    1912:   Input Parameters:
    1913: + P  - The basis space
    1914: . Q  - The dual space
    1915: . q  - The cell quadrature
    1916: - fq - The face quadrature

    1918:   Output Parameter:
    1919: . fem - The `PetscFE` object

    1921:   Level: beginner

    1923:   Note:
    1924:   The `PetscFE` takes ownership of these spaces by calling destroy on each. They should not be used after this call, and for borrowed references from `PetscFEGetSpace()` and the like,
    1925:   the caller must use `PetscObjectReference()` before this call.

    1927: .seealso: `PetscFE`, `PetscSpace`, `PetscDualSpace`, `PetscQuadrature`,
    1928:           `PetscFECreateLagrangeByCell()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
    1929: @*/
    1930: PetscErrorCode PetscFECreateFromSpaces(PetscSpace P, PetscDualSpace Q, PetscQuadrature q, PetscQuadrature fq, PetscFE *fem)
    1931: {
    1932:   PetscInt    Nc;
    1933:   PetscInt    p_Ns = -1, p_Nc = -1, q_Ns = -1, q_Nc = -1;
    1934:   PetscBool   p_is_uniform_sum = PETSC_FALSE, p_interleave_basis = PETSC_FALSE, p_interleave_components = PETSC_FALSE;
    1935:   PetscBool   q_is_uniform_sum = PETSC_FALSE, q_interleave_basis = PETSC_FALSE, q_interleave_components = PETSC_FALSE;
    1936:   const char *prefix;

    1938:   PetscFunctionBegin;
    1939:   PetscCall(PetscObjectTypeCompare((PetscObject)P, PETSCSPACESUM, &p_is_uniform_sum));
    1940:   if (p_is_uniform_sum) {
    1941:     PetscSpace subsp_0 = NULL;
    1942:     PetscCall(PetscSpaceSumGetNumSubspaces(P, &p_Ns));
    1943:     PetscCall(PetscSpaceGetNumComponents(P, &p_Nc));
    1944:     PetscCall(PetscSpaceSumGetConcatenate(P, &p_is_uniform_sum));
    1945:     PetscCall(PetscSpaceSumGetInterleave(P, &p_interleave_basis, &p_interleave_components));
    1946:     for (PetscInt s = 0; s < p_Ns; s++) {
    1947:       PetscSpace subsp;

    1949:       PetscCall(PetscSpaceSumGetSubspace(P, s, &subsp));
    1950:       if (!s) {
    1951:         subsp_0 = subsp;
    1952:       } else if (subsp != subsp_0) {
    1953:         p_is_uniform_sum = PETSC_FALSE;
    1954:       }
    1955:     }
    1956:   }
    1957:   PetscCall(PetscObjectTypeCompare((PetscObject)Q, PETSCDUALSPACESUM, &q_is_uniform_sum));
    1958:   if (q_is_uniform_sum) {
    1959:     PetscDualSpace subsp_0 = NULL;
    1960:     PetscCall(PetscDualSpaceSumGetNumSubspaces(Q, &q_Ns));
    1961:     PetscCall(PetscDualSpaceGetNumComponents(Q, &q_Nc));
    1962:     PetscCall(PetscDualSpaceSumGetConcatenate(Q, &q_is_uniform_sum));
    1963:     PetscCall(PetscDualSpaceSumGetInterleave(Q, &q_interleave_basis, &q_interleave_components));
    1964:     for (PetscInt s = 0; s < q_Ns; s++) {
    1965:       PetscDualSpace subsp;

    1967:       PetscCall(PetscDualSpaceSumGetSubspace(Q, s, &subsp));
    1968:       if (!s) {
    1969:         subsp_0 = subsp;
    1970:       } else if (subsp != subsp_0) {
    1971:         q_is_uniform_sum = PETSC_FALSE;
    1972:       }
    1973:     }
    1974:   }
    1975:   if (p_is_uniform_sum && q_is_uniform_sum && (p_interleave_basis == q_interleave_basis) && (p_interleave_components == q_interleave_components) && (p_Ns == q_Ns) && (p_Nc == q_Nc)) {
    1976:     PetscSpace     scalar_space;
    1977:     PetscDualSpace scalar_dspace;
    1978:     PetscFE        scalar_fe;

    1980:     PetscCall(PetscSpaceSumGetSubspace(P, 0, &scalar_space));
    1981:     PetscCall(PetscDualSpaceSumGetSubspace(Q, 0, &scalar_dspace));
    1982:     PetscCall(PetscObjectReference((PetscObject)scalar_space));
    1983:     PetscCall(PetscObjectReference((PetscObject)scalar_dspace));
    1984:     PetscCall(PetscObjectReference((PetscObject)q));
    1985:     PetscCall(PetscObjectReference((PetscObject)fq));
    1986:     PetscCall(PetscFECreateFromSpaces(scalar_space, scalar_dspace, q, fq, &scalar_fe));
    1987:     PetscCall(PetscFECreateVector(scalar_fe, p_Ns, p_interleave_basis, p_interleave_components, fem));
    1988:     PetscCall(PetscFEDestroy(&scalar_fe));
    1989:   } else {
    1990:     PetscCall(PetscFECreate(PetscObjectComm((PetscObject)P), fem));
    1991:     PetscCall(PetscFESetType(*fem, PETSCFEBASIC));
    1992:   }
    1993:   PetscCall(PetscSpaceGetNumComponents(P, &Nc));
    1994:   PetscCall(PetscFESetNumComponents(*fem, Nc));
    1995:   PetscCall(PetscFESetBasisSpace(*fem, P));
    1996:   PetscCall(PetscFESetDualSpace(*fem, Q));
    1997:   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)P, &prefix));
    1998:   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*fem, prefix));
    1999:   PetscCall(PetscFESetUp(*fem));
    2000:   PetscCall(PetscSpaceDestroy(&P));
    2001:   PetscCall(PetscDualSpaceDestroy(&Q));
    2002:   PetscCall(PetscFESetQuadrature(*fem, q));
    2003:   PetscCall(PetscFESetFaceQuadrature(*fem, fq));
    2004:   PetscCall(PetscQuadratureDestroy(&q));
    2005:   PetscCall(PetscQuadratureDestroy(&fq));
    2006:   PetscCall(PetscFESetDefaultName_Private(*fem));
    2007:   PetscFunctionReturn(PETSC_SUCCESS);
    2008: }

    2010: static PetscErrorCode PetscFECreate_Internal(MPI_Comm comm, PetscInt dim, PetscInt Nc, DMPolytopeType ct, const char prefix[], PetscInt degree, PetscInt qorder, PetscBool setFromOptions, PetscFE *fem)
    2011: {
    2012:   DM                           K;
    2013:   PetscSpace                   P;
    2014:   PetscDualSpace               Q;
    2015:   PetscQuadrature              q, fq;
    2016:   PetscBool                    tensor;
    2017:   PetscDTSimplexQuadratureType qtype = PETSCDTSIMPLEXQUAD_DEFAULT;

    2019:   PetscFunctionBegin;
    2020:   if (prefix) PetscAssertPointer(prefix, 5);
    2021:   PetscAssertPointer(fem, 9);
    2022:   switch (ct) {
    2023:   case DM_POLYTOPE_SEGMENT:
    2024:   case DM_POLYTOPE_POINT_PRISM_TENSOR:
    2025:   case DM_POLYTOPE_QUADRILATERAL:
    2026:   case DM_POLYTOPE_SEG_PRISM_TENSOR:
    2027:   case DM_POLYTOPE_HEXAHEDRON:
    2028:   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
    2029:     tensor = PETSC_TRUE;
    2030:     break;
    2031:   default:
    2032:     tensor = PETSC_FALSE;
    2033:   }
    2034:   /* Create space */
    2035:   PetscCall(PetscSpaceCreate(comm, &P));
    2036:   PetscCall(PetscSpaceSetType(P, PETSCSPACEPOLYNOMIAL));
    2037:   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)P, prefix));
    2038:   PetscCall(PetscSpacePolynomialSetTensor(P, tensor));
    2039:   PetscCall(PetscSpaceSetNumComponents(P, Nc));
    2040:   PetscCall(PetscSpaceSetNumVariables(P, dim));
    2041:   if (degree >= 0) {
    2042:     PetscCall(PetscSpaceSetDegree(P, degree, PETSC_DETERMINE));
    2043:     if (ct == DM_POLYTOPE_TRI_PRISM || ct == DM_POLYTOPE_TRI_PRISM_TENSOR) {
    2044:       PetscSpace Pend, Pside;

    2046:       PetscCall(PetscSpaceSetNumComponents(P, 1));
    2047:       PetscCall(PetscSpaceCreate(comm, &Pend));
    2048:       PetscCall(PetscSpaceSetType(Pend, PETSCSPACEPOLYNOMIAL));
    2049:       PetscCall(PetscSpacePolynomialSetTensor(Pend, PETSC_FALSE));
    2050:       PetscCall(PetscSpaceSetNumComponents(Pend, 1));
    2051:       PetscCall(PetscSpaceSetNumVariables(Pend, dim - 1));
    2052:       PetscCall(PetscSpaceSetDegree(Pend, degree, PETSC_DETERMINE));
    2053:       PetscCall(PetscSpaceCreate(comm, &Pside));
    2054:       PetscCall(PetscSpaceSetType(Pside, PETSCSPACEPOLYNOMIAL));
    2055:       PetscCall(PetscSpacePolynomialSetTensor(Pside, PETSC_FALSE));
    2056:       PetscCall(PetscSpaceSetNumComponents(Pside, 1));
    2057:       PetscCall(PetscSpaceSetNumVariables(Pside, 1));
    2058:       PetscCall(PetscSpaceSetDegree(Pside, degree, PETSC_DETERMINE));
    2059:       PetscCall(PetscSpaceSetType(P, PETSCSPACETENSOR));
    2060:       PetscCall(PetscSpaceTensorSetNumSubspaces(P, 2));
    2061:       PetscCall(PetscSpaceTensorSetSubspace(P, 0, Pend));
    2062:       PetscCall(PetscSpaceTensorSetSubspace(P, 1, Pside));
    2063:       PetscCall(PetscSpaceDestroy(&Pend));
    2064:       PetscCall(PetscSpaceDestroy(&Pside));

    2066:       if (Nc > 1) {
    2067:         PetscSpace scalar_P = P;

    2069:         PetscCall(PetscSpaceCreate(comm, &P));
    2070:         PetscCall(PetscSpaceSetNumVariables(P, dim));
    2071:         PetscCall(PetscSpaceSetNumComponents(P, Nc));
    2072:         PetscCall(PetscSpaceSetType(P, PETSCSPACESUM));
    2073:         PetscCall(PetscSpaceSumSetNumSubspaces(P, Nc));
    2074:         PetscCall(PetscSpaceSumSetConcatenate(P, PETSC_TRUE));
    2075:         PetscCall(PetscSpaceSumSetInterleave(P, PETSC_TRUE, PETSC_FALSE));
    2076:         for (PetscInt i = 0; i < Nc; i++) PetscCall(PetscSpaceSumSetSubspace(P, i, scalar_P));
    2077:         PetscCall(PetscSpaceDestroy(&scalar_P));
    2078:       }
    2079:     }
    2080:   }
    2081:   if (setFromOptions) PetscCall(PetscSpaceSetFromOptions(P));
    2082:   PetscCall(PetscSpaceSetUp(P));
    2083:   PetscCall(PetscSpaceGetDegree(P, &degree, NULL));
    2084:   PetscCall(PetscSpacePolynomialGetTensor(P, &tensor));
    2085:   PetscCall(PetscSpaceGetNumComponents(P, &Nc));
    2086:   /* Create dual space */
    2087:   PetscCall(PetscDualSpaceCreate(comm, &Q));
    2088:   PetscCall(PetscDualSpaceSetType(Q, PETSCDUALSPACELAGRANGE));
    2089:   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)Q, prefix));
    2090:   PetscCall(DMPlexCreateReferenceCell(PETSC_COMM_SELF, ct, &K));
    2091:   PetscCall(PetscDualSpaceSetDM(Q, K));
    2092:   PetscCall(DMDestroy(&K));
    2093:   PetscCall(PetscDualSpaceSetNumComponents(Q, Nc));
    2094:   PetscCall(PetscDualSpaceSetOrder(Q, degree));
    2095:   PetscCall(PetscDualSpaceLagrangeSetTensor(Q, (tensor || (ct == DM_POLYTOPE_TRI_PRISM)) ? PETSC_TRUE : PETSC_FALSE));
    2096:   if (setFromOptions) PetscCall(PetscDualSpaceSetFromOptions(Q));
    2097:   PetscCall(PetscDualSpaceSetUp(Q));

    2099:   qorder = qorder >= 0 ? qorder : degree;
    2100:   if (setFromOptions) {
    2101:     PetscObjectOptionsBegin((PetscObject)P);
    2102:     PetscCall(PetscOptionsBoundedInt("-petscfe_default_quadrature_order", "Quadrature order is one less than quadrature points per edge", "PetscFECreateDefault", qorder, &qorder, NULL, 0));
    2103:     PetscCall(PetscOptionsEnum("-petscfe_default_quadrature_type", "Simplex quadrature type", "PetscDTSimplexQuadratureType", PetscDTSimplexQuadratureTypes, (PetscEnum)qtype, (PetscEnum *)&qtype, NULL));
    2104:     PetscOptionsEnd();
    2105:   }
    2106:   PetscCall(PetscDTCreateQuadratureByCell(ct, qorder, qtype, &q, &fq));
    2107:   /* Create finite element */
    2108:   PetscCall(PetscFECreateFromSpaces(P, Q, q, fq, fem));
    2109:   if (setFromOptions) PetscCall(PetscFESetFromOptions(*fem));
    2110:   PetscFunctionReturn(PETSC_SUCCESS);
    2111: }

    2113: /*@
    2114:   PetscFECreateDefault - Create a `PetscFE` for basic FEM computation

    2116:   Collective

    2118:   Input Parameters:
    2119: + comm      - The MPI comm
    2120: . dim       - The spatial dimension
    2121: . Nc        - The number of components
    2122: . isSimplex - Flag for simplex reference cell, otherwise its a tensor product
    2123: . prefix    - The options prefix, or `NULL`
    2124: - qorder    - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree

    2126:   Output Parameter:
    2127: . fem - The `PetscFE` object

    2129:   Level: beginner

    2131:   Notes:
    2132:   Preferred usage is `PetscFECreateByCell()`

    2134:   Each subobject is SetFromOption() during creation, so that the object may be customized from the command line, using the prefix specified above.
    2135:   See the links below for the particular options available.

    2137: .seealso: `PetscFE`, `PetscFECreateLagrange()`, `PetscFECreateByCell()`, `PetscSpaceSetFromOptions()`, `PetscDualSpaceSetFromOptions()`, `PetscFESetFromOptions()`,
    2138:           `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
    2139: @*/
    2140: PetscErrorCode PetscFECreateDefault(MPI_Comm comm, PetscInt dim, PetscInt Nc, PetscBool isSimplex, const char prefix[], PetscInt qorder, PetscFE *fem)
    2141: {
    2142:   PetscFunctionBegin;
    2143:   PetscCall(PetscFECreate_Internal(comm, dim, Nc, DMPolytopeTypeSimpleShape(dim, isSimplex), prefix, PETSC_DECIDE, qorder, PETSC_TRUE, fem));
    2144:   PetscFunctionReturn(PETSC_SUCCESS);
    2145: }

    2147: /*@
    2148:   PetscFECreateByCell - Create a `PetscFE` for basic FEM computation

    2150:   Collective

    2152:   Input Parameters:
    2153: + comm   - The MPI comm
    2154: . dim    - The spatial dimension
    2155: . Nc     - The number of components
    2156: . ct     - The celltype of the reference cell
    2157: . prefix - The options prefix, or `NULL`
    2158: - qorder - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree

    2160:   Output Parameter:
    2161: . fem - The `PetscFE` object

    2163:   Level: beginner

    2165:   Note:
    2166:   Each subobject is SetFromOption() during creation, so that the object may be customized from the command line, using the prefix specified above. See the links below for the particular options available.

    2168:   Developer Notes:
    2169:   This should be called `PetscFECreateDefaultByCell()` since it is the extension/replacement for `PetscFECreateDefault()`

    2171:   Since this generalizes/replaces `PetscFECreateDefault()` for different `DMPolytopeType` its name should be `PetscFECreateDefaultByPolytopeType()`

    2173: .seealso: `PetscFE`, `PetscFECreateDefault()`, `PetscFECreateLagrange()`, `PetscSpaceSetFromOptions()`, `PetscDualSpaceSetFromOptions()`,
    2174:           `PetscFESetFromOptions()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`, `DMPolytopeType`
    2175: @*/
    2176: PetscErrorCode PetscFECreateByCell(MPI_Comm comm, PetscInt dim, PetscInt Nc, DMPolytopeType ct, const char prefix[], PetscInt qorder, PetscFE *fem)
    2177: {
    2178:   PetscFunctionBegin;
    2179:   PetscCall(PetscFECreate_Internal(comm, dim, Nc, ct, prefix, PETSC_DECIDE, qorder, PETSC_TRUE, fem));
    2180:   PetscFunctionReturn(PETSC_SUCCESS);
    2181: }

    2183: /*@
    2184:   PetscFECreateLagrange - Create a `PetscFE` for the basic Lagrange space of degree `k`

    2186:   Collective

    2188:   Input Parameters:
    2189: + comm      - The MPI comm
    2190: . dim       - The spatial dimension
    2191: . Nc        - The number of components
    2192: . isSimplex - Flag for simplex reference cell, otherwise its a tensor product
    2193: . k         - The degree of the space
    2194: - qorder    - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree

    2196:   Output Parameter:
    2197: . fem - The `PetscFE` object

    2199:   Level: beginner

    2201:   Notes:
    2202:   Preferred usage is `PetscFECreateLagrangeByCell()`

    2204:   For simplices, this element is the space of maximum polynomial degree `k`, otherwise it is a tensor product of 1D polynomials, each with maximal degree `k`.

    2206: .seealso: `PetscFE`, `PetscFECreateLagrangeByCell()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
    2207: @*/
    2208: PetscErrorCode PetscFECreateLagrange(MPI_Comm comm, PetscInt dim, PetscInt Nc, PetscBool isSimplex, PetscInt k, PetscInt qorder, PetscFE *fem)
    2209: {
    2210:   PetscFunctionBegin;
    2211:   PetscCall(PetscFECreate_Internal(comm, dim, Nc, DMPolytopeTypeSimpleShape(dim, isSimplex), NULL, k, qorder, PETSC_FALSE, fem));
    2212:   PetscFunctionReturn(PETSC_SUCCESS);
    2213: }

    2215: /*@
    2216:   PetscFECreateLagrangeByCell - Create a `PetscFE` for the basic Lagrange space of degree `k`

    2218:   Collective

    2220:   Input Parameters:
    2221: + comm   - The MPI comm
    2222: . dim    - The spatial dimension
    2223: . Nc     - The number of components
    2224: . ct     - The celltype of the reference cell
    2225: . k      - The degree of the space
    2226: - qorder - The quadrature order or `PETSC_DETERMINE` to use `PetscSpace` polynomial degree

    2228:   Output Parameter:
    2229: . fem - The `PetscFE` object

    2231:   Level: beginner

    2233:   Note:
    2234:   For simplices, this element is the space of maximum polynomial degree `k`, otherwise it is a tensor product of 1D polynomials, each with maximal degree `k`.

    2236:   Developer Note:
    2237:   Since this generalizes/replaces `PetscFECreateLagrange()` for different `DMPolytopeType` its name should be `PetscFECreateLagrangeByPolytopeType()`

    2239: .seealso: `PetscFE`, `PetscFECreateLagrange()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`,
    2240:           `DMPolytopeType`
    2241: @*/
    2242: PetscErrorCode PetscFECreateLagrangeByCell(MPI_Comm comm, PetscInt dim, PetscInt Nc, DMPolytopeType ct, PetscInt k, PetscInt qorder, PetscFE *fem)
    2243: {
    2244:   PetscFunctionBegin;
    2245:   PetscCall(PetscFECreate_Internal(comm, dim, Nc, ct, NULL, k, qorder, PETSC_FALSE, fem));
    2246:   PetscFunctionReturn(PETSC_SUCCESS);
    2247: }

    2249: /*@
    2250:   PetscFELimitDegree - Copy a `PetscFE` but limit the degree to be in the given range

    2252:   Collective

    2254:   Input Parameters:
    2255: + fe        - The `PetscFE`
    2256: . minDegree - The minimum degree, or `PETSC_DETERMINE` for no limit
    2257: - maxDegree - The maximum degree, or `PETSC_DETERMINE` for no limit

    2259:   Output Parameter:
    2260: . newfe - The `PetscFE` object

    2262:   Level: advanced

    2264:   Note:
    2265:   This currently only works for Lagrange elements.

    2267: .seealso: `PetscFECreateLagrange()`, `PetscFECreateDefault()`, `PetscFECreateByCell()`, `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
    2268: @*/
    2269: PetscErrorCode PetscFELimitDegree(PetscFE fe, PetscInt minDegree, PetscInt maxDegree, PetscFE *newfe)
    2270: {
    2271:   PetscDualSpace Q;
    2272:   PetscBool      islag, issum;
    2273:   PetscInt       oldk = 0, k;

    2275:   PetscFunctionBegin;
    2276:   PetscCall(PetscFEGetDualSpace(fe, &Q));
    2277:   PetscCall(PetscObjectTypeCompare((PetscObject)Q, PETSCDUALSPACELAGRANGE, &islag));
    2278:   PetscCall(PetscObjectTypeCompare((PetscObject)Q, PETSCDUALSPACESUM, &issum));
    2279:   if (islag) {
    2280:     PetscCall(PetscDualSpaceGetOrder(Q, &oldk));
    2281:   } else if (issum) {
    2282:     PetscDualSpace subQ;

    2284:     PetscCall(PetscDualSpaceSumGetSubspace(Q, 0, &subQ));
    2285:     PetscCall(PetscDualSpaceGetOrder(subQ, &oldk));
    2286:   } else {
    2287:     PetscCall(PetscObjectReference((PetscObject)fe));
    2288:     *newfe = fe;
    2289:     PetscFunctionReturn(PETSC_SUCCESS);
    2290:   }
    2291:   k = oldk;
    2292:   if (minDegree >= 0) k = PetscMax(k, minDegree);
    2293:   if (maxDegree >= 0) k = PetscMin(k, maxDegree);
    2294:   if (k != oldk) {
    2295:     DM              K;
    2296:     PetscSpace      P;
    2297:     PetscQuadrature q;
    2298:     DMPolytopeType  ct;
    2299:     PetscInt        dim, Nc;

    2301:     PetscCall(PetscFEGetBasisSpace(fe, &P));
    2302:     PetscCall(PetscSpaceGetNumVariables(P, &dim));
    2303:     PetscCall(PetscSpaceGetNumComponents(P, &Nc));
    2304:     PetscCall(PetscDualSpaceGetDM(Q, &K));
    2305:     PetscCall(DMPlexGetCellType(K, 0, &ct));
    2306:     PetscCall(PetscFECreateLagrangeByCell(PetscObjectComm((PetscObject)fe), dim, Nc, ct, k, PETSC_DETERMINE, newfe));
    2307:     PetscCall(PetscFEGetQuadrature(fe, &q));
    2308:     PetscCall(PetscFESetQuadrature(*newfe, q));
    2309:   } else {
    2310:     PetscCall(PetscObjectReference((PetscObject)fe));
    2311:     *newfe = fe;
    2312:   }
    2313:   PetscFunctionReturn(PETSC_SUCCESS);
    2314: }

    2316: /*@
    2317:   PetscFECreateBrokenElement - Create a discontinuous version of the input `PetscFE`

    2319:   Collective

    2321:   Input Parameters:
    2322: . cgfe - The continuous `PetscFE` object

    2324:   Output Parameter:
    2325: . dgfe - The discontinuous `PetscFE` object

    2327:   Level: advanced

    2329:   Note:
    2330:   This only works for Lagrange elements.

    2332: .seealso: `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`, `PetscFECreateLagrange()`, `PetscFECreateLagrangeByCell()`, `PetscDualSpaceLagrangeSetContinuity()`
    2333: @*/
    2334: PetscErrorCode PetscFECreateBrokenElement(PetscFE cgfe, PetscFE *dgfe)
    2335: {
    2336:   PetscSpace      P;
    2337:   PetscDualSpace  Q, dgQ;
    2338:   PetscQuadrature q, fq;
    2339:   PetscBool       is_lagrange, is_sum;

    2341:   PetscFunctionBegin;
    2342:   PetscCall(PetscFEGetBasisSpace(cgfe, &P));
    2343:   PetscCall(PetscObjectReference((PetscObject)P));
    2344:   PetscCall(PetscFEGetDualSpace(cgfe, &Q));
    2345:   PetscCall(PetscObjectTypeCompare((PetscObject)Q, PETSCDUALSPACELAGRANGE, &is_lagrange));
    2346:   PetscCall(PetscObjectTypeCompare((PetscObject)Q, PETSCDUALSPACESUM, &is_sum));
    2347:   PetscCheck(is_lagrange || is_sum, PETSC_COMM_SELF, PETSC_ERR_SUP, "Can only create broken elements of Lagrange elements");
    2348:   PetscCall(PetscDualSpaceDuplicate(Q, &dgQ));
    2349:   PetscCall(PetscDualSpaceLagrangeSetContinuity(dgQ, PETSC_FALSE));
    2350:   PetscCall(PetscDualSpaceSetUp(dgQ));
    2351:   PetscCall(PetscFEGetQuadrature(cgfe, &q));
    2352:   PetscCall(PetscObjectReference((PetscObject)q));
    2353:   PetscCall(PetscFEGetFaceQuadrature(cgfe, &fq));
    2354:   PetscCall(PetscObjectReference((PetscObject)fq));
    2355:   PetscCall(PetscFECreateFromSpaces(P, dgQ, q, fq, dgfe));
    2356:   PetscFunctionReturn(PETSC_SUCCESS);
    2357: }

    2359: /*@
    2360:   PetscFESetName - Names the `PetscFE` and its subobjects

    2362:   Not Collective

    2364:   Input Parameters:
    2365: + fe   - The `PetscFE`
    2366: - name - The name

    2368:   Level: intermediate

    2370: .seealso: `PetscFECreate()`, `PetscSpaceCreate()`, `PetscDualSpaceCreate()`
    2371: @*/
    2372: PetscErrorCode PetscFESetName(PetscFE fe, const char name[])
    2373: {
    2374:   PetscSpace     P;
    2375:   PetscDualSpace Q;

    2377:   PetscFunctionBegin;
    2378:   PetscCall(PetscFEGetBasisSpace(fe, &P));
    2379:   PetscCall(PetscFEGetDualSpace(fe, &Q));
    2380:   PetscCall(PetscObjectSetName((PetscObject)fe, name));
    2381:   PetscCall(PetscObjectSetName((PetscObject)P, name));
    2382:   PetscCall(PetscObjectSetName((PetscObject)Q, name));
    2383:   PetscFunctionReturn(PETSC_SUCCESS);
    2384: }

    2386: PetscErrorCode PetscFEEvaluateFieldJets_Internal(PetscDS ds, PetscInt Nf, PetscInt r, PetscInt q, PetscTabulation T[], PetscFEGeom *fegeom, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscScalar u[], PetscScalar u_x[], PetscScalar u_t[])
    2387: {
    2388:   PetscInt dOffset = 0, fOffset = 0, f, g;

    2390:   for (f = 0; f < Nf; ++f) {
    2391:     PetscCheck(r < T[f]->Nr, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Replica number %" PetscInt_FMT " should be in [0, %" PetscInt_FMT ")", r, T[f]->Nr);
    2392:     PetscCheck(q < T[f]->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point number %" PetscInt_FMT " should be in [0, %" PetscInt_FMT ")", q, T[f]->Np);
    2393:     PetscFE          fe;
    2394:     const PetscInt   k       = ds->jetDegree[f];
    2395:     const PetscInt   cdim    = T[f]->cdim;
    2396:     const PetscInt   dE      = fegeom->dimEmbed;
    2397:     const PetscInt   Nq      = T[f]->Np;
    2398:     const PetscInt   Nbf     = T[f]->Nb;
    2399:     const PetscInt   Ncf     = T[f]->Nc;
    2400:     const PetscReal *Bq      = &T[f]->T[0][(r * Nq + q) * Nbf * Ncf];
    2401:     const PetscReal *Dq      = &T[f]->T[1][(r * Nq + q) * Nbf * Ncf * cdim];
    2402:     const PetscReal *Hq      = k > 1 ? &T[f]->T[2][(r * Nq + q) * Nbf * Ncf * cdim * cdim] : NULL;
    2403:     PetscInt         hOffset = 0, b, c, d;

    2405:     PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
    2406:     for (c = 0; c < Ncf; ++c) u[fOffset + c] = 0.0;
    2407:     for (d = 0; d < dE * Ncf; ++d) u_x[fOffset * dE + d] = 0.0;
    2408:     for (b = 0; b < Nbf; ++b) {
    2409:       for (c = 0; c < Ncf; ++c) {
    2410:         const PetscInt cidx = b * Ncf + c;

    2412:         u[fOffset + c] += Bq[cidx] * coefficients[dOffset + b];
    2413:         for (d = 0; d < cdim; ++d) u_x[(fOffset + c) * dE + d] += Dq[cidx * cdim + d] * coefficients[dOffset + b];
    2414:       }
    2415:     }
    2416:     if (k > 1) {
    2417:       for (g = 0; g < Nf; ++g) hOffset += T[g]->Nc * dE;
    2418:       for (d = 0; d < dE * dE * Ncf; ++d) u_x[hOffset + fOffset * dE * dE + d] = 0.0;
    2419:       for (b = 0; b < Nbf; ++b) {
    2420:         for (c = 0; c < Ncf; ++c) {
    2421:           const PetscInt cidx = b * Ncf + c;

    2423:           for (d = 0; d < cdim * cdim; ++d) u_x[hOffset + (fOffset + c) * dE * dE + d] += Hq[cidx * cdim * cdim + d] * coefficients[dOffset + b];
    2424:         }
    2425:       }
    2426:       PetscCall(PetscFEPushforwardHessian(fe, fegeom, 1, &u_x[hOffset + fOffset * dE * dE]));
    2427:     }
    2428:     PetscCall(PetscFEPushforward(fe, fegeom, 1, &u[fOffset]));
    2429:     PetscCall(PetscFEPushforwardGradient(fe, fegeom, 1, &u_x[fOffset * dE]));
    2430:     if (u_t) {
    2431:       for (c = 0; c < Ncf; ++c) u_t[fOffset + c] = 0.0;
    2432:       for (b = 0; b < Nbf; ++b) {
    2433:         for (c = 0; c < Ncf; ++c) {
    2434:           const PetscInt cidx = b * Ncf + c;

    2436:           u_t[fOffset + c] += Bq[cidx] * coefficients_t[dOffset + b];
    2437:         }
    2438:       }
    2439:       PetscCall(PetscFEPushforward(fe, fegeom, 1, &u_t[fOffset]));
    2440:     }
    2441:     fOffset += Ncf;
    2442:     dOffset += Nbf;
    2443:   }
    2444:   return PETSC_SUCCESS;
    2445: }

    2447: PetscErrorCode PetscFEEvaluateFieldJets_Hybrid_Internal(PetscDS ds, PetscInt Nf, PetscInt rc, PetscInt qc, PetscTabulation Tab[], const PetscInt rf[], const PetscInt qf[], PetscTabulation Tabf[], PetscFEGeom *fegeom, PetscFEGeom *fegeomNbr, const PetscScalar coefficients[], const PetscScalar coefficients_t[], PetscScalar u[], PetscScalar u_x[], PetscScalar u_t[])
    2448: {
    2449:   PetscInt dOffset = 0, fOffset = 0, f;

    2451:   /* f is the field number in the DS */
    2452:   for (f = 0; f < Nf; ++f) {
    2453:     PetscBool isCohesive;
    2454:     PetscInt  Ns, s;

    2456:     if (!Tab[f]) continue;
    2457:     PetscCall(PetscDSGetCohesive(ds, f, &isCohesive));
    2458:     Ns = isCohesive ? 1 : 2;
    2459:     {
    2460:       PetscTabulation T   = isCohesive ? Tab[f] : Tabf[f];
    2461:       PetscFE         fe  = (PetscFE)ds->disc[f];
    2462:       const PetscInt  dEt = T->cdim;
    2463:       const PetscInt  dE  = fegeom->dimEmbed;
    2464:       const PetscInt  Nq  = T->Np;
    2465:       const PetscInt  Nbf = T->Nb;
    2466:       const PetscInt  Ncf = T->Nc;

    2468:       for (s = 0; s < Ns; ++s) {
    2469:         const PetscInt   r  = isCohesive ? rc : rf[s];
    2470:         const PetscInt   q  = isCohesive ? qc : qf[s];
    2471:         const PetscReal *Bq = &T->T[0][(r * Nq + q) * Nbf * Ncf];
    2472:         const PetscReal *Dq = &T->T[1][(r * Nq + q) * Nbf * Ncf * dEt];
    2473:         PetscInt         b, c, d;

    2475:         PetscCheck(r < T->Nr, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " Side %" PetscInt_FMT " Replica number %" PetscInt_FMT " should be in [0, %" PetscInt_FMT ")", f, s, r, T->Nr);
    2476:         PetscCheck(q < T->Np, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " Side %" PetscInt_FMT " Point number %" PetscInt_FMT " should be in [0, %" PetscInt_FMT ")", f, s, q, T->Np);
    2477:         for (c = 0; c < Ncf; ++c) u[fOffset + c] = 0.0;
    2478:         for (d = 0; d < dE * Ncf; ++d) u_x[fOffset * dE + d] = 0.0;
    2479:         for (b = 0; b < Nbf; ++b) {
    2480:           for (c = 0; c < Ncf; ++c) {
    2481:             const PetscInt cidx = b * Ncf + c;

    2483:             u[fOffset + c] += Bq[cidx] * coefficients[dOffset + b];
    2484:             for (d = 0; d < dEt; ++d) u_x[(fOffset + c) * dE + d] += Dq[cidx * dEt + d] * coefficients[dOffset + b];
    2485:           }
    2486:         }
    2487:         PetscCall(PetscFEPushforward(fe, isCohesive ? fegeom : &fegeomNbr[s], 1, &u[fOffset]));
    2488:         PetscCall(PetscFEPushforwardGradient(fe, isCohesive ? fegeom : &fegeomNbr[s], 1, &u_x[fOffset * dE]));
    2489:         if (u_t) {
    2490:           for (c = 0; c < Ncf; ++c) u_t[fOffset + c] = 0.0;
    2491:           for (b = 0; b < Nbf; ++b) {
    2492:             for (c = 0; c < Ncf; ++c) {
    2493:               const PetscInt cidx = b * Ncf + c;

    2495:               u_t[fOffset + c] += Bq[cidx] * coefficients_t[dOffset + b];
    2496:             }
    2497:           }
    2498:           PetscCall(PetscFEPushforward(fe, fegeom, 1, &u_t[fOffset]));
    2499:         }
    2500:         fOffset += Ncf;
    2501:         dOffset += Nbf;
    2502:       }
    2503:     }
    2504:   }
    2505:   return PETSC_SUCCESS;
    2506: }

    2508: PetscErrorCode PetscFEEvaluateFaceFields_Internal(PetscDS prob, PetscInt field, PetscInt faceLoc, const PetscScalar coefficients[], PetscScalar u[])
    2509: {
    2510:   PetscFE         fe;
    2511:   PetscTabulation Tc;
    2512:   PetscInt        b, c;

    2514:   if (!prob) return PETSC_SUCCESS;
    2515:   PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
    2516:   PetscCall(PetscFEGetFaceCentroidTabulation(fe, &Tc));
    2517:   {
    2518:     const PetscReal *faceBasis = Tc->T[0];
    2519:     const PetscInt   Nb        = Tc->Nb;
    2520:     const PetscInt   Nc        = Tc->Nc;

    2522:     for (c = 0; c < Nc; ++c) u[c] = 0.0;
    2523:     for (b = 0; b < Nb; ++b) {
    2524:       for (c = 0; c < Nc; ++c) u[c] += coefficients[b] * faceBasis[(faceLoc * Nb + b) * Nc + c];
    2525:     }
    2526:   }
    2527:   return PETSC_SUCCESS;
    2528: }

    2530: PetscErrorCode PetscFEUpdateElementVec_Internal(PetscFE fe, PetscTabulation T, PetscInt r, PetscScalar tmpBasis[], PetscScalar tmpBasisDer[], PetscInt e, PetscFEGeom *fegeom, PetscScalar f0[], PetscScalar f1[], PetscScalar elemVec[])
    2531: {
    2532:   PetscFEGeom      pgeom;
    2533:   const PetscInt   dEt      = T->cdim;
    2534:   const PetscInt   dE       = fegeom->dimEmbed;
    2535:   const PetscInt   Nq       = T->Np;
    2536:   const PetscInt   Nb       = T->Nb;
    2537:   const PetscInt   Nc       = T->Nc;
    2538:   const PetscReal *basis    = &T->T[0][r * Nq * Nb * Nc];
    2539:   const PetscReal *basisDer = &T->T[1][r * Nq * Nb * Nc * dEt];
    2540:   PetscInt         q, b, c, d;

    2542:   for (q = 0; q < Nq; ++q) {
    2543:     for (b = 0; b < Nb; ++b) {
    2544:       for (c = 0; c < Nc; ++c) {
    2545:         const PetscInt bcidx = b * Nc + c;

    2547:         tmpBasis[bcidx] = basis[q * Nb * Nc + bcidx];
    2548:         for (d = 0; d < dEt; ++d) tmpBasisDer[bcidx * dE + d] = basisDer[q * Nb * Nc * dEt + bcidx * dEt + d];
    2549:         for (d = dEt; d < dE; ++d) tmpBasisDer[bcidx * dE + d] = 0.0;
    2550:       }
    2551:     }
    2552:     PetscCall(PetscFEGeomGetCellPoint(fegeom, e, q, &pgeom));
    2553:     PetscCall(PetscFEPushforward(fe, &pgeom, Nb, tmpBasis));
    2554:     PetscCall(PetscFEPushforwardGradient(fe, &pgeom, Nb, tmpBasisDer));
    2555:     for (b = 0; b < Nb; ++b) {
    2556:       for (c = 0; c < Nc; ++c) {
    2557:         const PetscInt bcidx = b * Nc + c;
    2558:         const PetscInt qcidx = q * Nc + c;

    2560:         elemVec[b] += tmpBasis[bcidx] * f0[qcidx];
    2561:         for (d = 0; d < dE; ++d) elemVec[b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
    2562:       }
    2563:     }
    2564:   }
    2565:   return PETSC_SUCCESS;
    2566: }

    2568: PetscErrorCode PetscFEUpdateElementVec_Hybrid_Internal(PetscFE fe, PetscTabulation T, PetscInt r, PetscInt side, PetscScalar tmpBasis[], PetscScalar tmpBasisDer[], PetscFEGeom *fegeom, PetscScalar f0[], PetscScalar f1[], PetscScalar elemVec[])
    2569: {
    2570:   const PetscInt   dE       = T->cdim;
    2571:   const PetscInt   Nq       = T->Np;
    2572:   const PetscInt   Nb       = T->Nb;
    2573:   const PetscInt   Nc       = T->Nc;
    2574:   const PetscReal *basis    = &T->T[0][r * Nq * Nb * Nc];
    2575:   const PetscReal *basisDer = &T->T[1][r * Nq * Nb * Nc * dE];

    2577:   for (PetscInt q = 0; q < Nq; ++q) {
    2578:     for (PetscInt b = 0; b < Nb; ++b) {
    2579:       for (PetscInt c = 0; c < Nc; ++c) {
    2580:         const PetscInt bcidx = b * Nc + c;

    2582:         tmpBasis[bcidx] = basis[q * Nb * Nc + bcidx];
    2583:         for (PetscInt d = 0; d < dE; ++d) tmpBasisDer[bcidx * dE + d] = basisDer[q * Nb * Nc * dE + bcidx * dE + d];
    2584:       }
    2585:     }
    2586:     PetscCall(PetscFEPushforward(fe, fegeom, Nb, tmpBasis));
    2587:     // TODO This is currently broken since we do not pull the geometry down to the lower dimension
    2588:     // PetscCall(PetscFEPushforwardGradient(fe, fegeom, Nb, tmpBasisDer));
    2589:     if (side == 2) {
    2590:       // Integrating over whole cohesive cell, so insert for both sides
    2591:       for (PetscInt s = 0; s < 2; ++s) {
    2592:         for (PetscInt b = 0; b < Nb; ++b) {
    2593:           for (PetscInt c = 0; c < Nc; ++c) {
    2594:             const PetscInt bcidx = b * Nc + c;
    2595:             const PetscInt qcidx = (q * 2 + s) * Nc + c;

    2597:             elemVec[Nb * s + b] += tmpBasis[bcidx] * f0[qcidx];
    2598:             for (PetscInt d = 0; d < dE; ++d) elemVec[Nb * s + b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
    2599:           }
    2600:         }
    2601:       }
    2602:     } else {
    2603:       // Integrating over endcaps of cohesive cell, so insert for correct side
    2604:       for (PetscInt b = 0; b < Nb; ++b) {
    2605:         for (PetscInt c = 0; c < Nc; ++c) {
    2606:           const PetscInt bcidx = b * Nc + c;
    2607:           const PetscInt qcidx = q * Nc + c;

    2609:           elemVec[Nb * side + b] += tmpBasis[bcidx] * f0[qcidx];
    2610:           for (PetscInt d = 0; d < dE; ++d) elemVec[Nb * side + b] += tmpBasisDer[bcidx * dE + d] * f1[qcidx * dE + d];
    2611:         }
    2612:       }
    2613:     }
    2614:   }
    2615:   return PETSC_SUCCESS;
    2616: }

    2618: #define petsc_elemmat_kernel_g1(_NbI, _NcI, _NbJ, _NcJ, _dE) \
    2619:   do { \
    2620:     for (PetscInt fc = 0; fc < (_NcI); ++fc) { \
    2621:       for (PetscInt gc = 0; gc < (_NcJ); ++gc) { \
    2622:         const PetscScalar *G = g1 + (fc * (_NcJ) + gc) * _dE; \
    2623:         for (PetscInt f = 0; f < (_NbI); ++f) { \
    2624:           const PetscScalar tBIv = tmpBasisI[f * (_NcI) + fc]; \
    2625:           for (PetscInt g = 0; g < (_NbJ); ++g) { \
    2626:             const PetscScalar *tBDJ = tmpBasisDerJ + (g * (_NcJ) + gc) * (_dE); \
    2627:             PetscScalar        s    = 0.0; \
    2628:             for (PetscInt df = 0; df < _dE; ++df) s += G[df] * tBDJ[df]; \
    2629:             elemMat[(offsetI + f) * totDim + (offsetJ + g)] += s * tBIv; \
    2630:           } \
    2631:         } \
    2632:       } \
    2633:     } \
    2634:   } while (0)

    2636: #define petsc_elemmat_kernel_g2(_NbI, _NcI, _NbJ, _NcJ, _dE) \
    2637:   do { \
    2638:     for (PetscInt gc = 0; gc < (_NcJ); ++gc) { \
    2639:       for (PetscInt fc = 0; fc < (_NcI); ++fc) { \
    2640:         const PetscScalar *G = g2 + (fc * (_NcJ) + gc) * _dE; \
    2641:         for (PetscInt g = 0; g < (_NbJ); ++g) { \
    2642:           const PetscScalar tBJv = tmpBasisJ[g * (_NcJ) + gc]; \
    2643:           for (PetscInt f = 0; f < (_NbI); ++f) { \
    2644:             const PetscScalar *tBDI = tmpBasisDerI + (f * (_NcI) + fc) * (_dE); \
    2645:             PetscScalar        s    = 0.0; \
    2646:             for (PetscInt df = 0; df < _dE; ++df) s += tBDI[df] * G[df]; \
    2647:             elemMat[(offsetI + f) * totDim + (offsetJ + g)] += s * tBJv; \
    2648:           } \
    2649:         } \
    2650:       } \
    2651:     } \
    2652:   } while (0)

    2654: #define petsc_elemmat_kernel_g3(_NbI, _NcI, _NbJ, _NcJ, _dE) \
    2655:   do { \
    2656:     for (PetscInt fc = 0; fc < (_NcI); ++fc) { \
    2657:       for (PetscInt gc = 0; gc < (_NcJ); ++gc) { \
    2658:         const PetscScalar *G = g3 + (fc * (_NcJ) + gc) * (_dE) * (_dE); \
    2659:         for (PetscInt f = 0; f < (_NbI); ++f) { \
    2660:           const PetscScalar *tBDI = tmpBasisDerI + (f * (_NcI) + fc) * (_dE); \
    2661:           for (PetscInt g = 0; g < (_NbJ); ++g) { \
    2662:             PetscScalar        s    = 0.0; \
    2663:             const PetscScalar *tBDJ = tmpBasisDerJ + (g * (_NcJ) + gc) * (_dE); \
    2664:             for (PetscInt df = 0; df < (_dE); ++df) { \
    2665:               for (PetscInt dg = 0; dg < (_dE); ++dg) s += tBDI[df] * G[df * (_dE) + dg] * tBDJ[dg]; \
    2666:             } \
    2667:             elemMat[(offsetI + f) * totDim + (offsetJ + g)] += s; \
    2668:           } \
    2669:         } \
    2670:       } \
    2671:     } \
    2672:   } while (0)

    2674: PetscErrorCode PetscFEUpdateElementMat_Internal(PetscFE feI, PetscFE feJ, PetscInt r, PetscInt q, PetscTabulation TI, PetscScalar tmpBasisI[], PetscScalar tmpBasisDerI[], PetscTabulation TJ, PetscScalar tmpBasisJ[], PetscScalar tmpBasisDerJ[], PetscFEGeom *fegeom, const PetscScalar g0[], const PetscScalar g1[], const PetscScalar g2[], const PetscScalar g3[], PetscInt totDim, PetscInt offsetI, PetscInt offsetJ, PetscScalar elemMat[])
    2675: {
    2676:   const PetscInt   cdim      = TI->cdim;
    2677:   const PetscInt   dE        = fegeom->dimEmbed;
    2678:   const PetscInt   NqI       = TI->Np;
    2679:   const PetscInt   NbI       = TI->Nb;
    2680:   const PetscInt   NcI       = TI->Nc;
    2681:   const PetscReal *basisI    = &TI->T[0][(r * NqI + q) * NbI * NcI];
    2682:   const PetscReal *basisDerI = &TI->T[1][(r * NqI + q) * NbI * NcI * cdim];
    2683:   const PetscInt   NqJ       = TJ->Np;
    2684:   const PetscInt   NbJ       = TJ->Nb;
    2685:   const PetscInt   NcJ       = TJ->Nc;
    2686:   const PetscReal *basisJ    = &TJ->T[0][(r * NqJ + q) * NbJ * NcJ];
    2687:   const PetscReal *basisDerJ = &TJ->T[1][(r * NqJ + q) * NbJ * NcJ * cdim];

    2689:   for (PetscInt f = 0; f < NbI; ++f) {
    2690:     for (PetscInt fc = 0; fc < NcI; ++fc) {
    2691:       const PetscInt fidx = f * NcI + fc; /* Test function basis index */

    2693:       tmpBasisI[fidx] = basisI[fidx];
    2694:       for (PetscInt df = 0; df < cdim; ++df) tmpBasisDerI[fidx * dE + df] = basisDerI[fidx * cdim + df];
    2695:     }
    2696:   }
    2697:   PetscCall(PetscFEPushforward(feI, fegeom, NbI, tmpBasisI));
    2698:   PetscCall(PetscFEPushforwardGradient(feI, fegeom, NbI, tmpBasisDerI));
    2699:   if (feI != feJ) {
    2700:     for (PetscInt g = 0; g < NbJ; ++g) {
    2701:       for (PetscInt gc = 0; gc < NcJ; ++gc) {
    2702:         const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */

    2704:         tmpBasisJ[gidx] = basisJ[gidx];
    2705:         for (PetscInt dg = 0; dg < cdim; ++dg) tmpBasisDerJ[gidx * dE + dg] = basisDerJ[gidx * cdim + dg];
    2706:       }
    2707:     }
    2708:     PetscCall(PetscFEPushforward(feJ, fegeom, NbJ, tmpBasisJ));
    2709:     PetscCall(PetscFEPushforwardGradient(feJ, fegeom, NbJ, tmpBasisDerJ));
    2710:   } else {
    2711:     tmpBasisJ    = tmpBasisI;
    2712:     tmpBasisDerJ = tmpBasisDerI;
    2713:   }
    2714:   if (PetscUnlikely(g0)) {
    2715:     for (PetscInt f = 0; f < NbI; ++f) {
    2716:       const PetscInt i = offsetI + f; /* Element matrix row */

    2718:       for (PetscInt fc = 0; fc < NcI; ++fc) {
    2719:         const PetscScalar bI = tmpBasisI[f * NcI + fc]; /* Test function basis value */

    2721:         for (PetscInt g = 0; g < NbJ; ++g) {
    2722:           const PetscInt j    = offsetJ + g; /* Element matrix column */
    2723:           const PetscInt fOff = i * totDim + j;

    2725:           for (PetscInt gc = 0; gc < NcJ; ++gc) elemMat[fOff] += bI * g0[fc * NcJ + gc] * tmpBasisJ[g * NcJ + gc];
    2726:         }
    2727:       }
    2728:     }
    2729:   }
    2730:   if (PetscUnlikely(g1)) {
    2731: #if 1
    2732:     if (dE == 2) {
    2733:       petsc_elemmat_kernel_g1(NbI, NcI, NbJ, NcJ, 2);
    2734:     } else if (dE == 3) {
    2735:       petsc_elemmat_kernel_g1(NbI, NcI, NbJ, NcJ, 3);
    2736:     } else {
    2737:       petsc_elemmat_kernel_g1(NbI, NcI, NbJ, NcJ, dE);
    2738:     }
    2739: #else
    2740:     for (PetscInt f = 0; f < NbI; ++f) {
    2741:       const PetscInt i = offsetI + f; /* Element matrix row */

    2743:       for (PetscInt fc = 0; fc < NcI; ++fc) {
    2744:         const PetscScalar bI = tmpBasisI[f * NcI + fc]; /* Test function basis value */

    2746:         for (PetscInt g = 0; g < NbJ; ++g) {
    2747:           const PetscInt j    = offsetJ + g; /* Element matrix column */
    2748:           const PetscInt fOff = i * totDim + j;

    2750:           for (PetscInt gc = 0; gc < NcJ; ++gc) {
    2751:             const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */

    2753:             for (PetscInt df = 0; df < dE; ++df) elemMat[fOff] += bI * g1[(fc * NcJ + gc) * dE + df] * tmpBasisDerJ[gidx * dE + df];
    2754:           }
    2755:         }
    2756:       }
    2757:     }
    2758: #endif
    2759:   }
    2760:   if (PetscUnlikely(g2)) {
    2761: #if 1
    2762:     if (dE == 2) {
    2763:       petsc_elemmat_kernel_g2(NbI, NcI, NbJ, NcJ, 2);
    2764:     } else if (dE == 3) {
    2765:       petsc_elemmat_kernel_g2(NbI, NcI, NbJ, NcJ, 3);
    2766:     } else {
    2767:       petsc_elemmat_kernel_g2(NbI, NcI, NbJ, NcJ, dE);
    2768:     }
    2769: #else
    2770:     for (PetscInt g = 0; g < NbJ; ++g) {
    2771:       const PetscInt j = offsetJ + g; /* Element matrix column */

    2773:       for (PetscInt gc = 0; gc < NcJ; ++gc) {
    2774:         const PetscScalar bJ = tmpBasisJ[g * NcJ + gc]; /* Trial function basis value */

    2776:         for (PetscInt f = 0; f < NbI; ++f) {
    2777:           const PetscInt i    = offsetI + f; /* Element matrix row */
    2778:           const PetscInt fOff = i * totDim + j;

    2780:           for (PetscInt fc = 0; fc < NcI; ++fc) {
    2781:             const PetscInt fidx = f * NcI + fc; /* Test function basis index */

    2783:             for (PetscInt df = 0; df < dE; ++df) elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g2[(fc * NcJ + gc) * dE + df] * bJ;
    2784:           }
    2785:         }
    2786:       }
    2787:     }
    2788: #endif
    2789:   }
    2790:   if (PetscUnlikely(g3)) {
    2791: #if 1
    2792:     if (dE == 2) {
    2793:       petsc_elemmat_kernel_g3(NbI, NcI, NbJ, NcJ, 2);
    2794:     } else if (dE == 3) {
    2795:       petsc_elemmat_kernel_g3(NbI, NcI, NbJ, NcJ, 3);
    2796:     } else {
    2797:       petsc_elemmat_kernel_g3(NbI, NcI, NbJ, NcJ, dE);
    2798:     }
    2799: #else
    2800:     for (PetscInt f = 0; f < NbI; ++f) {
    2801:       const PetscInt i = offsetI + f; /* Element matrix row */

    2803:       for (PetscInt fc = 0; fc < NcI; ++fc) {
    2804:         const PetscInt fidx = f * NcI + fc; /* Test function basis index */

    2806:         for (PetscInt g = 0; g < NbJ; ++g) {
    2807:           const PetscInt j    = offsetJ + g; /* Element matrix column */
    2808:           const PetscInt fOff = i * totDim + j;

    2810:           for (PetscInt gc = 0; gc < NcJ; ++gc) {
    2811:             const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */

    2813:             for (PetscInt df = 0; df < dE; ++df) {
    2814:               for (PetscInt dg = 0; dg < dE; ++dg) elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g3[((fc * NcJ + gc) * dE + df) * dE + dg] * tmpBasisDerJ[gidx * dE + dg];
    2815:             }
    2816:           }
    2817:         }
    2818:       }
    2819:     }
    2820: #endif
    2821:   }
    2822:   return PETSC_SUCCESS;
    2823: }

    2825: #undef petsc_elemmat_kernel_g1
    2826: #undef petsc_elemmat_kernel_g2
    2827: #undef petsc_elemmat_kernel_g3

    2829: PetscErrorCode PetscFEUpdateElementMat_Hybrid_Internal(PetscFE feI, PetscBool isHybridI, PetscFE feJ, PetscBool isHybridJ, PetscInt r, PetscInt s, PetscInt t, PetscInt q, PetscTabulation TI, PetscScalar tmpBasisI[], PetscScalar tmpBasisDerI[], PetscTabulation TJ, PetscScalar tmpBasisJ[], PetscScalar tmpBasisDerJ[], PetscFEGeom *fegeom, const PetscScalar g0[], const PetscScalar g1[], const PetscScalar g2[], const PetscScalar g3[], PetscInt eOffset, PetscInt totDim, PetscInt offsetI, PetscInt offsetJ, PetscScalar elemMat[])
    2830: {
    2831:   const PetscInt   dE        = TI->cdim;
    2832:   const PetscInt   NqI       = TI->Np;
    2833:   const PetscInt   NbI       = TI->Nb;
    2834:   const PetscInt   NcI       = TI->Nc;
    2835:   const PetscReal *basisI    = &TI->T[0][(r * NqI + q) * NbI * NcI];
    2836:   const PetscReal *basisDerI = &TI->T[1][(r * NqI + q) * NbI * NcI * dE];
    2837:   const PetscInt   NqJ       = TJ->Np;
    2838:   const PetscInt   NbJ       = TJ->Nb;
    2839:   const PetscInt   NcJ       = TJ->Nc;
    2840:   const PetscReal *basisJ    = &TJ->T[0][(r * NqJ + q) * NbJ * NcJ];
    2841:   const PetscReal *basisDerJ = &TJ->T[1][(r * NqJ + q) * NbJ * NcJ * dE];
    2842:   const PetscInt   so        = isHybridI ? 0 : s;
    2843:   const PetscInt   to        = isHybridJ ? 0 : t;
    2844:   PetscInt         f, fc, g, gc, df, dg;

    2846:   for (f = 0; f < NbI; ++f) {
    2847:     for (fc = 0; fc < NcI; ++fc) {
    2848:       const PetscInt fidx = f * NcI + fc; /* Test function basis index */

    2850:       tmpBasisI[fidx] = basisI[fidx];
    2851:       for (df = 0; df < dE; ++df) tmpBasisDerI[fidx * dE + df] = basisDerI[fidx * dE + df];
    2852:     }
    2853:   }
    2854:   PetscCall(PetscFEPushforward(feI, fegeom, NbI, tmpBasisI));
    2855:   PetscCall(PetscFEPushforwardGradient(feI, fegeom, NbI, tmpBasisDerI));
    2856:   for (g = 0; g < NbJ; ++g) {
    2857:     for (gc = 0; gc < NcJ; ++gc) {
    2858:       const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */

    2860:       tmpBasisJ[gidx] = basisJ[gidx];
    2861:       for (dg = 0; dg < dE; ++dg) tmpBasisDerJ[gidx * dE + dg] = basisDerJ[gidx * dE + dg];
    2862:     }
    2863:   }
    2864:   PetscCall(PetscFEPushforward(feJ, fegeom, NbJ, tmpBasisJ));
    2865:   // TODO This is currently broken since we do not pull the geometry down to the lower dimension
    2866:   // PetscCall(PetscFEPushforwardGradient(feJ, fegeom, NbJ, tmpBasisDerJ));
    2867:   for (f = 0; f < NbI; ++f) {
    2868:     for (fc = 0; fc < NcI; ++fc) {
    2869:       const PetscInt fidx = f * NcI + fc;           /* Test function basis index */
    2870:       const PetscInt i    = offsetI + NbI * so + f; /* Element matrix row */
    2871:       for (g = 0; g < NbJ; ++g) {
    2872:         for (gc = 0; gc < NcJ; ++gc) {
    2873:           const PetscInt gidx = g * NcJ + gc;           /* Trial function basis index */
    2874:           const PetscInt j    = offsetJ + NbJ * to + g; /* Element matrix column */
    2875:           const PetscInt fOff = eOffset + i * totDim + j;

    2877:           elemMat[fOff] += tmpBasisI[fidx] * g0[fc * NcJ + gc] * tmpBasisJ[gidx];
    2878:           for (df = 0; df < dE; ++df) {
    2879:             elemMat[fOff] += tmpBasisI[fidx] * g1[(fc * NcJ + gc) * dE + df] * tmpBasisDerJ[gidx * dE + df];
    2880:             elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g2[(fc * NcJ + gc) * dE + df] * tmpBasisJ[gidx];
    2881:             for (dg = 0; dg < dE; ++dg) elemMat[fOff] += tmpBasisDerI[fidx * dE + df] * g3[((fc * NcJ + gc) * dE + df) * dE + dg] * tmpBasisDerJ[gidx * dE + dg];
    2882:           }
    2883:         }
    2884:       }
    2885:     }
    2886:   }
    2887:   return PETSC_SUCCESS;
    2888: }

    2890: /*@
    2891:   PetscFECreateCellGeometry - Populates the arrays in a `PetscFEGeom` for a single reference cell of a `PetscFE`.

    2893:   Not Collective

    2895:   Input Parameters:
    2896: + fe   - the `PetscFE` whose dual-space `DM` provides the reference cell
    2897: - quad - the quadrature at which to evaluate the geometry, or `NULL` to use the `PetscFE`'s own quadrature

    2899:   Output Parameter:
    2900: . cgeom - the `PetscFEGeom` populated with reference-cell coordinates, Jacobians, inverse Jacobians, and their determinants

    2902:   Level: developer

    2904:   Notes:
    2905:   This does not create `cgeom`, it allocates the arrays within one

    2907:   Free the storage with `PetscFEDestroyCellGeometry()`.

    2909: .seealso: `PetscFE`, `PetscFEGeom`, `PetscFEDestroyCellGeometry()`, `PetscFEGetQuadrature()`, `DMPlexComputeCellGeometryFEM()`
    2910: @*/
    2911: PetscErrorCode PetscFECreateCellGeometry(PetscFE fe, PetscQuadrature quad, PetscFEGeom *cgeom)
    2912: {
    2913:   PetscDualSpace  dsp;
    2914:   DM              dm;
    2915:   PetscQuadrature quadDef;
    2916:   PetscInt        dim, cdim, Nq;

    2918:   PetscFunctionBegin;
    2919:   PetscCall(PetscFEGetDualSpace(fe, &dsp));
    2920:   PetscCall(PetscDualSpaceGetDM(dsp, &dm));
    2921:   PetscCall(DMGetDimension(dm, &dim));
    2922:   PetscCall(DMGetCoordinateDim(dm, &cdim));
    2923:   PetscCall(PetscFEGetQuadrature(fe, &quadDef));
    2924:   quad = quad ? quad : quadDef;
    2925:   PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
    2926:   PetscCall(PetscMalloc1(Nq * cdim, &cgeom->v));
    2927:   PetscCall(PetscMalloc1(Nq * cdim * cdim, &cgeom->J));
    2928:   PetscCall(PetscMalloc1(Nq * cdim * cdim, &cgeom->invJ));
    2929:   PetscCall(PetscMalloc1(Nq, &cgeom->detJ));
    2930:   cgeom->dim       = dim;
    2931:   cgeom->dimEmbed  = cdim;
    2932:   cgeom->numCells  = 1;
    2933:   cgeom->numPoints = Nq;
    2934:   PetscCall(DMPlexComputeCellGeometryFEM(dm, 0, quad, cgeom->v, cgeom->J, cgeom->invJ, cgeom->detJ));
    2935:   PetscFunctionReturn(PETSC_SUCCESS);
    2936: }

    2938: /*@
    2939:   PetscFEDestroyCellGeometry - Free the arrays inside a `PetscFEGeom` allocated by `PetscFECreateCellGeometry()`.

    2941:   Not Collective

    2943:   Input Parameters:
    2944: + fe    - the `PetscFE` (unused, kept for API symmetry with `PetscFECreateCellGeometry()`)
    2945: - cgeom - the `PetscFEGeom` whose owned arrays should be freed

    2947:   Level: developer

    2949: .seealso: `PetscFE`, `PetscFEGeom`, `PetscFECreateCellGeometry()`
    2950: @*/
    2951: PetscErrorCode PetscFEDestroyCellGeometry(PetscFE fe, PetscFEGeom *cgeom)
    2952: {
    2953:   PetscFunctionBegin;
    2954:   PetscCall(PetscFree(cgeom->v));
    2955:   PetscCall(PetscFree(cgeom->J));
    2956:   PetscCall(PetscFree(cgeom->invJ));
    2957:   PetscCall(PetscFree(cgeom->detJ));
    2958:   PetscFunctionReturn(PETSC_SUCCESS);
    2959: }

    2961: #if 0
    2962: PetscErrorCode PetscFEUpdateElementMat_Internal_SparseIndices(PetscTabulation TI, PetscTabulation TJ, PetscInt dimEmbed, const PetscInt g0[], const PetscInt g1[], const PetscInt g2[], const PetscInt g3[], PetscInt totDim, PetscInt offsetI, PetscInt offsetJ, PetscInt *n_g0, PetscInt **g0_idxs_out, PetscInt *n_g1, PetscInt **g1_idxs_out, PetscInt *n_g2, PetscInt **g2_idxs_out, PetscInt *n_g3, PetscInt **g3_idxs_out)
    2963: {
    2964:   const PetscInt dE      = dimEmbed;
    2965:   const PetscInt NbI     = TI->Nb;
    2966:   const PetscInt NcI     = TI->Nc;
    2967:   const PetscInt NbJ     = TJ->Nb;
    2968:   const PetscInt NcJ     = TJ->Nc;
    2969:   PetscBool      has_g0  = g0 ? PETSC_TRUE : PETSC_FALSE;
    2970:   PetscBool      has_g1  = g1 ? PETSC_TRUE : PETSC_FALSE;
    2971:   PetscBool      has_g2  = g2 ? PETSC_TRUE : PETSC_FALSE;
    2972:   PetscBool      has_g3  = g3 ? PETSC_TRUE : PETSC_FALSE;
    2973:   PetscInt      *g0_idxs = NULL, *g1_idxs = NULL, *g2_idxs = NULL, *g3_idxs = NULL;
    2974:   PetscInt       g0_i, g1_i, g2_i, g3_i;

    2976:   PetscFunctionBegin;
    2977:   g0_i = g1_i = g2_i = g3_i = 0;
    2978:   if (has_g0)
    2979:     for (PetscInt i = 0; i < NcI * NcJ; i++)
    2980:       if (g0[i]) g0_i += NbI * NbJ;
    2981:   if (has_g1)
    2982:     for (PetscInt i = 0; i < NcI * NcJ * dE; i++)
    2983:       if (g1[i]) g1_i += NbI * NbJ;
    2984:   if (has_g2)
    2985:     for (PetscInt i = 0; i < NcI * NcJ * dE; i++)
    2986:       if (g2[i]) g2_i += NbI * NbJ;
    2987:   if (has_g3)
    2988:     for (PetscInt i = 0; i < NcI * NcJ * dE * dE; i++)
    2989:       if (g3[i]) g3_i += NbI * NbJ;
    2990:   if (g0_i == NbI * NbJ * NcI * NcJ) g0_i = 0;
    2991:   if (g1_i == NbI * NbJ * NcI * NcJ * dE) g1_i = 0;
    2992:   if (g2_i == NbI * NbJ * NcI * NcJ * dE) g2_i = 0;
    2993:   if (g3_i == NbI * NbJ * NcI * NcJ * dE * dE) g3_i = 0;
    2994:   has_g0 = g0_i ? PETSC_TRUE : PETSC_FALSE;
    2995:   has_g1 = g1_i ? PETSC_TRUE : PETSC_FALSE;
    2996:   has_g2 = g2_i ? PETSC_TRUE : PETSC_FALSE;
    2997:   has_g3 = g3_i ? PETSC_TRUE : PETSC_FALSE;
    2998:   if (has_g0) PetscCall(PetscMalloc1(4 * g0_i, &g0_idxs));
    2999:   if (has_g1) PetscCall(PetscMalloc1(4 * g1_i, &g1_idxs));
    3000:   if (has_g2) PetscCall(PetscMalloc1(4 * g2_i, &g2_idxs));
    3001:   if (has_g3) PetscCall(PetscMalloc1(4 * g3_i, &g3_idxs));
    3002:   g0_i = g1_i = g2_i = g3_i = 0;

    3004:   for (PetscInt f = 0; f < NbI; ++f) {
    3005:     const PetscInt i = offsetI + f; /* Element matrix row */
    3006:     for (PetscInt fc = 0; fc < NcI; ++fc) {
    3007:       const PetscInt fidx = f * NcI + fc; /* Test function basis index */

    3009:       for (PetscInt g = 0; g < NbJ; ++g) {
    3010:         const PetscInt j    = offsetJ + g; /* Element matrix column */
    3011:         const PetscInt fOff = i * totDim + j;
    3012:         for (PetscInt gc = 0; gc < NcJ; ++gc) {
    3013:           const PetscInt gidx = g * NcJ + gc; /* Trial function basis index */

    3015:           if (has_g0) {
    3016:             if (g0[fc * NcJ + gc]) {
    3017:               g0_idxs[4 * g0_i + 0] = fidx;
    3018:               g0_idxs[4 * g0_i + 1] = fc * NcJ + gc;
    3019:               g0_idxs[4 * g0_i + 2] = gidx;
    3020:               g0_idxs[4 * g0_i + 3] = fOff;
    3021:               g0_i++;
    3022:             }
    3023:           }

    3025:           for (PetscInt df = 0; df < dE; ++df) {
    3026:             if (has_g1) {
    3027:               if (g1[(fc * NcJ + gc) * dE + df]) {
    3028:                 g1_idxs[4 * g1_i + 0] = fidx;
    3029:                 g1_idxs[4 * g1_i + 1] = (fc * NcJ + gc) * dE + df;
    3030:                 g1_idxs[4 * g1_i + 2] = gidx * dE + df;
    3031:                 g1_idxs[4 * g1_i + 3] = fOff;
    3032:                 g1_i++;
    3033:               }
    3034:             }
    3035:             if (has_g2) {
    3036:               if (g2[(fc * NcJ + gc) * dE + df]) {
    3037:                 g2_idxs[4 * g2_i + 0] = fidx * dE + df;
    3038:                 g2_idxs[4 * g2_i + 1] = (fc * NcJ + gc) * dE + df;
    3039:                 g2_idxs[4 * g2_i + 2] = gidx;
    3040:                 g2_idxs[4 * g2_i + 3] = fOff;
    3041:                 g2_i++;
    3042:               }
    3043:             }
    3044:             if (has_g3) {
    3045:               for (PetscInt dg = 0; dg < dE; ++dg) {
    3046:                 if (g3[((fc * NcJ + gc) * dE + df) * dE + dg]) {
    3047:                   g3_idxs[4 * g3_i + 0] = fidx * dE + df;
    3048:                   g3_idxs[4 * g3_i + 1] = ((fc * NcJ + gc) * dE + df) * dE + dg;
    3049:                   g3_idxs[4 * g3_i + 2] = gidx * dE + dg;
    3050:                   g3_idxs[4 * g3_i + 3] = fOff;
    3051:                   g3_i++;
    3052:                 }
    3053:               }
    3054:             }
    3055:           }
    3056:         }
    3057:       }
    3058:     }
    3059:   }
    3060:   *n_g0 = g0_i;
    3061:   *n_g1 = g1_i;
    3062:   *n_g2 = g2_i;
    3063:   *n_g3 = g3_i;

    3065:   *g0_idxs_out = g0_idxs;
    3066:   *g1_idxs_out = g1_idxs;
    3067:   *g2_idxs_out = g2_idxs;
    3068:   *g3_idxs_out = g3_idxs;
    3069:   PetscFunctionReturn(PETSC_SUCCESS);
    3070: }

    3072: //example HOW TO USE
    3073:       for (PetscInt i = 0; i < g0_sparse_n; i++) {
    3074:         PetscInt bM = g0_sparse_idxs[4 * i + 0];
    3075:         PetscInt bN = g0_sparse_idxs[4 * i + 1];
    3076:         PetscInt bK = g0_sparse_idxs[4 * i + 2];
    3077:         PetscInt bO = g0_sparse_idxs[4 * i + 3];
    3078:         elemMat[bO] += tmpBasisI[bM] * g0[bN] * tmpBasisJ[bK];
    3079:       }
    3080: #endif