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

Actual source code: plexfem.c

      1: #include <petsc/private/dmpleximpl.h>
      2: #include <petscsf.h>

      4: #include <petscblaslapack.h>
      5: #include <petsc/private/hashsetij.h>
      6: #include <petsc/private/petscfeimpl.h>
      7: #include <petsc/private/petscfvimpl.h>

      9: PetscBool  Clementcite       = PETSC_FALSE;
     10: const char ClementCitation[] = "@article{clement1975approximation,\n"
     11:                                "  title   = {Approximation by finite element functions using local regularization},\n"
     12:                                "  author  = {Philippe Cl{\\'e}ment},\n"
     13:                                "  journal = {Revue fran{\\c{c}}aise d'automatique, informatique, recherche op{\\'e}rationnelle. Analyse num{\\'e}rique},\n"
     14:                                "  volume  = {9},\n"
     15:                                "  number  = {R2},\n"
     16:                                "  pages   = {77--84},\n"
     17:                                "  year    = {1975}\n}\n";

     19: static PetscErrorCode DMPlexConvertPlex(DM dm, DM *plex, PetscBool copy)
     20: {
     21:   PetscBool isPlex;

     23:   PetscFunctionBegin;
     24:   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
     25:   if (isPlex) {
     26:     *plex = dm;
     27:     PetscCall(PetscObjectReference((PetscObject)dm));
     28:   } else {
     29:     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
     30:     if (!*plex) {
     31:       PetscCall(DMConvert(dm, DMPLEX, plex));
     32:       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
     33:     } else {
     34:       PetscCall(PetscObjectReference((PetscObject)*plex));
     35:     }
     36:     if (copy) {
     37:       DMSubDomainHookLink link;

     39:       PetscCall(DMCopyDS(dm, PETSC_DETERMINE, PETSC_DETERMINE, *plex));
     40:       PetscCall(DMCopyAuxiliaryVec(dm, *plex));
     41:       /* Run the subdomain hook (this will copy the DMSNES/DMTS) */
     42:       for (link = dm->subdomainhook; link; link = link->next) {
     43:         if (link->ddhook) PetscCall((*link->ddhook)(dm, *plex, link->ctx));
     44:       }
     45:     }
     46:   }
     47:   PetscFunctionReturn(PETSC_SUCCESS);
     48: }

     50: static PetscErrorCode PetscContainerCtxDestroy_PetscFEGeom(PetscCtxRt ctx)
     51: {
     52:   PetscFEGeom *geom = *(PetscFEGeom **)ctx;

     54:   PetscFunctionBegin;
     55:   PetscCall(PetscFEGeomDestroy(&geom));
     56:   PetscFunctionReturn(PETSC_SUCCESS);
     57: }

     59: static PetscErrorCode DMPlexGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscFEGeomMode mode, PetscFEGeom **geom)
     60: {
     61:   char           composeStr[33] = {0};
     62:   PetscObjectId  id;
     63:   PetscContainer container;

     65:   PetscFunctionBegin;
     66:   PetscCall(PetscObjectGetId((PetscObject)quad, &id));
     67:   PetscCall(PetscSNPrintf(composeStr, 32, "DMPlexGetFEGeom_%" PetscInt64_FMT "\n", id));
     68:   PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
     69:   if (container) {
     70:     PetscCall(PetscContainerGetPointer(container, geom));
     71:   } else {
     72:     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, mode, geom));
     73:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
     74:     PetscCall(PetscContainerSetPointer(container, (void *)*geom));
     75:     PetscCall(PetscContainerSetCtxDestroy(container, PetscContainerCtxDestroy_PetscFEGeom));
     76:     PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
     77:     PetscCall(PetscContainerDestroy(&container));
     78:   }
     79:   PetscFunctionReturn(PETSC_SUCCESS);
     80: }

     82: static PetscErrorCode DMPlexRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscFEGeomMode mode, PetscFEGeom **geom)
     83: {
     84:   PetscFunctionBegin;
     85:   *geom = NULL;
     86:   PetscFunctionReturn(PETSC_SUCCESS);
     87: }

     89: /*@
     90:   DMPlexGetScale - Get the scale for the specified fundamental unit

     92:   Not Collective

     94:   Input Parameters:
     95: + dm   - the `DM`
     96: - unit - The SI unit

     98:   Output Parameter:
     99: . scale - The value used to scale all quantities with this unit

    101:   Level: advanced

    103: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetScale()`, `PetscUnit`
    104: @*/
    105: PetscErrorCode DMPlexGetScale(DM dm, PetscUnit unit, PetscReal *scale)
    106: {
    107:   DM_Plex *mesh = (DM_Plex *)dm->data;

    109:   PetscFunctionBegin;
    111:   PetscAssertPointer(scale, 3);
    112:   *scale = mesh->scale[unit];
    113:   PetscFunctionReturn(PETSC_SUCCESS);
    114: }

    116: /*@
    117:   DMPlexSetScale - Set the scale for the specified fundamental unit

    119:   Not Collective

    121:   Input Parameters:
    122: + dm    - the `DM`
    123: . unit  - The SI unit
    124: - scale - The value used to scale all quantities with this unit

    126:   Level: advanced

    128: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetScale()`, `PetscUnit`
    129: @*/
    130: PetscErrorCode DMPlexSetScale(DM dm, PetscUnit unit, PetscReal scale)
    131: {
    132:   DM_Plex *mesh = (DM_Plex *)dm->data;

    134:   PetscFunctionBegin;
    136:   mesh->scale[unit] = scale;
    137:   PetscFunctionReturn(PETSC_SUCCESS);
    138: }

    140: PetscErrorCode DMPlexGetUseCeed_Plex(DM dm, PetscBool *useCeed)
    141: {
    142:   DM_Plex *mesh = (DM_Plex *)dm->data;

    144:   PetscFunctionBegin;
    145:   *useCeed = mesh->useCeed;
    146:   PetscFunctionReturn(PETSC_SUCCESS);
    147: }
    148: PetscErrorCode DMPlexSetUseCeed_Plex(DM dm, PetscBool useCeed)
    149: {
    150:   DM_Plex *mesh = (DM_Plex *)dm->data;

    152:   PetscFunctionBegin;
    153:   mesh->useCeed = useCeed;
    154:   PetscFunctionReturn(PETSC_SUCCESS);
    155: }

    157: /*@
    158:   DMPlexGetUseCeed - Get flag for using the LibCEED backend

    160:   Not collective

    162:   Input Parameter:
    163: . dm - The `DM`

    165:   Output Parameter:
    166: . useCeed - The flag

    168:   Level: intermediate

    170: .seealso: `DMPlexSetUseCeed()`
    171: @*/
    172: PetscErrorCode DMPlexGetUseCeed(DM dm, PetscBool *useCeed)
    173: {
    174:   PetscFunctionBegin;
    176:   PetscAssertPointer(useCeed, 2);
    177:   *useCeed = PETSC_FALSE;
    178:   PetscTryMethod(dm, "DMPlexGetUseCeed_C", (DM, PetscBool *), (dm, useCeed));
    179:   PetscFunctionReturn(PETSC_SUCCESS);
    180: }

    182: /*@
    183:   DMPlexSetUseCeed - Set flag for using the LibCEED backend

    185:   Not collective

    187:   Input Parameters:
    188: + dm      - The `DM`
    189: - useCeed - The flag

    191:   Level: intermediate

    193: .seealso: `DMPlexGetUseCeed()`
    194: @*/
    195: PetscErrorCode DMPlexSetUseCeed(DM dm, PetscBool useCeed)
    196: {
    197:   PetscFunctionBegin;
    200:   PetscUseMethod(dm, "DMPlexSetUseCeed_C", (DM, PetscBool), (dm, useCeed));
    201:   PetscFunctionReturn(PETSC_SUCCESS);
    202: }

    204: /*@
    205:   DMPlexGetUseMatClosurePermutation - Get flag for using a closure permutation for matrix insertion

    207:   Not collective

    209:   Input Parameter:
    210: . dm - The `DM`

    212:   Output Parameter:
    213: . useClPerm - The flag

    215:   Level: intermediate

    217: .seealso: `DMPlexSetUseMatClosurePermutation()`
    218: @*/
    219: PetscErrorCode DMPlexGetUseMatClosurePermutation(DM dm, PetscBool *useClPerm)
    220: {
    221:   DM_Plex *mesh = (DM_Plex *)dm->data;

    223:   PetscFunctionBegin;
    225:   PetscAssertPointer(useClPerm, 2);
    226:   *useClPerm = mesh->useMatClPerm;
    227:   PetscFunctionReturn(PETSC_SUCCESS);
    228: }

    230: /*@
    231:   DMPlexSetUseMatClosurePermutation - Set flag for using a closure permutation for matrix insertion

    233:   Not collective

    235:   Input Parameters:
    236: + dm        - The `DM`
    237: - useClPerm - The flag

    239:   Level: intermediate

    241: .seealso: `DMPlexGetUseMatClosurePermutation()`
    242: @*/
    243: PetscErrorCode DMPlexSetUseMatClosurePermutation(DM dm, PetscBool useClPerm)
    244: {
    245:   DM_Plex *mesh = (DM_Plex *)dm->data;

    247:   PetscFunctionBegin;
    250:   mesh->useMatClPerm = useClPerm;
    251:   PetscFunctionReturn(PETSC_SUCCESS);
    252: }

    254: static PetscErrorCode DMPlexProjectRigidBody_Private(PetscInt dim, PetscReal t, const PetscReal X[], PetscInt Nc, PetscScalar *mode, PetscCtx ctx)
    255: {
    256:   const PetscInt eps[3][3][3] = {
    257:     {{0, 0, 0},  {0, 0, 1},  {0, -1, 0}},
    258:     {{0, 0, -1}, {0, 0, 0},  {1, 0, 0} },
    259:     {{0, 1, 0},  {-1, 0, 0}, {0, 0, 0} }
    260:   };
    261:   PetscInt *ctxInt = (PetscInt *)ctx;
    262:   PetscInt  dim2   = ctxInt[0];
    263:   PetscInt  d      = ctxInt[1];
    264:   PetscInt  i, j, k = dim > 2 ? d - dim : d;

    266:   PetscFunctionBegin;
    267:   PetscCheck(dim == dim2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input dimension %" PetscInt_FMT " does not match context dimension %" PetscInt_FMT, dim, dim2);
    268:   for (i = 0; i < dim; i++) mode[i] = 0.;
    269:   if (d < dim) {
    270:     mode[d] = 1.; /* Translation along axis d */
    271:   } else {
    272:     for (i = 0; i < dim; i++) {
    273:       for (j = 0; j < dim; j++) mode[j] += eps[i][j][k] * X[i]; /* Rotation about axis d */
    274:     }
    275:   }
    276:   PetscFunctionReturn(PETSC_SUCCESS);
    277: }

    279: /*@
    280:   DMPlexCreateRigidBody - For the default global section, create rigid body modes by function space interpolation

    282:   Collective

    284:   Input Parameters:
    285: + dm    - the `DM`
    286: - field - The field number for the rigid body space, or 0 for the default

    288:   Output Parameter:
    289: . sp - the null space

    291:   Level: advanced

    293:   Note:
    294:   This is necessary to provide a suitable coarse space for algebraic multigrid

    296: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`, `PCGAMG`
    297: @*/
    298: PetscErrorCode DMPlexCreateRigidBody(DM dm, PetscInt field, MatNullSpace *sp)
    299: {
    300:   PetscErrorCode (**func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *);
    301:   MPI_Comm     comm;
    302:   Vec          mode[6];
    303:   PetscSection section, globalSection;
    304:   PetscInt     dim, dimEmbed, Nf, n, m, mmin, d, i, j;
    305:   void       **ctxs;

    307:   PetscFunctionBegin;
    308:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
    309:   PetscCall(DMGetDimension(dm, &dim));
    310:   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
    311:   PetscCall(DMGetNumFields(dm, &Nf));
    312:   PetscCheck(!Nf || !(field < 0 || field >= Nf), comm, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", field, Nf);
    313:   if (dim == 1 && Nf < 2) {
    314:     PetscCall(MatNullSpaceCreate(comm, PETSC_TRUE, 0, NULL, sp));
    315:     PetscFunctionReturn(PETSC_SUCCESS);
    316:   }
    317:   PetscCall(DMGetLocalSection(dm, &section));
    318:   PetscCall(DMGetGlobalSection(dm, &globalSection));
    319:   PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
    320:   PetscCall(PetscCalloc2(Nf, &func, Nf, &ctxs));
    321:   m = (dim * (dim + 1)) / 2;
    322:   PetscCall(VecCreate(comm, &mode[0]));
    323:   PetscCall(VecSetType(mode[0], dm->vectype));
    324:   PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
    325:   PetscCall(VecSetUp(mode[0]));
    326:   PetscCall(VecGetSize(mode[0], &n));
    327:   mmin        = PetscMin(m, n);
    328:   func[field] = DMPlexProjectRigidBody_Private;
    329:   for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
    330:   for (d = 0; d < m; d++) {
    331:     PetscInt ctx[2];

    333:     ctxs[field] = (void *)(&ctx[0]);
    334:     ctx[0]      = dimEmbed;
    335:     ctx[1]      = d;
    336:     PetscCall(DMProjectFunction(dm, 0.0, func, ctxs, INSERT_VALUES, mode[d]));
    337:   }
    338:   /* Orthonormalize system */
    339:   for (i = 0; i < mmin; ++i) {
    340:     PetscScalar dots[6];
    341:     PetscReal   norm;

    343:     PetscCall(VecNormalize(mode[i], &norm));
    344:     if (PetscAbsReal(norm) <= PETSC_SQRT_MACHINE_EPSILON) {
    345:       PetscCall(VecDestroy(&mode[i]));
    346:       if (i < mmin - 1) {
    347:         for (j = i; j < mmin - 1; j++) mode[j] = mode[j + 1];
    348:         mode[mmin - 1] = NULL;
    349:       }
    350:       m--;
    351:       mmin--;
    352:       i--;
    353:       continue;
    354:     }
    355:     PetscCall(VecMDot(mode[i], mmin - i - 1, mode + i + 1, dots + i + 1));
    356:     for (j = i + 1; j < mmin; ++j) {
    357:       dots[j] *= -1.0;
    358:       PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
    359:     }
    360:   }
    361:   PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, mmin, mode, sp));
    362:   for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
    363:   PetscCall(PetscFree2(func, ctxs));
    364:   PetscFunctionReturn(PETSC_SUCCESS);
    365: }

    367: /*@
    368:   DMPlexCreateRigidBodies - For the default global section, create rigid body modes by function space interpolation

    370:   Collective

    372:   Input Parameters:
    373: + dm    - the `DM`
    374: . nb    - The number of bodies
    375: . label - The `DMLabel` marking each domain
    376: . nids  - The number of ids per body
    377: - ids   - An array of the label ids in sequence for each domain

    379:   Output Parameter:
    380: . sp - the null space

    382:   Level: advanced

    384:   Note:
    385:   This is necessary to provide a suitable coarse space for algebraic multigrid

    387: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`
    388: @*/
    389: PetscErrorCode DMPlexCreateRigidBodies(DM dm, PetscInt nb, DMLabel label, const PetscInt nids[], const PetscInt ids[], MatNullSpace *sp)
    390: {
    391:   MPI_Comm     comm;
    392:   PetscSection section, globalSection;
    393:   Vec         *mode;
    394:   PetscScalar *dots;
    395:   PetscInt     dim, dimEmbed, n, m, b, d, i, j, off;

    397:   PetscFunctionBegin;
    398:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
    399:   PetscCall(DMGetDimension(dm, &dim));
    400:   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
    401:   PetscCall(DMGetLocalSection(dm, &section));
    402:   PetscCall(DMGetGlobalSection(dm, &globalSection));
    403:   PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
    404:   m = nb * (dim * (dim + 1)) / 2;
    405:   PetscCall(PetscMalloc2(m, &mode, m, &dots));
    406:   PetscCall(VecCreate(comm, &mode[0]));
    407:   PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
    408:   PetscCall(VecSetUp(mode[0]));
    409:   for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
    410:   for (b = 0, off = 0; b < nb; ++b) {
    411:     for (d = 0; d < m / nb; ++d) {
    412:       PetscInt ctx[2];
    413:       PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *) = DMPlexProjectRigidBody_Private;
    414:       void *voidctx                                                                                   = (void *)(&ctx[0]);

    416:       ctx[0] = dimEmbed;
    417:       ctx[1] = d;
    418:       PetscCall(DMProjectFunctionLabel(dm, 0.0, label, nids[b], &ids[off], 0, NULL, &func, &voidctx, INSERT_VALUES, mode[d]));
    419:       off += nids[b];
    420:     }
    421:   }
    422:   /* Orthonormalize system */
    423:   for (i = 0; i < m; ++i) {
    424:     PetscScalar dots[6];

    426:     PetscCall(VecNormalize(mode[i], NULL));
    427:     PetscCall(VecMDot(mode[i], m - i - 1, mode + i + 1, dots + i + 1));
    428:     for (j = i + 1; j < m; ++j) {
    429:       dots[j] *= -1.0;
    430:       PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
    431:     }
    432:   }
    433:   PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, m, mode, sp));
    434:   for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
    435:   PetscCall(PetscFree2(mode, dots));
    436:   PetscFunctionReturn(PETSC_SUCCESS);
    437: }

    439: /*@
    440:   DMPlexSetMaxProjectionHeight - In DMPlexProjectXXXLocal() functions, the projected values of a basis function's dofs
    441:   are computed by associating the basis function with one of the mesh points in its transitively-closed support, and
    442:   evaluating the dual space basis of that point.

    444:   Input Parameters:
    445: + dm     - the `DMPLEX` object
    446: - height - the maximum projection height >= 0

    448:   Level: advanced

    450:   Notes:
    451:   A basis function is associated with the point in its transitively-closed support whose mesh
    452:   height is highest (w.r.t. DAG height), but not greater than the maximum projection height,
    453:   which is set with this function.  By default, the maximum projection height is zero, which
    454:   means that only mesh cells are used to project basis functions.  A height of one, for
    455:   example, evaluates a cell-interior basis functions using its cells dual space basis, but all
    456:   other basis functions with the dual space basis of a face.

    458: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
    459: @*/
    460: PetscErrorCode DMPlexSetMaxProjectionHeight(DM dm, PetscInt height)
    461: {
    462:   DM_Plex *plex = (DM_Plex *)dm->data;

    464:   PetscFunctionBegin;
    466:   plex->maxProjectionHeight = height;
    467:   PetscFunctionReturn(PETSC_SUCCESS);
    468: }

    470: /*@
    471:   DMPlexGetMaxProjectionHeight - Get the maximum height (w.r.t. DAG) of mesh points used to evaluate dual bases in
    472:   DMPlexProjectXXXLocal() functions.

    474:   Input Parameter:
    475: . dm - the `DMPLEX` object

    477:   Output Parameter:
    478: . height - the maximum projection height

    480:   Level: intermediate

    482: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
    483: @*/
    484: PetscErrorCode DMPlexGetMaxProjectionHeight(DM dm, PetscInt *height)
    485: {
    486:   DM_Plex *plex = (DM_Plex *)dm->data;

    488:   PetscFunctionBegin;
    490:   *height = plex->maxProjectionHeight;
    491:   PetscFunctionReturn(PETSC_SUCCESS);
    492: }

    494: typedef struct {
    495:   PetscReal    alpha; /* The first Euler angle, and in 2D the only one */
    496:   PetscReal    beta;  /* The second Euler angle */
    497:   PetscReal    gamma; /* The third Euler angle */
    498:   PetscInt     dim;   /* The dimension of R */
    499:   PetscScalar *R;     /* The rotation matrix, transforming a vector in the local basis to the global basis */
    500:   PetscScalar *RT;    /* The transposed rotation matrix, transforming a vector in the global basis to the local basis */
    501: } RotCtx;

    503: /*
    504:   Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
    505:   we rotate with respect to a fixed initial coordinate system, the local basis (x-y-z). The global basis (X-Y-Z) is reached as follows:
    506:   $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
    507:   $ The XYZ system rotates again about the x axis by beta. The Z axis is now at angle beta with respect to the z axis.
    508:   $ The XYZ system rotates a third time about the z axis by gamma.
    509: */
    510: static PetscErrorCode DMPlexBasisTransformSetUp_Rotation_Internal(DM dm, PetscCtx ctx)
    511: {
    512:   RotCtx   *rc  = (RotCtx *)ctx;
    513:   PetscInt  dim = rc->dim;
    514:   PetscReal c1, s1, c2, s2, c3, s3;

    516:   PetscFunctionBegin;
    517:   PetscCall(PetscMalloc2(PetscSqr(dim), &rc->R, PetscSqr(dim), &rc->RT));
    518:   switch (dim) {
    519:   case 2:
    520:     c1       = PetscCosReal(rc->alpha);
    521:     s1       = PetscSinReal(rc->alpha);
    522:     rc->R[0] = c1;
    523:     rc->R[1] = s1;
    524:     rc->R[2] = -s1;
    525:     rc->R[3] = c1;
    526:     PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
    527:     DMPlex_Transpose2D_Internal(rc->RT);
    528:     break;
    529:   case 3:
    530:     c1       = PetscCosReal(rc->alpha);
    531:     s1       = PetscSinReal(rc->alpha);
    532:     c2       = PetscCosReal(rc->beta);
    533:     s2       = PetscSinReal(rc->beta);
    534:     c3       = PetscCosReal(rc->gamma);
    535:     s3       = PetscSinReal(rc->gamma);
    536:     rc->R[0] = c1 * c3 - c2 * s1 * s3;
    537:     rc->R[1] = c3 * s1 + c1 * c2 * s3;
    538:     rc->R[2] = s2 * s3;
    539:     rc->R[3] = -c1 * s3 - c2 * c3 * s1;
    540:     rc->R[4] = c1 * c2 * c3 - s1 * s3;
    541:     rc->R[5] = c3 * s2;
    542:     rc->R[6] = s1 * s2;
    543:     rc->R[7] = -c1 * s2;
    544:     rc->R[8] = c2;
    545:     PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
    546:     DMPlex_Transpose3D_Internal(rc->RT);
    547:     break;
    548:   default:
    549:     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Dimension %" PetscInt_FMT " not supported", dim);
    550:   }
    551:   PetscFunctionReturn(PETSC_SUCCESS);
    552: }

    554: static PetscErrorCode DMPlexBasisTransformDestroy_Rotation_Internal(DM dm, PetscCtx ctx)
    555: {
    556:   RotCtx *rc = (RotCtx *)ctx;

    558:   PetscFunctionBegin;
    559:   PetscCall(PetscFree2(rc->R, rc->RT));
    560:   PetscCall(PetscFree(rc));
    561:   PetscFunctionReturn(PETSC_SUCCESS);
    562: }

    564: static PetscErrorCode DMPlexBasisTransformGetMatrix_Rotation_Internal(DM dm, const PetscReal x[], PetscBool l2g, const PetscScalar **A, PetscCtx ctx)
    565: {
    566:   RotCtx *rc = (RotCtx *)ctx;

    568:   PetscFunctionBeginHot;
    569:   PetscAssertPointer(ctx, 5);
    570:   if (l2g) {
    571:     *A = rc->R;
    572:   } else {
    573:     *A = rc->RT;
    574:   }
    575:   PetscFunctionReturn(PETSC_SUCCESS);
    576: }

    578: PetscErrorCode DMPlexBasisTransformApplyReal_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscReal *y, PetscReal *z, PetscCtx ctx)
    579: {
    580:   PetscFunctionBegin;
    581: #if defined(PETSC_USE_COMPLEX)
    582:   switch (dim) {
    583:   case 2: {
    584:     PetscScalar yt[2] = {y[0], y[1]}, zt[2] = {0.0, 0.0};

    586:     PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
    587:     z[0] = PetscRealPart(zt[0]);
    588:     z[1] = PetscRealPart(zt[1]);
    589:   } break;
    590:   case 3: {
    591:     PetscScalar yt[3] = {y[0], y[1], y[2]}, zt[3] = {0.0, 0.0, 0.0};

    593:     PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
    594:     z[0] = PetscRealPart(zt[0]);
    595:     z[1] = PetscRealPart(zt[1]);
    596:     z[2] = PetscRealPart(zt[2]);
    597:   } break;
    598:   }
    599: #else
    600:   PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, y, z, ctx));
    601: #endif
    602:   PetscFunctionReturn(PETSC_SUCCESS);
    603: }

    605: PetscErrorCode DMPlexBasisTransformApply_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscScalar *y, PetscScalar *z, PetscCtx ctx)
    606: {
    607:   const PetscScalar *A;

    609:   PetscFunctionBeginHot;
    610:   PetscCall((*dm->transformGetMatrix)(dm, x, l2g, &A, ctx));
    611:   switch (dim) {
    612:   case 2:
    613:     DMPlex_Mult2D_Internal(A, 1, y, z);
    614:     break;
    615:   case 3:
    616:     DMPlex_Mult3D_Internal(A, 1, y, z);
    617:     break;
    618:   }
    619:   PetscFunctionReturn(PETSC_SUCCESS);
    620: }

    622: static PetscErrorCode DMPlexBasisTransformField_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscInt f, PetscBool l2g, PetscScalar *a)
    623: {
    624:   PetscSection       ts;
    625:   const PetscScalar *ta, *tva;
    626:   PetscInt           dof;

    628:   PetscFunctionBeginHot;
    629:   PetscCall(DMGetLocalSection(tdm, &ts));
    630:   PetscCall(PetscSectionGetFieldDof(ts, p, f, &dof));
    631:   PetscCall(VecGetArrayRead(tv, &ta));
    632:   PetscCall(DMPlexPointLocalFieldRead(tdm, p, f, ta, &tva));
    633:   if (l2g) {
    634:     switch (dof) {
    635:     case 4:
    636:       DMPlex_Mult2D_Internal(tva, 1, a, a);
    637:       break;
    638:     case 9:
    639:       DMPlex_Mult3D_Internal(tva, 1, a, a);
    640:       break;
    641:     }
    642:   } else {
    643:     switch (dof) {
    644:     case 4:
    645:       DMPlex_MultTranspose2D_Internal(tva, 1, a, a);
    646:       break;
    647:     case 9:
    648:       DMPlex_MultTranspose3D_Internal(tva, 1, a, a);
    649:       break;
    650:     }
    651:   }
    652:   PetscCall(VecRestoreArrayRead(tv, &ta));
    653:   PetscFunctionReturn(PETSC_SUCCESS);
    654: }

    656: static PetscErrorCode DMPlexBasisTransformFieldTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt pf, PetscInt f, PetscInt pg, PetscInt g, PetscBool l2g, PetscInt lda, PetscScalar *a)
    657: {
    658:   PetscSection       s, ts;
    659:   const PetscScalar *ta, *tvaf, *tvag;
    660:   PetscInt           fdof, gdof, fpdof, gpdof;

    662:   PetscFunctionBeginHot;
    663:   PetscCall(DMGetLocalSection(dm, &s));
    664:   PetscCall(DMGetLocalSection(tdm, &ts));
    665:   PetscCall(PetscSectionGetFieldDof(s, pf, f, &fpdof));
    666:   PetscCall(PetscSectionGetFieldDof(s, pg, g, &gpdof));
    667:   PetscCall(PetscSectionGetFieldDof(ts, pf, f, &fdof));
    668:   PetscCall(PetscSectionGetFieldDof(ts, pg, g, &gdof));
    669:   PetscCall(VecGetArrayRead(tv, &ta));
    670:   PetscCall(DMPlexPointLocalFieldRead(tdm, pf, f, ta, &tvaf));
    671:   PetscCall(DMPlexPointLocalFieldRead(tdm, pg, g, ta, &tvag));
    672:   if (l2g) {
    673:     switch (fdof) {
    674:     case 4:
    675:       DMPlex_MatMult2D_Internal(tvaf, gpdof, lda, a, a);
    676:       break;
    677:     case 9:
    678:       DMPlex_MatMult3D_Internal(tvaf, gpdof, lda, a, a);
    679:       break;
    680:     }
    681:     switch (gdof) {
    682:     case 4:
    683:       DMPlex_MatMultTransposeLeft2D_Internal(tvag, fpdof, lda, a, a);
    684:       break;
    685:     case 9:
    686:       DMPlex_MatMultTransposeLeft3D_Internal(tvag, fpdof, lda, a, a);
    687:       break;
    688:     }
    689:   } else {
    690:     switch (fdof) {
    691:     case 4:
    692:       DMPlex_MatMultTranspose2D_Internal(tvaf, gpdof, lda, a, a);
    693:       break;
    694:     case 9:
    695:       DMPlex_MatMultTranspose3D_Internal(tvaf, gpdof, lda, a, a);
    696:       break;
    697:     }
    698:     switch (gdof) {
    699:     case 4:
    700:       DMPlex_MatMultLeft2D_Internal(tvag, fpdof, lda, a, a);
    701:       break;
    702:     case 9:
    703:       DMPlex_MatMultLeft3D_Internal(tvag, fpdof, lda, a, a);
    704:       break;
    705:     }
    706:   }
    707:   PetscCall(VecRestoreArrayRead(tv, &ta));
    708:   PetscFunctionReturn(PETSC_SUCCESS);
    709: }

    711: PetscErrorCode DMPlexBasisTransformPoint_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool fieldActive[], PetscBool l2g, PetscScalar *a)
    712: {
    713:   PetscSection    s;
    714:   PetscSection    clSection;
    715:   IS              clPoints;
    716:   const PetscInt *clp;
    717:   PetscInt       *points = NULL;
    718:   PetscInt        Nf, f, Np, cp, dof, d = 0;

    720:   PetscFunctionBegin;
    721:   PetscCall(DMGetLocalSection(dm, &s));
    722:   PetscCall(PetscSectionGetNumFields(s, &Nf));
    723:   PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
    724:   for (f = 0; f < Nf; ++f) {
    725:     for (cp = 0; cp < Np * 2; cp += 2) {
    726:       PetscCall(PetscSectionGetFieldDof(s, points[cp], f, &dof));
    727:       if (!dof) continue;
    728:       if (fieldActive[f]) PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, points[cp], f, l2g, &a[d]));
    729:       d += dof;
    730:     }
    731:   }
    732:   PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
    733:   PetscFunctionReturn(PETSC_SUCCESS);
    734: }

    736: PetscErrorCode DMPlexBasisTransformPointTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool l2g, PetscInt lda, PetscScalar *a)
    737: {
    738:   PetscSection    s;
    739:   PetscSection    clSection;
    740:   IS              clPoints;
    741:   const PetscInt *clp;
    742:   PetscInt       *points = NULL;
    743:   PetscInt        Nf, f, g, Np, cpf, cpg, fdof, gdof, r, c = 0;

    745:   PetscFunctionBegin;
    746:   PetscCall(DMGetLocalSection(dm, &s));
    747:   PetscCall(PetscSectionGetNumFields(s, &Nf));
    748:   PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
    749:   for (f = 0, r = 0; f < Nf; ++f) {
    750:     for (cpf = 0; cpf < Np * 2; cpf += 2) {
    751:       PetscCall(PetscSectionGetFieldDof(s, points[cpf], f, &fdof));
    752:       for (g = 0, c = 0; g < Nf; ++g) {
    753:         for (cpg = 0; cpg < Np * 2; cpg += 2) {
    754:           PetscCall(PetscSectionGetFieldDof(s, points[cpg], g, &gdof));
    755:           PetscCall(DMPlexBasisTransformFieldTensor_Internal(dm, tdm, tv, points[cpf], f, points[cpg], g, l2g, lda, &a[r * lda + c]));
    756:           c += gdof;
    757:         }
    758:       }
    759:       PetscCheck(c == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of columns %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
    760:       r += fdof;
    761:     }
    762:   }
    763:   PetscCheck(r == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of rows %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
    764:   PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
    765:   PetscFunctionReturn(PETSC_SUCCESS);
    766: }

    768: static PetscErrorCode DMPlexBasisTransform_Internal(DM dm, Vec lv, PetscBool l2g)
    769: {
    770:   DM                 tdm;
    771:   Vec                tv;
    772:   PetscSection       ts, s;
    773:   const PetscScalar *ta;
    774:   PetscScalar       *a, *va;
    775:   PetscInt           pStart, pEnd, p, Nf, f;

    777:   PetscFunctionBegin;
    778:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
    779:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
    780:   PetscCall(DMGetLocalSection(tdm, &ts));
    781:   PetscCall(DMGetLocalSection(dm, &s));
    782:   PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
    783:   PetscCall(PetscSectionGetNumFields(s, &Nf));
    784:   PetscCall(VecGetArray(lv, &a));
    785:   PetscCall(VecGetArrayRead(tv, &ta));
    786:   for (p = pStart; p < pEnd; ++p) {
    787:     for (f = 0; f < Nf; ++f) {
    788:       PetscCall(DMPlexPointLocalFieldRef(dm, p, f, a, &va));
    789:       PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, p, f, l2g, va));
    790:     }
    791:   }
    792:   PetscCall(VecRestoreArray(lv, &a));
    793:   PetscCall(VecRestoreArrayRead(tv, &ta));
    794:   PetscFunctionReturn(PETSC_SUCCESS);
    795: }

    797: /*@
    798:   DMPlexGlobalToLocalBasis - Transform the values in the given local vector from the global basis to the local basis

    800:   Input Parameters:
    801: + dm - The `DM`
    802: - lv - A local vector with values in the global basis

    804:   Output Parameter:
    805: . lv - A local vector with values in the local basis

    807:   Level: developer

    809:   Note:
    810:   This method is only intended to be called inside `DMGlobalToLocal()`. It is unlikely that a user will have a local vector full of coefficients for the global basis unless they are reimplementing GlobalToLocal.

    812: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexLocalToGlobalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
    813: @*/
    814: PetscErrorCode DMPlexGlobalToLocalBasis(DM dm, Vec lv)
    815: {
    816:   PetscFunctionBegin;
    819:   PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_FALSE));
    820:   PetscFunctionReturn(PETSC_SUCCESS);
    821: }

    823: /*@
    824:   DMPlexLocalToGlobalBasis - Transform the values in the given local vector from the local basis to the global basis

    826:   Input Parameters:
    827: + dm - The `DM`
    828: - lv - A local vector with values in the local basis

    830:   Output Parameter:
    831: . lv - A local vector with values in the global basis

    833:   Level: developer

    835:   Note:
    836:   This method is only intended to be called inside `DMGlobalToLocal()`. It is unlikely that a user would want a local vector full of coefficients for the global basis unless they are reimplementing GlobalToLocal.

    838: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
    839: @*/
    840: PetscErrorCode DMPlexLocalToGlobalBasis(DM dm, Vec lv)
    841: {
    842:   PetscFunctionBegin;
    845:   PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_TRUE));
    846:   PetscFunctionReturn(PETSC_SUCCESS);
    847: }

    849: /*@
    850:   DMPlexCreateBasisRotation - Create an internal transformation from the global basis, used to specify boundary conditions
    851:   and global solutions, to a local basis, appropriate for discretization integrals and assembly.

    853:   Input Parameters:
    854: + dm    - The `DM`
    855: . alpha - The first Euler angle, and in 2D the only one
    856: . beta  - The second Euler angle
    857: - gamma - The third Euler angle

    859:   Level: developer

    861:   Note:
    862:   Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
    863:   we rotate with respect to a fixed initial coordinate system, the local basis (x-y-z). The global basis (X-Y-Z) is reached as follows
    864: .vb
    865:    The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
    866:    The XYZ system rotates again about the x axis by beta. The Z axis is now at angle beta with respect to the z axis.
    867:    The XYZ system rotates a third time about the z axis by gamma.
    868: .ve

    870: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMPlexLocalToGlobalBasis()`
    871: @*/
    872: PetscErrorCode DMPlexCreateBasisRotation(DM dm, PetscReal alpha, PetscReal beta, PetscReal gamma)
    873: {
    874:   RotCtx  *rc;
    875:   PetscInt cdim;

    877:   PetscFunctionBegin;
    878:   PetscCall(DMGetCoordinateDim(dm, &cdim));
    879:   PetscCall(PetscMalloc1(1, &rc));
    880:   dm->transformCtx       = rc;
    881:   dm->transformSetUp     = DMPlexBasisTransformSetUp_Rotation_Internal;
    882:   dm->transformDestroy   = DMPlexBasisTransformDestroy_Rotation_Internal;
    883:   dm->transformGetMatrix = DMPlexBasisTransformGetMatrix_Rotation_Internal;
    884:   rc->dim                = cdim;
    885:   rc->alpha              = alpha;
    886:   rc->beta               = beta;
    887:   rc->gamma              = gamma;
    888:   PetscCall((*dm->transformSetUp)(dm, dm->transformCtx));
    889:   PetscCall(DMConstructBasisTransform_Internal(dm));
    890:   PetscFunctionReturn(PETSC_SUCCESS);
    891: }

    893: /*@C
    894:   DMPlexInsertBoundaryValuesEssential - Insert boundary values into a local vector using a function of the coordinates

    896:   Input Parameters:
    897: + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
    898: . time   - The time
    899: . field  - The field to constrain
    900: . Nc     - The number of constrained field components, or 0 for all components
    901: . comps  - An array of constrained component numbers, or `NULL` for all components
    902: . label  - The `DMLabel` defining constrained points
    903: . numids - The number of `DMLabel` ids for constrained points
    904: . ids    - An array of ids for constrained points
    905: . func   - A pointwise function giving boundary values
    906: - ctx    - An optional application context for `bcFunc`

    908:   Output Parameter:
    909: . locX - A local vector to receives the boundary values

    911:   Level: developer

    913: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
    914: @*/
    915: PetscErrorCode DMPlexInsertBoundaryValuesEssential(DM dm, PetscReal time, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), PetscCtx ctx, Vec locX)
    916: {
    917:   PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, PetscCtx ctx);
    918:   void   **ctxs;
    919:   PetscInt numFields;

    921:   PetscFunctionBegin;
    922:   PetscCall(DMGetNumFields(dm, &numFields));
    923:   PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
    924:   funcs[field] = func;
    925:   ctxs[field]  = ctx;
    926:   PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_BC_VALUES, locX));
    927:   PetscCall(PetscFree2(funcs, ctxs));
    928:   PetscFunctionReturn(PETSC_SUCCESS);
    929: }

    931: /*@C
    932:   DMPlexInsertBoundaryValuesEssentialField - Insert boundary values into a local vector using a function of the coordinates and field data

    934:   Input Parameters:
    935: + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
    936: . time   - The time
    937: . locU   - A local vector with the input solution values
    938: . field  - The field to constrain
    939: . Nc     - The number of constrained field components, or 0 for all components
    940: . comps  - An array of constrained component numbers, or `NULL` for all components
    941: . label  - The `DMLabel` defining constrained points
    942: . numids - The number of `DMLabel` ids for constrained points
    943: . ids    - An array of ids for constrained points
    944: . func   - A pointwise function giving boundary values
    945: - ctx    - An optional application context for `bcFunc`

    947:   Output Parameter:
    948: . locX - A local vector to receives the boundary values

    950:   Level: developer

    952: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
    953: @*/
    954: PetscErrorCode DMPlexInsertBoundaryValuesEssentialField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], void (*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[], PetscInt, const PetscScalar[], PetscScalar[]), PetscCtx ctx, Vec locX)
    955: {
    956:   void (**funcs)(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[], PetscInt, const PetscScalar[], PetscScalar[]);
    957:   void   **ctxs;
    958:   PetscInt numFields;

    960:   PetscFunctionBegin;
    961:   PetscCall(DMGetNumFields(dm, &numFields));
    962:   PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
    963:   funcs[field] = func;
    964:   ctxs[field]  = ctx;
    965:   PetscCall(DMProjectFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
    966:   PetscCall(PetscFree2(funcs, ctxs));
    967:   PetscFunctionReturn(PETSC_SUCCESS);
    968: }

    970: /*@C
    971:   DMPlexInsertBoundaryValuesEssentialBdField - Insert boundary values into a local vector using a function of the coordinates and boundary field data

    973:   Collective

    975:   Input Parameters:
    976: + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
    977: . time   - The time
    978: . locU   - A local vector with the input solution values
    979: . field  - The field to constrain
    980: . Nc     - The number of constrained field components, or 0 for all components
    981: . comps  - An array of constrained component numbers, or `NULL` for all components
    982: . label  - The `DMLabel` defining constrained points
    983: . numids - The number of `DMLabel` ids for constrained points
    984: . ids    - An array of ids for constrained points
    985: . func   - A pointwise function giving boundary values, the calling sequence is given in `DMProjectBdFieldLabelLocal()`
    986: - ctx    - An optional application context for `func`

    988:   Output Parameter:
    989: . locX - A local vector to receive the boundary values

    991:   Level: developer

    993: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectBdFieldLabelLocal()`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
    994: @*/
    995: PetscErrorCode DMPlexInsertBoundaryValuesEssentialBdField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], void (*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[]), PetscCtx ctx, Vec locX)
    996: {
    997:   void (**funcs)(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[]);
    998:   void   **ctxs;
    999:   PetscInt numFields;

    1001:   PetscFunctionBegin;
    1002:   PetscCall(DMGetNumFields(dm, &numFields));
    1003:   PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
    1004:   funcs[field] = func;
    1005:   ctxs[field]  = ctx;
    1006:   PetscCall(DMProjectBdFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
    1007:   PetscCall(PetscFree2(funcs, ctxs));
    1008:   PetscFunctionReturn(PETSC_SUCCESS);
    1009: }

    1011: /*@C
    1012:   DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector

    1014:   Input Parameters:
    1015: + dm           - The `DM`, with a `PetscDS` that matches the problem being constrained
    1016: . time         - The time
    1017: . faceGeometry - A vector with the FVM face geometry information
    1018: . cellGeometry - A vector with the FVM cell geometry information
    1019: . Grad         - A vector with the FVM cell gradient information
    1020: . field        - The field to constrain
    1021: . Nc           - The number of constrained field components, or 0 for all components
    1022: . comps        - An array of constrained component numbers, or `NULL` for all components
    1023: . label        - The `DMLabel` defining constrained points
    1024: . numids       - The number of `DMLabel` ids for constrained points
    1025: . ids          - An array of ids for constrained points
    1026: . func         - A pointwise function giving boundary values
    1027: - ctx          - An optional application context for bcFunc

    1029:   Output Parameter:
    1030: . locX - A local vector to receives the boundary values

    1032:   Level: developer

    1034:   Note:
    1035:   This implementation currently ignores the numcomps/comps argument from `DMAddBoundary()`

    1037: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
    1038: @*/
    1039: PetscErrorCode DMPlexInsertBoundaryValuesRiemann(DM dm, PetscReal time, Vec faceGeometry, Vec cellGeometry, Vec Grad, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *), PetscCtx ctx, Vec locX)
    1040: {
    1041:   PetscDS            prob;
    1042:   PetscSF            sf;
    1043:   DM                 dmFace, dmCell, dmGrad;
    1044:   const PetscScalar *facegeom, *cellgeom = NULL, *grad;
    1045:   const PetscInt    *leaves;
    1046:   PetscScalar       *x, *fx;
    1047:   PetscInt           dim, nleaves, loc, fStart, fEnd, pdim, i;
    1048:   PetscErrorCode     ierru = PETSC_SUCCESS;

    1050:   PetscFunctionBegin;
    1051:   PetscCall(DMGetPointSF(dm, &sf));
    1052:   PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
    1053:   nleaves = PetscMax(0, nleaves);
    1054:   PetscCall(DMGetDimension(dm, &dim));
    1055:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
    1056:   PetscCall(DMGetDS(dm, &prob));
    1057:   PetscCall(VecGetDM(faceGeometry, &dmFace));
    1058:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
    1059:   if (cellGeometry) {
    1060:     PetscCall(VecGetDM(cellGeometry, &dmCell));
    1061:     PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
    1062:   }
    1063:   if (Grad) {
    1064:     PetscFV fv;

    1066:     PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fv));
    1067:     PetscCall(VecGetDM(Grad, &dmGrad));
    1068:     PetscCall(VecGetArrayRead(Grad, &grad));
    1069:     PetscCall(PetscFVGetNumComponents(fv, &pdim));
    1070:     PetscCall(DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx));
    1071:   }
    1072:   PetscCall(VecGetArray(locX, &x));
    1073:   for (i = 0; i < numids; ++i) {
    1074:     IS              faceIS;
    1075:     const PetscInt *faces;
    1076:     PetscInt        numFaces, f;

    1078:     PetscCall(DMLabelGetStratumIS(label, ids[i], &faceIS));
    1079:     if (!faceIS) continue; /* No points with that id on this process */
    1080:     PetscCall(ISGetLocalSize(faceIS, &numFaces));
    1081:     PetscCall(ISGetIndices(faceIS, &faces));
    1082:     for (f = 0; f < numFaces; ++f) {
    1083:       const PetscInt   face = faces[f], *cells;
    1084:       PetscFVFaceGeom *fg;

    1086:       if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */
    1087:       PetscCall(PetscFindInt(face, nleaves, (PetscInt *)leaves, &loc));
    1088:       if (loc >= 0) continue;
    1089:       PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
    1090:       PetscCall(DMPlexGetSupport(dm, face, &cells));
    1091:       if (Grad) {
    1092:         PetscFVCellGeom *cg;
    1093:         PetscScalar     *cx, *cgrad;
    1094:         PetscScalar     *xG;
    1095:         PetscReal        dx[3];
    1096:         PetscInt         d;

    1098:         PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg));
    1099:         PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &cx));
    1100:         PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad));
    1101:         PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
    1102:         DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
    1103:         for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d * dim], dx);
    1104:         PetscCall((*func)(time, fg->centroid, fg->normal, fx, xG, ctx));
    1105:       } else {
    1106:         PetscScalar *xI;
    1107:         PetscScalar *xG;

    1109:         PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &xI));
    1110:         PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
    1111:         ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx);
    1112:         if (ierru) {
    1113:           PetscCall(ISRestoreIndices(faceIS, &faces));
    1114:           PetscCall(ISDestroy(&faceIS));
    1115:           goto cleanup;
    1116:         }
    1117:       }
    1118:     }
    1119:     PetscCall(ISRestoreIndices(faceIS, &faces));
    1120:     PetscCall(ISDestroy(&faceIS));
    1121:   }
    1122: cleanup:
    1123:   PetscCall(VecRestoreArray(locX, &x));
    1124:   if (Grad) {
    1125:     PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx));
    1126:     PetscCall(VecRestoreArrayRead(Grad, &grad));
    1127:   }
    1128:   if (cellGeometry) PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
    1129:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
    1130:   PetscCall(ierru);
    1131:   PetscFunctionReturn(PETSC_SUCCESS);
    1132: }

    1134: static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, PetscCtx ctx)
    1135: {
    1136:   PetscInt c;
    1137:   for (c = 0; c < Nc; ++c) u[c] = 0.0;
    1138:   return PETSC_SUCCESS;
    1139: }

    1141: PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
    1142: {
    1143:   PetscObject isZero;
    1144:   PetscDS     prob;
    1145:   PetscInt    numBd, b;

    1147:   PetscFunctionBegin;
    1148:   PetscCall(DMGetDS(dm, &prob));
    1149:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
    1150:   PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
    1151:   PetscCall(PetscDSUpdateBoundaryLabels(prob, dm));
    1152:   for (b = 0; b < numBd; ++b) {
    1153:     PetscWeakForm           wf;
    1154:     DMBoundaryConditionType type;
    1155:     const char             *name;
    1156:     DMLabel                 label;
    1157:     PetscInt                field, Nc;
    1158:     const PetscInt         *comps;
    1159:     PetscObject             obj;
    1160:     PetscClassId            id;
    1161:     PetscVoidFn            *bvfunc;
    1162:     PetscInt                numids;
    1163:     const PetscInt         *ids;
    1164:     void                   *ctx;

    1166:     PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx));
    1167:     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
    1168:     PetscCall(DMGetField(dm, field, NULL, &obj));
    1169:     PetscCall(PetscObjectGetClassId(obj, &id));
    1170:     if (id == PETSCFE_CLASSID) {
    1171:       switch (type) {
    1172:         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
    1173:       case DM_BC_ESSENTIAL: {
    1174:         PetscSimplePointFn *func = (PetscSimplePointFn *)bvfunc;

    1176:         if (isZero) func = zero;
    1177:         PetscCall(DMPlexLabelAddCells(dm, label));
    1178:         PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func, ctx, locX));
    1179:         PetscCall(DMPlexLabelClearCells(dm, label));
    1180:       } break;
    1181:       case DM_BC_ESSENTIAL_FIELD: {
    1182:         PetscPointFn *func = (PetscPointFn *)bvfunc;

    1184:         PetscCall(DMPlexLabelAddCells(dm, label));
    1185:         PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func, ctx, locX));
    1186:         PetscCall(DMPlexLabelClearCells(dm, label));
    1187:       } break;
    1188:       default:
    1189:         break;
    1190:       }
    1191:     } else if (id == PETSCFV_CLASSID) {
    1192:       {
    1193:         PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *) = (PetscErrorCode (*)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *))bvfunc;

    1195:         if (!faceGeomFVM) continue;
    1196:         PetscCall(DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids, func, ctx, locX));
    1197:       }
    1198:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1199:   }
    1200:   PetscFunctionReturn(PETSC_SUCCESS);
    1201: }

    1203: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
    1204: {
    1205:   PetscObject isZero;
    1206:   PetscDS     prob;
    1207:   PetscInt    numBd, b;

    1209:   PetscFunctionBegin;
    1210:   if (!locX) PetscFunctionReturn(PETSC_SUCCESS);
    1211:   PetscCall(DMGetDS(dm, &prob));
    1212:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
    1213:   PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
    1214:   for (b = 0; b < numBd; ++b) {
    1215:     PetscWeakForm           wf;
    1216:     DMBoundaryConditionType type;
    1217:     const char             *name;
    1218:     DMLabel                 label;
    1219:     PetscInt                field, Nc;
    1220:     const PetscInt         *comps;
    1221:     PetscObject             obj;
    1222:     PetscClassId            id;
    1223:     PetscInt                numids;
    1224:     const PetscInt         *ids;
    1225:     PetscVoidFn            *bvfunc;
    1226:     void                   *ctx;

    1228:     PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, NULL, &bvfunc, &ctx));
    1229:     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
    1230:     PetscCall(DMGetField(dm, field, NULL, &obj));
    1231:     PetscCall(PetscObjectGetClassId(obj, &id));
    1232:     if (id == PETSCFE_CLASSID) {
    1233:       switch (type) {
    1234:         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
    1235:       case DM_BC_ESSENTIAL: {
    1236:         PetscSimplePointFn *func_t = (PetscSimplePointFn *)bvfunc;

    1238:         if (isZero) func_t = zero;
    1239:         PetscCall(DMPlexLabelAddCells(dm, label));
    1240:         PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
    1241:         PetscCall(DMPlexLabelClearCells(dm, label));
    1242:       } break;
    1243:       case DM_BC_ESSENTIAL_FIELD: {
    1244:         PetscPointFn *func_t = (PetscPointFn *)bvfunc;

    1246:         PetscCall(DMPlexLabelAddCells(dm, label));
    1247:         PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
    1248:         PetscCall(DMPlexLabelClearCells(dm, label));
    1249:       } break;
    1250:       default:
    1251:         break;
    1252:       }
    1253:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1254:   }
    1255:   PetscFunctionReturn(PETSC_SUCCESS);
    1256: }

    1258: PetscErrorCode DMPlexInsertBounds_Plex(DM dm, PetscBool lower, PetscReal time, Vec locB)
    1259: {
    1260:   PetscDS  ds;
    1261:   PetscInt numBd;

    1263:   PetscFunctionBegin;
    1264:   PetscCall(DMGetDS(dm, &ds));
    1265:   PetscCall(PetscDSGetNumBoundary(ds, &numBd));
    1266:   PetscCall(PetscDSUpdateBoundaryLabels(ds, dm));
    1267:   for (PetscInt b = 0; b < numBd; ++b) {
    1268:     PetscWeakForm           wf;
    1269:     DMBoundaryConditionType type;
    1270:     const char             *name;
    1271:     DMLabel                 label;
    1272:     PetscInt                numids;
    1273:     const PetscInt         *ids;
    1274:     PetscInt                field, Nc;
    1275:     const PetscInt         *comps;
    1276:     PetscVoidFn            *bvfunc;
    1277:     void                   *ctx;

    1279:     PetscCall(PetscDSGetBoundary(ds, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx));
    1280:     if (lower && type != DM_BC_LOWER_BOUND) continue;
    1281:     if (!lower && type != DM_BC_UPPER_BOUND) continue;
    1282:     PetscCall(DMPlexLabelAddCells(dm, label));
    1283:     {
    1284:       PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, PetscCtx ctx);
    1285:       void   **ctxs;
    1286:       PetscInt Nf;

    1288:       PetscCall(DMGetNumFields(dm, &Nf));
    1289:       PetscCall(PetscCalloc2(Nf, &funcs, Nf, &ctxs));
    1290:       funcs[field] = (PetscSimplePointFn *)bvfunc;
    1291:       ctxs[field]  = ctx;
    1292:       PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_ALL_VALUES, locB));
    1293:       PetscCall(PetscFree2(funcs, ctxs));
    1294:     }
    1295:     PetscCall(DMPlexLabelClearCells(dm, label));
    1296:   }
    1297:   PetscFunctionReturn(PETSC_SUCCESS);
    1298: }

    1300: /*@
    1301:   DMPlexInsertBoundaryValues - Puts coefficients which represent boundary values into the local solution vector

    1303:   Not Collective

    1305:   Input Parameters:
    1306: + dm              - The `DM`
    1307: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
    1308: . time            - The time
    1309: . faceGeomFVM     - Face geometry data for FV discretizations
    1310: . cellGeomFVM     - Cell geometry data for FV discretizations
    1311: - gradFVM         - Gradient reconstruction data for FV discretizations

    1313:   Output Parameter:
    1314: . locX - Solution updated with boundary values

    1316:   Level: intermediate

    1318: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `DMAddBoundary()`
    1319: @*/
    1320: PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
    1321: {
    1322:   PetscFunctionBegin;
    1328:   PetscTryMethod(dm, "DMPlexInsertBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX, time, faceGeomFVM, cellGeomFVM, gradFVM));
    1329:   PetscFunctionReturn(PETSC_SUCCESS);
    1330: }

    1332: /*@
    1333:   DMPlexInsertTimeDerivativeBoundaryValues - Puts coefficients which represent boundary values of the time derivative into the local solution vector

    1335:   Input Parameters:
    1336: + dm              - The `DM`
    1337: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
    1338: . time            - The time
    1339: . faceGeomFVM     - Face geometry data for FV discretizations
    1340: . cellGeomFVM     - Cell geometry data for FV discretizations
    1341: - gradFVM         - Gradient reconstruction data for FV discretizations

    1343:   Output Parameter:
    1344: . locX_t - Solution updated with boundary values

    1346:   Level: developer

    1348: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`
    1349: @*/
    1350: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
    1351: {
    1352:   PetscFunctionBegin;
    1358:   PetscTryMethod(dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX_t, time, faceGeomFVM, cellGeomFVM, gradFVM));
    1359:   PetscFunctionReturn(PETSC_SUCCESS);
    1360: }

    1362: /*@
    1363:   DMPlexInsertBounds - Puts coefficients which represent solution bounds into the local bounds vector

    1365:   Not Collective

    1367:   Input Parameters:
    1368: + dm    - The `DM`
    1369: . lower - If `PETSC_TRUE` use `DM_BC_LOWER_BOUND` conditions, otherwise use `DM_BC_UPPER_BOUND`
    1370: - time  - The time

    1372:   Output Parameter:
    1373: . locB - Bounds vector updated with new bounds

    1375:   Level: intermediate

    1377: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `PetscDSAddBoundary()`
    1378: @*/
    1379: PetscErrorCode DMPlexInsertBounds(DM dm, PetscBool lower, PetscReal time, Vec locB)
    1380: {
    1381:   PetscFunctionBegin;
    1384:   PetscTryMethod(dm, "DMPlexInsertBounds_C", (DM, PetscBool, PetscReal, Vec), (dm, lower, time, locB));
    1385:   PetscFunctionReturn(PETSC_SUCCESS);
    1386: }

    1388: /*@
    1389:   DMPlexInsertBoundaryValuesFVM - Reconstruct cell gradients and insert non-essential (e.g. outflow) boundary values
    1390:   into a local finite-volume solution vector.

    1392:   Collective

    1394:   Input Parameters:
    1395: + dm   - the `DMPLEX`
    1396: . fv   - the `PetscFV` discretization
    1397: . locX - the local solution vector; updated with non-essential boundary values
    1398: - time - the current time

    1400:   Output Parameter:
    1401: . locGradient - if non-`NULL`, the local vector holding the reconstructed cell gradients

    1403:   Level: developer

    1405:   Note:
    1406:   The caller receives ownership of `*locGradient` via `DMGetLocalVector()` and must return it with
    1407:   `DMRestoreLocalVector()`.

    1409: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscFV`, `DMPlexInsertBoundaryValues()`, `DMPlexReconstructGradientsFVM()`
    1410: @*/
    1411: PetscErrorCode DMPlexInsertBoundaryValuesFVM(DM dm, PetscFV fv, Vec locX, PetscReal time, Vec *locGradient)
    1412: {
    1413:   DM  dmGrad;
    1414:   Vec cellGeometryFVM, faceGeometryFVM, locGrad = NULL;

    1416:   PetscFunctionBegin;
    1420:   if (locGradient) {
    1421:     PetscAssertPointer(locGradient, 5);
    1422:     *locGradient = NULL;
    1423:   }
    1424:   PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
    1425:   /* Reconstruct and limit cell gradients */
    1426:   PetscCall(DMPlexGetGradientDM(dm, fv, &dmGrad));
    1427:   if (dmGrad) {
    1428:     Vec      grad;
    1429:     PetscInt fStart, fEnd;

    1431:     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
    1432:     PetscCall(DMGetGlobalVector(dmGrad, &grad));
    1433:     PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
    1434:     /* Communicate gradient values */
    1435:     PetscCall(DMGetLocalVector(dmGrad, &locGrad));
    1436:     PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
    1437:     PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
    1438:     PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
    1439:   }
    1440:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
    1441:   if (locGradient) *locGradient = locGrad;
    1442:   else if (locGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
    1443:   PetscFunctionReturn(PETSC_SUCCESS);
    1444: }

    1446: PetscErrorCode DMComputeL2Diff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
    1447: {
    1448:   Vec localX;

    1450:   PetscFunctionBegin;
    1451:   PetscCall(DMGetLocalVector(dm, &localX));
    1452:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL));
    1453:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
    1454:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
    1455:   PetscCall(DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff));
    1456:   PetscCall(DMRestoreLocalVector(dm, &localX));
    1457:   PetscFunctionReturn(PETSC_SUCCESS);
    1458: }

    1460: /*@C
    1461:   DMPlexComputeL2DiffLocal - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h.

    1463:   Collective

    1465:   Input Parameters:
    1466: + dm     - The `DM`
    1467: . time   - The time
    1468: . funcs  - The functions to evaluate for each field component
    1469: . ctxs   - Optional array of contexts to pass to each function, or `NULL`.
    1470: - localX - The coefficient vector u_h, a local vector

    1472:   Output Parameter:
    1473: . diff - The diff ||u - u_h||_2

    1475:   Level: developer

    1477: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
    1478: @*/
    1479: PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
    1480: {
    1481:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
    1482:   DM               tdm;
    1483:   Vec              tv;
    1484:   PetscSection     section;
    1485:   PetscQuadrature  quad;
    1486:   PetscFEGeom      fegeom;
    1487:   PetscScalar     *funcVal, *interpolant;
    1488:   PetscReal       *coords, *gcoords;
    1489:   PetscReal        localDiff = 0.0;
    1490:   const PetscReal *quadWeights;
    1491:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset;
    1492:   PetscBool        transform;

    1494:   PetscFunctionBegin;
    1495:   PetscCall(DMGetDimension(dm, &dim));
    1496:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
    1497:   fegeom.dimEmbed = coordDim;
    1498:   PetscCall(DMGetLocalSection(dm, &section));
    1499:   PetscCall(PetscSectionGetNumFields(section, &numFields));
    1500:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
    1501:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
    1502:   PetscCall(DMHasBasisTransform(dm, &transform));
    1503:   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
    1504:   for (field = 0; field < numFields; ++field) {
    1505:     PetscObject  obj;
    1506:     PetscClassId id;
    1507:     PetscInt     Nc;

    1509:     PetscCall(DMGetField(dm, field, NULL, &obj));
    1510:     PetscCall(PetscObjectGetClassId(obj, &id));
    1511:     if (id == PETSCFE_CLASSID) {
    1512:       PetscFE fe = (PetscFE)obj;

    1514:       PetscCall(PetscFEGetQuadrature(fe, &quad));
    1515:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
    1516:     } else if (id == PETSCFV_CLASSID) {
    1517:       PetscFV fv = (PetscFV)obj;

    1519:       PetscCall(PetscFVGetQuadrature(fv, &quad));
    1520:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
    1521:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1522:     numComponents += Nc;
    1523:   }
    1524:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
    1525:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
    1526:   PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * (Nq + 1), &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
    1527:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
    1528:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
    1529:   for (c = cStart; c < cEnd; ++c) {
    1530:     PetscScalar *x        = NULL;
    1531:     PetscReal    elemDiff = 0.0;
    1532:     PetscInt     qc       = 0;

    1534:     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    1535:     PetscCall(DMPlexVecGetOrientedClosure(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));

    1537:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
    1538:       PetscObject  obj;
    1539:       PetscClassId id;
    1540:       void *const  ctx = ctxs ? ctxs[field] : NULL;
    1541:       PetscInt     Nb, Nc, q, fc;

    1543:       PetscCall(DMGetField(dm, field, NULL, &obj));
    1544:       PetscCall(PetscObjectGetClassId(obj, &id));
    1545:       if (id == PETSCFE_CLASSID) {
    1546:         PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
    1547:         PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
    1548:       } else if (id == PETSCFV_CLASSID) {
    1549:         PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
    1550:         Nb = 1;
    1551:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1552:       if (debug) {
    1553:         char title[1024];
    1554:         PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
    1555:         PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
    1556:       }
    1557:       for (q = 0; q < Nq; ++q) {
    1558:         PetscFEGeom    qgeom;
    1559:         PetscErrorCode ierr;

    1561:         qgeom.dimEmbed = fegeom.dimEmbed;
    1562:         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
    1563:         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
    1564:         qgeom.detJ     = &fegeom.detJ[q];
    1565:         PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", point %" PetscInt_FMT, (double)fegeom.detJ[q], c, q);
    1566:         if (transform) {
    1567:           gcoords = &coords[coordDim * Nq];
    1568:           PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
    1569:         } else {
    1570:           gcoords = &coords[coordDim * q];
    1571:         }
    1572:         PetscCall(PetscArrayzero(funcVal, Nc));
    1573:         ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
    1574:         if (ierr) {
    1575:           PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
    1576:           PetscCall(DMRestoreLocalVector(dm, &localX));
    1577:           PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1578:         }
    1579:         if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
    1580:         if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
    1581:         else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
    1582:         else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1583:         for (fc = 0; fc < Nc; ++fc) {
    1584:           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
    1585:           if (debug)
    1586:             PetscCall(PetscPrintf(PETSC_COMM_SELF, "    elem %" PetscInt_FMT " field %" PetscInt_FMT ",%" PetscInt_FMT " point %g %g %g diff %g (%g, %g)\n", c, field, fc, (double)(coordDim > 0 ? coords[coordDim * q] : 0), (double)(coordDim > 1 ? coords[coordDim * q + 1] : 0), (double)(coordDim > 2 ? coords[coordDim * q + 2] : 0),
    1587:                                   (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]), (double)PetscRealPart(interpolant[fc]), (double)PetscRealPart(funcVal[fc])));
    1588:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
    1589:         }
    1590:       }
    1591:       fieldOffset += Nb;
    1592:       qc += Nc;
    1593:     }
    1594:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
    1595:     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
    1596:     localDiff += elemDiff;
    1597:   }
    1598:   PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1599:   PetscCallMPI(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
    1600:   *diff = PetscSqrtReal(*diff);
    1601:   PetscFunctionReturn(PETSC_SUCCESS);
    1602: }

    1604: PetscErrorCode DMComputeL2GradientDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, const PetscReal n[], PetscReal *diff)
    1605: {
    1606:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
    1607:   DM               tdm;
    1608:   PetscSection     section;
    1609:   PetscQuadrature  quad;
    1610:   Vec              localX, tv;
    1611:   PetscScalar     *funcVal, *interpolant;
    1612:   const PetscReal *quadWeights;
    1613:   PetscFEGeom      fegeom;
    1614:   PetscReal       *coords, *gcoords;
    1615:   PetscReal        localDiff = 0.0;
    1616:   PetscInt         dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
    1617:   PetscBool        transform;

    1619:   PetscFunctionBegin;
    1620:   PetscCall(DMGetDimension(dm, &dim));
    1621:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
    1622:   fegeom.dimEmbed = coordDim;
    1623:   PetscCall(DMGetLocalSection(dm, &section));
    1624:   PetscCall(PetscSectionGetNumFields(section, &numFields));
    1625:   PetscCall(DMGetLocalVector(dm, &localX));
    1626:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
    1627:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
    1628:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
    1629:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
    1630:   PetscCall(DMHasBasisTransform(dm, &transform));
    1631:   for (field = 0; field < numFields; ++field) {
    1632:     PetscFE  fe;
    1633:     PetscInt Nc;

    1635:     PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
    1636:     PetscCall(PetscFEGetQuadrature(fe, &quad));
    1637:     PetscCall(PetscFEGetNumComponents(fe, &Nc));
    1638:     numComponents += Nc;
    1639:   }
    1640:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
    1641:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
    1642:   /* PetscCall(DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX)); */
    1643:   PetscCall(PetscMalloc6(numComponents, &funcVal, coordDim * (Nq + 1), &coords, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ, numComponents * coordDim, &interpolant, Nq, &fegeom.detJ));
    1644:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    1645:   for (c = cStart; c < cEnd; ++c) {
    1646:     PetscScalar *x        = NULL;
    1647:     PetscReal    elemDiff = 0.0;
    1648:     PetscInt     qc       = 0;

    1650:     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    1651:     PetscCall(DMPlexVecGetOrientedClosure(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));

    1653:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
    1654:       PetscFE     fe;
    1655:       void *const ctx = ctxs ? ctxs[field] : NULL;
    1656:       PetscInt    Nb, Nc, q, fc;

    1658:       PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
    1659:       PetscCall(PetscFEGetDimension(fe, &Nb));
    1660:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
    1661:       if (debug) {
    1662:         char title[1024];
    1663:         PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
    1664:         PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
    1665:       }
    1666:       for (q = 0; q < Nq; ++q) {
    1667:         PetscFEGeom    qgeom;
    1668:         PetscErrorCode ierr;

    1670:         qgeom.dimEmbed = fegeom.dimEmbed;
    1671:         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
    1672:         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
    1673:         qgeom.detJ     = &fegeom.detJ[q];
    1674:         PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], c, q);
    1675:         if (transform) {
    1676:           gcoords = &coords[coordDim * Nq];
    1677:           PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
    1678:         } else {
    1679:           gcoords = &coords[coordDim * q];
    1680:         }
    1681:         PetscCall(PetscArrayzero(funcVal, Nc));
    1682:         ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
    1683:         if (ierr) {
    1684:           PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
    1685:           PetscCall(DMRestoreLocalVector(dm, &localX));
    1686:           PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
    1687:         }
    1688:         if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
    1689:         PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant));
    1690:         /* Overwrite with the dot product if the normal is given */
    1691:         if (n) {
    1692:           for (fc = 0; fc < Nc; ++fc) {
    1693:             PetscScalar sum = 0.0;
    1694:             PetscInt    d;
    1695:             for (d = 0; d < dim; ++d) sum += interpolant[fc * dim + d] * n[d];
    1696:             interpolant[fc] = sum;
    1697:           }
    1698:         }
    1699:         for (fc = 0; fc < Nc; ++fc) {
    1700:           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
    1701:           if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "    elem %" PetscInt_FMT " fieldDer %" PetscInt_FMT ",%" PetscInt_FMT " diff %g\n", c, field, fc, (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
    1702:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
    1703:         }
    1704:       }
    1705:       fieldOffset += Nb;
    1706:       qc += Nc;
    1707:     }
    1708:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
    1709:     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
    1710:     localDiff += elemDiff;
    1711:   }
    1712:   PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
    1713:   PetscCall(DMRestoreLocalVector(dm, &localX));
    1714:   PetscCallMPI(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
    1715:   *diff = PetscSqrtReal(*diff);
    1716:   PetscFunctionReturn(PETSC_SUCCESS);
    1717: }

    1719: PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
    1720: {
    1721:   const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
    1722:   DM             tdm;
    1723:   DMLabel        depthLabel;
    1724:   PetscSection   section;
    1725:   Vec            localX, tv;
    1726:   PetscReal     *localDiff;
    1727:   PetscInt       dim, depth, dE, Nf, f, Nds, s;
    1728:   PetscBool      transform;

    1730:   PetscFunctionBegin;
    1731:   PetscCall(DMGetDimension(dm, &dim));
    1732:   PetscCall(DMGetCoordinateDim(dm, &dE));
    1733:   PetscCall(DMGetLocalSection(dm, &section));
    1734:   PetscCall(DMGetLocalVector(dm, &localX));
    1735:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
    1736:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
    1737:   PetscCall(DMHasBasisTransform(dm, &transform));
    1738:   PetscCall(DMGetNumFields(dm, &Nf));
    1739:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    1740:   PetscCall(DMLabelGetNumValues(depthLabel, &depth));

    1742:   PetscCall(VecSet(localX, 0.0));
    1743:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
    1744:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
    1745:   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
    1746:   PetscCall(DMGetNumDS(dm, &Nds));
    1747:   PetscCall(PetscCalloc1(Nf, &localDiff));
    1748:   for (s = 0; s < Nds; ++s) {
    1749:     PetscDS          ds;
    1750:     DMLabel          label;
    1751:     IS               fieldIS, pointIS;
    1752:     const PetscInt  *fields, *points = NULL;
    1753:     PetscQuadrature  quad;
    1754:     const PetscReal *quadPoints, *quadWeights;
    1755:     PetscFEGeom      fegeom;
    1756:     PetscReal       *coords, *gcoords;
    1757:     PetscScalar     *funcVal, *interpolant;
    1758:     PetscBool        isCohesive;
    1759:     PetscInt         qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf;

    1761:     PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
    1762:     PetscCall(ISGetIndices(fieldIS, &fields));
    1763:     PetscCall(PetscDSIsCohesive(ds, &isCohesive));
    1764:     PetscCall(PetscDSGetNumFields(ds, &dsNf));
    1765:     PetscCall(PetscDSGetTotalComponents(ds, &totNc));
    1766:     PetscCall(PetscDSGetQuadrature(ds, &quad));
    1767:     PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
    1768:     PetscCheck(!(qNc != 1) || !(qNc != totNc), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, totNc);
    1769:     PetscCall(PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE * (Nq + 1), &coords, Nq, &fegeom.detJ, dE * dE * Nq, &fegeom.J, dE * dE * Nq, &fegeom.invJ));
    1770:     if (!label) {
    1771:       PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    1772:     } else {
    1773:       PetscCall(DMLabelGetStratumIS(label, 1, &pointIS));
    1774:       PetscCall(ISGetLocalSize(pointIS, &cEnd));
    1775:       PetscCall(ISGetIndices(pointIS, &points));
    1776:     }
    1777:     for (c = cStart; c < cEnd; ++c) {
    1778:       const PetscInt  cell = points ? points[c] : c;
    1779:       PetscScalar    *x    = NULL;
    1780:       const PetscInt *cone;
    1781:       PetscInt        qc = 0, fOff = 0, dep;

    1783:       PetscCall(DMLabelGetValue(depthLabel, cell, &dep));
    1784:       if (dep != depth - 1) continue;
    1785:       if (isCohesive) {
    1786:         PetscCall(DMPlexGetCone(dm, cell, &cone));
    1787:         PetscCall(DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    1788:       } else {
    1789:         PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    1790:       }
    1791:       PetscCall(DMPlexVecGetOrientedClosure(dm, NULL, PETSC_FALSE, localX, cell, 0, NULL, &x));
    1792:       for (f = 0; f < dsNf; ++f) {
    1793:         PetscObject  obj;
    1794:         PetscClassId id;
    1795:         void *const  ctx = ctxs ? ctxs[fields[f]] : NULL;
    1796:         PetscInt     Nb, Nc, q, fc;
    1797:         PetscReal    elemDiff = 0.0;
    1798:         PetscBool    cohesive;

    1800:         PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
    1801:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
    1802:         PetscCall(PetscObjectGetClassId(obj, &id));
    1803:         if (id == PETSCFE_CLASSID) {
    1804:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
    1805:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
    1806:         } else if (id == PETSCFV_CLASSID) {
    1807:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
    1808:           Nb = 1;
    1809:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
    1810:         if (isCohesive && !cohesive) {
    1811:           fOff += Nb * 2;
    1812:           qc += Nc;
    1813:           continue;
    1814:         }
    1815:         if (debug) {
    1816:           char title[1024];
    1817:           PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, fields[f]));
    1818:           PetscCall(DMPrintCellVector(cell, title, Nb, &x[fOff]));
    1819:         }
    1820:         for (q = 0; q < Nq; ++q) {
    1821:           PetscFEGeom    qgeom;
    1822:           PetscErrorCode ierr;

    1824:           qgeom.dimEmbed = fegeom.dimEmbed;
    1825:           qgeom.J        = &fegeom.J[q * dE * dE];
    1826:           qgeom.invJ     = &fegeom.invJ[q * dE * dE];
    1827:           qgeom.detJ     = &fegeom.detJ[q];
    1828:           PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for cell %" PetscInt_FMT ", quadrature point %" PetscInt_FMT, (double)fegeom.detJ[q], cell, q);
    1829:           if (transform) {
    1830:             gcoords = &coords[dE * Nq];
    1831:             PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE * q], PETSC_TRUE, dE, &coords[dE * q], gcoords, dm->transformCtx));
    1832:           } else {
    1833:             gcoords = &coords[dE * q];
    1834:           }
    1835:           for (fc = 0; fc < Nc; ++fc) funcVal[fc] = 0.;
    1836:           ierr = (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx);
    1837:           if (ierr) {
    1838:             PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
    1839:             PetscCall(DMRestoreLocalVector(dm, &localX));
    1840:             PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1841:           }
    1842:           if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[dE * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
    1843:           /* Call once for each face, except for lagrange field */
    1844:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fOff], &qgeom, q, interpolant));
    1845:           else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fOff], q, interpolant));
    1846:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
    1847:           for (fc = 0; fc < Nc; ++fc) {
    1848:             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
    1849:             if (debug)
    1850:               PetscCall(PetscPrintf(PETSC_COMM_SELF, "    cell %" PetscInt_FMT " field %" PetscInt_FMT ",%" PetscInt_FMT " point %g %g %g diff %g\n", cell, fields[f], fc, (double)(dE > 0 ? coords[dE * q] : 0), (double)(dE > 1 ? coords[dE * q + 1] : 0), (double)(dE > 2 ? coords[dE * q + 2] : 0),
    1851:                                     (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
    1852:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
    1853:           }
    1854:         }
    1855:         fOff += Nb;
    1856:         qc += Nc;
    1857:         localDiff[fields[f]] += elemDiff;
    1858:         if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  cell %" PetscInt_FMT " field %" PetscInt_FMT " cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]]));
    1859:       }
    1860:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
    1861:     }
    1862:     if (label) {
    1863:       PetscCall(ISRestoreIndices(pointIS, &points));
    1864:       PetscCall(ISDestroy(&pointIS));
    1865:     }
    1866:     PetscCall(ISRestoreIndices(fieldIS, &fields));
    1867:     PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1868:   }
    1869:   PetscCall(DMRestoreLocalVector(dm, &localX));
    1870:   PetscCallMPI(MPIU_Allreduce(localDiff, diff, Nf, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
    1871:   PetscCall(PetscFree(localDiff));
    1872:   for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]);
    1873:   PetscFunctionReturn(PETSC_SUCCESS);
    1874: }

    1876: /*@C
    1877:   DMPlexComputeL2DiffVec - This function computes the cellwise L_2 difference between a function u and an FEM interpolant solution u_h, and stores it in a Vec.

    1879:   Collective

    1881:   Input Parameters:
    1882: + dm    - The `DM`
    1883: . time  - The time
    1884: . funcs - The functions to evaluate for each field component: `NULL` means that component does not contribute to error calculation
    1885: . ctxs  - Optional array of contexts to pass to each function, or `NULL`.
    1886: - X     - The coefficient vector u_h

    1888:   Output Parameter:
    1889: . D - A `Vec` which holds the difference ||u - u_h||_2 for each cell

    1891:   Level: developer

    1893: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
    1894: @*/
    1895: PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
    1896: {
    1897:   PetscSection     section;
    1898:   PetscQuadrature  quad;
    1899:   Vec              localX;
    1900:   PetscFEGeom      fegeom;
    1901:   PetscScalar     *funcVal, *interpolant;
    1902:   PetscReal       *coords;
    1903:   const PetscReal *quadPoints, *quadWeights;
    1904:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;

    1906:   PetscFunctionBegin;
    1907:   PetscCall(VecSet(D, 0.0));
    1908:   PetscCall(DMGetDimension(dm, &dim));
    1909:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
    1910:   PetscCall(DMGetLocalSection(dm, &section));
    1911:   PetscCall(PetscSectionGetNumFields(section, &numFields));
    1912:   PetscCall(DMGetLocalVector(dm, &localX));
    1913:   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
    1914:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
    1915:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
    1916:   for (field = 0; field < numFields; ++field) {
    1917:     PetscObject  obj;
    1918:     PetscClassId id;
    1919:     PetscInt     Nc;

    1921:     PetscCall(DMGetField(dm, field, NULL, &obj));
    1922:     PetscCall(PetscObjectGetClassId(obj, &id));
    1923:     if (id == PETSCFE_CLASSID) {
    1924:       PetscFE fe = (PetscFE)obj;

    1926:       PetscCall(PetscFEGetQuadrature(fe, &quad));
    1927:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
    1928:     } else if (id == PETSCFV_CLASSID) {
    1929:       PetscFV fv = (PetscFV)obj;

    1931:       PetscCall(PetscFVGetQuadrature(fv, &quad));
    1932:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
    1933:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1934:     numComponents += Nc;
    1935:   }
    1936:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
    1937:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
    1938:   PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
    1939:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    1940:   for (c = cStart; c < cEnd; ++c) {
    1941:     PetscScalar *x        = NULL;
    1942:     PetscScalar  elemDiff = 0.0;
    1943:     PetscInt     qc       = 0;

    1945:     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    1946:     PetscCall(DMPlexVecGetOrientedClosure(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));

    1948:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
    1949:       PetscObject  obj;
    1950:       PetscClassId id;
    1951:       void *const  ctx = ctxs ? ctxs[field] : NULL;
    1952:       PetscInt     Nb, Nc, q, fc;

    1954:       PetscCall(DMGetField(dm, field, NULL, &obj));
    1955:       PetscCall(PetscObjectGetClassId(obj, &id));
    1956:       if (id == PETSCFE_CLASSID) {
    1957:         PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
    1958:         PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
    1959:       } else if (id == PETSCFV_CLASSID) {
    1960:         PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
    1961:         Nb = 1;
    1962:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1963:       if (funcs[field]) {
    1964:         for (q = 0; q < Nq; ++q) {
    1965:           PetscFEGeom qgeom;

    1967:           qgeom.dimEmbed = fegeom.dimEmbed;
    1968:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
    1969:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
    1970:           qgeom.detJ     = &fegeom.detJ[q];
    1971:           PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], c, q);
    1972:           PetscCall((*funcs[field])(coordDim, time, &coords[q * coordDim], Nc, funcVal, ctx));
    1973: #if defined(needs_fix_with_return_code_argument)
    1974:           if (ierr) {
    1975:             PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
    1976:             PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1977:             PetscCall(DMRestoreLocalVector(dm, &localX));
    1978:           }
    1979: #endif
    1980:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
    1981:           else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
    1982:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1983:           for (fc = 0; fc < Nc; ++fc) {
    1984:             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
    1985:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
    1986:           }
    1987:         }
    1988:       }
    1989:       fieldOffset += Nb;
    1990:       qc += Nc;
    1991:     }
    1992:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
    1993:     PetscCall(VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES));
    1994:   }
    1995:   PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1996:   PetscCall(DMRestoreLocalVector(dm, &localX));
    1997:   PetscCall(VecSqrtAbs(D));
    1998:   PetscFunctionReturn(PETSC_SUCCESS);
    1999: }

    2001: /*@
    2002:   DMPlexComputeL2FluxDiffVecLocal - This function computes the integral of the difference between the gradient of field `f`in `u` and field `mf` in `mu`

    2004:   Collective

    2006:   Input Parameters:
    2007: + lu  - The local `Vec` containing the primal solution
    2008: . f   - The field number for the potential
    2009: . lmu - The local `Vec` containing the mixed solution
    2010: - mf  - The field number for the flux

    2012:   Output Parameter:
    2013: . eFlux - A global `Vec` which holds $||\nabla u_f - \mu_{mf}||$

    2015:   Level: advanced

    2017:   Notes:
    2018:   We assume that the `DM` for each solution has the same topology, geometry, and quadrature.

    2020:   This is usually used to get an error estimate for the primal solution, using the flux from a mixed solution.

    2022: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVec()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
    2023: @*/
    2024: PetscErrorCode DMPlexComputeL2FluxDiffVecLocal(Vec lu, PetscInt f, Vec lmu, PetscInt mf, Vec eFlux)
    2025: {
    2026:   DM               dm, mdm, edm;
    2027:   PetscFE          fe, mfe;
    2028:   PetscFEGeom      fegeom;
    2029:   PetscQuadrature  quad;
    2030:   const PetscReal *quadWeights;
    2031:   PetscReal       *coords;
    2032:   PetscScalar     *interpolant, *minterpolant, *earray;
    2033:   PetscInt         cdim, mcdim, cStart, cEnd, Nc, mNc, qNc, Nq;
    2034:   MPI_Comm         comm;

    2036:   PetscFunctionBegin;
    2037:   PetscCall(VecGetDM(lu, &dm));
    2038:   PetscCall(VecGetDM(lmu, &mdm));
    2039:   PetscCall(VecGetDM(eFlux, &edm));
    2040:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
    2041:   PetscCall(VecSet(eFlux, 0.0));

    2043:   // Check if the both problems are on the same mesh
    2044:   PetscCall(DMGetCoordinateDim(dm, &cdim));
    2045:   PetscCall(DMGetCoordinateDim(mdm, &mcdim));
    2046:   PetscCheck(cdim == mcdim, comm, PETSC_ERR_ARG_SIZ, "primal coordinate Dim %" PetscInt_FMT " != %" PetscInt_FMT " mixed coordinate Dim", cdim, mcdim);
    2047:   fegeom.dimEmbed = cdim;

    2049:   PetscCall(DMGetField(dm, f, NULL, (PetscObject *)&fe));
    2050:   PetscCall(DMGetField(mdm, mf, NULL, (PetscObject *)&mfe));
    2051:   PetscCall(PetscFEGetNumComponents(fe, &Nc));
    2052:   PetscCall(PetscFEGetNumComponents(mfe, &mNc));
    2053:   PetscCall(PetscFEGetQuadrature(fe, &quad));
    2054:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
    2055:   PetscCheck(qNc == 1 || qNc == mNc, comm, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, mNc);

    2057:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    2058:   PetscCall(VecGetArrayWrite(eFlux, &earray));
    2059:   PetscCall(PetscMalloc6(Nc * cdim, &interpolant, mNc * cdim, &minterpolant, cdim * (Nq + 1), &coords, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ, Nq, &fegeom.detJ));
    2060:   for (PetscInt c = cStart; c < cEnd; ++c) {
    2061:     PetscScalar *x            = NULL;
    2062:     PetscScalar *mx           = NULL;
    2063:     PetscScalar *eval         = NULL;
    2064:     PetscReal    fluxElemDiff = 0.0;

    2066:     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    2067:     PetscCall(DMPlexVecGetClosure(dm, NULL, lu, c, NULL, &x));
    2068:     PetscCall(DMPlexVecGetClosure(mdm, NULL, lmu, c, NULL, &mx));

    2070:     for (PetscInt q = 0; q < Nq; ++q) {
    2071:       PetscFEGeom qgeom;

    2073:       qgeom.dimEmbed = fegeom.dimEmbed;
    2074:       qgeom.J        = &fegeom.J[q * cdim * cdim];
    2075:       qgeom.invJ     = &fegeom.invJ[q * cdim * cdim];
    2076:       qgeom.detJ     = &fegeom.detJ[q];

    2078:       PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], c, q);

    2080:       PetscCall(PetscFEInterpolate_Static(mfe, &mx[0], &qgeom, q, minterpolant));
    2081:       PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[0], &qgeom, q, interpolant));

    2083:       /* Now take the elementwise difference and store that in a vector. */
    2084:       for (PetscInt fc = 0; fc < mNc; ++fc) {
    2085:         const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : fc)];
    2086:         fluxElemDiff += PetscSqr(PetscRealPart(interpolant[fc] - minterpolant[fc])) * wt * fegeom.detJ[q];
    2087:       }
    2088:     }
    2089:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, lu, c, NULL, &x));
    2090:     PetscCall(DMPlexVecRestoreClosure(mdm, NULL, lmu, c, NULL, &mx));
    2091:     PetscCall(DMPlexPointGlobalRef(edm, c, earray, (void *)&eval));
    2092:     if (eval) eval[0] = fluxElemDiff;
    2093:   }
    2094:   PetscCall(PetscFree6(interpolant, minterpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    2095:   PetscCall(VecRestoreArrayWrite(eFlux, &earray));

    2097:   PetscCall(VecAssemblyBegin(eFlux));
    2098:   PetscCall(VecAssemblyEnd(eFlux));
    2099:   PetscCall(VecSqrtAbs(eFlux));
    2100:   PetscFunctionReturn(PETSC_SUCCESS);
    2101: }

    2103: /*@
    2104:   DMPlexComputeL2FluxDiffVec - This function computes the integral of the difference between the gradient of field `f`in `u` and field `mf` in `mu`

    2106:   Collective

    2108:   Input Parameters:
    2109: + u  - The global `Vec` containing the primal solution
    2110: . f  - The field number for the potential
    2111: . mu - The global `Vec` containing the mixed solution
    2112: - mf - The field number for the flux

    2114:   Output Parameter:
    2115: . eFlux - A global `Vec` which holds $||\nabla u_f - \mu_{mf}||$

    2117:   Level: advanced

    2119:   Notes:
    2120:   We assume that the `DM` for each solution has the same topology, geometry, and quadrature.

    2122:   This is usually used to get an error estimate for the primal solution, using the flux from a mixed solution.

    2124: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVecLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
    2125: @*/
    2126: PetscErrorCode DMPlexComputeL2FluxDiffVec(Vec u, PetscInt f, Vec mu, PetscInt mf, Vec eFlux)
    2127: {
    2128:   DM  dm, mdm;
    2129:   Vec lu, lmu;

    2131:   PetscFunctionBegin;
    2132:   PetscCall(VecGetDM(u, &dm));
    2133:   PetscCall(DMGetLocalVector(dm, &lu));
    2134:   PetscCall(DMGlobalToLocal(dm, u, INSERT_VALUES, lu));
    2135:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, lu, 0.0, NULL, NULL, NULL));

    2137:   PetscCall(VecGetDM(mu, &mdm));
    2138:   PetscCall(DMGetLocalVector(mdm, &lmu));
    2139:   PetscCall(DMGlobalToLocal(mdm, mu, INSERT_VALUES, lmu));
    2140:   PetscCall(DMPlexInsertBoundaryValues(mdm, PETSC_TRUE, lmu, 0.0, NULL, NULL, NULL));

    2142:   PetscCall(DMPlexComputeL2FluxDiffVecLocal(lu, f, lmu, mf, eFlux));

    2144:   PetscCall(DMRestoreLocalVector(dm, &lu));
    2145:   PetscCall(DMRestoreLocalVector(mdm, &lmu));
    2146:   PetscFunctionReturn(PETSC_SUCCESS);
    2147: }

    2149: /*@
    2150:   DMPlexComputeClementInterpolant - This function computes the L2 projection of the cellwise values of a function u onto P1

    2152:   Collective

    2154:   Input Parameters:
    2155: + dm   - The `DM`
    2156: - locX - The coefficient vector u_h

    2158:   Output Parameter:
    2159: . locC - A `Vec` which holds the Clement interpolant of the function

    2161:   Level: developer

    2163:   Note:
    2164:   $ u_h(v_i) = \sum_{T_i \in support(v_i)} |T_i| u_h(T_i) / \sum_{T_i \in support(v_i)} |T_i| $ where $ |T_i| $ is the cell volume

    2166: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
    2167: @*/
    2168: PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC)
    2169: {
    2170:   PetscInt         debug = ((DM_Plex *)dm->data)->printFEM;
    2171:   DM               dmc;
    2172:   PetscQuadrature  quad;
    2173:   PetscScalar     *interpolant, *valsum;
    2174:   PetscFEGeom      fegeom;
    2175:   PetscReal       *coords;
    2176:   const PetscReal *quadPoints, *quadWeights;
    2177:   PetscInt         dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v;

    2179:   PetscFunctionBegin;
    2180:   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
    2181:   PetscCall(VecGetDM(locC, &dmc));
    2182:   PetscCall(VecSet(locC, 0.0));
    2183:   PetscCall(DMGetDimension(dm, &dim));
    2184:   PetscCall(DMGetCoordinateDim(dm, &cdim));
    2185:   fegeom.dimEmbed = cdim;
    2186:   PetscCall(DMGetNumFields(dm, &Nf));
    2187:   PetscCheck(Nf > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
    2188:   for (f = 0; f < Nf; ++f) {
    2189:     PetscObject  obj;
    2190:     PetscClassId id;
    2191:     PetscInt     fNc;

    2193:     PetscCall(DMGetField(dm, f, NULL, &obj));
    2194:     PetscCall(PetscObjectGetClassId(obj, &id));
    2195:     if (id == PETSCFE_CLASSID) {
    2196:       PetscFE fe = (PetscFE)obj;

    2198:       PetscCall(PetscFEGetQuadrature(fe, &quad));
    2199:       PetscCall(PetscFEGetNumComponents(fe, &fNc));
    2200:     } else if (id == PETSCFV_CLASSID) {
    2201:       PetscFV fv = (PetscFV)obj;

    2203:       PetscCall(PetscFVGetQuadrature(fv, &quad));
    2204:       PetscCall(PetscFVGetNumComponents(fv, &fNc));
    2205:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
    2206:     Nc += fNc;
    2207:   }
    2208:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
    2209:   PetscCheck(qNc == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " > 1", qNc);
    2210:   PetscCall(PetscMalloc6(Nc * 2, &valsum, Nc, &interpolant, cdim * Nq, &coords, Nq, &fegeom.detJ, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ));
    2211:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
    2212:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    2213:   for (v = vStart; v < vEnd; ++v) {
    2214:     PetscScalar volsum = 0.0;
    2215:     PetscInt   *star   = NULL;
    2216:     PetscInt    starSize, st, fc;

    2218:     PetscCall(PetscArrayzero(valsum, Nc));
    2219:     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
    2220:     for (st = 0; st < starSize * 2; st += 2) {
    2221:       const PetscInt cell = star[st];
    2222:       PetscScalar   *val  = &valsum[Nc];
    2223:       PetscScalar   *x    = NULL;
    2224:       PetscReal      vol  = 0.0;
    2225:       PetscInt       foff = 0;

    2227:       if ((cell < cStart) || (cell >= cEnd)) continue;
    2228:       PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    2229:       PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
    2230:       for (f = 0; f < Nf; ++f) {
    2231:         PetscObject  obj;
    2232:         PetscClassId id;
    2233:         PetscInt     Nb, fNc, q;

    2235:         PetscCall(PetscArrayzero(val, Nc));
    2236:         PetscCall(DMGetField(dm, f, NULL, &obj));
    2237:         PetscCall(PetscObjectGetClassId(obj, &id));
    2238:         if (id == PETSCFE_CLASSID) {
    2239:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &fNc));
    2240:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
    2241:         } else if (id == PETSCFV_CLASSID) {
    2242:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &fNc));
    2243:           Nb = 1;
    2244:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
    2245:         for (q = 0; q < Nq; ++q) {
    2246:           const PetscReal wt = quadWeights[q] * fegeom.detJ[q];
    2247:           PetscFEGeom     qgeom;

    2249:           qgeom.dimEmbed = fegeom.dimEmbed;
    2250:           qgeom.J        = &fegeom.J[q * cdim * cdim];
    2251:           qgeom.invJ     = &fegeom.invJ[q * cdim * cdim];
    2252:           qgeom.detJ     = &fegeom.detJ[q];
    2253:           PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], cell, q);
    2254:           PetscCheck(id == PETSCFE_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
    2255:           PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[foff], &qgeom, q, interpolant));
    2256:           for (fc = 0; fc < fNc; ++fc) val[foff + fc] += interpolant[fc] * wt;
    2257:           vol += wt;
    2258:         }
    2259:         foff += Nb;
    2260:       }
    2261:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
    2262:       for (fc = 0; fc < Nc; ++fc) valsum[fc] += val[fc];
    2263:       volsum += vol;
    2264:       if (debug) {
    2265:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " value: [", v, cell));
    2266:         for (fc = 0; fc < Nc; ++fc) {
    2267:           if (fc) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
    2268:           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(val[fc])));
    2269:         }
    2270:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
    2271:       }
    2272:     }
    2273:     for (fc = 0; fc < Nc; ++fc) valsum[fc] /= volsum;
    2274:     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
    2275:     PetscCall(DMPlexVecSetClosure(dmc, NULL, locC, v, valsum, INSERT_VALUES));
    2276:   }
    2277:   PetscCall(PetscFree6(valsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    2278:   PetscFunctionReturn(PETSC_SUCCESS);
    2279: }

    2281: /*@
    2282:   DMPlexComputeGradientClementInterpolant - This function computes the L2 projection of the cellwise gradient of a function u onto P1

    2284:   Collective

    2286:   Input Parameters:
    2287: + dm   - The `DM`
    2288: - locX - The coefficient vector u_h

    2290:   Output Parameter:
    2291: . locC - A `Vec` which holds the Clement interpolant of the gradient

    2293:   Level: developer

    2295:   Note:
    2296:   $\nabla u_h(v_i) = \sum_{T_i \in support(v_i)} |T_i| \nabla u_h(T_i) / \sum_{T_i \in support(v_i)} |T_i| $ where $ |T_i| $ is the cell volume

    2298: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
    2299: @*/
    2300: PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
    2301: {
    2302:   DM_Plex         *mesh  = (DM_Plex *)dm->data;
    2303:   PetscInt         debug = mesh->printFEM;
    2304:   DM               dmC;
    2305:   PetscQuadrature  quad;
    2306:   PetscScalar     *interpolant, *gradsum;
    2307:   PetscFEGeom      fegeom;
    2308:   PetscReal       *coords;
    2309:   const PetscReal *quadPoints, *quadWeights;
    2310:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;

    2312:   PetscFunctionBegin;
    2313:   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
    2314:   PetscCall(VecGetDM(locC, &dmC));
    2315:   PetscCall(VecSet(locC, 0.0));
    2316:   PetscCall(DMGetDimension(dm, &dim));
    2317:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
    2318:   fegeom.dimEmbed = coordDim;
    2319:   PetscCall(DMGetNumFields(dm, &numFields));
    2320:   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
    2321:   for (field = 0; field < numFields; ++field) {
    2322:     PetscObject  obj;
    2323:     PetscClassId id;
    2324:     PetscInt     Nc;

    2326:     PetscCall(DMGetField(dm, field, NULL, &obj));
    2327:     PetscCall(PetscObjectGetClassId(obj, &id));
    2328:     if (id == PETSCFE_CLASSID) {
    2329:       PetscFE fe = (PetscFE)obj;

    2331:       PetscCall(PetscFEGetQuadrature(fe, &quad));
    2332:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
    2333:     } else if (id == PETSCFV_CLASSID) {
    2334:       PetscFV fv = (PetscFV)obj;

    2336:       PetscCall(PetscFVGetQuadrature(fv, &quad));
    2337:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
    2338:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    2339:     numComponents += Nc;
    2340:   }
    2341:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
    2342:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
    2343:   PetscCall(PetscMalloc6(coordDim * numComponents * 2, &gradsum, coordDim * numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
    2344:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
    2345:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    2346:   for (v = vStart; v < vEnd; ++v) {
    2347:     PetscScalar volsum = 0.0;
    2348:     PetscInt   *star   = NULL;
    2349:     PetscInt    starSize, st, d, fc;

    2351:     PetscCall(PetscArrayzero(gradsum, coordDim * numComponents));
    2352:     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
    2353:     for (st = 0; st < starSize * 2; st += 2) {
    2354:       const PetscInt cell = star[st];
    2355:       PetscScalar   *grad = &gradsum[coordDim * numComponents];
    2356:       PetscScalar   *x    = NULL;
    2357:       PetscReal      vol  = 0.0;

    2359:       if ((cell < cStart) || (cell >= cEnd)) continue;
    2360:       PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    2361:       PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
    2362:       for (field = 0, fieldOffset = 0; field < numFields; ++field) {
    2363:         PetscObject  obj;
    2364:         PetscClassId id;
    2365:         PetscInt     Nb, Nc, q, qc = 0;

    2367:         PetscCall(PetscArrayzero(grad, coordDim * numComponents));
    2368:         PetscCall(DMGetField(dm, field, NULL, &obj));
    2369:         PetscCall(PetscObjectGetClassId(obj, &id));
    2370:         if (id == PETSCFE_CLASSID) {
    2371:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
    2372:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
    2373:         } else if (id == PETSCFV_CLASSID) {
    2374:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
    2375:           Nb = 1;
    2376:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    2377:         for (q = 0; q < Nq; ++q) {
    2378:           PetscFEGeom qgeom;

    2380:           qgeom.dimEmbed = fegeom.dimEmbed;
    2381:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
    2382:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
    2383:           qgeom.detJ     = &fegeom.detJ[q];
    2384:           PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], cell, q);
    2385:           PetscCheck(id == PETSCFE_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    2386:           PetscCall(PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant));
    2387:           for (fc = 0; fc < Nc; ++fc) {
    2388:             const PetscReal wt = quadWeights[q * qNc + qc];

    2390:             for (d = 0; d < coordDim; ++d) grad[fc * coordDim + d] += interpolant[fc * dim + d] * wt * fegeom.detJ[q];
    2391:           }
    2392:           vol += quadWeights[q * qNc] * fegeom.detJ[q];
    2393:         }
    2394:         fieldOffset += Nb;
    2395:         qc += Nc;
    2396:       }
    2397:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
    2398:       for (fc = 0; fc < numComponents; ++fc) {
    2399:         for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] += grad[fc * coordDim + d];
    2400:       }
    2401:       volsum += vol;
    2402:       if (debug) {
    2403:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " gradient: [", v, cell));
    2404:         for (fc = 0; fc < numComponents; ++fc) {
    2405:           for (d = 0; d < coordDim; ++d) {
    2406:             if (fc || d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
    2407:             PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc * coordDim + d])));
    2408:           }
    2409:         }
    2410:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
    2411:       }
    2412:     }
    2413:     for (fc = 0; fc < numComponents; ++fc) {
    2414:       for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] /= volsum;
    2415:     }
    2416:     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
    2417:     PetscCall(DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES));
    2418:   }
    2419:   PetscCall(PetscFree6(gradsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    2420:   PetscFunctionReturn(PETSC_SUCCESS);
    2421: }

    2423: PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec locX, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, PetscCtx ctx)
    2424: {
    2425:   DM           dmAux = NULL, plexA = NULL;
    2426:   PetscDS      prob, probAux       = NULL;
    2427:   PetscSection section, sectionAux;
    2428:   Vec          locA;
    2429:   PetscInt     dim, numCells = cEnd - cStart, c, f;
    2430:   PetscBool    useFVM = PETSC_FALSE;
    2431:   /* DS */
    2432:   PetscInt           Nf, totDim, *uOff, *uOff_x, numConstants;
    2433:   PetscInt           NfAux, totDimAux, *aOff;
    2434:   PetscScalar       *u, *a = NULL;
    2435:   const PetscScalar *constants;
    2436:   /* Geometry */
    2437:   PetscFEGeom       *cgeomFEM;
    2438:   DM                 dmGrad;
    2439:   PetscQuadrature    affineQuad      = NULL;
    2440:   Vec                cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
    2441:   PetscFVCellGeom   *cgeomFVM;
    2442:   const PetscScalar *lgrad;
    2443:   PetscInt           maxDegree;
    2444:   DMField            coordField;
    2445:   IS                 cellIS;

    2447:   PetscFunctionBegin;
    2448:   PetscCall(DMGetDS(dm, &prob));
    2449:   PetscCall(DMGetDimension(dm, &dim));
    2450:   PetscCall(DMGetLocalSection(dm, &section));
    2451:   PetscCall(DMGetNumFields(dm, &Nf));
    2452:   /* Determine which discretizations we have */
    2453:   for (f = 0; f < Nf; ++f) {
    2454:     PetscObject  obj;
    2455:     PetscClassId id;

    2457:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    2458:     PetscCall(PetscObjectGetClassId(obj, &id));
    2459:     if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
    2460:   }
    2461:   /* Read DS information */
    2462:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    2463:   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
    2464:   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
    2465:   PetscCall(ISCreateStride(PETSC_COMM_SELF, numCells, cStart, 1, &cellIS));
    2466:   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
    2467:   /* Read Auxiliary DS information */
    2468:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
    2469:   if (locA) {
    2470:     PetscCall(VecGetDM(locA, &dmAux));
    2471:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
    2472:     PetscCall(DMGetDS(dmAux, &probAux));
    2473:     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
    2474:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
    2475:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    2476:     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
    2477:   }
    2478:   /* Allocate data  arrays */
    2479:   PetscCall(PetscCalloc1(numCells * totDim, &u));
    2480:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
    2481:   /* Read out geometry */
    2482:   PetscCall(DMGetCoordinateField(dm, &coordField));
    2483:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    2484:   if (maxDegree <= 1) {
    2485:     PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
    2486:     if (affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, affineQuad, PETSC_FEGEOM_BASIC, &cgeomFEM));
    2487:   }
    2488:   if (useFVM) {
    2489:     PetscFV   fv = NULL;
    2490:     Vec       grad;
    2491:     PetscInt  fStart, fEnd;
    2492:     PetscBool compGrad;

    2494:     for (f = 0; f < Nf; ++f) {
    2495:       PetscObject  obj;
    2496:       PetscClassId id;

    2498:       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    2499:       PetscCall(PetscObjectGetClassId(obj, &id));
    2500:       if (id == PETSCFV_CLASSID) {
    2501:         fv = (PetscFV)obj;
    2502:         break;
    2503:       }
    2504:     }
    2505:     PetscCall(PetscFVGetComputeGradients(fv, &compGrad));
    2506:     PetscCall(PetscFVSetComputeGradients(fv, PETSC_TRUE));
    2507:     PetscCall(DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM));
    2508:     PetscCall(DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad));
    2509:     PetscCall(PetscFVSetComputeGradients(fv, compGrad));
    2510:     PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
    2511:     /* Reconstruct and limit cell gradients */
    2512:     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
    2513:     PetscCall(DMGetGlobalVector(dmGrad, &grad));
    2514:     PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
    2515:     /* Communicate gradient values */
    2516:     PetscCall(DMGetLocalVector(dmGrad, &locGrad));
    2517:     PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
    2518:     PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
    2519:     PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
    2520:     /* Handle non-essential (e.g. outflow) boundary values */
    2521:     PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad));
    2522:     PetscCall(VecGetArrayRead(locGrad, &lgrad));
    2523:   }
    2524:   /* Read out data from inputs */
    2525:   for (c = cStart; c < cEnd; ++c) {
    2526:     PetscScalar *x = NULL;
    2527:     PetscInt     i;

    2529:     PetscCall(DMPlexVecGetClosure(dm, section, locX, c, NULL, &x));
    2530:     for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
    2531:     PetscCall(DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x));
    2532:     if (dmAux) {
    2533:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, c, NULL, &x));
    2534:       for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
    2535:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, c, NULL, &x));
    2536:     }
    2537:   }
    2538:   /* Do integration for each field */
    2539:   for (f = 0; f < Nf; ++f) {
    2540:     PetscObject  obj;
    2541:     PetscClassId id;
    2542:     PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

    2544:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    2545:     PetscCall(PetscObjectGetClassId(obj, &id));
    2546:     if (id == PETSCFE_CLASSID) {
    2547:       PetscFE         fe = (PetscFE)obj;
    2548:       PetscQuadrature q;
    2549:       PetscFEGeom    *chunkGeom = NULL;
    2550:       PetscInt        Nq, Nb;

    2552:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    2553:       PetscCall(PetscFEGetQuadrature(fe, &q));
    2554:       PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
    2555:       PetscCall(PetscFEGetDimension(fe, &Nb));
    2556:       blockSize = Nb * Nq;
    2557:       batchSize = numBlocks * blockSize;
    2558:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    2559:       numChunks = numCells / (numBatches * batchSize);
    2560:       Ne        = numChunks * numBatches * batchSize;
    2561:       Nr        = numCells % (numBatches * batchSize);
    2562:       offset    = numCells - Nr;
    2563:       if (!affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, q, PETSC_FEGEOM_BASIC, &cgeomFEM));
    2564:       PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
    2565:       PetscCall(PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral));
    2566:       PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &chunkGeom));
    2567:       PetscCall(PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &cintegral[offset * Nf]));
    2568:       PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &chunkGeom));
    2569:       if (!affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
    2570:     } else if (id == PETSCFV_CLASSID) {
    2571:       PetscInt      foff;
    2572:       PetscPointFn *obj_func;

    2574:       PetscCall(PetscDSGetObjective(prob, f, &obj_func));
    2575:       PetscCall(PetscDSGetFieldOffset(prob, f, &foff));
    2576:       if (obj_func) {
    2577:         for (c = 0; c < numCells; ++c) {
    2578:           PetscScalar *u_x;
    2579:           PetscScalar  lint = 0.;

    2581:           PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x));
    2582:           obj_func(dim, Nf, NfAux, uOff, uOff_x, &u[totDim * c + foff], NULL, u_x, aOff, NULL, PetscSafePointerPlusOffset(a, totDimAux * c), NULL, NULL, 0.0, cgeomFVM[c].centroid, numConstants, constants, &lint);
    2583:           cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
    2584:         }
    2585:       }
    2586:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
    2587:   }
    2588:   /* Cleanup data arrays */
    2589:   if (useFVM) {
    2590:     PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
    2591:     PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
    2592:     PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
    2593:     PetscCall(VecDestroy(&faceGeometryFVM));
    2594:     PetscCall(VecDestroy(&cellGeometryFVM));
    2595:     PetscCall(DMDestroy(&dmGrad));
    2596:   }
    2597:   if (dmAux) PetscCall(PetscFree(a));
    2598:   PetscCall(DMDestroy(&plexA));
    2599:   PetscCall(PetscFree(u));
    2600:   /* Cleanup */
    2601:   if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
    2602:   PetscCall(PetscQuadratureDestroy(&affineQuad));
    2603:   PetscCall(ISDestroy(&cellIS));
    2604:   PetscFunctionReturn(PETSC_SUCCESS);
    2605: }

    2607: /*@
    2608:   DMPlexComputeIntegralFEM - Form the integral over the domain from the global input X using pointwise functions specified by the user

    2610:   Input Parameters:
    2611: + dm  - The mesh
    2612: . X   - Global input vector
    2613: - ctx - The application context

    2615:   Output Parameter:
    2616: . integral - Integral for each field

    2618:   Level: developer

    2620: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
    2621: @*/
    2622: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, PetscCtx ctx)
    2623: {
    2624:   PetscInt     printFEM;
    2625:   PetscScalar *cintegral, *lintegral;
    2626:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;
    2627:   Vec          locX;

    2629:   PetscFunctionBegin;
    2632:   PetscAssertPointer(integral, 3);
    2633:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
    2634:   PetscCall(DMPlexConvertPlex(dm, &dm, PETSC_TRUE));
    2635:   PetscCall(DMGetNumFields(dm, &Nf));
    2636:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
    2637:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
    2638:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
    2639:   PetscCall(PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral));
    2640:   /* Get local solution with boundary values */
    2641:   PetscCall(DMGetLocalVector(dm, &locX));
    2642:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
    2643:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
    2644:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
    2645:   PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, ctx));
    2646:   PetscCall(DMRestoreLocalVector(dm, &locX));
    2647:   printFEM = ((DM_Plex *)dm->data)->printFEM;
    2648:   /* Sum up values */
    2649:   for (cell = cStart; cell < cEnd; ++cell) {
    2650:     const PetscInt c = cell - cStart;

    2652:     if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
    2653:     for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f];
    2654:   }
    2655:   PetscCallMPI(MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
    2656:   if (printFEM) {
    2657:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:"));
    2658:     for (f = 0; f < Nf; ++f) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f])));
    2659:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "\n"));
    2660:   }
    2661:   PetscCall(PetscFree2(lintegral, cintegral));
    2662:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
    2663:   PetscCall(DMDestroy(&dm));
    2664:   PetscFunctionReturn(PETSC_SUCCESS);
    2665: }

    2667: /*@
    2668:   DMPlexComputeCellwiseIntegralFEM - Form the vector of cellwise integrals F from the global input X using pointwise functions specified by the user

    2670:   Input Parameters:
    2671: + dm  - The mesh
    2672: . X   - Global input vector
    2673: - ctx - The application context

    2675:   Output Parameter:
    2676: . F - Cellwise integrals for each field

    2678:   Level: developer

    2680: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
    2681: @*/
    2682: PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, PetscCtx ctx)
    2683: {
    2684:   PetscInt     printFEM;
    2685:   DM           dmF;
    2686:   PetscSection sectionF = NULL;
    2687:   PetscScalar *cintegral, *af;
    2688:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell, n;
    2689:   Vec          locX;

    2691:   PetscFunctionBegin;
    2695:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
    2696:   PetscCall(DMPlexConvertPlex(dm, &dm, PETSC_TRUE));
    2697:   PetscCall(DMGetNumFields(dm, &Nf));
    2698:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
    2699:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
    2700:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
    2701:   PetscCall(PetscCalloc1((cEnd - cStart) * Nf, &cintegral));
    2702:   /* Get local solution with boundary values */
    2703:   PetscCall(DMGetLocalVector(dm, &locX));
    2704:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
    2705:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
    2706:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
    2707:   PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, ctx));
    2708:   PetscCall(DMRestoreLocalVector(dm, &locX));
    2709:   /* Put values in F */
    2710:   PetscCall(VecGetArray(F, &af));
    2711:   PetscCall(VecGetDM(F, &dmF));
    2712:   if (dmF) PetscCall(DMGetLocalSection(dmF, &sectionF));
    2713:   PetscCall(VecGetLocalSize(F, &n));
    2714:   PetscCheck(n >= (cEnd - cStart) * Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Vector size %" PetscInt_FMT " < %" PetscInt_FMT, n, (cEnd - cStart) * Nf);
    2715:   printFEM = ((DM_Plex *)dm->data)->printFEM;
    2716:   for (cell = cStart; cell < cEnd; ++cell) {
    2717:     const PetscInt c   = cell - cStart;
    2718:     PetscInt       dof = Nf, off = c * Nf;

    2720:     if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
    2721:     if (sectionF) {
    2722:       PetscCall(PetscSectionGetDof(sectionF, cell, &dof));
    2723:       PetscCall(PetscSectionGetOffset(sectionF, cell, &off));
    2724:     }
    2725:     PetscCheck(dof == Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %" PetscInt_FMT " != %" PetscInt_FMT, dof, Nf);
    2726:     for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
    2727:   }
    2728:   PetscCall(VecRestoreArray(F, &af));
    2729:   PetscCall(PetscFree(cintegral));
    2730:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
    2731:   PetscCall(DMDestroy(&dm));
    2732:   PetscFunctionReturn(PETSC_SUCCESS);
    2733: }

    2735: static PetscErrorCode DMPlexComputeBdIntegral_Internal(DM dm, Vec locX, IS pointIS, void (**funcs)(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[]), PetscScalar *fintegral, PetscCtx ctx)
    2736: {
    2737:   DM                 plex = NULL, plexA = NULL;
    2738:   DMEnclosureType    encAux;
    2739:   PetscDS            prob, probAux       = NULL;
    2740:   PetscSection       section, sectionAux = NULL;
    2741:   Vec                locA = NULL;
    2742:   DMField            coordField;
    2743:   PetscInt           Nf, totDim, *uOff, *uOff_x;
    2744:   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
    2745:   PetscScalar       *u, *a = NULL;
    2746:   const PetscScalar *constants;
    2747:   PetscInt           numConstants, f;

    2749:   PetscFunctionBegin;
    2750:   PetscCall(DMGetCoordinateField(dm, &coordField));
    2751:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    2752:   PetscCall(DMGetDS(dm, &prob));
    2753:   PetscCall(DMGetLocalSection(dm, &section));
    2754:   PetscCall(PetscSectionGetNumFields(section, &Nf));
    2755:   /* Determine which discretizations we have */
    2756:   for (f = 0; f < Nf; ++f) {
    2757:     PetscObject  obj;
    2758:     PetscClassId id;

    2760:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    2761:     PetscCall(PetscObjectGetClassId(obj, &id));
    2762:     PetscCheck(id != PETSCFV_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported for FVM (field %" PetscInt_FMT ")", f);
    2763:   }
    2764:   /* Read DS information */
    2765:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    2766:   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
    2767:   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
    2768:   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
    2769:   /* Read Auxiliary DS information */
    2770:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
    2771:   if (locA) {
    2772:     DM dmAux;

    2774:     PetscCall(VecGetDM(locA, &dmAux));
    2775:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    2776:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
    2777:     PetscCall(DMGetDS(dmAux, &probAux));
    2778:     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
    2779:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
    2780:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    2781:     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
    2782:   }
    2783:   /* Integrate over points */
    2784:   {
    2785:     PetscFEGeom    *fgeom, *chunkGeom = NULL;
    2786:     PetscInt        maxDegree;
    2787:     PetscQuadrature qGeom = NULL;
    2788:     const PetscInt *points;
    2789:     PetscInt        numFaces, face, Nq, field;
    2790:     PetscInt        numChunks, chunkSize, chunk, Nr, offset;

    2792:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
    2793:     PetscCall(ISGetIndices(pointIS, &points));
    2794:     PetscCall(PetscCalloc2(numFaces * totDim, &u, (locA ? (size_t)numFaces * totDimAux : 0), &a));
    2795:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
    2796:     for (face = 0; face < numFaces; ++face) {
    2797:       const PetscInt point = points[face], *support;
    2798:       PetscScalar   *x     = NULL;

    2800:       PetscCall(DMPlexGetSupport(dm, point, &support));
    2801:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
    2802:       for (PetscInt i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
    2803:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
    2804:       if (locA) {
    2805:         PetscInt subp;
    2806:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
    2807:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
    2808:         for (PetscInt i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
    2809:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
    2810:       }
    2811:     }
    2812:     for (field = 0; field < Nf; ++field) {
    2813:       PetscFE fe;

    2815:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
    2816:       if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
    2817:       if (!qGeom) {
    2818:         PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
    2819:         PetscCall(PetscObjectReference((PetscObject)qGeom));
    2820:       }
    2821:       PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
    2822:       PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
    2823:       /* Get blocking */
    2824:       {
    2825:         PetscQuadrature q;
    2826:         PetscInt        numBatches, batchSize, numBlocks, blockSize;
    2827:         PetscInt        Nq, Nb;

    2829:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    2830:         PetscCall(PetscFEGetQuadrature(fe, &q));
    2831:         PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
    2832:         PetscCall(PetscFEGetDimension(fe, &Nb));
    2833:         blockSize = Nb * Nq;
    2834:         batchSize = numBlocks * blockSize;
    2835:         chunkSize = numBatches * batchSize;
    2836:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    2837:         numChunks = numFaces / chunkSize;
    2838:         Nr        = numFaces % chunkSize;
    2839:         offset    = numFaces - Nr;
    2840:       }
    2841:       /* Do integration for each field */
    2842:       for (chunk = 0; chunk < numChunks; ++chunk) {
    2843:         PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom));
    2844:         PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], chunkSize, chunkGeom, &u[chunk * chunkSize * totDim], probAux, PetscSafePointerPlusOffset(a, chunk * chunkSize * totDimAux), &fintegral[chunk * chunkSize * Nf]));
    2845:         PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
    2846:       }
    2847:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
    2848:       PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &fintegral[offset * Nf]));
    2849:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
    2850:       /* Cleanup data arrays */
    2851:       PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
    2852:       PetscCall(PetscQuadratureDestroy(&qGeom));
    2853:     }
    2854:     PetscCall(PetscFree2(u, a));
    2855:     PetscCall(ISRestoreIndices(pointIS, &points));
    2856:   }
    2857:   PetscCall(DMDestroy(&plex));
    2858:   PetscCall(DMDestroy(&plexA));
    2859:   PetscFunctionReturn(PETSC_SUCCESS);
    2860: }

    2862: /*@C
    2863:   DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user

    2865:   Input Parameters:
    2866: + dm      - The mesh
    2867: . X       - Global input vector
    2868: . label   - The boundary `DMLabel`
    2869: . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
    2870: . vals    - The label values to use, or NULL for all values
    2871: . funcs   - The functions to integrate along the boundary for each field
    2872: - ctx     - The application context

    2874:   Output Parameter:
    2875: . integral - Integral for each field

    2877:   Level: developer

    2879: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
    2880: @*/
    2881: PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[], void (**funcs)(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[]), PetscScalar *integral, PetscCtx ctx)
    2882: {
    2883:   Vec          locX;
    2884:   PetscSection section;
    2885:   DMLabel      depthLabel;
    2886:   IS           facetIS;
    2887:   PetscInt     dim, Nf, f, v;

    2889:   PetscFunctionBegin;
    2893:   if (vals) PetscAssertPointer(vals, 5);
    2894:   PetscAssertPointer(integral, 7);
    2895:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
    2896:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    2897:   PetscCall(DMGetDimension(dm, &dim));
    2898:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
    2899:   /* Filter out ghost facets (SF leaves) so that each boundary facet is only
    2900:      counted on one rank. Without this, shared facets at partition boundaries
    2901:      are integrated on multiple ranks, causing double-counting after MPI sum. */
    2902:   if (facetIS) {
    2903:     PetscSF         sf;
    2904:     PetscInt        nleaves;
    2905:     const PetscInt *leaves;

    2907:     PetscCall(DMGetPointSF(dm, &sf));
    2908:     PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
    2909:     if (nleaves > 0 && leaves) {
    2910:       IS leafIS, ownedFacetIS;

    2912:       PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nleaves, leaves, PETSC_USE_POINTER, &leafIS));
    2913:       PetscCall(ISDifference(facetIS, leafIS, &ownedFacetIS));
    2914:       PetscCall(ISDestroy(&leafIS));
    2915:       PetscCall(ISDestroy(&facetIS));
    2916:       facetIS = ownedFacetIS;
    2917:     }
    2918:   }
    2919:   PetscCall(DMGetLocalSection(dm, &section));
    2920:   PetscCall(PetscSectionGetNumFields(section, &Nf));
    2921:   /* Get local solution with boundary values */
    2922:   PetscCall(DMGetLocalVector(dm, &locX));
    2923:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
    2924:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
    2925:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
    2926:   /* Loop over label values */
    2927:   PetscCall(PetscArrayzero(integral, Nf));
    2928:   for (v = 0; v < numVals; ++v) {
    2929:     IS           pointIS;
    2930:     PetscInt     numFaces, face;
    2931:     PetscScalar *fintegral;

    2933:     PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
    2934:     if (!pointIS) continue; /* No points with that id on this process */
    2935:     {
    2936:       IS isectIS;

    2938:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
    2939:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
    2940:       PetscCall(ISDestroy(&pointIS));
    2941:       pointIS = isectIS;
    2942:     }
    2943:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
    2944:     PetscCall(PetscCalloc1(numFaces * Nf, &fintegral));
    2945:     PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, funcs, fintegral, ctx));
    2946:     /* Sum point contributions into integral */
    2947:     for (f = 0; f < Nf; ++f)
    2948:       for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f];
    2949:     PetscCall(PetscFree(fintegral));
    2950:     PetscCall(ISDestroy(&pointIS));
    2951:   }
    2952:   PetscCall(DMRestoreLocalVector(dm, &locX));
    2953:   PetscCall(ISDestroy(&facetIS));
    2954:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
    2955:   PetscFunctionReturn(PETSC_SUCCESS);
    2956: }

    2958: /*@
    2959:   DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix from the coarse `DM` to a uniformly refined `DM`.

    2961:   Input Parameters:
    2962: + dmc       - The coarse mesh
    2963: . dmf       - The fine mesh
    2964: . isRefined - Flag indicating regular refinement, rather than the same topology
    2965: - ctx       - The application context

    2967:   Output Parameter:
    2968: . In - The interpolation matrix

    2970:   Level: developer

    2972: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`
    2973: @*/
    2974: PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, PetscCtx ctx)
    2975: {
    2976:   DM_Plex     *mesh = (DM_Plex *)dmc->data;
    2977:   const char  *name = "Interpolator";
    2978:   PetscFE     *feRef;
    2979:   PetscFV     *fvRef;
    2980:   PetscSection fsection, fglobalSection;
    2981:   PetscSection csection, cglobalSection;
    2982:   PetscScalar *elemMat;
    2983:   PetscInt     dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
    2984:   PetscInt     cTotDim = 0, rTotDim = 0;

    2986:   PetscFunctionBegin;
    2987:   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
    2988:   PetscCall(DMGetDimension(dmf, &dim));
    2989:   PetscCall(DMGetLocalSection(dmf, &fsection));
    2990:   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
    2991:   PetscCall(DMGetLocalSection(dmc, &csection));
    2992:   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
    2993:   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
    2994:   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
    2995:   PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef));
    2996:   for (f = 0; f < Nf; ++f) {
    2997:     PetscObject  obj, objc;
    2998:     PetscClassId id, idc;
    2999:     PetscInt     rNb = 0, Nc = 0, cNb = 0;

    3001:     PetscCall(DMGetField(dmf, f, NULL, &obj));
    3002:     PetscCall(PetscObjectGetClassId(obj, &id));
    3003:     if (id == PETSCFE_CLASSID) {
    3004:       PetscFE fe = (PetscFE)obj;

    3006:       if (isRefined) PetscCall(PetscFERefine(fe, &feRef[f]));
    3007:       else {
    3008:         PetscCall(PetscObjectReference((PetscObject)fe));
    3009:         feRef[f] = fe;
    3010:       }
    3011:       PetscCall(PetscFEGetDimension(feRef[f], &rNb));
    3012:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
    3013:     } else if (id == PETSCFV_CLASSID) {
    3014:       PetscFV        fv = (PetscFV)obj;
    3015:       PetscDualSpace Q;

    3017:       if (isRefined) PetscCall(PetscFVRefine(fv, &fvRef[f]));
    3018:       else {
    3019:         PetscCall(PetscObjectReference((PetscObject)fv));
    3020:         fvRef[f] = fv;
    3021:       }
    3022:       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
    3023:       PetscCall(PetscDualSpaceGetDimension(Q, &rNb));
    3024:       PetscCall(PetscFVGetDualSpace(fv, &Q));
    3025:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
    3026:     }
    3027:     PetscCall(DMGetField(dmc, f, NULL, &objc));
    3028:     PetscCall(PetscObjectGetClassId(objc, &idc));
    3029:     if (idc == PETSCFE_CLASSID) {
    3030:       PetscFE fe = (PetscFE)objc;

    3032:       PetscCall(PetscFEGetDimension(fe, &cNb));
    3033:     } else if (id == PETSCFV_CLASSID) {
    3034:       PetscFV        fv = (PetscFV)obj;
    3035:       PetscDualSpace Q;

    3037:       PetscCall(PetscFVGetDualSpace(fv, &Q));
    3038:       PetscCall(PetscDualSpaceGetDimension(Q, &cNb));
    3039:     }
    3040:     rTotDim += rNb;
    3041:     cTotDim += cNb;
    3042:   }
    3043:   PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat));
    3044:   PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim));
    3045:   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
    3046:     PetscDualSpace   Qref;
    3047:     PetscQuadrature  f;
    3048:     const PetscReal *qpoints, *qweights;
    3049:     PetscReal       *points;
    3050:     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;

    3052:     /* Compose points from all dual basis functionals */
    3053:     if (feRef[fieldI]) {
    3054:       PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref));
    3055:       PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc));
    3056:     } else {
    3057:       PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref));
    3058:       PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc));
    3059:     }
    3060:     PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim));
    3061:     for (i = 0; i < fpdim; ++i) {
    3062:       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
    3063:       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL));
    3064:       npoints += Np;
    3065:     }
    3066:     PetscCall(PetscMalloc1(npoints * dim, &points));
    3067:     for (i = 0, k = 0; i < fpdim; ++i) {
    3068:       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
    3069:       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL));
    3070:       for (p = 0; p < Np; ++p, ++k)
    3071:         for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
    3072:     }

    3074:     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
    3075:       PetscObject  obj;
    3076:       PetscClassId id;
    3077:       PetscInt     NcJ = 0, cpdim = 0, j, qNc;

    3079:       PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
    3080:       PetscCall(PetscObjectGetClassId(obj, &id));
    3081:       if (id == PETSCFE_CLASSID) {
    3082:         PetscFE         fe = (PetscFE)obj;
    3083:         PetscTabulation T  = NULL;

    3085:         /* Evaluate basis at points */
    3086:         PetscCall(PetscFEGetNumComponents(fe, &NcJ));
    3087:         PetscCall(PetscFEGetDimension(fe, &cpdim));
    3088:         /* For now, fields only interpolate themselves */
    3089:         if (fieldI == fieldJ) {
    3090:           PetscCheck(Nc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, Nc, NcJ);
    3091:           PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T));
    3092:           for (i = 0, k = 0; i < fpdim; ++i) {
    3093:             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
    3094:             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
    3095:             PetscCheck(qNc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, NcJ);
    3096:             for (p = 0; p < Np; ++p, ++k) {
    3097:               for (j = 0; j < cpdim; ++j) {
    3098:                 /*
    3099:                    cTotDim:            Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
    3100:                    offsetI, offsetJ:   Offsets into the larger element interpolation matrix for different fields
    3101:                    fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
    3102:                    qNC, Nc, Ncj, c:    Number of components in this field
    3103:                    Np, p:              Number of quad points in the fine grid functional i
    3104:                    k:                  i*Np + p, overall point number for the interpolation
    3105:                 */
    3106:                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += T->T[0][k * cpdim * NcJ + j * Nc + c] * qweights[p * qNc + c];
    3107:               }
    3108:             }
    3109:           }
    3110:           PetscCall(PetscTabulationDestroy(&T));
    3111:         }
    3112:       } else if (id == PETSCFV_CLASSID) {
    3113:         PetscFV fv = (PetscFV)obj;

    3115:         /* Evaluate constant function at points */
    3116:         PetscCall(PetscFVGetNumComponents(fv, &NcJ));
    3117:         cpdim = 1;
    3118:         /* For now, fields only interpolate themselves */
    3119:         if (fieldI == fieldJ) {
    3120:           PetscCheck(Nc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, Nc, NcJ);
    3121:           for (i = 0, k = 0; i < fpdim; ++i) {
    3122:             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
    3123:             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
    3124:             PetscCheck(qNc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, NcJ);
    3125:             for (p = 0; p < Np; ++p, ++k) {
    3126:               for (j = 0; j < cpdim; ++j) {
    3127:                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
    3128:               }
    3129:             }
    3130:           }
    3131:         }
    3132:       }
    3133:       offsetJ += cpdim;
    3134:     }
    3135:     offsetI += fpdim;
    3136:     PetscCall(PetscFree(points));
    3137:   }
    3138:   if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat));
    3139:   /* Preallocate matrix */
    3140:   {
    3141:     Mat          preallocator;
    3142:     PetscScalar *vals;
    3143:     PetscInt    *cellCIndices, *cellFIndices;
    3144:     PetscInt     locRows, locCols, cell;

    3146:     PetscCall(MatGetLocalSize(In, &locRows, &locCols));
    3147:     PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator));
    3148:     PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
    3149:     PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
    3150:     PetscCall(MatSetUp(preallocator));
    3151:     PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices));
    3152:     if (locRows || locCols) {
    3153:       for (cell = cStart; cell < cEnd; ++cell) {
    3154:         if (isRefined) {
    3155:           PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices));
    3156:           PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES));
    3157:         } else {
    3158:           PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, preallocator, cell, vals, INSERT_VALUES));
    3159:         }
    3160:       }
    3161:     }
    3162:     PetscCall(PetscFree3(vals, cellCIndices, cellFIndices));
    3163:     PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
    3164:     PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
    3165:     PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In));
    3166:     PetscCall(MatDestroy(&preallocator));
    3167:   }
    3168:   /* Fill matrix */
    3169:   PetscCall(MatZeroEntries(In));
    3170:   for (c = cStart; c < cEnd; ++c) {
    3171:     if (isRefined) {
    3172:       PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
    3173:     } else {
    3174:       PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, In, c, elemMat, INSERT_VALUES));
    3175:     }
    3176:   }
    3177:   for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f]));
    3178:   PetscCall(PetscFree2(feRef, fvRef));
    3179:   PetscCall(PetscFree(elemMat));
    3180:   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
    3181:   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
    3182:   if (mesh->printFEM > 1) {
    3183:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name));
    3184:     PetscCall(MatFilter(In, 1.0e-10, PETSC_FALSE, PETSC_FALSE));
    3185:     PetscCall(MatView(In, NULL));
    3186:   }
    3187:   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
    3188:   PetscFunctionReturn(PETSC_SUCCESS);
    3189: }

    3191: /*@
    3192:   DMPlexComputeMassMatrixNested - Form the local portion of the mass matrix from a coarse `DM` to a nested fine `DM`.

    3194:   Collective

    3196:   Input Parameters:
    3197: + dmc - the coarse mesh
    3198: . dmf - the fine mesh
    3199: - ctx - the application context

    3201:   Output Parameter:
    3202: . mass - the mass matrix

    3204:   Level: developer

    3206:   Note:
    3207:   This routine is not implemented and currently raises `PETSC_ERR_SUP`.

    3209: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixGeneral()`, `DMPlexComputeInterpolatorNested()`
    3210: @*/
    3211: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, PetscCtx ctx)
    3212: {
    3213:   SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
    3214: }

    3216: /*@
    3217:   DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix from the coarse `DM` to a non-nested fine `DM`.

    3219:   Input Parameters:
    3220: + dmf - The fine mesh
    3221: . dmc - The coarse mesh
    3222: - ctx - The application context

    3224:   Output Parameter:
    3225: . In - The interpolation matrix

    3227:   Level: developer

    3229: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`
    3230: @*/
    3231: PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, PetscCtx ctx)
    3232: {
    3233:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
    3234:   const char  *name = "Interpolator";
    3235:   PetscDS      prob;
    3236:   Mat          interp;
    3237:   PetscSection fsection, globalFSection;
    3238:   PetscSection csection, globalCSection;
    3239:   PetscInt     locRows, locCols;
    3240:   PetscReal   *x, *v0, *J, *invJ, detJ;
    3241:   PetscReal   *v0c, *Jc, *invJc, detJc;
    3242:   PetscScalar *elemMat;
    3243:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;

    3245:   PetscFunctionBegin;
    3246:   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
    3247:   PetscCall(DMGetCoordinateDim(dmc, &dim));
    3248:   PetscCall(DMGetDS(dmc, &prob));
    3249:   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
    3250:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    3251:   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
    3252:   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
    3253:   PetscCall(DMGetLocalSection(dmf, &fsection));
    3254:   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
    3255:   PetscCall(DMGetLocalSection(dmc, &csection));
    3256:   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
    3257:   PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd));
    3258:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    3259:   PetscCall(PetscMalloc1(totDim, &elemMat));

    3261:   PetscCall(MatGetLocalSize(In, &locRows, &locCols));
    3262:   PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp));
    3263:   PetscCall(MatSetType(interp, MATPREALLOCATOR));
    3264:   PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
    3265:   PetscCall(MatSetUp(interp));
    3266:   for (s = 0; s < 2; ++s) {
    3267:     for (field = 0; field < Nf; ++field) {
    3268:       PetscObject      obj;
    3269:       PetscClassId     id;
    3270:       PetscDualSpace   Q = NULL;
    3271:       PetscTabulation  T = NULL;
    3272:       PetscQuadrature  f;
    3273:       const PetscReal *qpoints, *qweights;
    3274:       PetscInt         Nc, qNc, Np, fpdim, off, i, d;

    3276:       PetscCall(PetscDSGetFieldOffset(prob, field, &off));
    3277:       PetscCall(PetscDSGetDiscretization(prob, field, &obj));
    3278:       PetscCall(PetscObjectGetClassId(obj, &id));
    3279:       if (id == PETSCFE_CLASSID) {
    3280:         PetscFE fe = (PetscFE)obj;

    3282:         PetscCall(PetscFEGetDualSpace(fe, &Q));
    3283:         PetscCall(PetscFEGetNumComponents(fe, &Nc));
    3284:         if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
    3285:       } else if (id == PETSCFV_CLASSID) {
    3286:         PetscFV fv = (PetscFV)obj;

    3288:         PetscCall(PetscFVGetDualSpace(fv, &Q));
    3289:         Nc = 1;
    3290:       } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    3291:       PetscCall(PetscDualSpaceGetDimension(Q, &fpdim));
    3292:       /* For each fine grid cell */
    3293:       for (cell = cStart; cell < cEnd; ++cell) {
    3294:         PetscInt *findices, *cindices;
    3295:         PetscInt  numFIndices, numCIndices;

    3297:         PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
    3298:         PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
    3299:         PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim);
    3300:         for (i = 0; i < fpdim; ++i) {
    3301:           Vec                pointVec;
    3302:           PetscScalar       *pV;
    3303:           PetscSF            coarseCellSF = NULL;
    3304:           const PetscSFNode *coarseCells;
    3305:           PetscInt           numCoarseCells, cpdim, row = findices[i + off], q, c, j;

    3307:           /* Get points from the dual basis functional quadrature */
    3308:           PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
    3309:           PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
    3310:           PetscCheck(qNc == Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, Nc);
    3311:           PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec));
    3312:           PetscCall(VecSetBlockSize(pointVec, dim));
    3313:           PetscCall(VecGetArray(pointVec, &pV));
    3314:           for (q = 0; q < Np; ++q) {
    3315:             const PetscReal xi0[3] = {-1., -1., -1.};

    3317:             /* Transform point to real space */
    3318:             CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
    3319:             for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
    3320:           }
    3321:           PetscCall(VecRestoreArray(pointVec, &pV));
    3322:           /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
    3323:           /* OPT: Read this out from preallocation information */
    3324:           PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
    3325:           /* Update preallocation info */
    3326:           PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
    3327:           PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
    3328:           PetscCall(VecGetArray(pointVec, &pV));
    3329:           for (ccell = 0; ccell < numCoarseCells; ++ccell) {
    3330:             PetscReal       pVReal[3];
    3331:             const PetscReal xi0[3] = {-1., -1., -1.};

    3333:             PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
    3334:             if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetDimension((PetscFE)obj, &cpdim));
    3335:             else cpdim = 1;

    3337:             if (s) {
    3338:               /* Transform points from real space to coarse reference space */
    3339:               PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
    3340:               for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
    3341:               CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);

    3343:               if (id == PETSCFE_CLASSID) {
    3344:                 /* Evaluate coarse basis on contained point */
    3345:                 PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T));
    3346:                 PetscCall(PetscArrayzero(elemMat, cpdim));
    3347:                 /* Get elemMat entries by multiplying by weight */
    3348:                 for (j = 0; j < cpdim; ++j) {
    3349:                   for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c];
    3350:                 }
    3351:               } else {
    3352:                 for (j = 0; j < cpdim; ++j) {
    3353:                   for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c];
    3354:                 }
    3355:               }
    3356:               if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
    3357:             }
    3358:             /* Update interpolator */
    3359:             PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim);
    3360:             PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES));
    3361:             PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
    3362:           }
    3363:           PetscCall(VecRestoreArray(pointVec, &pV));
    3364:           PetscCall(PetscSFDestroy(&coarseCellSF));
    3365:           PetscCall(VecDestroy(&pointVec));
    3366:         }
    3367:         PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
    3368:       }
    3369:       if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
    3370:     }
    3371:     if (!s) {
    3372:       PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY));
    3373:       PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY));
    3374:       PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In));
    3375:       PetscCall(MatDestroy(&interp));
    3376:       interp = In;
    3377:     }
    3378:   }
    3379:   PetscCall(PetscFree3(v0, J, invJ));
    3380:   PetscCall(PetscFree3(v0c, Jc, invJc));
    3381:   PetscCall(PetscFree(elemMat));
    3382:   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
    3383:   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
    3384:   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
    3385:   PetscFunctionReturn(PETSC_SUCCESS);
    3386: }

    3388: /*@
    3389:   DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix from the coarse `DM` to a non-nested fine `DM`.

    3391:   Input Parameters:
    3392: + dmf - The fine mesh
    3393: . dmc - The coarse mesh
    3394: - ctx - The application context

    3396:   Output Parameter:
    3397: . mass - The mass matrix

    3399:   Level: developer

    3401: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`
    3402: @*/
    3403: PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, PetscCtx ctx)
    3404: {
    3405:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
    3406:   const char  *name = "Mass Matrix";
    3407:   PetscDS      prob;
    3408:   PetscSection fsection, csection, globalFSection, globalCSection;
    3409:   PetscHSetIJ  ht;
    3410:   PetscLayout  rLayout;
    3411:   PetscInt    *dnz, *onz;
    3412:   PetscInt     locRows, rStart, rEnd;
    3413:   PetscReal   *x, *v0, *J, *invJ, detJ;
    3414:   PetscReal   *v0c, *Jc, *invJc, detJc;
    3415:   PetscScalar *elemMat;
    3416:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell;

    3418:   PetscFunctionBegin;
    3419:   PetscCall(DMGetCoordinateDim(dmc, &dim));
    3420:   PetscCall(DMGetDS(dmc, &prob));
    3421:   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
    3422:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    3423:   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
    3424:   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
    3425:   PetscCall(DMGetLocalSection(dmf, &fsection));
    3426:   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
    3427:   PetscCall(DMGetLocalSection(dmc, &csection));
    3428:   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
    3429:   PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd));
    3430:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    3431:   PetscCall(PetscMalloc1(totDim, &elemMat));

    3433:   PetscCall(MatGetLocalSize(mass, &locRows, NULL));
    3434:   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout));
    3435:   PetscCall(PetscLayoutSetLocalSize(rLayout, locRows));
    3436:   PetscCall(PetscLayoutSetBlockSize(rLayout, 1));
    3437:   PetscCall(PetscLayoutSetUp(rLayout));
    3438:   PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd));
    3439:   PetscCall(PetscLayoutDestroy(&rLayout));
    3440:   PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz));
    3441:   PetscCall(PetscHSetIJCreate(&ht));
    3442:   for (field = 0; field < Nf; ++field) {
    3443:     PetscObject      obj;
    3444:     PetscClassId     id;
    3445:     PetscQuadrature  quad;
    3446:     const PetscReal *qpoints;
    3447:     PetscInt         Nq, Nc, i, d;

    3449:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
    3450:     PetscCall(PetscObjectGetClassId(obj, &id));
    3451:     if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
    3452:     else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
    3453:     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL));
    3454:     /* For each fine grid cell */
    3455:     for (cell = cStart; cell < cEnd; ++cell) {
    3456:       Vec                pointVec;
    3457:       PetscScalar       *pV;
    3458:       PetscSF            coarseCellSF = NULL;
    3459:       const PetscSFNode *coarseCells;
    3460:       PetscInt           numCoarseCells, q, c;
    3461:       PetscInt          *findices, *cindices;
    3462:       PetscInt           numFIndices, numCIndices;

    3464:       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
    3465:       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
    3466:       /* Get points from the quadrature */
    3467:       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
    3468:       PetscCall(VecSetBlockSize(pointVec, dim));
    3469:       PetscCall(VecGetArray(pointVec, &pV));
    3470:       for (q = 0; q < Nq; ++q) {
    3471:         const PetscReal xi0[3] = {-1., -1., -1.};

    3473:         /* Transform point to real space */
    3474:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
    3475:         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
    3476:       }
    3477:       PetscCall(VecRestoreArray(pointVec, &pV));
    3478:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
    3479:       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
    3480:       PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view"));
    3481:       /* Update preallocation info */
    3482:       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
    3483:       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
    3484:       {
    3485:         PetscHashIJKey key;
    3486:         PetscBool      missing;

    3488:         for (i = 0; i < numFIndices; ++i) {
    3489:           key.i = findices[i];
    3490:           if (key.i >= 0) {
    3491:             /* Get indices for coarse elements */
    3492:             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
    3493:               PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
    3494:               for (c = 0; c < numCIndices; ++c) {
    3495:                 key.j = cindices[c];
    3496:                 if (key.j < 0) continue;
    3497:                 PetscCall(PetscHSetIJQueryAdd(ht, key, &missing));
    3498:                 if (missing) {
    3499:                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart];
    3500:                   else ++onz[key.i - rStart];
    3501:                 }
    3502:               }
    3503:               PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
    3504:             }
    3505:           }
    3506:         }
    3507:       }
    3508:       PetscCall(PetscSFDestroy(&coarseCellSF));
    3509:       PetscCall(VecDestroy(&pointVec));
    3510:       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
    3511:     }
    3512:   }
    3513:   PetscCall(PetscHSetIJDestroy(&ht));
    3514:   PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL));
    3515:   PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
    3516:   PetscCall(PetscFree2(dnz, onz));
    3517:   for (field = 0; field < Nf; ++field) {
    3518:     PetscObject      obj;
    3519:     PetscClassId     id;
    3520:     PetscTabulation  T, Tfine;
    3521:     PetscQuadrature  quad;
    3522:     const PetscReal *qpoints, *qweights;
    3523:     PetscInt         Nq, Nc, i, d;

    3525:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
    3526:     PetscCall(PetscObjectGetClassId(obj, &id));
    3527:     if (id == PETSCFE_CLASSID) {
    3528:       PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
    3529:       PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine));
    3530:       PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T));
    3531:     } else {
    3532:       PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
    3533:     }
    3534:     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights));
    3535:     /* For each fine grid cell */
    3536:     for (cell = cStart; cell < cEnd; ++cell) {
    3537:       Vec                pointVec;
    3538:       PetscScalar       *pV;
    3539:       PetscSF            coarseCellSF = NULL;
    3540:       const PetscSFNode *coarseCells;
    3541:       PetscInt           numCoarseCells, cpdim, q, c, j;
    3542:       PetscInt          *findices, *cindices;
    3543:       PetscInt           numFIndices, numCIndices;

    3545:       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
    3546:       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
    3547:       /* Get points from the quadrature */
    3548:       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
    3549:       PetscCall(VecSetBlockSize(pointVec, dim));
    3550:       PetscCall(VecGetArray(pointVec, &pV));
    3551:       for (q = 0; q < Nq; ++q) {
    3552:         const PetscReal xi0[3] = {-1., -1., -1.};

    3554:         /* Transform point to real space */
    3555:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
    3556:         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
    3557:       }
    3558:       PetscCall(VecRestoreArray(pointVec, &pV));
    3559:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
    3560:       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
    3561:       /* Update matrix */
    3562:       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
    3563:       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
    3564:       PetscCall(VecGetArray(pointVec, &pV));
    3565:       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
    3566:         PetscReal       pVReal[3];
    3567:         const PetscReal xi0[3] = {-1., -1., -1.};

    3569:         PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
    3570:         /* Transform points from real space to coarse reference space */
    3571:         PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc));
    3572:         for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]);
    3573:         CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x);

    3575:         if (id == PETSCFE_CLASSID) {
    3576:           PetscFE fe = (PetscFE)obj;

    3578:           /* Evaluate coarse basis on contained point */
    3579:           PetscCall(PetscFEGetDimension(fe, &cpdim));
    3580:           PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T));
    3581:           /* Get elemMat entries by multiplying by weight */
    3582:           for (i = 0; i < numFIndices; ++i) {
    3583:             PetscCall(PetscArrayzero(elemMat, cpdim));
    3584:             for (j = 0; j < cpdim; ++j) {
    3585:               for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * Tfine->T[0][(ccell * numFIndices + i) * Nc + c] * qweights[ccell * Nc + c] * detJ;
    3586:             }
    3587:             /* Update interpolator */
    3588:             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
    3589:             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
    3590:             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
    3591:           }
    3592:         } else {
    3593:           cpdim = 1;
    3594:           for (i = 0; i < numFIndices; ++i) {
    3595:             PetscCall(PetscArrayzero(elemMat, cpdim));
    3596:             for (j = 0; j < cpdim; ++j) {
    3597:               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ;
    3598:             }
    3599:             /* Update interpolator */
    3600:             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
    3601:             PetscCall(PetscPrintf(PETSC_COMM_SELF, "Nq: %" PetscInt_FMT " %" PetscInt_FMT " Nf: %" PetscInt_FMT " %" PetscInt_FMT " Nc: %" PetscInt_FMT " %" PetscInt_FMT "\n", ccell, Nq, i, numFIndices, j, numCIndices));
    3602:             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
    3603:             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
    3604:           }
    3605:         }
    3606:         PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
    3607:       }
    3608:       PetscCall(VecRestoreArray(pointVec, &pV));
    3609:       PetscCall(PetscSFDestroy(&coarseCellSF));
    3610:       PetscCall(VecDestroy(&pointVec));
    3611:       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
    3612:     }
    3613:     if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
    3614:   }
    3615:   PetscCall(PetscFree3(v0, J, invJ));
    3616:   PetscCall(PetscFree3(v0c, Jc, invJc));
    3617:   PetscCall(PetscFree(elemMat));
    3618:   PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY));
    3619:   PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY));
    3620:   PetscFunctionReturn(PETSC_SUCCESS);
    3621: }

    3623: /*@
    3624:   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns

    3626:   Input Parameters:
    3627: + dmc - The coarse mesh
    3628: . dmf - The fine mesh
    3629: - ctx - The application context

    3631:   Output Parameter:
    3632: . sc - The mapping

    3634:   Level: developer

    3636: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`
    3637: @*/
    3638: PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, PetscCtx ctx)
    3639: {
    3640:   PetscDS      prob;
    3641:   PetscFE     *feRef;
    3642:   PetscFV     *fvRef;
    3643:   Vec          fv, cv;
    3644:   IS           fis, cis;
    3645:   PetscSection fsection, fglobalSection, csection, cglobalSection;
    3646:   PetscInt    *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
    3647:   PetscInt     cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
    3648:   PetscBool   *needAvg;

    3650:   PetscFunctionBegin;
    3651:   PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
    3652:   PetscCall(DMGetDimension(dmf, &dim));
    3653:   PetscCall(DMGetLocalSection(dmf, &fsection));
    3654:   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
    3655:   PetscCall(DMGetLocalSection(dmc, &csection));
    3656:   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
    3657:   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
    3658:   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
    3659:   PetscCall(DMGetDS(dmc, &prob));
    3660:   PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg));
    3661:   for (f = 0; f < Nf; ++f) {
    3662:     PetscObject  obj;
    3663:     PetscClassId id;
    3664:     PetscInt     fNb = 0, Nc = 0;

    3666:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    3667:     PetscCall(PetscObjectGetClassId(obj, &id));
    3668:     if (id == PETSCFE_CLASSID) {
    3669:       PetscFE    fe = (PetscFE)obj;
    3670:       PetscSpace sp;
    3671:       PetscInt   maxDegree;

    3673:       PetscCall(PetscFERefine(fe, &feRef[f]));
    3674:       PetscCall(PetscFEGetDimension(feRef[f], &fNb));
    3675:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
    3676:       PetscCall(PetscFEGetBasisSpace(fe, &sp));
    3677:       PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
    3678:       if (!maxDegree) needAvg[f] = PETSC_TRUE;
    3679:     } else if (id == PETSCFV_CLASSID) {
    3680:       PetscFV        fv = (PetscFV)obj;
    3681:       PetscDualSpace Q;

    3683:       PetscCall(PetscFVRefine(fv, &fvRef[f]));
    3684:       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
    3685:       PetscCall(PetscDualSpaceGetDimension(Q, &fNb));
    3686:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
    3687:       needAvg[f] = PETSC_TRUE;
    3688:     }
    3689:     fTotDim += fNb;
    3690:   }
    3691:   PetscCall(PetscDSGetTotalDimension(prob, &cTotDim));
    3692:   PetscCall(PetscMalloc1(cTotDim, &cmap));
    3693:   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
    3694:     PetscFE        feC;
    3695:     PetscFV        fvC;
    3696:     PetscDualSpace QF, QC;
    3697:     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;

    3699:     if (feRef[field]) {
    3700:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC));
    3701:       PetscCall(PetscFEGetNumComponents(feC, &NcC));
    3702:       PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
    3703:       PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
    3704:       PetscCall(PetscDualSpaceGetOrder(QF, &order));
    3705:       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
    3706:       PetscCall(PetscFEGetDualSpace(feC, &QC));
    3707:       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
    3708:     } else {
    3709:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC));
    3710:       PetscCall(PetscFVGetNumComponents(fvC, &NcC));
    3711:       PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
    3712:       PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
    3713:       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
    3714:       PetscCall(PetscFVGetDualSpace(fvC, &QC));
    3715:       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
    3716:     }
    3717:     PetscCheck(NcF == NcC, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, NcF, NcC);
    3718:     for (c = 0; c < cpdim; ++c) {
    3719:       PetscQuadrature  cfunc;
    3720:       const PetscReal *cqpoints, *cqweights;
    3721:       PetscInt         NqcC, NpC;
    3722:       PetscBool        found = PETSC_FALSE;

    3724:       PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
    3725:       PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
    3726:       PetscCheck(NqcC == NcC, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of quadrature components %" PetscInt_FMT " must match number of field components %" PetscInt_FMT, NqcC, NcC);
    3727:       PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
    3728:       for (f = 0; f < fpdim; ++f) {
    3729:         PetscQuadrature  ffunc;
    3730:         const PetscReal *fqpoints, *fqweights;
    3731:         PetscReal        sum = 0.0;
    3732:         PetscInt         NqcF, NpF;

    3734:         PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc));
    3735:         PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights));
    3736:         PetscCheck(NqcF == NcF, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of quadrature components %" PetscInt_FMT " must match number of field components %" PetscInt_FMT, NqcF, NcF);
    3737:         if (NpC != NpF) continue;
    3738:         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
    3739:         if (sum > 1.0e-9) continue;
    3740:         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]);
    3741:         if (sum < 1.0e-9) continue;
    3742:         cmap[offsetC + c] = offsetF + f;
    3743:         found             = PETSC_TRUE;
    3744:         break;
    3745:       }
    3746:       if (!found) {
    3747:         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
    3748:         PetscCheck(fvRef[field] || (feRef[field] && order == 0), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
    3749:         cmap[offsetC + c] = offsetF + 0;
    3750:       }
    3751:     }
    3752:     offsetC += cpdim;
    3753:     offsetF += fpdim;
    3754:   }
    3755:   for (f = 0; f < Nf; ++f) {
    3756:     PetscCall(PetscFEDestroy(&feRef[f]));
    3757:     PetscCall(PetscFVDestroy(&fvRef[f]));
    3758:   }
    3759:   PetscCall(PetscFree3(feRef, fvRef, needAvg));

    3761:   PetscCall(DMGetGlobalVector(dmf, &fv));
    3762:   PetscCall(DMGetGlobalVector(dmc, &cv));
    3763:   PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
    3764:   PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
    3765:   PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
    3766:   PetscCall(PetscMalloc1(m, &cindices));
    3767:   PetscCall(PetscMalloc1(m, &findices));
    3768:   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
    3769:   for (c = cStart; c < cEnd; ++c) {
    3770:     PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
    3771:     for (d = 0; d < cTotDim; ++d) {
    3772:       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
    3773:       PetscCheck(!(findices[cellCIndices[d] - startC] >= 0) || !(findices[cellCIndices[d] - startC] != cellFIndices[cmap[d]]), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Cell %" PetscInt_FMT " Coarse dof %" PetscInt_FMT " maps to both %" PetscInt_FMT " and %" PetscInt_FMT, c, cindices[cellCIndices[d] - startC], findices[cellCIndices[d] - startC], cellFIndices[cmap[d]]);
    3774:       cindices[cellCIndices[d] - startC] = cellCIndices[d];
    3775:       findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
    3776:     }
    3777:   }
    3778:   PetscCall(PetscFree(cmap));
    3779:   PetscCall(PetscFree2(cellCIndices, cellFIndices));

    3781:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
    3782:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
    3783:   PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
    3784:   PetscCall(ISDestroy(&cis));
    3785:   PetscCall(ISDestroy(&fis));
    3786:   PetscCall(DMRestoreGlobalVector(dmf, &fv));
    3787:   PetscCall(DMRestoreGlobalVector(dmc, &cv));
    3788:   PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
    3789:   PetscFunctionReturn(PETSC_SUCCESS);
    3790: }

    3792: /*@C
    3793:   DMPlexGetCellFields - Retrieve the field values values for a chunk of cells

    3795:   Input Parameters:
    3796: + dm     - The `DM`
    3797: . cellIS - The cells to include
    3798: . locX   - A local vector with the solution fields
    3799: . locX_t - A local vector with solution field time derivatives, or `NULL`
    3800: - locA   - A local vector with auxiliary fields, or `NULL`

    3802:   Output Parameters:
    3803: + u   - The field coefficients
    3804: . u_t - The fields derivative coefficients
    3805: - a   - The auxiliary field coefficients

    3807:   Level: developer

    3809: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
    3810: @*/
    3811: PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, PeOp Vec locX_t, PeOp Vec locA, PetscScalar *u[], PetscScalar *u_t[], PetscScalar *a[])
    3812: {
    3813:   DM              plex, plexA = NULL;
    3814:   DMEnclosureType encAux;
    3815:   PetscSection    section, sectionAux;
    3816:   PetscDS         prob;
    3817:   const PetscInt *cells;
    3818:   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;

    3820:   PetscFunctionBegin;
    3825:   PetscAssertPointer(u, 6);
    3826:   PetscAssertPointer(u_t, 7);
    3827:   PetscAssertPointer(a, 8);
    3828:   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
    3829:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    3830:   PetscCall(DMGetLocalSection(dm, &section));
    3831:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
    3832:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    3833:   if (locA) {
    3834:     DM      dmAux;
    3835:     PetscDS probAux;

    3837:     PetscCall(VecGetDM(locA, &dmAux));
    3838:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    3839:     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
    3840:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
    3841:     PetscCall(DMGetDS(dmAux, &probAux));
    3842:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    3843:   }
    3844:   numCells = cEnd - cStart;
    3845:   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
    3846:   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
    3847:   else *u_t = NULL;
    3848:   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
    3849:   else *a = NULL;
    3850:   for (c = cStart; c < cEnd; ++c) {
    3851:     const PetscInt cell = cells ? cells[c] : c;
    3852:     const PetscInt cind = c - cStart;
    3853:     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
    3854:     PetscInt       i;

    3856:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
    3857:     for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
    3858:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
    3859:     if (locX_t) {
    3860:       PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
    3861:       for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
    3862:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
    3863:     }
    3864:     if (locA) {
    3865:       PetscInt subcell;
    3866:       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
    3867:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x));
    3868:       for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
    3869:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x));
    3870:     }
    3871:   }
    3872:   PetscCall(DMDestroy(&plex));
    3873:   if (locA) PetscCall(DMDestroy(&plexA));
    3874:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    3875:   PetscFunctionReturn(PETSC_SUCCESS);
    3876: }

    3878: /*@C
    3879:   DMPlexRestoreCellFields - Restore the field values values for a chunk of cells

    3881:   Input Parameters:
    3882: + dm     - The `DM`
    3883: . cellIS - The cells to include
    3884: . locX   - A local vector with the solution fields
    3885: . locX_t - A local vector with solution field time derivatives, or `NULL`
    3886: - locA   - A local vector with auxiliary fields, or `NULL`

    3888:   Output Parameters:
    3889: + u   - The field coefficients
    3890: . u_t - The fields derivative coefficients
    3891: - a   - The auxiliary field coefficients

    3893:   Level: developer

    3895: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
    3896: @*/
    3897: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, PeOp Vec locX_t, PeOp Vec locA, PetscScalar *u[], PetscScalar *u_t[], PetscScalar *a[])
    3898: {
    3899:   PetscFunctionBegin;
    3900:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
    3901:   if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
    3902:   if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
    3903:   PetscFunctionReturn(PETSC_SUCCESS);
    3904: }

    3906: static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
    3907: {
    3908:   DM              plex, plexA = NULL;
    3909:   DMEnclosureType encAux;
    3910:   PetscSection    section, sectionAux;
    3911:   PetscDS         ds, dsIn;
    3912:   const PetscInt *cells;
    3913:   PetscInt        cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;

    3915:   PetscFunctionBegin;
    3921:   PetscAssertPointer(u, 6);
    3922:   PetscAssertPointer(u_t, 7);
    3923:   PetscAssertPointer(a, 8);
    3924:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    3925:   numCells = cEnd - cStart;
    3926:   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
    3927:   PetscCall(DMGetLocalSection(dm, &section));
    3928:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
    3929:   PetscCall(PetscDSGetNumFields(dsIn, &Nf));
    3930:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDim));
    3931:   if (locA) {
    3932:     DM      dmAux;
    3933:     PetscDS probAux;

    3935:     PetscCall(VecGetDM(locA, &dmAux));
    3936:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    3937:     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
    3938:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
    3939:     PetscCall(DMGetDS(dmAux, &probAux));
    3940:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    3941:   }
    3942:   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
    3943:   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
    3944:   else {
    3945:     *u_t = NULL;
    3946:   }
    3947:   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
    3948:   else {
    3949:     *a = NULL;
    3950:   }
    3951:   // Loop over cohesive cells
    3952:   for (c = cStart; c < cEnd; ++c) {
    3953:     const PetscInt  cell = cells ? cells[c] : c;
    3954:     const PetscInt  cind = c - cStart;
    3955:     PetscScalar    *xf = NULL, *xc = NULL, *x = NULL, *xf_t = NULL, *xc_t = NULL;
    3956:     PetscScalar    *ul = &(*u)[cind * totDim], *ul_t = PetscSafePointerPlusOffset(*u_t, cind * totDim);
    3957:     const PetscInt *cone, *ornt;
    3958:     PetscInt        Nx = 0, Nxf, s;

    3960:     PetscCall(DMPlexGetCone(dm, cell, &cone));
    3961:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
    3962:     // Put in cohesive unknowns
    3963:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
    3964:     if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t));
    3965:     for (f = 0; f < Nf; ++f) {
    3966:       PetscInt  fdofIn, foff, foffIn;
    3967:       PetscBool cohesive;

    3969:       PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
    3970:       if (!cohesive) continue;
    3971:       PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
    3972:       PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff));
    3973:       PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
    3974:       for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i];
    3975:       if (locX_t)
    3976:         for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + i] = xf_t[foff + i];
    3977:       Nx += fdofIn;
    3978:     }
    3979:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf));
    3980:     if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &xf_t));
    3981:     // Loop over sides of surface
    3982:     for (s = 0; s < 2; ++s) {
    3983:       const PetscInt *support;
    3984:       const PetscInt  face = cone[s];
    3985:       PetscDS         dsC;
    3986:       PetscInt        ssize, ncell, Nxc;

    3988:       // I don't think I need the face to have 0 orientation in the hybrid cell
    3989:       //PetscCheck(!ornt[s], PETSC_COMM_SELF, PETSC_ERR_SUP, "Face %" PetscInt_FMT " in hybrid cell %" PetscInt_FMT " has orientation %" PetscInt_FMT " != 0", face, cell, ornt[s]);
    3990:       PetscCall(DMPlexGetSupport(dm, face, &support));
    3991:       PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
    3992:       if (support[0] == cell) ncell = support[1];
    3993:       else if (support[1] == cell) ncell = support[0];
    3994:       else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
    3995:       // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields
    3996:       PetscCall(DMGetCellDS(dm, ncell, &dsC, NULL));
    3997:       PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc));
    3998:       if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, ncell, NULL, &xc_t));
    3999:       for (f = 0; f < Nf; ++f) {
    4000:         PetscInt  fdofIn, foffIn, foff;
    4001:         PetscBool cohesive;

    4003:         PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
    4004:         if (cohesive) continue;
    4005:         PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
    4006:         PetscCall(PetscDSGetFieldOffset(dsC, f, &foff));
    4007:         PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
    4008:         for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foff + i];
    4009:         if (locX_t)
    4010:           for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + s * fdofIn + i] = xc_t[foff + i];
    4011:         Nx += fdofIn;
    4012:       }
    4013:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc));
    4014:       if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, ncell, NULL, &xc_t));
    4015:     }
    4016:     PetscCheck(Nx == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for cell %" PetscInt_FMT " does not match DS size %" PetscInt_FMT, Nx, cell, totDim);

    4018:     if (locA) {
    4019:       PetscScalar *al = &(*a)[cind * totDimAux];
    4020:       PetscInt     subcell;

    4022:       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
    4023:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
    4024:       PetscCheck(Nx == totDimAux, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for subcell %" PetscInt_FMT "does not match DS size %" PetscInt_FMT, Nx, subcell, totDimAux);
    4025:       for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i];
    4026:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
    4027:     }
    4028:   }
    4029:   PetscCall(DMDestroy(&plex));
    4030:   PetscCall(DMDestroy(&plexA));
    4031:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    4032:   PetscFunctionReturn(PETSC_SUCCESS);
    4033: }

    4035: /*
    4036:   DMPlexGetHybridFields - Get the field values for the negative side (s = 0) and positive side (s = 1) of the interface

    4038:   Input Parameters:
    4039: + dm      - The full domain DM
    4040: . dmX     - An array of DM for the field, say an auxiliary DM, indexed by s
    4041: . dsX     - An array of PetscDS for the field, indexed by s
    4042: . cellIS  - The interface cells for which we want values
    4043: . locX    - An array of local vectors with the field values, indexed by s
    4044: - useCell - Flag to have values come from neighboring cell rather than endcap face

    4046:   Output Parameter:
    4047: . x       - An array of field values, indexed by s

    4049:   Note:
    4050:   The arrays in `x` will be allocated using `DMGetWorkArray()`, and must be returned using `DMPlexRestoreHybridFields()`.

    4052:   Level: advanced

    4054: .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()`
    4055: */
    4056: static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
    4057: {
    4058:   DM              plexX[2];
    4059:   DMEnclosureType encX[2];
    4060:   PetscSection    sectionX[2];
    4061:   const PetscInt *cells;
    4062:   PetscInt        cStart, cEnd, numCells, c, s, totDimX[2];

    4064:   PetscFunctionBegin;
    4065:   PetscAssertPointer(locX, 5);
    4066:   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
    4067:   PetscAssertPointer(dmX, 2);
    4068:   PetscAssertPointer(dsX, 3);
    4070:   PetscAssertPointer(x, 7);
    4071:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    4072:   numCells = cEnd - cStart;
    4073:   for (s = 0; s < 2; ++s) {
    4077:     PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE));
    4078:     PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s]));
    4079:     PetscCall(DMGetLocalSection(dmX[s], &sectionX[s]));
    4080:     PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s]));
    4081:     PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s]));
    4082:   }
    4083:   for (c = cStart; c < cEnd; ++c) {
    4084:     const PetscInt  cell = cells ? cells[c] : c;
    4085:     const PetscInt  cind = c - cStart;
    4086:     const PetscInt *cone, *ornt;

    4088:     PetscCall(DMPlexGetCone(dm, cell, &cone));
    4089:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
    4090:     //PetscCheck(!ornt[0], PETSC_COMM_SELF, PETSC_ERR_SUP, "Face %" PetscInt_FMT " in hybrid cell %" PetscInt_FMT " has orientation %" PetscInt_FMT " != 0", cone[0], cell, ornt[0]);
    4091:     for (s = 0; s < 2; ++s) {
    4092:       const PetscInt tdX     = totDimX[s];
    4093:       PetscScalar   *closure = NULL, *xl = &x[s][cind * tdX];
    4094:       PetscInt       face = cone[s], point = face, subpoint, Nx, i;

    4096:       if (useCell) {
    4097:         const PetscInt *support;
    4098:         PetscInt        ssize;

    4100:         PetscCall(DMPlexGetSupport(dm, face, &support));
    4101:         PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
    4102:         PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", face, cell, ssize);
    4103:         if (support[0] == cell) point = support[1];
    4104:         else if (support[1] == cell) point = support[0];
    4105:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
    4106:       }
    4107:       PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint));
    4108:       PetscCall(DMPlexVecGetOrientedClosure(plexX[s], sectionX[s], PETSC_FALSE, locX[s], subpoint, ornt[s], &Nx, &closure));
    4109:       PetscCheck(Nx == tdX, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for subpoint %" PetscInt_FMT " does not match DS size %" PetscInt_FMT, Nx, subpoint, tdX);
    4110:       for (i = 0; i < Nx; ++i) xl[i] = closure[i];
    4111:       PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure));
    4112:     }
    4113:   }
    4114:   for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s]));
    4115:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    4116:   PetscFunctionReturn(PETSC_SUCCESS);
    4117: }

    4119: static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
    4120: {
    4121:   PetscFunctionBegin;
    4122:   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
    4123:   PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0]));
    4124:   PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1]));
    4125:   PetscFunctionReturn(PETSC_SUCCESS);
    4126: }

    4128: /*@C
    4129:   DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces

    4131:   Input Parameters:
    4132: + dm           - The `DM`
    4133: . fStart       - The first face to include
    4134: . fEnd         - The first face to exclude
    4135: . locX         - A local vector with the solution fields
    4136: . locX_t       - A local vector with solution field time derivatives, or `NULL`
    4137: . faceGeometry - A local vector with face geometry
    4138: . cellGeometry - A local vector with cell geometry
    4139: - locGrad      - A local vector with field gradients, or `NULL`

    4141:   Output Parameters:
    4142: + Nface - The number of faces with field values
    4143: . uL    - The field values at the left side of the face
    4144: - uR    - The field values at the right side of the face

    4146:   Level: developer

    4148: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
    4149: @*/
    4150: PetscErrorCode DMPlexGetFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, PeOp Vec locX_t, Vec faceGeometry, Vec cellGeometry, PeOp Vec locGrad, PetscInt *Nface, PetscScalar *uL[], PetscScalar *uR[])
    4151: {
    4152:   DM                 dmFace, dmCell, dmGrad = NULL;
    4153:   PetscSection       section;
    4154:   PetscDS            prob;
    4155:   DMLabel            ghostLabel;
    4156:   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
    4157:   PetscBool         *isFE;
    4158:   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;

    4160:   PetscFunctionBegin;
    4167:   PetscAssertPointer(uL, 10);
    4168:   PetscAssertPointer(uR, 11);
    4169:   PetscCall(DMGetDimension(dm, &dim));
    4170:   PetscCall(DMGetDS(dm, &prob));
    4171:   PetscCall(DMGetLocalSection(dm, &section));
    4172:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    4173:   PetscCall(PetscDSGetTotalComponents(prob, &Nc));
    4174:   PetscCall(PetscMalloc1(Nf, &isFE));
    4175:   for (f = 0; f < Nf; ++f) {
    4176:     PetscObject  obj;
    4177:     PetscClassId id;

    4179:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    4180:     PetscCall(PetscObjectGetClassId(obj, &id));
    4181:     if (id == PETSCFE_CLASSID) {
    4182:       isFE[f] = PETSC_TRUE;
    4183:     } else if (id == PETSCFV_CLASSID) {
    4184:       isFE[f] = PETSC_FALSE;
    4185:     } else {
    4186:       isFE[f] = PETSC_FALSE;
    4187:     }
    4188:   }
    4189:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
    4190:   PetscCall(VecGetArrayRead(locX, &x));
    4191:   PetscCall(VecGetDM(faceGeometry, &dmFace));
    4192:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
    4193:   PetscCall(VecGetDM(cellGeometry, &dmCell));
    4194:   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
    4195:   if (locGrad) {
    4196:     PetscCall(VecGetDM(locGrad, &dmGrad));
    4197:     PetscCall(VecGetArrayRead(locGrad, &lgrad));
    4198:   }
    4199:   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL));
    4200:   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR));
    4201:   /* Right now just eat the extra work for FE (could make a cell loop) */
    4202:   for (face = fStart, iface = 0; face < fEnd; ++face) {
    4203:     const PetscInt  *cells;
    4204:     PetscFVFaceGeom *fg;
    4205:     PetscFVCellGeom *cgL, *cgR;
    4206:     PetscScalar     *xL, *xR, *gL, *gR;
    4207:     PetscScalar     *uLl = *uL, *uRl = *uR;
    4208:     PetscInt         ghost, nsupp, nchild;

    4210:     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
    4211:     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
    4212:     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
    4213:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
    4214:     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
    4215:     PetscCall(DMPlexGetSupport(dm, face, &cells));
    4216:     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
    4217:     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
    4218:     for (f = 0; f < Nf; ++f) {
    4219:       PetscInt off;

    4221:       PetscCall(PetscDSGetComponentOffset(prob, f, &off));
    4222:       if (isFE[f]) {
    4223:         const PetscInt *cone;
    4224:         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;

    4226:         xL = xR = NULL;
    4227:         PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
    4228:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, &xL));
    4229:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, &xR));
    4230:         PetscCall(DMPlexGetCone(dm, cells[0], &cone));
    4231:         PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
    4232:         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
    4233:           if (cone[faceLocL] == face) break;
    4234:         PetscCall(DMPlexGetCone(dm, cells[1], &cone));
    4235:         PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
    4236:         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
    4237:           if (cone[faceLocR] == face) break;
    4238:         PetscCheck(faceLocL != coneSizeL || faceLocR != coneSizeR, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find face %" PetscInt_FMT " in cone of cell %" PetscInt_FMT " or cell %" PetscInt_FMT, face, cells[0], cells[1]);
    4239:         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
    4240:         /* TODO: this is a hack that might not be right for nonconforming */
    4241:         if (faceLocL < coneSizeL) {
    4242:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
    4243:           if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
    4244:           else {
    4245:             for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
    4246:           }
    4247:         } else {
    4248:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
    4249:           PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
    4250:           for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
    4251:         }
    4252:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, &xL));
    4253:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, &xR));
    4254:       } else {
    4255:         PetscFV  fv;
    4256:         PetscInt numComp, c;

    4258:         PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
    4259:         PetscCall(PetscFVGetNumComponents(fv, &numComp));
    4260:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
    4261:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
    4262:         if (dmGrad) {
    4263:           PetscReal dxL[3], dxR[3];

    4265:           PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
    4266:           PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
    4267:           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
    4268:           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
    4269:           for (c = 0; c < numComp; ++c) {
    4270:             uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
    4271:             uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
    4272:           }
    4273:         } else {
    4274:           for (c = 0; c < numComp; ++c) {
    4275:             uLl[iface * Nc + off + c] = xL[c];
    4276:             uRl[iface * Nc + off + c] = xR[c];
    4277:           }
    4278:         }
    4279:       }
    4280:     }
    4281:     ++iface;
    4282:   }
    4283:   *Nface = iface;
    4284:   PetscCall(VecRestoreArrayRead(locX, &x));
    4285:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
    4286:   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
    4287:   if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
    4288:   PetscCall(PetscFree(isFE));
    4289:   PetscFunctionReturn(PETSC_SUCCESS);
    4290: }

    4292: /*@C
    4293:   DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces

    4295:   Input Parameters:
    4296: + dm           - The `DM`
    4297: . fStart       - The first face to include
    4298: . fEnd         - The first face to exclude
    4299: . locX         - A local vector with the solution fields
    4300: . locX_t       - A local vector with solution field time derivatives, or `NULL`
    4301: . faceGeometry - A local vector with face geometry
    4302: . cellGeometry - A local vector with cell geometry
    4303: - locGrad      - A local vector with field gradients, or `NULL`

    4305:   Output Parameters:
    4306: + Nface - The number of faces with field values
    4307: . uL    - The field values at the left side of the face
    4308: - uR    - The field values at the right side of the face

    4310:   Level: developer

    4312: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
    4313: @*/
    4314: PetscErrorCode DMPlexRestoreFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, PeOp Vec locX_t, Vec faceGeometry, Vec cellGeometry, PeOp Vec locGrad, PetscInt *Nface, PetscScalar *uL[], PetscScalar *uR[])
    4315: {
    4316:   PetscFunctionBegin;
    4317:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
    4318:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
    4319:   PetscFunctionReturn(PETSC_SUCCESS);
    4320: }

    4322: /*@C
    4323:   DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces

    4325:   Input Parameters:
    4326: + dm           - The `DM`
    4327: . fStart       - The first face to include
    4328: . fEnd         - The first face to exclude
    4329: . faceGeometry - A local vector with face geometry
    4330: - cellGeometry - A local vector with cell geometry

    4332:   Output Parameters:
    4333: + Nface - The number of faces with field values
    4334: . fgeom - The face centroid and normals
    4335: - vol   - The cell volumes

    4337:   Level: developer

    4339: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
    4340: @*/
    4341: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom *fgeom[], PetscReal *vol[])
    4342: {
    4343:   DM                 dmFace, dmCell;
    4344:   DMLabel            ghostLabel;
    4345:   const PetscScalar *facegeom, *cellgeom;
    4346:   PetscInt           dim, numFaces = fEnd - fStart, iface, face;

    4348:   PetscFunctionBegin;
    4352:   PetscAssertPointer(fgeom, 7);
    4353:   PetscAssertPointer(vol, 8);
    4354:   PetscCall(DMGetDimension(dm, &dim));
    4355:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
    4356:   PetscCall(VecGetDM(faceGeometry, &dmFace));
    4357:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
    4358:   PetscCall(VecGetDM(cellGeometry, &dmCell));
    4359:   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
    4360:   PetscCall(PetscMalloc1(numFaces, fgeom));
    4361:   PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol));
    4362:   for (face = fStart, iface = 0; face < fEnd; ++face) {
    4363:     const PetscInt  *cells;
    4364:     PetscFVFaceGeom *fg;
    4365:     PetscFVCellGeom *cgL, *cgR;
    4366:     PetscFVFaceGeom *fgeoml = *fgeom;
    4367:     PetscReal       *voll   = *vol;
    4368:     PetscInt         ghost, d, nchild, nsupp;

    4370:     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
    4371:     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
    4372:     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
    4373:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
    4374:     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
    4375:     PetscCall(DMPlexGetSupport(dm, face, &cells));
    4376:     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
    4377:     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
    4378:     for (d = 0; d < dim; ++d) {
    4379:       fgeoml[iface].centroid[d] = fg->centroid[d];
    4380:       fgeoml[iface].normal[d]   = fg->normal[d];
    4381:     }
    4382:     voll[iface * 2 + 0] = cgL->volume;
    4383:     voll[iface * 2 + 1] = cgR->volume;
    4384:     ++iface;
    4385:   }
    4386:   *Nface = iface;
    4387:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
    4388:   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
    4389:   PetscFunctionReturn(PETSC_SUCCESS);
    4390: }

    4392: /*@C
    4393:   DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces

    4395:   Input Parameters:
    4396: + dm           - The `DM`
    4397: . fStart       - The first face to include
    4398: . fEnd         - The first face to exclude
    4399: . faceGeometry - A local vector with face geometry
    4400: - cellGeometry - A local vector with cell geometry

    4402:   Output Parameters:
    4403: + Nface - The number of faces with field values
    4404: . fgeom - The face centroid and normals
    4405: - vol   - The cell volumes

    4407:   Level: developer

    4409: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
    4410: @*/
    4411: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom *fgeom[], PetscReal *vol[])
    4412: {
    4413:   PetscFunctionBegin;
    4414:   PetscCall(PetscFree(*fgeom));
    4415:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
    4416:   PetscFunctionReturn(PETSC_SUCCESS);
    4417: }

    4419: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscFEGeomMode mode, PetscFEGeom **geom)
    4420: {
    4421:   char           composeStr[33] = {0};
    4422:   PetscObjectId  id;
    4423:   PetscContainer container;

    4425:   PetscFunctionBegin;
    4426:   PetscCall(PetscObjectGetId((PetscObject)quad, &id));
    4427:   PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
    4428:   PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
    4429:   if (container) {
    4430:     PetscCall(PetscContainerGetPointer(container, geom));
    4431:   } else {
    4432:     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, mode, geom));
    4433:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
    4434:     PetscCall(PetscContainerSetPointer(container, (void *)*geom));
    4435:     PetscCall(PetscContainerSetCtxDestroy(container, PetscContainerCtxDestroy_PetscFEGeom));
    4436:     PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
    4437:     PetscCall(PetscContainerDestroy(&container));
    4438:   }
    4439:   PetscFunctionReturn(PETSC_SUCCESS);
    4440: }

    4442: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
    4443: {
    4444:   PetscFunctionBegin;
    4445:   *geom = NULL;
    4446:   PetscFunctionReturn(PETSC_SUCCESS);
    4447: }

    4449: PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, PetscCtx ctx)
    4450: {
    4451:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
    4452:   const char     *name       = "Residual";
    4453:   DM              dmAux      = NULL;
    4454:   DMLabel         ghostLabel = NULL;
    4455:   PetscDS         prob       = NULL;
    4456:   PetscDS         probAux    = NULL;
    4457:   PetscBool       useFEM     = PETSC_FALSE;
    4458:   PetscBool       isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
    4459:   DMField         coordField = NULL;
    4460:   Vec             locA;
    4461:   PetscScalar    *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
    4462:   IS              chunkIS;
    4463:   const PetscInt *cells;
    4464:   PetscInt        cStart, cEnd, numCells;
    4465:   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
    4466:   PetscInt        maxDegree = PETSC_INT_MAX;
    4467:   PetscFormKey    key;
    4468:   PetscQuadrature affineQuad = NULL, *quads = NULL;
    4469:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

    4471:   PetscFunctionBegin;
    4472:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
    4473:   /* FEM+FVM */
    4474:   /* 1: Get sizes from dm and dmAux */
    4475:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
    4476:   PetscCall(DMGetDS(dm, &prob));
    4477:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    4478:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    4479:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
    4480:   if (locA) {
    4481:     PetscCall(VecGetDM(locA, &dmAux));
    4482:     PetscCall(DMGetDS(dmAux, &probAux));
    4483:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    4484:   }
    4485:   /* 2: Get geometric data */
    4486:   for (f = 0; f < Nf; ++f) {
    4487:     PetscObject  obj;
    4488:     PetscClassId id;
    4489:     PetscBool    fimp;

    4491:     PetscCall(PetscDSGetImplicit(prob, f, &fimp));
    4492:     if (isImplicit != fimp) continue;
    4493:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    4494:     PetscCall(PetscObjectGetClassId(obj, &id));
    4495:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
    4496:     PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented");
    4497:   }
    4498:   if (useFEM) {
    4499:     PetscCall(DMGetCoordinateField(dm, &coordField));
    4500:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    4501:     if (maxDegree <= 1) {
    4502:       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
    4503:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FEGEOM_BASIC, &affineGeom));
    4504:     } else {
    4505:       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
    4506:       for (f = 0; f < Nf; ++f) {
    4507:         PetscObject  obj;
    4508:         PetscClassId id;
    4509:         PetscBool    fimp;

    4511:         PetscCall(PetscDSGetImplicit(prob, f, &fimp));
    4512:         if (isImplicit != fimp) continue;
    4513:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    4514:         PetscCall(PetscObjectGetClassId(obj, &id));
    4515:         if (id == PETSCFE_CLASSID) {
    4516:           PetscFE fe = (PetscFE)obj;

    4518:           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
    4519:           PetscCall(PetscObjectReference((PetscObject)quads[f]));
    4520:           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FEGEOM_BASIC, &geoms[f]));
    4521:         }
    4522:       }
    4523:     }
    4524:   }
    4525:   /* Loop over chunks */
    4526:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    4527:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
    4528:   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
    4529:   numCells      = cEnd - cStart;
    4530:   numChunks     = 1;
    4531:   cellChunkSize = numCells / numChunks;
    4532:   numChunks     = PetscMin(1, numCells);
    4533:   key.label     = NULL;
    4534:   key.value     = 0;
    4535:   key.part      = 0;
    4536:   for (chunk = 0; chunk < numChunks; ++chunk) {
    4537:     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
    4538:     PetscReal       *vol   = NULL;
    4539:     PetscFVFaceGeom *fgeom = NULL;
    4540:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
    4541:     PetscInt         numFaces = 0;

    4543:     /* Extract field coefficients */
    4544:     if (useFEM) {
    4545:       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
    4546:       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
    4547:       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
    4548:       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
    4549:     }
    4550:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
    4551:     /* Loop over fields */
    4552:     for (f = 0; f < Nf; ++f) {
    4553:       PetscObject  obj;
    4554:       PetscClassId id;
    4555:       PetscBool    fimp;
    4556:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

    4558:       key.field = f;
    4559:       PetscCall(PetscDSGetImplicit(prob, f, &fimp));
    4560:       if (isImplicit != fimp) continue;
    4561:       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    4562:       PetscCall(PetscObjectGetClassId(obj, &id));
    4563:       if (id == PETSCFE_CLASSID) {
    4564:         PetscFE         fe        = (PetscFE)obj;
    4565:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
    4566:         PetscFEGeom    *chunkGeom = NULL;
    4567:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
    4568:         PetscInt        Nq, Nb;

    4570:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    4571:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
    4572:         PetscCall(PetscFEGetDimension(fe, &Nb));
    4573:         blockSize = Nb;
    4574:         batchSize = numBlocks * blockSize;
    4575:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    4576:         numChunks = numCells / (numBatches * batchSize);
    4577:         Ne        = numChunks * numBatches * batchSize;
    4578:         Nr        = numCells % (numBatches * batchSize);
    4579:         offset    = numCells - Nr;
    4580:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
    4581:         /*   For FV, I think we use a P0 basis and the cell coefficients (for subdivided cells, we can tweak the basis tabulation to be the indicator function) */
    4582:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
    4583:         PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
    4584:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
    4585:         PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
    4586:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
    4587:       } else if (id == PETSCFV_CLASSID) {
    4588:         PetscFV fv = (PetscFV)obj;

    4590:         Ne = numFaces;
    4591:         /* Riemann solve over faces (need fields at face centroids) */
    4592:         /*   We need to evaluate FE fields at those coordinates */
    4593:         PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
    4594:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
    4595:     }
    4596:     /* Loop over domain */
    4597:     if (useFEM) {
    4598:       /* Add elemVec to locX */
    4599:       for (c = cS; c < cE; ++c) {
    4600:         const PetscInt cell = cells ? cells[c] : c;
    4601:         const PetscInt cind = c - cStart;

    4603:         if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
    4604:         if (ghostLabel) {
    4605:           PetscInt ghostVal;

    4607:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
    4608:           if (ghostVal > 0) continue;
    4609:         }
    4610:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
    4611:       }
    4612:     }
    4613:     /* Handle time derivative */
    4614:     if (locX_t) {
    4615:       PetscScalar *x_t, *fa;

    4617:       PetscCall(VecGetArray(locF, &fa));
    4618:       PetscCall(VecGetArray(locX_t, &x_t));
    4619:       for (f = 0; f < Nf; ++f) {
    4620:         PetscFV      fv;
    4621:         PetscObject  obj;
    4622:         PetscClassId id;
    4623:         PetscInt     pdim, d;

    4625:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    4626:         PetscCall(PetscObjectGetClassId(obj, &id));
    4627:         if (id != PETSCFV_CLASSID) continue;
    4628:         fv = (PetscFV)obj;
    4629:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
    4630:         for (c = cS; c < cE; ++c) {
    4631:           const PetscInt cell = cells ? cells[c] : c;
    4632:           PetscScalar   *u_t, *r;

    4634:           if (ghostLabel) {
    4635:             PetscInt ghostVal;

    4637:             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
    4638:             if (ghostVal > 0) continue;
    4639:           }
    4640:           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
    4641:           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
    4642:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
    4643:         }
    4644:       }
    4645:       PetscCall(VecRestoreArray(locX_t, &x_t));
    4646:       PetscCall(VecRestoreArray(locF, &fa));
    4647:     }
    4648:     if (useFEM) {
    4649:       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
    4650:       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
    4651:     }
    4652:   }
    4653:   if (useFEM) PetscCall(ISDestroy(&chunkIS));
    4654:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    4655:   /* TODO Could include boundary residual here (see DMPlexComputeResidualByKey) */
    4656:   if (useFEM) {
    4657:     if (maxDegree <= 1) {
    4658:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
    4659:       PetscCall(PetscQuadratureDestroy(&affineQuad));
    4660:     } else {
    4661:       for (f = 0; f < Nf; ++f) {
    4662:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
    4663:         PetscCall(PetscQuadratureDestroy(&quads[f]));
    4664:       }
    4665:       PetscCall(PetscFree2(quads, geoms));
    4666:     }
    4667:   }
    4668:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
    4669:   PetscFunctionReturn(PETSC_SUCCESS);
    4670: }

    4672: /*
    4673:   We always assemble JacP, and if the matrix is different from Jac and two different sets of point functions are provided, we also assemble Jac

    4675:   X   - The local solution vector
    4676:   X_t - The local solution time derivative vector, or NULL
    4677: */
    4678: PetscErrorCode DMPlexComputeJacobian_Patch_Internal(DM dm, PetscSection section, PetscSection globalSection, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, PetscCtx ctx)
    4679: {
    4680:   DM_Plex        *mesh = (DM_Plex *)dm->data;
    4681:   const char     *name = "Jacobian", *nameP = "JacobianPre";
    4682:   DM              dmAux = NULL;
    4683:   PetscDS         prob, probAux = NULL;
    4684:   PetscSection    sectionAux = NULL;
    4685:   Vec             A;
    4686:   DMField         coordField;
    4687:   PetscFEGeom    *cgeomFEM;
    4688:   PetscQuadrature qGeom = NULL;
    4689:   Mat             J = Jac, JP = JacP;
    4690:   PetscScalar    *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
    4691:   PetscBool       hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
    4692:   const PetscInt *cells;
    4693:   PetscFormKey    key;
    4694:   PetscInt        Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;

    4696:   PetscFunctionBegin;
    4697:   PetscCall(ISGetLocalSize(cellIS, &numCells));
    4698:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    4699:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    4700:   PetscCall(DMGetDS(dm, &prob));
    4701:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
    4702:   if (A) {
    4703:     PetscCall(VecGetDM(A, &dmAux));
    4704:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
    4705:     PetscCall(DMGetDS(dmAux, &probAux));
    4706:   }
    4707:   /* Get flags */
    4708:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    4709:   PetscCall(DMGetWorkArray(dm, Nf, MPI_C_BOOL, &isFE));
    4710:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
    4711:     PetscObject  disc;
    4712:     PetscClassId id;
    4713:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
    4714:     PetscCall(PetscObjectGetClassId(disc, &id));
    4715:     if (id == PETSCFE_CLASSID) {
    4716:       isFE[fieldI] = PETSC_TRUE;
    4717:     } else if (id == PETSCFV_CLASSID) {
    4718:       hasFV        = PETSC_TRUE;
    4719:       isFE[fieldI] = PETSC_FALSE;
    4720:     }
    4721:   }
    4722:   PetscCall(PetscDSHasJacobian(prob, &hasJac));
    4723:   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
    4724:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
    4725:   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
    4726:   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
    4727:   if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
    4728:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    4729:   if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    4730:   /* Compute batch sizes */
    4731:   if (isFE[0]) {
    4732:     PetscFE         fe;
    4733:     PetscQuadrature q;
    4734:     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;

    4736:     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
    4737:     PetscCall(PetscFEGetQuadrature(fe, &q));
    4738:     PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
    4739:     PetscCall(PetscFEGetDimension(fe, &Nb));
    4740:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    4741:     blockSize = Nb * numQuadPoints;
    4742:     batchSize = numBlocks * blockSize;
    4743:     chunkSize = numBatches * batchSize;
    4744:     numChunks = numCells / chunkSize + numCells % chunkSize;
    4745:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    4746:   } else {
    4747:     chunkSize = numCells;
    4748:     numChunks = 1;
    4749:   }
    4750:   /* Get work space */
    4751:   wsz = (((X ? 1 : 0) + (X_t ? 1 : 0)) * totDim + (dmAux ? 1 : 0) * totDimAux + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize;
    4752:   PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
    4753:   PetscCall(PetscArrayzero(work, wsz));
    4754:   off      = 0;
    4755:   u        = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
    4756:   u_t      = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
    4757:   a        = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
    4758:   elemMat  = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
    4759:   elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
    4760:   elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
    4761:   PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
    4762:   /* Setup geometry */
    4763:   PetscCall(DMGetCoordinateField(dm, &coordField));
    4764:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    4765:   if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
    4766:   if (!qGeom) {
    4767:     PetscFE fe;

    4769:     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
    4770:     PetscCall(PetscFEGetQuadrature(fe, &qGeom));
    4771:     PetscCall(PetscObjectReference((PetscObject)qGeom));
    4772:   }
    4773:   PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
    4774:   /* Compute volume integrals */
    4775:   if (assembleJac) PetscCall(MatZeroEntries(J));
    4776:   PetscCall(MatZeroEntries(JP));
    4777:   key.label = NULL;
    4778:   key.value = 0;
    4779:   key.part  = 0;
    4780:   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
    4781:     const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
    4782:     PetscInt       c;

    4784:     /* Extract values */
    4785:     for (c = 0; c < Ncell; ++c) {
    4786:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
    4787:       PetscScalar   *x = NULL, *x_t = NULL;
    4788:       PetscInt       i;

    4790:       if (X) {
    4791:         PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
    4792:         for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
    4793:         PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
    4794:       }
    4795:       if (X_t) {
    4796:         PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
    4797:         for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
    4798:         PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
    4799:       }
    4800:       if (dmAux) {
    4801:         PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
    4802:         for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
    4803:         PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
    4804:       }
    4805:     }
    4806:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
    4807:       PetscFE fe;
    4808:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
    4809:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    4810:         key.field = fieldI * Nf + fieldJ;
    4811:         if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
    4812:         if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
    4813:         if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
    4814:       }
    4815:       /* For finite volume, add the identity */
    4816:       if (!isFE[fieldI]) {
    4817:         PetscFV  fv;
    4818:         PetscInt eOffset = 0, Nc, fc, foff;

    4820:         PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
    4821:         PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
    4822:         PetscCall(PetscFVGetNumComponents(fv, &Nc));
    4823:         for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
    4824:           for (fc = 0; fc < Nc; ++fc) {
    4825:             const PetscInt i = foff + fc;
    4826:             if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
    4827:             if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
    4828:           }
    4829:         }
    4830:       }
    4831:     }
    4832:     /*   Add contribution from X_t */
    4833:     if (hasDyn) {
    4834:       for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
    4835:     }
    4836:     /* Insert values into matrix */
    4837:     for (c = 0; c < Ncell; ++c) {
    4838:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
    4839:       if (mesh->printFEM > 1) {
    4840:         if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]));
    4841:         if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]));
    4842:       }
    4843:       if (assembleJac) PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
    4844:       PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
    4845:     }
    4846:   }
    4847:   /* Cleanup */
    4848:   PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
    4849:   PetscCall(PetscQuadratureDestroy(&qGeom));
    4850:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
    4851:   PetscCall(DMRestoreWorkArray(dm, Nf, MPI_C_BOOL, &isFE));
    4852:   PetscCall(DMRestoreWorkArray(dm, ((1 + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize, MPIU_SCALAR, &work));
    4853:   /* Compute boundary integrals */
    4854:   /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
    4855:   /* Assemble matrix */
    4856:   if (assembleJac) {
    4857:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
    4858:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
    4859:   }
    4860:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
    4861:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
    4862:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    4863:   PetscFunctionReturn(PETSC_SUCCESS);
    4864: }

    4866: /* FEM Assembly Function */

    4868: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
    4869: {
    4870:   PetscBool isPlex;

    4872:   PetscFunctionBegin;
    4873:   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
    4874:   if (isPlex) {
    4875:     *plex = dm;
    4876:     PetscCall(PetscObjectReference((PetscObject)dm));
    4877:   } else {
    4878:     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
    4879:     if (!*plex) {
    4880:       PetscCall(DMConvert(dm, DMPLEX, plex));
    4881:       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
    4882:     } else {
    4883:       PetscCall(PetscObjectReference((PetscObject)*plex));
    4884:     }
    4885:     if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
    4886:   }
    4887:   PetscFunctionReturn(PETSC_SUCCESS);
    4888: }

    4890: /*@
    4891:   DMPlexGetGeometryFVM - Return precomputed geometric data

    4893:   Collective

    4895:   Input Parameter:
    4896: . dm - The `DM`

    4898:   Output Parameters:
    4899: + facegeom  - The values precomputed from face geometry
    4900: . cellgeom  - The values precomputed from cell geometry
    4901: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell, or `NULL` if not needed

    4903:   Level: developer

    4905: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
    4906: @*/
    4907: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PeOp PetscReal *minRadius)
    4908: {
    4909:   DM plex;

    4911:   PetscFunctionBegin;
    4913:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
    4914:   PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
    4915:   if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
    4916:   PetscCall(DMDestroy(&plex));
    4917:   PetscFunctionReturn(PETSC_SUCCESS);
    4918: }

    4920: /*@
    4921:   DMPlexGetGradientDM - Return gradient data layout

    4923:   Collective

    4925:   Input Parameters:
    4926: + dm - The `DM`
    4927: - fv - The `PetscFV`

    4929:   Output Parameter:
    4930: . dmGrad - The layout for gradient values

    4932:   Level: developer

    4934: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
    4935: @*/
    4936: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
    4937: {
    4938:   DM        plex;
    4939:   PetscBool computeGradients;

    4941:   PetscFunctionBegin;
    4944:   PetscAssertPointer(dmGrad, 3);
    4945:   PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
    4946:   if (!computeGradients) {
    4947:     *dmGrad = NULL;
    4948:     PetscFunctionReturn(PETSC_SUCCESS);
    4949:   }
    4950:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
    4951:   PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
    4952:   PetscCall(DMDestroy(&plex));
    4953:   PetscFunctionReturn(PETSC_SUCCESS);
    4954: }

    4956: /*@
    4957:   DMPlexComputeBdResidualSingleByKey - Compute the local boundary residual for terms matching the input key

    4959:   Not collective

    4961:   Input Parameters:
    4962: + dm         - The output `DM`
    4963: . wf         - The `PetscWeakForm` holding forms on this boundary
    4964: . key        - The `PetscFormKey` indicating what should be integrated
    4965: . facetIS    - The `IS` giving a set of faces to integrate over
    4966: . locX       - The local solution
    4967: . locX_t     - The time derivative of the local solution, or `NULL` for time-independent problems
    4968: . t          - The time
    4969: - coordField - The `DMField` object with coordinates for these faces

    4971:   Output Parameter:
    4972: . locF - The local residual

    4974:   Level: developer

    4976: .seealso: `DMPlexComputeBdResidualSingle()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
    4977: @*/
    4978: PetscErrorCode DMPlexComputeBdResidualSingleByKey(DM dm, PetscWeakForm wf, PetscFormKey key, IS facetIS, Vec locX, Vec locX_t, PetscReal t, DMField coordField, Vec locF)
    4979: {
    4980:   DM_Plex        *mesh = (DM_Plex *)dm->data;
    4981:   DM              plex = NULL, plexA = NULL;
    4982:   const char     *name = "BdResidual";
    4983:   DMEnclosureType encAux;
    4984:   PetscDS         prob, probAux       = NULL;
    4985:   PetscSection    section, sectionAux = NULL;
    4986:   Vec             locA = NULL;
    4987:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
    4988:   PetscInt        totDim, totDimAux = 0;

    4990:   PetscFunctionBegin;
    4991:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    4992:   PetscCall(DMGetLocalSection(dm, &section));
    4993:   PetscCall(DMGetDS(dm, &prob));
    4994:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    4995:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
    4996:   if (locA) {
    4997:     DM dmAux;

    4999:     PetscCall(VecGetDM(locA, &dmAux));
    5000:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    5001:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
    5002:     PetscCall(DMGetDS(plexA, &probAux));
    5003:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    5004:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
    5005:   }
    5006:   {
    5007:     PetscFEGeom    *fgeom;
    5008:     PetscInt        maxDegree;
    5009:     PetscQuadrature qGeom = NULL;
    5010:     IS              pointIS;
    5011:     const PetscInt *points;
    5012:     PetscInt        numFaces, face, Nq;

    5014:     PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
    5015:     if (!pointIS) goto end; /* No points with that id on this process */
    5016:     {
    5017:       IS isectIS;

    5019:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
    5020:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
    5021:       PetscCall(ISDestroy(&pointIS));
    5022:       pointIS = isectIS;
    5023:     }
    5024:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
    5025:     PetscCall(ISGetIndices(pointIS, &points));
    5026:     PetscCall(PetscMalloc4(numFaces * totDim, &u, (locX_t ? (size_t)numFaces * totDim : 0), &u_t, numFaces * totDim, &elemVec, (locA ? (size_t)numFaces * totDimAux : 0), &a));
    5027:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
    5028:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
    5029:     if (!qGeom) {
    5030:       PetscFE fe;

    5032:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
    5033:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
    5034:       PetscCall(PetscObjectReference((PetscObject)qGeom));
    5035:     }
    5036:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
    5037:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
    5038:     for (face = 0; face < numFaces; ++face) {
    5039:       const PetscInt point = points[face], *support;
    5040:       PetscScalar   *x     = NULL;
    5041:       PetscInt       i;

    5043:       PetscCall(DMPlexGetSupport(dm, point, &support));
    5044:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
    5045:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
    5046:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
    5047:       if (locX_t) {
    5048:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
    5049:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
    5050:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
    5051:       }
    5052:       if (locA) {
    5053:         PetscInt subp;

    5055:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
    5056:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
    5057:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
    5058:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
    5059:       }
    5060:     }
    5061:     PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
    5062:     {
    5063:       PetscFE      fe;
    5064:       PetscInt     Nb;
    5065:       PetscFEGeom *chunkGeom = NULL;
    5066:       /* Conforming batches */
    5067:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    5068:       /* Remainder */
    5069:       PetscInt Nr, offset;

    5071:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
    5072:       PetscCall(PetscFEGetDimension(fe, &Nb));
    5073:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    5074:       /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
    5075:       blockSize = Nb;
    5076:       batchSize = numBlocks * blockSize;
    5077:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    5078:       numChunks = numFaces / (numBatches * batchSize);
    5079:       Ne        = numChunks * numBatches * batchSize;
    5080:       Nr        = numFaces % (numBatches * batchSize);
    5081:       offset    = numFaces - Nr;
    5082:       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
    5083:       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
    5084:       PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
    5085:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
    5086:       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
    5087:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
    5088:     }
    5089:     for (face = 0; face < numFaces; ++face) {
    5090:       const PetscInt point = points[face], *support;

    5092:       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, name, totDim, &elemVec[face * totDim]));
    5093:       PetscCall(DMPlexGetSupport(plex, point, &support));
    5094:       PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES));
    5095:     }
    5096:     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
    5097:     PetscCall(PetscQuadratureDestroy(&qGeom));
    5098:     PetscCall(ISRestoreIndices(pointIS, &points));
    5099:     PetscCall(ISDestroy(&pointIS));
    5100:     PetscCall(PetscFree4(u, u_t, elemVec, a));
    5101:   }
    5102: end:
    5103:   if (mesh->printFEM) {
    5104:     PetscSection s;
    5105:     Vec          locFbc;
    5106:     PetscInt     pStart, pEnd, maxDof;
    5107:     PetscScalar *zeroes;

    5109:     PetscCall(DMGetLocalSection(dm, &s));
    5110:     PetscCall(VecDuplicate(locF, &locFbc));
    5111:     PetscCall(VecCopy(locF, locFbc));
    5112:     PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
    5113:     PetscCall(PetscSectionGetMaxDof(s, &maxDof));
    5114:     PetscCall(PetscCalloc1(maxDof, &zeroes));
    5115:     for (PetscInt p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, s, p, zeroes, INSERT_BC_VALUES));
    5116:     PetscCall(PetscFree(zeroes));
    5117:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
    5118:     PetscCall(VecDestroy(&locFbc));
    5119:   }
    5120:   PetscCall(DMDestroy(&plex));
    5121:   PetscCall(DMDestroy(&plexA));
    5122:   PetscFunctionReturn(PETSC_SUCCESS);
    5123: }

    5125: /*@
    5126:   DMPlexComputeBdResidualSingle - Compute the local boundary residual

    5128:   Not collective

    5130:   Input Parameters:
    5131: + dm     - The output `DM`
    5132: . wf     - The `PetscWeakForm` holding forms on this boundary
    5133: . key    - The `PetscFormKey` indicating what should be integrated
    5134: . locX   - The local solution
    5135: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
    5136: - t      - The time

    5138:   Output Parameter:
    5139: . locF - The local residual

    5141:   Level: developer

    5143: .seealso: `DMPlexComputeBdResidualSingleByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
    5144: @*/
    5145: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, PetscReal t, Vec locF)
    5146: {
    5147:   DMField  coordField;
    5148:   DMLabel  depthLabel;
    5149:   IS       facetIS;
    5150:   PetscInt dim;

    5152:   PetscFunctionBegin;
    5153:   PetscCall(DMGetDimension(dm, &dim));
    5154:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    5155:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
    5156:   PetscCall(DMGetCoordinateField(dm, &coordField));
    5157:   PetscCall(DMPlexComputeBdResidualSingleByKey(dm, wf, key, facetIS, locX, locX_t, t, coordField, locF));
    5158:   PetscCall(ISDestroy(&facetIS));
    5159:   PetscFunctionReturn(PETSC_SUCCESS);
    5160: }

    5162: static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, PetscCtx ctx)
    5163: {
    5164:   PetscDS  prob;
    5165:   PetscInt numBd, bd;
    5166:   DMField  coordField = NULL;
    5167:   IS       facetIS    = NULL;
    5168:   DMLabel  depthLabel;
    5169:   PetscInt dim;

    5171:   PetscFunctionBegin;
    5172:   PetscCall(DMGetDS(dm, &prob));
    5173:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    5174:   PetscCall(DMGetDimension(dm, &dim));
    5175:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
    5176:   /* Filter out ghost facets (SF leaves) so that boundary residual contributions
    5177:      from shared facets are only assembled on the owning rank. Without this,
    5178:      internal boundary natural BCs at partition junctions get double-counted
    5179:      because LocalToGlobal with ADD_VALUES sums contributions from all ranks. */
    5180:   if (facetIS) {
    5181:     PetscSF         sf;
    5182:     PetscInt        nleaves;
    5183:     const PetscInt *leaves;

    5185:     PetscCall(DMGetPointSF(dm, &sf));
    5186:     PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
    5187:     if (nleaves > 0 && leaves) {
    5188:       IS leafIS, ownedFacetIS;

    5190:       PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nleaves, leaves, PETSC_USE_POINTER, &leafIS));
    5191:       PetscCall(ISDifference(facetIS, leafIS, &ownedFacetIS));
    5192:       PetscCall(ISDestroy(&leafIS));
    5193:       PetscCall(ISDestroy(&facetIS));
    5194:       facetIS = ownedFacetIS;
    5195:     }
    5196:   }
    5197:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
    5198:   for (bd = 0; bd < numBd; ++bd) {
    5199:     PetscWeakForm           wf;
    5200:     DMBoundaryConditionType type;
    5201:     DMLabel                 label;
    5202:     const PetscInt         *values;
    5203:     PetscInt                field, numValues, v;
    5204:     PetscObject             obj;
    5205:     PetscClassId            id;
    5206:     PetscFormKey            key;

    5208:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
    5209:     if (type & DM_BC_ESSENTIAL) continue;
    5210:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
    5211:     PetscCall(PetscObjectGetClassId(obj, &id));
    5212:     if (id != PETSCFE_CLASSID) continue;
    5213:     if (!facetIS) {
    5214:       DMLabel  depthLabel;
    5215:       PetscInt dim;

    5217:       PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    5218:       PetscCall(DMGetDimension(dm, &dim));
    5219:       PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
    5220:     }
    5221:     PetscCall(DMGetCoordinateField(dm, &coordField));
    5222:     for (v = 0; v < numValues; ++v) {
    5223:       key.label = label;
    5224:       key.value = values[v];
    5225:       key.field = field;
    5226:       key.part  = 0;
    5227:       PetscCall(DMPlexComputeBdResidualSingleByKey(dm, wf, key, facetIS, locX, locX_t, t, coordField, locF));
    5228:     }
    5229:   }
    5230:   PetscCall(ISDestroy(&facetIS));
    5231:   PetscFunctionReturn(PETSC_SUCCESS);
    5232: }

    5234: /*@
    5235:   DMPlexComputeResidualByKey - Compute the local residual for terms matching the input key

    5237:   Collective

    5239:   Input Parameters:
    5240: + dm     - The output `DM`
    5241: . key    - The `PetscFormKey` indicating what should be integrated
    5242: . cellIS - The `IS` giving a set of cells to integrate over
    5243: . time   - The time, or `PETSC_MIN_REAL` to include implicit terms in a time-independent problems
    5244: . locX   - The local solution
    5245: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
    5246: . t      - The time
    5247: - ctx    - An optional application context, passed to the pointwise functions

    5249:   Output Parameter:
    5250: . locF - The local residual

    5252:   Level: developer

    5254: .seealso: `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
    5255: @*/
    5256: PetscErrorCode DMPlexComputeResidualByKey(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, PetscCtx ctx)
    5257: {
    5258:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
    5259:   const char     *name       = "Residual";
    5260:   DM              dmAux      = NULL;
    5261:   DM              dmGrad     = NULL;
    5262:   DMLabel         ghostLabel = NULL;
    5263:   PetscDS         ds         = NULL;
    5264:   PetscDS         dsAux      = NULL;
    5265:   PetscSection    section    = NULL;
    5266:   PetscBool       useFEM     = PETSC_FALSE;
    5267:   PetscBool       useFVM     = PETSC_FALSE;
    5268:   PetscBool       isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
    5269:   PetscFV         fvm        = NULL;
    5270:   DMField         coordField = NULL;
    5271:   Vec             locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
    5272:   PetscScalar    *u = NULL, *u_t, *a, *uL, *uR;
    5273:   IS              chunkIS;
    5274:   const PetscInt *cells;
    5275:   PetscInt        cStart, cEnd, numCells;
    5276:   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
    5277:   PetscInt        maxDegree  = PETSC_INT_MAX;
    5278:   PetscQuadrature affineQuad = NULL, *quads = NULL;
    5279:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

    5281:   PetscFunctionBegin;
    5282:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
    5283:   if (!cellIS) goto end;
    5284:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    5285:   if (cStart >= cEnd) goto end;
    5286:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
    5287:   /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
    5288:   /* FEM+FVM */
    5289:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
    5290:   /* 1: Get sizes from dm and dmAux */
    5291:   PetscCall(DMGetLocalSection(dm, &section));
    5292:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
    5293:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL));
    5294:   PetscCall(PetscDSGetNumFields(ds, &Nf));
    5295:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
    5296:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
    5297:   if (locA) {
    5298:     PetscInt subcell;
    5299:     PetscCall(VecGetDM(locA, &dmAux));
    5300:     PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
    5301:     PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL));
    5302:     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
    5303:   }
    5304:   /* 2: Get geometric data */
    5305:   for (f = 0; f < Nf; ++f) {
    5306:     PetscObject  obj;
    5307:     PetscClassId id;
    5308:     PetscBool    fimp;

    5310:     PetscCall(PetscDSGetImplicit(ds, f, &fimp));
    5311:     if (isImplicit != fimp) continue;
    5312:     PetscCall(PetscDSGetDiscretization(ds, f, &obj));
    5313:     PetscCall(PetscObjectGetClassId(obj, &id));
    5314:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
    5315:     if (id == PETSCFV_CLASSID) {
    5316:       useFVM = PETSC_TRUE;
    5317:       fvm    = (PetscFV)obj;
    5318:     }
    5319:   }
    5320:   if (useFEM) {
    5321:     PetscCall(DMGetCoordinateField(dm, &coordField));
    5322:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    5323:     if (maxDegree <= 1) {
    5324:       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
    5325:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FEGEOM_BASIC, &affineGeom));
    5326:     } else {
    5327:       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
    5328:       for (f = 0; f < Nf; ++f) {
    5329:         PetscObject  obj;
    5330:         PetscClassId id;
    5331:         PetscBool    fimp;

    5333:         PetscCall(PetscDSGetImplicit(ds, f, &fimp));
    5334:         if (isImplicit != fimp) continue;
    5335:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
    5336:         PetscCall(PetscObjectGetClassId(obj, &id));
    5337:         if (id == PETSCFE_CLASSID) {
    5338:           PetscFE fe = (PetscFE)obj;

    5340:           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
    5341:           PetscCall(PetscObjectReference((PetscObject)quads[f]));
    5342:           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FEGEOM_BASIC, &geoms[f]));
    5343:         }
    5344:       }
    5345:     }
    5346:   }
    5347:   // Handle non-essential (e.g. outflow) boundary values
    5348:   if (useFVM) {
    5349:     PetscCall(DMPlexInsertBoundaryValuesFVM(dm, fvm, locX, time, &locGrad));
    5350:     PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
    5351:     PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
    5352:   }
    5353:   /* Loop over chunks */
    5354:   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
    5355:   numCells      = cEnd - cStart;
    5356:   numChunks     = 1;
    5357:   cellChunkSize = numCells / numChunks;
    5358:   faceChunkSize = (fEnd - fStart) / numChunks;
    5359:   numChunks     = PetscMin(1, numCells);
    5360:   for (chunk = 0; chunk < numChunks; ++chunk) {
    5361:     PetscScalar     *elemVec, *fluxL, *fluxR;
    5362:     PetscReal       *vol;
    5363:     PetscFVFaceGeom *fgeom;
    5364:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
    5365:     PetscInt         fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;

    5367:     /* Extract field coefficients */
    5368:     if (useFEM) {
    5369:       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
    5370:       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
    5371:       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
    5372:       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
    5373:     }
    5374:     if (useFVM) {
    5375:       PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
    5376:       PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
    5377:       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
    5378:       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
    5379:       PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
    5380:       PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
    5381:     }
    5382:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
    5383:     /* Loop over fields */
    5384:     for (f = 0; f < Nf; ++f) {
    5385:       PetscObject  obj;
    5386:       PetscClassId id;
    5387:       PetscBool    fimp;
    5388:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

    5390:       key.field = f;
    5391:       PetscCall(PetscDSGetImplicit(ds, f, &fimp));
    5392:       if (isImplicit != fimp) continue;
    5393:       PetscCall(PetscDSGetDiscretization(ds, f, &obj));
    5394:       PetscCall(PetscObjectGetClassId(obj, &id));
    5395:       if (id == PETSCFE_CLASSID) {
    5396:         PetscFE         fe        = (PetscFE)obj;
    5397:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
    5398:         PetscFEGeom    *chunkGeom = NULL;
    5399:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
    5400:         PetscInt        Nq, Nb;

    5402:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    5403:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
    5404:         PetscCall(PetscFEGetDimension(fe, &Nb));
    5405:         blockSize = Nb;
    5406:         batchSize = numBlocks * blockSize;
    5407:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    5408:         numChunks = numCells / (numBatches * batchSize);
    5409:         Ne        = numChunks * numBatches * batchSize;
    5410:         Nr        = numCells % (numBatches * batchSize);
    5411:         offset    = numCells - Nr;
    5412:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
    5413:         /*   For FV, I think we use a P0 basis and the cell coefficients (for subdivided cells, we can tweak the basis tabulation to be the indicator function) */
    5414:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
    5415:         PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
    5416:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
    5417:         PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
    5418:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
    5419:       } else if (id == PETSCFV_CLASSID) {
    5420:         PetscFV fv = (PetscFV)obj;

    5422:         Ne = numFaces;
    5423:         /* Riemann solve over faces (need fields at face centroids) */
    5424:         /*   We need to evaluate FE fields at those coordinates */
    5425:         PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
    5426:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
    5427:     }
    5428:     /* Loop over domain */
    5429:     if (useFEM) {
    5430:       /* Add elemVec to locX */
    5431:       for (c = cS; c < cE; ++c) {
    5432:         const PetscInt cell = cells ? cells[c] : c;
    5433:         const PetscInt cind = c - cStart;

    5435:         if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim]));
    5436:         if (ghostLabel) {
    5437:           PetscInt ghostVal;

    5439:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
    5440:           if (ghostVal > 0) continue;
    5441:         }
    5442:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
    5443:       }
    5444:     }
    5445:     if (useFVM) {
    5446:       PetscScalar *fa;
    5447:       PetscInt     iface;

    5449:       PetscCall(VecGetArray(locF, &fa));
    5450:       for (f = 0; f < Nf; ++f) {
    5451:         PetscFV      fv;
    5452:         PetscObject  obj;
    5453:         PetscClassId id;
    5454:         PetscInt     cdim, foff, pdim;

    5456:         PetscCall(DMGetCoordinateDim(dm, &cdim));
    5457:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
    5458:         PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
    5459:         PetscCall(PetscObjectGetClassId(obj, &id));
    5460:         if (id != PETSCFV_CLASSID) continue;
    5461:         fv = (PetscFV)obj;
    5462:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
    5463:         /* Accumulate fluxes to cells */
    5464:         for (face = fS, iface = 0; face < fE; ++face) {
    5465:           const PetscInt *scells;
    5466:           PetscScalar    *fL = NULL, *fR = NULL;
    5467:           PetscInt        ghost, d, nsupp, nchild;

    5469:           PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
    5470:           PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
    5471:           PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
    5472:           if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
    5473:           PetscCall(DMPlexGetSupport(dm, face, &scells));
    5474:           PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
    5475:           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
    5476:           PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
    5477:           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
    5478:           if (mesh->printFVM > 1) {
    5479:             PetscCall(DMPrintCellVectorReal(face, "Residual: normal", cdim, fgeom[iface].normal));
    5480:             PetscCall(DMPrintCellVector(face, "Residual: left state", pdim, &uL[iface * totDim + foff]));
    5481:             PetscCall(DMPrintCellVector(face, "Residual: right state", pdim, &uR[iface * totDim + foff]));
    5482:             PetscCall(DMPrintCellVector(face, "Residual: left flux", pdim, &fluxL[iface * totDim + foff]));
    5483:             PetscCall(DMPrintCellVector(face, "Residual: right flux", pdim, &fluxR[iface * totDim + foff]));
    5484:           }
    5485:           for (d = 0; d < pdim; ++d) {
    5486:             if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
    5487:             if (fR) fR[d] += fluxR[iface * totDim + foff + d];
    5488:           }
    5489:           ++iface;
    5490:         }
    5491:       }
    5492:       PetscCall(VecRestoreArray(locF, &fa));
    5493:     }
    5494:     /* Handle time derivative */
    5495:     if (locX_t) {
    5496:       PetscScalar *x_t, *fa;

    5498:       PetscCall(VecGetArray(locF, &fa));
    5499:       PetscCall(VecGetArray(locX_t, &x_t));
    5500:       for (f = 0; f < Nf; ++f) {
    5501:         PetscFV      fv;
    5502:         PetscObject  obj;
    5503:         PetscClassId id;
    5504:         PetscInt     pdim, d;

    5506:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
    5507:         PetscCall(PetscObjectGetClassId(obj, &id));
    5508:         if (id != PETSCFV_CLASSID) continue;
    5509:         fv = (PetscFV)obj;
    5510:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
    5511:         for (c = cS; c < cE; ++c) {
    5512:           const PetscInt cell = cells ? cells[c] : c;
    5513:           PetscScalar   *u_t, *r;

    5515:           if (ghostLabel) {
    5516:             PetscInt ghostVal;

    5518:             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
    5519:             if (ghostVal > 0) continue;
    5520:           }
    5521:           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
    5522:           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
    5523:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
    5524:         }
    5525:       }
    5526:       PetscCall(VecRestoreArray(locX_t, &x_t));
    5527:       PetscCall(VecRestoreArray(locF, &fa));
    5528:     }
    5529:     if (useFEM) {
    5530:       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
    5531:       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
    5532:     }
    5533:     if (useFVM) {
    5534:       PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
    5535:       PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
    5536:       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
    5537:       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
    5538:       if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
    5539:     }
    5540:   }
    5541:   if (useFEM) PetscCall(ISDestroy(&chunkIS));
    5542:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));

    5544:   if (useFEM) {
    5545:     PetscCall(DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, ctx));

    5547:     if (maxDegree <= 1) {
    5548:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
    5549:       PetscCall(PetscQuadratureDestroy(&affineQuad));
    5550:     } else {
    5551:       for (f = 0; f < Nf; ++f) {
    5552:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
    5553:         PetscCall(PetscQuadratureDestroy(&quads[f]));
    5554:       }
    5555:       PetscCall(PetscFree2(quads, geoms));
    5556:     }
    5557:   }

    5559:   /* FEM */
    5560:   /* 1: Get sizes from dm and dmAux */
    5561:   /* 2: Get geometric data */
    5562:   /* 3: Handle boundary values */
    5563:   /* 4: Loop over domain */
    5564:   /*   Extract coefficients */
    5565:   /* Loop over fields */
    5566:   /*   Set tiling for FE*/
    5567:   /*   Integrate FE residual to get elemVec */
    5568:   /*     Loop over subdomain */
    5569:   /*       Loop over quad points */
    5570:   /*         Transform coords to real space */
    5571:   /*         Evaluate field and aux fields at point */
    5572:   /*         Evaluate residual at point */
    5573:   /*         Transform residual to real space */
    5574:   /*       Add residual to elemVec */
    5575:   /* Loop over domain */
    5576:   /*   Add elemVec to locX */

    5578:   /* FVM */
    5579:   /* Get geometric data */
    5580:   /* If using gradients */
    5581:   /*   Compute gradient data */
    5582:   /*   Loop over domain faces */
    5583:   /*     Count computational faces */
    5584:   /*     Reconstruct cell gradient */
    5585:   /*   Loop over domain cells */
    5586:   /*     Limit cell gradients */
    5587:   /* Handle boundary values */
    5588:   /* Loop over domain faces */
    5589:   /*   Read out field, centroid, normal, volume for each side of face */
    5590:   /* Riemann solve over faces */
    5591:   /* Loop over domain faces */
    5592:   /*   Accumulate fluxes to cells */
    5593:   /* TODO Change printFEM to printDisc here */
    5594:   if (mesh->printFEM) {
    5595:     Vec          locFbc;
    5596:     PetscInt     pStart, pEnd, p, maxDof;
    5597:     PetscScalar *zeroes;

    5599:     PetscCall(VecDuplicate(locF, &locFbc));
    5600:     PetscCall(VecCopy(locF, locFbc));
    5601:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
    5602:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
    5603:     PetscCall(PetscCalloc1(maxDof, &zeroes));
    5604:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
    5605:     PetscCall(PetscFree(zeroes));
    5606:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
    5607:     PetscCall(VecDestroy(&locFbc));
    5608:   }
    5609: end:
    5610:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
    5611:   PetscFunctionReturn(PETSC_SUCCESS);
    5612: }

    5614: /*@
    5615:   DMPlexComputeResidualHybridByKey - Compute the local residual over hybrid cells for terms matching the input key

    5617:   Collective

    5619:   Input Parameters:
    5620: + dm     - The output `DM`
    5621: . key    - The `PetscFormKey` array (left cell, right cell, cohesive cell) indicating what should be integrated
    5622: . cellIS - The `IS` give a set of cells to integrate over
    5623: . time   - The time, or `PETSC_MIN_REAL` to include implicit terms in a time-independent problems
    5624: . locX   - The local solution
    5625: . locX_t - The time derivative of the local solution, or `NULL` for time-independent problems
    5626: . t      - The time
    5627: - ctx    - An optional application context, passed to the pointwise functions

    5629:   Output Parameter:
    5630: . locF - The local residual

    5632:   Level: developer

    5634: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
    5635: @*/
    5636: PetscErrorCode DMPlexComputeResidualHybridByKey(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, PetscCtx ctx)
    5637: {
    5638:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
    5639:   const char     *name       = "Hybrid Residual";
    5640:   DM              dmAux[3]   = {NULL, NULL, NULL};
    5641:   DMLabel         ghostLabel = NULL;
    5642:   PetscDS         ds         = NULL;
    5643:   PetscDS         dsIn       = NULL;
    5644:   PetscDS         dsAux[3]   = {NULL, NULL, NULL};
    5645:   Vec             locA[3]    = {NULL, NULL, NULL};
    5646:   DM              dmScale[3] = {NULL, NULL, NULL};
    5647:   PetscDS         dsScale[3] = {NULL, NULL, NULL};
    5648:   Vec             locS[3]    = {NULL, NULL, NULL};
    5649:   PetscSection    section    = NULL;
    5650:   DMField         coordField = NULL;
    5651:   PetscScalar    *a[3]       = {NULL, NULL, NULL};
    5652:   PetscScalar    *s[3]       = {NULL, NULL, NULL};
    5653:   PetscScalar    *u          = NULL, *u_t;
    5654:   PetscScalar    *elemVecNeg, *elemVecPos, *elemVecCoh;
    5655:   IS              chunkISF, chunkISN;
    5656:   const PetscInt *cells;
    5657:   PetscInt       *faces, *neighbors;
    5658:   PetscInt        cStart, cEnd, numCells;
    5659:   PetscInt        Nf, f, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
    5660:   PetscInt        maxDegree   = PETSC_INT_MAX;
    5661:   PetscQuadrature affineQuadF = NULL, *quadsF = NULL;
    5662:   PetscFEGeom    *affineGeomF = NULL, **geomsF = NULL;
    5663:   PetscQuadrature affineQuadN = NULL, *quadsN = NULL;
    5664:   PetscFEGeom    *affineGeomN = NULL, **geomsN = NULL;

    5666:   PetscFunctionBegin;
    5667:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
    5668:   if (!cellIS) goto end;
    5669:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    5670:   PetscCall(ISGetLocalSize(cellIS, &numCells));
    5671:   if (cStart >= cEnd) goto end;
    5672:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
    5673:     const char *name;
    5674:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
    5675:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Form keys for each side of a cohesive surface must be different (%s, %" PetscInt_FMT ", %" PetscInt_FMT ")", name, key[0].value, key[0].part);
    5676:   }
    5677:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
    5678:   /* FEM */
    5679:   /* 1: Get sizes from dm and dmAux */
    5680:   PetscCall(DMGetLocalSection(dm, &section));
    5681:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
    5682:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
    5683:   PetscCall(PetscDSGetNumFields(ds, &Nf));
    5684:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
    5685:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
    5686:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
    5687:   if (locA[2]) {
    5688:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

    5690:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
    5691:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
    5692:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
    5693:     {
    5694:       const PetscInt *cone;
    5695:       PetscInt        c;

    5697:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
    5698:       for (c = 0; c < 2; ++c) {
    5699:         const PetscInt *support;
    5700:         PetscInt        ssize, s;

    5702:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
    5703:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
    5704:         PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize);
    5705:         if (support[0] == cellStart) s = 1;
    5706:         else if (support[1] == cellStart) s = 0;
    5707:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
    5708:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
    5709:         PetscCheck(locA[c], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Must have auxiliary vector for (%p, %" PetscInt_FMT ", %" PetscInt_FMT ")", (void *)key[c].label, key[c].value, key[c].part);
    5710:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
    5711:         else dmAux[c] = dmAux[2];
    5712:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
    5713:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
    5714:       }
    5715:     }
    5716:   }
    5717:   /* Handle mass matrix scaling
    5718:        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
    5719:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
    5720:   if (locS[2]) {
    5721:     const PetscInt cellStart = cells ? cells[cStart] : cStart;
    5722:     PetscInt       Nb, Nbs;

    5724:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
    5725:     PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL));
    5726:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
    5727:     // BRAD: This is not set correctly
    5728:     key[2].field = 2;
    5729:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
    5730:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
    5731:     PetscCheck(Nb == Nbs, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Field %" PetscInt_FMT " of size %" PetscInt_FMT " cannot be scaled by field of size %" PetscInt_FMT, key[2].field, Nb, Nbs);
    5732:     {
    5733:       const PetscInt *cone;
    5734:       PetscInt        c;

    5736:       locS[1] = locS[0] = locS[2];
    5737:       dmScale[1] = dmScale[0] = dmScale[2];
    5738:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
    5739:       for (c = 0; c < 2; ++c) {
    5740:         const PetscInt *support;
    5741:         PetscInt        ssize, s;

    5743:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
    5744:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
    5745:         PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize);
    5746:         if (support[0] == cellStart) s = 1;
    5747:         else if (support[1] == cellStart) s = 0;
    5748:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
    5749:         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
    5750:         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
    5751:       }
    5752:     }
    5753:   }
    5754:   /* 2: Setup geometric data */
    5755:   PetscCall(DMGetCoordinateField(dm, &coordField));
    5756:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    5757:   if (maxDegree > 1) {
    5758:     PetscCall(PetscCalloc4(Nf, &quadsF, Nf, &geomsF, Nf, &quadsN, Nf, &geomsN));
    5759:     for (f = 0; f < Nf; ++f) {
    5760:       PetscFE   fe;
    5761:       PetscBool isCohesiveField;

    5763:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
    5764:       if (fe) {
    5765:         PetscCall(PetscFEGetQuadrature(fe, &quadsF[f]));
    5766:         PetscCall(PetscObjectReference((PetscObject)quadsF[f]));
    5767:       }
    5768:       PetscCall(PetscDSGetDiscretization(dsIn, f, (PetscObject *)&fe));
    5769:       PetscCall(PetscDSGetCohesive(dsIn, f, &isCohesiveField));
    5770:       if (fe) {
    5771:         if (isCohesiveField) {
    5772:           for (PetscInt g = 0; g < Nf; ++g) {
    5773:             PetscCall(PetscDSGetDiscretization(dsIn, g, (PetscObject *)&fe));
    5774:             PetscCall(PetscDSGetCohesive(dsIn, g, &isCohesiveField));
    5775:             if (!isCohesiveField) break;
    5776:           }
    5777:         }
    5778:         PetscCall(PetscFEGetQuadrature(fe, &quadsN[f]));
    5779:         PetscCall(PetscObjectReference((PetscObject)quadsN[f]));
    5780:       }
    5781:     }
    5782:   }
    5783:   /* Loop over chunks */
    5784:   cellChunkSize = numCells;
    5785:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
    5786:   PetscCall(PetscCalloc2(2 * cellChunkSize, &faces, 2 * cellChunkSize, &neighbors));
    5787:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkISF));
    5788:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER, &chunkISN));
    5789:   /* Extract field coefficients */
    5790:   /* NOTE This needs the end cap faces to have identical orientations */
    5791:   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
    5792:   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
    5793:   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
    5794:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg));
    5795:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos));
    5796:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh));
    5797:   for (chunk = 0; chunk < numChunks; ++chunk) {
    5798:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

    5800:     PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
    5801:     PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
    5802:     PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
    5803:     /* Get faces and neighbors */
    5804:     for (c = cS; c < cE; ++c) {
    5805:       const PetscInt  cell = cells ? cells[c] : c;
    5806:       const PetscInt *cone, *support;
    5807:       PetscCall(DMPlexGetCone(dm, cell, &cone));
    5808:       faces[(c - cS) * 2 + 0] = cone[0];
    5809:       faces[(c - cS) * 2 + 1] = cone[1];
    5810:       PetscCall(DMPlexGetSupport(dm, cone[0], &support));
    5811:       neighbors[(c - cS) * 2 + 0] = support[0] == cell ? support[1] : support[0];
    5812:       PetscCall(DMPlexGetSupport(dm, cone[1], &support));
    5813:       neighbors[(c - cS) * 2 + 1] = support[0] == cell ? support[1] : support[0];
    5814:     }
    5815:     PetscCall(ISGeneralSetIndices(chunkISF, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
    5816:     PetscCall(ISGeneralSetIndices(chunkISN, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER));
    5817:     /* Get geometric data */
    5818:     if (maxDegree <= 1) {
    5819:       if (!affineQuadF) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkISF, &affineQuadF));
    5820:       if (affineQuadF) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, affineQuadF, PETSC_FEGEOM_COHESIVE, &affineGeomF));
    5821:       if (!affineQuadN) {
    5822:         PetscInt dim;
    5823:         PetscCall(PetscQuadratureGetData(affineQuadF, &dim, NULL, NULL, NULL, NULL));
    5824:         PetscCall(DMFieldCreateDefaultFaceQuadrature(coordField, chunkISN, &affineQuadN));
    5825:         PetscCall(PetscQuadratureSetData(affineQuadN, dim + 1, PETSC_DECIDE, PETSC_DECIDE, NULL, NULL));
    5826:       }
    5827:       if (affineQuadN) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, affineQuadN, PETSC_FEGEOM_BASIC, &affineGeomN));
    5828:     } else {
    5829:       for (f = 0; f < Nf; ++f) {
    5830:         if (quadsF[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, quadsF[f], PETSC_FEGEOM_COHESIVE, &geomsF[f]));
    5831:         if (quadsN[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, quadsN[f], PETSC_FEGEOM_BASIC, &geomsN[f]));
    5832:       }
    5833:     }
    5834:     /* Loop over fields */
    5835:     for (f = 0; f < Nf; ++f) {
    5836:       PetscFE         fe;
    5837:       PetscFEGeom    *geomF      = affineGeomF ? affineGeomF : geomsF[f];
    5838:       PetscFEGeom    *chunkGeomF = NULL, *remGeomF = NULL;
    5839:       PetscFEGeom    *geomN      = affineGeomN ? affineGeomN : geomsN[f];
    5840:       PetscFEGeom    *chunkGeomN = NULL, *remGeomN = NULL;
    5841:       PetscQuadrature quadF = affineQuadF ? affineQuadF : quadsF[f];
    5842:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
    5843:       PetscBool       isCohesiveField;

    5845:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
    5846:       if (!fe) continue;
    5847:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    5848:       PetscCall(PetscQuadratureGetData(quadF, NULL, NULL, &Nq, NULL, NULL));
    5849:       PetscCall(PetscFEGetDimension(fe, &Nb));
    5850:       blockSize = Nb;
    5851:       batchSize = numBlocks * blockSize;
    5852:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    5853:       numChunks = numCells / (numBatches * batchSize);
    5854:       Ne        = numChunks * numBatches * batchSize;
    5855:       Nr        = numCells % (numBatches * batchSize);
    5856:       offset    = numCells - Nr;
    5857:       PetscCall(PetscFEGeomGetChunk(geomF, 0, offset * 2, &chunkGeomF));
    5858:       PetscCall(PetscFEGeomGetChunk(geomF, offset * 2, numCells * 2, &remGeomF));
    5859:       PetscCall(PetscFEGeomGetChunk(geomN, 0, offset * 2, &chunkGeomN));
    5860:       PetscCall(PetscFEGeomGetChunk(geomN, offset * 2, numCells * 2, &remGeomN));
    5861:       PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
    5862:       // TODO Do I need to set isCohesive on the chunks?
    5863:       key[0].field = f;
    5864:       key[1].field = f;
    5865:       key[2].field = f;
    5866:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[0], a[0], t, elemVecNeg));
    5867:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], PetscSafePointerPlusOffset(a[0], offset * totDimAux[0]), t, &elemVecNeg[offset * totDim]));
    5868:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[1], a[1], t, elemVecPos));
    5869:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], PetscSafePointerPlusOffset(a[1], offset * totDimAux[1]), t, &elemVecPos[offset * totDim]));
    5870:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[2], a[2], t, elemVecCoh));
    5871:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], PetscSafePointerPlusOffset(a[2], offset * totDimAux[2]), t, &elemVecCoh[offset * totDim]));
    5872:       PetscCall(PetscFEGeomRestoreChunk(geomF, offset, numCells, &remGeomF));
    5873:       PetscCall(PetscFEGeomRestoreChunk(geomF, 0, offset, &chunkGeomF));
    5874:       PetscCall(PetscFEGeomRestoreChunk(geomN, offset, numCells, &remGeomN));
    5875:       PetscCall(PetscFEGeomRestoreChunk(geomN, 0, offset, &chunkGeomN));
    5876:     }
    5877:     /* Add elemVec to locX */
    5878:     for (c = cS; c < cE; ++c) {
    5879:       const PetscInt cell = cells ? cells[c] : c;
    5880:       const PetscInt cind = c - cStart;
    5881:       PetscInt       i;

    5883:       /* Scale element values */
    5884:       if (locS[0]) {
    5885:         PetscInt  Nb, off = cind * totDim, soff = cind * totDimScale[0];
    5886:         PetscBool cohesive;

    5888:         for (f = 0; f < Nf; ++f) {
    5889:           PetscCall(PetscDSGetFieldSize(ds, f, &Nb));
    5890:           PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
    5891:           if (f == key[2].field) {
    5892:             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
    5893:             // No cohesive scaling field is currently input
    5894:             for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i];
    5895:             off += Nb;
    5896:           } else {
    5897:             const PetscInt N = cohesive ? Nb : Nb * 2;

    5899:             for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
    5900:             off += N;
    5901:           }
    5902:         }
    5903:       } else {
    5904:         for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
    5905:       }
    5906:       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
    5907:       if (ghostLabel) {
    5908:         PetscInt ghostVal;

    5910:         PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
    5911:         if (ghostVal > 0) continue;
    5912:       }
    5913:       PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES));
    5914:     }
    5915:   }
    5916:   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
    5917:   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
    5918:   PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
    5919:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg));
    5920:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos));
    5921:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh));
    5922:   PetscCall(PetscFree2(faces, neighbors));
    5923:   PetscCall(ISDestroy(&chunkISF));
    5924:   PetscCall(ISDestroy(&chunkISN));
    5925:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    5926:   if (maxDegree <= 1) {
    5927:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadF, PETSC_FALSE, &affineGeomF));
    5928:     PetscCall(PetscQuadratureDestroy(&affineQuadF));
    5929:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadN, PETSC_FALSE, &affineGeomN));
    5930:     PetscCall(PetscQuadratureDestroy(&affineQuadN));
    5931:   } else {
    5932:     for (f = 0; f < Nf; ++f) {
    5933:       if (geomsF) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quadsF[f], PETSC_FALSE, &geomsF[f]));
    5934:       if (quadsF) PetscCall(PetscQuadratureDestroy(&quadsF[f]));
    5935:       if (geomsN) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quadsN[f], PETSC_FALSE, &geomsN[f]));
    5936:       if (quadsN) PetscCall(PetscQuadratureDestroy(&quadsN[f]));
    5937:     }
    5938:     PetscCall(PetscFree4(quadsF, geomsF, quadsN, geomsN));
    5939:   }
    5940:   if (mesh->printFEM) {
    5941:     Vec          locFbc;
    5942:     PetscInt     pStart, pEnd, p, maxDof;
    5943:     PetscScalar *zeroes;

    5945:     PetscCall(VecDuplicate(locF, &locFbc));
    5946:     PetscCall(VecCopy(locF, locFbc));
    5947:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
    5948:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
    5949:     PetscCall(PetscCalloc1(maxDof, &zeroes));
    5950:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
    5951:     PetscCall(PetscFree(zeroes));
    5952:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
    5953:     PetscCall(VecDestroy(&locFbc));
    5954:   }
    5955: end:
    5956:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
    5957:   PetscFunctionReturn(PETSC_SUCCESS);
    5958: }

    5960: /*@
    5961:   DMPlexComputeBdJacobianSingleByLabel - Compute the local boundary Jacobian for terms matching the input label

    5963:   Not collective

    5965:   Input Parameters:
    5966: + dm         - The output `DM`
    5967: . wf         - The `PetscWeakForm` holding forms on this boundary
    5968: . label      - The `DMLabel` indicating what faces should be integrated over
    5969: . numValues  - The number of label values
    5970: . values     - The array of label values
    5971: . fieldI     - The test field for these integrals
    5972: . facetIS    - The `IS` giving the set of possible faces to integrate over (intersected with the label)
    5973: . locX       - The local solution
    5974: . locX_t     - The time derivative of the local solution, or `NULL` for time-independent problems
    5975: . t          - The time
    5976: . coordField - The `DMField` object with coordinates for these faces
    5977: - X_tShift   - The multiplier for dF/dxdot

    5979:   Output Parameters:
    5980: + Jac  - The local Jacobian
    5981: - JacP - The local Jacobian preconditioner

    5983:   Level: developer

    5985: .seealso: `DMPlexComputeBdJacobianSingle()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
    5986: @*/
    5987: PetscErrorCode DMPlexComputeBdJacobianSingleByLabel(DM dm, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt fieldI, IS facetIS, Vec locX, Vec locX_t, PetscReal t, DMField coordField, PetscReal X_tShift, Mat Jac, Mat JacP)
    5988: {
    5989:   DM_Plex        *mesh = (DM_Plex *)dm->data;
    5990:   DM              plex = NULL, plexA = NULL, tdm;
    5991:   DMEnclosureType encAux;
    5992:   PetscDS         ds, dsAux           = NULL;
    5993:   PetscSection    section, sectionAux = NULL;
    5994:   PetscSection    globalSection;
    5995:   Vec             locA = NULL, tv;
    5996:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL;
    5997:   PetscInt        v;
    5998:   PetscInt        Nf, totDim, totDimAux = 0;
    5999:   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, transform;

    6001:   PetscFunctionBegin;
    6002:   PetscCall(DMHasBasisTransform(dm, &transform));
    6003:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
    6004:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
    6005:   PetscCall(DMGetLocalSection(dm, &section));
    6006:   PetscCall(DMGetDS(dm, &ds));
    6007:   PetscCall(PetscDSGetNumFields(ds, &Nf));
    6008:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
    6009:   PetscCall(PetscWeakFormHasBdJacobian(wf, &hasJac));
    6010:   PetscCall(PetscWeakFormHasBdJacobianPreconditioner(wf, &hasPrec));
    6011:   if (!hasJac && !hasPrec) PetscFunctionReturn(PETSC_SUCCESS);
    6012:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    6013:   PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
    6014:   if (locA) {
    6015:     DM dmAux;

    6017:     PetscCall(VecGetDM(locA, &dmAux));
    6018:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    6019:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
    6020:     PetscCall(DMGetDS(plexA, &dsAux));
    6021:     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
    6022:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
    6023:   }

    6025:   PetscCall(DMGetGlobalSection(dm, &globalSection));
    6026:   for (v = 0; v < numValues; ++v) {
    6027:     PetscFEGeom    *fgeom;
    6028:     PetscInt        maxDegree;
    6029:     PetscQuadrature qGeom = NULL;
    6030:     IS              pointIS;
    6031:     const PetscInt *points;
    6032:     PetscFormKey    key;
    6033:     PetscInt        numFaces, face, Nq;

    6035:     key.label = label;
    6036:     key.value = values[v];
    6037:     key.part  = 0;
    6038:     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
    6039:     if (!pointIS) continue; /* No points with that id on this process */
    6040:     {
    6041:       IS isectIS;

    6043:       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
    6044:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
    6045:       PetscCall(ISDestroy(&pointIS));
    6046:       pointIS = isectIS;
    6047:     }
    6048:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
    6049:     PetscCall(ISGetIndices(pointIS, &points));
    6050:     PetscCall(PetscMalloc5(numFaces * totDim, &u, (locX_t ? (size_t)numFaces * totDim : 0), &u_t, (hasJac ? (size_t)numFaces * totDim * totDim : 0), &elemMat, (hasPrec ? (size_t)numFaces * totDim * totDim : 0), &elemMatP, (locA ? (size_t)numFaces * totDimAux : 0), &a));
    6051:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
    6052:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
    6053:     if (!qGeom) {
    6054:       PetscFE fe;

    6056:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
    6057:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
    6058:       PetscCall(PetscObjectReference((PetscObject)qGeom));
    6059:     }
    6060:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
    6061:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_FEGEOM_BOUNDARY, &fgeom));
    6062:     for (face = 0; face < numFaces; ++face) {
    6063:       const PetscInt point = points[face], *support;
    6064:       PetscScalar   *x     = NULL;
    6065:       PetscInt       i;

    6067:       PetscCall(DMPlexGetSupport(dm, point, &support));
    6068:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
    6069:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
    6070:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
    6071:       if (locX_t) {
    6072:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
    6073:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
    6074:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
    6075:       }
    6076:       if (locA) {
    6077:         PetscInt subp;
    6078:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
    6079:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
    6080:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
    6081:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
    6082:       }
    6083:     }
    6084:     if (elemMat) PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
    6085:     if (elemMatP) PetscCall(PetscArrayzero(elemMatP, numFaces * totDim * totDim));
    6086:     {
    6087:       PetscFE  fe;
    6088:       PetscInt Nb;
    6089:       /* Conforming batches */
    6090:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    6091:       /* Remainder */
    6092:       PetscFEGeom *chunkGeom = NULL;
    6093:       PetscInt     fieldJ, Nr, offset;

    6095:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
    6096:       PetscCall(PetscFEGetDimension(fe, &Nb));
    6097:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    6098:       blockSize = Nb;
    6099:       batchSize = numBlocks * blockSize;
    6100:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    6101:       numChunks = numFaces / (numBatches * batchSize);
    6102:       Ne        = numChunks * numBatches * batchSize;
    6103:       Nr        = numFaces % (numBatches * batchSize);
    6104:       offset    = numFaces - Nr;
    6105:       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
    6106:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    6107:         key.field = fieldI * Nf + fieldJ;
    6108:         if (hasJac) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat));
    6109:         if (hasPrec) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP));
    6110:       }
    6111:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
    6112:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    6113:         key.field = fieldI * Nf + fieldJ;
    6114:         if (hasJac)
    6115:           PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * totDim]));
    6116:         if (hasPrec)
    6117:           PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatP[offset * totDim * totDim]));
    6118:       }
    6119:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
    6120:     }
    6121:     for (face = 0; face < numFaces; ++face) {
    6122:       const PetscInt point = points[face], *support;

    6124:       /* Transform to global basis before insertion in Jacobian */
    6125:       PetscCall(DMPlexGetSupport(plex, point, &support));
    6126:       if (hasJac && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
    6127:       if (hasPrec && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMatP[face * totDim * totDim]));
    6128:       if (hasPrec) {
    6129:         if (hasJac) {
    6130:           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
    6131:           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
    6132:         }
    6133:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMatP[face * totDim * totDim]));
    6134:         PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMatP[face * totDim * totDim], ADD_VALUES));
    6135:       } else {
    6136:         if (hasJac) {
    6137:           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
    6138:           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
    6139:         }
    6140:       }
    6141:     }
    6142:     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
    6143:     PetscCall(PetscQuadratureDestroy(&qGeom));
    6144:     PetscCall(ISRestoreIndices(pointIS, &points));
    6145:     PetscCall(ISDestroy(&pointIS));
    6146:     PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, a));
    6147:   }
    6148:   PetscCall(DMDestroy(&plex));
    6149:   PetscCall(DMDestroy(&plexA));
    6150:   PetscFunctionReturn(PETSC_SUCCESS);
    6151: }

    6153: /*@
    6154:   DMPlexComputeBdJacobianSingle - Compute the local boundary Jacobian

    6156:   Not collective

    6158:   Input Parameters:
    6159: + dm        - The output `DM`
    6160: . wf        - The `PetscWeakForm` holding forms on this boundary
    6161: . label     - The `DMLabel` indicating what faces should be integrated over
    6162: . numValues - The number of label values
    6163: . values    - The array of label values
    6164: . fieldI    - The test field for these integrals
    6165: . locX      - The local solution
    6166: . locX_t    - The time derivative of the local solution, or `NULL` for time-independent problems
    6167: . t         - The time
    6168: - X_tShift  - The multiplier for dF/dxdot

    6170:   Output Parameters:
    6171: + Jac  - The local Jacobian
    6172: - JacP - The local Jacobian preconditioner

    6174:   Level: developer

    6176: .seealso: `DMPlexComputeBdJacobianSingleByLabel()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
    6177: @*/
    6178: PetscErrorCode DMPlexComputeBdJacobianSingle(DM dm, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt fieldI, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP)
    6179: {
    6180:   DMField  coordField;
    6181:   DMLabel  depthLabel;
    6182:   IS       facetIS;
    6183:   PetscInt dim;

    6185:   PetscFunctionBegin;
    6186:   PetscCall(DMGetDimension(dm, &dim));
    6187:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    6188:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
    6189:   PetscCall(DMGetCoordinateField(dm, &coordField));
    6190:   PetscCall(DMPlexComputeBdJacobianSingleByLabel(dm, wf, label, numValues, values, fieldI, facetIS, locX, locX_t, t, coordField, X_tShift, Jac, JacP));
    6191:   PetscCall(ISDestroy(&facetIS));
    6192:   PetscFunctionReturn(PETSC_SUCCESS);
    6193: }

    6195: static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, PetscCtx ctx)
    6196: {
    6197:   PetscDS  prob;
    6198:   PetscInt dim, numBd, bd;
    6199:   DMLabel  depthLabel;
    6200:   DMField  coordField = NULL;
    6201:   IS       facetIS;

    6203:   PetscFunctionBegin;
    6204:   PetscCall(DMGetDS(dm, &prob));
    6205:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    6206:   PetscCall(DMGetDimension(dm, &dim));
    6207:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
    6208:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
    6209:   PetscCall(DMGetCoordinateField(dm, &coordField));
    6210:   for (bd = 0; bd < numBd; ++bd) {
    6211:     PetscWeakForm           wf;
    6212:     DMBoundaryConditionType type;
    6213:     DMLabel                 label;
    6214:     const PetscInt         *values;
    6215:     PetscInt                fieldI, numValues;
    6216:     PetscObject             obj;
    6217:     PetscClassId            id;

    6219:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
    6220:     if (type & DM_BC_ESSENTIAL) continue;
    6221:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
    6222:     PetscCall(PetscObjectGetClassId(obj, &id));
    6223:     if (id != PETSCFE_CLASSID) continue;
    6224:     PetscCall(DMPlexComputeBdJacobianSingleByLabel(dm, wf, label, numValues, values, fieldI, facetIS, locX, locX_t, t, coordField, X_tShift, Jac, JacP));
    6225:   }
    6226:   PetscCall(ISDestroy(&facetIS));
    6227:   PetscFunctionReturn(PETSC_SUCCESS);
    6228: }

    6230: /*@
    6231:   DMPlexComputeJacobianByKey - Compute the local Jacobian for terms matching the input key

    6233:   Collective

    6235:   Input Parameters:
    6236: + dm       - The output `DM`
    6237: . key      - The `PetscFormKey` indicating what should be integrated
    6238: . cellIS   - The `IS` give a set of cells to integrate over
    6239: . t        - The time
    6240: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
    6241: . locX     - The local solution
    6242: . locX_t   - The time derivative of the local solution, or `NULL` for time-independent problems
    6243: - ctx      - An optional application context, passed to the pointwise functions

    6245:   Output Parameters:
    6246: + Jac  - The local Jacobian
    6247: - JacP - The local Jacobian preconditioner

    6249:   Level: developer

    6251: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
    6252: @*/
    6253: PetscErrorCode DMPlexComputeJacobianByKey(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, PetscCtx ctx)
    6254: {
    6255:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
    6256:   const char     *name  = "Jacobian";
    6257:   DM              dmAux = NULL, plex, tdm;
    6258:   DMEnclosureType encAux;
    6259:   Vec             A, tv;
    6260:   DMField         coordField;
    6261:   PetscDS         prob, probAux = NULL;
    6262:   PetscSection    section, globalSection, sectionAux;
    6263:   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
    6264:   const PetscInt *cells;
    6265:   PetscInt        Nf, fieldI, fieldJ;
    6266:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
    6267:   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;

    6269:   PetscFunctionBegin;
    6270:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    6271:   PetscCall(DMGetLocalSection(dm, &section));
    6272:   PetscCall(DMGetGlobalSection(dm, &globalSection));
    6273:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
    6274:   if (A) {
    6275:     PetscCall(VecGetDM(A, &dmAux));
    6276:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    6277:     PetscCall(DMConvert(dmAux, DMPLEX, &plex));
    6278:     PetscCall(DMGetLocalSection(plex, &sectionAux));
    6279:     PetscCall(DMGetDS(dmAux, &probAux));
    6280:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    6281:   }
    6282:   PetscCall(DMGetCoordinateField(dm, &coordField));
    6283:   if (!cellIS) goto end;
    6284:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    6285:   PetscCall(ISGetLocalSize(cellIS, &numCells));
    6286:   if (cStart >= cEnd) goto end;
    6287:   PetscCall(DMHasBasisTransform(dm, &transform));
    6288:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
    6289:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
    6290:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
    6291:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    6292:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    6293:   PetscCall(PetscDSHasJacobian(prob, &hasJac));
    6294:   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
    6295:   /* user passed in the same matrix, avoid double contributions and
    6296:      only assemble the Jacobian */
    6297:   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
    6298:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
    6299:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
    6300:   PetscCall(PetscMalloc5(numCells * totDim, &u, (locX_t ? (size_t)numCells * totDim : 0), &u_t, (hasJac ? (size_t)numCells * totDim * totDim : 0), &elemMat, (hasPrec ? (size_t)numCells * totDim * totDim : 0), &elemMatP, (hasDyn ? (size_t)numCells * totDim * totDim : 0), &elemMatD));
    6301:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
    6302:   for (c = cStart; c < cEnd; ++c) {
    6303:     const PetscInt cell = cells ? cells[c] : c;
    6304:     const PetscInt cind = c - cStart;
    6305:     PetscScalar   *x = NULL, *x_t = NULL;
    6306:     PetscInt       i;

    6308:     PetscCall(DMPlexVecGetClosure(dm, section, locX, cell, NULL, &x));
    6309:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
    6310:     PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cell, NULL, &x));
    6311:     if (locX_t) {
    6312:       PetscCall(DMPlexVecGetClosure(dm, section, locX_t, cell, NULL, &x_t));
    6313:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
    6314:       PetscCall(DMPlexVecRestoreClosure(dm, section, locX_t, cell, NULL, &x_t));
    6315:     }
    6316:     if (dmAux) {
    6317:       PetscInt subcell;
    6318:       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
    6319:       PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
    6320:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
    6321:       PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
    6322:     }
    6323:   }
    6324:   if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
    6325:   if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
    6326:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
    6327:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
    6328:     PetscClassId    id;
    6329:     PetscFE         fe;
    6330:     PetscQuadrature qGeom = NULL;
    6331:     PetscInt        Nb;
    6332:     /* Conforming batches */
    6333:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    6334:     /* Remainder */
    6335:     PetscInt     Nr, offset, Nq;
    6336:     PetscInt     maxDegree;
    6337:     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

    6339:     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
    6340:     PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
    6341:     if (id == PETSCFV_CLASSID) {
    6342:       hasFV = PETSC_TRUE;
    6343:       continue;
    6344:     }
    6345:     PetscCall(PetscFEGetDimension(fe, &Nb));
    6346:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    6347:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    6348:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
    6349:     if (!qGeom) {
    6350:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
    6351:       PetscCall(PetscObjectReference((PetscObject)qGeom));
    6352:     }
    6353:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
    6354:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
    6355:     blockSize = Nb;
    6356:     batchSize = numBlocks * blockSize;
    6357:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    6358:     numChunks = numCells / (numBatches * batchSize);
    6359:     Ne        = numChunks * numBatches * batchSize;
    6360:     Nr        = numCells % (numBatches * batchSize);
    6361:     offset    = numCells - Nr;
    6362:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
    6363:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
    6364:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    6365:       key.field = fieldI * Nf + fieldJ;
    6366:       if (hasJac) {
    6367:         PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
    6368:         PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * totDim]));
    6369:       }
    6370:       if (hasPrec) {
    6371:         PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
    6372:         PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_PRE, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatP[offset * totDim * totDim]));
    6373:       }
    6374:       if (hasDyn) {
    6375:         PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
    6376:         PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatD[offset * totDim * totDim]));
    6377:       }
    6378:     }
    6379:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
    6380:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
    6381:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
    6382:     PetscCall(PetscQuadratureDestroy(&qGeom));
    6383:   }
    6384:   /*   Add contribution from X_t */
    6385:   if (hasDyn) {
    6386:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
    6387:   }
    6388:   if (hasFV) {
    6389:     PetscClassId id;
    6390:     PetscFV      fv;
    6391:     PetscInt     offsetI, NcI, NbI = 1, fc, f;

    6393:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
    6394:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
    6395:       PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
    6396:       PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
    6397:       if (id != PETSCFV_CLASSID) continue;
    6398:       /* Put in the weighted identity */
    6399:       PetscCall(PetscFVGetNumComponents(fv, &NcI));
    6400:       for (c = cStart; c < cEnd; ++c) {
    6401:         const PetscInt cind    = c - cStart;
    6402:         const PetscInt eOffset = cind * totDim * totDim;
    6403:         PetscReal      vol;

    6405:         PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
    6406:         for (fc = 0; fc < NcI; ++fc) {
    6407:           for (f = 0; f < NbI; ++f) {
    6408:             const PetscInt i = offsetI + f * NcI + fc;
    6409:             if (hasPrec) {
    6410:               if (hasJac) elemMat[eOffset + i * totDim + i] = vol;
    6411:               elemMatP[eOffset + i * totDim + i] = vol;
    6412:             } else {
    6413:               elemMat[eOffset + i * totDim + i] = vol;
    6414:             }
    6415:           }
    6416:         }
    6417:       }
    6418:     }
    6419:     /* No allocated space for FV stuff, so ignore the zero entries */
    6420:     PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
    6421:   }
    6422:   /* Insert values into matrix */
    6423:   for (c = cStart; c < cEnd; ++c) {
    6424:     const PetscInt cell = cells ? cells[c] : c;
    6425:     const PetscInt cind = c - cStart;

    6427:     /* Transform to global basis before insertion in Jacobian */
    6428:     if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
    6429:     if (hasPrec) {
    6430:       if (hasJac) {
    6431:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
    6432:         PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
    6433:       }
    6434:       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
    6435:       PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
    6436:     } else {
    6437:       if (hasJac) {
    6438:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
    6439:         PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
    6440:       }
    6441:     }
    6442:   }
    6443:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    6444:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
    6445:   PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
    6446:   if (dmAux) PetscCall(PetscFree(a));
    6447:   /* Compute boundary integrals */
    6448:   PetscCall(DMPlexComputeBdJacobian_Internal(dm, locX, locX_t, t, X_tShift, Jac, JacP, ctx));
    6449:   /* Assemble matrix */
    6450: end: {
    6451:   PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;

    6453:   if (dmAux) PetscCall(DMDestroy(&plex));
    6454:   PetscCallMPI(MPIU_Allreduce(&assOp, &gassOp, 1, MPI_C_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
    6455:   if (hasJac && hasPrec) {
    6456:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
    6457:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
    6458:   }
    6459: }
    6460:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
    6461:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
    6462:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    6463:   PetscFunctionReturn(PETSC_SUCCESS);
    6464: }

    6466: /*@
    6467:   DMPlexComputeJacobianByKeyGeneral - Assemble the Jacobian and its preconditioning matrix over a cell range
    6468:   described by a `PetscFormKey` for a general (possibly non-square, non-nested) pair of row/column `DM`s.

    6470:   Collective

    6472:   Input Parameters:
    6473: + dmr      - the row `DMPLEX`
    6474: . dmc      - the column `DMPLEX`
    6475: . key      - the `PetscFormKey` selecting the label, value, part, and field for assembly
    6476: . cellIS   - the `IS` listing cells to process, or `NULL`
    6477: . t        - the current time
    6478: . X_tShift - the time-derivative shift used to combine dynamic and static Jacobian contributions
    6479: . locX     - the local solution vector
    6480: . locX_t   - the local time-derivative vector, or `NULL`
    6481: - ctx      - the application context (unused; kept for API symmetry)

    6483:   Output Parameters:
    6484: + Jac  - the assembled Jacobian matrix
    6485: - JacP - the assembled matrix from which the preconditioner is constructed

    6487:   Level: developer

    6489: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscFormKey`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeInterpolatorGeneral()`
    6490: @*/
    6491: PetscErrorCode DMPlexComputeJacobianByKeyGeneral(DM dmr, DM dmc, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, PetscCtx ctx)
    6492: {
    6493:   DM_Plex        *mesh     = (DM_Plex *)dmr->data;
    6494:   const char     *name     = "Jacobian";
    6495:   DM              dmAux    = NULL, plex, tdm;
    6496:   PetscInt        printFEM = mesh->printFEM;
    6497:   PetscBool       clPerm   = mesh->useMatClPerm;
    6498:   DMEnclosureType encAux;
    6499:   Vec             A, tv;
    6500:   DMField         coordField;
    6501:   PetscDS         rds, cds, dsAux = NULL;
    6502:   PetscSection    rsection, rglobalSection, csection, cglobalSection, sectionAux;
    6503:   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
    6504:   const PetscInt *cells;
    6505:   PetscInt        Nf, cNf;
    6506:   PetscInt        totDim, ctotDim, totDimAux = 0, cStart, cEnd, numCells;
    6507:   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;
    6508:   MPI_Comm        comm;

    6510:   PetscFunctionBegin;
    6511:   PetscCall(PetscObjectGetComm((PetscObject)dmr, &comm));
    6512:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dmr, 0, 0, 0));
    6513:   PetscCall(DMGetLocalSection(dmr, &rsection));
    6514:   PetscCall(DMGetGlobalSection(dmr, &rglobalSection));
    6515:   PetscCall(DMGetLocalSection(dmc, &csection));
    6516:   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
    6517:   PetscCall(DMGetAuxiliaryVec(dmr, key.label, key.value, key.part, &A));
    6518:   if (A) {
    6519:     PetscCall(VecGetDM(A, &dmAux));
    6520:     PetscCall(DMGetEnclosureRelation(dmAux, dmr, &encAux));
    6521:     PetscCall(DMConvert(dmAux, DMPLEX, &plex));
    6522:     PetscCall(DMGetLocalSection(plex, &sectionAux));
    6523:     PetscCall(DMGetDS(dmAux, &dsAux));
    6524:     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
    6525:   }
    6526:   PetscCall(DMGetCoordinateField(dmr, &coordField));
    6527:   if (!cellIS) goto end;
    6528:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    6529:   PetscCall(ISGetLocalSize(cellIS, &numCells));
    6530:   if (cStart >= cEnd) goto end;
    6531:   PetscCall(DMHasBasisTransform(dmr, &transform));
    6532:   PetscCall(DMGetBasisTransformDM_Internal(dmr, &tdm));
    6533:   PetscCall(DMGetBasisTransformVec_Internal(dmr, &tv));
    6534:   PetscCall(DMGetCellDS(dmr, cells ? cells[cStart] : cStart, &rds, NULL));
    6535:   PetscCall(DMGetCellDS(dmc, cells ? cells[cStart] : cStart, &cds, NULL));
    6536:   PetscCall(PetscDSGetNumFields(rds, &Nf));
    6537:   PetscCall(PetscDSGetNumFields(cds, &cNf));
    6538:   PetscCheck(Nf == cNf, comm, PETSC_ERR_ARG_WRONG, "Number of row fields %" PetscInt_FMT " != %" PetscInt_FMT " number of columns field", Nf, cNf);
    6539:   PetscCall(PetscDSGetTotalDimension(rds, &totDim));
    6540:   PetscCall(PetscDSGetTotalDimension(cds, &ctotDim));
    6541:   PetscCall(PetscDSHasJacobian(rds, &hasJac));
    6542:   PetscCall(PetscDSHasJacobianPreconditioner(rds, &hasPrec));
    6543:   /* user passed in the same matrix, avoid double contributions and
    6544:      only assemble the Jacobian */
    6545:   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
    6546:   PetscCall(PetscDSHasDynamicJacobian(rds, &hasDyn));
    6547:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
    6548:   PetscCall(PetscMalloc5(numCells * totDim, &u, (locX_t ? (size_t)numCells * totDim : 0), &u_t, (hasJac ? (size_t)numCells * totDim * ctotDim : 0), &elemMat, (hasPrec ? (size_t)numCells * totDim * ctotDim : 0), &elemMatP, (hasDyn ? (size_t)numCells * totDim * ctotDim : 0), &elemMatD));
    6549:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
    6550:   for (PetscInt c = cStart; c < cEnd; ++c) {
    6551:     const PetscInt cell = cells ? cells[c] : c;
    6552:     const PetscInt cind = c - cStart;
    6553:     PetscScalar   *x = NULL, *x_t = NULL;
    6554:     PetscInt       i;

    6556:     PetscCall(DMPlexVecGetClosure(dmr, rsection, locX, cell, NULL, &x));
    6557:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
    6558:     PetscCall(DMPlexVecRestoreClosure(dmr, rsection, locX, cell, NULL, &x));
    6559:     if (locX_t) {
    6560:       PetscCall(DMPlexVecGetClosure(dmr, rsection, locX_t, cell, NULL, &x_t));
    6561:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
    6562:       PetscCall(DMPlexVecRestoreClosure(dmr, rsection, locX_t, cell, NULL, &x_t));
    6563:     }
    6564:     if (dmAux) {
    6565:       PetscInt subcell;
    6566:       PetscCall(DMGetEnclosurePoint(dmAux, dmr, encAux, cell, &subcell));
    6567:       PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
    6568:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
    6569:       PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
    6570:     }
    6571:   }
    6572:   if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * ctotDim));
    6573:   if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * ctotDim));
    6574:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * ctotDim));
    6575:   for (PetscInt fieldI = 0; fieldI < Nf; ++fieldI) {
    6576:     PetscClassId    id;
    6577:     PetscFE         fe;
    6578:     PetscQuadrature qGeom = NULL;
    6579:     PetscInt        Nb;
    6580:     /* Conforming batches */
    6581:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    6582:     /* Remainder */
    6583:     PetscInt     Nr, offset, Nq;
    6584:     PetscInt     maxDegree;
    6585:     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

    6587:     PetscCall(PetscDSGetDiscretization(rds, fieldI, (PetscObject *)&fe));
    6588:     PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
    6589:     if (id == PETSCFV_CLASSID) {
    6590:       hasFV = PETSC_TRUE;
    6591:       continue;
    6592:     }
    6593:     PetscCall(PetscFEGetDimension(fe, &Nb));
    6594:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    6595:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    6596:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
    6597:     if (!qGeom) {
    6598:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
    6599:       PetscCall(PetscObjectReference((PetscObject)qGeom));
    6600:     }
    6601:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
    6602:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
    6603:     blockSize = Nb;
    6604:     batchSize = numBlocks * blockSize;
    6605:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    6606:     numChunks = numCells / (numBatches * batchSize);
    6607:     Ne        = numChunks * numBatches * batchSize;
    6608:     Nr        = numCells % (numBatches * batchSize);
    6609:     offset    = numCells - Nr;
    6610:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
    6611:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
    6612:     for (PetscInt fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    6613:       key.field = fieldI * Nf + fieldJ;
    6614:       if (hasJac) {
    6615:         PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat));
    6616:         PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * ctotDim]));
    6617:       }
    6618:       if (hasPrec) {
    6619:         PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP));
    6620:         PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN_PRE, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatP[offset * totDim * ctotDim]));
    6621:       }
    6622:       if (hasDyn) {
    6623:         PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatD));
    6624:         PetscCall(PetscFEIntegrateJacobian(rds, cds, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMatD[offset * totDim * ctotDim]));
    6625:       }
    6626:     }
    6627:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
    6628:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
    6629:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
    6630:     PetscCall(PetscQuadratureDestroy(&qGeom));
    6631:   }
    6632:   /*   Add contribution from X_t */
    6633:   if (hasDyn) {
    6634:     for (PetscInt c = 0; c < numCells * totDim * ctotDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
    6635:   }
    6636:   if (hasFV) {
    6637:     PetscClassId id;
    6638:     PetscFV      fv;
    6639:     PetscInt     offsetI, NcI, NbI = 1;

    6641:     for (PetscInt fieldI = 0; fieldI < Nf; ++fieldI) {
    6642:       PetscCall(PetscDSGetDiscretization(rds, fieldI, (PetscObject *)&fv));
    6643:       PetscCall(PetscDSGetFieldOffset(rds, fieldI, &offsetI));
    6644:       PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
    6645:       if (id != PETSCFV_CLASSID) continue;
    6646:       /* Put in the weighted identity */
    6647:       PetscCall(PetscFVGetNumComponents(fv, &NcI));
    6648:       for (PetscInt c = cStart; c < cEnd; ++c) {
    6649:         const PetscInt cind    = c - cStart;
    6650:         const PetscInt eOffset = cind * totDim * ctotDim;
    6651:         PetscReal      vol;

    6653:         PetscCall(DMPlexComputeCellGeometryFVM(dmr, c, &vol, NULL, NULL));
    6654:         for (PetscInt fc = 0; fc < NcI; ++fc) {
    6655:           for (PetscInt f = 0; f < NbI; ++f) {
    6656:             const PetscInt i = offsetI + f * NcI + fc;
    6657:             if (hasPrec) {
    6658:               if (hasJac) elemMat[eOffset + i * ctotDim + i] = vol;
    6659:               elemMatP[eOffset + i * ctotDim + i] = vol;
    6660:             } else {
    6661:               elemMat[eOffset + i * ctotDim + i] = vol;
    6662:             }
    6663:           }
    6664:         }
    6665:       }
    6666:     }
    6667:     /* No allocated space for FV stuff, so ignore the zero entries */
    6668:     PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
    6669:   }
    6670:   /* Insert values into matrix */
    6671:   for (PetscInt c = cStart; c < cEnd; ++c) {
    6672:     const PetscInt cell = cells ? cells[c] : c;
    6673:     const PetscInt cind = c - cStart;

    6675:     /* Transform to global basis before insertion in Jacobian */
    6676:     if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dmr, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * ctotDim]));
    6677:     if (hasPrec) {
    6678:       if (hasJac) {
    6679:         if (printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, ctotDim, &elemMat[cind * totDim * ctotDim]));
    6680:         PetscCall(DMPlexMatSetClosureGeneral(dmr, rsection, rglobalSection, clPerm, dmc, csection, cglobalSection, clPerm, Jac, cell, &elemMat[cind * totDim * ctotDim], ADD_VALUES));
    6681:       }
    6682:       if (printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, ctotDim, &elemMatP[cind * totDim * ctotDim]));
    6683:       PetscCall(DMPlexMatSetClosureGeneral(dmr, rsection, rglobalSection, clPerm, dmc, csection, cglobalSection, clPerm, JacP, cell, &elemMatP[cind * totDim * ctotDim], ADD_VALUES));
    6684:     } else {
    6685:       if (hasJac) {
    6686:         if (printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, ctotDim, &elemMat[cind * totDim * ctotDim]));
    6687:         PetscCall(DMPlexMatSetClosureGeneral(dmr, rsection, rglobalSection, clPerm, dmc, csection, cglobalSection, clPerm, JacP, cell, &elemMat[cind * totDim * ctotDim], ADD_VALUES));
    6688:       }
    6689:     }
    6690:   }
    6691:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    6692:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
    6693:   PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
    6694:   if (dmAux) PetscCall(PetscFree(a));
    6695:   /* Compute boundary integrals */
    6696:   PetscCall(DMPlexComputeBdJacobian_Internal(dmr, locX, locX_t, t, X_tShift, Jac, JacP, ctx));
    6697:   /* Assemble matrix */
    6698: end: {
    6699:   PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;

    6701:   if (dmAux) PetscCall(DMDestroy(&plex));
    6702:   PetscCallMPI(MPIU_Allreduce(&assOp, &gassOp, 1, MPI_C_BOOL, MPI_LOR, comm));
    6703:   if (hasJac && hasPrec) {
    6704:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
    6705:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
    6706:   }
    6707: }
    6708:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
    6709:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
    6710:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dmr, 0, 0, 0));
    6711:   PetscFunctionReturn(PETSC_SUCCESS);
    6712: }

    6714: /*@
    6715:   DMPlexComputeJacobianHybridByKey - Compute the local Jacobian over hybrid cells for terms matching the input key

    6717:   Collective

    6719:   Input Parameters:
    6720: + dm       - The output `DM`
    6721: . key      - The `PetscFormKey` array (left cell, right cell, cohesive cell) indicating what should be integrated
    6722: . cellIS   - The `IS` give a set of cells to integrate over
    6723: . t        - The time
    6724: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
    6725: . locX     - The local solution
    6726: . locX_t   - The time derivative of the local solution, or `NULL` for time-independent problems
    6727: - ctx      - An optional application context, passed to the pointwise functions

    6729:   Output Parameters:
    6730: + Jac  - The local Jacobian
    6731: - JacP - The local Jacobian preconditioner

    6733:   Level: developer

    6735: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `PetscFormKey`
    6736: @*/
    6737: PetscErrorCode DMPlexComputeJacobianHybridByKey(DM dm, PetscFormKey key[], IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, PetscCtx ctx)
    6738: {
    6739:   DM_Plex        *mesh          = (DM_Plex *)dm->data;
    6740:   const char     *name          = "Hybrid Jacobian";
    6741:   DM              dmAux[3]      = {NULL, NULL, NULL};
    6742:   DMLabel         ghostLabel    = NULL;
    6743:   DM              plex          = NULL;
    6744:   DM              plexA         = NULL;
    6745:   PetscDS         ds            = NULL;
    6746:   PetscDS         dsIn          = NULL;
    6747:   PetscDS         dsAux[3]      = {NULL, NULL, NULL};
    6748:   Vec             locA[3]       = {NULL, NULL, NULL};
    6749:   DM              dmScale[3]    = {NULL, NULL, NULL};
    6750:   PetscDS         dsScale[3]    = {NULL, NULL, NULL};
    6751:   Vec             locS[3]       = {NULL, NULL, NULL};
    6752:   PetscSection    section       = NULL;
    6753:   PetscSection    sectionAux[3] = {NULL, NULL, NULL};
    6754:   DMField         coordField    = NULL;
    6755:   PetscScalar    *a[3]          = {NULL, NULL, NULL};
    6756:   PetscScalar    *s[3]          = {NULL, NULL, NULL};
    6757:   PetscScalar    *u             = NULL, *u_t;
    6758:   PetscScalar    *elemMatNeg, *elemMatPos, *elemMatCoh;
    6759:   PetscScalar    *elemMatNegP, *elemMatPosP, *elemMatCohP;
    6760:   PetscSection    globalSection;
    6761:   IS              chunkISF, chunkISN;
    6762:   const PetscInt *cells;
    6763:   PetscInt       *faces, *neighbors;
    6764:   PetscInt        cStart, cEnd, numCells;
    6765:   PetscInt        Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
    6766:   PetscInt        maxDegree   = PETSC_INT_MAX;
    6767:   PetscQuadrature affineQuadF = NULL, *quadsF = NULL;
    6768:   PetscFEGeom    *affineGeomF = NULL, **geomsF = NULL;
    6769:   PetscQuadrature affineQuadN = NULL;
    6770:   PetscFEGeom    *affineGeomN = NULL;
    6771:   PetscBool       hasBdJac, hasBdPrec;

    6773:   PetscFunctionBegin;
    6774:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    6775:   if (!cellIS) goto end;
    6776:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    6777:   PetscCall(ISGetLocalSize(cellIS, &numCells));
    6778:   if (cStart >= cEnd) goto end;
    6779:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
    6780:     const char *name;
    6781:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
    6782:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Form keys for each side of a cohesive surface must be different (%s, %" PetscInt_FMT ", %" PetscInt_FMT ")", name, key[0].value, key[0].part);
    6783:   }
    6784:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    6785:   PetscCall(DMGetLocalSection(dm, &section));
    6786:   PetscCall(DMGetGlobalSection(dm, &globalSection));
    6787:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
    6788:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
    6789:   PetscCall(PetscDSGetNumFields(ds, &Nf));
    6790:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
    6791:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
    6792:   PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
    6793:   PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
    6794:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
    6795:   if (locA[2]) {
    6796:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

    6798:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
    6799:     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
    6800:     PetscCall(DMGetLocalSection(dmAux[2], &sectionAux[2]));
    6801:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
    6802:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
    6803:     {
    6804:       const PetscInt *cone;
    6805:       PetscInt        c;

    6807:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
    6808:       for (c = 0; c < 2; ++c) {
    6809:         const PetscInt *support;
    6810:         PetscInt        ssize, s;

    6812:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
    6813:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
    6814:         PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize);
    6815:         if (support[0] == cellStart) s = 1;
    6816:         else if (support[1] == cellStart) s = 0;
    6817:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
    6818:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
    6819:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
    6820:         else dmAux[c] = dmAux[2];
    6821:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
    6822:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
    6823:       }
    6824:     }
    6825:   }
    6826:   /* Handle mass matrix scaling
    6827:        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
    6828:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
    6829:   if (locS[2]) {
    6830:     const PetscInt cellStart = cells ? cells[cStart] : cStart;
    6831:     PetscInt       Nb, Nbs;

    6833:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
    6834:     PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
    6835:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
    6836:     // BRAD: This is not set correctly
    6837:     key[2].field = 2;
    6838:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
    6839:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
    6840:     PetscCheck(Nb == Nbs, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Field %" PetscInt_FMT " of size %" PetscInt_FMT " cannot be scaled by field of size %" PetscInt_FMT, key[2].field, Nb, Nbs);
    6841:     {
    6842:       const PetscInt *cone;
    6843:       PetscInt        c;

    6845:       locS[1] = locS[0] = locS[2];
    6846:       dmScale[1] = dmScale[0] = dmScale[2];
    6847:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
    6848:       for (c = 0; c < 2; ++c) {
    6849:         const PetscInt *support;
    6850:         PetscInt        ssize, s;

    6852:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
    6853:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
    6854:         PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize);
    6855:         if (support[0] == cellStart) s = 1;
    6856:         else if (support[1] == cellStart) s = 0;
    6857:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
    6858:         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
    6859:         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
    6860:       }
    6861:     }
    6862:   }
    6863:   /* 2: Setup geometric data */
    6864:   PetscCall(DMGetCoordinateField(dm, &coordField));
    6865:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    6866:   if (maxDegree > 1) {
    6867:     PetscInt f;
    6868:     PetscCall(PetscCalloc2(Nf, &quadsF, Nf, &geomsF));
    6869:     for (f = 0; f < Nf; ++f) {
    6870:       PetscFE fe;

    6872:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
    6873:       if (fe) {
    6874:         PetscCall(PetscFEGetQuadrature(fe, &quadsF[f]));
    6875:         PetscCall(PetscObjectReference((PetscObject)quadsF[f]));
    6876:       }
    6877:     }
    6878:   }
    6879:   /* Loop over chunks */
    6880:   cellChunkSize = numCells;
    6881:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
    6882:   PetscCall(PetscCalloc2(2 * cellChunkSize, &faces, 2 * cellChunkSize, &neighbors));
    6883:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkISF));
    6884:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER, &chunkISN));
    6885:   /* Extract field coefficients */
    6886:   /* NOTE This needs the end cap faces to have identical orientations */
    6887:   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
    6888:   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
    6889:   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
    6890:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
    6891:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
    6892:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
    6893:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
    6894:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
    6895:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
    6896:   for (chunk = 0; chunk < numChunks; ++chunk) {
    6897:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

    6899:     if (hasBdJac) {
    6900:       PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim));
    6901:       PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim));
    6902:       PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim));
    6903:     }
    6904:     if (hasBdPrec) {
    6905:       PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim));
    6906:       PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim));
    6907:       PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim));
    6908:     }
    6909:     /* Get faces */
    6910:     for (c = cS; c < cE; ++c) {
    6911:       const PetscInt  cell = cells ? cells[c] : c;
    6912:       const PetscInt *cone, *support;
    6913:       PetscCall(DMPlexGetCone(plex, cell, &cone));
    6914:       faces[(c - cS) * 2 + 0] = cone[0];
    6915:       faces[(c - cS) * 2 + 1] = cone[1];
    6916:       PetscCall(DMPlexGetSupport(dm, cone[0], &support));
    6917:       neighbors[(c - cS) * 2 + 0] = support[0] == cell ? support[1] : support[0];
    6918:       PetscCall(DMPlexGetSupport(dm, cone[1], &support));
    6919:       neighbors[(c - cS) * 2 + 1] = support[0] == cell ? support[1] : support[0];
    6920:     }
    6921:     PetscCall(ISGeneralSetIndices(chunkISF, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
    6922:     PetscCall(ISGeneralSetIndices(chunkISN, 2 * cellChunkSize, neighbors, PETSC_USE_POINTER));
    6923:     if (maxDegree <= 1) {
    6924:       if (!affineQuadF) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkISF, &affineQuadF));
    6925:       if (affineQuadF) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, affineQuadF, PETSC_FEGEOM_COHESIVE, &affineGeomF));
    6926:       if (!affineQuadN) {
    6927:         PetscInt dim;
    6928:         PetscCall(PetscQuadratureGetData(affineQuadF, &dim, NULL, NULL, NULL, NULL));
    6929:         PetscCall(DMFieldCreateDefaultFaceQuadrature(coordField, chunkISN, &affineQuadN));
    6930:         PetscCall(PetscQuadratureSetData(affineQuadN, dim + 1, PETSC_DECIDE, PETSC_DECIDE, NULL, NULL));
    6931:       }
    6932:       if (affineQuadN) PetscCall(DMSNESGetFEGeom(coordField, chunkISN, affineQuadN, PETSC_FEGEOM_BASIC, &affineGeomN));
    6933:     } else {
    6934:       PetscInt f;
    6935:       for (f = 0; f < Nf; ++f) {
    6936:         if (quadsF[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkISF, quadsF[f], PETSC_FEGEOM_COHESIVE, &geomsF[f]));
    6937:       }
    6938:     }

    6940:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
    6941:       PetscFE         feI;
    6942:       PetscFEGeom    *geomF      = affineGeomF ? affineGeomF : geomsF[fieldI];
    6943:       PetscFEGeom    *chunkGeomF = NULL, *remGeomF = NULL;
    6944:       PetscFEGeom    *geomN      = affineGeomN ? affineGeomN : geomsF[fieldI];
    6945:       PetscFEGeom    *chunkGeomN = NULL, *remGeomN = NULL;
    6946:       PetscQuadrature quadF = affineQuadF ? affineQuadF : quadsF[fieldI];
    6947:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
    6948:       PetscBool       isCohesiveField;

    6950:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
    6951:       if (!feI) continue;
    6952:       PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
    6953:       PetscCall(PetscQuadratureGetData(quadF, NULL, NULL, &Nq, NULL, NULL));
    6954:       PetscCall(PetscFEGetDimension(feI, &Nb));
    6955:       blockSize = Nb;
    6956:       batchSize = numBlocks * blockSize;
    6957:       PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
    6958:       numChunks = numCells / (numBatches * batchSize);
    6959:       Ne        = numChunks * numBatches * batchSize;
    6960:       Nr        = numCells % (numBatches * batchSize);
    6961:       offset    = numCells - Nr;
    6962:       PetscCall(PetscFEGeomGetChunk(geomF, 0, offset * 2, &chunkGeomF));
    6963:       PetscCall(PetscFEGeomGetChunk(geomF, offset * 2, numCells * 2, &remGeomF));
    6964:       PetscCall(PetscFEGeomGetChunk(geomN, 0, offset * 2, &chunkGeomN));
    6965:       PetscCall(PetscFEGeomGetChunk(geomN, offset * 2, numCells * 2, &remGeomN));
    6966:       PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
    6967:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    6968:         PetscFE feJ;

    6970:         PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
    6971:         if (!feJ) continue;
    6972:         key[0].field = fieldI * Nf + fieldJ;
    6973:         key[1].field = fieldI * Nf + fieldJ;
    6974:         key[2].field = fieldI * Nf + fieldJ;
    6975:         if (hasBdJac) {
    6976:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg));
    6977:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], PetscSafePointerPlusOffset(a[0], offset * totDimAux[0]), t, X_tShift, &elemMatNeg[offset * totDim * totDim]));
    6978:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos));
    6979:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], PetscSafePointerPlusOffset(a[1], offset * totDimAux[1]), t, X_tShift, &elemMatPos[offset * totDim * totDim]));
    6980:         }
    6981:         if (hasBdPrec) {
    6982:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP));
    6983:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNegP[offset * totDim * totDim]));
    6984:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP));
    6985:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPosP[offset * totDim * totDim]));
    6986:         }
    6987:         if (hasBdJac) {
    6988:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh));
    6989:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], PetscSafePointerPlusOffset(a[2], offset * totDimAux[2]), t, X_tShift, &elemMatCoh[offset * totDim * totDim]));
    6990:         }
    6991:         if (hasBdPrec) {
    6992:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeomF, chunkGeomN, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP));
    6993:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Nr, remGeomF, remGeomN, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCohP[offset * totDim * totDim]));
    6994:         }
    6995:       }
    6996:       PetscCall(PetscFEGeomRestoreChunk(geomF, offset, numCells, &remGeomF));
    6997:       PetscCall(PetscFEGeomRestoreChunk(geomF, 0, offset, &chunkGeomF));
    6998:       PetscCall(PetscFEGeomRestoreChunk(geomN, offset, numCells, &remGeomN));
    6999:       PetscCall(PetscFEGeomRestoreChunk(geomN, 0, offset, &chunkGeomN));
    7000:     }
    7001:     /* Insert values into matrix */
    7002:     for (c = cS; c < cE; ++c) {
    7003:       const PetscInt cell = cells ? cells[c] : c;
    7004:       const PetscInt cind = c - cS, coff = cind * totDim * totDim;
    7005:       PetscInt       i, j;

    7007:       /* Scale element values */
    7008:       if (locS[0]) {
    7009:         PetscInt  Nb, soff = cind * totDimScale[0], off = 0;
    7010:         PetscBool cohesive;

    7012:         for (fieldI = 0; fieldI < Nf; ++fieldI) {
    7013:           PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
    7014:           PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));

    7016:           if (fieldI == key[2].field) {
    7017:             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
    7018:             for (i = 0; i < Nb; ++i) {
    7019:               for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += s[0][soff + i] * elemMatNeg[coff + (off + i) * totDim + j] + s[1][soff + i] * elemMatPos[coff + (off + i) * totDim + j];
    7020:               if (hasBdPrec)
    7021:                 for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += s[0][soff + i] * elemMatNegP[coff + (off + i) * totDim + j] + s[1][soff + i] * elemMatPosP[coff + (off + i) * totDim + j];
    7022:             }
    7023:             off += Nb;
    7024:           } else {
    7025:             const PetscInt N = cohesive ? Nb : Nb * 2;

    7027:             for (i = 0; i < N; ++i) {
    7028:               for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j];
    7029:               if (hasBdPrec)
    7030:                 for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += elemMatNegP[coff + (off + i) * totDim + j] + elemMatPosP[coff + (off + i) * totDim + j];
    7031:             }
    7032:             off += N;
    7033:           }
    7034:         }
    7035:       } else {
    7036:         for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i];
    7037:         if (hasBdPrec)
    7038:           for (i = 0; i < totDim * totDim; ++i) elemMatCohP[coff + i] += elemMatNegP[coff + i] + elemMatPosP[coff + i];
    7039:       }
    7040:       if (hasBdPrec) {
    7041:         if (hasBdJac) {
    7042:           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
    7043:           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
    7044:         }
    7045:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim]));
    7046:         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES));
    7047:       } else if (hasBdJac) {
    7048:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
    7049:         PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
    7050:       }
    7051:     }
    7052:   }
    7053:   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
    7054:   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
    7055:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
    7056:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
    7057:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
    7058:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
    7059:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
    7060:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
    7061:   PetscCall(PetscFree2(faces, neighbors));
    7062:   PetscCall(ISDestroy(&chunkISF));
    7063:   PetscCall(ISDestroy(&chunkISN));
    7064:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    7065:   if (maxDegree <= 1) {
    7066:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadF, PETSC_FALSE, &affineGeomF));
    7067:     PetscCall(PetscQuadratureDestroy(&affineQuadF));
    7068:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuadN, PETSC_FALSE, &affineGeomN));
    7069:     PetscCall(PetscQuadratureDestroy(&affineQuadN));
    7070:   } else {
    7071:     PetscInt f;
    7072:     for (f = 0; f < Nf; ++f) {
    7073:       if (geomsF) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quadsF[f], PETSC_FALSE, &geomsF[f]));
    7074:       if (quadsF) PetscCall(PetscQuadratureDestroy(&quadsF[f]));
    7075:     }
    7076:     PetscCall(PetscFree2(quadsF, geomsF));
    7077:   }
    7078:   if (dmAux[2]) PetscCall(DMDestroy(&plexA));
    7079:   PetscCall(DMDestroy(&plex));
    7080: end:
    7081:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    7082:   PetscFunctionReturn(PETSC_SUCCESS);
    7083: }

    7085: /*@
    7086:   DMPlexComputeJacobianActionByKey - Compute the local Jacobian for terms matching the input key

    7088:   Collective

    7090:   Input Parameters:
    7091: + dm       - The output `DM`
    7092: . key      - The `PetscFormKey` indicating what should be integrated
    7093: . cellIS   - The `IS` give a set of cells to integrate over
    7094: . t        - The time
    7095: . X_tShift - The multiplier for the Jacobian with respect to $X_t$
    7096: . locX     - The local solution
    7097: . locX_t   - The time derivative of the local solution, or `NULL` for time-independent problems
    7098: . locY     - The local vector acted on by J
    7099: - ctx      - An optional application context, passed to the pointwise functions

    7101:   Output Parameter:
    7102: . locF - The local residual F = J(X) Y

    7104:   Level: developer

    7106: .seealso: `DMPlexComputeResidualByKey()`, `DMPlexComputeJacobianByKey()`, `DMPlexComputeResidualHybridByKey()`, `DMPlexComputeJacobianHybridByKey()`, `PetscFormKey`
    7107: @*/
    7108: PetscErrorCode DMPlexComputeJacobianActionByKey(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Vec locY, Vec locF, PetscCtx ctx)
    7109: {
    7110:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
    7111:   const char     *name  = "Jacobian";
    7112:   DM              dmAux = NULL, plex, plexAux = NULL;
    7113:   DMEnclosureType encAux;
    7114:   Vec             A;
    7115:   DMField         coordField;
    7116:   PetscDS         prob, probAux = NULL;
    7117:   PetscQuadrature quad;
    7118:   PetscSection    section, globalSection, sectionAux;
    7119:   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
    7120:   const PetscInt *cells;
    7121:   PetscInt        Nf, fieldI, fieldJ;
    7122:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
    7123:   PetscBool       hasDyn;

    7125:   PetscFunctionBegin;
    7126:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    7127:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    7128:   PetscCall(ISGetLocalSize(cellIS, &numCells));
    7129:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    7130:   PetscCall(DMGetLocalSection(dm, &section));
    7131:   PetscCall(DMGetGlobalSection(dm, &globalSection));
    7132:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
    7133:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    7134:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    7135:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
    7136:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
    7137:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
    7138:   if (A) {
    7139:     PetscCall(VecGetDM(A, &dmAux));
    7140:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    7141:     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
    7142:     PetscCall(DMGetLocalSection(plexAux, &sectionAux));
    7143:     PetscCall(DMGetDS(dmAux, &probAux));
    7144:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    7145:   }
    7146:   PetscCall(VecSet(locF, 0.0));
    7147:   PetscCall(PetscMalloc6(numCells * totDim, &u, (locX_t ? (size_t)numCells * totDim : 0), &u_t, numCells * totDim * totDim, &elemMat, (hasDyn ? (size_t)numCells * totDim * totDim : 0), &elemMatD, numCells * totDim, &y, totDim, &z));
    7148:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
    7149:   PetscCall(DMGetCoordinateField(dm, &coordField));
    7150:   for (c = cStart; c < cEnd; ++c) {
    7151:     const PetscInt cell = cells ? cells[c] : c;
    7152:     const PetscInt cind = c - cStart;
    7153:     PetscScalar   *x = NULL, *x_t = NULL;
    7154:     PetscInt       i;

    7156:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
    7157:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
    7158:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
    7159:     if (locX_t) {
    7160:       PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
    7161:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
    7162:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
    7163:     }
    7164:     if (dmAux) {
    7165:       PetscInt subcell;
    7166:       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
    7167:       PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
    7168:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
    7169:       PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
    7170:     }
    7171:     PetscCall(DMPlexVecGetClosure(plex, section, locY, cell, NULL, &x));
    7172:     for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
    7173:     PetscCall(DMPlexVecRestoreClosure(plex, section, locY, cell, NULL, &x));
    7174:   }
    7175:   PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
    7176:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
    7177:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
    7178:     PetscFE  fe;
    7179:     PetscInt Nb;
    7180:     /* Conforming batches */
    7181:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    7182:     /* Remainder */
    7183:     PetscInt        Nr, offset, Nq;
    7184:     PetscQuadrature qGeom = NULL;
    7185:     PetscInt        maxDegree;
    7186:     PetscFEGeom    *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

    7188:     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
    7189:     PetscCall(PetscFEGetQuadrature(fe, &quad));
    7190:     PetscCall(PetscFEGetDimension(fe, &Nb));
    7191:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    7192:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    7193:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
    7194:     if (!qGeom) {
    7195:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
    7196:       PetscCall(PetscObjectReference((PetscObject)qGeom));
    7197:     }
    7198:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
    7199:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FEGEOM_BASIC, &cgeomFEM));
    7200:     blockSize = Nb;
    7201:     batchSize = numBlocks * blockSize;
    7202:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    7203:     numChunks = numCells / (numBatches * batchSize);
    7204:     Ne        = numChunks * numBatches * batchSize;
    7205:     Nr        = numCells % (numBatches * batchSize);
    7206:     offset    = numCells - Nr;
    7207:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
    7208:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
    7209:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    7210:       key.field = fieldI * Nf + fieldJ;
    7211:       PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
    7212:       PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, X_tShift, &elemMat[offset * totDim * totDim]));
    7213:       if (hasDyn) {
    7214:         PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
    7215:         PetscCall(PetscFEIntegrateJacobian(prob, prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, X_tShift, &elemMatD[offset * totDim * totDim]));
    7216:       }
    7217:     }
    7218:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
    7219:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
    7220:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
    7221:     PetscCall(PetscQuadratureDestroy(&qGeom));
    7222:   }
    7223:   if (hasDyn) {
    7224:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
    7225:   }
    7226:   for (c = cStart; c < cEnd; ++c) {
    7227:     const PetscInt     cell = cells ? cells[c] : c;
    7228:     const PetscInt     cind = c - cStart;
    7229:     const PetscBLASInt one  = 1;
    7230:     PetscBLASInt       M;
    7231:     const PetscScalar  a = 1.0, b = 0.0;

    7233:     PetscCall(PetscBLASIntCast(totDim, &M));
    7234:     PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
    7235:     if (mesh->printFEM > 1) {
    7236:       PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
    7237:       PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
    7238:       PetscCall(DMPrintCellVector(c, "Z", totDim, z));
    7239:     }
    7240:     PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, z, ADD_VALUES));
    7241:   }
    7242:   PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
    7243:   if (mesh->printFEM) {
    7244:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)locF), "Z:\n"));
    7245:     PetscCall(VecView(locF, NULL));
    7246:   }
    7247:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    7248:   PetscCall(PetscFree(a));
    7249:   PetscCall(DMDestroy(&plexAux));
    7250:   PetscCall(DMDestroy(&plex));
    7251:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    7252:   PetscFunctionReturn(PETSC_SUCCESS);
    7253: }

    7255: static void f0_1(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
    7256: {
    7257:   f0[0] = u[0];
    7258: }

    7260: static void f0_x(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
    7261: {
    7262:   f0[0] = x[(int)PetscRealPart(constants[0])] * u[0];
    7263: }

    7265: static void f0_x2(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
    7266: {
    7267:   PetscInt d;

    7269:   f0[0] = 0.0;
    7270:   for (d = 0; d < dim; ++d) f0[0] += PetscSqr(x[d]) * u[0];
    7271: }

    7273: /*@
    7274:   DMPlexComputeMoments - Compute the first three moments for a field

    7276:   Noncollective

    7278:   Input Parameters:
    7279: + dm - the `DMPLEX`
    7280: - u  - the field

    7282:   Output Parameter:
    7283: . moments - the field moments

    7285:   Level: intermediate

    7287:   Note:
    7288:   The `moments` array should be of length cdim + 2, where cdim is the number of components for the coordinate field.

    7290: .seealso: `DM`, `DMPLEX`, `DMSwarmComputeMoments()`
    7291: @*/
    7292: PetscErrorCode DMPlexComputeMoments(DM dm, Vec u, PetscReal moments[])
    7293: {
    7294:   PetscDS            ds;
    7295:   PetscScalar        mom, constants[1];
    7296:   const PetscScalar *oldConstants;
    7297:   PetscInt           cdim, Nf, field = 0, Ncon;
    7298:   MPI_Comm           comm;
    7299:   void              *ctx;

    7301:   PetscFunctionBeginUser;
    7302:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
    7303:   PetscCall(DMGetCoordinateDim(dm, &cdim));
    7304:   PetscCall(DMGetApplicationContext(dm, &ctx));
    7305:   PetscCall(DMGetDS(dm, &ds));
    7306:   PetscCall(PetscDSGetNumFields(ds, &Nf));
    7307:   PetscCall(PetscDSGetConstants(ds, &Ncon, &oldConstants));
    7308:   PetscCheck(Nf == 1, comm, PETSC_ERR_ARG_WRONG, "We currently only support 1 field, not %" PetscInt_FMT, Nf);
    7309:   PetscCall(PetscDSSetObjective(ds, field, &f0_1));
    7310:   PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, ctx));
    7311:   moments[0] = PetscRealPart(mom);
    7312:   for (PetscInt c = 0; c < cdim; ++c) {
    7313:     constants[0] = c;
    7314:     PetscCall(PetscDSSetConstants(ds, 1, constants));
    7315:     PetscCall(PetscDSSetObjective(ds, field, &f0_x));
    7316:     PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, ctx));
    7317:     moments[c + 1] = PetscRealPart(mom);
    7318:   }
    7319:   PetscCall(PetscDSSetObjective(ds, field, &f0_x2));
    7320:   PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, ctx));
    7321:   moments[cdim + 1] = PetscRealPart(mom);
    7322:   PetscCall(PetscDSSetConstants(ds, Ncon, (PetscScalar *)oldConstants));
    7323:   PetscFunctionReturn(PETSC_SUCCESS);
    7324: }