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:       if (copy) {
 34:         DMSubDomainHookLink link;

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

 49: static PetscErrorCode PetscContainerUserDestroy_PetscFEGeom(void *ctx)
 50: {
 51:   PetscFEGeom *geom = (PetscFEGeom *)ctx;

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

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

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

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

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

 91:   Not Collective

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

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

100:   Level: advanced

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

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

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

118:   Not Collective

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

125:   Level: advanced

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

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

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

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

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

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

159:   Not collective

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

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

167:   Level: intermediate

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

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

184:   Not collective

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

190:   Level: intermediate

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

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

206:   Not collective

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

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

214:   Level: intermediate

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

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

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

232:   Not collective

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

238:   Level: intermediate

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

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

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

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

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

281:   Collective

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

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

290:   Level: advanced

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

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

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

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

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

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

357:   Collective

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

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

369:   Level: advanced

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

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

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

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

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

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

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

435:   Level: advanced

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

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

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

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

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

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

467:   Level: intermediate

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

794:   Level: developer

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

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

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

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

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

820:   Level: developer

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

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

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

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

846:   Level: developer

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

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

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

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

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

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

898:   Level: developer

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

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

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

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

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

937:   Level: developer

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

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

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

960:   Collective

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

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

978:   Level: developer

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

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

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

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

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

1019:   Level: developer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1247:   Not Collective

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

1257:   Output Parameter:
1258: . locX - Solution updated with boundary values

1260:   Level: intermediate

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

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

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

1287:   Output Parameter:
1288: . locX_t - Solution updated with boundary values

1290:   Level: developer

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

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

1310:   PetscFunctionBegin;
1311:   PetscCall(DMGetLocalVector(dm, &localX));
1312:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL));
1313:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1314:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1315:   PetscCall(DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff));
1316:   PetscCall(DMRestoreLocalVector(dm, &localX));
1317:   PetscFunctionReturn(PETSC_SUCCESS);
1318: }

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

1323:   Collective

1325:   Input Parameters:
1326: + dm     - The `DM`
1327: . time   - The time
1328: . funcs  - The functions to evaluate for each field component
1329: . ctxs   - Optional array of contexts to pass to each function, or `NULL`.
1330: - localX - The coefficient vector u_h, a local vector

1332:   Output Parameter:
1333: . diff - The diff ||u - u_h||_2

1335:   Level: developer

1337: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1338: @*/
1339: PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff)
1340: {
1341:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
1342:   DM               tdm;
1343:   Vec              tv;
1344:   PetscSection     section;
1345:   PetscQuadrature  quad;
1346:   PetscFEGeom      fegeom;
1347:   PetscScalar     *funcVal, *interpolant;
1348:   PetscReal       *coords, *gcoords;
1349:   PetscReal        localDiff = 0.0;
1350:   const PetscReal *quadWeights;
1351:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset;
1352:   PetscBool        transform;

1354:   PetscFunctionBegin;
1355:   PetscCall(DMGetDimension(dm, &dim));
1356:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1357:   fegeom.dimEmbed = coordDim;
1358:   PetscCall(DMGetLocalSection(dm, &section));
1359:   PetscCall(PetscSectionGetNumFields(section, &numFields));
1360:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1361:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1362:   PetscCall(DMHasBasisTransform(dm, &transform));
1363:   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1364:   for (field = 0; field < numFields; ++field) {
1365:     PetscObject  obj;
1366:     PetscClassId id;
1367:     PetscInt     Nc;

1369:     PetscCall(DMGetField(dm, field, NULL, &obj));
1370:     PetscCall(PetscObjectGetClassId(obj, &id));
1371:     if (id == PETSCFE_CLASSID) {
1372:       PetscFE fe = (PetscFE)obj;

1374:       PetscCall(PetscFEGetQuadrature(fe, &quad));
1375:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1376:     } else if (id == PETSCFV_CLASSID) {
1377:       PetscFV fv = (PetscFV)obj;

1379:       PetscCall(PetscFVGetQuadrature(fv, &quad));
1380:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
1381:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1382:     numComponents += Nc;
1383:   }
1384:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1385:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1386:   PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * (Nq + 1), &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1387:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1388:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
1389:   for (c = cStart; c < cEnd; ++c) {
1390:     PetscScalar *x        = NULL;
1391:     PetscReal    elemDiff = 0.0;
1392:     PetscInt     qc       = 0;

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

1397:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1398:       PetscObject  obj;
1399:       PetscClassId id;
1400:       void *const  ctx = ctxs ? ctxs[field] : NULL;
1401:       PetscInt     Nb, Nc, q, fc;

1403:       PetscCall(DMGetField(dm, field, NULL, &obj));
1404:       PetscCall(PetscObjectGetClassId(obj, &id));
1405:       if (id == PETSCFE_CLASSID) {
1406:         PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1407:         PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1408:       } else if (id == PETSCFV_CLASSID) {
1409:         PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1410:         Nb = 1;
1411:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1412:       if (debug) {
1413:         char title[1024];
1414:         PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1415:         PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1416:       }
1417:       for (q = 0; q < Nq; ++q) {
1418:         PetscFEGeom    qgeom;
1419:         PetscErrorCode ierr;

1421:         qgeom.dimEmbed = fegeom.dimEmbed;
1422:         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1423:         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1424:         qgeom.detJ     = &fegeom.detJ[q];
1425:         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);
1426:         if (transform) {
1427:           gcoords = &coords[coordDim * Nq];
1428:           PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1429:         } else {
1430:           gcoords = &coords[coordDim * q];
1431:         }
1432:         PetscCall(PetscArrayzero(funcVal, Nc));
1433:         ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx);
1434:         if (ierr) {
1435:           PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1436:           PetscCall(DMRestoreLocalVector(dm, &localX));
1437:           PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1438:         }
1439:         if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1440:         if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1441:         else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1442:         else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1443:         for (fc = 0; fc < Nc; ++fc) {
1444:           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1445:           if (debug)
1446:             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.),
1447:                                   (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]), (double)PetscRealPart(interpolant[fc]), (double)PetscRealPart(funcVal[fc])));
1448:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1449:         }
1450:       }
1451:       fieldOffset += Nb;
1452:       qc += Nc;
1453:     }
1454:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1455:     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1456:     localDiff += elemDiff;
1457:   }
1458:   PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1459:   PetscCall(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1460:   *diff = PetscSqrtReal(*diff);
1461:   PetscFunctionReturn(PETSC_SUCCESS);
1462: }

1464: 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)
1465: {
1466:   const PetscInt   debug = ((DM_Plex *)dm->data)->printL2;
1467:   DM               tdm;
1468:   PetscSection     section;
1469:   PetscQuadrature  quad;
1470:   Vec              localX, tv;
1471:   PetscScalar     *funcVal, *interpolant;
1472:   const PetscReal *quadWeights;
1473:   PetscFEGeom      fegeom;
1474:   PetscReal       *coords, *gcoords;
1475:   PetscReal        localDiff = 0.0;
1476:   PetscInt         dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset;
1477:   PetscBool        transform;

1479:   PetscFunctionBegin;
1480:   PetscCall(DMGetDimension(dm, &dim));
1481:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1482:   fegeom.dimEmbed = coordDim;
1483:   PetscCall(DMGetLocalSection(dm, &section));
1484:   PetscCall(PetscSectionGetNumFields(section, &numFields));
1485:   PetscCall(DMGetLocalVector(dm, &localX));
1486:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1487:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1488:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1489:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1490:   PetscCall(DMHasBasisTransform(dm, &transform));
1491:   for (field = 0; field < numFields; ++field) {
1492:     PetscFE  fe;
1493:     PetscInt Nc;

1495:     PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1496:     PetscCall(PetscFEGetQuadrature(fe, &quad));
1497:     PetscCall(PetscFEGetNumComponents(fe, &Nc));
1498:     numComponents += Nc;
1499:   }
1500:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights));
1501:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1502:   /* PetscCall(DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX)); */
1503:   PetscCall(PetscMalloc6(numComponents, &funcVal, coordDim * (Nq + 1), &coords, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ, numComponents * coordDim, &interpolant, Nq, &fegeom.detJ));
1504:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1505:   for (c = cStart; c < cEnd; ++c) {
1506:     PetscScalar *x        = NULL;
1507:     PetscReal    elemDiff = 0.0;
1508:     PetscInt     qc       = 0;

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

1513:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1514:       PetscFE     fe;
1515:       void *const ctx = ctxs ? ctxs[field] : NULL;
1516:       PetscInt    Nb, Nc, q, fc;

1518:       PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe));
1519:       PetscCall(PetscFEGetDimension(fe, &Nb));
1520:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1521:       if (debug) {
1522:         char title[1024];
1523:         PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field));
1524:         PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset]));
1525:       }
1526:       for (q = 0; q < Nq; ++q) {
1527:         PetscFEGeom    qgeom;
1528:         PetscErrorCode ierr;

1530:         qgeom.dimEmbed = fegeom.dimEmbed;
1531:         qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1532:         qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1533:         qgeom.detJ     = &fegeom.detJ[q];
1534:         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);
1535:         if (transform) {
1536:           gcoords = &coords[coordDim * Nq];
1537:           PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx));
1538:         } else {
1539:           gcoords = &coords[coordDim * q];
1540:         }
1541:         PetscCall(PetscArrayzero(funcVal, Nc));
1542:         ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx);
1543:         if (ierr) {
1544:           PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1545:           PetscCall(DMRestoreLocalVector(dm, &localX));
1546:           PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1547:         }
1548:         if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1549:         PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant));
1550:         /* Overwrite with the dot product if the normal is given */
1551:         if (n) {
1552:           for (fc = 0; fc < Nc; ++fc) {
1553:             PetscScalar sum = 0.0;
1554:             PetscInt    d;
1555:             for (d = 0; d < dim; ++d) sum += interpolant[fc * dim + d] * n[d];
1556:             interpolant[fc] = sum;
1557:           }
1558:         }
1559:         for (fc = 0; fc < Nc; ++fc) {
1560:           const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1561:           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])));
1562:           elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1563:         }
1564:       }
1565:       fieldOffset += Nb;
1566:       qc += Nc;
1567:     }
1568:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1569:     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff));
1570:     localDiff += elemDiff;
1571:   }
1572:   PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ));
1573:   PetscCall(DMRestoreLocalVector(dm, &localX));
1574:   PetscCall(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1575:   *diff = PetscSqrtReal(*diff);
1576:   PetscFunctionReturn(PETSC_SUCCESS);
1577: }

1579: PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff)
1580: {
1581:   const PetscInt debug = ((DM_Plex *)dm->data)->printL2;
1582:   DM             tdm;
1583:   DMLabel        depthLabel;
1584:   PetscSection   section;
1585:   Vec            localX, tv;
1586:   PetscReal     *localDiff;
1587:   PetscInt       dim, depth, dE, Nf, f, Nds, s;
1588:   PetscBool      transform;

1590:   PetscFunctionBegin;
1591:   PetscCall(DMGetDimension(dm, &dim));
1592:   PetscCall(DMGetCoordinateDim(dm, &dE));
1593:   PetscCall(DMGetLocalSection(dm, &section));
1594:   PetscCall(DMGetLocalVector(dm, &localX));
1595:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
1596:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
1597:   PetscCall(DMHasBasisTransform(dm, &transform));
1598:   PetscCall(DMGetNumFields(dm, &Nf));
1599:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
1600:   PetscCall(DMLabelGetNumValues(depthLabel, &depth));

1602:   PetscCall(VecSet(localX, 0.0));
1603:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1604:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1605:   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1606:   PetscCall(DMGetNumDS(dm, &Nds));
1607:   PetscCall(PetscCalloc1(Nf, &localDiff));
1608:   for (s = 0; s < Nds; ++s) {
1609:     PetscDS          ds;
1610:     DMLabel          label;
1611:     IS               fieldIS, pointIS;
1612:     const PetscInt  *fields, *points = NULL;
1613:     PetscQuadrature  quad;
1614:     const PetscReal *quadPoints, *quadWeights;
1615:     PetscFEGeom      fegeom;
1616:     PetscReal       *coords, *gcoords;
1617:     PetscScalar     *funcVal, *interpolant;
1618:     PetscBool        isCohesive;
1619:     PetscInt         qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf;

1621:     PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL));
1622:     PetscCall(ISGetIndices(fieldIS, &fields));
1623:     PetscCall(PetscDSIsCohesive(ds, &isCohesive));
1624:     PetscCall(PetscDSGetNumFields(ds, &dsNf));
1625:     PetscCall(PetscDSGetTotalComponents(ds, &totNc));
1626:     PetscCall(PetscDSGetQuadrature(ds, &quad));
1627:     PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1628:     PetscCheck(!(qNc != 1) || !(qNc != totNc), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, totNc);
1629:     PetscCall(PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE * (Nq + 1), &coords, Nq, &fegeom.detJ, dE * dE * Nq, &fegeom.J, dE * dE * Nq, &fegeom.invJ));
1630:     if (!label) {
1631:       PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1632:     } else {
1633:       PetscCall(DMLabelGetStratumIS(label, 1, &pointIS));
1634:       PetscCall(ISGetLocalSize(pointIS, &cEnd));
1635:       PetscCall(ISGetIndices(pointIS, &points));
1636:     }
1637:     for (c = cStart; c < cEnd; ++c) {
1638:       const PetscInt  cell = points ? points[c] : c;
1639:       PetscScalar    *x    = NULL;
1640:       const PetscInt *cone;
1641:       PetscInt        qc = 0, fOff = 0, dep;

1643:       PetscCall(DMLabelGetValue(depthLabel, cell, &dep));
1644:       if (dep != depth - 1) continue;
1645:       if (isCohesive) {
1646:         PetscCall(DMPlexGetCone(dm, cell, &cone));
1647:         PetscCall(DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1648:       } else {
1649:         PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1650:       }
1651:       PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, cell, 0, NULL, &x));
1652:       for (f = 0; f < dsNf; ++f) {
1653:         PetscObject  obj;
1654:         PetscClassId id;
1655:         void *const  ctx = ctxs ? ctxs[fields[f]] : NULL;
1656:         PetscInt     Nb, Nc, q, fc;
1657:         PetscReal    elemDiff = 0.0;
1658:         PetscBool    cohesive;

1660:         PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
1661:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
1662:         PetscCall(PetscObjectGetClassId(obj, &id));
1663:         if (id == PETSCFE_CLASSID) {
1664:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1665:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1666:         } else if (id == PETSCFV_CLASSID) {
1667:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1668:           Nb = 1;
1669:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1670:         if (isCohesive && !cohesive) {
1671:           fOff += Nb * 2;
1672:           qc += Nc;
1673:           continue;
1674:         }
1675:         if (debug) {
1676:           char title[1024];
1677:           PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, fields[f]));
1678:           PetscCall(DMPrintCellVector(cell, title, Nb, &x[fOff]));
1679:         }
1680:         for (q = 0; q < Nq; ++q) {
1681:           PetscFEGeom    qgeom;
1682:           PetscErrorCode ierr;

1684:           qgeom.dimEmbed = fegeom.dimEmbed;
1685:           qgeom.J        = &fegeom.J[q * dE * dE];
1686:           qgeom.invJ     = &fegeom.invJ[q * dE * dE];
1687:           qgeom.detJ     = &fegeom.detJ[q];
1688:           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);
1689:           if (transform) {
1690:             gcoords = &coords[dE * Nq];
1691:             PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE * q], PETSC_TRUE, dE, &coords[dE * q], gcoords, dm->transformCtx));
1692:           } else {
1693:             gcoords = &coords[dE * q];
1694:           }
1695:           for (fc = 0; fc < Nc; ++fc) funcVal[fc] = 0.;
1696:           ierr = (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx);
1697:           if (ierr) {
1698:             PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1699:             PetscCall(DMRestoreLocalVector(dm, &localX));
1700:             PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1701:           }
1702:           if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[dE * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx));
1703:           /* Call once for each face, except for lagrange field */
1704:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fOff], &qgeom, q, interpolant));
1705:           else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fOff], q, interpolant));
1706:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]);
1707:           for (fc = 0; fc < Nc; ++fc) {
1708:             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1709:             if (debug)
1710:               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.),
1711:                                     (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q])));
1712:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1713:           }
1714:         }
1715:         fOff += Nb;
1716:         qc += Nc;
1717:         localDiff[fields[f]] += elemDiff;
1718:         if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  cell %" PetscInt_FMT " field %" PetscInt_FMT " cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]]));
1719:       }
1720:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x));
1721:     }
1722:     if (label) {
1723:       PetscCall(ISRestoreIndices(pointIS, &points));
1724:       PetscCall(ISDestroy(&pointIS));
1725:     }
1726:     PetscCall(ISRestoreIndices(fieldIS, &fields));
1727:     PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1728:   }
1729:   PetscCall(DMRestoreLocalVector(dm, &localX));
1730:   PetscCall(MPIU_Allreduce(localDiff, diff, Nf, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
1731:   PetscCall(PetscFree(localDiff));
1732:   for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]);
1733:   PetscFunctionReturn(PETSC_SUCCESS);
1734: }

1736: /*@C
1737:   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.

1739:   Collective

1741:   Input Parameters:
1742: + dm    - The `DM`
1743: . time  - The time
1744: . funcs - The functions to evaluate for each field component: `NULL` means that component does not contribute to error calculation
1745: . ctxs  - Optional array of contexts to pass to each function, or `NULL`.
1746: - X     - The coefficient vector u_h

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

1751:   Level: developer

1753: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1754: @*/
1755: PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D)
1756: {
1757:   PetscSection     section;
1758:   PetscQuadrature  quad;
1759:   Vec              localX;
1760:   PetscFEGeom      fegeom;
1761:   PetscScalar     *funcVal, *interpolant;
1762:   PetscReal       *coords;
1763:   const PetscReal *quadPoints, *quadWeights;
1764:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset;

1766:   PetscFunctionBegin;
1767:   PetscCall(VecSet(D, 0.0));
1768:   PetscCall(DMGetDimension(dm, &dim));
1769:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
1770:   PetscCall(DMGetLocalSection(dm, &section));
1771:   PetscCall(PetscSectionGetNumFields(section, &numFields));
1772:   PetscCall(DMGetLocalVector(dm, &localX));
1773:   PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX));
1774:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX));
1775:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX));
1776:   for (field = 0; field < numFields; ++field) {
1777:     PetscObject  obj;
1778:     PetscClassId id;
1779:     PetscInt     Nc;

1781:     PetscCall(DMGetField(dm, field, NULL, &obj));
1782:     PetscCall(PetscObjectGetClassId(obj, &id));
1783:     if (id == PETSCFE_CLASSID) {
1784:       PetscFE fe = (PetscFE)obj;

1786:       PetscCall(PetscFEGetQuadrature(fe, &quad));
1787:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
1788:     } else if (id == PETSCFV_CLASSID) {
1789:       PetscFV fv = (PetscFV)obj;

1791:       PetscCall(PetscFVGetQuadrature(fv, &quad));
1792:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
1793:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1794:     numComponents += Nc;
1795:   }
1796:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1797:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
1798:   PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ));
1799:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1800:   for (c = cStart; c < cEnd; ++c) {
1801:     PetscScalar *x        = NULL;
1802:     PetscScalar  elemDiff = 0.0;
1803:     PetscInt     qc       = 0;

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

1808:     for (field = 0, fieldOffset = 0; field < numFields; ++field) {
1809:       PetscObject  obj;
1810:       PetscClassId id;
1811:       void *const  ctx = ctxs ? ctxs[field] : NULL;
1812:       PetscInt     Nb, Nc, q, fc;

1814:       PetscCall(DMGetField(dm, field, NULL, &obj));
1815:       PetscCall(PetscObjectGetClassId(obj, &id));
1816:       if (id == PETSCFE_CLASSID) {
1817:         PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
1818:         PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1819:       } else if (id == PETSCFV_CLASSID) {
1820:         PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
1821:         Nb = 1;
1822:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1823:       if (funcs[field]) {
1824:         for (q = 0; q < Nq; ++q) {
1825:           PetscFEGeom qgeom;

1827:           qgeom.dimEmbed = fegeom.dimEmbed;
1828:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
1829:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
1830:           qgeom.detJ     = &fegeom.detJ[q];
1831:           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);
1832:           PetscCall((*funcs[field])(coordDim, time, &coords[q * coordDim], Nc, funcVal, ctx));
1833: #if defined(needs_fix_with_return_code_argument)
1834:           if (ierr) {
1835:             PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1836:             PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1837:             PetscCall(DMRestoreLocalVector(dm, &localX));
1838:           }
1839: #endif
1840:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant));
1841:           else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant));
1842:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
1843:           for (fc = 0; fc < Nc; ++fc) {
1844:             const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)];
1845:             elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q];
1846:           }
1847:         }
1848:       }
1849:       fieldOffset += Nb;
1850:       qc += Nc;
1851:     }
1852:     PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x));
1853:     PetscCall(VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES));
1854:   }
1855:   PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1856:   PetscCall(DMRestoreLocalVector(dm, &localX));
1857:   PetscCall(VecSqrtAbs(D));
1858:   PetscFunctionReturn(PETSC_SUCCESS);
1859: }

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

1864:   Collective

1866:   Input Parameters:
1867: + dm   - The `DM`
1868: - locX - The coefficient vector u_h

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

1873:   Level: developer

1875:   Note:
1876:   $ 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

1878: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
1879: @*/
1880: PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC)
1881: {
1882:   PetscInt         debug = ((DM_Plex *)dm->data)->printFEM;
1883:   DM               dmc;
1884:   PetscQuadrature  quad;
1885:   PetscScalar     *interpolant, *valsum;
1886:   PetscFEGeom      fegeom;
1887:   PetscReal       *coords;
1888:   const PetscReal *quadPoints, *quadWeights;
1889:   PetscInt         dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v;

1891:   PetscFunctionBegin;
1892:   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
1893:   PetscCall(VecGetDM(locC, &dmc));
1894:   PetscCall(VecSet(locC, 0.0));
1895:   PetscCall(DMGetDimension(dm, &dim));
1896:   PetscCall(DMGetCoordinateDim(dm, &cdim));
1897:   fegeom.dimEmbed = cdim;
1898:   PetscCall(DMGetNumFields(dm, &Nf));
1899:   PetscCheck(Nf > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
1900:   for (f = 0; f < Nf; ++f) {
1901:     PetscObject  obj;
1902:     PetscClassId id;
1903:     PetscInt     fNc;

1905:     PetscCall(DMGetField(dm, f, NULL, &obj));
1906:     PetscCall(PetscObjectGetClassId(obj, &id));
1907:     if (id == PETSCFE_CLASSID) {
1908:       PetscFE fe = (PetscFE)obj;

1910:       PetscCall(PetscFEGetQuadrature(fe, &quad));
1911:       PetscCall(PetscFEGetNumComponents(fe, &fNc));
1912:     } else if (id == PETSCFV_CLASSID) {
1913:       PetscFV fv = (PetscFV)obj;

1915:       PetscCall(PetscFVGetQuadrature(fv, &quad));
1916:       PetscCall(PetscFVGetNumComponents(fv, &fNc));
1917:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1918:     Nc += fNc;
1919:   }
1920:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
1921:   PetscCheck(qNc == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " > 1", qNc);
1922:   PetscCall(PetscMalloc6(Nc * 2, &valsum, Nc, &interpolant, cdim * Nq, &coords, Nq, &fegeom.detJ, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ));
1923:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1924:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
1925:   for (v = vStart; v < vEnd; ++v) {
1926:     PetscScalar volsum = 0.0;
1927:     PetscInt   *star   = NULL;
1928:     PetscInt    starSize, st, fc;

1930:     PetscCall(PetscArrayzero(valsum, Nc));
1931:     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1932:     for (st = 0; st < starSize * 2; st += 2) {
1933:       const PetscInt cell = star[st];
1934:       PetscScalar   *val  = &valsum[Nc];
1935:       PetscScalar   *x    = NULL;
1936:       PetscReal      vol  = 0.0;
1937:       PetscInt       foff = 0;

1939:       if ((cell < cStart) || (cell >= cEnd)) continue;
1940:       PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
1941:       PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
1942:       for (f = 0; f < Nf; ++f) {
1943:         PetscObject  obj;
1944:         PetscClassId id;
1945:         PetscInt     Nb, fNc, q;

1947:         PetscCall(PetscArrayzero(val, Nc));
1948:         PetscCall(DMGetField(dm, f, NULL, &obj));
1949:         PetscCall(PetscObjectGetClassId(obj, &id));
1950:         if (id == PETSCFE_CLASSID) {
1951:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &fNc));
1952:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
1953:         } else if (id == PETSCFV_CLASSID) {
1954:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &fNc));
1955:           Nb = 1;
1956:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1957:         for (q = 0; q < Nq; ++q) {
1958:           const PetscReal wt = quadWeights[q] * fegeom.detJ[q];
1959:           PetscFEGeom     qgeom;

1961:           qgeom.dimEmbed = fegeom.dimEmbed;
1962:           qgeom.J        = &fegeom.J[q * cdim * cdim];
1963:           qgeom.invJ     = &fegeom.invJ[q * cdim * cdim];
1964:           qgeom.detJ     = &fegeom.detJ[q];
1965:           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);
1966:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[foff], &qgeom, q, interpolant));
1967:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
1968:           for (fc = 0; fc < fNc; ++fc) val[foff + fc] += interpolant[fc] * wt;
1969:           vol += wt;
1970:         }
1971:         foff += Nb;
1972:       }
1973:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
1974:       for (fc = 0; fc < Nc; ++fc) valsum[fc] += val[fc];
1975:       volsum += vol;
1976:       if (debug) {
1977:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " value: [", v, cell));
1978:         for (fc = 0; fc < Nc; ++fc) {
1979:           if (fc) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
1980:           PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(val[fc])));
1981:         }
1982:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
1983:       }
1984:     }
1985:     for (fc = 0; fc < Nc; ++fc) valsum[fc] /= volsum;
1986:     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
1987:     PetscCall(DMPlexVecSetClosure(dmc, NULL, locC, v, valsum, INSERT_VALUES));
1988:   }
1989:   PetscCall(PetscFree6(valsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
1990:   PetscFunctionReturn(PETSC_SUCCESS);
1991: }

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

1996:   Collective

1998:   Input Parameters:
1999: + dm   - The `DM`
2000: - locX - The coefficient vector u_h

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

2005:   Level: developer

2007:   Note:
2008:   $\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

2010: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()`
2011: @*/
2012: PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC)
2013: {
2014:   DM_Plex         *mesh  = (DM_Plex *)dm->data;
2015:   PetscInt         debug = mesh->printFEM;
2016:   DM               dmC;
2017:   PetscQuadrature  quad;
2018:   PetscScalar     *interpolant, *gradsum;
2019:   PetscFEGeom      fegeom;
2020:   PetscReal       *coords;
2021:   const PetscReal *quadPoints, *quadWeights;
2022:   PetscInt         dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset;

2024:   PetscFunctionBegin;
2025:   PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite));
2026:   PetscCall(VecGetDM(locC, &dmC));
2027:   PetscCall(VecSet(locC, 0.0));
2028:   PetscCall(DMGetDimension(dm, &dim));
2029:   PetscCall(DMGetCoordinateDim(dm, &coordDim));
2030:   fegeom.dimEmbed = coordDim;
2031:   PetscCall(DMGetNumFields(dm, &numFields));
2032:   PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!");
2033:   for (field = 0; field < numFields; ++field) {
2034:     PetscObject  obj;
2035:     PetscClassId id;
2036:     PetscInt     Nc;

2038:     PetscCall(DMGetField(dm, field, NULL, &obj));
2039:     PetscCall(PetscObjectGetClassId(obj, &id));
2040:     if (id == PETSCFE_CLASSID) {
2041:       PetscFE fe = (PetscFE)obj;

2043:       PetscCall(PetscFEGetQuadrature(fe, &quad));
2044:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
2045:     } else if (id == PETSCFV_CLASSID) {
2046:       PetscFV fv = (PetscFV)obj;

2048:       PetscCall(PetscFVGetQuadrature(fv, &quad));
2049:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
2050:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2051:     numComponents += Nc;
2052:   }
2053:   PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights));
2054:   PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents);
2055:   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));
2056:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2057:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
2058:   for (v = vStart; v < vEnd; ++v) {
2059:     PetscScalar volsum = 0.0;
2060:     PetscInt   *star   = NULL;
2061:     PetscInt    starSize, st, d, fc;

2063:     PetscCall(PetscArrayzero(gradsum, coordDim * numComponents));
2064:     PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2065:     for (st = 0; st < starSize * 2; st += 2) {
2066:       const PetscInt cell = star[st];
2067:       PetscScalar   *grad = &gradsum[coordDim * numComponents];
2068:       PetscScalar   *x    = NULL;
2069:       PetscReal      vol  = 0.0;

2071:       if ((cell < cStart) || (cell >= cEnd)) continue;
2072:       PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ));
2073:       PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x));
2074:       for (field = 0, fieldOffset = 0; field < numFields; ++field) {
2075:         PetscObject  obj;
2076:         PetscClassId id;
2077:         PetscInt     Nb, Nc, q, qc = 0;

2079:         PetscCall(PetscArrayzero(grad, coordDim * numComponents));
2080:         PetscCall(DMGetField(dm, field, NULL, &obj));
2081:         PetscCall(PetscObjectGetClassId(obj, &id));
2082:         if (id == PETSCFE_CLASSID) {
2083:           PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc));
2084:           PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb));
2085:         } else if (id == PETSCFV_CLASSID) {
2086:           PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc));
2087:           Nb = 1;
2088:         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2089:         for (q = 0; q < Nq; ++q) {
2090:           PetscFEGeom qgeom;

2092:           qgeom.dimEmbed = fegeom.dimEmbed;
2093:           qgeom.J        = &fegeom.J[q * coordDim * coordDim];
2094:           qgeom.invJ     = &fegeom.invJ[q * coordDim * coordDim];
2095:           qgeom.detJ     = &fegeom.detJ[q];
2096:           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);
2097:           if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant));
2098:           else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2099:           for (fc = 0; fc < Nc; ++fc) {
2100:             const PetscReal wt = quadWeights[q * qNc + qc];

2102:             for (d = 0; d < coordDim; ++d) grad[fc * coordDim + d] += interpolant[fc * dim + d] * wt * fegeom.detJ[q];
2103:           }
2104:           vol += quadWeights[q * qNc] * fegeom.detJ[q];
2105:         }
2106:         fieldOffset += Nb;
2107:         qc += Nc;
2108:       }
2109:       PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x));
2110:       for (fc = 0; fc < numComponents; ++fc) {
2111:         for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] += grad[fc * coordDim + d];
2112:       }
2113:       volsum += vol;
2114:       if (debug) {
2115:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " gradient: [", v, cell));
2116:         for (fc = 0; fc < numComponents; ++fc) {
2117:           for (d = 0; d < coordDim; ++d) {
2118:             if (fc || d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
2119:             PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc * coordDim + d])));
2120:           }
2121:         }
2122:         PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
2123:       }
2124:     }
2125:     for (fc = 0; fc < numComponents; ++fc) {
2126:       for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] /= volsum;
2127:     }
2128:     PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star));
2129:     PetscCall(DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES));
2130:   }
2131:   PetscCall(PetscFree6(gradsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ));
2132:   PetscFunctionReturn(PETSC_SUCCESS);
2133: }

2135: static PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec X, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, void *user)
2136: {
2137:   DM           dmAux = NULL;
2138:   PetscDS      prob, probAux = NULL;
2139:   PetscSection section, sectionAux;
2140:   Vec          locX, locA;
2141:   PetscInt     dim, numCells = cEnd - cStart, c, f;
2142:   PetscBool    useFVM = PETSC_FALSE;
2143:   /* DS */
2144:   PetscInt           Nf, totDim, *uOff, *uOff_x, numConstants;
2145:   PetscInt           NfAux, totDimAux, *aOff;
2146:   PetscScalar       *u, *a;
2147:   const PetscScalar *constants;
2148:   /* Geometry */
2149:   PetscFEGeom       *cgeomFEM;
2150:   DM                 dmGrad;
2151:   PetscQuadrature    affineQuad      = NULL;
2152:   Vec                cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL;
2153:   PetscFVCellGeom   *cgeomFVM;
2154:   const PetscScalar *lgrad;
2155:   PetscInt           maxDegree;
2156:   DMField            coordField;
2157:   IS                 cellIS;

2159:   PetscFunctionBegin;
2160:   PetscCall(DMGetDS(dm, &prob));
2161:   PetscCall(DMGetDimension(dm, &dim));
2162:   PetscCall(DMGetLocalSection(dm, &section));
2163:   PetscCall(DMGetNumFields(dm, &Nf));
2164:   /* Determine which discretizations we have */
2165:   for (f = 0; f < Nf; ++f) {
2166:     PetscObject  obj;
2167:     PetscClassId id;

2169:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2170:     PetscCall(PetscObjectGetClassId(obj, &id));
2171:     if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE;
2172:   }
2173:   /* Get local solution with boundary values */
2174:   PetscCall(DMGetLocalVector(dm, &locX));
2175:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2176:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2177:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2178:   /* Read DS information */
2179:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2180:   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2181:   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2182:   PetscCall(ISCreateStride(PETSC_COMM_SELF, numCells, cStart, 1, &cellIS));
2183:   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2184:   /* Read Auxiliary DS information */
2185:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2186:   if (locA) {
2187:     PetscCall(VecGetDM(locA, &dmAux));
2188:     PetscCall(DMGetDS(dmAux, &probAux));
2189:     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2190:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
2191:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2192:     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2193:   }
2194:   /* Allocate data  arrays */
2195:   PetscCall(PetscCalloc1(numCells * totDim, &u));
2196:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
2197:   /* Read out geometry */
2198:   PetscCall(DMGetCoordinateField(dm, &coordField));
2199:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
2200:   if (maxDegree <= 1) {
2201:     PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
2202:     if (affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &cgeomFEM));
2203:   }
2204:   if (useFVM) {
2205:     PetscFV   fv = NULL;
2206:     Vec       grad;
2207:     PetscInt  fStart, fEnd;
2208:     PetscBool compGrad;

2210:     for (f = 0; f < Nf; ++f) {
2211:       PetscObject  obj;
2212:       PetscClassId id;

2214:       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2215:       PetscCall(PetscObjectGetClassId(obj, &id));
2216:       if (id == PETSCFV_CLASSID) {
2217:         fv = (PetscFV)obj;
2218:         break;
2219:       }
2220:     }
2221:     PetscCall(PetscFVGetComputeGradients(fv, &compGrad));
2222:     PetscCall(PetscFVSetComputeGradients(fv, PETSC_TRUE));
2223:     PetscCall(DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM));
2224:     PetscCall(DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad));
2225:     PetscCall(PetscFVSetComputeGradients(fv, compGrad));
2226:     PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2227:     /* Reconstruct and limit cell gradients */
2228:     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
2229:     PetscCall(DMGetGlobalVector(dmGrad, &grad));
2230:     PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
2231:     /* Communicate gradient values */
2232:     PetscCall(DMGetLocalVector(dmGrad, &locGrad));
2233:     PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
2234:     PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
2235:     PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
2236:     /* Handle non-essential (e.g. outflow) boundary values */
2237:     PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad));
2238:     PetscCall(VecGetArrayRead(locGrad, &lgrad));
2239:   }
2240:   /* Read out data from inputs */
2241:   for (c = cStart; c < cEnd; ++c) {
2242:     PetscScalar *x = NULL;
2243:     PetscInt     i;

2245:     PetscCall(DMPlexVecGetClosure(dm, section, locX, c, NULL, &x));
2246:     for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
2247:     PetscCall(DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x));
2248:     if (dmAux) {
2249:       PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, locA, c, NULL, &x));
2250:       for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
2251:       PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, locA, c, NULL, &x));
2252:     }
2253:   }
2254:   /* Do integration for each field */
2255:   for (f = 0; f < Nf; ++f) {
2256:     PetscObject  obj;
2257:     PetscClassId id;
2258:     PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

2260:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2261:     PetscCall(PetscObjectGetClassId(obj, &id));
2262:     if (id == PETSCFE_CLASSID) {
2263:       PetscFE         fe = (PetscFE)obj;
2264:       PetscQuadrature q;
2265:       PetscFEGeom    *chunkGeom = NULL;
2266:       PetscInt        Nq, Nb;

2268:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2269:       PetscCall(PetscFEGetQuadrature(fe, &q));
2270:       PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2271:       PetscCall(PetscFEGetDimension(fe, &Nb));
2272:       blockSize = Nb * Nq;
2273:       batchSize = numBlocks * blockSize;
2274:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2275:       numChunks = numCells / (numBatches * batchSize);
2276:       Ne        = numChunks * numBatches * batchSize;
2277:       Nr        = numCells % (numBatches * batchSize);
2278:       offset    = numCells - Nr;
2279:       if (!affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, q, PETSC_FALSE, &cgeomFEM));
2280:       PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
2281:       PetscCall(PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral));
2282:       PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &chunkGeom));
2283:       PetscCall(PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset * totDim], probAux, &a[offset * totDimAux], &cintegral[offset * Nf]));
2284:       PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &chunkGeom));
2285:       if (!affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2286:     } else if (id == PETSCFV_CLASSID) {
2287:       PetscInt       foff;
2288:       PetscPointFunc obj_func;
2289:       PetscScalar    lint;

2291:       PetscCall(PetscDSGetObjective(prob, f, &obj_func));
2292:       PetscCall(PetscDSGetFieldOffset(prob, f, &foff));
2293:       if (obj_func) {
2294:         for (c = 0; c < numCells; ++c) {
2295:           PetscScalar *u_x;

2297:           PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x));
2298:           obj_func(dim, Nf, NfAux, uOff, uOff_x, &u[totDim * c + foff], NULL, u_x, aOff, NULL, &a[totDimAux * c], NULL, NULL, 0.0, cgeomFVM[c].centroid, numConstants, constants, &lint);
2299:           cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume;
2300:         }
2301:       }
2302:     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
2303:   }
2304:   /* Cleanup data arrays */
2305:   if (useFVM) {
2306:     PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
2307:     PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
2308:     PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
2309:     PetscCall(VecDestroy(&faceGeometryFVM));
2310:     PetscCall(VecDestroy(&cellGeometryFVM));
2311:     PetscCall(DMDestroy(&dmGrad));
2312:   }
2313:   if (dmAux) PetscCall(PetscFree(a));
2314:   PetscCall(PetscFree(u));
2315:   /* Cleanup */
2316:   if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM));
2317:   PetscCall(PetscQuadratureDestroy(&affineQuad));
2318:   PetscCall(ISDestroy(&cellIS));
2319:   PetscCall(DMRestoreLocalVector(dm, &locX));
2320:   PetscFunctionReturn(PETSC_SUCCESS);
2321: }

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

2326:   Input Parameters:
2327: + dm   - The mesh
2328: . X    - Global input vector
2329: - user - The user context

2331:   Output Parameter:
2332: . integral - Integral for each field

2334:   Level: developer

2336: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2337: @*/
2338: PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user)
2339: {
2340:   DM_Plex     *mesh = (DM_Plex *)dm->data;
2341:   PetscScalar *cintegral, *lintegral;
2342:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;

2344:   PetscFunctionBegin;
2347:   PetscAssertPointer(integral, 3);
2348:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2349:   PetscCall(DMGetNumFields(dm, &Nf));
2350:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2351:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2352:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2353:   PetscCall(PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral));
2354:   PetscCall(DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user));
2355:   /* Sum up values */
2356:   for (cell = cStart; cell < cEnd; ++cell) {
2357:     const PetscInt c = cell - cStart;

2359:     if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2360:     for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f];
2361:   }
2362:   PetscCall(MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm)));
2363:   if (mesh->printFEM) {
2364:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:"));
2365:     for (f = 0; f < Nf; ++f) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f])));
2366:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "\n"));
2367:   }
2368:   PetscCall(PetscFree2(lintegral, cintegral));
2369:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2370:   PetscFunctionReturn(PETSC_SUCCESS);
2371: }

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

2376:   Input Parameters:
2377: + dm   - The mesh
2378: . X    - Global input vector
2379: - user - The user context

2381:   Output Parameter:
2382: . F - Cellwise integrals for each field

2384:   Level: developer

2386: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()`
2387: @*/
2388: PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user)
2389: {
2390:   DM_Plex     *mesh = (DM_Plex *)dm->data;
2391:   DM           dmF;
2392:   PetscSection sectionF;
2393:   PetscScalar *cintegral, *af;
2394:   PetscInt     Nf, f, cellHeight, cStart, cEnd, cell;

2396:   PetscFunctionBegin;
2400:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2401:   PetscCall(DMGetNumFields(dm, &Nf));
2402:   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
2403:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
2404:   /* TODO Introduce a loop over large chunks (right now this is a single chunk) */
2405:   PetscCall(PetscCalloc1((cEnd - cStart) * Nf, &cintegral));
2406:   PetscCall(DMPlexComputeIntegral_Internal(dm, X, cStart, cEnd, cintegral, user));
2407:   /* Put values in F*/
2408:   PetscCall(VecGetDM(F, &dmF));
2409:   PetscCall(DMGetLocalSection(dmF, &sectionF));
2410:   PetscCall(VecGetArray(F, &af));
2411:   for (cell = cStart; cell < cEnd; ++cell) {
2412:     const PetscInt c = cell - cStart;
2413:     PetscInt       dof, off;

2415:     if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf]));
2416:     PetscCall(PetscSectionGetDof(sectionF, cell, &dof));
2417:     PetscCall(PetscSectionGetOffset(sectionF, cell, &off));
2418:     PetscCheck(dof == Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %" PetscInt_FMT " != %" PetscInt_FMT, dof, Nf);
2419:     for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f];
2420:   }
2421:   PetscCall(VecRestoreArray(F, &af));
2422:   PetscCall(PetscFree(cintegral));
2423:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2424:   PetscFunctionReturn(PETSC_SUCCESS);
2425: }

2427: static PetscErrorCode DMPlexComputeBdIntegral_Internal(DM dm, Vec locX, IS pointIS, 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[]), PetscScalar *fintegral, void *user)
2428: {
2429:   DM                 plex = NULL, plexA = NULL;
2430:   DMEnclosureType    encAux;
2431:   PetscDS            prob, probAux       = NULL;
2432:   PetscSection       section, sectionAux = NULL;
2433:   Vec                locA = NULL;
2434:   DMField            coordField;
2435:   PetscInt           Nf, totDim, *uOff, *uOff_x;
2436:   PetscInt           NfAux = 0, totDimAux = 0, *aOff = NULL;
2437:   PetscScalar       *u, *a = NULL;
2438:   const PetscScalar *constants;
2439:   PetscInt           numConstants, f;

2441:   PetscFunctionBegin;
2442:   PetscCall(DMGetCoordinateField(dm, &coordField));
2443:   PetscCall(DMConvert(dm, DMPLEX, &plex));
2444:   PetscCall(DMGetDS(dm, &prob));
2445:   PetscCall(DMGetLocalSection(dm, &section));
2446:   PetscCall(PetscSectionGetNumFields(section, &Nf));
2447:   /* Determine which discretizations we have */
2448:   for (f = 0; f < Nf; ++f) {
2449:     PetscObject  obj;
2450:     PetscClassId id;

2452:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
2453:     PetscCall(PetscObjectGetClassId(obj, &id));
2454:     PetscCheck(id != PETSCFV_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported for FVM (field %" PetscInt_FMT ")", f);
2455:   }
2456:   /* Read DS information */
2457:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2458:   PetscCall(PetscDSGetComponentOffsets(prob, &uOff));
2459:   PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x));
2460:   PetscCall(PetscDSGetConstants(prob, &numConstants, &constants));
2461:   /* Read Auxiliary DS information */
2462:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
2463:   if (locA) {
2464:     DM dmAux;

2466:     PetscCall(VecGetDM(locA, &dmAux));
2467:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
2468:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
2469:     PetscCall(DMGetDS(dmAux, &probAux));
2470:     PetscCall(PetscDSGetNumFields(probAux, &NfAux));
2471:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
2472:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
2473:     PetscCall(PetscDSGetComponentOffsets(probAux, &aOff));
2474:   }
2475:   /* Integrate over points */
2476:   {
2477:     PetscFEGeom    *fgeom, *chunkGeom = NULL;
2478:     PetscInt        maxDegree;
2479:     PetscQuadrature qGeom = NULL;
2480:     const PetscInt *points;
2481:     PetscInt        numFaces, face, Nq, field;
2482:     PetscInt        numChunks, chunkSize, chunk, Nr, offset;

2484:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
2485:     PetscCall(ISGetIndices(pointIS, &points));
2486:     PetscCall(PetscCalloc2(numFaces * totDim, &u, locA ? numFaces * totDimAux : 0, &a));
2487:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
2488:     for (field = 0; field < Nf; ++field) {
2489:       PetscFE fe;

2491:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe));
2492:       if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
2493:       if (!qGeom) {
2494:         PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
2495:         PetscCall(PetscObjectReference((PetscObject)qGeom));
2496:       }
2497:       PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
2498:       PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
2499:       for (face = 0; face < numFaces; ++face) {
2500:         const PetscInt point = points[face], *support;
2501:         PetscScalar   *x     = NULL;
2502:         PetscInt       i;

2504:         PetscCall(DMPlexGetSupport(dm, point, &support));
2505:         PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
2506:         for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
2507:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
2508:         if (locA) {
2509:           PetscInt subp;
2510:           PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
2511:           PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
2512:           for (i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i];
2513:           PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
2514:         }
2515:       }
2516:       /* Get blocking */
2517:       {
2518:         PetscQuadrature q;
2519:         PetscInt        numBatches, batchSize, numBlocks, blockSize;
2520:         PetscInt        Nq, Nb;

2522:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
2523:         PetscCall(PetscFEGetQuadrature(fe, &q));
2524:         PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL));
2525:         PetscCall(PetscFEGetDimension(fe, &Nb));
2526:         blockSize = Nb * Nq;
2527:         batchSize = numBlocks * blockSize;
2528:         chunkSize = numBatches * batchSize;
2529:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
2530:         numChunks = numFaces / chunkSize;
2531:         Nr        = numFaces % chunkSize;
2532:         offset    = numFaces - Nr;
2533:       }
2534:       /* Do integration for each field */
2535:       for (chunk = 0; chunk < numChunks; ++chunk) {
2536:         PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom));
2537:         PetscCall(PetscFEIntegrateBd(prob, field, func, chunkSize, chunkGeom, u, probAux, a, fintegral));
2538:         PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
2539:       }
2540:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
2541:       PetscCall(PetscFEIntegrateBd(prob, field, func, Nr, chunkGeom, &u[offset * totDim], probAux, a ? &a[offset * totDimAux] : NULL, &fintegral[offset * Nf]));
2542:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
2543:       /* Cleanup data arrays */
2544:       PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
2545:       PetscCall(PetscQuadratureDestroy(&qGeom));
2546:       PetscCall(PetscFree2(u, a));
2547:       PetscCall(ISRestoreIndices(pointIS, &points));
2548:     }
2549:   }
2550:   if (plex) PetscCall(DMDestroy(&plex));
2551:   if (plexA) PetscCall(DMDestroy(&plexA));
2552:   PetscFunctionReturn(PETSC_SUCCESS);
2553: }

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

2558:   Input Parameters:
2559: + dm      - The mesh
2560: . X       - Global input vector
2561: . label   - The boundary `DMLabel`
2562: . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values
2563: . vals    - The label values to use, or NULL for all values
2564: . func    - The function to integrate along the boundary
2565: - user    - The user context

2567:   Output Parameter:
2568: . integral - Integral for each field

2570:   Level: developer

2572: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()`
2573: @*/
2574: PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[], 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[]), PetscScalar *integral, void *user)
2575: {
2576:   Vec          locX;
2577:   PetscSection section;
2578:   DMLabel      depthLabel;
2579:   IS           facetIS;
2580:   PetscInt     dim, Nf, f, v;

2582:   PetscFunctionBegin;
2586:   if (vals) PetscAssertPointer(vals, 5);
2587:   PetscAssertPointer(integral, 7);
2588:   PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2589:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
2590:   PetscCall(DMGetDimension(dm, &dim));
2591:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
2592:   PetscCall(DMGetLocalSection(dm, &section));
2593:   PetscCall(PetscSectionGetNumFields(section, &Nf));
2594:   /* Get local solution with boundary values */
2595:   PetscCall(DMGetLocalVector(dm, &locX));
2596:   PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL));
2597:   PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX));
2598:   PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX));
2599:   /* Loop over label values */
2600:   PetscCall(PetscArrayzero(integral, Nf));
2601:   for (v = 0; v < numVals; ++v) {
2602:     IS           pointIS;
2603:     PetscInt     numFaces, face;
2604:     PetscScalar *fintegral;

2606:     PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS));
2607:     if (!pointIS) continue; /* No points with that id on this process */
2608:     {
2609:       IS isectIS;

2611:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
2612:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
2613:       PetscCall(ISDestroy(&pointIS));
2614:       pointIS = isectIS;
2615:     }
2616:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
2617:     PetscCall(PetscCalloc1(numFaces * Nf, &fintegral));
2618:     PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, func, fintegral, user));
2619:     /* Sum point contributions into integral */
2620:     for (f = 0; f < Nf; ++f)
2621:       for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f];
2622:     PetscCall(PetscFree(fintegral));
2623:     PetscCall(ISDestroy(&pointIS));
2624:   }
2625:   PetscCall(DMRestoreLocalVector(dm, &locX));
2626:   PetscCall(ISDestroy(&facetIS));
2627:   PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0));
2628:   PetscFunctionReturn(PETSC_SUCCESS);
2629: }

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

2634:   Input Parameters:
2635: + dmc       - The coarse mesh
2636: . dmf       - The fine mesh
2637: . isRefined - Flag indicating regular refinement, rather than the same topology
2638: - user      - The user context

2640:   Output Parameter:
2641: . In - The interpolation matrix

2643:   Level: developer

2645: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
2646: @*/
2647: PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user)
2648: {
2649:   DM_Plex     *mesh = (DM_Plex *)dmc->data;
2650:   const char  *name = "Interpolator";
2651:   PetscFE     *feRef;
2652:   PetscFV     *fvRef;
2653:   PetscSection fsection, fglobalSection;
2654:   PetscSection csection, cglobalSection;
2655:   PetscScalar *elemMat;
2656:   PetscInt     dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c;
2657:   PetscInt     cTotDim = 0, rTotDim = 0;

2659:   PetscFunctionBegin;
2660:   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2661:   PetscCall(DMGetDimension(dmf, &dim));
2662:   PetscCall(DMGetLocalSection(dmf, &fsection));
2663:   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
2664:   PetscCall(DMGetLocalSection(dmc, &csection));
2665:   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
2666:   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
2667:   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
2668:   PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef));
2669:   for (f = 0; f < Nf; ++f) {
2670:     PetscObject  obj, objc;
2671:     PetscClassId id, idc;
2672:     PetscInt     rNb = 0, Nc = 0, cNb = 0;

2674:     PetscCall(DMGetField(dmf, f, NULL, &obj));
2675:     PetscCall(PetscObjectGetClassId(obj, &id));
2676:     if (id == PETSCFE_CLASSID) {
2677:       PetscFE fe = (PetscFE)obj;

2679:       if (isRefined) {
2680:         PetscCall(PetscFERefine(fe, &feRef[f]));
2681:       } else {
2682:         PetscCall(PetscObjectReference((PetscObject)fe));
2683:         feRef[f] = fe;
2684:       }
2685:       PetscCall(PetscFEGetDimension(feRef[f], &rNb));
2686:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
2687:     } else if (id == PETSCFV_CLASSID) {
2688:       PetscFV        fv = (PetscFV)obj;
2689:       PetscDualSpace Q;

2691:       if (isRefined) {
2692:         PetscCall(PetscFVRefine(fv, &fvRef[f]));
2693:       } else {
2694:         PetscCall(PetscObjectReference((PetscObject)fv));
2695:         fvRef[f] = fv;
2696:       }
2697:       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
2698:       PetscCall(PetscDualSpaceGetDimension(Q, &rNb));
2699:       PetscCall(PetscFVGetDualSpace(fv, &Q));
2700:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
2701:     }
2702:     PetscCall(DMGetField(dmc, f, NULL, &objc));
2703:     PetscCall(PetscObjectGetClassId(objc, &idc));
2704:     if (idc == PETSCFE_CLASSID) {
2705:       PetscFE fe = (PetscFE)objc;

2707:       PetscCall(PetscFEGetDimension(fe, &cNb));
2708:     } else if (id == PETSCFV_CLASSID) {
2709:       PetscFV        fv = (PetscFV)obj;
2710:       PetscDualSpace Q;

2712:       PetscCall(PetscFVGetDualSpace(fv, &Q));
2713:       PetscCall(PetscDualSpaceGetDimension(Q, &cNb));
2714:     }
2715:     rTotDim += rNb;
2716:     cTotDim += cNb;
2717:   }
2718:   PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat));
2719:   PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim));
2720:   for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) {
2721:     PetscDualSpace   Qref;
2722:     PetscQuadrature  f;
2723:     const PetscReal *qpoints, *qweights;
2724:     PetscReal       *points;
2725:     PetscInt         npoints = 0, Nc, Np, fpdim, i, k, p, d;

2727:     /* Compose points from all dual basis functionals */
2728:     if (feRef[fieldI]) {
2729:       PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref));
2730:       PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc));
2731:     } else {
2732:       PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref));
2733:       PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc));
2734:     }
2735:     PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim));
2736:     for (i = 0; i < fpdim; ++i) {
2737:       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2738:       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL));
2739:       npoints += Np;
2740:     }
2741:     PetscCall(PetscMalloc1(npoints * dim, &points));
2742:     for (i = 0, k = 0; i < fpdim; ++i) {
2743:       PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2744:       PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL));
2745:       for (p = 0; p < Np; ++p, ++k)
2746:         for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d];
2747:     }

2749:     for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) {
2750:       PetscObject  obj;
2751:       PetscClassId id;
2752:       PetscInt     NcJ = 0, cpdim = 0, j, qNc;

2754:       PetscCall(DMGetField(dmc, fieldJ, NULL, &obj));
2755:       PetscCall(PetscObjectGetClassId(obj, &id));
2756:       if (id == PETSCFE_CLASSID) {
2757:         PetscFE         fe = (PetscFE)obj;
2758:         PetscTabulation T  = NULL;

2760:         /* Evaluate basis at points */
2761:         PetscCall(PetscFEGetNumComponents(fe, &NcJ));
2762:         PetscCall(PetscFEGetDimension(fe, &cpdim));
2763:         /* For now, fields only interpolate themselves */
2764:         if (fieldI == fieldJ) {
2765:           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);
2766:           PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T));
2767:           for (i = 0, k = 0; i < fpdim; ++i) {
2768:             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2769:             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
2770:             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);
2771:             for (p = 0; p < Np; ++p, ++k) {
2772:               for (j = 0; j < cpdim; ++j) {
2773:                 /*
2774:                    cTotDim:            Total columns in element interpolation matrix, sum of number of dual basis functionals in each field
2775:                    offsetI, offsetJ:   Offsets into the larger element interpolation matrix for different fields
2776:                    fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals
2777:                    qNC, Nc, Ncj, c:    Number of components in this field
2778:                    Np, p:              Number of quad points in the fine grid functional i
2779:                    k:                  i*Np + p, overall point number for the interpolation
2780:                 */
2781:                 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];
2782:               }
2783:             }
2784:           }
2785:           PetscCall(PetscTabulationDestroy(&T));
2786:         }
2787:       } else if (id == PETSCFV_CLASSID) {
2788:         PetscFV fv = (PetscFV)obj;

2790:         /* Evaluate constant function at points */
2791:         PetscCall(PetscFVGetNumComponents(fv, &NcJ));
2792:         cpdim = 1;
2793:         /* For now, fields only interpolate themselves */
2794:         if (fieldI == fieldJ) {
2795:           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);
2796:           for (i = 0, k = 0; i < fpdim; ++i) {
2797:             PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f));
2798:             PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights));
2799:             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);
2800:             for (p = 0; p < Np; ++p, ++k) {
2801:               for (j = 0; j < cpdim; ++j) {
2802:                 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c];
2803:               }
2804:             }
2805:           }
2806:         }
2807:       }
2808:       offsetJ += cpdim;
2809:     }
2810:     offsetI += fpdim;
2811:     PetscCall(PetscFree(points));
2812:   }
2813:   if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat));
2814:   /* Preallocate matrix */
2815:   {
2816:     Mat          preallocator;
2817:     PetscScalar *vals;
2818:     PetscInt    *cellCIndices, *cellFIndices;
2819:     PetscInt     locRows, locCols, cell;

2821:     PetscCall(MatGetLocalSize(In, &locRows, &locCols));
2822:     PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator));
2823:     PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
2824:     PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
2825:     PetscCall(MatSetUp(preallocator));
2826:     PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices));
2827:     for (cell = cStart; cell < cEnd; ++cell) {
2828:       if (isRefined) {
2829:         PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices));
2830:         PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES));
2831:       } else {
2832:         PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, preallocator, cell, vals, INSERT_VALUES));
2833:       }
2834:     }
2835:     PetscCall(PetscFree3(vals, cellCIndices, cellFIndices));
2836:     PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
2837:     PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
2838:     PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In));
2839:     PetscCall(MatDestroy(&preallocator));
2840:   }
2841:   /* Fill matrix */
2842:   PetscCall(MatZeroEntries(In));
2843:   for (c = cStart; c < cEnd; ++c) {
2844:     if (isRefined) {
2845:       PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES));
2846:     } else {
2847:       PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, In, c, elemMat, INSERT_VALUES));
2848:     }
2849:   }
2850:   for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f]));
2851:   PetscCall(PetscFree2(feRef, fvRef));
2852:   PetscCall(PetscFree(elemMat));
2853:   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
2854:   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
2855:   if (mesh->printFEM > 1) {
2856:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name));
2857:     PetscCall(MatFilter(In, 1.0e-10, PETSC_FALSE, PETSC_FALSE));
2858:     PetscCall(MatView(In, NULL));
2859:   }
2860:   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2861:   PetscFunctionReturn(PETSC_SUCCESS);
2862: }

2864: PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user)
2865: {
2866:   SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness");
2867: }

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

2872:   Input Parameters:
2873: + dmf  - The fine mesh
2874: . dmc  - The coarse mesh
2875: - user - The user context

2877:   Output Parameter:
2878: . In - The interpolation matrix

2880:   Level: developer

2882: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
2883: @*/
2884: PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user)
2885: {
2886:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
2887:   const char  *name = "Interpolator";
2888:   PetscDS      prob;
2889:   Mat          interp;
2890:   PetscSection fsection, globalFSection;
2891:   PetscSection csection, globalCSection;
2892:   PetscInt     locRows, locCols;
2893:   PetscReal   *x, *v0, *J, *invJ, detJ;
2894:   PetscReal   *v0c, *Jc, *invJc, detJc;
2895:   PetscScalar *elemMat;
2896:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s;

2898:   PetscFunctionBegin;
2899:   PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
2900:   PetscCall(DMGetCoordinateDim(dmc, &dim));
2901:   PetscCall(DMGetDS(dmc, &prob));
2902:   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
2903:   PetscCall(PetscDSGetNumFields(prob, &Nf));
2904:   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
2905:   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
2906:   PetscCall(DMGetLocalSection(dmf, &fsection));
2907:   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
2908:   PetscCall(DMGetLocalSection(dmc, &csection));
2909:   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
2910:   PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd));
2911:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
2912:   PetscCall(PetscMalloc1(totDim, &elemMat));

2914:   PetscCall(MatGetLocalSize(In, &locRows, &locCols));
2915:   PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp));
2916:   PetscCall(MatSetType(interp, MATPREALLOCATOR));
2917:   PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE));
2918:   PetscCall(MatSetUp(interp));
2919:   for (s = 0; s < 2; ++s) {
2920:     for (field = 0; field < Nf; ++field) {
2921:       PetscObject      obj;
2922:       PetscClassId     id;
2923:       PetscDualSpace   Q = NULL;
2924:       PetscTabulation  T = NULL;
2925:       PetscQuadrature  f;
2926:       const PetscReal *qpoints, *qweights;
2927:       PetscInt         Nc, qNc, Np, fpdim, off, i, d;

2929:       PetscCall(PetscDSGetFieldOffset(prob, field, &off));
2930:       PetscCall(PetscDSGetDiscretization(prob, field, &obj));
2931:       PetscCall(PetscObjectGetClassId(obj, &id));
2932:       if (id == PETSCFE_CLASSID) {
2933:         PetscFE fe = (PetscFE)obj;

2935:         PetscCall(PetscFEGetDualSpace(fe, &Q));
2936:         PetscCall(PetscFEGetNumComponents(fe, &Nc));
2937:         if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T));
2938:       } else if (id == PETSCFV_CLASSID) {
2939:         PetscFV fv = (PetscFV)obj;

2941:         PetscCall(PetscFVGetDualSpace(fv, &Q));
2942:         Nc = 1;
2943:       } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field);
2944:       PetscCall(PetscDualSpaceGetDimension(Q, &fpdim));
2945:       /* For each fine grid cell */
2946:       for (cell = cStart; cell < cEnd; ++cell) {
2947:         PetscInt *findices, *cindices;
2948:         PetscInt  numFIndices, numCIndices;

2950:         PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
2951:         PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
2952:         PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim);
2953:         for (i = 0; i < fpdim; ++i) {
2954:           Vec                pointVec;
2955:           PetscScalar       *pV;
2956:           PetscSF            coarseCellSF = NULL;
2957:           const PetscSFNode *coarseCells;
2958:           PetscInt           numCoarseCells, cpdim, row = findices[i + off], q, c, j;

2960:           /* Get points from the dual basis functional quadrature */
2961:           PetscCall(PetscDualSpaceGetFunctional(Q, i, &f));
2962:           PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights));
2963:           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);
2964:           PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec));
2965:           PetscCall(VecSetBlockSize(pointVec, dim));
2966:           PetscCall(VecGetArray(pointVec, &pV));
2967:           for (q = 0; q < Np; ++q) {
2968:             const PetscReal xi0[3] = {-1., -1., -1.};

2970:             /* Transform point to real space */
2971:             CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
2972:             for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
2973:           }
2974:           PetscCall(VecRestoreArray(pointVec, &pV));
2975:           /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
2976:           /* OPT: Read this out from preallocation information */
2977:           PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
2978:           /* Update preallocation info */
2979:           PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
2980:           PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
2981:           PetscCall(VecGetArray(pointVec, &pV));
2982:           for (ccell = 0; ccell < numCoarseCells; ++ccell) {
2983:             PetscReal       pVReal[3];
2984:             const PetscReal xi0[3] = {-1., -1., -1.};

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

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

2996:               if (id == PETSCFE_CLASSID) {
2997:                 /* Evaluate coarse basis on contained point */
2998:                 PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T));
2999:                 PetscCall(PetscArrayzero(elemMat, cpdim));
3000:                 /* Get elemMat entries by multiplying by weight */
3001:                 for (j = 0; j < cpdim; ++j) {
3002:                   for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c];
3003:                 }
3004:               } else {
3005:                 for (j = 0; j < cpdim; ++j) {
3006:                   for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c];
3007:                 }
3008:               }
3009:               if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3010:             }
3011:             /* Update interpolator */
3012:             PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim);
3013:             PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES));
3014:             PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3015:           }
3016:           PetscCall(VecRestoreArray(pointVec, &pV));
3017:           PetscCall(PetscSFDestroy(&coarseCellSF));
3018:           PetscCall(VecDestroy(&pointVec));
3019:         }
3020:         PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3021:       }
3022:       if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3023:     }
3024:     if (!s) {
3025:       PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY));
3026:       PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY));
3027:       PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In));
3028:       PetscCall(MatDestroy(&interp));
3029:       interp = In;
3030:     }
3031:   }
3032:   PetscCall(PetscFree3(v0, J, invJ));
3033:   PetscCall(PetscFree3(v0c, Jc, invJc));
3034:   PetscCall(PetscFree(elemMat));
3035:   PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY));
3036:   PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY));
3037:   PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0));
3038:   PetscFunctionReturn(PETSC_SUCCESS);
3039: }

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

3044:   Input Parameters:
3045: + dmf  - The fine mesh
3046: . dmc  - The coarse mesh
3047: - user - The user context

3049:   Output Parameter:
3050: . mass - The mass matrix

3052:   Level: developer

3054: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()`
3055: @*/
3056: PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user)
3057: {
3058:   DM_Plex     *mesh = (DM_Plex *)dmf->data;
3059:   const char  *name = "Mass Matrix";
3060:   PetscDS      prob;
3061:   PetscSection fsection, csection, globalFSection, globalCSection;
3062:   PetscHSetIJ  ht;
3063:   PetscLayout  rLayout;
3064:   PetscInt    *dnz, *onz;
3065:   PetscInt     locRows, rStart, rEnd;
3066:   PetscReal   *x, *v0, *J, *invJ, detJ;
3067:   PetscReal   *v0c, *Jc, *invJc, detJc;
3068:   PetscScalar *elemMat;
3069:   PetscInt     dim, Nf, field, totDim, cStart, cEnd, cell, ccell;

3071:   PetscFunctionBegin;
3072:   PetscCall(DMGetCoordinateDim(dmc, &dim));
3073:   PetscCall(DMGetDS(dmc, &prob));
3074:   PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL));
3075:   PetscCall(PetscDSGetNumFields(prob, &Nf));
3076:   PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ));
3077:   PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc));
3078:   PetscCall(DMGetLocalSection(dmf, &fsection));
3079:   PetscCall(DMGetGlobalSection(dmf, &globalFSection));
3080:   PetscCall(DMGetLocalSection(dmc, &csection));
3081:   PetscCall(DMGetGlobalSection(dmc, &globalCSection));
3082:   PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd));
3083:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3084:   PetscCall(PetscMalloc1(totDim, &elemMat));

3086:   PetscCall(MatGetLocalSize(mass, &locRows, NULL));
3087:   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout));
3088:   PetscCall(PetscLayoutSetLocalSize(rLayout, locRows));
3089:   PetscCall(PetscLayoutSetBlockSize(rLayout, 1));
3090:   PetscCall(PetscLayoutSetUp(rLayout));
3091:   PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd));
3092:   PetscCall(PetscLayoutDestroy(&rLayout));
3093:   PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz));
3094:   PetscCall(PetscHSetIJCreate(&ht));
3095:   for (field = 0; field < Nf; ++field) {
3096:     PetscObject      obj;
3097:     PetscClassId     id;
3098:     PetscQuadrature  quad;
3099:     const PetscReal *qpoints;
3100:     PetscInt         Nq, Nc, i, d;

3102:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3103:     PetscCall(PetscObjectGetClassId(obj, &id));
3104:     if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3105:     else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3106:     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL));
3107:     /* For each fine grid cell */
3108:     for (cell = cStart; cell < cEnd; ++cell) {
3109:       Vec                pointVec;
3110:       PetscScalar       *pV;
3111:       PetscSF            coarseCellSF = NULL;
3112:       const PetscSFNode *coarseCells;
3113:       PetscInt           numCoarseCells, q, c;
3114:       PetscInt          *findices, *cindices;
3115:       PetscInt           numFIndices, numCIndices;

3117:       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3118:       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3119:       /* Get points from the quadrature */
3120:       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3121:       PetscCall(VecSetBlockSize(pointVec, dim));
3122:       PetscCall(VecGetArray(pointVec, &pV));
3123:       for (q = 0; q < Nq; ++q) {
3124:         const PetscReal xi0[3] = {-1., -1., -1.};

3126:         /* Transform point to real space */
3127:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3128:         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3129:       }
3130:       PetscCall(VecRestoreArray(pointVec, &pV));
3131:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3132:       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3133:       PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view"));
3134:       /* Update preallocation info */
3135:       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3136:       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3137:       {
3138:         PetscHashIJKey key;
3139:         PetscBool      missing;

3141:         for (i = 0; i < numFIndices; ++i) {
3142:           key.i = findices[i];
3143:           if (key.i >= 0) {
3144:             /* Get indices for coarse elements */
3145:             for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3146:               PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3147:               for (c = 0; c < numCIndices; ++c) {
3148:                 key.j = cindices[c];
3149:                 if (key.j < 0) continue;
3150:                 PetscCall(PetscHSetIJQueryAdd(ht, key, &missing));
3151:                 if (missing) {
3152:                   if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart];
3153:                   else ++onz[key.i - rStart];
3154:                 }
3155:               }
3156:               PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3157:             }
3158:           }
3159:         }
3160:       }
3161:       PetscCall(PetscSFDestroy(&coarseCellSF));
3162:       PetscCall(VecDestroy(&pointVec));
3163:       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3164:     }
3165:   }
3166:   PetscCall(PetscHSetIJDestroy(&ht));
3167:   PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL));
3168:   PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
3169:   PetscCall(PetscFree2(dnz, onz));
3170:   for (field = 0; field < Nf; ++field) {
3171:     PetscObject      obj;
3172:     PetscClassId     id;
3173:     PetscTabulation  T, Tfine;
3174:     PetscQuadrature  quad;
3175:     const PetscReal *qpoints, *qweights;
3176:     PetscInt         Nq, Nc, i, d;

3178:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
3179:     PetscCall(PetscObjectGetClassId(obj, &id));
3180:     if (id == PETSCFE_CLASSID) {
3181:       PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad));
3182:       PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine));
3183:       PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T));
3184:     } else {
3185:       PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad));
3186:     }
3187:     PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights));
3188:     /* For each fine grid cell */
3189:     for (cell = cStart; cell < cEnd; ++cell) {
3190:       Vec                pointVec;
3191:       PetscScalar       *pV;
3192:       PetscSF            coarseCellSF = NULL;
3193:       const PetscSFNode *coarseCells;
3194:       PetscInt           numCoarseCells, cpdim, q, c, j;
3195:       PetscInt          *findices, *cindices;
3196:       PetscInt           numFIndices, numCIndices;

3198:       PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3199:       PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ));
3200:       /* Get points from the quadrature */
3201:       PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec));
3202:       PetscCall(VecSetBlockSize(pointVec, dim));
3203:       PetscCall(VecGetArray(pointVec, &pV));
3204:       for (q = 0; q < Nq; ++q) {
3205:         const PetscReal xi0[3] = {-1., -1., -1.};

3207:         /* Transform point to real space */
3208:         CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x);
3209:         for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d];
3210:       }
3211:       PetscCall(VecRestoreArray(pointVec, &pV));
3212:       /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */
3213:       PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF));
3214:       /* Update matrix */
3215:       PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells));
3216:       PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located");
3217:       PetscCall(VecGetArray(pointVec, &pV));
3218:       for (ccell = 0; ccell < numCoarseCells; ++ccell) {
3219:         PetscReal       pVReal[3];
3220:         const PetscReal xi0[3] = {-1., -1., -1.};

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

3228:         if (id == PETSCFE_CLASSID) {
3229:           PetscFE fe = (PetscFE)obj;

3231:           /* Evaluate coarse basis on contained point */
3232:           PetscCall(PetscFEGetDimension(fe, &cpdim));
3233:           PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T));
3234:           /* Get elemMat entries by multiplying by weight */
3235:           for (i = 0; i < numFIndices; ++i) {
3236:             PetscCall(PetscArrayzero(elemMat, cpdim));
3237:             for (j = 0; j < cpdim; ++j) {
3238:               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;
3239:             }
3240:             /* Update interpolator */
3241:             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3242:             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3243:             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3244:           }
3245:         } else {
3246:           cpdim = 1;
3247:           for (i = 0; i < numFIndices; ++i) {
3248:             PetscCall(PetscArrayzero(elemMat, cpdim));
3249:             for (j = 0; j < cpdim; ++j) {
3250:               for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ;
3251:             }
3252:             /* Update interpolator */
3253:             if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat));
3254:             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));
3255:             PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim);
3256:             PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES));
3257:           }
3258:         }
3259:         PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL));
3260:       }
3261:       PetscCall(VecRestoreArray(pointVec, &pV));
3262:       PetscCall(PetscSFDestroy(&coarseCellSF));
3263:       PetscCall(VecDestroy(&pointVec));
3264:       PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL));
3265:     }
3266:     if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T));
3267:   }
3268:   PetscCall(PetscFree3(v0, J, invJ));
3269:   PetscCall(PetscFree3(v0c, Jc, invJc));
3270:   PetscCall(PetscFree(elemMat));
3271:   PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY));
3272:   PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY));
3273:   PetscFunctionReturn(PETSC_SUCCESS);
3274: }

3276: /*@
3277:   DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns

3279:   Input Parameters:
3280: + dmc  - The coarse mesh
3281: . dmf  - The fine mesh
3282: - user - The user context

3284:   Output Parameter:
3285: . sc - The mapping

3287:   Level: developer

3289: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()`
3290: @*/
3291: PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user)
3292: {
3293:   PetscDS      prob;
3294:   PetscFE     *feRef;
3295:   PetscFV     *fvRef;
3296:   Vec          fv, cv;
3297:   IS           fis, cis;
3298:   PetscSection fsection, fglobalSection, csection, cglobalSection;
3299:   PetscInt    *cmap, *cellCIndices, *cellFIndices, *cindices, *findices;
3300:   PetscInt     cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m;
3301:   PetscBool   *needAvg;

3303:   PetscFunctionBegin;
3304:   PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3305:   PetscCall(DMGetDimension(dmf, &dim));
3306:   PetscCall(DMGetLocalSection(dmf, &fsection));
3307:   PetscCall(DMGetGlobalSection(dmf, &fglobalSection));
3308:   PetscCall(DMGetLocalSection(dmc, &csection));
3309:   PetscCall(DMGetGlobalSection(dmc, &cglobalSection));
3310:   PetscCall(PetscSectionGetNumFields(fsection, &Nf));
3311:   PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd));
3312:   PetscCall(DMGetDS(dmc, &prob));
3313:   PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg));
3314:   for (f = 0; f < Nf; ++f) {
3315:     PetscObject  obj;
3316:     PetscClassId id;
3317:     PetscInt     fNb = 0, Nc = 0;

3319:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3320:     PetscCall(PetscObjectGetClassId(obj, &id));
3321:     if (id == PETSCFE_CLASSID) {
3322:       PetscFE    fe = (PetscFE)obj;
3323:       PetscSpace sp;
3324:       PetscInt   maxDegree;

3326:       PetscCall(PetscFERefine(fe, &feRef[f]));
3327:       PetscCall(PetscFEGetDimension(feRef[f], &fNb));
3328:       PetscCall(PetscFEGetNumComponents(fe, &Nc));
3329:       PetscCall(PetscFEGetBasisSpace(fe, &sp));
3330:       PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree));
3331:       if (!maxDegree) needAvg[f] = PETSC_TRUE;
3332:     } else if (id == PETSCFV_CLASSID) {
3333:       PetscFV        fv = (PetscFV)obj;
3334:       PetscDualSpace Q;

3336:       PetscCall(PetscFVRefine(fv, &fvRef[f]));
3337:       PetscCall(PetscFVGetDualSpace(fvRef[f], &Q));
3338:       PetscCall(PetscDualSpaceGetDimension(Q, &fNb));
3339:       PetscCall(PetscFVGetNumComponents(fv, &Nc));
3340:       needAvg[f] = PETSC_TRUE;
3341:     }
3342:     fTotDim += fNb;
3343:   }
3344:   PetscCall(PetscDSGetTotalDimension(prob, &cTotDim));
3345:   PetscCall(PetscMalloc1(cTotDim, &cmap));
3346:   for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) {
3347:     PetscFE        feC;
3348:     PetscFV        fvC;
3349:     PetscDualSpace QF, QC;
3350:     PetscInt       order = -1, NcF, NcC, fpdim, cpdim;

3352:     if (feRef[field]) {
3353:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC));
3354:       PetscCall(PetscFEGetNumComponents(feC, &NcC));
3355:       PetscCall(PetscFEGetNumComponents(feRef[field], &NcF));
3356:       PetscCall(PetscFEGetDualSpace(feRef[field], &QF));
3357:       PetscCall(PetscDualSpaceGetOrder(QF, &order));
3358:       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3359:       PetscCall(PetscFEGetDualSpace(feC, &QC));
3360:       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3361:     } else {
3362:       PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC));
3363:       PetscCall(PetscFVGetNumComponents(fvC, &NcC));
3364:       PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF));
3365:       PetscCall(PetscFVGetDualSpace(fvRef[field], &QF));
3366:       PetscCall(PetscDualSpaceGetDimension(QF, &fpdim));
3367:       PetscCall(PetscFVGetDualSpace(fvC, &QC));
3368:       PetscCall(PetscDualSpaceGetDimension(QC, &cpdim));
3369:     }
3370:     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);
3371:     for (c = 0; c < cpdim; ++c) {
3372:       PetscQuadrature  cfunc;
3373:       const PetscReal *cqpoints, *cqweights;
3374:       PetscInt         NqcC, NpC;
3375:       PetscBool        found = PETSC_FALSE;

3377:       PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc));
3378:       PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights));
3379:       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);
3380:       PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments");
3381:       for (f = 0; f < fpdim; ++f) {
3382:         PetscQuadrature  ffunc;
3383:         const PetscReal *fqpoints, *fqweights;
3384:         PetscReal        sum = 0.0;
3385:         PetscInt         NqcF, NpF;

3387:         PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc));
3388:         PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights));
3389:         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);
3390:         if (NpC != NpF) continue;
3391:         for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]);
3392:         if (sum > 1.0e-9) continue;
3393:         for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]);
3394:         if (sum < 1.0e-9) continue;
3395:         cmap[offsetC + c] = offsetF + f;
3396:         found             = PETSC_TRUE;
3397:         break;
3398:       }
3399:       if (!found) {
3400:         /* TODO We really want the average here, but some asshole put VecScatter in the interface */
3401:         if (fvRef[field] || (feRef[field] && order == 0)) {
3402:           cmap[offsetC + c] = offsetF + 0;
3403:         } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection");
3404:       }
3405:     }
3406:     offsetC += cpdim;
3407:     offsetF += fpdim;
3408:   }
3409:   for (f = 0; f < Nf; ++f) {
3410:     PetscCall(PetscFEDestroy(&feRef[f]));
3411:     PetscCall(PetscFVDestroy(&fvRef[f]));
3412:   }
3413:   PetscCall(PetscFree3(feRef, fvRef, needAvg));

3415:   PetscCall(DMGetGlobalVector(dmf, &fv));
3416:   PetscCall(DMGetGlobalVector(dmc, &cv));
3417:   PetscCall(VecGetOwnershipRange(cv, &startC, &endC));
3418:   PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m));
3419:   PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices));
3420:   PetscCall(PetscMalloc1(m, &cindices));
3421:   PetscCall(PetscMalloc1(m, &findices));
3422:   for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1;
3423:   for (c = cStart; c < cEnd; ++c) {
3424:     PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices));
3425:     for (d = 0; d < cTotDim; ++d) {
3426:       if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue;
3427:       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]]);
3428:       cindices[cellCIndices[d] - startC] = cellCIndices[d];
3429:       findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]];
3430:     }
3431:   }
3432:   PetscCall(PetscFree(cmap));
3433:   PetscCall(PetscFree2(cellCIndices, cellFIndices));

3435:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis));
3436:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis));
3437:   PetscCall(VecScatterCreate(cv, cis, fv, fis, sc));
3438:   PetscCall(ISDestroy(&cis));
3439:   PetscCall(ISDestroy(&fis));
3440:   PetscCall(DMRestoreGlobalVector(dmf, &fv));
3441:   PetscCall(DMRestoreGlobalVector(dmc, &cv));
3442:   PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0));
3443:   PetscFunctionReturn(PETSC_SUCCESS);
3444: }

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

3449:   Input Parameters:
3450: + dm     - The `DM`
3451: . cellIS - The cells to include
3452: . locX   - A local vector with the solution fields
3453: . locX_t - A local vector with solution field time derivatives, or NULL
3454: - locA   - A local vector with auxiliary fields, or NULL

3456:   Output Parameters:
3457: + u   - The field coefficients
3458: . u_t - The fields derivative coefficients
3459: - a   - The auxiliary field coefficients

3461:   Level: developer

3463: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3464: @*/
3465: PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3466: {
3467:   DM              plex, plexA = NULL;
3468:   DMEnclosureType encAux;
3469:   PetscSection    section, sectionAux;
3470:   PetscDS         prob;
3471:   const PetscInt *cells;
3472:   PetscInt        cStart, cEnd, numCells, totDim, totDimAux, c;

3474:   PetscFunctionBegin;
3479:   PetscAssertPointer(u, 6);
3480:   PetscAssertPointer(u_t, 7);
3481:   PetscAssertPointer(a, 8);
3482:   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3483:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3484:   PetscCall(DMGetLocalSection(dm, &section));
3485:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
3486:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
3487:   if (locA) {
3488:     DM      dmAux;
3489:     PetscDS probAux;

3491:     PetscCall(VecGetDM(locA, &dmAux));
3492:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3493:     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3494:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
3495:     PetscCall(DMGetDS(dmAux, &probAux));
3496:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3497:   }
3498:   numCells = cEnd - cStart;
3499:   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3500:   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3501:   else *u_t = NULL;
3502:   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3503:   else *a = NULL;
3504:   for (c = cStart; c < cEnd; ++c) {
3505:     const PetscInt cell = cells ? cells[c] : c;
3506:     const PetscInt cind = c - cStart;
3507:     PetscScalar   *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a;
3508:     PetscInt       i;

3510:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x));
3511:     for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i];
3512:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x));
3513:     if (locX_t) {
3514:       PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t));
3515:       for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i];
3516:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t));
3517:     }
3518:     if (locA) {
3519:       PetscInt subcell;
3520:       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3521:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3522:       for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i];
3523:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x));
3524:     }
3525:   }
3526:   PetscCall(DMDestroy(&plex));
3527:   if (locA) PetscCall(DMDestroy(&plexA));
3528:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3529:   PetscFunctionReturn(PETSC_SUCCESS);
3530: }

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

3535:   Input Parameters:
3536: + dm     - The `DM`
3537: . cellIS - The cells to include
3538: . locX   - A local vector with the solution fields
3539: . locX_t - A local vector with solution field time derivatives, or NULL
3540: - locA   - A local vector with auxiliary fields, or NULL

3542:   Output Parameters:
3543: + u   - The field coefficients
3544: . u_t - The fields derivative coefficients
3545: - a   - The auxiliary field coefficients

3547:   Level: developer

3549: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3550: @*/
3551: PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3552: {
3553:   PetscFunctionBegin;
3554:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u));
3555:   if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t));
3556:   if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a));
3557:   PetscFunctionReturn(PETSC_SUCCESS);
3558: }

3560: static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a)
3561: {
3562:   DM              plex, plexA = NULL;
3563:   DMEnclosureType encAux;
3564:   PetscSection    section, sectionAux;
3565:   PetscDS         ds, dsIn;
3566:   const PetscInt *cells;
3567:   PetscInt        cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f;

3569:   PetscFunctionBegin;
3575:   PetscAssertPointer(u, 6);
3576:   PetscAssertPointer(u_t, 7);
3577:   PetscAssertPointer(a, 8);
3578:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3579:   numCells = cEnd - cStart;
3580:   PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE));
3581:   PetscCall(DMGetLocalSection(dm, &section));
3582:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
3583:   PetscCall(PetscDSGetNumFields(dsIn, &Nf));
3584:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDim));
3585:   if (locA) {
3586:     DM      dmAux;
3587:     PetscDS probAux;

3589:     PetscCall(VecGetDM(locA, &dmAux));
3590:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
3591:     PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE));
3592:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
3593:     PetscCall(DMGetDS(dmAux, &probAux));
3594:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
3595:   }
3596:   PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u));
3597:   if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t));
3598:   else {
3599:     *u_t = NULL;
3600:   }
3601:   if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a));
3602:   else {
3603:     *a = NULL;
3604:   }
3605:   // Loop over cohesive cells
3606:   for (c = cStart; c < cEnd; ++c) {
3607:     const PetscInt  cell = cells ? cells[c] : c;
3608:     const PetscInt  cind = c - cStart;
3609:     PetscScalar    *xf = NULL, *xc = NULL, *x = NULL, *xf_t = NULL, *xc_t = NULL;
3610:     PetscScalar    *ul = &(*u)[cind * totDim], *ul_t = u_t ? &(*u_t)[cind * totDim] : NULL;
3611:     const PetscInt *cone, *ornt;
3612:     PetscInt        Nx = 0, Nxf, s;

3614:     PetscCall(DMPlexGetCone(dm, cell, &cone));
3615:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3616:     // Put in cohesive unknowns
3617:     PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf));
3618:     if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t));
3619:     for (f = 0; f < Nf; ++f) {
3620:       PetscInt  fdofIn, foff, foffIn;
3621:       PetscBool cohesive;

3623:       PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3624:       if (!cohesive) continue;
3625:       PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3626:       PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff));
3627:       PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3628:       for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i];
3629:       if (locX_t)
3630:         for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + i] = xf_t[foff + i];
3631:       Nx += fdofIn;
3632:     }
3633:     PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf));
3634:     if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &xf_t));
3635:     // Loop over sides of surface
3636:     for (s = 0; s < 2; ++s) {
3637:       const PetscInt *support;
3638:       const PetscInt  face = cone[s];
3639:       PetscInt        ssize, ncell, Nxc;

3641:       // I don't think I need the face to have 0 orientation in the hybrid cell
3642:       //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]);
3643:       PetscCall(DMPlexGetSupport(dm, face, &support));
3644:       PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3645:       if (support[0] == cell) ncell = support[1];
3646:       else if (support[1] == cell) ncell = support[0];
3647:       else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3648:       // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields
3649:       PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc));
3650:       if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3651:       for (f = 0; f < Nf; ++f) {
3652:         PetscInt  fdofIn, foffIn;
3653:         PetscBool cohesive;

3655:         PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive));
3656:         if (cohesive) continue;
3657:         PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn));
3658:         PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn));
3659:         for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foffIn + i];
3660:         if (locX_t)
3661:           for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + s * fdofIn + i] = xc_t[foffIn + i];
3662:         Nx += fdofIn;
3663:       }
3664:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc));
3665:       if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, ncell, NULL, &xc_t));
3666:     }
3667:     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);

3669:     if (locA) {
3670:       PetscScalar *al = &(*a)[cind * totDimAux];
3671:       PetscInt     subcell;

3673:       PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell));
3674:       PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3675:       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);
3676:       for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i];
3677:       PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x));
3678:     }
3679:   }
3680:   PetscCall(DMDestroy(&plex));
3681:   PetscCall(DMDestroy(&plexA));
3682:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3683:   PetscFunctionReturn(PETSC_SUCCESS);
3684: }

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

3689:   Input Parameters:
3690: + dm      - The full domain DM
3691: . dmX     - An array of DM for the field, say an auxiliary DM, indexed by s
3692: . dsX     - An array of PetscDS for the field, indexed by s
3693: . cellIS  - The interface cells for which we want values
3694: . locX    - An array of local vectors with the field values, indexed by s
3695: - useCell - Flag to have values come from neighboring cell rather than endcap face

3697:   Output Parameter:
3698: . x       - An array of field values, indexed by s

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

3703:   Level: advanced

3705: .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()`
3706: */
3707: static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3708: {
3709:   DM              plexX[2];
3710:   DMEnclosureType encX[2];
3711:   PetscSection    sectionX[2];
3712:   const PetscInt *cells;
3713:   PetscInt        cStart, cEnd, numCells, c, s, totDimX[2];

3715:   PetscFunctionBegin;
3716:   PetscAssertPointer(locX, 5);
3717:   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3718:   PetscAssertPointer(dmX, 2);
3719:   PetscAssertPointer(dsX, 3);
3721:   PetscAssertPointer(x, 7);
3722:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
3723:   numCells = cEnd - cStart;
3724:   for (s = 0; s < 2; ++s) {
3728:     PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE));
3729:     PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s]));
3730:     PetscCall(DMGetLocalSection(dmX[s], &sectionX[s]));
3731:     PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s]));
3732:     PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s]));
3733:   }
3734:   for (c = cStart; c < cEnd; ++c) {
3735:     const PetscInt  cell = cells ? cells[c] : c;
3736:     const PetscInt  cind = c - cStart;
3737:     const PetscInt *cone, *ornt;

3739:     PetscCall(DMPlexGetCone(dm, cell, &cone));
3740:     PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
3741:     //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]);
3742:     for (s = 0; s < 2; ++s) {
3743:       const PetscInt tdX     = totDimX[s];
3744:       PetscScalar   *closure = NULL, *xl = &x[s][cind * tdX];
3745:       PetscInt       face = cone[s], point = face, subpoint, Nx, i;

3747:       if (useCell) {
3748:         const PetscInt *support;
3749:         PetscInt        ssize;

3751:         PetscCall(DMPlexGetSupport(dm, face, &support));
3752:         PetscCall(DMPlexGetSupportSize(dm, face, &ssize));
3753:         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);
3754:         if (support[0] == cell) point = support[1];
3755:         else if (support[1] == cell) point = support[0];
3756:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell);
3757:       }
3758:       PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint));
3759:       PetscCall(DMPlexVecGetOrientedClosure_Internal(plexX[s], sectionX[s], PETSC_FALSE, locX[s], subpoint, ornt[s], &Nx, &closure));
3760:       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);
3761:       for (i = 0; i < Nx; ++i) xl[i] = closure[i];
3762:       PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure));
3763:     }
3764:   }
3765:   for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s]));
3766:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
3767:   PetscFunctionReturn(PETSC_SUCCESS);
3768: }

3770: static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[])
3771: {
3772:   PetscFunctionBegin;
3773:   if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS);
3774:   PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0]));
3775:   PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1]));
3776:   PetscFunctionReturn(PETSC_SUCCESS);
3777: }

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

3782:   Input Parameters:
3783: + dm           - The `DM`
3784: . fStart       - The first face to include
3785: . fEnd         - The first face to exclude
3786: . locX         - A local vector with the solution fields
3787: . locX_t       - A local vector with solution field time derivatives, or NULL
3788: . faceGeometry - A local vector with face geometry
3789: . cellGeometry - A local vector with cell geometry
3790: - locGrad      - A local vector with field gradients, or NULL

3792:   Output Parameters:
3793: + Nface - The number of faces with field values
3794: . uL    - The field values at the left side of the face
3795: - uR    - The field values at the right side of the face

3797:   Level: developer

3799: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3800: @*/
3801: 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)
3802: {
3803:   DM                 dmFace, dmCell, dmGrad = NULL;
3804:   PetscSection       section;
3805:   PetscDS            prob;
3806:   DMLabel            ghostLabel;
3807:   const PetscScalar *facegeom, *cellgeom, *x, *lgrad;
3808:   PetscBool         *isFE;
3809:   PetscInt           dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face;

3811:   PetscFunctionBegin;
3818:   PetscAssertPointer(uL, 10);
3819:   PetscAssertPointer(uR, 11);
3820:   PetscCall(DMGetDimension(dm, &dim));
3821:   PetscCall(DMGetDS(dm, &prob));
3822:   PetscCall(DMGetLocalSection(dm, &section));
3823:   PetscCall(PetscDSGetNumFields(prob, &Nf));
3824:   PetscCall(PetscDSGetTotalComponents(prob, &Nc));
3825:   PetscCall(PetscMalloc1(Nf, &isFE));
3826:   for (f = 0; f < Nf; ++f) {
3827:     PetscObject  obj;
3828:     PetscClassId id;

3830:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
3831:     PetscCall(PetscObjectGetClassId(obj, &id));
3832:     if (id == PETSCFE_CLASSID) {
3833:       isFE[f] = PETSC_TRUE;
3834:     } else if (id == PETSCFV_CLASSID) {
3835:       isFE[f] = PETSC_FALSE;
3836:     } else {
3837:       isFE[f] = PETSC_FALSE;
3838:     }
3839:   }
3840:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
3841:   PetscCall(VecGetArrayRead(locX, &x));
3842:   PetscCall(VecGetDM(faceGeometry, &dmFace));
3843:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
3844:   PetscCall(VecGetDM(cellGeometry, &dmCell));
3845:   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
3846:   if (locGrad) {
3847:     PetscCall(VecGetDM(locGrad, &dmGrad));
3848:     PetscCall(VecGetArrayRead(locGrad, &lgrad));
3849:   }
3850:   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL));
3851:   PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR));
3852:   /* Right now just eat the extra work for FE (could make a cell loop) */
3853:   for (face = fStart, iface = 0; face < fEnd; ++face) {
3854:     const PetscInt  *cells;
3855:     PetscFVFaceGeom *fg;
3856:     PetscFVCellGeom *cgL, *cgR;
3857:     PetscScalar     *xL, *xR, *gL, *gR;
3858:     PetscScalar     *uLl = *uL, *uRl = *uR;
3859:     PetscInt         ghost, nsupp, nchild;

3861:     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
3862:     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
3863:     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
3864:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
3865:     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
3866:     PetscCall(DMPlexGetSupport(dm, face, &cells));
3867:     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
3868:     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
3869:     for (f = 0; f < Nf; ++f) {
3870:       PetscInt off;

3872:       PetscCall(PetscDSGetComponentOffset(prob, f, &off));
3873:       if (isFE[f]) {
3874:         const PetscInt *cone;
3875:         PetscInt        comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d;

3877:         xL = xR = NULL;
3878:         PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3879:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
3880:         PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
3881:         PetscCall(DMPlexGetCone(dm, cells[0], &cone));
3882:         PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL));
3883:         for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL)
3884:           if (cone[faceLocL] == face) break;
3885:         PetscCall(DMPlexGetCone(dm, cells[1], &cone));
3886:         PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR));
3887:         for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR)
3888:           if (cone[faceLocR] == face) break;
3889:         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]);
3890:         /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */
3891:         /* TODO: this is a hack that might not be right for nonconforming */
3892:         if (faceLocL < coneSizeL) {
3893:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off]));
3894:           if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
3895:           else {
3896:             for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d];
3897:           }
3898:         } else {
3899:           PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off]));
3900:           PetscCall(PetscSectionGetFieldComponents(section, f, &comp));
3901:           for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d];
3902:         }
3903:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL));
3904:         PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR));
3905:       } else {
3906:         PetscFV  fv;
3907:         PetscInt numComp, c;

3909:         PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv));
3910:         PetscCall(PetscFVGetNumComponents(fv, &numComp));
3911:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL));
3912:         PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR));
3913:         if (dmGrad) {
3914:           PetscReal dxL[3], dxR[3];

3916:           PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL));
3917:           PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR));
3918:           DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL);
3919:           DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR);
3920:           for (c = 0; c < numComp; ++c) {
3921:             uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL);
3922:             uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR);
3923:           }
3924:         } else {
3925:           for (c = 0; c < numComp; ++c) {
3926:             uLl[iface * Nc + off + c] = xL[c];
3927:             uRl[iface * Nc + off + c] = xR[c];
3928:           }
3929:         }
3930:       }
3931:     }
3932:     ++iface;
3933:   }
3934:   *Nface = iface;
3935:   PetscCall(VecRestoreArrayRead(locX, &x));
3936:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
3937:   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
3938:   if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad));
3939:   PetscCall(PetscFree(isFE));
3940:   PetscFunctionReturn(PETSC_SUCCESS);
3941: }

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

3946:   Input Parameters:
3947: + dm           - The `DM`
3948: . fStart       - The first face to include
3949: . fEnd         - The first face to exclude
3950: . locX         - A local vector with the solution fields
3951: . locX_t       - A local vector with solution field time derivatives, or NULL
3952: . faceGeometry - A local vector with face geometry
3953: . cellGeometry - A local vector with cell geometry
3954: - locGrad      - A local vector with field gradients, or NULL

3956:   Output Parameters:
3957: + Nface - The number of faces with field values
3958: . uL    - The field values at the left side of the face
3959: - uR    - The field values at the right side of the face

3961:   Level: developer

3963: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
3964: @*/
3965: 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)
3966: {
3967:   PetscFunctionBegin;
3968:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL));
3969:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR));
3970:   PetscFunctionReturn(PETSC_SUCCESS);
3971: }

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

3976:   Input Parameters:
3977: + dm           - The `DM`
3978: . fStart       - The first face to include
3979: . fEnd         - The first face to exclude
3980: . faceGeometry - A local vector with face geometry
3981: - cellGeometry - A local vector with cell geometry

3983:   Output Parameters:
3984: + Nface - The number of faces with field values
3985: . fgeom - The extract the face centroid and normal
3986: - vol   - The cell volume

3988:   Level: developer

3990: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()`
3991: @*/
3992: PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
3993: {
3994:   DM                 dmFace, dmCell;
3995:   DMLabel            ghostLabel;
3996:   const PetscScalar *facegeom, *cellgeom;
3997:   PetscInt           dim, numFaces = fEnd - fStart, iface, face;

3999:   PetscFunctionBegin;
4003:   PetscAssertPointer(fgeom, 7);
4004:   PetscAssertPointer(vol, 8);
4005:   PetscCall(DMGetDimension(dm, &dim));
4006:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4007:   PetscCall(VecGetDM(faceGeometry, &dmFace));
4008:   PetscCall(VecGetArrayRead(faceGeometry, &facegeom));
4009:   PetscCall(VecGetDM(cellGeometry, &dmCell));
4010:   PetscCall(VecGetArrayRead(cellGeometry, &cellgeom));
4011:   PetscCall(PetscMalloc1(numFaces, fgeom));
4012:   PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol));
4013:   for (face = fStart, iface = 0; face < fEnd; ++face) {
4014:     const PetscInt  *cells;
4015:     PetscFVFaceGeom *fg;
4016:     PetscFVCellGeom *cgL, *cgR;
4017:     PetscFVFaceGeom *fgeoml = *fgeom;
4018:     PetscReal       *voll   = *vol;
4019:     PetscInt         ghost, d, nchild, nsupp;

4021:     PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
4022:     PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
4023:     PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
4024:     if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
4025:     PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg));
4026:     PetscCall(DMPlexGetSupport(dm, face, &cells));
4027:     PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL));
4028:     PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR));
4029:     for (d = 0; d < dim; ++d) {
4030:       fgeoml[iface].centroid[d] = fg->centroid[d];
4031:       fgeoml[iface].normal[d]   = fg->normal[d];
4032:     }
4033:     voll[iface * 2 + 0] = cgL->volume;
4034:     voll[iface * 2 + 1] = cgR->volume;
4035:     ++iface;
4036:   }
4037:   *Nface = iface;
4038:   PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom));
4039:   PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom));
4040:   PetscFunctionReturn(PETSC_SUCCESS);
4041: }

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

4046:   Input Parameters:
4047: + dm           - The `DM`
4048: . fStart       - The first face to include
4049: . fEnd         - The first face to exclude
4050: . faceGeometry - A local vector with face geometry
4051: - cellGeometry - A local vector with cell geometry

4053:   Output Parameters:
4054: + Nface - The number of faces with field values
4055: . fgeom - The extract the face centroid and normal
4056: - vol   - The cell volume

4058:   Level: developer

4060: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()`
4061: @*/
4062: PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol)
4063: {
4064:   PetscFunctionBegin;
4065:   PetscCall(PetscFree(*fgeom));
4066:   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol));
4067:   PetscFunctionReturn(PETSC_SUCCESS);
4068: }

4070: PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
4071: {
4072:   char           composeStr[33] = {0};
4073:   PetscObjectId  id;
4074:   PetscContainer container;

4076:   PetscFunctionBegin;
4077:   PetscCall(PetscObjectGetId((PetscObject)quad, &id));
4078:   PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id));
4079:   PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container));
4080:   if (container) {
4081:     PetscCall(PetscContainerGetPointer(container, (void **)geom));
4082:   } else {
4083:     PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom));
4084:     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container));
4085:     PetscCall(PetscContainerSetPointer(container, (void *)*geom));
4086:     PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom));
4087:     PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container));
4088:     PetscCall(PetscContainerDestroy(&container));
4089:   }
4090:   PetscFunctionReturn(PETSC_SUCCESS);
4091: }

4093: PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom)
4094: {
4095:   PetscFunctionBegin;
4096:   *geom = NULL;
4097:   PetscFunctionReturn(PETSC_SUCCESS);
4098: }

4100: PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user)
4101: {
4102:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
4103:   const char     *name       = "Residual";
4104:   DM              dmAux      = NULL;
4105:   DMLabel         ghostLabel = NULL;
4106:   PetscDS         prob       = NULL;
4107:   PetscDS         probAux    = NULL;
4108:   PetscBool       useFEM     = PETSC_FALSE;
4109:   PetscBool       isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4110:   DMField         coordField = NULL;
4111:   Vec             locA;
4112:   PetscScalar    *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL;
4113:   IS              chunkIS;
4114:   const PetscInt *cells;
4115:   PetscInt        cStart, cEnd, numCells;
4116:   PetscInt        Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd;
4117:   PetscInt        maxDegree = PETSC_MAX_INT;
4118:   PetscFormKey    key;
4119:   PetscQuadrature affineQuad = NULL, *quads = NULL;
4120:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

4122:   PetscFunctionBegin;
4123:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4124:   /* FEM+FVM */
4125:   /* 1: Get sizes from dm and dmAux */
4126:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4127:   PetscCall(DMGetDS(dm, &prob));
4128:   PetscCall(PetscDSGetNumFields(prob, &Nf));
4129:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4130:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA));
4131:   if (locA) {
4132:     PetscCall(VecGetDM(locA, &dmAux));
4133:     PetscCall(DMGetDS(dmAux, &probAux));
4134:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4135:   }
4136:   /* 2: Get geometric data */
4137:   for (f = 0; f < Nf; ++f) {
4138:     PetscObject  obj;
4139:     PetscClassId id;
4140:     PetscBool    fimp;

4142:     PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4143:     if (isImplicit != fimp) continue;
4144:     PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4145:     PetscCall(PetscObjectGetClassId(obj, &id));
4146:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4147:     PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented");
4148:   }
4149:   if (useFEM) {
4150:     PetscCall(DMGetCoordinateField(dm, &coordField));
4151:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4152:     if (maxDegree <= 1) {
4153:       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4154:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4155:     } else {
4156:       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4157:       for (f = 0; f < Nf; ++f) {
4158:         PetscObject  obj;
4159:         PetscClassId id;
4160:         PetscBool    fimp;

4162:         PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4163:         if (isImplicit != fimp) continue;
4164:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4165:         PetscCall(PetscObjectGetClassId(obj, &id));
4166:         if (id == PETSCFE_CLASSID) {
4167:           PetscFE fe = (PetscFE)obj;

4169:           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4170:           PetscCall(PetscObjectReference((PetscObject)quads[f]));
4171:           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4172:         }
4173:       }
4174:     }
4175:   }
4176:   /* Loop over chunks */
4177:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4178:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4179:   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4180:   numCells      = cEnd - cStart;
4181:   numChunks     = 1;
4182:   cellChunkSize = numCells / numChunks;
4183:   numChunks     = PetscMin(1, numCells);
4184:   key.label     = NULL;
4185:   key.value     = 0;
4186:   key.part      = 0;
4187:   for (chunk = 0; chunk < numChunks; ++chunk) {
4188:     PetscScalar     *elemVec, *fluxL = NULL, *fluxR = NULL;
4189:     PetscReal       *vol   = NULL;
4190:     PetscFVFaceGeom *fgeom = NULL;
4191:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4192:     PetscInt         numFaces = 0;

4194:     /* Extract field coefficients */
4195:     if (useFEM) {
4196:       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4197:       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4198:       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4199:       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4200:     }
4201:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4202:     /* Loop over fields */
4203:     for (f = 0; f < Nf; ++f) {
4204:       PetscObject  obj;
4205:       PetscClassId id;
4206:       PetscBool    fimp;
4207:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

4209:       key.field = f;
4210:       PetscCall(PetscDSGetImplicit(prob, f, &fimp));
4211:       if (isImplicit != fimp) continue;
4212:       PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4213:       PetscCall(PetscObjectGetClassId(obj, &id));
4214:       if (id == PETSCFE_CLASSID) {
4215:         PetscFE         fe        = (PetscFE)obj;
4216:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4217:         PetscFEGeom    *chunkGeom = NULL;
4218:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
4219:         PetscInt        Nq, Nb;

4221:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4222:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4223:         PetscCall(PetscFEGetDimension(fe, &Nb));
4224:         blockSize = Nb;
4225:         batchSize = numBlocks * blockSize;
4226:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4227:         numChunks = numCells / (numBatches * batchSize);
4228:         Ne        = numChunks * numBatches * batchSize;
4229:         Nr        = numCells % (numBatches * batchSize);
4230:         offset    = numCells - Nr;
4231:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4232:         /*   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) */
4233:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4234:         PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4235:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4236:         PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim]));
4237:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4238:       } else if (id == PETSCFV_CLASSID) {
4239:         PetscFV fv = (PetscFV)obj;

4241:         Ne = numFaces;
4242:         /* Riemann solve over faces (need fields at face centroids) */
4243:         /*   We need to evaluate FE fields at those coordinates */
4244:         PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4245:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4246:     }
4247:     /* Loop over domain */
4248:     if (useFEM) {
4249:       /* Add elemVec to locX */
4250:       for (c = cS; c < cE; ++c) {
4251:         const PetscInt cell = cells ? cells[c] : c;
4252:         const PetscInt cind = c - cStart;

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

4258:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4259:           if (ghostVal > 0) continue;
4260:         }
4261:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
4262:       }
4263:     }
4264:     /* Handle time derivative */
4265:     if (locX_t) {
4266:       PetscScalar *x_t, *fa;

4268:       PetscCall(VecGetArray(locF, &fa));
4269:       PetscCall(VecGetArray(locX_t, &x_t));
4270:       for (f = 0; f < Nf; ++f) {
4271:         PetscFV      fv;
4272:         PetscObject  obj;
4273:         PetscClassId id;
4274:         PetscInt     pdim, d;

4276:         PetscCall(PetscDSGetDiscretization(prob, f, &obj));
4277:         PetscCall(PetscObjectGetClassId(obj, &id));
4278:         if (id != PETSCFV_CLASSID) continue;
4279:         fv = (PetscFV)obj;
4280:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
4281:         for (c = cS; c < cE; ++c) {
4282:           const PetscInt cell = cells ? cells[c] : c;
4283:           PetscScalar   *u_t, *r;

4285:           if (ghostLabel) {
4286:             PetscInt ghostVal;

4288:             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
4289:             if (ghostVal > 0) continue;
4290:           }
4291:           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
4292:           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
4293:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
4294:         }
4295:       }
4296:       PetscCall(VecRestoreArray(locX_t, &x_t));
4297:       PetscCall(VecRestoreArray(locF, &fa));
4298:     }
4299:     if (useFEM) {
4300:       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4301:       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4302:     }
4303:   }
4304:   if (useFEM) PetscCall(ISDestroy(&chunkIS));
4305:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
4306:   /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */
4307:   if (useFEM) {
4308:     if (maxDegree <= 1) {
4309:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4310:       PetscCall(PetscQuadratureDestroy(&affineQuad));
4311:     } else {
4312:       for (f = 0; f < Nf; ++f) {
4313:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4314:         PetscCall(PetscQuadratureDestroy(&quads[f]));
4315:       }
4316:       PetscCall(PetscFree2(quads, geoms));
4317:     }
4318:   }
4319:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4320:   PetscFunctionReturn(PETSC_SUCCESS);
4321: }

4323: /*
4324:   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

4326:   X   - The local solution vector
4327:   X_t - The local solution time derivative vector, or NULL
4328: */
4329: 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)
4330: {
4331:   DM_Plex        *mesh = (DM_Plex *)dm->data;
4332:   const char     *name = "Jacobian", *nameP = "JacobianPre";
4333:   DM              dmAux = NULL;
4334:   PetscDS         prob, probAux = NULL;
4335:   PetscSection    sectionAux = NULL;
4336:   Vec             A;
4337:   DMField         coordField;
4338:   PetscFEGeom    *cgeomFEM;
4339:   PetscQuadrature qGeom = NULL;
4340:   Mat             J = Jac, JP = JacP;
4341:   PetscScalar    *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL;
4342:   PetscBool       hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE;
4343:   const PetscInt *cells;
4344:   PetscFormKey    key;
4345:   PetscInt        Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0;

4347:   PetscFunctionBegin;
4348:   PetscCall(ISGetLocalSize(cellIS, &numCells));
4349:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4350:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4351:   PetscCall(DMGetDS(dm, &prob));
4352:   PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A));
4353:   if (A) {
4354:     PetscCall(VecGetDM(A, &dmAux));
4355:     PetscCall(DMGetLocalSection(dmAux, &sectionAux));
4356:     PetscCall(DMGetDS(dmAux, &probAux));
4357:   }
4358:   /* Get flags */
4359:   PetscCall(PetscDSGetNumFields(prob, &Nf));
4360:   PetscCall(DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4361:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
4362:     PetscObject  disc;
4363:     PetscClassId id;
4364:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc));
4365:     PetscCall(PetscObjectGetClassId(disc, &id));
4366:     if (id == PETSCFE_CLASSID) {
4367:       isFE[fieldI] = PETSC_TRUE;
4368:     } else if (id == PETSCFV_CLASSID) {
4369:       hasFV        = PETSC_TRUE;
4370:       isFE[fieldI] = PETSC_FALSE;
4371:     }
4372:   }
4373:   PetscCall(PetscDSHasJacobian(prob, &hasJac));
4374:   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
4375:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
4376:   assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE;
4377:   hasDyn      = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
4378:   if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */
4379:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4380:   if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4381:   /* Compute batch sizes */
4382:   if (isFE[0]) {
4383:     PetscFE         fe;
4384:     PetscQuadrature q;
4385:     PetscInt        numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb;

4387:     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4388:     PetscCall(PetscFEGetQuadrature(fe, &q));
4389:     PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL));
4390:     PetscCall(PetscFEGetDimension(fe, &Nb));
4391:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4392:     blockSize = Nb * numQuadPoints;
4393:     batchSize = numBlocks * blockSize;
4394:     chunkSize = numBatches * batchSize;
4395:     numChunks = numCells / chunkSize + numCells % chunkSize;
4396:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4397:   } else {
4398:     chunkSize = numCells;
4399:     numChunks = 1;
4400:   }
4401:   /* Get work space */
4402:   wsz = (((X ? 1 : 0) + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize;
4403:   PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work));
4404:   PetscCall(PetscArrayzero(work, wsz));
4405:   off      = 0;
4406:   u        = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4407:   u_t      = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL;
4408:   a        = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL;
4409:   elemMat  = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4410:   elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4411:   elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL;
4412:   PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz);
4413:   /* Setup geometry */
4414:   PetscCall(DMGetCoordinateField(dm, &coordField));
4415:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4416:   if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
4417:   if (!qGeom) {
4418:     PetscFE fe;

4420:     PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe));
4421:     PetscCall(PetscFEGetQuadrature(fe, &qGeom));
4422:     PetscCall(PetscObjectReference((PetscObject)qGeom));
4423:   }
4424:   PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4425:   /* Compute volume integrals */
4426:   if (assembleJac) PetscCall(MatZeroEntries(J));
4427:   PetscCall(MatZeroEntries(JP));
4428:   key.label = NULL;
4429:   key.value = 0;
4430:   key.part  = 0;
4431:   for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) {
4432:     const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell);
4433:     PetscInt       c;

4435:     /* Extract values */
4436:     for (c = 0; c < Ncell; ++c) {
4437:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4438:       PetscScalar   *x = NULL, *x_t = NULL;
4439:       PetscInt       i;

4441:       if (X) {
4442:         PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
4443:         for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i];
4444:         PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
4445:       }
4446:       if (X_t) {
4447:         PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
4448:         for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i];
4449:         PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
4450:       }
4451:       if (dmAux) {
4452:         PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x));
4453:         for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i];
4454:         PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x));
4455:       }
4456:     }
4457:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
4458:       PetscFE fe;
4459:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
4460:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
4461:         key.field = fieldI * Nf + fieldJ;
4462:         if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat));
4463:         if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP));
4464:         if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD));
4465:       }
4466:       /* For finite volume, add the identity */
4467:       if (!isFE[fieldI]) {
4468:         PetscFV  fv;
4469:         PetscInt eOffset = 0, Nc, fc, foff;

4471:         PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff));
4472:         PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
4473:         PetscCall(PetscFVGetNumComponents(fv, &Nc));
4474:         for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) {
4475:           for (fc = 0; fc < Nc; ++fc) {
4476:             const PetscInt i = foff + fc;
4477:             if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
4478:             if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0;
4479:           }
4480:         }
4481:       }
4482:     }
4483:     /*   Add contribution from X_t */
4484:     if (hasDyn) {
4485:       for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
4486:     }
4487:     /* Insert values into matrix */
4488:     for (c = 0; c < Ncell; ++c) {
4489:       const PetscInt cell = cells ? cells[c + offCell] : c + offCell;
4490:       if (mesh->printFEM > 1) {
4491:         if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim]));
4492:         if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim]));
4493:       }
4494:       if (assembleJac) PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4495:       PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES));
4496:     }
4497:   }
4498:   /* Cleanup */
4499:   PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
4500:   PetscCall(PetscQuadratureDestroy(&qGeom));
4501:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
4502:   PetscCall(DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE));
4503:   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));
4504:   /* Compute boundary integrals */
4505:   /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */
4506:   /* Assemble matrix */
4507:   if (assembleJac) {
4508:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
4509:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
4510:   }
4511:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
4512:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
4513:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
4514:   PetscFunctionReturn(PETSC_SUCCESS);
4515: }

4517: /******** FEM Assembly Function ********/

4519: static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy)
4520: {
4521:   PetscBool isPlex;

4523:   PetscFunctionBegin;
4524:   PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex));
4525:   if (isPlex) {
4526:     *plex = dm;
4527:     PetscCall(PetscObjectReference((PetscObject)dm));
4528:   } else {
4529:     PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex));
4530:     if (!*plex) {
4531:       PetscCall(DMConvert(dm, DMPLEX, plex));
4532:       PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex));
4533:       if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex));
4534:     } else {
4535:       PetscCall(PetscObjectReference((PetscObject)*plex));
4536:     }
4537:   }
4538:   PetscFunctionReturn(PETSC_SUCCESS);
4539: }

4541: /*@
4542:   DMPlexGetGeometryFVM - Return precomputed geometric data

4544:   Collective

4546:   Input Parameter:
4547: . dm - The `DM`

4549:   Output Parameters:
4550: + facegeom  - The values precomputed from face geometry
4551: . cellgeom  - The values precomputed from cell geometry
4552: - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell

4554:   Level: developer

4556: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()`
4557: @*/
4558: PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius)
4559: {
4560:   DM plex;

4562:   PetscFunctionBegin;
4564:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4565:   PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL));
4566:   if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius));
4567:   PetscCall(DMDestroy(&plex));
4568:   PetscFunctionReturn(PETSC_SUCCESS);
4569: }

4571: /*@
4572:   DMPlexGetGradientDM - Return gradient data layout

4574:   Collective

4576:   Input Parameters:
4577: + dm - The `DM`
4578: - fv - The `PetscFV`

4580:   Output Parameter:
4581: . dmGrad - The layout for gradient values

4583:   Level: developer

4585: .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()`
4586: @*/
4587: PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad)
4588: {
4589:   DM        plex;
4590:   PetscBool computeGradients;

4592:   PetscFunctionBegin;
4595:   PetscAssertPointer(dmGrad, 3);
4596:   PetscCall(PetscFVGetComputeGradients(fv, &computeGradients));
4597:   if (!computeGradients) {
4598:     *dmGrad = NULL;
4599:     PetscFunctionReturn(PETSC_SUCCESS);
4600:   }
4601:   PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE));
4602:   PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad));
4603:   PetscCall(DMDestroy(&plex));
4604:   PetscFunctionReturn(PETSC_SUCCESS);
4605: }

4607: static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS)
4608: {
4609:   DM_Plex        *mesh = (DM_Plex *)dm->data;
4610:   DM              plex = NULL, plexA = NULL;
4611:   DMEnclosureType encAux;
4612:   PetscDS         prob, probAux       = NULL;
4613:   PetscSection    section, sectionAux = NULL;
4614:   Vec             locA = NULL;
4615:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL;
4616:   PetscInt        totDim, totDimAux = 0;

4618:   PetscFunctionBegin;
4619:   PetscCall(DMConvert(dm, DMPLEX, &plex));
4620:   PetscCall(DMGetLocalSection(dm, &section));
4621:   PetscCall(DMGetDS(dm, &prob));
4622:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
4623:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4624:   if (locA) {
4625:     DM dmAux;

4627:     PetscCall(VecGetDM(locA, &dmAux));
4628:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
4629:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
4630:     PetscCall(DMGetDS(plexA, &probAux));
4631:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
4632:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
4633:   }
4634:   {
4635:     PetscFEGeom    *fgeom;
4636:     PetscInt        maxDegree;
4637:     PetscQuadrature qGeom = NULL;
4638:     IS              pointIS;
4639:     const PetscInt *points;
4640:     PetscInt        numFaces, face, Nq;

4642:     PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS));
4643:     if (!pointIS) goto end; /* No points with that id on this process */
4644:     {
4645:       IS isectIS;

4647:       /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */
4648:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
4649:       PetscCall(ISDestroy(&pointIS));
4650:       pointIS = isectIS;
4651:     }
4652:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
4653:     PetscCall(ISGetIndices(pointIS, &points));
4654:     PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim, &elemVec, locA ? numFaces * totDimAux : 0, &a));
4655:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
4656:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
4657:     if (!qGeom) {
4658:       PetscFE fe;

4660:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4661:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
4662:       PetscCall(PetscObjectReference((PetscObject)qGeom));
4663:     }
4664:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
4665:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4666:     for (face = 0; face < numFaces; ++face) {
4667:       const PetscInt point = points[face], *support;
4668:       PetscScalar   *x     = NULL;
4669:       PetscInt       i;

4671:       PetscCall(DMPlexGetSupport(dm, point, &support));
4672:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
4673:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
4674:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
4675:       if (locX_t) {
4676:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
4677:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
4678:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
4679:       }
4680:       if (locA) {
4681:         PetscInt subp;

4683:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
4684:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
4685:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
4686:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
4687:       }
4688:     }
4689:     PetscCall(PetscArrayzero(elemVec, numFaces * totDim));
4690:     {
4691:       PetscFE      fe;
4692:       PetscInt     Nb;
4693:       PetscFEGeom *chunkGeom = NULL;
4694:       /* Conforming batches */
4695:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
4696:       /* Remainder */
4697:       PetscInt Nr, offset;

4699:       PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe));
4700:       PetscCall(PetscFEGetDimension(fe, &Nb));
4701:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4702:       /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */
4703:       blockSize = Nb;
4704:       batchSize = numBlocks * blockSize;
4705:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4706:       numChunks = numFaces / (numBatches * batchSize);
4707:       Ne        = numChunks * numBatches * batchSize;
4708:       Nr        = numFaces % (numBatches * batchSize);
4709:       offset    = numFaces - Nr;
4710:       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
4711:       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec));
4712:       PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom));
4713:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
4714:       PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, &elemVec[offset * totDim]));
4715:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
4716:     }
4717:     for (face = 0; face < numFaces; ++face) {
4718:       const PetscInt point = points[face], *support;

4720:       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, "BdResidual", totDim, &elemVec[face * totDim]));
4721:       PetscCall(DMPlexGetSupport(plex, point, &support));
4722:       PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES));
4723:     }
4724:     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
4725:     PetscCall(PetscQuadratureDestroy(&qGeom));
4726:     PetscCall(ISRestoreIndices(pointIS, &points));
4727:     PetscCall(ISDestroy(&pointIS));
4728:     PetscCall(PetscFree4(u, u_t, elemVec, a));
4729:   }
4730: end:
4731:   PetscCall(DMDestroy(&plex));
4732:   PetscCall(DMDestroy(&plexA));
4733:   PetscFunctionReturn(PETSC_SUCCESS);
4734: }

4736: PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF)
4737: {
4738:   DMField  coordField;
4739:   DMLabel  depthLabel;
4740:   IS       facetIS;
4741:   PetscInt dim;

4743:   PetscFunctionBegin;
4744:   PetscCall(DMGetDimension(dm, &dim));
4745:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4746:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4747:   PetscCall(DMGetCoordinateField(dm, &coordField));
4748:   PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4749:   PetscCall(ISDestroy(&facetIS));
4750:   PetscFunctionReturn(PETSC_SUCCESS);
4751: }

4753: static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4754: {
4755:   PetscDS  prob;
4756:   PetscInt numBd, bd;
4757:   DMField  coordField = NULL;
4758:   IS       facetIS    = NULL;
4759:   DMLabel  depthLabel;
4760:   PetscInt dim;

4762:   PetscFunctionBegin;
4763:   PetscCall(DMGetDS(dm, &prob));
4764:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4765:   PetscCall(DMGetDimension(dm, &dim));
4766:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4767:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
4768:   for (bd = 0; bd < numBd; ++bd) {
4769:     PetscWeakForm           wf;
4770:     DMBoundaryConditionType type;
4771:     DMLabel                 label;
4772:     const PetscInt         *values;
4773:     PetscInt                field, numValues, v;
4774:     PetscObject             obj;
4775:     PetscClassId            id;
4776:     PetscFormKey            key;

4778:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL));
4779:     if (type & DM_BC_ESSENTIAL) continue;
4780:     PetscCall(PetscDSGetDiscretization(prob, field, &obj));
4781:     PetscCall(PetscObjectGetClassId(obj, &id));
4782:     if (id != PETSCFE_CLASSID) continue;
4783:     if (!facetIS) {
4784:       DMLabel  depthLabel;
4785:       PetscInt dim;

4787:       PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
4788:       PetscCall(DMGetDimension(dm, &dim));
4789:       PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
4790:     }
4791:     PetscCall(DMGetCoordinateField(dm, &coordField));
4792:     for (v = 0; v < numValues; ++v) {
4793:       key.label = label;
4794:       key.value = values[v];
4795:       key.field = field;
4796:       key.part  = 0;
4797:       PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS));
4798:     }
4799:   }
4800:   PetscCall(ISDestroy(&facetIS));
4801:   PetscFunctionReturn(PETSC_SUCCESS);
4802: }

4804: PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
4805: {
4806:   DM_Plex         *mesh       = (DM_Plex *)dm->data;
4807:   const char      *name       = "Residual";
4808:   DM               dmAux      = NULL;
4809:   DM               dmGrad     = NULL;
4810:   DMLabel          ghostLabel = NULL;
4811:   PetscDS          ds         = NULL;
4812:   PetscDS          dsAux      = NULL;
4813:   PetscSection     section    = NULL;
4814:   PetscBool        useFEM     = PETSC_FALSE;
4815:   PetscBool        useFVM     = PETSC_FALSE;
4816:   PetscBool        isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE;
4817:   PetscFV          fvm        = NULL;
4818:   PetscFVCellGeom *cgeomFVM   = NULL;
4819:   PetscFVFaceGeom *fgeomFVM   = NULL;
4820:   DMField          coordField = NULL;
4821:   Vec              locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, grad, locGrad = NULL;
4822:   PetscScalar     *u = NULL, *u_t, *a, *uL, *uR;
4823:   IS               chunkIS;
4824:   const PetscInt  *cells;
4825:   PetscInt         cStart, cEnd, numCells;
4826:   PetscInt         Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd;
4827:   PetscInt         maxDegree  = PETSC_MAX_INT;
4828:   PetscQuadrature  affineQuad = NULL, *quads = NULL;
4829:   PetscFEGeom     *affineGeom = NULL, **geoms = NULL;

4831:   PetscFunctionBegin;
4832:   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
4833:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
4834:   if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
4835:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
4836:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
4837:   /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */
4838:   /* FEM+FVM */
4839:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4840:   /* 1: Get sizes from dm and dmAux */
4841:   PetscCall(DMGetLocalSection(dm, &section));
4842:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
4843:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL));
4844:   PetscCall(PetscDSGetNumFields(ds, &Nf));
4845:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
4846:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA));
4847:   if (locA) {
4848:     PetscInt subcell;
4849:     PetscCall(VecGetDM(locA, &dmAux));
4850:     PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell));
4851:     PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL));
4852:     PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux));
4853:   }
4854:   /* 2: Get geometric data */
4855:   for (f = 0; f < Nf; ++f) {
4856:     PetscObject  obj;
4857:     PetscClassId id;
4858:     PetscBool    fimp;

4860:     PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4861:     if (isImplicit != fimp) continue;
4862:     PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4863:     PetscCall(PetscObjectGetClassId(obj, &id));
4864:     if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE;
4865:     if (id == PETSCFV_CLASSID) {
4866:       useFVM = PETSC_TRUE;
4867:       fvm    = (PetscFV)obj;
4868:     }
4869:   }
4870:   if (useFEM) {
4871:     PetscCall(DMGetCoordinateField(dm, &coordField));
4872:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
4873:     if (maxDegree <= 1) {
4874:       PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad));
4875:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
4876:     } else {
4877:       PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
4878:       for (f = 0; f < Nf; ++f) {
4879:         PetscObject  obj;
4880:         PetscClassId id;
4881:         PetscBool    fimp;

4883:         PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4884:         if (isImplicit != fimp) continue;
4885:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4886:         PetscCall(PetscObjectGetClassId(obj, &id));
4887:         if (id == PETSCFE_CLASSID) {
4888:           PetscFE fe = (PetscFE)obj;

4890:           PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
4891:           PetscCall(PetscObjectReference((PetscObject)quads[f]));
4892:           PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
4893:         }
4894:       }
4895:     }
4896:   }
4897:   if (useFVM) {
4898:     PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL));
4899:     PetscCall(VecGetArrayRead(faceGeometryFVM, (const PetscScalar **)&fgeomFVM));
4900:     PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM));
4901:     /* Reconstruct and limit cell gradients */
4902:     PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad));
4903:     if (dmGrad) {
4904:       PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
4905:       PetscCall(DMGetGlobalVector(dmGrad, &grad));
4906:       PetscCall(DMPlexReconstructGradients_Internal(dm, fvm, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad));
4907:       /* Communicate gradient values */
4908:       PetscCall(DMGetLocalVector(dmGrad, &locGrad));
4909:       PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad));
4910:       PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad));
4911:       PetscCall(DMRestoreGlobalVector(dmGrad, &grad));
4912:     }
4913:     /* Handle non-essential (e.g. outflow) boundary values */
4914:     PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad));
4915:   }
4916:   /* Loop over chunks */
4917:   if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS));
4918:   numCells      = cEnd - cStart;
4919:   numChunks     = 1;
4920:   cellChunkSize = numCells / numChunks;
4921:   faceChunkSize = (fEnd - fStart) / numChunks;
4922:   numChunks     = PetscMin(1, numCells);
4923:   for (chunk = 0; chunk < numChunks; ++chunk) {
4924:     PetscScalar     *elemVec, *fluxL, *fluxR;
4925:     PetscReal       *vol;
4926:     PetscFVFaceGeom *fgeom;
4927:     PetscInt         cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;
4928:     PetscInt         fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face;

4930:     /* Extract field coefficients */
4931:     if (useFEM) {
4932:       PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells));
4933:       PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
4934:       PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
4935:       PetscCall(PetscArrayzero(elemVec, numCells * totDim));
4936:     }
4937:     if (useFVM) {
4938:       PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
4939:       PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
4940:       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
4941:       PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
4942:       PetscCall(PetscArrayzero(fluxL, numFaces * totDim));
4943:       PetscCall(PetscArrayzero(fluxR, numFaces * totDim));
4944:     }
4945:     /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */
4946:     /* Loop over fields */
4947:     for (f = 0; f < Nf; ++f) {
4948:       PetscObject  obj;
4949:       PetscClassId id;
4950:       PetscBool    fimp;
4951:       PetscInt     numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset;

4953:       key.field = f;
4954:       PetscCall(PetscDSGetImplicit(ds, f, &fimp));
4955:       if (isImplicit != fimp) continue;
4956:       PetscCall(PetscDSGetDiscretization(ds, f, &obj));
4957:       PetscCall(PetscObjectGetClassId(obj, &id));
4958:       if (id == PETSCFE_CLASSID) {
4959:         PetscFE         fe        = (PetscFE)obj;
4960:         PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
4961:         PetscFEGeom    *chunkGeom = NULL;
4962:         PetscQuadrature quad      = affineQuad ? affineQuad : quads[f];
4963:         PetscInt        Nq, Nb;

4965:         PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
4966:         PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
4967:         PetscCall(PetscFEGetDimension(fe, &Nb));
4968:         blockSize = Nb;
4969:         batchSize = numBlocks * blockSize;
4970:         PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
4971:         numChunks = numCells / (numBatches * batchSize);
4972:         Ne        = numChunks * numBatches * batchSize;
4973:         Nr        = numCells % (numBatches * batchSize);
4974:         offset    = numCells - Nr;
4975:         /* Integrate FE residual to get elemVec (need fields at quadrature points) */
4976:         /*   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) */
4977:         PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom));
4978:         PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec));
4979:         PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom));
4980:         PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux, a ? &a[offset * totDimAux] : NULL, t, &elemVec[offset * totDim]));
4981:         PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom));
4982:       } else if (id == PETSCFV_CLASSID) {
4983:         PetscFV fv = (PetscFV)obj;

4985:         Ne = numFaces;
4986:         /* Riemann solve over faces (need fields at face centroids) */
4987:         /*   We need to evaluate FE fields at those coordinates */
4988:         PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR));
4989:       } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f);
4990:     }
4991:     /* Loop over domain */
4992:     if (useFEM) {
4993:       /* Add elemVec to locX */
4994:       for (c = cS; c < cE; ++c) {
4995:         const PetscInt cell = cells ? cells[c] : c;
4996:         const PetscInt cind = c - cStart;

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

5002:           PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5003:           if (ghostVal > 0) continue;
5004:         }
5005:         PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES));
5006:       }
5007:     }
5008:     if (useFVM) {
5009:       PetscScalar *fa;
5010:       PetscInt     iface;

5012:       PetscCall(VecGetArray(locF, &fa));
5013:       for (f = 0; f < Nf; ++f) {
5014:         PetscFV      fv;
5015:         PetscObject  obj;
5016:         PetscClassId id;
5017:         PetscInt     foff, pdim;

5019:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5020:         PetscCall(PetscDSGetFieldOffset(ds, f, &foff));
5021:         PetscCall(PetscObjectGetClassId(obj, &id));
5022:         if (id != PETSCFV_CLASSID) continue;
5023:         fv = (PetscFV)obj;
5024:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
5025:         /* Accumulate fluxes to cells */
5026:         for (face = fS, iface = 0; face < fE; ++face) {
5027:           const PetscInt *scells;
5028:           PetscScalar    *fL = NULL, *fR = NULL;
5029:           PetscInt        ghost, d, nsupp, nchild;

5031:           PetscCall(DMLabelGetValue(ghostLabel, face, &ghost));
5032:           PetscCall(DMPlexGetSupportSize(dm, face, &nsupp));
5033:           PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL));
5034:           if (ghost >= 0 || nsupp > 2 || nchild > 0) continue;
5035:           PetscCall(DMPlexGetSupport(dm, face, &scells));
5036:           PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost));
5037:           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL));
5038:           PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost));
5039:           if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR));
5040:           for (d = 0; d < pdim; ++d) {
5041:             if (fL) fL[d] -= fluxL[iface * totDim + foff + d];
5042:             if (fR) fR[d] += fluxR[iface * totDim + foff + d];
5043:           }
5044:           ++iface;
5045:         }
5046:       }
5047:       PetscCall(VecRestoreArray(locF, &fa));
5048:     }
5049:     /* Handle time derivative */
5050:     if (locX_t) {
5051:       PetscScalar *x_t, *fa;

5053:       PetscCall(VecGetArray(locF, &fa));
5054:       PetscCall(VecGetArray(locX_t, &x_t));
5055:       for (f = 0; f < Nf; ++f) {
5056:         PetscFV      fv;
5057:         PetscObject  obj;
5058:         PetscClassId id;
5059:         PetscInt     pdim, d;

5061:         PetscCall(PetscDSGetDiscretization(ds, f, &obj));
5062:         PetscCall(PetscObjectGetClassId(obj, &id));
5063:         if (id != PETSCFV_CLASSID) continue;
5064:         fv = (PetscFV)obj;
5065:         PetscCall(PetscFVGetNumComponents(fv, &pdim));
5066:         for (c = cS; c < cE; ++c) {
5067:           const PetscInt cell = cells ? cells[c] : c;
5068:           PetscScalar   *u_t, *r;

5070:           if (ghostLabel) {
5071:             PetscInt ghostVal;

5073:             PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5074:             if (ghostVal > 0) continue;
5075:           }
5076:           PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t));
5077:           PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r));
5078:           for (d = 0; d < pdim; ++d) r[d] += u_t[d];
5079:         }
5080:       }
5081:       PetscCall(VecRestoreArray(locX_t, &x_t));
5082:       PetscCall(VecRestoreArray(locF, &fa));
5083:     }
5084:     if (useFEM) {
5085:       PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a));
5086:       PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec));
5087:     }
5088:     if (useFVM) {
5089:       PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR));
5090:       PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol));
5091:       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL));
5092:       PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR));
5093:       if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad));
5094:     }
5095:   }
5096:   if (useFEM) PetscCall(ISDestroy(&chunkIS));
5097:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));

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

5102:     if (maxDegree <= 1) {
5103:       PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5104:       PetscCall(PetscQuadratureDestroy(&affineQuad));
5105:     } else {
5106:       for (f = 0; f < Nf; ++f) {
5107:         PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5108:         PetscCall(PetscQuadratureDestroy(&quads[f]));
5109:       }
5110:       PetscCall(PetscFree2(quads, geoms));
5111:     }
5112:   }

5114:   /* FEM */
5115:   /* 1: Get sizes from dm and dmAux */
5116:   /* 2: Get geometric data */
5117:   /* 3: Handle boundary values */
5118:   /* 4: Loop over domain */
5119:   /*   Extract coefficients */
5120:   /* Loop over fields */
5121:   /*   Set tiling for FE*/
5122:   /*   Integrate FE residual to get elemVec */
5123:   /*     Loop over subdomain */
5124:   /*       Loop over quad points */
5125:   /*         Transform coords to real space */
5126:   /*         Evaluate field and aux fields at point */
5127:   /*         Evaluate residual at point */
5128:   /*         Transform residual to real space */
5129:   /*       Add residual to elemVec */
5130:   /* Loop over domain */
5131:   /*   Add elemVec to locX */

5133:   /* FVM */
5134:   /* Get geometric data */
5135:   /* If using gradients */
5136:   /*   Compute gradient data */
5137:   /*   Loop over domain faces */
5138:   /*     Count computational faces */
5139:   /*     Reconstruct cell gradient */
5140:   /*   Loop over domain cells */
5141:   /*     Limit cell gradients */
5142:   /* Handle boundary values */
5143:   /* Loop over domain faces */
5144:   /*   Read out field, centroid, normal, volume for each side of face */
5145:   /* Riemann solve over faces */
5146:   /* Loop over domain faces */
5147:   /*   Accumulate fluxes to cells */
5148:   /* TODO Change printFEM to printDisc here */
5149:   if (mesh->printFEM) {
5150:     Vec          locFbc;
5151:     PetscInt     pStart, pEnd, p, maxDof;
5152:     PetscScalar *zeroes;

5154:     PetscCall(VecDuplicate(locF, &locFbc));
5155:     PetscCall(VecCopy(locF, locFbc));
5156:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5157:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5158:     PetscCall(PetscCalloc1(maxDof, &zeroes));
5159:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5160:     PetscCall(PetscFree(zeroes));
5161:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5162:     PetscCall(VecDestroy(&locFbc));
5163:   }
5164:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5165:   PetscFunctionReturn(PETSC_SUCCESS);
5166: }

5168: /*
5169:   1) Allow multiple kernels for BdResidual for hybrid DS

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

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

5176:   4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before
5177: */
5178: PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user)
5179: {
5180:   DM_Plex        *mesh       = (DM_Plex *)dm->data;
5181:   const char     *name       = "Hybrid Residual";
5182:   DM              dmAux[3]   = {NULL, NULL, NULL};
5183:   DMLabel         ghostLabel = NULL;
5184:   PetscDS         ds         = NULL;
5185:   PetscDS         dsIn       = NULL;
5186:   PetscDS         dsAux[3]   = {NULL, NULL, NULL};
5187:   Vec             locA[3]    = {NULL, NULL, NULL};
5188:   DM              dmScale[3] = {NULL, NULL, NULL};
5189:   PetscDS         dsScale[3] = {NULL, NULL, NULL};
5190:   Vec             locS[3]    = {NULL, NULL, NULL};
5191:   PetscSection    section    = NULL;
5192:   DMField         coordField = NULL;
5193:   PetscScalar    *a[3]       = {NULL, NULL, NULL};
5194:   PetscScalar    *s[3]       = {NULL, NULL, NULL};
5195:   PetscScalar    *u          = NULL, *u_t;
5196:   PetscScalar    *elemVecNeg, *elemVecPos, *elemVecCoh;
5197:   IS              chunkIS;
5198:   const PetscInt *cells;
5199:   PetscInt       *faces;
5200:   PetscInt        cStart, cEnd, numCells;
5201:   PetscInt        Nf, f, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
5202:   PetscInt        maxDegree  = PETSC_MAX_INT;
5203:   PetscQuadrature affineQuad = NULL, *quads = NULL;
5204:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;

5206:   PetscFunctionBegin;
5207:   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
5208:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5209:   PetscCall(ISGetLocalSize(cellIS, &numCells));
5210:   if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
5211:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5212:     const char *name;
5213:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5214:     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);
5215:   }
5216:   PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5217:   /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */
5218:   /* FEM */
5219:   /* 1: Get sizes from dm and dmAux */
5220:   PetscCall(DMGetSection(dm, &section));
5221:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5222:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5223:   PetscCall(PetscDSGetNumFields(ds, &Nf));
5224:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5225:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5226:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5227:   if (locA[2]) {
5228:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

5230:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5231:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5232:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5233:     {
5234:       const PetscInt *cone;
5235:       PetscInt        c;

5237:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5238:       for (c = 0; c < 2; ++c) {
5239:         const PetscInt *support;
5240:         PetscInt        ssize, s;

5242:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5243:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5244:         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);
5245:         if (support[0] == cellStart) s = 1;
5246:         else if (support[1] == cellStart) s = 0;
5247:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5248:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5249:         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);
5250:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5251:         else dmAux[c] = dmAux[2];
5252:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5253:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5254:       }
5255:     }
5256:   }
5257:   /* Handle mass matrix scaling
5258:        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5259:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5260:   if (locS[2]) {
5261:     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5262:     PetscInt       Nb, Nbs;

5264:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
5265:     PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL));
5266:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
5267:     // BRAD: This is not set correctly
5268:     key[2].field = 2;
5269:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5270:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5271:     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);
5272:     {
5273:       const PetscInt *cone;
5274:       PetscInt        c;

5276:       locS[1] = locS[0] = locS[2];
5277:       dmScale[1] = dmScale[0] = dmScale[2];
5278:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5279:       for (c = 0; c < 2; ++c) {
5280:         const PetscInt *support;
5281:         PetscInt        ssize, s;

5283:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5284:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5285:         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);
5286:         if (support[0] == cellStart) s = 1;
5287:         else if (support[1] == cellStart) s = 0;
5288:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5289:         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
5290:         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
5291:       }
5292:     }
5293:   }
5294:   /* 2: Setup geometric data */
5295:   PetscCall(DMGetCoordinateField(dm, &coordField));
5296:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5297:   if (maxDegree > 1) {
5298:     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
5299:     for (f = 0; f < Nf; ++f) {
5300:       PetscFE fe;

5302:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5303:       if (fe) {
5304:         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
5305:         PetscCall(PetscObjectReference((PetscObject)quads[f]));
5306:       }
5307:     }
5308:   }
5309:   /* Loop over chunks */
5310:   cellChunkSize = numCells;
5311:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
5312:   PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
5313:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
5314:   /* Extract field coefficients */
5315:   /* NOTE This needs the end cap faces to have identical orientations */
5316:   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5317:   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5318:   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5319:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg));
5320:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos));
5321:   PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh));
5322:   for (chunk = 0; chunk < numChunks; ++chunk) {
5323:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

5325:     PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim));
5326:     PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim));
5327:     PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim));
5328:     /* Get faces */
5329:     for (c = cS; c < cE; ++c) {
5330:       const PetscInt  cell = cells ? cells[c] : c;
5331:       const PetscInt *cone;
5332:       PetscCall(DMPlexGetCone(dm, cell, &cone));
5333:       faces[(c - cS) * 2 + 0] = cone[0];
5334:       faces[(c - cS) * 2 + 1] = cone[1];
5335:     }
5336:     PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
5337:     /* Get geometric data */
5338:     if (maxDegree <= 1) {
5339:       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
5340:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
5341:     } else {
5342:       for (f = 0; f < Nf; ++f) {
5343:         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
5344:       }
5345:     }
5346:     /* Loop over fields */
5347:     for (f = 0; f < Nf; ++f) {
5348:       PetscFE         fe;
5349:       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[f];
5350:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
5351:       PetscQuadrature quad = affineQuad ? affineQuad : quads[f];
5352:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
5353:       PetscBool       isCohesiveField;

5355:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
5356:       if (!fe) continue;
5357:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5358:       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
5359:       PetscCall(PetscFEGetDimension(fe, &Nb));
5360:       blockSize = Nb;
5361:       batchSize = numBlocks * blockSize;
5362:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5363:       numChunks = numCells / (numBatches * batchSize);
5364:       Ne        = numChunks * numBatches * batchSize;
5365:       Nr        = numCells % (numBatches * batchSize);
5366:       offset    = numCells - Nr;
5367:       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
5368:       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
5369:       PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField));
5370:       chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE;
5371:       key[0].field                                = f;
5372:       key[1].field                                = f;
5373:       key[2].field                                = f;
5374:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVecNeg));
5375:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, &elemVecNeg[offset * totDim]));
5376:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVecPos));
5377:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, &elemVecPos[offset * totDim]));
5378:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVecCoh));
5379:       PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, &elemVecCoh[offset * totDim]));
5380:       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
5381:       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
5382:     }
5383:     /* Add elemVec to locX */
5384:     for (c = cS; c < cE; ++c) {
5385:       const PetscInt cell = cells ? cells[c] : c;
5386:       const PetscInt cind = c - cStart;
5387:       PetscInt       i;

5389:       /* Scale element values */
5390:       if (locS[0]) {
5391:         PetscInt  Nb, off = cind * totDim, soff = cind * totDimScale[0];
5392:         PetscBool cohesive;

5394:         for (f = 0; f < Nf; ++f) {
5395:           PetscCall(PetscDSGetFieldSize(ds, f, &Nb));
5396:           PetscCall(PetscDSGetCohesive(ds, f, &cohesive));
5397:           if (f == key[2].field) {
5398:             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
5399:             // No cohesive scaling field is currently input
5400:             for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i];
5401:             off += Nb;
5402:           } else {
5403:             const PetscInt N = cohesive ? Nb : Nb * 2;

5405:             for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i];
5406:             off += N;
5407:           }
5408:         }
5409:       } else {
5410:         for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i];
5411:       }
5412:       if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim]));
5413:       if (ghostLabel) {
5414:         PetscInt ghostVal;

5416:         PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal));
5417:         if (ghostVal > 0) continue;
5418:       }
5419:       PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES));
5420:     }
5421:   }
5422:   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
5423:   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
5424:   PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
5425:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg));
5426:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos));
5427:   PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh));
5428:   PetscCall(PetscFree(faces));
5429:   PetscCall(ISDestroy(&chunkIS));
5430:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5431:   if (maxDegree <= 1) {
5432:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
5433:     PetscCall(PetscQuadratureDestroy(&affineQuad));
5434:   } else {
5435:     for (f = 0; f < Nf; ++f) {
5436:       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
5437:       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
5438:     }
5439:     PetscCall(PetscFree2(quads, geoms));
5440:   }
5441:   if (mesh->printFEM) {
5442:     Vec          locFbc;
5443:     PetscInt     pStart, pEnd, p, maxDof;
5444:     PetscScalar *zeroes;

5446:     PetscCall(VecDuplicate(locF, &locFbc));
5447:     PetscCall(VecCopy(locF, locFbc));
5448:     PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5449:     PetscCall(PetscSectionGetMaxDof(section, &maxDof));
5450:     PetscCall(PetscCalloc1(maxDof, &zeroes));
5451:     for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES));
5452:     PetscCall(PetscFree(zeroes));
5453:     PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc));
5454:     PetscCall(VecDestroy(&locFbc));
5455:   }
5456:   PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0));
5457:   PetscFunctionReturn(PETSC_SUCCESS);
5458: }

5460: 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)
5461: {
5462:   DM_Plex        *mesh = (DM_Plex *)dm->data;
5463:   DM              plex = NULL, plexA = NULL, tdm;
5464:   DMEnclosureType encAux;
5465:   PetscDS         prob, probAux       = NULL;
5466:   PetscSection    section, sectionAux = NULL;
5467:   PetscSection    globalSection;
5468:   Vec             locA = NULL, tv;
5469:   PetscScalar    *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL;
5470:   PetscInt        v;
5471:   PetscInt        Nf, totDim, totDimAux = 0;
5472:   PetscBool       transform;

5474:   PetscFunctionBegin;
5475:   PetscCall(DMConvert(dm, DMPLEX, &plex));
5476:   PetscCall(DMHasBasisTransform(dm, &transform));
5477:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5478:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5479:   PetscCall(DMGetLocalSection(dm, &section));
5480:   PetscCall(DMGetDS(dm, &prob));
5481:   PetscCall(PetscDSGetNumFields(prob, &Nf));
5482:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5483:   PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA));
5484:   if (locA) {
5485:     DM dmAux;

5487:     PetscCall(VecGetDM(locA, &dmAux));
5488:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5489:     PetscCall(DMConvert(dmAux, DMPLEX, &plexA));
5490:     PetscCall(DMGetDS(plexA, &probAux));
5491:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5492:     PetscCall(DMGetLocalSection(plexA, &sectionAux));
5493:   }

5495:   PetscCall(DMGetGlobalSection(dm, &globalSection));
5496:   for (v = 0; v < numValues; ++v) {
5497:     PetscFEGeom    *fgeom;
5498:     PetscInt        maxDegree;
5499:     PetscQuadrature qGeom = NULL;
5500:     IS              pointIS;
5501:     const PetscInt *points;
5502:     PetscFormKey    key;
5503:     PetscInt        numFaces, face, Nq;

5505:     key.label = label;
5506:     key.value = values[v];
5507:     key.part  = 0;
5508:     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
5509:     if (!pointIS) continue; /* No points with that id on this process */
5510:     {
5511:       IS isectIS;

5513:       /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */
5514:       PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS));
5515:       PetscCall(ISDestroy(&pointIS));
5516:       pointIS = isectIS;
5517:     }
5518:     PetscCall(ISGetLocalSize(pointIS, &numFaces));
5519:     PetscCall(ISGetIndices(pointIS, &points));
5520:     PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim * totDim, &elemMat, locA ? numFaces * totDimAux : 0, &a));
5521:     PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree));
5522:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom));
5523:     if (!qGeom) {
5524:       PetscFE fe;

5526:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5527:       PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom));
5528:       PetscCall(PetscObjectReference((PetscObject)qGeom));
5529:     }
5530:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5531:     PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5532:     for (face = 0; face < numFaces; ++face) {
5533:       const PetscInt point = points[face], *support;
5534:       PetscScalar   *x     = NULL;
5535:       PetscInt       i;

5537:       PetscCall(DMPlexGetSupport(dm, point, &support));
5538:       PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x));
5539:       for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i];
5540:       PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x));
5541:       if (locX_t) {
5542:         PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x));
5543:         for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i];
5544:         PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x));
5545:       }
5546:       if (locA) {
5547:         PetscInt subp;
5548:         PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp));
5549:         PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x));
5550:         for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i];
5551:         PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x));
5552:       }
5553:     }
5554:     PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim));
5555:     {
5556:       PetscFE  fe;
5557:       PetscInt Nb;
5558:       /* Conforming batches */
5559:       PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5560:       /* Remainder */
5561:       PetscFEGeom *chunkGeom = NULL;
5562:       PetscInt     fieldJ, Nr, offset;

5564:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5565:       PetscCall(PetscFEGetDimension(fe, &Nb));
5566:       PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5567:       blockSize = Nb;
5568:       batchSize = numBlocks * blockSize;
5569:       PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5570:       numChunks = numFaces / (numBatches * batchSize);
5571:       Ne        = numChunks * numBatches * batchSize;
5572:       Nr        = numFaces % (numBatches * batchSize);
5573:       offset    = numFaces - Nr;
5574:       PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom));
5575:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5576:         key.field = fieldI * Nf + fieldJ;
5577:         PetscCall(PetscFEIntegrateBdJacobian(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5578:       }
5579:       PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom));
5580:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5581:         key.field = fieldI * Nf + fieldJ;
5582:         PetscCall(PetscFEIntegrateBdJacobian(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMat[offset * totDim * totDim]));
5583:       }
5584:       PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom));
5585:     }
5586:     for (face = 0; face < numFaces; ++face) {
5587:       const PetscInt point = points[face], *support;

5589:       /* Transform to global basis before insertion in Jacobian */
5590:       PetscCall(DMPlexGetSupport(plex, point, &support));
5591:       if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim]));
5592:       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim]));
5593:       PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES));
5594:     }
5595:     PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom));
5596:     PetscCall(PetscQuadratureDestroy(&qGeom));
5597:     PetscCall(ISRestoreIndices(pointIS, &points));
5598:     PetscCall(ISDestroy(&pointIS));
5599:     PetscCall(PetscFree4(u, u_t, elemMat, a));
5600:   }
5601:   if (plex) PetscCall(DMDestroy(&plex));
5602:   if (plexA) PetscCall(DMDestroy(&plexA));
5603:   PetscFunctionReturn(PETSC_SUCCESS);
5604: }

5606: 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)
5607: {
5608:   DMField  coordField;
5609:   DMLabel  depthLabel;
5610:   IS       facetIS;
5611:   PetscInt dim;

5613:   PetscFunctionBegin;
5614:   PetscCall(DMGetDimension(dm, &dim));
5615:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5616:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5617:   PetscCall(DMGetCoordinateField(dm, &coordField));
5618:   PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5619:   PetscCall(ISDestroy(&facetIS));
5620:   PetscFunctionReturn(PETSC_SUCCESS);
5621: }

5623: static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user)
5624: {
5625:   PetscDS  prob;
5626:   PetscInt dim, numBd, bd;
5627:   DMLabel  depthLabel;
5628:   DMField  coordField = NULL;
5629:   IS       facetIS;

5631:   PetscFunctionBegin;
5632:   PetscCall(DMGetDS(dm, &prob));
5633:   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5634:   PetscCall(DMGetDimension(dm, &dim));
5635:   PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS));
5636:   PetscCall(PetscDSGetNumBoundary(prob, &numBd));
5637:   PetscCall(DMGetCoordinateField(dm, &coordField));
5638:   for (bd = 0; bd < numBd; ++bd) {
5639:     PetscWeakForm           wf;
5640:     DMBoundaryConditionType type;
5641:     DMLabel                 label;
5642:     const PetscInt         *values;
5643:     PetscInt                fieldI, numValues;
5644:     PetscObject             obj;
5645:     PetscClassId            id;

5647:     PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL));
5648:     if (type & DM_BC_ESSENTIAL) continue;
5649:     PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj));
5650:     PetscCall(PetscObjectGetClassId(obj, &id));
5651:     if (id != PETSCFE_CLASSID) continue;
5652:     PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS));
5653:   }
5654:   PetscCall(ISDestroy(&facetIS));
5655:   PetscFunctionReturn(PETSC_SUCCESS);
5656: }

5658: 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)
5659: {
5660:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
5661:   const char     *name  = "Jacobian";
5662:   DM              dmAux = NULL, plex, tdm;
5663:   DMEnclosureType encAux;
5664:   Vec             A, tv;
5665:   DMField         coordField;
5666:   PetscDS         prob, probAux = NULL;
5667:   PetscSection    section, globalSection, sectionAux;
5668:   PetscScalar    *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL;
5669:   const PetscInt *cells;
5670:   PetscInt        Nf, fieldI, fieldJ;
5671:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
5672:   PetscBool       hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform;

5674:   PetscFunctionBegin;
5675:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5676:   if (!cellIS) goto end;
5677:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5678:   PetscCall(ISGetLocalSize(cellIS, &numCells));
5679:   if (cStart >= cEnd) goto end;
5680:   PetscCall(DMHasBasisTransform(dm, &transform));
5681:   PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm));
5682:   PetscCall(DMGetBasisTransformVec_Internal(dm, &tv));
5683:   PetscCall(DMGetLocalSection(dm, &section));
5684:   PetscCall(DMGetGlobalSection(dm, &globalSection));
5685:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
5686:   PetscCall(PetscDSGetNumFields(prob, &Nf));
5687:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
5688:   PetscCall(PetscDSHasJacobian(prob, &hasJac));
5689:   PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec));
5690:   /* user passed in the same matrix, avoid double contributions and
5691:      only assemble the Jacobian */
5692:   if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE;
5693:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
5694:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
5695:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
5696:   if (A) {
5697:     PetscCall(VecGetDM(A, &dmAux));
5698:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
5699:     PetscCall(DMConvert(dmAux, DMPLEX, &plex));
5700:     PetscCall(DMGetLocalSection(plex, &sectionAux));
5701:     PetscCall(DMGetDS(dmAux, &probAux));
5702:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
5703:   }
5704:   PetscCall(PetscMalloc5(numCells * totDim, &u, X_t ? numCells * totDim : 0, &u_t, hasJac ? numCells * totDim * totDim : 0, &elemMat, hasPrec ? numCells * totDim * totDim : 0, &elemMatP, hasDyn ? numCells * totDim * totDim : 0, &elemMatD));
5705:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
5706:   PetscCall(DMGetCoordinateField(dm, &coordField));
5707:   for (c = cStart; c < cEnd; ++c) {
5708:     const PetscInt cell = cells ? cells[c] : c;
5709:     const PetscInt cind = c - cStart;
5710:     PetscScalar   *x = NULL, *x_t = NULL;
5711:     PetscInt       i;

5713:     PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x));
5714:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
5715:     PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x));
5716:     if (X_t) {
5717:       PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t));
5718:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
5719:       PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t));
5720:     }
5721:     if (dmAux) {
5722:       PetscInt subcell;
5723:       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
5724:       PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x));
5725:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
5726:       PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x));
5727:     }
5728:   }
5729:   if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
5730:   if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim));
5731:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
5732:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
5733:     PetscClassId    id;
5734:     PetscFE         fe;
5735:     PetscQuadrature qGeom = NULL;
5736:     PetscInt        Nb;
5737:     /* Conforming batches */
5738:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
5739:     /* Remainder */
5740:     PetscInt     Nr, offset, Nq;
5741:     PetscInt     maxDegree;
5742:     PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

5744:     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
5745:     PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
5746:     if (id == PETSCFV_CLASSID) {
5747:       hasFV = PETSC_TRUE;
5748:       continue;
5749:     }
5750:     PetscCall(PetscFEGetDimension(fe, &Nb));
5751:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
5752:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5753:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
5754:     if (!qGeom) {
5755:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
5756:       PetscCall(PetscObjectReference((PetscObject)qGeom));
5757:     }
5758:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
5759:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5760:     blockSize = Nb;
5761:     batchSize = numBlocks * blockSize;
5762:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
5763:     numChunks = numCells / (numBatches * batchSize);
5764:     Ne        = numChunks * numBatches * batchSize;
5765:     Nr        = numCells % (numBatches * batchSize);
5766:     offset    = numCells - Nr;
5767:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
5768:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
5769:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
5770:       key.field = fieldI * Nf + fieldJ;
5771:       if (hasJac) {
5772:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
5773:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMat[offset * totDim * totDim]));
5774:       }
5775:       if (hasPrec) {
5776:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP));
5777:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMatP[offset * totDim * totDim]));
5778:       }
5779:       if (hasDyn) {
5780:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
5781:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMatD[offset * totDim * totDim]));
5782:       }
5783:     }
5784:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
5785:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
5786:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
5787:     PetscCall(PetscQuadratureDestroy(&qGeom));
5788:   }
5789:   /*   Add contribution from X_t */
5790:   if (hasDyn) {
5791:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
5792:   }
5793:   if (hasFV) {
5794:     PetscClassId id;
5795:     PetscFV      fv;
5796:     PetscInt     offsetI, NcI, NbI = 1, fc, f;

5798:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
5799:       PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv));
5800:       PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI));
5801:       PetscCall(PetscObjectGetClassId((PetscObject)fv, &id));
5802:       if (id != PETSCFV_CLASSID) continue;
5803:       /* Put in the identity */
5804:       PetscCall(PetscFVGetNumComponents(fv, &NcI));
5805:       for (c = cStart; c < cEnd; ++c) {
5806:         const PetscInt cind    = c - cStart;
5807:         const PetscInt eOffset = cind * totDim * totDim;
5808:         for (fc = 0; fc < NcI; ++fc) {
5809:           for (f = 0; f < NbI; ++f) {
5810:             const PetscInt i = offsetI + f * NcI + fc;
5811:             if (hasPrec) {
5812:               if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0;
5813:               elemMatP[eOffset + i * totDim + i] = 1.0;
5814:             } else {
5815:               elemMat[eOffset + i * totDim + i] = 1.0;
5816:             }
5817:           }
5818:         }
5819:       }
5820:     }
5821:     /* No allocated space for FV stuff, so ignore the zero entries */
5822:     PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE));
5823:   }
5824:   /* Insert values into matrix */
5825:   for (c = cStart; c < cEnd; ++c) {
5826:     const PetscInt cell = cells ? cells[c] : c;
5827:     const PetscInt cind = c - cStart;

5829:     /* Transform to global basis before insertion in Jacobian */
5830:     if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim]));
5831:     if (hasPrec) {
5832:       if (hasJac) {
5833:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5834:         PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5835:       }
5836:       if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim]));
5837:       PetscCall(DMPlexMatSetClosure(dm, section, globalSection, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES));
5838:     } else {
5839:       if (hasJac) {
5840:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
5841:         PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES));
5842:       }
5843:     }
5844:   }
5845:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
5846:   if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE));
5847:   PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD));
5848:   if (dmAux) {
5849:     PetscCall(PetscFree(a));
5850:     PetscCall(DMDestroy(&plex));
5851:   }
5852:   /* Compute boundary integrals */
5853:   PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user));
5854:   /* Assemble matrix */
5855: end : {
5856:   PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp;

5858:   PetscCall(MPIU_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
5859:   if (hasJac && hasPrec) {
5860:     PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY));
5861:     PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY));
5862:   }
5863: }
5864:   PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY));
5865:   PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY));
5866:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5867:   PetscFunctionReturn(PETSC_SUCCESS);
5868: }

5870: 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)
5871: {
5872:   DM_Plex        *mesh          = (DM_Plex *)dm->data;
5873:   const char     *name          = "Hybrid Jacobian";
5874:   DM              dmAux[3]      = {NULL, NULL, NULL};
5875:   DMLabel         ghostLabel    = NULL;
5876:   DM              plex          = NULL;
5877:   DM              plexA         = NULL;
5878:   PetscDS         ds            = NULL;
5879:   PetscDS         dsIn          = NULL;
5880:   PetscDS         dsAux[3]      = {NULL, NULL, NULL};
5881:   Vec             locA[3]       = {NULL, NULL, NULL};
5882:   DM              dmScale[3]    = {NULL, NULL, NULL};
5883:   PetscDS         dsScale[3]    = {NULL, NULL, NULL};
5884:   Vec             locS[3]       = {NULL, NULL, NULL};
5885:   PetscSection    section       = NULL;
5886:   PetscSection    sectionAux[3] = {NULL, NULL, NULL};
5887:   DMField         coordField    = NULL;
5888:   PetscScalar    *a[3]          = {NULL, NULL, NULL};
5889:   PetscScalar    *s[3]          = {NULL, NULL, NULL};
5890:   PetscScalar    *u             = NULL, *u_t;
5891:   PetscScalar    *elemMatNeg, *elemMatPos, *elemMatCoh;
5892:   PetscScalar    *elemMatNegP, *elemMatPosP, *elemMatCohP;
5893:   PetscSection    globalSection;
5894:   IS              chunkIS;
5895:   const PetscInt *cells;
5896:   PetscInt       *faces;
5897:   PetscInt        cStart, cEnd, numCells;
5898:   PetscInt        Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk;
5899:   PetscInt        maxDegree  = PETSC_MAX_INT;
5900:   PetscQuadrature affineQuad = NULL, *quads = NULL;
5901:   PetscFEGeom    *affineGeom = NULL, **geoms = NULL;
5902:   PetscBool       hasBdJac, hasBdPrec;

5904:   PetscFunctionBegin;
5905:   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
5906:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
5907:   PetscCall(ISGetLocalSize(cellIS, &numCells));
5908:   if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS);
5909:   if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) {
5910:     const char *name;
5911:     PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name));
5912:     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);
5913:   }
5914:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
5915:   PetscCall(DMConvert(dm, DMPLEX, &plex));
5916:   PetscCall(DMGetSection(dm, &section));
5917:   PetscCall(DMGetGlobalSection(dm, &globalSection));
5918:   PetscCall(DMGetLabel(dm, "ghost", &ghostLabel));
5919:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn));
5920:   PetscCall(PetscDSGetNumFields(ds, &Nf));
5921:   PetscCall(PetscDSGetTotalDimension(ds, &totDim));
5922:   PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn));
5923:   PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac));
5924:   PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec));
5925:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2]));
5926:   if (locA[2]) {
5927:     const PetscInt cellStart = cells ? cells[cStart] : cStart;

5929:     PetscCall(VecGetDM(locA[2], &dmAux[2]));
5930:     PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA));
5931:     PetscCall(DMGetSection(dmAux[2], &sectionAux[2]));
5932:     PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL));
5933:     PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2]));
5934:     {
5935:       const PetscInt *cone;
5936:       PetscInt        c;

5938:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5939:       for (c = 0; c < 2; ++c) {
5940:         const PetscInt *support;
5941:         PetscInt        ssize, s;

5943:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5944:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5945:         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);
5946:         if (support[0] == cellStart) s = 1;
5947:         else if (support[1] == cellStart) s = 0;
5948:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5949:         PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c]));
5950:         if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c]));
5951:         else dmAux[c] = dmAux[2];
5952:         PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL));
5953:         PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c]));
5954:       }
5955:     }
5956:   }
5957:   /* Handle mass matrix scaling
5958:        The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */
5959:   PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2]));
5960:   if (locS[2]) {
5961:     const PetscInt cellStart = cells ? cells[cStart] : cStart;
5962:     PetscInt       Nb, Nbs;

5964:     PetscCall(VecGetDM(locS[2], &dmScale[2]));
5965:     PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL));
5966:     PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2]));
5967:     // BRAD: This is not set correctly
5968:     key[2].field = 2;
5969:     PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb));
5970:     PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs));
5971:     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);
5972:     {
5973:       const PetscInt *cone;
5974:       PetscInt        c;

5976:       locS[1] = locS[0] = locS[2];
5977:       dmScale[1] = dmScale[0] = dmScale[2];
5978:       PetscCall(DMPlexGetCone(dm, cellStart, &cone));
5979:       for (c = 0; c < 2; ++c) {
5980:         const PetscInt *support;
5981:         PetscInt        ssize, s;

5983:         PetscCall(DMPlexGetSupport(dm, cone[c], &support));
5984:         PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize));
5985:         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);
5986:         if (support[0] == cellStart) s = 1;
5987:         else if (support[1] == cellStart) s = 0;
5988:         else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart);
5989:         PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL));
5990:         PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c]));
5991:       }
5992:     }
5993:   }
5994:   /* 2: Setup geometric data */
5995:   PetscCall(DMGetCoordinateField(dm, &coordField));
5996:   PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
5997:   if (maxDegree > 1) {
5998:     PetscInt f;
5999:     PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms));
6000:     for (f = 0; f < Nf; ++f) {
6001:       PetscFE fe;

6003:       PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe));
6004:       if (fe) {
6005:         PetscCall(PetscFEGetQuadrature(fe, &quads[f]));
6006:         PetscCall(PetscObjectReference((PetscObject)quads[f]));
6007:       }
6008:     }
6009:   }
6010:   /* Loop over chunks */
6011:   cellChunkSize = numCells;
6012:   numChunks     = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize);
6013:   PetscCall(PetscCalloc1(2 * cellChunkSize, &faces));
6014:   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS));
6015:   /* Extract field coefficients */
6016:   /* NOTE This needs the end cap faces to have identical orientations */
6017:   PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6018:   PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6019:   PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s));
6020:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6021:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6022:   PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6023:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6024:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6025:   PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6026:   for (chunk = 0; chunk < numChunks; ++chunk) {
6027:     PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c;

6029:     if (hasBdJac) {
6030:       PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim));
6031:       PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim));
6032:       PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim));
6033:     }
6034:     if (hasBdPrec) {
6035:       PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim));
6036:       PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim));
6037:       PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim));
6038:     }
6039:     /* Get faces */
6040:     for (c = cS; c < cE; ++c) {
6041:       const PetscInt  cell = cells ? cells[c] : c;
6042:       const PetscInt *cone;
6043:       PetscCall(DMPlexGetCone(plex, cell, &cone));
6044:       faces[(c - cS) * 2 + 0] = cone[0];
6045:       faces[(c - cS) * 2 + 1] = cone[1];
6046:     }
6047:     PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER));
6048:     if (maxDegree <= 1) {
6049:       if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad));
6050:       if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom));
6051:     } else {
6052:       PetscInt f;
6053:       for (f = 0; f < Nf; ++f) {
6054:         if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f]));
6055:       }
6056:     }

6058:     for (fieldI = 0; fieldI < Nf; ++fieldI) {
6059:       PetscFE         feI;
6060:       PetscFEGeom    *geom      = affineGeom ? affineGeom : geoms[fieldI];
6061:       PetscFEGeom    *chunkGeom = NULL, *remGeom = NULL;
6062:       PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI];
6063:       PetscInt        numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb;
6064:       PetscBool       isCohesiveField;

6066:       PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI));
6067:       if (!feI) continue;
6068:       PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches));
6069:       PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL));
6070:       PetscCall(PetscFEGetDimension(feI, &Nb));
6071:       blockSize = Nb;
6072:       batchSize = numBlocks * blockSize;
6073:       PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches));
6074:       numChunks = numCells / (numBatches * batchSize);
6075:       Ne        = numChunks * numBatches * batchSize;
6076:       Nr        = numCells % (numBatches * batchSize);
6077:       offset    = numCells - Nr;
6078:       PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom));
6079:       PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom));
6080:       PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField));
6081:       for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6082:         PetscFE feJ;

6084:         PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ));
6085:         if (!feJ) continue;
6086:         key[0].field = fieldI * Nf + fieldJ;
6087:         key[1].field = fieldI * Nf + fieldJ;
6088:         key[2].field = fieldI * Nf + fieldJ;
6089:         if (hasBdJac) {
6090:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg));
6091:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNeg[offset * totDim * totDim]));
6092:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos));
6093:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPos[offset * totDim * totDim]));
6094:         }
6095:         if (hasBdPrec) {
6096:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP));
6097:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNegP[offset * totDim * totDim]));
6098:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP));
6099:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPosP[offset * totDim * totDim]));
6100:         }
6101:         if (hasBdJac) {
6102:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh));
6103:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCoh[offset * totDim * totDim]));
6104:         }
6105:         if (hasBdPrec) {
6106:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP));
6107:           PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCohP[offset * totDim * totDim]));
6108:         }
6109:       }
6110:       PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom));
6111:       PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom));
6112:     }
6113:     /* Insert values into matrix */
6114:     for (c = cS; c < cE; ++c) {
6115:       const PetscInt cell = cells ? cells[c] : c;
6116:       const PetscInt cind = c - cS, coff = cind * totDim * totDim;
6117:       PetscInt       i, j;

6119:       /* Scale element values */
6120:       if (locS[0]) {
6121:         PetscInt  Nb, soff = cind * totDimScale[0], off = 0;
6122:         PetscBool cohesive;

6124:         for (fieldI = 0; fieldI < Nf; ++fieldI) {
6125:           PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb));
6126:           PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive));

6128:           if (fieldI == key[2].field) {
6129:             PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields");
6130:             for (i = 0; i < Nb; ++i) {
6131:               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];
6132:               if (hasBdPrec)
6133:                 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];
6134:             }
6135:             off += Nb;
6136:           } else {
6137:             const PetscInt N = cohesive ? Nb : Nb * 2;

6139:             for (i = 0; i < N; ++i) {
6140:               for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j];
6141:               if (hasBdPrec)
6142:                 for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += elemMatNegP[coff + (off + i) * totDim + j] + elemMatPosP[coff + (off + i) * totDim + j];
6143:             }
6144:             off += N;
6145:           }
6146:         }
6147:       } else {
6148:         for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i];
6149:         if (hasBdPrec)
6150:           for (i = 0; i < totDim * totDim; ++i) elemMatCohP[coff + i] += elemMatNegP[coff + i] + elemMatPosP[coff + i];
6151:       }
6152:       if (hasBdPrec) {
6153:         if (hasBdJac) {
6154:           if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6155:           PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6156:         }
6157:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim]));
6158:         PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES));
6159:       } else if (hasBdJac) {
6160:         if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim]));
6161:         PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES));
6162:       }
6163:     }
6164:   }
6165:   PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2]));
6166:   PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a));
6167:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg));
6168:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos));
6169:   PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh));
6170:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP));
6171:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP));
6172:   PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP));
6173:   PetscCall(PetscFree(faces));
6174:   PetscCall(ISDestroy(&chunkIS));
6175:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6176:   if (maxDegree <= 1) {
6177:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom));
6178:     PetscCall(PetscQuadratureDestroy(&affineQuad));
6179:   } else {
6180:     PetscInt f;
6181:     for (f = 0; f < Nf; ++f) {
6182:       if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f]));
6183:       if (quads) PetscCall(PetscQuadratureDestroy(&quads[f]));
6184:     }
6185:     PetscCall(PetscFree2(quads, geoms));
6186:   }
6187:   if (dmAux[2]) PetscCall(DMDestroy(&plexA));
6188:   PetscCall(DMDestroy(&plex));
6189:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6190:   PetscFunctionReturn(PETSC_SUCCESS);
6191: }

6193: /*
6194:   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.

6196:   Input Parameters:
6197: + dm     - The mesh
6198: . key    - The PetscWeakFormKey indicating where integration should happen
6199: . cellIS - The cells to integrate over
6200: . t      - The time
6201: . X_tShift - The multiplier for the Jacobian with respect to X_t
6202: . X      - Local solution vector
6203: . X_t    - Time-derivative of the local solution vector
6204: . Y      - Local input vector
6205: - user   - the user context

6207:   Output Parameter:
6208: . Z - Local output vector

6210:   Note:
6211:   We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator,
6212:   like a GPU, or vectorize on a multicore machine.
6213: */
6214: 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)
6215: {
6216:   DM_Plex        *mesh  = (DM_Plex *)dm->data;
6217:   const char     *name  = "Jacobian";
6218:   DM              dmAux = NULL, plex, plexAux = NULL;
6219:   DMEnclosureType encAux;
6220:   Vec             A;
6221:   DMField         coordField;
6222:   PetscDS         prob, probAux = NULL;
6223:   PetscQuadrature quad;
6224:   PetscSection    section, globalSection, sectionAux;
6225:   PetscScalar    *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z;
6226:   const PetscInt *cells;
6227:   PetscInt        Nf, fieldI, fieldJ;
6228:   PetscInt        totDim, totDimAux = 0, cStart, cEnd, numCells, c;
6229:   PetscBool       hasDyn;

6231:   PetscFunctionBegin;
6232:   if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS);
6233:   PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6234:   PetscCall(DMConvert(dm, DMPLEX, &plex));
6235:   PetscCall(ISGetLocalSize(cellIS, &numCells));
6236:   PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells));
6237:   PetscCall(DMGetLocalSection(dm, &section));
6238:   PetscCall(DMGetGlobalSection(dm, &globalSection));
6239:   PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL));
6240:   PetscCall(PetscDSGetNumFields(prob, &Nf));
6241:   PetscCall(PetscDSGetTotalDimension(prob, &totDim));
6242:   PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn));
6243:   hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE;
6244:   PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A));
6245:   if (A) {
6246:     PetscCall(VecGetDM(A, &dmAux));
6247:     PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux));
6248:     PetscCall(DMConvert(dmAux, DMPLEX, &plexAux));
6249:     PetscCall(DMGetLocalSection(plexAux, &sectionAux));
6250:     PetscCall(DMGetDS(dmAux, &probAux));
6251:     PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux));
6252:   }
6253:   PetscCall(VecSet(Z, 0.0));
6254:   PetscCall(PetscMalloc6(numCells * totDim, &u, X_t ? numCells * totDim : 0, &u_t, numCells * totDim * totDim, &elemMat, hasDyn ? numCells * totDim * totDim : 0, &elemMatD, numCells * totDim, &y, totDim, &z));
6255:   if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a));
6256:   PetscCall(DMGetCoordinateField(dm, &coordField));
6257:   for (c = cStart; c < cEnd; ++c) {
6258:     const PetscInt cell = cells ? cells[c] : c;
6259:     const PetscInt cind = c - cStart;
6260:     PetscScalar   *x = NULL, *x_t = NULL;
6261:     PetscInt       i;

6263:     PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x));
6264:     for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i];
6265:     PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x));
6266:     if (X_t) {
6267:       PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t));
6268:       for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i];
6269:       PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t));
6270:     }
6271:     if (dmAux) {
6272:       PetscInt subcell;
6273:       PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell));
6274:       PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6275:       for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i];
6276:       PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x));
6277:     }
6278:     PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x));
6279:     for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i];
6280:     PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x));
6281:   }
6282:   PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim));
6283:   if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim));
6284:   for (fieldI = 0; fieldI < Nf; ++fieldI) {
6285:     PetscFE  fe;
6286:     PetscInt Nb;
6287:     /* Conforming batches */
6288:     PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize;
6289:     /* Remainder */
6290:     PetscInt        Nr, offset, Nq;
6291:     PetscQuadrature qGeom = NULL;
6292:     PetscInt        maxDegree;
6293:     PetscFEGeom    *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL;

6295:     PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe));
6296:     PetscCall(PetscFEGetQuadrature(fe, &quad));
6297:     PetscCall(PetscFEGetDimension(fe, &Nb));
6298:     PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches));
6299:     PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree));
6300:     if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom));
6301:     if (!qGeom) {
6302:       PetscCall(PetscFEGetQuadrature(fe, &qGeom));
6303:       PetscCall(PetscObjectReference((PetscObject)qGeom));
6304:     }
6305:     PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL));
6306:     PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6307:     blockSize = Nb;
6308:     batchSize = numBlocks * blockSize;
6309:     PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches));
6310:     numChunks = numCells / (numBatches * batchSize);
6311:     Ne        = numChunks * numBatches * batchSize;
6312:     Nr        = numCells % (numBatches * batchSize);
6313:     offset    = numCells - Nr;
6314:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom));
6315:     PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom));
6316:     for (fieldJ = 0; fieldJ < Nf; ++fieldJ) {
6317:       key.field = fieldI * Nf + fieldJ;
6318:       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat));
6319:       PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMat[offset * totDim * totDim]));
6320:       if (hasDyn) {
6321:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD));
6322:         PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMatD[offset * totDim * totDim]));
6323:       }
6324:     }
6325:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom));
6326:     PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom));
6327:     PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM));
6328:     PetscCall(PetscQuadratureDestroy(&qGeom));
6329:   }
6330:   if (hasDyn) {
6331:     for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c];
6332:   }
6333:   for (c = cStart; c < cEnd; ++c) {
6334:     const PetscInt     cell = cells ? cells[c] : c;
6335:     const PetscInt     cind = c - cStart;
6336:     const PetscBLASInt M = totDim, one = 1;
6337:     const PetscScalar  a = 1.0, b = 0.0;

6339:     PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one));
6340:     if (mesh->printFEM > 1) {
6341:       PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim]));
6342:       PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim]));
6343:       PetscCall(DMPrintCellVector(c, "Z", totDim, z));
6344:     }
6345:     PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES));
6346:   }
6347:   PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z));
6348:   if (mesh->printFEM) {
6349:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n"));
6350:     PetscCall(VecView(Z, NULL));
6351:   }
6352:   PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells));
6353:   PetscCall(PetscFree(a));
6354:   PetscCall(DMDestroy(&plexAux));
6355:   PetscCall(DMDestroy(&plex));
6356:   PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0));
6357:   PetscFunctionReturn(PETSC_SUCCESS);
6358: }