Date: 2025-03-28T21:10:46+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 PetscContainerUserDestroy_PetscFEGeom(void *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, PetscBool faceData, 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, (void **)geom));
     71:   } else {
     72:     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
     73:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
     74:     PetscCall(PetscContainerSetPointer(container, (void *)*geom));
     75:     PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_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, PetscBool faceData, 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, void *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];

    342:     PetscCall(VecNormalize(mode[i], NULL));
    343:     PetscCall(VecMDot(mode[i], mmin - i - 1, mode + i + 1, dots + i + 1));
    344:     for (j = i + 1; j < mmin; ++j) {
    345:       dots[j] *= -1.0;
    346:       PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
    347:     }
    348:   }
    349:   PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, mmin, mode, sp));
    350:   for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
    351:   PetscCall(PetscFree2(func, ctxs));
    352:   PetscFunctionReturn(PETSC_SUCCESS);
    353: }

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

    358:   Collective

    360:   Input Parameters:
    361: + dm    - the `DM`
    362: . nb    - The number of bodies
    363: . label - The `DMLabel` marking each domain
    364: . nids  - The number of ids per body
    365: - ids   - An array of the label ids in sequence for each domain

    367:   Output Parameter:
    368: . sp - the null space

    370:   Level: advanced

    372:   Note:
    373:   This is necessary to provide a suitable coarse space for algebraic multigrid

    375: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`
    376: @*/
    377: PetscErrorCode DMPlexCreateRigidBodies(DM dm, PetscInt nb, DMLabel label, const PetscInt nids[], const PetscInt ids[], MatNullSpace *sp)
    378: {
    379:   MPI_Comm     comm;
    380:   PetscSection section, globalSection;
    381:   Vec         *mode;
    382:   PetscScalar *dots;
    383:   PetscInt     dim, dimEmbed, n, m, b, d, i, j, off;

    385:   PetscFunctionBegin;
    386:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
    387:   PetscCall(DMGetDimension(dm, &dim));
    388:   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
    389:   PetscCall(DMGetLocalSection(dm, &section));
    390:   PetscCall(DMGetGlobalSection(dm, &globalSection));
    391:   PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n));
    392:   m = nb * (dim * (dim + 1)) / 2;
    393:   PetscCall(PetscMalloc2(m, &mode, m, &dots));
    394:   PetscCall(VecCreate(comm, &mode[0]));
    395:   PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE));
    396:   PetscCall(VecSetUp(mode[0]));
    397:   for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i]));
    398:   for (b = 0, off = 0; b < nb; ++b) {
    399:     for (d = 0; d < m / nb; ++d) {
    400:       PetscInt ctx[2];
    401:       PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *) = DMPlexProjectRigidBody_Private;
    402:       void *voidctx                                                                                   = (void *)(&ctx[0]);

    404:       ctx[0] = dimEmbed;
    405:       ctx[1] = d;
    406:       PetscCall(DMProjectFunctionLabel(dm, 0.0, label, nids[b], &ids[off], 0, NULL, &func, &voidctx, INSERT_VALUES, mode[d]));
    407:       off += nids[b];
    408:     }
    409:   }
    410:   /* Orthonormalize system */
    411:   for (i = 0; i < m; ++i) {
    412:     PetscScalar dots[6];

    414:     PetscCall(VecNormalize(mode[i], NULL));
    415:     PetscCall(VecMDot(mode[i], m - i - 1, mode + i + 1, dots + i + 1));
    416:     for (j = i + 1; j < m; ++j) {
    417:       dots[j] *= -1.0;
    418:       PetscCall(VecAXPY(mode[j], dots[j], mode[i]));
    419:     }
    420:   }
    421:   PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, m, mode, sp));
    422:   for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i]));
    423:   PetscCall(PetscFree2(mode, dots));
    424:   PetscFunctionReturn(PETSC_SUCCESS);
    425: }

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

    432:   Input Parameters:
    433: + dm     - the `DMPLEX` object
    434: - height - the maximum projection height >= 0

    436:   Level: advanced

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

    446: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
    447: @*/
    448: PetscErrorCode DMPlexSetMaxProjectionHeight(DM dm, PetscInt height)
    449: {
    450:   DM_Plex *plex = (DM_Plex *)dm->data;

    452:   PetscFunctionBegin;
    454:   plex->maxProjectionHeight = height;
    455:   PetscFunctionReturn(PETSC_SUCCESS);
    456: }

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

    462:   Input Parameter:
    463: . dm - the `DMPLEX` object

    465:   Output Parameter:
    466: . height - the maximum projection height

    468:   Level: intermediate

    470: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()`
    471: @*/
    472: PetscErrorCode DMPlexGetMaxProjectionHeight(DM dm, PetscInt *height)
    473: {
    474:   DM_Plex *plex = (DM_Plex *)dm->data;

    476:   PetscFunctionBegin;
    478:   *height = plex->maxProjectionHeight;
    479:   PetscFunctionReturn(PETSC_SUCCESS);
    480: }

    482: typedef struct {
    483:   PetscReal    alpha; /* The first Euler angle, and in 2D the only one */
    484:   PetscReal    beta;  /* The second Euler angle */
    485:   PetscReal    gamma; /* The third Euler angle */
    486:   PetscInt     dim;   /* The dimension of R */
    487:   PetscScalar *R;     /* The rotation matrix, transforming a vector in the local basis to the global basis */
    488:   PetscScalar *RT;    /* The transposed rotation matrix, transforming a vector in the global basis to the local basis */
    489: } RotCtx;

    491: /*
    492:   Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
    493:   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:
    494:   $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
    495:   $ 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.
    496:   $ The XYZ system rotates a third time about the z axis by gamma.
    497: */
    498: static PetscErrorCode DMPlexBasisTransformSetUp_Rotation_Internal(DM dm, void *ctx)
    499: {
    500:   RotCtx   *rc  = (RotCtx *)ctx;
    501:   PetscInt  dim = rc->dim;
    502:   PetscReal c1, s1, c2, s2, c3, s3;

    504:   PetscFunctionBegin;
    505:   PetscCall(PetscMalloc2(PetscSqr(dim), &rc->R, PetscSqr(dim), &rc->RT));
    506:   switch (dim) {
    507:   case 2:
    508:     c1       = PetscCosReal(rc->alpha);
    509:     s1       = PetscSinReal(rc->alpha);
    510:     rc->R[0] = c1;
    511:     rc->R[1] = s1;
    512:     rc->R[2] = -s1;
    513:     rc->R[3] = c1;
    514:     PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
    515:     DMPlex_Transpose2D_Internal(rc->RT);
    516:     break;
    517:   case 3:
    518:     c1       = PetscCosReal(rc->alpha);
    519:     s1       = PetscSinReal(rc->alpha);
    520:     c2       = PetscCosReal(rc->beta);
    521:     s2       = PetscSinReal(rc->beta);
    522:     c3       = PetscCosReal(rc->gamma);
    523:     s3       = PetscSinReal(rc->gamma);
    524:     rc->R[0] = c1 * c3 - c2 * s1 * s3;
    525:     rc->R[1] = c3 * s1 + c1 * c2 * s3;
    526:     rc->R[2] = s2 * s3;
    527:     rc->R[3] = -c1 * s3 - c2 * c3 * s1;
    528:     rc->R[4] = c1 * c2 * c3 - s1 * s3;
    529:     rc->R[5] = c3 * s2;
    530:     rc->R[6] = s1 * s2;
    531:     rc->R[7] = -c1 * s2;
    532:     rc->R[8] = c2;
    533:     PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim)));
    534:     DMPlex_Transpose3D_Internal(rc->RT);
    535:     break;
    536:   default:
    537:     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Dimension %" PetscInt_FMT " not supported", dim);
    538:   }
    539:   PetscFunctionReturn(PETSC_SUCCESS);
    540: }

    542: static PetscErrorCode DMPlexBasisTransformDestroy_Rotation_Internal(DM dm, void *ctx)
    543: {
    544:   RotCtx *rc = (RotCtx *)ctx;

    546:   PetscFunctionBegin;
    547:   PetscCall(PetscFree2(rc->R, rc->RT));
    548:   PetscCall(PetscFree(rc));
    549:   PetscFunctionReturn(PETSC_SUCCESS);
    550: }

    552: static PetscErrorCode DMPlexBasisTransformGetMatrix_Rotation_Internal(DM dm, const PetscReal x[], PetscBool l2g, const PetscScalar **A, void *ctx)
    553: {
    554:   RotCtx *rc = (RotCtx *)ctx;

    556:   PetscFunctionBeginHot;
    557:   PetscAssertPointer(ctx, 5);
    558:   if (l2g) {
    559:     *A = rc->R;
    560:   } else {
    561:     *A = rc->RT;
    562:   }
    563:   PetscFunctionReturn(PETSC_SUCCESS);
    564: }

    566: PetscErrorCode DMPlexBasisTransformApplyReal_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscReal *y, PetscReal *z, void *ctx)
    567: {
    568:   PetscFunctionBegin;
    569: #if defined(PETSC_USE_COMPLEX)
    570:   switch (dim) {
    571:   case 2: {
    572:     PetscScalar yt[2] = {y[0], y[1]}, zt[2] = {0.0, 0.0};

    574:     PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
    575:     z[0] = PetscRealPart(zt[0]);
    576:     z[1] = PetscRealPart(zt[1]);
    577:   } break;
    578:   case 3: {
    579:     PetscScalar yt[3] = {y[0], y[1], y[2]}, zt[3] = {0.0, 0.0, 0.0};

    581:     PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx));
    582:     z[0] = PetscRealPart(zt[0]);
    583:     z[1] = PetscRealPart(zt[1]);
    584:     z[2] = PetscRealPart(zt[2]);
    585:   } break;
    586:   }
    587: #else
    588:   PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, y, z, ctx));
    589: #endif
    590:   PetscFunctionReturn(PETSC_SUCCESS);
    591: }

    593: PetscErrorCode DMPlexBasisTransformApply_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscScalar *y, PetscScalar *z, void *ctx)
    594: {
    595:   const PetscScalar *A;

    597:   PetscFunctionBeginHot;
    598:   PetscCall((*dm->transformGetMatrix)(dm, x, l2g, &A, ctx));
    599:   switch (dim) {
    600:   case 2:
    601:     DMPlex_Mult2D_Internal(A, 1, y, z);
    602:     break;
    603:   case 3:
    604:     DMPlex_Mult3D_Internal(A, 1, y, z);
    605:     break;
    606:   }
    607:   PetscFunctionReturn(PETSC_SUCCESS);
    608: }

    610: static PetscErrorCode DMPlexBasisTransformField_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscInt f, PetscBool l2g, PetscScalar *a)
    611: {
    612:   PetscSection       ts;
    613:   const PetscScalar *ta, *tva;
    614:   PetscInt           dof;

    616:   PetscFunctionBeginHot;
    617:   PetscCall(DMGetLocalSection(tdm, &ts));
    618:   PetscCall(PetscSectionGetFieldDof(ts, p, f, &dof));
    619:   PetscCall(VecGetArrayRead(tv, &ta));
    620:   PetscCall(DMPlexPointLocalFieldRead(tdm, p, f, ta, &tva));
    621:   if (l2g) {
    622:     switch (dof) {
    623:     case 4:
    624:       DMPlex_Mult2D_Internal(tva, 1, a, a);
    625:       break;
    626:     case 9:
    627:       DMPlex_Mult3D_Internal(tva, 1, a, a);
    628:       break;
    629:     }
    630:   } else {
    631:     switch (dof) {
    632:     case 4:
    633:       DMPlex_MultTranspose2D_Internal(tva, 1, a, a);
    634:       break;
    635:     case 9:
    636:       DMPlex_MultTranspose3D_Internal(tva, 1, a, a);
    637:       break;
    638:     }
    639:   }
    640:   PetscCall(VecRestoreArrayRead(tv, &ta));
    641:   PetscFunctionReturn(PETSC_SUCCESS);
    642: }

    644: static PetscErrorCode DMPlexBasisTransformFieldTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt pf, PetscInt f, PetscInt pg, PetscInt g, PetscBool l2g, PetscInt lda, PetscScalar *a)
    645: {
    646:   PetscSection       s, ts;
    647:   const PetscScalar *ta, *tvaf, *tvag;
    648:   PetscInt           fdof, gdof, fpdof, gpdof;

    650:   PetscFunctionBeginHot;
    651:   PetscCall(DMGetLocalSection(dm, &s));
    652:   PetscCall(DMGetLocalSection(tdm, &ts));
    653:   PetscCall(PetscSectionGetFieldDof(s, pf, f, &fpdof));
    654:   PetscCall(PetscSectionGetFieldDof(s, pg, g, &gpdof));
    655:   PetscCall(PetscSectionGetFieldDof(ts, pf, f, &fdof));
    656:   PetscCall(PetscSectionGetFieldDof(ts, pg, g, &gdof));
    657:   PetscCall(VecGetArrayRead(tv, &ta));
    658:   PetscCall(DMPlexPointLocalFieldRead(tdm, pf, f, ta, &tvaf));
    659:   PetscCall(DMPlexPointLocalFieldRead(tdm, pg, g, ta, &tvag));
    660:   if (l2g) {
    661:     switch (fdof) {
    662:     case 4:
    663:       DMPlex_MatMult2D_Internal(tvaf, gpdof, lda, a, a);
    664:       break;
    665:     case 9:
    666:       DMPlex_MatMult3D_Internal(tvaf, gpdof, lda, a, a);
    667:       break;
    668:     }
    669:     switch (gdof) {
    670:     case 4:
    671:       DMPlex_MatMultTransposeLeft2D_Internal(tvag, fpdof, lda, a, a);
    672:       break;
    673:     case 9:
    674:       DMPlex_MatMultTransposeLeft3D_Internal(tvag, fpdof, lda, a, a);
    675:       break;
    676:     }
    677:   } else {
    678:     switch (fdof) {
    679:     case 4:
    680:       DMPlex_MatMultTranspose2D_Internal(tvaf, gpdof, lda, a, a);
    681:       break;
    682:     case 9:
    683:       DMPlex_MatMultTranspose3D_Internal(tvaf, gpdof, lda, a, a);
    684:       break;
    685:     }
    686:     switch (gdof) {
    687:     case 4:
    688:       DMPlex_MatMultLeft2D_Internal(tvag, fpdof, lda, a, a);
    689:       break;
    690:     case 9:
    691:       DMPlex_MatMultLeft3D_Internal(tvag, fpdof, lda, a, a);
    692:       break;
    693:     }
    694:   }
    695:   PetscCall(VecRestoreArrayRead(tv, &ta));
    696:   PetscFunctionReturn(PETSC_SUCCESS);
    697: }

    699: PetscErrorCode DMPlexBasisTransformPoint_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool fieldActive[], PetscBool l2g, PetscScalar *a)
    700: {
    701:   PetscSection    s;
    702:   PetscSection    clSection;
    703:   IS              clPoints;
    704:   const PetscInt *clp;
    705:   PetscInt       *points = NULL;
    706:   PetscInt        Nf, f, Np, cp, dof, d = 0;

    708:   PetscFunctionBegin;
    709:   PetscCall(DMGetLocalSection(dm, &s));
    710:   PetscCall(PetscSectionGetNumFields(s, &Nf));
    711:   PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
    712:   for (f = 0; f < Nf; ++f) {
    713:     for (cp = 0; cp < Np * 2; cp += 2) {
    714:       PetscCall(PetscSectionGetFieldDof(s, points[cp], f, &dof));
    715:       if (!dof) continue;
    716:       if (fieldActive[f]) PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, points[cp], f, l2g, &a[d]));
    717:       d += dof;
    718:     }
    719:   }
    720:   PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
    721:   PetscFunctionReturn(PETSC_SUCCESS);
    722: }

    724: PetscErrorCode DMPlexBasisTransformPointTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool l2g, PetscInt lda, PetscScalar *a)
    725: {
    726:   PetscSection    s;
    727:   PetscSection    clSection;
    728:   IS              clPoints;
    729:   const PetscInt *clp;
    730:   PetscInt       *points = NULL;
    731:   PetscInt        Nf, f, g, Np, cpf, cpg, fdof, gdof, r, c = 0;

    733:   PetscFunctionBegin;
    734:   PetscCall(DMGetLocalSection(dm, &s));
    735:   PetscCall(PetscSectionGetNumFields(s, &Nf));
    736:   PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp));
    737:   for (f = 0, r = 0; f < Nf; ++f) {
    738:     for (cpf = 0; cpf < Np * 2; cpf += 2) {
    739:       PetscCall(PetscSectionGetFieldDof(s, points[cpf], f, &fdof));
    740:       for (g = 0, c = 0; g < Nf; ++g) {
    741:         for (cpg = 0; cpg < Np * 2; cpg += 2) {
    742:           PetscCall(PetscSectionGetFieldDof(s, points[cpg], g, &gdof));
    743:           PetscCall(DMPlexBasisTransformFieldTensor_Internal(dm, tdm, tv, points[cpf], f, points[cpg], g, l2g, lda, &a[r * lda + c]));
    744:           c += gdof;
    745:         }
    746:       }
    747:       PetscCheck(c == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of columns %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
    748:       r += fdof;
    749:     }
    750:   }
    751:   PetscCheck(r == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of rows %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda);
    752:   PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp));
    753:   PetscFunctionReturn(PETSC_SUCCESS);
    754: }

    756: static PetscErrorCode DMPlexBasisTransform_Internal(DM dm, Vec lv, PetscBool l2g)
    757: {
    758:   DM                 tdm;
    759:   Vec                tv;
    760:   PetscSection       ts, s;
    761:   const PetscScalar *ta;
    762:   PetscScalar       *a, *va;
    763:   PetscInt           pStart, pEnd, p, Nf, f;

    765:   PetscFunctionBegin;
    766:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
    767:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
    768:   PetscCall(DMGetLocalSection(tdm, &ts));
    769:   PetscCall(DMGetLocalSection(dm, &s));
    770:   PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
    771:   PetscCall(PetscSectionGetNumFields(s, &Nf));
    772:   PetscCall(VecGetArray(lv, &a));
    773:   PetscCall(VecGetArrayRead(tv, &ta));
    774:   for (p = pStart; p < pEnd; ++p) {
    775:     for (f = 0; f < Nf; ++f) {
    776:       PetscCall(DMPlexPointLocalFieldRef(dm, p, f, a, &va));
    777:       PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, p, f, l2g, va));
    778:     }
    779:   }
    780:   PetscCall(VecRestoreArray(lv, &a));
    781:   PetscCall(VecRestoreArrayRead(tv, &ta));
    782:   PetscFunctionReturn(PETSC_SUCCESS);
    783: }

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

    788:   Input Parameters:
    789: + dm - The `DM`
    790: - lv - A local vector with values in the global basis

    792:   Output Parameter:
    793: . lv - A local vector with values in the local basis

    795:   Level: developer

    797:   Note:
    798:   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.

    800: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexLocalToGlobalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
    801: @*/
    802: PetscErrorCode DMPlexGlobalToLocalBasis(DM dm, Vec lv)
    803: {
    804:   PetscFunctionBegin;
    807:   PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_FALSE));
    808:   PetscFunctionReturn(PETSC_SUCCESS);
    809: }

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

    814:   Input Parameters:
    815: + dm - The `DM`
    816: - lv - A local vector with values in the local basis

    818:   Output Parameter:
    819: . lv - A local vector with values in the global basis

    821:   Level: developer

    823:   Note:
    824:   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.

    826: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()`
    827: @*/
    828: PetscErrorCode DMPlexLocalToGlobalBasis(DM dm, Vec lv)
    829: {
    830:   PetscFunctionBegin;
    833:   PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_TRUE));
    834:   PetscFunctionReturn(PETSC_SUCCESS);
    835: }

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

    841:   Input Parameters:
    842: + dm    - The `DM`
    843: . alpha - The first Euler angle, and in 2D the only one
    844: . beta  - The second Euler angle
    845: - gamma - The third Euler angle

    847:   Level: developer

    849:   Note:
    850:   Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that
    851:   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
    852: .vb
    853:    The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis.
    854:    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.
    855:    The XYZ system rotates a third time about the z axis by gamma.
    856: .ve

    858: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMPlexLocalToGlobalBasis()`
    859: @*/
    860: PetscErrorCode DMPlexCreateBasisRotation(DM dm, PetscReal alpha, PetscReal beta, PetscReal gamma)
    861: {
    862:   RotCtx  *rc;
    863:   PetscInt cdim;

    865:   PetscFunctionBegin;
    866:   PetscCall(DMGetCoordinateDim(dm, &cdim));
    867:   PetscCall(PetscMalloc1(1, &rc));
    868:   dm->transformCtx       = rc;
    869:   dm->transformSetUp     = DMPlexBasisTransformSetUp_Rotation_Internal;
    870:   dm->transformDestroy   = DMPlexBasisTransformDestroy_Rotation_Internal;
    871:   dm->transformGetMatrix = DMPlexBasisTransformGetMatrix_Rotation_Internal;
    872:   rc->dim                = cdim;
    873:   rc->alpha              = alpha;
    874:   rc->beta               = beta;
    875:   rc->gamma              = gamma;
    876:   PetscCall((*dm->transformSetUp)(dm, dm->transformCtx));
    877:   PetscCall(DMConstructBasisTransform_Internal(dm));
    878:   PetscFunctionReturn(PETSC_SUCCESS);
    879: }

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

    884:   Input Parameters:
    885: + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
    886: . time   - The time
    887: . field  - The field to constrain
    888: . Nc     - The number of constrained field components, or 0 for all components
    889: . comps  - An array of constrained component numbers, or `NULL` for all components
    890: . label  - The `DMLabel` defining constrained points
    891: . numids - The number of `DMLabel` ids for constrained points
    892: . ids    - An array of ids for constrained points
    893: . func   - A pointwise function giving boundary values
    894: - ctx    - An optional user context for bcFunc

    896:   Output Parameter:
    897: . locX - A local vector to receives the boundary values

    899:   Level: developer

    901: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
    902: @*/
    903: 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 *), void *ctx, Vec locX)
    904: {
    905:   PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx);
    906:   void   **ctxs;
    907:   PetscInt numFields;

    909:   PetscFunctionBegin;
    910:   PetscCall(DMGetNumFields(dm, &numFields));
    911:   PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
    912:   funcs[field] = func;
    913:   ctxs[field]  = ctx;
    914:   PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_BC_VALUES, locX));
    915:   PetscCall(PetscFree2(funcs, ctxs));
    916:   PetscFunctionReturn(PETSC_SUCCESS);
    917: }

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

    922:   Input Parameters:
    923: + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
    924: . time   - The time
    925: . locU   - A local vector with the input solution values
    926: . field  - The field to constrain
    927: . Nc     - The number of constrained field components, or 0 for all components
    928: . comps  - An array of constrained component numbers, or `NULL` for all components
    929: . label  - The `DMLabel` defining constrained points
    930: . numids - The number of `DMLabel` ids for constrained points
    931: . ids    - An array of ids for constrained points
    932: . func   - A pointwise function giving boundary values
    933: - ctx    - An optional user context for bcFunc

    935:   Output Parameter:
    936: . locX - A local vector to receives the boundary values

    938:   Level: developer

    940: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()`
    941: @*/
    942: 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[]), void *ctx, Vec locX)
    943: {
    944:   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[]);
    945:   void   **ctxs;
    946:   PetscInt numFields;

    948:   PetscFunctionBegin;
    949:   PetscCall(DMGetNumFields(dm, &numFields));
    950:   PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
    951:   funcs[field] = func;
    952:   ctxs[field]  = ctx;
    953:   PetscCall(DMProjectFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
    954:   PetscCall(PetscFree2(funcs, ctxs));
    955:   PetscFunctionReturn(PETSC_SUCCESS);
    956: }

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

    961:   Collective

    963:   Input Parameters:
    964: + dm     - The `DM`, with a `PetscDS` that matches the problem being constrained
    965: . time   - The time
    966: . locU   - A local vector with the input solution values
    967: . field  - The field to constrain
    968: . Nc     - The number of constrained field components, or 0 for all components
    969: . comps  - An array of constrained component numbers, or `NULL` for all components
    970: . label  - The `DMLabel` defining constrained points
    971: . numids - The number of `DMLabel` ids for constrained points
    972: . ids    - An array of ids for constrained points
    973: . func   - A pointwise function giving boundary values, the calling sequence is given in `DMProjectBdFieldLabelLocal()`
    974: - ctx    - An optional user context for `func`

    976:   Output Parameter:
    977: . locX - A local vector to receive the boundary values

    979:   Level: developer

    981: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectBdFieldLabelLocal()`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
    982: @*/
    983: 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[]), void *ctx, Vec locX)
    984: {
    985:   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[]);
    986:   void   **ctxs;
    987:   PetscInt numFields;

    989:   PetscFunctionBegin;
    990:   PetscCall(DMGetNumFields(dm, &numFields));
    991:   PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs));
    992:   funcs[field] = func;
    993:   ctxs[field]  = ctx;
    994:   PetscCall(DMProjectBdFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX));
    995:   PetscCall(PetscFree2(funcs, ctxs));
    996:   PetscFunctionReturn(PETSC_SUCCESS);
    997: }

    999: /*@C
    1000:   DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector

    1002:   Input Parameters:
    1003: + dm           - The `DM`, with a `PetscDS` that matches the problem being constrained
    1004: . time         - The time
    1005: . faceGeometry - A vector with the FVM face geometry information
    1006: . cellGeometry - A vector with the FVM cell geometry information
    1007: . Grad         - A vector with the FVM cell gradient information
    1008: . field        - The field to constrain
    1009: . Nc           - The number of constrained field components, or 0 for all components
    1010: . comps        - An array of constrained component numbers, or `NULL` for all components
    1011: . label        - The `DMLabel` defining constrained points
    1012: . numids       - The number of `DMLabel` ids for constrained points
    1013: . ids          - An array of ids for constrained points
    1014: . func         - A pointwise function giving boundary values
    1015: - ctx          - An optional user context for bcFunc

    1017:   Output Parameter:
    1018: . locX - A local vector to receives the boundary values

    1020:   Level: developer

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

    1025: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()`
    1026: @*/
    1027: 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 *), void *ctx, Vec locX)
    1028: {
    1029:   PetscDS            prob;
    1030:   PetscSF            sf;
    1031:   DM                 dmFace, dmCell, dmGrad;
    1032:   const PetscScalar *facegeom, *cellgeom = NULL, *grad;
    1033:   const PetscInt    *leaves;
    1034:   PetscScalar       *x, *fx;
    1035:   PetscInt           dim, nleaves, loc, fStart, fEnd, pdim, i;
    1036:   PetscErrorCode     ierru = PETSC_SUCCESS;

    1038:   PetscFunctionBegin;
    1039:   PetscCall(DMGetPointSF(dm, &sf));
    1040:   PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
    1041:   nleaves = PetscMax(0, nleaves);
    1042:   PetscCall(DMGetDimension(dm, &dim));
    1043:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
    1044:   PetscCall(DMGetDS(dm, &prob));
    1045:   PetscCall(VecGetDM(faceGeometry, &dmFace));
    1046:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
    1047:   if (cellGeometry) {
    1048:     PetscCall(VecGetDM(cellGeometry, &dmCell));
    1049:     PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
    1050:   }
    1051:   if (Grad) {
    1052:     PetscFV fv;

    1054:     PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fv));
    1055:     PetscCall(VecGetDM(Grad, &dmGrad));
    1056:     PetscCall(VecGetArrayRead(Grad, &grad));
    1057:     PetscCall(PetscFVGetNumComponents(fv, &pdim));
    1058:     PetscCall(DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx));
    1059:   }
    1060:   PetscCall(VecGetArray(locX, &x));
    1061:   for (i = 0; i < numids; ++i) {
    1062:     IS              faceIS;
    1063:     const PetscInt *faces;
    1064:     PetscInt        numFaces, f;

    1066:     PetscCall(DMLabelGetStratumIS(label, ids[i], &faceIS));
    1067:     if (!faceIS) continue; /* No points with that id on this process */
    1068:     PetscCall(ISGetLocalSize(faceIS, &numFaces));
    1069:     PetscCall(ISGetIndices(faceIS, &faces));
    1070:     for (f = 0; f < numFaces; ++f) {
    1071:       const PetscInt   face = faces[f], *cells;
    1072:       PetscFVFaceGeom *fg;

    1074:       if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */
    1075:       PetscCall(PetscFindInt(face, nleaves, (PetscInt *)leaves, &loc));
    1076:       if (loc >= 0) continue;
    1077:       PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
    1078:       PetscCall(DMPlexGetSupport(dm, face, &cells));
    1079:       if (Grad) {
    1080:         PetscFVCellGeom *cg;
    1081:         PetscScalar     *cx, *cgrad;
    1082:         PetscScalar     *xG;
    1083:         PetscReal        dx[3];
    1084:         PetscInt         d;

    1086:         PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg));
    1087:         PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &cx));
    1088:         PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad));
    1089:         PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
    1090:         DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx);
    1091:         for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d * dim], dx);
    1092:         PetscCall((*func)(time, fg->centroid, fg->normal, fx, xG, ctx));
    1093:       } else {
    1094:         PetscScalar *xI;
    1095:         PetscScalar *xG;

    1097:         PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &xI));
    1098:         PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG));
    1099:         ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx);
    1100:         if (ierru) {
    1101:           PetscCall(ISRestoreIndices(faceIS, &faces));
    1102:           PetscCall(ISDestroy(&faceIS));
    1103:           goto cleanup;
    1104:         }
    1105:       }
    1106:     }
    1107:     PetscCall(ISRestoreIndices(faceIS, &faces));
    1108:     PetscCall(ISDestroy(&faceIS));
    1109:   }
    1110: cleanup:
    1111:   PetscCall(VecRestoreArray(locX, &x));
    1112:   if (Grad) {
    1113:     PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx));
    1114:     PetscCall(VecRestoreArrayRead(Grad, &grad));
    1115:   }
    1116:   if (cellGeometry) PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
    1117:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
    1118:   PetscCall(ierru);
    1119:   PetscFunctionReturn(PETSC_SUCCESS);
    1120: }

    1122: static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
    1123: {
    1124:   PetscInt c;
    1125:   for (c = 0; c < Nc; ++c) u[c] = 0.0;
    1126:   return PETSC_SUCCESS;
    1127: }

    1129: PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
    1130: {
    1131:   PetscObject isZero;
    1132:   PetscDS     prob;
    1133:   PetscInt    numBd, b;

    1135:   PetscFunctionBegin;
    1136:   PetscCall(DMGetDS(dm, &prob));
    1137:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
    1138:   PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
    1139:   PetscCall(PetscDSUpdateBoundaryLabels(prob, dm));
    1140:   for (b = 0; b < numBd; ++b) {
    1141:     PetscWeakForm           wf;
    1142:     DMBoundaryConditionType type;
    1143:     const char             *name;
    1144:     DMLabel                 label;
    1145:     PetscInt                field, Nc;
    1146:     const PetscInt         *comps;
    1147:     PetscObject             obj;
    1148:     PetscClassId            id;
    1149:     void (*bvfunc)(void);
    1150:     PetscInt        numids;
    1151:     const PetscInt *ids;
    1152:     void           *ctx;

    1154:     PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx));
    1155:     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
    1156:     PetscCall(DMGetField(dm, field, NULL, &obj));
    1157:     PetscCall(PetscObjectGetClassId(obj, &id));
    1158:     if (id == PETSCFE_CLASSID) {
    1159:       switch (type) {
    1160:         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
    1161:       case DM_BC_ESSENTIAL: {
    1162:         PetscSimplePointFn *func = (PetscSimplePointFn *)bvfunc;

    1164:         if (isZero) func = zero;
    1165:         PetscCall(DMPlexLabelAddCells(dm, label));
    1166:         PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func, ctx, locX));
    1167:         PetscCall(DMPlexLabelClearCells(dm, label));
    1168:       } break;
    1169:       case DM_BC_ESSENTIAL_FIELD: {
    1170:         PetscPointFunc func = (PetscPointFunc)bvfunc;

    1172:         PetscCall(DMPlexLabelAddCells(dm, label));
    1173:         PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func, ctx, locX));
    1174:         PetscCall(DMPlexLabelClearCells(dm, label));
    1175:       } break;
    1176:       default:
    1177:         break;
    1178:       }
    1179:     } else if (id == PETSCFV_CLASSID) {
    1180:       {
    1181:         PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *) = (PetscErrorCode (*)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *))bvfunc;

    1183:         if (!faceGeomFVM) continue;
    1184:         PetscCall(DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids, func, ctx, locX));
    1185:       }
    1186:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1187:   }
    1188:   PetscFunctionReturn(PETSC_SUCCESS);
    1189: }

    1191: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
    1192: {
    1193:   PetscObject isZero;
    1194:   PetscDS     prob;
    1195:   PetscInt    numBd, b;

    1197:   PetscFunctionBegin;
    1198:   if (!locX) PetscFunctionReturn(PETSC_SUCCESS);
    1199:   PetscCall(DMGetDS(dm, &prob));
    1200:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
    1201:   PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero));
    1202:   for (b = 0; b < numBd; ++b) {
    1203:     PetscWeakForm           wf;
    1204:     DMBoundaryConditionType type;
    1205:     const char             *name;
    1206:     DMLabel                 label;
    1207:     PetscInt                field, Nc;
    1208:     const PetscInt         *comps;
    1209:     PetscObject             obj;
    1210:     PetscClassId            id;
    1211:     PetscInt                numids;
    1212:     const PetscInt         *ids;
    1213:     void (*bvfunc)(void);
    1214:     void *ctx;

    1216:     PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, NULL, &bvfunc, &ctx));
    1217:     if (insertEssential != (type & DM_BC_ESSENTIAL)) continue;
    1218:     PetscCall(DMGetField(dm, field, NULL, &obj));
    1219:     PetscCall(PetscObjectGetClassId(obj, &id));
    1220:     if (id == PETSCFE_CLASSID) {
    1221:       switch (type) {
    1222:         /* for FEM, there is no insertion to be done for non-essential boundary conditions */
    1223:       case DM_BC_ESSENTIAL: {
    1224:         PetscSimplePointFn *func_t = (PetscSimplePointFn *)bvfunc;

    1226:         if (isZero) func_t = zero;
    1227:         PetscCall(DMPlexLabelAddCells(dm, label));
    1228:         PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
    1229:         PetscCall(DMPlexLabelClearCells(dm, label));
    1230:       } break;
    1231:       case DM_BC_ESSENTIAL_FIELD: {
    1232:         PetscPointFunc func_t = (PetscPointFunc)bvfunc;

    1234:         PetscCall(DMPlexLabelAddCells(dm, label));
    1235:         PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func_t, ctx, locX));
    1236:         PetscCall(DMPlexLabelClearCells(dm, label));
    1237:       } break;
    1238:       default:
    1239:         break;
    1240:       }
    1241:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1242:   }
    1243:   PetscFunctionReturn(PETSC_SUCCESS);
    1244: }

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

    1249:   Not Collective

    1251:   Input Parameters:
    1252: + dm              - The `DM`
    1253: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
    1254: . time            - The time
    1255: . faceGeomFVM     - Face geometry data for FV discretizations
    1256: . cellGeomFVM     - Cell geometry data for FV discretizations
    1257: - gradFVM         - Gradient reconstruction data for FV discretizations

    1259:   Output Parameter:
    1260: . locX - Solution updated with boundary values

    1262:   Level: intermediate

    1264: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `DMAddBoundary()`
    1265: @*/
    1266: PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
    1267: {
    1268:   PetscFunctionBegin;
    1274:   PetscTryMethod(dm, "DMPlexInsertBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX, time, faceGeomFVM, cellGeomFVM, gradFVM));
    1275:   PetscFunctionReturn(PETSC_SUCCESS);
    1276: }

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

    1281:   Input Parameters:
    1282: + dm              - The `DM`
    1283: . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions
    1284: . time            - The time
    1285: . faceGeomFVM     - Face geometry data for FV discretizations
    1286: . cellGeomFVM     - Cell geometry data for FV discretizations
    1287: - gradFVM         - Gradient reconstruction data for FV discretizations

    1289:   Output Parameter:
    1290: . locX_t - Solution updated with boundary values

    1292:   Level: developer

    1294: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`
    1295: @*/
    1296: PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM)
    1297: {
    1298:   PetscFunctionBegin;
    1304:   PetscTryMethod(dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX_t, time, faceGeomFVM, cellGeomFVM, gradFVM));
    1305:   PetscFunctionReturn(PETSC_SUCCESS);
    1306: }

    1308: // Handle non-essential (e.g. outflow) boundary values
    1309: PetscErrorCode DMPlexInsertBoundaryValuesFVM(DM dm, PetscFV fv, Vec locX, PetscReal time, Vec *locGradient)
    1310: {
    1311:   DM  dmGrad;
    1312:   Vec cellGeometryFVM, faceGeometryFVM, locGrad = NULL;

    1314:   PetscFunctionBegin;
    1318:   if (locGradient) {
    1319:     PetscAssertPointer(locGradient, 5);
    1320:     *locGradient = NULL;
    1321:   }
    1322:   PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
    1323:   /* Reconstruct and limit cell gradients */
    1324:   PetscCall(DMPlexGetGradientDM(dm, fv, &dmGrad));
    1325:   if (dmGrad) {
    1326:     Vec      grad;
    1327:     PetscInt fStart, fEnd;

    1329:     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
    1330:     PetscCall(DMGetGlobalVector(dmGrad, &grad));
    1331:     PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
    1332:     /* Communicate gradient values */
    1333:     PetscCall(DMGetLocalVector(dmGrad, &locGrad));
    1334:     PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
    1335:     PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
    1336:     PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
    1337:   }
    1338:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
    1339:   if (locGradient) *locGradient = locGrad;
    1340:   else if (locGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
    1341:   PetscFunctionReturn(PETSC_SUCCESS);
    1342: }

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

    1348:   PetscFunctionBegin;
    1349:   PetscCall(DMGetLocalVector(dm, &localX));
    1350:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL));
    1351:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
    1352:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
    1353:   PetscCall(DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff));
    1354:   PetscCall(DMRestoreLocalVector(dm, &localX));
    1355:   PetscFunctionReturn(PETSC_SUCCESS);
    1356: }

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

    1361:   Collective

    1363:   Input Parameters:
    1364: + dm     - The `DM`
    1365: . time   - The time
    1366: . funcs  - The functions to evaluate for each field component
    1367: . ctxs   - Optional array of contexts to pass to each function, or `NULL`.
    1368: - localX - The coefficient vector u_h, a local vector

    1370:   Output Parameter:
    1371: . diff - The diff ||u - u_h||_2

    1373:   Level: developer

    1375: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
    1376: @*/
    1377: PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
    1378: {
    1379:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
    1380:   DM               tdm;
    1381:   Vec              tv;
    1382:   PetscSection     section;
    1383:   PetscQuadrature  quad;
    1384:   PetscFEGeom      fegeom;
    1385:   PetscScalar     *funcVal, *interpolant;
    1386:   PetscReal       *coords, *gcoords;
    1387:   PetscReal        localDiff = 0.0;
    1388:   const PetscReal *quadWeights;
    1389:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset;
    1390:   PetscBool        transform;

    1392:   PetscFunctionBegin;
    1393:   PetscCall(DMGetDimension(dm, &dim));
    1394:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
    1395:   fegeom.dimEmbed = coordDim;
    1396:   PetscCall(DMGetLocalSection(dm, &section));
    1397:   PetscCall(PetscSectionGetNumFields(section, &numFields));
    1398:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
    1399:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
    1400:   PetscCall(DMHasBasisTransform(dm, &transform));
    1401:   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
    1402:   for (field = 0; field < numFields; ++field) {
    1403:     PetscObject  obj;
    1404:     PetscClassId id;
    1405:     PetscInt     Nc;

    1407:     PetscCall(DMGetField(dm, field, NULL, &obj));
    1408:     PetscCall(PetscObjectGetClassId(obj, &id));
    1409:     if (id == PETSCFE_CLASSID) {
    1410:       PetscFE fe = (PetscFE)obj;

    1412:       PetscCall(PetscFEGetQuadrature(fe, &quad));
    1413:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
    1414:     } else if (id == PETSCFV_CLASSID) {
    1415:       PetscFV fv = (PetscFV)obj;

    1417:       PetscCall(PetscFVGetQuadrature(fv, &quad));
    1418:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
    1419:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1420:     numComponents += Nc;
    1421:   }
    1422:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
    1423:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
    1424:   PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * (Nq + 1), &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
    1425:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
    1426:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
    1427:   for (c = cStart; c < cEnd; ++c) {
    1428:     PetscScalar *x        = NULL;
    1429:     PetscReal    elemDiff = 0.0;
    1430:     PetscInt     qc       = 0;

    1432:     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    1433:     PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));

    1435:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
    1436:       PetscObject  obj;
    1437:       PetscClassId id;
    1438:       void *const  ctx = ctxs ? ctxs[field] : NULL;
    1439:       PetscInt     Nb, Nc, q, fc;

    1441:       PetscCall(DMGetField(dm, field, NULL, &obj));
    1442:       PetscCall(PetscObjectGetClassId(obj, &id));
    1443:       if (id == PETSCFE_CLASSID) {
    1444:         PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
    1445:         PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
    1446:       } else if (id == PETSCFV_CLASSID) {
    1447:         PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
    1448:         Nb = 1;
    1449:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1450:       if (debug) {
    1451:         char title[1024];
    1452:         PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
    1453:         PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
    1454:       }
    1455:       for (q = 0; q < Nq; ++q) {
    1456:         PetscFEGeom    qgeom;
    1457:         PetscErrorCode ierr;

    1459:         qgeom.dimEmbed = fegeom.dimEmbed;
    1460:         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
    1461:         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
    1462:         qgeom.detJ     = &fegeom.detJ[q];
    1463:         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);
    1464:         if (transform) {
    1465:           gcoords = &coords[coordDim * Nq];
    1466:           PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
    1467:         } else {
    1468:           gcoords = &coords[coordDim * q];
    1469:         }
    1470:         PetscCall(PetscArrayzero(funcVal, Nc));
    1471:         ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
    1472:         if (ierr) {
    1473:           PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
    1474:           PetscCall(DMRestoreLocalVector(dm, &localX));
    1475:           PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1476:         }
    1477:         if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
    1478:         if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
    1479:         else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
    1480:         else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1481:         for (fc = 0; fc < Nc; ++fc) {
    1482:           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
    1483:           if (debug)
    1484:             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.),
    1485:                                   (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]), (double)PetscRealPart(interpolant[fc]), (double)PetscRealPart(funcVal[fc])));
    1486:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
    1487:         }
    1488:       }
    1489:       fieldOffset += Nb;
    1490:       qc += Nc;
    1491:     }
    1492:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
    1493:     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
    1494:     localDiff += elemDiff;
    1495:   }
    1496:   PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1497:   PetscCallMPI(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
    1498:   *diff = PetscSqrtReal(*diff);
    1499:   PetscFunctionReturn(PETSC_SUCCESS);
    1500: }

    1502: 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)
    1503: {
    1504:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
    1505:   DM               tdm;
    1506:   PetscSection     section;
    1507:   PetscQuadrature  quad;
    1508:   Vec              localX, tv;
    1509:   PetscScalar     *funcVal, *interpolant;
    1510:   const PetscReal *quadWeights;
    1511:   PetscFEGeom      fegeom;
    1512:   PetscReal       *coords, *gcoords;
    1513:   PetscReal        localDiff = 0.0;
    1514:   PetscInt         dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
    1515:   PetscBool        transform;

    1517:   PetscFunctionBegin;
    1518:   PetscCall(DMGetDimension(dm, &dim));
    1519:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
    1520:   fegeom.dimEmbed = coordDim;
    1521:   PetscCall(DMGetLocalSection(dm, &section));
    1522:   PetscCall(PetscSectionGetNumFields(section, &numFields));
    1523:   PetscCall(DMGetLocalVector(dm, &localX));
    1524:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
    1525:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
    1526:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
    1527:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
    1528:   PetscCall(DMHasBasisTransform(dm, &transform));
    1529:   for (field = 0; field < numFields; ++field) {
    1530:     PetscFE  fe;
    1531:     PetscInt Nc;

    1533:     PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
    1534:     PetscCall(PetscFEGetQuadrature(fe, &quad));
    1535:     PetscCall(PetscFEGetNumComponents(fe, &Nc));
    1536:     numComponents += Nc;
    1537:   }
    1538:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
    1539:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
    1540:   /* PetscCall(DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX)); */
    1541:   PetscCall(PetscMalloc6(numComponents, &funcVal, coordDim * (Nq + 1), &coords, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ, numComponents * coordDim, &interpolant, Nq, &fegeom.detJ));
    1542:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    1543:   for (c = cStart; c < cEnd; ++c) {
    1544:     PetscScalar *x        = NULL;
    1545:     PetscReal    elemDiff = 0.0;
    1546:     PetscInt     qc       = 0;

    1548:     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    1549:     PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));

    1551:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
    1552:       PetscFE     fe;
    1553:       void *const ctx = ctxs ? ctxs[field] : NULL;
    1554:       PetscInt    Nb, Nc, q, fc;

    1556:       PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
    1557:       PetscCall(PetscFEGetDimension(fe, &Nb));
    1558:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
    1559:       if (debug) {
    1560:         char title[1024];
    1561:         PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
    1562:         PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
    1563:       }
    1564:       for (q = 0; q < Nq; ++q) {
    1565:         PetscFEGeom    qgeom;
    1566:         PetscErrorCode ierr;

    1568:         qgeom.dimEmbed = fegeom.dimEmbed;
    1569:         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
    1570:         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
    1571:         qgeom.detJ     = &fegeom.detJ[q];
    1572:         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);
    1573:         if (transform) {
    1574:           gcoords = &coords[coordDim * Nq];
    1575:           PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
    1576:         } else {
    1577:           gcoords = &coords[coordDim * q];
    1578:         }
    1579:         PetscCall(PetscArrayzero(funcVal, Nc));
    1580:         ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
    1581:         if (ierr) {
    1582:           PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
    1583:           PetscCall(DMRestoreLocalVector(dm, &localX));
    1584:           PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
    1585:         }
    1586:         if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
    1587:         PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant));
    1588:         /* Overwrite with the dot product if the normal is given */
    1589:         if (n) {
    1590:           for (fc = 0; fc < Nc; ++fc) {
    1591:             PetscScalar sum = 0.0;
    1592:             PetscInt    d;
    1593:             for (d = 0; d < dim; ++d) sum += interpolant[fc * dim + d] * n[d];
    1594:             interpolant[fc] = sum;
    1595:           }
    1596:         }
    1597:         for (fc = 0; fc < Nc; ++fc) {
    1598:           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
    1599:           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])));
    1600:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
    1601:         }
    1602:       }
    1603:       fieldOffset += Nb;
    1604:       qc += Nc;
    1605:     }
    1606:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
    1607:     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
    1608:     localDiff += elemDiff;
    1609:   }
    1610:   PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
    1611:   PetscCall(DMRestoreLocalVector(dm, &localX));
    1612:   PetscCallMPI(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
    1613:   *diff = PetscSqrtReal(*diff);
    1614:   PetscFunctionReturn(PETSC_SUCCESS);
    1615: }

    1617: PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
    1618: {
    1619:   const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
    1620:   DM             tdm;
    1621:   DMLabel        depthLabel;
    1622:   PetscSection   section;
    1623:   Vec            localX, tv;
    1624:   PetscReal     *localDiff;
    1625:   PetscInt       dim, depth, dE, Nf, f, Nds, s;
    1626:   PetscBool      transform;

    1628:   PetscFunctionBegin;
    1629:   PetscCall(DMGetDimension(dm, &dim));
    1630:   PetscCall(DMGetCoordinateDim(dm, &dE));
    1631:   PetscCall(DMGetLocalSection(dm, &section));
    1632:   PetscCall(DMGetLocalVector(dm, &localX));
    1633:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
    1634:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
    1635:   PetscCall(DMHasBasisTransform(dm, &transform));
    1636:   PetscCall(DMGetNumFields(dm, &Nf));
    1637:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    1638:   PetscCall(DMLabelGetNumValues(depthLabel, &depth));

    1640:   PetscCall(VecSet(localX, 0.0));
    1641:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
    1642:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
    1643:   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
    1644:   PetscCall(DMGetNumDS(dm, &Nds));
    1645:   PetscCall(PetscCalloc1(Nf, &localDiff));
    1646:   for (s = 0; s < Nds; ++s) {
    1647:     PetscDS          ds;
    1648:     DMLabel          label;
    1649:     IS               fieldIS, pointIS;
    1650:     const PetscInt  *fields, *points = NULL;
    1651:     PetscQuadrature  quad;
    1652:     const PetscReal *quadPoints, *quadWeights;
    1653:     PetscFEGeom      fegeom;
    1654:     PetscReal       *coords, *gcoords;
    1655:     PetscScalar     *funcVal, *interpolant;
    1656:     PetscBool        isCohesive;
    1657:     PetscInt         qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf;

    1659:     PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
    1660:     PetscCall(ISGetIndices(fieldIS, &fields));
    1661:     PetscCall(PetscDSIsCohesive(ds, &isCohesive));
    1662:     PetscCall(PetscDSGetNumFields(ds, &dsNf));
    1663:     PetscCall(PetscDSGetTotalComponents(ds, &totNc));
    1664:     PetscCall(PetscDSGetQuadrature(ds, &quad));
    1665:     PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
    1666:     PetscCheck(!(qNc != 1) || !(qNc != totNc), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, totNc);
    1667:     PetscCall(PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE * (Nq + 1), &coords, Nq, &fegeom.detJ, dE * dE * Nq, &fegeom.J, dE * dE * Nq, &fegeom.invJ));
    1668:     if (!label) {
    1669:       PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    1670:     } else {
    1671:       PetscCall(DMLabelGetStratumIS(label, 1, &pointIS));
    1672:       PetscCall(ISGetLocalSize(pointIS, &cEnd));
    1673:       PetscCall(ISGetIndices(pointIS, &points));
    1674:     }
    1675:     for (c = cStart; c < cEnd; ++c) {
    1676:       const PetscInt  cell = points ? points[c] : c;
    1677:       PetscScalar    *x    = NULL;
    1678:       const PetscInt *cone;
    1679:       PetscInt        qc = 0, fOff = 0, dep;

    1681:       PetscCall(DMLabelGetValue(depthLabel, cell, &dep));
    1682:       if (dep != depth - 1) continue;
    1683:       if (isCohesive) {
    1684:         PetscCall(DMPlexGetCone(dm, cell, &cone));
    1685:         PetscCall(DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    1686:       } else {
    1687:         PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    1688:       }
    1689:       PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, cell, 0, NULL, &x));
    1690:       for (f = 0; f < dsNf; ++f) {
    1691:         PetscObject  obj;
    1692:         PetscClassId id;
    1693:         void *const  ctx = ctxs ? ctxs[fields[f]] : NULL;
    1694:         PetscInt     Nb, Nc, q, fc;
    1695:         PetscReal    elemDiff = 0.0;
    1696:         PetscBool    cohesive;

    1698:         PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
    1699:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
    1700:         PetscCall(PetscObjectGetClassId(obj, &id));
    1701:         if (id == PETSCFE_CLASSID) {
    1702:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
    1703:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
    1704:         } else if (id == PETSCFV_CLASSID) {
    1705:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
    1706:           Nb = 1;
    1707:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
    1708:         if (isCohesive && !cohesive) {
    1709:           fOff += Nb * 2;
    1710:           qc += Nc;
    1711:           continue;
    1712:         }
    1713:         if (debug) {
    1714:           char title[1024];
    1715:           PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, fields[f]));
    1716:           PetscCall(DMPrintCellVector(cell, title, Nb, &x[fOff]));
    1717:         }
    1718:         for (q = 0; q < Nq; ++q) {
    1719:           PetscFEGeom    qgeom;
    1720:           PetscErrorCode ierr;

    1722:           qgeom.dimEmbed = fegeom.dimEmbed;
    1723:           qgeom.J        = &fegeom.J[q * dE * dE];
    1724:           qgeom.invJ     = &fegeom.invJ[q * dE * dE];
    1725:           qgeom.detJ     = &fegeom.detJ[q];
    1726:           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);
    1727:           if (transform) {
    1728:             gcoords = &coords[dE * Nq];
    1729:             PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE * q], PETSC_TRUE, dE, &coords[dE * q], gcoords, dm->transformCtx));
    1730:           } else {
    1731:             gcoords = &coords[dE * q];
    1732:           }
    1733:           for (fc = 0; fc < Nc; ++fc) funcVal[fc] = 0.;
    1734:           ierr = (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx);
    1735:           if (ierr) {
    1736:             PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
    1737:             PetscCall(DMRestoreLocalVector(dm, &localX));
    1738:             PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1739:           }
    1740:           if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[dE * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
    1741:           /* Call once for each face, except for lagrange field */
    1742:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fOff], &qgeom, q, interpolant));
    1743:           else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fOff], q, interpolant));
    1744:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
    1745:           for (fc = 0; fc < Nc; ++fc) {
    1746:             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
    1747:             if (debug)
    1748:               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.),
    1749:                                     (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
    1750:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
    1751:           }
    1752:         }
    1753:         fOff += Nb;
    1754:         qc += Nc;
    1755:         localDiff[fields[f]] += elemDiff;
    1756:         if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  cell %" PetscInt_FMT " field %" PetscInt_FMT " cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]]));
    1757:       }
    1758:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
    1759:     }
    1760:     if (label) {
    1761:       PetscCall(ISRestoreIndices(pointIS, &points));
    1762:       PetscCall(ISDestroy(&pointIS));
    1763:     }
    1764:     PetscCall(ISRestoreIndices(fieldIS, &fields));
    1765:     PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1766:   }
    1767:   PetscCall(DMRestoreLocalVector(dm, &localX));
    1768:   PetscCallMPI(MPIU_Allreduce(localDiff, diff, (PetscMPIInt)Nf, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
    1769:   PetscCall(PetscFree(localDiff));
    1770:   for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]);
    1771:   PetscFunctionReturn(PETSC_SUCCESS);
    1772: }

    1774: /*@C
    1775:   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.

    1777:   Collective

    1779:   Input Parameters:
    1780: + dm    - The `DM`
    1781: . time  - The time
    1782: . funcs - The functions to evaluate for each field component: `NULL` means that component does not contribute to error calculation
    1783: . ctxs  - Optional array of contexts to pass to each function, or `NULL`.
    1784: - X     - The coefficient vector u_h

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

    1789:   Level: developer

    1791: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
    1792: @*/
    1793: PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
    1794: {
    1795:   PetscSection     section;
    1796:   PetscQuadrature  quad;
    1797:   Vec              localX;
    1798:   PetscFEGeom      fegeom;
    1799:   PetscScalar     *funcVal, *interpolant;
    1800:   PetscReal       *coords;
    1801:   const PetscReal *quadPoints, *quadWeights;
    1802:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;

    1804:   PetscFunctionBegin;
    1805:   PetscCall(VecSet(D, 0.0));
    1806:   PetscCall(DMGetDimension(dm, &dim));
    1807:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
    1808:   PetscCall(DMGetLocalSection(dm, &section));
    1809:   PetscCall(PetscSectionGetNumFields(section, &numFields));
    1810:   PetscCall(DMGetLocalVector(dm, &localX));
    1811:   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
    1812:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
    1813:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
    1814:   for (field = 0; field < numFields; ++field) {
    1815:     PetscObject  obj;
    1816:     PetscClassId id;
    1817:     PetscInt     Nc;

    1819:     PetscCall(DMGetField(dm, field, NULL, &obj));
    1820:     PetscCall(PetscObjectGetClassId(obj, &id));
    1821:     if (id == PETSCFE_CLASSID) {
    1822:       PetscFE fe = (PetscFE)obj;

    1824:       PetscCall(PetscFEGetQuadrature(fe, &quad));
    1825:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
    1826:     } else if (id == PETSCFV_CLASSID) {
    1827:       PetscFV fv = (PetscFV)obj;

    1829:       PetscCall(PetscFVGetQuadrature(fv, &quad));
    1830:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
    1831:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1832:     numComponents += Nc;
    1833:   }
    1834:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
    1835:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
    1836:   PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
    1837:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    1838:   for (c = cStart; c < cEnd; ++c) {
    1839:     PetscScalar *x        = NULL;
    1840:     PetscScalar  elemDiff = 0.0;
    1841:     PetscInt     qc       = 0;

    1843:     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    1844:     PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x));

    1846:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
    1847:       PetscObject  obj;
    1848:       PetscClassId id;
    1849:       void *const  ctx = ctxs ? ctxs[field] : NULL;
    1850:       PetscInt     Nb, Nc, q, fc;

    1852:       PetscCall(DMGetField(dm, field, NULL, &obj));
    1853:       PetscCall(PetscObjectGetClassId(obj, &id));
    1854:       if (id == PETSCFE_CLASSID) {
    1855:         PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
    1856:         PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
    1857:       } else if (id == PETSCFV_CLASSID) {
    1858:         PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
    1859:         Nb = 1;
    1860:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1861:       if (funcs[field]) {
    1862:         for (q = 0; q < Nq; ++q) {
    1863:           PetscFEGeom qgeom;

    1865:           qgeom.dimEmbed = fegeom.dimEmbed;
    1866:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
    1867:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
    1868:           qgeom.detJ     = &fegeom.detJ[q];
    1869:           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);
    1870:           PetscCall((*funcs[field])(coordDim, time, &coords[q * coordDim], Nc, funcVal, ctx));
    1871: #if defined(needs_fix_with_return_code_argument)
    1872:           if (ierr) {
    1873:             PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
    1874:             PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1875:             PetscCall(DMRestoreLocalVector(dm, &localX));
    1876:           }
    1877: #endif
    1878:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
    1879:           else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
    1880:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    1881:           for (fc = 0; fc < Nc; ++fc) {
    1882:             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
    1883:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
    1884:           }
    1885:         }
    1886:       }
    1887:       fieldOffset += Nb;
    1888:       qc += Nc;
    1889:     }
    1890:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
    1891:     PetscCall(VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES));
    1892:   }
    1893:   PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1894:   PetscCall(DMRestoreLocalVector(dm, &localX));
    1895:   PetscCall(VecSqrtAbs(D));
    1896:   PetscFunctionReturn(PETSC_SUCCESS);
    1897: }

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

    1902:   Collective

    1904:   Input Parameters:
    1905: + lu  - The local `Vec` containing the primal solution
    1906: . f   - The field number for the potential
    1907: . lmu - The local `Vec` containing the mixed solution
    1908: - mf  - The field number for the flux

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

    1913:   Level: advanced

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

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

    1920: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeL2FluxDiffVec()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
    1921: @*/
    1922: PetscErrorCode DMPlexComputeL2FluxDiffVecLocal(Vec lu, PetscInt f, Vec lmu, PetscInt mf, Vec eFlux)
    1923: {
    1924:   DM               dm, mdm, edm;
    1925:   PetscFE          fe, mfe;
    1926:   PetscFEGeom      fegeom;
    1927:   PetscQuadrature  quad;
    1928:   const PetscReal *quadWeights;
    1929:   PetscReal       *coords;
    1930:   PetscScalar     *interpolant, *minterpolant, *earray;
    1931:   PetscInt         cdim, mcdim, cStart, cEnd, Nc, mNc, qNc, Nq;
    1932:   MPI_Comm         comm;

    1934:   PetscFunctionBegin;
    1935:   PetscCall(VecGetDM(lu, &dm));
    1936:   PetscCall(VecGetDM(lmu, &mdm));
    1937:   PetscCall(VecGetDM(eFlux, &edm));
    1938:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
    1939:   PetscCall(VecSet(eFlux, 0.0));

    1941:   // Check if the both problems are on the same mesh
    1942:   PetscCall(DMGetCoordinateDim(dm, &cdim));
    1943:   PetscCall(DMGetCoordinateDim(mdm, &mcdim));
    1944:   PetscCheck(cdim == mcdim, comm, PETSC_ERR_ARG_SIZ, "primal coordinate Dim %" PetscInt_FMT " != %" PetscInt_FMT " mixed coordinate Dim", cdim, mcdim);
    1945:   fegeom.dimEmbed = cdim;

    1947:   PetscCall(DMGetField(dm, f, NULL, (PetscObject *)&fe));
    1948:   PetscCall(DMGetField(mdm, mf, NULL, (PetscObject *)&mfe));
    1949:   PetscCall(PetscFEGetNumComponents(fe, &Nc));
    1950:   PetscCall(PetscFEGetNumComponents(mfe, &mNc));
    1951:   PetscCall(PetscFEGetQuadrature(fe, &quad));
    1952:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
    1953:   PetscCheck(qNc == 1 || qNc == mNc, comm, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, mNc);

    1955:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    1956:   PetscCall(VecGetArrayWrite(eFlux, &earray));
    1957:   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));
    1958:   for (PetscInt c = cStart; c < cEnd; ++c) {
    1959:     PetscScalar *x            = NULL;
    1960:     PetscScalar *mx           = NULL;
    1961:     PetscScalar *eval         = NULL;
    1962:     PetscReal    fluxElemDiff = 0.0;

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

    1968:     for (PetscInt q = 0; q < Nq; ++q) {
    1969:       PetscFEGeom qgeom;

    1971:       qgeom.dimEmbed = fegeom.dimEmbed;
    1972:       qgeom.J        = &fegeom.J[q * cdim * cdim];
    1973:       qgeom.invJ     = &fegeom.invJ[q * cdim * cdim];
    1974:       qgeom.detJ     = &fegeom.detJ[q];

    1976:       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);

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

    1981:       /* Now take the elementwise difference and store that in a vector. */
    1982:       for (PetscInt fc = 0; fc < mNc; ++fc) {
    1983:         const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : fc)];
    1984:         fluxElemDiff += PetscSqr(PetscRealPart(interpolant[fc] - minterpolant[fc])) * wt * fegeom.detJ[q];
    1985:       }
    1986:     }
    1987:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, lu, c, NULL, &x));
    1988:     PetscCall(DMPlexVecRestoreClosure(mdm, NULL, lmu, c, NULL, &mx));
    1989:     PetscCall(DMPlexPointGlobalRef(edm, c, earray, (void *)&eval));
    1990:     if (eval) eval[0] = fluxElemDiff;
    1991:   }
    1992:   PetscCall(PetscFree6(interpolant, minterpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    1993:   PetscCall(VecRestoreArrayWrite(eFlux, &earray));

    1995:   PetscCall(VecAssemblyBegin(eFlux));
    1996:   PetscCall(VecAssemblyEnd(eFlux));
    1997:   PetscCall(VecSqrtAbs(eFlux));
    1998:   PetscFunctionReturn(PETSC_SUCCESS);
    1999: }

    2001: /*@
    2002:   DMPlexComputeL2FluxDiffVec - 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: + u  - The global `Vec` containing the primal solution
    2008: . f  - The field number for the potential
    2009: . mu - The global `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`, `DMPlexComputeL2FluxDiffVecLocal()`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
    2023: @*/
    2024: PetscErrorCode DMPlexComputeL2FluxDiffVec(Vec u, PetscInt f, Vec mu, PetscInt mf, Vec eFlux)
    2025: {
    2026:   DM  dm, mdm;
    2027:   Vec lu, lmu;

    2029:   PetscFunctionBegin;
    2030:   PetscCall(VecGetDM(u, &dm));
    2031:   PetscCall(DMGetLocalVector(dm, &lu));
    2032:   PetscCall(DMGlobalToLocal(dm, u, INSERT_VALUES, lu));
    2033:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, lu, 0.0, NULL, NULL, NULL));

    2035:   PetscCall(VecGetDM(mu, &mdm));
    2036:   PetscCall(DMGetLocalVector(mdm, &lmu));
    2037:   PetscCall(DMGlobalToLocal(mdm, mu, INSERT_VALUES, lmu));
    2038:   PetscCall(DMPlexInsertBoundaryValues(mdm, PETSC_TRUE, lmu, 0.0, NULL, NULL, NULL));

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

    2042:   PetscCall(DMRestoreLocalVector(dm, &lu));
    2043:   PetscCall(DMRestoreLocalVector(mdm, &lmu));
    2044:   PetscFunctionReturn(PETSC_SUCCESS);
    2045: }

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

    2050:   Collective

    2052:   Input Parameters:
    2053: + dm   - The `DM`
    2054: - locX - The coefficient vector u_h

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

    2059:   Level: developer

    2061:   Note:
    2062:   $ 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

    2064: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
    2065: @*/
    2066: PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC)
    2067: {
    2068:   PetscInt         debug = ((DM_Plex *)dm->data)->printFEM;
    2069:   DM               dmc;
    2070:   PetscQuadrature  quad;
    2071:   PetscScalar     *interpolant, *valsum;
    2072:   PetscFEGeom      fegeom;
    2073:   PetscReal       *coords;
    2074:   const PetscReal *quadPoints, *quadWeights;
    2075:   PetscInt         dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v;

    2077:   PetscFunctionBegin;
    2078:   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
    2079:   PetscCall(VecGetDM(locC, &dmc));
    2080:   PetscCall(VecSet(locC, 0.0));
    2081:   PetscCall(DMGetDimension(dm, &dim));
    2082:   PetscCall(DMGetCoordinateDim(dm, &cdim));
    2083:   fegeom.dimEmbed = cdim;
    2084:   PetscCall(DMGetNumFields(dm, &Nf));
    2085:   PetscCheck(Nf > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
    2086:   for (f = 0; f < Nf; ++f) {
    2087:     PetscObject  obj;
    2088:     PetscClassId id;
    2089:     PetscInt     fNc;

    2091:     PetscCall(DMGetField(dm, f, NULL, &obj));
    2092:     PetscCall(PetscObjectGetClassId(obj, &id));
    2093:     if (id == PETSCFE_CLASSID) {
    2094:       PetscFE fe = (PetscFE)obj;

    2096:       PetscCall(PetscFEGetQuadrature(fe, &quad));
    2097:       PetscCall(PetscFEGetNumComponents(fe, &fNc));
    2098:     } else if (id == PETSCFV_CLASSID) {
    2099:       PetscFV fv = (PetscFV)obj;

    2101:       PetscCall(PetscFVGetQuadrature(fv, &quad));
    2102:       PetscCall(PetscFVGetNumComponents(fv, &fNc));
    2103:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
    2104:     Nc += fNc;
    2105:   }
    2106:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
    2107:   PetscCheck(qNc == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " > 1", qNc);
    2108:   PetscCall(PetscMalloc6(Nc * 2, &valsum, Nc, &interpolant, cdim * Nq, &coords, Nq, &fegeom.detJ, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ));
    2109:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
    2110:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    2111:   for (v = vStart; v < vEnd; ++v) {
    2112:     PetscScalar volsum = 0.0;
    2113:     PetscInt   *star   = NULL;
    2114:     PetscInt    starSize, st, fc;

    2116:     PetscCall(PetscArrayzero(valsum, Nc));
    2117:     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
    2118:     for (st = 0; st < starSize * 2; st += 2) {
    2119:       const PetscInt cell = star[st];
    2120:       PetscScalar   *val  = &valsum[Nc];
    2121:       PetscScalar   *x    = NULL;
    2122:       PetscReal      vol  = 0.0;
    2123:       PetscInt       foff = 0;

    2125:       if ((cell < cStart) || (cell >= cEnd)) continue;
    2126:       PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    2127:       PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
    2128:       for (f = 0; f < Nf; ++f) {
    2129:         PetscObject  obj;
    2130:         PetscClassId id;
    2131:         PetscInt     Nb, fNc, q;

    2133:         PetscCall(PetscArrayzero(val, Nc));
    2134:         PetscCall(DMGetField(dm, f, NULL, &obj));
    2135:         PetscCall(PetscObjectGetClassId(obj, &id));
    2136:         if (id == PETSCFE_CLASSID) {
    2137:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &fNc));
    2138:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
    2139:         } else if (id == PETSCFV_CLASSID) {
    2140:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &fNc));
    2141:           Nb = 1;
    2142:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
    2143:         for (q = 0; q < Nq; ++q) {
    2144:           const PetscReal wt = quadWeights[q] * fegeom.detJ[q];
    2145:           PetscFEGeom     qgeom;

    2147:           qgeom.dimEmbed = fegeom.dimEmbed;
    2148:           qgeom.J        = &fegeom.J[q * cdim * cdim];
    2149:           qgeom.invJ     = &fegeom.invJ[q * cdim * cdim];
    2150:           qgeom.detJ     = &fegeom.detJ[q];
    2151:           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);
    2152:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[foff], &qgeom, q, interpolant));
    2153:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
    2154:           for (fc = 0; fc < fNc; ++fc) val[foff + fc] += interpolant[fc] * wt;
    2155:           vol += wt;
    2156:         }
    2157:         foff += Nb;
    2158:       }
    2159:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
    2160:       for (fc = 0; fc < Nc; ++fc) valsum[fc] += val[fc];
    2161:       volsum += vol;
    2162:       if (debug) {
    2163:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " value: [", v, cell));
    2164:         for (fc = 0; fc < Nc; ++fc) {
    2165:           if (fc) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
    2166:           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(val[fc])));
    2167:         }
    2168:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
    2169:       }
    2170:     }
    2171:     for (fc = 0; fc < Nc; ++fc) valsum[fc] /= volsum;
    2172:     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
    2173:     PetscCall(DMPlexVecSetClosure(dmc, NULL, locC, v, valsum, INSERT_VALUES));
    2174:   }
    2175:   PetscCall(PetscFree6(valsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    2176:   PetscFunctionReturn(PETSC_SUCCESS);
    2177: }

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

    2182:   Collective

    2184:   Input Parameters:
    2185: + dm   - The `DM`
    2186: - locX - The coefficient vector u_h

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

    2191:   Level: developer

    2193:   Note:
    2194:   $\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

    2196: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
    2197: @*/
    2198: PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
    2199: {
    2200:   DM_Plex         *mesh  = (DM_Plex *)dm->data;
    2201:   PetscInt         debug = mesh->printFEM;
    2202:   DM               dmC;
    2203:   PetscQuadrature  quad;
    2204:   PetscScalar     *interpolant, *gradsum;
    2205:   PetscFEGeom      fegeom;
    2206:   PetscReal       *coords;
    2207:   const PetscReal *quadPoints, *quadWeights;
    2208:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;

    2210:   PetscFunctionBegin;
    2211:   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
    2212:   PetscCall(VecGetDM(locC, &dmC));
    2213:   PetscCall(VecSet(locC, 0.0));
    2214:   PetscCall(DMGetDimension(dm, &dim));
    2215:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
    2216:   fegeom.dimEmbed = coordDim;
    2217:   PetscCall(DMGetNumFields(dm, &numFields));
    2218:   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
    2219:   for (field = 0; field < numFields; ++field) {
    2220:     PetscObject  obj;
    2221:     PetscClassId id;
    2222:     PetscInt     Nc;

    2224:     PetscCall(DMGetField(dm, field, NULL, &obj));
    2225:     PetscCall(PetscObjectGetClassId(obj, &id));
    2226:     if (id == PETSCFE_CLASSID) {
    2227:       PetscFE fe = (PetscFE)obj;

    2229:       PetscCall(PetscFEGetQuadrature(fe, &quad));
    2230:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
    2231:     } else if (id == PETSCFV_CLASSID) {
    2232:       PetscFV fv = (PetscFV)obj;

    2234:       PetscCall(PetscFVGetQuadrature(fv, &quad));
    2235:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
    2236:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    2237:     numComponents += Nc;
    2238:   }
    2239:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
    2240:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
    2241:   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));
    2242:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
    2243:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    2244:   for (v = vStart; v < vEnd; ++v) {
    2245:     PetscScalar volsum = 0.0;
    2246:     PetscInt   *star   = NULL;
    2247:     PetscInt    starSize, st, d, fc;

    2249:     PetscCall(PetscArrayzero(gradsum, coordDim * numComponents));
    2250:     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
    2251:     for (st = 0; st < starSize * 2; st += 2) {
    2252:       const PetscInt cell = star[st];
    2253:       PetscScalar   *grad = &gradsum[coordDim * numComponents];
    2254:       PetscScalar   *x    = NULL;
    2255:       PetscReal      vol  = 0.0;

    2257:       if ((cell < cStart) || (cell >= cEnd)) continue;
    2258:       PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
    2259:       PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
    2260:       for (field = 0, fieldOffset = 0; field < numFields; ++field) {
    2261:         PetscObject  obj;
    2262:         PetscClassId id;
    2263:         PetscInt     Nb, Nc, q, qc = 0;

    2265:         PetscCall(PetscArrayzero(grad, coordDim * numComponents));
    2266:         PetscCall(DMGetField(dm, field, NULL, &obj));
    2267:         PetscCall(PetscObjectGetClassId(obj, &id));
    2268:         if (id == PETSCFE_CLASSID) {
    2269:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
    2270:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
    2271:         } else if (id == PETSCFV_CLASSID) {
    2272:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
    2273:           Nb = 1;
    2274:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    2275:         for (q = 0; q < Nq; ++q) {
    2276:           PetscFEGeom qgeom;

    2278:           qgeom.dimEmbed = fegeom.dimEmbed;
    2279:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
    2280:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
    2281:           qgeom.detJ     = &fegeom.detJ[q];
    2282:           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);
    2283:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant));
    2284:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    2285:           for (fc = 0; fc < Nc; ++fc) {
    2286:             const PetscReal wt = quadWeights[q * qNc + qc];

    2288:             for (d = 0; d < coordDim; ++d) grad[fc * coordDim + d] += interpolant[fc * dim + d] * wt * fegeom.detJ[q];
    2289:           }
    2290:           vol += quadWeights[q * qNc] * fegeom.detJ[q];
    2291:         }
    2292:         fieldOffset += Nb;
    2293:         qc += Nc;
    2294:       }
    2295:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
    2296:       for (fc = 0; fc < numComponents; ++fc) {
    2297:         for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] += grad[fc * coordDim + d];
    2298:       }
    2299:       volsum += vol;
    2300:       if (debug) {
    2301:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " gradient: [", v, cell));
    2302:         for (fc = 0; fc < numComponents; ++fc) {
    2303:           for (d = 0; d < coordDim; ++d) {
    2304:             if (fc || d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
    2305:             PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc * coordDim + d])));
    2306:           }
    2307:         }
    2308:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
    2309:       }
    2310:     }
    2311:     for (fc = 0; fc < numComponents; ++fc) {
    2312:       for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] /= volsum;
    2313:     }
    2314:     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
    2315:     PetscCall(DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES));
    2316:   }
    2317:   PetscCall(PetscFree6(gradsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
    2318:   PetscFunctionReturn(PETSC_SUCCESS);
    2319: }

    2321: PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec locX, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, void *user)
    2322: {
    2323:   DM           dmAux = NULL, plexA = NULL;
    2324:   PetscDS      prob, probAux       = NULL;
    2325:   PetscSection section, sectionAux;
    2326:   Vec          locA;
    2327:   PetscInt     dim, numCells = cEnd - cStart, c, f;
    2328:   PetscBool    useFVM = PETSC_FALSE;
    2329:   /* DS */
    2330:   PetscInt           Nf, totDim, *uOff, *uOff_x, numConstants;
    2331:   PetscInt           NfAux, totDimAux, *aOff;
    2332:   PetscScalar       *u, *a = NULL;
    2333:   const PetscScalar *constants;
    2334:   /* Geometry */
    2335:   PetscFEGeom       *cgeomFEM;
    2336:   DM                 dmGrad;
    2337:   PetscQuadrature    affineQuad      = NULL;
    2338:   Vec                cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
    2339:   PetscFVCellGeom   *cgeomFVM;
    2340:   const PetscScalar *lgrad;
    2341:   PetscInt           maxDegree;
    2342:   DMField            coordField;
    2343:   IS                 cellIS;

    2345:   PetscFunctionBegin;
    2346:   PetscCall(DMGetDS(dm, &prob));
    2347:   PetscCall(DMGetDimension(dm, &dim));
    2348:   PetscCall(DMGetLocalSection(dm, &section));
    2349:   PetscCall(DMGetNumFields(dm, &Nf));
    2350:   /* Determine which discretizations we have */
    2351:   for (f = 0; f < Nf; ++f) {
    2352:     PetscObject  obj;
    2353:     PetscClassId id;

    2355:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    2356:     PetscCall(PetscObjectGetClassId(obj, &id));
    2357:     if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
    2358:   }
    2359:   /* Read DS information */
    2360:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    2361:   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
    2362:   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
    2363:   PetscCall(ISCreateStride(PETSC_COMM_SELF, numCells, cStart, 1, &cellIS));
    2364:   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
    2365:   /* Read Auxiliary DS information */
    2366:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
    2367:   if (locA) {
    2368:     PetscCall(VecGetDM(locA, &dmAux));
    2369:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
    2370:     PetscCall(DMGetDS(dmAux, &probAux));
    2371:     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
    2372:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
    2373:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    2374:     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
    2375:   }
    2376:   /* Allocate data  arrays */
    2377:   PetscCall(PetscCalloc1(numCells * totDim, &u));
    2378:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
    2379:   /* Read out geometry */
    2380:   PetscCall(DMGetCoordinateField(dm, &coordField));
    2381:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    2382:   if (maxDegree <= 1) {
    2383:     PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
    2384:     if (affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &cgeomFEM));
    2385:   }
    2386:   if (useFVM) {
    2387:     PetscFV   fv = NULL;
    2388:     Vec       grad;
    2389:     PetscInt  fStart, fEnd;
    2390:     PetscBool compGrad;

    2392:     for (f = 0; f < Nf; ++f) {
    2393:       PetscObject  obj;
    2394:       PetscClassId id;

    2396:       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    2397:       PetscCall(PetscObjectGetClassId(obj, &id));
    2398:       if (id == PETSCFV_CLASSID) {
    2399:         fv = (PetscFV)obj;
    2400:         break;
    2401:       }
    2402:     }
    2403:     PetscCall(PetscFVGetComputeGradients(fv, &compGrad));
    2404:     PetscCall(PetscFVSetComputeGradients(fv, PETSC_TRUE));
    2405:     PetscCall(DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM));
    2406:     PetscCall(DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad));
    2407:     PetscCall(PetscFVSetComputeGradients(fv, compGrad));
    2408:     PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
    2409:     /* Reconstruct and limit cell gradients */
    2410:     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
    2411:     PetscCall(DMGetGlobalVector(dmGrad, &grad));
    2412:     PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
    2413:     /* Communicate gradient values */
    2414:     PetscCall(DMGetLocalVector(dmGrad, &locGrad));
    2415:     PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
    2416:     PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
    2417:     PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
    2418:     /* Handle non-essential (e.g. outflow) boundary values */
    2419:     PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad));
    2420:     PetscCall(VecGetArrayRead(locGrad, &lgrad));
    2421:   }
    2422:   /* Read out data from inputs */
    2423:   for (c = cStart; c < cEnd; ++c) {
    2424:     PetscScalar *x = NULL;
    2425:     PetscInt     i;

    2427:     PetscCall(DMPlexVecGetClosure(dm, section, locX, c, NULL, &x));
    2428:     for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
    2429:     PetscCall(DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x));
    2430:     if (dmAux) {
    2431:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, c, NULL, &x));
    2432:       for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
    2433:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, c, NULL, &x));
    2434:     }
    2435:   }
    2436:   /* Do integration for each field */
    2437:   for (f = 0; f < Nf; ++f) {
    2438:     PetscObject  obj;
    2439:     PetscClassId id;
    2440:     PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

    2442:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    2443:     PetscCall(PetscObjectGetClassId(obj, &id));
    2444:     if (id == PETSCFE_CLASSID) {
    2445:       PetscFE         fe = (PetscFE)obj;
    2446:       PetscQuadrature q;
    2447:       PetscFEGeom    *chunkGeom = NULL;
    2448:       PetscInt        Nq, Nb;

    2450:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    2451:       PetscCall(PetscFEGetQuadrature(fe, &q));
    2452:       PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
    2453:       PetscCall(PetscFEGetDimension(fe, &Nb));
    2454:       blockSize = Nb * Nq;
    2455:       batchSize = numBlocks * blockSize;
    2456:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    2457:       numChunks = numCells / (numBatches * batchSize);
    2458:       Ne        = numChunks * numBatches * batchSize;
    2459:       Nr        = numCells % (numBatches * batchSize);
    2460:       offset    = numCells - Nr;
    2461:       if (!affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, q, PETSC_FALSE, &cgeomFEM));
    2462:       PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
    2463:       PetscCall(PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral));
    2464:       PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &chunkGeom));
    2465:       PetscCall(PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &cintegral[offset * Nf]));
    2466:       PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &chunkGeom));
    2467:       if (!affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
    2468:     } else if (id == PETSCFV_CLASSID) {
    2469:       PetscInt       foff;
    2470:       PetscPointFunc obj_func;

    2472:       PetscCall(PetscDSGetObjective(prob, f, &obj_func));
    2473:       PetscCall(PetscDSGetFieldOffset(prob, f, &foff));
    2474:       if (obj_func) {
    2475:         for (c = 0; c < numCells; ++c) {
    2476:           PetscScalar *u_x;
    2477:           PetscScalar  lint = 0.;

    2479:           PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x));
    2480:           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);
    2481:           cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
    2482:         }
    2483:       }
    2484:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
    2485:   }
    2486:   /* Cleanup data arrays */
    2487:   if (useFVM) {
    2488:     PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
    2489:     PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
    2490:     PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
    2491:     PetscCall(VecDestroy(&faceGeometryFVM));
    2492:     PetscCall(VecDestroy(&cellGeometryFVM));
    2493:     PetscCall(DMDestroy(&dmGrad));
    2494:   }
    2495:   if (dmAux) PetscCall(PetscFree(a));
    2496:   PetscCall(DMDestroy(&plexA));
    2497:   PetscCall(PetscFree(u));
    2498:   /* Cleanup */
    2499:   if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
    2500:   PetscCall(PetscQuadratureDestroy(&affineQuad));
    2501:   PetscCall(ISDestroy(&cellIS));
    2502:   PetscFunctionReturn(PETSC_SUCCESS);
    2503: }

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

    2508:   Input Parameters:
    2509: + dm   - The mesh
    2510: . X    - Global input vector
    2511: - user - The user context

    2513:   Output Parameter:
    2514: . integral - Integral for each field

    2516:   Level: developer

    2518: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
    2519: @*/
    2520: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
    2521: {
    2522:   PetscInt     printFEM;
    2523:   PetscScalar *cintegral, *lintegral;
    2524:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;
    2525:   Vec          locX;

    2527:   PetscFunctionBegin;
    2530:   PetscAssertPointer(integral, 3);
    2531:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
    2532:   PetscCall(DMPlexConvertPlex(dm, &dm, PETSC_TRUE));
    2533:   PetscCall(DMGetNumFields(dm, &Nf));
    2534:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
    2535:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
    2536:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
    2537:   PetscCall(PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral));
    2538:   /* Get local solution with boundary values */
    2539:   PetscCall(DMGetLocalVector(dm, &locX));
    2540:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
    2541:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
    2542:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
    2543:   PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, user));
    2544:   PetscCall(DMRestoreLocalVector(dm, &locX));
    2545:   printFEM = ((DM_Plex *)dm->data)->printFEM;
    2546:   /* Sum up values */
    2547:   for (cell = cStart; cell < cEnd; ++cell) {
    2548:     const PetscInt c = cell - cStart;

    2550:     if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
    2551:     for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f];
    2552:   }
    2553:   PetscCallMPI(MPIU_Allreduce(lintegral, integral, (PetscMPIInt)Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
    2554:   if (printFEM) {
    2555:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:"));
    2556:     for (f = 0; f < Nf; ++f) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f])));
    2557:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "\n"));
    2558:   }
    2559:   PetscCall(PetscFree2(lintegral, cintegral));
    2560:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
    2561:   PetscCall(DMDestroy(&dm));
    2562:   PetscFunctionReturn(PETSC_SUCCESS);
    2563: }

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

    2568:   Input Parameters:
    2569: + dm   - The mesh
    2570: . X    - Global input vector
    2571: - user - The user context

    2573:   Output Parameter:
    2574: . F - Cellwise integrals for each field

    2576:   Level: developer

    2578: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
    2579: @*/
    2580: PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user)
    2581: {
    2582:   PetscInt     printFEM;
    2583:   DM           dmF;
    2584:   PetscSection sectionF = NULL;
    2585:   PetscScalar *cintegral, *af;
    2586:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell, n;
    2587:   Vec          locX;

    2589:   PetscFunctionBegin;
    2593:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
    2594:   PetscCall(DMPlexConvertPlex(dm, &dm, PETSC_TRUE));
    2595:   PetscCall(DMGetNumFields(dm, &Nf));
    2596:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
    2597:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
    2598:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
    2599:   PetscCall(PetscCalloc1((cEnd - cStart) * Nf, &cintegral));
    2600:   /* Get local solution with boundary values */
    2601:   PetscCall(DMGetLocalVector(dm, &locX));
    2602:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
    2603:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
    2604:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
    2605:   PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, user));
    2606:   PetscCall(DMRestoreLocalVector(dm, &locX));
    2607:   /* Put values in F */
    2608:   PetscCall(VecGetArray(F, &af));
    2609:   PetscCall(VecGetDM(F, &dmF));
    2610:   if (dmF) PetscCall(DMGetLocalSection(dmF, &sectionF));
    2611:   PetscCall(VecGetLocalSize(F, &n));
    2612:   PetscCheck(n >= (cEnd - cStart) * Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Vector size %" PetscInt_FMT " < %" PetscInt_FMT, n, (cEnd - cStart) * Nf);
    2613:   printFEM = ((DM_Plex *)dm->data)->printFEM;
    2614:   for (cell = cStart; cell < cEnd; ++cell) {
    2615:     const PetscInt c   = cell - cStart;
    2616:     PetscInt       dof = Nf, off = c * Nf;

    2618:     if (printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
    2619:     if (sectionF) {
    2620:       PetscCall(PetscSectionGetDof(sectionF, cell, &dof));
    2621:       PetscCall(PetscSectionGetOffset(sectionF, cell, &off));
    2622:     }
    2623:     PetscCheck(dof == Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %" PetscInt_FMT " != %" PetscInt_FMT, dof, Nf);
    2624:     for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
    2625:   }
    2626:   PetscCall(VecRestoreArray(F, &af));
    2627:   PetscCall(PetscFree(cintegral));
    2628:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
    2629:   PetscCall(DMDestroy(&dm));
    2630:   PetscFunctionReturn(PETSC_SUCCESS);
    2631: }

    2633: 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, void *user)
    2634: {
    2635:   DM                 plex = NULL, plexA = NULL;
    2636:   DMEnclosureType    encAux;
    2637:   PetscDS            prob, probAux       = NULL;
    2638:   PetscSection       section, sectionAux = NULL;
    2639:   Vec                locA = NULL;
    2640:   DMField            coordField;
    2641:   PetscInt           Nf, totDim, *uOff, *uOff_x;
    2642:   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
    2643:   PetscScalar       *u, *a = NULL;
    2644:   const PetscScalar *constants;
    2645:   PetscInt           numConstants, f;

    2647:   PetscFunctionBegin;
    2648:   PetscCall(DMGetCoordinateField(dm, &coordField));
    2649:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    2650:   PetscCall(DMGetDS(dm, &prob));
    2651:   PetscCall(DMGetLocalSection(dm, &section));
    2652:   PetscCall(PetscSectionGetNumFields(section, &Nf));
    2653:   /* Determine which discretizations we have */
    2654:   for (f = 0; f < Nf; ++f) {
    2655:     PetscObject  obj;
    2656:     PetscClassId id;

    2658:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    2659:     PetscCall(PetscObjectGetClassId(obj, &id));
    2660:     PetscCheck(id != PETSCFV_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported for FVM (field %" PetscInt_FMT ")", f);
    2661:   }
    2662:   /* Read DS information */
    2663:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    2664:   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
    2665:   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
    2666:   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
    2667:   /* Read Auxiliary DS information */
    2668:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
    2669:   if (locA) {
    2670:     DM dmAux;

    2672:     PetscCall(VecGetDM(locA, &dmAux));
    2673:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    2674:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
    2675:     PetscCall(DMGetDS(dmAux, &probAux));
    2676:     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
    2677:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
    2678:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    2679:     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
    2680:   }
    2681:   /* Integrate over points */
    2682:   {
    2683:     PetscFEGeom    *fgeom, *chunkGeom = NULL;
    2684:     PetscInt        maxDegree;
    2685:     PetscQuadrature qGeom = NULL;
    2686:     const PetscInt *points;
    2687:     PetscInt        numFaces, face, Nq, field;
    2688:     PetscInt        numChunks, chunkSize, chunk, Nr, offset;

    2690:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
    2691:     PetscCall(ISGetIndices(pointIS, &points));
    2692:     PetscCall(PetscCalloc2(numFaces * totDim, &u, (locA ? (size_t)numFaces * totDimAux : 0), &a));
    2693:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
    2694:     for (face = 0; face < numFaces; ++face) {
    2695:       const PetscInt point = points[face], *support;
    2696:       PetscScalar   *x     = NULL;

    2698:       PetscCall(DMPlexGetSupport(dm, point, &support));
    2699:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
    2700:       for (PetscInt i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
    2701:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
    2702:       if (locA) {
    2703:         PetscInt subp;
    2704:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
    2705:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
    2706:         for (PetscInt i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
    2707:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
    2708:       }
    2709:     }
    2710:     for (field = 0; field < Nf; ++field) {
    2711:       PetscFE fe;

    2713:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
    2714:       if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
    2715:       if (!qGeom) {
    2716:         PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
    2717:         PetscCall(PetscObjectReference((PetscObject)qGeom));
    2718:       }
    2719:       PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
    2720:       PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
    2721:       /* Get blocking */
    2722:       {
    2723:         PetscQuadrature q;
    2724:         PetscInt        numBatches, batchSize, numBlocks, blockSize;
    2725:         PetscInt        Nq, Nb;

    2727:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    2728:         PetscCall(PetscFEGetQuadrature(fe, &q));
    2729:         PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
    2730:         PetscCall(PetscFEGetDimension(fe, &Nb));
    2731:         blockSize = Nb * Nq;
    2732:         batchSize = numBlocks * blockSize;
    2733:         chunkSize = numBatches * batchSize;
    2734:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    2735:         numChunks = numFaces / chunkSize;
    2736:         Nr        = numFaces % chunkSize;
    2737:         offset    = numFaces - Nr;
    2738:       }
    2739:       /* Do integration for each field */
    2740:       for (chunk = 0; chunk < numChunks; ++chunk) {
    2741:         PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom));
    2742:         PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], chunkSize, chunkGeom, &u[chunk * chunkSize * totDim], probAux, PetscSafePointerPlusOffset(a, chunk * chunkSize * totDimAux), &fintegral[chunk * chunkSize * Nf]));
    2743:         PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
    2744:       }
    2745:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
    2746:       PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &fintegral[offset * Nf]));
    2747:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
    2748:       /* Cleanup data arrays */
    2749:       PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
    2750:       PetscCall(PetscQuadratureDestroy(&qGeom));
    2751:     }
    2752:     PetscCall(PetscFree2(u, a));
    2753:     PetscCall(ISRestoreIndices(pointIS, &points));
    2754:   }
    2755:   if (plex) PetscCall(DMDestroy(&plex));
    2756:   if (plexA) PetscCall(DMDestroy(&plexA));
    2757:   PetscFunctionReturn(PETSC_SUCCESS);
    2758: }

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

    2763:   Input Parameters:
    2764: + dm      - The mesh
    2765: . X       - Global input vector
    2766: . label   - The boundary `DMLabel`
    2767: . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
    2768: . vals    - The label values to use, or NULL for all values
    2769: . funcs   - The functions to integrate along the boundary for each field
    2770: - user    - The user context

    2772:   Output Parameter:
    2773: . integral - Integral for each field

    2775:   Level: developer

    2777: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
    2778: @*/
    2779: 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, void *user)
    2780: {
    2781:   Vec          locX;
    2782:   PetscSection section;
    2783:   DMLabel      depthLabel;
    2784:   IS           facetIS;
    2785:   PetscInt     dim, Nf, f, v;

    2787:   PetscFunctionBegin;
    2791:   if (vals) PetscAssertPointer(vals, 5);
    2792:   PetscAssertPointer(integral, 7);
    2793:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
    2794:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    2795:   PetscCall(DMGetDimension(dm, &dim));
    2796:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
    2797:   PetscCall(DMGetLocalSection(dm, &section));
    2798:   PetscCall(PetscSectionGetNumFields(section, &Nf));
    2799:   /* Get local solution with boundary values */
    2800:   PetscCall(DMGetLocalVector(dm, &locX));
    2801:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
    2802:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
    2803:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
    2804:   /* Loop over label values */
    2805:   PetscCall(PetscArrayzero(integral, Nf));
    2806:   for (v = 0; v < numVals; ++v) {
    2807:     IS           pointIS;
    2808:     PetscInt     numFaces, face;
    2809:     PetscScalar *fintegral;

    2811:     PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
    2812:     if (!pointIS) continue; /* No points with that id on this process */
    2813:     {
    2814:       IS isectIS;

    2816:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
    2817:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
    2818:       PetscCall(ISDestroy(&pointIS));
    2819:       pointIS = isectIS;
    2820:     }
    2821:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
    2822:     PetscCall(PetscCalloc1(numFaces * Nf, &fintegral));
    2823:     PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, funcs, fintegral, user));
    2824:     /* Sum point contributions into integral */
    2825:     for (f = 0; f < Nf; ++f)
    2826:       for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f];
    2827:     PetscCall(PetscFree(fintegral));
    2828:     PetscCall(ISDestroy(&pointIS));
    2829:   }
    2830:   PetscCall(DMRestoreLocalVector(dm, &locX));
    2831:   PetscCall(ISDestroy(&facetIS));
    2832:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
    2833:   PetscFunctionReturn(PETSC_SUCCESS);
    2834: }

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

    2839:   Input Parameters:
    2840: + dmc       - The coarse mesh
    2841: . dmf       - The fine mesh
    2842: . isRefined - Flag indicating regular refinement, rather than the same topology
    2843: - user      - The user context

    2845:   Output Parameter:
    2846: . In - The interpolation matrix

    2848:   Level: developer

    2850: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`
    2851: @*/
    2852: PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user)
    2853: {
    2854:   DM_Plex     *mesh = (DM_Plex *)dmc->data;
    2855:   const char  *name = "Interpolator";
    2856:   PetscFE     *feRef;
    2857:   PetscFV     *fvRef;
    2858:   PetscSection fsection, fglobalSection;
    2859:   PetscSection csection, cglobalSection;
    2860:   PetscScalar *elemMat;
    2861:   PetscInt     dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
    2862:   PetscInt     cTotDim = 0, rTotDim = 0;

    2864:   PetscFunctionBegin;
    2865:   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
    2866:   PetscCall(DMGetDimension(dmf, &dim));
    2867:   PetscCall(DMGetLocalSection(dmf, &fsection));
    2868:   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
    2869:   PetscCall(DMGetLocalSection(dmc, &csection));
    2870:   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
    2871:   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
    2872:   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
    2873:   PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef));
    2874:   for (f = 0; f < Nf; ++f) {
    2875:     PetscObject  obj, objc;
    2876:     PetscClassId id, idc;
    2877:     PetscInt     rNb = 0, Nc = 0, cNb = 0;

    2879:     PetscCall(DMGetField(dmf, f, NULL, &obj));
    2880:     PetscCall(PetscObjectGetClassId(obj, &id));
    2881:     if (id == PETSCFE_CLASSID) {
    2882:       PetscFE fe = (PetscFE)obj;

    2884:       if (isRefined) {
    2885:         PetscCall(PetscFERefine(fe, &feRef[f]));
    2886:       } else {
    2887:         PetscCall(PetscObjectReference((PetscObject)fe));
    2888:         feRef[f] = fe;
    2889:       }
    2890:       PetscCall(PetscFEGetDimension(feRef[f], &rNb));
    2891:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
    2892:     } else if (id == PETSCFV_CLASSID) {
    2893:       PetscFV        fv = (PetscFV)obj;
    2894:       PetscDualSpace Q;

    2896:       if (isRefined) {
    2897:         PetscCall(PetscFVRefine(fv, &fvRef[f]));
    2898:       } else {
    2899:         PetscCall(PetscObjectReference((PetscObject)fv));
    2900:         fvRef[f] = fv;
    2901:       }
    2902:       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
    2903:       PetscCall(PetscDualSpaceGetDimension(Q, &rNb));
    2904:       PetscCall(PetscFVGetDualSpace(fv, &Q));
    2905:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
    2906:     }
    2907:     PetscCall(DMGetField(dmc, f, NULL, &objc));
    2908:     PetscCall(PetscObjectGetClassId(objc, &idc));
    2909:     if (idc == PETSCFE_CLASSID) {
    2910:       PetscFE fe = (PetscFE)objc;

    2912:       PetscCall(PetscFEGetDimension(fe, &cNb));
    2913:     } else if (id == PETSCFV_CLASSID) {
    2914:       PetscFV        fv = (PetscFV)obj;
    2915:       PetscDualSpace Q;

    2917:       PetscCall(PetscFVGetDualSpace(fv, &Q));
    2918:       PetscCall(PetscDualSpaceGetDimension(Q, &cNb));
    2919:     }
    2920:     rTotDim += rNb;
    2921:     cTotDim += cNb;
    2922:   }
    2923:   PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat));
    2924:   PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim));
    2925:   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
    2926:     PetscDualSpace   Qref;
    2927:     PetscQuadrature  f;
    2928:     const PetscReal *qpoints, *qweights;
    2929:     PetscReal       *points;
    2930:     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;

    2932:     /* Compose points from all dual basis functionals */
    2933:     if (feRef[fieldI]) {
    2934:       PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref));
    2935:       PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc));
    2936:     } else {
    2937:       PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref));
    2938:       PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc));
    2939:     }
    2940:     PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim));
    2941:     for (i = 0; i < fpdim; ++i) {
    2942:       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
    2943:       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL));
    2944:       npoints += Np;
    2945:     }
    2946:     PetscCall(PetscMalloc1(npoints * dim, &points));
    2947:     for (i = 0, k = 0; i < fpdim; ++i) {
    2948:       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
    2949:       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL));
    2950:       for (p = 0; p < Np; ++p, ++k)
    2951:         for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
    2952:     }

    2954:     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
    2955:       PetscObject  obj;
    2956:       PetscClassId id;
    2957:       PetscInt     NcJ = 0, cpdim = 0, j, qNc;

    2959:       PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
    2960:       PetscCall(PetscObjectGetClassId(obj, &id));
    2961:       if (id == PETSCFE_CLASSID) {
    2962:         PetscFE         fe = (PetscFE)obj;
    2963:         PetscTabulation T  = NULL;

    2965:         /* Evaluate basis at points */
    2966:         PetscCall(PetscFEGetNumComponents(fe, &NcJ));
    2967:         PetscCall(PetscFEGetDimension(fe, &cpdim));
    2968:         /* For now, fields only interpolate themselves */
    2969:         if (fieldI == fieldJ) {
    2970:           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);
    2971:           PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T));
    2972:           for (i = 0, k = 0; i < fpdim; ++i) {
    2973:             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
    2974:             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
    2975:             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);
    2976:             for (p = 0; p < Np; ++p, ++k) {
    2977:               for (j = 0; j < cpdim; ++j) {
    2978:                 /*
    2979:                    cTotDim:            Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
    2980:                    offsetI, offsetJ:   Offsets into the larger element interpolation matrix for different fields
    2981:                    fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
    2982:                    qNC, Nc, Ncj, c:    Number of components in this field
    2983:                    Np, p:              Number of quad points in the fine grid functional i
    2984:                    k:                  i*Np + p, overall point number for the interpolation
    2985:                 */
    2986:                 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];
    2987:               }
    2988:             }
    2989:           }
    2990:           PetscCall(PetscTabulationDestroy(&T));
    2991:         }
    2992:       } else if (id == PETSCFV_CLASSID) {
    2993:         PetscFV fv = (PetscFV)obj;

    2995:         /* Evaluate constant function at points */
    2996:         PetscCall(PetscFVGetNumComponents(fv, &NcJ));
    2997:         cpdim = 1;
    2998:         /* For now, fields only interpolate themselves */
    2999:         if (fieldI == fieldJ) {
    3000:           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);
    3001:           for (i = 0, k = 0; i < fpdim; ++i) {
    3002:             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
    3003:             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
    3004:             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);
    3005:             for (p = 0; p < Np; ++p, ++k) {
    3006:               for (j = 0; j < cpdim; ++j) {
    3007:                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
    3008:               }
    3009:             }
    3010:           }
    3011:         }
    3012:       }
    3013:       offsetJ += cpdim;
    3014:     }
    3015:     offsetI += fpdim;
    3016:     PetscCall(PetscFree(points));
    3017:   }
    3018:   if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat));
    3019:   /* Preallocate matrix */
    3020:   {
    3021:     Mat          preallocator;
    3022:     PetscScalar *vals;
    3023:     PetscInt    *cellCIndices, *cellFIndices;
    3024:     PetscInt     locRows, locCols, cell;

    3026:     PetscCall(MatGetLocalSize(In, &locRows, &locCols));
    3027:     PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator));
    3028:     PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
    3029:     PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
    3030:     PetscCall(MatSetUp(preallocator));
    3031:     PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices));
    3032:     for (cell = cStart; cell < cEnd; ++cell) {
    3033:       if (isRefined) {
    3034:         PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices));
    3035:         PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES));
    3036:       } else {
    3037:         PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, preallocator, cell, vals, INSERT_VALUES));
    3038:       }
    3039:     }
    3040:     PetscCall(PetscFree3(vals, cellCIndices, cellFIndices));
    3041:     PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
    3042:     PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
    3043:     PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In));
    3044:     PetscCall(MatDestroy(&preallocator));
    3045:   }
    3046:   /* Fill matrix */
    3047:   PetscCall(MatZeroEntries(In));
    3048:   for (c = cStart; c < cEnd; ++c) {
    3049:     if (isRefined) {
    3050:       PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
    3051:     } else {
    3052:       PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, In, c, elemMat, INSERT_VALUES));
    3053:     }
    3054:   }
    3055:   for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f]));
    3056:   PetscCall(PetscFree2(feRef, fvRef));
    3057:   PetscCall(PetscFree(elemMat));
    3058:   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
    3059:   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
    3060:   if (mesh->printFEM > 1) {
    3061:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name));
    3062:     PetscCall(MatFilter(In, 1.0e-10, PETSC_FALSE, PETSC_FALSE));
    3063:     PetscCall(MatView(In, NULL));
    3064:   }
    3065:   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
    3066:   PetscFunctionReturn(PETSC_SUCCESS);
    3067: }

    3069: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
    3070: {
    3071:   SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
    3072: }

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

    3077:   Input Parameters:
    3078: + dmf  - The fine mesh
    3079: . dmc  - The coarse mesh
    3080: - user - The user context

    3082:   Output Parameter:
    3083: . In - The interpolation matrix

    3085:   Level: developer

    3087: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`
    3088: @*/
    3089: PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
    3090: {
    3091:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
    3092:   const char  *name = "Interpolator";
    3093:   PetscDS      prob;
    3094:   Mat          interp;
    3095:   PetscSection fsection, globalFSection;
    3096:   PetscSection csection, globalCSection;
    3097:   PetscInt     locRows, locCols;
    3098:   PetscReal   *x, *v0, *J, *invJ, detJ;
    3099:   PetscReal   *v0c, *Jc, *invJc, detJc;
    3100:   PetscScalar *elemMat;
    3101:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;

    3103:   PetscFunctionBegin;
    3104:   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
    3105:   PetscCall(DMGetCoordinateDim(dmc, &dim));
    3106:   PetscCall(DMGetDS(dmc, &prob));
    3107:   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
    3108:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    3109:   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
    3110:   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
    3111:   PetscCall(DMGetLocalSection(dmf, &fsection));
    3112:   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
    3113:   PetscCall(DMGetLocalSection(dmc, &csection));
    3114:   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
    3115:   PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd));
    3116:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    3117:   PetscCall(PetscMalloc1(totDim, &elemMat));

    3119:   PetscCall(MatGetLocalSize(In, &locRows, &locCols));
    3120:   PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp));
    3121:   PetscCall(MatSetType(interp, MATPREALLOCATOR));
    3122:   PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
    3123:   PetscCall(MatSetUp(interp));
    3124:   for (s = 0; s < 2; ++s) {
    3125:     for (field = 0; field < Nf; ++field) {
    3126:       PetscObject      obj;
    3127:       PetscClassId     id;
    3128:       PetscDualSpace   Q = NULL;
    3129:       PetscTabulation  T = NULL;
    3130:       PetscQuadrature  f;
    3131:       const PetscReal *qpoints, *qweights;
    3132:       PetscInt         Nc, qNc, Np, fpdim, off, i, d;

    3134:       PetscCall(PetscDSGetFieldOffset(prob, field, &off));
    3135:       PetscCall(PetscDSGetDiscretization(prob, field, &obj));
    3136:       PetscCall(PetscObjectGetClassId(obj, &id));
    3137:       if (id == PETSCFE_CLASSID) {
    3138:         PetscFE fe = (PetscFE)obj;

    3140:         PetscCall(PetscFEGetDualSpace(fe, &Q));
    3141:         PetscCall(PetscFEGetNumComponents(fe, &Nc));
    3142:         if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
    3143:       } else if (id == PETSCFV_CLASSID) {
    3144:         PetscFV fv = (PetscFV)obj;

    3146:         PetscCall(PetscFVGetDualSpace(fv, &Q));
    3147:         Nc = 1;
    3148:       } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
    3149:       PetscCall(PetscDualSpaceGetDimension(Q, &fpdim));
    3150:       /* For each fine grid cell */
    3151:       for (cell = cStart; cell < cEnd; ++cell) {
    3152:         PetscInt *findices, *cindices;
    3153:         PetscInt  numFIndices, numCIndices;

    3155:         PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
    3156:         PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
    3157:         PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim);
    3158:         for (i = 0; i < fpdim; ++i) {
    3159:           Vec                pointVec;
    3160:           PetscScalar       *pV;
    3161:           PetscSF            coarseCellSF = NULL;
    3162:           const PetscSFNode *coarseCells;
    3163:           PetscInt           numCoarseCells, cpdim, row = findices[i + off], q, c, j;

    3165:           /* Get points from the dual basis functional quadrature */
    3166:           PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
    3167:           PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
    3168:           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);
    3169:           PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec));
    3170:           PetscCall(VecSetBlockSize(pointVec, dim));
    3171:           PetscCall(VecGetArray(pointVec, &pV));
    3172:           for (q = 0; q < Np; ++q) {
    3173:             const PetscReal xi0[3] = {-1., -1., -1.};

    3175:             /* Transform point to real space */
    3176:             CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
    3177:             for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
    3178:           }
    3179:           PetscCall(VecRestoreArray(pointVec, &pV));
    3180:           /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
    3181:           /* OPT: Read this out from preallocation information */
    3182:           PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
    3183:           /* Update preallocation info */
    3184:           PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
    3185:           PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
    3186:           PetscCall(VecGetArray(pointVec, &pV));
    3187:           for (ccell = 0; ccell < numCoarseCells; ++ccell) {
    3188:             PetscReal       pVReal[3];
    3189:             const PetscReal xi0[3] = {-1., -1., -1.};

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

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

    3201:               if (id == PETSCFE_CLASSID) {
    3202:                 /* Evaluate coarse basis on contained point */
    3203:                 PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T));
    3204:                 PetscCall(PetscArrayzero(elemMat, cpdim));
    3205:                 /* Get elemMat entries by multiplying by weight */
    3206:                 for (j = 0; j < cpdim; ++j) {
    3207:                   for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c];
    3208:                 }
    3209:               } else {
    3210:                 for (j = 0; j < cpdim; ++j) {
    3211:                   for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c];
    3212:                 }
    3213:               }
    3214:               if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
    3215:             }
    3216:             /* Update interpolator */
    3217:             PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim);
    3218:             PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES));
    3219:             PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
    3220:           }
    3221:           PetscCall(VecRestoreArray(pointVec, &pV));
    3222:           PetscCall(PetscSFDestroy(&coarseCellSF));
    3223:           PetscCall(VecDestroy(&pointVec));
    3224:         }
    3225:         PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
    3226:       }
    3227:       if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
    3228:     }
    3229:     if (!s) {
    3230:       PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY));
    3231:       PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY));
    3232:       PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In));
    3233:       PetscCall(MatDestroy(&interp));
    3234:       interp = In;
    3235:     }
    3236:   }
    3237:   PetscCall(PetscFree3(v0, J, invJ));
    3238:   PetscCall(PetscFree3(v0c, Jc, invJc));
    3239:   PetscCall(PetscFree(elemMat));
    3240:   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
    3241:   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
    3242:   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
    3243:   PetscFunctionReturn(PETSC_SUCCESS);
    3244: }

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

    3249:   Input Parameters:
    3250: + dmf  - The fine mesh
    3251: . dmc  - The coarse mesh
    3252: - user - The user context

    3254:   Output Parameter:
    3255: . mass - The mass matrix

    3257:   Level: developer

    3259: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`
    3260: @*/
    3261: PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
    3262: {
    3263:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
    3264:   const char  *name = "Mass Matrix";
    3265:   PetscDS      prob;
    3266:   PetscSection fsection, csection, globalFSection, globalCSection;
    3267:   PetscHSetIJ  ht;
    3268:   PetscLayout  rLayout;
    3269:   PetscInt    *dnz, *onz;
    3270:   PetscInt     locRows, rStart, rEnd;
    3271:   PetscReal   *x, *v0, *J, *invJ, detJ;
    3272:   PetscReal   *v0c, *Jc, *invJc, detJc;
    3273:   PetscScalar *elemMat;
    3274:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell;

    3276:   PetscFunctionBegin;
    3277:   PetscCall(DMGetCoordinateDim(dmc, &dim));
    3278:   PetscCall(DMGetDS(dmc, &prob));
    3279:   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
    3280:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    3281:   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
    3282:   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
    3283:   PetscCall(DMGetLocalSection(dmf, &fsection));
    3284:   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
    3285:   PetscCall(DMGetLocalSection(dmc, &csection));
    3286:   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
    3287:   PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd));
    3288:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    3289:   PetscCall(PetscMalloc1(totDim, &elemMat));

    3291:   PetscCall(MatGetLocalSize(mass, &locRows, NULL));
    3292:   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout));
    3293:   PetscCall(PetscLayoutSetLocalSize(rLayout, locRows));
    3294:   PetscCall(PetscLayoutSetBlockSize(rLayout, 1));
    3295:   PetscCall(PetscLayoutSetUp(rLayout));
    3296:   PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd));
    3297:   PetscCall(PetscLayoutDestroy(&rLayout));
    3298:   PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz));
    3299:   PetscCall(PetscHSetIJCreate(&ht));
    3300:   for (field = 0; field < Nf; ++field) {
    3301:     PetscObject      obj;
    3302:     PetscClassId     id;
    3303:     PetscQuadrature  quad;
    3304:     const PetscReal *qpoints;
    3305:     PetscInt         Nq, Nc, i, d;

    3307:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
    3308:     PetscCall(PetscObjectGetClassId(obj, &id));
    3309:     if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
    3310:     else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
    3311:     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL));
    3312:     /* For each fine grid cell */
    3313:     for (cell = cStart; cell < cEnd; ++cell) {
    3314:       Vec                pointVec;
    3315:       PetscScalar       *pV;
    3316:       PetscSF            coarseCellSF = NULL;
    3317:       const PetscSFNode *coarseCells;
    3318:       PetscInt           numCoarseCells, q, c;
    3319:       PetscInt          *findices, *cindices;
    3320:       PetscInt           numFIndices, numCIndices;

    3322:       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
    3323:       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
    3324:       /* Get points from the quadrature */
    3325:       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
    3326:       PetscCall(VecSetBlockSize(pointVec, dim));
    3327:       PetscCall(VecGetArray(pointVec, &pV));
    3328:       for (q = 0; q < Nq; ++q) {
    3329:         const PetscReal xi0[3] = {-1., -1., -1.};

    3331:         /* Transform point to real space */
    3332:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
    3333:         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
    3334:       }
    3335:       PetscCall(VecRestoreArray(pointVec, &pV));
    3336:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
    3337:       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
    3338:       PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view"));
    3339:       /* Update preallocation info */
    3340:       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
    3341:       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
    3342:       {
    3343:         PetscHashIJKey key;
    3344:         PetscBool      missing;

    3346:         for (i = 0; i < numFIndices; ++i) {
    3347:           key.i = findices[i];
    3348:           if (key.i >= 0) {
    3349:             /* Get indices for coarse elements */
    3350:             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
    3351:               PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
    3352:               for (c = 0; c < numCIndices; ++c) {
    3353:                 key.j = cindices[c];
    3354:                 if (key.j < 0) continue;
    3355:                 PetscCall(PetscHSetIJQueryAdd(ht, key, &missing));
    3356:                 if (missing) {
    3357:                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart];
    3358:                   else ++onz[key.i - rStart];
    3359:                 }
    3360:               }
    3361:               PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
    3362:             }
    3363:           }
    3364:         }
    3365:       }
    3366:       PetscCall(PetscSFDestroy(&coarseCellSF));
    3367:       PetscCall(VecDestroy(&pointVec));
    3368:       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
    3369:     }
    3370:   }
    3371:   PetscCall(PetscHSetIJDestroy(&ht));
    3372:   PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL));
    3373:   PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
    3374:   PetscCall(PetscFree2(dnz, onz));
    3375:   for (field = 0; field < Nf; ++field) {
    3376:     PetscObject      obj;
    3377:     PetscClassId     id;
    3378:     PetscTabulation  T, Tfine;
    3379:     PetscQuadrature  quad;
    3380:     const PetscReal *qpoints, *qweights;
    3381:     PetscInt         Nq, Nc, i, d;

    3383:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
    3384:     PetscCall(PetscObjectGetClassId(obj, &id));
    3385:     if (id == PETSCFE_CLASSID) {
    3386:       PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
    3387:       PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine));
    3388:       PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T));
    3389:     } else {
    3390:       PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
    3391:     }
    3392:     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights));
    3393:     /* For each fine grid cell */
    3394:     for (cell = cStart; cell < cEnd; ++cell) {
    3395:       Vec                pointVec;
    3396:       PetscScalar       *pV;
    3397:       PetscSF            coarseCellSF = NULL;
    3398:       const PetscSFNode *coarseCells;
    3399:       PetscInt           numCoarseCells, cpdim, q, c, j;
    3400:       PetscInt          *findices, *cindices;
    3401:       PetscInt           numFIndices, numCIndices;

    3403:       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
    3404:       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
    3405:       /* Get points from the quadrature */
    3406:       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
    3407:       PetscCall(VecSetBlockSize(pointVec, dim));
    3408:       PetscCall(VecGetArray(pointVec, &pV));
    3409:       for (q = 0; q < Nq; ++q) {
    3410:         const PetscReal xi0[3] = {-1., -1., -1.};

    3412:         /* Transform point to real space */
    3413:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
    3414:         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
    3415:       }
    3416:       PetscCall(VecRestoreArray(pointVec, &pV));
    3417:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
    3418:       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
    3419:       /* Update matrix */
    3420:       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
    3421:       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
    3422:       PetscCall(VecGetArray(pointVec, &pV));
    3423:       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
    3424:         PetscReal       pVReal[3];
    3425:         const PetscReal xi0[3] = {-1., -1., -1.};

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

    3433:         if (id == PETSCFE_CLASSID) {
    3434:           PetscFE fe = (PetscFE)obj;

    3436:           /* Evaluate coarse basis on contained point */
    3437:           PetscCall(PetscFEGetDimension(fe, &cpdim));
    3438:           PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T));
    3439:           /* Get elemMat entries by multiplying by weight */
    3440:           for (i = 0; i < numFIndices; ++i) {
    3441:             PetscCall(PetscArrayzero(elemMat, cpdim));
    3442:             for (j = 0; j < cpdim; ++j) {
    3443:               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;
    3444:             }
    3445:             /* Update interpolator */
    3446:             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
    3447:             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
    3448:             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
    3449:           }
    3450:         } else {
    3451:           cpdim = 1;
    3452:           for (i = 0; i < numFIndices; ++i) {
    3453:             PetscCall(PetscArrayzero(elemMat, cpdim));
    3454:             for (j = 0; j < cpdim; ++j) {
    3455:               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ;
    3456:             }
    3457:             /* Update interpolator */
    3458:             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
    3459:             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));
    3460:             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
    3461:             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
    3462:           }
    3463:         }
    3464:         PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
    3465:       }
    3466:       PetscCall(VecRestoreArray(pointVec, &pV));
    3467:       PetscCall(PetscSFDestroy(&coarseCellSF));
    3468:       PetscCall(VecDestroy(&pointVec));
    3469:       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
    3470:     }
    3471:     if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
    3472:   }
    3473:   PetscCall(PetscFree3(v0, J, invJ));
    3474:   PetscCall(PetscFree3(v0c, Jc, invJc));
    3475:   PetscCall(PetscFree(elemMat));
    3476:   PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY));
    3477:   PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY));
    3478:   PetscFunctionReturn(PETSC_SUCCESS);
    3479: }

    3481: /*@
    3482:   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns

    3484:   Input Parameters:
    3485: + dmc  - The coarse mesh
    3486: . dmf  - The fine mesh
    3487: - user - The user context

    3489:   Output Parameter:
    3490: . sc - The mapping

    3492:   Level: developer

    3494: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`
    3495: @*/
    3496: PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
    3497: {
    3498:   PetscDS      prob;
    3499:   PetscFE     *feRef;
    3500:   PetscFV     *fvRef;
    3501:   Vec          fv, cv;
    3502:   IS           fis, cis;
    3503:   PetscSection fsection, fglobalSection, csection, cglobalSection;
    3504:   PetscInt    *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
    3505:   PetscInt     cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
    3506:   PetscBool   *needAvg;

    3508:   PetscFunctionBegin;
    3509:   PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
    3510:   PetscCall(DMGetDimension(dmf, &dim));
    3511:   PetscCall(DMGetLocalSection(dmf, &fsection));
    3512:   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
    3513:   PetscCall(DMGetLocalSection(dmc, &csection));
    3514:   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
    3515:   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
    3516:   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
    3517:   PetscCall(DMGetDS(dmc, &prob));
    3518:   PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg));
    3519:   for (f = 0; f < Nf; ++f) {
    3520:     PetscObject  obj;
    3521:     PetscClassId id;
    3522:     PetscInt     fNb = 0, Nc = 0;

    3524:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    3525:     PetscCall(PetscObjectGetClassId(obj, &id));
    3526:     if (id == PETSCFE_CLASSID) {
    3527:       PetscFE    fe = (PetscFE)obj;
    3528:       PetscSpace sp;
    3529:       PetscInt   maxDegree;

    3531:       PetscCall(PetscFERefine(fe, &feRef[f]));
    3532:       PetscCall(PetscFEGetDimension(feRef[f], &fNb));
    3533:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
    3534:       PetscCall(PetscFEGetBasisSpace(fe, &sp));
    3535:       PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
    3536:       if (!maxDegree) needAvg[f] = PETSC_TRUE;
    3537:     } else if (id == PETSCFV_CLASSID) {
    3538:       PetscFV        fv = (PetscFV)obj;
    3539:       PetscDualSpace Q;

    3541:       PetscCall(PetscFVRefine(fv, &fvRef[f]));
    3542:       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
    3543:       PetscCall(PetscDualSpaceGetDimension(Q, &fNb));
    3544:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
    3545:       needAvg[f] = PETSC_TRUE;
    3546:     }
    3547:     fTotDim += fNb;
    3548:   }
    3549:   PetscCall(PetscDSGetTotalDimension(prob, &cTotDim));
    3550:   PetscCall(PetscMalloc1(cTotDim, &cmap));
    3551:   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
    3552:     PetscFE        feC;
    3553:     PetscFV        fvC;
    3554:     PetscDualSpace QF, QC;
    3555:     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;

    3557:     if (feRef[field]) {
    3558:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC));
    3559:       PetscCall(PetscFEGetNumComponents(feC, &NcC));
    3560:       PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
    3561:       PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
    3562:       PetscCall(PetscDualSpaceGetOrder(QF, &order));
    3563:       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
    3564:       PetscCall(PetscFEGetDualSpace(feC, &QC));
    3565:       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
    3566:     } else {
    3567:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC));
    3568:       PetscCall(PetscFVGetNumComponents(fvC, &NcC));
    3569:       PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
    3570:       PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
    3571:       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
    3572:       PetscCall(PetscFVGetDualSpace(fvC, &QC));
    3573:       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
    3574:     }
    3575:     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);
    3576:     for (c = 0; c < cpdim; ++c) {
    3577:       PetscQuadrature  cfunc;
    3578:       const PetscReal *cqpoints, *cqweights;
    3579:       PetscInt         NqcC, NpC;
    3580:       PetscBool        found = PETSC_FALSE;

    3582:       PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
    3583:       PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
    3584:       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);
    3585:       PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
    3586:       for (f = 0; f < fpdim; ++f) {
    3587:         PetscQuadrature  ffunc;
    3588:         const PetscReal *fqpoints, *fqweights;
    3589:         PetscReal        sum = 0.0;
    3590:         PetscInt         NqcF, NpF;

    3592:         PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc));
    3593:         PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights));
    3594:         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);
    3595:         if (NpC != NpF) continue;
    3596:         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
    3597:         if (sum > 1.0e-9) continue;
    3598:         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]);
    3599:         if (sum < 1.0e-9) continue;
    3600:         cmap[offsetC + c] = offsetF + f;
    3601:         found             = PETSC_TRUE;
    3602:         break;
    3603:       }
    3604:       if (!found) {
    3605:         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
    3606:         if (fvRef[field] || (feRef[field] && order == 0)) {
    3607:           cmap[offsetC + c] = offsetF + 0;
    3608:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
    3609:       }
    3610:     }
    3611:     offsetC += cpdim;
    3612:     offsetF += fpdim;
    3613:   }
    3614:   for (f = 0; f < Nf; ++f) {
    3615:     PetscCall(PetscFEDestroy(&feRef[f]));
    3616:     PetscCall(PetscFVDestroy(&fvRef[f]));
    3617:   }
    3618:   PetscCall(PetscFree3(feRef, fvRef, needAvg));

    3620:   PetscCall(DMGetGlobalVector(dmf, &fv));
    3621:   PetscCall(DMGetGlobalVector(dmc, &cv));
    3622:   PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
    3623:   PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
    3624:   PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
    3625:   PetscCall(PetscMalloc1(m, &cindices));
    3626:   PetscCall(PetscMalloc1(m, &findices));
    3627:   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
    3628:   for (c = cStart; c < cEnd; ++c) {
    3629:     PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
    3630:     for (d = 0; d < cTotDim; ++d) {
    3631:       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
    3632:       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]]);
    3633:       cindices[cellCIndices[d] - startC] = cellCIndices[d];
    3634:       findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
    3635:     }
    3636:   }
    3637:   PetscCall(PetscFree(cmap));
    3638:   PetscCall(PetscFree2(cellCIndices, cellFIndices));

    3640:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
    3641:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
    3642:   PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
    3643:   PetscCall(ISDestroy(&cis));
    3644:   PetscCall(ISDestroy(&fis));
    3645:   PetscCall(DMRestoreGlobalVector(dmf, &fv));
    3646:   PetscCall(DMRestoreGlobalVector(dmc, &cv));
    3647:   PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
    3648:   PetscFunctionReturn(PETSC_SUCCESS);
    3649: }

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

    3654:   Input Parameters:
    3655: + dm     - The `DM`
    3656: . cellIS - The cells to include
    3657: . locX   - A local vector with the solution fields
    3658: . locX_t - A local vector with solution field time derivatives, or NULL
    3659: - locA   - A local vector with auxiliary fields, or NULL

    3661:   Output Parameters:
    3662: + u   - The field coefficients
    3663: . u_t - The fields derivative coefficients
    3664: - a   - The auxiliary field coefficients

    3666:   Level: developer

    3668: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
    3669: @*/
    3670: PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
    3671: {
    3672:   DM              plex, plexA = NULL;
    3673:   DMEnclosureType encAux;
    3674:   PetscSection    section, sectionAux;
    3675:   PetscDS         prob;
    3676:   const PetscInt *cells;
    3677:   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;

    3679:   PetscFunctionBegin;
    3684:   PetscAssertPointer(u, 6);
    3685:   PetscAssertPointer(u_t, 7);
    3686:   PetscAssertPointer(a, 8);
    3687:   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
    3688:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    3689:   PetscCall(DMGetLocalSection(dm, &section));
    3690:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
    3691:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    3692:   if (locA) {
    3693:     DM      dmAux;
    3694:     PetscDS probAux;

    3696:     PetscCall(VecGetDM(locA, &dmAux));
    3697:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    3698:     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
    3699:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
    3700:     PetscCall(DMGetDS(dmAux, &probAux));
    3701:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    3702:   }
    3703:   numCells = cEnd - cStart;
    3704:   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
    3705:   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
    3706:   else *u_t = NULL;
    3707:   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
    3708:   else *a = NULL;
    3709:   for (c = cStart; c < cEnd; ++c) {
    3710:     const PetscInt cell = cells ? cells[c] : c;
    3711:     const PetscInt cind = c - cStart;
    3712:     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
    3713:     PetscInt       i;

    3715:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
    3716:     for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
    3717:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
    3718:     if (locX_t) {
    3719:       PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
    3720:       for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
    3721:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
    3722:     }
    3723:     if (locA) {
    3724:       PetscInt subcell;
    3725:       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
    3726:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x));
    3727:       for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
    3728:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x));
    3729:     }
    3730:   }
    3731:   PetscCall(DMDestroy(&plex));
    3732:   if (locA) PetscCall(DMDestroy(&plexA));
    3733:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    3734:   PetscFunctionReturn(PETSC_SUCCESS);
    3735: }

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

    3740:   Input Parameters:
    3741: + dm     - The `DM`
    3742: . cellIS - The cells to include
    3743: . locX   - A local vector with the solution fields
    3744: . locX_t - A local vector with solution field time derivatives, or NULL
    3745: - locA   - A local vector with auxiliary fields, or NULL

    3747:   Output Parameters:
    3748: + u   - The field coefficients
    3749: . u_t - The fields derivative coefficients
    3750: - a   - The auxiliary field coefficients

    3752:   Level: developer

    3754: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
    3755: @*/
    3756: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
    3757: {
    3758:   PetscFunctionBegin;
    3759:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
    3760:   if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
    3761:   if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
    3762:   PetscFunctionReturn(PETSC_SUCCESS);
    3763: }

    3765: static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
    3766: {
    3767:   DM              plex, plexA = NULL;
    3768:   DMEnclosureType encAux;
    3769:   PetscSection    section, sectionAux;
    3770:   PetscDS         ds, dsIn;
    3771:   const PetscInt *cells;
    3772:   PetscInt        cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;

    3774:   PetscFunctionBegin;
    3780:   PetscAssertPointer(u, 6);
    3781:   PetscAssertPointer(u_t, 7);
    3782:   PetscAssertPointer(a, 8);
    3783:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    3784:   numCells = cEnd - cStart;
    3785:   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
    3786:   PetscCall(DMGetLocalSection(dm, &section));
    3787:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
    3788:   PetscCall(PetscDSGetNumFields(dsIn, &Nf));
    3789:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDim));
    3790:   if (locA) {
    3791:     DM      dmAux;
    3792:     PetscDS probAux;

    3794:     PetscCall(VecGetDM(locA, &dmAux));
    3795:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    3796:     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
    3797:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
    3798:     PetscCall(DMGetDS(dmAux, &probAux));
    3799:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    3800:   }
    3801:   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
    3802:   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
    3803:   else {
    3804:     *u_t = NULL;
    3805:   }
    3806:   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
    3807:   else {
    3808:     *a = NULL;
    3809:   }
    3810:   // Loop over cohesive cells
    3811:   for (c = cStart; c < cEnd; ++c) {
    3812:     const PetscInt  cell = cells ? cells[c] : c;
    3813:     const PetscInt  cind = c - cStart;
    3814:     PetscScalar    *xf = NULL, *xc = NULL, *x = NULL, *xf_t = NULL, *xc_t = NULL;
    3815:     PetscScalar    *ul = &(*u)[cind * totDim], *ul_t = PetscSafePointerPlusOffset(*u_t, cind * totDim);
    3816:     const PetscInt *cone, *ornt;
    3817:     PetscInt        Nx = 0, Nxf, s;

    3819:     PetscCall(DMPlexGetCone(dm, cell, &cone));
    3820:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
    3821:     // Put in cohesive unknowns
    3822:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
    3823:     if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t));
    3824:     for (f = 0; f < Nf; ++f) {
    3825:       PetscInt  fdofIn, foff, foffIn;
    3826:       PetscBool cohesive;

    3828:       PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
    3829:       if (!cohesive) continue;
    3830:       PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
    3831:       PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff));
    3832:       PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
    3833:       for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i];
    3834:       if (locX_t)
    3835:         for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + i] = xf_t[foff + i];
    3836:       Nx += fdofIn;
    3837:     }
    3838:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf));
    3839:     if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &xf_t));
    3840:     // Loop over sides of surface
    3841:     for (s = 0; s < 2; ++s) {
    3842:       const PetscInt *support;
    3843:       const PetscInt  face = cone[s];
    3844:       PetscInt        ssize, ncell, Nxc;

    3846:       // I don't think I need the face to have 0 orientation in the hybrid cell
    3847:       //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]);
    3848:       PetscCall(DMPlexGetSupport(dm, face, &support));
    3849:       PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
    3850:       if (support[0] == cell) ncell = support[1];
    3851:       else if (support[1] == cell) ncell = support[0];
    3852:       else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
    3853:       // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields
    3854:       PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc));
    3855:       if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, ncell, NULL, &xc_t));
    3856:       for (f = 0; f < Nf; ++f) {
    3857:         PetscInt  fdofIn, foffIn;
    3858:         PetscBool cohesive;

    3860:         PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
    3861:         if (cohesive) continue;
    3862:         PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
    3863:         PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
    3864:         for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foffIn + i];
    3865:         if (locX_t)
    3866:           for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + s * fdofIn + i] = xc_t[foffIn + i];
    3867:         Nx += fdofIn;
    3868:       }
    3869:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc));
    3870:       if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, ncell, NULL, &xc_t));
    3871:     }
    3872:     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);

    3874:     if (locA) {
    3875:       PetscScalar *al = &(*a)[cind * totDimAux];
    3876:       PetscInt     subcell;

    3878:       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
    3879:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
    3880:       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);
    3881:       for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i];
    3882:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
    3883:     }
    3884:   }
    3885:   PetscCall(DMDestroy(&plex));
    3886:   PetscCall(DMDestroy(&plexA));
    3887:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    3888:   PetscFunctionReturn(PETSC_SUCCESS);
    3889: }

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

    3894:   Input Parameters:
    3895: + dm      - The full domain DM
    3896: . dmX     - An array of DM for the field, say an auxiliary DM, indexed by s
    3897: . dsX     - An array of PetscDS for the field, indexed by s
    3898: . cellIS  - The interface cells for which we want values
    3899: . locX    - An array of local vectors with the field values, indexed by s
    3900: - useCell - Flag to have values come from neighboring cell rather than endcap face

    3902:   Output Parameter:
    3903: . x       - An array of field values, indexed by s

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

    3908:   Level: advanced

    3910: .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()`
    3911: */
    3912: static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
    3913: {
    3914:   DM              plexX[2];
    3915:   DMEnclosureType encX[2];
    3916:   PetscSection    sectionX[2];
    3917:   const PetscInt *cells;
    3918:   PetscInt        cStart, cEnd, numCells, c, s, totDimX[2];

    3920:   PetscFunctionBegin;
    3921:   PetscAssertPointer(locX, 5);
    3922:   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
    3923:   PetscAssertPointer(dmX, 2);
    3924:   PetscAssertPointer(dsX, 3);
    3926:   PetscAssertPointer(x, 7);
    3927:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    3928:   numCells = cEnd - cStart;
    3929:   for (s = 0; s < 2; ++s) {
    3933:     PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE));
    3934:     PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s]));
    3935:     PetscCall(DMGetLocalSection(dmX[s], &sectionX[s]));
    3936:     PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s]));
    3937:     PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s]));
    3938:   }
    3939:   for (c = cStart; c < cEnd; ++c) {
    3940:     const PetscInt  cell = cells ? cells[c] : c;
    3941:     const PetscInt  cind = c - cStart;
    3942:     const PetscInt *cone, *ornt;

    3944:     PetscCall(DMPlexGetCone(dm, cell, &cone));
    3945:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
    3946:     //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]);
    3947:     for (s = 0; s < 2; ++s) {
    3948:       const PetscInt tdX     = totDimX[s];
    3949:       PetscScalar   *closure = NULL, *xl = &x[s][cind * tdX];
    3950:       PetscInt       face = cone[s], point = face, subpoint, Nx, i;

    3952:       if (useCell) {
    3953:         const PetscInt *support;
    3954:         PetscInt        ssize;

    3956:         PetscCall(DMPlexGetSupport(dm, face, &support));
    3957:         PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
    3958:         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);
    3959:         if (support[0] == cell) point = support[1];
    3960:         else if (support[1] == cell) point = support[0];
    3961:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
    3962:       }
    3963:       PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint));
    3964:       PetscCall(DMPlexVecGetOrientedClosure_Internal(plexX[s], sectionX[s], PETSC_FALSE, locX[s], subpoint, ornt[s], &Nx, &closure));
    3965:       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);
    3966:       for (i = 0; i < Nx; ++i) xl[i] = closure[i];
    3967:       PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure));
    3968:     }
    3969:   }
    3970:   for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s]));
    3971:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    3972:   PetscFunctionReturn(PETSC_SUCCESS);
    3973: }

    3975: static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
    3976: {
    3977:   PetscFunctionBegin;
    3978:   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
    3979:   PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0]));
    3980:   PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1]));
    3981:   PetscFunctionReturn(PETSC_SUCCESS);
    3982: }

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

    3987:   Input Parameters:
    3988: + dm           - The `DM`
    3989: . fStart       - The first face to include
    3990: . fEnd         - The first face to exclude
    3991: . locX         - A local vector with the solution fields
    3992: . locX_t       - A local vector with solution field time derivatives, or NULL
    3993: . faceGeometry - A local vector with face geometry
    3994: . cellGeometry - A local vector with cell geometry
    3995: - locGrad      - A local vector with field gradients, or NULL

    3997:   Output Parameters:
    3998: + Nface - The number of faces with field values
    3999: . uL    - The field values at the left side of the face
    4000: - uR    - The field values at the right side of the face

    4002:   Level: developer

    4004: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
    4005: @*/
    4006: PetscErrorCode DMPlexGetFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR)
    4007: {
    4008:   DM                 dmFace, dmCell, dmGrad = NULL;
    4009:   PetscSection       section;
    4010:   PetscDS            prob;
    4011:   DMLabel            ghostLabel;
    4012:   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
    4013:   PetscBool         *isFE;
    4014:   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;

    4016:   PetscFunctionBegin;
    4023:   PetscAssertPointer(uL, 10);
    4024:   PetscAssertPointer(uR, 11);
    4025:   PetscCall(DMGetDimension(dm, &dim));
    4026:   PetscCall(DMGetDS(dm, &prob));
    4027:   PetscCall(DMGetLocalSection(dm, &section));
    4028:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    4029:   PetscCall(PetscDSGetTotalComponents(prob, &Nc));
    4030:   PetscCall(PetscMalloc1(Nf, &isFE));
    4031:   for (f = 0; f < Nf; ++f) {
    4032:     PetscObject  obj;
    4033:     PetscClassId id;

    4035:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    4036:     PetscCall(PetscObjectGetClassId(obj, &id));
    4037:     if (id == PETSCFE_CLASSID) {
    4038:       isFE[f] = PETSC_TRUE;
    4039:     } else if (id == PETSCFV_CLASSID) {
    4040:       isFE[f] = PETSC_FALSE;
    4041:     } else {
    4042:       isFE[f] = PETSC_FALSE;
    4043:     }
    4044:   }
    4045:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
    4046:   PetscCall(VecGetArrayRead(locX, &x));
    4047:   PetscCall(VecGetDM(faceGeometry, &dmFace));
    4048:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
    4049:   PetscCall(VecGetDM(cellGeometry, &dmCell));
    4050:   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
    4051:   if (locGrad) {
    4052:     PetscCall(VecGetDM(locGrad, &dmGrad));
    4053:     PetscCall(VecGetArrayRead(locGrad, &lgrad));
    4054:   }
    4055:   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL));
    4056:   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR));
    4057:   /* Right now just eat the extra work for FE (could make a cell loop) */
    4058:   for (face = fStart, iface = 0; face < fEnd; ++face) {
    4059:     const PetscInt  *cells;
    4060:     PetscFVFaceGeom *fg;
    4061:     PetscFVCellGeom *cgL, *cgR;
    4062:     PetscScalar     *xL, *xR, *gL, *gR;
    4063:     PetscScalar     *uLl = *uL, *uRl = *uR;
    4064:     PetscInt         ghost, nsupp, nchild;

    4066:     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
    4067:     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
    4068:     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
    4069:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
    4070:     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
    4071:     PetscCall(DMPlexGetSupport(dm, face, &cells));
    4072:     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
    4073:     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
    4074:     for (f = 0; f < Nf; ++f) {
    4075:       PetscInt off;

    4077:       PetscCall(PetscDSGetComponentOffset(prob, f, &off));
    4078:       if (isFE[f]) {
    4079:         const PetscInt *cone;
    4080:         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;

    4082:         xL = xR = NULL;
    4083:         PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
    4084:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
    4085:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
    4086:         PetscCall(DMPlexGetCone(dm, cells[0], &cone));
    4087:         PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
    4088:         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
    4089:           if (cone[faceLocL] == face) break;
    4090:         PetscCall(DMPlexGetCone(dm, cells[1], &cone));
    4091:         PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
    4092:         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
    4093:           if (cone[faceLocR] == face) break;
    4094:         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]);
    4095:         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
    4096:         /* TODO: this is a hack that might not be right for nonconforming */
    4097:         if (faceLocL < coneSizeL) {
    4098:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
    4099:           if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
    4100:           else {
    4101:             for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
    4102:           }
    4103:         } else {
    4104:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
    4105:           PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
    4106:           for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
    4107:         }
    4108:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
    4109:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
    4110:       } else {
    4111:         PetscFV  fv;
    4112:         PetscInt numComp, c;

    4114:         PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
    4115:         PetscCall(PetscFVGetNumComponents(fv, &numComp));
    4116:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
    4117:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
    4118:         if (dmGrad) {
    4119:           PetscReal dxL[3], dxR[3];

    4121:           PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
    4122:           PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
    4123:           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
    4124:           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
    4125:           for (c = 0; c < numComp; ++c) {
    4126:             uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
    4127:             uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
    4128:           }
    4129:         } else {
    4130:           for (c = 0; c < numComp; ++c) {
    4131:             uLl[iface * Nc + off + c] = xL[c];
    4132:             uRl[iface * Nc + off + c] = xR[c];
    4133:           }
    4134:         }
    4135:       }
    4136:     }
    4137:     ++iface;
    4138:   }
    4139:   *Nface = iface;
    4140:   PetscCall(VecRestoreArrayRead(locX, &x));
    4141:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
    4142:   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
    4143:   if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
    4144:   PetscCall(PetscFree(isFE));
    4145:   PetscFunctionReturn(PETSC_SUCCESS);
    4146: }

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

    4151:   Input Parameters:
    4152: + dm           - The `DM`
    4153: . fStart       - The first face to include
    4154: . fEnd         - The first face to exclude
    4155: . locX         - A local vector with the solution fields
    4156: . locX_t       - A local vector with solution field time derivatives, or NULL
    4157: . faceGeometry - A local vector with face geometry
    4158: . cellGeometry - A local vector with cell geometry
    4159: - locGrad      - A local vector with field gradients, or NULL

    4161:   Output Parameters:
    4162: + Nface - The number of faces with field values
    4163: . uL    - The field values at the left side of the face
    4164: - uR    - The field values at the right side of the face

    4166:   Level: developer

    4168: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
    4169: @*/
    4170: PetscErrorCode DMPlexRestoreFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR)
    4171: {
    4172:   PetscFunctionBegin;
    4173:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
    4174:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
    4175:   PetscFunctionReturn(PETSC_SUCCESS);
    4176: }

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

    4181:   Input Parameters:
    4182: + dm           - The `DM`
    4183: . fStart       - The first face to include
    4184: . fEnd         - The first face to exclude
    4185: . faceGeometry - A local vector with face geometry
    4186: - cellGeometry - A local vector with cell geometry

    4188:   Output Parameters:
    4189: + Nface - The number of faces with field values
    4190: . fgeom - The extract the face centroid and normal
    4191: - vol   - The cell volume

    4193:   Level: developer

    4195: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
    4196: @*/
    4197: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
    4198: {
    4199:   DM                 dmFace, dmCell;
    4200:   DMLabel            ghostLabel;
    4201:   const PetscScalar *facegeom, *cellgeom;
    4202:   PetscInt           dim, numFaces = fEnd - fStart, iface, face;

    4204:   PetscFunctionBegin;
    4208:   PetscAssertPointer(fgeom, 7);
    4209:   PetscAssertPointer(vol, 8);
    4210:   PetscCall(DMGetDimension(dm, &dim));
    4211:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
    4212:   PetscCall(VecGetDM(faceGeometry, &dmFace));
    4213:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
    4214:   PetscCall(VecGetDM(cellGeometry, &dmCell));
    4215:   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
    4216:   PetscCall(PetscMalloc1(numFaces, fgeom));
    4217:   PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol));
    4218:   for (face = fStart, iface = 0; face < fEnd; ++face) {
    4219:     const PetscInt  *cells;
    4220:     PetscFVFaceGeom *fg;
    4221:     PetscFVCellGeom *cgL, *cgR;
    4222:     PetscFVFaceGeom *fgeoml = *fgeom;
    4223:     PetscReal       *voll   = *vol;
    4224:     PetscInt         ghost, d, nchild, nsupp;

    4226:     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
    4227:     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
    4228:     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
    4229:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
    4230:     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
    4231:     PetscCall(DMPlexGetSupport(dm, face, &cells));
    4232:     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
    4233:     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
    4234:     for (d = 0; d < dim; ++d) {
    4235:       fgeoml[iface].centroid[d] = fg->centroid[d];
    4236:       fgeoml[iface].normal[d]   = fg->normal[d];
    4237:     }
    4238:     voll[iface * 2 + 0] = cgL->volume;
    4239:     voll[iface * 2 + 1] = cgR->volume;
    4240:     ++iface;
    4241:   }
    4242:   *Nface = iface;
    4243:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
    4244:   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
    4245:   PetscFunctionReturn(PETSC_SUCCESS);
    4246: }

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

    4251:   Input Parameters:
    4252: + dm           - The `DM`
    4253: . fStart       - The first face to include
    4254: . fEnd         - The first face to exclude
    4255: . faceGeometry - A local vector with face geometry
    4256: - cellGeometry - A local vector with cell geometry

    4258:   Output Parameters:
    4259: + Nface - The number of faces with field values
    4260: . fgeom - The extract the face centroid and normal
    4261: - vol   - The cell volume

    4263:   Level: developer

    4265: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
    4266: @*/
    4267: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
    4268: {
    4269:   PetscFunctionBegin;
    4270:   PetscCall(PetscFree(*fgeom));
    4271:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
    4272:   PetscFunctionReturn(PETSC_SUCCESS);
    4273: }

    4275: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
    4276: {
    4277:   char           composeStr[33] = {0};
    4278:   PetscObjectId  id;
    4279:   PetscContainer container;

    4281:   PetscFunctionBegin;
    4282:   PetscCall(PetscObjectGetId((PetscObject)quad, &id));
    4283:   PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
    4284:   PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
    4285:   if (container) {
    4286:     PetscCall(PetscContainerGetPointer(container, (void **)geom));
    4287:   } else {
    4288:     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
    4289:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
    4290:     PetscCall(PetscContainerSetPointer(container, (void *)*geom));
    4291:     PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom));
    4292:     PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
    4293:     PetscCall(PetscContainerDestroy(&container));
    4294:   }
    4295:   PetscFunctionReturn(PETSC_SUCCESS);
    4296: }

    4298: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
    4299: {
    4300:   PetscFunctionBegin;
    4301:   *geom = NULL;
    4302:   PetscFunctionReturn(PETSC_SUCCESS);
    4303: }

    4305: PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
    4306: {
    4307:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
    4308:   const char     *name       = "Residual";
    4309:   DM              dmAux      = NULL;
    4310:   DMLabel         ghostLabel = NULL;
    4311:   PetscDS         prob       = NULL;
    4312:   PetscDS         probAux    = NULL;
    4313:   PetscBool       useFEM     = PETSC_FALSE;
    4314:   PetscBool       isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
    4315:   DMField         coordField = NULL;
    4316:   Vec             locA;
    4317:   PetscScalar    *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
    4318:   IS              chunkIS;
    4319:   const PetscInt *cells;
    4320:   PetscInt        cStart, cEnd, numCells;
    4321:   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
    4322:   PetscInt        maxDegree = PETSC_INT_MAX;
    4323:   PetscFormKey    key;
    4324:   PetscQuadrature affineQuad = NULL, *quads = NULL;
    4325:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

    4327:   PetscFunctionBegin;
    4328:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
    4329:   /* FEM+FVM */
    4330:   /* 1: Get sizes from dm and dmAux */
    4331:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
    4332:   PetscCall(DMGetDS(dm, &prob));
    4333:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    4334:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    4335:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
    4336:   if (locA) {
    4337:     PetscCall(VecGetDM(locA, &dmAux));
    4338:     PetscCall(DMGetDS(dmAux, &probAux));
    4339:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    4340:   }
    4341:   /* 2: Get geometric data */
    4342:   for (f = 0; f < Nf; ++f) {
    4343:     PetscObject  obj;
    4344:     PetscClassId id;
    4345:     PetscBool    fimp;

    4347:     PetscCall(PetscDSGetImplicit(prob, f, &fimp));
    4348:     if (isImplicit != fimp) continue;
    4349:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    4350:     PetscCall(PetscObjectGetClassId(obj, &id));
    4351:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
    4352:     PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented");
    4353:   }
    4354:   if (useFEM) {
    4355:     PetscCall(DMGetCoordinateField(dm, &coordField));
    4356:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    4357:     if (maxDegree <= 1) {
    4358:       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
    4359:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
    4360:     } else {
    4361:       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
    4362:       for (f = 0; f < Nf; ++f) {
    4363:         PetscObject  obj;
    4364:         PetscClassId id;
    4365:         PetscBool    fimp;

    4367:         PetscCall(PetscDSGetImplicit(prob, f, &fimp));
    4368:         if (isImplicit != fimp) continue;
    4369:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    4370:         PetscCall(PetscObjectGetClassId(obj, &id));
    4371:         if (id == PETSCFE_CLASSID) {
    4372:           PetscFE fe = (PetscFE)obj;

    4374:           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
    4375:           PetscCall(PetscObjectReference((PetscObject)quads[f]));
    4376:           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
    4377:         }
    4378:       }
    4379:     }
    4380:   }
    4381:   /* Loop over chunks */
    4382:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    4383:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
    4384:   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
    4385:   numCells      = cEnd - cStart;
    4386:   numChunks     = 1;
    4387:   cellChunkSize = numCells / numChunks;
    4388:   numChunks     = PetscMin(1, numCells);
    4389:   key.label     = NULL;
    4390:   key.value     = 0;
    4391:   key.part      = 0;
    4392:   for (chunk = 0; chunk < numChunks; ++chunk) {
    4393:     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
    4394:     PetscReal       *vol   = NULL;
    4395:     PetscFVFaceGeom *fgeom = NULL;
    4396:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
    4397:     PetscInt         numFaces = 0;

    4399:     /* Extract field coefficients */
    4400:     if (useFEM) {
    4401:       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
    4402:       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
    4403:       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
    4404:       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
    4405:     }
    4406:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
    4407:     /* Loop over fields */
    4408:     for (f = 0; f < Nf; ++f) {
    4409:       PetscObject  obj;
    4410:       PetscClassId id;
    4411:       PetscBool    fimp;
    4412:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

    4414:       key.field = f;
    4415:       PetscCall(PetscDSGetImplicit(prob, f, &fimp));
    4416:       if (isImplicit != fimp) continue;
    4417:       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    4418:       PetscCall(PetscObjectGetClassId(obj, &id));
    4419:       if (id == PETSCFE_CLASSID) {
    4420:         PetscFE         fe        = (PetscFE)obj;
    4421:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
    4422:         PetscFEGeom    *chunkGeom = NULL;
    4423:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
    4424:         PetscInt        Nq, Nb;

    4426:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    4427:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
    4428:         PetscCall(PetscFEGetDimension(fe, &Nb));
    4429:         blockSize = Nb;
    4430:         batchSize = numBlocks * blockSize;
    4431:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    4432:         numChunks = numCells / (numBatches * batchSize);
    4433:         Ne        = numChunks * numBatches * batchSize;
    4434:         Nr        = numCells % (numBatches * batchSize);
    4435:         offset    = numCells - Nr;
    4436:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
    4437:         /*   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) */
    4438:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
    4439:         PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
    4440:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
    4441:         PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
    4442:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
    4443:       } else if (id == PETSCFV_CLASSID) {
    4444:         PetscFV fv = (PetscFV)obj;

    4446:         Ne = numFaces;
    4447:         /* Riemann solve over faces (need fields at face centroids) */
    4448:         /*   We need to evaluate FE fields at those coordinates */
    4449:         PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
    4450:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
    4451:     }
    4452:     /* Loop over domain */
    4453:     if (useFEM) {
    4454:       /* Add elemVec to locX */
    4455:       for (c = cS; c < cE; ++c) {
    4456:         const PetscInt cell = cells ? cells[c] : c;
    4457:         const PetscInt cind = c - cStart;

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

    4463:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
    4464:           if (ghostVal > 0) continue;
    4465:         }
    4466:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
    4467:       }
    4468:     }
    4469:     /* Handle time derivative */
    4470:     if (locX_t) {
    4471:       PetscScalar *x_t, *fa;

    4473:       PetscCall(VecGetArray(locF, &fa));
    4474:       PetscCall(VecGetArray(locX_t, &x_t));
    4475:       for (f = 0; f < Nf; ++f) {
    4476:         PetscFV      fv;
    4477:         PetscObject  obj;
    4478:         PetscClassId id;
    4479:         PetscInt     pdim, d;

    4481:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
    4482:         PetscCall(PetscObjectGetClassId(obj, &id));
    4483:         if (id != PETSCFV_CLASSID) continue;
    4484:         fv = (PetscFV)obj;
    4485:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
    4486:         for (c = cS; c < cE; ++c) {
    4487:           const PetscInt cell = cells ? cells[c] : c;
    4488:           PetscScalar   *u_t, *r;

    4490:           if (ghostLabel) {
    4491:             PetscInt ghostVal;

    4493:             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
    4494:             if (ghostVal > 0) continue;
    4495:           }
    4496:           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
    4497:           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
    4498:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
    4499:         }
    4500:       }
    4501:       PetscCall(VecRestoreArray(locX_t, &x_t));
    4502:       PetscCall(VecRestoreArray(locF, &fa));
    4503:     }
    4504:     if (useFEM) {
    4505:       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
    4506:       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
    4507:     }
    4508:   }
    4509:   if (useFEM) PetscCall(ISDestroy(&chunkIS));
    4510:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    4511:   /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
    4512:   if (useFEM) {
    4513:     if (maxDegree <= 1) {
    4514:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
    4515:       PetscCall(PetscQuadratureDestroy(&affineQuad));
    4516:     } else {
    4517:       for (f = 0; f < Nf; ++f) {
    4518:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
    4519:         PetscCall(PetscQuadratureDestroy(&quads[f]));
    4520:       }
    4521:       PetscCall(PetscFree2(quads, geoms));
    4522:     }
    4523:   }
    4524:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
    4525:   PetscFunctionReturn(PETSC_SUCCESS);
    4526: }

    4528: /*
    4529:   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

    4531:   X   - The local solution vector
    4532:   X_t - The local solution time derivative vector, or NULL
    4533: */
    4534: 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, void *ctx)
    4535: {
    4536:   DM_Plex        *mesh = (DM_Plex *)dm->data;
    4537:   const char     *name = "Jacobian", *nameP = "JacobianPre";
    4538:   DM              dmAux = NULL;
    4539:   PetscDS         prob, probAux = NULL;
    4540:   PetscSection    sectionAux = NULL;
    4541:   Vec             A;
    4542:   DMField         coordField;
    4543:   PetscFEGeom    *cgeomFEM;
    4544:   PetscQuadrature qGeom = NULL;
    4545:   Mat             J = Jac, JP = JacP;
    4546:   PetscScalar    *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
    4547:   PetscBool       hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
    4548:   const PetscInt *cells;
    4549:   PetscFormKey    key;
    4550:   PetscInt        Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;

    4552:   PetscFunctionBegin;
    4553:   PetscCall(ISGetLocalSize(cellIS, &numCells));
    4554:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    4555:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    4556:   PetscCall(DMGetDS(dm, &prob));
    4557:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
    4558:   if (A) {
    4559:     PetscCall(VecGetDM(A, &dmAux));
    4560:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
    4561:     PetscCall(DMGetDS(dmAux, &probAux));
    4562:   }
    4563:   /* Get flags */
    4564:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    4565:   PetscCall(DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE));
    4566:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
    4567:     PetscObject  disc;
    4568:     PetscClassId id;
    4569:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
    4570:     PetscCall(PetscObjectGetClassId(disc, &id));
    4571:     if (id == PETSCFE_CLASSID) {
    4572:       isFE[fieldI] = PETSC_TRUE;
    4573:     } else if (id == PETSCFV_CLASSID) {
    4574:       hasFV        = PETSC_TRUE;
    4575:       isFE[fieldI] = PETSC_FALSE;
    4576:     }
    4577:   }
    4578:   PetscCall(PetscDSHasJacobian(prob, &hasJac));
    4579:   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
    4580:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
    4581:   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
    4582:   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
    4583:   if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
    4584:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    4585:   if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    4586:   /* Compute batch sizes */
    4587:   if (isFE[0]) {
    4588:     PetscFE         fe;
    4589:     PetscQuadrature q;
    4590:     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;

    4592:     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
    4593:     PetscCall(PetscFEGetQuadrature(fe, &q));
    4594:     PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
    4595:     PetscCall(PetscFEGetDimension(fe, &Nb));
    4596:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    4597:     blockSize = Nb * numQuadPoints;
    4598:     batchSize = numBlocks * blockSize;
    4599:     chunkSize = numBatches * batchSize;
    4600:     numChunks = numCells / chunkSize + numCells % chunkSize;
    4601:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    4602:   } else {
    4603:     chunkSize = numCells;
    4604:     numChunks = 1;
    4605:   }
    4606:   /* Get work space */
    4607:   wsz = (((X ? 1 : 0) + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize;
    4608:   PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
    4609:   PetscCall(PetscArrayzero(work, wsz));
    4610:   off      = 0;
    4611:   u        = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
    4612:   u_t      = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
    4613:   a        = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
    4614:   elemMat  = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
    4615:   elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
    4616:   elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
    4617:   PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
    4618:   /* Setup geometry */
    4619:   PetscCall(DMGetCoordinateField(dm, &coordField));
    4620:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    4621:   if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
    4622:   if (!qGeom) {
    4623:     PetscFE fe;

    4625:     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
    4626:     PetscCall(PetscFEGetQuadrature(fe, &qGeom));
    4627:     PetscCall(PetscObjectReference((PetscObject)qGeom));
    4628:   }
    4629:   PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
    4630:   /* Compute volume integrals */
    4631:   if (assembleJac) PetscCall(MatZeroEntries(J));
    4632:   PetscCall(MatZeroEntries(JP));
    4633:   key.label = NULL;
    4634:   key.value = 0;
    4635:   key.part  = 0;
    4636:   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
    4637:     const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
    4638:     PetscInt       c;

    4640:     /* Extract values */
    4641:     for (c = 0; c < Ncell; ++c) {
    4642:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
    4643:       PetscScalar   *x = NULL, *x_t = NULL;
    4644:       PetscInt       i;

    4646:       if (X) {
    4647:         PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
    4648:         for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
    4649:         PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
    4650:       }
    4651:       if (X_t) {
    4652:         PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
    4653:         for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
    4654:         PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
    4655:       }
    4656:       if (dmAux) {
    4657:         PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
    4658:         for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
    4659:         PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
    4660:       }
    4661:     }
    4662:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
    4663:       PetscFE fe;
    4664:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
    4665:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    4666:         key.field = fieldI * Nf + fieldJ;
    4667:         if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
    4668:         if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
    4669:         if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
    4670:       }
    4671:       /* For finite volume, add the identity */
    4672:       if (!isFE[fieldI]) {
    4673:         PetscFV  fv;
    4674:         PetscInt eOffset = 0, Nc, fc, foff;

    4676:         PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
    4677:         PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
    4678:         PetscCall(PetscFVGetNumComponents(fv, &Nc));
    4679:         for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
    4680:           for (fc = 0; fc < Nc; ++fc) {
    4681:             const PetscInt i = foff + fc;
    4682:             if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
    4683:             if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
    4684:           }
    4685:         }
    4686:       }
    4687:     }
    4688:     /*   Add contribution from X_t */
    4689:     if (hasDyn) {
    4690:       for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
    4691:     }
    4692:     /* Insert values into matrix */
    4693:     for (c = 0; c < Ncell; ++c) {
    4694:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
    4695:       if (mesh->printFEM > 1) {
    4696:         if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]));
    4697:         if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]));
    4698:       }
    4699:       if (assembleJac) PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
    4700:       PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
    4701:     }
    4702:   }
    4703:   /* Cleanup */
    4704:   PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
    4705:   PetscCall(PetscQuadratureDestroy(&qGeom));
    4706:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
    4707:   PetscCall(DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE));
    4708:   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));
    4709:   /* Compute boundary integrals */
    4710:   /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
    4711:   /* Assemble matrix */
    4712:   if (assembleJac) {
    4713:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
    4714:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
    4715:   }
    4716:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
    4717:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
    4718:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    4719:   PetscFunctionReturn(PETSC_SUCCESS);
    4720: }

    4722: /* FEM Assembly Function */

    4724: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
    4725: {
    4726:   PetscBool isPlex;

    4728:   PetscFunctionBegin;
    4729:   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
    4730:   if (isPlex) {
    4731:     *plex = dm;
    4732:     PetscCall(PetscObjectReference((PetscObject)dm));
    4733:   } else {
    4734:     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
    4735:     if (!*plex) {
    4736:       PetscCall(DMConvert(dm, DMPLEX, plex));
    4737:       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
    4738:     } else {
    4739:       PetscCall(PetscObjectReference((PetscObject)*plex));
    4740:     }
    4741:     if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
    4742:   }
    4743:   PetscFunctionReturn(PETSC_SUCCESS);
    4744: }

    4746: /*@
    4747:   DMPlexGetGeometryFVM - Return precomputed geometric data

    4749:   Collective

    4751:   Input Parameter:
    4752: . dm - The `DM`

    4754:   Output Parameters:
    4755: + facegeom  - The values precomputed from face geometry
    4756: . cellgeom  - The values precomputed from cell geometry
    4757: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell

    4759:   Level: developer

    4761: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
    4762: @*/
    4763: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
    4764: {
    4765:   DM plex;

    4767:   PetscFunctionBegin;
    4769:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
    4770:   PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
    4771:   if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
    4772:   PetscCall(DMDestroy(&plex));
    4773:   PetscFunctionReturn(PETSC_SUCCESS);
    4774: }

    4776: /*@
    4777:   DMPlexGetGradientDM - Return gradient data layout

    4779:   Collective

    4781:   Input Parameters:
    4782: + dm - The `DM`
    4783: - fv - The `PetscFV`

    4785:   Output Parameter:
    4786: . dmGrad - The layout for gradient values

    4788:   Level: developer

    4790: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
    4791: @*/
    4792: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
    4793: {
    4794:   DM        plex;
    4795:   PetscBool computeGradients;

    4797:   PetscFunctionBegin;
    4800:   PetscAssertPointer(dmGrad, 3);
    4801:   PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
    4802:   if (!computeGradients) {
    4803:     *dmGrad = NULL;
    4804:     PetscFunctionReturn(PETSC_SUCCESS);
    4805:   }
    4806:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
    4807:   PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
    4808:   PetscCall(DMDestroy(&plex));
    4809:   PetscFunctionReturn(PETSC_SUCCESS);
    4810: }

    4812: static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
    4813: {
    4814:   DM_Plex        *mesh = (DM_Plex *)dm->data;
    4815:   DM              plex = NULL, plexA = NULL;
    4816:   const char     *name = "BdResidual";
    4817:   DMEnclosureType encAux;
    4818:   PetscDS         prob, probAux       = NULL;
    4819:   PetscSection    section, sectionAux = NULL;
    4820:   Vec             locA = NULL;
    4821:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
    4822:   PetscInt        totDim, totDimAux = 0;

    4824:   PetscFunctionBegin;
    4825:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    4826:   PetscCall(DMGetLocalSection(dm, &section));
    4827:   PetscCall(DMGetDS(dm, &prob));
    4828:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    4829:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
    4830:   if (locA) {
    4831:     DM dmAux;

    4833:     PetscCall(VecGetDM(locA, &dmAux));
    4834:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    4835:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
    4836:     PetscCall(DMGetDS(plexA, &probAux));
    4837:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    4838:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
    4839:   }
    4840:   {
    4841:     PetscFEGeom    *fgeom;
    4842:     PetscInt        maxDegree;
    4843:     PetscQuadrature qGeom = NULL;
    4844:     IS              pointIS;
    4845:     const PetscInt *points;
    4846:     PetscInt        numFaces, face, Nq;

    4848:     PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
    4849:     if (!pointIS) goto end; /* No points with that id on this process */
    4850:     {
    4851:       IS isectIS;

    4853:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
    4854:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
    4855:       PetscCall(ISDestroy(&pointIS));
    4856:       pointIS = isectIS;
    4857:     }
    4858:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
    4859:     PetscCall(ISGetIndices(pointIS, &points));
    4860:     PetscCall(PetscMalloc4(numFaces * totDim, &u, (locX_t ? (size_t)numFaces * totDim : 0), &u_t, numFaces * totDim, &elemVec, (locA ? (size_t)numFaces * totDimAux : 0), &a));
    4861:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
    4862:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
    4863:     if (!qGeom) {
    4864:       PetscFE fe;

    4866:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
    4867:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
    4868:       PetscCall(PetscObjectReference((PetscObject)qGeom));
    4869:     }
    4870:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
    4871:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
    4872:     for (face = 0; face < numFaces; ++face) {
    4873:       const PetscInt point = points[face], *support;
    4874:       PetscScalar   *x     = NULL;
    4875:       PetscInt       i;

    4877:       PetscCall(DMPlexGetSupport(dm, point, &support));
    4878:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
    4879:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
    4880:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
    4881:       if (locX_t) {
    4882:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
    4883:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
    4884:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
    4885:       }
    4886:       if (locA) {
    4887:         PetscInt subp;

    4889:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
    4890:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
    4891:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
    4892:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
    4893:       }
    4894:     }
    4895:     PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
    4896:     {
    4897:       PetscFE      fe;
    4898:       PetscInt     Nb;
    4899:       PetscFEGeom *chunkGeom = NULL;
    4900:       /* Conforming batches */
    4901:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    4902:       /* Remainder */
    4903:       PetscInt Nr, offset;

    4905:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
    4906:       PetscCall(PetscFEGetDimension(fe, &Nb));
    4907:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    4908:       /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
    4909:       blockSize = Nb;
    4910:       batchSize = numBlocks * blockSize;
    4911:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    4912:       numChunks = numFaces / (numBatches * batchSize);
    4913:       Ne        = numChunks * numBatches * batchSize;
    4914:       Nr        = numFaces % (numBatches * batchSize);
    4915:       offset    = numFaces - Nr;
    4916:       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
    4917:       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
    4918:       PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
    4919:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
    4920:       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
    4921:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
    4922:     }
    4923:     for (face = 0; face < numFaces; ++face) {
    4924:       const PetscInt point = points[face], *support;

    4926:       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, name, totDim, &elemVec[face * totDim]));
    4927:       PetscCall(DMPlexGetSupport(plex, point, &support));
    4928:       PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES));
    4929:     }
    4930:     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
    4931:     PetscCall(PetscQuadratureDestroy(&qGeom));
    4932:     PetscCall(ISRestoreIndices(pointIS, &points));
    4933:     PetscCall(ISDestroy(&pointIS));
    4934:     PetscCall(PetscFree4(u, u_t, elemVec, a));
    4935:   }
    4936: end:
    4937:   if (mesh->printFEM) {
    4938:     PetscSection s;
    4939:     Vec          locFbc;
    4940:     PetscInt     pStart, pEnd, maxDof;
    4941:     PetscScalar *zeroes;

    4943:     PetscCall(DMGetLocalSection(dm, &s));
    4944:     PetscCall(VecDuplicate(locF, &locFbc));
    4945:     PetscCall(VecCopy(locF, locFbc));
    4946:     PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
    4947:     PetscCall(PetscSectionGetMaxDof(s, &maxDof));
    4948:     PetscCall(PetscCalloc1(maxDof, &zeroes));
    4949:     for (PetscInt p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, s, p, zeroes, INSERT_BC_VALUES));
    4950:     PetscCall(PetscFree(zeroes));
    4951:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
    4952:     PetscCall(VecDestroy(&locFbc));
    4953:   }
    4954:   PetscCall(DMDestroy(&plex));
    4955:   PetscCall(DMDestroy(&plexA));
    4956:   PetscFunctionReturn(PETSC_SUCCESS);
    4957: }

    4959: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
    4960: {
    4961:   DMField  coordField;
    4962:   DMLabel  depthLabel;
    4963:   IS       facetIS;
    4964:   PetscInt dim;

    4966:   PetscFunctionBegin;
    4967:   PetscCall(DMGetDimension(dm, &dim));
    4968:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    4969:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
    4970:   PetscCall(DMGetCoordinateField(dm, &coordField));
    4971:   PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
    4972:   PetscCall(ISDestroy(&facetIS));
    4973:   PetscFunctionReturn(PETSC_SUCCESS);
    4974: }

    4976: static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
    4977: {
    4978:   PetscDS  prob;
    4979:   PetscInt numBd, bd;
    4980:   DMField  coordField = NULL;
    4981:   IS       facetIS    = NULL;
    4982:   DMLabel  depthLabel;
    4983:   PetscInt dim;

    4985:   PetscFunctionBegin;
    4986:   PetscCall(DMGetDS(dm, &prob));
    4987:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    4988:   PetscCall(DMGetDimension(dm, &dim));
    4989:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
    4990:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
    4991:   for (bd = 0; bd < numBd; ++bd) {
    4992:     PetscWeakForm           wf;
    4993:     DMBoundaryConditionType type;
    4994:     DMLabel                 label;
    4995:     const PetscInt         *values;
    4996:     PetscInt                field, numValues, v;
    4997:     PetscObject             obj;
    4998:     PetscClassId            id;
    4999:     PetscFormKey            key;

    5001:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
    5002:     if (type & DM_BC_ESSENTIAL) continue;
    5003:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
    5004:     PetscCall(PetscObjectGetClassId(obj, &id));
    5005:     if (id != PETSCFE_CLASSID) continue;
    5006:     if (!facetIS) {
    5007:       DMLabel  depthLabel;
    5008:       PetscInt dim;

    5010:       PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    5011:       PetscCall(DMGetDimension(dm, &dim));
    5012:       PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
    5013:     }
    5014:     PetscCall(DMGetCoordinateField(dm, &coordField));
    5015:     for (v = 0; v < numValues; ++v) {
    5016:       key.label = label;
    5017:       key.value = values[v];
    5018:       key.field = field;
    5019:       key.part  = 0;
    5020:       PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
    5021:     }
    5022:   }
    5023:   PetscCall(ISDestroy(&facetIS));
    5024:   PetscFunctionReturn(PETSC_SUCCESS);
    5025: }

    5027: PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
    5028: {
    5029:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
    5030:   const char     *name       = "Residual";
    5031:   DM              dmAux      = NULL;
    5032:   DM              dmGrad     = NULL;
    5033:   DMLabel         ghostLabel = NULL;
    5034:   PetscDS         ds         = NULL;
    5035:   PetscDS         dsAux      = NULL;
    5036:   PetscSection    section    = NULL;
    5037:   PetscBool       useFEM     = PETSC_FALSE;
    5038:   PetscBool       useFVM     = PETSC_FALSE;
    5039:   PetscBool       isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
    5040:   PetscFV         fvm        = NULL;
    5041:   DMField         coordField = NULL;
    5042:   Vec             locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
    5043:   PetscScalar    *u = NULL, *u_t, *a, *uL, *uR;
    5044:   IS              chunkIS;
    5045:   const PetscInt *cells;
    5046:   PetscInt        cStart, cEnd, numCells;
    5047:   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
    5048:   PetscInt        maxDegree  = PETSC_INT_MAX;
    5049:   PetscQuadrature affineQuad = NULL, *quads = NULL;
    5050:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

    5052:   PetscFunctionBegin;
    5053:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
    5054:   if (!cellIS) goto end;
    5055:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    5056:   if (cStart >= cEnd) goto end;
    5057:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
    5058:   /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
    5059:   /* FEM+FVM */
    5060:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
    5061:   /* 1: Get sizes from dm and dmAux */
    5062:   PetscCall(DMGetLocalSection(dm, &section));
    5063:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
    5064:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL));
    5065:   PetscCall(PetscDSGetNumFields(ds, &Nf));
    5066:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
    5067:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
    5068:   if (locA) {
    5069:     PetscInt subcell;
    5070:     PetscCall(VecGetDM(locA, &dmAux));
    5071:     PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
    5072:     PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL));
    5073:     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
    5074:   }
    5075:   /* 2: Get geometric data */
    5076:   for (f = 0; f < Nf; ++f) {
    5077:     PetscObject  obj;
    5078:     PetscClassId id;
    5079:     PetscBool    fimp;

    5081:     PetscCall(PetscDSGetImplicit(ds, f, &fimp));
    5082:     if (isImplicit != fimp) continue;
    5083:     PetscCall(PetscDSGetDiscretization(ds, f, &obj));
    5084:     PetscCall(PetscObjectGetClassId(obj, &id));
    5085:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
    5086:     if (id == PETSCFV_CLASSID) {
    5087:       useFVM = PETSC_TRUE;
    5088:       fvm    = (PetscFV)obj;
    5089:     }
    5090:   }
    5091:   if (useFEM) {
    5092:     PetscCall(DMGetCoordinateField(dm, &coordField));
    5093:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    5094:     if (maxDegree <= 1) {
    5095:       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
    5096:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
    5097:     } else {
    5098:       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
    5099:       for (f = 0; f < Nf; ++f) {
    5100:         PetscObject  obj;
    5101:         PetscClassId id;
    5102:         PetscBool    fimp;

    5104:         PetscCall(PetscDSGetImplicit(ds, f, &fimp));
    5105:         if (isImplicit != fimp) continue;
    5106:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
    5107:         PetscCall(PetscObjectGetClassId(obj, &id));
    5108:         if (id == PETSCFE_CLASSID) {
    5109:           PetscFE fe = (PetscFE)obj;

    5111:           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
    5112:           PetscCall(PetscObjectReference((PetscObject)quads[f]));
    5113:           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
    5114:         }
    5115:       }
    5116:     }
    5117:   }
    5118:   // Handle non-essential (e.g. outflow) boundary values
    5119:   if (useFVM) {
    5120:     PetscCall(DMPlexInsertBoundaryValuesFVM(dm, fvm, locX, time, &locGrad));
    5121:     PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
    5122:     PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
    5123:   }
    5124:   /* Loop over chunks */
    5125:   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
    5126:   numCells      = cEnd - cStart;
    5127:   numChunks     = 1;
    5128:   cellChunkSize = numCells / numChunks;
    5129:   faceChunkSize = (fEnd - fStart) / numChunks;
    5130:   numChunks     = PetscMin(1, numCells);
    5131:   for (chunk = 0; chunk < numChunks; ++chunk) {
    5132:     PetscScalar     *elemVec, *fluxL, *fluxR;
    5133:     PetscReal       *vol;
    5134:     PetscFVFaceGeom *fgeom;
    5135:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
    5136:     PetscInt         fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;

    5138:     /* Extract field coefficients */
    5139:     if (useFEM) {
    5140:       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
    5141:       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
    5142:       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
    5143:       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
    5144:     }
    5145:     if (useFVM) {
    5146:       PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
    5147:       PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
    5148:       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
    5149:       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
    5150:       PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
    5151:       PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
    5152:     }
    5153:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
    5154:     /* Loop over fields */
    5155:     for (f = 0; f < Nf; ++f) {
    5156:       PetscObject  obj;
    5157:       PetscClassId id;
    5158:       PetscBool    fimp;
    5159:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

    5161:       key.field = f;
    5162:       PetscCall(PetscDSGetImplicit(ds, f, &fimp));
    5163:       if (isImplicit != fimp) continue;
    5164:       PetscCall(PetscDSGetDiscretization(ds, f, &obj));
    5165:       PetscCall(PetscObjectGetClassId(obj, &id));
    5166:       if (id == PETSCFE_CLASSID) {
    5167:         PetscFE         fe        = (PetscFE)obj;
    5168:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
    5169:         PetscFEGeom    *chunkGeom = NULL;
    5170:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
    5171:         PetscInt        Nq, Nb;

    5173:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    5174:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
    5175:         PetscCall(PetscFEGetDimension(fe, &Nb));
    5176:         blockSize = Nb;
    5177:         batchSize = numBlocks * blockSize;
    5178:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    5179:         numChunks = numCells / (numBatches * batchSize);
    5180:         Ne        = numChunks * numBatches * batchSize;
    5181:         Nr        = numCells % (numBatches * batchSize);
    5182:         offset    = numCells - Nr;
    5183:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
    5184:         /*   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) */
    5185:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
    5186:         PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
    5187:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
    5188:         PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim]));
    5189:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
    5190:       } else if (id == PETSCFV_CLASSID) {
    5191:         PetscFV fv = (PetscFV)obj;

    5193:         Ne = numFaces;
    5194:         /* Riemann solve over faces (need fields at face centroids) */
    5195:         /*   We need to evaluate FE fields at those coordinates */
    5196:         PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
    5197:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
    5198:     }
    5199:     /* Loop over domain */
    5200:     if (useFEM) {
    5201:       /* Add elemVec to locX */
    5202:       for (c = cS; c < cE; ++c) {
    5203:         const PetscInt cell = cells ? cells[c] : c;
    5204:         const PetscInt cind = c - cStart;

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

    5210:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
    5211:           if (ghostVal > 0) continue;
    5212:         }
    5213:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
    5214:       }
    5215:     }
    5216:     if (useFVM) {
    5217:       PetscScalar *fa;
    5218:       PetscInt     iface;

    5220:       PetscCall(VecGetArray(locF, &fa));
    5221:       for (f = 0; f < Nf; ++f) {
    5222:         PetscFV      fv;
    5223:         PetscObject  obj;
    5224:         PetscClassId id;
    5225:         PetscInt     cdim, foff, pdim;

    5227:         PetscCall(DMGetCoordinateDim(dm, &cdim));
    5228:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
    5229:         PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
    5230:         PetscCall(PetscObjectGetClassId(obj, &id));
    5231:         if (id != PETSCFV_CLASSID) continue;
    5232:         fv = (PetscFV)obj;
    5233:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
    5234:         /* Accumulate fluxes to cells */
    5235:         for (face = fS, iface = 0; face < fE; ++face) {
    5236:           const PetscInt *scells;
    5237:           PetscScalar    *fL = NULL, *fR = NULL;
    5238:           PetscInt        ghost, d, nsupp, nchild;

    5240:           PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
    5241:           PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
    5242:           PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
    5243:           if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
    5244:           PetscCall(DMPlexGetSupport(dm, face, &scells));
    5245:           PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
    5246:           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
    5247:           PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
    5248:           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
    5249:           if (mesh->printFVM > 1) {
    5250:             PetscCall(DMPrintCellVectorReal(face, "Residual: normal", cdim, fgeom[iface].normal));
    5251:             PetscCall(DMPrintCellVector(face, "Residual: left state", pdim, &uL[iface * totDim + foff]));
    5252:             PetscCall(DMPrintCellVector(face, "Residual: right state", pdim, &uR[iface * totDim + foff]));
    5253:             PetscCall(DMPrintCellVector(face, "Residual: left flux", pdim, &fluxL[iface * totDim + foff]));
    5254:             PetscCall(DMPrintCellVector(face, "Residual: right flux", pdim, &fluxR[iface * totDim + foff]));
    5255:           }
    5256:           for (d = 0; d < pdim; ++d) {
    5257:             if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
    5258:             if (fR) fR[d] += fluxR[iface * totDim + foff + d];
    5259:           }
    5260:           ++iface;
    5261:         }
    5262:       }
    5263:       PetscCall(VecRestoreArray(locF, &fa));
    5264:     }
    5265:     /* Handle time derivative */
    5266:     if (locX_t) {
    5267:       PetscScalar *x_t, *fa;

    5269:       PetscCall(VecGetArray(locF, &fa));
    5270:       PetscCall(VecGetArray(locX_t, &x_t));
    5271:       for (f = 0; f < Nf; ++f) {
    5272:         PetscFV      fv;
    5273:         PetscObject  obj;
    5274:         PetscClassId id;
    5275:         PetscInt     pdim, d;

    5277:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
    5278:         PetscCall(PetscObjectGetClassId(obj, &id));
    5279:         if (id != PETSCFV_CLASSID) continue;
    5280:         fv = (PetscFV)obj;
    5281:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
    5282:         for (c = cS; c < cE; ++c) {
    5283:           const PetscInt cell = cells ? cells[c] : c;
    5284:           PetscScalar   *u_t, *r;

    5286:           if (ghostLabel) {
    5287:             PetscInt ghostVal;

    5289:             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
    5290:             if (ghostVal > 0) continue;
    5291:           }
    5292:           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
    5293:           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
    5294:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
    5295:         }
    5296:       }
    5297:       PetscCall(VecRestoreArray(locX_t, &x_t));
    5298:       PetscCall(VecRestoreArray(locF, &fa));
    5299:     }
    5300:     if (useFEM) {
    5301:       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
    5302:       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
    5303:     }
    5304:     if (useFVM) {
    5305:       PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
    5306:       PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
    5307:       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
    5308:       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
    5309:       if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
    5310:     }
    5311:   }
    5312:   if (useFEM) PetscCall(ISDestroy(&chunkIS));
    5313:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));

    5315:   if (useFEM) {
    5316:     PetscCall(DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, user));

    5318:     if (maxDegree <= 1) {
    5319:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
    5320:       PetscCall(PetscQuadratureDestroy(&affineQuad));
    5321:     } else {
    5322:       for (f = 0; f < Nf; ++f) {
    5323:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
    5324:         PetscCall(PetscQuadratureDestroy(&quads[f]));
    5325:       }
    5326:       PetscCall(PetscFree2(quads, geoms));
    5327:     }
    5328:   }

    5330:   /* FEM */
    5331:   /* 1: Get sizes from dm and dmAux */
    5332:   /* 2: Get geometric data */
    5333:   /* 3: Handle boundary values */
    5334:   /* 4: Loop over domain */
    5335:   /*   Extract coefficients */
    5336:   /* Loop over fields */
    5337:   /*   Set tiling for FE*/
    5338:   /*   Integrate FE residual to get elemVec */
    5339:   /*     Loop over subdomain */
    5340:   /*       Loop over quad points */
    5341:   /*         Transform coords to real space */
    5342:   /*         Evaluate field and aux fields at point */
    5343:   /*         Evaluate residual at point */
    5344:   /*         Transform residual to real space */
    5345:   /*       Add residual to elemVec */
    5346:   /* Loop over domain */
    5347:   /*   Add elemVec to locX */

    5349:   /* FVM */
    5350:   /* Get geometric data */
    5351:   /* If using gradients */
    5352:   /*   Compute gradient data */
    5353:   /*   Loop over domain faces */
    5354:   /*     Count computational faces */
    5355:   /*     Reconstruct cell gradient */
    5356:   /*   Loop over domain cells */
    5357:   /*     Limit cell gradients */
    5358:   /* Handle boundary values */
    5359:   /* Loop over domain faces */
    5360:   /*   Read out field, centroid, normal, volume for each side of face */
    5361:   /* Riemann solve over faces */
    5362:   /* Loop over domain faces */
    5363:   /*   Accumulate fluxes to cells */
    5364:   /* TODO Change printFEM to printDisc here */
    5365:   if (mesh->printFEM) {
    5366:     Vec          locFbc;
    5367:     PetscInt     pStart, pEnd, p, maxDof;
    5368:     PetscScalar *zeroes;

    5370:     PetscCall(VecDuplicate(locF, &locFbc));
    5371:     PetscCall(VecCopy(locF, locFbc));
    5372:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
    5373:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
    5374:     PetscCall(PetscCalloc1(maxDof, &zeroes));
    5375:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
    5376:     PetscCall(PetscFree(zeroes));
    5377:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
    5378:     PetscCall(VecDestroy(&locFbc));
    5379:   }
    5380: end:
    5381:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
    5382:   PetscFunctionReturn(PETSC_SUCCESS);
    5383: }

    5385: /*
    5386:   1) Allow multiple kernels for BdResidual for hybrid DS

    5388:   DONE 2) Get out dsAux for either side at the same time as cohesive cell dsAux

    5390:   DONE 3) Change DMGetCellFields() to get different aux data a[] for each side
    5391:      - I think I just need to replace a[] with the closure from each face

    5393:   4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
    5394: */
    5395: PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
    5396: {
    5397:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
    5398:   const char     *name       = "Hybrid Residual";
    5399:   DM              dmAux[3]   = {NULL, NULL, NULL};
    5400:   DMLabel         ghostLabel = NULL;
    5401:   PetscDS         ds         = NULL;
    5402:   PetscDS         dsIn       = NULL;
    5403:   PetscDS         dsAux[3]   = {NULL, NULL, NULL};
    5404:   Vec             locA[3]    = {NULL, NULL, NULL};
    5405:   DM              dmScale[3] = {NULL, NULL, NULL};
    5406:   PetscDS         dsScale[3] = {NULL, NULL, NULL};
    5407:   Vec             locS[3]    = {NULL, NULL, NULL};
    5408:   PetscSection    section    = NULL;
    5409:   DMField         coordField = NULL;
    5410:   PetscScalar    *a[3]       = {NULL, NULL, NULL};
    5411:   PetscScalar    *s[3]       = {NULL, NULL, NULL};
    5412:   PetscScalar    *u          = NULL, *u_t;
    5413:   PetscScalar    *elemVecNeg, *elemVecPos, *elemVecCoh;
    5414:   IS              chunkIS;
    5415:   const PetscInt *cells;
    5416:   PetscInt       *faces;
    5417:   PetscInt        cStart, cEnd, numCells;
    5418:   PetscInt        Nf, f, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
    5419:   PetscInt        maxDegree  = PETSC_INT_MAX;
    5420:   PetscQuadrature affineQuad = NULL, *quads = NULL;
    5421:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

    5423:   PetscFunctionBegin;
    5424:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
    5425:   if (!cellIS) goto end;
    5426:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    5427:   PetscCall(ISGetLocalSize(cellIS, &numCells));
    5428:   if (cStart >= cEnd) goto end;
    5429:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
    5430:     const char *name;
    5431:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
    5432:     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);
    5433:   }
    5434:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
    5435:   /* FEM */
    5436:   /* 1: Get sizes from dm and dmAux */
    5437:   PetscCall(DMGetSection(dm, &section));
    5438:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
    5439:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
    5440:   PetscCall(PetscDSGetNumFields(ds, &Nf));
    5441:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
    5442:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
    5443:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
    5444:   if (locA[2]) {
    5445:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

    5447:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
    5448:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
    5449:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
    5450:     {
    5451:       const PetscInt *cone;
    5452:       PetscInt        c;

    5454:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
    5455:       for (c = 0; c < 2; ++c) {
    5456:         const PetscInt *support;
    5457:         PetscInt        ssize, s;

    5459:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
    5460:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
    5461:         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);
    5462:         if (support[0] == cellStart) s = 1;
    5463:         else if (support[1] == cellStart) s = 0;
    5464:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
    5465:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
    5466:         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);
    5467:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
    5468:         else dmAux[c] = dmAux[2];
    5469:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
    5470:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
    5471:       }
    5472:     }
    5473:   }
    5474:   /* Handle mass matrix scaling
    5475:        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
    5476:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
    5477:   if (locS[2]) {
    5478:     const PetscInt cellStart = cells ? cells[cStart] : cStart;
    5479:     PetscInt       Nb, Nbs;

    5481:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
    5482:     PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL));
    5483:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
    5484:     // BRAD: This is not set correctly
    5485:     key[2].field = 2;
    5486:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
    5487:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
    5488:     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);
    5489:     {
    5490:       const PetscInt *cone;
    5491:       PetscInt        c;

    5493:       locS[1] = locS[0] = locS[2];
    5494:       dmScale[1] = dmScale[0] = dmScale[2];
    5495:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
    5496:       for (c = 0; c < 2; ++c) {
    5497:         const PetscInt *support;
    5498:         PetscInt        ssize, s;

    5500:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
    5501:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
    5502:         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);
    5503:         if (support[0] == cellStart) s = 1;
    5504:         else if (support[1] == cellStart) s = 0;
    5505:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
    5506:         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
    5507:         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
    5508:       }
    5509:     }
    5510:   }
    5511:   /* 2: Setup geometric data */
    5512:   PetscCall(DMGetCoordinateField(dm, &coordField));
    5513:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    5514:   if (maxDegree > 1) {
    5515:     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
    5516:     for (f = 0; f < Nf; ++f) {
    5517:       PetscFE fe;

    5519:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
    5520:       if (fe) {
    5521:         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
    5522:         PetscCall(PetscObjectReference((PetscObject)quads[f]));
    5523:       }
    5524:     }
    5525:   }
    5526:   /* Loop over chunks */
    5527:   cellChunkSize = numCells;
    5528:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
    5529:   PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
    5530:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
    5531:   /* Extract field coefficients */
    5532:   /* NOTE This needs the end cap faces to have identical orientations */
    5533:   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
    5534:   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
    5535:   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
    5536:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg));
    5537:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos));
    5538:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh));
    5539:   for (chunk = 0; chunk < numChunks; ++chunk) {
    5540:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

    5542:     PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
    5543:     PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
    5544:     PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
    5545:     /* Get faces */
    5546:     for (c = cS; c < cE; ++c) {
    5547:       const PetscInt  cell = cells ? cells[c] : c;
    5548:       const PetscInt *cone;
    5549:       PetscCall(DMPlexGetCone(dm, cell, &cone));
    5550:       faces[(c - cS) * 2 + 0] = cone[0];
    5551:       faces[(c - cS) * 2 + 1] = cone[1];
    5552:     }
    5553:     PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
    5554:     /* Get geometric data */
    5555:     if (maxDegree <= 1) {
    5556:       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
    5557:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
    5558:     } else {
    5559:       for (f = 0; f < Nf; ++f) {
    5560:         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
    5561:       }
    5562:     }
    5563:     /* Loop over fields */
    5564:     for (f = 0; f < Nf; ++f) {
    5565:       PetscFE         fe;
    5566:       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
    5567:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
    5568:       PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
    5569:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
    5570:       PetscBool       isCohesiveField;

    5572:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
    5573:       if (!fe) continue;
    5574:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    5575:       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
    5576:       PetscCall(PetscFEGetDimension(fe, &Nb));
    5577:       blockSize = Nb;
    5578:       batchSize = numBlocks * blockSize;
    5579:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    5580:       numChunks = numCells / (numBatches * batchSize);
    5581:       Ne        = numChunks * numBatches * batchSize;
    5582:       Nr        = numCells % (numBatches * batchSize);
    5583:       offset    = numCells - Nr;
    5584:       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
    5585:       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
    5586:       PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
    5587:       chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE;
    5588:       key[0].field                                = f;
    5589:       key[1].field                                = f;
    5590:       key[2].field                                = f;
    5591:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVecNeg));
    5592:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], PetscSafePointerPlusOffset(a[0], offset * totDimAux[0]), t, &elemVecNeg[offset * totDim]));
    5593:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVecPos));
    5594:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], PetscSafePointerPlusOffset(a[1], offset * totDimAux[1]), t, &elemVecPos[offset * totDim]));
    5595:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVecCoh));
    5596:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], PetscSafePointerPlusOffset(a[2], offset * totDimAux[2]), t, &elemVecCoh[offset * totDim]));
    5597:       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
    5598:       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
    5599:     }
    5600:     /* Add elemVec to locX */
    5601:     for (c = cS; c < cE; ++c) {
    5602:       const PetscInt cell = cells ? cells[c] : c;
    5603:       const PetscInt cind = c - cStart;
    5604:       PetscInt       i;

    5606:       /* Scale element values */
    5607:       if (locS[0]) {
    5608:         PetscInt  Nb, off = cind * totDim, soff = cind * totDimScale[0];
    5609:         PetscBool cohesive;

    5611:         for (f = 0; f < Nf; ++f) {
    5612:           PetscCall(PetscDSGetFieldSize(ds, f, &Nb));
    5613:           PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
    5614:           if (f == key[2].field) {
    5615:             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
    5616:             // No cohesive scaling field is currently input
    5617:             for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i];
    5618:             off += Nb;
    5619:           } else {
    5620:             const PetscInt N = cohesive ? Nb : Nb * 2;

    5622:             for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
    5623:             off += N;
    5624:           }
    5625:         }
    5626:       } else {
    5627:         for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
    5628:       }
    5629:       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
    5630:       if (ghostLabel) {
    5631:         PetscInt ghostVal;

    5633:         PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
    5634:         if (ghostVal > 0) continue;
    5635:       }
    5636:       PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES));
    5637:     }
    5638:   }
    5639:   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
    5640:   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
    5641:   PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
    5642:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg));
    5643:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos));
    5644:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh));
    5645:   PetscCall(PetscFree(faces));
    5646:   PetscCall(ISDestroy(&chunkIS));
    5647:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    5648:   if (maxDegree <= 1) {
    5649:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
    5650:     PetscCall(PetscQuadratureDestroy(&affineQuad));
    5651:   } else {
    5652:     for (f = 0; f < Nf; ++f) {
    5653:       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
    5654:       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
    5655:     }
    5656:     PetscCall(PetscFree2(quads, geoms));
    5657:   }
    5658:   if (mesh->printFEM) {
    5659:     Vec          locFbc;
    5660:     PetscInt     pStart, pEnd, p, maxDof;
    5661:     PetscScalar *zeroes;

    5663:     PetscCall(VecDuplicate(locF, &locFbc));
    5664:     PetscCall(VecCopy(locF, locFbc));
    5665:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
    5666:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
    5667:     PetscCall(PetscCalloc1(maxDof, &zeroes));
    5668:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
    5669:     PetscCall(PetscFree(zeroes));
    5670:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
    5671:     PetscCall(VecDestroy(&locFbc));
    5672:   }
    5673: end:
    5674:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
    5675:   PetscFunctionReturn(PETSC_SUCCESS);
    5676: }

    5678: static PetscErrorCode DMPlexComputeBdJacobian_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt fieldI, Vec locX, Vec locX_t, PetscReal X_tShift, Mat Jac, Mat JacP, DMField coordField, IS facetIS)
    5679: {
    5680:   DM_Plex        *mesh = (DM_Plex *)dm->data;
    5681:   DM              plex = NULL, plexA = NULL, tdm;
    5682:   DMEnclosureType encAux;
    5683:   PetscDS         ds, dsAux           = NULL;
    5684:   PetscSection    section, sectionAux = NULL;
    5685:   PetscSection    globalSection;
    5686:   Vec             locA = NULL, tv;
    5687:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL;
    5688:   PetscInt        v;
    5689:   PetscInt        Nf, totDim, totDimAux = 0;
    5690:   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, transform;

    5692:   PetscFunctionBegin;
    5693:   PetscCall(DMHasBasisTransform(dm, &transform));
    5694:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
    5695:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
    5696:   PetscCall(DMGetLocalSection(dm, &section));
    5697:   PetscCall(DMGetDS(dm, &ds));
    5698:   PetscCall(PetscDSGetNumFields(ds, &Nf));
    5699:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
    5700:   PetscCall(PetscWeakFormHasBdJacobian(wf, &hasJac));
    5701:   PetscCall(PetscWeakFormHasBdJacobianPreconditioner(wf, &hasPrec));
    5702:   if (!hasJac && !hasPrec) PetscFunctionReturn(PETSC_SUCCESS);
    5703:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    5704:   PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
    5705:   if (locA) {
    5706:     DM dmAux;

    5708:     PetscCall(VecGetDM(locA, &dmAux));
    5709:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    5710:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
    5711:     PetscCall(DMGetDS(plexA, &dsAux));
    5712:     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
    5713:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
    5714:   }

    5716:   PetscCall(DMGetGlobalSection(dm, &globalSection));
    5717:   for (v = 0; v < numValues; ++v) {
    5718:     PetscFEGeom    *fgeom;
    5719:     PetscInt        maxDegree;
    5720:     PetscQuadrature qGeom = NULL;
    5721:     IS              pointIS;
    5722:     const PetscInt *points;
    5723:     PetscFormKey    key;
    5724:     PetscInt        numFaces, face, Nq;

    5726:     key.label = label;
    5727:     key.value = values[v];
    5728:     key.part  = 0;
    5729:     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
    5730:     if (!pointIS) continue; /* No points with that id on this process */
    5731:     {
    5732:       IS isectIS;

    5734:       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
    5735:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
    5736:       PetscCall(ISDestroy(&pointIS));
    5737:       pointIS = isectIS;
    5738:     }
    5739:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
    5740:     PetscCall(ISGetIndices(pointIS, &points));
    5741:     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));
    5742:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
    5743:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
    5744:     if (!qGeom) {
    5745:       PetscFE fe;

    5747:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
    5748:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
    5749:       PetscCall(PetscObjectReference((PetscObject)qGeom));
    5750:     }
    5751:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
    5752:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
    5753:     for (face = 0; face < numFaces; ++face) {
    5754:       const PetscInt point = points[face], *support;
    5755:       PetscScalar   *x     = NULL;
    5756:       PetscInt       i;

    5758:       PetscCall(DMPlexGetSupport(dm, point, &support));
    5759:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
    5760:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
    5761:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
    5762:       if (locX_t) {
    5763:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
    5764:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
    5765:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
    5766:       }
    5767:       if (locA) {
    5768:         PetscInt subp;
    5769:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
    5770:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
    5771:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
    5772:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
    5773:       }
    5774:     }
    5775:     if (elemMat) PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
    5776:     if (elemMatP) PetscCall(PetscArrayzero(elemMatP, numFaces * totDim * totDim));
    5777:     {
    5778:       PetscFE  fe;
    5779:       PetscInt Nb;
    5780:       /* Conforming batches */
    5781:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    5782:       /* Remainder */
    5783:       PetscFEGeom *chunkGeom = NULL;
    5784:       PetscInt     fieldJ, Nr, offset;

    5786:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe));
    5787:       PetscCall(PetscFEGetDimension(fe, &Nb));
    5788:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    5789:       blockSize = Nb;
    5790:       batchSize = numBlocks * blockSize;
    5791:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    5792:       numChunks = numFaces / (numBatches * batchSize);
    5793:       Ne        = numChunks * numBatches * batchSize;
    5794:       Nr        = numFaces % (numBatches * batchSize);
    5795:       offset    = numFaces - Nr;
    5796:       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
    5797:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    5798:         key.field = fieldI * Nf + fieldJ;
    5799:         if (hasJac) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat));
    5800:         if (hasPrec) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP));
    5801:       }
    5802:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
    5803:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    5804:         key.field = fieldI * Nf + fieldJ;
    5805:         if (hasJac)
    5806:           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]));
    5807:         if (hasPrec)
    5808:           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]));
    5809:       }
    5810:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
    5811:     }
    5812:     for (face = 0; face < numFaces; ++face) {
    5813:       const PetscInt point = points[face], *support;

    5815:       /* Transform to global basis before insertion in Jacobian */
    5816:       PetscCall(DMPlexGetSupport(plex, point, &support));
    5817:       if (hasJac && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
    5818:       if (hasPrec && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMatP[face * totDim * totDim]));
    5819:       if (hasPrec) {
    5820:         if (hasJac) {
    5821:           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
    5822:           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
    5823:         }
    5824:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMatP[face * totDim * totDim]));
    5825:         PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMatP[face * totDim * totDim], ADD_VALUES));
    5826:       } else {
    5827:         if (hasJac) {
    5828:           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
    5829:           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
    5830:         }
    5831:       }
    5832:     }
    5833:     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
    5834:     PetscCall(PetscQuadratureDestroy(&qGeom));
    5835:     PetscCall(ISRestoreIndices(pointIS, &points));
    5836:     PetscCall(ISDestroy(&pointIS));
    5837:     PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, a));
    5838:   }
    5839:   if (plex) PetscCall(DMDestroy(&plex));
    5840:   if (plexA) PetscCall(DMDestroy(&plexA));
    5841:   PetscFunctionReturn(PETSC_SUCCESS);
    5842: }

    5844: PetscErrorCode DMPlexComputeBdJacobianSingle(DM dm, PetscReal t, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt field, Vec locX, Vec locX_t, PetscReal X_tShift, Mat Jac, Mat JacP)
    5845: {
    5846:   DMField  coordField;
    5847:   DMLabel  depthLabel;
    5848:   IS       facetIS;
    5849:   PetscInt dim;

    5851:   PetscFunctionBegin;
    5852:   PetscCall(DMGetDimension(dm, &dim));
    5853:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    5854:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
    5855:   PetscCall(DMGetCoordinateField(dm, &coordField));
    5856:   PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
    5857:   PetscCall(ISDestroy(&facetIS));
    5858:   PetscFunctionReturn(PETSC_SUCCESS);
    5859: }

    5861: static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
    5862: {
    5863:   PetscDS  prob;
    5864:   PetscInt dim, numBd, bd;
    5865:   DMLabel  depthLabel;
    5866:   DMField  coordField = NULL;
    5867:   IS       facetIS;

    5869:   PetscFunctionBegin;
    5870:   PetscCall(DMGetDS(dm, &prob));
    5871:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
    5872:   PetscCall(DMGetDimension(dm, &dim));
    5873:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
    5874:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
    5875:   PetscCall(DMGetCoordinateField(dm, &coordField));
    5876:   for (bd = 0; bd < numBd; ++bd) {
    5877:     PetscWeakForm           wf;
    5878:     DMBoundaryConditionType type;
    5879:     DMLabel                 label;
    5880:     const PetscInt         *values;
    5881:     PetscInt                fieldI, numValues;
    5882:     PetscObject             obj;
    5883:     PetscClassId            id;

    5885:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
    5886:     if (type & DM_BC_ESSENTIAL) continue;
    5887:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
    5888:     PetscCall(PetscObjectGetClassId(obj, &id));
    5889:     if (id != PETSCFE_CLASSID) continue;
    5890:     PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
    5891:   }
    5892:   PetscCall(ISDestroy(&facetIS));
    5893:   PetscFunctionReturn(PETSC_SUCCESS);
    5894: }

    5896: PetscErrorCode DMPlexComputeJacobian_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, void *user)
    5897: {
    5898:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
    5899:   const char     *name  = "Jacobian";
    5900:   DM              dmAux = NULL, plex, tdm;
    5901:   DMEnclosureType encAux;
    5902:   Vec             A, tv;
    5903:   DMField         coordField;
    5904:   PetscDS         prob, probAux = NULL;
    5905:   PetscSection    section, globalSection, sectionAux;
    5906:   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
    5907:   const PetscInt *cells;
    5908:   PetscInt        Nf, fieldI, fieldJ;
    5909:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
    5910:   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;

    5912:   PetscFunctionBegin;
    5913:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    5914:   PetscCall(DMGetLocalSection(dm, &section));
    5915:   PetscCall(DMGetGlobalSection(dm, &globalSection));
    5916:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
    5917:   if (A) {
    5918:     PetscCall(VecGetDM(A, &dmAux));
    5919:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    5920:     PetscCall(DMConvert(dmAux, DMPLEX, &plex));
    5921:     PetscCall(DMGetLocalSection(plex, &sectionAux));
    5922:     PetscCall(DMGetDS(dmAux, &probAux));
    5923:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    5924:   }
    5925:   PetscCall(DMGetCoordinateField(dm, &coordField));
    5926:   if (!cellIS) goto end;
    5927:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    5928:   PetscCall(ISGetLocalSize(cellIS, &numCells));
    5929:   if (cStart >= cEnd) goto end;
    5930:   PetscCall(DMHasBasisTransform(dm, &transform));
    5931:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
    5932:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
    5933:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
    5934:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    5935:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    5936:   PetscCall(PetscDSHasJacobian(prob, &hasJac));
    5937:   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
    5938:   /* user passed in the same matrix, avoid double contributions and
    5939:      only assemble the Jacobian */
    5940:   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
    5941:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
    5942:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
    5943:   PetscCall(PetscMalloc5(numCells * totDim, &u, (X_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));
    5944:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
    5945:   for (c = cStart; c < cEnd; ++c) {
    5946:     const PetscInt cell = cells ? cells[c] : c;
    5947:     const PetscInt cind = c - cStart;
    5948:     PetscScalar   *x = NULL, *x_t = NULL;
    5949:     PetscInt       i;

    5951:     PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
    5952:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
    5953:     PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
    5954:     if (X_t) {
    5955:       PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
    5956:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
    5957:       PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
    5958:     }
    5959:     if (dmAux) {
    5960:       PetscInt subcell;
    5961:       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
    5962:       PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
    5963:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
    5964:       PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
    5965:     }
    5966:   }
    5967:   if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
    5968:   if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
    5969:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
    5970:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
    5971:     PetscClassId    id;
    5972:     PetscFE         fe;
    5973:     PetscQuadrature qGeom = NULL;
    5974:     PetscInt        Nb;
    5975:     /* Conforming batches */
    5976:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    5977:     /* Remainder */
    5978:     PetscInt     Nr, offset, Nq;
    5979:     PetscInt     maxDegree;
    5980:     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

    5982:     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
    5983:     PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
    5984:     if (id == PETSCFV_CLASSID) {
    5985:       hasFV = PETSC_TRUE;
    5986:       continue;
    5987:     }
    5988:     PetscCall(PetscFEGetDimension(fe, &Nb));
    5989:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    5990:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    5991:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
    5992:     if (!qGeom) {
    5993:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
    5994:       PetscCall(PetscObjectReference((PetscObject)qGeom));
    5995:     }
    5996:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
    5997:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
    5998:     blockSize = Nb;
    5999:     batchSize = numBlocks * blockSize;
    6000:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    6001:     numChunks = numCells / (numBatches * batchSize);
    6002:     Ne        = numChunks * numBatches * batchSize;
    6003:     Nr        = numCells % (numBatches * batchSize);
    6004:     offset    = numCells - Nr;
    6005:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
    6006:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
    6007:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    6008:       key.field = fieldI * Nf + fieldJ;
    6009:       if (hasJac) {
    6010:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
    6011:         PetscCall(PetscFEIntegrateJacobian(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]));
    6012:       }
    6013:       if (hasPrec) {
    6014:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
    6015:         PetscCall(PetscFEIntegrateJacobian(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]));
    6016:       }
    6017:       if (hasDyn) {
    6018:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
    6019:         PetscCall(PetscFEIntegrateJacobian(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]));
    6020:       }
    6021:     }
    6022:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
    6023:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
    6024:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
    6025:     PetscCall(PetscQuadratureDestroy(&qGeom));
    6026:   }
    6027:   /*   Add contribution from X_t */
    6028:   if (hasDyn) {
    6029:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
    6030:   }
    6031:   if (hasFV) {
    6032:     PetscClassId id;
    6033:     PetscFV      fv;
    6034:     PetscInt     offsetI, NcI, NbI = 1, fc, f;

    6036:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
    6037:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
    6038:       PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
    6039:       PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
    6040:       if (id != PETSCFV_CLASSID) continue;
    6041:       /* Put in the weighted identity */
    6042:       PetscCall(PetscFVGetNumComponents(fv, &NcI));
    6043:       for (c = cStart; c < cEnd; ++c) {
    6044:         const PetscInt cind    = c - cStart;
    6045:         const PetscInt eOffset = cind * totDim * totDim;
    6046:         PetscReal      vol;

    6048:         PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
    6049:         for (fc = 0; fc < NcI; ++fc) {
    6050:           for (f = 0; f < NbI; ++f) {
    6051:             const PetscInt i = offsetI + f * NcI + fc;
    6052:             if (hasPrec) {
    6053:               if (hasJac) elemMat[eOffset + i * totDim + i] = vol;
    6054:               elemMatP[eOffset + i * totDim + i] = vol;
    6055:             } else {
    6056:               elemMat[eOffset + i * totDim + i] = vol;
    6057:             }
    6058:           }
    6059:         }
    6060:       }
    6061:     }
    6062:     /* No allocated space for FV stuff, so ignore the zero entries */
    6063:     PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
    6064:   }
    6065:   /* Insert values into matrix */
    6066:   for (c = cStart; c < cEnd; ++c) {
    6067:     const PetscInt cell = cells ? cells[c] : c;
    6068:     const PetscInt cind = c - cStart;

    6070:     /* Transform to global basis before insertion in Jacobian */
    6071:     if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
    6072:     if (hasPrec) {
    6073:       if (hasJac) {
    6074:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
    6075:         PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
    6076:       }
    6077:       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
    6078:       PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
    6079:     } else {
    6080:       if (hasJac) {
    6081:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
    6082:         PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
    6083:       }
    6084:     }
    6085:   }
    6086:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    6087:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
    6088:   PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
    6089:   if (dmAux) PetscCall(PetscFree(a));
    6090:   /* Compute boundary integrals */
    6091:   PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user));
    6092:   /* Assemble matrix */
    6093: end: {
    6094:   PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;

    6096:   if (dmAux) PetscCall(DMDestroy(&plex));
    6097:   PetscCallMPI(MPIU_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
    6098:   if (hasJac && hasPrec) {
    6099:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
    6100:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
    6101:   }
    6102: }
    6103:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
    6104:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
    6105:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    6106:   PetscFunctionReturn(PETSC_SUCCESS);
    6107: }

    6109: PetscErrorCode DMPlexComputeJacobian_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, void *user)
    6110: {
    6111:   DM_Plex        *mesh          = (DM_Plex *)dm->data;
    6112:   const char     *name          = "Hybrid Jacobian";
    6113:   DM              dmAux[3]      = {NULL, NULL, NULL};
    6114:   DMLabel         ghostLabel    = NULL;
    6115:   DM              plex          = NULL;
    6116:   DM              plexA         = NULL;
    6117:   PetscDS         ds            = NULL;
    6118:   PetscDS         dsIn          = NULL;
    6119:   PetscDS         dsAux[3]      = {NULL, NULL, NULL};
    6120:   Vec             locA[3]       = {NULL, NULL, NULL};
    6121:   DM              dmScale[3]    = {NULL, NULL, NULL};
    6122:   PetscDS         dsScale[3]    = {NULL, NULL, NULL};
    6123:   Vec             locS[3]       = {NULL, NULL, NULL};
    6124:   PetscSection    section       = NULL;
    6125:   PetscSection    sectionAux[3] = {NULL, NULL, NULL};
    6126:   DMField         coordField    = NULL;
    6127:   PetscScalar    *a[3]          = {NULL, NULL, NULL};
    6128:   PetscScalar    *s[3]          = {NULL, NULL, NULL};
    6129:   PetscScalar    *u             = NULL, *u_t;
    6130:   PetscScalar    *elemMatNeg, *elemMatPos, *elemMatCoh;
    6131:   PetscScalar    *elemMatNegP, *elemMatPosP, *elemMatCohP;
    6132:   PetscSection    globalSection;
    6133:   IS              chunkIS;
    6134:   const PetscInt *cells;
    6135:   PetscInt       *faces;
    6136:   PetscInt        cStart, cEnd, numCells;
    6137:   PetscInt        Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
    6138:   PetscInt        maxDegree  = PETSC_INT_MAX;
    6139:   PetscQuadrature affineQuad = NULL, *quads = NULL;
    6140:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
    6141:   PetscBool       hasBdJac, hasBdPrec;

    6143:   PetscFunctionBegin;
    6144:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    6145:   if (!cellIS) goto end;
    6146:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    6147:   PetscCall(ISGetLocalSize(cellIS, &numCells));
    6148:   if (cStart >= cEnd) goto end;
    6149:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
    6150:     const char *name;
    6151:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
    6152:     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);
    6153:   }
    6154:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    6155:   PetscCall(DMGetSection(dm, &section));
    6156:   PetscCall(DMGetGlobalSection(dm, &globalSection));
    6157:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
    6158:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
    6159:   PetscCall(PetscDSGetNumFields(ds, &Nf));
    6160:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
    6161:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
    6162:   PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
    6163:   PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
    6164:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
    6165:   if (locA[2]) {
    6166:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

    6168:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
    6169:     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
    6170:     PetscCall(DMGetSection(dmAux[2], &sectionAux[2]));
    6171:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
    6172:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
    6173:     {
    6174:       const PetscInt *cone;
    6175:       PetscInt        c;

    6177:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
    6178:       for (c = 0; c < 2; ++c) {
    6179:         const PetscInt *support;
    6180:         PetscInt        ssize, s;

    6182:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
    6183:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
    6184:         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);
    6185:         if (support[0] == cellStart) s = 1;
    6186:         else if (support[1] == cellStart) s = 0;
    6187:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
    6188:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
    6189:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
    6190:         else dmAux[c] = dmAux[2];
    6191:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
    6192:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
    6193:       }
    6194:     }
    6195:   }
    6196:   /* Handle mass matrix scaling
    6197:        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
    6198:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
    6199:   if (locS[2]) {
    6200:     const PetscInt cellStart = cells ? cells[cStart] : cStart;
    6201:     PetscInt       Nb, Nbs;

    6203:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
    6204:     PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
    6205:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
    6206:     // BRAD: This is not set correctly
    6207:     key[2].field = 2;
    6208:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
    6209:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
    6210:     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);
    6211:     {
    6212:       const PetscInt *cone;
    6213:       PetscInt        c;

    6215:       locS[1] = locS[0] = locS[2];
    6216:       dmScale[1] = dmScale[0] = dmScale[2];
    6217:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
    6218:       for (c = 0; c < 2; ++c) {
    6219:         const PetscInt *support;
    6220:         PetscInt        ssize, s;

    6222:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
    6223:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
    6224:         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);
    6225:         if (support[0] == cellStart) s = 1;
    6226:         else if (support[1] == cellStart) s = 0;
    6227:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
    6228:         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
    6229:         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
    6230:       }
    6231:     }
    6232:   }
    6233:   /* 2: Setup geometric data */
    6234:   PetscCall(DMGetCoordinateField(dm, &coordField));
    6235:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    6236:   if (maxDegree > 1) {
    6237:     PetscInt f;
    6238:     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
    6239:     for (f = 0; f < Nf; ++f) {
    6240:       PetscFE fe;

    6242:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
    6243:       if (fe) {
    6244:         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
    6245:         PetscCall(PetscObjectReference((PetscObject)quads[f]));
    6246:       }
    6247:     }
    6248:   }
    6249:   /* Loop over chunks */
    6250:   cellChunkSize = numCells;
    6251:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
    6252:   PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
    6253:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
    6254:   /* Extract field coefficients */
    6255:   /* NOTE This needs the end cap faces to have identical orientations */
    6256:   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
    6257:   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
    6258:   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
    6259:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
    6260:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
    6261:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
    6262:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
    6263:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
    6264:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
    6265:   for (chunk = 0; chunk < numChunks; ++chunk) {
    6266:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

    6268:     if (hasBdJac) {
    6269:       PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim));
    6270:       PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim));
    6271:       PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim));
    6272:     }
    6273:     if (hasBdPrec) {
    6274:       PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim));
    6275:       PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim));
    6276:       PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim));
    6277:     }
    6278:     /* Get faces */
    6279:     for (c = cS; c < cE; ++c) {
    6280:       const PetscInt  cell = cells ? cells[c] : c;
    6281:       const PetscInt *cone;
    6282:       PetscCall(DMPlexGetCone(plex, cell, &cone));
    6283:       faces[(c - cS) * 2 + 0] = cone[0];
    6284:       faces[(c - cS) * 2 + 1] = cone[1];
    6285:     }
    6286:     PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
    6287:     if (maxDegree <= 1) {
    6288:       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
    6289:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
    6290:     } else {
    6291:       PetscInt f;
    6292:       for (f = 0; f < Nf; ++f) {
    6293:         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
    6294:       }
    6295:     }

    6297:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
    6298:       PetscFE         feI;
    6299:       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[fieldI];
    6300:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
    6301:       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
    6302:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
    6303:       PetscBool       isCohesiveField;

    6305:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
    6306:       if (!feI) continue;
    6307:       PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
    6308:       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
    6309:       PetscCall(PetscFEGetDimension(feI, &Nb));
    6310:       blockSize = Nb;
    6311:       batchSize = numBlocks * blockSize;
    6312:       PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
    6313:       numChunks = numCells / (numBatches * batchSize);
    6314:       Ne        = numChunks * numBatches * batchSize;
    6315:       Nr        = numCells % (numBatches * batchSize);
    6316:       offset    = numCells - Nr;
    6317:       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
    6318:       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
    6319:       PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
    6320:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    6321:         PetscFE feJ;

    6323:         PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
    6324:         if (!feJ) continue;
    6325:         key[0].field = fieldI * Nf + fieldJ;
    6326:         key[1].field = fieldI * Nf + fieldJ;
    6327:         key[2].field = fieldI * Nf + fieldJ;
    6328:         if (hasBdJac) {
    6329:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg));
    6330:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], PetscSafePointerPlusOffset(a[0], offset * totDimAux[0]), t, X_tShift, &elemMatNeg[offset * totDim * totDim]));
    6331:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos));
    6332:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], PetscSafePointerPlusOffset(a[1], offset * totDimAux[1]), t, X_tShift, &elemMatPos[offset * totDim * totDim]));
    6333:         }
    6334:         if (hasBdPrec) {
    6335:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP));
    6336:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNegP[offset * totDim * totDim]));
    6337:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP));
    6338:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPosP[offset * totDim * totDim]));
    6339:         }
    6340:         if (hasBdJac) {
    6341:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh));
    6342:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], PetscSafePointerPlusOffset(a[2], offset * totDimAux[2]), t, X_tShift, &elemMatCoh[offset * totDim * totDim]));
    6343:         }
    6344:         if (hasBdPrec) {
    6345:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP));
    6346:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Nr, remGeom, &u[offset * totDimIn], PetscSafePointerPlusOffset(u_t, offset * totDimIn), dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCohP[offset * totDim * totDim]));
    6347:         }
    6348:       }
    6349:       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
    6350:       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
    6351:     }
    6352:     /* Insert values into matrix */
    6353:     for (c = cS; c < cE; ++c) {
    6354:       const PetscInt cell = cells ? cells[c] : c;
    6355:       const PetscInt cind = c - cS, coff = cind * totDim * totDim;
    6356:       PetscInt       i, j;

    6358:       /* Scale element values */
    6359:       if (locS[0]) {
    6360:         PetscInt  Nb, soff = cind * totDimScale[0], off = 0;
    6361:         PetscBool cohesive;

    6363:         for (fieldI = 0; fieldI < Nf; ++fieldI) {
    6364:           PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
    6365:           PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));

    6367:           if (fieldI == key[2].field) {
    6368:             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
    6369:             for (i = 0; i < Nb; ++i) {
    6370:               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];
    6371:               if (hasBdPrec)
    6372:                 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];
    6373:             }
    6374:             off += Nb;
    6375:           } else {
    6376:             const PetscInt N = cohesive ? Nb : Nb * 2;

    6378:             for (i = 0; i < N; ++i) {
    6379:               for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j];
    6380:               if (hasBdPrec)
    6381:                 for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += elemMatNegP[coff + (off + i) * totDim + j] + elemMatPosP[coff + (off + i) * totDim + j];
    6382:             }
    6383:             off += N;
    6384:           }
    6385:         }
    6386:       } else {
    6387:         for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i];
    6388:         if (hasBdPrec)
    6389:           for (i = 0; i < totDim * totDim; ++i) elemMatCohP[coff + i] += elemMatNegP[coff + i] + elemMatPosP[coff + i];
    6390:       }
    6391:       if (hasBdPrec) {
    6392:         if (hasBdJac) {
    6393:           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
    6394:           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
    6395:         }
    6396:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim]));
    6397:         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES));
    6398:       } else if (hasBdJac) {
    6399:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
    6400:         PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
    6401:       }
    6402:     }
    6403:   }
    6404:   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
    6405:   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
    6406:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
    6407:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
    6408:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
    6409:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
    6410:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
    6411:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
    6412:   PetscCall(PetscFree(faces));
    6413:   PetscCall(ISDestroy(&chunkIS));
    6414:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    6415:   if (maxDegree <= 1) {
    6416:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
    6417:     PetscCall(PetscQuadratureDestroy(&affineQuad));
    6418:   } else {
    6419:     PetscInt f;
    6420:     for (f = 0; f < Nf; ++f) {
    6421:       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
    6422:       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
    6423:     }
    6424:     PetscCall(PetscFree2(quads, geoms));
    6425:   }
    6426:   if (dmAux[2]) PetscCall(DMDestroy(&plexA));
    6427:   PetscCall(DMDestroy(&plex));
    6428: end:
    6429:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    6430:   PetscFunctionReturn(PETSC_SUCCESS);
    6431: }

    6433: /*
    6434:   DMPlexComputeJacobian_Action_Internal - Form the local portion of the Jacobian action Z = J(X) Y at the local solution X using pointwise functions specified by the user.

    6436:   Input Parameters:
    6437: + dm     - The mesh
    6438: . key    - The PetscWeakFormKey indicating where integration should happen
    6439: . cellIS - The cells to integrate over
    6440: . t      - The time
    6441: . X_tShift - The multiplier for the Jacobian with respect to X_t
    6442: . X      - Local solution vector
    6443: . X_t    - Time-derivative of the local solution vector
    6444: . Y      - Local input vector
    6445: - user   - the user context

    6447:   Output Parameter:
    6448: . Z - Local output vector

    6450:   Note:
    6451:   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
    6452:   like a GPU, or vectorize on a multicore machine.
    6453: */
    6454: PetscErrorCode DMPlexComputeJacobian_Action_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Vec Y, Vec Z, void *user)
    6455: {
    6456:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
    6457:   const char     *name  = "Jacobian";
    6458:   DM              dmAux = NULL, plex, plexAux = NULL;
    6459:   DMEnclosureType encAux;
    6460:   Vec             A;
    6461:   DMField         coordField;
    6462:   PetscDS         prob, probAux = NULL;
    6463:   PetscQuadrature quad;
    6464:   PetscSection    section, globalSection, sectionAux;
    6465:   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
    6466:   const PetscInt *cells;
    6467:   PetscInt        Nf, fieldI, fieldJ;
    6468:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
    6469:   PetscBool       hasDyn;

    6471:   PetscFunctionBegin;
    6472:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    6473:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    6474:   PetscCall(ISGetLocalSize(cellIS, &numCells));
    6475:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
    6476:   PetscCall(DMGetLocalSection(dm, &section));
    6477:   PetscCall(DMGetGlobalSection(dm, &globalSection));
    6478:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
    6479:   PetscCall(PetscDSGetNumFields(prob, &Nf));
    6480:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
    6481:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
    6482:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
    6483:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
    6484:   if (A) {
    6485:     PetscCall(VecGetDM(A, &dmAux));
    6486:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
    6487:     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
    6488:     PetscCall(DMGetLocalSection(plexAux, &sectionAux));
    6489:     PetscCall(DMGetDS(dmAux, &probAux));
    6490:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
    6491:   }
    6492:   PetscCall(VecSet(Z, 0.0));
    6493:   PetscCall(PetscMalloc6(numCells * totDim, &u, (X_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));
    6494:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
    6495:   PetscCall(DMGetCoordinateField(dm, &coordField));
    6496:   for (c = cStart; c < cEnd; ++c) {
    6497:     const PetscInt cell = cells ? cells[c] : c;
    6498:     const PetscInt cind = c - cStart;
    6499:     PetscScalar   *x = NULL, *x_t = NULL;
    6500:     PetscInt       i;

    6502:     PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x));
    6503:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
    6504:     PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x));
    6505:     if (X_t) {
    6506:       PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t));
    6507:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
    6508:       PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t));
    6509:     }
    6510:     if (dmAux) {
    6511:       PetscInt subcell;
    6512:       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
    6513:       PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
    6514:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
    6515:       PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
    6516:     }
    6517:     PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x));
    6518:     for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
    6519:     PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x));
    6520:   }
    6521:   PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
    6522:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
    6523:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
    6524:     PetscFE  fe;
    6525:     PetscInt Nb;
    6526:     /* Conforming batches */
    6527:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
    6528:     /* Remainder */
    6529:     PetscInt        Nr, offset, Nq;
    6530:     PetscQuadrature qGeom = NULL;
    6531:     PetscInt        maxDegree;
    6532:     PetscFEGeom    *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

    6534:     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
    6535:     PetscCall(PetscFEGetQuadrature(fe, &quad));
    6536:     PetscCall(PetscFEGetDimension(fe, &Nb));
    6537:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
    6538:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
    6539:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
    6540:     if (!qGeom) {
    6541:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
    6542:       PetscCall(PetscObjectReference((PetscObject)qGeom));
    6543:     }
    6544:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
    6545:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
    6546:     blockSize = Nb;
    6547:     batchSize = numBlocks * blockSize;
    6548:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
    6549:     numChunks = numCells / (numBatches * batchSize);
    6550:     Ne        = numChunks * numBatches * batchSize;
    6551:     Nr        = numCells % (numBatches * batchSize);
    6552:     offset    = numCells - Nr;
    6553:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
    6554:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
    6555:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
    6556:       key.field = fieldI * Nf + fieldJ;
    6557:       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
    6558:       PetscCall(PetscFEIntegrateJacobian(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]));
    6559:       if (hasDyn) {
    6560:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
    6561:         PetscCall(PetscFEIntegrateJacobian(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]));
    6562:       }
    6563:     }
    6564:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
    6565:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
    6566:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
    6567:     PetscCall(PetscQuadratureDestroy(&qGeom));
    6568:   }
    6569:   if (hasDyn) {
    6570:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
    6571:   }
    6572:   for (c = cStart; c < cEnd; ++c) {
    6573:     const PetscInt     cell = cells ? cells[c] : c;
    6574:     const PetscInt     cind = c - cStart;
    6575:     const PetscBLASInt one  = 1;
    6576:     PetscBLASInt       M;
    6577:     const PetscScalar  a = 1.0, b = 0.0;

    6579:     PetscCall(PetscBLASIntCast(totDim, &M));
    6580:     PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
    6581:     if (mesh->printFEM > 1) {
    6582:       PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
    6583:       PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
    6584:       PetscCall(DMPrintCellVector(c, "Z", totDim, z));
    6585:     }
    6586:     PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES));
    6587:   }
    6588:   PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
    6589:   if (mesh->printFEM) {
    6590:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n"));
    6591:     PetscCall(VecView(Z, NULL));
    6592:   }
    6593:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
    6594:   PetscCall(PetscFree(a));
    6595:   PetscCall(DMDestroy(&plexAux));
    6596:   PetscCall(DMDestroy(&plex));
    6597:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
    6598:   PetscFunctionReturn(PETSC_SUCCESS);
    6599: }